feat(tri): gates quiet -- the steps whose pass survives the subject going missing - #3049
Conversation
…oing missing
`grep` exits 0 for a match, 1 for no match, and 2 for no such file. Those
are three answers. `2>/dev/null` deletes the message and an `||` arm
deletes the exit code, and what survives is one bit: clean. A missing
subject and a clean subject then print the same characters.
`tri gates quiet` walks the workflows and reports the shapes in which
that happens: 49 files read, 32 steps in a quiet shape -- 16 whose
failure branch passes, 9 counters that read zero, 7 gated on the file
existing. The counter is the same defect in a different costume:
`$(grep -r "^Admitted" *.v 2>/dev/null | wc -l)` reads 0 from a directory
with no proofs, and 0 is the number a clean tree prints. `[ -f X ]` is
reported apart because it is often exactly right; what separates it from
the defect is whether the output NAMES what it read, and nothing here
does.
The reading that matters is not the count of shapes. Of the 32, exactly
ONE names a tracked path -- phi-loop-ci.yml:30, subject ffi/src/, on disk
today -- so no gate here is currently guarding nothing, which is said
plainly. Twenty-two name no path at all, and that is the harder finding:
a step that does not say what it read cannot be checked by this tool, by
a reader, or by the next person to rename something.
The first version of this command reported 25 of 32 subjects missing. It
was wrong, and the way it was wrong is worth more than the number: four
different answers had been collapsed into one. No path on the line (22 --
the tool cannot say anything, and cannot say is not is missing); the run
builds it (build/fpga/synth/synth.log is absent from a checkout because
the workflow creates it later); a variable in the path
(specs/fpga/${m}.v); and one the tool invented outright -- `subject_of`
took the first token carrying a `/`, so from an inline python one-liner
it returned `json;print(len(json.load(open('/tmp/r.json'))['checks']` and
reported that as a tracked path that is missing.
With the four separated, the honest count of tracked paths missing today
is 0.
A detector that cannot distinguish its own ignorance from a finding will
always find something, and 25 of 32 is 78% -- just under the 80% line, so
the ratio alone would not have caught it. What caught it was reading the
list: the first row was python source. Section 464 for the third time --
the list is the check. `--list` prints every step counted and `--excluded`
every line refused.
Eight clauses mutated, eight killed. The `exit 1` check in the failure
arm survived until a test was written for the one input it discriminates:
`|| echo "FAILED" && exit 1`, an arm that starts by echoing and then
fails.
Skill sections 471 and 472.
Refs #2994
|
📓 NotebookLM Notebook linked to this PR
This notebook contains session context, decisions, and artifacts for this work. |
Ran
|
| gate | reads the tree? | verdict |
|---|---|---|
scripts/ci/test_untrusted_javascript_payloads.py |
no (0 tree-reading calls) | fine — self-contained fixtures, and it prints Scope: JavaScript source in actions/github-script. Shell has a separate checker |
scripts/ci/test_untrusted_payloads.py |
no | fine — prints Scope: this tests the two shell forms, not the live workflow. It cannot show that no other step interpolates untrusted data |
tools/gft_backprop_microcode.py |
no | fine — three named self-tests with their own data |
tools/pack_index_consistency_gate.py --selftest |
no | fine — ends selftest OK: the gate is falsifiable |
tools/check_conflict_markers.py |
yes (5 calls) | fine — prints its population: tracked files read 7741 here, tracked files read 0 against an empty tree I built to check |
So the discriminator that separates "correctly independent of the tree" from "blind to the tree" is not whether it passes over nothing. It is whether the output says what it examined — either a declared scope, or a printed population size. All five do one or the other.
That is the same shape as this PR's own §472, one turn later: 25 of 32 became 0 once the categories were separated, and 5 of 5 here are legitimate once the question is "does it name its subject" rather than "did it pass".
Suggestion, cheap: gates empty could print that column itself — for each invocation, whether the script contains any tree-reading call (glob/walk/listdir/rglob/git ls-files). It would have separated 4 from 1 without anyone reading five files, and it would keep the next reader from re-doing this pass. Happy to send it as a patch if you would rather not have it in your branch.
Two notes while I was in there. gates quiet and gates empty between them cover ground I had a read-only fan-out scanning at the same time — I have stopped counting workflow steps and will subtract your output from mine rather than report both. And #3054 (open) is the same class one layer down in the shell: scripts/tri exited 1 for "t27c is not built", which .githooks/pre-commit turned into a refused commit whose message named a build step. It exits 2 there now, with tools/check_now_entry_shape.py as the house precedent for that code.
…3066) * tri topic: has someone already done this, or are they doing it now? Closes #3065 Twice in one session I started work another session had shipped or had open. #3049 shipped `tri gates quiet` and `tri gates empty`, which cover two of the four lenses a 29-agent fan-out of mine was scanning at the same moment. #3056 shipped `tri skill refs` -- every cross-reference and whether it resolves -- which is the tool for a dangling pointer I had just written a paragraph about. The first was an OPEN PULL REQUEST the whole time I was scanning. The second was a merged commit. Neither was hard to find afterwards; both were free to find before, and the reason I did not is that it takes four commands and none of them is habit. So: one command over open PRs, open issues, the last N commits on the base branch, and every SKILL.md section title. A row matches on any keyword and rows are ordered by how many DISTINCT keywords they carry -- ranking by occurrences would put a title repeating one word above one carrying two. Verified retroactively on both collisions: each comes out at the top of its own query. It prints the DISTRIBUTION rather than applying a threshold. 468 of 694 on a three-word query is not a result, and a cutoff would need a number nobody has measured -- which is the mistake that produced a three-state column with no correct members earlier the same day. It refuses when `gh` cannot answer, and says so in the words this repository uses. "Nobody else is working on this" and "I could not ask" are the same empty list, which is the defect the last five issues here have all been about. Four mutants, four kills, each by exactly one test, with passed+failed asserted against the expected six every time -- because `cargo test topic` also matches `issues::tests::the_matcher_reads_the_claim_and_not_the_topic`, and a filter that catches somebody else's test is how a mutant reads as surviving. Stated in the module rather than left to be found: it reads titles, not bodies or diffs, so a pull request whose title does not name its subject is invisible here, and the ordering is a word count, not an understanding. * skill: a verification that only prints is a suggestion Refs #3065 The section this session earned twice over. My own check printed OK=False -- 442 sections where 440 were expected -- and I pushed. The file had three of master's sections duplicated. The tool was handed the wrong input, which is ordinary; the lesson is that the guard which caught it reached nobody because its answer was a line of text rather than an exit code the push was downstream of. Two smaller versions the same hour, both caught because the guard sat in the right place: a stale binary answered with its usage text (the merge was resolved but never concluded, so the binary predated the code), and a completed merge still did not have origin/master as an ancestor because master moved underneath it. The section is placed by the command it sits next to, so its number came from tri skill renumber rather than by hand -- and this commit's own check gates the push on exit code, which is the thing it is about. * topic: refuse a keyword carrying spaces Refs #3065 Found on this command's SECOND use, by me. `for q in "a b c"; do tri topic $q; done` in zsh does not word-split, so the whole phrase arrived as one keyword, matched nothing, and the command answered `rows matching 0` -- which reads as "nobody else is working on this". That is the exact sentence this command exists to keep from being said wrongly, produced by its own input handling, four queries in a row, and I believed all four until I re-ran one I knew had matched. Refused rather than answered, with the split spelled out. A single word padded with spaces is not a quoting mistake and is not flagged -- mutating the guard to `k.contains` instead of `k.trim().contains` kills that assertion. Prior art, checked rather than assumed: pytest reserves exit code 5 for "No tests were collected" as a public-API outcome distinct from 1 (tests failed), 2 (interrupted), 3 (internal error) and 4 (usage error). This repository's convention -- 2 for everything that is not a reading -- is coarser than the field's by one distinction: "the instrument is missing" and "the population is empty" are the same code here and different codes there.
grephas three answers and the chain keeps onegrepexits 0 for a match, 1 for no match, and 2 for no such file. Those are three answers.2>/dev/nulldeletes the message and an||arm deletes the exit code, and what survives is one bit: clean. A missing subject and a clean subject then print the same characters.The counter is the same defect in a different costume:
ADMISSIONS=$(grep -r "^Admitted" *.v 2>/dev/null | wc -l)reads 0 from a directory with no proofs in it, and 0 is the number a clean tree prints.[ -f X ]is listed apart because it is often exactly right — a step that legitimately has nothing to do should not fail. What separates it from the defect is whether the output NAMES what it read, and nothing here does.The reading that matters is not the count of shapes. Of the 32, exactly one names a tracked path:
phi-loop-ci.yml:30, whose subjectffi/src/is on disk today. So no gate here is currently guarding nothing — said plainly, because it is a result. Twenty-two name no path at all, and that is the harder finding: a step that does not say what it read cannot be checked by this tool, by a reader, or by the next person to rename something."Cannot check" is not "absent"
The first version reported 25 of 32 subjects missing. It was wrong, and the way it was wrong is worth more than the number. Four different answers had been collapsed into one:
build/fpga/synth/synth.logis absent from a checkout because the workflow creates it laterspecs/fpga/${m}.vnames a different file per runThat last one:
subject_oftook the first token carrying a/, so from an inline python one-liner it returnedjson;print(len(json.load(open('/tmp/r.json'))['checks']and reported that as a tracked path that is missing. Punctuation which cannot appear in a path —(,),;,=,,— now rules the token out.With the four separated, the honest count is 0.
A detector that cannot distinguish its own ignorance from a finding will always find something. 25 of 32 is 78% — just under the 80% line at which this file calls a matcher one that describes its input, so the ratio alone would not have caught it. What caught it was reading the list: the first row was python source.
§464 for the third time — the list is the check.
--listprints every step counted and--excludedevery line refused, because a census that prints only its totals cannot be argued with, and this one was wrong in its totals while every total looked plausible.exit 1check in the failure arm survived until a test was written for the one input it discriminates:|| echo "FAILED" && exit 1, an arm that starts by echoing and then failscargo test -p tri— 542 passed, 0 failedRefs #2994