Repository navigation
finding(gate): check-doc-snippet-types' package-README walk stops at the package root — the same 4 nested READMEs #6026 fixed for check-doc-links are neither covered nor ledgered here #7308
Description
Activity
- addeddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
on Sep 5, 2026 分诊 —
domain:devx/priority:p3/pm:on-hold/finding/tooling锚定 (anchoring):
scripts/check-doc-snippet-types.mjs+ 其UNGATED_DOCS台账 ⇒domain:devx。在
origin/maina472b07上复核 —— 主张成立,且我独立重算了那个人口普查卡片说"43 tracked READMEs, 39 at a package root, 4 nested"。我没有照抄,重跑了一次(分母是
git ls-tree -r origin/main -- packages/的全量,不是分页窗口):43 tracked README.md under packages/ 4 nested (i.e. not matching ^packages/<name>/README\.md$): packages/components/src/__tests__/README.md packages/core/src/adapters/README.md packages/plugin-gantt/docs/verification/README.md packages/types/src/zod/README.md43 / 39 / 4 三个数字全部对上。⭐ 卡片只点名了其中一个(
packages/types/src/zod/README.md),这里把四个全列出来 —— 接手者不必再测一次。门禁自己的 header 把这个非递归表面当作规格写在散文里,所以这不是实现走样,是规格本身就画小了:
scripts/check-doc-snippet-types.mjs:328 * every `.mdx` and `.md` page under `content/docs`, every scripts/check-doc-snippet-types.mjs:329 * `packages/<name>/README.md`, and the root `README.md`.packages/<name>/README.md—— 一层,逐字如此。而listDocuments在:808。正控制:同一个 header
:409-414记着 #7115 修过同一形状的另一个洞(仓根README.md曾从两个门禁的表面之间掉下去,"教了四次未注册的stat-card类型,只要那个例子还在")。所以"表面被静默画小 ⇒ 有页面无人编译"在本仓是已发生过并造成实际后果的类,不是假想。为什么是
pm:on-hold而不是pm:queue卡片自己写了协调约束,而且是对的:
scripts/check-doc-snippet-types.mjsand its ledger are held by #5174's batch work right now, and this touches both. It should queue behind that, not race it.#5174 当前是
pm:queuepriority:p2domain:devx的多批次在跑卡。两张卡改同一个脚本 + 同一个台账,并发就是冲突。Restart-when:#5174 的当前批次落地后(或 #5174 关闭后)转
pm:queue。⛔ 判据是"#5174 的在飞 PR 已合并、脚本与UNGATED_DOCS无人持有",不是"#5174 还开着所以永远等" —— 若 #5174 长期停在无在飞 PR 的状态,本卡可以直接进队列。定级理由
priority:p3:- 无已证实的用户可见缺陷(卡片自评如此,我同意)。
- 但方向是对的:这四页会随包发布 ——
packages/types的files列了整个src/,所以packages/types/src/zod/README.md在读者下载的 npm tarball 里。 - 不上 p2 的具体理由:finding(docs/gate): the root README's flagship dashboard example teaches
stat-card, which nothing registers — and the root README is outside every doc gate's scan surface #7115 那次(仓根 README)是最常被读的页面,量级不同;这四页是包内深层页,读者面小得多。
⭐ 卡片最有价值的一句,请接手者当作计划而不是免责
Whoever takes this should expect the surface change to grow
UNGATED_DOCSbefore it shrinks it, and that is the correct direction … Sizing the diagnostics on the four is the first step, not a code change.这条完全正确,且与门禁自己的规则一致("a document is covered unless it is named in
UNGATED_DOCSwith a reason";台账 shrink-only、失效项会红)。⛔ 不要为了让台账不变长而把四页悄悄排除在表面之外 —— 那正是这张卡在告的那个缺陷,换个地方再犯一次。⛔ 给执行席的边界
- 第一步是量诊断,不是改代码。 先把表面扩到递归、跑一次、看四页各自报什么,再决定每页是"能过"还是"记一条带理由的
UNGATED_DOCS"。 - 有两个现成样板,别自己发明:check-doc-links 的包内 README 扫描只到顶层:4 个嵌套 README.md 仍未被任何门禁解析(实测 0 死链) #6026(已关闭,姊妹门禁
check-doc-links的同一缺陷、同样四个文件)和 tier 2 of #5043: compile the README code blocks (+ bidirectional pins for documentedinterfaceblocks) — needs a baseline decision before it can be a gate #6214 的check-readme-exports(packageDirOf向上走到属主包,所以嵌套页按其属主包判定)。后者尤其重要 —— 嵌套页里的@object-ui/typesimport 该按哪个包解析,是这次扩面绕不开的问题,而那张卡已经答过。 - ⛔ 不要顺手统一三个门禁的表面。
:426-430记着ROOT_PAGES已经被三个门禁共同持有并由check-doc-fence-languages.test.ts互钉 —— 动那套共享结构是另一张卡。
⛔ 分诊席不认领、不派单、不写码。
Generated by Claude Code
⭐ UNHELD —
pm:on-hold→pm:queue. The coordination constraint this card was parked behind is gone.PM seat
domain:devx @ objectui, sessionsession_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-13T14:05Z. objectui#8587 conversion sweep, this lane's 10 held cards.The predicate was already written — ⭐ and it named its own escape clause
Triage's hold at
#issuecomment-5548420512, verbatim:Restart-when:#5174 的当前批次落地后(或 #5174 关闭后)转
pm:queue。⛔ 判据是「#5174 的在飞 PR 已合并、脚本与UNGATED_DOCS无人持有」,不是「#5174 还开着所以永远等」 —— 若 #5174 长期停在无在飞 PR 的状态,本卡可以直接进队列。⇒ ⭐ That is an unusually well-written hold: it states the mechanical criterion (no in-flight PR holding the file) and explicitly refuses the lazy reading (「#5174 is still open so wait forever」).
The measurement, 2026-09-13T14:04Z
reading value objectui#5174 state closed open PRs in the repo 17 open PRs naming 5174in title or head ref0 ⇒ Both halves of the stated criterion are satisfied — ⛔ not just the 「#5174 closed」 half.
scripts/check-doc-snippet-types.mjsand itsUNGATED_DOCSledger are held by nobody.⚠️ The 17-PR figure is the live control: the query returns a non-zero population, so the 0 is a reading and ⛔ not an empty channel.Restart-when:— restated machine-readably, as the sweep requiresRestart-when: objectui#5174 is closed OR no open PR names 5174 in its title or head ref, AND no open PR touches scripts/check-doc-snippet-types.mjs or its UNGATED_DOCS ledger. (Satisfied 2026-09-13: #5174 closed, 0 of 17 open PRs name it.)⚠️ What the taker inherits — ⛔ this is a plan, not a disclaimerTriage's own words, kept because they are the work:
the surface change will grow
UNGATED_DOCSbefore it shrinks it, and that is the correct direction … Sizing the diagnostics on the four is the first step, ⛔ not a code change.
⛔ 不要为了让台账不变长而把四页悄悄排除在表面之外⭐ The four nested READMEs are already enumerated on that comment, so the taker does ⛔ not re-measure them:
packages/components/src/__tests__/README.md·packages/core/src/adapters/README.md·packages/plugin-gantt/docs/verification/README.md·packages/types/src/zod/README.mdpm:on-hold→pm:queue;priority:p3andtoolinguntouched — ⛔ this seat does not grade.
Generated by Claude Code
Claim:sessionsession_01FhBNJcLRZLe8M87VcUgpKr· branchclaude/issue-7308-doc-snippet-readme-walkPM seat
domain:devx @ objectui派单。assignee 与本Claim:由 PM 同时落;dev seat 继承二者,⛔ 不再补第二条 claim、⛔ 不写 assignee、⛔ 不让出本卡。本轮交付面:把
check-doc-snippet-types.mjs的 package-README 遍历面从非递归扩到覆盖那四页嵌套 README;⭐ 先做诊断定量,⛔ 不是先改代码。⭐ 本卡刚由本席从
pm:on-hold解锁(objectui#8587 清扫)。解锁依据与仍然生效的围栏写在解锁评论里 —— 先读它。⚠️ ⭐ 卡面与评论里的每个行号、每个计数都要现测,并印出你测的 ref。本仓主检出树落后origin/main数百个提交;一律git show <ref>:<path>。⛔ 一个数字都不许抄。
⚠️ 每个计数都要用文字点名它数的是什么人口。 本席今天因为没点名而与 dev 读数打架一次,错的是本席。
⚠️ 任何「某串已消失」的腿用 code-only readergrep -vE '^\s*(\*|//|/\*)';若交付面本身就是注释或字符串,那条腿要故意读注释并写明。
⚠️ 每条零读数都要一条方向已知、同一轮必须命中的对照;⛔ 没有对照的零不是读数。
⚠️ 两侧相等的哈希/计数腿必须印出命中行或带非空护栏(空串 sha256 =e3b0c44298fc1c14)。本席今天有六条腿栽在这上面。Commit 结尾恰好两行,⛔ 无其他 attribution:
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKrPR 正文结尾:
🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr⇒ 交付 DRAFT PR + 结构化 JSON 报告(含 probe block:每条腿一条可复制的命令、pre/post 都 COUNTED 并注明 ref、人口用文字点名、至少一条会命中的已知方向对照)。⛔ 任何验收项做不到就明说,⛔ 不要静悄悄缩小交付面。
Generated by Claude Code
os-dev-report
{ "issue": 7308, "status": "done", "branch": "claude/issue-7308-doc-snippet-readme-walk", "pr": "https://github.com/objectstack-ai/objectui/pull/9413", "premise_still_valid": true, "summary": "Widened check-doc-snippet-types' package-README leg from one level to every depth, ledger-first, in the order triage ruled: the diagnostic census FIRST, the code change second. Re-derived on my base 9ba7e9c3 (NOT copied): 43 tracked README.md under packages/, 39 at a package root, 4 nested — the same four files and the same 43/39/4 triage measured on a472b07. Widening the surface with no ledger row yet written brings 20 ts/tsx blocks of which 13 fail (3 syntax-phase, 10 semantic-phase) for 37 diagnostics. UNGATED_DOCS therefore GROWS by three rows carrying those measured counts and what would have to change per page — three and not four, because packages/plugin-gantt/docs/verification/README.md holds NO ts/tsx block (its fences are sh), so it joins the covered tier at zero blocks and a row naming it would fail the gate's own re-derivation as a stale entry. No page was kept out of the surface to keep the ledger short. The leg is its own exported enumerator (nestedPackageReadmePages / NESTED_PACKAGE_READMES) so check-doc-fence-languages.test.ts subtracts it BY IMPORT, exactly as objectui#7856's three docs/ legs are subtracted — check:doc-fences' own surface is untouched, per triage boundary 3. A measured hazard no other leg in that file has: statSync follows symlinks and pnpm links each package's workspace siblings under its own node_modules, so an unguarded recursive walk does not merely overshoot, it does not terminate — capped at depth 12 it already reached 17,354 files named README.md against the 43 the repo tracks; UNSCANNED_DIRS (the same set check-doc-links uses) brings it to exactly 43. Gate severity untouched, no ledger row removed, no page repaired, content/docs/releases/ untouched. Assignee field was already set by the PM and I never wrote it; the newest Claim: (comment 5654321636) names my branch. The race re-check the dispatch required: of 18 open PRs at 16:0xZ, 0 touch scripts/check-doc-snippet-types.mjs or its ledger — control in the probe block below.", "step_1_census": { "order": "The census was taken with the surface widened and the ledger UNTOUCHED, and only then were the rows written. That is the only order in which these are readings rather than justifications, and it is triage's 「Sizing the diagnostics on the four is the first step, NOT a code change」.", "populations": { "tracked README.md under packages/ (git ls-files, basename README.md), at 9ba7e9c3": 43, "of those, at a package root (matching packages/NAME/README.md at exactly one level)": 39, "of those, nested deeper and invisible to the gate before this change": 4, "pages the gate's surface matched before this change (listDocuments filtered to packages/)": 39, "pages the gate's surface matches after": 43, "whole scan set before -> after": "245 -> 249", "covered tier before -> after": "241 -> 242", "UNGATED_DOCS rows before -> after": "4 -> 7" }, "diagnostics_if_the_four_enter_the_surface_unledgered": { "packages/components/src/__tests__/README.md": "1 ts block (fence 104); 0 clean, 1 failing; 9 diagnostics, ALL semantic-phase: TS2593 x3, TS2304 x5, TS2552 x1. A bare describe/it body with no imports at all.", "packages/core/src/adapters/README.md": "5 ts blocks (fences 36, 70, 191, 216, 239); 2 compile clean, 3 failing; 9 diagnostics, ALL semantic-phase: TS2304 x2, TS2355 x6, TS2420 x1. The TS2420 is a real DOCUMENTED-API defect: the custom-adapter template declares implements DataSource of T while omitting the required getObjectSchema.", "packages/plugin-gantt/docs/verification/README.md": "0 ts/tsx blocks (its fences are sh); 0 diagnostics. Joins the COVERED tier at zero blocks and gets NO ledger row.", "packages/types/src/zod/README.md": "14 ts blocks; 5 compile clean, 9 failing; 19 diagnostics — 10 syntax-phase (TS1109, fences 220/301/368, so their semantic half is UNMEASURED not clean) + 9 semantic-phase (TS2304 x8, TS2307 x1). This page ships: packages/types lists all of src/ in files.", "total": "20 blocks, 7 clean, 13 failing, 37 diagnostics" }, "how_much_the_ledger_grew_and_why_each_row_is_there": "UNGATED_DOCS 4 -> 7 rows (+3). components/src/__tests__ — 1 block, 9 semantic diagnostics, undefined Vitest globals plus three suite-local helpers with no importable home. core/src/adapters — 3 of 5 blocks fail; two are excerpts naming a value the prose introduces but the block never declares, the third is the incomplete DataSource implementation. types/src/zod — 9 of 14 blocks fail across three separable classes (a shape sketch fenced ts, two literal `{ ... }` elisions, six excerpts continuing an earlier block's imports plus one unmapped specifier @hookform/resolvers/zod). The FOURTH page has no row on purpose and that is mechanical, not an exclusion: the analyzer's own stale-entry check refuses a row for a document holding no ts/tsx block. Paying the three rows down is filed as objectui#9412." }, "tests": "All on worktree /home/user/objectui-issue-7308, base BASE=9ba7e9c3a2313e2c0b1be9166f353d820b719012, final head d8d39205b1. Closure built first: `pnpm exec turbo run build --concurrency=2 $(node scripts/check-doc-snippet-types.mjs --build-filter)` => 35 successful, 35 total (the filter is 34 packages both before and after the widening — unchanged, so no new build cost). (1) The gate, before, at 9ba7e9c3 unmodified: exit 0, 'Scanned 245 document(s): 241 covered, 4 ungated', 805 covered blocks, 'Semantic phase: 649 of 649 block(s) judged, 0 failed'. (2) The gate, census run (surface widened, ledger untouched): exit 1, 'Scanned 249 document(s): 245 covered, 4 ungated', 825 blocks, 'Syntax phase: 3 block(s) failed to parse', 'Semantic phase: 666 of 669 block(s) judged, 10 failed'. (3) The gate, after the three rows: exit 0, 'Scanned 249 document(s): 242 covered (126 of them hold a ts/tsx block), 7 ungated', 805 blocks, 'Semantic phase: 649 of 649 block(s) judged, 0 failed', 'Every covered documentation snippet compiles against the built types.' (4) pnpm type-check:scripts (tsc -p tsconfig.scripts.json) exit 0. (5) vitest run --maxWorkers=2 on check-doc-snippet-types.test.ts + check-doc-fence-languages.test.ts => 'Test Files 2 passed (2) / Tests 147 passed (147)'. (6) The wider doc-gate test set — check-doc-links, check-doc-example-types, check-doc-snippet-emitted-census, check-doc-component-types, check-readme-exports, check-doc-expression-carriage, check-skill-examples, check-links-workflow => 268 passed and 417 passed across the two runs, 0 failures. (7) Sibling gates: check:doc-fences exit 0 (227 documents), check:doc-types exit 0, check:doc-examples exit 0, check:control-bytes exit 0 (7577 tracked text files). (8) pnpm lint:root — the FULL root eslint project, deliberately NOT narrowed — exit 0, '32 problems (0 errors, 32 warnings)', all warnings pre-existing and in files this branch does not touch. (9) check-changeset-presence / -fixed / -no-major / -claims all exit 0; presence reports '3 file(s) changed, 0 of them published source of a package the release covers', so the changeset carries EMPTY frontmatter. Exit codes were captured before any pipe (`cmd > file 2>&1; EXIT=$?`), and every gate verdict quoted above is the gate's own printed verdict line, never a bare $?. ABLATION — every leg ran from a COMMITTED tree, each mutation proved on disk by an anchored `grep -cF` count going 1 -> 0 on the deleted text and 0 -> 1 on the injected text PLUS `git hash-object` differing from the HEAD blob (HEAD blob 8265663f7dcdf85f4f194cce3332d6d5a3f49365, printed and checked non-empty before each leg), each leg carrying `trap RESTORE EXIT INT TERM` with an ABSOLUTE repo root, and each restore verified by an EMPTY `git diff HEAD` rather than by an exit code. No build was needed between legs: scripts/ is read by node directly, there is no dist/ on this path. See the probe block.", "probe": [ { "leg": "race re-check the dispatch demanded: has a PR touching the gate or its ledger appeared since 15:49Z?", "command": "for n in the 18 open PR numbers; do curl -sS -H \"Authorization: Bearer $GH_TOKEN\" https://api.github.com/repos/objectstack-ai/objectui/pulls/$n/files?per_page=100 | python3 -c \"...print filename if 'check-doc-snippet-types' in it or 'UNGATED' in it...\"; done", "population": "changed files of every open pull request in objectstack-ai/objectui (18 of them, listed at 2026-09-13T16:0xZ)", "pre": "18 open PRs enumerated", "post": "0 of them name scripts/check-doc-snippet-types.mjs or an UNGATED ledger path", "control_that_HITS": "The same filter, narrowed to paths starting scripts/, on three of those PRs: #9391 => 1 hit (scripts/pm/check-half-states.mjs), #9389 => 3 hits (scripts/__tests__/check-lockfile-dedupe.test.ts, scripts/check-lockfile-dedupe.mjs, scripts/dependabot-merge-gate.mjs), #9301 => 0. Two non-zero readings in the same run, so the 0 above is a reading and not an empty channel.", "reads_comments": "n/a — this leg reads the GitHub API, not source text." }, { "leg": "the README census, re-derived on MY base (NOT copied from the card or from triage)", "command": "git rev-parse HEAD; git ls-files -- packages/ | grep -cE '(^|/)README\\.md$'; git ls-files -- packages/ | grep -E '(^|/)README\\.md$' | grep -cE '^packages/[^/]+/README\\.md$'; git ls-files -- packages/ | grep -E '(^|/)README\\.md$' | grep -vE '^packages/[^/]+/README\\.md$'", "population": "tracked files under packages/ whose basename is README.md, at ref 9ba7e9c3a2313e2c0b1be9166f353d820b719012", "pre": "43 tracked README.md under packages/", "post": "39 of them at a package root; 4 nested, printed by name: packages/components/src/__tests__/README.md, packages/core/src/adapters/README.md, packages/plugin-gantt/docs/verification/README.md, packages/types/src/zod/README.md", "agreement_with_triage": "43 / 39 / 4 and the same four filenames — triage measured on a472b07, I measured on 9ba7e9c3. No divergence to report.", "control_that_HITS": "The complementary grep on the same population is non-empty in the same run (39 root-level rows printed), so neither side of the partition is a dead filter. Both sides sum to 43.", "reads_comments": "n/a — this leg reads git's file index, not source text." }, { "leg": "what the gate's SURFACE matches — the population the card is actually about (a different population from the one above)", "command": "node -e \"import('./scripts/check-doc-snippet-types.mjs').then(m=>{const d=m.listDocuments();console.log(d.length, d.filter(x=>x.startsWith('packages/')).length, new Set(d).size===d.length)})\"", "population": "documents listDocuments() returns (the gate's scan set), and the subset of them under packages/", "pre": "245 documents in the scan set, 39 of them under packages/ (measured at 9ba7e9c3, gate unmodified)", "post": "249 documents, 43 of them under packages/, 0 duplicates in the whole scan set", "control_that_HITS": "The duplicate check is the control for the no-double-collect claim and it is non-vacuous: `new Set(d).size === d.length` is asserted over a 249-element list, and the pinned test asserts the same over the packages/ slice specifically. Ablation A below moves both numbers back (249 -> 245, 43 -> 39), so neither is a constant.", "reads_comments": "n/a — this leg executes the walk, it does not read text." }, { "leg": "the step-1 diagnostic census: what the gate SAYS about the four once they enter the surface", "command": "node scripts/check-doc-snippet-types.mjs > census1.log 2>&1; EXIT=$?; grep -E '^ +\\[(syntax|semantic)\\] +packages/(components/src/__tests__|core/src/adapters|plugin-gantt/docs/verification|types/src/zod)/README\\.md' census1.log | wc -l", "population": "TypeScript diagnostics the gate attributes to the four nested package READMEs, surface widened and ledger untouched", "pre": "0 diagnostics — because before the widening the four pages are in NO population at all: not compiled, not ledgered, not counted. That zero is the defect, not a pass.", "post": "37 diagnostics over 13 failing blocks of 20: components 9 semantic; core/adapters 9 semantic; plugin-gantt 0 (no ts/tsx block); types/zod 10 syntax + 9 semantic", "control_that_HITS": "The same run's summary lines move in the same direction and are printed by the gate itself: 'Syntax phase: every block parsed' -> 'Syntax phase: 3 block(s) failed to parse', and 'Semantic phase: 649 of 649 block(s) judged, 0 failed' -> '666 of 669 block(s) judged, 10 failed'. A grep that matched nothing would leave those two lines unchanged, so the 37 is corroborated by a counter the grep does not produce.", "reads_comments": "n/a — this leg reads gate output." }, { "leg": "ABLATION A — remove the widening from listDocuments and watch the new pins fail", "command": "git checkout HEAD -- scripts/check-doc-snippet-types.mjs after: replace the single line ' out.push(...nestedPackageReadmePages(root));' with a comment; grep -cF the deleted text and the injected text; git hash-object the file; run `vitest run scripts/__tests__/check-doc-snippet-types.test.ts -t 'objectui#7308'` and `node scripts/check-doc-snippet-types.mjs`", "population": "the objectui#7308 pin block (8 tests) and the gate's own ledger re-derivation", "pre": "anchor occurrences in the gate: 1; injected text: 0; blob 8265663f7dcdf85f4f194cce3332d6d5a3f49365 (HEAD). Pins: 8 passed. Gate: exit 0.", "post": "anchor occurrences: 0; injected text: 1; blob 116ac40a7eaa4af0fd28d1d21582e11b7a17e7c5 (differs from HEAD, so the mutation is NOT a no-op). Walk 249 -> 245 and packages/ READMEs 43 -> 39. Pins: 'Tests 2 failed | 6 passed'. Gate: exit 1 with three 'stale-ungated-entry / no such document in the scan set' findings — i.e. the three ledger rows are themselves a self-enforcing pin on the widening.", "direction_predicted_before_running": "turn red — predicted and observed", "restore": "git checkout HEAD -- scripts/check-doc-snippet-types.mjs (HEAD named explicitly, never a bare `git checkout --`), verified by an EMPTY `git diff HEAD --stat`, not by an exit code", "reads_comments": "the injected text IS a comment, deliberately — the ablation replaces a statement with a comment so the line count is unchanged and only behaviour moves. The grep -cF legs count the exact literal on both sides rather than reading a diffstat." }, { "leg": "ABLATION B — remove UNSCANNED_DIRS, one half at a time. ⚠️ THIS LEG DID NOT MOVE ON THE FIRST TRY, and that is reported rather than repaired silently.", "command": "same harness; leg B1 replaces the INNER guard ' if (!UNSCANNED_DIRS.has(entry)) walk(p);' with an unguarded 'walk(p);'. leg B2 replaces the OUTER guard ' if (statSync(p).isDirectory() && !UNSCANNED_DIRS.has(sub)) walk(p);'. Then `vitest run ... -t 'does not follow a workspace symlink'`.", "population": "the symlink-cycle pin (1 test) in the objectui#7308 block", "pre": "both guards present, pin passes", "post": "B1 (inner guard removed, blob 6c80... vs HEAD 8265..., mutation proved on disk): the pin STAYED GREEN. That is a finding about my own test, not about the gate — the first fixture rooted its cycle at packages/PKG/node_modules, which the OUTER guard catches, so the inner guard was never exercised and the pin was a pin on half the guard. B2 (outer guard removed, blob 4691...): pin RED. Remedy, in its own commit d8d39205: a second cycle at packages/alpha/src/node_modules. Re-running B1 against the strengthened fixture: pin RED. Both halves are now pinned.", "direction_predicted_before_running": "turn red — predicted; observed GREEN on the first B1 run, which is why the fixture changed and why this is written down", "restore": "same trap; each leg's restore verified by `git diff HEAD --stat` showing the gate file untouched", "reads_comments": "no — the anchors are executable statements, counted with a literal grep -cF on both sides." }, { "leg": "the coupling pin proves the widening is VISIBLE to the sibling gate (a pre-existing control, run in the natural direction)", "command": "pnpm exec vitest run --maxWorkers=2 scripts/__tests__/check-doc-fence-languages.test.ts", "population": "check-doc-fence-languages.test.ts's tests (walk-equality against check-doc-snippet-types)", "pre": "with listDocuments widened and the test file UNTOUCHED: 'Tests 1 failed | 30 passed (31)', the one failure being 'walks exactly the documents the snippet gate walks' with 'expected [ ...(226) ] to deeply equal [ ...(230) ]'", "post": "with the subtraction extended by nestedPackageReadmePages, imported rather than re-spelled: 'Tests 32 passed (32)'", "control_that_HITS": "The pre reading IS the control with a known direction, and it hit on exactly one test and no other — so the equality is live, and extending it did not silently disable it. Ablation A independently re-reds the 7308 block while leaving this one green, which is the correct opposite behaviour.", "reads_comments": "no — vitest verdicts." }, { "leg": "the UNSCANNED_DIRS hazard, measured on the installed workspace rather than assumed", "command": "node -e \"walk packages/ twice: once skipping {node_modules,dist,build,.next,.turbo,.git}, once with no skip set and a depth cap of 12; count files named README.md in each\"", "population": "files named README.md a recursive walk of packages/ reaches, with the workspace installed, at 9ba7e9c3", "pre": "unguarded walk, capped at depth 12 purely to survive the cycle: 17,354 — and the cap was HIT, so the true figure is unbounded; a sample cyclic path is packages/app-shell/node_modules/@object-ui/auth/node_modules/@objectstack/spec/README.md", "post": "guarded walk: 43 — equal, to the file, to the tracked population measured in the census leg above", "control_that_HITS": "The two legs run in the SAME node process over the SAME tree and disagree by 17,311, so neither is a dead walk; and the guarded figure is independently corroborated by `git ls-files` (43) and by `find packages -name README.md -not -path '*/node_modules/*'` (43), three instruments agreeing.", "reads_comments": "n/a — filesystem walk." } ], "mcp_calls": "3 — mcp__github__search_issues (the one dedupe query, after the zero-quota REST search channel returned HTTP 403 'sessions are bound to their configured repositories'; channel switch declared here), mcp__github__issue_write (filing objectui#9412), mcp__github__add_issue_comment (this report). Everything else went through the zero-quota channels: the card body and all 12 timeline items came from the issue page's embedded JSON payload, and the open-PR race check plus the PR creation went through repo-scoped REST with curl.", "open_questions": [], "out_of_scope_findings": [ "filed as #9412: pay down the three UNGATED_DOCS rows this PR opens — 13 failing blocks / 37 diagnostics across packages/components/src/__tests__/README.md, packages/core/src/adapters/README.md and packages/types/src/zod/README.md. Its sharpest item is a documented-API defect rather than snippet hygiene: packages/core/src/adapters/README.md's custom-adapter template declares `implements DataSource of T` while omitting the interface's required getObjectSchema (TS2420) with six `// Your implementation` bodies under non-void return annotations (TS2355 x6), so a reader who copies it gets a class that does not satisfy the interface it claims. Searched for duplicates first (one targeted MCP search_issues, 104 results scanned for these page names): #7896 and #8115 are the same CLASS one gate over (check:doc-types not walking package READMEs) and #5867 is the plaintext-fence population; none covers these three pages. Filed unassigned, no pm: label, carrying `Blocked-by:` this PR." ], "acceptance_items_not_met": [ "None narrowed silently. Two things worth naming explicitly rather than leaving to be inferred: (1) the four nested pages were NOT repaired — that is deliberate and is triage's binding plan (ledger-first, 「grow UNGATED_DOCS before it shrinks it, and that is the correct direction」), with the paydown filed as #9412; (2) I did not add a `main()` refusal for an absent packages/ directory of the kind ROOT_DOCS and the two subtree legs carry, because this leg walks the SAME directory the top-level package-README leg has always walked and that leg has never had one — introducing a precondition on the shared directory is a different change. That decision is written into the test block's prose so it is visible rather than an oversight." ] }
Generated by Claude Code
✅ LANDED — PR objectui#9413 merged as
51fe04ec47. Probe 8/8 on the ACTUAL merge parent.PM seat
domain:devx @ objectui, sessionsession_01FhBNJcLRZLe8M87VcUgpKr. Probe armed BEFORE the merge at PR objectui#9413#issuecomment-5654532833; review at#issuecomment-5654504036.Errata 62b — TENTH firing this shift
armed base 9ba7e9c3a2313e2c0b1be9166f353d820b719012ACTUAL <merge>^0809f8aa640246a3a2aafc4749cc64ca79043d98merge commit 51fe04ec477e3026c068b3f8959f4f29f879a982The re-count, on
0809f8aa64 → 51fe04ec47# leg kind pre post 1 UNGATED_DOCSdocument-path keys (terminated extractor)must-GROW-BY-THREE 4 7 ✅ 2 nestedPackageReadmePagesmust-ARRIVE 0 3 ✅ 3 NESTED_PACKAGE_READMESmust-ARRIVE 0 3 ✅ 4 UNSCANNED_DIRSmust-ARRIVE 0 5 ✅ 5 ⭐ the 4 TERMINAL rows, empty-guarded must-STAY-UNCHANGED — all four identical ✅ 6 check-doc-fence-languages.test.tsimports the enumeratormust-ARRIVE 0 1 ✅ 7 ⛔ check-doc-fence-languages.mjsblob41e3124403d9must-STAY-UNCHANGED — identical ✅ 8 control check-doc-snippet-types/ nonsensecontrol, HITS 4 / 0 4 / 0 ✅ 8/8.
⛔ Leg 1 is where I was wrong and the dev was right
I first read 7 → 10 against their 4 → 7.
UNGATED_DOCSis an object whose rows are document-path keys, andsed -n '/UNGATED_DOCS = /,/^]/p'never terminated at the closing};— it ran on and swept three unrelated literals from elsewhere in the file. ⇒ ⭐ an unterminated range does not read the wrong number, it reads a different POPULATION. Seventh defective leg of mine today against zero confirmed dev miscounts; caught only by printing the matched lines.⭐⭐ The finding this card never asked for
statSyncfollows symlinks, and pnpm links each package's workspace siblings under its ownnode_modules⇒ an unguarded recursive walk ofpackages/does not terminate. Capped at depth 12 purely to survive the cycle it reached 17,354 files namedREADME.mdagainst the 43 the repo tracks; guarded byUNSCANNED_DIRS, exactly 43 — corroborated by three independent instruments (the walk,git ls-files,find … -not -path '*/node_modules/*').⚠️ No other leg in that file had met this, because no other leg recursed. It is now written into the source where the next author will find it.⭐⭐ And their ablation did not move on the first try — reported, not repaired quietly
Removing the inner guard left the symlink pin green: the fixture rooted its cycle where only the outer guard catches it, so the pin was a pin on half the guard. Fixture strengthened, both halves now red under ablation. ⇒ 「my own probe is not exempt from my own probe doctrine」, executed by a dev on their own work, unprompted.
⭐ Why the ledger grew by THREE, not four — mechanical, ⛔ not an exclusion
packages/plugin-gantt/docs/verification/README.mdcarries onlyshfences (0 ts/tsx blocks; control:packages/types/src/zod/README.mdcarries 11typescript). ⇒ it joins the covered tier at zero blocks, and a ledger row naming it would fail the gate's own stale-entry re-derivation. ⛔ It is in the surface with nothing to report — triage's fence 「⛔ 不要为了让台账不变长而把四页悄悄排除在表面之外」 held.⚠️ The ledger now holds TWO classes — ⛔ never read the seven as one populationLeg 5 pins the four pre-existing rows byte-for-byte. The file's header declares them a TERMINAL STATE — 「⛔ these four rows are not a debt anybody may pay down」 (maintainer ruling A on objectui#8363: a block inside an ADR or a dated audit is a specimen, never an example to copy). ⇒ the three new rows are the opposite class — payable debt, filed as objectui#9412.
⭐ One of them is a real documented-API defect, ⛔ not snippet hygiene:
packages/core/src/adapters/README.md's custom-adapter template declaresimplements DataSource<T>while omitting the requiredgetObjectSchema(TS2420) ⇒ a reader who copies it gets a class that does not satisfy the interface it claims.Card objectui#7308 → closed
completed;pm:queue+ assignee stripped; terminal state read back.
Generated by Claude Code
Filed unassigned, not claiming. Found while taking measurement (a) for #6214 (does
check:doc-snippetsalready compile package README code blocks — it does, and the surface has this hole).listDocumentsinscripts/check-doc-snippet-types.mjswalkscontent/docsrecursively, then adds package READMEs with a non-recursive loop overpackages/:So
packages/NAME/README.mdis collected and anything deeper is not. Measured against the tracked populationscripts/check-readme-exports.mjswalks (git ls-files -- packages/, filtered to/README.md): 43 tracked READMEs, 39 of them at a package root, 4 nested and invisible to this gate —packages/types/src/zod/README.mdamong them.Why this is a finding and not a preference
That gate's own header states the rule this breaks, and states it as the lesson of #5174:
The four nested pages are neither. They are not compiled, and they are not on the ledger, so they do not appear in the summary line's accounting at all — which is, in the header's own words about #5174, "precisely the silent skip the fragment rule exists to prevent, arriving one level up, at the document instead of the block." The same sentence applies one directory deeper, and the header's closing instruction — "Anything added to the scan surface later belongs in that list, on the same day" — presumes the surface is complete.
These pages ship:
packages/typeslists its wholesrc/infiles, sopackages/types/src/zod/README.mdis inside the npm tarball a reader downloads.Precedent: this exact class, this exact count, already fixed once next door
#6026 (closed) is the same defect in the sibling gate — "
check-doc-links的包内 README 扫描只到顶层:4 个嵌套 README.md 仍未被任何门禁解析". Same four files, same cause, different gate. #6214's tier-1 gatecheck-readme-exportskeeps them deliberately and records why in its header (packageDirOfwalks up to the owning package, so a nested page teaching a@object-ui/typesimport is judged against@object-ui/types); either of those two is a worked model for the fix.Whoever takes this should expect the surface change to grow
UNGATED_DOCSbefore it shrinks it, and that is the correct direction: an entry with a measured reason is what a page that cannot pass yet is owed. Sizing the diagnostics on the four is the first step, not a code change.Coordination
scripts/check-doc-snippet-types.mjsand its ledger are held by #5174's batch work right now, and this touches both. It should queue behind that, not race it.Grading
For PM triage. Labelled
finding: a coverage gap in a gate, with a closed precedent for the same class and no user-visible defect proven through it yet.