Skip to content

Commit 5a22eb5

Browse files
test(spec): the first api/ file group's test titles state each cited decision in words instead of a tracker number (stage 24) (#21961)
Part of #20749 Clause-②: no Stage 24 of this card: the next area of class (e), the test strings shipped under `packages/spec/src`, as ruled in `5902360492` on #20513. This stage takes the first name-ordered `api/` group: the 27 id-bearing test files directly under `packages/spec/src/api/` from `ai-agents-envelope.test.ts` to `package-lifecycle.test.ts`. Those files carried 100 messages and 106 tracker ids, citing 65 records. All 106 now either state what their record decided, in words (form D), or are dropped where the title already says it. No needle sits in this group. Text only: no assertion, identifier, test count or code comment changes, and no file is renamed. ## Census at the base (`a3bd157730`) Instruments: `census10.cjs` (md5 `9d08602ab972b4b8643c90d64d40fa41`), `census.cjs` (md5 `6e42a45a926d375013c32d62f16a296e`), `census-wide.cjs` (md5 `c98410a19529c439adb0afbfb00026a2`) and `dirtable.cjs` (md5 `dda605c54745b4a60cc14c9a686e4eff`), byte-identical to the copies stages 10 to 23 used. A literal counts as a test title when its folded message is argument 0 of a `describe` / `it` / `test` call, `.each` / `.skip` / `.only` chains included. Everything else is an "other" string. The worktree was cut from `origin/main` at `a3bd157730`, the claim's base and stage 23's landing. Both instruments read **471 messages / 498 ids in 111 files**, the seat's reading and stage 23's head reading. | directory | files | messages / ids | titles | other | |:--|--:|--:|--:|--:| | `api/` (this PR: 27 of the 40 files) | 40 | 189 / 201 | 181 / 193 | 8 / 8 | | `system/` | 34 | 154 / 167 | 128 / 138 | 26 / 29 | | (files directly in `src/`) | 30 | 118 / 120 | 117 / 119 | 1 / 1 | | `ui/` | 5 | 7 / 7 | 0 | 7 / 7 | | `ai/` | 1 | 2 / 2 | 0 | 2 / 2 | | `contracts/` | 1 | 1 / 1 | 0 | 1 / 1 | | **total** | **111** | **471 / 498** | **426 / 450** | **45 / 48** | The group reads **100 messages / 106 ids in 27 files**, the seat's figures file for file: | file (under `api/`) | messages / ids | titles | other | |:--|--:|--:|--:| | `ai-agents-envelope.test.ts` | 1 / 1 | 1 / 1 | 0 | | `analytics.test.ts` | 3 / 3 | 3 / 3 | 0 | | `api-entry-graph.pin.test.ts` | 1 / 1 | 1 / 1 | 0 | | `api-error-code-type.test.ts` | 1 / 1 | 1 / 1 | 0 | | `apis-publish-gates.test.ts` | 12 / 12 | 12 / 12 | 0 | | `auth-endpoints.test.ts` | 2 / 2 | 2 / 2 | 0 | | `auth.test.ts` | 2 / 2 | 1 / 1 | 1 / 1 | | `automation-api.zod.test.ts` | 4 / 5 | 4 / 5 | 0 | | `batch.test.ts` | 2 / 2 | 2 / 2 | 0 | | `contract.test.ts` | 3 / 3 | 3 / 3 | 0 | | `dataset-selection.test.ts` | 5 / 5 | 5 / 5 | 0 | | `discovery-auth-families.pin.test.ts` | 2 / 2 | 2 / 2 | 0 | | `discovery-environment-subset.pin.test.ts` | 2 / 2 | 1 / 1 | 1 / 1 | | `discovery.test.ts` | 10 / 11 | 10 / 11 | 0 | | `dispatcher.test.ts` | 2 / 2 | 2 / 2 | 0 | | `endpoint.test.ts` | 4 / 4 | 4 / 4 | 0 | | `envelope-violations.test.ts` | 1 / 1 | 1 / 1 | 0 | | `error-code-ledger.test.ts` | 7 / 11 | 7 / 11 | 0 | | `errors.test.ts` | 3 / 3 | 3 / 3 | 0 | | `export-job-family-retirement.test.ts` | 6 / 6 | 3 / 3 | 3 / 3 | | `export.test.ts` | 3 / 3 | 3 / 3 | 0 | | `meta-item-response-shapes.test.ts` | 2 / 2 | 2 / 2 | 0 | | `metadata.test.ts` | 1 / 1 | 1 / 1 | 0 | | `odata-orderby-dual-declaration.test.ts` | 1 / 1 | 1 / 1 | 0 | | `package-api.test.ts` | 10 / 10 | 10 / 10 | 0 | | `package-install-one-authority.test.ts` | 1 / 1 | 1 / 1 | 0 | | `package-lifecycle.test.ts` | 9 / 9 | 9 / 9 | 0 | | **27 files** | **100 / 106** | **95 / 101** | **5 / 5** | Five more test files sit in the same name range and carry no id (`documentation.test.ts`, `error-catalog-docs.test.ts`, `events.test.ts`, `http-cache.test.ts`, `odata.test.ts`). The five "other" strings are expect messages, rewritten and declared to the text-only tool: `auth.test.ts:155`, `discovery-environment-subset.pin.test.ts:65` (one leaf of a `+` chain) and `export-job-family-retirement.test.ts:112` (a template literal), `:158` and `:349`. - **Controls.** Lit: `ui/notification.test.ts` (1 id) and `api/protocol.test.ts` (50 ids), outside the group, read the same at the base and at the head. Dark: `package-api.test.ts` reads 0 at the head while 30 of its comment lines still carry a number. Planted in a scratch tree: an id put into a `package-lifecycle.test.ts` title reads 1 / 1 (`title:describe`), and an id put into a `batch.test.ts` comment reads 0. - **A wider pattern** (any `#` plus digits) reads the same as the gate pattern in all 27 files at the base, and 0 in all 27 at the head. - **At the head:** 371 messages / 392 ids in 84 files. The 27 files read 0 / 0, `api/` reads 89 / 95 in 13 files, and no other file moved. ## How the area was chosen `api/` has no subdirectory, so it is taken in name-ordered file groups near the ~100-id bound, the rule stages 20 to 23 used. Stage 23's cut named this group at 106 ids, and this census reads 106, so no re-cut was needed. **Named for the next stages** (cut from the head census, 371 / 392): - **The second `api/` group:** `plugin-rest-api.handler-status-retirement.test.ts` through `zod-issues-to-fields.test.ts`, 13 files, 89 messages / 95 ids (86 / 92 titles, 3 / 3 other), `protocol.test.ts` alone 46 / 50 and `rest-server.test.ts` 19 / 19. That finishes `api/`. - `system/` 167, two stages. The files directly in `src/`, 120, one. - The needles: the three docblock needles, the kept `ui/component-props-unknown-members.pin.test.ts:322` and stage 22's two. One stage, with an at-tier review. The four colour literals stay, as stage 21 decided. ## What each id became - **18 literals (22 ids)** now state a decision in words. - **10 literals (10 ids)** get their subject back in words, where the number stood for a thing. - **72 literals (74 ids)** drop a number the title already explains. Every cited record was fetched with all its comments through REST (357 comments, #4052's included), and its decision was read from its ruling, ACCEPT and landing comments. 65 records are cited: 59 answer 200 and 6 answer 404. Two of the 200s are PRs (#4049 and #20218), read from their bodies. One citation is objectui's and was read from objectui: `objectui#6593`. The six that answer 404 were read from what landed, through the commits endpoint (this checkout is shallow), each named by the commit the stage-2 re-anchoring of `api/` comments gave it: - **#6287**, from `84c86fb454` (#6610): `preview` and `trial` fold to `sandbox` by declaration, and the fold table is typed total over `EnvironmentType`; - **#6704**, from `c3f4916266` (#7015): `ImportRequest.runAutomations` declares the default the import route applies; - **#10330**, from `b9e9227e36` (#11316): `mappingName` declared on `ImportRequestSchema`, with the mutual-exclusion refine; - **#10338**, from `d2619fd0cd` (#11290): `ApiEndpoint.target` is optional, and the publish gate holds the flow requirement; - **#11504**, from `f90e820249` (#12611): `FLOW_INPUT_SCHEMA_INVALID` registered, the never-dispatched code; - **#16649**, from `613bfbd3db` (#16879): the fourteen remaining `door: 'none'` codes registered. One citation names a different record. `batch.test.ts:78` read "(#3963 follow-up)"; #3963 is the `api.requireAuth` retirement. The `validateOnly` tombstone is #4052's decision, read too: never implemented, so tombstoned rather than half-built. The title already says that ("rejects the retired `validateOnly` key with its prescription"), so the number is dropped. Where a record's decision was refined later, the title follows the refined one: - **#4936 and #5111:** #4936's ruling refused every non-empty `apis:`; #5111 narrowed that to per-endpoint gates. The `:152` title says what held through both: an empty or absent `apis:` was never refused. - **#4910 Q2:** that ruling left endpoint-level `rateLimit` unwired and tracked under #4936; #4936's ruling then kept it in the vocabulary for the endpoint executor to wire. The title names that destination. - **#17518:** its 2026-09-13 ruling was re-presented and briefly replaced (batch #149, withdrawn as unexecutable), then confirmed (batch #159, letter A) and given its mechanism (batch #192, letter A′), which adds the record-stage body. The title "the row's manifest is the RECORD stage" is that body, so only the number goes. - **#18605:** ruling letter 1 made the request contract the one authority, and #18877's later ruling made that key optional so the door sees an absence; the title says only "has ONE authority", which both keep, so only the number goes. **Stated in words:** | record | literal (under `api/`) | now reads | the decision | |:--|:--|:--|:--| | #18576 | `api-entry-graph.pin.test.ts:77` | "… stays off the assembled package body (ruled: split the entry rather than watch its weight)" | Ruling B (batch #145 item 1, maintainer 2026-09-17): the cost is removed, not watched; `./api` is split and the assembled-package declarations move to `@objectstack/spec/api-assembled`. | | #4936 | `apis-publish-gates.test.ts:152` | "still accepts an EMPTY and an ABSENT `apis:` — never refused, even while a non-empty one was" | Maintainer ruling 2026-08-04: v17 loudly refuses a non-empty `apis:` and keeps the vocabulary; an empty or absent one stays publishable, then and after #5111's narrowing. | | #4910 | `apis-publish-gates.test.ts:568` | "keeps endpoint-level `rateLimit` in the vocabulary (ruled: left to the endpoint executor, not the server-level seam)" | Q2 = B (2026-08-03): that card wires the server level only; the endpoint-level keys stay, and #4936's ruling has the endpoint executor wire them. | | #5189 | `apis-publish-gates.test.ts:597` | "still refuses D6 — the gate with no runtime counterpart, so the per-item publish path runs it too" | Triage disposition (E7b, 2026-08-04): `publishPackage` reuses the same gate function, because D6 alone has no runtime counterpart. | | #7481 | `auth-endpoints.test.ts:112` | "AuthFeaturesConfig retired flags (ruled: stop advertising them)" | Maintainer ruling 2026-08-11: `passkeys` / `magicLink` leave the `/api/v1/auth/config` payload. | | #14788 | `auth.test.ts:88` | "SessionUser.language retirement (ADR-0049 — ruled: gone, with no replacement field)" | Maintainer ruling D (2026-09-03): retired under ADR-0049, no producer and no consumer; no replacement field until a real producer exists. | | #9378, #9510 | `automation-api.zod.test.ts:327` | "… status, runId and the screen (a pause is the third state, not a failure)" | #9510's ruling (2026-08-18): a pause is not a failure, and callers learn the third state deliberately; `status: 'paused'` + `runId` + `screen` is the trigger contract's third state. | | #4828 | `discovery.test.ts:1167` | "scoping (ruled: declare what REST actually emits)" | Maintainer ruling 2026-08-05, item 3: `scoping` is declared on `DiscoverySchema` as an optional key. | | #4828 | `discovery.test.ts:1207` | "resolveDiscoveryEnvironment (ruled: an enum, not a passthrough)" | Item 4: the schema is authoritative, so every producer's `environment` is mapped into the declared enum. | | #8211 | `error-code-ledger.test.ts:68` | "standard-synonym detection (ruled: refused unless waived)" | Option C (triage adjudication, 2026-08-12): the admission gate refuses a semantic synonym of a standard member unless a recorded waiver admits it; the four existing ones are waived. | | #10025, #11504 | `error-code-ledger.test.ts:220` | "accepts the definition-level input-schema refusal code (ruled non-retryable: a never-dispatched exit)" | Maintainer ruling B (2026-08-20): the refusal is non-retryable and becomes a never-dispatched exit with its own ADR-0112 code. | | #16449, #16404 | `error-code-ledger.test.ts:234` | "accepts the nine-code batch — every code that ships in dist, door or no door (ruled: the ledger is the published face)" | #16404 option D (batch #62, 2026-09-07): the ledger is the published face, so every code in `dist` is registered; #16449 registered the nine. | | #16649, #16404 | `error-code-ledger.test.ts:308` | "accepts the fourteen remaining door:none codes, each under its stamping package (ruled: the ledger is the published face)" | The same ruling; `613bfbd3db` registered the fourteen. | | #17158 | `export-job-family-retirement.test.ts:158`, `:349` (expect messages) | "… the retirement is being undone — nothing served, bound or consumed the family" | Ruling A (batch #122 item 3, 2026-09-12; landing route A, batch #221 item 2): ADR-0049 retires a declared API that nothing serves, binds or consumes. | | #12038 | `package-api.test.ts:603` | "package-rollback-response retirement (ruled: it described the wrong operation on the live path)" | Ruling 3A (2026-08-27): the published version-rollback schema, bound to the live commit-rollback path, is retired first. | | #12038 | `package-lifecycle.test.ts:25` | "the ruled re-export of PackagePublishResultSchema into the `/api` namespace" | Ruling 5A: re-export the existing schema into the namespace the ledger resolver searches, never a second copy. | | #12038 | `package-lifecycle.test.ts:140` | "RollbackToPackageCommitResponseSchema declares the COMMIT-rollback body (ruled: authored once the wrong-operation schema was retired)" | Ruling 3A's binding sequence: retire the false declaration, then author the true commit-rollback schema. | **Subject back in words** (10 literals): "the pre-#4053 bare body" becomes "the bare body from before the envelope relocation" (#4053's end state: both producers relocated the payload under `data`); "(#3891 shim dialect)" becomes "(the degraded shim dialect)"; "the duplicate-payload drift #4049 removed" becomes "the duplicate-payload drift the /share-links domain stopped emitting", the PR's own title; "zero holders after #17158" becomes "after the export-job family retirement"; "the #10330 TS2353 repro" becomes "the original TS2353 repro"; the three "since PR #20218" titles become "since the door parses its whole body" (twice) and "so does the door, which parses the whole body", the PR's own title; the `#17534` title now names "the reverse-domain id rule", that card's ruling A; "the objectui#6593 confusion" becomes "the envelope-vs-payload `success` confusion", the defect objectui#6593 measured. **Dropped where already stated** (72 literals, 74 ids). A number goes only where the title already says its decision. Examples: the eight `[#5111]` describes ("the flip — a well-formed `apis:` publishes", "gate (a)" to "gate (e)", …), `[#5310]`, `[#19920]`, the two `[#21046]`, `[#5676]`, `[#5672]`, `[#5679]` and `[#6287]` prefixes; the four `#17551` / `#17550` section prefixes in `dataset-selection.test.ts`, which keep the file's own `§1` to `§5`; `#5384 —`, `#5227 —`, `#5950`, `#5882`, `#17518`, `#18058 —` and `#18605 —`; the four `#15677` citations on the "→ …Seconds" renames; and the tails `(#3878)`, `(#6442)`, `(#19543)` x2, `(#7359)`, `(#3939)`, `(#18124)`, `(#3842)` x3, `(#10338)`, `(#6704)`, `(#10330)`, `(#4587)`, `(#17667)`, `(#19116)`, `(#17431)`, `(#19441)`, `(#8211)`, the five `(#12038)` and the one `(#12038 4A)` after "declares the four fixed keys and stays open". `(federated ledger, #4805)`, `(ADR-0076 D12, #2462)` and `(ADR-0112 amendment 2026-08-18, #9266)` keep their words and lose the number. The ADR-0087 conversion id `api-endpoint-cache-ttl-to-cache-ttl-seconds` stays: it is not a tracker id. **No file is renamed.** ## Readers - **`error-code-ledger.test.ts`** (11 ids in 7 titles): no ledger, gate or self-test reads its strings. `scripts/check-error-code-casing.mjs` names the file only to exempt it whole ("the ledger admission test"); the ledger's docblock and its generated reference page name the file, never a title; the provenance and dispatcher-vocabulary gates read `error-code-ledger.zod.ts`, not the test. - **Needles:** none. The five declared strings are all assertion failure messages (the second argument of `expect`), none is an expected value, and no title or message in the group is matched against a source docblock or another file's text. - **Test-name filters:** none. No tracked script, workflow or package config passes `-t` / `--testNamePattern` to vitest; the one vitest `-t` hit is a README example under `packages/qa/dogfood` filtering its own fixture. - **Snapshots:** none. No `__snapshots__` directory is tracked under `packages/spec`, and none of the 27 files calls a snapshot matcher. - **Projects:** `export-job-family-retirement.test.ts` is in the `repo` project (`packages/spec/vitest.repo-tests.json:30`); the other 26 run in `local`. The base-versus-head run below takes both projects. - **By substring:** every old literal, its id-bearing fragment and a window around each id (294 needles) was searched with `git grep` at the base, across the tracked tree outside its own file. No gate, doc, filter, snapshot, QA checklist entry or `scripts/check-*.mjs` self-test reads one. The 17 hits are windows that share wording with code comments and one CHANGELOG line: "(ADR-0076 D12, #2462)" in comments in `runtime/http-dispatcher.ts`, `spec/api/discovery.zod.ts` and `objectql/protocol-discovery.test.ts` and at `packages/runtime/CHANGELOG.md:14161`; "(#18576 ruling, letter B)" in three comments; "(#3891 shim dialect)" in `runtime/domains/analytics.ts:41`; "is retired (#19543)" in `spec/api/automation-api.zod.ts:645`. ## Text-only proof Stage 10's scratch tool (`textonly10.cjs`, md5 `d5e4801dbb4329ab1984da91e92fc47c`) compares base and head file by file on three legs: 1. **Skeleton:** the full AST, with string pieces masked. It must be identical. 2. **Comments:** every comment, byte-equal. 3. **Strings:** each changed string leaf must sit in a test-call title position or on a declared line, must carry a tracker id before, and must carry no `#` plus digits after. This stage declares the five expect-message lines named above. - **Result:** 27 of 27 files SAME on all three legs, with the per-file counts predicted in writing before the run. - **Totals:** 100 changed string leaves in 100 literals: 95 titles and 5 declared. The diff's `+` and `-` lines are exactly the 100 planned lines as multisets, and every file keeps its line count. - **Controls (14 of 14 as predicted on the first run, on scratch copies, each anchor hit once):** identifier rename DIFF; numeric literal DIFF; comment edit COMMENT DIFF; a non-title string given an id VIOLATION; a rewritten title given a new id VIOLATION; a title that was id-free at base edited VIOLATION; one title reverted to base SAME; an `it.each` row given an id VIOLATION; an undeclared expect message changed VIOLATION; a title re-split into a `+` chain DIFF; a declared expect message reverted to base SAME; a declared expect message given a new id VIOLATION; a declared `+`-chain leaf given a new id VIOLATION; a template-literal message given a new id VIOLATION. - **Templates and tables:** no `.each` title and no `$name` placeholder changes. The one template literal, `export-job-family-retirement.test.ts:112`, changes only its text after `${name}`. **Test counts:** the 27 files were run at the base, in a separate base worktree, and at the head, with `--project local --project repo`. Both sides read 831 tests in 27 files, all passed, with the same count and status sequence per file in 27 of 27. 325 full test names change, and each changed name equals the base name with the planned replacements applied: 0 mismatches once the plan's text is read the way the source writes it (the comparison tool reads the plan's `—` escape at `errors.test.ts:439` literally, so its first pass reports that title's three names as mismatches; decoding the escape, as vitest does, reads 0). No full name repeats on either side. ## Changeset: `skip-changeset` Measured, not assumed: - `npm pack --dry-run` of `@objectstack/spec` lists 2068 files. 0 of the 27 touched files are in it, and no `*.test.ts` at all. The controls `src/api/package-lifecycle.zod.ts`, `src/api/error-code-ledger.zod.ts` and `dist/index.mjs` are in it. - In the built `dist/`, a new phrase and an old one each read in 0 files. The control `Unrecognized key` reads in 42. So this PR publishes nothing, and no changeset is added. ## Verification (at `dffd240655`) - `pnpm turbo run build` over all packages: 71 / 71, through the shared verify lock (`VERDICT command-exit 0`). - `@objectstack/spec`: - `vitest run --project local`: 619 files, 18471 passed, 1 todo. - `typecheck`: exit 0, including `check:test-typecheck` (52 files / 246 errors / 135 pinned signatures held). Its program holds all 27 group files, counted by path with `tsc --listFilesOnly -p tsconfig.test.json`. - `check:generated`: all 15 generated artifacts up to date, against the `dist/` the build above wrote. - **Gates:** `dispatch-gates --commands` derived 80 families: stage 23's 79 plus `check:error-code-casing`, which the two touched files it names bring in. All 80 exit 0. `--ran` reconciles: 80 derived, 80 run, 0 NOT-MEASURED, 0 UNRUN, every family with its exit code recorded. The same 80 derive from `origin/main` `01e0f71ad8` with this diff applied. The roster families stage 23 also ran (`check:meta-url-spelling`, `check:spec-changes`, `check:authz-resolver`, `check:filter-alias-parity`) each exit 0. - **ESLint, a proven narrowing:** `--no-inline-config` over the 27 files reads 0 errors and 0 warnings. The population comes from ESLint's own config: 27 configured, 0 ignored. No file sets `parserOptions.project` or `projectService`, so no untouched file's verdict can move. - `check-governed-merges --test`: NOT governed, 200 changed lines (+100 / -100). - A control-byte scan over the 27 changed files finds none. ## `main` since the base Re-fetched just before this PR opened, `origin/main` was two commits past the base (`01e0f71ad8`: #21940, #21953). They touch 31 files, none of the 27 and none under `packages/spec`, so `main` was not merged and the census on that tree is the base's. `git merge-tree` onto `01e0f71ad8` is clean, and none of the 5 open PRs touches any of the 27 files. ## Acceptance notes - **Same-id test titles in this card's later stages** go with those stages: 23 lines in `packages/spec/src`, among them `api/protocol.test.ts` (`[#5672]` x2, `(#12038)` x5, `(#12038 1C)`, `(#19543, door ③)`), `api/plugin-rest-api.test.ts`, `api/router.test.ts` and `api/websocket.test.ts` (`(#15677)`), `stack-json-stage-package-body.test.ts` (`#17518` x4), `system/book.test.ts` (`(#12038)`) and three `system/` titles citing `(#18124)`. - **Same-id test titles in other packages** stay: 96 lines in 12 packages (`runtime` 37, `rest` 24, `client` 9, `metadata-protocol` 6, `service-automation` 6, `metadata` 5, `cli` 3, `objectql` 2, and one each in `examples/app-showcase`, `core`, `plugin-hono-server` and `verify`), each package's share under the #20513 lane children. - **Code comments with live ids** remain in these files and their sources, among them the `// package-rollback-response retirement (#12038 3A)` banner above its describe, the `[#5111 / #5040 E7]` and `[#5189 / #5040 E7b]` headers in `apis-publish-gates.test.ts`, and the `[#17158]` header in `export-job-family-retirement.test.ts`. Code comments are not this card's share. --- _Generated by [Claude Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2a22177 commit 5a22eb5

