Repository navigation
docs(spec): field.useGrouping is live at the objectui pin, and its docblock describes the heuristic the renderer uses - #20908
Conversation
…jectui reader at the pin The row flips planned to live with verifiedAt 2026-09-30, cross-repo scope, the reader as evidence (shouldGroupDisplayNumber, formatDisplayNumber, NumberCellRenderer) and the seven relay sites as producer, all read at objectui db11afd4967c, this repo's pin. The note no longer says the consumer is in flight. state-counts/field.md is regenerated by gen:liveness-counts: live 91 to 92, planned 2 to 1. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
… the renderer actually uses Three sentences of the FieldSchema.useGrouping JSDoc were false at the objectui pin db11afd4967c. The interim heuristic never reads bounds: a declared scale 0 drops grouping and any other scale, or none, keeps it. The renderer half has landed and keeps that heuristic for an absent key rather than retiring it. And the motivating sentence about renderers grouping unconditionally is past, so it now reads in the past tense. The .describe() string was true and is unchanged. One spec patch changeset for the ledger flip and this text. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 081b10a820dc0ea2eeaac8d90047d3de085a8eed && git checkout 081b10a820dc0ea2eeaac8d90047d3de085a8eed
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cb4c31dd5279c5c960d82526f7a3054d7de76ae9 f06c26f536230b7326d7ddfd8620b2c39c0255b6 && git checkout -B drift-repro cb4c31dd5279c5c960d82526f7a3054d7de76ae9 && git merge --no-ff f06c26f536230b7326d7ddfd8620b2c39c0255b6
node scripts/docs-audit/affected-docs.mjs --json cb4c31dd5279c5c960d82526f7a3054d7de76ae9
|
…and two useGrouping comments say what holds The liveness gate's planned-entry missing-file case relied on field.useGrouping being planned in the shipped ledger. Once the row went live the case kept passing while it drove a live row. It now sets the copied row to planned, moves the copy's count shard with it so the rot stays the run's only cause, and reads the row back as its control. setStatus moves to module scope for that; readRow is new. The typo fixture's comment no longer says the row carries no evidence. The gate never scans evidence at an unrecognised status, and the row's producer paths are all objectui-attributed. field.test.ts's comment on the absent default stops saying the objectui half will retire the interim heuristic: it landed and keeps it for an absent key. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
…resolved, not that they are counted The gate counts foreign paths for evidence only; for producer it reads nothing but the missing list, so "counted" was wrong about producer. Claude-Session: https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Read: card #20313 (body and all 13 comments — triage Check-runs on the head, read at 2026-09-30T18:30Z: 42, all ① Derived judgmentsAccept-set: none moves.
② Semver level
③ Boundary flagsDev flags, round 1 (
Open questions, round 1:
Dev flags, round 2 (
Round 2 Card acceptance: the row leaves Implemented-by: VERDICT: PASS Generated by Claude Code |
… contract, and an identity-only column is judged as the type it renders (objectstack-ai#20901) (objectstack-ai#20927) Closes objectstack-ai#20901 Clause-②: yes (narrowing) - `FormViewSchema.subforms[].columns` references `InlineGridColumnSchema` (was `z.array(z.any())`): the card's typed `currency` + `scale` column and its `zzz_not_a_key` column are refused at the view parse. - `defineStack`'s cross-reference check (`packages/spec/src/stack.zod.ts#collectHydratedInlineColumnErrors`, called from `validateCrossReferences`) re-parses a column that declares no `type` as the type it renders (`currency` over a `currency` child field) through `InlineGridColumnSchema`, on both carriers. The card's identity-only column with `scale` is refused there with the column schema's own message; there is no second `scale` rule. - ADR-0087: D3 entries `form-view-subform-columns-closed` and `inline-grid-column-identity-only-currency-scale-refused`; the registry was regenerated after merging a `main` that carries objectstack-ai#20903 and objectstack-ai#20911. Evidence at `feba1a99bd`: pins `packages/spec/src/inline-grid-column-carriers.test.ts` 13/13; `@objectstack/spec` suite 584 files / 17179 tests green; `@objectstack/spec` typecheck green; 114/114 derived gates exit 0. Ablations, each restored with `git diff HEAD` empty: reference removed, 5 red; cross-reference call removed, 3 red. `os validate` on a probe stack: identity-only column, exit 1 `STACK_CROSS_REFERENCE_INVALID`; typed and bogus columns, exit 1 `STACK_SCHEMA_INVALID`; valid columns, exit 0. ## Acceptance notes - No check read `subforms[].childObject` before this change (`validateCrossReferences` read only `form.data.object`), so the child-object lookup is new here. - The identity-only check runs in `defineStack` only. A view saved through the metadata door, or a subform whose child object lives in another package, gets the schema half alone (NOT MEASURED at the save door). objectui's `@object-ui/types` mirror is still `z.any()`, and the render-time warning stays the backstop there. - `field.zod.ts`'s `scale` describe still names only the declared-type refusal. PR objectstack-ai#20908 holds that file. --- _Generated by [Claude Code](https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…s stale not-enforced-yet omit row (objectstack-ai#20934) Fixes objectstack-ai#20920 Clause-②: no ## What `field.useGrouping` is `live` since `d78a0bda07`, the enforcement carried by objectstack-ai#20313: objectui's `shouldGroupDisplayNumber` answers an authored value first. The reconciliation ledger's own rule assigns the offer decision to that enforcement ("Once a key is enforced its row is stale: delete it and decide the offer then"), and its seat ruled **offer it** (`5916202706`, corrected by `5916211005`). The at-tier review `5917470646` on PR objectstack-ai#20908 routed the move to this card. This PR carries it out: - `metadata-form-zod-reconciliation.test.ts`: the `field/useGrouping` "declared, not enforced yet" omit row is deleted, not re-reasoned. - `field.form.ts`: one `useGrouping` row beside `scale`, with `scale`'s gate `visibleWhen: "data.type == 'number'"`. - The four `platform-objects` metadata-form catalogs, regenerated with the generator. The two new leaves are authored in `zh-CN`, `ja-JP` and `es-ES`. - Two population pins that the row moves mechanically, and one changeset. | key | form, section | control | the row it copies | gate | |:--|:--|:--|:--|:--| | `field.useGrouping` | `field.form.ts`, Configuration | plain row, no `widget`, no `type` | `field.form.ts` `allowCreate` (the same `z.boolean().optional()` node, no default) | `number` (`scale`'s) | The help text follows the key's `.describe()`: "Digit grouping (thousands separators) in displayed values. Unset: the renderer decides; an untouched switch writes nothing, so it reads off even where the renderer groups. Off: never grouped, e.g. a year or an ID. On: always grouped." The middle clause restates, for this switch, the sentence the object form's `userActions` row carries for the same renderer behaviour (see Acceptance notes). No schema accept set moves and no export changes: `check:api-surface` reports the surface unchanged, and `check:authorable-surface` exits 0. ## The control (dispatch assumption 1) | commit | tree | `metadata-form-zod-reconciliation.test.ts` | |:--|:--|:--| | `699517cfb9` | omit row deleted, no form row | **red**: `field.(root): accepted by the Zod but unauthorable in the form — offer it, or add a root ledger entry that records why it is not offered: expected [ 'useGrouping' ] to deeply equal []`. Tests 1 failed, 75 passed (76). `VERDICT command-exit 1` | | `75773930ed` | form row added | **green**: Tests 76 passed (76). `VERDICT command-exit 0` | At the final head the same file is green inside the full `@objectstack/spec` run below. ## The console renders it (dispatch assumption 4) Read in objectui at the pin `db11afd4967c`, source only, no browser: - The served node is `{ "type": "boolean", "description": … }` (`packages/spec/json-schema/data/Field.json`). - `SchemaForm.tsx` `inferWidget`: the row has no `widget` and no `type`, so the JSON Schema branch runs, and `type === 'boolean'` gives `'switch'`. No name detector fires: `useGrouping` admits no string, so the condition, secret, icon, colour and field-ref detectors all decline. - `resolveFieldFace`: `'switch'` is not a `WIDGETS` key but is in `KNOWN_PASSTHROUGH_WIDGETS`, so the scalar chain runs, and `effectiveType === 'boolean'` gives `{ kind: 'scalar' }`. - `FieldControl`'s boolean branch renders a `Switch` with `checked={!!value}` and writes only from `onCheckedChange`. Nothing is written on mount. - objectui's own `SchemaForm.requiredMarker-10367.test.tsx` renders a bare `{ type: 'boolean' }` property as `role="switch"` with no wiring. The field form's `allowCreate` row takes the same path today. So the row renders with no extra wiring, and the offer is not the ADR-0078 shape in reverse. ## Catalogs (dispatch assumption 3) 1. `node scripts/check-i18n-bundles.mjs --write`, the regenerator AGENTS.md names for all nine bundle sets. Only `platform-objects` moved: the `en` pair, English fills in the three translated locales, and two provenance rows per locale. That output is committed unedited as `b9a8778e0b`. 2. The six translated leaves are written by hand in this catalog's authored vocabulary: "Unset:" is 未设置 / 未設定の場合は / Sin definir, "renderer" is 渲染端 / レンダラー / renderizador, and the untouched-switch clause follows each locale's authored `object.fields.userActions.helpText`. Labels: 千位分隔符 / 桁区切り / Separador de miles. 3. `--write` again. Merge mode kept every value and dropped the provenance rows, so the three `*.source-hashes.generated.ts` files are byte-identical to the base (`7fea903c24`). This is the rule `5757463712` and `f4ce10c89d` read: an extractor fill is a byte copy recorded in the provenance table, and a translated leaf is authored. ## Pins this PR moves Each went red at exactly the predicted number first, and then moved: - `packages/lint/src/validate-predicate-path-refs.test.ts`: the shipped-form predicate census 82 → 83, and its literal-comparison count 57 → 58. Measured against the merge base `d78a0bda07`: the shipped `*.form.ts` corpus adds exactly `field :: useGrouping :: data.type == 'number'` and removes none. - `packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts`: the translated-label positive control 658 → 659, one row label authored in all three locales. `field-panel-echo-decisions.test.ts` did not move: its no-echo rule reads the two new leaves as authored. Every changed file is inside the claim's file surface. ## Changeset `.changeset/20920-use-grouping-form-row.md`: `@objectstack/spec` **minor** and `@objectstack/platform-objects` **patch**, `Clause-②: no`. These are PR objectstack-ai#20428's levels for the same shape (form rows plus their catalog leaves). What ships was measured after the build, with a positive control each time: - spec: the new row's text is in `dist/data/index.mjs`, `dist/system/index.js` and their `dist/browser/` twins (`dist` is in `files[]`), beside the control `Number of decimal digits`. The served form payload gains an authoring control, which is a backwards-compatible addition, so the level is minor. - platform-objects: after a rebuild, the new leaves are in `dist/plugin.mjs`, `dist/index.mjs` and `dist/metadata-translations/index.mjs` (`Separador de miles` literally, the zh-CN label as escapes), beside the control `Decimales`. This is catalog content only, so the level is patch. - No accept or refuse outcome moves, so the declaration is `no`. ## Verification, head `841a8734bd` Every build, test and typecheck ran through `scripts/pm/os-verify-lock.sh`. Each reported `VERDICT command-exit 0`, except the three first-red measurements named above. | run | result | |:--|:--| | `pnpm build` (whole workspace) | 72 of 72 tasks successful | | `pnpm --filter @objectstack/spec test` | 583 files passed · 17166 tests passed, 1 todo | | `pnpm --filter @objectstack/spec test:repo` | 47 files passed · 831 tests passed | | `pnpm --filter @objectstack/platform-objects test` | 59 files passed · 948 tests passed | | `pnpm --filter @objectstack/lint test` | 117 files passed · 5438 tests passed | | spec / platform-objects / lint `typecheck` | exit 0 each. `check:test-typecheck` OK at 52/248/136, 1/3/2 and 2/6/2 files/errors/signatures held | | the form-registry readers in other packages: cli unit `i18n-coverage` + `i18n-duplicate-demand`; metadata-protocol `protocol.meta-types-*` (4 files); objectql `protocol-meta-types-rich` | 2 files / 27 tests, 4 / 58, 1 / 5, all passed | | `pnpm --filter @objectstack/spec check:generated` | all 15 generated artifacts up to date | | `pnpm check:i18n` | OK: 9 packages, all bundles in sync, no undeclared authoring keys | | `pnpm check:i18n-stale-fill` | OK: 0 stale-fill leaves, 0 baselined | **Gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 86 commands at `841a8734bd`. All 86 ran, and each exit code was written to disk before any pipe. `--ran` reports: 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN, and all 86 exited 0. **Lint, narrowed.** The 8 changed `.ts` files, each matched by eslint's own config (`--print-config` exits 0 for each). The `--format json` output reads 8 files, 0 errors, 0 warnings. Invariance: `eslint.config.mjs` enables no type-aware linting (no `parserOptions.project`), so this diff cannot move the verdict on a file it does not touch. The repo-wide `pnpm lint` is CI's run. No ablation was run: this PR adds a row and moves population pins, and adds no guard. The reconciliation red/green pair above is the measurement of the delta. `origin/main` is three commits past this branch's base (`67c1b11a20`, `aaad682dbc`, `87847a27ce`). None of them touches a file in this diff, so it was not merged in. ## Acceptance notes - **A two-state control on a three-state key.** The switch cannot show "unset", and cannot put a touched field back to unset: once toggled, an author reaches unset again only in the Source tab. The help text states that the untouched state reads off. The same bound holds for every switch over an optional boolean whose absence is not `false`, the object form's `userActions` entries among them. Nothing is refused or dropped, so no card is owed. Carrier: none. --- _Generated by [Claude Code](https://claude.ai/code/session_018fxqvRJW12TaHC7DUQ89Y6)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #20313
Clause-②: no
field.useGroupingmovesplanned→livein the liveness ledger, citing the objectui reader at this repo's.objectui-shapin, and the key's JSDoc stops describing a grouping heuristic the renderer does not use. Six files, the claim's surface as the seat amended it for the patch round (claim5915464335; see Patch round below):packages/spec/liveness/field.json: theuseGroupingrow only.status: live,verifiedAt: 2026-09-30,evidenceScope: cross-repo,evidence= the reader,producer= the seven relay sites, and a rewrittennotethat no longer says the consumer is in flight.packages/spec/liveness/state-counts/field.md: regenerated bypnpm --filter @objectstack/spec gen:liveness-counts,live 91 → 92,planned 2 → 1.packages/spec/src/data/field.zod.ts: the JSDoc aboveuseGroupingonly. The.describe()string was true and is unchanged, so no generated file moves (check:generated: all 15 artifacts up to date)..changeset/20313-use-grouping-live.md: one@objectstack/specpatch. The ledger andsrc/**/*.zod.tsboth ship in the tarball. The two test files below do not ship, so the changeset is unchanged by the patch round.packages/spec/scripts/liveness/check-liveness.test.ts(patch round): the planned missing-file case makes its ownplannedrow, and the typo fixture's comment says what holds.packages/spec/src/data/field.test.ts(patch round): the comment at:843-846stops repeating the false closing sentence.Reader citations: objectui at the pin
db11afd4967cd9d39381c5e21dc2deec9d706204The pin carries the landing. REST
compare ae582b70a...db11afd4967con objectui answersahead,ahead_by 21,behind_by 0(read 2026-09-30). The local objectui clone is shallow and lacksae582b70a, so no local ancestry reading is claimed.file#symbolevidencepackages/core/src/utils/number-display.ts#shouldGroupDisplayNumber:200(authored boolean returns:209, currency:212,scale !== 0:214)evidencepackages/core/src/utils/number-display.ts#formatDisplayNumber:227(:263-266)Intl.NumberFormat:useGrouping: false, ortruewhen authoredtrueevidencepackages/fields/src/index.tsx#NumberCellRenderer:770(reads:810-812, passes:819)field.useGrouping, booleans onlyproducerpackages/plugin-grid/src/ObjectGrid.tsx#generateColumns:3357,:3586,:3761scaleon all three column pathsproducerpackages/plugin-detail/src/fieldEnrichment.ts#ENRICHED_FIELD_METADATA_KEYS:208producerpackages/plugin-detail/src/RecordDetailPanel.tsx#buildRecordDetailFields:171scaleproducerpackages/plugin-detail/src/RelatedList.tsx#effectiveColumns:1562scaleproducerpackages/plugin-list/src/ObjectGallery.tsx#ObjectGallery:376scaleThese are the landing note's (
5890301145) readers and seven relay sites, each re-located at the pin. The relay sites go inproducerper the README's table: the cell reads field metadata those sites build, never the object definition. The gate attributes every one of these paths to objectui, so it counts them and never resolves them.scanEvidenceon the new row returns zero local paths: 2 foreign inevidence, 5 inproducer.The docblock: what was false at the pin, and what it says now
The dispatch named two false sentences. A third one was false too, and it is changed by tense only.
scale: 0with no upper bound keeps grouping and that a small bounded integer range drops it. The pin's heuristic never reads a bound: a declaredscale: 0drops grouping, and any otherscale, or none, keeps it. It now says so, keepingobjectui#4033, which answers 200 and is the issue that introduced the heuristic.ae582b70a, and it keeps the heuristic for an absent key rather than retiring it. The sentence now says that and names objectui'sshouldGroupDisplayNumber. No tracker number is added.Intl.NumberFormatwith grouping unconditionally ON", so ascale: 0year "renders2,026everywhere it is shown". That is false at the pin, where the same field renders2026. Only the three verbs change: constructed, rendered, was shown.The rest of the docblock is untouched, its other tracker numbers included (#20234 owns those).
Controls
Every reading below is objectstack at this branch, base
33b6e8bece, and objectui at the pin.33b6e8bece, untouched tree):pnpm --filter @objectstack/spec check:livenessexit 0,field 94 classified (live 91, planned 2, dead 1), total967 live · 21 planned.packages/spec/liveness/state-counts/field.md is STALE.b842de02e0): exit 0,field 94 classified (live 92, planned 1, dead 1), total968 live · 20 planned. The count moved by exactly one. The success line still claims every repo-local evidence path resolves (664/664). Attributed-to-another-repo count 228 → 230, rows citing a producer 118 → 119, cross-repo scope 142 → 143,verifiedAt652 → 653.node scripts/ablation-replace.mjs, wrap mode, over the committedb842de02e0): the anchor; objectui: packages/fields/src/index.tsx#NumberCellRendererbecomes; packages/fields/src/index.tsx#NumberCellRenderer. That drops the realm marker, which is the realistic way this row rots. The anchor count went 1 → 0 and the blob wentec2da038770a → 40c4a5c2ec52.check:livenessexited 1 with✗ 1 'live' / 'planned' / 'experimental' / 'live-elsewhere' entr(ies) cite a file that is missing from THIS repo: field/useGrouping → packages/fields/src/index.tsx. Restored: blob after = blob at HEAD =ec2da038770a,git diff HEADempty,git statusclean. A re-run is exit 0 withlive 92, planned 1.Tests and gates, first round (at
b842de02e0)pnpm --filter @objectstack/spec build: exit 0, and it moved no tracked file.check:generated: exit 0, all 15 artifacts up to date.vitest --project local: 582 files / 17161 tests passed (1 todo).test:repo: 46 files / 813 tests passed.typecheck(tsc, scripts, test layer): exit 0.node scripts/pm/dispatch-gates.mjsderived 80 commands, all run, exit codes captured before any pipe.--ranreconciliation:80 derived, 79 run, 1 NOT-MEASURED, 0 UNRUN. The exceptions:check:dual-build-cjs-loads: exit 3, NOT MEASURED (PREREQUISITE NOT MET: it needs the built output of 86 packages, and this worktree built the spec, formula, lint and objectql closures only). CI builds the whole tree.check:platform-checklist: exit 1 with 2 problems,coverage.json · picklist: UNCLASSIFIEDandareas/identity-auth.json … auth-plugin.ts#twoFactor. Pre-existing: the same run on a detached checkout of the base33b6e8beceexits 1 with the identical two problems. Neither is touched here.check:doc-formula-expressionsandcheck:lean-entry-closurefirst exited 3 (prerequisites not built). Afterturbo run buildof the formula, lint and objectql closures, both exit 0.--no-inline-config --format jsonover the 4 changed files returns 4 results.field.zod.tshas 0 errors and 0 warnings. For the other three, eslint's own output is "File ignored because no matching configuration was supplied".eslint.config.mjsnever enables type-aware linting (noparserOptions.project, no typed rules), so this diff cannot move the verdict on any untouched file.Patch round (the seat's ruling on the report's two open questions)
Head
f06c26f536. It holds three commits afterb842de02e0:8c1fa09815, the patch itself.1608b8857f, a merge oforigin/mainat9509ea106a.dispatch-gatesflagged the tree as stale on a family file (scripts/pm/check-governed-queue-guard.mjs), so main was merged. The merge brings nothing intopackages/spec:git diff 8c1fa09815 1608b8857f -- packages/specis empty.f06c26f536, a three-line correction to one comment.check-liveness.test.ts, the planned missing-file case (:403-426). It no longer relies on the shipped ledger gradingfield.useGroupingplanned. The case now does four things in the copy:planned;moveCount;expect(readRow(...).status).toBe('planned').setStatusmoves verbatim from the typo block to module scope, andreadRowis new.The copied row keeps its
producer,evidenceScopeandverifiedAt, because none of them trips anything on aplannedrow. Measured on a scratch copy:plannedalone: exit 1, and the only finding isstate-counts/field.md is STALE;plannedplus the count move: exit 0.So the rotted pointer stays the run's only cause.
Control: an ablation through
scripts/ablation-replace.mjs --delete, wrap mode, over the committed8c1fa09815.setStatus(root, 'field', 'useGrouping', 'planned');(anchor 1 → 0, blob06861b68122f → 27ad6cf2ec7b).AssertionError: expected 'live' to be 'planned', in 19 ms and before the gate was spawned. Theliveandexperimentalsiblings stayed green.06861b68122f, andgit diff HEADis empty.dist/leg.f06c26f536changes only a comment in another block of this file.The typo fixture's comment (
:934-939) now states what holds. This was read from the gate, not assumed:check-liveness.mts:886scansevidenceonly for a status inEVIDENCE_SCANNED_STATUSES, andplanedis not one of them.:880-885resolvesproducerat any status, but every path the row'sproducercites is objectui-attributed.scanEvidencereturns zero local paths, so none is resolved against this checkout.producer.mtsfailsproducerorevidenceScopeonly when the value is malformed.field.test.ts:843-846now says the renderer half keeps the interim heuristic for an absent key, and names objectui'sshouldGroupDisplayNumber. It is the same wording class as JSDoc sentence (b).The changeset is unchanged. Neither test file ships:
files[]carriessrc/**/*.zod.ts, never*.test.ts, and nothing underscripts/.Readings. At
f06c26f536unless noted:check:liveness: exit 0,field 94 classified (live 92, planned 1, dead 1).8c1fa09815and1608b8857f.typecheck: exit 0. The test-layer ledger is unchanged at 52 files / 249 errors / 137 signatures.test:repo: 46 files / 813 tests passed at1608b8857fand at8c1fa09815. It was not re-run atf06c26f536: that commit changes only three comment lines in alocal-project test file.vitest --project local: 582 files / 17161 tests passed at8c1fa09815. The spec tree is byte-identical at1608b8857f.dispatch-gates --commandsderived 87 commands. The test-file kind addscheck:engine-double-contract,check:objectql-double-limit,check:pm-governed-merges,check:query-options-erasure,check:type-check-coverage,check:type-check-debtandcheck:where-matcher. All 87 were run after a fullturbo run buildof./packages/*and./packages/*/*, 71/71 tasks.--ranreconciliation:87 derived, 87 run, 0 NOT-MEASURED, 0 UNRUN.check:dual-build-cjs-loads(not measured in the first round) andcheck:type-check-debt("none above its recorded number").check:platform-checklistexits 1 with the same two pre-existing problems measured on the base in the first round.--no-inline-config --format jsonover the 6 changed files. The 3 it lints (check-liveness.test.ts,field.test.ts,field.zod.ts) have 0 errors and 0 warnings. The other 3 report "no matching configuration". Type-aware linting is never enabled.Acceptance notes
Findings outside the claim's file surface as first delivered. Two are folded into the patch round; the rest are untouched.
check-liveness.test.tsfixtures, which this flip had left driving aliverow. See Patch round.packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:387-393, thefield/useGroupingomit row, says "declared, not enforced yet — liveness verdictplanned(the renderer read side … is not landed)". It is stale after this flip, by the file's own rule at:307-309: once the key is enforced, "delete it and decide the offer then — that decision belongs to the enforcement". The seat ruled the offer: offer it. Carrier: the seat's follow-up card, filed at ACCEPT. That card deletes the omit row, adds auseGroupingrow onfield.form.tsfor the number type, and regenerates the catalogs the form rows feed. Neither file is touched here.packages/spec/src/data/field.test.tsrepeated the false closing sentence. See Patch round.packages/plugin-form(deriveMasterDetail.ts:271,:323;sectionFields.ts:409) copiesscalewithoutuseGrouping. This is a read-only inference, not reproduced, and it may be only an input widget. The ledger's CAVEAT limits theliveclaim to the surfacesproducernames. Carrier: none.5915472266("supplies" atfield.zod.ts2649-2650) is not taken, because it is outside the claim's surface. Its pointer names Aselect/radiofield with neitheroptionsnorpicklist: refuse it at theFieldSchemadoor (an accept-set narrowing), or keep it at the ADR-0078 completeness gate? (split from #19518) #20827 as the carrier.Generated by Claude Code