Skip to content

docs(spec): field-scale docblock states number's ruled absent width - #20248

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-19628-number-absent-scale-ruled
Sep 27, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-19628-number-absent-scale-ruled

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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

An absent `scale` on a `number` field means no fixed width, the value's
natural precision, as maintainer ruling 5791809146 (batch 215 item 2,
letter A′) declares. The docblock's `number` bullet and the closing
sentence no longer call it open, the existing resolveFieldScale pin for
`number` is tied to the ruling, and grouping is named as useGrouping's to
decide. No code token of field-scale.ts moves.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ
The footer/widget changes and the computed-result rounding rule are the
ruling's consumer half, not yet landed; say so rather than state them in
the present tense. Comment text only.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ
Measured after `pnpm --filter @objectstack/spec build`: the module
docblock of field-scale.ts reaches none of the package's files[] paths
(0 hits for its heading, the old sentence and the ruling id), while the
unchanged resolveFieldScale TSDoc (positive control) hits in
dist/data/index.d.ts. field-scale.ts is outside src/**/*.zod.ts. A diff
that publishes nothing takes skip-changeset, not a patch.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiCTczDo7tGhafXjf61dUJ
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/data/field-scale.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/data/field-scale.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json fdb26698f975511a01a01f53d506058977db7cef → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bf7b0ff033aa2a1d15fad07a0d29df9a79b6b367

Read: card #19628 body, triage 5782023392, ruling 5791809146 (items 1–4), PM claim 5856375966, dev report 5856798203 (the PM's corrected id); PR #20248 object, body, file list, labels, 39 check-runs and the ruleset's required contexts on the head; the full diff against merge-base c02fa12; field-scale.ts, field-scale.test.ts, field.zod.ts (FieldSchema.useGrouping TSDoc and describe), package.json files/exports, tsup.config.ts, scripts/tsup-drop-sources-content.mjs, the changeset gate headers, the Check Changeset job definition and its step outcomes, AGENTS.md's citation and changeset rules, .claude/agents/os-dev.md's published criterion, and objectui packages/core/src/utils/number-display.ts at the pinned .objectui-sha f8a9d0fb0596f4521076628e2bbfe27e6ce67d52. Ran: a TypeScript-scanner (6.0.2; repo pins 6.0.3) comments-stripped token diff of both files base vs head with a code-token positive control and a comment-only control; an assertion-sequence diff of the test; a byte cmp of the currency bullet; a tsc declaration-emit probe of base and head field-scale.ts with resolveFieldScale's TSDoc as positive control; git grep over every checked-in files[] path at the head. NOT MEASURED: no local test execution (the shared checkout has no node_modules and no dev worktree is present in this container; CI's Test Core shards are the record), no local tsup build (the tsup .d.ts bundle and JS bundles are inferred from the tsc emit, esbuild's documented comment dropping and sourcesContent: false, not from a built dist), and no gate family was re-run locally.

① Derived judgments

