|
| 1 | +--- |
| 2 | +"@objectstack/runtime": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(scripts,runtime): the code-helper stamp shape reaches the OBJECT-LITERAL position (#13233) |
| 6 | + |
| 7 | +`check:dispatcher-error-vocabulary`'s `codehelper` shape was anchored on an |
| 8 | +assignment — `.code = ident` — so the equally ordinary helper that builds an |
| 9 | +object literal was invisible to it: |
| 10 | + |
| 11 | +```ts |
| 12 | +function postureError(code: string, message: string) { |
| 13 | + return { severity: 'error', code, message }; // <- nothing matched |
| 14 | +} |
| 15 | +``` |
| 16 | + |
| 17 | +No pattern in that gate or in `check:error-code-casing` fired there. `objlit` |
| 18 | +needs a quote, `objlitconst` needs a SCREAMING_SNAKE identifier (the |
| 19 | +conventional parameter name is `code`), `objlittemplate` needs backticks. So |
| 20 | +there was no site **and** no unresolved entry — the one way the gate's own |
| 21 | +"reported, never dropped" bound can fail without anything saying so. The new |
| 22 | +`objlithelper` shape closes it, guarded structurally twice over: the innermost |
| 23 | +enclosing bracket at the `code` token must be a `{` (which is what separates an |
| 24 | +object literal from the argument list `f(a, code, b)` and the array |
| 25 | +`[a, code]`), and the identifier must be a parameter of the enclosing |
| 26 | +declaration. Together with the class-method declaration form, the live instance |
| 27 | +that motivated the enquiry — `Parser#error` in `@objectstack/sdui-parser` — is |
| 28 | +now reached. |
| 29 | + |
| 30 | +Measured on `packages/**` non-test source, through the gate's own derivation |
| 31 | +rather than a separate instrument: **13 helpers · 117 newly reached in-file call |
| 32 | +sites · 29 new verdict rows · 5 helpers that reduce to nothing · 0 unregistered |
| 33 | +wire codes hiding**. All 29 rows are one genre — ADR-0114 D2 `FieldErrorCode` |
| 34 | +members stamped by four validation helpers whose `code` parameter is *typed* to |
| 35 | +that closed enum, landing at `ApiError.details.fields[].code` and never at |
| 36 | +`error.code` (ADR-0112 D6). They are declared `foreign-vocabulary`. |
| 37 | + |
| 38 | +`@objectstack/runtime` carries the declaration half. `UNREGISTERED_CODE_SITES` |
| 39 | +gains those 29 rows, and a second declared list `UNRESOLVED_CODE_HELPERS` |
| 40 | +classifies the five helpers the scan can see but cannot read. That list is the |
| 41 | +answer to the blocker this change had to clear: an `unresolved` entry is pushed |
| 42 | +unconditionally and no row discharged it, so every one of the five would have |
| 43 | +been a **red gate with no verdict available**. A `reason: 'helper'` entry is now |
| 44 | +dischargeable by a row carrying a door, a verdict and its evidence, reconciled |
| 45 | +in both directions like any site row. The restriction is the argument: every |
| 46 | +other unresolved reason names a remedy the author can carry out ("resolve the |
| 47 | +constant", "spell it `const`"), and a row there would buy an exemption from |
| 48 | +work that is possible — a helper whose callers live in another package, pass a |
| 49 | +vocabulary declared out of the gate's population, or pass a genuine runtime |
| 50 | +value has no such remedy. |
| 51 | + |
| 52 | +Internal to the package (`dispatcher-error-vocabulary.ts` is not re-exported |
| 53 | +from the package index), so no published surface changes and no runtime |
| 54 | +behaviour moves — the gate's population does. |
| 55 | + |
| 56 | +⚠️ Recorded rather than smoothed over: this position's precision is measurably |
| 57 | +lower than the assignment position's. `.code = ident` needs a property named |
| 58 | +`code` on a value being mutated; `{ code }` is how any record carries any field |
| 59 | +called `code`. Two of the thirteen helpers reached carry no error code at all — |
| 60 | +an SMS one-time password and a YAML fence body — and no sibling-key test |
| 61 | +separates them (the obvious candidate, requiring a `message` sibling, drops 25 |
| 62 | +of the 29 rows with them). Both are classified rather than filtered out. |
0 commit comments