Skip to content

Commit 347adbd

Browse files
test(rest): drop the registrar-file list's length pins from six rest tests (#26518)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XWY3q77HVXScL7HC17h5BrU)_ Fixes #26433 Part of #26431 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`packages/rest` 里六个测试文件删掉了「注册器文件清单长度 = 18」这条手抄断言(`meta-draft-read-door-census.test.ts` 顺带删掉 `SITES` = 8),只改测试,不发布任何东西。 - 为什么改:清单本身就是权威;这条数字每次把一个路由模块拆出 `rest-server.ts` 都会让六个文件同时变红,钉的是树的转录,不是不变量(维护者在退役检查清扫里「同意」)。 - 风险与回滚:只删断言,每个文件原本要抓的缺陷都做了一次消融,仍然变红;回滚就是 revert 这一个 commit。 - 先看:`meta-draft-read-door-census.test.ts`(唯一加了防空守卫的文件)、`rest-server-docblock-position.test.ts`(还留着一个手算的楼层和 `70`,见 Acceptance notes)、`rest-server-canonical-query-ast.test.ts`。 - 两分钟验证:`pnpm --filter @objectstack/rest exec vitest run src/meta-draft-read-door-census.test.ts src/rest-server-docblock-position.test.ts`,预期 2 个文件全绿。 - 需要你做的:无,席位落地。 ## What changes - Six readers of `REST_SERVER_REGISTRAR_FILES` lose `expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18)`: `ui-view-route-identity.measurement.test.ts`, `ui-view-route-tenancy.measurement.test.ts`, `rest-server-canonical-query-ast.test.ts`, `rest-server-query-number-census.test.ts`, `meta-draft-read-door-census.test.ts`, `rest-server-docblock-position.test.ts`. Each keeps iterating the list it imports. - `meta-draft-read-door-census.test.ts` also loses `expect(SITES).toHaveLength(8)`: its first case ("the sites found are exactly the ledgered ones") already holds the sum as an exact set equality against the ledger. The case that held both pins is left with only a list-mirroring assertion, which passes on an empty list, so it gains the ruled anti-vacuity guard `expect(REST_SERVER_REGISTRAR_FILES.length).toBeGreaterThan(0)`. No other file goes vacuous on an empty list (measured below), so no other guard is added. - Prose the deletion made false is corrected in place, nothing more: the four case titles that said "its length and … are pinned" now name only what is still pinned, the header sentences that said a case "pins the list's length" drop that clause, and each slice-history comment log gets one closing `[#26433]` line. The `length N → M` history entries stay as history. ## Why The list file's own header says it is ⛔ not a gate and that each reader stays the authority on what it counts. The length pin made every reader red on every registrar move, for a number nobody derives anything from (#26431's rule: a pin on something that already has an authority is retired, not loosened). ## Tests All runs on head `83cff86f77` (base `45de8c6cba`), rest's dependency closure built first (`pnpm --filter '@objectstack/rest^...' build`, lock VERDICT exit 0). - The six files: `Test Files 6 passed (6)`, `Tests 223 passed | 18 skipped`. - `pnpm --filter @objectstack/rest test` (project `local`): `Test Files 324 passed (324)`, `Tests 6483 passed | 326 skipped`, VERDICT exit 0. - `pnpm --filter @objectstack/rest test:repo` (project `repo`, which holds `rest-server-canonical-query-ast.test.ts`): `Test Files 5 passed (5)`, `Tests 280 passed | 18 skipped`, VERDICT exit 0. - `pnpm --filter @objectstack/rest typecheck` (tsc plus `check:test-typecheck` over `tsconfig.test.json`, which includes `*.test.ts`): VERDICT exit 0. - Acceptance grep: `git grep -n "REGISTRAR_FILES).toHaveLength" -- packages/rest` → no hit, exit 1. <details><summary>Each file still reds on the defect it was written for (one ablation per file, via scripts/ablation-replace.mjs; every mutation landed and was restored to the HEAD blob)</summary> The six tests read route sources as text from `src/` or mount `RestServer` from `src/` by relative import, so no `dist/` sits on the path and no rebuild is owed per leg. Each ablation mutates a moved route module (not `rest-server.ts`), so it also proves the reader still reaches files off the list. | File | Ablation (route module) | Result | |:--|:--|:--| | `ui-view-route-identity.measurement.test.ts` | delete `if (this.enforceAuth(req, res, context)) return;` in `ui-routes.ts` | 1 file failed: "the handler now calls `enforceAuth` — read off the mounted registrar…" | | `ui-view-route-tenancy.measurement.test.ts` | delete the `enforceEnvironmentOwnership(...)` line in `ui-routes.ts` | 1 file failed, 11 cases, including "⭐ the registrar body itself carries the three steps" and "⭐ THE PIN" | | `rest-server-canonical-query-ast.test.ts` | `where: { id: jobId }` → `filter: { id: jobId }` in `data-action-routes.ts` | 1 file failed: "no server-built `query` literal spells a wire alias" | | `rest-server-query-number-census.test.ts` | add a bare `Number(req.query.zzAbl)` in `search-routes.ts` | 1 file failed: "no coercion is unclassified…" and the sum case (`sites` 8 → 9) | | `meta-draft-read-door-census.test.ts` | add `req.query?.state === 'draft'` in `search-routes.ts` | 1 file failed: "the sites found are exactly the ledgered ones" (so dropping `SITES` 8 loses nothing) | | `rest-server-docblock-position.test.ts` | stack a TSDoc block above another in `email-routes.ts` | 1 file failed: "binds every TSDoc block to a declaration — none is stacked above another" | Every leg: `ablation-replace: ok mutation landed: anchor 1 -> 0`, then `ok restored: blob == HEAD … and git diff HEAD is empty`. (In a first pass the tool refused five of the six legs before any test ran, because each anchor was a substring of its own replacement or was not deleted; the canonical leg ran in that pass and red as it does in the table. All six were rerun with non-overlapping anchors or `--delete`, and the table is that rerun.) </details> <details><summary>Dry move: a new registrar module appended to the list (the point of the change)</summary> Probe: a new `zz-dry-move-routes.ts` (module header and `export {};`, no route body) appended to `REST_SERVER_REGISTRAR_FILES`, list length 19. All legs restored; final `git diff HEAD` empty, `git status` clean. | Leg | Result | |:--|:--| | base test files (pins in) | `Test Files 6 failed (6)`: each of the six reds on its `toHaveLength(18)` line | | this head | `Test Files 2 failed / 4 passed`: identity, tenancy, query-number and meta-draft stay green. `rest-server-canonical-query-ast` and `rest-server-docblock-position` red because their per-file floor tables (`REGISTRAR_QUERY_SLOT_FLOORS`, `MOVED_FILE_FLOORS`) have no row for the new module | | this head plus the two floor rows the module would owe (`0` and `1`) | `Test Files 1 failed / 5 passed`: only `rest-server-docblock-position`'s `expect(…minBlocks sum).toBe(70)` reds | So on this head a new module costs the list file plus its own floor row in two tables, plus the docblock file's `70`. The floor rows are per-file measurements the two censuses ask for by design ("a file the split adds to the list needs a floor here before this census reads it"), not length transcriptions, and the ruling keeps them. The `70` is a hand sum of the table beside it: see Acceptance notes. </details> <details><summary>Empty list: no reader goes vacuous</summary> `REST_SERVER_REGISTRAR_FILES` emptied in place (`].filter(() => false);`), restored after: `Test Files 6 failed (6)`, 11 cases. Each file reds on an assertion it already had (identity and tenancy on the over-40 call counts, canonical on the floors' keys and the slot sum, query-number on its sum and positive control, docblock on the floors' keys), and meta-draft's list case reds on the new guard (`meta-draft-read-door-census.test.ts:291`). </details> ## Verification <details><summary>Local gate subset: 49 derived, 48 run exit 0, 1 NOT MEASURED</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on this diff named 49 commands; each was run with its exit code captured before any pipe, then reconciled with `--ran`: `✓ dispatch-gates --ran: 49 derived famil(ies) accounted for — 48 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)`. - NOT MEASURED: `pnpm check:dual-build-cjs-loads`, exit 3 PREREQUISITE NOT MET (needs every package's `dist/`; only rest's closure is built here). CI runs it. - Lint scoped to the touched package: `eslint packages/rest --no-inline-config` exit 0. - Not run locally, CI's: the repo-wide `pnpm lint`, Test Core shards, the type-check lanes, dogfood (no `!` in the title, no `(narrowing)` arm). </details> `skip-changeset`: tests only. Nothing under `packages/rest` that ships in `files[]` changes. ## Acceptance notes - **Seventh copy, not in this PR:** `packages/qa/dogfood/test/authz-conformance.test.ts:1646` on `origin/main` `b7f2b0c290` (`expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18)`; it sat at 1642 before PR #26491 landed in that file) is `domain:devx`'s; the seat files its child card. - **Same pin under an alias:** `packages/rest/src/execctx-consumer-census.test.ts:848` pins `expect(SITE_FILES).toHaveLength(18)`, where `SITE_FILES = REST_SERVER_REGISTRAR_FILES` (line 149). The card's acceptance grep does not see it. That file is #26436's; it belongs on that card. - **Hand sum beside its own table:** `rest-server-docblock-position.test.ts:301`, `expect(MOVED_FILES.reduce(…)).toBe(70)` is the sum of `MOVED_FILE_FLOORS` in the same file, and it is the one red left in the dry move once the new module has its floor row. Same family as #26431; left alone here because the ruling says nothing else changes. - **Header prose now partly false:** `rest-server-registrar-files.ts:23-26` still says each reader "pins the list's `length`". After this PR that is true only of the dogfood copy and the `execctx` alias above. The file is outside this card's file surface; whichever of those two cards removes the last pin should reword it. --- _Generated by [Claude Code](https://claude.ai/code/session_01FEkQ5nSKhKswuL33xRZZ3g)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 42ec951 commit 347adbd

6 files changed

Lines changed: 22 additions & 20 deletions

‎packages/rest/src/meta-draft-read-door-census.test.ts‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
* their own, and a door that reads a draft is no less a door there. A site's
5050
* key names its route or function, never its file, so the ledger is the same
5151
* ledger. The node helpers below read each node's OWN source file for that
52-
* reason. The census's last case pins the list's length and its sum over it.
52+
* reason. The census's first case holds its sum: the sites found are the ledgered ones.
5353
*/
5454

5555
import { describe, it, expect } from 'vitest';
@@ -236,7 +236,7 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
236236
.toBe(`return isObjectSchemaMaskExempt(${param.name.getText(sf)});`);
237237
});
238238