27 files changed

Lines changed: 100 additions & 100 deletions

‎packages/spec/src/api/ai-agents-envelope.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe('GET /ai/agents — the shapes a producer must NOT drift back into', ()
9191
expect(servesTheDeclaredShape(body)).toBe(false);
9292
});
9393

94-
it('the pre-#4053 bare body — no flag for `unwrapResponse` to key on', () => {
94+
it('the bare body from before the envelope relocation — no flag for `unwrapResponse` to key on', () => {
9595
const body = { agents: [ASK] };
9696
expect(servesTheDeclaredShape(body)).toBe(false);
9797
expect(envelopeViolations(body)).toContain('success is missing, must be a boolean');

‎packages/spec/src/api/analytics.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('AnalyticsEndpoint', () => {
6363
});
6464
});
6565

66-
describe('AnalyticsQueryRequestSchema — the BARE AnalyticsQuery shape (#3878)', () => {
66+
describe('AnalyticsQueryRequestSchema — the BARE AnalyticsQuery shape', () => {
6767
it('should accept a minimal bare query', () => {
6868
const req = AnalyticsQueryRequestSchema.parse({
6969
cube: 'orders',
@@ -104,7 +104,7 @@ describe('AnalyticsQueryRequestSchema — the BARE AnalyticsQuery shape (#3878)'
104104
).toThrow();
105105
});
106106

107-
it('should reject the retired {cube, query: {...}} envelope (#3891 shim dialect)', () => {
107+
it('should reject the retired {cube, query: {...}} envelope (the degraded shim dialect)', () => {
108108
expect(() =>
109109
AnalyticsQueryRequestSchema.parse({
110110
cube: 'orders',
@@ -384,7 +384,7 @@ describe('GetAnalyticsMetaRequestSchema', () => {
384384
* replaced by the assertion that carries the actual load of this change: the
385385
* previously-declared shape is now rejected.
386386
*/
387-
describe('AnalyticsMetadataResponseSchema — the CubeMeta[] projection (#6442)', () => {
387+
describe('AnalyticsMetadataResponseSchema — the CubeMeta[] projection', () => {
388388
/**
389389
* A real `GET /analytics/meta` body: what `AnalyticsService.getMeta` builds —
390390
* measure/dimension names CUBE-QUALIFIED, `title` projected from the

‎packages/spec/src/api/api-entry-graph.pin.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function valueGraph(entry: string): Set<string> {
7474
return new Set([...seen].map((f) => relative(SRC, f).split('\\').join('/')));
7575
}
7676

77-
describe('`@objectstack/spec/api` stays off the assembled package body (#18576 ruling, letter B)', () => {
77+
describe('`@objectstack/spec/api` stays off the assembled package body (ruled: split the entry rather than watch its weight)', () => {
7878
const api = valueGraph('api/index.ts');
7979
const assembled = valueGraph('api-assembled/index.ts');
8080

‎packages/spec/src/api/api-error-code-type.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function refusalAt(result: z.ZodSafeParseResult<unknown>): Array<{ code: string;
6868
return (result.error?.issues ?? []).map((issue) => ({ code: issue.code, path: issue.path }));
6969
}
7070

71-
describe('[#19920] ApiError.code is typed as the vocabulary its schema parses against', () => {
71+
describe('ApiError.code is typed as the vocabulary its schema parses against', () => {
7272
it('every body the type refuses is refused by the schema at `code`', () => {
7373
expect(refusalAt(ApiErrorSchema.safeParse({ code: 42, message: 'x' }))).toEqual([
7474
{ code: 'invalid_value', path: ['code'] },

‎packages/spec/src/api/apis-publish-gates.test.ts‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function accept(stack: Record<string, unknown>) {
8585
return result.success ? result.data.apis : undefined;
8686
}
8787

88-
describe('[#5111] the flip — a well-formed `apis:` publishes', () => {
88+
describe('the flip — a well-formed `apis:` publishes', () => {
8989
it('accepts an object_operation endpoint under the stack\'s own namespace', () => {
9090
const apis = accept({ manifest, apis: [validObjectEndpoint] });
9191
expect(apis).toHaveLength(1);
@@ -149,7 +149,7 @@ describe('[#5111] the flip — a well-formed `apis:` publishes', () => {
149149
});
150150
});
151151

152-
it('still accepts an EMPTY and an ABSENT `apis:` — the #4936 regression pin', () => {
152+
it('still accepts an EMPTY and an ABSENT `apis:` — never refused, even while a non-empty one was', () => {
153153
expect(ObjectStackDefinitionSchema.safeParse({ manifest, apis: [] }).success).toBe(true);
154154
expect(ObjectStackDefinitionSchema.safeParse({ manifest }).success).toBe(true);
155155
expect(() => defineStack({ manifest, apis: [] })).not.toThrow();
@@ -162,7 +162,7 @@ describe('[#5111] the flip — a well-formed `apis:` publishes', () => {
162162
});
163163
});
164164

165-
describe('[#5111] gate (c) — namespace carve-out (ADR-0121 D1/D2)', () => {
165+
describe('gate (c) — namespace carve-out (ADR-0121 D1/D2)', () => {
166166
it('rejects a path outside the `apps/<namespace>/` mount, naming the endpoint and the shape', () => {
167167
const message = reject({
168168
manifest,
@@ -214,7 +214,7 @@ describe('[#5111] gate (c) — namespace carve-out (ADR-0121 D1/D2)', () => {
214214
});
215215
});
216216

217-
describe('[#5310] the `path` vocabulary text is itself publishable', () => {
217+
describe('the `path` vocabulary text is itself publishable', () => {
218218
/**
219219
* `ApiEndpointSchema.path`'s `.describe()` is not a code comment. Since `api`
220220
* became a registered metadata kind (#5271) it is the field help the
@@ -273,7 +273,7 @@ describe('[#5310] the `path` vocabulary text is itself publishable', () => {
273273
});
274274
});
275275

276-
describe('[#5111] gate (a) — the supported subset (mirrors `planEndpointTarget`)', () => {
276+
describe('gate (a) — the supported subset (mirrors `planEndpointTarget`)', () => {
277277
it("rejects `type: 'script'` with the flow prescription", () => {
278278
const message = reject({
279279
manifest,
@@ -324,7 +324,7 @@ describe('[#5111] gate (a) — the supported subset (mirrors `planEndpointTarget
324324
});
325325
});
326326

327-
describe('[#5111] gate (b) — mapping declarations (mirrors `mappingDeclarationRejection`)', () => {
327+
describe('gate (b) — mapping declarations (mirrors `mappingDeclarationRejection`)', () => {
328328
const bodyEndpoint = {
329329
...validObjectEndpoint,
330330
name: 'showcase_task_create',
@@ -399,7 +399,7 @@ describe('[#5111] gate (b) — mapping declarations (mirrors `mappingDeclaration
399399
});
400400
});
401401

402-
describe('[#5111] gate (e) — policy keys (ADR-0121 D6 + the E4 refusals)', () => {
402+
describe('gate (e) — policy keys (ADR-0121 D6 + the E4 refusals)', () => {
403403
it('rejects `authRequired: false` with NO rateLimit', () => {
404404
const message = reject({ manifest, apis: [{ ...validFlowEndpoint, authRequired: false }] });
405405
expect(message).toMatch(/ADR-0121 D6|armed rate limit/i);
@@ -464,7 +464,7 @@ describe('[#5111] gate (e) — policy keys (ADR-0121 D6 + the E4 refusals)', ()
464464
});
465465
});
466466

467-
describe('[#5111] gate (d) — one claim per METHOD + path inside a stack', () => {
467+
describe('gate (d) — one claim per METHOD + path inside a stack', () => {
468468
it('rejects two endpoints claiming the same method and path, naming BOTH', () => {
469469
const message = reject({
470470
manifest,
@@ -508,7 +508,7 @@ describe('[#5111] gate (d) — one claim per METHOD + path inside a stack', () =
508508
});
509509
});
510510

511-
describe('[#5111] every rejection is actionable, and reaches every publish seam', () => {
511+
describe('every rejection is actionable, and reaches every publish seam', () => {
512512
it('reports one issue per offending endpoint — three bad endpoints, three rejections', () => {
513513
const result = ObjectStackDefinitionSchema.safeParse({
514514
manifest,
@@ -538,7 +538,7 @@ describe('[#5111] every rejection is actionable, and reaches every publish seam'
538538
});
539539
});
540540

541-
describe('[#5111] the `ApiEndpoint` vocabulary itself is untouched', () => {
541+
describe('the `ApiEndpoint` vocabulary itself is untouched', () => {
542542
// Anti-vacuity for the whole file: the flip is validation logic on the
543543
// EXISTING keys. If a key had been added, removed or renamed, every
544544
// assertion above could still pass while the frozen-vocabulary constraint
@@ -565,7 +565,7 @@ describe('[#5111] the `ApiEndpoint` vocabulary itself is untouched', () => {
565565
expect(parsed.authRequired).toBe(true);
566566
});
567567

568-
it('keeps endpoint-level `rateLimit` in the vocabulary (#4910-Q2 routed it here)', () => {
568+
it('keeps endpoint-level `rateLimit` in the vocabulary (ruled: left to the endpoint executor, not the server-level seam)', () => {
569569
const parsed = ApiEndpointSchema.parse({
570570
name: 'x_endpoint',
571571
path: '/api/v1/apps/showcase/x',
@@ -594,7 +594,7 @@ describe('identityFreeEndpointGateFailure — the same judge, minus stack identi
594594
expect(identityFreeEndpointGateFailure(ApiEndpointSchema.parse(validObjectEndpoint))).toBeUndefined();
595595
});
596596

597-
it('still refuses D6 — the gate with no runtime counterpart, and the reason #5189 exists', () => {
597+
it('still refuses D6 — the gate with no runtime counterpart, so the per-item publish path runs it too', () => {
598598
const failure = identityFreeEndpointGateFailure(
599599
ApiEndpointSchema.parse({ ...validObjectEndpoint, cacheTtlSeconds: undefined, authRequired: false }),
600600
);

‎packages/spec/src/api/auth-endpoints.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe('AuthEndpointSchema', () => {
109109
// leave nothing to grep (#3733, ADR-0104) — the tombstone is what makes the
110110
// withdrawal audible, so it is pinned on both legs: the prescription a writer
111111
// hits, and the absence a reader gets.
112-
describe('AuthFeaturesConfig retired flags (#7481)', () => {
112+
describe('AuthFeaturesConfig retired flags (ruled: stop advertising them)', () => {
113113
const valid = { twoFactor: false, organization: true };
114114

115115
it('rejects `passkeys` with its own prescription, naming the missing consumer', () => {
@@ -181,7 +181,7 @@ describe('getAuthEndpointUrl', () => {
181181
// than a bare unrecognized-key error, and the value survives at the same
182182
// magnitude. Asserting the message, not just `.toThrow()`: a bare throw stays
183183
// green when the schema throws for some unrelated reason.
184-
describe('DeviceRequestResponse.interval \u2192 intervalSeconds (#15677)', () => {
184+
describe('DeviceRequestResponse.interval \u2192 intervalSeconds', () => {
185185
const base = {
186186
code: 'ABCD-1234',
187187
verificationUrl: 'https://example.com/device',

‎packages/spec/src/api/auth.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe('SessionUserSchema', () => {
8585
});
8686
});
8787

88-
describe('SessionUser.language retirement (#14788, ADR-0049 — maintainer ruling D, 2026-09-03)', () => {
88+
describe('SessionUser.language retirement (ADR-0049 — ruled: gone, with no replacement field)', () => {
8989
const base = { id: 'usr_1', email: 'a@b.com', name: 'A' };
9090

9191
it('REJECTS a `language` value, with the prescription in the message', () => {
@@ -152,7 +152,7 @@ describe('SessionUser.language retirement (#14788, ADR-0049 — maintainer rulin
152152
// and the excluded ledger really is where the key's record lives.
153153
expect(fs.existsSync(tombstone)).toBe(true);
154154
expect(fs.existsSync(path.join(ledger, 'entries', 'retired-keys', '18.api__SessionUser__language.ts'))).toBe(true);
155-
expect(readers, 'a reader of SessionUser.language reappeared — the key is retired (#14788)').toEqual([]);
155+
expect(readers, 'a reader of SessionUser.language reappeared — the key is retired').toEqual([]);
156156
});
157157
});
158158

‎packages/spec/src/api/automation-api.zod.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ describe('AutomationRunPathParamsSchema', () => {
8989
// List Flows — RETIRED (#19543, door ④)
9090
// ==========================================
9191

92-
describe('the GET /api/v1/automation flow-list door is retired (#19543)', () => {
92+
describe('the GET /api/v1/automation flow-list door is retired', () => {
9393
// Flows are metadata (ADR-0106); the list is `GET /api/v1/meta/flow`. The
9494
// three schemas left with the route and are registered as whole-def removals
9595
// (`RETIRED_DEFS_BY_MAJOR[18]`), so an export that came back would be a
@@ -324,7 +324,7 @@ describe('TriggerFlowResponseSchema', () => {
324324
// undeclared keys (BaseResponseSchema.extend + plain z.object), so before
325325
// the widening the paused-run triple below "passed" while the parse silently
326326
// dropped `status`, the `runId` a caller resumes with and the whole screen.
327-
it('should preserve the paused-run triple — status, runId and the screen (#9378/#9510 third state)', () => {
327+
it('should preserve the paused-run triple — status, runId and the screen (a pause is the third state, not a failure)', () => {
328328
const result = TriggerFlowResponseSchema.parse({
329329
success: true,
330330
data: {
@@ -596,7 +596,7 @@ describe('ListRunsRequestSchema', () => {
596596
})).toThrow();
597597
});
598598

599-
it('declares exactly the canonical ExecutionStatus set (#7359)', () => {
599+
it('declares exactly the canonical ExecutionStatus set', () => {
600600
// The wire's filter and the runtime boundary that now enforces it must
601601
// accept ONE set. The boundary reads `ExecutionStatus.options`; this pins
602602
// that the schema does too, so a member added to the enum cannot end up
@@ -754,7 +754,7 @@ describe('AutomationApiContracts', () => {
754754
expect(Object.keys(AutomationApiContracts)).toHaveLength(8);
755755
});
756756

757-
it('declares no flow-list entry and no GET at the bare /api/v1/automation path (#19543)', () => {
757+
it('declares no flow-list entry and no GET at the bare /api/v1/automation path', () => {
758758
expect(Object.keys(AutomationApiContracts)).not.toContain('listFlows');
759759
const routes = Object.values(AutomationApiContracts).map((c) => `${c.method} ${c.path}`);
760760
expect(routes).not.toContain('GET /api/v1/automation');

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('BatchOptionsSchema', () => {
7575
expect(options.continueOnError).toBe(true);
7676
});
7777

78-
it('rejects the retired `validateOnly` key with its prescription (#3963 follow-up)', () => {
78+
it('rejects the retired `validateOnly` key with its prescription', () => {
7979
// Never implemented — a "dry-run" that silently persisted. Tombstoned so
8080
// writing it is audible rather than silently stripped (ADR-0104 / PD #10).
8181
const result = BatchOptionsSchema.safeParse({ validateOnly: true });
@@ -197,7 +197,7 @@ describe('UpdateManyRequestSchema', () => {
197197
// had already drifted: batch.zod's accepted `{}` rows, protocol.zod's required
198198
// id+data, and only the latter was ever enforced. Pin the relationship so a
199199
// future edit to one cannot silently fork them again.
200-
describe('protocol request schemas derive from the wire-body schemas (#3939)', () => {
200+
describe('protocol request schemas derive from the wire-body schemas', () => {
201201
it('updateMany: same record shape, plus object', async () => {
202202
const { UpdateManyDataRequestSchema } = await import('./protocol.zod');
203203
const records = [{ id: '1', data: { name: 'x' } }];

‎packages/spec/src/api/contract.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ describe('QueryOptimizationConfigSchema', () => {
701701
* unregistered code must still fail, and `ApiErrorSchema` must not have been
702702
* widened by the factory's existence.
703703
*/
704-
describe('makeApiErrorSchema (federated ledger, #4805)', () => {
704+
describe('makeApiErrorSchema (federated ledger)', () => {
705705
const DOWNSTREAM_CODES = ['CONTACT_SALES_PLAN', 'PRODUCTION_ENV_LIMIT'] as const;
706706
const DownstreamApiError = makeApiErrorSchema(DOWNSTREAM_CODES);
707707

@@ -778,7 +778,7 @@ describe('makeApiErrorSchema (federated ledger, #4805)', () => {
778778
// than a bare unrecognized-key error, and the value survives at the same
779779
// magnitude. Asserting the message, not just `.toThrow()`: a bare throw stays
780780
// green when the schema throws for some unrelated reason.
781-
describe('DataLoaderConfig.cacheTtl \u2192 cacheTtlSeconds (#15677)', () => {
781+
describe('DataLoaderConfig.cacheTtl \u2192 cacheTtlSeconds', () => {
782782
it('REFUSES the retired `cacheTtl` spelling with the rename in the message', () => {
783783
const result = DataLoaderConfigSchema.safeParse({ cacheTtl: 60 });
784784
expect(result.success).toBe(false);
@@ -810,7 +810,7 @@ describe('DataLoaderConfig.cacheTtl \u2192 cacheTtlSeconds (#15677)', () => {
810810
// contradict, the declaration is taken from the sibling channel that does speak:
811811
// every key in this spec that spells the unit for a processing time spells
812812
// milliseconds (`tracing.durationMs`, `worker.durationMs`, `worker.avgExecutionMs`).
813-
describe('BaseResponse.meta.duration declares milliseconds (#18124)', () => {
813+
describe('BaseResponse.meta.duration declares milliseconds', () => {
814814
it('refuses a fractional millisecond count', () => {
815815
const result = BaseResponseSchema.safeParse({
816816
success: true,

0 commit comments

Comments
 (0)