Skip to content

approvals inbox: the request drawer's record summary renders raw stored values, labels two fields "Owner", and logs duplicate React keys #11677

Description

@objectstack-fleet

Path: approvals and automation — the approvals inbox | 缺项 | P2

Split by the triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U from objectui#11672 (the 2026-10-06 console dogfood sweep, showcase, framework ce577ec4), items 4 and 6. Re-read on objectui main (848ba0e12f) before filing: still live, with no open duplicate. ⛔ Not a claim, ⛔ not a dispatch.

Triage: lands in apps/console/src/pages/system/ApprovalsInboxPage.tsx (formatPayloadValue and prettifyKey, about :348–:370; the keyed lists about :2216, :2281 and :2352) ⇒ domain:ui; rationale: the summary formats values without their field types, and derives labels from keys.

What happens

Setup → Approvals Inbox → "Invoice Dual Sign-off" shows Status sent, Region emea (the invoice list shows an "EMEA" badge), Issued On 2026-07-23, Subtotal 0, and two rows both labelled "Owner" (prettifyKey drops _id, so owner and owner_id collide). "High-Value Expense — Committee Quorum" shows Approved 0 for a boolean. Opening a drawer, and again after Approve, logs React's "two children with the same key" (seen twice). The duplicated key is not yet identified; the candidates are server-supplied ids in the three keyed lists above.

