diff --git a/.changeset/stack-refusal-envelopes.md b/.changeset/stack-refusal-envelopes.md index b4a60319e1..9ff1212e20 100644 --- a/.changeset/stack-refusal-envelopes.md +++ b/.changeset/stack-refusal-envelopes.md @@ -21,7 +21,7 @@ Message text is byte-for-byte unchanged at every site — this adds the machine- The schema arm was judged separately rather than copied from the five semantic cross-checks, because it is an aggregate of zod issues against the schema the stack declares, not a rule evaluated on a parsed stack. The reading: `@objectstack/spec` has no zod-failure envelope to reuse (`formatZodError` / `safeParsePretty` return prose); the two zod-shaped refusals the ledger already carries are both spelled `*_SCHEMA_INVALID` — `METADATA_SCHEMA_INVALID` (metadata-core's `SchemaValidationError`, the `issues`-carrying precedent; nothing in the tree assigns it a status) and `FLOW_INPUT_SCHEMA_INVALID` (answered 422 by the runtime's flow-dispatch table) — and the zod-shaped refusal `metadata-protocol` actually stamps at 422 is `INVALID_METADATA`; the two other channels a zod failure travels on — `400 VALIDATION_ERROR` (request syntax) and `VALIDATION_FAILED` + `fields[]` (record validation, duck-typed on `name === 'ValidationError'`) — would each file an authored stack as something it is not. So it is its own code at 422, and its `issues` carries the zod issues structurally (path, code, message per entry) rather than the formatted lines the message already renders. `issues` is therefore heterogeneous across the seven: strings for the six semantic refusals, zod issue objects for the schema arm; a reader branches on `code` first. -Not narrowed: `defineStack` accepts and refuses exactly the inputs it did before, and no export changes — the error classes stay module-local, as `StackCrossReferenceError` did, because `packages/spec/src/index.ts` re-exports the module with `export *` and the ADR-0112 contract is the `code` / `status` pair read structurally. None of the six is registered in `ERROR_CODE_LEDGER`, for the reason the precedent was not: no wire door raises them — `defineStack` runs at authoring and boot time, and no HTTP domain handler calls it (re-measured: every non-test `defineStack` occurrence under `packages/runtime/src` and `packages/rest/src` is a docstring, a comment or the vocabulary table's own prose). +Not narrowed: `defineStack` accepts and refuses exactly the inputs it did before, and no export changes — the error classes stay module-local, as `StackCrossReferenceError` did, because `packages/spec/src/index.ts` re-exports the module with `export *` and the ADR-0112 contract is the `code` / `status` pair read structurally. All seven are registered in `ERROR_CODE_LEDGER` under `@objectstack/spec` — the six new codes and the `STACK_CROSS_REFERENCE_INVALID` precedent alike — not by this change but by #16449 (PR #16652), which landed after this changeset was written and registered them under the #16404 ruling that every code shipped in `dist` is registered, door or no door. That registration records the same reachability reading this change measured rather than contradicting it: no wire door raises them — `defineStack` runs at authoring and boot time, and no HTTP domain handler calls it (re-measured: every non-test `defineStack` occurrence under `packages/runtime/src` and `packages/rest/src` is a docstring, a comment or the vocabulary table's own prose). **Why `minor`, not the `patch` the #14552 precedent took.** Six new `STACK_*` spellings ship in `packages/spec/dist/index.js` and `dist/index.mjs`; nothing reaches `.d.ts`, but once shipped a consumer's `catch (e) { switch (e.code) … }` depends on them and they cannot be renamed without breaking it. That is a purely additive widening of a published package's public surface, and the maintainer ruling of 2026-09-04 (decision batch #35, on #15294) requires at least `minor` for it: the commit type may raise a bump but never lower it below what the act requires, so a `fix(` that widens the surface is `minor`. The `patch` precedent is pre-rule; this level is required by that ruling, not chosen by taste.