(a) No code token moves — holds. field-scale.ts base c02fa12 vs head bf7b0ff through ts.createScanner with trivia skipped: 123 = 123 tokens, identical=true, token-stream sha256 prefix 65aed4895c596175 on both sides (matches the dev's figure independently). Positive control (head with percent: 0 changed to percent: 1): identical=false, first diff at token #20 0 vs 1, exit 1. Comment-only control (one docblock word changed): identical=true, while cmp reports the bytes differ, so the scanner is blind to comments and sensitive to code, as required. At the head there is no number: row (grep for a number|currency|slider|rating key row exits 1), ABSENT_SCALE_BY_TYPE still holds percent: 0 only, and resolveFieldScale's body is token-identical.

(b) The test keeps its meaning — holds. The 25 expect(...) statements, with line numbers stripped, are byte-identical in sequence between base and head (diff exit 0). expect(resolveFieldScale({ type: 'number' })).toBeUndefined() is still asserted (head :54) and expect(resolveFieldScale({ type: 'currency' })).toBeUndefined() is unchanged (head :61). With the it titles neutralised, the comments-stripped token diff of the test file is exactly one insertion at token #219: }); it('T', () => { between those two assertions, i.e. the split of one case into two and nothing else. The two-direction roster pin (number: undefined in the numeric-family map) is untouched. Test count goes 13 to 14 cases in the file by the split; no assertion added, removed or altered.

(c) The docblock states the ruling faithfully — holds. Head :59–:62 gives item 1 as the platform's declared answer: "an absent scale on a number field means NO fixed width — the value's natural precision — and that is the platform's declared answer, ⛔ not an open question (maintainer ruling 5791809146, batch 215 item 2, letter A′, 「215 同意」)". The citation is the ruling record's id, the same form the file already uses for 5749111840, 5791803339 and 5805782503; no tracker number stands as the citation (the only # references in the file are the pre-existing #19629 and objectui#9568/#10221, plus objectui#9843, which the ruling itself names as the carrier of items 2–3, used here as the carrier link, not as the citation). Items 2–3 are attributed to objectui#9843 at :66–:71 (footer and widget drop their private ?? 0 and read through resolveFieldScale; computed results round to the widest decimal count among the inputs, never a constant). Grouping at :71–:76 matches item 4: FieldSchema.useGrouping decides, the interim heuristic for an absent useGrouping reads the DECLARED scale only, never a resolved width, so nothing here moves a thousands separator. The stale sentence is gone: base :84 "number still awaits its own ruling" is replaced by head :95–:97 "number's absent value is ruled to be no fixed width"; a grep for awaits|nobody has ruled|not yet ruled|unruled|still open at the head returns only the "not an open question" negation. The currency bullet (15 lines) is byte-identical base vs head (cmp exit 0). The batch-194 refusal is restated in the ruling's own words and its record is cited at :16. The resolveFieldScale TSDoc (unchanged) already says undefined means "no fixed width, the value's natural precision", and FieldSchema.scale's describe says "omitted on any OTHER numeric type declares NO fixed width", so the three surfaces agree.

(d) skip-changeset — holds; the docblock does not ship. @objectstack/spec files[] is dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json. packages/spec/src/data/field-scale.ts does not match src/**/*.zod.ts, so no source ships. dist types: tsc declaration emit of the file (base and head alike) carries only the declaration-attached TSDoc of FieldScaleMeta and resolveFieldScale (positive control "Callers must not spell a fallback" hits 1 in both) and 0 hits for the detached module docblock in either version ("What an ABSENT", "Why the table has exactly one row", "5791809146", "ruled to be no fixed width", "awaits its own ruling" all 0); ABSENT_SCALE_BY_TYPE is not exported and does not appear. tsup's dts: { only: true } pass bundles TypeScript's own declaration output, which cannot add comment text the emitter never produced. dist JS: esbuild drops non-legal comments (the docblock carries no /*!, @license or @preserve), and tsup.config.ts :35 states the same for this build. Source maps: esbuildOptions: dropSourcesContent sets sourcesContent: false (pinned by check:sourcemap-no-sources-content). The checked-in published paths at the head give 0 hits for every docblock sentence; api-surface/data.json's one resolveFieldScale hit is the signature entry "resolveFieldScale (function)", no doc text. json-schema is generated from Zod describes, not from this file. Under AGENTS.md §3 (skip-changeset is for a diff that publishes nothing from any released package) and os-dev.md :298 (published = what files[] actually ships), the label is correct and no patch changeset is owed. The PR diff carries no .changeset/*.md (added in ce8d3d3, removed in bf7b0ff; net zero against the merge base).

(e) The out-of-scope note — consistent with the truth, and non-blocking here. At the pinned objectui sha f8a9d0fb, number-display.ts:166–:172 is shouldGroupDisplayNumber(scale?, currency?): money always groups, otherwise return scale !== 0 with the in-line comment "Only an explicitly declared scale of 0 is an ordinal. undefined !== 0"; formatDisplayNumber :213–:215 sets useGrouping = false only when that returns false. No min/max/bound is read (dark control zzzNotAFunction: 0 hits). This PR's docblock ("a declared scale: 0 marks a discrete integer and renders ungrouped; reads the DECLARED scale only") matches that reading and ruling item 4. The FieldSchema.useGrouping TSDoc in field.zod.ts :1262–:1265 ("scale: 0 + no upper bound reads as a plain count and keeps grouping, a small bounded integer range reads as ordinal-shaped") does not match the pin: that is comment drift in an untouched file; the author-facing .describe() on :1285 says only "renderer decides (interim heuristic today, locale default eventually)", which is accurate. It is outside this PR's ruled file surface (item 1 names field-scale.ts only), the PR body records it in Acceptance notes, and nothing in this PR restates the bounds-based version, so it does not block. field-scale.test.ts :40–:42 ("a DECLARED 0 ... rendered ungrouped") is unchanged base text and is true of the interim heuristic.

② Semver level

Clause-②: no is declared on the PR body's second line and is right: no key, schema, export, signature or runtime behaviour moves (judgment (a)), and no published artefact of @objectstack/spec changes content (judgment (d)). No changeset is introduced, so check-changeset-no-major's launch-window guard is vacuous and its level axis (a yes declaration must grade some package minor or above) is not engaged by a no. check-empty-changeset's two rules (no empty-frontmatter changeset added; no foreign changeset modified) are not engaged by a diff that touches no .changeset/*.md. Level: none published; skip-changeset stands.

③ Boundary flags

Blocking: none.
Non-blocking: (1) the FieldSchema.useGrouping TSDoc drift in packages/spec/src/data/field.zod.ts :1262–:1265 (bounds-based description vs the pin's scale !== 0), noted by the dev with no carrier; a docs-only follow-up when someone next touches that block. (2) The Check Changeset success on this head was reached through the label exemption: step 9 "Settle the skip-changeset window" ran and steps 10–15 (require a changeset, etc.) were skipped, because skip-changeset was written at 14:36:17Z, 39 seconds after that run started; the labeled re-run at 14:36:21Z is skipped at job level by the fast path. That is the workflow's designed behaviour, and the exemption is justified by (d), but the green is the label's, not a measurement of publication. (3) GitHub had not yet computed mergeable (unknown) at both reads; the head is 3 commits on top of merge-base c02fa12 with main at fdb2669, and the two touched files are not touched by any other open PR per the PM claim's census (not re-taken here).

CI at this head: 39 check-runs, all completed; 31 success, 8 skipped (Auto Label, Check PR Size, Check Changeset on the labeled re-run, Packed-tarball smoke ×2 opt-in, Console Pin Gate, Build Docs), 0 failure/cancelled/timed_out/action_required. Every context the main ruleset requires is success: TypeScript Type Check, Test Core (and shards 1–6/6), Dogfood Regression Gate (and 1–3/3), Build Core, Temporal Conformance (live PG + MySQL), Lint & Repo Gates, Governed Surface Queue Guard. Also success: Type Check · workspace / source gates / consumer gates / debt ledger, Spec property liveness, Dogfood Verify CLI, Flag docs affected by code changes, Check Documentation Links, Check PR Size, Check Changeset (synchronize run), the four claim/closing-keyword guards, and the Vercel commit status. Closing keywords: the body carries exactly one, Fixes #19628; the only other # reference is objectstack-ai/objectui#9843 in prose, not a closing keyword. Head unchanged across the review: bf7b0ff033aa2a1d15fad07a0d29df9a79b6b367 on the PR object and on refs/heads/claude/issue-19628-number-absent-scale-ruled at the final re-read.

Implemented-by: claude/issue-19628-number-absent-scale-ruled
Reviewed-by: session_01CiCTczDo7tGhafXjf61dUJ

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 27, 2026 15:14
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 27, 2026
Merged via the queue into main with commit 14ae40b Sep 27, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19628-number-absent-scale-ruled branch September 27, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

protocol:data size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants