Skip to content

fix(spec): render the reference section for a non-object root schema (#7658) - #7808

Draft
os-help wants to merge 2 commits into
mainfrom
claude/issue-7658-opaque-string-schema-docs
Draft

fix(spec): render the reference section for a non-object root schema (#7658)#7808
os-help wants to merge 2 commits into
mainfrom
claude/issue-7658-opaque-string-schema-docs

Conversation

@os-help

@os-help os-help commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7658

What was wrong

packages/spec/scripts/build-docs.ts's generateMarkdown() picked the node it was documenting by enumerating shapesproperties, enum, anyOf, oneOf — and answered "none of those" with return ''.

A JSON Schema root is routinely none of those:

Zod spelling JSON Schema root old rendering
z.string().describe(…) { type: 'string', description } — nothing —
z.record(…) type: object + additionalProperties, no properties — nothing —
z.array(…) { type: 'array', items } — nothing —
z.intersection(…) { allOf: [...] } — nothing —

Each lost its entire ## Name section — the heading, and with it the .describe() prose an author wrote to be read — while the page's ## TypeScript Usage block, which is spelled from the export surface rather than from this function, went on naming the export. The page read as if it had forgotten to finish rendering an entry it had just imported.

The count, re-measured today

The card says "~23 exports with a real .describe()". Re-deriving it by replaying generateMarkdown's exact root-selection over all 1578 published schemas (wc -l on the emitted lists, no pager in the pipeline):

today card
sections rendered 1533
sections dropped (return '') 45 ~31
...carrying a root description 33 ~23
bare-scalar roots specifically 32 (22 with a description) 31 (~23)

The card scanned only the bare-scalar spelling. The other 13 are record maps, one array root (system/OidcProvidersConfig) and one allOf (data/FilterCondition) — the same defect through the same line, so they are fixed with it. Scope expansion, disclosed rather than absorbed.

Positive control on the same page, before the fix: content/docs/references/cloud/tenant.mdx renders ## TenantContext, ## TenantDatabase and 7 more, and does not render ## TenantPlan — while line 26 of that same file imports TenantPlanSchema. The zero is a measurement, not a dead walk.

The fix

The renderer moves to packages/spec/scripts/lib/schema-section.ts so it can be pinned directly: the extraction lib/format-type.ts got at #4912, for the same reason. Its output here was the empty string, which is exactly what grepping emitted .mdx cannot see — and why check:docs (generated vs. committed) stayed green over this for months.

Regeneration

Gates

gate result
pnpm --filter @objectstack/spec run check:docs ✅ 230 generated files in sync
pnpm --filter @objectstack/spec run gen:docs ×2 ✅ stable, identical output
pnpm --filter @objectstack/spec exec vitest run scripts/schema-section.test.ts ✅ 13 passed
pnpm --filter @objectstack/spec run typecheck
pnpm --filter @objectstack/spec run check:api-surface ✅ public API surface unchanged
pnpm --filter @objectstack/spec run check:generated ✅ all 13 generated artifacts up to date
pnpm exec eslint <changed files> ✅ clean
pnpm run check:doc-authoring / check:empty-changeset / check:adr-0087-registration
pnpm --filter @objectstack/spec run test 9960 passed, 1 pre-existing failure — see below

Reverse verification: restoring the old behaviour (dropping the unconditional root fallback and returning '') turns 7 of the 13 new cases red — the six render cases lose their whole section, heading included — and leaves the other 6 green. That asymmetry is the point: the four shapes the old code enumerated were always correct, which is why this survived unnoticed.

Pre-existing failure, not from this diff: src/data/api-methods-batch-conformance.test.ts fails on sys_api_key: [get, list, update] grants single-record writes but not 'bulk' in packages/platform-objects/src/identity/sys-api-key.object.ts — a file this branch does not touch (git diff --name-only origin/main -- packages/platform-objects → 0).

…7658)

`build-docs.ts` picked the node it was documenting by enumerating shapes —
`properties`, `enum`, `anyOf`, `oneOf` — and answered "none of those" with
`return ''`. A JSON Schema root is routinely none of those:
`z.string().describe(…)` compiles to a bare scalar, `z.record(…)` to an object
with `additionalProperties` and no `properties`, `z.array(…)` to an array,
`z.intersection(…)` to an `allOf`. Each lost its whole `## Name` section —
heading included, and with it the `.describe()` prose written to be read — while
the page's `## TypeScript Usage` block, spelled from the export surface rather
than from this function, went on naming the export.

Measured on today's tree: 45 published schemas were in that state, 33 carrying a
description. The card counted 22 (`~23`), having scanned only the bare-scalar
spelling. Regenerating adds 244 lines across 27 pages and removes none, so the
1533 sections that already rendered are byte-identical. Emitted files 230 → 230:
an empty section never removed a page, so no category's `pages.length` moves and
the #7303 `meta.json` guard is untouched.

The renderer moves to `scripts/lib/schema-section.ts` so it can be pinned
directly — the extraction `lib/format-type.ts` got at #4912, for the same reason.
Its output here was the empty string, which is precisely what grepping emitted
`.mdx` cannot see, and why `check:docs` stayed green over this for months.

Reverse-verified: restoring the old fallback turns 7 of the 13 new cases red and
leaves the other 6 green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RpBD5DrXb9gsPKkRHL8FHG
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 11, 2026 6:35pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

os-help commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

CI red on Test Core (1/3) — not from this diff. One failing test, and it is the pre-existing one disclosed in the PR body. Diagnosis and why I am not fixing it here:

FAIL src/data/api-methods-batch-conformance.test.ts
  > grants bulk wherever it grants create / update / delete
  + "sys_api_key: [get, list, update] grants single-record writes but not 'bulk'
     — /batch and the *Many routes will 405
     (packages/platform-objects/src/identity/sys-api-key.object.ts)"

Cause52200b4 ("give API-key revoke/restore a working product route (#7727) (#7769)", on main at ~14:43Z today, ~15 min before this branch was cut) widened sys_api_key's whitelist:

-    apiMethods: ['get', 'list'],
+    apiMethods: ['get', 'list', 'update'],

Granting a single-record write verb without 'bulk' is exactly what the #3026 ratchet forbids. This branch touches zero files in that package (git diff --name-only origin/main -- packages/platform-objects → 0), and the test is a filesystem scan of packages/**/*.object.ts — it never reads anything this PR changes.

Why main looks green, and why that is not evidence. The Test Core (1/3) job at 098b629 (this PR's base) passed — as a turbo cache replay, not a run. turbo.json's test task uses $TURBO_DEFAULT$ inputs, so @objectstack/spec#test's cache key covers packages/spec/** plus ^build of its internal deps — and @objectstack/spec has no @objectstack/* dependencies at all. The test reads packages/platform-objects/** at runtime, which the key cannot see. So editing sys-api-key.object.ts does not invalidate the task, and main kept replaying a stale PASS; 52200b4's own CI run was cancelled, so no completed run ever exercised it. This PR edits packages/spec/scripts/**, which changed the key and forced a real run (Cached: 1 cached, 57 total) — surfacing the break rather than causing it.

That cache blind spot is worth a card of its own: a cross-package scanning test whose inputs turbo cannot express will keep reporting the last state anyone happened to invalidate it in.

Why I am not fixing it in this PR. The test's own docblock says to pick one deliberately — grant 'bulk', or register the object in SINGLE_RECORD_WRITE_ONLY with a written reason. That is a product decision about API-key batch semantics, on the surface #7727/#7769 landed twenty minutes ago, and it is outside this card's scope (packages/spec/scripts/build-docs.ts, the regenerated tree, a changeset). Making that call silently inside a docs-generator PR would put an unreviewed API decision in a diff nobody would look for it in.

Every gate this PR is responsible for is green: check:docs (230 in sync), gen:docs ×2 stable, check:api-surface, check:generated (13/13), typecheck, eslint, and the 13 new pins. I will re-check when the base recovers.


Generated by Claude Code

os-help commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

Correcting the "Pre-existing failure, not from this diff" note at the bottom of the body — it was the right call, but argued from an empty diff rather than a measurement, and it did not name the card that already owns it.

Test Core (1/3)'s red is #7793 (sys_api_key grants update without bulk, from #7769 / 52200b4), already fixed by open PR #7803. Measured on this PR's own base, with nothing from this branch applied:

$ git worktree add ../base --detach 098b629 && pnpm --filter @objectstack/spec exec vitest run \
    src/data/api-methods-batch-conformance.test.ts
 Test Files  1 failed (1)      Tests  1 failed | 3 passed (4)
     × grants bulk wherever it grants create / update / delete

The sibling-PR control that motivated the send-back doesn't hold: #7810's three Test Core shards declared 6300 / 6385 / 6047 tests, and @objectstack/spec alone declares 9961 — so it never ran there. This PR's shard 1/3 declared 14554. turbo run test --filter=@objectstack/spec --dry=json confirms why: 1082 inputs, none from platform-objects, dependencies: [].

No assertion was weakened and nothing was pushed — the branch is unchanged at f6bdfdb. This PR goes green once #7803 lands and this branch is rebased/merged onto the new main. Full evidence: #7658 (comment 5255686646).


Generated by Claude Code

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] build-docs.ts silently drops the whole reference-doc section for a bare opaque-string top-level schema (~23 exports with a real .describe())

2 participants