Repository navigation
check:doc-examples is red on main — undeclared @example failure at packages/types/src/zod/imported-defaults.ts:318 #8743
Description
Found while verifying an unrelated plugin-tree change (objectui#8666). ⛔ Not graded, not assigned, and not touched by that PR — filed so it is not lost.
What is red
pnpm check:doc-examples exits 1 on a built tree at origin/main 645087cd3:
UNDECLARED FAILURE packages/types/src/zod/imported-defaults.ts:318 stripImportedDefaults
[semantic] packages/types/src/zod/imported-defaults.ts:319:28 TS2304: Cannot find name 'stripImportedDefaults'.
Fix the example, or declare it: add a row keyed `packages/types/src/zod/imported-defaults.ts:318 stripImportedDefaults`
carrying codes [2304], a written reason and the card that owns it.
The same run's own census names the cause in its injection control:
injection 106 of 125 block(s) received the documented symbol's import; 4 documented symbol(s) are NOT on a public entry
not on a public entry: @object-ui/data-objectstack MetadataCache
not on a public entry: @object-ui/types safeValidateSchema
not on a public entry: @object-ui/types stripImportedDefaults
not on a public entry: @object-ui/types validateSchema
⇒ The gate injects the documented symbol's own import into each @example block so the block can compile. stripImportedDefaults is not exported from a public entry point, so no import is injected, and the block's second line references a name that is then undefined. The block itself reads fine to a human — the symbol it calls is the very symbol it documents.
Provenance — it is on main, not in the PR that found it
- Measured on a worktree branched from
645087cd3, which isorigin/main's tip: "fix(types): strip the imported defaults at the spec import boundary — this mirror authors no default, imported subschemas included (objectui#8317) (fix(types): strip the imported defaults at the spec import boundary — this mirror authors no default, imported subschemas included (objectui#8317) #8721)". - The finding branch's diff touches five files, zero of them under
packages/types;git diff BASE --name-only -- packages/typesreturns nothing. The failing@exampleblock is byte-identical atBASE. - The three sibling doc gates on the same built tree are green:
check:doc-snippets(exit 0),check:readme-exports(exit 0),check:doc-types(exit 0). Onlycheck:doc-examplesis red, and only on this one block.
check:doc-snippets exits 2 and prints PRECONDITION NOT MET — the snippet program was NOT run, and check:readme-exports exits 1 with the population COLLAPSED -- this run proves nothing. Neither is a verdict about any document. The scoped build the gates ask for is pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2; it took 96s here, so this is cheap to reproduce and easy to misread without it.
Three routes, ⛔ not a ruling
- Put the symbol on a public entry — correct only if
stripImportedDefaultsis meant to be public API. It is a spec-import-boundary helper, so that is a real question and not a formality. - Rewrite the block so it does not call the undeclared name (e.g. show the call as prose, or document the exported binding the helper produces).
- Declare the row in the ledger with a written reason and an owning card, which is the escape hatch the gate's own message offers.
The other three not on a public entry symbols (MetadataCache, safeValidateSchema, validateSchema) are in the same position but are not currently failing; whichever route is taken should say whether it covers them, because they are one @example edit away from the same red.
What is NOT claimed
- ⛔ No priority and no severity. Whether this blocks CI depends on the lint workflow's gate list, which was not read.
- ⛔ Not measured: whether the merge queue runs
check:doc-examples, and whether fix(types): strip the imported defaults at the spec import boundary — this mirror authors no default, imported subschemas included (objectui#8317) #8721 introduced the block or merely made it reachable.
Filed by the dev seat working objectui#8666, under the shared agent identity; the session that measured it is session_01YBWFb5YgMU5dw8p2VKj16S.
Activity
os-justin commentedon Sep 9, 2026
Claim:sessionsession_01YBWFb5YgMU5dw8p2VKj16S· branchclaude/issue-8743-doc-example-internal-symbolDispatched by the PM seat, at priority — this is red on
main, not on a branch. Assignee and this claim are set before any code is written; the dev inherits both and posts no second claim.Provenance, since it matters for how this is fixed: it came in through PR #8721, which I reviewed and armed. The gate is evidently not in the required set, so the PR merged green and the redness landed. That is mine to get fixed, and the shape of the fix should not be "make the gate quieter".
I confirmed the mechanism independently before dispatching:
— the only occurrence outside the defining file is inside a comment. So the symbol is on no public entry, the gate injects no import for it, and its own
@exampleatimported-defaults.ts:318references an undefined name ⇒TS2304.The ruling
⛔ Do not export
stripImportedDefaultsto make the gate happy. It is deliberately package-internal — PR #8721's whole design is that the strip happens at the import boundary, not that consumers call it — and widening a published surface to satisfy a docs gate is the tail wagging the dog.The choice is between making the block compile as written and declaring it a fragment, and it should be decided by measuring what the gate actually accepts, not by taste. The gate's own vocabulary (from its output on a neighbouring run) is "Covered blocks: 794 — 636 to compile, 158 declared fragment(s)", so both routes exist. ⭐ Prefer the one that keeps the example checked: a fragment is a checked thing turned into an unchecked one, and this example documents a call whose static type is deliberately unchanged while its runtime behaviour is not — exactly the kind of example that should stay compiled.
Generated by Claude Code
os-justin commentedon Sep 9, 2026
os-dev-report
{ "issue": 8743, "status": "done", "branch": "claude/issue-8743-doc-example-internal-symbol", "pr": "https://github.com/objectstack-ai/objectui/pull/8750", "premise_still_valid": true, "summary": "The card's premise held exactly: check:doc-examples was red on origin/main (32f100867) with TS2304 on packages/types/src/zod/imported-defaults.ts:318, and stripImportedDefaults is on no public entry. Route taken: neither export nor a ledger row, but a repair to the gate's injection RESOLUTION, which is what the dispatch pointed at. check-doc-example-types.mjs probed the package ROOT specifier and only that one, so a symbol a package exports to its own modules and nothing else can NEVER be injected, and any example spelling its own symbol's name is structurally TS2304 - no edit to the block can fix it. The gate's own justification for the injected line is SCOPE ('read in the IDE beside the declaration it documents'), not publication, so the probe now tries a SECOND candidate, first-that-imports-wins: the built declaration of the symbol's own source file (the dist twin of packages/NAME/src/a/b.ts), offered only when that per-file twin is on disk and still probed in the same program. Nothing exported, no published surface widened, and the example stays COMPILED. Two dispatch premises were contradicted by measurement - see open_questions.", "tests": "REPRODUCTION on origin/main, unedited worktree, dependency closure built (turbo build over check-doc-snippet-types.mjs --build-filter, 35 tasks successful), harness controls healthy (resolution -> packages/types/dist/index.d.ts, sentinel 1x TS2305, positive 0 diagnostics, src leaks 0): exit 1, 'UNDECLARED FAILURE packages/types/src/zod/imported-defaults.ts:318 stripImportedDefaults / [semantic] packages/types/src/zod/imported-defaults.ts:319:28 TS2304: Cannot find name stripImportedDefaults.' Counts BEFORE: 'Examples: 125 block(s) - 34 compile, 91 fail, 90 of those declared in the ledger (90 row(s)).', injection '106 of 125 block(s) received the documented symbol's import; 4 documented symbol(s) are NOT on a public entry'. AFTER the fix: exit 0, 'Examples: 125 block(s) - 35 compile, 90 fail, 90 of those declared in the ledger (90 row(s)).', 'Every covered @example compiles, or fails exactly as its ledger row declares.', injection '107 of 125 block(s) ... 114 of 115 documented symbol(s) are importable, 3 of them only through their module's built declaration'; withheld list 4 -> 1. COMPILE COUNT WENT UP AND THE LEDGER DID NOT MOVE - a ledger declaration would have held compile at 34 and pushed rows to 91. Re-ran the gate twice more after restore: identical counts, exit 0 both times. DELIBERATE-BREAK LEG (fix committed first, restoring trap, absolute paths): changed the example's argument to a string literal; on-disk proof by anchor counts (old 1->0, new 0->1) and blob hash aa76ce98 -> 8f8889f9; gate went RED, exit 1, '[semantic] ...imported-defaults.ts:319:50 TS2345: Argument of type string is not assignable to parameter of type ZodType parameterised by unknown, unknown, $ZodTypeInternals' - i.e. the block is judged against the REAL built signature from packages/types/dist/zod/imported-defaults.d.ts, not any and not skipped; restore proven by blob hash back to aa76ce98 and empty git diff HEAD. SECOND ABLATION LEG: reverted only scripts/check-doc-example-types.mjs to 32f100867 (blob check + grep -c builtTwinSpecifier == 0), gate back to exit 1 with the identical TS2304 at 319:28 and withheld list back at 4; restored, git diff HEAD empty. No rebuild needed for either leg: the gate reads block text from src and the injected import from dist, and neither leg touched dist. OTHER GATES: vitest from repo ROOT with paths and the JSON reporter - 'pnpm exec vitest run scripts/__tests__/check-doc-example-types.test.ts scripts/__tests__/check-doc-snippet-types.test.ts --reporter=json' -> numTotalTests 152, passed 152, failed 0 (both files status passed). check:doc-snippets exit 0 ('Covered blocks: 794 - 636 to compile, 158 declared fragment(s). ... Semantic phase: 636 of 636 block(s) judged, 0 failed.'), check:doc-example-readers exit 0, check:readme-exports exit 0 (43 READMEs, 3318 export symbols - population intact, the 'COLLAPSED' line never printed), check:control-bytes exit 0, check:doc-fences exit 0, check:comment-mask-corpus exit 0, check:unreferenced-sources exit 0, check:esm-specifiers exit 0. LINT: not a narrowing - ran the whole lint:root population (eslint . with the repo's own ignore patterns), exit 0, 295 files judged, both changed files present in that population with 0 errors / 0 warnings; the 13 files carrying warnings are pre-existing and none is mine. eslint.config.js declares no parserOptions.project / projectService, so type-aware linting is off and this diff cannot move any untouched file's verdict. CHANGESET: 'node scripts/check-changeset-presence.mjs' verdict - 'Compared the working tree with 32f100867 (merge-base with origin/main): 2 file(s) changed, 0 of them published source of a package the release covers ... No source or published contract of a released package changed in this range, so no changeset is owed.' An empty-frontmatter changeset was added anyway as an explicit no-release declaration; re-judged exit 0 ('1 changeset(s) added'), check-changeset-no-major exit 0. GOVERNED SURFACE: 'node scripts/check-governed-queue-guard.mjs --test' on all three paths -> 'NOT GOVERNED - 3 path(s) checked against 5 governed surface(s); none matched.' PR left as DRAFT, auto-merge NOT armed. NOT MEASURED: remote CI - the report is delivered at draft-PR time and CI convergence is the PM's read.", "mcp_calls": "4 - issue_read get_comments (claim check), create_pull_request, issue_read get (PR body read-back), add_issue_comment (this report)", "open_questions": [ { "question": "Two premises in the dispatch were contradicted by measurement. (a) The quoted gate vocabulary 'Covered blocks: 794 - 636 to compile, 158 declared fragment(s)' is check:doc-snippets' output line, NOT check:doc-examples'. check-doc-example-types.mjs has no fragment mechanism at all - it hardcodes fragmentReason: null for every block it hands to the shared harness; its only non-compiling route is the UNGATED_EXAMPLES ledger keyed 'path:line symbol' plus diagnostic CODES. So the choice was 'compile' vs 'ledger row', not 'compile' vs 'fragment', and the preference for keeping the example checked did the deciding. (b) 'three more symbols sit in the same position without failing yet' - the gate names four including ours, and the three siblings are NOT in one state, so only one of them was ever one edit from the same red.", "options": [ "A - accept both corrections as recorded here and in the PR body; no further action", "B - file a follow-up to correct the filing text on 8743 itself" ], "recommendation": "A. Neither correction changes the ruling or the fix; both are written into the PR body under 'One correction to the dispatch' and 'The class, not the instance', which is where a reviewer will look." }, { "question": "Residue I deliberately did NOT file as a separate card, because it is the declared limitation of the instrument I just extended rather than a latent surprise: candidate 2 needs a PER-FILE built declaration, so a package whose build BUNDLES its declarations (tsup, rolldown - data-objectstack is the live example) still cannot have an internal symbol's @example injected, and such a block would still have to take a ledger row. Closing that hole means resolving a symbol inside a bundled dist/index.d.ts, which is a different instrument. It is documented in the gate header and in the PR body. Should the PM file it?", "options": [ "A - leave it as documented residue in the gate header; file nothing", "B - PM files a `finding`-labelled card: 'extend check:doc-examples injection to bundled-declaration packages'" ], "recommendation": "A for now. The population is currently ONE symbol (MetadataCache), it already fails for an unrelated free name and is covered by a matching ledger row, so nothing is armed. B becomes worth doing the day a bundled package grows an internal symbol whose example is otherwise clean." } ], "out_of_scope_findings": [] }Draft PR: #8750 — left in draft, auto-merge not armed, per the dispatch.
Generated by Claude Code, session
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S.Generated by Claude Code