Skip to content

plugin-approvals: the approval request page's Timeline lists each action's step as the machine id (dual_signoff) — its related list asks for step_name, while every row stores step_label #25501

Closed

Description

@objectstack-fleet

Filing-gate: ① product defect · reach: the served sys_approval_request record page, opened from the approvals inbox as admin
Reader: objectstack triage seat → the lane that owns packages/plugins/plugin-approvals; the fix is one column name in src/sys-approval-request.page.ts
Duplicate search: semantic search "approval request timeline related list step_name machine name instead of step_label" in objectstack, incl. closed → 6 hits: #25471 (open, a different surface: the record's Approvals tab, which objectui#13248 fixed), #24281 / #24264 / #24309 / #23889 / #24863 (closed: the highlights strip, the lists and the snapshot, not this related list). None covers the page's Timeline tab.

Measured during an exploratory showcase walkthrough on 2026-10-23: objectstack main b9c7a8f (PORT=3000 pnpm dev, stock showcase), objectui main 6fb31dc console (Vite dev, :5180), locale zh-CN.

What happens

Open an approval request, e.g. EXP-2001 in the showcase showcase_expense_signoff flow. The highlights strip correctly reads 审批步骤: 经理 + 财务签批 (the node label Manager + Finance Sign-off). Directly below it, the 时间线 / Timeline tab lists three rows, and the Step column of every row reads the raw node id:

2026/10/23 下午3:00  批准  Ada Auditor (demo)  dual_signoff  财务同意
2026/10/23 下午2:58  批准  Dev Admin           dual_signoff  经理同意
2026/10/23 下午2:35  提交  Mei Phone (demo)    dual_signoff  —

Where

packages/plugins/plugin-approvals/src/sys-approval-request.page.ts, the Timeline tab's record:related_list:

columns: ['created_at', 'action', 'actor_id', 'step_name', 'comment', 'attachments'],

sys-approval-action.object.ts declares step_name as "Machine name of the step at the time of the action". Next to it, step_label ("Label of the step at the time of the action") is filled on every row, with the boot backfill covering older rows. So the page asks for the wrong one of the two columns.

Expected

The Timeline's Step column shows step_label, the same text the strip above it shows. That means swapping the column, plus whatever the translated-label contract from #24863 asks for.

Session: https://claude.ai/code/session_01A48x1XYLTmiM2pgQxsgeKJ

