Skip to content

Commit 062f5cd

Browse files
os-billclaude
andauthored
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

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
`BatchUpdateRequestSchema`'s cap comment no longer calls the batch-size cap "DEPLOYMENT policy". It is embedder-only, and this correction narrows the claim onto what is actually reachable.
6+
7+
`packages/spec/src/api/batch.zod.ts` ships in this package's tarball (`files[]` carries `src/**/*.zod.ts`), so the sentence a reader finds beside `records` is published text. It told them the cap — `RestServerConfig.batch.maxBatchSize`, 1..1000, default 200 — was deployment policy, i.e. something an operator deploying this platform could move. No shipped boot path makes that true.
8+
9+
**What the comment says now.** The cap keeps its span and its default as schema facts; the reachability sentence says who can write it. A `RestServerConfig` is the ARGUMENT a host passes when it constructs the server, and there is exactly one door: `createRestApiPlugin({ api })`. Neither shipped boot path opens it with a `batch` config — `os serve` forwards exactly two keys out of the stack config's `api:` block (`api.enableProjectScoping`, `api.projectResolution`) and the dev plugin calls `createRestApiPlugin()` with no config at all. A CLI-started deployment therefore always gets the default of 200, and no flag, config file or CLI option moves it; only the embedding host reaches anywhere in the 1..1000 span.
10+
11+
**Nothing executable moves.** No schema key is added, removed or renamed, no accept set widens or narrows, no export changes, and no runtime behaviour is touched. `records` still carries shape only, the cap is still enforced at the route, and `.min(1)` is still absent. The diff is comment text inside one `lazySchema` factory.
12+
13+
**Why this shipped as its own correction.** The same false claim had four other carriers, all already corrected under the same 2026-09-07 ruling: this package's `RestServerConfigSchema` docblocks and WHO CAN WRITE THIS CONFIG header, `enforceBatchSize` in `@objectstack/rest`, and the `data-api` and `http-protocol` reference pages. This was the fifth, and it carried the exact phrase struck from `enforceBatchSize` one package over. The wording is copied from those landings rather than invented, so the five now read the same way — as does the per-key REACHABILITY row in `liveness/batch_endpoints.json`, which also ships here.
14+
15+
Clause-②: no — comment text only. No authorable key moves, no export is added or removed, and no accept set changes in either direction.

‎packages/spec/src/api/batch.zod.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,23 @@ export type BatchOptionsParsed = z.infer<typeof BatchOptionsSchema>;
125125
*/
126126
export const BatchUpdateRequestSchema = lazySchema(() => z.object({
127127
operation: BatchOperationType.describe('Type of batch operation'),
128-
// [#3939] No `.max()` here: the batch-size cap is DEPLOYMENT policy
129-
// (`RestServerConfig.batch.maxBatchSize`, 1..1000, default 200), enforced at
128+
// [#3939] No `.max()` here: the batch-size cap is
129+
// `RestServerConfig.batch.maxBatchSize` (1..1000, default 200), enforced at
130130
// the route so one place decides it. A hardcoded bound in the spec was a
131131
// second source of truth that never matched — it said 200 while the routes
132132
// enforced nothing at all. `.min(1)` is gone too: an empty batch is a no-op
133133
// (`total: 0`), not a client error.
134+
//
135+
// Reachability: EMBEDDER-ONLY (#15543, #16801). ⛔ The cap is NOT DEPLOYMENT
136+
// policy — this comment said exactly that until now, and no shipped boot path
137+
// makes it true. It is written only by a host that constructs the
138+
// `RestServerConfig` itself, never by `os serve` or the dev plugin, so a
139+
// CLI-started deployment always gets the default of 200 and no flag, config
140+
// file or CLI option moves it; only the embedding host reaches the 1..1000
141+
// span above. See the WHO CAN WRITE THIS CONFIG (#15543) header in
142+
// `packages/spec/src/api/rest-server.zod.ts`, which names `batch` among the
143+
// embedder-only sub-objects, and the per-key REACHABILITY row in
144+
// `packages/spec/liveness/batch_endpoints.json`.
134145
records: z.array(BatchRecordSchema).describe('Array of records to process (server caps the count — see batch.maxBatchSize)'),
135146
options: BatchOptionsSchema.optional().describe('Batch operation options'),
136147
}));

0 commit comments

Comments
 (0)