Skip to content

Commit ba77509

Browse files
docs(spec): split the install door's residual clause 1 into its closed version half and open type half (#19935)
Fixes #19327 Clause-②: no `PackageInstallBodySchema`'s docblock in `packages/spec/src/api/package-api.zod.ts` lists the bodies `POST /api/v1/packages` answers `201` to while the declaration refuses them. Its clause 1 recorded "a manifest missing `type` and/or `version`" as ONE class. PR #19326 made the door parse `ManifestSchema.shape.version` by reference, so only the `type` half of that clause is still true. This PR splits the clause. The diff is the docblock, one spec test file and one changeset. No schema, accept set, export or runtime code moves, and residual classes 2 to 5 are untouched. ## Both halves re-measured at `origin/main` `fdeeea0cc9`, before any edit Both halves read exactly as the card says. | half | body sent to the door | status | `error.code` | installed | |---|---|---|---|---| | control | wrapped, well-formed | 201 | none | yes | | `version` | wrapped, no `version` | **400** | `VALIDATION_ERROR` | no | | `version` | bare, no `version` | **400** | `VALIDATION_ERROR` | no | | `type` | wrapped, no `type` | **201** | none | yes | | `type` | bare, no `type` | **201** | none | yes | **How it was driven.** I used a one-shot probe, deleted right after the reading and never committed. It was a vitest file at `packages/runtime/src/domains/probe-19327-oneshot.test.ts` with the harness of `packages-install-manifest-version.test.ts`: a real `HttpDispatcher`, spied protocol and registry install writers, and `OS_HOME` redirected. It called `handlePackages('', 'POST', body, {}, admin)` once per row and printed status, code and whether either writer was called. It ran in one invocation with the existing drives that the docblock's parenthesis «both door drives above» points at: ``` bash scripts/pm/os-verify-lock.sh -c "pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/domains/probe-19327-oneshot.test.ts src/domains/packages-install-manifest-version.test.ts src/domain-handler-registry.test.ts src/package-door-namespace-conflict-code.test.ts" Test Files 4 passed (4) Tests 82 passed (82) os-verify-lock: VERDICT command-exit 0 ``` - **The `type` half, from both existing drives.** Each drive posts a manifest with no `type`, and each asserts that the door answers `201`: - `package-door-namespace-conflict-code.test.ts` posts `{ id, name: id, namespace, version: '1.0.0' }`. - The duplicate-id case in `domain-handler-registry.test.ts` posts `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }` (`:600`): `409` first, then `201` on `?overwrite=true`. The body `{ id: 'pkg-a', name: 'A', version: '1.0.0' }` has been that file's REVERSED pin since PR #19473 and is answered `400` (`:622-623`), so it is not a residual. - **The `version` half, from #19326's door test.** §1 of `packages/runtime/src/domains/packages-install-manifest-version.test.ts` pins `400` + `VALIDATION_ERROR` + neither writer called, on both body forms. It is **cited, not duplicated**. ## What changed **`packages/spec/src/api/package-api.zod.ts` (docblock only)** - **Clause 1 is split:** - **1a, missing `version`:** marked CLOSED, naming PR #19326 and the door-side pin. - **1b, missing `type`:** still OPEN, answered `201`, with «both door drives above». - The labels 1a/1b match the table in #19326's own body, so the two records read the same way. - **The count stays true.** "five classes" remains correct because class 1 is still open through its `type` half. The count sentence now says «class 1 in its `type` half only, since PR #19326». Numbering 1 to 5 is unchanged, so no citation of clauses 2 to 5 moves. - **In-place fix of the drives paragraph (same defect class).** The paragraph above the list quoted the registry drive as `{ id: 'pkg-a', name: 'A' }` and said «the second carries no `version` either». That drive was repaired twice: PR #19326 gave it a `version`, and PR #19473 replaced its id `pkg-a`, which `MANIFEST_ID_PATTERN` refuses. At the merge base `fdeeea0cc9` and at `origin/main` it posts `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }`. The paragraph is clause 1b's antecedent, so left alone it would have filed a no-longer-posted body under the open residual. It now quotes the body the drive posts, says the declaration refuses both drives on `type` alone and the door answers both `201`, credits both repairs, and says the door answers the old `pkg-a` body `400`. **`packages/spec/src/api/package-api.test.ts` (the pin of the clause)** - `DOOR_DRIVE_REGISTRY` now transcribes the body the drive posts: `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }`. Its comment credits both repairs, and its `it` title drops "no `version`". - The control that rested on the stale id is now «the missing `type` is what decides it, for BOTH drives — the registry drive's old id is refused on its own». It asserts that the registry drive parses once `type` is added, as the conflict drive does. The old `pkg-a` reading is kept, pointed at the old body: `{ ...registryKeysCompleted, id: 'pkg-a' }` is refused on the id alone, and the green parse just above it is its lit control. - The residual list is hoisted to `DOOR_201_RESIDUALS`, and the existing "the door answers 201 to all of them anyway" assertion is unchanged over it. - **New pin:** «✅ clause 1a is CLOSED — every body in the live residual carries a `version` the declaration accepts». It checks each residual's manifest with `ManifestSchema.shape.version`. Its lit control is that `undefined` is refused. It cites the door-side pin rather than repeating it, because this package cannot import the door. **`.changeset/19327-install-door-residual-split.md`: `patch` for `@objectstack/spec`** A changeset is owed, not `skip-changeset`, because `files[]` ships `src/**/*.zod.ts` and the docblock is also emitted into the built declarations. After the build, the new clause text is in `dist/api/index.d.ts` and `dist/api/index.d.mts`, and the old spelling "and/or `version`" hits 0 across `dist` and `src`. The bump is `patch` because it is a text correction in a released package, with no API change. Note that npm `@objectstack/spec@17.4.0` has no `PackageInstallBodySchema` at all: grep 0, with `PackageInstallRequestSchema` = 4 as the control in the same file. The stale clause has therefore not shipped yet. The next release would be the first to carry it. ## Verification, round 1 (head `826e612b39`) **Reverse verification.** The fix was committed first. I then restored the old drive transcription `{ id: 'pkg-a', name: 'A' }` through `scripts/ablation-replace.mjs`, with the anchor counted 1 → 0, the replacement 0 → 1, and the blob `e976bff69a` → `5b952ed89a`. - **Predicted beforehand:** 2 red, 73 green. The two reds were the new 1a pin, plus the #17534 lit control in "the missing keys are what decide it", which now takes the drive's `version` through the spread. - **Observed:** `Tests 2 failed | 73 passed (75)`, exactly those two. - **Restore:** the blob is back to `e976bff69a`, equal to HEAD, and `git diff HEAD` is empty. Restore was proven by hash, not by exit code. **Spec package** - **Test:** `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` gives `Test Files 527 passed (527)`, `Tests 15504 passed | 1 todo`, exit 0. - **Typecheck:** `pnpm --filter @objectstack/spec run typecheck` exits 0. `check:test-typecheck` reports OK. `tsc -p tsconfig.test.json --listFilesOnly` lists `src/api/package-api.test.ts` (1 hit), and that file has no debt-ledger entry. - **Build:** `turbo run build --filter=@objectstack/spec` exits 0. `git status` was clean afterwards, so no generated artifact moved. **Gates.** From `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at this head: 82 derived, run one by one with each exit code captured before any pipe. `--ran` reports: «✓ dispatch-gates --ran: 82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero — all 82 recorded an exit code and none of them is 3)». - Two gates first answered exit 3 (PREREQUISITE NOT MET) on this fresh worktree: `check:dual-build-cjs-loads` and `check:type-check-debt`. After `turbo run build --filter='./packages/*' --filter='./packages/*/*'` (72 tasks, exit 0), both measured 0: - «✓ check:dual-build-cjs-loads — 104 published require entry point(s) across 67 package(s) load». - «check-type-check-coverage --re-measure: OK — 4 ledger entr(ies) re-measured». **Lint (a narrowed pass, stated as one).** I ran `eslint --no-inline-config --format json` on the three touched paths. The JSON has 3 results. The two `.ts` files show 0 errors and 0 warnings. The `.md` is outside eslint's own population ("File ignored because no matching configuration was supplied"). This narrowing covers everything the full run would: `eslint.config.mjs` never enables type-aware linting (its note at lines 327-328: no `parserOptions.project`, no typed rules), so this diff cannot move the verdict of any untouched file. The full `pnpm lint` union is CI's. **History.** The first commit `b685c67ad2` also rewrote the pending `.changeset/18058-install-door-contract-rebind.md`. `check:empty-changeset`'s foreign-changeset rule refused that with exit 1. The second commit `826e612b39` restores the file byte-identical to the merge base, and the gate reads exit 0 at head. Acceptance note 1 below has the details. ## Patch round: the registry drive, transcribed as it posts (head `0251069c5`) The at-tier contract review of head `826e612b39` (record `5808378321`) failed one item. This PR had transcribed the registry drive as `{ id: 'pkg-a', name: 'A', version: '1.0.0' }` and filed it under the open `201` residual. That drive stopped posting that body at PR #19473; the body is now the drive file's REVERSED pin, answered `400`. Commit `0251069c5` corrects every copy in one round. The clause split, 1a's CLOSED marking, the count sentence and the `patch` level are unchanged. **Read at both refs before any edit.** `packages/runtime/src/domain-handler-registry.test.ts` and `packages/runtime/src/package-door-namespace-conflict-code.test.ts` are each byte-identical at the merge base `fdeeea0cc9` and at `origin/main` `2c1011b01b`: - The duplicate-id case posts `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }` (`:600`). It is answered `409`, then `201` on `?overwrite=true` (`:601-604`). - The REVERSED pin posts `{ id: 'pkg-a', name: 'A', version: '1.0.0' }` (`:622`) and asserts `400` (`:623`). - The namespace drive posts `{ id, name: id, namespace, version: '1.0.0' }` (`:83`) and asserts `201` on the first install. **Clause 1b re-judged against both drives.** Both still show it, so 1b stays byte-unchanged. - **Declaration**, from a one-shot `tsx` probe on `src` (not committed): both real bodies fail `ManifestSchema` on `type` alone, and both parse once `type: 'app'` is added. The stale body fails on `id` and `type`. - **Door**, at `0251069c5`: `pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 --reporter=verbose src/domain-handler-registry.test.ts src/package-door-namespace-conflict-code.test.ts` gives `Tests 57 passed (57)`. That includes the duplicate-id case (`409`, then `201`), the REVERSED pin (`400`) and namespace section 1 (first install `201`). **What changed** - `package-api.zod.ts`, the drives paragraph only: - It quotes the real body, and says the declaration refuses both bodies on `type` alone while the door answers both `201`. - It credits the `version` repair to PR #19326 and the id repair to PR #19473. - It says the door answers the old `pkg-a` body `400`, so that body is not part of the residual. - `package-api.test.ts`: - `DOOR_DRIVE_REGISTRY` holds the real body. - The control that rested on the stale id now asserts that the real body parses once `type` is added. It keeps the old `pkg-a` reading, refused on the id alone. - The clause-1a pin's comment now names the versionless first transcription it meant. - `DOOR_201_RESIDUALS` and its doc comment are unchanged: with the corrected constant, every entry is a body the door answers `201`. - `.changeset/19327-install-door-residual-split.md`: the drives sentence names the body the drive posts, and the `pkg-a` body the door answers `400`. **Reverse verification.** Run after the fix was committed, through `node scripts/ablation-replace.mjs`, which put the stale transcription back. - Mutation: anchor ×1 → ×0, replacement ×0 → ×1, blob `37e99f04ca` → `0331f39bc4`. - Predicted beforehand: 1 red, the renamed control. - Observed: `Tests 1 failed | 74 passed (75)`. The failure is at `package-api.test.ts:945` (`expected false to be true`), the parse of the real body plus `type`. - Restored: the blob is back to `37e99f04ca`, equal to HEAD, and `git diff HEAD` is empty. - Round 1 had no assertion that could go red on this transcription. This head has one. **Verification at `0251069c5`** - **`package-api.test.ts`:** `Tests 75 passed (75)`. On `origin/main` `2c1011b01b` plus this branch's patch it gives `Tests 79 passed (79)`; the patch applies cleanly beside #19937's changes to the same two files. - **Spec tests:** `--project local` gives `Test Files 527 passed (527)` and `Tests 15504 passed | 1 todo`. The three `repo`-project tests that read `.changeset/` or mention `package-api` give `136 passed`. - **Typecheck:** `pnpm --filter @objectstack/spec run typecheck` exits 0. `package-api.test.ts` is in `tsconfig.test.json`'s program and has no debt entry. - **Build:** the built `dist/api/index.d.ts` and `.d.mts` each carry the real body once and the stale body zero times. - **Gates:** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 82. Deriving on `origin/main` plus the patch gives the same 82, with an empty set difference both ways. - `--ran` on the record as captured: «82 derived famil(ies) accounted for — 80 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)». - The two were `check:dual-build-cjs-loads` and `check:type-check-debt`, with PREREQUISITE NOT MET on a fresh worktree. - After `turbo run build --filter='./packages/*' --filter='./packages/*/*'` (72 tasks, exit 0), separate re-runs measured both at exit 0: «104 published require entry point(s) across 67 package(s) load» and «4 ledger entr(ies) re-measured … none above its recorded number». - **Lint, a narrowed pass stated as one:** `eslint --no-inline-config --format json` on the three paths gives 3 results. - Both `.ts` files have 0 errors and 0 warnings. The `.md` is outside eslint's population. - `eslint.config.mjs` enables no type-aware linting (its note at `:327-328`), so this diff cannot move any untouched file's verdict. - The full `pnpm lint` is CI's. ## Acceptance notes 1. **The pending release note `.changeset/18058-install-door-contract-rebind.md` restates the same clause.** - It reads «a manifest missing `type` and/or `version` (both of the runtime's own door drives post one)». That has been half false since #19326. - It will publish into the `@objectstack/spec`, `@objectstack/runtime` and `@objectstack/client` CHANGELOGs at the next release. - It is not edited here. Correcting another PR's pending release note is a release decision that `check:empty-changeset` routes to a person, and that gate stays red on any PR that makes the edit. The proposed replacement sentence goes to the seat in the report. 2. **Clause 5 of the same residual list is stale too.** - It says the door answers `400` to a whitespace-only `id` «this declaration admits». - Since PR #18319, `ManifestSchema.id` carries `MANIFEST_ID_PATTERN` and the declaration refuses it. `package-api.test.ts` already pins that refusal. - Not touched: the card fences the other classes. Reported to the seat. 3. **The header of `packages/runtime/src/domains/packages-install-manifest-version.test.ts` counts the docblock's five classes differently from the docblock.** - The header counts `version`, `type`, unknown keys, string-typed options and bare-form options. - The docblock has `type`/`version` as one class and the whitespace `id` as class 5. - This predates this PR and is not made false by it. That runtime file is not touched. --- _Generated by [Claude Code](https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b46dec3 commit ba77509