239-
it('[#23050 slice 3] walks every file on the registrar list — its length and this sum are pinned', () => {
239+
it('[#23050 slice 3] walks every file on the registrar list', () => {
240240
// A module added to the list makes this re-read its number rather than
241241
// widening unnoticed. 8 switches, all in `rest-server.ts` (the `/meta`
242242
// and analytics doors); the data-action, batch and approval bodies read
@@ -285,8 +285,10 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
285285
// `meta-item-history-routes.ts`, carrying no draft switch: neither door reads
286286
// one, so no ledger row changed:
287287
// 8 = 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3 + 2 + 1 + 0 + 0, length 17 → 18.
288-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
288+
// [#26433] Neither the list's length nor the sum is pinned here any more: the
289+
// list is the authority on the files, and the first case's ledger equality
290+
// holds the sum.
291+
expect(REST_SERVER_REGISTRAR_FILES.length).toBeGreaterThan(0);
289292
expect(SOURCE_FILES.map((file) => file.fileName)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
290-
expect(SITES).toHaveLength(8);
291293
});
292294
});

‎packages/rest/src/rest-server-canonical-query-ast.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ interface CensusEntry {
123123
/**
124124
* [#23050 slice 3] Each registrar file's floor, its own measured `querySlots`
125125
* count: a file the split adds to the list needs a floor here before this
126-
* census reads it. The §1 controls pin the list's length and the census's sum
126+
* census reads it. The §1 controls pin the census's sum
127127
* over it (4 = 1 + 3 + 0 + 0; the batch and approval bodies build no `query:`
128128
* literal).
129129
*
@@ -403,7 +403,7 @@ describe('[#16638] §1 CONTROLS on the census instrument itself', () => {
403403
expect(wireKeysAnywhere('const a = { where: filter, limit: 2 };')).toEqual([]);
404404
});
405405

406-
it('[#23050 slice 3] censuses every file on the registrar list — its length and the slot sum are pinned', () => {
406+
it('[#23050 slice 3] censuses every file on the registrar list — the slot sum is pinned', () => {
407407
// A module added to the list makes this re-read its number rather than
408408
// widening unnoticed. [#23050 slice 4] Length 4 → 5, the sum unchanged.
409409
// [#23050 slice 5] Length 5 → 6, the sum unchanged.
@@ -425,7 +425,7 @@ describe('[#16638] §1 CONTROLS on the census instrument itself', () => {
425425
// carries no slot).
426426
// [#23050 slice 13e] Length 17 → 18, the sum unchanged (neither the object
427427
// state-machine read nor the published read carries a slot).
428-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
428+
// [#26433] The list's length is no longer pinned: the list is the authority.
429429
expect(Object.keys(REGISTRAR_QUERY_SLOT_FLOORS).sort()).toEqual([...REST_SERVER_REGISTRAR_FILES].sort());
430430
expect(CENSUS.filter(({ file }) => REGISTRAR_SOURCES.has(file)).map(({ file }) => file))
431431
.toEqual([...REST_SERVER_REGISTRAR_FILES]);

‎packages/rest/src/rest-server-docblock-position.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ describe('rest-server.ts docblock position', () => {
141141
* floor is that module's own measured count — the batch and approval modules
142142
* carry three apiece (the module header, the host interface's block and the
143143
* function's) — and a module added to the list needs one here before it reads.
144-
* The case below the loop pins the list's length and the sum of the floors.
144+
* The case below the loop pins the sum of the floors.
145145
*
146146
* [#23050 slice 4] `sharing-routes.ts` carries eight: the module header, two
147147
* host interfaces' blocks and two functions' (it holds two registrars' bodies),
@@ -272,7 +272,7 @@ for (const { file, minBlocks } of MOVED_FILES) {
272272
}
273273

274274
describe('[#23050 slice 3] the registrar list this pin reads', () => {
275-
it('reads every module on it, each against its own floor — the length and the floors\' sum are pinned', () => {
275+
it('reads every module on it, each against its own floor — the floors\' sum is pinned', () => {
276276
// A module added to the list makes this re-read its numbers rather than
277277
// widening unnoticed: 4 + 3 + 3 = 10 blocks over the three modules.
278278
// [#23050 slice 4] 4 + 3 + 3 + 8 = 18 over four, length 4 → 5.
@@ -295,7 +295,7 @@ describe('[#23050 slice 3] the registrar list this pin reads', () => {
295295
// [#23050 slice 13e] 4 + 3 + 3 + 8 + 3 + 8 + 5 + 3 + 3 + 3 + 3 + 3 + 5 + 5 + 3 + 5 + 3 = 70
296296
// over seventeen, length 17 → 18 (`meta-item-history-routes.ts` 3 → 5: the
297297
// published read's host interface and function).
298-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
298+
// [#26433] The list's length is no longer pinned: the list is the authority.
299299
expect(Object.keys(MOVED_FILE_FLOORS).sort())
300300
.toEqual(REST_SERVER_REGISTRAR_FILES.filter((file) => file !== FILE).sort());
301301
expect(MOVED_FILES.reduce((n, { minBlocks }) => n + minBlocks, 0)).toBe(70);

‎packages/rest/src/rest-server-query-number-census.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ const SOURCE = readFileSync(resolve(HERE, SOURCE_FILE), 'utf8');
7171
// function, never its file, so the ledger below did not change a row.
7272
// [#23050 slice 3] …and now every file on THE list of the files `RestServer`'s
7373
// registrars live in, imported rather than spelt here, so a registrar body the
74-
// split moves next stays inside this census. §2's last case pins the list's
75-
// length and the census's sum over it.
74+
// split moves next stays inside this census. §2's last case pins the census's
75+
// sum over it.
7676
const SOURCES: ReadonlyArray<readonly [string, string]> = REST_SERVER_REGISTRAR_FILES.map((file) =>
7777
[file, file === SOURCE_FILE ? SOURCE : readFileSync(resolve(HERE, file), 'utf8')] as const);
7878

@@ -303,7 +303,7 @@ describe('#20139 census — every numeric coercion in rest-server.ts is classifi
303303
}
304304
});
305305

306-
it('[#23050 slice 3] reads every file on the registrar list — its length and this sum are pinned', () => {
306+
it('[#23050 slice 3] reads every file on the registrar list — this sum is pinned', () => {
307307
// A module added to the list makes this re-read its number rather than
308308
// widening unnoticed. 9 sites = 8 in `rest-server.ts` + 1 in
309309
// `data-action-routes.ts` (the export door's `page`); the batch and
@@ -355,7 +355,7 @@ describe('#20139 census — every numeric coercion in rest-server.ts is classifi
355355
// judged as it came and its query value goes through the reader, so its
356356
// exemption row left the ledger with it:
357357
// 8 = 7 + 1 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0, length 18.
358-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
358+
// [#26433] The list's length is no longer pinned: the list is the authority.
359359
expect(SOURCES.map(([file]) => file)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
360360
expect(sites).toHaveLength(8);
361361
});

‎packages/rest/src/ui-view-route-identity.measurement.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
8484
// `registerBatchEndpoints`' body moved to. [#23050 slice 3] …and every module
8585
// on THE list of the files RestServer's registrars live in, read off that list
8686
// (`rest-server-registrar-files.ts`) rather than named here, so the slice that
87-
// moves the next body widens these checks by adding its module there. The
88-
// reverse checks pin the list's length.
87+
// moves the next body widens these checks by adding its module there.
88+
// [#26433] The list's length is not pinned: the list is the authority.
8989
const REGISTRAR_SOURCES = REST_SERVER_REGISTRAR_FILES
9090
.map((file) => (file === 'rest-server.ts' ? SOURCE : readFileSync(resolve(HERE, file), 'utf8'))).join('\n');
9191
// [#23050 slice 10] `registerUiEndpoints`' BODY moved to `ui-routes.ts`; the
@@ -400,7 +400,7 @@ describe('[#13214] §1 the REST seam — absent context vs entitled context', ()
400400
// (which joined `meta-item-history-routes.ts`) calls `resolveExecCtx` or
401401
// `enforceAuth`, so neither host literal the inner method builds for them
402402
// forwards either, and no call site moved.
403-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
403+
// [#26433] The list's length is no longer pinned: the list is the authority.
404404
expect(REGISTRAR_SOURCES.split('this.enforceAuth(').length - 1).toBeGreaterThan(40);
405405
expect(REGISTRAR_SOURCES.split('this.resolveExecCtx(').length - 1).toBeGreaterThan(40);
406406
});

‎packages/rest/src/ui-view-route-tenancy.measurement.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
105105
// `registerBatchEndpoints`' body moved to. [#23050 slice 3] …and every module
106106
// on THE list of the files RestServer's registrars live in, read off that list
107107
// (`rest-server-registrar-files.ts`) rather than named here, so the slice that
108-
// moves the next body widens these checks by adding its module there. The
109-
// reverse checks pin the list's length.
108+
// moves the next body widens these checks by adding its module there.
109+
// [#26433] The list's length is not pinned: the list is the authority.
110110
const REGISTRAR_SOURCES = REST_SERVER_REGISTRAR_FILES
111111
.map((file) => (file === 'rest-server.ts' ? SOURCE : readFileSync(resolve(HERE, file), 'utf8'))).join('\n');
112112
// [#23050 slice 10] `registerUiEndpoints`' BODY moved to `ui-routes.ts`; the
@@ -1177,7 +1177,7 @@ describe('[#13214] §4 preconditions', () => {
11771177
// `enforceAuth`, so neither host literal the inner method builds for them
11781178
// forwards either, and no call site moved. Neither calls the ownership
11791179
// guard, so it is still 1 call site and 1 forwarder.
1180-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
1180+
// [#26433] The list's length is no longer pinned: the list is the authority.
11811181
expect(REGISTRAR_SOURCES.split('this.enforceAuth(').length - 1).toBeGreaterThan(40);
11821182
expect(REGISTRAR_SOURCES.split('this.resolveExecCtx(').length - 1).toBeGreaterThan(40);
11831183
// ...and the ownership guard is NOT sprayed across the file — it is a

0 commit comments

Comments
 (0)