Skip to content

Commit 0e2ebce

Browse files
docs(protocol): the batch cap is embedder-only, not “configurable via maxBatchSize” — the fourth carrier, plus the fifth-carrier enumeration (#18737)
Fixes #17183 ## What this lands `content/docs/protocol/kernel/http-protocol.mdx`, in the batch endpoint's **Behavior** list, told the reader the 200-operation cap is *"configurable via `maxBatchSize`"* with no statement of who may write it. That claim was ruled false on 2026-09-07 (director seat, summon #17, decision batch #2, maintainer verbatim 「同意」) and has already been corrected on three carriers (#15543 / PR #16775; #16801; #16940). This page was the fourth. No fifth wording was invented. The landed vocabulary is **compressed** into this page's terse bullet register rather than pasted from `data-api.mdx`'s running prose — that compression is the one piece of writing judgement the card asked for. ``` - Maximum batch size: **200** operations. The cap is `batch.maxBatchSize`, and it is **embedder-only** (#15543): it is written only by a host that constructs the `RestServerConfig` itself, never by `os serve` or the dev plugin, so a CLI-started deployment always gets the default of 200. Over the cap is `400 BATCH_TOO_LARGE`, carrying the `count` sent and the `max` allowed. ... ``` The `1..1000` range is deliberately not carried over: it is detail for the embedding host, it lives on the generated reference page, and the correction does not need it. Citing `(#15543)` matches this page's existing convention (9 issue citations already). ## Zone-2 assumptions, re-taken on `objectstack-ai/objectstack` @ `080688b56` | assumption | verdict | |:--|:--| | the sentence still reads verbatim as filed (located by **content**, not line number) | **holds** — sole occurrence, at `:851` | | `grep -rl maxBatchSize content/docs` reaches 9 files (probe-reaches-the-tree control) | **holds** — 9 | | the card body reads complete, not truncated | **holds** — ends at its `## Refs` list | ## Triage's first question: does a fifth carrier exist? **Yes — two, and one of them is a live source file that every previous sweep walked past.** Answered by enumeration, not impression. **Spellings used**, so the coverage can be judged rather than taken on faith: | # | spelling | population | |:--|:--|:--| | 1 | `max[._-]?batch[._-]?size`, case-insensitive | whole tracked tree minus `content/**` | | 2 | literal `deployment policy`, case-insensitive | whole tracked tree | | 3 | `` raise `batch.maxBatchSize` `` | whole tracked tree | | 4 | proximity `batch.{0,40}configur` / `configur.{0,40}batch`, case-insensitive | whole tracked tree | Spelling 1 is deliberately wider than the census token that let the fourth carrier survive three sweeps: the census positive token was `batch.maxBatchSize` and this page spelled the key bare, so a probe written over one spelling of a claim under-counted by exactly that difference. Spellings 2 and 4 are there because a carrier need not name the key at all — and spelling 2 is what actually found the fifth. Spelling 1 reaches **29 files** outside `content/`. Adjudicated: - **Not carriers, different key entirely** (9 files): `packages/plugins/plugin-audit/**` (`readAudit.maxBatchSize`, the read-audit flush buffer) · `packages/spec/src/system/tracing.*` (OTel batch span processor) · `packages/spec/src/api/contract.*` (DataLoader) · `docs/qa/platform-checklist/areas/access-security.json` (the audit flush window). - **Not carriers, already correct**: `packages/spec/src/api/rest-server.zod.ts` (the `WHO CAN WRITE THIS CONFIG` header) · `packages/rest/src/rest-server.ts#enforceBatchSize` (corrected under #16801) · `packages/spec/liveness/batch_endpoints.json` and `liveness/README.md` (per-key `REACHABILITY` sentences) · `docs/qa/platform-checklist/FOLLOW-UPS.md` row E2 and `areas/api-backend.json` (which speak of *construction*: "harness: construct with `{ batch: { maxBatchSize: 5 } }`"). - **Not carriers, no who-writes claim**: the six test files, the three generated `authorable-surface` / `authorable-defaults` inventories, and the migration registry entries — all name the key without asserting anything about who may write it. ### Fifth carrier A — a live source file, `packages/spec/src/api/batch.zod.ts:128` ``` // [#3939] No `.max()` here: the batch-size cap is DEPLOYMENT policy // (`RestServerConfig.batch.maxBatchSize`, 1..1000, default 200), enforced at // the route so one place decides it. ``` That is **the same phrase, verbatim**, that #16801 struck from `packages/rest/src/rest-server.ts#enforceBatchSize`, whose docblock now reads *"Reachability: EMBEDDER-ONLY (#15543, #16801). ⛔ It is NOT deployment policy — this docblock said exactly that until #16801"*. The twin survived in `batch.zod.ts`, in the same package whose file header carries the corrected contract. **Why every sweep missed it, measured:** the `deployment policy` probe recorded in `FOLLOW-UPS.md` E2 was scoped to the single file `packages/spec/src/api/rest-server.zod.ts` (with `maxBatchSize`'s 3 hits there as its control), and the `maxBatchSize` censuses were scoped to `content/`. `batch.zod.ts` is in neither population — a file-scoped phrase probe and a tree-scoped token probe, and the carrier sat in the gap between them. ⛔ **Not repaired here.** The dispatch fences the file surface to `content/docs/protocol/kernel/http-protocol.mdx`, and editing `packages/spec` opens the eight-artifact regeneration family — a new verification surface, so the in-place-repair exemption does not hold. It is reported for its own card, in `domain:spec`. ### Fifth carrier B — the published changelog text, `packages/*/CHANGELOG.md` The 17.0.0 entry carries the claim in its **strongest** form found anywhere in the tree, and it carries it to npm: - *"Batch size is deployment policy — `RestServerConfig.batch.maxBatchSize`, 1..1000, default 200"* — `packages/rest/CHANGELOG.md` ×2, `packages/spec/CHANGELOG.md` ×2 - *"Deployments that were quietly relying on unbounded batches should raise `batch.maxBatchSize` (up to 1000) rather than discover the cap in production."* — `packages/rest/CHANGELOG.md` ×2, `packages/spec/CHANGELOG.md` ×2 That second sentence does not merely call the cap configurable; it instructs deployments to raise it. AGENTS.md names `packages/*/CHANGELOG.md` RELEASE-OWNED and says a factual error in a released entry is amended in a **dedicated docs-only PR**, ⛔ never a rider on code changes and ⛔ never an erratum in a later entry — and a later entry is exactly what exists today: `packages/spec/CHANGELOG.md:3581` already records the correction, while the 17.0.0 entry an upgrading agent greps still carries the claim. ⛔ **Not repaired here**; reported for its own card. ### Where a fifth carrier is *not* `examples/**` and root-level `*.md` were swept with all three spellings and hold **zero** carriers — zero against the same probes that return 38 files tree-wide, so these are real zeros, not dead probes. The two `deployment policy` hits under `examples/` are about flow scheduling, a different subject. ## Triage's second question: could anything pin the claim to the actual cap? **One sentence: nothing today can, because the reachability fact exists only as free prose — the `REACHABILITY` text inside the `note` string of `packages/spec/liveness/*_endpoints.json` and the docblocks — with no structured field and no gate that reads it, so a pin would first have to promote `embedder-only` to a machine-readable per-key value and only then could a prose gate hold every mention honest.** Measured, not assumed: `REACHABILITY` appears in six liveness ledgers as prose inside `note`; the three `scripts/**` hits for the word (`check-dispatcher-error-vocabulary.mjs`, `check-half-states.mjs`, `bare-root-worklist.mjs`) are unrelated uses in comments. Nothing consumes it. The shape such a pin would take already exists as a precedent: `scripts/check-docs-spec-enumerations.mjs` (#17388) holds hand-written prose equal to a derived set, and its own header states this card's whole problem — > Prose is where the class hides, because nothing reads it. ⛔ Not built here, per the dispatch. ## Report items, deliberately not in this diff 1. `content/docs/releases/v17/17-0.mdx:1734` — the claim once more, on the RELEASE-OWNED tree. Named by the card; a dedicated docs-only PR or its own card, never a rider. 2. Fifth carrier A — `packages/spec/src/api/batch.zod.ts:128`. 3. Fifth carrier B — `packages/rest/CHANGELOG.md` and `packages/spec/CHANGELOG.md`. 4. `content/docs/references/api/**` is AUTO-GENERATED and untouched; `rest-server.mdx:287` already carries the corrected wording. ## Verification **Changeset: `skip-changeset`, measured.** `content/docs/**` appears in no published package's `files[]`, and its only owner `@objectstack/docs` is `private: true`. Positive control: `@objectstack/spec` and `@objectstack/rest` are published and do declare `files[]` arrays, neither of which lists `content/`. Nothing published moves. **Gates: 39 derived, 39 run, 39 green.** Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (three-dot change set vs merge base `080688b56`), reconciled with `--ran`: *"39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN"*. Exit codes captured before any pipe. Five refused on the first pass with **PREREQUISITE NOT MET** — not findings, and nothing was measured by them: `check:doc-formula-expressions` (3), `check:doc-security-posture` (3), `check:docs-transcript-drift` (3), `check:docs` (1, `packages/spec/json-schema` missing), `check:skill-examples` (1, no `.d.ts` in `dist`). All five were re-run to a real verdict after building `@objectstack/spec`, `@objectstack/lint`, `@objectstack/formula`, `@objectstack/client` and `@objectstack/client-react` under the shared verify lock (`VERDICT command-exit 0`, two laps). Final reading on the built tree: **all 39 exit 0**, including `check:nul-bytes`, `check:doc-authoring`, `check:doc-anchors`, `check:docs-single-h1` and `check:docs-spec-enumerations`. Note for the record: `check:cross-package-test-inputs` is carded as reddening on a **built** worktree (#18348). It did **not** reproduce here — exit 0 both before and after the build, with `packages/spec/dist/*.d.ts` present on the second reading. Reported as a non-reproduction, ⛔ not filed as anything new. **Repo-wide scans** (`pnpm lint` and the artifact-roster / wide-population families `dispatch-gates` prints outside the derived total) are CI-owned and are not claimed here. **Firing controls**, per the standing rule that a control must read 0 inside the diff and be present in the probed file: | term | in diff | in file | role | |:--|--:|--:|:--| | `embedder-only` | 1 | 1 | firing | | `batch.maxBatchSize` | 1 | 1 | firing | | `BATCH_UNRESOLVED_REF` | 0 | 1 | control ✅ | | `maxDepth` | 0 | 1 | control ✅ | | `BATCH_NOT_ATOMIC` | 1 | 1 | ⛔ rejected — it appears in the diff's context lines, so it is not a control | **Control bytes:** `grep -naP` over the touched file for the C0 range plus DEL returns no match. ## Acceptance notes - noted, not filed: `content/docs/protocol/kernel/http-protocol.mdx:543` carries a structurally identical sentence for a different knob — *"Maximum expand depth: 3 levels by default (configurable via the query adapter's `maxDepth`)"*. It is **not** in this card's defect class: no ruling has been taken on `maxDepth`'s reachability, so calling it a carrier would be inventing an adjudication rather than applying one. Recorded because the next reader of this page will see the same shape two screens up, and because it is the natural second subject for the pin described above. Successor: whoever takes fifth carrier A, since `maxDepth`'s reachability question is the same measurement on a different key. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk --- _Generated by [Claude Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0f10c65 commit 0e2ebce

1 file changed

Lines changed: 7 additions & 4 deletions

File tree

‎content/docs/protocol/kernel/http-protocol.mdx‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -848,10 +848,13 @@ Content-Type: application/json
848848
```
849849

850850
**Behavior:**
851-
- Maximum batch size: 200 operations by default (configurable via `maxBatchSize`).
852-
Over the cap is `400 BATCH_TOO_LARGE`, carrying the `count` sent and the `max`
853-
allowed. The same cap and the same code apply to every bulk write route
854-
(`createMany` / `updateMany` / `deleteMany` / per-object `batch`)
851+
- Maximum batch size: **200** operations. The cap is `batch.maxBatchSize`, and it
852+
is **embedder-only** (#15543): it is written only by a host that constructs the
853+
`RestServerConfig` itself, never by `os serve` or the dev plugin, so a
854+
CLI-started deployment always gets the default of 200. Over the cap is
855+
`400 BATCH_TOO_LARGE`, carrying the `count` sent and the `max` allowed. The
856+
same cap and the same code apply to every bulk write route (`createMany` /
857+
`updateMany` / `deleteMany` / per-object `batch`)
855858
- The entire batch runs inside **one engine transaction** — if any operation
856859
fails, all are rolled back (commit-all-or-nothing). The batch is always
857860
atomic; an explicit `"atomic": false` is rejected with `400 BATCH_NOT_ATOMIC`

0 commit comments

Comments
 (0)