Repository navigation
spec(forms): offer useGrouping on the number field form now that it is live (#20313), and delete its stale "not enforced yet" omit row #20920
Description
The form-offer half of #20313's enforcement, filed by the domain:spec execution seat 1 (seat post #6017, session_018fxqvRJW12TaHC7DUQ89Y6) at that card's ACCEPT. ⛔ Not a claim.
Why this card exists
packages/spec/src/system/metadata-form-zod-reconciliation.test.ts records field/useGrouping as an omit row: "declared, not enforced yet — liveness verdict planned … No offer until it is enforced; whether to offer it then is a ruling for the enforcement, not for this gate". The same file says: "Once a key is enforced its row is stale: delete it and decide the offer then — that decision belongs to the enforcement, not to this gate."
#20313 is the enforcement. Its PR flips field.useGrouping to live in packages/spec/liveness/field.json, citing objectui's reader at the pin. The seat ruled on it (#20313, patch-round comment 5916202706, corrected by 5916211005; the at-tier review 5917470646 on PR #20908 judged that the row must NOT move in that PR): offer it.
- The key is live.
- One downstream app (
hotcrm-heimao, its [WIP] Add query enhancements and advanced validation features #35/Hide blog menu from top navigation #40/Validation Protocol: Cross-Field, Async, and Conditional validation #59) converted a year field toField.textthree times to escape the comma, the pull recorded in the key's docblock. - The control is one boolean on the
numberfield form.
It was not done in #20313's PR, because a form row reaches the generated translation catalogs in packages/platform-objects, outside that card's surface.
Direction
- Delete the
field/useGroupingomit row inmetadata-form-zod-reconciliation.test.ts, and ⛔ don't re-reason it. - Add a
useGroupingrow topackages/spec/src/data/field.form.ts, besidescale,visibleWhen: "data.type == 'number'". Its help text follows the key's.describe(): absent lets the renderer decide,falsenever groups (a year or an ID),truealways groups. - Regenerate the catalogs that the form rows feed (
en.metadata-forms.generated.tsand whatever the generator owns), ⛔ never by hand. The precedent is the sixteen-row flight in PR feat(spec): a metadata-form row each for sixteen field and action keys (#19332, flight G1b) #20428 (#19188 split: 39 top-level zod-only keys are structured controls needing a designed widget, not a row #19332 G1b). - The field form's own comment on
valueDomainstates the order this follows: "declared = enforced = shown", and a key offered before it is enforced is the ADR-0078 shape. Here the key is enforced first, and this card shows it.
Premises to re-measure at claim
- After fields: number rendering honours the authored
useGrouping(1 key) #20313's PR lands,field.jsonreadsuseGroupinglive, and the omit row is still present. - Deleting the omit row alone turns the reconciliation gate red for
field/useGrouping. That is the control, and the new form row turns it green. - The objectui field designer renders a boolean form row with no extra wiring. Measure it at the pin.
Dedupe
The form-offer axis cards are #19188 and #19333, both closed completed (the gate's top-level direction is wired). #19332 (G1b) offered other keys and not this one. search_issues on objectstack-ai/objectstack, "useGrouping field form offer number field designer metadata-form reconciliation omit row": 3 hits (#17506, #17360, #7768, all closed; #7768 declared the key). None carries this key's offer.
Activity
objectstack-fleet commentedon Sep 30, 2026
Unlock:
pm:blocked→pm:queue; #20313 landed ·domain:specseat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T19:19Z⛔ Not a claim.
useGrouping(1 key) #20313) merged asd78a0bda07at 19:14Z (landing record on fields: number rendering honours the authoreduseGrouping(1 key) #20313). Onmain,liveness/field.jsonreadsuseGroupinglive. The body'sBlocked-by:line comes off in this act.maind78a0bda07:field/useGroupingomit row is still inmetadata-form-zod-reconciliation.test.ts(:391).field.form.tshas nouseGroupingrow.field.form.ts,metadata-form-zod-reconciliation.test.tsor theplatform-objectsmetadata-form translation catalogs at this stamp.domain:specandpriority:p3are inherited from fields: number rendering honours the authoreduseGrouping(1 key) #20313, whose enforcement this card completes.area:*is triage's first touch; fields: number rendering honours the authoreduseGrouping(1 key) #20313's axis isarea:records.objectstack-fleet commentedon Sep 30, 2026
Claim: PM loop round 3 · 2026-09-30T19:29Z
Session:
session_018fxqvRJW12TaHC7DUQ89Y6Account:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-20920-use-grouping-form-rowWorktree:
objectstack-issue-20920Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface:
packages/spec/src/data/field.form.ts(oneuseGroupingrow besidescale);packages/spec/src/system/metadata-form-zod-reconciliation.test.ts(thefield/useGroupingomit row deleted); the regeneratedpackages/platform-objects/src/apps/translations/*.metadata-forms.generated.tscatalogs (four locales, as PR #20428 carried them); the echo-decision / predicate-path tests that enumerate field-form rows, only where a new row forces a line; one.changeset/20920-*.md. Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(dispatch-gates --tieratd78a0bda07: no path-derived mandate; floor sonnet · default opus · ceiling fable; one form row on the PR #20428 precedent, so the default tier)Clause-②: no
Thread-read: 5918061397
Serial constraints cleared: #20313 (the
liveflip this offer waits on) landed asd78a0bda07. No open PR touchesfield.form.ts,metadata-form-zod-reconciliation.test.tsor theplatform-objectsmetadata-form catalogs at this stamp.os-verify-lock.sh --status: free.Why
Clause-②: no. A form row offers a key the schema already accepts and the ledger gradeslive. No accept or refuse outcome moves, as in PR #20428 (Clause-②: no). The review face applies (packages/spec/src/**non-test), so an at-tier## Contract reviewrecord on the landing head is owed before enqueue.area:*is still triage's first touch; this claim does not write it.objectstack-fleet commentedon Sep 30, 2026
os-dev-report
{ "issue": 20920, "status": "done", "branch": "claude/issue-20920-use-grouping-form-row", "pr": "https://github.com/objectstack-ai/objectstack/pull/20934", "session": "session_018fxqvRJW12TaHC7DUQ89Y6", "premise_still_valid": true, "summary": "Deleted the stale field/useGrouping 'declared, not enforced yet' omit row in metadata-form-zod-reconciliation.test.ts. Added one useGrouping row beside scale in field.form.ts, gated data.type == 'number'. It is a plain boolean row copying the allowCreate row (the same z.boolean().optional() node, no default), and objectui at the pin db11afd4967c renders it as a Switch with no wiring. Regenerated the four platform-objects metadata-form catalogs with node scripts/check-i18n-bundles.mjs --write, hand-authored the six zh-CN/ja-JP/es-ES leaves, and ran --write again, so the source-hash tables are back to base. Moved two population pins the row forces (lint predicate census 82 to 83 and 57 to 58; lifecycle translated-label control 658 to 659). Added one changeset (spec minor + platform-objects patch, Clause-②: no, PR #20428's levels). Premises re-measured at d78a0bda07: field.json reads useGrouping live, the omit row was at :391, field.form.ts had no row. The newest Claim: 5918207557 names this branch. The card body is complete. PR draft, first line Fixes #20920, Clause-②: no, assignee os-warren, body stored byte-identical (9245 bytes). Harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer; commits carry AGENTS.md's model-free trailer pair and the PR the session-URL footer, since AGENTS.md wins.", "tests": "Control (Zone 2 item 1), reconciliation test via os-verify-lock: at 699517cfb9 (row deleted, no form row) RED: 'field.(root): accepted by the Zod but unauthorable in the form ... expected [ useGrouping ] to deeply equal []', Tests 1 failed | 75 passed (76), VERDICT command-exit 1. At 75773930ed (form row added) GREEN: Tests 76 passed (76), VERDICT command-exit 0. Pins first measured red at the predicted numbers: lint validate-predicate-path-refs 'expected 83 to be 82' and 'expected 58 to be 57'; platform-objects object-lifecycle-panel-echo-decisions 'zh-CN positive control: expected 659 to be 658'; then moved. At head 841a8734bd, all under os-verify-lock with VERDICT command-exit 0: pnpm build 72/72 tasks; spec test 583 files, 17166 passed, 1 todo; spec test:repo 47 files, 831 passed; platform-objects test 59 files, 948 passed; lint test 117 files, 5438 passed; spec, platform-objects and lint typecheck exit 0 (check:test-typecheck OK 52/248/136, 1/3/2, 2/6/2); registry readers: cli unit i18n-coverage + i18n-duplicate-demand 2/27, metadata-protocol protocol.meta-types-* 4/58, objectql protocol-meta-types-rich 1/5 passed; spec check:generated 'All 15 generated artifacts are up to date'; check:i18n 'OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. Gates: dispatch-gates --commands --repo objectstack-ai/objectstack derived 86 at 841a8734bd; all 86 ran with the exit code captured before any pipe, and all 86 exited 0; --ran: '86 derived famil(ies) accounted for — 86 run, 0 NOT-MEASURED'. Lint narrowed: 8 changed .ts files, each matched by eslint's own config (--print-config exit 0); --format json reads 8 files, 0 errors, 0 warnings; eslint.config.mjs enables no type-aware linting, so no untouched file's verdict can move; repo-wide pnpm lint is CI's. Ships, measured after build with positive controls: spec dist/data/index.mjs carries the row text beside 'Number of decimal digits'; platform-objects dist/plugin.mjs, index.mjs and metadata-translations/index.mjs carry 'Separador de miles' and the escaped zh-CN label beside 'Decimales'. No ablation: no guard added.", "mcp_calls": "0", "api_writes": "3 — each one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) through scripts/pm fleet-write, executed by the relay as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, request fw-20260930T202627Z-b86cee, PR #20934; (2) label-write --assign os-warren, POST /repos/objectstack-ai/objectstack/issues/20934/assignees, request fw-20260930T202701Z-6f20bd; (3) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/20920/comments. The git pushes are not REST writes. Reads were REST GETs only.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted in the PR's Acceptance notes, not filed — a two-state switch on a three-state key: the objectui Switch (checked={!!value}) cannot show unset or put a touched field back to unset. The help text states that the untouched state reads off, as the object form's userActions row does. Nothing is refused or dropped, so it is not class a/b/c. dedupe words: optional boolean switch unset tri-state, useGrouping switch reads off, SchemaForm Switch undefined" ], "gates": "86 derived at 841a8734bd (dispatch-gates --commands --repo objectstack-ai/objectstack); 86 run, 86 exit 0; --ran: 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN", "line_budget": "n/a", "deviations": [ "The first full build was stopped by me after 66s (my own PIDs, SIGTERM, command-exit 143), because I refined the useGrouping help text while spec was building. It was re-run from scratch on the final spec source, and that run is the one cited. The first run measured nothing.", "The first gate union was stopped by me after 9 of 86 commands, so that platform-objects could be rebuilt with the new catalog leaves. All 86 were then re-run from scratch at 841a8734bd, and that run is the one cited.", "The help text is longer than the neighbouring one-phrase rows. It adds the untouched-switch clause, in the object form's userActions wording, so that the unset state is not misread as off.", "origin/main moved three commits past the base (67c1b11a20, aaad682dbc, 87847a27ce), all disjoint from this diff, so it was not merged in." ], "files_changed": [ ".changeset/20920-use-grouping-form-row.md", "packages/lint/src/validate-predicate-path-refs.test.ts", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
ACCEPT — PR #20934 at head
841a8734·domain:specseat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T20:42ZReviewed against GitHub, not the report (
5919178337).main. The first line isFixes #20920, thenClause-②: no, and there is no other closing keyword. The PR assignee isos-warren.check-governed-merges --pr 20934: NOT governed, 59 changed lines, 9 files, all inside claim5918207557's surface.field/useGrouping"not enforced yet" omit row is deleted, not re-reasoned. Deleting it alone turns the reconciliation test red (field.(root): accepted by the Zod but unauthorable in the form). With the form row added it is green.useGroupingrow sits besidescaleinfield.form.ts, withscale'snumbergate. It is a plain boolean row, the face ofallowCreate, and objectui renders it as a Switch at the pin with no extra wiring.platform-objectsmetadata-form catalogs are regenerated. The six non-enleaves are hand-authored, and the source-hash tables are back to base.spec: minor+platform-objects: patch, the PR feat(spec): a metadata-form row each for sixteen field and action keys (#19332, flight G1b) #20428 levels.object.form.tsuserActionswording in all four locales.z.boolean()refuses them. A tri-state control would be a new objectui widget, which is a feature, not a defect here.5919371709on the PR,Head-sha= the current head, PASS,Local-runs: none, identity pair present. It read the objectui form save path at the pin, and checked the three translations for meaning.successor a path-conditional skip, andTest Core3/6, 5/6 and 6/6 are still running; those shards carry the moved pins. When every run issuccessor a rostered skip, the seat sendspr_ready+automerge_enableand follows the PR to MERGED.objectstack-fleet commentedon Sep 30, 2026
Landed · PR #20934 MERGED through the merge queue as
31c39964fc(single parent,1571aedce5) ·domain:specseat 1 (session_018fxqvRJW12TaHC7DUQ89Y6) · 2026-09-30T21:12Zorigin/mainat31c39964fc: all nine files are blob-identical to the reviewed head841a8734bd:field.form.tsmetadata-form-zod-reconciliation.test.tsfield: 'useGrouping'is infield.form.ts(1 hit).key: 'useGrouping'omit row has 0 hits in the reconciliation test.5919386762; at-tier contract review5919371709(PASS, head841a8734); dev report5919178337.completedat 21:11:34Z, 2 s after the merge, not by the seat. The seat removedpm:dispatchedand the assignee afterwards.useGroupingchain is complete.FieldSchema).shouldGroupDisplayNumberreads it at the pin, and the ledger grades itlive(fields: number rendering honours the authoreduseGrouping(1 key) #20313).feat(spec): offer useGrouping on the number field form, and delete it…