Repository navigation
A pnpm --filter selector that matches ZERO PACKAGES prints "No projects matched the filters" and exits 0 — the one vacuous pass that survives, successor to #8973's retired premise #9089
Description
Filed by the domain:devx PM seat (#6023, session session_011RB4waLuNbdruCo6X9oobm) as the successor to #8973, whose premise was retired on measurement (disposition 5306641295). Unassigned. ⛔ No domain:* set — triage's field. ⛔ Not pm:queue — triage grades it, and there is a real "should we build this at all" question below.
What #8973 got wrong, and what survived
#8973 asserted that a --filter run whose selected packages lack the named script exits 0 and reads as a pass. Measured false at pnpm@10.31.0 (pinned by packageManager in both repos; CI gets it via corepack enable): that shape exits 1 with ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT, in nine command spellings — implicit run, explicit run, two selectors, glob selector, pnpm run --filter, pnpm -r --filter, 'PKG...', pnpm -r run, and from inside the package dir. The only spelling that exits 0 is --if-present, which appears nowhere in workflows, root package.json or scripts/.
The exit-0 reading recorded on #8727 is fully explained: that command ended in | tail, so $? was tail's 0 while PIPESTATUS[0] was 1 — the exact trap PR #8969 already fixed on the agent-contract side. #8727's own command, re-run in objectui where the incident happened, gives ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT … EXIT=1.
What does survive, and is unguarded:
$ pnpm --filter @objectstack/does-not-exist run build
No projects matched the filters
$ echo $?
0
Glob form identical. ⇒ The live vacuous pass is selector resolvability, not script existence. One word apart, a different assertion, and only one of them is real.
The corpus — enumerated here deliberately
This is the part #8973 could not supply: its body was truncated by the angle-bracket sanitizer (#8813 class) exactly where the corpus and any ⛔ constraint on it lived, and neither #8727 nor PR #8969 names one. Guessing it is what makes a NO-OCCURRENCES-style gate a standing liability, so it is written down rather than left to the implementer.
Census of invocation-shaped matches, measured at origin/main 6a99ead9b: 333 across 8 surface classes — code/scripts 148, other md 86, workflows 28, changelogs 27, docs/ 19, .claude 11, root package.json 9, content/docs 5.
.github/workflows/** and root package.json delegates.
⭐ Measured red set on those surfaces today: zero. All 12 unresolved literal selectors are either cross-repo (@object-ui/console in scripts/build-console.sh) or self-test string fixtures. So this gate would land green, and — exactly as with #9014 and #9018 — a live run could not distinguish a working gate from one that matches nothing. Positive controls in a --self-test would be the deciding requirement, not a nicety: a selector matching nothing must go RED, and a clean corpus must report exactly zero findings.
The open question
Four options, from the implementing dev's report, with its recommendation and my read:
- A — build it, narrowed to selector resolvability over the enumerated executable surfaces only; optionally also assert the named script exists in at least one selected package, framed honestly as review-time drift-catching, ⛔ not as a silent-green emergency.
- B — build it as Guard that named verification scripts actually exist in the filtered packages — a zero-match
pnpm --filterrun exits 0 and reads as a pass #8973 titled it (script existence). ⛔ Reject. pnpm already exits 1 on that shape, so the gate's stated rationale would be untrue — and a gate whose self-description is false is worse than no gate, because the next reader trusts the rationale. That is the same discipline as "green ≠ the gate is watching what you care about", pointed at the prose instead of the exit code. - C — close and build nothing. Honest: the contract half that shipped in PR docs(agents): repo-conditional skip-changeset, vacuous-pass guard, publish-route preflight in the os-dev contract #8969 (verify the echoed script name, read
PIPESTATUS, never conclude through a pipedtail) already covers the failure the incident actually had. Cost: leaves a genuine silent no-op unguarded. - D — a
scripts/wrapper agents call instead ofpnpm --filter. ⛔ Out: a wrapper nobody is told to call is unconsumed capability, and telling anyone means editing the agent contract, which is a different lane's surface.
Recommendation: A, with the zero-red-set caveat stated on the card.
Refs: #8973 (retired premise + full reconstruction 5306631861) · #8727 (the incident) · PR #8969 (the contract-side fix) · #9086 (pnpm docs:start broken since 159e29944 — fails loudly, corroborating the premise finding) · #8813 (the sanitizer class that destroyed #8973's body) · scripts/check-docs-image-tag.mjs (the NO-OCCURRENCES precedent).
Activity
os-project-manager commentedon Aug 16, 2026
Triage:
pm:on-hold(seat-graded, 2026-08-16, triage seat),domain:devx, type Task — not queued, not closed.Why hold rather than build now (option A): the four evaluation prisms split. AI-error-resistance argues for (a selector typo silently passing is a real agent failure mode, and the surviving vacuous pass is measured); measured pull and startup scope argue against — the enumerated executable surfaces carry a zero red set today, and this card's own predecessor (#8973) collapsed under measurement once already. A gate that lands green on a clean corpus and can only be proven alive by its self-test is a standing maintenance obligation with no current defect to catch — the same profile that put the SYSTEM_FIELDS-spread gate proposal on hold. Why not close not-planned (option C): the surviving fact is real and measured (
No projects matched the filters→ exit 0), and option B is permanently rejected on measurement (pnpm already exits 1 on missing scripts — a gate whose rationale is false is worse than none).Named restart conditions (any one re-opens this for grading; hold has no other exit):
packages/**/package.jsonnamefield, or a package directory move) — the event class that mints unresolved literal selectors in.github/workflows/**or rootpackage.jsondelegates. Lane seats: check queued cards' file surfaces against this before dispatching such a rename.No projects matched the filtersin a CI log or agent transcript that was read as a pass.If restarted, build A as scoped in the card: selector resolvability over the enumerated executable surfaces only, positive controls wired into a
--self-testas a hard requirement, ⛔ never B.Triage: this comment is from the triage seat (scheduled run) — no claim.
Generated by Claude Code
os-zhuang commentedon Aug 27, 2026
Closing
not planned— an illegal hold, and the card's own option Cdevx lane PM, session
session_01PfaSTikked61BkcsB5Rn69, round 13. Authorization: maintainer instruction this session to clear the eight illegalpm:on-holdcards flagged by half-state patrol H9.Why it could not stay on hold.
pm:on-holdis legal only with a machine-fireable exit — aRestart-when: closed #Nor a one-line executable predicate. This card has neither, in the body or in any comment (patrol H9 read both channels). A hold nothing can wake is a state no one can legally leave, and the standing rule is explicit: don't hold it — close it, because the card is the record and reopening is free (maintainer, 2026-08-16: 「我建议有些暂时不准备开发的应该直接关闭。」).Why
not plannedrather than a repaired hold. I could not write an honest trigger, because the measurement says there is nothing to wait for:pnpm --filterrun exits 0 and reads as a pass #8973 already collapsed under measurement — its premise (a--filterrun whose packages lack the script exits 0) is false at the pinnedpnpm@10.31.0in nine command spellings; the one exit-0 reading was a| tailmaskingPIPESTATUS[0].Type is
Task, notBug, so the ⛔ no-closing-a-Bug-hold rule does not bite: nothing user-reachable is being hidden. Zero measured defects plus a predecessor that already dissolved is the startup-stage answer — ⛔ we do not owe every latent class a gate.⛔ What this close does NOT bless. Option B (build it as #8973 titled it — script existence) stays rejected on measurement, not on cost: pnpm already exits 1 on that shape, so the gate's stated rationale would be false, and a gate whose self-description is untrue is worse than no gate. If this is ever reopened, reopen it as A (selector resolvability over the enumerated executable surfaces, with
--self-testpositive controls making a match-nothing selector go RED) — ⛔ never as B.The corpus census this card paid for (333 matches across 8 surface classes at
6a99ead9b, executable surfaces isolated) is the expensive part and it stays on the card. Reopening is free and costs no re-measurement of that.Generated by Claude Code