HOMELAB-2682: feat(sbx-gits): declaration-anchored proof check (gits-proof-suite) #871

Merged
pi-landing merged 4 commits from plane/HOMELAB-2682-declaration into live 2026-11-06 07:19:49 +00:00
Owner

What this delivers

The FULL desired state of the proof tool: the #859 reviewed content (whose file a later
revert took out) as the base, #869's suite_relpath work intact on top, plus the
declaration of the canonical suite (gits-proof-suite).

The sequence this closes

  1. #859 (9119eb2b) — the proof tool anchored to the smoke committed at the head: 1623
    lines, the whole anchor machinery (git_obj / GIT_NO_REPLACE_OBJECTS=1 +
    core.useReplaceRefs=false, git_path_type, git_blob_sha with the oid binding and the
    zero-byte refusal, the verdict line, the canonical-red warning, --tree).
  2. #866 (22c2c328) — reverted the file (-586/+48), pending this declaration work.
    Live's tool went back to pre-#859 with #869's forward-compat on top.
  3. #869 (a848b717) — suite_relpath: chain_text argument 21, read and written, so
    the lines the #859 tool wrote while it was briefly deployed recompute byte for byte.
  4. this PR — #859's tool restored and #869 re-applied on the same line, plus the
    declaration. git diff 9119eb2b:<tool> <this> shows only the declaration additions:
    rel_under is byte-identical to #859's, and the chain_text recompute call in check is
    byte-identical to #869's (argument 21 included).

1. Declaration support (the format, settled and acked by homelab-33)

gits-proof-suite at the root of the repo being checked — fixed filename, not
env-configurable
(an anchor that can be redefined is not an anchor), read from the head
commit
, never from the working tree:

# comments allowed, blank lines free
suite=scripts/ci/gits-sbx-emu-smoke.sh
  • exactly one suite=<repo-relative path> line;
  • any other key → the check AND the run REFUSE (rc 2, message names the key), and so do two
    suite= lines, an empty suite=, an absolute path, or one that escapes with ... The
    declaration is never half-read;
  • GITS's real declaration (PR #207 to ghostty-in-the-shell dev) is
    suite=scripts/ci/gits-sbx-emu-smoke.sh — a copy of the smoke committed in the app repo.

2. Resolution order for the canonical suite at head H

case canonical suite anchor
(1) H carries gits-proof-suite the path it declares the blob of that path in H, with all of #859's machinery
(2) else, H carries EMU_PROOF_SMOKE_RELPATH that one (unchanged behaviour) its blob in H
(3) neither the head cannot pass (rc 1) — the message names the missing declaration —

A declared path that does not exist in H is rc 1 as well: the declaration and the suite it
names travel in the same commit. No fallback, no substitute suite (B2 preserved).

