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.

Record approvals panel: the action history draws the humanized step_name, not the authored step_label that every sys_approval_action row stores #13248

Closed

Description

@objectstack-fleet

Filing gate ①: a product defect with a measured instance. This is the console half of objectstack-ai/objectstack#24863, split off by objectstack triage (R537, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim.

Reader (who acts): the objectui domain:ui seat, in one PR on packages/app-shell/src/views/RecordApprovalsPanel.tsx and its test.

The finding

Done when

Dedupe

objectui issues and PRs updated since 2026-08-01 were grepped for step_label, step_name, and approval near step or history. The nearest hit is objectui#12608 (closed): "Requests and Action History show record names, step labels…". It covers the Setup pages, not the record panel's history row. New.


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 20, 2026
  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (priority:p3, bug)
    Session: session_01GL347th7aWQUnY2k8diSLG
    Account: os-support-ai (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-13248-approval-history-step-label
    Worktree: objectui-issue-13248
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: packages/app-shell/src/views/RecordApprovalsPanel.tsx (read at objectui main f22b980), the action-history row: it draws a.step_label when present and falls back to prettifyMachineName(a.step_name) only when absent, the headline's existing rule in the same file. Tests beside the panel, both cases. One .changeset/13248-approval-history-step-label.md (@object-ui/app-shell patch). ⛔ No server change (localizing step_label is objectstack-ai/objectstack#24863's server half). ⛔ Not the inbox page. ⛔ No new package export. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui over this surface reads no path-derived mandate)
    Clause-②: no
    Responsibility: the risk is produced by objectui's own record approvals panel, whose history rows prettify the step's machine name and never read the step_label every sys_approval_action row stores. The platform path that covers it: the panel's headline and the inbox already prefer step_label. Who reaches it: every reader of a record's approval history whose step label differs from its humanized node name (measured on HotCRM: "Manager Review" drawn for "Sales Manager Review").
    Thread-read: none
    Serial constraints cleared: open objectui PRs touching RecordApprovalsPanel.tsx: none (the ten open PRs' file lists were read at this claim).

    Why Clause-②: no: a console panel reads a column it already receives. No export, prop, schema key or accept set of a published package moves.

    Claimed at: 2026-10-20T12:20Z


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 13248,
      "status": "done",
      "branch": "claude/issue-13248-approval-history-step-label",
      "pr": "https://github.com/objectstack-ai/objectui/pull/13257",
      "session": "session_01GL347th7aWQUnY2k8diSLG (subagent run: the parent seat's harness-stamped id, as on the commits' Claude-Session trailer)",
      "premise_still_valid": true,
      "summary": "The record approvals panel's history rows now show the step label the flow author wrote. Each row looks up its request's step_label by request_id, and falls back to prettifyMachineName(step_name) only when there is none. The header now uses the same helper, stepDisplayText, so the two cannot drift. PM mechanism assumption 2 is DISPROVED. The rows the panel reads never carry step_label: GET /approvals/requests/:id/actions returns listActions, which maps every row through rowFromAction. rowFromAction names step_name and not step_label, and the spec's ApprovalActionRow declares no step_label. Both hold at objectui's pin f4755a68a and at objectstack origin/main 3fbef41b4, and this read has no $select. Reading a.step_label, as the card's Done-when spells it, would therefore change nothing on any backend today, and serving it is the server change the card forbids. So I changed the route to match the ruling's intent. The server writes sys_approval_action.step_label as a copy of the owning request's step label: all 15 inserts at the pin go through actionRow, which writes requestDisplayLabels(request).current_step_label. The request get and list both serve that same value as step_label through rowFromRequest. The panel already holds those request rows, so each row reads its label there. No server change, no new package export, no new key on ApprovalActionLite, and the inbox is untouched. The card's defect premise holds: the rows drew the prettified step_name and never the authored label.",
      "tests": "At HEAD 1f5b8dc, run from the repo root: pnpm exec vitest run over 11 files. These were the new RecordApprovalsPanel.actionStepLabel-13248.test.tsx, the six other RecordApprovalsPanel*.test.tsx suites, and the RecordDetailView suites recordOpenRequests-12701, approvalDeclaredActions, approvalLock-12220 and approvalRecallGate. Result: 'Test Files 11 passed (11)', 'Tests 68 passed (68)', lock VERDICT command-exit 0. The verbose reporter shows each of the new file's four cases passing. ABLATION (one, because the tested row behaviour changed), run on the committed fix 11ac365 with objectstack's scripts/ablation-replace.mjs in wrap mode under the lock. It replaced 'stepDisplayText(stepLabelByRequest.get(a.request_id), a.step_name)' with 'stepDisplayText(null, a.step_name)', which is the old row behaviour. The tool's on-disk proof: anchor x1 to x0, replacement x0 to x1, blob e808998a62ba to 5e5221616d94. Predicted: cases 1, 3 and 4 red, case 2 (the fallback) green. Observed: 'Tests 3 failed | 1 passed (4)'. Case 1 failed with: expected '· Manager Review' to be '· Sales Manager Review'. Case 4 received '· Finance Review' where it expected '· Finance Director Sign-off'. Restore proven: blob after restore e808998a62ba equals HEAD, git diff HEAD is empty, and my own grep -c afterwards read anchor 1 and replacement 0. No dist leg was needed: the test imports ./RecordApprovalsPanel by relative path, so it resolves to src. The later commit 1f5b8dc changes only the test's fetch-stub cast (any to Response). The panel source is byte-identical between 11ac365 and 1f5b8dc, so the ablation reading stands for the head.",
      "gates": "Local subset at HEAD 1f5b8dc. Each exit code was captured before any pipe: output redirected to a log file, then EXIT=$?. (1) Build closure, before type-check: pnpm turbo run build --filter='@object-ui/app-shell^...' --concurrency=2, under the lock: 'Tasks: 28 successful, 28 total', VERDICT command-exit 0. The diff touches none of those dependencies. (2) The vitest run above: exit 0. (3) pnpm --filter @object-ui/app-shell type-check: the script echoed 'tsc --noEmit && tsc -p tsconfig.test.json', VERDICT command-exit 0. tsc -p tsconfig.test.json --listFilesOnly lists the new test, with a count of 1. (4) pnpm --filter @object-ui/app-shell lint: '4163 problems (0 errors, 4163 warnings)', exit 0, and no hit names the new file. At 11ac365 the count was 4165; the two any-casts in the new test were removed in 1f5b8dc. (5) node scripts/check-changeset-presence.mjs: exit 0 ('2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'). (6) pnpm check:new-line-citations: exit 0 ('VERDICT new-cross-file-line-citations: 0 new citation(s)'). (7) pnpm check:control-bytes: exit 0. (8) pnpm check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:changeset-claims, check:pending-changeset-literals and check:unreferenced-sources: each exit 0. The --ran reconciliation does not apply: dispatch-gates.mjs is objectstack-only, so I hand-derived this list from the dispatch's objectui gate list. No i18n key, README or content/docs fence was touched, so those families are not owed. CI check-runs on 1f5b8dc as read at report time (35 runs). Success: Build Docs, Bundle Analysis, Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Live E2E (informational), label. Skipped: Test (coverage), Test (coverage shard N/8), dependabot. in_progress: Build & E2E, Lint, Repo Gates, Test (dist pins), Test (shard 1/16 to 16/16), Type Check. I did not wait on these, per the dispatch contract.",
      "mcp_calls": "0. No MCP GitHub tool was called.",
      "api_writes": "2 relay strokes. (1) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, request fw-20261020T124432Z-211425, run 39424638694 success). The fleet executed it as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectui/pulls (draft #13257) and POST /repos/objectstack-ai/objectui/issues/13257/assignees (os-support-ai). The relay's read-back: 7143 bytes sent, 7143 stored, identical. (2) This os-dev-report, via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectui/issues/13248/comments. Separately there were 3 git pushes (empty branch, 11ac365, 1f5b8dc), which are not REST. No labels were written. The labels tests and 'package: app-shell' on the PR came from the repo's labeler. All other GitHub traffic was REST GETs: issue 13248 and its comments, objectstack#24863, PR 13257, and its check-runs.",
      "open_questions": [
        {
          "question": "Do you accept the route change? The history row reads its REQUEST's step_label, joined by request_id, instead of a.step_label as the card's Done-when spells it. The rows on that door carry no step_label, at the pin or on objectstack main.",
          "options": [
            "A: accept as landed. The value is the one the server stores on the action row, since actionRow copies the request's label. It is read from a field the contract declares and the panel already holds. No server change and no new key on ApprovalActionLite. The header and the rows read one served value, so objectstack#24863's localization reaches both together.",
            "B: require the literal a.step_label. objectstack would have to declare step_label on ApprovalActionRow and serve it from rowFromAction, which is a server change the card rules out, and objectui would need a new key on ApprovalActionLite. Until then the row stays unchanged on every backend."
          ],
          "recommendation": "A. It answers the measured defect today without the server change the card forbids. It follows contract-first: no undeclared key is added on the consumer side, and no read is added for a field the door never sends, which would declare a capability the runtime does not honour. It adds no new surface at this stage. B would put a second copy of the same value on the wire with no reader beyond this panel."
        }
      ],
      "out_of_scope_findings": [
        "Observation, not a filed class: GET /approvals/requests/:id/actions (rowFromAction) leaves out four display columns that sys_approval_action stores: step_label, acted_as_label, object_name and record_id. The spec's ApprovalActionRow does not declare them, so no contract is violated. After this PR, no objectui reader needs step_label from that door. carrier: none (承接者:无). Noted in PR #13257's Acceptance notes and not filed. Dedupe words: rowFromAction step_label, ApprovalActionRow display columns, approval actions door acted_as_label."
      ],
      "deviations": [
        "I did not read a.step_label as the Done-when spells it. Mechanism assumption 2 was disproved, so I changed the route per the ruling's intent; see summary and open_questions.",
        "The dispatch's standing line 'if the producer is elsewhere, fix on the producer side' conflicts with the ruling's 'no server change'. I followed the ruling, because its intent could be met without touching the producer. The producer-side gap is recorded as an Acceptance note, not silently resolved either way.",
        "I added data-testid='approval-action-step' on the row's step span. It is the tests' selector and the seat's browser selector, inside the named file.",
        "Commits carry the model-free trailer pair (Claude-Session plus 'Co-authored-by: Claude'), per objectui AGENTS.md, rather than the harness reminder's model-named Co-Authored-By line.",
        "Repo-wide lint, test:repo and the full test farm are CI's and were not run locally. The browser reading was not taken (not owed); the pointer is below.",
        "The worktree /home/user/objectui-issue-13248 was removed after the PR opened: node_modules deleted, then git worktree remove without --force succeeded on a clean tree."
      ],
      "files_changed": [
        "packages/app-shell/src/views/RecordApprovalsPanel.tsx (+33/-3)",
        "packages/app-shell/src/views/RecordApprovalsPanel.actionStepLabel-13248.test.tsx (new, 204 lines)",
        ".changeset/13248-approval-history-step-label.md (new, @object-ui/app-shell patch)"
      ],
      "line_budget": "Not applicable. No skills/** or governed-surface path is touched (Tier: not governed). The diff is +244/-3 over 3 files against base f22b980.",
      "browser_reading_pointer": "On a stock showcase backend with --seed-admin, use object showcase_invoice and flow showcase_invoice_signoff. Set an invoice's Status to Sent; the form then also requires Issued On. That opens a request at approval node dual_signoff, authored 'Finance + Legal Sign-off', which prettifies to 'Dual Signoff'. On the invoice's record page the history rows should read '· Finance + Legal Sign-off'; before this change they read '· Dual Signoff'. Row selector: [data-testid=\"record-approvals-panel\"] li [data-testid=\"approval-action-step\"]. Other showcase nodes whose label differs from the prettified node name: ask_signoff 'Manager Sign-off', review 'Task Sign-off' (showcase_task), committee 'Committee Sign-off (2 of 3)' (showcase_expense_report)."
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #13257 comment 6291376733 · domain:ui seat 3, session_01GL347th7aWQUnY2k8diSLG · 2026-10-20T12:58Z · head 1f5b8dc · the row reads its request's step label (the Done-when's a.step_label is never served by the actions door; measured in the record) · live reading in the record · landing through the merge queue.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 3, session_01GL347th7aWQUnY2k8diSLG · 2026-10-20T13:20Z

    reading result
    PR #13257 MERGED through the merge queue, merge commit 12cde04 on main
    queue branch gh-readonly-queue/main/pr-13257-* absent (git ls-remote: 0 refs)
    origin/main 12cde04, packages/app-shell/src/views/RecordApprovalsPanel.tsx function stepDisplayText (1)

    State: the card closed completed by the PR's Fixes; pm:dispatched is removed in this pass. The live history reading ("Finance + Legal Sign-off" at the head; "Dual Signoff" at the parent) and the route note (the row reads its request's label; the actions door serves no step_label) are in the ACCEPT, 6291376733.


    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:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions