Files
jason.woltje 158205c5cc lead: commit darkwing's untracked evidence (348 files: queue-137 queue-141 queue-150 queue-162 queue-165 queue-166 queue-167 queue-169 queue-179 queue-180 queue-181 queue-182 queue-185 queue-186 queue-187 queue-188 queue-190 queue-93)
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.
2026-11-02 15:05:14 -06:00

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.