Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
93 lines
6.1 KiB
Markdown
93 lines
6.1 KiB
Markdown
# Row 188: stack-release tests for a carried proof pair and a proof file inside the repository
|
|
|
|
Brief: `docs/plans/2026-10-20_proof-test-gaps.md` (on origin/refactor 525990ecb). Gitea #1686, P19 409 (M8) and
|
|
410 (M9), Filbert's N1 and N2 on row 187 (#1685 comment 29424). Owner Darkwing, reviewer Filbert, gate owner
|
|
Sage. Mos ruled this row lands before the Part B window runs (mos-0956).
|
|
|
|
## Files
|
|
|
|
| File | What |
|
|
|---|---|
|
|
| `build.patch` | the change, `git diff 525990ecb` over the one path below |
|
|
| `candidate-manifest.sha256` | one line for that path |
|
|
|
|
- Base: 525990ecb. The brief names 742e98976, and the three commits above it touch only docs and the queue.
|
|
- Changed: `packages/cli/tests/stack-release.test.mjs` only. `scripts/stack-release.sh` is untouched, and
|
|
its blob is still `2586a2a37cd2e7c7aeaba6d31490e5b7920fb4b2`, the one the Part B window file names.
|
|
`scripts/webui-service.sh`, `docs/TOOLS.md` and `packages/webui` are untouched too. P19 407 and 408 are
|
|
not in this row.
|
|
|
|
Script line numbers below are at 525990ecb, which match the brief's (same blob).
|
|
|
|
## The tests
|
|
|
|
One new top-level test, `T54 row 188`, after T53, with six subtests. Each one makes its own stand-in with
|
|
`mk` and the preview flags with `seedPreview`, and ends with `noLeak`. That check fails on any open or read of
|
|
a watched proof file, and on SENTINEL in any output, unit, pointer, log or steps log.
|
|
|
|
| Subtest | Brief test | Setup | What it pins |
|
|
|---|---|---|---|
|
|
| test 1, chmod 644 | 1 | activate B with the pair, then chmod the file 644 | `--plan` for `activate C1` lists `proof: carried` and exactly one refusal, `proof-file-invalid: ... has mode 644, not 600 or 400`; the run refuses the same before anything stops; the pointer stays on B with the pair |
|
|
| test 1, symlink | 1 | same, then the path becomes a symlink to a watched mode-600 file | the same, with `... is a symlink` |
|
|
| test 1, removal | 1 | same, then the path is removed | the same, with `... cannot be found` |
|
|
| test 2 | 2 | activate C1 with the pair, then chmod 644 | `--plan` for `rollback` (to B, whose Console takes the pair) lists `proof: carried` and the one refusal; the run refuses before anything stops; the pointer stays on C1 with the pair |
|
|
| test 3 | 3 | a mode-600 file of the test's uid at `<repo>/proof-r188/proof.188`, with the pair on `activate C1` | the plan and the run refuse `... is inside the repository or the copies` |
|
|
| test 4 | 4 | `<e.root>/repo-link.188`, a directory symlink to `<repo>/proof-r188`, with the file given through it | the test asserts that the given path's last part is not a symlink and the path is outside REPO; the plan and the run refuse on its realpath with the same reason |
|
|
|
|
"The run refuses before anything stops" is the existing `refused` helper (:881 of the test file): exit 3,
|
|
the exact message, no unit, wrapper, pointer or store change, no start or stop, and one log line with
|
|
`result: refused`.
|
|
|
|
The other change to the test file: `proofFile` takes an optional fourth argument, the directory, with the old
|
|
`e.root` as its default, so tests 3 and 4 can put a watched file inside the fixture REPO. Each of those
|
|
subtests removes its `proof-r188` directory in `t.after`. `linkSync` joins the `node:fs` import.
|
|
|
|
The uid case is out of reach without root, as the brief says, and has no test.
|
|
|
|
### Readings of the brief
|
|
|
|
1. "No log line" in brief test 1. A precheck refusal in a real run writes one log line, `result: refused`
|
|
with the code, as every refusal does since the log began (T53 test 5 and every other `refused` case pin
|
|
it). I read the brief as no activation line, which `refused` pins: one line, `refused`, and no unit or
|
|
pointer change. Changing that would need a script change, which this row can't make.
|
|
2. Removal and the symlink swap keep a hard link to the old file under `e.root` before they unlink its path.
|
|
The read watch is on the inode. When a watched inode is freed, inotify sends `IN_IGNORED`, which no mask
|
|
filters out, and the watch counts it as an event. A probe of the T53 watch counted 1 after a plain unlink and 0
|
|
with a hard link kept. The script sees the same thing either way: the path is gone, or it is a symlink.
|
|
|
|
## Results
|
|
|
|
All runs are from `~/darkwing-scratch/r188/run188.sh`, in a git worktree of 525990ecb plus the candidate, with
|
|
TMPDIR under `~/darkwing-scratch`. The runs took 10:38:55Z-10:46:50Z.
|
|
|
|
- The whole `stack-release.test.mjs` passes 151/151 (144 before this row, plus T54 and its six subtests).
|
|
- Control: on the unchanged script (blob 2586a2a37cd2), T53 and T54 pass 19/19. The brief's base control is
|
|
this run. The row changes no script, so base and candidate run the same script, and the new tests must pass
|
|
on it.
|
|
- Mutants. The runner runs T53 and T54 under each one, then puts the script back and checks its sha256. The
|
|
restore matched every time, and the worktree script is blob 2586a2a37cd2 after the run.
|
|
|
|
| Mutant | Replacement | Fails | Failing subtests |
|
|
|---|---|---|---|
|
|
| M8 | `return 0` before the `fi` that ends the carried branch (:738) | 5 | T54 test 1 (all three changes) and test 2 |
|
|
| M9 | `"$REPO" \| "$REPO"/* \| ` dropped from the realpath case (:775) | 3 | T54 tests 3 and 4 |
|
|
| M10 | `why=""; [ -z "$PROOF_HEADER" ] \|\| why="$(proof_file_problem "$2")"` (:759) | 5 | T54 test 1 (all three changes) and test 2 |
|
|
|
|
The fail counts include the T54 parent. Each failing subtest stops at its first `--plan` (exit 0, not 3).
|
|
T53 passes under all three mutants, which matches Filbert's finding that M8 and M9 survived it. The new
|
|
tests are what kill them.
|
|
|
|
## Candidate
|
|
|
|
- `candidate-manifest.sha256` (sha256 86aa549bad75...):
|
|
`d38a2f9d6e4ece57907e95b9c24601ec823cfab8d22ab783a8ee64c79104100b packages/cli/tests/stack-release.test.mjs`
|
|
- `build.patch` (sha256 db7e41b2ba76..., 85 lines) applies on 525990ecb, and `sha256sum -c` of the manifest
|
|
passes on the result.
|
|
|
|
## For Sage's gate
|
|
|
|
- The one changed path is `packages/cli/tests/stack-release.test.mjs`. No test or script reads it as a file
|
|
(`grep -rln stack-release.test packages/*/tests scripts/test-*.sh` finds nothing), and no file pins the
|
|
base blob's sha256 (95e44c2f...).
|
|
- Run the cli suite and every `scripts/test-*.sh` from a git worktree of the candidate, as the brief says.
|