Skip to content

One contract hash family: git-blob-sha1 everywhere, and reconcile reads the registry


One contract hash family: git-blob-sha1 everywhere, and reconcile reads the registry

Section titled “One contract hash family: git-blob-sha1 everywhere, and reconcile reads the registry”

Date: 2026-10-01 Status: closed Supersedes: none Superseded-by: none — current MR-compliance: MR-7, MR-14

docs/decisions/2026-05-24-contract-sha-hash-method-fix-hermes.md (closed) found two hash families for the same file and ruled the git-blob-sha1 one canonical, matching registry.yaml and git hash-object. It named a follow-up, “petrova doctor should emit git-blob-sha1, not sha256”, and deferred it to “its own PR + test coverage + decision doc”. That follow-up was never done.

On main at 58f38241:

writerfieldhash
cli/src/verbs/sweep_state.ts:106-110 (gitBlobShortSha)state.actual_contract_shagit-blob-sha1
registry.yaml (petrova_act_register_consumer.ts:130, reconcile)repos[].contract_shagit-blob-sha1
cli/src/verbs/doctor.ts:73, written at :157state.contract_shasha256

docs/findings/20260825-1200-a-state-field-nothing-writes-outlives-the-value-it-copied.md (PTV-FND-0059) then recorded state.contract_sha as a stale copy that no writer maintained. Since #499 that has changed. The CI deep probe runs doctor --commit-state every 8 hours, so the field is now rewritten on every pass, in the wrong family. Measured on 2026-10-01 over the 26 state files that carry it: 0 equal actual_contract_sha, 1 equals the registry value, 25 equal neither. The 2026-10-01 triage of kahn-hq and rocky-hq read exactly this as drift that was not there.

The field also has a consumer that assumes a third meaning. cli/src/verbs/petrova_act_reconcile_drift.ts asks the caller for a from_sha, documented as what they “believe is currently registered” (:16-22). It then guards it against state.contract_sha (:94) and reports that value as before / “registered” in the PR body (:125, :159). Its own test fixture sets state and registry to the same value (cli/tests/reconcile_drift.test.ts:34-56), so it was written for a world where state.contract_sha mirrors the registry. Against today’s state files the guard can only pass if the operator copies a sha256 that appears nowhere else, and the PR it opens names that sha256 as the registered value.

One hash family, git-blob-sha1 truncated to 12 hex, for every contract sha. Each field has one meaning.

  1. doctor emits git-blob-sha1. Move gitBlobShortSha out of sweep_state.ts into cli/src/contract.ts and use it in doctor.ts. This is the 2026-05-24 follow-up, as ruled there.
  2. state.contract_sha means “the contract doctor last probed”. Written by doctor only. After (1) it equals actual_contract_sha whenever the deep probe and the sweep saw the same commit. contracts/state.schema.json gains a description saying so, and that it is not the registered value.
  3. reconcile_drift reads the registry, not the state file, for both its from_sha guard and its reported before. That is the value its docstring already says the caller is asserting. FROM_SHA_MISMATCH then means what its message says.

No data migration is needed. The next deep-probe pass rewrites every state.contract_sha in the new family, and the registry is already git-blob-sha1.

  • Drop state.contract_sha from the state schema — rejected for now. The host reads contract_sha on RepoState (host/src/sources/petrova.ts:46), and removal is a consumer-visible schema change for the eva/petrova consumption contracts. With (1) and (2) the field is correct and maintained; removal can be its own decision if it still earns nothing.
  • Fix doctor only — rejected. state.contract_sha would then equal actual_contract_sha, so reconcile_drift would report before == after and open a PR claiming to bump a sha to itself.
  • Make the sweep overwrite state.contract_sha with the registry value — rejected. That gives one field two writers with two meanings, which is PTV-FND-0059’s failure shape again.

For code:

  • cli/src/contract.ts: export gitBlobShortSha. sweep_state.ts and doctor.ts import it.
  • petrova_act_reconcile_drift.ts: guard and before from registry.yaml. Messages and the docstring are updated to match.
  • Tests: doctor’s contract_sha equals git hash-object for a fixture. The reconcile guard passes on the registry sha and refuses a state-only sha.

For docs:

  • contracts/state.schema.json gets a contract_sha description.
  • PTV-FND-0059 can close by reference to this record once the first post-merge deep probe lands.

For in-flight phases: none.

For invariants: none added or repealed. MR-14 is unchanged: promotion to registry.yaml stays a human-merged PR.

  • docs/decisions/2026-05-24-contract-sha-hash-method-fix-hermes.md
  • docs/findings/20260825-1200-a-state-field-nothing-writes-outlives-the-value-it-copied.md (PTV-FND-0059)
  • docs/findings/20260922-1330-null0-toll-contract-incomplete-stub.md
  • docs/decisions/2026-10-01-deep-probe-runs-in-ci.md, #499
  • Subagent: Claude Opus 5.5, 2026-10-01 (drafted; evidence above read at 58f38241)
  • Human: flight@arno.host — that every contract sha is git-blob-sha1, that state.contract_sha means “last probed by doctor”, and that reconcile_drift guards against the registry.

Countersigned by human:flight@arno.host on 2026-10-01 — box ticked in commit 8bda31dd, authored by the human (pushed to the #501 head branch after merge; cherry-picked here). Status flipped open→closed on 2026-10-01 by an agent as scribe (MR-7).