Skip to content

Name the instant freshness is judged against in verify-run's report (#55) - #65

Merged
kikashy merged 2 commits into
mainfrom
fix/55-freshness-dependency
Oct 11, 2026
Merged

kikashy merged 2 commits into
mainfrom
fix/55-freshness-dependency

Conversation

@kikashy

@kikashy kikashy commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Part of #55 (tracking #57).

What changes for a verifier's reader

verify-run now says which instant freshness was judged against. When a run has a verified acquisition that was not skipped, the report carries one new member, unsignedFreshness. The line on standard error gives the same in words. This holds for copy mappings as well as rules, which closes the gap the audit found: a calculator on a copy mapping drew no warning at all.

"unsignedFreshness":{"against":"preparation.verifiedAt","verifiedAt":"2026-10-11T22:33:39Z","sources":[{"source":"fx","checks":["acquisition","calculation"],"targets":3}]}

Acquisition and calculator freshness for 1 source were judged against preparation.verifiedAt, which the export supplies and no signature covers: freshness is consistent with the instant the export supplies; nothing establishes that instant was the admission instant.

This is a report and refuses nothing new. The exit code and --require-signed, --require-witnessed and --require-sourced behave as before. Two kinds of export carry no member: a run with no verified acquisition, which judged no freshness (a local file is not one), and an export without lineage.

Design

Part 1, the report (this PR). Preparation lists every source whose freshness it judged against its instant, at the one place both checks have already passed. That covers every verified acquisition that was not skipped, and a calculator only when it computed: a calculator that did not compute has none of its tables held to their limits. Verification gets the list from its own recomputation of the preparation, so the report names exactly what was judged. Other callers of preparation ignore the list. The preparation's bytes, the export and the stored runs do not change.

Part 2, committing the instant: filed as Judgment-Pack/judgment-pack-runtime#263, not implemented here. Neither slot the brief names exists:

  • Runner has no signature of its own. A version-5 export carries the Runtime's record-signature sidecar, which signs only the audit record's line.
  • The Runtime's record has no member a caller can fill. It carries facts, evidence and cites, and cites is held to the shape of a gateway citation, so a digest cannot ride in it.

The issue describes the missing member precisely:

  • What Runner would pass: --binding with {"kind","digest"}, where the digest is the SHA-256 of the canonical preparation object.
  • What the record would carry: that object as given, after a shape check, absent when the input is not given, and covered by the record signature and the chain.
  • What audit verify would check: the shape only.
  • What verify-run would then do: recompute the digest and refuse a mismatch.

Until that lands, this report is the honest state.

What it establishes and what it does not

  • Establishes: freshness is consistent with the instant the export supplies.
  • Does not establish: nothing establishes that instant was the admission instant. No signature covers preparation.verifiedAt:
    • the record signature covers the audit record's bytes, which do not carry it;
    • a receipt signs its source's own times;
    • the chain entry binds the record's digest.
  • The record's at: it is signed, but it is the evaluation instant, which can come any time after admission. It bounds verifiedAt from above only, and this PR does not use it.

Tests

  • The audit's reproduction, end to end (TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst, cmd). A calculated source on a copy mapping is signed through the test's signing Runtime and witnessed by a held checkpoint. Then:
    • with --public-key --require-signed --expect --require-witnessed, the report and the line name the instant;
    • recomputed one second later, the export passes with the same flags, with the same record digest, signature and checkpoint, and the report names the moved instant;
    • the instant moved alone, without recomputing, is refused with retained lineage does not match recomputation;
    • unsignedParameters is absent throughout.
  • The same at the library level (TestAPreparationMovedBySecondVerifiesAndItsInstantIsNamed). It moves the instant through Runner's own normalizeV2 and asserts that AuditBytes, AuditSignatures and Chain are unchanged.
  • Which sources are named (TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource):
    • a calculator read by a rule, or by a copy, gets acquisition and calculation, and the copy case has no unsignedParameters;
    • a calculator whose answer maps no target is still named;
    • a calculator that could not compute gets acquisition only, and the source skipped behind it is not named;
    • a case-only run has no member.
  • The sentence's counts (TestTheFreshnessSentenceCountsWhatWasJudged).
  • Both formats, word for word. The existing exact-report tests now include the member and the sentence: TestVerifyRunReportsUnsignedParametersEndToEnd, TestVerifyRunRefusesAnAssertedTargetBeforeAnUnsignedOne, TestVerifyRunReportsUnsignedParameters and TestVerifyRunAcceptsInputsDerivedFromReceipts. TestVerifyRunReportsUnsignedParametersWithTheReleaseRuntime is updated the same way; it skips unless the Runtime under test is v0.24.0.

Mutation tally

Eleven mutants, each run in a scratch copy against the focused tests: seven before review and four from it. Every one compiled and was caught.

