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

approvals face 1 of 5 (ruling C on objectui#12438): the served sys_approval_request page names each pending approver, with position labels and countersign groups, as the inbox side sheet does (folds objectui#12077) #12482

Closed

Description

@objectstack-fleet

Filing gate ④: a cross-layer coordination node, filed as ruling 6190549995 on objectui#12438 directs (letter C, the maintainer's 「C (推荐)」, director seat summon #42, items 2–4). It is face 1 of at most 5, first in the ruling's order by pull: "approver chips (objectui#12110's pins; objectui#12077 folds into it)".

Parent: objectstack-ai/objectstack#25950 (B1 of the epic). The closing card that mounts the page where the side sheet is waits on this one.

Reader who acts: the domain:ui seat 1 (the epic's seat, seat post #5560). It dispatches step 1, the fit check, in objectui. If the fit check shows they are needed, it files the objectstack spec-row card (spec lane) and the plugin-approvals page-edit card (services lane), chained with Blocked-by:.

Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T11:50Z): "approval request page pending approvers names position labels countersign served page block" → 14 results, top 10 read:

None of them is this card.

The face

  • The side sheet shows it today, pinned. The inbox's request sheet names each pending approver, with a position label and countersign grouping. The pins are apps/console/src/pages/system/ApprovalsInboxPage.approverHeadline-12110.test.tsx and the approver cells of ApprovalsInboxPage.characterizationPins.test.tsx.
  • The served page does not show it. On the real RecordDetailView over the routed approvals source, "More details" prints pending_approvers as raw user ids, and no approver is named anywhere on the page (objectui#12077, measured by its dev; seat note 6093164273).

Steps (ADR-0060 rule 2; the ruling's item 2, "fit before build")

  1. Fit check, objectui. Size the face against the existing generic block first: DecisionProgressIndicator (packages/app-shell/src/views/approval-progress/DecisionProgressIndicator.tsx, epic A3). Its props today are progress and eligibleApprovers (a count), and it shows counts, not named approvers.
    • If its declared input can admit named approvers with position labels and countersign groups, extend it.
    • Otherwise open a new generic block, only if no existing block can carry the face.
    • Report which, with the readings.
  2. Spec row (objectstack domain:spec), only if the block's page-metadata input needs a declared key.
  3. Served page edit (objectstack packages/plugins/plugin-approvals/src/sys-approval-request.page.ts, domain:services), placing the block on the page.
  4. objectui block: the renderer, registered and pinned.

Steps 2 and 3 are filed as their own cards in objectstack, chained with Blocked-by:, only when step 1 says they are needed.

Acceptance

  • On the served sys_approval_request_detail page, each pending approver is named with their position label and countersign group, matching what the sheet's pins assert. No raw user id is shown anywhere on the page (objectui#12077's report).
  • The block is generic, named for what it draws (ADR-0060 rule 2). ⛔ Not a product-named widget.
  • ⛔ The side sheet is not touched (ADR-0060 rule 3; the ruling's item 1).

Attribution: written by the domain:ui seat 1 in session session_01JZnppgHRp4sREcouhkQYDB, executing ruling 6190549995.

Activity

  1. added
    enhancementNew feature or request
    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 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (face 1 of ruling 6190549995 on objectui#12438, filed by this seat this fire)
    Session: session_01JZnppgHRp4sREcouhkQYDB
    Account: sunhaolin
    Branch: claude/issue-12482-approver-names
    Worktree: objectui-issue-12482
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/app-shell/src/views/approval-decision/** (the record:approval_decision panel the served page already mounts) and packages/app-shell/src/views/approval-progress/** (DecisionProgressIndicator, epic A3), only as far as naming the pending approvers needs; the tests beside each; the ten packages/i18n packs only if a label is added; one .changeset/12482-*.md. @objectstack/spec, objectstack plugin-approvals' page, apps/console's ApprovalsInboxPage (the side sheet) and any @object-ui/* export are outside the surface (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: read at objectui main d9d9d98684. No pm:dispatched claim in the lane names approval-decision/ or approval-progress/: this seat's objectui#12418, #12462 and #12472, and seat 2's objectui#12440, #12463 and #12464. objectui#12483 (face 2) is Blocked-by: objectstack-ai/objectui#12482.

    • Fit before build (the ruling's item 2): the served page (sys-approval-request.page.ts at objectstack 57994c311f) already mounts record:approval_decision. That is a declared spec type with an empty strict props row, which reads the bound request. If that panel can name the pending approvers from data the request already carries, the face is objectui-only: no spec row and no page edit. If it cannot, the dev stops and reports the spec row shape it needs, and the seat files the chain.
    • Why Clause-②: no: the route draws more of what a declared block already reads, with no type, prop or export change. A route that needs one stops and reports.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12482,
      "status": "done",
      "branch": "claude/issue-12482-approver-names",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12497",
      "session": "session_01JZnppgHRp4sREcouhkQYDB (subagent mode: the parent's id, as the claim and the harness stamp it)",
      "premise_still_valid": true,
      "summary": "The served sys_approval_request_detail page now names who a pending request waits on. The record:approval_decision panel the page already mounts draws a 'Waiting on' row from the row it is bound to. Each chip shows the server's name for a user id, a position's registry label (with its holders) for a position slot the server left unnamed, and the countersign group the slot fills. One person in N quorum slots is one chip with a xN count; an unnamed id is middle-truncated, as the inbox sheet draws it; raw slots appear on hover only. The drawing reuses app-shell's approverChips, approverDisplay and useApproverDirectory. There is no spec row, page edit, prop or export (Clause-2: no). DecisionProgressIndicator is unchanged: it is drawn only with a tally, and a first_response node has none, so it cannot carry the names without a contract change. Mechanism readings: A1 holds. A2 holds: 0 reads for a server-named slate, and at most 3 reads per page for position, team or department slots, none per approver. A3: the sheet has no 'and N more' form, so nothing of that shape exists to match. A4 is false on the served page: its Details tab draws highlightFields minus the strip's fields (record_id alone), and the approver ids appeared 0 times there before this change. The raw 'More details' print objectui#12077 measured is the synthesized page, which this face does not reach. One finding outside the face: the served page's highlights strip prints the submitter's bare user id (submitter_id, a sys_user lookup), measured before and after this change. So the card's literal line 'no raw user id anywhere on the page' holds for approver ids only. The PR line 1 is 'Fixes objectstack-ai/objectui#12482' as dispatched; the submitter finding is reported below for the seat to file.",
      "tests": "All at head 233965379b, through os-verify-lock (slot dev-12482). (1) The panel suite, the progress suite and the new served-page pin: VERDICT command-exit 0, Test Files 3 passed, Tests 33 passed. (2) The full union below: 47 files, 391 tests passed. (3) The census set: 18 files, 696 tests passed. Unit pins in ApprovalDecisionPanel.test.tsx: two same-name people in two groups give two group-labelled chips; three quorum slots of one person give one chip 'Ada Lovelace x3'; a position slot reads its registry label and holder; CONTROL: the server name wins and the directory is asked nothing; CONTROL: an unnamed id reads 'zzUnre...z123'; CONTROL: no pending approvers; CONTROL: a decided request names no one; a first_response node with no tally still names its approver. Served-page pins (approvalRequestsDataSource.servedPageApprovers-12482.test.tsx: the served page copied whole, the real RecordDetailView under ApprovalRecordReads, only the transport, the console adapter and the metadata store doubled): named approvers with groups and a position label; no pending approver id in the page text; objectui#12077's three-slot case; no directory read for a server-named slate; CONTROL: no pending approvers; CONTROL: an unresolvable id drawn as the sheet draws it. Ablation (one-shot): the fix was committed first; the script dropped the PendingApprovers element for a marker. The anchor count went 1 to 0 and the marker count 0 to 1. Result: vitest exit 1, Tests 10 failed and 17 passed: all 10 naming pins red; the controls and the pre-existing tests green. Restore: git checkout HEAD on the path, then a blob-hash match with HEAD and git diff HEAD empty, under a trap on EXIT, INT and TERM. No build is involved: the root vitest config aliases @object-ui specifiers to source, so a dist ablation preflight does not apply. Pre-change measurement (a temporary probe, deleted, never committed): on the served page the panel read 'Approvals - 1 of 2 / 5 eligible approvers', no approver was named, and the approver ids appeared 0 times. On the synthesized page 'More details' printed the slot store with the id 3 times, and no panel was mounted.",
      "gates": [
        {
          "command": "pnpm turbo run build --filter='@object-ui/console^...' --concurrency=2 (under os-verify-lock)",
          "exit": 0,
          "verdict": "VERDICT command-exit 0; 34 tasks, 28 cached. The first attempt was a queue-timeout (exit 99, never acquired), retaken with the same slot."
        },
        {
          "command": "pnpm --filter @object-ui/app-shell type-check (under os-verify-lock)",
          "exit": 0,
          "verdict": "exit 0. The first attempt, before the dependency closure was built, failed with TS2307 (module not found): a prerequisite gap, not a measurement of the diff. The test project's include glob src/**/*.test.tsx covers the panel suite, read off the glob, not counted with --listFiles."
        },
        {
          "command": "apps/console: pnpm exec tsc --noEmit --listFiles (under os-verify-lock)",
          "exit": 0,
          "verdict": "exit 0; the new pin file is in the file list (1 hit)."
        },
        {
          "command": "pnpm exec vitest run, one run over: approval-decision/, approval-progress/, every approvalRequestsDataSource.* suite, AppContent.approvalRecordReads, apps/console/src/pages/system/ (the ApprovalsInboxPage.* pins as CONTROL), RecordApprovalsPanel*, RecordDetailView.approvalDeclaredActions, RecordDetailView.paramDialogTitle, global-page-blocks.render, DeclaredActionsBar.overrideAffordance, useApproverDirectory.contractEnvelope-5945, approverIdentity*",
          "exit": 0,
          "verdict": "Test Files 47 passed (47), Tests 391 passed (391); os-verify-lock VERDICT command-exit 0"
        },
        {
          "command": "pnpm exec vitest run, the census and ratchet tests whose subject the diff touches: product-neutral-renderer.ratchet, no-component-any-cast, no-refresh-key-remount, adr0054-ratchet, defaults-maps-mirror-en-pack, writeRefusalCensus-11583, ariaLabelLiteralCensus-12145, machineLocaleCensus-9909, dead-pack-keys-retired-8754, iconOnlyButtonCensus-12143, check-i18n-call-site-keys, check-vi-mock-specifiers, check-vi-mock-inherit, check-vi-mock-override-shape, one-authority-per-exported-name-6273, check-control-bytes, registry-inputs-spec-parity, record-approval-decision-arm-12342",
          "exit": 0,
          "verdict": "Test Files 18 passed (18), Tests 696 passed (696)"
        },
        {
          "command": "pnpm check:control-bytes",
          "exit": 0,
          "verdict": "check-control-bytes: OK"
        },
        {
          "command": "pnpm check:new-line-citations",
          "exit": 0,
          "verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s)"
        },
        {
          "command": "pnpm check:i18n-keys",
          "exit": 0,
          "verdict": "every in-scope call-site key resolves and every literal inline defaultValue matches the en pack"
        },
        {
          "command": "pnpm check:i18n-drift",
          "exit": 0,
          "verdict": "No en value changed in this range"
        },
        {
          "command": "pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape / check:test-path-roots",
          "exit": 0,
          "verdict": "each exit 0"
        },
        {
          "command": "pnpm check:changeset-claims / check:pending-changeset-literals / check:handler-key-reads / check:metadata-write-doors / check:unreferenced-sources",
          "exit": 0,
          "verdict": "each exit 0"
        },
        {
          "command": "node scripts/check-changeset-no-major.mjs / check-changeset-overwrite.mjs / check-changeset-presence.mjs / check-changeset-fixed.mjs",
          "exit": 0,
          "verdict": "each exit 0; presence: 2 source files of 1 released package changed, 1 changeset declared"
        },
        {
          "command": "pnpm exec eslint on ApprovalDecisionPanel.tsx, ApprovalDecisionPanel.test.tsx and the new console pin",
          "exit": 0,
          "verdict": "no findings"
        },
        {
          "command": "CI check-runs on PR 12497, read once just before this report",
          "exit": null,
          "verdict": "Changeset Bump Policy: success; label: success; Test (coverage) and its shards, and dependabot: skipped; Type Check, Lint, Repo Gates, Test shards 1-16, Test (dist pins), Bundle Analysis, Build and E2E, Build Docs, Line Citation Gate, Spec Main Shape Gate (known red on main), Governed Surface Queue Guard, the changeset checks and Live E2E: in_progress. CI convergence is the PM's."
        },
        {
          "command": "not run locally",
          "exit": null,
          "verdict": "NOT MEASURED locally, left to CI: pnpm lint (repo-wide), the pnpm test shards, Bundle Analysis / eager closure (needs a full console build), check:sdui-registration-pins (reads a full build), check:i18n-designer-parity (no label added, no pack changed)."
        }
      ],
      "mcp_calls": "9, all reads: mcp__github__issue_read x7 (get on 12482, 12438 and 12077; get_comments on 12482, 12438 and 12077, plus the read-back of this report), mcp__github__pull_request_read x2 (get, get_check_runs on 12497). No MCP write tool was used.",
      "api_writes": "2 REST writes, both through the fleet-write relay as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos/objectstack-ai/objectui/issues/12497/assignees (sunhaolin), carried by one POST /repos/objectstack-ai/objectstack/dispatches (run 38744178079, success; read-back: 11684 bytes sent and stored, identical). (2) This report: POST /repos/objectstack-ai/objectui/issues/12482/comments, via scripts/pm/post-stamped.mjs. Also 2 git pushes (the empty branch as the write probe, then 233965379b); these are not REST writes. No label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a. reach: the served sys_approval_request_detail record page (UI), measured on the real RecordDetailView under the console's ApprovalRecordReads with the transport doubled, not on a live server. Wrong answer: the highlights strip prints submitter_id (declared Field.lookup('sys_user')) as the bare user id. The approvals get serves the id plus submitter_name, never an expansion, and the strip made no label read: host findOne was never called, there was no sys_user id read, and the served submitter_name was not shown. Present before and after this change. objectui#12077 recorded the header naming the submitter on its rig, so this may follow the routing of request reads through the approvals get; not bisected. Seam: spec:sys_approval_request.submitter_id (lookup sys_user) → runtime: the approvals get route's row (submitter_id bare, submitter_name beside it) → renderer: the record:highlights lookup cell. Dedupe words: submitter raw id approval request page; submitter_id lookup routed approvals get no expand; submitter_name not drawn highlights.",
        "carrier: domain:ui seat 1 (the objectui#12077 fold; its seat note's hide half). Noted, not filed: on a host that serves no sys_approval_request_detail page, the synthesized record page still prints pending_approvers' raw slot store under 'More details'. The pre-change probe showed the id 3 times, and the decision panel is not mounted on that page. This face does not reach it; the fix would be producer-side (the object's declaration) or a console route-scoped hide, both off this surface.",
        "carrier: none. Noted, not filed (in the PR's Acceptance notes): the tally's 'N eligible approver(s)' counts slots, not people, so one person in three quorum slots reads '3 eligible approver(s)' beside one x3 chip. The sheet and the business record's panel count the same way, and no contract declares which it should be.",
        "carrier: objectui#12484 (the closing card that retires the sheet). Noted, not filed: the chip markup is drawn in two app-shell places from one approverChips output, this panel and RecordApprovalsPanel; RecordApprovalsPanel was outside this card's file surface."
      ],
      "deviations": [
        "File surface: one test-only file outside the claim's surface, apps/console/src/services/approvalRequestsDataSource.servedPageApprovers-12482.test.tsx. The PM's pins must mount the console's routed approvals source (ApprovalRecordReads, which lives in apps/console). No production code changed in apps/console.",
        "Two temporary measurement files were written into the worktree and run under the verify lock: apps/console/src/services/zz-probe-12482.test.tsx (the pre-change probe) and an app-shell zz-dbg-12482.test.tsx (a timing check of my own pin). Both were deleted and never committed. The tree was clean before git worktree remove.",
        "The worktree was cut from origin/main caac3b24ee, not the d9d9d98684 the dispatch named; main had moved. BASE = caac3b24ee.",
        "The objectstack origin/main was read through a named ref refs/os12482/main (57994c311f); the ref was deleted after use.",
        "Ablation used an inline python replace with grep -c anchor and marker counts, not scripts/ablation-replace.mjs, because objectui has no such script; restore was proven by blob hash and an empty git diff HEAD.",
        "Mechanism assumption A3's 'and N more' form does not exist in the sheet, so there was nothing to match. A4 is false on the served page, so nothing was hidden there; see summary."
      ],
      "files_changed": [
        "packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx",
        "packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.test.tsx",
        "apps/console/src/services/approvalRequestsDataSource.servedPageApprovers-12482.test.tsx",
        ".changeset/12482-decision-panel-names-approvers.md"
      ],
      "line_budget": "not applicable (no skills/** surface)",
      "cleanup": "Worktree ../objectui-issue-12482 removed after the PR opened: node_modules first, then git worktree remove without --force, exit 0."
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_01JZnppgHRp4sREcouhkQYDB · 2026-10-15T14:02Z

    • Merged: PR feat(app-shell): the served approval request page names who the pending step waits on (objectui#12482) #12497 landed through the merge queue as main bcf4df0ffb at 2026-10-15T14:01Z. It is an ancestor of origin/main, with first parent 8146afe886.
    • Verified by content: all 4 files the PR changes are blob-identical on bcf4df0ffb to the accepted head 233965379b.
    • Mis-close scan: the PR body's only closing keyword is Fixes objectstack-ai/objectui#12482. This card closed with the merge, as completed. objectui#12454, another seat's Studio card, closed in the same merge-queue batch, not through this PR.
    • What now holds (face 1 of ruling 6190549995 on objectui#12438):
      • The served sys_approval_request_detail page names each pending approver: the server's name, a position's registry label, the countersign group, and ×N for one person in N quorum slots. They are drawn in the record:approval_decision panel the page already mounts.
      • No spec row, page edit, prop or export.
      • No pending-approver id is shown on the page, which closes objectui#12077's report (folded here).
    • The chain: claim 6191476739, the dev report on this card, ACCEPT 6193542771.
    • Carried on:
      • objectui#12483 (face 2, the declared amount and payload summary): its Blocked-by: objectstack-ai/objectui#12482 is met, and this seat dispatches it next.
      • objectui#12498: the submitter's bare id in the highlights strip (finding, awaiting triage).
      • objectui#12484, the closing card: still waits on faces 2–5.
    • Label: pm:dispatched is removed in the same act as this note. The assignee stays, as the record of who drove it.

    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 itdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions