DAN-860: unlock's Seal identity moves to the fulfillment — main goes green - #5
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…green main has been red since 2026-07-21. Python and TypeScript implemented disjoint requirements field sets for the same scheme; the intersection was empty. A Seal threshold key-server model replaced the encryptionId model on 2026-07-20 and reached the conformance vectors and the TS runtime, but not Python or the written spec. Python was faithfully implementing a stale spec. Seal is canonical. The settling test: can the server know this value at the moment it must send it? Requirements ship in the 402, before payment exists. packageId/keyServers/threshold/contentDigest are properties of how the seller already encrypted, so the server knows them. The Seal identity is receiptId||nonce, and that receipt is minted by a transaction the BUYER constructs in response to the 402 — so encryptionId in requirements was not stale, it was unsatisfiable by the party required to populate it. - python/src/s402/http.py: unlock requirements and payload key sets, and validate_unlock_shape, brought to the vectors. Payload encryptionId check deleted. - docs/specification.md: 4.9, the unlock payload table, and both known-key lists. The payload table also stopped describing a two-stage TX1/TX2 flow; unlock has been single-transaction pay_and_mint since 2026-07-20. - docs/adr/012: records the decision and the reusable test. NOT changed, deliberately: the conformance vectors and the TS runtime were already correct. TypeScript types were too — the ticket cited types.ts:436 as a required requirements field, but that line is inside s402UnlockFulfillment, where encryptionId belongs. Three of five surfaces needed nothing. No CI job is added. DAN-860's criterion 4 calls typescript/test/conformance/vectors/ a stale duplicate; it is gitignored build output that scripts/prepare-publish.sh copies from spec/vectors/ at publish time, failing closed if the source is absent. It cannot drift and is correctly missing from a clean checkout. A drift check on it is not just unnecessary, it is red on every clean checkout — which is how a correct guard gets switched off (DAN-719). The real uncovered gap is that spec/vectors/ is generated from live code by generate-vectors.ts and nothing asserts the tracked vectors still match what the code produces. Not built here: tsx is not an installed dependency, so it could not be watched green, and an unverified gate is worse than none. Recorded in ADR-012. Verified: python 154 passed (was 2 failed/152 passed) · typescript 1102 passed, unchanged · ci.yml byte-identical to main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Danny-Devs
force-pushed
the
agent-a/DAN-860-unlock-seal-canonical
branch
from
August 16, 2026 07:07
2793045 to
deeec71
Compare
… deleted Records the check I added and removed inside DAN-860. spec/vectors/ and typescript/test/conformance/vectors/ are byte-identical, and I read that as two hand-maintained copies needing a drift guard. The second is gitignored build output that prepare-publish.sh copies from the first at publish time, so the guard was red on every clean checkout — the shape DAN-719 already named as how a correct guard gets switched off. Identical contents tell you what is true, never why. Read .gitignore and the packaging manifest before concluding two directories are peers, and run any new gate against a fresh clone rather than a working tree full of build output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the 3.5-week red
main. 154 passed in Python (was 2 failed / 152 passed); TypeScript 1102 passed, unchanged.What was actually wrong
Not "a test disagrees with a decoder." Python and TypeScript implemented disjoint requirements field sets for the same scheme — the intersection was empty.
A Seal threshold key-server model replaced the
encryptionIdmodel on 2026-07-20. It reached the conformance vectors and the TS runtime; it did not reach Python ordocs/specification.md. Python was faithfully implementing a stale spec, which is why making CI green by editing a vector would have made the spec bug permanent.The ruling, and the test that settles it
Seal is canonical.
encryptionIdbelongs in neither requirements nor payload — it travels in the fulfillment (s402UnlockFulfillment, which already existed).Requirements ship in the
402, before payment exists.packageId/keyServers/threshold/contentDigestare properties of how the seller already encrypted — knowable. The Seal identity isreceiptId ‖ nonce, and that receipt is minted by a transaction the buyer constructs in response to that 402.So
encryptionIdin requirements was never merely stale — it was unsatisfiable by the party required to populate it. Recorded as ADR-012.seal_policy.move's escrow flow theescrow_idis deterministic from signed TX1 bytes.unlockwas rebuilt as a single-transaction scheme with no escrow at all; its anchor is the receipt's own object ID.REVIEW-NOTES.mdcontains both statements — §3 item C and §6b — and §6b is the later and correct one.Changed — three files
python/src/s402/http.pyvalidate_unlock_shapebrought to the vectors; payloadencryptionIdcheck deleteddocs/specification.mddocs/adr/012-…mdNot touched, deliberately: the conformance vectors and the TS runtime. No vector was edited.
.github/workflows/ci.ymlis byte-identical tomain.Two of the ticket's premises did not survive checking
1 ·
types.tsneeded no change. The ticket citestypescript/src/types.ts:436as a required requirements field. That line is insides402UnlockFulfillment— whereencryptionIdcorrectly belongs.s402UnlockExtraands402UnlockPayloadwere already right. Three of five surfaces needed nothing; the real scope was two.2 · CI was never the gap. Criterion 5 says "Nothing was merged, so nothing ran." CI runs on
push: branches: [main]and it did run — run29811549631,completed failure, 44s, on the exact commit, 2026-07-21T07:44Z. It reported red on time and nobody looked. Adding a scheduled run would produce a second unwatched red. The encoding is notification or a required check — left for @Danny-Devs, since it is a repo setting rather than code.Criterion 4, and a correction to my own first pass
Criterion 4 asks to delete
typescript/test/conformance/vectors/as "an unread duplicate ... a trap with no upside."It is not stale, not a duplicate, and not hand-maintained. It is gitignored build output:
typescript/scripts/prepare-publish.shcopiesspec/vectors/*.jsoninto it duringprepublishOnly, and hard-fails if the source is missing. It is inpackage.json'sfilesarray so npm consumers receive the contract. It cannot drift —cphas no opinions — and it is correctly absent from a clean checkout. Deleting it would strip the vectors from the published package.An earlier revision of this PR added a drift check on it. That was wrong and has been removed. I had read the two directories as copies kept in sync by discipline. They are not; one is generated from the other at publish time. Worse, the check failed on the CI runner with
published dir missing— because a clean checkout correctly does not have it — which would have made CI red on every commit. That is precisely how a correct guard gets switched off (DAN-719), and I built the thing I had flagged as the hazard.The exit-2 "cannot check" path is what surfaced this, so the failure was loud rather than silent — but the right outcome is no check here at all.
The real uncovered gap, recorded in ADR-012 rather than patched:
spec/vectors/is itself generated, bytest/conformance/generate-vecters.tsrunning the liveencode/decodefunctions. Nothing asserts the tracked vectors still match what the current code produces. That check is worth building; it is not built here becausetsxis not an installed dependency, so it could not be watched green — and an unverified gate is worse than an absent one.Verify:
cd python && uv run --extra dev pytest -v→ 154 passed on this branch.🤖 Generated with Claude Code