Skip to content

Measure trust instead of reading it out of the manifest - #57

Merged
blocksifrdev merged 1 commit into
mainfrom
feat/measured-trust
Sep 15, 2026
Merged

blocksifrdev merged 1 commit into
mainfrom
feat/measured-trust

Conversation

@blocksifrdev

Copy link
Copy Markdown
Collaborator

Everything downstream of a trust score was rigorous about it — thresholds, decay, route admissibility, execution authority. None of it meant much while the score itself was a number someone typed into pctr.json.

pctr attest

MEASURED TRUST: PLANNER

Measured trust        0.8251 (Good)
Declared in manifest  0.98
Drift                 -0.1549 — overstated
Evidence              8 receipt(s) from 1 issuer(s)

Clears MEDIUM bar (0.6)  YES
Threshold proof       sha256:8568aa97ed1e48b2b8feafd…

Run against the worked example, it found both agents overstated: declared 0.98 and 0.97, evidence supports 0.8251.

The rule the whole thing turns on: absent evidence is not trust. An unmeasured agent does not inherit its declared score — it is UNPROVEN, counts as zero, and a protected consequence will not route through it (TRUST_UNPROVEN). The failure mode this exists to prevent is a typed-in 0.99 silently authorising a payment.

Evidence comes from PCTR's own execution receipts, scored on the scale in protocol/scoring-semantics.md §3.2 (a clean execution 0.95; reaching for authority it lacks 0.15; replaying an authority 0.20; waiting on a human approval 0.75 — an agent blocked on a human is not an agent misbehaving), plus attestors configured per agent. Attestations go through TTP's own verify_attestation, so stale or wrong-subject ones contribute nothing, and a failing attestor yields no evidence rather than favourable evidence.

The normative algorithm had no implementation

protocol/aggregation-spec.md is marked normative and ships nine test vectors. Nothing in this repository implemented it until now. All nine run in tests/aggregation.test.mjs.

Three do not agree with the algorithm the document itself defines. They are asserted as known divergences rather than skipped or worked around:

Vector Expects Written formula yields Why
agg-003 0.4 0.5 Superseded by agg-003-corrected (identical receipts, expects 0.5). Its own _explanation field works through the arithmetic, catches itself mid-sentence — "wait let me recalculate" — and concludes 0.5, while expected still says 0.4.
agg-006 0.5 0.5799 Expecting 0.5 requires both issuers capped at 0.40. B's uncapped fraction is 0.29, and step 5 says min(fraction, max_issuer_weight) — a cap, not a floor.
agg-008 0.917 0.918 Off by 0.0010, just outside the vectors' own ±0.001 tolerance.

One of these is not a typo

Behind agg-006 is a design question the spec should answer, not the code. Step 5 caps a dominant issuer at 0.40 and then re-normalizes — which hands most of the capped weight straight back when the other issuers are light. 50 perfect receipts from one issuer against two bad ones from two others still yields ~0.90, with the "capped" issuer holding 87% of the weight. The cap only bites when the rest of the field is comparable.

That behaviour is pinned by a test so it can't be mistaken for an implementation bug here, but agg-006 documents an intent the algorithm doesn't deliver, and that's worth deciding deliberately.

I did not change the spec or the vectors — that changes conformance for anyone implementing against them.

Verification

192 tests (25 new), Python 19, AGT parity green at 52 checks.

🤖 Generated with Claude Code

Everything downstream of a trust score was rigorous about it — thresholds,
decay, route admissibility, execution authority — while the score itself
was a number someone typed into pctr.json. This closes that.

Implements protocol/aggregation-spec.md, which is marked normative and
ships nine test vectors, and which nothing in this repository implemented
until now. All nine vectors run in tests. Three do not agree with the
algorithm the document defines; they are asserted as known divergences
rather than skipped or worked around:

- agg-003 expects 0.4 where the formula yields 0.5. It is superseded by
  agg-003-corrected, which has identical receipts and expects 0.5, and
  its own _explanation field works the arithmetic, catches itself
  mid-sentence ("wait let me recalculate") and concludes 0.5.
- agg-006 expects 0.5, which requires both issuers capped at 0.40. B's
  uncapped fraction is 0.29, and step 5 specifies min(fraction, cap) — a
  cap, not a floor. The formula yields 0.5799.
- agg-008 is off by 0.0010, just outside the vectors' own tolerance.

Behind agg-006 is something the spec should decide rather than the code:
capping a dominant issuer at 0.40 and then re-normalizing hands most of
the capped weight back when other issuers are light, so 50 perfect
receipts from one issuer still outvote two bad ones from two others. A
test pins that behaviour so it cannot be mistaken for a bug here.

pctr attest measures each agent from PCTR's own execution receipts,
scored on the scale in protocol/scoring-semantics.md 3.2, plus any
attestors configured per agent. Attestations are verified with TTP's own
verify_attestation, so a stale one or one about another subject
contributes nothing, and a failing attestor yields no evidence rather
than favourable evidence. Output is a TTP TrustThresholdProof naming what
it rests on.

The rule the whole thing turns on: absent evidence is not trust. An
unmeasured agent does not inherit its declared score — it is UNPROVEN,
counts as zero, and a protected consequence will not route through it.
Run against the working example, it found both agents overstated:
declared 0.98 and 0.97, evidence supports 0.8251.

192 tests (25 new).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@blocksifrdev
blocksifrdev merged commit 65ad011 into main Sep 15, 2026
4 checks passed
@blocksifrdev
blocksifrdev deleted the feat/measured-trust branch September 15, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant