Skip to content

HAC-343: anchor the freeze proof to tags; restore the cockpit exit controls - #31

Merged
qmarcelle merged 2 commits into
mainfrom
hac/343-freeze-anchors
Aug 23, 2026
Merged

HAC-343: anchor the freeze proof to tags; restore the cockpit exit controls#31
qmarcelle merged 2 commits into
mainfrom
hac/343-freeze-anchors

Conversation

@qmarcelle

Copy link
Copy Markdown
Contributor

Two fixes, both for defects visible on main right now.

main is red: the freeze proof depended on branch history

verify-packet.mjs asked git log -1 -- <contract> whether each frozen contract
was still the file its freeze commit introduced. That question is about whichever
branch is checked out, and this repository squash-merges — so after #29 landed,
all three contracts appear to have been introduced by the squash commit 062c8db
and every freeze check failed, while nothing had actually been edited.

The claim worth keeping was never "this commit last touched the file". It is
"these bytes were frozen at that commit, before the result existed". Three
assertions now carry it, none consulting the current branch:

  1. a tag resolves to the pinned freeze commit — the anchor is the right one;
  2. the bytes at that commit are the bytes on disk;
  3. the freeze commit is an ancestor of the canonical result, so it was frozen
    before the result rather than merely differing from it.

Tags because HAC-342 already does exactly this, recording a permanenceAnchor
for the same reason. Four anchors are pushed:

tag commit
hac-343-freeze-metric-definitions 0a6babb
hac-343-freeze-corpus dbdcaa9
hac-343-freeze-execution-semantics 276750b
hac-343-canonical-result 7ede0f9

This also closes a quieter problem: those commits were reachable only from
hac/343-metric-manifest. Deleting that branch after merge would have collected
them and broken the byte comparison too, not just the history check. Verified
against a clone with the branch deleted and gc --prune=now run.

canonicalResultCommit had the same defect and is now pinned rather than read
from git log -1 -- results.json. It is a judge-facing value the cockpit renders;
on a squashed main it rebuilt to the squash commit, so the export stopped
matching its own committed bytes. Dropping the lookup leaves the builder with no
git dependency at all.

The cockpit exit controls are broken on main

A dropped import — mine, in the commit that became part of #29 — left
GUIDE_FREE_STATE undefined, so "Explore freely", "Exit to cockpit" and "Explore
the complete cockpit" each threw on click and left the page untouched.

Nothing caught it: a handler that throws leaves the DOM exactly as it was, so a
gate reading only the DOM cannot tell a dead control from a working one. Two
independent fixes, both confirmed to fail the broken file on their own:

  • every page now reports pageerror, so any uncaught exception fails the run;
  • assertGuidedExit drives both entry points and asserts the document lands on
    guide.local.free with the bar gone and the full cockpit behind it.

Verification

  • pnpm run check exit 0
  • 637 tests (6 new negative proofs for the anchors, each asserting its mutation
    landed first — deleted tag, moved freeze tag, moved result tag, edited
    contract, and a contract frozen after the result with pin and tag moved
    together so only the ordering check can catch it)
  • contrast floor: 5,592 text nodes, 46 scenarios, 0 failures, min 5.15:1
  • judge export byte-identical to its rebuild
  • packet verified on squashed history and on a checkout where the branch is gone

Replaces #30, which conflicted only because its merge-base predates the squash.

…andler

I removed two guide imports from the cockpit in 1296935. SonarCloud had only
flagged one of them as unused; I took the two-name removal from the verifier's
import, where both really were dead, and applied it to the cockpit without
re-checking which names that file actually used. `GUIDE_FREE_STATE` is used —
it is the whole of the exit branch — so every click on "Explore freely",
"Exit to cockpit" and "Explore the complete cockpit" threw
`GUIDE_FREE_STATE is not defined` and left the page exactly as it was.

The more useful finding is that nothing caught it. 631 tests, the cockpit
contract gate and a browser gate covering 46 scenarios all stayed green, for
two reasons that are worth fixing separately:

  - No page ever reported its uncaught exceptions. A handler that throws
    leaves the DOM untouched, so a gate reading only the DOM cannot tell a
    dead control from a working one. `browser.newPage` is now wrapped once,
    so every page — including any added later — reports `pageerror`, and any
    uncaught exception fails the run.

  - The exit path was never exercised. The walk's next/back/ablate controls
    were clicked; the three controls that leave the walk were not, though
    leaving at any point is a stated requirement. `assertGuidedExit` now
    drives both entry points and asserts the document lands on
    `guide.local.free` with the bar gone and the full cockpit behind it.

Both mechanisms were confirmed against the broken file: each fails it
independently, and both pass once the import is restored.
Main is red, and the evidence is fine. `verify-packet.mjs` asked
`git log -1 -- <contract>` whether each frozen contract was still the file its
freeze commit introduced. That question is about whichever branch is checked
out, and this repository squash-merges: after #29 landed, all three contracts
appear to have been introduced by the squash commit, so every freeze check
failed on main while nothing had been edited.

The claim worth keeping is not "this commit last touched the file". It is
"these bytes were frozen at that commit, before the result existed". Three
assertions now carry it, none of which consult the current branch:

  1. a tag resolves to the pinned freeze commit — the anchor is the right one;
  2. the bytes at that commit are the bytes on disk;
  3. the freeze commit is an ancestor of the canonical result, so it was frozen
     before the result rather than merely differing from it. `--is-ancestor` is
     topological, so a rewritten author date cannot fake the ordering.

Tags because HAC-342 already does this, recording a `permanenceAnchor` for the
same reason. They also fix a quieter problem: the freeze commits were reachable
only from `hac/343-metric-manifest`, so deleting the branch after merge would
have collected them and broken the byte comparison too, not just the history
check. Verified against a clone with the branch deleted and gc run.

`canonicalResultCommit` had the same defect and is now pinned rather than read
from `git log -1 -- results.json`. It is a judge-facing value the cockpit
renders; on a squashed main it rebuilt to the squash commit, so the export no
longer matched its own committed bytes. That is the toolchain-field mistake
again — a rendered fact that changes with where it is rebuilt. Dropping the
lookup leaves the builder with no git dependency at all.

Six negative proofs added, each asserting its mutation landed first: a deleted
tag, a moved freeze tag, a moved result tag, an edited contract, and — the
load-bearing one — a contract frozen *after* the result, with pin and tag moved
together so every anchor still agrees and only the ordering can catch it.

Verified: check exit 0, 637 tests, export byte-identical, packet verified on
squashed history and on a checkout where the branch no longer exists.
Copilot AI lite review requested due to automatic review settings August 21, 2026 11:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vercel

vercel Bot commented Aug 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
interlock Ready Ready Preview Aug 21, 2026 11:55am

@qmarcelle qmarcelle mentioned this pull request Aug 21, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qmarcelle
qmarcelle merged commit 7d62f6f into main Aug 23, 2026
24 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.

2 participants