3. Class output (homelab-33's condition)

The check always prints a class line. When the diff
git diff <merge-base with the live/default branch>..<H> -- gits-proof-suite <declared suite>
is non-empty:

PROOF  GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required

Base ref: origin/HEAD, else the first of origin/live|origin/dev|origin/main|..., with a
timeout-bounded fetch as a last resort; --base <ref> pins it. No usable base →
class unknown — assume gate tooling. Informational only — pass/fail unchanged, printed on
both the OK and the FAIL path.

4. FAIL visibility (homelab-33's FAIL-veto answer)

Unchanged from #859 and now pinned by a test: a red run of the same canonical smoke on
the same head prints PROOF aviso el smoke CANONICO TAMBIEN CORRIO EN ROJO ... result=FAIL rc=1
next to the passing verdict. The red line is neither deleted nor hidden.

5. Cross-version ledger reading stays

class / results_sha256 / suite_relpath remain optional; lines from every era (18, 20,
21 fields) recompute byte for byte. Nothing about the ledger format changes here.

The pin: this PR does NOT deploy

#870 pinned the sbx-gits-tools ConfigMap content as an explicit literals: block, so editing
tools/*.sh leaves the render byte-identical and ArgoCD has nothing to sync. kustomization.yaml
and statefulset.yaml are untouched in this PR. The deployment happens later in a deliberate
pin-bump, after GITS dev carries its gits-proof-suite declaration.

Verification

  • bash -n on the tool and on all six tools/*.sh + the harness: clean.
  • Harness, SOLO run on this branch (never concurrent) — see the comment below for the
    before/after table; new section 27 covers: declared-repo pass with the anchor on the
    declared blob (the repo also carries the infra-core canonical path with different
    content, so the case proves the declaration wins), declared-path-missing → rc 1, five
    malformed-declaration refusals for check and run (no line written), class on the
    declaration, class on the declared suite, class unknown — assume gate tooling, FAIL
    visibility, and "the name is not configurable from the environment".
  • T7[diff] / 17[R159] (d): SBX_GITS_DIFF_SET re-measured for this branch
    (M tools/gits-sbx-emu-proof.sh + M README.md). The pre-existing 10b[R99] red (documented
    as deliberate in README §10.5 after the pin removed files:) is untouched by this PR.

Scope

tools/gits-sbx-emu-proof.sh, README.md (§10.6), docs/gits-sbx/run-proof.md, and the
harness. No statefulset, no kustomization, no smoke.

Do not merge on one seat: two gate seats follow (homelab-33's condition).

## What this delivers The FULL desired state of the proof tool: the **#859 reviewed content** (whose file a later revert took out) as the base, **#869's `suite_relpath` work intact on top**, plus the **declaration** of the canonical suite (`gits-proof-suite`). ### The sequence this closes 1. **#859** (`9119eb2b`) — the proof tool anchored to the smoke committed at the head: 1623 lines, the whole anchor machinery (`git_obj` / `GIT_NO_REPLACE_OBJECTS=1` + `core.useReplaceRefs=false`, `git_path_type`, `git_blob_sha` with the oid binding and the zero-byte refusal, the verdict line, the canonical-red warning, `--tree`). 2. **#866** (`22c2c328`) — **reverted the file** (`-586/+48`), pending this declaration work. Live's tool went back to pre-#859 with **#869's forward-compat** on top. 3. **#869** (`a848b717`) — `suite_relpath`: chain_text argument **21**, read *and* written, so the lines the #859 tool wrote while it was briefly deployed recompute byte for byte. 4. **this PR** — #859's tool restored and #869 re-applied on the same line, plus the declaration. `git diff 9119eb2b:<tool> <this>` shows only the declaration additions: `rel_under` is byte-identical to #859's, and the `chain_text` recompute call in `check` is byte-identical to #869's (argument 21 included). ### 1. Declaration support (the format, settled and acked by homelab-33) `gits-proof-suite` at the **root** of the repo being checked — **fixed filename, not env-configurable** (an anchor that can be redefined is not an anchor), read **from the head commit**, never from the working tree: ``` # comments allowed, blank lines free suite=scripts/ci/gits-sbx-emu-smoke.sh ``` - **exactly one** `suite=<repo-relative path>` line; - **any other key → the check AND the run REFUSE** (rc 2, message names the key), and so do two `suite=` lines, an empty `suite=`, an absolute path, or one that escapes with `..`. The declaration is never half-read; - GITS's real declaration (PR #207 to `ghostty-in-the-shell` `dev`) is `suite=scripts/ci/gits-sbx-emu-smoke.sh` — a **copy of the smoke** committed in the app repo. ### 2. Resolution order for the canonical suite at head `H` | case | canonical suite | anchor | |------|-----------------|--------| | (1) `H` carries `gits-proof-suite` | the path it declares | the **blob of that path in `H`**, with all of #859's machinery | | (2) else, `H` carries `EMU_PROOF_SMOKE_RELPATH` | that one (unchanged behaviour) | its blob in `H` | | (3) neither | **the head cannot pass** (rc 1) — the message names the missing declaration | — | A **declared path that does not exist in `H`** is rc 1 as well: the declaration and the suite it names travel in the same commit. No fallback, no substitute suite (B2 preserved). ### 3. Class output (homelab-33's condition) The check always prints a class line. When the diff `git diff <merge-base with the live/default branch>..<H> -- gits-proof-suite <declared suite>` is non-empty: ``` PROOF GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required ``` Base ref: `origin/HEAD`, else the first of `origin/live|origin/dev|origin/main|...`, with a timeout-bounded `fetch` as a last resort; `--base <ref>` pins it. No usable base → `class unknown — assume gate tooling`. **Informational only — pass/fail unchanged**, printed on both the OK and the FAIL path. ### 4. FAIL visibility (homelab-33's FAIL-veto answer) Unchanged from #859 and now **pinned by a test**: a red run of the **same canonical smoke** on the same head prints `PROOF aviso el smoke CANONICO TAMBIEN CORRIO EN ROJO ... result=FAIL rc=1` **next to** the passing verdict. The red line is neither deleted nor hidden. ### 5. Cross-version ledger reading stays `class` / `results_sha256` / `suite_relpath` remain **optional**; lines from every era (18, 20, 21 fields) recompute byte for byte. Nothing about the ledger format changes here. ## The pin: this PR does NOT deploy #870 pinned the `sbx-gits-tools` ConfigMap content as an explicit `literals:` block, so editing `tools/*.sh` leaves the render **byte-identical** and ArgoCD has nothing to sync. `kustomization.yaml` and `statefulset.yaml` are **untouched** in this PR. The deployment happens later in a deliberate **pin-bump**, **after** GITS `dev` carries its `gits-proof-suite` declaration. ## Verification - `bash -n` on the tool and on all six `tools/*.sh` + the harness: clean. - Harness, SOLO run on this branch (never concurrent) — see the comment below for the before/after table; new **section 27** covers: declared-repo pass with the anchor on the **declared** blob (the repo also carries the infra-core canonical path with *different* content, so the case proves the declaration wins), declared-path-missing → rc 1, five malformed-declaration refusals for `check` **and** `run` (no line written), class on the declaration, class on the declared suite, `class unknown — assume gate tooling`, FAIL visibility, and "the name is not configurable from the environment". - `T7[diff]` / `17[R159] (d)`: `SBX_GITS_DIFF_SET` re-measured for this branch (`M tools/gits-sbx-emu-proof.sh` + `M README.md`). The pre-existing `10b[R99]` red (documented as deliberate in README §10.5 after the pin removed `files:`) is untouched by this PR. ## Scope `tools/gits-sbx-emu-proof.sh`, `README.md` (§10.6), `docs/gits-sbx/run-proof.md`, and the harness. **No** statefulset, **no** kustomization, **no** smoke. Do **not** merge on one seat: two gate seats follow (homelab-33's condition).
HOMELAB-2682: feat(sbx-gits): gits-proof-suite declaration anchoring on the reviewed 859 base
All checks were successful
CI Review / runner-manifest-test (pull_request) Successful in 5s
Alert Disposition Contract / alert-contract (pull_request) Successful in 14s
CI Review / helm-validate (pull_request) Successful in 11s
CI Review / receiver-test (pull_request) Successful in 6s
CI Review / alert-receiver-test (pull_request) Successful in 22s
Lint & Validate / shellcheck (pull_request) Successful in 6s
Lint & Validate / yaml-lint (pull_request) Successful in 24s
CI Review / record-sql-test (pull_request) Successful in 1m9s
Lint & Validate / terraform-validate (pull_request) Successful in 1m56s
CI Review / cockpit-test (pull_request) Successful in 2m57s
CI Review / cockpit-e2e (pull_request) Successful in 3m3s
CI Review / dispatcher-test (pull_request) Successful in 3m30s
4621c3bd9c
The tool goes back to the #859 reviewed content (9119eb2b) — the revert #866 took it
out and left the harness expecting it — with the #869 `suite_relpath` work intact on
top (chain_text argument 21, read AND written, byte-identical position), and gains the
DECLARATION of the canonical suite:

- `gits-proof-suite` at the ROOT of the repo being checked (fixed name, no env
  override, read from the head commit) declares `suite=<repo-relative path>`.
  Comments `#` and blank lines are free; any other key, two `suite=`, an empty,
  absolute or `..` path is a REFUSAL (rc 2), not a warning. `run` validates it too,
  before spending the emulator.
- Resolution order at head H: (1) the declaration wins and the anchor is the blob of
  the declared path IN H, with all of #859's machinery (type, size, oid binding,
  zero-byte and refs/replace refusals); (2) else the infra-core canonical path;
  (3) else the head cannot pass (rc 1) naming the missing declaration. A declared
  path that does not exist in H is rc 1 as well: declaration and suite travel in the
  same commit.
- CLASS (homelab-33): the check always prints a class line. When the diff from the
  merge-base with the base branch touches `gits-proof-suite` or the suite it names it
  says "GATE-TOOLING CLASS: ... two review seats required"; with no usable base it
  says "class unknown — assume gate tooling". Informational: pass/fail unchanged.
- FAIL visibility unchanged and now pinned: a red run of the SAME canonical smoke on
  the same head is printed next to the passing verdict, never hidden.
- Cross-version ledger reading stays: class / results_sha256 / suite_relpath optional.

Harness: new section 27 (declared-repo pass with the anchor on the declared blob,
declared-path-missing, five malformed-declaration refusals for check AND run,
class-on-declaration, class-on-declared-suite, class-unknown, FAIL visibility, the
name is not configurable, docs). SBX_GITS_DIFF_SET re-measured for this branch
(tools/gits-sbx-emu-proof.sh + README.md): the #870 delivery pin still holds the
ConfigMap, so this PR changes NO manifest and does not reach the pod.

No kustomization.yaml, no statefulset.yaml, no smoke change.
HOMELAB-2682: test(sbx-gits): the harness follows the restored tool (chain fixture, no-anchor wording)
All checks were successful
CI Review / helm-validate (pull_request) Successful in 13s
Alert Disposition Contract / alert-contract (pull_request) Successful in 14s
CI Review / runner-manifest-test (pull_request) Successful in 5s
CI Review / receiver-test (pull_request) Successful in 5s
CI Review / alert-receiver-test (pull_request) Successful in 21s
Lint & Validate / yaml-lint (pull_request) Successful in 20s
Lint & Validate / shellcheck (pull_request) Successful in 5s
CI Review / record-sql-test (pull_request) Successful in 49s
Lint & Validate / terraform-validate (pull_request) Successful in 2m12s
CI Review / cockpit-test (pull_request) Successful in 3m0s
CI Review / cockpit-e2e (pull_request) Successful in 3m10s
CI Review / dispatcher-test (pull_request) Successful in 3m39s
95b0d62e02
Restoring #859's tool changed two things the harness pinned to the REVERTED shape:

- 26 (the `suite_relpath` forward-read case, added on the reverted tool) built a ledger for a
  head that is NOT a commit (`fd79ff4e…` x5) and never passed `--tree`, so with the #859 gate
  back it stopped at the git gate and (b2)/(c) passed/failed for the wrong reason. The fixture
  is now a real repo: the suite is committed at the canonical path, the head is that commit's,
  and its blob is the four lines' `script_sha256`. (b)/(b2)/(c) now really measure the chain.
- 25(f) asserted the OLD wording of "this head has no anchor". The message now NAMES the
  missing declaration (`gits-proof-suite`), which is what this ticket requires; the rule it
  pins is unchanged (no fallback to "any script in the commit"). The case also says why the
  check exits before reading the ledger (fail-closed, like B1/B3) and that the
  substitute-suite-is-evidence semantics are pinned by (c)/(f2) on heads that do have an anchor.

Tool: chain_text's comment carries #869's "lectura hacia delante" paragraph on top of #859's
(the position rationale is the contract the harness pins); no behaviour change.
Author
Owner

Verification evidence (head 95b0d62e) — evidence only, NOT a review verdict

Posted with the shared FORGEJO_API_KEY (so authorship shows aaron); the text is from the
pi worker session that implemented this PR. Review verdicts are separate seats.

Harness, SOLO runs (never concurrent)

run tree result
baseline origin/live = 56356d90 (pre-PR) FAIL — 53 of 656
this branch 95b0d62e FAIL — 6 of 672

The 6 remaining failures are exactly the 6 that already failed on live — comm on the two
FAIL sets reports zero branch-only failures:

probe uses an EMPTY pre-existing --dir and leaves it in place (rc=2)
manifest[2] memoria requests 6Gi / limit 10Gi
manifest[3] imagenes = sbx-fleet@sha256:7e0513ca... + android-emulator:230756a@sha256:66430940...
manifest[3b] bump de imagen H5 (falta: sbx-gits-sin-sbx-fleet sbx-gits-otro-sbx-fleet)
T7 emulador dentro del pod (falla: requests-memoria=18(!=12Gi) limits-memoria=21(!=18Gi) limits-cpu=24(!=18))
T7+R99[10b] scripts por manifiesto (falla: generator-sin-fichero-gits-sbx-* ...)

The 10b[R99] red is the one documented as deliberate in README.md §10.5 (the #870 pin
removed files:, and the guard has to be re-enunciated for the pin in a separate harness
change). The other five are manifest/media, untouched by this PR.

The 47 that went green are the revert's own debt: §22, §24, §25 and §26 were written against
the #859 tool and were red on live because live's tool was the reverted one
(argumento desconocido en check: '--tree').

The cases this PR is about

  • §25 (anchor): green, including B1 tree/gitlink refusal, B3 zero-byte and deleted-object
    refusal, B6/B7 git replace forgery, A1 mount signal, B2 no-fallback.
  • §26 (suite_relpath forward-read): 7/7 green. check's chain_text recompute call is
    byte-identical to #869's, argument 21 included; rel_under is byte-identical to
    #859's
    . (b2)/(c) were vacuous on live (the fixture's head was not a commit, so the
    check stopped at the git gate); the fixture is now a real repo, so the chain is really
    measured.
  • §27 (new, the declaration): 12/12 green — declared-repo pass with the anchor on the
    declared blob (the fixture also commits the infra-core canonical path with different
    content, so the case proves the declaration wins), declared-path-missing → rc 1, five
    malformed declarations refused by check and run with no line written, class on
    the declaration, class on the declared suite, class unknown — assume gate tooling with an
    orphan base, FAIL visibility next to the passing verdict, and "the name is not configurable
    from the environment".
  • T7[diff] and 17[R159] (d): green, with both mutagens biting
    (M README.md + M tools/gits-sbx-emu-proof.sh, re-measured for this branch).

Provenance of the tool at this head

#859 anchor machinery:  git_blob_sha / git_path_type / git_obj(GIT_NO_REPLACE_OBJECTS=1 +
                        -c core.useReplaceRefs=false) / ancla=smoke@commit   -> present (24 hits)
#869 suite_relpath:     chain_text arg 21 (printf 'suite_relpath=%s') + run writes it +
                        check reads crel_line and passes it as arg 21        -> present
rel_under:              byte-identical to 9119eb2b:<tool>
chain_text recompute:   byte-identical to a848b717:<tool>
declaration:            EMU_PROOF_SUITE_DECL='gits-proof-suite' (fixed name, not env-read)

kustomization.yaml, statefulset.yaml and the smoke are not touched (git diff --stat:
README + tool + docs + harness). The #870 pin still holds the ConfigMap, so this PR does not
reach the pod; the delivery is a later pin-bump, after GITS dev carries its declaration.

## Verification evidence (head `95b0d62e`) — evidence only, NOT a review verdict > Posted with the shared `FORGEJO_API_KEY` (so authorship shows `aaron`); the text is from the > pi worker session that implemented this PR. Review verdicts are separate seats. ### Harness, SOLO runs (never concurrent) | run | tree | result | |-----|------|--------| | baseline | `origin/live` = `56356d90` (pre-PR) | **FAIL — 53 of 656** | | this branch | `95b0d62e` | **FAIL — 6 of 672** | **The 6 remaining failures are exactly the 6 that already failed on live** — `comm` on the two FAIL sets reports **zero** branch-only failures: ``` probe uses an EMPTY pre-existing --dir and leaves it in place (rc=2) manifest[2] memoria requests 6Gi / limit 10Gi manifest[3] imagenes = sbx-fleet@sha256:7e0513ca... + android-emulator:230756a@sha256:66430940... manifest[3b] bump de imagen H5 (falta: sbx-gits-sin-sbx-fleet sbx-gits-otro-sbx-fleet) T7 emulador dentro del pod (falla: requests-memoria=18(!=12Gi) limits-memoria=21(!=18Gi) limits-cpu=24(!=18)) T7+R99[10b] scripts por manifiesto (falla: generator-sin-fichero-gits-sbx-* ...) ``` The `10b[R99]` red is the one **documented as deliberate** in `README.md` §10.5 (the #870 pin removed `files:`, and the guard has to be re-enunciated for the pin in a separate harness change). The other five are manifest/media, untouched by this PR. The 47 that went green are the revert's own debt: §22, §24, §25 and §26 were written against the #859 tool and were red on live because live's tool was the reverted one (`argumento desconocido en check: '--tree'`). ### The cases this PR is about - **§25** (anchor): green, including B1 tree/gitlink refusal, B3 zero-byte and deleted-object refusal, B6/B7 `git replace` forgery, A1 mount signal, B2 no-fallback. - **§26** (`suite_relpath` forward-read): 7/7 green. `check`'s `chain_text` recompute call is **byte-identical to #869's**, argument 21 included; `rel_under` is **byte-identical to #859's**. `(b2)`/`(c)` were vacuous on live (the fixture's head was not a commit, so the check stopped at the git gate); the fixture is now a real repo, so the chain is really measured. - **§27** (new, the declaration): **12/12 green** — declared-repo pass with the anchor on the **declared** blob (the fixture also commits the infra-core canonical path with *different* content, so the case proves the declaration wins), declared-path-missing → rc 1, five malformed declarations refused by `check` **and** `run` with **no line written**, class on the declaration, class on the declared suite, `class unknown — assume gate tooling` with an orphan base, FAIL visibility next to the passing verdict, and "the name is not configurable from the environment". - **`T7[diff]`** and **`17[R159] (d)`**: green, with both mutagens biting (`M README.md` + `M tools/gits-sbx-emu-proof.sh`, re-measured for this branch). ### Provenance of the tool at this head ``` #859 anchor machinery: git_blob_sha / git_path_type / git_obj(GIT_NO_REPLACE_OBJECTS=1 + -c core.useReplaceRefs=false) / ancla=smoke@commit -> present (24 hits) #869 suite_relpath: chain_text arg 21 (printf 'suite_relpath=%s') + run writes it + check reads crel_line and passes it as arg 21 -> present rel_under: byte-identical to 9119eb2b:<tool> chain_text recompute: byte-identical to a848b717:<tool> declaration: EMU_PROOF_SUITE_DECL='gits-proof-suite' (fixed name, not env-read) ``` `kustomization.yaml`, `statefulset.yaml` and the smoke are **not touched** (`git diff --stat`: README + tool + docs + harness). The #870 pin still holds the ConfigMap, so this PR does not reach the pod; the delivery is a later pin-bump, after GITS `dev` carries its declaration.
HOMELAB-2682: feat(sbx-gits): drift visibility for declared suites (A1 normalized, anchor exact)
All checks were successful
CI Review / helm-validate (pull_request) Successful in 9s
Alert Disposition Contract / alert-contract (pull_request) Successful in 12s
CI Review / runner-manifest-test (pull_request) Successful in 5s
CI Review / receiver-test (pull_request) Successful in 6s
CI Review / alert-receiver-test (pull_request) Successful in 20s
Lint & Validate / shellcheck (pull_request) Successful in 5s
Lint & Validate / yaml-lint (pull_request) Successful in 20s
CI Review / record-sql-test (pull_request) Successful in 49s
Lint & Validate / terraform-validate (pull_request) Successful in 1m51s
CI Review / cockpit-e2e (pull_request) Successful in 2m27s
CI Review / dispatcher-test (pull_request) Successful in 2m36s
CI Review / cockpit-test (pull_request) Successful in 2m41s
607e582455
GITS-508's two review seats, relayed by homelab-33. Neither changes the resolution order.

1. DRIFT VISIBILITY. A declaration names a PATH with no digest, so the repo's copy and the
   mounted copy can drift unnoticed. The A1 signal already compared the committed blob of the
   canonical suite against the mounted file — for a DECLARED repo that is the declared blob,
   which is what we want — and the check now says the two roles out loud, in a dedicated
   `ANCLA/DERIVA` line (printed on the OK and on the FAIL path): the gate anchors to the
   COMMITTED blob of the declared path in the head, and the comparison with the mounted file is
   INFORMATIVE drift detection, not a criterion.

2. SPDX HEADER. The repo copy carries an SPDX/licence header the mounted copy lacks, so a raw
   comparison always differed — a warning that always fires is noise. The ADVISORY comparison is
   now normalized: leading comment/blank header lines are stripped from BOTH the committed blob
   and the mounted file before hashing, and the flag reads
     IGUAL              bytes are the committed blob's
     IGUAL(sin-cabecera) only the header differs — no content drift
     DISTINTO           the suite's content differs — real drift
     no-legible         the mounted file could not be read
   The GATE anchor is untouched: `git_blob_sha` (exact blob, header included) still decides, and
   `ancla_blob` / the line's `script_sha256` are byte-for-byte what they were.

Documented in the tool's --help, docs/gits-sbx/run-proof.md and README §10.6. Harness case
27 (h) pins both halves at once: a repo whose committed suite carries SPDX with a mounted copy
without it gives rc 0, `ancla_blob` = the EXACT committed blob, `montado=IGUAL(sin-cabecera)`;
one extra line in the usable content gives `montado=DISTINTO` WITHOUT moving the anchor; a
byte-identical mount gives `montado=IGUAL`.
HOMELAB-2682: merge origin/live (#867 volume floors, #868 gradle daemon pair) into the declaration branch
All checks were successful
CI Review / receiver-test (pull_request) Successful in 5s
CI Review / runner-manifest-test (pull_request) Successful in 9s
CI Review / helm-validate (pull_request) Successful in 11s
Lint & Validate / shellcheck (pull_request) Successful in 6s
CI Review / alert-receiver-test (pull_request) Successful in 21s
Lint & Validate / yaml-lint (pull_request) Successful in 23s
CI Review / record-sql-test (pull_request) Successful in 51s
Lint & Validate / terraform-validate (pull_request) Successful in 2m12s
CI Review / dispatcher-test (pull_request) Successful in 2m33s
CI Review / cockpit-test (pull_request) Successful in 2m34s
CI Review / cockpit-e2e (pull_request) Successful in 2m34s
4b747c5c0a
Three conflicts, all in the harness's pinned sbx-gits diff set, resolved by RE-MEASURING it
against the new tip (4197ca48) rather than carrying either side over:

- SBX_GITS_DIFF_SET: this branch's set is `M tools/gits-sbx-emu-proof.sh` + `M README.md`.
  Live's own change (`M tools/gits-sbx-android-install.sh`, the idle pair) is already IN live,
  so it is not an entry here; its superseded assignment is gone and the set's history
  (#869 forward-read, #870 delivery pin, #868 idle pair) stays as prose, which is what that
  block has been doing all along.
- T7[diff] and 17[R159] (d) pass messages: this branch's, since the set they describe is this
  branch's. Verified: `git diff --name-status origin/live -- core/manifests/apps/sbx-gits`
  gives exactly those two entries.
Author
Owner

Design additions folded in (GITS-508's two seats) + final harness record (head 4b747c5c)

Posted with the shared FORGEJO_API_KEY (authorship shows aaron); the text is from the pi
worker session that implemented this PR. Evidence only — NOT a review verdict.

1. Drift visibility for declared repos

A declaration names a path with no digest, so the repo's copy and the mounted copy can drift
unnoticed. The check now prints a dedicated ANCLA/DERIVA line (on the OK and the FAIL path)
that says the two roles out loud: the gate anchors to the COMMITTED blob of the declared path in
the head
, and the comparison with the mounted file is advisory drift detection — informative,
never a criterion. The comparison was already pointed at the declared blob (not the infra-core
canonical path); what was missing was the wording that keeps a reader from confusing the two.

2. SPDX header — the advisory comparison is normalized, the anchor is not

The repo copy carries an SPDX/licence header the mounted copy lacks, so a raw comparison always
differed — a warning that always fires is noise. strip_leading_comments now removes the leading
comment/blank header block from both the committed blob and the mounted file before hashing:

montado= meaning
IGUAL bytes are exactly the committed blob's
IGUAL(sin-cabecera) only the header differs — no content drift
DISTINTO the suite's content differs — real drift
no-legible the mounted file could not be read

The gate anchor is untouched: git_blob_sha (exact blob, header included) still decides, and
ancla_blob / the line's script_sha256 are byte-for-byte what they were — the normalization
touches only the advisory signal. Documented in the tool's --help, docs/gits-sbx/run-proof.md
and README.md §10.6, and pinned by harness case 27 (h): repo with SPDX + mounted copy without
it → rc 0, ancla_blob = the exact committed blob, montado=IGUAL(sin-cabecera); one extra line
in the usable content → montado=DISTINTO without moving the anchor; byte-identical mount →
montado=IGUAL.

Harness, SOLO runs

run tree result
baseline origin/live = 56356d90 FAIL — 53 of 656
this branch, before the live merge 607e5824 FAIL — 7 of 673
this branch, after merging live (#867 + #868) 4b747c5c FAIL — 6 of 673

Zero branch-only failures: the 6 are exactly live's own reds (comm on the two FAIL sets is
empty). T7[diff] + 17[R159] (d): 6 PASS, 0 FAIL (both mutagens bite).
§25/§26/§27: 52 PASS, 0 FAIL.

The one extra red in the middle run — 22[R172c-fix] (n1), the 10-waiter lock stress — was a
load flake, not a regression: the queue/lock scripts are byte-identical to live
(git diff --quiet origin/live on all three), the reported invariant parts were all empty (no
repeated sequence, no mismatched name, last-seq correct — only "corrieron 7 of 10" missed), and
it is green in the final run on the same code.

Merge of live

Live moved under the branch (#867 volume floors, #868 gradle-idle pair). Merged 4197ca48 in; the
three conflicts were all in the harness's pinned sbx-gits set and were resolved by re-measuring
it against the new tip (M tools/gits-sbx-emu-proof.sh + M README.md), with the superseded
M tools/gits-sbx-android-install.sh assignment (already in live via #868) removed and the set's
history kept as prose.

Head: 4b747c5c. Two gate seats still to go before any merge (homelab-33's condition); this PR
does not deploy — the delivery is the later pin-bump.

## Design additions folded in (GITS-508's two seats) + final harness record (head `4b747c5c`) > Posted with the shared `FORGEJO_API_KEY` (authorship shows `aaron`); the text is from the pi > worker session that implemented this PR. Evidence only — NOT a review verdict. ### 1. Drift visibility for declared repos A declaration names a **path with no digest**, so the repo's copy and the mounted copy can drift unnoticed. The check now prints a dedicated `ANCLA/DERIVA` line (on the OK **and** the FAIL path) that says the two roles out loud: **the gate anchors to the COMMITTED blob of the declared path in the head**, and **the comparison with the mounted file is advisory drift detection** — informative, never a criterion. The comparison was already pointed at the declared blob (not the infra-core canonical path); what was missing was the wording that keeps a reader from confusing the two. ### 2. SPDX header — the advisory comparison is normalized, the anchor is not The repo copy carries an SPDX/licence header the mounted copy lacks, so a raw comparison always differed — a warning that always fires is noise. `strip_leading_comments` now removes the leading comment/blank header block from **both** the committed blob and the mounted file before hashing: | `montado=` | meaning | |------------|---------| | `IGUAL` | bytes are exactly the committed blob's | | `IGUAL(sin-cabecera)` | **only** the header differs — no content drift | | `DISTINTO` | the suite's **content** differs — real drift | | `no-legible` | the mounted file could not be read | **The gate anchor is untouched**: `git_blob_sha` (exact blob, header included) still decides, and `ancla_blob` / the line's `script_sha256` are byte-for-byte what they were — the normalization touches only the advisory signal. Documented in the tool's `--help`, `docs/gits-sbx/run-proof.md` and `README.md` §10.6, and pinned by harness case **27 (h)**: repo with SPDX + mounted copy without it → rc 0, `ancla_blob` = the exact committed blob, `montado=IGUAL(sin-cabecera)`; one extra line in the usable content → `montado=DISTINTO` **without moving the anchor**; byte-identical mount → `montado=IGUAL`. ### Harness, SOLO runs | run | tree | result | |-----|------|--------| | baseline | `origin/live` = `56356d90` | **FAIL — 53 of 656** | | this branch, before the live merge | `607e5824` | FAIL — 7 of 673 | | **this branch, after merging live (#867 + #868)** | **`4b747c5c`** | **FAIL — 6 of 673** | **Zero branch-only failures**: the 6 are exactly live's own reds (`comm` on the two FAIL sets is empty). `T7[diff]` + `17[R159] (d)`: **6 PASS, 0 FAIL** (both mutagens bite). §25/§26/§27: **52 PASS, 0 FAIL**. The one extra red in the middle run — `22[R172c-fix] (n1)`, the 10-waiter lock stress — was a **load flake**, not a regression: the queue/lock scripts are **byte-identical to live** (`git diff --quiet origin/live` on all three), the reported invariant parts were all empty (no repeated sequence, no mismatched name, `last-seq` correct — only "corrieron 7 of 10" missed), and it is **green in the final run** on the same code. ### Merge of live Live moved under the branch (#867 volume floors, #868 gradle-idle pair). Merged `4197ca48` in; the three conflicts were all in the harness's pinned sbx-gits set and were resolved by **re-measuring** it against the new tip (`M tools/gits-sbx-emu-proof.sh` + `M README.md`), with the superseded `M tools/gits-sbx-android-install.sh` assignment (already in live via #868) removed and the set's history kept as prose. Head: **`4b747c5c`**. Two gate seats still to go before any merge (homelab-33's condition); this PR does not deploy — the delivery is the later pin-bump.
Collaborator

Verdict of record — ld-reviewer — head 4b747c5c0a: APPROVE
model: deepseek-flash
session: seat2-ds-75943d536293f232-20261106T070626Z

Blockers

None.

Concerns

  • C1 (low) — the class signal, the only control for a declaration that points at a trivial suite, is silenceable from inside the checkout that produces it. Measured on a fixture repo whose declaration was switched to a trivial suite that still writes test XML: the abused head passes (rc 0) with the right line, PROOF GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required (merge-base live..H toca: gits-proof-suite scripts/ci/trivial.sh). Then, same head, same ledger: git branch -f live <H> in the checkout → clase: routine — ni 'gits-proof-suite' ni 'scripts/ci/trivial.sh' cambian entre live y H, rc 0, no class line at all; --base <H> gives the same; check --quiet prints 0 bytes and still returns rc 0, so the class line is gone with the FAIL text. proof_class_base's fallback list contains the bare local names live dev main master behind the origin/* refs, so a stale (or planted) local live is not only a spoofing lever but an innocent false-negative generator. Not a blocker: the class is informational by design (pass/fail is unchanged, homelab-33), the anchor is untouched by any of this, and a declaration rewrite is visible in the PR diff the seats read. Suggest dropping the bare local names from the fallback (remote-tracking only) and, since the class line is now the two-seat hint, not letting --quiet swallow it.
  • C2 (informational, pre-existing) — the 6 harness reds are live's, not this branch's. My solo run: RESULT: FAIL (6 of 673 checks failed, 6 skipped); the 6 are exactly the author's baseline set (probe EMPTY pre-existing --dir, manifest[2], manifest[3], manifest[3b], T7 emulador, T7+R99[10b]). Independently corroborated: statefulset.yaml and kustomization.yaml are byte-identical to origin/live on this head, and none of those six assertions appear in the diff. §25+§26: 39 PASS / 0 FAIL; §27: 13 PASS / 0 FAIL.
  • C3 (nit) — a symlinked declared suite can never anchor, and the message does not say why. git_path_type maps mode 120000 to blob (correct for git), so with suite=scripts/link.sh -> real.sh the anchor is the sha256 of the link target text while run hashes the target's content: the line can never match and the head fails closed with el blob hashea d7b1… ; ninguna linea OK lleva ESE script_sha256. Fail-closed, so no hole — only an opaque dead end if GITS ever symlinks its smoke.

What I measured (unit//tmp git fixtures only; no real PR, pod or emulator)

Declaration games — every one fails closed, no silent acceptance found:

  • suite=../x and suite=scripts/../../x → rc 2 (se SALE del repo); absolute → rc 2; a second suite= → rc 2 (MAS DE UNA VEZ); an unknown key → rc 2 naming the key; empty suite= → rc 2; a zero-byte or comment-only declaration file → rc 2 (NO declara ninguna suite). run refuses the same forms and writes no line.
  • Declared path that is a tree → rc 2 (es un 'tree', NO un blob); a gitlink declaration → rc 2 (es un 'gitlink', no un fichero); a zero-byte declared blob → rc 1 (B3); a declared path absent from H → rc 1 naming both the declaration and the path, with no fallback to the canonical path that the same commit also carries (B2 intact).
  • Declared path is a symlink → rc 1, fail-closed (see C3).
  • H wins over the working tree: after committing suite=scripts/ci/s.sh and then rewriting gits-proof-suite on disk to suite=other/evil.sh (uncommitted), the check still anchors on scripts/ci/s.sh, rc 0 — the declaration is read from the commit, never from disk.
  • CRLF declaration (suite=…\r\n) → parsed, rc 0. suite = <path> with spaces around = and trailing blanks is also accepted (trimmed); permissive, harmless, no path is invented.
  • Paths with a leading ./ or : resolve literally (./ok.sh, :colon.sh → rc 1 "no existe"); the :(top)<path> pathspec survives a :colon.sh suite (checked: the class diff still matched it).

SPDX normalization scope — does not touch the anchor. Committed suite carrying # SPDX… + # Copyright…, mounted copy with the header stripped: rc 0, montado=IGUAL(sin-cabecera), and ancla_blob=1a4f8382c54b… is byte-equal to sha256(cat-file blob H:<declared path>) with the header — the normalization moved the advisory flag only. Changing a comment after the first code line gives montado=DISTINTO (only the leading comment/blank block is stripped), and the ANCLA/DERIVA line keeps saying the anchor is the exact committed blob. (Degenerate: two different comment-only files both normalize to e3b0c442…; advisory only, and a comment-only suite produces no XML and fails on tests_run=0.)

Replace-refs / config lever — still closed on the declared anchor. git replace <H> <F> where F carries a different body at the declared path: rc 0 with the AVISO naming the refs/replace, ancla_blob unchanged (the real blob of H). Adding git config core.useReplaceRefs true to the checkout: identical result — git_obj's -c core.useReplaceRefs=false still wins.

Regressions on the canonical/declared path still bite. record → rc 2, nothing written. B1 before-run: tree_clean=false line → FAIL naming it. B1 during-run: the suite creating an untracked file → tree_clean_after=false. B5/FAIL visibility: green line 1 plus a red run of the same canonical suite on the same head → rc 0 and PROOF aviso el smoke CANONICO TAMBIEN CORRIO EN ROJO … result=FAIL rc=1 printed next to the verdict. B4 attribution, class/results_sha256/suite_relpath optionality and forward-reading are covered by §24–§26 (39 PASS / 0 FAIL). bash -n clean on the tool, the six tools/*.sh and the harness.

Claim honesty. git diff 9119eb2b:<tool> <this head> is +347/-13, and every deleted line is a mount-comparison or declaration line superseded by the normalized advisory comparison — rel_under and the chain_text recompute (argument 21) are present as described. kustomization.yaml, statefulset.yaml and the smoke are untouched, so this PR does not reach the pod: the delivery is a later pin-bump, as stated.

One seat. The two-seat condition of homelab-33 is not satisfied by this comment alone.

**Verdict of record — ld-reviewer — head 4b747c5c0a935e9c5bda6e2e07129bf6244d6e9a: APPROVE** model: deepseek-flash session: seat2-ds-75943d536293f232-20261106T070626Z ## Blockers None. ## Concerns - **C1 (low) — the class signal, the only control for a declaration that points at a trivial suite, is silenceable from inside the checkout that produces it.** Measured on a fixture repo whose declaration was switched to a trivial suite that still writes test XML: the abused head passes (`rc 0`) with the right line, `PROOF GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required (merge-base live..H toca: gits-proof-suite scripts/ci/trivial.sh)`. Then, same head, same ledger: `git branch -f live <H>` in the checkout → `clase: routine — ni 'gits-proof-suite' ni 'scripts/ci/trivial.sh' cambian entre live y H`, `rc 0`, no class line at all; `--base <H>` gives the same; `check --quiet` prints **0 bytes** and still returns rc 0, so the class line is gone with the FAIL text. `proof_class_base`'s fallback list contains the **bare local** names `live dev main master` behind the `origin/*` refs, so a stale (or planted) local `live` is not only a spoofing lever but an innocent false-negative generator. Not a blocker: the class is informational by design (pass/fail is unchanged, homelab-33), the anchor is untouched by any of this, and a declaration rewrite is visible in the PR diff the seats read. Suggest dropping the bare local names from the fallback (remote-tracking only) and, since the class line is now the two-seat hint, not letting `--quiet` swallow it. - **C2 (informational, pre-existing) — the 6 harness reds are live's, not this branch's.** My solo run: `RESULT: FAIL (6 of 673 checks failed, 6 skipped)`; the 6 are exactly the author's baseline set (`probe EMPTY pre-existing --dir`, `manifest[2]`, `manifest[3]`, `manifest[3b]`, `T7 emulador`, `T7+R99[10b]`). Independently corroborated: `statefulset.yaml` and `kustomization.yaml` are byte-identical to `origin/live` on this head, and none of those six assertions appear in the diff. §25+§26: 39 PASS / 0 FAIL; §27: 13 PASS / 0 FAIL. - **C3 (nit) — a symlinked declared suite can never anchor, and the message does not say why.** `git_path_type` maps mode `120000` to `blob` (correct for git), so with `suite=scripts/link.sh -> real.sh` the anchor is the sha256 of the *link target text* while `run` hashes the *target's* content: the line can never match and the head fails closed with `el blob hashea d7b1… ; ninguna linea OK lleva ESE script_sha256`. Fail-closed, so no hole — only an opaque dead end if GITS ever symlinks its smoke. ## What I measured (unit//tmp git fixtures only; no real PR, pod or emulator) **Declaration games — every one fails closed**, no silent acceptance found: - `suite=../x` and `suite=scripts/../../x` → rc 2 (`se SALE del repo`); absolute → rc 2; a second `suite=` → rc 2 (`MAS DE UNA VEZ`); an unknown key → rc 2 naming the key; empty `suite=` → rc 2; a **zero-byte** or comment-only declaration file → rc 2 (`NO declara ninguna suite`). `run` refuses the same forms and writes no line. - Declared path that is a **tree** → rc 2 (`es un 'tree', NO un blob`); a **gitlink** declaration → rc 2 (`es un 'gitlink', no un fichero`); a **zero-byte** declared blob → rc 1 (B3); a declared path absent from H → rc 1 naming both the declaration and the path, with **no fallback** to the canonical path that the same commit also carries (B2 intact). - **Declared path is a symlink** → rc 1, fail-closed (see C3). - **H wins over the working tree**: after committing `suite=scripts/ci/s.sh` and then rewriting `gits-proof-suite` on disk to `suite=other/evil.sh` (uncommitted), the check still anchors on `scripts/ci/s.sh`, `rc 0` — the declaration is read from the commit, never from disk. - **CRLF** declaration (`suite=…\r\n`) → parsed, rc 0. `suite = <path>` with spaces around `=` and trailing blanks is also accepted (trimmed); permissive, harmless, no path is invented. - Paths with a leading `./` or `:` resolve literally (`./ok.sh`, `:colon.sh` → rc 1 "no existe"); the `:(top)<path>` pathspec survives a `:colon.sh` suite (checked: the class diff still matched it). **SPDX normalization scope — does not touch the anchor.** Committed suite carrying `# SPDX…` + `# Copyright…`, mounted copy with the header stripped: `rc 0`, `montado=IGUAL(sin-cabecera)`, and `ancla_blob=1a4f8382c54b…` is byte-equal to `sha256(cat-file blob H:<declared path>)` **with the header** — the normalization moved the advisory flag only. Changing a comment **after** the first code line gives `montado=DISTINTO` (only the *leading* comment/blank block is stripped), and the `ANCLA/DERIVA` line keeps saying the anchor is the exact committed blob. (Degenerate: two different comment-only files both normalize to `e3b0c442…`; advisory only, and a comment-only suite produces no XML and fails on `tests_run=0`.) **Replace-refs / config lever — still closed on the declared anchor.** `git replace <H> <F>` where `F` carries a *different* body at the declared path: `rc 0` with the AVISO naming the refs/replace, `ancla_blob` unchanged (the real blob of H). Adding `git config core.useReplaceRefs true` to the checkout: identical result — `git_obj`'s `-c core.useReplaceRefs=false` still wins. **Regressions on the canonical/declared path still bite.** `record` → rc 2, nothing written. B1 before-run: `tree_clean=false` line → FAIL naming it. B1 during-run: the suite creating an untracked file → `tree_clean_after=false`. B5/FAIL visibility: green line 1 plus a red run of the **same** canonical suite on the same head → `rc 0` and `PROOF aviso el smoke CANONICO TAMBIEN CORRIO EN ROJO … result=FAIL rc=1` printed next to the verdict. B4 attribution, `class`/`results_sha256`/`suite_relpath` optionality and forward-reading are covered by §24–§26 (39 PASS / 0 FAIL). `bash -n` clean on the tool, the six `tools/*.sh` and the harness. **Claim honesty.** `git diff 9119eb2b:<tool> <this head>` is `+347/-13`, and every deleted line is a mount-comparison or declaration line superseded by the normalized advisory comparison — `rel_under` and the `chain_text` recompute (argument 21) are present as described. `kustomization.yaml`, `statefulset.yaml` and the smoke are untouched, so this PR does not reach the pod: the delivery is a later pin-bump, as stated. One seat. The two-seat condition of homelab-33 is not satisfied by this comment alone.
Collaborator

Verdict of record — ld-reviewer — head 4b747c5c0a: APPROVE
model: deepseek-flash
session: 01a1afa2-f413-757a-b84d-27ea75679d35

Independent review seat 1 of 2 (gate tooling). Reviewed from primary sources only: the diff at the head, the tool at the head executed against throwaway git fixtures (unit//tmp), and the two frozen predecessors (git show 9119eb2b:…, git show a848b717:…), plus a read-only pull of the live pod ledger for the cross-era fixture. Not dependent on seat 2.

Blockers

None.

Provenance — verified, not taken on trust

  • Live's tool file at origin/live (4197ca48) is byte-identical to a848b717:… (#869), and the head file is #859's content (9119eb2b:…, 1623 lines) plus additions: diff 859 → head is 477 lines and every hunk is declaration / class / SPDX-normalization text — no removal, no behavioural edit of the anchor.
  • rel_under is byte-identical to #859's (8/8 lines).
  • chain_text body is functionally identical to #859's — the only change is the added comment block; field 21 (suite_relpath) was already emitted by #859 at exactly that position, which is why both eras read each other.
  • The check recompute call (the chain_text invocation carrying arguments … "$cclass" "$results_sha_line" "$crel_line") is byte-identical across 859, 869 and head. That is the line that decides whether cross-era lines recompute; it did not drift.

Declaration semantics — verified by execution

Resolution order is exactly declared → canonical → fail closed, with no fourth path:

  • Fixture carrying both the declaration (suite=sui/smoke.sh) and infra-core's canonical path with different content: verdict OK, smoke_rel=sui/smoke.sh, suite_sha256 = sha256 of the declared blob, and the canonical blob's sha appears nowhere. The declaration wins.
  • No declaration + no canonical path: rc 1, message names gits-proof-suite as the missing declaration, no PROOF: OK line.
  • Declaration present but its named path absent from H: rc 1, fail closed, message names both the declaration and the missing path.
  • Malformed declarations refused as rc 2 by check and by run, with zero lines written: unknown key (owner=), a non-suite= key, two suite= lines, empty suite=, absolute path, .. escape, and a non-key=value line. Seven for seven.
  • The anchor for a declared path keeps ALL of #859's machinery: declared path that is a tree → refused ("NO un blob"); a gitlink → refused and named gitlink; a zero-byte blob → refused on the byte accounting (no empty-string sha256 as anchor); and replace-proof: with git replace H F plus git config core.useReplaceRefs true in the checkout, the check still reads the real blob, prints the real sha, and never emits the forged blob's sha.

Class output — fires correctly and never moves the verdict

  • Head that touches gits-proof-suite (comment-only edit): PROOF GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required, verdict OK, rc 0.
  • Head that touches only the declared suite: same class line (it names both paths), verdict unchanged.
  • Routine head (neither path): clase: routine, no gate line, verdict unchanged.
  • No usable base (repo whose only branch is trunk, no origin, nothing to fetch): GATE-TOOLING CLASS: class unknown — assume gate tooling, verdict unchanged.
  • The line is printed on both the OK and the FAIL path (say before the ledger read, and check_report still emits its reasons). It is informational only — nothing in ok_line/gate depends on it.

SPDX normalization — advisory only, gate hash untouched

  • Committed suite carrying an SPDX/licence/copyright header, mounted copy without it: rc 0, montado=IGUAL(sin-cabecera), and ancla_blob= / suite_sha256= still the exact committed blob (header included).
  • Mounted copy with a real content difference: montado=DISTINTO.
  • A header-only edit of the committed blob does not pass: a new head whose only change is one extra comment line at the top of the committed suite, checked against a ledger line carrying the previous blob's sha256, is rc 1 (no PROOF: OK). The anchor still includes the header — the normalization touches only the comparison.

Cross-era ledger — the pod's real ledger as the fixture

Pulled read-only (/home/ld/work/.emu-proof/ledger.jsonl, 38 lines) and recomputed every line with the head tool's own chain_text/sha256_text: 38/38 recompute hash and prev-chain, mixing eras 18 fields, 20 fields (results_sha256, 8 lines) and 21 fields (suite_relpath, lines 33/36/37/38 — the #859-written one included). Nothing in the format change breaks the chain for any era.

Harness (SOLO, this branch vs a live baseline, same env/refs)

  • Branch 4b747c5c: 667 PASS / 6 FAIL / 6 SKIP (673 checks).
  • origin/live baseline, same invocation: 602 PASS / 53 FAIL / 8 SKIP (655 checks) — the ~54 baseline reds the brief warns about.
  • The branch's 6 reds are a subset of live's: no new red anywhere. They are the pre-existing ones (the manifest[2]/[3]/[3b] memory+image pins, the T7 emulator resource pin, 10b[R99] documented as deliberate in README §10.5, and one unrelated probe). The branch additionally turns 47 live reds green — mostly the §22[R176b]/§25/§26 cases that were red on live because live's tool is the pre-859 one without --tree and the rest of the anchor machinery.
  • New section 27 is present with 13 cases and all pass: declared-repo pass on the declared blob, declared-path-missing rc 1, the malformed-declaration set refused by check AND run, class on the declaration, class on the declared suite, class unknown, FAIL visibility beside the passing verdict, the name not being environment-configurable, and three doc invariants. §25/§26: all pass, 52 PASS / 0 FAIL across §25/§26/§27.

Scope / pin

Files: tools/gits-sbx-emu-proof.sh, README.md (§10.6), docs/gits-sbx/run-proof.md, scripts/gits-sbx-epic-test.sh. kustomization.yaml and statefulset.yaml untouched — merging deploys nothing, as the #870 pin intends.

Concerns

  1. Non-blocking, out of the settled format: the class pathspec is gits-proof-suite + the named suite only. A head that rewrites the proof tool itself (tools/gits-sbx-emu-proof.sh) and nothing else reads clase: routine by that rule — this very PR is such a head. The settled format (homelab-33) says "declaration or its named suite", so the tool is behaving as specified; worth considering in a follow-up whether the tool belongs in that pathspec.
  2. Cosmetic: strip_leading_comments also strips the shebang (it is a # line at the top), so a mounted copy that lost or changed #!/usr/bin/env bash reads as no drift. Advisory only — the gate hash is unaffected — but the signal's name ("sin-cabecera") slightly overstates what was normalized.
  3. Cosmetic: a zero-byte declared suite exits rc 1 with a message rather than rc 2 (usage-class). The outcome is fail-closed and correctly worded; only the class differs from the other hard anchor rejections.
**Verdict of record — ld-reviewer — head 4b747c5c0a935e9c5bda6e2e07129bf6244d6e9a: APPROVE** model: deepseek-flash session: 01a1afa2-f413-757a-b84d-27ea75679d35 Independent review seat 1 of 2 (gate tooling). Reviewed from primary sources only: the diff at the head, the tool at the head executed against throwaway git fixtures (unit//tmp), and the two frozen predecessors (`git show 9119eb2b:…`, `git show a848b717:…`), plus a read-only pull of the live pod ledger for the cross-era fixture. Not dependent on seat 2. ## Blockers None. ## Provenance — verified, not taken on trust - Live's tool file at `origin/live` (4197ca48) is **byte-identical** to `a848b717:…` (#869), and the head file is #859's content (`9119eb2b:…`, 1623 lines) plus additions: `diff 859 → head` is 477 lines and **every** hunk is declaration / class / SPDX-normalization text — no removal, no behavioural edit of the anchor. - `rel_under` is byte-identical to #859's (8/8 lines). - `chain_text` body is **functionally identical** to #859's — the only change is the added comment block; field 21 (`suite_relpath`) was already emitted by #859 at exactly that position, which is why both eras read each other. - The `check` recompute call (the `chain_text` invocation carrying arguments `… "$cclass" "$results_sha_line" "$crel_line"`) is **byte-identical** across 859, 869 and head. That is the line that decides whether cross-era lines recompute; it did not drift. ## Declaration semantics — verified by execution Resolution order is exactly declared → canonical → fail closed, with no fourth path: - Fixture carrying **both** the declaration (`suite=sui/smoke.sh`) and infra-core's canonical path with *different* content: verdict OK, `smoke_rel=sui/smoke.sh`, `suite_sha256` = sha256 of the **declared** blob, and the canonical blob's sha appears nowhere. The declaration wins. - No declaration + no canonical path: **rc 1**, message names `gits-proof-suite` as the missing declaration, no `PROOF: OK` line. - Declaration present but its named path absent from H: **rc 1**, fail closed, message names both the declaration and the missing path. - Malformed declarations refused as **rc 2** by `check` **and** by `run`, with **zero lines written**: unknown key (`owner=`), a non-`suite=` key, two `suite=` lines, empty `suite=`, absolute path, `..` escape, and a non-`key=value` line. Seven for seven. - The anchor for a declared path keeps ALL of #859's machinery: declared path that is a **tree** → refused ("NO un blob"); a **gitlink** → refused and named `gitlink`; a **zero-byte** blob → refused on the byte accounting (no empty-string sha256 as anchor); and **replace-proof**: with `git replace H F` plus `git config core.useReplaceRefs true` in the checkout, the check still reads the real blob, prints the real sha, and never emits the forged blob's sha. ## Class output — fires correctly and never moves the verdict - Head that touches `gits-proof-suite` (comment-only edit): `PROOF GATE-TOOLING CLASS: this change touches the proof declaration or its named suite — two review seats required`, **verdict OK, rc 0**. - Head that touches only the **declared suite**: same class line (it names both paths), verdict unchanged. - Routine head (neither path): `clase: routine`, no gate line, verdict unchanged. - No usable base (repo whose only branch is `trunk`, no origin, nothing to fetch): `GATE-TOOLING CLASS: class unknown — assume gate tooling`, verdict unchanged. - The line is printed on both the OK and the FAIL path (`say` before the ledger read, and `check_report` still emits its reasons). It is informational only — nothing in `ok_line`/`gate` depends on it. ## SPDX normalization — advisory only, gate hash untouched - Committed suite carrying an SPDX/licence/copyright header, mounted copy without it: rc 0, `montado=IGUAL(sin-cabecera)`, and `ancla_blob=` / `suite_sha256=` still the **exact committed blob** (header included). - Mounted copy with a real content difference: `montado=DISTINTO`. - **A header-only edit of the committed blob does not pass**: a new head whose only change is one extra comment line at the top of the committed suite, checked against a ledger line carrying the previous blob's sha256, is rc 1 (no `PROOF: OK`). The anchor still includes the header — the normalization touches only the comparison. ## Cross-era ledger — the pod's real ledger as the fixture Pulled read-only (`/home/ld/work/.emu-proof/ledger.jsonl`, 38 lines) and recomputed every line with the head tool's own `chain_text`/`sha256_text`: **38/38 recompute hash and prev-chain**, mixing eras 18 fields, 20 fields (`results_sha256`, 8 lines) and 21 fields (`suite_relpath`, lines 33/36/37/38 — the #859-written one included). Nothing in the format change breaks the chain for any era. ## Harness (SOLO, this branch vs a live baseline, same env/refs) - Branch 4b747c5c: **667 PASS / 6 FAIL / 6 SKIP (673 checks)**. - `origin/live` baseline, same invocation: **602 PASS / 53 FAIL / 8 SKIP (655 checks)** — the ~54 baseline reds the brief warns about. - The branch's 6 reds are a **subset** of live's: no new red anywhere. They are the pre-existing ones (the `manifest[2]/[3]/[3b]` memory+image pins, the T7 emulator resource pin, `10b[R99]` documented as deliberate in README §10.5, and one unrelated probe). The branch additionally turns 47 live reds green — mostly the §22[R176b]/§25/§26 cases that were red on live because live's tool is the pre-859 one without `--tree` and the rest of the anchor machinery. - New section 27 is present with 13 cases and **all pass**: declared-repo pass on the declared blob, declared-path-missing rc 1, the malformed-declaration set refused by check AND run, class on the declaration, class on the declared suite, `class unknown`, FAIL visibility beside the passing verdict, the name not being environment-configurable, and three doc invariants. §25/§26: all pass, 52 PASS / 0 FAIL across §25/§26/§27. ## Scope / pin Files: `tools/gits-sbx-emu-proof.sh`, `README.md` (§10.6), `docs/gits-sbx/run-proof.md`, `scripts/gits-sbx-epic-test.sh`. `kustomization.yaml` and `statefulset.yaml` untouched — merging deploys nothing, as the #870 pin intends. ## Concerns 1. **Non-blocking, out of the settled format:** the class pathspec is `gits-proof-suite` + the named suite only. A head that rewrites the **proof tool itself** (`tools/gits-sbx-emu-proof.sh`) and nothing else reads `clase: routine` by that rule — this very PR is such a head. The settled format (homelab-33) says "declaration or its named suite", so the tool is behaving as specified; worth considering in a follow-up whether the tool belongs in that pathspec. 2. **Cosmetic:** `strip_leading_comments` also strips the shebang (it is a `#` line at the top), so a mounted copy that lost or changed `#!/usr/bin/env bash` reads as no drift. Advisory only — the gate hash is unaffected — but the signal's name ("sin-cabecera") slightly overstates what was normalized. 3. **Cosmetic:** a zero-byte declared suite exits rc 1 with a message rather than rc 2 (usage-class). The outcome is fail-closed and correctly worded; only the class differs from the other hard anchor rejections.
Sign in to join this conversation.
No reviewers
No labels
operator-review
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Homelab/infra-core!871
No description provided.