feat(spec): add the optional origin block, and stop software-only meaning two things - #135
Merged
Conversation
…ning two things
runtime.platform: 'software-only' is the honest value in two situations that are
not the same claim. A dev-mode record is unattested because nothing attested it.
A record transcribed from a third-party control plane is unattested because the
party asserting the evidence also wrote the log. A consumer weighing a record
could not tell those apart from platform alone, and adapters over other vendors'
governance output are about to make that gap load-bearing.
origin is {kind, producer, source_event_id, ingested_at}, optional and additive,
under the same v0.2 profile URI. Same shape of change as delegation in 0.4.0:
existing records stay valid and absence means self. kind is a closed set rather
than free text, because the whole value of the field is that a verifier can key
on it.
The rule that makes it worth having: a record whose kind is not self MUST carry
runtime.platform 'software-only'. An importer holding someone else's log has no
quote to present, so a hardware platform there is untrue rather than stronger,
and it is exactly what an adapter produces by starting from a hardware example
and editing the fields it understood. Enforced in the model and in the JSON
schema with if/then, so a validator that never loads the Python rejects it too,
and tested from both directions in both places.
The block launders assurance in neither direction. Naming a producer does not
make unattested evidence attested, and a verified hardware record is what it is
whether or not it says origin: self.
Also fixed: docs/schema.md still described transparency as required with an
empty string for unanchored records. 0.5.1 made it optional below Level 2 and
rejects the empty string.
202 passed, ruff and mypy clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
lywinged
added a commit
to lywinged/trace-spec
that referenced
this pull request
Aug 10, 2026
Upstream merged agentrust-io#148, the last large piece this fork was carrying, which the maintainer rebased and opened himself under this fork's authorship. With agentrust-io#122, agentrust-io#125, agentrust-io#126, agentrust-io#136 and agentrust-io#137 already merged, most of the thirty-three commits here described work that now lives upstream with better provenance than this fork can give it: a PR number and a maintainer's review. `git rebase upstream/main` was tried first and abandoned. It stopped on the oldest commit in the set, a schema-and-version alignment whose content upstream has since taken, superseded and released three times over. Replaying thirty-three commits against fifteen of upstream's resolves early commits into shapes that no longer mean anything. So: main reset to upstream/main, fork-only material re-applied. The old history is tagged `archive/pre-576507b` and pushed rather than discarded. What is held here, and why it is held: - agentrust-io#117 gap disclosure: design note, two normative drafts, 18 vectors, generator - agentrust-io#116 verifier compatibility: 8 vectors, generator, normative draft - the normative crosswalk, mapping every RFC 2119 statement to whom it binds - `docs/conformance-method.md`, `coverage-report/` (historical), DECISIONS.md - the independent signature path and the package-consistency test Each is an unaccepted proposal or a method write-up, not unfinished work. Four files needed a real merge, and not in the same direction. `models.py` and `__init__.py` are upstream's plus this fork's two profile constants, because upstream had moved on with `origin` (agentrust-io#135) and the `declared` enforcement mode (agentrust-io#143) and a wholesale copy would have dropped both. `sign.py` and `test_sign.py` are this fork's `accepted_profiles` version, which supersedes the minimal cutover check upstream took from agentrust-io#125 -- a supersession CLAUDE.md predicted when agentrust-io#125 was offered. All four merged cleanly three-way against a817621, the last commit the two histories agree on. The crosswalk guard earned its place in the same run: upstream added two normative statements this fork had never seen, and `test_normative_crosswalk.py` failed until both had rows. A source-derived inventory noticing its subject moved is the property that document exists to have. 432 passed, 1 skipped. ruff and mypy clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.com>
lywinged
added a commit
to lywinged/trace-spec
that referenced
this pull request
Aug 24, 2026
Upstream merged agentrust-io#148, the last large piece this fork was carrying, which the maintainer rebased and opened himself under this fork's authorship. With agentrust-io#122, described work that now lives upstream with better provenance than this fork can give it: a PR number and a maintainer's review. `git rebase upstream/main` was tried first and abandoned. It stopped on the oldest commit in the set, a schema-and-version alignment whose content upstream has since taken, superseded and released three times over. Replaying thirty-three commits against fifteen of upstream's resolves early commits into shapes that no longer mean anything. So: main reset to upstream/main, fork-only material re-applied. The old history is tagged `archive/pre-576507b` and pushed rather than discarded. What is held here, and why it is held: - agentrust-io#117 gap disclosure: design note, two normative drafts, 18 vectors, generator - agentrust-io#116 verifier compatibility: 8 vectors, generator, normative draft - the normative crosswalk, mapping every RFC 2119 statement to whom it binds - `docs/conformance-method.md`, `coverage-report/` (historical), DECISIONS.md - the independent signature path and the package-consistency test Each is an unaccepted proposal or a method write-up, not unfinished work. Four files needed a real merge, and not in the same direction. `models.py` and `__init__.py` are upstream's plus this fork's two profile constants, because upstream had moved on with `origin` (agentrust-io#135) and the `declared` enforcement mode (agentrust-io#143) and a wholesale copy would have dropped both. `sign.py` and `test_sign.py` are this fork's `accepted_profiles` version, which supersedes the minimal cutover check upstream took from agentrust-io#125 -- a supersession CLAUDE.md predicted when agentrust-io#125 was offered. All four merged cleanly three-way against a817621, the last commit the two histories agree on. The crosswalk guard earned its place in the same run: upstream added two normative statements this fork had never seen, and `test_normative_crosswalk.py` failed until both had rows. A source-derived inventory noticing its subject moved is the property that document exists to have. 432 passed, 1 skipped. ruff and mypy clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: lywinged <48041247+lywinged@users.noreply.github.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.
Prerequisite for the third-party control-plane adapters (response plan list 2, item 3). Ships as 0.7.0.
The problem
runtime.platform: "software-only"is the honest value in two situations that are not the same claim:A consumer weighing a record cannot tell those apart from
platformalone. That gap is about to become load-bearing: an adapter that emits a record indistinguishable from a dev-mode one destroys the argument the adapters exist to make.The block
Optional and additive, under the same v0.2 profile URI. Same shape of change as
delegationin 0.4.0: existing records stay valid, and absence meansself, so every hardware profile in the spec is unaffected.kindis a closed set (self,third-party-control-plane,log-import) rather than free text, because the entire value of the field is that a verifier can key on it.The rule that makes it worth having
A record whose
kindis notselfMUST carryruntime.platform: "software-only".An importer holding someone else's log has no quote to present, so a hardware platform value there is untrue rather than stronger. It is also precisely what an adapter produces by starting from a hardware example and editing the fields it understood, which is why it is a MUST.
Enforced in two places so they cannot drift, and tested from both directions in each:
models.py, a cross-fieldmodel_validator;schema/trace-claim.jsonand the packaged schema, viaif/then, so a validator that never loads the Python rejects it too.What it does not do
It launders assurance in neither direction. Naming your producer does not make unattested evidence attested. A record with a verified hardware quote is what it is whether or not it says
origin: self.Also here
docs/schema.mdstill describedtransparencyas required, with an empty string for unanchored records. 0.5.1 made it optional below Level 2 and rejects"".Checks
202 passed, 1 skipped.
ruffclean,mypyclean. 11 new tests across the model and the schema.Note for downstreams:
TrustRecordisextra="forbid", so a verifier older than 0.7.0 rejects a record carryingorigin, the same one-directional breakcmcptook withgateway.attestation_evidence. Producers should not emitoriginuntil their consumers are on 0.7.0.🤖 Generated with Claude Code