Skip to content

Make artifact digest verification unclaimable without a comparison - #116

Merged
mdheller merged 1 commit into
mainfrom
feat/artifact-digest-honesty
Jul 29, 2026
Merged

Make artifact digest verification unclaimable without a comparison#116
mdheller merged 1 commit into
mainfrom
feat/artifact-digest-honesty

Conversation

@mdheller

Copy link
Copy Markdown
Contributor

The problem

resolve_steering_artifacts wrote "verified": true into every receipt while computing the sha256 of the file it had just downloaded. There was nothing to compare that digest against — the sourceset schema declared digestRequired but carried no field able to hold an expected value, so the flag asserted a check that could not have been performed.

The repo already knew. gpt2-small-res-jb.steering-sourceset.json sets digestRequired: true and lists "artifact digest lock" under activation.missing, while the fixture receipt claimed verified: true over an all-zeros placeholder digest.

The change

  • Sourceset gains expectedDigests (pinned filePath -> sha256) on both model and sae. This is what makes digestRequired satisfiable at all.
  • Receipt gains a required verificationMethod of pinned-digest / trust-on-first-use / unverified. verified: true is now representable only alongside pinned-digest and the expectedSha256 that was compared against.
  • Ordering. Resolution fails closed before the first network call when a section requires pinning but carries no pins, rather than fetching and adjudicating afterwards. A mismatch raises. Unpinned files record trust-on-first-use with verified: false — what arrived, not a claim it was correct.
  • Dry runs report the unsatisfiable pin requirement in missing[] instead of raising, since they fetch nothing.

Verification

make validate-artifact-digest-honesty carries the negative controls — the three previously representable dishonest shapes are asserted to be rejected:

REJECTED verified true with no verificationMethod (the pre-change shape)
REJECTED verified true while admitting nothing was compared
REJECTED verified true claiming a pin but carrying no expectedSha256

A constraint with no negative control is indistinguishable from one that is not enforced.

⚠️ main is currently red, independently of this PR

Found while rebasing. Four instances of the same pathology — a merge duplicated a block and it was committed:

File Breakage
src/agent_machine/cli.py SyntaxError: keyword argument repeatedthe CLI does not compile
src/agent_machine/steering_loader.py SyntaxError: unterminated triple-quoted string (duplicated docstring)
contracts/external-trust-signal-provider.schema.json duplicated "required" block, invalid JSON — breaks validate-json
Makefile 9 duplicate .PHONY lines and 9 duplicate validate: targets

Not fixed here — unrelated to this change and it should not hide inside it. The Makefile conflict was resolved by adding lines rather than editing any of the 9, so nothing upstream is silently restructured.

🤖 Generated with Claude Code

@mdheller
mdheller force-pushed the feat/artifact-digest-honesty branch from e8fda9f to 4474b46 Compare July 29, 2026 13:49
@mdheller
mdheller changed the base branch from main to fix/merge-duplication-breaks July 29, 2026 13:49
resolve_steering_artifacts wrote "verified": true into every receipt while
computing the sha256 of the file it had just downloaded. There was nothing to
compare that digest against: the sourceset schema declared digestRequired but
carried no field able to hold an expected value, so the flag asserted a check
that could not have been performed. The gpt2-small.res-jb example set
digestRequired: true and listed "artifact digest lock" as missing, while the
fixture receipt claimed verified: true over an all-zeros placeholder digest.

Sourceset gains expectedDigests (a pinned filePath -> sha256 map) on both the
model and sae sections, which is what makes digestRequired satisfiable at all.
Receipts gain a required verificationMethod of pinned-digest, trust-on-first-use
or unverified, and the schema now makes verified: true representable only
alongside verificationMethod: pinned-digest and the expectedSha256 that was
compared against.

Resolution fails closed before the first network call when a section requires
pinning but carries no pins, rather than fetching and adjudicating afterwards.
A digest mismatch raises instead of being recorded. Unpinned files are recorded
as trust-on-first-use with verified: false, which states what arrived without
claiming it was correct. Dry runs report the unsatisfiable pin requirement in
missing[] instead of raising, since they fetch nothing.

validate-artifact-digest-honesty carries the negative controls: the three
previously representable dishonest shapes are asserted to be rejected. A
constraint with no negative control is indistinguishable from one that is not
enforced.
@mdheller
mdheller force-pushed the feat/artifact-digest-honesty branch from 4474b46 to 0086f7c Compare July 29, 2026 14:32
@mdheller
mdheller changed the base branch from fix/merge-duplication-breaks to main July 29, 2026 14:32
@mdheller mdheller closed this Jul 29, 2026
@mdheller mdheller reopened this Jul 29, 2026
@mdheller
mdheller merged commit 8ab6f94 into main Jul 29, 2026
5 checks passed
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