|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +--- |
| 4 | + |
| 5 | +fix(spec)!: `scale` is bounded at the renderer ceiling of 100 (#18972) |
| 6 | + |
| 7 | +Clause-②: no (narrowing) |
| 8 | + |
| 9 | +`FieldSchema.scale` — and the inline grid column's own `scale` — were declared as |
| 10 | +any non-negative integer with no upper bound. Every renderer that turns a declared |
| 11 | +`scale` into fraction digits reaches one of two platform primitives, and both of |
| 12 | +them refuse above 100: `Number.prototype.toFixed` throws `RangeError: toFixed() |
| 13 | +digits argument must be between 0 and 100`, and `Intl.NumberFormat` throws |
| 14 | +`RangeError: maximumFractionDigits value is out of range.` So a spec-valid |
| 15 | +declaration was unrenderable by any conforming consumer, and its author got no |
| 16 | +signal at publish time — the failure arrived as a render-time crash in someone |
| 17 | +else's repository. Both live readers are objectui's: the grid's `computeRow` rounds |
| 18 | +a computed cell with `Number(v.toFixed(column.scale))`, and the number cell renderer |
| 19 | +passes a field's `scale` straight into `maximumFractionDigits`. |
| 20 | + |
| 21 | +Both declarations now carry an upper bound of 100, and the refusal says **why** — |
| 22 | +it names both primitives, the `RangeError` and the legal maximum — so an author |
| 23 | +reads a platform limit they can verify rather than a cap somebody chose. The bound |
| 24 | +is the platform's own: at 100 both primitives are measured to succeed, at 101 both |
| 25 | +are measured to throw, and a unit test re-measures that boundary on every run |
| 26 | +rather than trusting the literal. |
| 27 | + |
| 28 | +**BREAKING** — a declaration above 100 that parsed clean before is refused at |
| 29 | +authoring now. This is a deliberate narrowing of a published accepted set, priced |
| 30 | +as such rather than as a tidy-up. The declarations it refuses could only ever have |
| 31 | +crashed a renderer: there is no value above 100 that any conforming consumer can |
| 32 | +render, which is why the bound is the platform's limit and not a policy number. |
| 33 | +`packages/objectql` already carries the consumer-side half of the same fact and |
| 34 | +skips its formula rounding past 100, so no read is newly affected. |
| 35 | + |
| 36 | +Unchanged in both directions: `scale: 100` still parses, `scale: 0` still parses, |
| 37 | +absence is still absence, and the malformed-declaration refusals from #8321 |
| 38 | +(`scale: -1`, `scale: 2.5`) keep their existing codes and their existing wording. |
| 39 | +`precision` is untouched — it is a total digit count that reaches neither |
| 40 | +primitive, so the renderer-ceiling argument does not carry to it. |
| 41 | + |
| 42 | +Shipped as `minor` under the repo's launch-window convention, in which |
| 43 | +`check-changeset-no-major` refuses `major` and breaking-ness is carried by this |
| 44 | +banner plus the ADR-0087 disposition rather than by the level. |
| 45 | + |
| 46 | +<!-- adr-0087: not-required (no-migration-prescription) no authorable key is renamed, retired or reshaped: `scale` keeps its name, its place and its type, and what moves is the top of one existing key's accepted numeric range. So `objectstack migrate meta` has nothing to visit — there is no stored spelling to rewrite and no FROM side to map, because the values this now refuses have no correct mechanical replacement: 101 fraction digits is not a precision a renderer can honour at all, and picking the display precision an author actually meant is their judgement, not a transform the ledger can carry. A ledger row would therefore have to invent the very semantics ADR-0078 and PD #12 forbid inventing, and the channel that does reach every affected author is the parse refusal itself, which names both primitives, the `RangeError` and the legal maximum at the moment the declaration is written. Measured on this tree: 123 `scale:` declarations across `*.ts` / `*.tsx` / `*.json` / `*.mdx`, of which zero declare more than 100 — the lit control for a zero whose radius is this repository's tracked files and whose known outside is a `sys_metadata` row already stored in a running deployment, which no in-repo instrument reaches. The other four categories are closed on facts: `@objectstack/spec` publishes to npm and declares no `private` (not `unpublished`); no ADR-0087 id is minted in this diff (not `registered`) and none pre-dating the base covers it (not `already-registered`); and the surface that moves is a Zod metadata schema, not a runtime-only TS interface and not a type annotation (neither `runtime-interface-only` nor `type-surface-only`). --> |
0 commit comments