Skip to content

Commit 14ae40b

Browse files
docs(spec): field-scale docblock states number's ruled absent width (#20248)
Fixes #19628 Clause-②: no ## What this does This is the spec half of ruling 5791809146 (batch 215 item 2, letter A′, maintainer 「215 同意」), item 1 only. It changes docblock prose and one test case's title and comment. No key, schema, export or behaviour changes. - `packages/spec/src/data/field-scale.ts`: the `number` bullet under "Why the table has exactly one row" now states the ruled answer instead of the open question. An absent `scale` on a `number` field means NO fixed width, the value's natural precision. That is the platform's declared answer, not an open question, and the bullet cites the ruling by its record. - The closing sentence that said `number` 「still awaits its own ruling」 now says its absent value is ruled to be no fixed width. - Grouping is named as `FieldSchema.useGrouping`'s to decide. The interim heuristic for an absent `useGrouping` reads the DECLARED `scale` only, never a resolved width (ruling item 4). - The `currency` bullet is byte-identical. - **No `number` row.** No code token moves (evidence below). - `packages/spec/src/data/field-scale.test.ts`: the existing pin `resolveFieldScale({ type: 'number' })` returning `undefined` now has its own case, whose title and comment carry the ruling. The `currency` assertion keeps its own case, and its comment facts are unchanged. No assertion was added, removed or altered. Items 2–4 of the ruling live on objectstack-ai/objectui#9843, which carries them on its own card. Those items are: every consumer reads the width through `resolveFieldScale` and drops its private `?? 0`; computed results round to the widest decimal count among their inputs; grouping reads the declared `scale` only. This PR does not touch objectui. ## Evidence (all at HEAD `bf7b0ff03`, base `c02fa1276`) - **No code token of `field-scale.ts` moves.** I compared base and head with the TypeScript scanner, comments skipped. - Result: 123 = 123 tokens, `identical=true`, and the sha256 prefix of the token stream is `65aed4895c596175` on both sides. - Control: a copy with one code token changed (`percent: 0` to `percent: 1`) answers `identical=false`, exit 1. The raw bytes do differ (`cmp` exit 1), since the comments changed. - **Tests.** - `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/data/field-scale.test.ts`: 13 passed, exit 0. - Full spec suite `vitest run --project local --maxWorkers=2`: 546 files, 16071 passed and 2 todo, exit 0. - `pnpm --filter @objectstack/spec typecheck`: exit 0. This runs tsc, the scripts program and `check:test-typecheck`. The test program lists `field-scale.test.ts` (1 hit via `--listFilesOnly`); the main `tsconfig.json` excludes tests. - **Gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 77 families, and `--ran` reconciles them as 74 run, 3 NOT MEASURED and 0 unrun, with every exit code recorded. - All 74 exit 0. `check:doc-formula-expressions` first answered exit 3 (its formula, sdui-parser and lint closure was not built). After building that closure it exits 0. - NOT MEASURED, exit 3, `PREREQUISITE NOT MET` (each needs a full workspace build, so CI runs them): `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:type-check-debt`. - Five roster families whose roster sits in a directory this diff touches all exit 0: `check:meta-url-spelling`, `check:spec-changes`, `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`. - **Lint, narrowed.** `eslint --no-inline-config --format json` over the 2 touched files lints 2 files with 0 errors and 0 warnings. The resolved config for this path carries no `parserOptions.project` or `projectService`, so no type-aware rule runs. That means this diff cannot move the verdict of any untouched file. The repo-wide `pnpm lint` is CI's. ## Changeset: none, `skip-changeset` The dispatch asked for a `patch` changeset on the premise that the docblock ships. I measured it after `pnpm --filter @objectstack/spec build`, and it does not. - The module docblock reaches none of `@objectstack/spec`'s `files[]` paths. `dist`, `json-schema`, `liveness`, `prompts`, `llms.txt`, `README.md`, `CHANGELOG.md`, `api-surface` and `spec-changes.json` give 0 hits for the docblock heading, the old sentence, the new text and the ruling id. - Positive control: the unchanged `resolveFieldScale` TSDoc does hit, in `dist/data/index.d.ts` and `dist/data/index.d.mts`. - `field-scale.ts` is not matched by `src/**/*.zod.ts`, so it does not ship as source either. A diff that publishes nothing takes `skip-changeset`, so the changeset I first added was removed in `bf7b0ff03`. ## Acceptance notes - **Changeset.** This deviates from the dispatch, as measured above; the agent definition's criterion governs on conflict. - **Grouping heuristic description.** The `FieldSchema.useGrouping` TSDoc in `packages/spec/src/data/field.zod.ts` gives the interim heuristic's example as "`scale: 0` + no upper bound reads as a plain count and keeps grouping". The objectui heuristic reads differently: at the pinned `.objectui-sha` `f8a9d0fb`, `packages/core/src/utils/number-display.ts:166-171` is `scale !== 0`, with no bound. The control token got 0 hits. - This is comment drift outside this claim's file surface, and it is not filed. Carrier: none. - `field-scale.test.ts:40-42` still describes a declared `0` as "rendered ungrouped" without naming `useGrouping`. That is true of the interim heuristic for an absent `useGrouping` (ruling item 4). It is outside the claimed lines and was left alone. --- _Generated by [Claude Code](https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 08c8484 commit 14ae40b

2 files changed

Lines changed: 36 additions & 17 deletions

File tree

‎packages/spec/src/data/field-scale.test.ts‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,20 @@ describe('resolveFieldScale', () => {
4444
expect(resolveFieldScale({ type: 'percent', scale: 0 })).toBe(0);
4545
});
4646

47-
it('declares NO width for number and currency — the census came back inconsistent', () => {
48-
// ⛔ Not a hole to fill with a fallback: `number`'s faces disagree by
49-
// design (no-fixed-width on the cell, 0 in the summary footer) and the
50-
// grouping policy keys on absent-vs-declared-0; `currency` resolves its
51-
// cell's fraction digits from ISO 4217 minor units and can no longer
52-
// declare this key at all (retired from the type, refused at parse).
47+
it('declares NO width for number — ruled (5791809146, letter A′): natural precision, and it stays so', () => {
48+
// ⛔ Not a hole to fill with a fallback, and not an open question: an
49+
// absent `scale` on a `number` field means NO fixed width — the value's
50+
// natural precision — as the platform's declared answer (maintainer ruling
51+
// 5791809146, batch 215 item 2, letter A′). A face that prints `0` for it
52+
// is the consumer defect, not this answer; grouping is `useGrouping`'s to
53+
// decide, and its interim heuristic reads the DECLARED `scale`, never this.
5354
expect(resolveFieldScale({ type: 'number' })).toBeUndefined();
55+
});
56+
57+
it('declares NO width for currency — the key is retired from the type', () => {
58+
// `currency` resolves its cell's fraction digits from ISO 4217 minor units
59+
// and can no longer declare this key at all (retired from the type,
60+
// refused at parse).
5461
expect(resolveFieldScale({ type: 'currency' })).toBeUndefined();
5562
});
5663

‎packages/spec/src/data/field-scale.ts‎

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,26 @@
5656
* the ruling's own instruction each takes its own card with its readings rather
5757
* than a guessed default here:
5858
*
59-
* - `number` — the faces deliberately DISAGREE, and one of them load-bearingly.
60-
* The cell renderer resolves absence to "no fixed width" (minimum 0 digits,
61-
* maximum 20) while the grid summary footer and the metric widget resolve it
62-
* to `0`; and the display-grouping policy keys on the very distinction a
63-
* default would erase — a DECLARED `scale: 0` marks a discrete integer (a
64-
* year, a fiscal period, an ordinal) and is rendered ungrouped, while an
65-
* absent `scale` means "decimals unknown" and keeps its separators. Declaring
66-
* `number ⇒ 0` would print every undeclared number field ungrouped: `2026`
67-
* where the platform shows `2,026`.
59+
* - `number` — ruled since, and not by a row here either: an absent `scale` on
60+
* a `number` field means NO fixed width — the value's natural precision —
61+
* and that is the platform's declared answer, ⛔ not an open question
62+
* (maintainer ruling 5791809146, batch 215 item 2, letter A′, 「215 同意」).
63+
* The census that sent it to its own card found the faces disagreeing: the
64+
* cell renderer resolved absence to "no fixed width" (minimum 0 digits,
65+
* maximum 20) while the grid summary footer and the metric widget resolved
66+
* it to `0`. The ruling makes the cell's answer the protocol's, and its
67+
* consumer half is objectui#9843: the footer and the widget drop their
68+
* private `?? 0` and read the width through {@link resolveFieldScale}, and a
69+
* computed result over a no-fixed-width column rounds to the widest decimal
70+
* count among the values that entered it — derived from the data, ⛔ never a
71+
* constant. Grouping is not this key's to decide:
72+
* `FieldSchema.useGrouping` decides, and the renderer's interim heuristic
73+
* for an absent `useGrouping` reads the DECLARED `scale` only — a declared
74+
* `scale: 0` marks a discrete integer (a year, a fiscal period, an ordinal)
75+
* and renders ungrouped — never a resolved width, so no answer from this
76+
* module moves a thousands separator. A `0` row stays refused: as a width it
77+
* would cut every undeclared number to an integer, and batch 194 refused
78+
* `number ⇒ 0` by name for printing `2026` where the platform shows `2,026`.
6879
* - `currency` — ruled since, and not by a row here: `scale` is RETIRED from
6980
* the `currency` type (#19629, ruling 5791803339 letter B) and refused at
7081
* parse, so a currency field declares no `scale` for this table to default.
@@ -81,8 +92,9 @@
8192
* inline grid COLUMN's rounding of a computed result, not to a field's
8293
* display width.
8394
*
84-
* ⛔ Neither reading is an argument for a `0` row: `number` still awaits its
85-
* own ruling, and `currency` no longer has a declaration to resolve.
95+
* ⛔ Neither reading is an argument for a `0` row: `number`'s absent value is
96+
* ruled to be no fixed width, and `currency` no longer has a declaration to
97+
* resolve.
8698
*
8799
* ## Why the table is not exported
88100
*

0 commit comments

Comments
 (0)