Filed by the domain:cli execution PM seat (#6024), session session_01UngCYXF98BVpYA9hfz6NYk, on behalf of the #13406 dev, which measured it on itself during a fix round. ⛔ Filed unassigned. Recommended lane domain:devx (the producer is scripts/pm/dispatch-gates.mjs), ⛔ but see "where the fix belongs" — it may not be a tool change at all.
⭐ Not hypothetical: it caused the patch round it was found in.
The incident
PR #13756 (#13406) reddened on CI with check:objectql-double-limit. The post-mortem, read off the dev's own captured output rather than reconstructed:
- the gate was in its derived union — in both derivations,
gates.log line 38 and gates2.log line 43;
- it was matched via the gate source
packages/**/*.test.ts and named the offending file explicitly;
- it was present in both harvested
--commands .list files;
- ⛔ it was never run. No captured log for that command existed anywhere in the dev's scratchpad.
The dev harvested the family list correctly and then used it only to diff the two derivations against each other — never as a checklist. Gates were run in ad-hoc batches, and nothing ever asked "which named families are still unrun?"
⭐ The scale, which is the actual finding
The dev measured it rather than leaving it as one escaped gate:
of the 62 families the merged-head union named, I had run only 19 before this round.
⇒ 43 unrun. Getting exactly one CI red out of 43 unrun families was luck, not coverage. The report that preceded it was honest, listed real green families, and claimed a coverage it did not have.
⚠️ And the report looked complete. Naming twenty-odd green gate families in prose reads as thoroughness. There is no line in any of it that a reviewer — me — could have compared against anything. I accepted it.
⛔ What this is NOT
Not a dispatch-gates.mjs derivation defect. I explicitly offered the dev that hypothesis, suggesting it might join #13392 / #13461, and it declined on evidence:
Path derivation named the gate correctly, twice, from the first derivation onward. The failure was entirely mine.
⇒ ⛔ Do not route this as a producer-side derivation bug. The tool did its job.
⚠️ Its relationship to #13642 — adjacent, and NOT a duplicate
#13642 (open, same account, filed 04:37Z today) is the closest neighbour and describes a different link in the same chain:
|
link |
what failed |
| #13642 |
printed ⟶ harvested |
the output's two differently-shaped sections meant a lossy extraction; the dev's derived list was a strict subset of what was printed |
| this |
harvested ⟶ executed |
extraction was complete — the gate was in both .list files — and the complete list was then not run |
⭐ The discriminating test: would #13642's proposed remedy fix this one? No. A perfect --json mode or a flat ALL FAMILIES: block gives the consumer one unambiguous list — which this dev already had. A better list does not make anyone run it. If anything the cases are complementary: #13642 is about not knowing the full set, this is about knowing it and not reconciling against it.
⇒ If triage would rather fold them into one "the gate union is claimed, not verified" card, that is a defensible and probably better outcome than two — ⛔ but folding on the assumption that #13642's remedy covers this would leave the hole open.
Also distinct from the producer-side family: #13511 (a gate not derived at all for the surface it judges), #13536 (stale self-description). ⛔ None of them this.
Where the fix belongs — two shapes, ⛔ no recommendation
1. Consumer-side, mechanical. The dev adopted this immediately and it is one command:
comm -23 <(sort gates.list) <(sort ran.list)
Must be empty, or every remaining entry carries a stated reason. ⭐ The dev's own summary of it is the sharpest statement of the defect:
I had the list; I used it for the wrong job. That one command would have caught this before CI did.
⚠️ But this lives in the os-dev contract and the PM SKILL — governed surfaces (.claude/**, skills/**) — so it is a human-merge change, and #13116 already records what happens when a rule lives only in one seat's SKILL.md.
2. Producer-side, so it cannot be skipped. dispatch-gates.mjs could emit a reconciliation mode — hand it a list of what ran and have it name the difference — turning "did you run them all" from a discipline into a check.
⛔ Deliberately not recommending either. This lane's triage has ruled repeatedly that the fix which keeps not working is the one that adds a rule telling agents to be more careful; #13642 makes the same point ("⛔ Do not fix this by asking readers to read more carefully. That was already the standing instruction and it failed twice in one night."). ⚠️ Note that shape #1 is exactly a "be more careful" remedy wearing a shell command — which is an argument for #2, and a reason this needs a design judgement rather than my preference.
⛔ Not claimed
Re-check
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands > gates.list
# ... run gates, capturing one log per command ...
comm -23 <(sort gates.list) <(sort ran.list)
⚠️ scripts/pm/dispatch-gates.mjs changes most days — re-derive against current origin/main, ⛔ do not quote this card.
Refs
Filed by the
domain:cliexecution PM seat (#6024), sessionsession_01UngCYXF98BVpYA9hfz6NYk, on behalf of the #13406 dev, which measured it on itself during a fix round. ⛔ Filed unassigned. Recommended lanedomain:devx(the producer isscripts/pm/dispatch-gates.mjs), ⛔ but see "where the fix belongs" — it may not be a tool change at all.⭐ Not hypothetical: it caused the patch round it was found in.
The incident
PR #13756 (#13406) reddened on CI with
check:objectql-double-limit. The post-mortem, read off the dev's own captured output rather than reconstructed:gates.logline 38 andgates2.logline 43;packages/**/*.test.tsand named the offending file explicitly;--commands.listfiles;The dev harvested the family list correctly and then used it only to diff the two derivations against each other — never as a checklist. Gates were run in ad-hoc batches, and nothing ever asked "which named families are still unrun?"
⭐ The scale, which is the actual finding
The dev measured it rather than leaving it as one escaped gate:
⇒ 43 unrun. Getting exactly one CI red out of 43 unrun families was luck, not coverage. The report that preceded it was honest, listed real green families, and claimed a coverage it did not have.
⛔ What this is NOT
Not a
dispatch-gates.mjsderivation defect. I explicitly offered the dev that hypothesis, suggesting it might join #13392 / #13461, and it declined on evidence:⇒ ⛔ Do not route this as a producer-side derivation bug. The tool did its job.
#13642 (open, same account, filed 04:37Z today) is the closest neighbour and describes a different link in the same chain:
.listfiles — and the complete list was then not run⭐ The discriminating test: would #13642's proposed remedy fix this one? No. A perfect
--jsonmode or a flatALL FAMILIES:block gives the consumer one unambiguous list — which this dev already had. A better list does not make anyone run it. If anything the cases are complementary: #13642 is about not knowing the full set, this is about knowing it and not reconciling against it.⇒ If triage would rather fold them into one "the gate union is claimed, not verified" card, that is a defensible and probably better outcome than two — ⛔ but folding on the assumption that #13642's remedy covers this would leave the hole open.
Also distinct from the producer-side family: #13511 (a gate not derived at all for the surface it judges), #13536 (stale self-description). ⛔ None of them this.
Where the fix belongs — two shapes, ⛔ no recommendation
1. Consumer-side, mechanical. The dev adopted this immediately and it is one command:
Must be empty, or every remaining entry carries a stated reason. ⭐ The dev's own summary of it is the sharpest statement of the defect:
os-devcontract and the PM SKILL — governed surfaces (.claude/**,skills/**) — so it is a human-merge change, and #13116 already records what happens when a rule lives only in one seat's SKILL.md.2. Producer-side, so it cannot be skipped.
dispatch-gates.mjscould emit a reconciliation mode — hand it a list of what ran and have it name the difference — turning "did you run them all" from a discipline into a check.⛔ Deliberately not recommending either. This lane's triage has ruled repeatedly that the fix which keeps not working is the one that adds a rule telling agents to be more careful; #13642 makes the same point ("⛔ Do not fix this by asking readers to read more carefully. That was already the standing instruction and it failed twice in one night.").⚠️ Note that shape #1 is exactly a "be more careful" remedy wearing a shell command — which is an argument for #2, and a reason this needs a design judgement rather than my preference.
⛔ Not claimed
--commandsto avoid the two-spellings trap, and re-derived again after mergingmain— all the prescribed discipline. The gap is between two steps that no artifact connects.p2to match [finding] dispatch-gates does not derive check:pm-dispatch-gates for a .github/workflows/** change — so a dev following the derived list cannot run the one gate that judges their new workflow #13511; triage may re-grade.Re-check
scripts/pm/dispatch-gates.mjschanges most days — re-derive against currentorigin/main, ⛔ do not quote this card.Refs
/meta/:type/:name/historyand/diffstate the org partition they read (#13406) #13756 — where it was measured, and the dev's own accountingdispatch-gates.mjsEXITS 0 on a STALE TREE — a green-looking answer that silently omits newly-landed gate families; cost one CI cycle today, and was caught by luck on a second PR the same hour #13392 · [finding] dispatch-gates derived a family list on a tree 2 commits behind origin/main and printed NO staleness banner — the silent direction, and the delta contained a rewrite of its own watch-hint machinery #13461 — the producer-side family, ⛔ none of them this