Activity

  1. added
    area:workflowApprovals and automation — the work that runs without a person driving it
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Oct 23, 2026
  2. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: lands in packages/plugins/plugin-approvals/src/sys-approval-request.page.ts (~L123, the Timeline record:related_list columns); rationale: the page asks for step_name where every row stores step_label; swap the column. One file, size S.
    Path: workflow · 审批在界面里做 | approvals.record-page-decisions (P2) | step ②
    Grade: priority:p2 — inherits the P2 item: the page renders, with the wrong text.
    Type: Bug
    Dedupe: #25471 is the record Approvals tab (objectui, fixed by objectui#13248), not this page; distinct.

    Session: session_01AkmasAfXt3cu83xGzkuKhe (objectstack triage, run on the maintainer's request in the project thread)


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · domain:services seat 1 (seat post #6021, project run by the Sales account) · S mechanical card, in-seat subagent · 2026-10-23T18:22Z
    Session: session_016g7nYP9wdNx1GDa5XDr7Uy
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-25501-approval-timeline-step-label
    Worktree: objectstack-issue-25501
    Domain: domain:server
    Seat: domain:services#1
    File surface: packages/plugins/plugin-approvals/src/sys-approval-request.page.ts, the Timeline record:related_list column step_name → step_label, plus its pin and a changeset. ⛔ packages/spec is not edited. Stop on breach; explain in the report.
    Container & model: S 级机械卡, mode:subagent (general-purpose subagent loading .claude/agents/os-dev.md as its role file), model: default tier, effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: plugin-approvals' own record page asks for a column no row stores | the Timeline shows the machine id dual_signoff | an approver or admin reading the request page, today
    Thread-read: none
    Serial constraints cleared: no open PR touches sys-approval-request.page.ts at claim (13 open PRs read at 2026-10-23T18:15:00Z on 842a0aef9d).
    Resume-from: none

  4. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25501,
      "status": "done",
      "branch": "claude/issue-25501-approval-timeline-step-label",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25526",
      "session": "session_016g7nYP9wdNx1GDa5XDr7Uy",
      "premise_still_valid": true,
      "summary": "Base origin/main dc3e5b9d7b. The Timeline record:related_list on the sys_approval_request page (packages/plugins/plugin-approvals/src/sys-approval-request.page.ts L123) asked for column step_name; it now asks for step_label, and the page doc comment says why. Measured on sys-approval-action.object.ts and approval-service.ts: both fields are declared and stored. step_name is the Approval node id (every writer sets step_name: nodeId or raw.flow_node_id ?? raw.current_step). step_label is written on every row by ApprovalService.actionRow from the request's current_step_label and backfilled by display-label-backfill.ts. So the premise holds with one correction: step_name is not absent, it is the machine id. Every other field the page names exists on its object and is not a machine id where a label exists: the Timeline's created_at/action/actor_id/comment/attachments and its sort field created_at, and the strip's process_label/status/current_step_label/submitter_id/updated_at. Landing is the claimed file; packages/spec is untouched. CI converged all green on head 4e80cfeeaf.",
      "tests": "Added 'names each Timeline step by its label, step_label, never by the node's machine id, step_name' to src/sys-approval-request-page.test.ts. It reads the page's Timeline columns, asserts step_label is present and step_name is absent, asserts step_label is the declared 'Approval Step' text field, and keeps step_name declared as a control. The file's existing test 'every field the page names exists on its object' already asserts that each Timeline column names a field of sys_approval_action; it is green on the defect too, because step_name is a declared field, so it cannot pin this. Ablation at 4e80cfeeaf via scripts/ablation-replace.mjs under the verify lock (anchor \"'actor_id', 'step_label', 'comment'\" -> step_name): 'mutation landed: anchor 1 -> 0, blob 1b31bf215cb3 -> 48f41780fd75'; the new test failed, 'Tests 1 failed | 19 passed (20)', and only the new test went red; then 'restored: blob == HEAD (1b31bf215cb3) and git diff HEAD is empty'. No dist rebuild was needed because the test imports the page from src through a relative import. The first ablation attempt was a lock queue-timeout (exit 99, never acquired, file untouched, git diff HEAD empty); it was retried under the same slot. pnpm --filter @objectstack/plugin-approvals test at 4e80cfeeaf: 'Test Files 95 passed (95)', 'Tests 1234 passed (1234)'. pnpm --filter @objectstack/plugin-approvals typecheck: exit 0, 'check:test-typecheck: OK'. Lock VERDICT command-exit 0.",
      "gates": "Local, at 4e80cfeeaf: dependency-closure build pnpm --filter '@objectstack/plugin-approvals^...' build, VERDICT command-exit 0. node scripts/pm/dispatch-gates.mjs --commands derived 69 commands, each run with its exit code captured before any pipe. 67 exited 0. pnpm check:i18n exited 3 (its prerequisite, the CLI build, was missing); after its named turbo build closure it exited 0 with 'check-i18n-bundles: OK'. pnpm check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET (it needs a whole-workspace build): NOT MEASURED locally. --ran with 'cmd :: exit N' records: '69 derived famil(ies) accounted for — 68 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. CI on PR head 4e80cfeeaf, converged: 34 success, 7 skipped, 0 failing. Required: Lint & Repo Gates success, TypeScript Type Check success, Test Core success (shards 1-6 all success), Build Core success, Governed Surface Queue Guard success. Also Check Changeset success, Dogfood Regression Gate success, Lint · PM dispatch-gates success, Type Check · workspace/source/consumer/debt success, The card this PR closes must claim this branch success. Skipped by their selectors: Console Pin Gate, Dogfood shards, Dogfood Verify CLI, Temporal Conformance, Build Docs, the opt-in upgrade harness and tarball smoke.",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no carries no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes. (1) fleet-write dispatch.mjs pr_create, run 40010783271: POST /repos/objectstack-ai/objectstack/pulls (draft, #25526) plus the POST /repos/objectstack-ai/objectstack/issues/25526/assignees leg (os-sales); read-back said 5115 bytes sent and 5115 stored, identical. (2) post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/25501/comments, this os-dev-report. Not REST: git push of the branch (empty probe, then 4e80cfeeaf).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted in the PR's Acceptance notes, not filed. The sys_approval_action object itself still names the step by step_name in its titleFormat ('{action} · {step_name}'), its display_title formula (the nameField record title) and its highlightFields. So an action record's own title and strip read for example 'approve · dual_signoff'. This is a separate surface (the action record's own page and title, not the request page's Timeline), and the reach was not measured through a public door, so it is not a class a/b/c filing. dedupe words: sys_approval_action display_title step_name, approval action record title machine id, approval action highlightFields step_label"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · 2026-10-23T20:10Z

    reading value
    PR #25526 MERGED through the queue (ACCEPT 6373367068), head 4e80cfeeaf
    origin/main carries b8c959013b

    State: Fixes #25501 closed the card. pm:dispatched is now off and the assignee is cleared.


    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

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions