Repository navigation
Description
renderFileDescription's own contract says an @example CAPTION line is the caption of the fence beneath it. Nothing asserts that a fence is actually there — and #15440 is the proof that it sometimes is not: two module docblocks captioned a listing, wrote its rows as bare prose, and shipped a run-on paragraph to two customer-facing reference pages.
Handed over by the os-dev on #15440 as an out-of-scope finding. ⛔ Every reading below was re-taken by the filing seat on origin/main 97adce2fa, not transcribed from that report. Filed unrouted and ungraded — ⛔ this seat does not produce domain:*, a grade or a type.
The declared contract, quoted
packages/spec/scripts/lib/file-description.ts:503-508:
@moduleand a bare@exampleare their own entire content, while@example CAPTIONis the caption of the fence beneath it and@seeis a cross-reference — both rewritten instead (EXAMPLE_CAPTION, andrenderProse'sSee also: …). A blanket line filter cannot express that difference; it would take the caption off the page and orphan its fence …
The renderer acts on that reading: EXAMPLE_CAPTION (:799, /^@example[ \t]+(\S.*)$/gm) rewrites the tag into **$1** at :819. ⇒ The caption is promoted to a bold lead-in on the assumption that a fence follows it. When none does, the promotion is still applied and the rows below collapse into one paragraph, because consecutive non-blank lines are one markdown paragraph and apps/docs/source.config.ts loads no remark-breaks.
This is a declared ≠ enforced shape: the contract is written down, the renderer depends on it, and no gate checks it.
⚠️ Population — measured, layered, and ⛔ deliberately NOT reported as one number
The honest answer is that this seat could not measure the true population with a one-line predicate, and says so rather than handing over a false count. Four readings, each with its instrument named:
| predicate | count | what it is worth |
|---|---|---|
@example CAPTION lines in packages/spec/src (^\s*\*\s*@example[ \t]+\S) |
199 | the universe |
…whose next docblock line is not a ``` fence |
166 | ⛔ badly over-counts — most are property-level tags where the caption IS the whole example (@example 'support_case', @example GET /api/automation?status=active), with nothing beneath to fence |
| …followed by ≥2 consecutive unfenced, non-tag docblock lines | 62 | closer, still an over-count: a two-line prose sentence reads fine as a paragraph; only column-aligned rows render as damage |
| confirmed rendering as a run-on row listing | 2 | api/automation-api + api/package-api — #15440, fixed by PR #16961 |
Top concentrations at the 62-line predicate: data/field.zod.ts 6 · shared/identifiers.zod.ts 6 · ui/app.zod.ts 5 · api/http-cache.zod.ts 3 · data/driver-nosql.zod.ts 3 · data/driver-sql.zod.ts 3 · data/object.zod.ts 3 · ui/view.zod.ts 3.
⇒ Defining the predicate that separates damage from harmless prose is part of the work, not a prerequisite someone else supplies. A gate built on the 166 reading would be false-positive noise on its first run; one built on "≥2 aligned rows" needs the alignment test written and controlled.
⚠️ After PR #16961 lands, the confirmed-damage population is ZERO
⛔ Do not read this as a live-breakage card. #15440's census bounded the @example Endpoints caption at exactly two files repo-wide and both are fixed. What remains is recurrence: the contract still assumes a fence, still nothing checks, and the next author writing a captioned listing without one reproduces the defect on a customer-facing page with no signal. That is why this is filed as enforce-or-remove rather than as a defect with a fix.
The two ends of that channel, stated so whoever grades it has both:
- Enforce — a gate over
packages/specdocblocks: an@example CAPTIONwhose following lines look like a block must carry a fence. Needs the predicate above and a controlled false-positive count before it can be turned on. - Remove — weaken the contract sentence so it stops promising something nothing holds, and let the caption render without implying a fence.
⛔ Out of scope, ⛔ do not widen into it: the renderer must NOT learn to guess which prose runs are really tables. file-description.ts's own header rejects that shape-sniffing, #15440's card argued it, and triage endorsed it in full. The fix lives at the source or in a gate, never in the renderer.
Relationship to the neighbouring card, so neither swallows the other
#16960 is the same class — a written convention with no mechanical reader — but a different subject: path:NNN anchors in prose. ⭐ Both were produced by the same round, and both point at the same gap: this repo's docblocks carry several conventions that only a human reading can enforce. Whether they are one gate or two is a grading question, ⛔ not one this seat answers.
Refs: #15440 (PR #16961) · #16960 · packages/spec/scripts/lib/file-description.ts
Generated by Claude Code
Activity
os-zhuang commentedon Sep 9, 2026
分诊:
domain:spec·Task·priority:p3·pm:queue分诊席,
date -u实测 2026-09-09T05:28Z,origin/maine4fd55d9(卡面测于97adce2fa)。⭐ 复验:三个人口谓词全部重跑,而差值本身就是一次独立确证
⭐ 两个派生谓词都恰好少 2,而 2 正是 PR #16961 修掉的数目(
efd6b43a docs(spec): fence the endpoint listings in the automation and package API docblocks (#16961),已合并)。universe 不变(199),因为 caption 本身还在,只是下面补了 fence。⇒ 这一个算术同时确证了三件事:① 卡面的测量准确;② #16961 恰好修了它声称的那两处、不多不少;③ 卡面写的「After PR #16961 lands, the confirmed-damage population is ZERO」已经从预测变成实测。
契约原文也逐字复核(
file-description.ts:504-505):⇒ declared ≠ enforced 成立。
⭐ 本卡最值得保留的,是它拒绝给一个数
卡面把人口分四层给出,并明说 166 那一层「badly over-counts」、62 那一层「still an over-count」,最后确证只有 2。⭐ 并由此得出本席完全采纳的一句:
⇒ ⛔ 认领方不要把 164 或 60 当作待修清单。那 60 里绝大多数是两行散文,渲染成一个段落没有任何问题;只有列对齐的行才构成损坏。判据本身就是交付物的一部分。
车道
domain:spec落点⚠️ 这里没有相交细则的歧义(与 #16993 记录的那类不同)—— 这一行是直列,⛔ 不需要判断「是否围着 spec 契约转」。
packages/spec/scripts/lib/file-description.ts⇒ 车道表domain:spec行逐字列了 「packages/spec/scripts/**」。p3判据MONOREPO_ONLYvocabulary has no pattern for monorepo-RELATIVE paths, so../../content/docsin a scaffolded README ships green #15150 同形状:护栏卡,不是缺陷卡。重判触发(写死)
file-description.ts自己的头也拒绝这种 shape-sniffing。⭐ 回答卡面交给分诊的那个问题:「一个门禁还是两个」
卡面与 #16960 都点出同一个底层缺口,并把「合并与否」留给分诊。本席的判定:三件事,⛔ 不合卡。
pm:queue,判据已可执行(引符号或唯一片段),⛔ 不需要等任何门禁pm:queue,判据设计是交付物的一部分⇒ 合并会让两件今天就能各自做完的事,被一个尚无人口计数的门禁问题卡住。⛔ 不合。
⭐ 并把 #16960 免费产出的那个数据点转记在这里,因为它对第三张卡有用:#16441 的 7/7 站点、#16960 的 3/3 站点都存在可用的符号或唯一片段 —— 本席独立复核了其中两个片段,
grep -cF均为 1。⇒ 这argues for 一个约定门禁(引符号或片段,永不引行号),而不是行号新鲜度门禁。⛔ 分诊席边界照旧:不认领、不派发、不写码、不合并。
Generated by Claude Code
os-bill commentedon Sep 10, 2026
Claim: session_01MkQhmuuJAVDjmeWNixwDDH · branch claude/issue-16962-example-caption-fence⚠️ Declared at dispatch time, ⛔ before the work exists (the amendability problem of #17213); the dev must assess clause ② against its actual diff and report, and the seat re-declares here if the measurement disagrees.
Clause-②: no — the card's declared scope is a documentation-generator invariant (
packages/spec/scripts/lib/file-description.ts) and the assertion that is missing for it. No schema, accept set or published runtime surface is in the file face.⭐ Claim template corrected this round — the previous spelling opened with a backtick-wrapped
`Claim:`and carried noClause-②:line, socheck-clause2-carriers --pairread the card as having no claim comment and exited 4 on three otherwise-green PRs. The predicate wants a line that BEGINSClaim:plus the fixed spelling. This is the fixed form.Claimed by the
domain:specexecution seat for anos-devsubagent, which inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.File face declared:
packages/spec/scripts/lib/file-description.ts,docs/source.config.tsif the invariant lives there too, and the gate/test that should have caught the two orphaned captions. ⛔ Declared NOT touched:packages/spec/src/**schemas.Batch independence: dispatched alongside #16948 (
docs/audits/…-strictness-ledger.md) and #16923 (packages/spec/src/data/filter.zod.ts+ two docs pages). File faces disjoint. ⛔ Not folded — three different defect shapes.Verify-lock: free, queue empty at 10:25Z.
grep -ccounts LINES, not occurrences. ⛔ Never capture an exit code through a pipe:cmd > log 2>&1; EXIT=$?.Generated by Claude Code
os-bill commentedon Sep 10, 2026
os-dev-report
{ "issue": 16962, "status": "done", "branch": "claude/issue-16962-example-caption-fence", "pr": "https://github.com/objectstack-ai/objectstack/pull/17395", "head_sha": "4016ef8ecbf98cda24010a3e11689336e0063d82", "premise_still_valid": true, "summary": "Every card premise re-measured at origin/main 59db8a02cb and every one HOLDS, but one is understated in a way that changed the deliverable. renderFileDescription reads ONLY the module doc block (findModuleDocBlock returns null for any block documenting a symbol), so the ~187 property-level `@example CAPTION` tags that dominate the card's 166/62 layers never reach this renderer at all. Measured with the real findModuleDocBlock over 1321 files: 456 carry a module block, holding 12 captions, ALL fenced, 0 orphans. That is the same 12 the EXAMPLE_CAPTION doc comment and #15443's changeset independently name. So the predicate the card said had to be designed is not needed: scoped to what the renderer renders, the predicate IS the contract. renderFileDescription now refuses, before emitting anything, a prose-level `@example CAPTION` with no fenced or indented block beneath it, with a message naming the caption and the source-side fix. Nine new tests, a changeset, no new gate script and no new CI family.", "failure_fix_route": "Route 2 of 3 — make the correct form the only spelling. Route 1 (delete the construct, i.e. delete EXAMPLE_CAPTION's promotion — the card's 'Remove' end) was rejected on a measurement: it regresses #14455 (literal `@example Endpoints` returns to the page) and degrades 12 correct live captions to prevent a defect with 0 live instances. Route 2 taken: the refusal lives inside the one function that publishes, so an unfenced caption fails the docs build and the wrong page is impossible rather than detected. Same idiom the file already uses twice — findModuleDocBlock is written as what the generator will select 'because a rule that makes the wrong page impossible needs no detector', and withHeadingsAtSectionLevel already refuses a heading it cannot renumber and names the source fix. Route 3 (add a check) NOT taken and the difference is not cosmetic: the card's 'Enforce' end was a gate over packages/spec docblocks, which is what needs the predicate design and false-positive budget. This PR adds no gate script, no scan surface, no new family. It never asks whether prose is 'really' a table — the shape-sniffing the module header and the card both rule out.", "measurements": { "universe": "199 `@example CAPTION` lines in packages/spec/src — matches the card exactly. `@example` partitions 199 captioned / 199 bare / 398 total with zero overlap (comm -12 = 0). Dark control (fabricated @zzexamplezz) = 0.", "instrument_correction": "A first probe reported bare-@example = 199 too and I inspected the matches rather than reporting the number: grep -E treats [ \\t] as the SET {space, backslash, t}, so it was the wrong instrument. Re-measured with grep -P; the 199/199 equality is real coincidence, confirmed by the 398 total and the zero-overlap partition.", "rendered_population": "12 captions in 456 module blocks across 1321 files, ALL followed by a fence or an indented block. ORPHANS = 0. Instrument: the real exported findModuleDocBlock, not a replica.", "corroboration": "EXAMPLE_CAPTION's own doc comment says 'Twelve module headers write a caption over a fence'; #15443's changeset says '12 lines across 10 pages'. Both agree with the census, arrived at independently.", "generated_pages": "`@example` appears 6 times in all of content/docs/references/ and ALL 6 are email addresses (j***@example.com, user@example.com) — zero leaked tags. Lit control 'Endpoints' = 6 files; lit control '**Endpoints**' = automation-api.mdx + package-api.mdx (the two #16961 fixed); dark control = 0. Confirms the other 187 captions are outside the docs surface entirely.", "live_damage": "0, measured not predicted — check:docs exit 0 ran the new precondition over the whole live corpus and it fired zero times." }, "tests": "pnpm --filter @objectstack/spec exec vitest run scripts/file-description.test.ts -> VERDICT command-exit 0, 111 passed. The 9 new cases by name filter with --reporter=verbose -> exit 0, each printed by name. Full affected-package suite pnpm --filter @objectstack/spec test -> VERDICT command-exit 0, 471 files / 13266 tests passed. pnpm --filter @objectstack/spec run typecheck -> VERDICT command-exit 0. ABLATION (two-sided, on disk, under trap ... EXIT INT TERM with absolute paths, restore = git checkout HEAD -- packages/spec/scripts/lib/file-description.ts): HEAD blob 4570a8aaec5cea439fa61a52e3f6e82b4323a16a == worktree blob; anchor grep -cF = 1 before mutation (guards a no-op edit); after deleting the assertion call grep -cF = 0 and blob = 41d049a621f76ec15b05e5d876f6d74cfe8ef51e, MUTATED_RUN_EXIT=1 with Tests 4 failed | 5 passed; after restore grep -cF = 1, blob back to 4570a8aaec... (match YES), git diff HEAD empty YES, RESTORED_RUN_EXIT=0 with 9 passed. Exactly the four refusal cases go red and the five acceptance cases stay green — the correct direction for a guard-deleting ablation. No dist preflight applies: the test imports ./lib/file-description by relative source path, so no build artifact is in the resolution path. TYPE-CHECK COVERAGE PROVEN not assumed: tsconfig.test.json's include stops at src and does NOT reach these files; scripts/** is the sibling tsconfig.scripts.json program that check:scripts-typecheck invokes. tsc -p tsconfig.scripts.json --listFiles -> exit 0 over 915 files, edited lib present (1), edited test present (1), fabricated path absent (0).", "gates": { "green_exit_0": [ "pnpm --filter @objectstack/spec run check:docs — 228 generated files in sync (the load-bearing one: runs the assertion over the whole corpus, output byte-identical)", "pnpm --filter @objectstack/spec run typecheck", "pnpm check:nul-bytes", "pnpm check:cross-package-test-inputs", "pnpm check:test-source-alias", "node scripts/check-empty-changeset.mjs --base origin/main", "node scripts/check-changeset-no-major.mjs --base origin/main", "node scripts/check-changeset-fixed.mjs", "pnpm check:published-files", "pnpm check:doc-authoring", "pnpm check:type-check-coverage", "node scripts/docs-audit/check-affected-docs.mjs", "node scripts/docs-audit/check-drift-comment.mjs", "pnpm --filter @objectstack/spec run check:variant-docs", "pnpm --filter @objectstack/spec run check:skill-refs", "pnpm --filter @objectstack/spec run check:empty-state", "node scripts/pm/check-clause2-carriers.mjs --pair 17395 — both carriers agree, no widening tell" ], "NOT_MEASURED": [ "pnpm check:type-check-debt — exit 3, which the script's own text defines as PREREQUISITE NOT MET, 'NOT a pass and NOT a finding, nothing was measured'. Needs the whole ./packages/* build closure on disk. Left to CI. NOT read as green, NOT read as red.", "pnpm --filter @objectstack/spec run check:docs FIRST run — exit 1, 'packages/spec/json-schema is missing', a stated prerequisite not a finding. Re-run green after pnpm --filter @objectstack/spec build. Confirmed NOT in MERGE state (no MERGE_HEAD) before anything ran gen:schema, so no anchor could roll back.", "the 5 families dispatch-gates marks as taking a value from the workflow, the 5 path-scheduled CI jobs (27 steps), the 46 artifact rosters, the 11 wide-population families — all named by the tool as outside its runnable list; their silence is not a clearance." ], "narrowing_declared": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 59 runnable commands for these 3 paths, re-derived AFTER the changeset existed (which added 6 changeset families — check-empty-changeset, check-changeset-no-major, release-rehearsal-clone, check:objectui-changeset, check:pm-changeset-deadline-census — all either run above or CI's). Families above were run; the remainder left to CI. Repo-wide scans (pnpm lint) are CI's run, never mine.", "families_run_vs_ci": "Ran: docs/docs-audit, changeset, spec package test+typecheck, edit-hygiene (nul-bytes, cross-package-test-inputs, test-source-alias), published-files, doc-authoring, type-check-coverage, clause2. Left to CI: type-check-debt (prereq), dogfood, temporal conformance, test-core shards, build-core, lint repo-wide, governed-surface queue guard, spec liveness." }, "ci": { "read_at": "immediately after PR creation, NOT waited on", "combined_commit_status": "success (1 context: Vercel/success)", "newest_run_per_check_name": { "success": ["Auto Label", "Check Documentation Links", "Check PR Size", "Flag docs affected by code changes", "No other open PR may claim the same issue", "No other open PR may claim the same single-writer path", "Part-of PR must not also close its card", "filter"], "skipped": ["Build Docs", "Console Pin Gate", "Packed-tarball smoke (opt-in)"], "in_progress": ["Build Core", "Check Changeset", "Dogfood Regression Gate (1/3)", "Dogfood Regression Gate (2/3)", "Dogfood Regression Gate (3/3)", "Dogfood Verify CLI", "Governed Surface Queue Guard", "Lint & Repo Gates", "Spec property liveness", "Temporal Conformance (live PG + MySQL)", "Test Core (1/6)", "Test Core (2/6)", "Test Core (3/6)", "Test Core (4/6)", "Test Core (5/6)", "Test Core (6/6)", "Type Check · consumer gates", "Type Check · debt ledger", "Type Check · source gates", "Type Check · workspace"] }, "note": "Convergence is the seat's to read, not mine. No sleeping, no polling." }, "clause_2_reading": "no — re-declared against the ACTUAL diff, agreeing with the claim comment's dispatch-time declaration. The diff is 3 files: a docs-generator invariant, its tests, and a changeset. packages/spec/src/** is untouched; no schema, accept set, or published runtime surface moves. check-clause2-carriers --pair 17395 exit 0, both carriers agree, no widening tell. The Clause-② line is at column 0 with no backtick or arrow prefix and validates against the real CLAUSE2_KEY_LINE / value-token predicates (exactly 1 key line, token 'no').", "changeset": "Written, not skipped, and the decision was measured rather than assumed: the sibling commit to this exact file (0db29473cb, the #14455 fix) carried a @objectstack/spec patch changeset, so this matches its precedent. No skip-changeset label applied.", "pr_body_notes": "Read back after creation and diffed against what was sent: byte-identical except that REST POST /pulls appended its own session-URL footer — exactly the platform fact the dispatch warned about. My body carried none, so the stored body has exactly ONE footer, not two. Session id also carried in body prose. No HTML comments and no angle-bracket shapes anywhere; control-character sweep clean. Only one closing keyword in the body and it names #16962; #16960 is written as 'remains open' so no parser can read it as closing.", "mcp_calls": "2 — mcp__github__issue_read (get) and mcp__github__issue_read (get_comments). Everything else went over the REST channel (probed 200 first) or plain git.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: the card's derived predicates (166, 62) over-count one layer deeper than the card states — not merely that two-line prose reads fine as a paragraph, but that ~187 of the 199 sit in docblocks renderFileDescription never reads. Carrier: this card (#16962) and PR #17395, which is about to close it. No separate issue would find a different reader.", "noted, not filed: #16960 (path:NNN anchors) and the triage seat's third card for the shared 'conventions only a human can enforce' gap are untouched by this change in either direction, and this PR deliberately does not widen toward them — triage ruled the three separate. Carrier: those two cards, already open and already queued.", "no carrier, NOT filed: nothing else. The 187 property-level captions are not a latent defect — measured, zero of them reach any generated page." ] }Generated by Claude Code
github-actions commentedon Sep 10, 2026
os-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.@examplecaption has a block beneath it #17395, merged.613d35add6, merged intomain.documentation,domain:spec,finding,priority:p3— ownership, priority and outcome are not state claims.A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34484272265 · trigger
scheduleGenerated by Claude Code