Skip to content

Commit a933ed7

Browse files
feat(spec): declare the audit door and catch the delete request schema up — both meta-door casts retired (#12003)
* feat(spec): declare auditMetaItem on MetadataProtocol and retire the REST audit door's casts The audit door (GET /meta/:type/:name/audit) had neither request nor response declared; the door reached the verb through (p as any) twice. Declares AuditMetaItemRequest/Response mirroring the implementation member for member, adds the optional interface member, and compiles the door's request literal against the contract. environmentId stays out per the transport-level ruling; the honest {events: []} miss shape is recorded in the declared types. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * feat(spec): DeleteMetaItemRequestSchema declares the contract members the reset door sends; reset-door cast retired The member was declared all along but the request schema carried 2 of the 8 members DELETE /meta/:type/:name sends. Declares organizationId (tenant scope selecting WHICH row a reset destroys), parentVersion (OCC pin), actor, state and dropStorage, mirroring the implementation; environmentId stays out per the transport-level ruling and rides TransportScopedMetaRequest at the door. Both rest changeset and reset-door literal typing land here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * chore(spec): regenerate artifacts for the audit/delete door declarations check:generated proved 4 stale (api-surface, export-origins, docs references, strictness-ledger counts); --fix regenerated exactly those. The authorable-surface and json-schema.manifest shard updates are the spec build's own gen:schema product for the same declarations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * fix(spec): adr0112-ok opt-out on the audit fixture's item_locked — persisted audit column vocabulary, not an error code check:error-code-casing read the fixture's code: 'item_locked' as an emission; it is the sys_metadata_audit code column's own lowercase vocabulary (the D6b class, precedent at meta-audit-capability-gap.test.ts:162), suppressed with the corpus's exact reason line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * fix(spec): Iso857/Iso858 pin the audit schemas' input-infer isomorphism (ADR-0122) check:spec-parsed-alias flagged AuditMetaItemRequest/Response as author-state aliases with nothing naming their parsed state. Both trees carry no .default(), .transform(), .catch() or .pipe(), so input and infer coincide - the Iso pin route (the Iso137/Iso856 sibling precedent), not an XParsed pair. All three machine-checked count sentences move 833 -> 835 with the history note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY * chore(spec): regenerate artifacts on the merged tree (discharges the os-regen deferral) os-regen-merge.sh sequence: merge committed first, then the full gen chain on the merged source. check:generated --fix regenerated the 4 provably stale artifacts; the audit-door entries (this PR) and the #12001/#12006 sibling entries coexist in the regenerated set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K93P8PbH7kVjsAXkqzH1zY --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 134a506 commit a933ed7

14 files changed

Lines changed: 617 additions & 28 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
"@objectstack/rest": patch
3+
---
4+
5+
refactor(rest): the audit and reset door call sites are compiled against the declared contract (#11678, #11679)
6+
7+
The `GET /meta/:type/:name/audit` and `DELETE /meta/:type/:name` doors in
8+
`packages/rest/src/rest-server.ts` reached their protocol methods through
9+
`(p as any)` — once for each feature-detection guard, once for each call — so
10+
the compiler checked nothing about the request literals they built. The two
11+
casts were load-bearing in opposite ways, both measured: the audit door's on
12+
**member existence** (`auditMetaItem` was undeclared in `packages/spec`
13+
entirely — removing the cast answered `TS2339`), the reset door's on **request
14+
shape** (`deleteMetaItem` was declared, but its request schema carried 2 of the
15+
8 members the door sends — removing the cast answered `TS2353` on six keys).
16+
17+
With `MetadataProtocol.auditMetaItem` declared and
18+
`DeleteMetaItemRequestSchema` caught up (the spec half of this landing), the
19+
guards are now `typeof p.auditMetaItem !== 'function'` / `if (!p.deleteMetaItem)`
20+
and each request is a named const typed against the spec contract — the reset
21+
door through `TransportScopedMetaRequest<DeleteMetaItemRequest>` (it still
22+
spreads the transport-level `environmentId`, which stays layered on by the
23+
#9741 envelope rather than becoming a protocol key), the audit door as a plain
24+
`AuditMetaItemRequest` (it stopped sending `environmentId` when #8747 scoped
25+
the read, so there is no transport member left to layer on).
26+
27+
**No behaviour change of any kind, and nothing about the wire moves.** The
28+
outgoing payloads are byte-identical (same keys, same conditional spreads); the
29+
edits hoist each literal into a const and drop type-level casts. The 501
30+
feature-detection guards survive on purpose: both members are declared
31+
**optional** (a kernel may implement neither door), and each guard is also what
32+
narrows its member to callable at the call site. An undeclared key in either
33+
literal is now a compile error instead of a payload member no contract has ever
34+
seen.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
**`MetadataProtocol` declares the optional `auditMetaItem` member, and the audit door's request/response schemas join the spec** (#11678 — the #11006 maintainer-ruled pattern, 2026-08-22 option B, carried one door over).
6+
7+
`GET /api/v1/meta/:type/:name/audit` — the ADR-0010 §3.6 compliance trail behind Studio's 审计日志 / Audit log tab — was a step behind the half-declared publish door #11006 adjudicated: **neither** side was declared (`auditMetaItem` appeared nowhere in `packages/spec`), so the REST door reached the verb through `(p as any)` twice (feature-detection guard + call) and its request literal was compiled against nothing.
8+
9+
Additive, not breaking:
10+
11+
- `AuditMetaItemRequestSchema` / `AuditMetaItemRequest``{ type, name, organizationId?: string | null, limit? }`, mirroring the implementation's parameter type in `@objectstack/metadata-protocol` member for member. `organizationId` is nullable because the REST door always sends it, possibly `null` (#8747's fail-closed tenant scoping: `null`/absent = env-wide rows only, never every tenant's). `limit` declares no bounds because the implementation clamps to [1, 500] rather than refusing. `environmentId` stays out by the #9741 ruling (transport-level routing key) — and on this door it is not even on the wire any more (#8747 removed it; the implementation never read it).
12+
- `AuditMetaItemResponseSchema` / `AuditMetaItemResponse` — the `{ events: [...] }` body, newest first, with the closed `operation` (save/publish/rollback/delete/reset) and `outcome` (allowed/denied/forced) vocabularies and the ADR-0010 §3.3 `lockState`. The #9426 miss-vs-fault honesty is recorded in the declared types: `{ events: [] }` is the honest answer for a clean trail, a find-less host engine, or an unprovisioned audit table — never for a missing capability (501 before the call) and never for a failed read (propagated, not invented into an empty trail).
13+
- `MetadataProtocol.auditMetaItem?(request: AuditMetaItemRequest): Promise<AuditMetaItemResponse>` — optional like its `deleteMetaItem` / `getMetaItemLayered` siblings: additive to a shipped contract, implementation predating declaration. An undeclared key in a request literal at the member's call shape is now a compile error.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
**`DeleteMetaItemRequestSchema` declares the contract members the REST reset door sends** (#11679 — the #11006 maintainer-ruled pattern on the request-shape half).
6+
7+
`MetadataProtocol.deleteMetaItem` was declared all along, but its request schema declared 2 of the 8 members `DELETE /api/v1/meta/:type/:name` sends — so the door's call site had to stay behind an `(p as any)` cast (removing it surfaced `TS2353` on six keys, the opposite half of the publish door's `TS2339`), and the one member most worth having a contract — `organizationId`, which selects WHICH overlay row a reset destroys (ADR-0005 org partition; an org-less delete reaches the environment-wide row) — was on the wire with no declaration behind it.
8+
9+
Additive, not breaking — the five contract-level members join the schema, mirroring the implementation's parameter type in `@objectstack/metadata-protocol`:
10+
11+
- `organizationId?` — tenant scope for the reset (#8805); load-bearing, decides which row the delete destroys.
12+
- `parentVersion?` — the ADR-0008 optimistic-concurrency pin (REST: the `If-Match` header); absent = last-write-wins.
13+
- `actor?` — identity recorded on the history tombstone row (one producer, #7749); absent = recorded actor-less, never "system" (#4556).
14+
- `state?``'active' | 'draft'`; `draft` discards the pending draft overlay only.
15+
- `dropStorage?` — destructive opt-in (default false): also drop the object's physical table (`object` + `active` only; never `sys_`).
16+
17+
Two wire members stay out, by ruling rather than omission: `environmentId` (transport-level routing key per #9741, layered on by `packages/rest`'s `TransportScopedMetaRequest`) — and there are no internal coordination keys on this door (`_skipSeedApply` is publish-batch-only).

0 commit comments

Comments
 (0)