Seal hash simplification

Simplify the contract seal system from git-commit-based verification to content-hash-based verification. This eliminates the post-commit `.saves.json` fixup that makes the file permanently dirty and blocks branch switches on every pipeline run.

verdict PASSscore 13 / 13findings 4 (0 risk · 0 debt · 4 obs)duration 29mrejection cycles 0shipped Apr 26, 2026surface cli

Pipeline timeline

Intent to proven code in 29m across Think, Plan, Build, and Verify.

Think
7m
Plan
7m
Build
17m
Verify
6m

Assertion ledger

13 claims, each independently verified. Showing 8 — show all →

IDSaysMatcher
A001An unchanged contract is verified as intactverifiedok
A002A modified contract is detected as tamperedverifiedok
A003A tampered contract blocks saving the verify reportverifiedok
A004Tag search finds tests changed since the feature branch divergedverifiedok
A005Tag search falls back to global when merge-base is unavailableverifiedok
A006A missing saves file makes the seal unverifiableverifiedok
A007New proof chain entries record no commit hashverifiedok
A008The commit field is no longer written to saves metadataverifiedok

Findings 4 total

obspackages/cli/tests/commands/artifact.test.tsclosed
Stale commit assertion passes trivially: `artifact.test.ts:1233,1242` — the "appends to existing .saves.json" test saves `scope.commit` and later asserts it's unchanged. Since `writeSaveMetadata` no longer writes `commit`, both values are `undefined`, so `expect(undefined).toBe(undefined)` passes without testing anything. The test still verifies scope entry survives a spec save via `toBeDefined()` at line 1239, but the commit-specific line is dead weight. Next cycle touching this test should remove lines 1233 and 1242.
obspackages/cli/src/commands/verify.tsclosed
`execSync` import retained but usage reduced: `verify.ts:17` — `execSync` is still imported and used for the merge-base/diff commands (lines 150-157). The old `git show` call was the fragile one (required exact commit hash). The remaining `execSync` calls use merge-base, which is robust. Not a problem — just noting that the file still has a child-process dependency for the scoped search path.
obsana.jsonclosed
A005 fallback test triggers via missing ana.json, not missing branch: `verify.test.ts:548-590` — the fallback test causes `readArtifactBranch` to throw by omitting `ana.json` entirely. This is a valid trigger but tests a different failure mode than "artifact branch exists but merge-base fails" (e.g., on a repo with no shared history). Both paths hit the same catch block, so the behavioral coverage is equivalent. A future cycle could add a test where `ana.json` exists but `git merge-base` fails (orphan branches, shallow clones).
obspackages/cli/tests/commands/pr.test.tsclosed
pr.test.ts fixture retains old `commit` fields: `pr.test.ts:278` — the fixture has `commit: 'def456'` on the contract entry. This represents old-format data and is harmless (JSON.parse ignores extra properties), but it means the pr.test.ts fixture doesn't reflect what current code produces. If a future test asserts on the shape of `.saves.json` entries, this fixture would give a false impression. Low priority.

Integrity seal

scopesha256:c78c7724c4754...
contractsha256:10f28abd1a9f7...
plansha256:e9f97d3db2537...
specsha256:4017622839f92...
build-reportsha256:6d369a73eb8dd...
verify-reportsha256:21a60d89904c6...
audit cmd$ ana proof audit seal-hash-simplification   → all hashes match