Mutant Caught by
Dependency report dropped in the library (Freshness nil) TestAPreparationMovedBySecondVerifiesAndItsInstantIsNamed, TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst, and the four updated exact-report tests
Member dropped from the verified-inputs report only TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst, and the four updated exact-report tests
Sentence dropped TestTheFreshnessSentenceCountsWhatWasJudged, TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst, and the four updated exact-report tests
Copy-mapping sources excluded (rule-only, as unsignedParameters scopes itself) TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource, TestAPreparationMovedBySecondVerifiesAndItsInstantIsNamed, TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst, TestVerifyRunAcceptsInputsDerivedFromReceipts
Calculator tables never named TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource, TestAPreparationMovedBySecondVerifiesAndItsInstantIsNamed, TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst
Tables named for a calculator that did not compute TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource
A skipped source named TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource
Review: the verified guard dropped (a local file named) TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource/a_local_file_beside_an_acquired_calculator
Review: member dropped from the unwitnessed report TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst
Review: member dropped from the signature-invalid report TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst
Review: member dropped from the disposition-differs report TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst

Part 2 is not in this PR, so there is no digest comparison to mutate.

Checks

  • gofmt -l .: clean.
  • go vet ./...: clean.
  • The focused tests above, with Runtime v0.30.0 as JPACK_TEST_BIN: pass. The neighbouring calculator and coverage tests also pass: TestCalculatedRunIsEvaluatedAndVerifiedOffline, TestACalculatedSignedAndWitnessedRunRefusesAnswerAndTableTimeTampering, TestAFailedCalculationIsSignedAndWitnessedAndAMalformedReceiptPinIsRefused, TestARunRecordedBeforeCalculatorsVerifiesUnchanged and TestVerificationCoverageInHelpAndGuide.
  • CI at 48687f6 is green: Runner (ubuntu-latest), Runner (macos-latest) and Release contents. That covers formatting, vet, the race tests with Runtime v0.30.0 and v0.27.1, and the must-run chain and signature tests.
  • At e9a7457, after the review fixes: gofmt -l . clean, go vet ./... clean, and the four new-or-extended focused tests pass. CI at e9a7457 is green: Runner (ubuntu-latest), Runner (macos-latest) and Release contents.

Docs

  • docs/MAPPING-V2.md: one paragraph and an example member, after unsignedParameters.
  • docs/design/calculated-values.md: one bullet under "What this does not establish".
  • docs/releases/v0.9.0.md: one bullet. An added report member is a minor version, as v0.7.0's added report status was.

Not in this PR

Review fixes

A claims review of 48687f6 by a second Claude agent found no HIGH. Its one MEDIUM and three LOW are fixed in e9a7457:

  • MEDIUM: the verified guard on the judged list was untested. TestFreshnessJudgedAgainstTheInstantIsNamedSourceBySource gains a subtest that puts a local file beside the acquired calculator and asserts that the local file is not named. The mutant that drops the guard is now caught.
  • LOW: the member on the refusal reports was untested. TestVerifyRunNamesTheInstantFreshnessWasJudgedAgainst now asserts the exact refusal and the member in three reports. The three mutants that drop it are now caught.
    • unwitnessed: the held checkpoint withheld, with --require-witnessed.
    • signature-invalid: another --public-key.
    • disposition-differs: the moved export as version 2 with its answer changed, re-executed by the release's Runtime.
  • LOW: the docs were looser than the code. docs/MAPPING-V2.md and the v0.9.0 bullet now say "a verified acquisition that was not skipped". The bullet also says the member is absent for an export without lineage.
  • LOW: gaps in A caller-supplied binding digest on the evaluation record, so a companion can commit its preparation (runner #55) judgment-pack-runtime#263. The issue body now also says three things:
    • for a record without a binding, verify-run keeps reporting unsignedFreshness and refuses only under an opt-in such as --require-bound;
    • verify-run refuses a binding of another kind before it compares digests;
    • the operator holds the key and can re-sign a record with a new binding, which only a witnessed chain (--require-witnessed) catches.

Review

Seam: a verification claim. A second-reviewer round follows, run by the coordinator.

🤖 Generated with Claude Code

)

verify-run's report and its line on standard error now say that each
acquisition's freshness, and each computed calculator's table freshness,
was judged against preparation.verifiedAt, which the export supplies and
no signature covers: freshness is consistent with the instant the export
supplies; nothing establishes that instant was the admission instant.

The new member, unsignedFreshness, is present for every run any source of
which was acquired, copy mappings included. It names the member and the
instant, and lists each such source with its checks and target count, as
the preparation's recomputation judged them. Exit codes, --require-signed,
--require-witnessed and --require-sourced are unchanged.

Committing the instant to signed material needs a member of the Runtime's
record that a caller can fill (runtime #263).

Part of #55.

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #65:

- MEDIUM: nothing pinned the verified guard on the judged list. A subtest
  now puts a local file beside the acquired calculator and asserts that the
  local file is not named.
- LOW: nothing pinned the member on the unwitnessed, signature-invalid and
  disposition-differs reports. The end-to-end test now asserts each refusal
  exactly, with the member.
- LOW: MAPPING-V2 and the v0.9.0 bullet now say "a verified acquisition that
  was not skipped", and the bullet says the member is absent for an export
  without lineage.

Part of #55.

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kikashy
kikashy merged commit 336a7bc into main Oct 11, 2026
3 checks passed
kikashy added a commit that referenced this pull request Oct 12, 2026
Complete the v0.9.0 notes: the operator's note (Runtime v0.31.0 for the binding; exports unchanged) and the verification record for #65 and #66.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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