diff --git a/CHANGELOG.md b/CHANGELOG.md index b6bc7d7..232dc78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,11 +29,11 @@ What changed what admissible means: rather than read as evidence/2. Qualification is scoped to the predicates the consumer's own policy puts in play. -Installation (#2): `scripts/install-ladder.sh` and `scripts/check-install.sh` -install and check a pinned checkout. **Their pin still names `760977f`**, which -predates everything above and still reports `evidence-v3.1`. Moving the pin to -this release is a separate change, because the installer refuses a pin that is -not already on `main`. +Installation (#2, #7): `scripts/install-ladder.sh` and `scripts/check-install.sh` +install and check a pinned checkout. #7 moves their pin from `760977f`, which +predates everything above and reports `evidence-v3.1`, to `c86bf47`, the commit +that released this contract. It had to be a separate change because the +installer refuses a pin that is not already on `main`. ## 0.2.0 — contract `evidence-v3.1` diff --git a/INSTALL.md b/INSTALL.md index 5e026e9..89af96d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -200,10 +200,12 @@ rather than reporting a success it did not establish. ### Where the bootstrap tooling lives -Neither script is in the pinned checkout. The pin predates `scripts/`, so -detaching `~/src/verification-ladder` to it *removes both scripts from that -directory* - including `install-ladder.sh` while it is running. The installer -therefore copies the pair out first, to +The pinned checkout is not where these scripts run from. Detaching +`~/src/verification-ladder` to the pin *replaces both scripts in that directory* +with the pin's own copy, including `install-ladder.sh` while it is running. That +copy is an older pair, reviewed with an older pin, so running it would check +the install against the wrong runtime. The installer therefore copies the pair +it was reviewed with out first, to ``` ${XDG_DATA_HOME:-~/.local/share}/verification-ladder/ @@ -212,7 +214,8 @@ ${XDG_DATA_HOME:-~/.local/share}/verification-ladder/ PROVENANCE ``` -and names that path in its closing message. Run the checker from there: +and names that path in its closing message. Run the checker from there, never +from `~/src/verification-ladder/scripts/`: ```sh ~/.local/share/verification-ladder/check-install.sh # --codex to check Codex too diff --git a/scripts/check-install.sh b/scripts/check-install.sh index 7913af0..77ebc18 100755 --- a/scripts/check-install.sh +++ b/scripts/check-install.sh @@ -8,7 +8,7 @@ # repository with no verification policy. set -u -PIN=760977f69d6b74b9888be4c9cbb404f7726bea73 +PIN=c86bf47e70d7a3aa18c968011b487190e2becbea INV_SHA=282f21173aad9815a22a6ece35ad0fdd42653dd7a001dc6d85e6d51189aa5699 CLONE=$HOME/src/verification-ladder LINK=$HOME/.claude/skills/verification-ladder @@ -30,7 +30,8 @@ fail() { echo "FAIL $1"; rc=1; } ok() { echo "ok $1"; } note() { echo "note $1"; } -# These scripts live outside the pinned checkout, because $PIN predates them. +# These scripts live outside the pinned checkout: the scripts/ inside it are +# $PIN's own, older pair, reviewed with an older pin, and are not this checker. # Report where this copy came from: once detached from its source commit it has # no other identity. Informational - running from a branch checkout is valid. BOOTSTRAP_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/verification-ladder diff --git a/scripts/install-ladder.sh b/scripts/install-ladder.sh index 0d49a20..4e4f684 100755 --- a/scripts/install-ladder.sh +++ b/scripts/install-ladder.sh @@ -10,7 +10,7 @@ # to the checker it was reviewed with, for the same reason. set -u -PIN=760977f69d6b74b9888be4c9cbb404f7726bea73 +PIN=c86bf47e70d7a3aa18c968011b487190e2becbea REPO=https://github.com/Deep-Sixed/Verification-Ladder # sha256 of the invariant block in INSTALL.md at $PIN. Moving the pin without # revalidating this makes the installer fail closed rather than paste unknown @@ -18,9 +18,10 @@ REPO=https://github.com/Deep-Sixed/Verification-Ladder INV_SHA=282f21173aad9815a22a6ece35ad0fdd42653dd7a001dc6d85e6d51189aa5699 CLONE=$HOME/src/verification-ladder CODEX_HOME=${CODEX_HOME:-$HOME/.codex} -# Bootstrap tooling lives OUTSIDE the pinned checkout. $PIN predates these -# scripts, so detaching to it removes them from $CLONE - including this file -# while it is running. They are copied here first so the checker survives. +# Bootstrap tooling lives OUTSIDE the pinned checkout. Detaching $CLONE to $PIN +# replaces this pair with $PIN's own copy - an older pair, reviewed with an +# older pin - including this file while it is running. They are copied here +# first so that the pair reviewed with THIS pin is the one that survives. BOOTSTRAP_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/verification-ladder # sha256 of the check-install.sh this installer was reviewed with. The two are # reviewed as a pair and preserved as a pair, so requiring the checker merely to @@ -29,7 +30,7 @@ BOOTSTRAP_DIR=${XDG_DATA_HOME:-$HOME/.local/share}/verification-ladder # afterwards. Changing check-install.sh means changing this constant in the same # commit; a mismatch is refused rather than reported, because the checker is the # only thing that would have caught it. -CHECKER_SHA=09f65bcc7f81c91fea7ea1694b0f5a0d524974e35b3df2b2d334ef01c8ae0f1b +CHECKER_SHA=d7b922510547b8f9d856a897bd493a4a5db3785f2d2fd15fc259e542dcf6ce07 SELF=$(readlink -f "$0" 2>/dev/null || echo "$0") SELF_DIR=$(dirname "$SELF") DO_CODEX=0; DRY=0; FORCE=0 @@ -176,11 +177,12 @@ done # ---- step 1b: preserve bootstrap tooling outside the pinned checkout --- step "1b. Preserving bootstrap tooling in $BOOTSTRAP_DIR" preserve_bootstrap() { - # Fail closed: $PIN does not contain check-install.sh, so if it is not beside - # the installer now it cannot be recovered later, and the closing message - # would name a checker that was never preserved. + # Fail closed: the only checker reviewed with this installer is the one beside + # it. $PIN's own copy is an older checker, reviewed with an older pin, so if + # this one is not here now it cannot be recovered later, and the closing + # message would name a checker that was never preserved. [ -f "$SELF_DIR/check-install.sh" ] \ - || die "check-install.sh is not beside $SELF; refusing because $PIN does not contain it" + || die "check-install.sh is not beside $SELF; refusing because $PIN carries only an older checker" # Identity, not existence. Checked before the early return below, so a re-run # from $BOOTSTRAP_DIR re-establishes the pair rather than trusting what an # earlier run left there. @@ -343,5 +345,5 @@ echo "Steps 1-5 done. Next:" [ "$DO_CODEX" -eq 1 ] && CODEX_ARG=" --codex" || CODEX_ARG="" echo " $BOOTSTRAP_DIR/check-install.sh$CODEX_ARG" echo "then the discovery test, then the agent-level policy-less BLOCKED test." -echo "(\$CLONE is pinned at $PIN, which predates these scripts, so they live" -echo " in $BOOTSTRAP_DIR rather than inside the checkout.)" +echo "(Run the pair in $BOOTSTRAP_DIR. \$CLONE is pinned at $PIN, and the" +echo " scripts/ inside it are that revision's older pair, reviewed with an older pin.)" diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 6414c88..6a11d87 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -36,7 +36,7 @@ REPO = Path(__file__).resolve().parents[1] INSTALLER = REPO / "scripts" / "install-ladder.sh" CHECKER = REPO / "scripts" / "check-install.sh" -PIN = "760977f69d6b74b9888be4c9cbb404f7726bea73" +PIN = "c86bf47e70d7a3aa18c968011b487190e2becbea" ORIGIN = "https://github.com/Deep-Sixed/Verification-Ladder" @@ -318,7 +318,7 @@ def test_installer_rejects_unknown_arguments(tmp_path): def test_installer_refuses_missing_sibling(tmp_path): - """$PIN does not contain check-install.sh, so it cannot be recovered later.""" + """$PIN carries only an older checker, so this one cannot be recovered later.""" solo = tmp_path / "bin" solo.mkdir() shutil.copy(INSTALLER, solo / "install-ladder.sh") @@ -512,6 +512,24 @@ def test_digest_read_failure_is_not_a_mismatch(tmp_path, stub, why): assert not (tmp_path / ".local" / "share" / "verification-ladder").exists() +def test_the_pin_installs_the_hardened_release(): + """The installer must deliver the verifier the rest of this repository describes. + + It pinned 760977f: the build from before execution binding, the CI chain and + the committed adoption line, reporting `evidence-v3.1`. A scripted install + therefore got none of that hardening, under the same contract identity as a + build that had it. Hardcoded rather than compared with the working tree, + because a future contract bump lands one PR before the pin can follow it. + """ + assert PIN != "760977f69d6b74b9888be4c9cbb404f7726bea73" + runtime = _git("show", f"{PIN}:skills/verification-ladder/bin/verify.py", cwd=REPO).stdout + assert 'COMPATIBILITY = "evidence-v3.2"' in runtime + assert 'VERSION = "0.3.0"' in runtime + assert 'EVIDENCE_CONTRACT = "evidence"' in runtime, "the pinned runtime cannot read the adoption line" + for script in (INSTALLER, CHECKER): + assert f"PIN={PIN}" in script.read_text(), f"{script.name} pins a different runtime" + + def test_network_surface_has_not_moved(): """Guard the assumption the markers rest on, not the markers themselves.