Uh oh!
There was an error while loading. Please reload this page.
fix(devx): refuse a pnpm --filter that matches nothing, instead of exiting 0 - #10903
Merged
Conversation
…iting 0 (#10853) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
…lter-zero-match-guard
os-zhuang
marked this pull request as ready for review
August 22, 2026 03:57
Uh oh!
There was an error while loading. Please reload this page.
Contributor
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32551036907 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#10853
pnpm --filter <name>exits 0 when the filter matches no project. Re-measured on this branch:The typo is not the defect; the exit 0 is. Every discipline this repo uses to make a run trustworthy is defeated by it —
cmd > log 2>&1; ec=$?captures the 0 faithfully, and a report saying "suite green, exit 0" is then true and worthless because nothing ran. Same family as the---before-vitest-args trap: exit 0, nothing measured, output that reads like success.The card is explicit that its two directions are not substitutes. Both are implemented here, and neither is presented as covering the other.
os-verify-lock.shfilter preflightpnpm --filter …typed at a prompt is still exit 0, still silentcheck:pnpm-filter-targetspackage.jsonscripts,.github/workflows/**,scripts/**Left uncovered, plainly: a
pnpm --filter <typo>typed directly into a shell, outside the lock wrapper, behaves exactly as it does today. Nothing here changes pnpm's exit code. ① narrows that population to "commands that skip the mandated wrapper" rather than eliminating it, and ② cannot see it at all. Direction ③ from the card (the dispatch template's example) is out of scope —.claude/skills/**is governed, human-merge-only, and the PM's triage comment already records that no committed template teaches the bad name.The card's "no guard exists" finding — re-verified, not inherited
Both greps re-run on this tree:
git grep -n "No projects matched the filters" -- scripts .github/workflows package.json→ zero hits (exit 1). The card's precise finding holds.git grep -nE "filter.*exists|verify.*filter" -- scripts .github/workflows→ 8 files now (the card recorded 4). Every hit isArray.prototype.filternext toexistsSync, or this wrapper's own usage examples. None guards a zero match. The loose grep is still the wrong instrument; the targeted one still settles it.scripts/check-ci-filter-parity.mjswas checked by name because it sounds adjacent — it is about ci.yml's dorny path filters, not pnpm package selectors, and does not overlap.The matching rule was measured, not assumed
--filterresolution is one shared module (scripts/pnpm-filter-targets.mjs) so the two consumers cannot answer the same question differently. pnpm matches the package name, full or scope-stripped, and not the directory — all measured withpnpm --filter <sel> ls --depth -1 --jsonon pnpm 10.31.0:@objectstack/honohonoexample-todoapp-todoexamples/app-todo— not a match@objectstack/adapter-honoThe
app-todorow is the one that makes the rule non-obvious: a matcher written from the directory intuition would report live selectors as dead. Fidelity check: all 49 judgeable selectors in a corpus built from every committed spelling were resolved against real pnpm and compared with the local matcher — 49/49 agree, 0 divergence.A selector is judged only when the answer cannot be argued with (a plain name). Globs, path selectors, since-ref selectors and interpolations are refused a verdict by design — deciding globs would need a picomatch-compatible matcher, i.e. a second recogniser, the trade
check-ci-filter-parity.mjsalready rejected. The unjudged count is printed, so an unjudged population is visible rather than silent.Ablations — predictions written down before the runs
None of these subjects is consumed through a package
exportsmap: all three are read from disk bynode scripts/…/bash scripts/…on every invocation, so there is nodist/between the edit and the run. Each mutation was still proved on disk by marker counts, never by an editing tool's exit code.A — the ⭐ direction, both ways, no mutation. Predicted: reds on the card's literal reproduction, silent on the same command with the real name.
B — mutation: delete the guard call from
mode_run. Marker countfilter_preflight2 → 1 (call gone, definition kept). Predicted direction: red → green (not "more diagnostics", not reversed).The pre-fix world exactly: exit 0, nothing measured — and it spent the fleet's shared lock to do it. Restore leg: marker count back to 2, guard reds again,
git diff --quiet scripts/pm/os-verify-lock.sh→ 0 (byte-identical).C — mutation: plant a dead filter in the real
lint.yml. Marker countadapter-honoin lint.yml 0 → 1. Predicted: gate exits 1, naming file:line and the suggestion.Restore leg: marker count back to 0, gate green,
git diff --quiet .github/workflows/lint.yml→ 0 (byte-identical).Two false positives the gate found in its own first run — both fixed, both pinned
Recorded because they are the reason the discrimination exists, not incidental:
git fetch --unshallow --filter=blob:none— git's partial-clone filter, reported as a dead package. A--filternow has to belong to apnpm/turbocommand word, cut at the last command separator sopnpm build && git fetch --filter=…does not inherit thepnpm.name:—- name: Every committed pnpm --filter names a real packageparses as--filter names. A YAML step name is a label, not a command, the same distinction [finding] check-required-contexts' self-test proves its own WIRING with text a comment can supply — two presence assertions that go green on prose #10877 had to make. Both are pinned in--self-testwith the live text as the fixture.Self-tests have real negative controls
pnpm-filter-targets.mjs --self-test— 54 assertions over the 77 real workspace packages; the measured pnpm rule pinned row by row; the preflight observed both refusing and silent.check-pnpm-filter-targets.mjs --self-test— 36 assertions; a dead filter observed red in all four carriers (workflow, package.json, shell, JS) and the same fixtures observed silent with a real name.os-verify-lock.sh --self-test— 72 cases, 16 new, including: refused command never ran, exit 2 and not 99 (99 means "never got a turn" and invites a retry that would only reproduce the silent green), a real name sails through and its command actually runs, undecidable selectors proceed, and the escape hatch works.Three
#4690vacuity refusals: zero workspace packages, zero--filteroccurrences found, and zero occurrences judged each fail loudly rather than reading as a clean tree.Scope, exemptions and registration
@object-ui/is inFOREIGN_SCOPESwith its reason:scripts/build-console.shandscripts/gen-sdui-manifest.shrun those filters inside a checkout ofobjectstack-ai/objectui, never against this workspace. A new foreign scope must be added deliberately.pnpm-workspace.yamland the manifests it names: no network, no pnpm invocation, no lockfile, so it cannot become the unbounded wait that file exists to prevent. Escape hatch:OS_VERIFY_LOCK_NO_FILTER_CHECK=1.check:pnpm-filter-targetskey in rootpackage.json. Read off Migrate the release toolchain to @changesets/cli v3 — one atomic PR carrying the bump, the pre-mode restructure, and the gates that model v2's semantics #9465 directly: that lane's fence names rootpackage.json(the@changesets/clirange and theversionscript) — a scoping parenthetical, so a newcheck:*key is allowed. (lint.yml carries fifteen comments asserting a wider claim; [finding] lint.yml states the #9465 fence as covering rootpackage.json— fifteen times, and the over-broad reading is acquirable by copying #10894 tracks those, and none of that phrasing is copied here.)lintjob (~:1971), deliberately away from:184and:561. fix(ci): collect every self-test verdict in a step, instead of masking after the first failure #10887merged while this was in progress;origin/mainis merged in and re-verified on the merged head — the newcheck-step-collectors.mjsis green on this branch, and it does not flag the new step (one command, no--self-testin the block).Verification
Gate union derived with
node scripts/pm/dispatch-gates.mjs(no paths passed — it reads the changeset from the merge base itself), re-run on the final commit63ffc88712: 17/17 green, plusos-verify-lock.sh --self-test(72 cases).One declared narrowing:
check:type-check-debtwas not completed. Its--re-measurehalf refuses to run without a fully built workspace closure (a deliberate #6376 refusal — measuring without it would silently measure a different world). Its--self-testhalf andcheck:type-check-coverageboth pass. This diff adds no TypeScript to any package's tsc program — two.mjsscripts, a bash script, a workflow step and apackage.jsonscript key — so it cannot move a DEBT or TEST_DEBT number. CI runs it with the closure built.No changeset: nothing published changes (
scripts/**,.github/workflows/**, rootpackage.jsonscripts), so this carriesskip-changeset.Generated by Claude Code