Skip to content

tooling: compile @example blocks in .ts sources (and code inside template literals) under the doc-snippet gate — three measured instances (#7974 useSpecGesture, #7976 DESIGN.md, #7977 mdx Output Example) share the blind spot #8258

Closed

Description

@os-zhuang

Filed by the director seat under the #7974 ruling (decision batch #70, 2026-09-07). Triage named this as a separate gate question with its own population; that population is now three cards.

The blind spot

check-doc-snippet-types.mjs compiles fenced blocks in Markdown documents. It does not read:

Scope

  • Extend the gate (or add a sibling) to extract @example blocks from exported symbols' JSDoc under packages/*/src/** and compile them against the built types, with the same ledger / exemption discipline the Markdown gate uses (UNGATED_DOCS-style burn-down if the first run is large).
  • Template-literal code: decide per case whether to extract (a fenced marker inside the literal) or to exempt with a reason; do not attempt to parse arbitrary strings.
  • First run: report the count of failing examples on this card before enforcing; enforce with a ledger that only shrinks.

Acceptance

Refs #7974, #7976, #7977, #5174.

Activity

  1. claude commented on Sep 7, 2026

    @claude
    Contributor

    Deferred at candidate selection, with the known pit recorded so the next dispatcher does not re-derive it — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-07T07:44Z. Labels untouched; this stays pm:queue.

    Why not now. The gate this card extends, scripts/check-doc-snippet-types.mjs, is the hot file of the #5174 burn-down (batches 35–37: PR #8271 in the merge queue, packages/fields/README.md and the root README.md still to go), under a strictness-region licence whose hash (2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b, the ── Fence scanning banner to EOF) every batch must leave byte-identical. An @example extractor that touches the compile harness moves that region; a sibling script that imports the harness's exports (scanFences, compileSnippets are already exported and were used as the batch-35 census instrument) does not. Same-file serial: this card waits until batch 37 lands.

    Dispatch shape when it comes up: a SIBLING script (scripts/check-doc-example-types.mjs or similar) that extracts @example fences from exported symbols' JSDoc under packages/*/src/** and feeds them to the exported compile harness — ⛔ not an edit inside the licensed region; first run reports the count on this card before enforcing; ledger that only shrinks; #7974's post-fix example is the positive control (a planted scalar direction must fail). Template-literal code (#7976, #7977) is a per-case exemption with a reason, not a parser.


    Generated by Claude Code

  2. claude commented on Sep 7, 2026

    @claude
    Contributor

    Claim: PM loop round R46 — domain:devx @ objectui execution seat.
    Session: session_01FhBNJcLRZLe8M87VcUgpKr
    Branch: claude/issue-8258-jsdoc-example-gate
    Worktree: /home/user/objectui-issue-8258
    Domain: domain:devx
    File surface: a new scripts/check-doc-example-types.mjs (a SIBLING importing scripts/check-doc-snippet-types.mjs's exported harness — scanFences, compileSnippets, analyze), its test under scripts/__tests__/, and one check:* line in the root package.json. ⛔ No edit inside check-doc-snippet-types.mjs's licensed strictness region (the ── Fence scanning banner to EOF, hash 2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b); ⛔ not .github/workflows/**; ⛔ no packages/*/src/** edit except a JSDoc @example this gate's own first run proves broken AND whose package is domain:devx — anything else is a filed finding.
    Dispatch: one os-dev subagent of this session (mode:subagent, model: opus); dispatch note follows. Same-file family serial cleared: #5174's batch 37 (PR #8312) merged at 11:32:20Z and that card is closed. Labels pm:queue → pm:dispatched and assignee set in one write, read back at 2026-09-07T11:34Z.


    Generated by Claude Code

  3. claude commented on Sep 7, 2026

    @claude
    Contributor

    Dispatched — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-07T11:35Z. The only dev in flight; #5174 closed at 11:33Z, which released this card's same-file-family serial.

    Dev. One os-dev subagent of this session on branch claude/issue-8258-jsdoc-example-gate, worktree /home/user/objectui-issue-8258, base origin/main at c84259489 or newer. Race re-read at 11:34Z: the newest Claim: on this card is 5570009153, naming that branch.

    Brief, in one paragraph. Measurement first, as the card orders: a census funnel over @example in packages/*/src/** (upper bound measured on main: 156 files, 334 occurrences) narrowed step by step — exported vs local symbol, source vs __tests__, fenced vs bare-indented, code vs prose — so the enforced population is derived rather than asserted; the count goes on this card before anything is enforced. Then the gate as a SIBLING script (scripts/check-doc-example-types.mjs) importing the Markdown gate's exported harness (scanFences, compileSnippets, analyze), ⛔ never an edit inside its licensed strictness region (hash 2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b, verified on base and head). Shrink-only ledger in the UNGATED_DOCS idiom, one row per failing example naming the card that owns it; the negative control asks whether a PASSING example is actually BOUND (an example that compiles because nothing checks it is the defect this gate exists to prevent); the positive controls are four legs on the committed tree — planted scalar RED, deleted live ledger row RED, stale ledger row RED, unmodified GREEN. Template-literal code (#7976, #7977) is decided per case under the four axes, and "no rule, exempt with a reason" is an acceptable measured answer — ⛔ no parsing of arbitrary strings.

    Premise the seat measured, which changes the card's acceptance criterion: #7974 is still OPEN and its defect is still on main (packages/mobile/src/useSpecGesture.ts:69–75, the scalar direction inside swipe), and it is labelled domain:ui — another lane's card, ⛔ not this dev's to fix. So the card's "post-fix" phrasing cannot be met as written: the gate reds on day one over a live filed defect, which is precisely what its own ledger clause is for. That example becomes ledger row one, naming #7974; when that fix lands, the row's removal is that card's business and this gate reds if the row outlives the defect. #7977 is CLOSED (completed) — its outcome is read before the template-literal call is made; #7976 is open in this lane.

    Also declared: ⛔ no packages/*/src/** edit except an @example this gate's own first run proves broken AND whose package is domain:devx; anything else is a filed finding. pnpm check:doc-snippets must stay green with its numbers unchanged (229 scanned / 229 covered / 0 ungated / 631 of 631 judged) — the sibling may not perturb the gate it borrows from.


    Generated by Claude Code

  4. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 8258,
      "status": "done",
      "branch": "claude/issue-8258-jsdoc-example-gate",
      "pr": "https://github.com/objectstack-ai/objectui/pull/8322",
      "base_sha": "c84259489150063f91a7d50db26fed44909f83e6",
      "final_head": "352b1a9b07baebfe030285115a2d473776a3e469",
      "premise_still_valid": true,
      "premise_note": "The PM's premise correction is CONFIRMED and was load-bearing. objectui#7974 is still open and its defect is live on main (packages/mobile/src/useSpecGesture.ts line 70 still carries the scalar swipe.direction), so the gate reddens on day one over another lane's card. The card's acceptance criterion assumed a repaired example; it was met by control legs vi and vi-b instead, which repair the example in a trap-protected mutation, show the gate GREEN, then plant the scalar back and show it RED. A SECOND premise moved that the PM had not measured: the template-literal half of this card is ALREADY DELIVERED on main by objectui#7864's --emit-census, which walks the same packages/NAME/src tree; see template_literal_decision.",
      "census_funnel": {
        "method": "TypeScript AST, never a regex; tooling split by check-phantom-dependencies' own TOOLING_FILE",
        "source_files_non_tooling": 1418,
        "tooling_files_excluded": 2421,
        "example_tags_found": 228,
        "tags_in_tooling_files": 0,
        "tags_on_non_exported_declarations": 15,
        "tags_on_exported_declarations": 213,
        "with_ts_tsx_fence_THE_COMPILED_TIER": 124,
        "with_only_a_non_ts_fence": 1,
        "bare_no_fence": 88,
        "bare_that_parse_as_tsx": 67,
        "reconciliation_with_pm_upper_bound": "PM read git grep -c '@example' = 156 files / 334 occurrences. That counts test files and every textual occurrence including prose; the AST finds 228 real tags, 213 on exported declarations. The funnel is printed by the gate on every run, so the enforced number stays derived rather than asserted.",
        "why_bare_is_counted_not_compiled": "MEASURED, not assumed. 67 of 88 bare bodies parse as TSX, which sounds compilable and is not: the dominant shape is a VALUE illustration on an interface property (packages/types/src/base.ts documents `type` with the body 'input', 'button', 'form', 'grid'), which parses only because a comma-separated list of string literals is a legal expression statement. Compiling it judges nothing. Same reason TS_FENCE_LANGUAGES excludes js/jsx in the sibling."
      },
      "first_run_failures": {
        "blocks_in_compiled_tier": 124,
        "compile_clean": 34,
        "fail": 90,
        "of_which_parse_failures": 5,
        "of_which_semantic_failures": 85,
        "arms_measured_before_choosing": {
          "A_strict_isolation_the_markdown_gate_rule": "115 of 119 fail; 303 of 348 diagnostics are TS2304 naming the DOCUMENTED SYMBOL ITSELF, and objectui#7974's real TS2322 is MASKED. Ledger would be 115 rows and say nothing.",
          "B_chosen_inject_the_documented_symbols_import": "85 of 119 fail. objectui#7974's TS2322 becomes visible. Ledger 90 rows (85 + 5 parse).",
          "D_refused_also_declare_free_names_as_any": "27 of 119 fail, ledger 32 rows. REFUSED: it invents 8 diagnostics of its own (6 TS2749 'refers to a value but is used as a type', 2 TS2451 redeclarations), makes every downstream check on those names vacuous, and teaches an author that referencing an undeclared name stops the gate looking. That is consumer-side tolerance, AGENTS.md commandment #0.1, one level up."
        },
        "shape_of_the_90": {
          "usage fragment naming undeclared ambient names": 66,
          "hook-body excerpt whose return sits outside any function": 7,
          "does not parse as TSX (prose-and-code mixture)": 5,
          "shorthand property standing for caller-supplied context": 4,
          "says something about the documented API": 3
        }
      },
      "ledger": {
        "name": "UNGATED_EXAMPLES in scripts/check-doc-example-types.mjs",
        "key": "path:line symbol",
        "row_count": 90,
        "reasons_hand_written": 17,
        "reasons_derived_per_row": 73,
        "rows_naming_a_card": [
          "packages/mobile/src/useSpecGesture.ts:69 useSpecGesture -> objectui#7974, codes [1108, 2322]"
        ],
        "rows_sample": [
          {
            "file": "packages/mobile/src/useSpecGesture.ts",
            "symbol": "useSpecGesture",
            "card": "objectui#7974",
            "reason": "the scalar swipe.direction this example passes is rejected by the declared SpecSwipeDirection[] (TS2322); objectui#7974 owns both halves and is on another lane"
          },
          {
            "file": "packages/core/src/actions/TransactionManager.ts",
            "symbol": "TransactionManager",
            "card": null,
            "reason": "usage fragment: references `manager`, which the example never declares"
          },
          {
            "file": "packages/core/src/utils/freeze-schema.ts",
            "symbol": "defineSystemView",
            "card": null,
            "reason": "demonstrates that the returned view is frozen by showing a push the readonly type rejects - the diagnostic IS the lesson"
          },
          {
            "file": "packages/auth/src/AuthGuard.tsx",
            "symbol": "AuthGuard",
            "card": null,
            "reason": "two sibling JSX elements with no wrapper: a render-body excerpt, not a module"
          }
        ],
        "shrink_only_proof": "The ledger reddens in THREE directions, not one, and each is proven both as a unit case on judge() and as a control leg through the real gate on the committed tree. (1) block fails + no row = RED undeclared-failure. (2) block COMPILES + row present = RED stale-ledger-row, 'the debt was paid and the row outlived it' - control leg iii, exit 1. (3) block fails with DIFFERENT codes than the row declares = RED ledger-row-drifted, 're-derive the row' - control leg v, exit 1. (4) row naming a block that is gone = RED stale. Because each row stores the diagnostic CODES, a row cannot go on covering a block whose failure changed; that is what makes it shrink-only rather than a mute button."
      },
      "template_literal_decision": {
        "ruling": "NO NEW RULE HERE, and the exemption is measured rather than 'too hard'.",
        "finding_that_decides_it": "The extraction this card imagines ALREADY EXISTS on main and already runs on this exact tree. check-doc-snippet-types.mjs --emit-census (objectui#7864) walks the same packages/NAME/src, recognises a template carrying an import, substitutes its holes and compiles it through the same compileSnippets(). Verified on this branch: 20 recognised sites across packages/cli, packages/create-plugin and packages/vscode-extension, with a diagnostic split by artefact class.",
        "per_case": {
          "objectui#7977": "no longer open; objectui#8112 corrected its prose. Nothing owed.",
          "objectui#7976": "its residue is NOT a template-parsing problem. It is packages/vscode-extension/DESIGN.md holding a hand-copied MIRROR of a template's text. Compiling that template - which the census already does, at 0 diagnostics - says nothing about whether the mirror still matches it. That card needs an equality pin between two texts, a different instrument from a type-checker, and it belongs on that card."
        },
        "four_axis": {
          "actual_business_need": "The measured need is already served by an existing instrument. A second reader of one population is how two answers start disagreeing; there is no unserved case.",
          "long_term_soundness": "Contract-first argues for one owner per question. A second extractor with its own recognition rules is the definition of a second de-facto contract.",
          "prevents_ai_error": "Two extractors that disagree make a green unreadable, which is worse than one gate with a stated blind spot. The existing census already prints its blind side every run.",
          "startup_no_scope_creep": "No pull. Implementation-first says do not build a second reader for a population that has one."
        }
      },
      "negative_control": "Answers whether a PASSING example is actually BOUND, on two different passing examples, both length-preserving so the mutation cannot be mistaken for a rewrite. (A) packages/types/src/base.ts BaseSchema: `id: 'greeting',` -> `id: 1234567890,` (identical byte length, git numstat 1 added / 1 removed) -> gate exit 1, TS2322 'Type number is not assignable to type string', naming base.ts:60 BaseSchema. (B) packages/i18n/src/utils/spec-formatters.ts formatDateSpec: `timeStyle: 'short',` -> `timeStylX: 'short',` (identical length) -> gate exit 1, TS2561 naming spec-formatters.ts:118 formatDateSpec. Both mutations proven ON DISK by grep counts BEFORE any result was read; both restored by `git checkout HEAD -- ABSOLUTE_PATH` with the restore proven by blob hash AND empty `git diff HEAD`. Conclusion: the 34 green examples are green because they are checked, not because nothing looks at them.",
      "positive_control": "All on the COMMITTED tree, each script carrying trap RESTORE_FN EXIT INT TERM (angle brackets spelled out as a word: GitHub eats tag-shaped fragments) with absolute paths. (iv) unmodified -> GREEN, exit 0, in the same session as the reds. (i) planted scalar where an OBJECT is required in a currently-green example (formatNumberSpec's format argument) -> exit 1, 'UNDECLARED FAILURE packages/i18n/src/utils/spec-formatters.ts:167 formatNumberSpec', TS2559, naming file AND symbol. (ii) objectui#7974's ledger row DELETED while its defect still exists -> exit 1, 'UNDECLARED FAILURE packages/mobile/src/useSpecGesture.ts:69 useSpecGesture', TS2322 + TS1108. (iii) a row planted for a block that compiles -> exit 1, 'STALE LEDGER ROW packages/types/src/base.ts:60 BaseSchema - this example COMPILES now'. (v) objectui#7974's example repaired but its row left alone -> exit 1, 'LEDGER ROW DRIFTED, declared TS1108 TS2322 but produces TS1108', so the row cannot outlive the defect. (vi) example repaired AND row re-derived -> GREEN exit 0; (vi-b) scalar direction planted back -> exit 1 naming the file and symbol. Legs vi and vi-b ARE the card's acceptance criterion, met literally. TWO LEGS READ VOID ON FIRST ATTEMPT AND WERE RE-RUN, NOT ACCEPTED: leg ii was handed 'objectui#7974' as its before-anchor, a string the header itself carries 3 times, so the proof reported VOID on a mutation that had landed; leg v asserted 2 occurrences of the scalar where the file has 1, so nothing was written and the run returned exit 0 - the on-disk proof caught it before the number was read. Both re-run with unique anchors.",
      "strictness_region": "UNTOUCHED and verified twice. `awk '/-- Fence scanning/{f=1} f' scripts/check-doc-snippet-types.mjs | sha256sum` = 2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b on the BASE commit c84259489 and again on the FINAL head 352b1a9b0. No export keyword was needed above the banner either: scanFences, analyze and compileSnippets are already exported, so nothing in check-doc-snippet-types.mjs was edited at all. check:doc-snippets is unperturbed: 229 scanned / 229 covered / 0 ungated / 631 of 631 judged, identical to the base reading.",
      "gates": {
        "measured_on": "final head 352b1a9b0, after MERGING origin/main 8b7ea3945 (never rebased) and rebuilding the closure",
        "vitest scripts/__tests__/check-doc-example-types.test.ts (46 cases)": 0,
        "vitest scripts/__tests__/ (119 files, 3537 tests)": 0,
        "node scripts/check-doc-example-types.mjs": 0,
        "pnpm check:doc-snippets": 0,
        "pnpm check:doc-fences": 0,
        "pnpm check:entry-guard": 0,
        "pnpm check:phantom-deps": 0,
        "pnpm check:unused-deps": 0,
        "pnpm check:comment-mask-corpus": 0,
        "pnpm type-check:scripts": 0,
        "pnpm lint:root": "0 (0 errors, 32 pre-existing warnings, none from these files)",
        "pnpm check:control-bytes": 0,
        "node scripts/check-changeset-presence.mjs": "0 - no changeset owed",
        "node scripts/check-governed-queue-guard.mjs --test on all 3 changed paths": "0 - NOT GOVERNED",
        "build closure": "pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2 -> 35/35 successful, run twice (base and merged head)",
        "ci_status": "in_progress - reported at draft-PR time as the contract requires; CI convergence is the PM's to read"
      },
      "files_changed": [
        "scripts/check-doc-example-types.mjs (new, the gate + its 90-row ledger)",
        "scripts/__tests__/check-doc-example-types.test.ts (new, 46 cases)",
        "package.json (one line: check:doc-examples)"
      ],
      "changeset": "None owed and none added. node scripts/check-changeset-presence.mjs exits 0: '0 of them published source of a package the release covers, 0 of them a manifest whose published contract moved'. The root manifest is not a published package and scripts/ is not published source. No skip-changeset label was applied - this repo's changeset guard answers by path, and it already answers 'not owed'.",
      "filed": [
        "#8320 - finding(data-objectstack): MetadataCache's @example documents a class the package's only export does not expose. Found by this gate's own export probe (TS2305 on the root specifier); the package declares exactly one export key and never re-exports the class.",
        "#8321 - finding(devx): burn down this gate's 90-row ledger; 66 are usage fragments, 3 say something about a documented API. Carries the two routes (repair batch by batch, or give @example a fragment marker the way the Markdown gate has one) since neither was in this card's file surface."
      ],
      "dedup_evidence": "REST list of open issues labelled finding, 2 pages, 172 issues. Control terms HIT, so the read is validated rather than an empty answer: '@example' matched [8258, 7974], 'example' matched [8310, 8258, 8106, 7974, 7925], 'ledger' matched [8072, 7953, 7308]. Dedup terms 'MetadataCache', 'burn-down', 'burndown', 'data-objectstack' matched nothing on either page. No /search used (403 on this seat).",
      "deviations": [
        "The card's acceptance criterion 'objectui#7974's example (post-fix) is compiled by the gate' could not be met as written because objectui#7974 is open and belongs to domain:ui / pm:queue. Met instead by control legs vi and vi-b, which repair the example under a trap, show GREEN, then plant the scalar back and show RED. The live defect is carried as a ledger row naming that card, and leg v proves the row cannot survive the repair.",
        "The card scopes the gate to 'exported symbols' @example blocks'. Within that, only ts/tsx FENCED blocks are compiled; the 88 bare bodies are counted and printed every run but not compiled. Reason measured and written into the gate's header, not assumed.",
        "The export probe asks each package's ROOT specifier only, so a symbol published under a SUBPATH reads as 'not injected'. Conservative by design (it withholds the injection rather than guessing), and stated in the header: 2 of the 3 hits are @object-ui/types/zod subpath exports whose blocks compile anyway.",
        "No skip-changeset label applied: this repo's guard answers by path and already answers 'not owed'. Applying a label the guard does not read would be a phantom gate.",
        "origin/main moved mid-run (c84259489 -> 8b7ea3945, touching packages/types). MERGED, never rebased; closure rebuilt; every number re-measured on the merged head. The census did not move."
      ],
      "open_questions": [],
      "mcp_calls": 0
    }
    

    Generated by Claude Code

  5. claude commented on Sep 7, 2026

    @claude
    Contributor

    LANDED and CLOSED on the measurement — domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-07T12:51Z.

    PR #8322 merged at 12:50:02Z as fedfa3e4a; content probe on re-fetched origin/main passed, including the two controls that say the borrowed harness was not perturbed.

    The card's three asks, against the tree

    ask state on origin/main fedfa3e4a
    Extend the gate (or add a sibling) to compile @example blocks from exported symbols' JSDoc under packages/*/src/**, with the Markdown gate's ledger discipline scripts/check-doc-example-types.mjs (pnpm check:doc-examples), a SIBLING importing the exported harness; the snippet gate is not edited at all, its licensed region byte-identical
    Template-literal code: extract or exempt per case, no parsing of arbitrary strings exempted on a falsified premise, not on difficulty — that extraction already exists and runs on this tree (--emit-census, objectui#7864, 20 recognised sites). #7977 is closed (#8112 corrected its prose); #7976's residue is a hand-copied MIRROR of a template, which needs an equality pin between two texts — a different instrument, on that card
    First run reports the count before enforcing; enforce with a shrink-only ledger the funnel is printed every run — 1,418 non-tooling source files → 228 @example tags → 213 on exported declarations → 124 fenced blocks compiled, 34 green, 90 ledgered; the 88 bare bodies are counted and printed but not compiled, with the reason measured (67 parse as TSX, but the dominant shape is a value illustration that compiles while judging nothing)

    Acceptance, met literally. The card asked that objectui#7974's example be compiled and that a planted scalar direction fail it. #7974 is another lane's open card and its defect is still live, so the "post-fix" phrasing could not be met as written; control legs vi and vi-b met it instead — repair the example and re-derive the row → GREEN; plant the scalar back → RED. The live defect is ledger row one naming that card, and leg v proves the row cannot survive the repair: a row whose stored diagnostic codes stop matching reds as DRIFTED.

    Why the ledger is honest debt rather than a mute button. It reddens three ways, each proven as a unit case and again through the real gate on the committed tree: a failure with no row, a row whose block now compiles (stale), and a row whose codes drifted. The test judges this repository, not only fixtures, so the ledger binds today through the vitest shards.

    Closed as completed; pm:dispatched removed and assignee cleared in the same write.

    Routed, not folded in: the 90-row burn-down is #8321; a defect the gate's own export probe found is #8320; the missing workflow step for check:doc-examples is recorded on #8301, which now carries the same question for three gates (check:unused-deps, check:doc-examples, and the corpus sweep on #8286) so one PR can settle it.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpriority:p3tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions