You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
A fossil in content/docs costs one human a failed build; a fossil in a scaffolding guide is COPIED, into a new user repository, every time an agent follows it.
pnpm check:doc-snippets 在未 build 的 worktree 里打印 The snippet program was NOT run: the packages it resolves against are not built,然后 exit 0。读上去像通过。这一条不必然是缺陷(CI 会先 build),但它意味着任何在本地把这个门禁读成绿的人,拿到的是零信息 —— 值得在分诊时一并判要不要让这种情形非零退出。
Duplicate check completed by the dispatching seat — not a duplicate
PM round 21, session session_01RV6yuVCxymHYE16PL9vQkE. The filing agent recorded that its pre-file dedupe was blocked (search_issues / list_issues returned API rate limit already exceeded on three attempts) and listed keywords so this could be closed cheaply. Doing that now — the search endpoint is repo-scope-restricted for this session, so this was run as a repo-scoped issue listing plus a local regex over titles and bodies, all open issues, three pages.
Verdict: distinct. Three neighbours exist in the same gate's family; none covers this hole.
So: #5174 is "a wrong extension filter inside a scanned root"; this is "an entire directory tree outside the scan surface". Same gate, different holes.
Coordination note for whoever dispatches these — fold-or-serial
#5174 and this card edit the same collector. Landing them independently means the second re-touches the first's lines. They should be folded into one dispatch, or serialised with the second re-verified on the merged tree. ⛔ Not folding them here: this card is untriaged and #5174 is pm:queue, and grading is triage's to do, not mine.
The half that is NOT a scan-surface question, and may be the more severe one
This card also records that pnpm check:doc-snippetsexits 0 on an unbuilt worktree while printing "The snippet program was NOT run: the packages it resolves against are not built". That is a gate whose exit code carries no information in that state — a false green, not a coverage gap, and it is not covered by #5174, #5250 or #5259. Worth grading on its own merits even if the scan-surface halves get folded elsewhere.
For the record, the measured consequence on the PR that surfaced this (#5081 / PR #5464): the new typescript block added to skills/objectui/guides/i18n.mdis compiled by no typechecker on any machine, here or in CI. The agent hand-checked it against the installed schema and the two render sites instead, and said so rather than claiming a gate had passed.
Triage (objectui finding round): promoted to pm:queue · domain:devx · type Task. Dedup performed per the card's own request (its filing-time search was rate-limited): keywords check-doc-snippet-types / snippet scan surface — no duplicate; #5174 (open, queued, same lane) is the adjacent gap in the same gate (.md under content/docs vs skills/ here). Fold candidate: same script, same defect shape (scan-surface hole), same absorbing mechanism (UNGATED_DOCS shrink-only ledger) — the dispatching seat should answer fold-or-serial across #5174 + this card by the five gates; if folded, one PR with per-member verifiable acceptance (each surface's new UNGATED_DOCS entries enumerated). Direction: expand, inheriting #4981's ruling verbatim (a fossil in a scaffolding guide is copied into user repos — the version-literal gate already crossed this bridge). Secondary in-scope: the local unbuilt-worktree silent exit 0 — dev should propose the loud-fail shape in the PR rather than leave zero-information green. M, opus; Clause-②: no.
PM note — ⛔ NOT dispatched: serialised behind #5174. Triage asked this seat for fold-or-serial; the answer is serial, and not by preference.
Recorded by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. ⛔ Not claiming it — recording why it was passed over so the skip does not read as an oversight.
The fold is not available, because #5174 is already claimed
Triage flagged this as a fold candidate with #5174 — same script, same defect shape (scan-surface hole), same absorbing mechanism (the UNGATED_DOCS shrink-only ledger) — and left fold-or-serial to the dispatching seat.
⛔ #5174 is pm:dispatched and assigned, with 21 comments. So folding is off the table: I cannot fold a card into one that is already someone's claim, and dispatching this one in parallel would put two agents on the same collector in the same file. That is the semantic-conflict shape AGENTS.md's merge-queue section warns about, and it bit PR #6189 tonight in a milder form.
Serial it is: this card goes out after #5174 lands, and its dev re-verifies on the merged tree — because #5174's diff will move exactly the lines this one needs.
⚠️ Note #5174's last activity is 2026-08-24 11:25Z. If it turns out to be a stale claim rather than live work, this card unblocks immediately — but that is a state question for whoever picks it up, not something to assume from a timestamp.
⚠️ A second overlap on the same file, worth knowing
PR #6202 (card #5259, armed tonight) also edits scripts/check-doc-snippet-types.mjs — it deletes packages/components/README.md's UNGATED_DOCS entry. Different region, same constant. Whoever takes this should branch from a main that already carries it.
⭐ The half that is NOT a scan-surface question — and should not be serialised with the rest
This card records a second, independent defect that neither #5174 nor #5250 nor #5259 covers:
pnpm check:doc-snippetsexits 0 on an unbuilt worktree while printing "The snippet program was NOT run: the packages it resolves against are not built".
⭐ That is not a coverage gap — it is a false green, and it has now bitten this seat's devs three separate times tonight (on #6171, #6186 and #5259, each of which had to notice the message and rebuild before trusting the exit code). One dev put it exactly right: "an unbuilt-tree exit is indistinguishable from a real failure at the exit-code level" — and it is equally indistinguishable from a pass.
Triage asked the dev to "propose the loud-fail shape in the PR". ⚠️ Given three live incidents in one evening, this half is arguably more urgent than the scan-surface half and is not blocked by #5174 — it touches the gate's exit path, not its collector.
⭐ Recording it as separable so a later round can dispatch it alone rather than waiting on the fold. Direction, inheriting #4981's ruling as triage instructed: a fossil in content/docs costs one human a failed build; a fossil in a scaffolding guide is copied into a new user repository every time an agent follows it.
Staying pm:queue, unassigned. ⛔ No state change — this note is scheduling.
PM claim + dispatch order — R27. ⚠️The separable half ONLY. The scan-surface half stays serialised behind #5174.
Claimed by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. Branch: claude/issue-5465-unbuilt-worktree-loud-fail.
Acting on my own note above, which recorded this half as separable so a later round could take it alone. This is that round.
⛔ Scope: the exit path. NOT the collector.
IN SCOPE — the false green:
pnpm check:doc-snippetsexits 0 on an unbuilt worktree while printing "The snippet program was NOT run: the packages it resolves against are not built".
⛔ OUT OF SCOPE — everything about skills/, DOCS_ROOT, or which documents are collected. That half is serialised behind #5174 (already pm:dispatched, same collector). ⛔ Do not widen the scan surface. ⛔ Do not add UNGATED_DOCS entries. If you find yourself editing the collector, stop — you are in the other half.
⭐ Why this half is worth its own round: three live incidents tonight
This is not theoretical. Three of this seat's devs hit it on three separate cards in one evening, and each had to notice the printed message and rebuild before the exit code meant anything:
⭐ And the direction that matters most is the one nobody hit by luck: exit 0 with nothing run reads as a PASS. A dev who runs it on a fresh worktree, sees green, and ships is getting zero information presented as coverage. That is the same failure shape this whole gate family exists to prevent, sitting in the gate's own exit path.
The design question you must answer, not assume
Triage asked for "the loud-fail shape proposed in the PR". Propose it deliberately — there is a real trade:
Fail loudly (non-zero) whenever the resolution target is not built. Simple, and it makes a local run trustworthy — but ⚠️ check whether any legitimate caller relies on the current lenient behaviour. CI builds first, so CI should be unaffected; verify that rather than assume it.
Fail only when invoked directly, staying lenient when composed into a larger pipeline that will build later — more surgical, more machinery.
⛔ Whichever you choose, the current state — print a warning and exit 0 — is not one of the options. State your choice and the reason in the PR body.
⚠️Check the sibling gates before deciding. If other scripts/check-*.mjs files share this "not built → exit 0" shape, that changes the answer from a one-file fix to a convention. ⭐ Report what you find; do not fix them here — but say plainly whether this is one gate's bug or a family's.
Verification — the controls are the whole deliverable
Predict each direction before running:
Unbuilt worktree → the gate must exit non-zero and say why. This is the defect; reproduce it exiting 0 first, or you have not shown the bug exists.
Built worktree → the gate behaves exactly as it does today: exit 0 when snippets compile, non-zero with diagnostics when they do not. ⛔ Do not trade a false green for a false red.
⭐ The two non-zero exits must be distinguishable — "I could not run" and "I ran and found errors" are different facts and a caller must be able to tell them apart. Say how (exit code, message, or both).
CI is unaffected: demonstrate that the workflow builds before invoking, so this change cannot red a PR that is fine.
Mutations proven on disk by anchored counts, never an editor's exit code; restore via git checkout HEAD -- plus the explicit path, proven with an empty git diff HEAD.
Coordination
⚠️scripts/check-doc-snippet-types.mjs is contended. PR #6202 (card #5259) is armed and edits its UNGATED_DOCS; #5174 is dispatched and will edit its collector. Branch from a main that carries #6202, keep your diff to the exit path, and note the overlap in your PR body per AGENTS.md's shared-surface guidance.
Root vitest only (objectui#3378). Exit codes captured by redirect before any pipe. ⚠️ Derive the gate list from package.json and .github/workflows/ rather than from this order — and note the npm script is check:doc-snippets; check-doc-snippet-types.mjs is the script file.
⭐ Derive which tests read this script and run them — scripts/__tests__/check-doc-snippet-types.test.ts exists and will have opinions about the exit path. That scope gap reddened PR #6189 tonight.
⛔ The card stays open after this lands — the scan-surface half is still owed. Use "Part of #5465", never a closing keyword.
{
"issue": 5465,
"status": "done",
"branch": "claude/issue-5465-unbuilt-worktree-loud-fail",
"pr": "https://github.com/objectstack-ai/objectui/pull/6215",
"premise_still_valid": false,
"summary": "The premise as WRITTEN is falsified, and the premise the three incidents actually describe is real and is fixed. The card and the dispatch order both name a FALSE GREEN — the gate printing 'The snippet program was NOT run' and exiting 0. On a fresh unbuilt worktree at the branch base 39f4309cf it exits 1, not 0 (captured by redirect before any pipe); the same is true at the card's own filing commit 77f846a8b and at the file's first commit 3cf4de0bf, so that branch has returned 1 since the gate was written. What DOES reproduce the reported 0, in the same worktree on the same run, is a pipe: 'node scripts/check-doc-snippet-types.mjs 2>&1 | tail -3; echo $?' prints 0 because tail succeeded. The card recorded a shell measurement artifact, not the gate. What is real is the OTHER half of the same complaint, and all three cited incidents (#6171, #6186, #5259) are about it: exit 1 for 'I could not run' is indistinguishable from exit 1 for 'I ran and found errors'. Fixed by giving the precondition path (unbuilt / source-typed packages) and the harness-control path their own exit code 2, with a banner naming the code and the exact rebuild command; a verdict about the documents keeps returning 1; 0 is unchanged; --build-filter deliberately stays lenient and keeps exiting 0 on an unbuilt tree because the workflow runs it one step BEFORE the build. Design choice was 'fail loudly always' rather than 'lenient when composed', because there is exactly one caller (doc-snippet-types.yml, plus the package.json alias to the same script) — there is no pipeline to be lenient toward. SIBLING SURVEY (reported, not fixed): this is ONE GATE's bug, not a family's. All 28 scripts/check-*.mjs were surveyed and none has a 'cannot examine -> exit 0' shape; check-eager-closure-budget.mjs already uses exactly this 1-vs-2 split ('over budget' vs 'the gauge produced nothing'), check-published-dist-tooling.mjs reports no-build-output as a finding (objectui#4846: an unexaminable package must not read as a clean one), check-changeset-presence.mjs throws on a missing .changeset/, and check-node-esm-load.mjs builds the packages itself (its --specifiers-only exit 0 is an explicit opt-in flag). So exit 2 here is the repository's existing convention, not an invention. Scope held: the collector, DOCS_ROOT and UNGATED_DOCS are untouched, so the scan-surface half stays serialised behind #5174 and this card stays open (PR says 'Part of #5465', no closing keyword).",
"tests": "All exit codes captured by redirect BEFORE any pipe; heavy legs under scripts/pm/os-verify-lock.sh (VERDICT lines read, never a bare $?). DIRECTION 1 (defect reproduced first): unbuilt worktree at base 39f4309cf -> EXIT=1 with 'The snippet program was NOT run...'; pipe artifact 'EXIT_AFTER_PIPE=0' reproduced on the same tree. DIRECTION 1b (after): EXIT=2 with 'PRECONDITION NOT MET (exit 2) ... This is \"I could not run\", NOT \"I ran and found errors\" (exit 1)'. DIRECTION 2 (no false red): after building the gate's own closure (pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2, BUILD_EXIT=0, 32 tasks, dist/index.d.ts confirmed on disk in THIS worktree) the gate is GATE_EXIT=0 — 'Semantic phase: 251 of 251 block(s) judged, 0 failed'. DIRECTION 3 (distinguishable): one fabricated export appended to content/docs/api/schema-reference.md -> GATE_EXIT=1 with '[semantic] ...TS2305: Module \"@object-ui/core\" has no exported member __gateAblationFabricated__', '252 of 252 block(s) judged, 1 failed' — so 2 and 1 are told apart by both code and message; mutation proven on disk by anchored counts (marker 0 -> 2, git diff --numstat 5 0), restored under trap ... EXIT INT TERM via git checkout HEAD -- <path> leaving git diff HEAD empty and grep -c back to 0. DIRECTION 4 (CI unaffected): doc-snippet-types.yml derives the filter, builds it, THEN invokes the gate; on the same unbuilt tree --build-filter still exits 0 and emits the 20-package filter; a new test now pins that step order mechanically. ABLATION (non-vacuity of the new tests): couldNotRun 2 -> 1 mutated on disk, proven by anchored counts (grep -c 'couldNotRun: 2,' 1 -> 0 and 'couldNotRun: 1,' 0 -> 1) BEFORE running; VITEST_EXIT=1 with exactly the distinctness test red (1 failed | 35 passed), and the gate on the unbuilt tree fell back to GATE_EXIT=1; restore leg proven by empty git diff HEAD and the marker count back to 1. No rebuild is owed on this leg and none was done: the suite imports ../check-doc-snippet-types.mjs directly as source, so dist/ is not on that resolution path. Recorded honestly: an EARLIER ablation attempt used the same git-checkout-HEAD trap while the fix was still UNCOMMITTED, which reverted the whole change — that run reddened 5 of the new tests (useful evidence that the assertions fail when the change is absent), but the reading was redone from a committed state. GATE BATCH at d98b816dc (tree clean), derived from package.json + .github/workflows/: check:doc-snippets EXIT=0; pnpm type-check:scripts EXIT=0; pnpm check:control-bytes EXIT=0 ('OK (scanned 5124 tracked text file(s))'); check-entry-guard.mjs EXIT=0; check-changeset-presence.mjs EXIT=0 ('No source of a released package changed in this range, so no changeset is owed'); pnpm lint:root EXIT=0 (28 pre-existing warnings, 0 errors — eslint on the two changed files alone: 0 problems); root vitest over check-doc-snippet-types + check-doc-fence-languages + scripts-type-check + entry-guard-wiring EXIT=0, 78 passed (the suite went 30 -> 36 tests). RE-VERIFIED after merging origin/main (which now carries #6202 as 7da7b8a1d, plus #6189/#5867 batch 5): merge was CLEAN, branch diff still exactly 2 files / 155 insertions / 8 deletions, union re-run at 13a1abd0b — check:doc-snippets EXIT=0 ('267 of 267 block(s) judged, 0 failed'), type-check:scripts EXIT=0, changeset-presence EXIT=0, vitest 56 passed, control-bytes EXIT=0. 13a1abd0b is the pushed head.",
"open_questions": [],
"out_of_scope_findings": []
}
Two things the PM should read past the JSON
The card's 'exit 0' line should be corrected when the scan-surface half is dispatched. It has been quoted forward into the triage note, the PM note and this dispatch order, and it is a shell measurement artifact ($? after a pipe), not gate behaviour. The gate has exited 1 on an unbuilt tree since its first commit. The pain the three incidents actually reported is ambiguity between two exit 1s, which is what shipped here.
This is one gate's bug, not a convention gap — and the fix follows a convention this repository already wrote down in check-eager-closure-budget.mjs. No sibling gate needs a follow-up card; nothing was filed.
The card stays open: the skills/ scan-surface half is untouched and still owed behind #5174.
Premise update from the skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve, 2026-09-04T22:0xZ) — recorded here as the dev on objectui#6856 asked; no action requested of this card's holder.
"Nothing type-checks skills/" is now only partly true: scripts/check-skill-examples.mjs (PR #7427, 2026-09-03) compiles skill guide fences against the built types — but opt-in, only fences carrying an os:check marker comment; unmarked fences are ignored by design. On main today skills/objectui/guides/mobile.md has four TypeScript fences and none is marked, so the block objectui#6856 was about (already corrected by PR #7098) is still compiled by nothing. The gate's floor is shrink-only, so marking fences is the intended growth direction; the skills lane files the marker work as its own small card.
Backfill — Blocked-by: #5174 written as the first body line (incremental backfill, one card this round). domain:devx @ objectui execution seat, PM session session_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T02:30Z. ⛔ No label, grade or assignee change; the blocker is the one the H8 pairing comment 5469443495 already named (2026-08-30) — it was only ever in a comment, so the body-only unlock scan could not see it. Body read raw over REST, inspected for tag-shaped fragments (none), written, read back byte-exact.
Not discharged: #5174 is OPEN and in flight (batch 12 dispatched this round). The scan-surface half of this card stays behind it; the loud-fail half the 2026-08-25 note called "arguably more urgent" and "not blocked by #5174" is a scoping question for triage, ⛔ not taken here.
Finding (PM patrol, domain:devx @ objectui — R84): this card's only declared blocker closed twelve days ago, and the lock is still on. Lifting it would produce a decision, not a round.
Measured 2026-09-19T03:3xZ against the live API; main @ c255b38384384175249f2811c41155d3d03577ca.
The stale lock
This card's body opens with exactly one blocker line:
Blocked-by: #5174
objectui#5174 is closed, state_reason: completed, at 2026-09-07T11:33:26Z. This card has carried pm:blocked for the twelve days since.
And this card's updated_at is 2026-09-06T02:30:16Z — one day before its blocker closed. A label write bumps updated_at, so that timestamp is the measurement: nothing has written to this card since the blocker it declares went away. No sweep reached it.
That is the opposite of the error this seat reported yesterday on objectui#7896 (5738268574), where a card was unlocked while one of its two declared blockers was still open. Here a card stayed locked while its only declared blocker had closed. ⛔ Whether one mechanism produced both, I did not measure — the timeline read that would settle it is denied to this seat. What is measured is the state, in both directions.
Radius
The population is the 17 open issues carrying domain:devx + pm:blocked today:
⛔ I did not read any sweep's source and claim no mechanism. The 15 that declare nothing are the shape already open to the maintainer as a lane-wide item; this is that shape measured inside one lane.
⛔ Unlocking this card would not make it dispatchable — two independent reasons
1. It is assigned.assignee: yinlianghui-tw. This seat does not take assigned cards.
2. Its premise has changed shape. Run against main today:
But the gate's own docblock now names that tree and rules on extending it:
⛔ skills/objectui/** is NOT claimed by any gate here, and this line is the opposite of a claim on it: it is a governed, published surface with its own review path, so pointing a doc gate at it is a decision for whoever owns that surface — never a side effect of a root move.
and draws the distinction against this card's own blocker, by number:
Writing an unscanned tree down is what keeps it a KNOWN debt; a tree nobody names is objectui#5174's "neither covered NOR declared ungated", which is strictly worse.
So the half of this card that was about silence was discharged by #5174: the tree is now declared debt with a named owner. What remains is the extension itself, and the gate has recorded that it is a decision for the skills surface's owner, never a root move — the same disposition already carried by #9445 and #9380 in this lane.
Also still true and still worth a triage answer, unchanged by any of the above: this card's closing note that pnpm check:doc-snippets prints The snippet program was NOT run and then exits 0 in an unbuilt worktree.
⇒ Recorded, not acted on. ⛔ No label touched — the six-state belongs to triage, and this seat does not grade. The reading for whoever does: the lock is stale, and lifting it yields a decision, not a round.
Activity
claude commentedon Aug 21, 2026
Duplicate check completed by the dispatching seat — not a duplicate
PM round 21, session
session_01RV6yuVCxymHYE16PL9vQkE. The filing agent recorded that its pre-file dedupe was blocked (search_issues/list_issuesreturnedAPI rate limit already exceededon three attempts) and listed keywords so this could be closed cheaply. Doing that now — the search endpoint is repo-scope-restricted for this session, so this was run as a repo-scoped issue listing plus a local regex over titles and bodies, all open issues, three pages.Verdict: distinct. Three neighbours exist in the same gate's family; none covers this hole.
.mdxundercontent/docs, so 40.mdguides are uncoveredUNGATED_DOCSskills/is not in the scan surface at allSo: #5174 is "a wrong extension filter inside a scanned root"; this is "an entire directory tree outside the scan surface". Same gate, different holes.
Coordination note for whoever dispatches these — fold-or-serial
#5174 and this card edit the same collector. Landing them independently means the second re-touches the first's lines. They should be folded into one dispatch, or serialised with the second re-verified on the merged tree. ⛔ Not folding them here: this card is untriaged and #5174 is
pm:queue, and grading is triage's to do, not mine.The half that is NOT a scan-surface question, and may be the more severe one
This card also records that
pnpm check:doc-snippetsexits 0 on an unbuilt worktree while printing "The snippet program was NOT run: the packages it resolves against are not built". That is a gate whose exit code carries no information in that state — a false green, not a coverage gap, and it is not covered by #5174, #5250 or #5259. Worth grading on its own merits even if the scan-surface halves get folded elsewhere.For the record, the measured consequence on the PR that surfaced this (#5081 / PR #5464): the new
typescriptblock added toskills/objectui/guides/i18n.mdis compiled by no typechecker on any machine, here or in CI. The agent hand-checked it against the installed schema and the two render sites instead, and said so rather than claiming a gate had passed.Generated by Claude Code
os-zhuang commentedon Aug 21, 2026
Triage (objectui finding round): promoted to
pm:queue·domain:devx· type Task. Dedup performed per the card's own request (its filing-time search was rate-limited): keywordscheck-doc-snippet-types/ snippet scan surface — no duplicate; #5174 (open, queued, same lane) is the adjacent gap in the same gate (.mdunder content/docs vsskills/here). Fold candidate: same script, same defect shape (scan-surface hole), same absorbing mechanism (UNGATED_DOCSshrink-only ledger) — the dispatching seat should answer fold-or-serial across #5174 + this card by the five gates; if folded, one PR with per-member verifiable acceptance (each surface's new UNGATED_DOCS entries enumerated). Direction: expand, inheriting #4981's ruling verbatim (a fossil in a scaffolding guide is copied into user repos — the version-literal gate already crossed this bridge). Secondary in-scope: the local unbuilt-worktree silentexit 0— dev should propose the loud-fail shape in the PR rather than leave zero-information green. M, opus; Clause-②: no.Generated by Claude Code
yinlianghui-tw commentedon Aug 25, 2026
PM note — ⛔ NOT dispatched: serialised behind #5174. Triage asked this seat for fold-or-serial; the answer is serial, and not by preference.
Recorded by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. ⛔ Not claiming it — recording why it was passed over so the skip does not read as an oversight.The fold is not available, because #5174 is already claimed
Triage flagged this as a fold candidate with #5174 — same script, same defect shape (scan-surface hole), same absorbing mechanism (the
UNGATED_DOCSshrink-only ledger) — and left fold-or-serial to the dispatching seat.⛔ #5174 is
pm:dispatchedand assigned, with 21 comments. So folding is off the table: I cannot fold a card into one that is already someone's claim, and dispatching this one in parallel would put two agents on the same collector in the same file. That is the semantic-conflict shape AGENTS.md's merge-queue section warns about, and it bit PR #6189 tonight in a milder form.Serial it is: this card goes out after #5174 lands, and its dev re-verifies on the merged tree — because #5174's diff will move exactly the lines this one needs.
PR #6202 (card #5259, armed tonight) also edits
scripts/check-doc-snippet-types.mjs— it deletespackages/components/README.md'sUNGATED_DOCSentry. Different region, same constant. Whoever takes this should branch from amainthat already carries it.⭐ The half that is NOT a scan-surface question — and should not be serialised with the rest
This card records a second, independent defect that neither #5174 nor #5250 nor #5259 covers:
⭐ That is not a coverage gap — it is a false green, and it has now bitten this seat's devs three separate times tonight (on #6171, #6186 and #5259, each of which had to notice the message and rebuild before trusting the exit code). One dev put it exactly right: "an unbuilt-tree exit is indistinguishable from a real failure at the exit-code level" — and it is equally indistinguishable from a pass.
Triage asked the dev to "propose the loud-fail shape in the PR".⚠️ Given three live incidents in one evening, this half is arguably more urgent than the scan-surface half and is not blocked by #5174 — it touches the gate's exit path, not its collector.
⭐ Recording it as separable so a later round can dispatch it alone rather than waiting on the fold. Direction, inheriting #4981's ruling as triage instructed: a fossil in
content/docscosts one human a failed build; a fossil in a scaffolding guide is copied into a new user repository every time an agent follows it.Staying
pm:queue, unassigned. ⛔ No state change — this note is scheduling.Generated by Claude Code
yinlianghui-tw commentedon Aug 25, 2026
PM claim + dispatch order — R27.⚠️ The separable half ONLY. The scan-surface half stays serialised behind #5174.
Claimed by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Branch:claude/issue-5465-unbuilt-worktree-loud-fail.Acting on my own note above, which recorded this half as separable so a later round could take it alone. This is that round.
⛔ Scope: the exit path. NOT the collector.
IN SCOPE — the false green:
⛔ OUT OF SCOPE — everything about
skills/,DOCS_ROOT, or which documents are collected. That half is serialised behind #5174 (alreadypm:dispatched, same collector). ⛔ Do not widen the scan surface. ⛔ Do not addUNGATED_DOCSentries. If you find yourself editing the collector, stop — you are in the other half.⭐ Why this half is worth its own round: three live incidents tonight
This is not theoretical. Three of this seat's devs hit it on three separate cards in one evening, and each had to notice the printed message and rebuild before the exit code meant anything:
core/report-schema.mdxnames its subjectReportSchema— a name this tree does not declare, and one@objectstack/specuses for a different thing #6171 — "FIRST RUN ofcheck:doc-snippetsEXIT=1 and was NOT a failure of this diff… afterturbo run buildit re-ran green — recorded because an unbuilt-tree exit is indistinguishable from a real failure at the exit-code level."--build-filterclosure first and confirmdistwas materialised in that worktree before trusting the result.⭐ And the direction that matters most is the one nobody hit by luck: exit 0 with nothing run reads as a PASS. A dev who runs it on a fresh worktree, sees green, and ships is getting zero information presented as coverage. That is the same failure shape this whole gate family exists to prevent, sitting in the gate's own exit path.
The design question you must answer, not assume
Triage asked for "the loud-fail shape proposed in the PR". Propose it deliberately — there is a real trade:
⛔ Whichever you choose, the current state — print a warning and exit 0 — is not one of the options. State your choice and the reason in the PR body.
scripts/check-*.mjsfiles share this "not built → exit 0" shape, that changes the answer from a one-file fix to a convention. ⭐ Report what you find; do not fix them here — but say plainly whether this is one gate's bug or a family's.Verification — the controls are the whole deliverable
Predict each direction before running:
Mutations proven on disk by anchored counts, never an editor's exit code; restore via
git checkout HEAD --plus the explicit path, proven with an emptygit diff HEAD.Coordination
scripts/check-doc-snippet-types.mjsis contended. PR #6202 (card #5259) is armed and edits itsUNGATED_DOCS; #5174 is dispatched and will edit its collector. Branch from amainthat carries #6202, keep your diff to the exit path, and note the overlap in your PR body per AGENTS.md's shared-surface guidance.Root vitest only (objectui#3378). Exit codes captured by redirect before any pipe.⚠️ Derive the gate list from
package.jsonand.github/workflows/rather than from this order — and note the npm script ischeck:doc-snippets;check-doc-snippet-types.mjsis the script file.⭐ Derive which tests read this script and run them —
scripts/__tests__/check-doc-snippet-types.test.tsexists and will have opinions about the exit path. That scope gap reddened PR #6189 tonight.⛔ The card stays open after this lands — the scan-surface half is still owed. Use "Part of #5465", never a closing keyword.
Generated by Claude Code
yinlianghui-tw commentedon Aug 25, 2026
os-dev-report
{ "issue": 5465, "status": "done", "branch": "claude/issue-5465-unbuilt-worktree-loud-fail", "pr": "https://github.com/objectstack-ai/objectui/pull/6215", "premise_still_valid": false, "summary": "The premise as WRITTEN is falsified, and the premise the three incidents actually describe is real and is fixed. The card and the dispatch order both name a FALSE GREEN — the gate printing 'The snippet program was NOT run' and exiting 0. On a fresh unbuilt worktree at the branch base 39f4309cf it exits 1, not 0 (captured by redirect before any pipe); the same is true at the card's own filing commit 77f846a8b and at the file's first commit 3cf4de0bf, so that branch has returned 1 since the gate was written. What DOES reproduce the reported 0, in the same worktree on the same run, is a pipe: 'node scripts/check-doc-snippet-types.mjs 2>&1 | tail -3; echo $?' prints 0 because tail succeeded. The card recorded a shell measurement artifact, not the gate. What is real is the OTHER half of the same complaint, and all three cited incidents (#6171, #6186, #5259) are about it: exit 1 for 'I could not run' is indistinguishable from exit 1 for 'I ran and found errors'. Fixed by giving the precondition path (unbuilt / source-typed packages) and the harness-control path their own exit code 2, with a banner naming the code and the exact rebuild command; a verdict about the documents keeps returning 1; 0 is unchanged; --build-filter deliberately stays lenient and keeps exiting 0 on an unbuilt tree because the workflow runs it one step BEFORE the build. Design choice was 'fail loudly always' rather than 'lenient when composed', because there is exactly one caller (doc-snippet-types.yml, plus the package.json alias to the same script) — there is no pipeline to be lenient toward. SIBLING SURVEY (reported, not fixed): this is ONE GATE's bug, not a family's. All 28 scripts/check-*.mjs were surveyed and none has a 'cannot examine -> exit 0' shape; check-eager-closure-budget.mjs already uses exactly this 1-vs-2 split ('over budget' vs 'the gauge produced nothing'), check-published-dist-tooling.mjs reports no-build-output as a finding (objectui#4846: an unexaminable package must not read as a clean one), check-changeset-presence.mjs throws on a missing .changeset/, and check-node-esm-load.mjs builds the packages itself (its --specifiers-only exit 0 is an explicit opt-in flag). So exit 2 here is the repository's existing convention, not an invention. Scope held: the collector, DOCS_ROOT and UNGATED_DOCS are untouched, so the scan-surface half stays serialised behind #5174 and this card stays open (PR says 'Part of #5465', no closing keyword).", "tests": "All exit codes captured by redirect BEFORE any pipe; heavy legs under scripts/pm/os-verify-lock.sh (VERDICT lines read, never a bare $?). DIRECTION 1 (defect reproduced first): unbuilt worktree at base 39f4309cf -> EXIT=1 with 'The snippet program was NOT run...'; pipe artifact 'EXIT_AFTER_PIPE=0' reproduced on the same tree. DIRECTION 1b (after): EXIT=2 with 'PRECONDITION NOT MET (exit 2) ... This is \"I could not run\", NOT \"I ran and found errors\" (exit 1)'. DIRECTION 2 (no false red): after building the gate's own closure (pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2, BUILD_EXIT=0, 32 tasks, dist/index.d.ts confirmed on disk in THIS worktree) the gate is GATE_EXIT=0 — 'Semantic phase: 251 of 251 block(s) judged, 0 failed'. DIRECTION 3 (distinguishable): one fabricated export appended to content/docs/api/schema-reference.md -> GATE_EXIT=1 with '[semantic] ...TS2305: Module \"@object-ui/core\" has no exported member __gateAblationFabricated__', '252 of 252 block(s) judged, 1 failed' — so 2 and 1 are told apart by both code and message; mutation proven on disk by anchored counts (marker 0 -> 2, git diff --numstat 5 0), restored under trap ... EXIT INT TERM via git checkout HEAD -- <path> leaving git diff HEAD empty and grep -c back to 0. DIRECTION 4 (CI unaffected): doc-snippet-types.yml derives the filter, builds it, THEN invokes the gate; on the same unbuilt tree --build-filter still exits 0 and emits the 20-package filter; a new test now pins that step order mechanically. ABLATION (non-vacuity of the new tests): couldNotRun 2 -> 1 mutated on disk, proven by anchored counts (grep -c 'couldNotRun: 2,' 1 -> 0 and 'couldNotRun: 1,' 0 -> 1) BEFORE running; VITEST_EXIT=1 with exactly the distinctness test red (1 failed | 35 passed), and the gate on the unbuilt tree fell back to GATE_EXIT=1; restore leg proven by empty git diff HEAD and the marker count back to 1. No rebuild is owed on this leg and none was done: the suite imports ../check-doc-snippet-types.mjs directly as source, so dist/ is not on that resolution path. Recorded honestly: an EARLIER ablation attempt used the same git-checkout-HEAD trap while the fix was still UNCOMMITTED, which reverted the whole change — that run reddened 5 of the new tests (useful evidence that the assertions fail when the change is absent), but the reading was redone from a committed state. GATE BATCH at d98b816dc (tree clean), derived from package.json + .github/workflows/: check:doc-snippets EXIT=0; pnpm type-check:scripts EXIT=0; pnpm check:control-bytes EXIT=0 ('OK (scanned 5124 tracked text file(s))'); check-entry-guard.mjs EXIT=0; check-changeset-presence.mjs EXIT=0 ('No source of a released package changed in this range, so no changeset is owed'); pnpm lint:root EXIT=0 (28 pre-existing warnings, 0 errors — eslint on the two changed files alone: 0 problems); root vitest over check-doc-snippet-types + check-doc-fence-languages + scripts-type-check + entry-guard-wiring EXIT=0, 78 passed (the suite went 30 -> 36 tests). RE-VERIFIED after merging origin/main (which now carries #6202 as 7da7b8a1d, plus #6189/#5867 batch 5): merge was CLEAN, branch diff still exactly 2 files / 155 insertions / 8 deletions, union re-run at 13a1abd0b — check:doc-snippets EXIT=0 ('267 of 267 block(s) judged, 0 failed'), type-check:scripts EXIT=0, changeset-presence EXIT=0, vitest 56 passed, control-bytes EXIT=0. 13a1abd0b is the pushed head.", "open_questions": [], "out_of_scope_findings": [] }Two things the PM should read past the JSON
$?after a pipe), not gate behaviour. The gate has exited 1 on an unbuilt tree since its first commit. The pain the three incidents actually reported is ambiguity between two exit 1s, which is what shipped here.check-eager-closure-budget.mjs. No sibling gate needs a follow-up card; nothing was filed.The card stays open: the
skills/scan-surface half is untouched and still owed behind #5174.Generated by Claude Code
Generated by Claude Code
os-project-manager commentedon Aug 30, 2026
H8 配对写入:
pm:dispatched→pm:blockedBlocked-by: #5174
交付 PR #6215 已于 2026-08-25T02:33:45Z 合并,本卡仍挂
pm:dispatched,已挂 5.5 天。按 dev report(
5404130318)末句定级,原文:... | tail -3; echo $?读到了tail的退出码。真正的痛点是两个 exit 1 分不开(「我跑不了」vs「我跑了且发现错误」),这正是本次交付修的东西 —— 前提条件路径现在返回自己的 exit 2 并打印重建命令。⇒ 派发剩余半边时请勿再引用那句 exit 0。本笔是维护者指令下的 H8 清账(2026-08-30,session
session_014kugUSM5M5fJBsk1f8KdtN):⛔ 未改卡面范围、未动 assignee。Generated by Claude Code
claude commentedon Sep 4, 2026
Premise update from the skills seat (session
session_019RfFHiRCSs3JXLK4cwcfox, os-steve, 2026-09-04T22:0xZ) — recorded here as the dev on objectui#6856 asked; no action requested of this card's holder."Nothing type-checks
skills/" is now only partly true:scripts/check-skill-examples.mjs(PR #7427, 2026-09-03) compiles skill guide fences against the built types — but opt-in, only fences carrying anos:checkmarker comment; unmarked fences are ignored by design. Onmaintodayskills/objectui/guides/mobile.mdhas four TypeScript fences and none is marked, so the block objectui#6856 was about (already corrected by PR #7098) is still compiled by nothing. The gate's floor is shrink-only, so marking fences is the intended growth direction; the skills lane files the marker work as its own small card.Generated by Claude Code
claude commentedon Sep 6, 2026
Backfill —
Blocked-by: #5174written as the first body line (incremental backfill, one card this round).domain:devx @ objectuiexecution seat, PM sessionsession_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T02:30Z. ⛔ No label, grade or assignee change; the blocker is the one the H8 pairing comment5469443495already named (2026-08-30) — it was only ever in a comment, so the body-only unlock scan could not see it. Body read raw over REST, inspected for tag-shaped fragments (none), written, read back byte-exact.Not discharged: #5174 is OPEN and in flight (batch 12 dispatched this round). The scan-surface half of this card stays behind it; the loud-fail half the 2026-08-25 note called "arguably more urgent" and "not blocked by #5174" is a scoping question for triage, ⛔ not taken here.
Generated by Claude Code
os-try-charles commentedon Sep 19, 2026
Finding (PM patrol,
domain:devx@ objectui — R84): this card's only declared blocker closed twelve days ago, and the lock is still on. Lifting it would produce a decision, not a round.Measured
2026-09-19T03:3xZagainst the live API;main@c255b38384384175249f2811c41155d3d03577ca.The stale lock
This card's body opens with exactly one blocker line:
objectui#5174 is closed,
state_reason: completed, at 2026-09-07T11:33:26Z. This card has carriedpm:blockedfor the twelve days since.And this card's
updated_atis 2026-09-06T02:30:16Z — one day before its blocker closed. A label write bumpsupdated_at, so that timestamp is the measurement: nothing has written to this card since the blocker it declares went away. No sweep reached it.That is the opposite of the error this seat reported yesterday on objectui#7896 (
5738268574), where a card was unlocked while one of its two declared blockers was still open. Here a card stayed locked while its only declared blocker had closed. ⛔ Whether one mechanism produced both, I did not measure — the timeline read that would settle it is denied to this seat. What is measured is the state, in both directions.Radius
The population is the 17 open issues carrying
domain:devx+pm:blockedtoday:Blocked-by:linepm:queue, open) ⇒ correctly blockedBlocked-by:line at all⛔ I did not read any sweep's source and claim no mechanism. The 15 that declare nothing are the shape already open to the maintainer as a lane-wide item; this is that shape measured inside one lane.
⛔ Unlocking this card would not make it dispatchable — two independent reasons
1. It is assigned.
assignee: yinlianghui-tw. This seat does not take assigned cards.2. Its premise has changed shape. Run against
maintoday:The hole this card reports is still real.
scripts/check-doc-snippet-types.mjscollectscontent/docs(now both.mdxand.md—DOC_EXTENSIONS = ['.mdx', '.md'], which was check-doc-snippet-types.mjs collects only.mdxunder content/docs — 40.mdguides are uncovered with no UNGATED_DOCS entry, contradicting its own "covered by default" rule #5174's fix, the half this card was waiting on), theapps/<app>/docstrees, everyREADME.mdunderpackages/at any depth, twodocs/subtrees and the rootREADME.md.skills/objectui/**is in none of them.But the gate's own docblock now names that tree and rules on extending it:
and draws the distinction against this card's own blocker, by number:
So the half of this card that was about silence was discharged by #5174: the tree is now declared debt with a named owner. What remains is the extension itself, and the gate has recorded that it is a decision for the skills surface's owner, never a root move — the same disposition already carried by #9445 and #9380 in this lane.
Also still true and still worth a triage answer, unchanged by any of the above: this card's closing note that
pnpm check:doc-snippetsprintsThe snippet program was NOT runand then exits 0 in an unbuilt worktree.⇒ Recorded, not acted on. ⛔ No label touched — the six-state belongs to triage, and this seat does not grade. The reading for whoever does: the lock is stale, and lifting it yields a decision, not a round.
—
domain:devx@ objectui execution seat (objectui#5748), sessionsession_015h79niBMyoB1xcaQje3uizGenerated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
关闭
completed并取消指派 —— 卡里两半都有了着落:"未构建时不报失败"已由 PR #6215 修好;skills/的代码样例现在由专门的检查按标记编译分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T04:33Z。objectui 清理第四批:本席读完了卡面与全部 9 条评论,把结论和理由报给维护者。本卡有指派人,维护者答「第④批 同意」,明确同意关闭并取消指派。本卡的两半
check:doc-snippets在未构建的树上的退出码:PR fix(scripts): doc-snippet gate exits 2 when it could not run, 1 when it found errors #6215(2026-08-25T02:33Z 合并)已处理。dev 实测纠正了卡面的前提:它原本退出 1 而不是 0,"0"是| tail之后读$?读到的管道退出码。真正的问题是"跑不了"和"跑了发现错误"都是 1、分不开。现在"跑不了"单独返回 2,并打印重建命令。skills/:它等的 check-doc-snippet-types.mjs collects only.mdxunder content/docs — 40.mdguides are uncovered with no UNGATED_DOCS entry, contradicting its own "covered by default" rule #5174 已于 2026-09-07T11:33Z 关闭(.md现在也会被收集)。skills/objectui/**另有专门的检查:scripts/check-skill-examples.mjs(PR feat(gates): check the marked fenced examples in the skill guides #7427)按os:check标记把 skills 里的代码块编译进去,标记只增不减。main(0cf2d6644b)实测:skills/objectui里 ts/tsx/typescript 代码块 113 个,带os:check标记 88 个,约 78% 已被编译。skills/objectui/**是受控的发布面,要不要让文档检查扫它,由它的所有者决定。为什么以 completed 关闭
卡担心的是"agent 抄进用户仓的样例没人编译过"。这件事现在由
check-skill-examples.mjs负责,并且覆盖面在按标记增长;另一半也已修好。devx 席位 2026-09-19T03:33Z 指出本卡的阻塞早已解除却一直没人来关,指派也一直挂着。以后
继续给
skills/objectui/guides/*.md里还没标记的代码块加os:check,归 skills 车道。发现某个没标记的样例误导了用户或 agent,就在那里补标记并修样例。标签与指派:取消指派
yinlianghui-tw;摘pm:blocked;以completed关闭。Generated by Claude Code