|
| 1 | +--- |
| 2 | +"@objectstack/spec": minor |
| 3 | +--- |
| 4 | + |
| 5 | +<!-- adr-0087: not-required (no-migration-prescription) this change retires NO key. The endpoint vocabulary is byte-identical and only the unknown-key POSTURE moves, from strip to reject. Nothing exists for `objectstack migrate meta` to rewrite, because an undeclared key was never honoured in the first place: it was dropped at parse and so never reached storage, the matcher or the executor. No stored shape carries one, and no authored shape that ever WORKED becomes invalid. There is also no single FROM/TO rule a ledger entry could state, since what is now refused is an open set of author typos rather than a renamed key. The upgrade channel is the schema rejection itself, which is strictly more specific than any ledger line: it names the offending key at the author's own path and carries either the canonical spelling or a wrong-layer pointer at the surface that really owns the key. The one ADR-0087 entry that DOES govern this surface, `declarative-apis-endpoints-live`, is already registered for protocol 17 and is updated by this PR rather than duplicated. Measured blast radius: 0 affected entries across `examples/*` (2 endpoints) and the `cloud` repo (0 endpoints). --> |
| 6 | + |
| 7 | +`ApiEndpointSchema` rejects undeclared keys (#5384), and the author-state type is named on the upgrade path (#5227) |
| 8 | + |
| 9 | +`api` became a registered metadata type at #5312, which made |
| 10 | +`packages/spec/src/api/endpoint.zod.ts` an AUTHORING surface — `defineStack({ apis })`, |
| 11 | +the Studio metadata-admin form, and `PUT /meta/api/:name`'s 422 — while it was still a |
| 12 | +plain open `z.object`. An undeclared key was therefore dropped on every path: a |
| 13 | +`cacheTTL` / `objectParam` / `outputMappings` typo parsed green, published green, and the |
| 14 | +endpoint then served without the policy or projection its author wrote. That direction is |
| 15 | +fail-safe for `authRequired` alone (an unrecognized spelling leaves the default `true` |
| 16 | +standing); it was never fail-safe for the mapping, cache and rate-limit blocks. |
| 17 | + |
| 18 | +The shape is now `strictObject`, so an undeclared key is a named rejection carrying the |
| 19 | +surface, the offending key and a rename. Two curated wrong-layer prescriptions ship with |
| 20 | +it: |
| 21 | + |
| 22 | +- **`namespace`** — ADR-0121 D2 derives the namespace segment of `path` from |
| 23 | + `manifest.namespace`; it has never been per-endpoint, so the rejection points at the |
| 24 | + manifest instead of suggesting a rename. |
| 25 | +- **the six stored-envelope bookkeeping keys** (`packageId`, `state`, `version`, |
| 26 | + `published*`) — written onto the stored ROW by `register` / `publishPackage`, never onto |
| 27 | + a declaration. |
| 28 | + |
| 29 | +**The order this landed in is the part worth keeping.** Closing the shape was measured and |
| 30 | +REFUSED first: the same schema parsed STORED rows at `buildEndpointIndex` and |
| 31 | +`gateApiItemsForPublish`, so a naked `strictObject` failed every row with |
| 32 | +`unrecognized_keys: ['packageId', 'state']` — the load-time backstop excluded the endpoint |
| 33 | +(404) and the publish gate reported a schema error in place of the ADR-0121 D6 verdict it |
| 34 | +exists to give. The debt was real and it was not in this vocabulary, so #5309 (PR #6576) |
| 35 | +paid it at the layer that owned it (`peelStoredEnvelope`). `ApiEndpointSchema` never |
| 36 | +learned a bookkeeping key. |
| 37 | + |
| 38 | +**Breaking for metadata that was already silently broken.** An `apis:` entry carrying an |
| 39 | +undeclared key now fails `objectstack validate`, `objectstack build` and the metadata write |
| 40 | +path instead of publishing with the key discarded. Measured before landing: the example |
| 41 | +corpus (2 endpoints) and the `cloud` repository (0 endpoints) carry zero undeclared keys, |
| 42 | +so nothing in-tree changes verdict. |
| 43 | + |
| 44 | +`api` also leaves the #4001 campaign's `STILL_STRIP` list — closed registered types 24 → 25 |
| 45 | +of 26, with `view` the only entry left — and the CLI metadata gate's row moves from |
| 46 | +`NOT_YET_CLOSED` into `GATED_AT`. |
| 47 | + |
| 48 | +**#5227** — no schema change. `ApiEndpoint` already denotes the AUTHOR state after ADR-0122 |
| 49 | +phase 2, so omitting `authRequired` compiles; what was missing was anywhere saying so. The |
| 50 | +`declarative-apis-endpoints-live` upgrade-guide entry, whose whole safety argument is that |
| 51 | +"an omission is SAFE", now carries the type annotation that makes the omission writable: |
| 52 | +annotate declarations `ApiEndpoint`, hold parse results as `ApiEndpointParsed`. |
0 commit comments