Commit 062f5cd
docs(spec): the batch cap is embedder-only, not "DEPLOYMENT policy" — the fifth carrier (#18757)
Clause-②: no
Fixes #18739
One comment changes. `packages/spec/src/api/batch.zod.ts:128`, beside
`BatchUpdateRequestSchema.records`, told a reader the batch-size cap is
DEPLOYMENT policy. That claim was ruled false on 2026-09-07 (director
seat, summon #17, decision batch #2, maintainer verbatim 「同意」). This was
the fifth carrier; the other four are already corrected under the same
ruling, and this one carried the exact phrase #16801 struck from
`packages/rest/src/rest-server.ts#enforceBatchSize` one package over.
No schema key moves, no accept set widens or narrows, no export is added
or removed. The diff is comment text inside one `lazySchema` factory,
plus the changeset.
## The wording is copied, not invented
`enforceBatchSize`'s corrected docblock is the shape this carries over:
keep the span and the default as schema facts, then state reachability
explicitly and name the door. The new paragraph follows it sentence for
sentence, so the five carriers now read the same way.
It also points at the two records that already hold the answer, so the
next reader does not re-derive it: the WHO CAN WRITE THIS CONFIG
(#15543) header in `packages/spec/src/api/rest-server.zod.ts`, and the
per-key REACHABILITY row in
`packages/spec/liveness/batch_endpoints.json`.
The two sibling cap comments in this file (`UpdateManyRequestSchema`,
`DeleteManyRequestSchema`) already say only "cap lives at the route —
see `BatchUpdateRequestSchema` above" and carry no reachability claim of
their own, so correcting the one paragraph corrects what they point at.
They are untouched.
## The question the dispatch asked: does that header's scope actually
cover `batch`?
The seat flagged this as unverified. Measured here, and the answer is
yes — three independent records, none of which needed inference:
1. **The header says it in as many words.**
`packages/spec/src/api/rest-server.zod.ts:47-50`: "⇒ On a CLI-started
deployment every OTHER key here is EMBEDDER-ONLY: the whole of `crud`,
`metadata` and `batch`, and the rest of `api`." `batch` is named, not
implied.
2. **`maxBatchSize` is inside that named sub-object.**
`RestServerConfigSchema.batch` is `BatchEndpointsConfigSchema`
(`rest-server.zod.ts:706`), whose own docblock already opens
"Reachability: EMBEDDER-ONLY (#15543)" and whose `.describe()` on the
parent key already says "embedder-only: written by a host that
constructs this config, never by `os serve` or the dev plugin".
3. **The liveness ledger says it per key.**
`packages/spec/liveness/batch_endpoints.json`, the `maxBatchSize` row:
"REACHABILITY (#15543, measured 2026-09-08 on origin/main 8ccf7a1):
embedder-only."
The header carries exactly one carve-out, and it is not this key:
`metadata.maskObjectFields` via `OS_ALLOW_UNMASKED_OBJECT_METADATA`.
Nothing else outside an embedder's argument moves any value there.
⇒ The corrected comment asserts **embedder-only**, which is the same
thing the four landed carriers assert. No new claim is introduced.
## What happened to the `1..1000` range
The card asked whoever fixed this to decide explicitly whether the range
survives as a schema fact or goes with the claim. **It survives,
reattached.**
`1..1000` is what `z.number().int().min(1).max(1000).default(200)`
actually enforces at construction, so it is a true statement about the
schema; what was false was the implied reader — an operator deploying
the platform. Both landed carriers keep it and reattach it to the
embedding host: `enforceBatchSize` keeps "(1..1000, default 200)", and
`content/docs/api/data-api.mdx` keeps "only the embedding host can set
it, anywhere in 1–1000". Dropping it here would have invented a fifth
shape for a number the other four still print, and would have deleted a
true schema fact to fix a false reachability claim. The comment now says
the span is reachable by the embedding host and by nothing else.
## Acceptance controls
**LIT** — the search fires. All four already-corrected carriers were
found carrying correct wording, not one:
| carrier | reads |
|---|---|
| `packages/spec/src/api/rest-server.zod.ts:496`, `:707` |
"Reachability: EMBEDDER-ONLY (#15543)" |
| `packages/rest/src/rest-server.ts:2213` | "Reachability: EMBEDDER-ONLY
(#15543, #16801). ⛔ It is NOT deployment policy" |
| `content/docs/api/data-api.mdx:327` | "it is **embedder-only**
(#15543)" |
| `content/docs/protocol/kernel/http-protocol.mdx:851` | "it is
**embedder-only** (#15543)" |
**DARK** — after this change the verbatim string `the batch-size cap is
DEPLOYMENT policy` reads **0** across the tracked tree, against a live
control (the same grep for `the batch-size cap is` still returns this
file, so the probe fires). Historical mentions in CHANGELOG files are
untouched: they are the record of what shipped, not residue.
## The census, redone wrap-immune — and it found a SIXTH carrier
Triage asked for a mechanical enumeration rather than a fix of the fifth
alone. My first pass was line-scoped `git grep`, and **its own positive
control read 0** on a phrase I had already read with my eyes: the claim
wraps across two comment lines, so no line-scoped regex can match it.
That is this card's defect class one level down — a probe narrower than
the claim under-counts and reports the shortfall as a zero — so I redid
it.
The census normalises every tracked text file (strip comment leaders,
collapse all whitespace including newlines), then searches. **8830 files
scanned.**
| probe | hits |
|---|---|
| A — verbatim `the batch-size cap is DEPLOYMENT policy` | **0** |
| B — `batch-size cap is … deployment policy` (any filler) | **0** |
| C — `deployment's configured batch.maxBatchSize` | **1** |
| D — `maxBatchSize` within 200 chars of "deployment" (broad) | 20 hits
/ 9 files |
Probe C is the sixth carrier:
**`packages/spec/src/api/batch.test.ts:110`** — "the one route that did
cap read the deployment's configured `batch.maxBatchSize` (1..1000)
instead". Same false attribution, different spelling, wrapped across
lines, invisible to every probe run over this claim so far including the
one in the card.
⛔ **Not repaired here.** It is outside this card's declared file
surface, and the dispatch is explicit that other defects are handed back
rather than folded in. It is reported for a successor card. It is also
not published — `packages/spec` ships `src/**/*.zod.ts`, and a
`.test.ts` is not in that set — which makes it a weaker carrier than the
five before it, but it is the same claim.
Probe D's remaining files are accounted for and none is a carrier: the
four corrected carriers, `docs/qa/platform-checklist/FOLLOW-UPS.md` (the
record of the correction, past tense), the two CHANGELOGs, and
`packages/plugins/plugin-audit/src/read-audit.ts`, whose `maxBatchSize`
is an unrelated homonym (the read-audit batcher's flush threshold,
default 50).
Population declared: tracked files in this repository at `90043af0fb`.
Anything outside it is unread, not absent.
## Changeset
`patch` for `@objectstack/spec`, and it is owed rather than optional.
Measured, not assumed: `packages/spec`'s `files[]` carries
`src/**/*.zod.ts`, so the edited file ships verbatim in the tarball —
`npm pack --dry-run` lists `src/api/batch.zod.ts` among its 275 files,
against a lit positive control (`src/api/rest-server.zod.ts`) and a zero
negative control (no non-`.zod.ts` file under `src/` ships). The
corrected sentence is published text a consumer greps, so it gets a
published correction.
## Verification
All readings taken at `90043af0fb` with a clean working tree.
- `pnpm --filter @objectstack/spec build` — green (under the shared
verify lock).
- `pnpm --filter @objectstack/spec check:generated` — **15/15 generated
artifacts up to date**, nothing to regenerate. Expected: a line comment
is neither rendered onto a reference page nor an authorable key.
- `pnpm --filter @objectstack/spec typecheck` — green.
- `pnpm --filter @objectstack/spec test` — **486 test files, 13964
tests, all passing**.
- Full repo build (`turbo run build --filter=!@objectstack/docs`) —
**73/73 tasks successful**.
- **Derived gate families: 75 derived, 75 run, 0 NOT-MEASURED, 0 UNRUN**
per `scripts/pm/dispatch-gates.mjs --ran`, re-derived after the
changeset existed. Three of them first returned exit 3 (`PREREQUISITE
NOT MET` — their own code for "nothing was measured", distinct from a
finding's 1) because they read built output of packages this diff never
touches; the full build above cleared all three and they were re-run
green: `check:doc-formula-expressions`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure`.
- `pnpm lint` — the whole-repo `eslint . --no-inline-config`, exit 0, no
output. Run in full, so no narrowing is claimed.
## Acceptance notes
- **The sixth carrier above**
(`packages/spec/src/api/batch.test.ts:110`) is handed back, not fixed.
- **`docs/qa/platform-checklist/FOLLOW-UPS.md:575`** quotes the old REST
wording as the record of what was corrected, correctly framed in the
past tense. Noted, not filed.
- **The line-scoped-probe failure is the durable lesson**, not a repo
defect: the two probe populations that missed this line were scoped by
directory, and my own first probe was scoped by line. A claim that wraps
is invisible to both. The census script used here is in the scratchpad,
not committed — it is a measurement, not an artifact this repo should
carry.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 922923b commit 062f5cd
2 files changed
Lines changed: 28 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | | - | |
| 128 | + | |
| 129 | + | |
130 | 130 | | |
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
134 | 145 | | |
135 | 146 | | |
136 | 147 | | |
| |||
0 commit comments