3 files changed

Lines changed: 108 additions & 33 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
`PackageInstallBodySchema`'s residual docblock splits clause 1: a manifest missing `version` is refused by the install door now, and only the `type` half is still residual
6+
7+
Clause-②: no
8+
9+
The docblock lists the bodies `POST /api/v1/packages` answers `201` to while
10+
the declaration refuses them. Its clause 1 recorded "a manifest missing `type`
11+
and/or `version`" as ONE class. Since #19326 the door parses
12+
`ManifestSchema.shape.version` by reference, so a manifest missing `version`
13+
answers `400` / `VALIDATION_ERROR` and installs nothing; a manifest missing
14+
`type` still answers `201`. Half of the clause had become false.
15+
16+
The clause is now split: **1a** (missing `version`) is marked CLOSED by #19326
17+
and names the door-side pin, and **1b** (missing `type`) stays an open residual.
18+
The count of five classes is unchanged and still true, because class 1 stays
19+
open through its `type` half; the count sentence now says so. The paragraph
20+
that quotes the runtime's two door drives is updated too. It quoted the
21+
duplicate-id drive as `{ id: 'pkg-a', name: 'A' }`, but that drive has posted a
22+
`version` since #19326 and the reverse-domain id `com.example.pkg-a` since
23+
#19473, and the door answers `400` to `pkg-a`. The docblock now quotes the body
24+
the drive posts, `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }`,
25+
which the declaration refuses on `type` alone and the door answers `201`.
26+
27+
⛔ No behaviour changes. No schema, accept set, export or runtime code moves,
28+
and the other four residual classes are untouched.
29+
30+
**Why this carries a changeset and not `skip-changeset`.** `@objectstack/spec`'s
31+
`files[]` ships `src/**/*.zod.ts` verbatim, and the docblock is also emitted
32+
into `dist/api/index.d.ts` and `dist/api/index.d.mts`. The published content
33+
changes, even though no line of code does.

