Skip to content

receipt-core: CoreDecisionOutputV1 semantic core (valid_until binding, normalize-before-hash)#4

Merged
aeoess merged 3 commits into
mainfrom
fix/core-decision-output-valid-until
Jul 24, 2026
Merged

receipt-core: CoreDecisionOutputV1 semantic core (valid_until binding, normalize-before-hash)#4
aeoess merged 3 commits into
mainfrom
fix/core-decision-output-valid-until

Conversation

@aeoess

@aeoess aeoess commented Jul 24, 2026

Copy link
Copy Markdown
Owner

What this changes

The core decision output becomes a closed five-member object and its digest path is enforced.

  1. fix(receipt-core): bind valid_until in CoreDecisionOutputV1. The output object is exactly profile, verdict, effective_authority_ref, constraints, valid_until. valid_until is required: an RFC 3339 UTC millisecond timestamp for permit and narrow, exactly null for deny. Draft -03 specifies the member; the prior code accepted outputs without it.

  2. fix(receipt-core): enforce normalize-before-hash in decision_ref construction. The builder normalizes the decision output at runtime before hashing, so an unnormalized or malformed output cannot reach the digest through the construction path. computeDecisionComponentRefV1 is documented as the internal primitive that expects already normalized input.

Python and Go additionally carry feat(receipt-core): port receipt-core v1 module as the first commit: receipt-core previously existed only in the TypeScript repository, and the fixes land on top of the port. In Go the builder takes the typed CoreDecisionOutputV1 struct, which turns the old opaque-value bypass into a compile error.

Cross-language evidence

Five decision outputs through the builder in TypeScript, Python, and Go: 5/5 component digests match and 5/5 full decision_ref values match, including an NFC-decomposed constraint and a supplementary-plane (U+1F600) constraint. Five malformed outputs rejected in all three; the unknown-extra-member case is unrepresentable in Go and fails at compile time. One known-answer test per language pinned the previous digest shape and is repinned here: the pinned decision_ref moves from 2157809a… to e474f27b…, identical across the three languages, and the new component digest reproduces from stock JSON and SHA-256 libraries with no repository code.

Suites at these heads: TypeScript 4351 passed, 0 failed; Python 762 passed; Go all packages ok. All exit 0.

Boundary, stated plainly

This PR does not claim conformance. Three gates remain open and are tracked as follow-up work: the receipt-level relation that valid_until must be later than the receipt's issued_at (the receipt carries decision_ref as a digest, so neither validator currently sees both operands; the enforcement point is a design decision in progress), dispatch-level enforcement, and strict raw duplicate-member parsing. receipt-core remains unexported from the public API in all three repositories. Merged means the construction path is specified and tested, not that the semantic core is fully conformant.

Sibling PRs: aeoess/agent-passport-system#84, aeoess/agent-passport-go#5

aeoess added 3 commits July 24, 2026 09:35
Ports the receipt-core v1 module (strict JCS, decision_ref construction,
ReceiptV1, supporting records) with its test file. In-repo and tested, not
part of the public API: nothing is exported from agent_passport/__init__.py.

Signed-off-by: aeoess <signal@aeoess.com>
draft-pidlisnyi-aps-03 defines valid_until as the fifth member of CoreDecisionOutputV1. The implementations omitted it, so decision_ref did not bind the decision expiry. This adds the field to the type, the closed shape, verdict-dependent shape validation, and the hashed preimage.

The cross-field rule requiring valid_until to be later than receipt issued_at, dispatch-time expiry and consumption enforcement, and strict duplicate-member parsing remain separate follow-up gates.

The module is not exposed through the supported package entry points. Internal deep-import users, if any, may observe the changed digest.

Signed-off-by: aeoess <signal@aeoess.com>
…truction

Signed-off-by: aeoess <signal@aeoess.com>
@aeoess
aeoess merged commit d901fde into main Jul 24, 2026
1 check passed
@aeoess
aeoess deleted the fix/core-decision-output-valid-until branch July 24, 2026 16:51
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