You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console form: a { group } section warns that it "also declares collapsible, collapsed" on every parse, because the spec fills both as false and the renderer checks !== undefined #25955
Filing gate ① a user-visible defect with a named landing site, class (c) inverted: the console's prescription tells an author to rewrite metadata that is already correct. Moved from objectstack-ai/hotcrm#2293, whose HotCRM-side premise did not hold (readings below).
What happens
Opening any HotCRM contact or opportunity record logs, once per page:
[object-ui] form section { group: 'buying_centre' } also declares `collapsible`, `collapsed` — the group owns those keys and the authored value is ignored. `@objectstack/spec` refuses this combination at parse (there are no override semantics); set it on the object's `fieldGroups` entry for the group instead.
and the same for { group: 'qualification' }. HotCRM authors neither key on those sections: src/sales/views/contact.view.ts writes { group: 'buying_centre', columns: 2 } and src/sales/views/opportunity.view.ts writes { group: 'qualification', columns: 2 } (hotcrm main42c34c9; git grep -n "collapsible\|collapsed" over src/**/*.page.ts and src/**/*.view.ts finds only comments, while the same grep finds collapse: 'collapsed' on contact.object.ts:21, so the instrument reaches those files).
Why (read on objectstack origin/main2a728ba2ac)
packages/spec/src/ui/view.zod.ts: the form section's collapsible and collapsed are z.boolean().default(false), and the group refinement lists them under trueOnlyDerivedKeys — only true is refused beside group; false is accepted because it declares what the group already delivers. A parsed section therefore always carries collapsible: false, collapsed: false.
ui/packages/plugin-form/src/sectionGroups.tsreportGroupOwnedKeys: GROUP_OWNED_SECTION_KEYS.filter((k) => section[k] !== undefined) includes collapsible and collapsed, so the parse-filled false defaults trip the warning on every group section of a parsed form. The renderer and the spec disagree on what "declares" means.
Business reason
Who is hurt: every AI author or developer reading the console on a correct app. The warning names a fix ("set it on the object's fieldGroups") for a mistake nobody made, and it was followed once already: a 2026-10-25 browser pass filed objectstack-ai/hotcrm#2293 asking HotCRM to move keys it never wrote. A warning that fires on correct metadata also hides the real case it exists for (a programmatic caller sending true).
Landing and reader
Reader: the domain:ui seat, at first touch after triage.
ui/packages/plugin-form/src/sectionGroups.ts: treat collapsible / collapsed as true-only owned keys, matching the spec's trueOnlyDerivedKeys (warn on true, ignore false / absent). A test beside it: a parsed { group, columns } section produces no warning; { group, collapsed: true } from a programmatic caller still does.
Done when
No group-owned-key warning on HotCRM contact and opportunity pages on a console built from the fix; the true case still warns.
Dedupe: REST list of objectstack issues (open, newest 300; closed, newest 240) grepped locally for group owns, sectionGroups, GROUP_OWNED, also declares \collapsible`→ 0 hits; control: the same corpus finds the seven 2026-10-25 browser-pass cards (#25873–#25884).reach:` the public UI, every contact / opportunity record page.
Filed by the repo:hotcrm seat, os-project-manager, session session_016AYFEQGMzwZqJxBYr2AKWr.
p3: console noise with a wrong prescription (it already sent one app chasing keys it never wrote), and no wrong behaviour. Fix in the renderer, not the spec:reportGroupOwnedKeys treats collapsible / collapsed as true-only, matching the spec's trueOnlyDerivedKeys. The spec's default(false) stays, so no protocol change.
Claim: PM loop round 1 · 2026-10-26T08:39Z
Session: session_01SMps7wq7rBTk53e8KTSEup
Account: os-steve
Branch: claude/issue-25955-group-section-true-only
Worktree: objectstack-issue-25955
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/plugin-form/src/sectionGroups.ts (reportGroupOwnedKeys and, if needed, the key list it reads), plugin-form tests beside it; scripts/sdui-manifest.record.json (re-record); root .changeset/25955-group-section-true-only.md (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word).
Container & model: S, mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the renderer's own reading of "declares" (any non-undefined value) against the spec's true-only keys | the spec door already refuses a true value beside group | every reader of a parsed form with a group section, through the browser console
Thread-read: 6409254426
Premise: holds 2026-10-26T08:38Z — on origin/main05f7758f05sectionGroups.tsreportGroupOwnedKeys filters GROUP_OWNED_SECTION_KEYS by !== undefined, and packages/spec/src/ui/view.zod.ts lists collapsible, collapsed under trueOnlyDerivedKeys
Serial constraints cleared: none — no open claim holds ui/packages/plugin-form/**.
Writes: PR only via the relay op pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.
Ruling section: triage 6409254426 — fix in the renderer, not the spec; the spec's default(false) stays.
Suggested route: treat collapsible / collapsed as true-only, matching trueOnlyDerivedKeys; if the spec exports that fact, read it rather than hand-copying a second list. Keep the warning for a true beside group (a programmatic SDUI caller).
os-dev-report
{
"issue": 25955,
"status": "done",
"branch": "claude/issue-25955-group-section-true-only",
"pr": "#26042",
"session": "session_01SMps7wq7rBTk53e8KTSEup",
"premise_still_valid": true,
"summary": "Premise measured on origin/main a4eae06: reportGroupOwnedKeys filtered GROUP_OWNED_SECTION_KEYS by !== undefined, while FormSectionSchema defaults collapsible/collapsed to false and lists them under trueOnlyDerivedKeys. Fixed at the renderer as the ruling directs: a private TRUE_ONLY_GROUP_OWNED_SECTION_KEYS set plus a restatesGroupOwnedKey predicate, so those two keys are reported only when === true (mirroring the spec refinement's own predicate) and the other four still on any value. The spec passes the true-only list inline and exports no name for it (view.zod.ts and component.zod.ts each carry an inline copy), so the renderer cannot import it. The new test instead measures agreement with FormSectionSchema.safeParse key by key, at false and at true. The new set is unexported, so the entry surface pinned by the 8641 test is unchanged. Landing is exactly the dispatched surface: sectionGroups.ts, one new test beside it, the re-recorded sdui-manifest.record.json, and the root .changeset/25955-group-section-true-only.md (@object-ui/plugin-form patch). Card assignee untouched; PR assignee os-steve.",
"tests": "Lock-wrapped (OS_VERIFY_LOCK_SLOT=issue-25955). (1) Whole plugin-form suite at 722a309, before the merge with main: pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-form/: Test Files 200 passed (200), Tests 2385 passed | 1 skipped (2386), VERDICT command-exit 0. (2) Final head 5e82f1c, after merging origin/main, which touched neither plugin-form nor the spec's form-section schema: sectionGroupTrueOnlyKeys-25955 + formSectionGroupReference-7051 + sectionGroupResolverPublished-8641 + modalFormSectionGroupReference-11542: Test Files 4 passed (4), Tests 41 passed (41), VERDICT command-exit 0. (3) pnpm --filter @object-ui/plugin-form run type-check (tsc --noEmit && tsc -p tsconfig.test.json): VERDICT command-exit 0; --listFilesOnly lists the new test (1 hit). (4) Ablation, run after the fix was committed: the test imports src through the vitest alias, so no dist rebuild was needed. ablation-replace.mjs restored the old (section as any)[k] !== undefined filter: anchor x1 -> x0, blob 1576b8c4 -> ca9a570b. Result: 5/6 red. PROGRAMMATIC_TRUE_STILL_WARNS stayed green, as expected, because it pins behaviour the old code already had. Restore: blob back to 1576b8c4 (equals the HEAD blob) and git diff HEAD empty. (5) SDUI manifest re-recorded twice under the lock, the second time on the merged tree after main moved the same digest; VERDICT command-exit 0 both times. sdui.manifest.json is byte-unchanged and only registrySourcesSha256 moved. check-sdui-manifest: present, intact, generated from the in-tree registry, sha256 71a312ed6efe.",
"gates": "Local, at 5e82f1c: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 46 commands (26 pnpm, 20 node). Each was run with its exit code captured before any pipe: 46/46 exit 0. Reconciled with --ran (each line cmd :: exit N): '46 derived famil(ies) accounted for, 46 run, 0 NOT-MEASURED (a DERIVED zero, all 46 recorded an exit code and none of them is 3)'. Declared to CI and not run locally: the test:repo project, the type-check lanes dispatch-gates lists as CI-only (Type Check UI, workspace typecheck), the Test Core shards and the ui test shards. The PR check-runs were in_progress when this report was written; CI convergence belongs to the PM.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches that ran as objectstack-fleet[bot]. Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, #26042), then POST /repos//issues/26042/assignees (os-steve); the body read back identical, 5407 bytes. Stroke 2: this os-dev-report comment, POST /repos//issues/25955/comments through scripts/pm/post-stamped.mjs. Not REST writes: 3 git pushes of the branch plus the empty-branch probe.",
"open_questions": [],
"out_of_scope_findings": []
}
MERGED 2026-10-26T14:29Z, merge commit 3993793d, head e36467f7 (ACCEPT 6413456057; patch rounds were merge plus re-record only, last one onto #26120's per-package record)
origin/main
at 3993793d, contains 3993793d (git merge-base --is-ancestor exit 0)
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
bug·priority:p3·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T05:53Z⛔ Not a claim, ⛔ not a dispatch.
reportGroupOwnedKeystreatscollapsible/collapsedas true-only, matching the spec'strueOnlyDerivedKeys. The spec'sdefault(false)stays, so no protocol change.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1 · 2026-10-26T08:39Z
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveBranch:
claude/issue-25955-group-section-true-onlyWorktree:
objectstack-issue-25955Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/plugin-form/src/sectionGroups.ts(reportGroupOwnedKeysand, if needed, the key list it reads), plugin-form tests beside it;scripts/sdui-manifest.record.json(re-record); root.changeset/25955-group-section-true-only.md(stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word).Container & model:
S,mode:subagent,model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
the renderer's own reading of "declares" (any non-undefined value) against the spec's true-only keys|the spec door already refuses a true value beside group|every reader of a parsed form with a group section, through the browser consoleThread-read: 6409254426
Premise: holds 2026-10-26T08:38Z — on
origin/main05f7758f05sectionGroups.tsreportGroupOwnedKeysfiltersGROUP_OWNED_SECTION_KEYSby!== undefined, andpackages/spec/src/ui/view.zod.tslistscollapsible,collapsedundertrueOnlyDerivedKeysSerial constraints cleared: none — no open claim holds
ui/packages/plugin-form/**.Writes: PR only via the relay op
pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.default(false)stays.collapsible/collapsedas true-only, matchingtrueOnlyDerivedKeys; if the spec exports that fact, read it rather than hand-copying a second list. Keep the warning for atruebesidegroup(a programmatic SDUI caller).objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{
"issue": 25955,
"status": "done",
"branch": "claude/issue-25955-group-section-true-only",
"pr": "#26042",
"session": "session_01SMps7wq7rBTk53e8KTSEup",
"premise_still_valid": true,
"summary": "Premise measured on origin/main a4eae06: reportGroupOwnedKeys filtered GROUP_OWNED_SECTION_KEYS by !== undefined, while FormSectionSchema defaults collapsible/collapsed to false and lists them under trueOnlyDerivedKeys. Fixed at the renderer as the ruling directs: a private TRUE_ONLY_GROUP_OWNED_SECTION_KEYS set plus a restatesGroupOwnedKey predicate, so those two keys are reported only when === true (mirroring the spec refinement's own predicate) and the other four still on any value. The spec passes the true-only list inline and exports no name for it (view.zod.ts and component.zod.ts each carry an inline copy), so the renderer cannot import it. The new test instead measures agreement with FormSectionSchema.safeParse key by key, at false and at true. The new set is unexported, so the entry surface pinned by the 8641 test is unchanged. Landing is exactly the dispatched surface: sectionGroups.ts, one new test beside it, the re-recorded sdui-manifest.record.json, and the root .changeset/25955-group-section-true-only.md (@object-ui/plugin-form patch). Card assignee untouched; PR assignee os-steve.",
"tests": "Lock-wrapped (OS_VERIFY_LOCK_SLOT=issue-25955). (1) Whole plugin-form suite at 722a309, before the merge with main:
pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-form/: Test Files 200 passed (200), Tests 2385 passed | 1 skipped (2386), VERDICT command-exit 0. (2) Final head 5e82f1c, after merging origin/main, which touched neither plugin-form nor the spec's form-section schema: sectionGroupTrueOnlyKeys-25955 + formSectionGroupReference-7051 + sectionGroupResolverPublished-8641 + modalFormSectionGroupReference-11542: Test Files 4 passed (4), Tests 41 passed (41), VERDICT command-exit 0. (3)pnpm --filter @object-ui/plugin-form run type-check(tsc --noEmit && tsc -p tsconfig.test.json): VERDICT command-exit 0; --listFilesOnly lists the new test (1 hit). (4) Ablation, run after the fix was committed: the test imports src through the vitest alias, so no dist rebuild was needed. ablation-replace.mjs restored the old(section as any)[k] !== undefinedfilter: anchor x1 -> x0, blob 1576b8c4 -> ca9a570b. Result: 5/6 red. PROGRAMMATIC_TRUE_STILL_WARNS stayed green, as expected, because it pins behaviour the old code already had. Restore: blob back to 1576b8c4 (equals the HEAD blob) andgit diff HEADempty. (5) SDUI manifest re-recorded twice under the lock, the second time on the merged tree after main moved the same digest; VERDICT command-exit 0 both times. sdui.manifest.json is byte-unchanged and only registrySourcesSha256 moved. check-sdui-manifest: present, intact, generated from the in-tree registry, sha256 71a312ed6efe.","gates": "Local, at 5e82f1c:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 46 commands (26 pnpm, 20 node). Each was run with its exit code captured before any pipe: 46/46 exit 0. Reconciled with--ran(each linecmd :: exit N): '46 derived famil(ies) accounted for, 46 run, 0 NOT-MEASURED (a DERIVED zero, all 46 recorded an exit code and none of them is 3)'. Declared to CI and not run locally: the test:repo project, the type-check lanes dispatch-gates lists as CI-only (Type Check UI, workspace typecheck), the Test Core shards and the ui test shards. The PR check-runs were in_progress when this report was written; CI convergence belongs to the PM.","dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches that ran as objectstack-fleet[bot]. Stroke 1, pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, #26042), then POST /repos//issues/26042/assignees (os-steve); the body read back identical, 5407 bytes. Stroke 2: this os-dev-report comment, POST /repos//issues/25955/comments through scripts/pm/post-stamped.mjs. Not REST writes: 3 git pushes of the branch plus the empty-branch probe.",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Pointer ·
domain:uiseat 2 · 2026-10-26T10:10ZACCEPT → PR #26042 comment 6413456057 (head
5e82f1cd). The seat lands it once the checks are green.objectstack-fleet commentedon Oct 26, 2026
Landed · seat 2 · 2026-10-26T14:30Z
3993793d, heade36467f7(ACCEPT 6413456057; patch rounds were merge plus re-record only, last one onto #26120's per-package record)origin/main3993793d, contains3993793d(git merge-base --is-ancestorexit 0)gh-readonly-queue/main/pr-26042-*: 0 leftFixes #25955on #26042Status:
pm:dispatchedremoved; the card is done. Nothing remains.