Done when

  • The summary renders each value with the same faces the record page uses: option labels, booleans, dates, currency. It uses the object's field metadata and its labels, not a prettified key.
  • Two fields never share a label.
  • The duplicate key is identified and the list keys are made unique.
  • objectui#2763 (parked for v18) plans to rebuild this surface. This card fixes today's drawer and does not start that rebuild.
  • Pins: the two measured requests render labels and formatted values, and no duplicate-key warning is logged.

Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:workflowApprovals and automation — the work that runs without a person driving it
    and removed on Oct 6, 2026
  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01FngvPpdrnhHMdHHq6vwwju
    Account: os-justin
    Branch: claude/issue-11677-approvals-summary-faces
    Worktree: objectui-issue-11677
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: apps/console/src/pages/system/ApprovalsInboxPage.tsx (prettifyKey / formatPayloadValue about :348–:370, the request drawer's record summary that calls them, and the keyed lists about :2216, :2281 and :2352), the existing field-display helper the record page already uses (imported, not copied; edited only if it cannot be reached as is), the tests beside them, and .changeset/11677-*.md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier)
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: none. No open PR (objectui#11669, objectui#11069) and no in-flight claim touches ApprovalsInboxPage.tsx. objectui#2763 (pm:on-hold, parked for v18) plans to rebuild this surface; it is not in flight. Read 2026-10-06T06:29Z.

    Why Clause-②: no: the fix lives inside the console's approvals page and reuses existing display helpers. No published package export, registry input or locale key is expected to change. If rendering the faces needs one, the dev reports it with the built-declaration reading, and the seat amends this line before any review.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11677,
    "status": "done",
    "branch": "claude/issue-11677-approvals-summary-faces",
    "pr": "#11702",
    "session": "session_01FngvPpdrnhHMdHHq6vwwju — subagent run, the parent PM session's id (the Claude-Session trailer on e4b9a61)",
    "premise_still_valid": true,
    "summary": "The drawer's record summary now reads the request object's field declarations with useRequestFieldDefs. This is the same cached getObjectSchema read that useHiddenFieldsByObject makes, so no network request is added. Each declared field renders under its declared label (useSafeFieldLabel) through the record page's own cell dispatch, resolveCellRendererType then getCellRenderer from @object-ui/fields. That is imported, not a second formatter. The lead amount follows the same rules. Undeclared snapshot keys and unresolved references take no row. Unknown metadata falls back to the old snapshot rendering. Measured on a live showcase boot (objectstack 01e0f71a) plus this branch's console: before, the invoice card read Owner x2 / amer / sent / 2026-04-19 and logged React's duplicate-key warning twice; after, it reads one Owner / AMER / Sent / Apr 19 / Total 0, the record page's faces for the same record, with no warning on open or after Approve. Premise corrections: (1) Zone 2 #3 is falsified. The duplicate key was in none of the three candidate lists: the summary grid itself was keyed by label, and in jsdom the warning's key argument reads Owner. (2) Zone 2 #4 is corrected. owner_id IS declared: it is the injected system:true lookup, labelled Owner, and both the live meta read and payload_labels say so. The collision is between two declarations. It is fixed by replacing the page's private six-name system list with @object-ui/types isSystemManagedField, which drops owner_id as bookkeeping, plus a distinct-label backstop (withDistinctLabels). (3) The card's 'Approved 0 for a boolean' is wrong. approved_amount is a summary roll-up, and showcase_expense_report declares no boolean. Visible delta to weigh in review: summary-type values, including both measured leads, print through the record page's summary cell, ungrouped (8900 where the old card printed 8,900), exactly as the record page prints them. The PR assignee was NOT set: the auto-mode classifier refused the label-write call (see deviations).",
    "tests": "At head e4b9a61. pnpm exec vitest run apps/console/ (the console project): Test Files 151 passed (151), Tests 1783 passed (1783), lock VERDICT command-exit 0. pnpm exec vitest run apps/console/src/pages/system/ (before the commit): 18 files, Tests 119 passed (119). The new ApprovalsInboxPage.summaryFaces-11677.test.tsx alone: Tests 8 passed (8). pnpm --filter @object-ui/console type-check: exit 0, after turbo run build --filter='@object-ui/console^...' (34 tasks, 32 cached). tsc --noEmit --listFiles includes every ApprovalsInboxPage test file, the new one included. pnpm --filter @object-ui/console lint: exit 0, 0 errors / 221 warnings. The page carries the same 19 warnings as at the merge base, none new. An earlier run had flagged one react-hooks/static-components error on RecordFieldFace; it carries a reasoned disable, following the plugin-detail and plugin-dashboard precedent. Reverse verification, fix committed first, script under the verify lock with a trap restore (git checkout HEAD -- absolute path) and a proof per leg (blob == HEAD blob ad56146, git diff HEAD empty). Leg 1: the page at the merge base 57d82cb (on-disk blob == BASE blob, data-summary-field count 0): Tests 8 failed (8). Leg 2: via scripts/ablation-replace.mjs (anchor 1 to 0, blob changed), the old six-name list in place of isSystemManagedField: Tests 3 failed / 5 passed (the two Owner tests and fail-open). Leg 3: withDistinctLabels removed: Tests 1 failed / 7 passed (two declared fields sharing a label). HEAD: Tests 8 passed (8). The duplicate-key assertions sit after the label assertions, so their red direction is the merge-base measurement (jsdom: two console.error with key Owner; live Chromium: two 'Encountered two children with the same key' per open), not those lines themselves. Live verification: showcase backend (objectstack dev --seed-admin --fresh, a detached objectstack worktree at 01e0f71a) plus vite on this branch, driven with Playwright 1.62.1 and /opt/pw-browsers/chromium. Before/after card dumps and screenshots taken. The invoice was approved from the drawer: zero duplicate-key errors after open and after approve, and the API then reads status approved.",
    "mcp_calls": "0",
    "api_writes": "2 — (1) POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay, request fw-20261006T071801Z-889320, run 37428803183 success), which executed pr_create = POST /repos/objectstack-ai/objectui/pulls (draft forced) as objectstack-fleet[bot]; read-back 9579 bytes sent / 9579 stored, identical. (2) this os-dev-report comment, POST /repos//issues/11677/comments via scripts/pm/post-stamped.mjs. The classifier refused one attempt before any request was sent: node scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 11702 --assign os-justin (0 writes). git push x2 (the empty-branch probe, then e4b9a61) are not REST writes. Everything else was a read: gh api GET for the issue, its comments, and the PR read-back.",
    "gates": [
    {
    "cmd": "pnpm exec vitest run apps/console/ --maxWorkers=2",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm exec vitest run apps/console/src/pages/system/ApprovalsInboxPage.summaryFaces-11677.test.tsx",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm --filter @object-ui/console type-check",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm --filter @object-ui/console lint",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:new-line-citations",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:control-bytes",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:changeset-claims",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:pending-changeset-literals",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:handler-key-reads",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "node scripts/check-changeset-no-major.mjs",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "node scripts/check-changeset-presence.mjs",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "node scripts/check-changeset-fixed.mjs",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "node scripts/check-changeset-overwrite.mjs",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:vi-mock-override-shape",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:test-path-roots",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:phantom-deps",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:self-import",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "pnpm check:i18n-keys",
    "exit": 0,
    "head": "e4b9a61"
    },
    {
    "cmd": "repo-wide pnpm lint / pnpm test / pnpm type-check farm",
    "exit": "NOT MEASURED",
    "head": "e4b9a61",
    "reason": "declared to CI per the local-scope rule; objectui families were derived by hand from package.json and .github/workflows (dispatch-gates.mjs answers for objectstack only)"
    }
    ],
    "line_budget": "n/a — no skills/** file and no line-ratcheted ledger in the diff",
    "files_changed": [
    "apps/console/src/pages/system/ApprovalsInboxPage.tsx (+253/-34; the fix: useRequestFieldDefs, indexFieldDefs, RecordFieldFace, withDistinctLabels, isBookkeepingKey replacing PAYLOAD_SYSTEM_KEYS, rows keyed by field key)",
    "apps/console/src/pages/system/ApprovalsInboxPage.summaryFaces-11677.test.tsx (+485, new, 8 tests)",
    "apps/console/src/pages/system/ApprovalsInboxPage.hiddenFieldTrim.test.tsx (+13/-10, fixtures declare every snapshot key)",
    "apps/console/src/pages/system/ApprovalsInboxPage.characterizationPins.test.tsx (+15/-12, the _id-trim fixture moves from owner_id to vendor_id)",
    "apps/console/src/pages/system/ApprovalsInboxPage.rawPayloadGate.test.tsx (+2/-1, header comment names the classifier instead of the removed list)",
    ".changeset/11677-approvals-summary-faces.md (+15, @object-ui/console: patch)"
    ],
    "deviations": [
    "PR assignee NOT written. The auto-mode classifier refused node scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 11702 --assign os-justin with reason [External System Writes]. Per the role file it was not retried by another route. Seat to set the PR #11702 assignee to os-justin. The PR currently carries only the labeler's apps/tests labels and no assignee.",
    "Zone 3 'declared order' was not adopted. The card keeps snapshot order, which the live read shows is the engine's column order (injected columns, then declaration order), so the visible order matches the declared one. Changing it would have churned the objectui#5565 promotion pins for no visible gain.",
    "Three tests beside the page were edited (hiddenFieldTrim, characterizationPins, rawPayloadGate). They are within the claim's 'the tests beside them'. No file outside the claimed surface was touched, and hiddenFields.ts is unchanged: the drawer's field-defs read lives in the page.",
    "For live verification a detached objectstack worktree (/home/user/objectstack-probe-11677 at 01e0f71a) was created, installed and built through the verify lock (VERDICT command-exit 0, 325s) to boot the showcase backend. Its processes and the worktree were removed after measuring, as was the objectui worktree after the PR opened.",
    "Attribution: commits carry the AGENTS.md model-free trailer pair (Co-authored-by: Claude, Claude-Session) instead of the harness reminder's model-named Co-Authored-By. The PR footer uses the role file's session-URL form instead of the harness's robot-emoji form. Both follow the precedence the role file and AGENTS.md state."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed — the record page's summary/formula cell (FormulaCellRenderer in @object-ui/fields) prints a roll-up's stored number ungrouped and without currency: EXP-DEMO's Total reads 8900 on the record page. The drawer now inherits that face by design (parity). This is a presentation gap in a published cell, outside this card's file surface, and does not fall in classes a/b/c; it is recorded in the PR's Acceptance notes.",
    "carrier: 承接者:无 · noted, not filed — showcase's translation bundle labels showcase_invoice.total 'Total' while the object declares 'Subtotal'. The meta read serves the bundle's label (the record page and now the drawer show Total), while payload_labels carries the object's own label. The spec documents payload_labels as localized only for single-locale projects. Observation; recorded in the Acceptance notes."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR objectui#11702 at e4b9a61 (Fixes #11677)

    domain:ui execution seat 2 · session_01FngvPpdrnhHMdHHq6vwwju (os-justin) · 2026-10-06T07:23Z. Dev report: 6011424708.

    • Shape: draft, base main, first line Fixes #11677, and the body has no other closing keyword. Clause-②: no is at the start of a line. 6 files, all inside the claim's surface: ApprovalsInboxPage.tsx, four tests beside it, one changeset. No governed path, no content/docs/releases/ edit.
    • Seat's own reading of the diff:
      • The summary rows come from the request object's field declarations. Each value goes through @object-ui/fields' resolveCellRendererType and getCellRenderer, which are imported, not a second formatter. Labels come from useSafeFieldLabel.
      • The page's private six-name list is replaced by @object-ui/types isSystemManagedField, which drops owner_id. withDistinctLabels is the backstop, and rows are keyed by field key.
      • When metadata is unreadable, the old snapshot path is kept.
      • No export is added (Clause-②: no holds).
    • Changeset prose (@object-ui/console: patch), checked sentence by sentence against the diff: the declared-labels, cell-faces, undeclared-keys and unresolved-references, metadata-fallback, owner_id bookkeeping, distinct-labels and key sentences all match the code. The visible change it states, that a summary roll-up now prints as the record page prints it (ungrouped), is in the diff and stated, not hidden.
    • Premise corrections accepted, measured by the dev:
      • The duplicate key was the summary grid keyed by label, not one of the three lists the card named.
      • owner_id is a declared system: true lookup labelled "Owner".
      • "Approved 0" is a summary roll-up, not a boolean.
    • Tests: 8 new pins, plus three reverse legs (merge-base page, the old list restored, withDistinctLabels removed), each red in the predicted direction and restored by blob. Live before/after on a showcase boot. CI at this read: 26 success, 3 skipped, 13 in progress, 0 failed.
    • Out-of-scope findings (2):
      • The record page's summary/formula cell prints a roll-up ungrouped and without currency. Carrier: objectui#11683, the queued card for the formula cell printing a raw number; not filed again.
      • The showcase's translation bundle labels showcase_invoice.total "Total" where the object declares "Subtotal". Acceptance notes.
    • Deviation: the dev's PR-assignee write was refused by its session's permission classifier. Per the role file it was not retried, and the seat does not redo a refused write under another identity. It has been raised to the maintainer. No landing gate reads the PR assignee.
    • Landing: no contract-review face is touched (Clause-②: no, no spec path, no governed text). Ready and auto-merge follow once every check on this head is green.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11702 → main 3409fe8, verified by content. Fixes closed this card

    domain:ui execution seat 2 · session_01FngvPpdrnhHMdHHq6vwwju (os-justin) · 2026-10-06T07:54Z.

    • The merge. Squash 3409fe8 went through the merge queue, with one parent, ce464d9. It is an ancestor of origin/main. 6 files, +783/−57, the same as the PR.

    • Content check against the first parent, in apps/console/src/pages/system/ApprovalsInboxPage.tsx:

      reading ce464d9 3409fe8
      function useRequestFieldDefs 0 1
      function withDistinctLabels 0 1
      PAYLOAD_SYSTEM_KEYS (the private list it replaced) 3 0
      rows keyed key={row.key} 0 1
      ApprovalsInboxPage.summaryFaces-11677.test.tsx present 0 1
    • Closures. Fixes #11677 closed this card as completed at the merge. No other issue closed in the landing window. pm:dispatched comes off in this stroke.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:workflowApprovals and automation — the work that runs without a person driving itbugSomething 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