Skip to content

spec(references)!: entries validated by recomputation, and position-commitment registered (#321) - #595

Open
imran-siddique wants to merge 2 commits into
mainfrom
spec/references-recompute-validation
Open

imran-siddique wants to merge 2 commits into
mainfrom
spec/references-recompute-validation

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Closes the two asks in #321.

Section 3.1.2 treats every references entry as a fact a party holds and a verifier fetches, so resolver is required and rule 4 makes a producer with no obliged party omit the entry. Michael Argento's position-commitment profile hits the case where that is wrong: id is a digest the verifier recomputes from a proof the holder carries, and nothing is held anywhere.

The field (v0.3 draft only). validation: "recompute" on an entry, in schema/trace-claim-v0.3-draft.json. For such an entry:

  1. id is validated by recomputation, and the proof must commit this record's digest, the §3.1.3 preimage. A proof is public once presented, so a check that stops at "the commitment is real" passes any record that carries it. This is the 09-10 correction.
  2. resolver and retention MUST be absent. Nobody has undertaken to retain anything.
  3. "Cannot recompute" means the entry is absent, not a finding. "Recomputed and disagreed" is a contradiction and is never reported as an absence, the same split docs/rfcs/a2a-delegation-profile.md §4.3 makes for delegation links.

scripts/gen_v03_draft_schema.py takes the new member, the entry's required list and its resolver condition from the draft. Everything else still comes from canonical, so a relation registered later reaches the draft without a second edit.

The relation. position-commitment is registered with the 09-10 wording in §3.1.2, the rel description of schema/trace-claim.json and its two packaged copies, docs/schema.md, and a new section in docs/references-registry.md. Under v0.2 it earns nothing a pointer does not, and resolver is still required.

Compatibility. Every existing record still validates against both schemas, and no v0.2 rule changes. A record that uses validation is rejected by the v0.2 schema and by TrustRecord, so this is a breaking change under GOVERNANCE.md. The 30-day comment period starts when this PR opens and closes no earlier than 30 days after that. It also needs Project Lead sign-off.

Not in this PR. The reference library does not read references[] (sign.py), so no verifier path is added; the two outcomes in rule 3 are conformance-suite rules, like rules 3 and 4 today. Step 3 of the registry's "Adding a name" asks for an example set with recomputable digests, and none is committed. Michael's package ships the vectors, so they can come from there.

Testing

  • New tests/test_references_recompute_v03_draft.py: 17 cases. The draft accepts a recompute entry with no resolver and rejects one that names a resolver or retention, an unknown or empty validation, and a plain entry with no resolver. v0.2 rejects every shape the draft newly accepts, including validation beside a resolver.
  • tests/test_references_block.py gains the relation and two v0.2 rejection cases, and tests/test_v03_draft_schema.py strips the new draft members.
  • Mutation check: removing each rule (resolver allowed, retention allowed, the resolver requirement when validation is absent, the enum, validation accepted by canonical, validation accepted by the packaged copy, the generator's allOf copy) makes at least one test fail.
  • Full suite on Windows, Python 3.12: 4298 passed, 62 skipped. The one failure (test_review_bar.py, worktree cleanup) and 232 errors (test_independent_verifier.py, WinError 206 path too long) are Windows path limits and also occur on main.

Requested on #321 by @imran-siddique; construction and the binding correction from Michael Argento (Argento Computing Inc.), named with permission.

🤖 Generated with Claude Code

…ommitment registered (#321)

Section 3.1.2 treats every reference as a fact a party holds and a verifier
fetches, so resolver is required and rule 4 makes a producer with no obliged
party omit the entry. When id is a digest the verifier recomputes from a proof
the holder carries, nothing is fetched or retained.

The v0.3 draft schema adds an optional validation member whose only value is
recompute. On such an entry the recomputation must bind the entry to this
record (the 3.1.3 digest), resolver and retention must be absent, and "cannot
recompute" (absent, not a finding) stays separate from "recomputed and
disagreed" (a contradiction). The generator takes the member, the entry's
required list and its resolver condition from the draft; everything else stays
canonical.

position-commitment is registered in 3.1.2, the rel description and its
packaged copies, docs/schema.md and docs/references-registry.md. Under v0.2 it
earns nothing a pointer does not.

BREAKING CHANGE: existing records keep validating, but a record that uses
validation is rejected by the v0.2 schema and TrustRecord. Version gate for
v0.3, 30-day comment period under GOVERNANCE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Imran Siddique <45405841+imran-siddique@users.noreply.github.com>
@imran-siddique
imran-siddique requested review from a team, lywinged and rajnisht7 as code owners October 20, 2026 04:41

@dinakarjs dinakarjs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head eb40a81. The v0.2/v0.3 shape boundary is implemented coherently: 174 reference/draft-schema tests pass locally, and separate in-memory removals of resolver refusal, retention refusal, ordinary-entry resolver requiredness and the validation enum each produce behavioral assertion failures. Exact-head technical workflows pass.

Two completion gaps remain: the newly registered relation has no committed recomputable example set despite this registry's Adding a name step 3, and the generated draft's rel description still says a resolver is required although recompute entries forbid one. See inline comments.

For the example, please make independently supplied sequence/committer trust explicit and distinguish a matching record, another record carrying the same proof, missing proof and altered proof. A self-supplied verification key plus digest agreement establishes consistency, not independently established ordering. Also qualify “needs no retention at all”: there is no resolver retention undertaking, but later recomputation still needs the holder's proof bytes.

This is a review of draft semantics/schema, not a demonstrated deployed verifier vulnerability. No resolver or proof-verifier implementation is demanded beyond the declared scope; the relation can instead remain pending until its required example is available. Comment-period, sponsor/maintainer disposition and Project Lead gates remain separate. References: §3.1.2/§3.1.3 and docs/references-registry.md at this head; RFC 8785 defines the named preimage. Reused Linux/Python 3.12 environment; no full-suite or hardware rerun. Reviewed with Codex.

Comment thread docs/references-registry.md Outdated
| `condition-appraisal` | An independent check's finding on whether a stated condition is established by a stated subject | This document, below | [`examples/condition-appraisal/`](../examples/condition-appraisal/) |
| `observed-effect` | A signed record of the state change an observer outside the agent saw over one interval | This document, below | [`examples/observed-effect/`](../examples/observed-effect/) |
| `applicability-assessment` | An assessor's statement of whether a stated legal provision applies to a stated party, held in another system | This document, below | [`examples/applicability-assessment/`](../examples/applicability-assessment/) |
| `position-commitment` | A commitment to a position in an externally ordered sequence, taken before the digest of this record exists | This document, below | none committed |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Complete the registration's executable example requirement. Adding a name step 3 below calls for committed objects, recomputable digests/outcomes and unavailable/altered cases registered with test_adequacy_all_sets.py. This row explicitly has none, and the PR's new tests only validate entry shape: they cannot establish the record-binding correction or the unavailable/contradictory distinction. Please bring a bounded example set into this PR, or defer registration until it exists; an external package alone does not exercise the repository's stated acceptance contract.

Comment thread schema/trace-claim-v0.3-draft.json Outdated
"type": "string",
"minLength": 1,
"description": "Relationship type. The registered values are a registry that grows, so this is not a closed set. authorized-intent: an authorization decided before execution, held in another system. approval-outcome: an attributable human approval attached to a step-up or defer decision. behavior-trace: a behavioural record of what the agent did, of which this record is the environment evidence. condition-appraisal: an independent check's finding on whether a stated condition is established by a stated subject, bound to both by digest. observed-effect: a signed record of the state change an observer outside the agent saw over one interval, with the state before and after, the paths observed and the authority change ran under. applicability-assessment: an assessor's statement of whether a stated legal provision applies to a stated party, held in another system."
"description": "Relationship type. The registered values are a registry that grows, so this is not a closed set. authorized-intent: an authorization decided before execution, held in another system. approval-outcome: an attributable human approval attached to a step-up or defer decision. behavior-trace: a behavioural record of what the agent did, of which this record is the environment evidence. condition-appraisal: an independent check's finding on whether a stated condition is established by a stated subject, bound to both by digest. observed-effect: a signed record of the state change an observer outside the agent saw over one interval, with the state before and after, the paths observed and the authority change ran under. applicability-assessment: an assessor's statement of whether a stated legal provision applies to a stated party, held in another system. position-commitment: a commitment to a position in an externally ordered sequence, taken before the digest of this record exists; id is the commitment, validated by recomputation from a proof the holder carries and that commits this record, rather than by resolution. Under this schema the entry still names a resolver, so a producer that retains nothing omits it."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This generated v0.3 description says “Under this schema the entry still names a resolver”, but the new allOf correctly forbids resolver when validation=recompute. The positive no-resolver test is a concrete counterexample to that sentence. Please qualify the shared canonical description as “Under v0.2 …” (or equivalent) and regenerate the draft so prose and accepted instances agree.

…ify the v0.2 resolver sentence (#321)

Registration step 3 asks for a committed, recomputable example set.
examples/position-commitment/ holds five v0.3 draft records with
validation: recompute: a proof that establishes the position, the same
proof on another record, no proof held, a proof altered to an earlier
position, and a proof whose only committer key is the one it carries.
Committer keys and the sequence come from relying-party.json, never from
a proof: agreement under a self-supplied key is consistency, not
ordering. tests/test_position_commitment_fixtures.py recomputes every
verdict and re-runs the generator; the set is named in
test_adequacy_all_sets.py.

The shared rel description now says "Under v0.2" for the resolver
sentence, and the draft is regenerated. Section 3.1.2 and the registry
say a recompute entry has no retention undertaking but still needs the
holder's proof bytes to be recomputed later.

Signed-off-by: Imran Siddique <45405841+imran-siddique@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@imran-siddique
imran-siddique force-pushed the spec/references-recompute-validation branch from 4ee3bd5 to 0382dfe Compare October 20, 2026 14:52
@imran-siddique

Copy link
Copy Markdown
Member Author

Both are in 0382dfe.

examples/position-commitment/ has five v0.3 draft records: established, the same proof on another record, no proof held, a proof backdated to position 8700, and a proof carrying its own committer key. Keys and the sequence come only from relying-party.json, so case 05 stays consistent and unestablished. tests/test_position_commitment_fixtures.py recomputes every verdict (25 tests), and the set is named in test_adequacy_all_sets.py.

The rel description now says "Under v0.2", the draft is regenerated, and §3.1.2 says the holder keeps the proof bytes.

@dinakarjs dinakarjs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed 0382dfe. Both completion gaps in my previous review are addressed: the registered relation now has a committed, recomputable five-case example set, and the shared description qualifies the resolver statement as v0.2. Holder retention of proof bytes is explicit.

Independent exact-checkout validation on Linux/Python 3.12.14 with hash-locked development dependencies: 231 selected tests pass (position fixtures, recompute shape, references, v0.3 draft and adequacy inventory). Generator output reproduces the committed bytes. Three isolated in-memory removals of record binding, independent-committer gating and sequence comparison each fail their corresponding assertion; restored controls pass. An initial generator run lacked the checkout on subprocess PYTHONPATH; the final run explicitly supplies it. No production source edits.

Approval is bounded to this draft schema/example correction. The example trusts separately supplied committer keys and sequence; it does not independently authenticate a live sequence, establish hardware execution, or implement a deployed general-purpose proof verifier. Normative comment-period and Project Lead requirements remain separate. References: this head's sections 3.1.2/3.1.3, references registry and example README. Reviewed with Codex.

@lywinged lywinged left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REVIEW on #595, head 0382dfe: Comment

Measured on 0382dfe against its merge base 53e347c, with the hash-pinned requirements, on 3.11 and on 3.12: ruff, mypy and the dash check clean, 4572 passed and 48 skipped, the conformance tests 874 passed, 2 skipped and 5 xpassed, and gen_v03_draft_schema.py --check clean. 17 and 25 are the counts collected, and TrustRecord refuses the member. No objection to the substance. Two questions and a few sentences stand between this and an approval, and the period runs either way.

  1. Where the draft rules live. Nothing in the v0.2 text names a v0.3 draft member before this change: spec/trace-v0.2.md at main carries no row and no rule for runtime.evidence or manifest, whose homes are the draft schema, docs/rfcs/runtime-evidence-profile.md and the manifest-ref vector set. Here the §3.1.2 field table gains a row the v0.2 schema rejects, and the section gains six MUST and one MUST NOT that bind nothing until v0.3 is adopted, under one marker. The row and the registration carry the other two, and the rewritten Unsettled paragraph none. It reads fine as written. My preference is the pattern the other two members set, an RFC beside them with §3.1.2 carrying the registration and a pointer, so that the v0.2 text carries no MUST that does not bind. The decision is yours, and if it stays here nothing else needs to change.

  2. What a verifier reports at the record level on the third outcome. Rule 3 says a verifier MUST NOT reject a record because an entry cannot be resolved, and the registry says of each of the three relations it defines that its findings are about the object and none reaches the record. The new text says a recomputation that disagrees "is a contradiction in the record" and MUST be reported as one. The delegation profile it cites fails the appraisal on a link that contradicts, while the example set's verdicts are per entry and every record's signature still verifies. One sentence saying whether a disagreeing recomputation fails the record under §3.3 or stays a finding the relying party weighs would settle what an implementer does with it.

  3. The class and the compatibility statement. GOVERNANCE treats a wire format change as breaking regardless of the backward-compatibility argument, which is the clause a new member on the wire falls under. The description's reason, that v0.2 rejects it, is forward incompatibility, which GOVERNANCE does not list. v0.2 is a Draft, so the last-resort conditions do not apply, and an approval here would not be a finding that one is met. CONTRIBUTING step 6 asks the statement to name what breaks and what implementers must do; the Compatibility paragraph has the first and not the second. And the sentence itself: "every existing record keeps validating against both schemas" in the CHANGELOG, "still validates against both schemas" in the description. The draft pins eat_profile to the v0.3 tag, so none of the 25 example records valid under v0.2 validates against it as published. Every one does once re-profiled, and the draft narrows nothing v0.2 accepts, which is what test_a_resolved_entry_is_accepted_by_both states. The CHANGELOG entries of those three relations carry "Informative: no requirement, schema shape or wire format changes", as registry step 4 asks. This entry bundles the registration with the field, so a clause saying which half is which would keep that.

  4. The description, which the period is read against. It still says "none is committed. Michael's package ships the vectors". The set, its 25 tests and relying-party.json are in your comment and not in the text. And the sponsor: dinakarjs noted the disposition is separate, and CONTRIBUTING step 3 has the reviewer identify one or convert, so: Argento Computing, which implements it, if willing to be named as accountable, or the Project Lead carrying it with Michael Argento credited as the proposer. The description should say which.

  5. The clock. You run the 30 days from the opening, so the earliest decision is 2026-11-19. CONTRIBUTING's steps put the period after the issue, and 30 days from #321's opening ran out on 2026-10-10. Which applies is yours to say. An approval shortens neither, dinakarjs's changes-requested stands until he re-reviews, and since the sign-off is your own, the Maintainer review is the independent one.

  6. Confirmed. Both P2s are closed on this head, and so are the four things the body of that review asked for: independently supplied sequence and committer trust (relying-party.json), the four distinguished cases, the self-supplied key as consistency (case 05), and the retention qualification (the Unsettled paragraph). The set is named in test_adequacy_all_sets.py under MEASURED_ELSEWHERE and reproduced by its generator. The shared rel description says "Under v0.2" and the regenerated draft the opposite for the draft. The registration answers no to both questions that separate a vocabulary entry from a rule change under v0.2, it concludes nothing beyond rule 3 and needs no field §3.1.2 lacks, and the field is where the rules are, which is how #321 and the description divide them. On the whole suite, each of the seven mutations the description lists fails at least one test: a resolver allowed on a recompute entry, a retention allowed, and the resolver requirement dropped when validation is absent fail one each, the enum removed fails two, the canonical schema accepting the member fails seventeen, the packaged copy accepting it fails six, and the generator's allOf copy removed fails --check and the draft-strip test. The narrower schema a reviewer would have written, resolver left in the entry's required beside the allOf, fails ten, the four acceptances and six in the fixture tests. Against the merge base the review bar sorts 25 of the changed tests as failing on the missing set and 7 on behaviour, the draft's four new acceptances, the registered-value check, the adequacy register's new entry and the draft-strip test. The other thirteen recompute cases and the three new cases in test_references_block.py pass there, because v0.2 already rejects an unknown member, so the four acceptances are the assertions only this change makes true. As the description says, no verifier here reads references[]: sign.py says so, the conformance suite has no module for the block, and trust-record-verify does not mention it. For an entry validated by recomputation, cases 02 to 04 of the set are the only executable statement of absent against contradiction, and that is the right place for it until a verifier takes the block up.

Comment for now. With the description brought to 0382dfe, the compatibility statement completed and the sponsor named, I will approve, and the period runs regardless.

Copy link
Copy Markdown
Contributor

Follow-through on lywinged's review: I prepared a narrow compatibility-wording companion at dinakarjs#3, commit 207ebe4, based on 0382dfe. It corrects the changelog's claim that unchanged records validate against both schemas, names the draft profile/signing/consumer migration, and separates informative registration from the breaking wire member. The test docstring now matches its profile-specific fixtures. 42 recompute/position-fixture tests pass; Ruff/whitespace pass; published file hashes match the local files.

The remaining items are author/Project Lead decisions: (1) whether contradictory recomputation changes the record verdict or is a relying-party finding; (2) RFC placement versus retaining draft text here; (3) accountable sponsor; (4) the authoritative comment-period start. Please also replace the PR body's stale “none is committed” statement with the five-case examples/position-commitment set, its 25 fixture tests and relying-party.json.

My previous approval was bounded to the schema/example correction and does not settle these new contract/process questions. Once the author has incorporated or otherwise disposed of the wording and answered those questions, the final head needs re-review. This companion is not independent approval of changes I supplied.

@mikeargento

Copy link
Copy Markdown

Michael Argento here, for Argento Computing Inc., which operates BitGraph.

On the sponsor question: Argento Computing is not taking on the implement-and-maintain undertaking for validation: "recompute"; the Project Lead carrying it, with the construction and the record-binding correction credited to me as proposer in the description and CHANGELOG as they are now, is the right disposition.

One sentence in the registry would keep an attested committer out of case 05. "A committer key … it holds independently of the proof" assumes a key the relying party has in advance. An enclave committer's key is generated inside the enclave each epoch and is never pre-known; what the relying party holds independently is the attestation root and the enclave's published measurement, and a verified attestation bound to the signed record establishes the key. Something like "a committer key it holds independently of the proof, or establishes from a verified attestation under a root and measurement it holds independently, together with its own copy of the sequence" covers both. Case 05 stays as it is.

A live instance, native BitGraph, no TRACE record: BitGraph #6 is a request whose nonce is the only input to a deterministic drawing; the picture is bound as BitGraph #10. Each has a Base block bound right before it and its proof hash written to Base after it, so the picture could not exist before block 52,920,085 and existed by 52,920,088. npx @mikeargento/bitgraph-sdk verify <the BitGraphed PNG> checks the bytes, the attestation against the AWS root and the published measurement, the signature and counts, and the Base write, offline.

This branch has not been deployed

No deployments
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.

4 participants