Skip to content

docs(spec): RestServerConfig's crud / metadata / batch keys are embedder-only, and the schema now says so - #16775

Merged
zhuangjianguo merged 7 commits into
mainfrom
claude/issue-15543-rest-server-config-embedder-only
Sep 8, 2026
Merged

docs(spec): RestServerConfig's crud / metadata / batch keys are embedder-only, and the schema now says so#16775
zhuangjianguo merged 7 commits into
mainfrom
claude/issue-15543-rest-server-config-embedder-only

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #15543

Executes the director-seat ruling of 2026-09-07 (summon #17, decision batch #2, comment 5572155531, maintainer verbatim 「同意」), option 2: the crud / metadata / batch keys of RestServerConfig stay and keep their runtime reads, their docblocks stop describing a deployment posture nobody can author from the CLI, and the ADR-0049 question "is this key reachable?" gets its written answer.

No behaviour changes. No schema shape, default, bound or refusal moves — Clause-②: no, and the machine-checkable proof is below.

What the re-derivation found (the ruling cited two sentences; one does not exist)

The dispatch asked me to re-derive the actual population rather than trust the card's two citations. Measured on origin/main 8ccf7a1df:

citation verdict
batch.maxBatchSize's docblock calls the cap "deployment policy" FALSE — that phrase occurs 0 times in the file. Control: maxBatchSize occurs 3 times, so the zero is a real zero and not a broken search. The docblock at its declaration reads only Maximum batch size.
metadata.maskObjectFields: false "opts this server out" REAL, at :328. Repaired.
(not in the card, found by the sweep) api.enableSearch REAL, two carriers — the TSDoc at :100 calls it a "Deployment-wide switch" and the describe() at :112 a "deployment-wide search opt-out". os serve threads only enableProjectScoping / projectResolution, so this key is not reachable from a CLI-started deployment either. Repaired.

Real count: 1 inside the ruling's named key set; 2 keys / 3 sentence-carriers across every live key of the file.

Four further hits are measured boundary, not counted and not edited, with the reason stated so the next sweep does not re-litigate them:

  • api.requireAuth (:132, :142) and batch.defaultAtomic (:465, :469, :476) are retiredKey() tombstones describing keys that are now refused; requireAuth's text denies a deployment posture rather than asserting one.
  • RouteGenerationConfigSchema (:504) and routes.overrides (:535) call crud.dataPrefix "deployment-wide" in the scope-of-effect sense (one setting for the whole server, contrasted with the per-object overrides being retired) — that is true, and it is not a reachability claim. :535 is additionally author-facing retiredKey() rejection copy, which I do not move.

The bounded in-place fix, declared

api.enableSearch is outside the ruling's named key set (crud / metadata / batch), and I fixed it anyway. Declaring it so the reviewing seat can strip it if it disagrees:

  • Same defect class as the card, same file, mechanical, same gate family, no new verification surface, no other claim on this file — all four conditions of the bounded in-place-fix exemption hold.
  • The decisive reason: this PR adds a file-header paragraph stating the measured boot-path fact. Once that paragraph is in the file, a docblock three screens below still saying "Deployment-wide switch" is an internal contradiction in my own edit, and a reader would take the contrast as evidence that enableSearch IS deployment-reachable. Fixing it makes the change self-consistent rather than widening it.
  • It is prose only: enableSearch keeps its type, its default(true) and its runtime read.

What changed

packages/spec/src/api/rest-server.zod.ts

  • A WHO CAN WRITE THIS CONFIG section on the module docblock. This is the carrier that matters for an operator: build-docs.ts renders the module docblock onto content/docs/references/api/rest-server.mdx, while the per-schema docblocks are not rendered there at all. It names both programmatic doors, both boot paths and their measured arguments.
  • Reachability: EMBEDDER-ONLY on the crud, metadata and batch sub-schema docblocks (the ruling's "three docblocks").
  • The crud / metadata / batch members of RestServerConfigSchema say it in their describe() too, so the fact survives into the generated reference table an operator actually reads, not only into the TS source.
  • metadata.maskObjectFields and api.enableSearch repaired as above.
  • The contributor-facing rule ("no docblock here may describe a key as a deployment posture without saying who can set it") is deliberately a line comment, not a docblock — it would otherwise be rendered verbatim onto the public reference page, which is not its audience. That distinction was found by reading the regenerated page, not assumed.

packages/spec/liveness/{crud,metadata,batch}_endpoints.json — the ADR-0049 answer in writing.

  • The ledger has no reachability field and no convention for one, so per the ledger's own conventions the answer went into prose: a REACHABILITY sentence on each of the 18 live rows (6 crud + 7 metadata + 5 batch), and the measurement once in each file's _note. Precedent: batch_endpoints.json's _note already carries its AUTHOR-WARN CHANNEL paragraph, which is a reachability statement made once per file.
  • status and verifiedAt are deliberately untouched. live answers who READS a key; reachability answers who can SET it. Adding the second re-verified no call graph, and bumping verifiedAt would silently reset the liveness staleness clock on a claim nobody re-measured. Each row and each _note says so.
  • Note for the reader: liveness/api.json is the ApiEndpoint metadata type, not RestApiConfigSchema — the api sub-object of RestServerConfig has no ledger file, which is why the ruling's three-file key set is exactly the set with live ledger rows.

QA checklist closure — so the knownGaps this card is cited in do not go stale.

Verified before editing (the dispatch flagged these as unverified): §10b E2 exists verbatim at FOLLOW-UPS.md:566/:571, and all three checklist item ids exist with the spellings the ruling names. Nothing drifted.

Verification

Gate set derived, never guessed — node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, re-derived after the changeset landed, then reconciled with --ran, which surfaced 24 further families the regenerated .mdx pulled in. All at 9ffbde5b1, clean tree.

  • 94 of 94 derived families run. 93 green.
  • 1 declared NOT MEASURED — check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET). It reads emitted CJS/ESM bytes from all 83 packages' dist/; that is a repo-wide build, which is CI's run, not a local one. My diff adds no export, entry or tsup change, so it cannot move this gate. ⛔ Recorded as not measured, never as a pass.
  • Seven further gates first returned a prerequisite refusal against a stale dist (check:api-surface, check:dual-source-exports, check:entry-nameability, check:exported-any, check:browser-reachable-entries, check:doc-formula-expressions, check:skill-examples). Those are NOT MEASURED, not failures; all seven were re-run green after building the packages each named.

The tier boundary, mechanically: pnpm --filter @objectstack/spec check:generated reports 15 of 15 artifacts current, with check:authorable-surface and check:api-surface both green and requiring no regeneration. Only check:docs went stale, and only from the describe() edits. An accept-set or export-surface move could not produce that result — this is the evidence for Clause-②: no, not an assertion of it.

Testspnpm --filter @objectstack/spec test: 465 passed (465) test files, 12952 passed (12952) tests, wrapper verdict command-exit 0. pnpm --filter @objectstack/spec typecheck: command-exit 0, test layer compiles under tsconfig.test.json. Both through scripts/pm/os-verify-lock.sh; verdicts read from its VERDICT command-exit line, never a bare $?.

Dependency-closure build (step ①) is an empty run by construction: packages/spec has no workspace dependencies.

Lint — a declared narrowing, with its three pieces of evidence, taken at final 9ffbde5b1. The repo-wide pnpm lint scan is CI's.

  1. Population read from eslint's own config, not guessed: of the 8 changed paths, eslint supplies a matching configuration for exactly onepackages/spec/src/api/rest-server.zod.ts. The other seven are reported by eslint itself as File ignored because no matching configuration was supplied; they are outside the lint population by its reckoning.
  2. Count read from --format json: 8 entries returned, 1 in population, 0 errors, 0 in-population warnings, exit 0.
  3. Invariance for untouched files: eslint.config.mjs states in its own words (line 328) that this repo "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file", measured there with a positive control. So no edit of mine can move an untouched file's verdict.

All three hold, so the narrowing is a measurement rather than a skipped run.

Bytespnpm check:nul-bytes green, plus an independent scan of every changed file for control characters: zero hits.

验收备注

Observations from the sweep. None is filed, and each names who would meet it.

  • serve.ts:4019 reaches createRestApiPlugin through an as any cast, so even the two keys it does pass are unchecked at that seam. Named by triage as incidental. Not a defect this PR can reach without moving packages/cli, and the ruling refuses option 1. Successor: whoever takes option 1, should a request ever arrive. noted, not filed.
  • RestApiConfigSchema's other nine live keys (version, basePath, enableCrud, enableMetadata, enableUi, enableBatch, enableDiscovery, enableOpenApi, plus the documentation / responseFormat blocks) are embedder-only on the same measurement, and none carries a false posture sentence — their describe() strings are plain capability statements. The file-header paragraph covers them; I did not append a per-key line to nine honest docblocks. Successor: the same option-1 card. noted, not filed.
  • api has no liveness ledger file, so its keys carry no reachability row and no liveness verdict at all. Seeding one is a ledger-governance decision (SPEC_ONLY_SCHEMAS in check-liveness.mts), not a rider here. Successor: none identified — recorded so the next reader of these three files does not read api's absence as a clean bill. noted, not filed.

Governance

No governed surface is touched — the diff is .changeset/, content/docs/references/, docs/qa/, packages/spec/liveness/ and packages/spec/src/; docs/adr/**, .claude/**, skills/**, AGENTS.md and CLAUDE.md are all absent. Left as a draft for the seat, per the dispatch.


Generated by Claude Code

…e schema and the ledger

WIP: docblocks, liveness reachability rows and QA checklist closure.

Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Co-authored-by: Claude <noreply@anthropic.com>
…butor rule off the published page

Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/spec/liveness/batch_endpoints.json, packages/spec/liveness/crud_endpoints.json, packages/spec/liveness/metadata_endpoints.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx (via RestApiConfigSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/liveness/batch_endpoints.json, packages/spec/liveness/crud_endpoints.json, packages/spec/liveness/metadata_endpoints.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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.

Coarse fallback — 130 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 dd2184ae255dde66b66a042da020dcc5e54fa478packageMentionDocs.

Which tree this was computed on

This run read content/docs from ffc743e66e2830253554713cc4672d0c92d52617 — the merge of head fcbbbebfe2a11e23771ee6f9d12625f578fb5b8c into base dd2184ae255dde66b66a042da020dcc5e54fa478, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ffc743e66e2830253554713cc4672d0c92d52617 && git checkout ffc743e66e2830253554713cc4672d0c92d52617
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin dd2184ae255dde66b66a042da020dcc5e54fa478 fcbbbebfe2a11e23771ee6f9d12625f578fb5b8c && git checkout -B drift-repro dd2184ae255dde66b66a042da020dcc5e54fa478 && git merge --no-ff fcbbbebfe2a11e23771ee6f9d12625f578fb5b8c

node scripts/docs-audit/affected-docs.mjs --json dd2184ae255dde66b66a042da020dcc5e54fa478

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs dd2184ae255dde66b66a042da020dcc5e54fa478 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Collaborator

ACCEPT — merged head fcbbbebfe, 34/34 checks green. Readying and enqueueing.

domain:spec execution seat, session session_016N6xmWt5hYm94ffVEwGH8x, 2026-09-08T06:47Z (clock read).

Enqueue reading from check runs: all 34 on fcbbbebfe2a11e23771ee6f9d12625f578fb5b8c are completed, every conclusion success or skipped, none in_progress, none failed. ⛔ Not taken from the check_suite.completed rollup, which excludes cancelled suites and legacy statuses by its own terms.

The merge, verified in-seat

  • A real merge commit, two parentsb8cc67dab + a749dcc4b. ⛔ No rebase, no amend, no force-push, so reviewers' checkouts stay valid.
  • The PR's own diff against the current merge-base is unchanged in shape: 8 files, +216/−51.
  • content/docs/releases/**: 0 files. Its drift bot flags v12.mdx as naming RestApiConfigSchema; that page is release-owned and read-only, and it was correctly never touched across three heads.
  • Governed surface: 0 files.

⭐ The swallow check — the failure this merge sequence exists to prevent

A generated-artifact merge can silently drop the other side's regeneration. Verified against origin/main rather than against the merge's exit code:

sibling check result
#16784 (#15080) packages/spec/liveness/action.json on the branch operationlive, patchlive, authorWarn absent on both (control: 40 props total)
#16784 same file, branch vs origin/main byte-identical
#16792 (#14977) docs/audits/…strictness-ledger.md, branch vs origin/main byte-identical

Both siblings landed at 06:07Z, both survived. os-regen-merge.sh chose per file and said so — keeping the branch's bytes of rest-server.mdx (branch changed it, main did not) and taking main's side of action.json and the generated state-counts.md. ⛔ Nothing hand-edited to reconcile, which is the rule that makes the result trustworthy.

The dev was already using the script before the seat's addendum arrived — the addendum named a sequence it had chosen on its own.

The stale-tree deviation is retired by measurement, not by assertion

Last round's declared risk was that the derivation had been taken on a tree 12+ commits behind, and that the merge might add gate families. Answered directly: the derivation on the merged head is 94 families, and diff against the stale-head derivation is EMPTY. The merge added none, and the STALE TREE warning is gone. ⇒ ⛔ That is how a declared deviation should close — with the measurement that resolves it, not with silence.

Also correctly held: no regeneration was owed after the merge, and that is measured rather than assumed — check:generated reports "All 15 generated artifacts are up to date" on the merged tree. state-counts.md did not need recomputing because the three ledger diffs are note / _note prose only; no status moved, so #16784's counts remain correct over the merged set.

Re-run on the merged head, not carried forward: 94 derived / 94 run / 93 green; test → 465 files, 12955 tests (three more than round 1's 12952 — the sibling work the merge brought in); typecheck exit 0; the full ratchet family green. check:dual-build-cjs-loads stays NOT MEASURED with its verbatim PREREQUISITE NOT MET text — ⛔ never a pass.

Deviation accepted

5 commits behind origin/main at report time, not re-merged. Accepted: the three collision-risk files are byte-identical to current main, mergeable_state is clean, the merge queue rebuilds against main at land time, and a second merge would have invalidated the gate run this report cites. ⇒ Declared rather than silently carried, which is the part that matters.

Actions

ACCEPT → ready → enqueue. Not governed (Governed Surface Queue Guard green). Fixes #15543 closes the card on merge; ⛔ this seat does not merge by hand and does not bypass the queue.

The packages/rest finding this PR correctly refused to fix in scope is filed as #16801, with the seat's correction to its routing recorded there: packages/rest is domain:cli; there is no domain:rest lane.

https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 8, 2026 06:53
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 094b8fd Sep 8, 2026
36 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-15543-rest-server-config-embedder-only branch September 8, 2026 07:19
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/m tooling

Projects

None yet

2 participants