Skip to content

docs(spec): bring the odata module's Programmatic Use example under check:skill-examples - #19745

Merged
os-support-ai merged 2 commits into
mainfrom
claude/issue-19065-odata-example-blocks-checked
Sep 22, 2026
Merged

os-support-ai merged 2 commits into
mainfrom
claude/issue-19065-odata-example-blocks-checked

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Part of #19065

Clause-②: no

The module docblock of packages/spec/src/api/odata.zod.ts carries two file-level @example blocks. Both ship twice — inside the tarball as src/api/odata.zod.ts (this package's files[] lists src/**/*.zod.ts) and on the generated reference page content/docs/references/api/odata.mdx — and neither was inside check:skill-examples' reach. This brings the TypeScript one under the gate.

The diff

  • an os:check marker line (the HTML-comment spelling the gate declares for this surface) on the line directly above the @example Programmatic Use fence;
  • the one import that block needs to compile standalone, import type { ODataQuery } from '@objectstack/spec/api';, plus its blank line;
  • the regenerated content/docs/references/api/odata.mdx, and a patch changeset.

That is 3 added lines in the source file and 2 on the generated page. No other file under packages/spec/src/** is touched, no prose is tidied and no citation is repointed — #17242 holds a broad claim over comment prose in that tree and this stays one hunk for it to merge past.

The card asked for TWO markers. Only one of the two blocks can carry one — measured, not argued

@example OData Query is an HTTP request under a bare fence, not TypeScript. The gate's fence-language predicate is FENCE_OPEN_RE in packages/spec/scripts/check-skill-examples.ts, which recognises ts / tsx / typescript and nothing else; a marker above any other fence is an orphan, which the gate treats as an error rather than a no-op. Probed on this branch with scripts/ablation-replace.mjs (mutation and restore both proven on disk):

GATE_EXIT[orphan]=1

✗ Found an os:check marker not directly above a ts / tsx / typescript fence
  - packages/spec/src/api/odata.zod.ts:43

(the gate's own line spells those three with their fences; they are written bare here so this body stays one flat code block)

So marking that block does not check it — it fails the gate. Making it checkable would mean rewriting an HTTP URL example as TypeScript, or teaching the gate a fourth fence language: both are decisions, neither is this card. Hence Part of rather than a closing keyword — the seat decides whether the HTTP half is closeable at all.

Acceptance — both halves, exit codes captured before any pipe

The card's test: strip the $ from one key in the @example Programmatic Use block, the gate must go RED; put it back, GREEN.

RED — scripts/ablation-replace.mjs --file packages/spec/src/api/odata.zod.ts --anchor ' * $select: [' --replacement ' * select: [' --expect 1, then rebuild packages/spec and run pnpm --filter @objectstack/spec check:skill-examples:

ON-DISK[red] blob=1e0293a959aeae2ee00bfd7452a1097378b59a5c
BUILD_EXIT[red]=0
GATE_EXIT[red]=1

✗ [spec source TSDoc (@objectstack/spec)] examples do not compile:
  packages/spec/src/api/odata.zod.ts:60:3
      error TS2561: Object literal may only specify known properties, but 'select'
      does not exist in type '{ $select?: …; $filter?: …; … }'. Did you mean to write '$select'?

GREEN — restore, rebuild, re-run:

ON-DISK[green] blob=9157561783280ae6118140edcdd58a3f011e3952
BUILD_EXIT[green]=0
GATE_EXIT[green]=0
✅ 259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them

The tree was restored from the committed state and proven: blob == HEAD (915756178328) and git diff HEAD empty, on both ablations. The rebuild in each leg is a prerequisite of the gate (its dist-freshness guard refuses a dist older than src), not the carrier of the mutation: the mutated text is a TSDoc comment the gate reads from src, and dist only supplies the declarations the block is checked against.

Before / after on the gate's own counts: 258 blocks / 10 on the spec-source surface → 259 / 11.

What moved on the reference page

Exactly one line. The marker is machinery and renderFileDescription drops it before classification (packages/spec/scripts/lib/file-description.ts, SKILL_EXAMPLE_MARKER), which is also why it cannot break the MDX build:

under the page's **Programmatic Use** heading and its typescript fence, two lines are added at the top of the block — import type { ODataQuery } from '@objectstack/spec/api'; and the blank line under it — and nothing else on the page changes.

Sibling entries under content/docs/references/** survive the regeneration — 225 files on both sides, exactly one differing (api/odata.mdx), and three quoted-exact git grep -F probes taken from the three most recent reference-page commits on main read identical counts on origin/main and on this branch (1 / 1, 1 / 1, 4 / 4), against a dark control reading 0. scripts/pm/os-regen-merge.sh was run before the push and refused with "origin/main was already contained in this branch — this refusal IS the whole outcome": origin/main has not moved since this branch's base, so there was no merge to make.

Local verification

  • pnpm --filter @objectstack/spec typecheck → exit 0
  • pnpm --filter @objectstack/spec test → exit 0, 515 files / 15043 tests passed
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 102 families from this worktree; all 102 were run and reconciled with --ran: 101 exit 0, 1 NOT MEASURED. The one is pnpm check:dual-build-cjs-loads, exit 3 — it needs a whole-workspace build (33 packages have no dist here, @objectstack/account, @objectstack/setup, @objectstack/studio, … ), which a docs-only diff does not buy; Build Core covers it in CI.
  • pnpm lint was narrowed rather than skipped, with the three readings that make a narrowing a measurement: ① the population is read from eslint.config.mjs's own files globs, every one of them a {ts,tsx,mts,cts,js,jsx,mjs,cjs} extension, so of the three changed paths only the .ts one is in it (the .mdx and the changeset each came back errorCount 0 / warningCount 1 — the ignored-file warning); ② --format json reports 1 file linted, 0 errors, 0 warnings; ③ the config never enables type-aware linting for any file (no parserOptions.project, no typed @typescript-eslint rules — stated and measured in its own header), so a comment-only edit in one file cannot move the verdict on a file it did not touch. Readings taken at 4a54706.

Acceptance notes

  • The @example OData Query block stays structurally unverifiable. Not a defect and not filed: the gate is a TypeScript compiler, and that block is an HTTP request. Noted here rather than carried further.
  • Coverage on this surface is still 7 files of the 134 that write @example under packages/spec/src (the card's own reading, at 221dabb72a). This PR moves it by one file; the rest is the card's explicit non-scope.
  • ⛔ Not touched, deliberately: whether ODataQuerySchema should refuse undeclared keys instead of stripping them. That changes the accept set of a published schema with consequences on the wire, and the example blocks here are written so they do not imply it either way.

Generated by Claude Code

…kill-examples

The `@example Programmatic Use` block in `packages/spec/src/api/odata.zod.ts`
ships in the tarball and on the generated reference page, but carried no
`os:check` marker, so nothing type-checked it. Mark it and give it the
`ODataQuery` import it needs to compile standalone.

The sibling `@example OData Query` block is an HTTP request under a bare
fence, not TypeScript: the gate only recognises ts/tsx/typescript fences, so a
marker there is an orphan rather than a check.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/api/odata.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/api/odata.zod.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 16d090ede01e8e940313364603a254a659a2355b → packageMentionDocs.

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 177/177 CONTRACT_REVIEW_TIER (seat-measured, 2026-09-22T20:00Z)
Head-sha: 4a5470603b9bb305cbb560ad8028f17af7989e48

Rendered by an isolated at-tier review subagent.

① Derived judgments

  • Path limb: fires. dispatch-gates --tier at origin/main names packages/spec/src/api/odata.zod.ts ⇢ packages/spec/src/** as the clause-② suspect surface. This review is owed on that limb alone.
  • Declaration limb: Clause-②: no — judged CORRECT against the diff. The diff adds 3 lines inside the module docblock: the os:check marker, an import type { ODataQuery } from '@objectstack/spec/api';, and one blank gutter line. ODataQuerySchema and export type ODataQuery are byte-identical. A REAL build on both origin/main and the head, then diff -rq of the two dist/ trees (216 files each): 212 identical, 4 differ — the four .map files. Decoded: 1451 odata segments shifted by exactly +3 lines, 0 other differences, sources/names identical. Firing control: the two src/api/odata.zod.ts files differ (exit 1). ⇒ no declaration or bundle byte moves, no accept set relaxes, no public surface widens.
  • The marked block compiles, and the gate really reads it. Head as committed: exit 0, 259 prose examples type-check across 3 surface(s), spec-source surface 11 blocks. RED leg ($select: ⇒ select:): exit 1, odata.zod.ts:60:3 error TS2561 … Did you mean to write '$select'?. Restored to the HEAD blob on every leg, status clean.
  • The added import is correct and minimal. Removing it: exit 1, TS2304: Cannot find name 'ODataQuery' ⇒ necessary. ODataQuery appears 3× in built dist/api/index.d.ts and 0× in root dist/index.d.ts, so @objectstack/spec/api is the specifier that resolves it.
  • Where the new lines ship. npm pack --dry-run on the built head (2031 entries): POSITIVE src/api/odata.zod.ts present; NEGATIVE *.test.ts 0, non-zod src/*.ts 0, scripts/check-skill-examples.ts absent. CLASS control: a symbol-level TSDoc string from the same file reaches 4 dist files, while the pre-existing file-level text reaches 0 on both sides — file-level docblocks never emit. Tracked api-surface/, json-schema/, liveness/: git diff empty.

② Semver level

patch on @objectstack/spec — consistent. Tarball bytes move (a shipped .zod.ts source and four shipped .map files), so skip-changeset would be wrong; nothing is added to the public surface, so minor would over-declare.

③ Boundary flags

  • content/docs/references/api/odata.mdx is generator output, ⛔ not a hand edit. build-docs.ts --check at head: exit 0, 225 generated files in sync. Firing control: delete the added import line from the committed page ⇒ exit 1 naming api/odata.mdx (out of date); restored clean. Determinism control on origin/main: exit 0. The marker itself is dropped by isMarkerLine, which is why only the import and its blank line show on the page.
  • os-regen-merge.sh's refusal is the correct outcome, ⛔ not a skipped step. Reproduced: exit 1, «HEAD already contains origin/main and NO pre-merge base is recorded … this refusal IS the whole outcome». merge-base --is-ancestor exit 0 with the reverse control exit 1; 0 commits on main not on the branch; HEAD, status and merge count unchanged after the run.
  • Manual floor: not hit. No feature, ADR, protocol change, security or permission boundary, gate weakening or new runtime dependency. Gate coverage widens 10 ⇒ 11 blocks.
  • ⭐ The card's scope sentence («both blocks») is falsified by measurement. FENCE_OPEN_RE = /^```(ts|tsx|typescript)\s*$/; @example OData Query opens a bare fence. Marker planted above it: gate exit 1, «Found an os:check marker not directly above a ts / tsx / typescript fence … odata.zod.ts:43», raised before any compile pass. ⇒ Part of rather than a closing keyword is the honest keyword, and Part-of PR must not also close its card is success.
  • CI at head, read last, newest per name (35 names): 33 success, 2 skipped, 0 failure, 0 in_progress; mergeable_state: clean. Test Core (5/6) completed success — that is the shard card [finding] Test Core (5/6) times out at its 30-minute ceiling for any PR touching packages/spec — measured twice at 30.3 min on one head, siblings pass in 20-26 #16395 recorded timing out for packages/spec PRs, so it was the one plausible red and it did not fire.

Implemented-by: claude/issue-19065-odata-example-blocks-checked
Reviewed-by: session_013RDBh5DqXd2xnLwvHLgLFr

VERDICT: PASS


⚠️ The open question: option A's reasoning survives only IN PART

The seat routed the implementer's three options to triage (5782585327) and quoted its recommendation — A, on the reasoning that «the marked block now pins the exact $-prefixed key spellings the URL block illustrates, so drift in one is visible in the other». The review measured that reasoning rather than repeating it:

  • Survives: the two blocks name the identical 7-key set ($select $filter $orderby $top $skip $expand $count), all in the schema.
  • ⛔ Does NOT survive: the gate is blind to the URL block. Misspelling $select= as select= on line 45 leaves the gate GREEN — exit 0, 259/11. ⇒ «drift in one is visible in the other» is a human-comparison claim, ⛔ not a mechanical pin.
  • ⛔ Also does not survive: the two blocks illustrate different value encodings — $select=name,email / $expand=orders as comma-strings versus arrays, both accepted by the schema's unions. So the marked block pins key names, ⛔ not the wire form.

⇒ The seat is correcting its own retriage note on #19065 with this, because triage was handed A's reasoning as the implementer stated it and one of its two legs is now measured false. ⛔ This does not decide between A, B and C — B and C remain decision-shaped — but triage should rule knowing that A means «accept an unguarded block», ⛔ not «guarded by proxy».

Tier control — measured by the seat

  • 177 assistant requests, 177 stamped claude-fable-5-1; 218/218 counting every model field.
  • Dark control: 0 — filtering for anything else returns empty.
  • Two fallback/overload keyword hits, both tool-schema prose. ⛔ Neither is a notice.

Three findings that did not change the verdict

  1. ⚠️ The gate cannot be run in a spec-only-built tree — it exits 3 PREREQUISITE NOT MET without client-react/dist declarations. Anyone reproducing the acceptance test must build client and client-react first. That is a property of the gate's three-surface design, ⛔ not of this PR.
  2. The docs-drift bot reports odata.zod.ts yielded no anchor, so that run does not cover the page; the generator --check covers it instead.
  3. Four shipped .map files change line offsets, with no consumer-visible effect.

Generated by Claude Code

@os-support-ai
os-support-ai marked this pull request as ready for review September 22, 2026 20:00
@os-support-ai
os-support-ai added this pull request to the merge queue Sep 22, 2026
Merged via the queue into main with commit c120dbd Sep 22, 2026
37 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-19065-odata-example-blocks-checked branch September 22, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants