|
| 1 | +--- |
| 2 | +"@objectstack/metadata-protocol": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(metadata): the three read-side `/meta` verbs reach the canonical type boundary — history, audit and references (#9157) |
| 6 | + |
| 7 | +<!-- adr-0087: not-required (no-migration-prescription) No authorable key is |
| 8 | +added, renamed, retired or tombstoned. This routes three existing protocol |
| 9 | +methods through an existing request-boundary function; the only externally |
| 10 | +visible movement is on three GET routes, described below. --> |
| 11 | + |
| 12 | +Step ① of the maintainer ruling in #9180 (2026-08-16): **the `/meta` type |
| 13 | +segment is singular, always.** |
| 14 | + |
| 15 | +`auditMetaItem`, `historyMetaItem` and `findReferencesToMeta` each opened by |
| 16 | +deriving their type key from `PLURAL_TO_SINGULAR` — the MANIFEST-COLLECTION map |
| 17 | +that #7894 moved this boundary off — instead of calling |
| 18 | +`canonicalizeMetaRequestType`, which the nine sibling `/meta` verbs already |
| 19 | +call. That one call carries **both** the URL spelling map **and** |
| 20 | +`metaUrlSpellingRefusal`, and the refusal is the half these three could never |
| 21 | +reach: it lives *inside* the function they skipped. |
| 22 | + |
| 23 | +**What changes on the wire**, on `GET /api/v1/meta/:type/:name/history`, |
| 24 | +`…/audit` and `…/references`: |
| 25 | + |
| 26 | +| caller's `:type` | before | after | |
| 27 | +| --- | --- | --- | |
| 28 | +| `viewes` — an unrecognised spelling of a **declared** type | 200 with an empty body | **400 `INVALID_REQUEST`**, naming both accepted spellings (`view`, `views`) | |
| 29 | +| `translations`, `fields`, `seeds`, `external_catalogs` — recognised plurals of the four types absent from the manifest map | 200 with an empty body | 200 with the **real** rows | |
| 30 | +| `views` — a recognised plural already in the manifest map | unchanged | unchanged | |
| 31 | +| `fieldz` — reaches for no declared type | unchanged | unchanged; the refusal stays narrow, so a plugin-registered kind can never trip it | |
| 32 | + |
| 33 | +The harm being closed is the empty-accumulator shape: a plural read answered |
| 34 | +`{ "events": [] }` / `{ "references": [] }` — read by an operator as *"nothing |
| 35 | +depends on this"* — at exactly the moment they were about to rename or delete. |
| 36 | +**Loudly wrong beats quietly lying**, so a spelling the platform cannot honour |
| 37 | +is now refused with the canonical one named rather than answered emptily. |
| 38 | + |
| 39 | +Two measured details worth stating, because both invert an intuition: |
| 40 | + |
| 41 | +- On `historyMetaItem` the unfolded plural was not merely a wrong key, it was a |
| 42 | + door **around** a gate. `field` declares neither `allowOrgOverride` nor |
| 43 | + `allowRuntimeCreate`, so the canonical spelling is refused by the overlay gate |
| 44 | + and never reaches the store — while `fields` took |
| 45 | + `isRuntimeCreateAllowed`'s no-static-registry-entry arm (the plugin path, |
| 46 | + permissive by construction) and issued a real `sys_metadata_history` read |
| 47 | + keyed `'fields'`. Same empty body, opposite path. |
| 48 | +- On `findReferencesToMeta` the refusal is the **whole** visible change. Every |
| 49 | + `REFERENCE_PATHS` key is manifest-present and already folded, so a |
| 50 | + manifest-absent target still answers `{ "references": [] }` — which that |
| 51 | + method documents as a legitimate no-hit. Widening that registry is a coverage |
| 52 | + question, not a spelling one. |
| 53 | + |
| 54 | +Recognised plural spellings are **not** retired here — `metaUrlSpellingRefusal` |
| 55 | +returns `null` for `views` and for `translations`, and a pin asserts it. That is |
| 56 | +#9180 step ③, which the ruling requires to stay independently revertible. |
0 commit comments