‎packages/spec/src/api/package-api.test.ts‎

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -965,54 +965,82 @@ describe('#18058 — install contract bound to the live door', () => {
965965
describe('the measured bare-form senders are the RESIDUAL, not green fixtures', () => {
966966
/** The `manifest` helper in `packages/runtime/src/package-door-namespace-conflict-code.test.ts` — no `type`. */
967967
const DOOR_DRIVE_CONFLICT = { id: 'com.acme.crm', name: 'com.acme.crm', namespace: 'crm', version: '1.0.0' };
968-
/** The duplicate-id drive in `packages/runtime/src/domain-handler-registry.test.ts` — no `type`, no `version`. */
969-
const DOOR_DRIVE_REGISTRY = { id: 'pkg-a', name: 'A' };
968+
/**
969+
* The duplicate-id drive in `packages/runtime/src/domain-handler-registry.test.ts` — no `type`.
970+
* Two of its keys were repaired, each by the PR that made the door parse that leg: PR #19326
971+
* gave it the `version` it lacked (the docblock's clause 1a, CLOSED), and PR #19473 replaced its
972+
* id `pkg-a`, which `MANIFEST_ID_PATTERN` refuses. The old body is the REVERSED pin beside the
973+
* drive, answered `400`, so ⛔ it is no residual and is not transcribed here. This is the body
974+
* the drive posts: `409` first, then `201` on `?overwrite=true`.
975+
*/
976+
const DOOR_DRIVE_REGISTRY = { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' };
970977

971978
it('the namespace-conflict drive is REFUSED — it carries no `type`', () => {
972979
expect(PackageInstallBodySchema.safeParse(DOOR_DRIVE_CONFLICT).success).toBe(false);
973980
});
974981

975-
it('the domain-handler-registry drive is REFUSED — no `type`, no `version`', () => {
982+
it('the domain-handler-registry drive is REFUSED — it carries no `type`', () => {
976983
expect(PackageInstallBodySchema.safeParse(DOOR_DRIVE_REGISTRY).success).toBe(false);
977984
});
978985

979-
it('the missing keys are what decide it — and since #17534 the registry drive needs its id repaired too', () => {
986+
it('the missing `type` is what decides it, for BOTH drives — the registry drive\'s old id is refused on its own', () => {
980987
// The control that makes the two refusals above a measurement of the
981988
// MANIFEST's required keys rather than of the bare branch existing at all.
982989
expect(PackageInstallBodySchema.safeParse({ ...DOOR_DRIVE_CONFLICT, type: 'app' }).success).toBe(true);
983-
// ⭐ #17534 moved this half. `ManifestSchema.id` carries
984-
// `MANIFEST_ID_PATTERN` now, and `pkg-a` is not reverse-domain notation,
985-
// so completing the missing keys is no longer sufficient for THIS drive —
986-
// it stays refused, on the id's shape rather than on an absent key.
987-
// ⛔ The remedy is to say that, not to relax the pattern: the drive posts
988-
// an id the registry face has always refused to publish.
989-
const registryKeysCompleted = { ...DOOR_DRIVE_REGISTRY, version: '1.0.0', type: 'app' };
990-
expect(PackageInstallBodySchema.safeParse(registryKeysCompleted).success).toBe(false);
991-
// Lit control — the id is what decides it now: the same body with a
992-
// reverse-domain id parses green, so the refusal above is not the missing
993-
// keys coming back.
994-
expect(PackageInstallBodySchema.safeParse({
995-
...registryKeysCompleted, id: 'com.acme.pkg-a',
996-
}).success).toBe(true);
990+
const registryKeysCompleted = { ...DOOR_DRIVE_REGISTRY, type: 'app' };
991+
expect(PackageInstallBodySchema.safeParse(registryKeysCompleted).success).toBe(true);
992+
// ⭐ Until PR #19473 this half ran the other way. The drive posted `pkg-a`,
993+
// which `MANIFEST_ID_PATTERN` has refused since #17534 (PR #18319), so
994+
// completing its keys was not sufficient and this case pinned the
995+
// refusal. PR #19473 made the door parse the id leg as well, so the door
996+
// answers that body `400` (the REVERSED pin beside the drive), and it
997+
// repaired the drive's id. ⛔ The old reading is kept, pointed at the old
998+
// body: refused on the id alone (the lit control is the green parse just
999+
// above, the same body with the drive's current id), so it is no part of
1000+
// the `201` residual.
1001+
expect(PackageInstallBodySchema.safeParse({ ...registryKeysCompleted, id: 'pkg-a' }).success).toBe(false);
9971002
});
9981003

1004+
/** Bodies the door still answers `201` to while this declaration refuses them — the live residual. */
1005+
const DOOR_201_RESIDUALS: ReadonlyArray<Record<string, unknown>> = [
1006+
DOOR_DRIVE_CONFLICT,
1007+
DOOR_DRIVE_REGISTRY,
1008+
{ ...SDK_MANIFEST, label: 'an unknown key on the bare form' },
1009+
{ ...SDK_MANIFEST, enableOnInstall: false },
1010+
{ manifest: SDK_MANIFEST, enableOnInstall: 'false' },
1011+
{ manifest: SDK_MANIFEST, overwrite: 'true' },
1012+
];
1013+
9991014
it('the door answers 201 to all of them anyway — so this declaration is a SUBSET of the door', () => {
10001015
// Pinned as prose-with-a-parse rather than a live HTTP drive: the door
10011016
// lives in `@objectstack/runtime`, which this package cannot import.
10021017
// `packages/runtime/src/domains/packages-install-enable-on-install.test.ts`
10031018
// and the two drive files above are where the 201s are measured.
1004-
for (const residual of [
1005-
DOOR_DRIVE_CONFLICT,
1006-
DOOR_DRIVE_REGISTRY,
1007-
{ ...SDK_MANIFEST, label: 'an unknown key on the bare form' },
1008-
{ ...SDK_MANIFEST, enableOnInstall: false },
1009-
{ manifest: SDK_MANIFEST, enableOnInstall: 'false' },
1010-
{ manifest: SDK_MANIFEST, overwrite: 'true' },
1011-
]) {
1019+
for (const residual of DOOR_201_RESIDUALS) {
10121020
expect(PackageInstallBodySchema.safeParse(residual).success).toBe(false);
10131021
}
10141022
});
10151023

1024+
it('✅ clause 1a is CLOSED — every body in the live residual carries a `version` the declaration accepts', () => {
1025+
// PR #19326 made the door parse `ManifestSchema.shape.version` by
1026+
// reference: a manifest missing `version` answers `400` /
1027+
// `VALIDATION_ERROR` there now and installs nothing. The door-side pin is
1028+
// §1 of `packages/runtime/src/domains/packages-install-manifest-version.test.ts`,
1029+
// cited rather than repeated — this package cannot import the door. On
1030+
// that key the declaration and the door agree, so a versionless body in
1031+
// the list above would record a `201` the door no longer answers, which
1032+
// is what the registry drive's first transcription, `{ id: 'pkg-a',
1033+
// name: 'A' }`, did.
1034+
// ⛔ Clause 1b stays open: both drives above still carry no `type`.
1035+
for (const residual of DOOR_201_RESIDUALS) {
1036+
const manifest = ('manifest' in residual ? residual.manifest : residual) as { version?: unknown };
1037+
expect(ManifestSchema.shape.version.safeParse(manifest.version).success).toBe(true);
1038+
}
1039+
// Lit control — the check bites: a manifest with no `version` key reads
1040+
// `undefined` above, and the declaration refuses that.
1041+
expect(ManifestSchema.shape.version.safeParse(undefined).success).toBe(false);
1042+
});
1043+
10161044
it('⭐ #17534 closed the one spelling that ran the OTHER way — a whitespace-only `id` is refused HERE now, not only by the door', () => {
10171045
// What this pinned before: `handlePackages` trims before keying and
10181046
// refuses an empty id, while this declaration ADMITTED `' '` — the one

‎packages/spec/src/api/package-api.zod.ts‎

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -475,10 +475,15 @@ export type PackageInstallRequestParsed = z.infer<typeof PackageInstallRequestSc
475475
*
476476
* ⚠️ What those two drives post is NOT covered by this branch, and saying so
477477
* is the point. Measured: `{ id, name: id, namespace, version: '1.0.0' }` and
478-
* `{ id: 'pkg-a', name: 'A' }` are both refused here (`invalid_union`) because
479-
* neither carries `type`, and the second carries no `version` either. They are
480-
* bare in FORM and incomplete in CONTENT — the form is declared, the content
481-
* is part of the residual below, and they are pinned as REFUSED in
478+
* `{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }` are both refused
479+
* here (`invalid_union`) on `type` alone, and the door answers both `201` (the
480+
* second on the `?overwrite=true` limb of its duplicate-id case). The second
481+
* was repaired twice, each time by the PR that made the door parse the leg it
482+
* broke: PR #19326 gave it the `version` it lacked (clause 1a below), and
483+
* PR #19473 replaced its id `pkg-a`, which `MANIFEST_ID_PATTERN` refuses. The
484+
* door answers that old body `400` now, so it is no part of the residual. They
485+
* are bare in FORM and incomplete in CONTENT — the form is declared, the
486+
* content is part of the residual below, and they are pinned as REFUSED in
482487
* `package-api.test.ts` rather than dressed up as green fixtures.
483488
*
484489
* ## The two branches are disjoint — but only ONE of them is closed
@@ -507,9 +512,18 @@ export type PackageInstallRequestParsed = z.infer<typeof PackageInstallRequestSc
507512
*
508513
* This is a SUBSET description of the live door, deliberately. Measured
509514
* through `HttpDispatcher.handlePackages`, the door additionally answers `201`
510-
* to five classes this schema refuses:
511-
*
512-
* 1. a manifest missing `type` and/or `version` (both door drives above);
515+
* to five classes this schema refuses — class 1 in its `type` half only,
516+
* since PR #19326:
517+
*
518+
* 1. a manifest missing `type` or `version` — recorded as one class until
519+
* PR #19326, split since, because its two halves no longer answer alike:
520+
* - 1a. missing `version` — ✅ CLOSED by PR #19326, no longer residual: the
521+
* door parses `ManifestSchema.shape.version` by reference and answers
522+
* `400` / `VALIDATION_ERROR` without installing, so declaration and door
523+
* agree (door-side pin:
524+
* `packages/runtime/src/domains/packages-install-manifest-version.test.ts`);
525+
* - 1b. missing `type` — still OPEN, answered `201` (both door drives
526+
* above);
513527
* 2. unknown keys on either form — refused by name on the bare branch,
514528
* silently dropped on the wrapped one, `201` either way;
515529
* 3. a string-typed `enableOnInstall` / `overwrite` — the door compares

0 commit comments

Comments
 (0)