You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(metadata-protocol,rest,runtime): one declared shape for the protocol.deletePackage seam (#10060)
* refactor(metadata-protocol,rest,runtime): one declared shape for the `deletePackage` seam
`protocol.deletePackage` had three independent statements of its own contract
and none of them agreed:
1. the producer's own inline structural type on the method,
2. `PackageRoutesOptions.protocol.deletePackage` in `@objectstack/rest`, which
named neither `organizationId` nor `keepData` and omitted `deleted` from
the response,
3. the dispatcher twin in `@objectstack/runtime`, which typed the seam not at
all and reached it through `(protocol as any)` — while routinely sending
exactly the two keys (2) could not express.
`organizationId` is the key that decides an uninstall's blast radius (the
protocol refuses a call naming neither it nor `allTenants` —
`TENANT_SCOPE_REQUIRED`, 400), so the member the REST seam had no word for is
the one that matters most.
`DeletePackageRequest` / `DeletePackageResponse` are now declared once at the
producer and exported from `@objectstack/metadata-protocol`; both consumers
import them and the `as any` is gone. Types only — identical members, identical
call, no accept-set or behaviour change. No `packages/spec` declaration: minting
protocol surface for a verb with zero external consumers is a spec-seat decision
nobody has asked for.
The member stays OPTIONAL and the runtime's `typeof … === 'function'` capability
probe stays: the `protocol` service slot is deliberately uncontracted, the
spec's `PackageProtocol` does not declare this verb, and registrants that carry
no `deletePackage` are real in-tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
* chore(changeset): record the deletePackage seam convergence (#9960)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
* test(rest): the `deletePackage` double speaks the declared response (#9960)
Bounded in-place fix, same defect class as the card: `package-routes-query-multiplicity.test.ts`
built a protocol double whose uninstall response omitted `deleted`. That
compiled only while the option's type omitted it too — with the option now
carrying the producer's `DeletePackageResponse`, the double stops type-checking,
and `@objectstack/rest`'s TEST_DEBT ledger entry drifted 155 → 156.
Fixed at the author's end (`deleted: []`), never by raising the ratchet: the
ledger is shrink-only and raising it is maintainer-only. Re-measured back to
155, and `check:type-check-debt` reports "none above its recorded number".
The cases in this file count protocol CALLS, not deleted rows, so an empty
`deleted` changes nothing they assert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
* fix(rest): resolve the metadata-protocol type import to source, not to dist (#9960)
`check:type-source-resolution` went red on the branch:
✗ @objectstack/rest: NEW dist-resolved type import(s) since this entry was
measured: @objectstack/metadata-protocol.
Correct, and caused by this card: `src/package-routes.ts` type-imports the
declared `deletePackage` shapes, and with no `paths` rule tsc resolved that
specifier through the dependency's `exports` map — `dist/index.d.ts`, a build
artifact. This package's `typecheck` was therefore a verdict about the last
`pnpm build` rather than about the producer's source in the checkout, which is
the failure mode whose symptom is a typecheck that PASSES.
Fixed where the gate prescribes — the package's own `tsconfig.json`:
* ONE `paths` rule, bare key, targeting `../metadata-protocol/src/index.ts`.
No `/*` sibling: that package's `exports` map has only `"."`, so there is no
subpath to redirect, and a rule pointing at files that are not on disk makes
tsc fall back to node resolution silently.
* `rootDir` widened from `./src` to `..`, as a consequence rather than a
preference: the producer's source is now in the program, `rootDir` is
enforced even under `--noEmit` (measured: 20 x TS6059, and deleting the key
makes tsc infer one and report the identical 20), and `..` is the directory
that genuinely contains every file in the program. Nothing that ships reads
it — the package builds with tsup.
⛔ The `KNOWN_DIST_RESOLVED_TYPE_IMPORTS` registry was NOT widened. It is
shrink-only and maintainer territory; `@objectstack/rest`'s entry keeps its
eight other dist-resolved deps unchanged, and the gate audits that set for
equality in both directions.
Verified red-to-green, in that order: `node scripts/check-type-source-resolution.mjs`
exit 1 with the exact CI message, then exit 0 ("76 packages scanned; 51
registered"). `pnpm --filter @objectstack/rest typecheck` is exit 0 with ZERO
errors now that it reads the producer's source — no error was hiding behind the
stale artifact, and no ledger was touched to get there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
---------
Co-authored-by: Claude <noreply@anthropic.com>
refactor(metadata-protocol,rest,runtime): one declared shape for the `protocol.deletePackage` seam, imported by both doors (#9960)
8
+
9
+
`deletePackage` had **three** independent statements of its own contract, and
10
+
they did not agree:
11
+
12
+
| site | what it said |
13
+
|---|---|
14
+
|`packages/metadata-protocol/src/protocol.ts` (the producer) | an inline structural type on the method — `packageId`, `organizationId?`, `allTenants?`, `actor?`, `keepData?`|
15
+
|`packages/rest/src/package-routes.ts` (direct-mount option) |`{ packageId; actor?; allTenants? }` — named **neither**`organizationId`**nor**`keepData`, and its response omitted `deleted`|
16
+
|`packages/runtime/src/domains/packages.ts` (dispatcher twin) | nothing at all — it reached the verb through `(protocol as any)`|
17
+
18
+
The twin routinely sent exactly the two keys the REST option's type could not
19
+
express, and the only reason that was not a compile error was the cast.
20
+
21
+
`organizationId` is the member that makes this load-bearing rather than
22
+
cosmetic: the protocol refuses a call naming neither it nor `allTenants`
23
+
(`TENANT_SCOPE_REQUIRED`, 400), so it is precisely the key whose presence
24
+
decides an uninstall's blast radius — and it was the key one of the two doors
25
+
had no word for.
26
+
27
+
**What changes:**`DeletePackageRequest` and `DeletePackageResponse` are
28
+
declared once at the producer and exported from `@objectstack/metadata-protocol`
29
+
(the only user-visible half of this change — two additive type exports); both
30
+
consumers import them, and the `as any` seam is gone. `@objectstack/rest` also
31
+
gains three compile-time pins over its option, in compiled source rather than a
32
+
test file, so a later hand-rolled restatement fails `tsc` instead of drifting
33
+
green.
34
+
35
+
**What does not change:** nothing about what the verb accepts or returns. The
36
+
members are identical to the ones the implementation already had, the live call
37
+
sites send the same keys, and the emitted JavaScript of both consumers is
38
+
unchanged. The member stays optional at both seams and the runtime's
39
+
`typeof … === 'function'` capability probe stays — the `protocol` service slot
40
+
is deliberately uncontracted, the spec's `PackageProtocol` does not declare this
41
+
verb, and registrants carrying no `deletePackage` are real.
42
+
43
+
No `packages/spec` declaration: minting protocol surface for a verb with zero
44
+
external consumers is a spec-seat decision nobody has asked for.
0 commit comments