Skip to content

Automations pillar, remainder of objectui#12454: node-card styling on the flow canvas and the node configuration aside (decision outcomes as cards) #12488

Closed

Description

@objectstack-fleet

This card carries targets 4 and 5 of objectui#12454. objectui#12454 keeps targets 1–3 (rail grouping, item header, tab row) and closes with PR #12481. The two targets live in shared metadata-admin files outside that claim's surface; the developer's report (6190957173, open question 1) and the seat's REWORK (6192072808) moved them here.

Filing gate: ④, a child of the epic #12446 split off an in-flight card. It carries the parent's domain:ui and priority:p2 and stays in the epic PM's reservation (pm:epic). The dispatch seat is domain:ui#4.

Source

Epic decisions 1 and 3. Reference artboard: docs/design/studio-redesign/automations.html and .png on main. The epic's README gives the shared numbers.

Target (restated from objectui#12454's targets 4 and 5)

  1. Node cards on the canvas: 260 px wide, 8 px radius, and a 28 px tile coloured by kind: start green, screen pink, get-record blue, decision amber, loop violet, end grey. The label is 13 px / 600; the kind is 10 px uppercase with the object api name in mono. The selected node carries the primary border and ring. Node layout and edges stay the existing engine's; only the card styling changes.
  2. Node configuration aside, in the skeleton's StudioInspector:
    • one header (eyebrow "Node · Decision", the node label, close) instead of today's two stacked headers;
    • a decision node shows each outcome as a card with its target node name and its CEL condition in a mono textarea;
    • "Add outcome" is a dashed button.

File surface (measured by objectui#12454's developer on 1de2c1d)

  • packages/app-shell/src/views/metadata-admin/previews/flow-canvas-parts.tsx (NodeCard).
  • packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx (InspectorShell: a variant that drops its own header under the Studio inspector).
  • packages/app-shell/src/views/metadata-admin/inspectors/FlowObjectListField.tsx (the outcomes list).

All three render in the metadata-admin designer as well. Keep that surface working (objectui's verify skill drives it), or scope the Studio look the way PR objectui#12481 scoped its chrome (FlowPreviewChromeProvider).

Acceptance

  1. The selected decision node and its aside match the artboard's region; light and dark screenshots at 1280 px, with the README's deviation list.
  2. The metadata-admin designer's flow preview and node inspector are unchanged, or the change is shown in the same screenshots.
  3. The decision inspector's existing behaviour stays. The outcome cards edit the same branches the Branches table edits today. HotCRM routes decisions by out-edge conditions; its developer noted that the Branches table then reads "No items yet" while the canvas shows two branches. Show what the outcome cards read in that case.
  4. Every test under metadata-admin/previews/ and metadata-admin/inspectors/ that the diff reaches stays green or is updated in the same PR, each update ablated once.

Constraints

  • Tailwind utilities and theme tokens only; no inline style; existing data-testids and i18n keys stay.
  • Lands after objectui#12454 (same area, and the line below).

Blocked-by: #12454

Parent: #12446


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    on Oct 15, 2026
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Measurement for acceptance item 3, read 2026-10-15T13:53Z on objectui d4bc81e with HotCRM 63db83e in a local console. Studio /studio/app.objectstack.hotcrm/automations?surface=flow:campaign_enrollment, decision node "Campaign Open?" (check_campaign_open) selected:

    • Branches: "No items yet." with an "Add item" button, while the canvas draws two branches (edges e4 "Open · Leads" and e7 "Open · Contacts", both conditional).
    • Branch mode: the combobox shows "—" (unset, which means exclusive).
    • Edge e4 inspector: Branch label "Open · Leads", Condition has(vars.campaignRecord…, "Default branch (else)" unchecked, Type Conditional.
    • Canvas edge chips: they show the condition text ("if has(vars.campaig…"), not the authored label.

    So the Branches table lists config.conditions entries only, and a decision that routes on out-edge conditions (HotCRM's shape) shows no branches in its own inspector. The outcome cards this card adds should list the out-edge branches in that case, each with its target and condition. If they list only config.conditions, the new cards read "No items yet" too.

    Two things on the same canvas are filed elsewhere and are not this card's:


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (epic #12446 subtree)
    Session: session_01SJn1GEkrbbBQkhGC26wEDw
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12488-flow-node-cards
    Worktree: objectui-issue-12488 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/app-shell/src/views/metadata-admin/previews/flow-canvas-parts.tsx (NodeCard), packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx (an InspectorShell variant without its own header), inspectors/FlowObjectListField.tsx (the outcomes list), inspectors/FlowNodeConfigField.tsx (the decision branches mount only), previews/FlowPreview.tsx (the node aside's wiring under FlowPreviewChromeProvider only), studio-design/StudioDesignSurface.tsx (the AutomationsPillar inspector mount only), metadata-admin/i18n.ts (new keys only), new tests beside them, the screenshots docs/design/studio-redesign/after/12488-*.png, one .changeset/12488-*.md (stop on breach; explain in the report)
    Container & model: M, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)
    Clause-②: no
    Responsibility: n/a — not a defect card
    Thread-read: 6193761050
    Serial constraints cleared: objectui#12454, the card this one waits on, merged as PR objectui#12481 (8146afe). Open PRs at claim time on this surface: only PR objectui#12501 (objectui#12451), on the shared metadata-admin/i18n.ts, which is ordinary concurrency. objectui#12456 (AccessPillar) and objectui#12455 (InterfacesPillar) are in other regions of StudioDesignSurface.tsx. objectui#12499 (the flow checker's root list, filed, not dispatched) is in inspectors/flow-ref-check.ts, which is outside this surface. objectui#12453 follows on AutomationsPillar.

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12488,
    "status": "done",
    "branch": "claude/issue-12488-flow-node-cards",
    "pr": null,
    "session": "session_01Re8dJAwLn4Cd5jvAikubnw (dev cloud session; dispatched by seat domain:ui#4, session_01SJn1GEkrbbBQkhGC26wEDw)",
    "premise_still_valid": true,
    "summary": "Three parts, pushed as cb887d5 (base 1c3b069, origin/main merged in at cb887d5 with no conflicts). (1) NodeCard now looks the same in the Studio canvas and the metadata-admin designer: 8 px radius, a 28 px tile coloured by kind (start emerald, screen pink, record blue, decision amber, loop violet, end muted grey), the kind shown by its display name in the designer locale with the raw type on hover, and a primary border with a 3 px ring when selected. Layout and edges are unchanged. (2) The Studio Automations aside now has one header instead of two. A new InspectorShellHostProvider in _shared.tsx tells InspectorShell not to draw its own header, and the aside header reads 'Node · Decision', then the node's label, then close (flowSelectionHeading in FlowPreview.tsx). The narrow-row drawer and the designer are unchanged. (3) A top-level decision's Branches now render as cards: target in the header, Label and Expression below (Expression as a mono textarea), and a dashed 'Add branch' button. Edits go through the same FlowObjectListField commit, then applyDecisionBranches, as before. When conditions is empty (HotCRM's edge-routed shape), the out-edges show as read-only cards with target and condition. Under the Studio host each card has an 'Edit connection' link that opens that edge's inspector. All four PM mechanism assumptions held. The contract file and this dispatch disagree on writes: os-dev.md says open the draft PR and post the os-dev-report comment, while the dispatch's degraded channel says I may not. I followed the dispatch: git push only, PR body below. Housekeeping: the shared checkout /home/user/objectui held a local branch with this name and no commits of its own. I detached it at 1c3b069 and deleted that branch before creating the worktree. The worktree ../objectui-issue-12488 is kept for patch rounds, because the PR is not open yet.",
    "tests": "Pins at cb887d5 (the merged head): pnpm exec vitest run --maxWorkers=4 packages/app-shell/src/views/studio-design/ → Test Files 180 passed (180), Tests 1327 passed (1327). This covers every AutomationsPillar.* (tabs-12454 included), StudioDesignSurface.flowCanvasChip-11665, automationsReadOnly*, and the new AutomationsPillar.nodeAside-12488. Also at cb887d5: the six metadata-admin top-level i18n tests, machineLocaleCensus-9909, FlowObjectListField.branchCards-12488, flow-canvas-parts.nodeCard-12488 and flow-canvas-seeds.saveErrors-10948 → Test Files 10 passed (10), Tests 79 passed (79). Pre-merge at 9946d80: metadata-admin/inspectors/ → 162 passed (162), 2186 passed and 1 skipped; metadata-admin/previews/ → 126 passed (126), 1525 passed; the other 207 metadata-admin tests plus the census → 208 passed (208), 1958 passed. The merge changed no inspectors/ or previews/ file; in metadata-admin it changed only i18n.ts (gates and i18n tests re-run on cb887d5) and two Access-pillar files from main. One existing test changed: flow-canvas-seeds.saveErrors-10948 now clicks 'Add branch' (it clicked 'Add item') because the decision's add button is renamed. Ablation: 10 mutations by on-disk anchor replace, each anchor hit once (the first A1 attempt hit twice and was refused, then rerun with a unique anchor). Each was restored with git checkout HEAD -- path, the HEAD blob hash matched, and git diff HEAD was 0 bytes. All went red: A1 tile h-7 w-7 changed to h-9 (nodeCard, 1 failed); A1b decision tile tint back to the old /10 ring form (nodeCard, 1 failed); A2 kind shown as raw type (nodeCard, 2 failed); A3 old selected ring (nodeCard, 1 failed); A4 InspectorShell host branch disabled (branchCards plus nodeAside, 2 failed); A5 edge branches not read (branchCards plus nodeAside, 5 failed); A6 branches variant not passed at the mount (branchCards plus saveErrors-10948, 3 failed); A7 add label back to list.add (saveErrors-10948, 1 failed); A8 aside eyebrow fixed at 'Configuration' (nodeAside, 2 failed); A9 no host provider at the Studio mount (nodeAside, 2 failed); A10 expression not multiline (branchCards, 1 failed). No dist build was needed: the tests import src through relative paths.",
    "gates": "Each exit code captured before any pipe, at cb887d5 unless noted. pnpm --filter @object-ui/app-shell run type-check → 0 (the script echoed tsc --noEmit && tsc -p tsconfig.test.json; deps closure built first under os-verify-lock, VERDICT command-exit 0). pnpm --filter @object-ui/app-shell run build → 0, dist completeness 1100 files (at 9946d80; the merge brought only main's own source). eslint --no-inline-config on the 11 touched .ts/.tsx files → exit 0, 0 errors, 38 warnings. Against the base, per file: FlowObjectListField 3→5 (one react-refresh/only-export-components for decisionEdgeBranches, and a second instance of the existing react-hooks/refs warning from the new rows.map); _shared 7→8 and FlowPreview 1→2 (react-refresh/only-export-components for the new exports); flow-canvas-parts 8→8; StudioDesignSurface 15→15. pnpm check:i18n-keys 0 · pnpm check:i18n-drift 0 · pnpm check:i18n-designer-parity 0 · pnpm check:new-line-citations 0 (VERDICT 0 new citations) · node scripts/check-changeset-presence.mjs 0 · node scripts/check-changeset-no-major.mjs 0 (at 9946d80). No new Intl.* call, and machineLocaleCensus-9909 is green. objectui has no scripts/pm/dispatch-gates.mjs, so I derived the local set by hand from the dispatch's list; no --ran reconciliation is available. CI families: no PR exists yet, so there are no check-runs to cite; NOT MEASURED: CI, reason: the PM opens the PR.",
    "mcp_calls": "5 GitHub MCP reads, 0 writes: issue_read get #12488, issue_read get_comments #12488, issue_read get #12446, issue_read get_comments #12446, pull_request_read get #12481. Also 2 claude-code-remote add_repo calls (read access; both answered read_available, nothing attached) and 2 ToolSearch schema loads.",
    "api_writes": "0 REST writes. The only remote writes were git pushes to objectui branch claude/issue-12488-flow-node-cards (empty branch, 419f2d8, then cb887d5). Local-only: one PUT /api/v1/meta/flow/campaign_enrollment_copy to my own throwaway objectstack dev --fresh HotCRM backend on :4412, to get a writable flow for the designer screenshot. That database was deleted when the process stopped.",
    "open_questions": [
    {
    "question": "Editing a decision that routes on its out-edges (HotCRM's shape, empty config.conditions). The cards now show each out-edge read-only (target and condition) with an 'Edit connection' link to FlowEdgeInspector. Should the card edit the edge's condition inline?",
    "options": [
    "A — keep read-only cards with the link (shipped). Edges stay the only store and FlowEdgeInspector stays the only editor. Work: none.",
    "B — inline-edit edge.condition from the node inspector. Needs a new write path in FlowNodeInspector.setField that patches draft.edges directly, outside this claim's file surface, and a second editor for the same key. Work: one small card.",
    "C — turn the out-edges into config.conditions on first edit. This changes the stored shape, and the spec refuses mode beside a non-empty conditions list, so HotCRM's mode-carrying decisions would break. Not recommended."
    ],
    "recommendation": "A. Actual need: the measured gap was 'the inspector shows no branches', and A closes it; nobody has asked to edit edge guards from the node. Long-term soundness: one store and one editor per key, so nothing to keep in sync. Hard for AI to get wrong: no second write path that could drift from FlowEdgeInspector's isDefault/condition rules. Startup scope: adds nothing."
    },
    {
    "question": "The artboard's decision kind line reads 'Decision · 2 outcomes'. The count comes from nodeSummary in previews/FlowCanvas.tsx (outside the claim), which counts config.conditions only, so an edge-routed decision shows 'Decision' alone. Make it a follow-up?",
    "options": [
    "A — record as a deviation (done).",
    "B — follow-up card: nodeSummary counts plain out-edges when conditions is empty, reusing decisionEdgeBranches."
    ],
    "recommendation": "A for this card. B only if the maintainer wants the count, since nothing else asks for it today."
    },
    {
    "question": "Where the NodeCard styling applies (decided here by the four axes; recorded, not blocking).",
    "options": [
    "A — both the Studio canvas and the metadata-admin designer (shipped; the designer shot shows it).",
    "B — Studio only, through FlowPreviewChromeProvider."
    ],
    "recommendation": "A. One renderer, one look, as Decision D's ruling and #12481's canvas restyle already did. No Studio-only fork of a shared component, and less code: no context gating. Nothing in the end-user runtime renders NodeCard."
    }
    ],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — the aside's eyebrow and title come from flowSelectionHeading, which repeats the title rule from FlowNodeInspector and FlowEdgeInspector (node label or id; selection label, else source → target). Those two files are outside the claim, so the rule could not live in one place. If either inspector's title changes, the Studio header follows only after a matching edit."
    ],
    "deviations": [
    "Dispatch vs contract: os-dev.md requires opening the draft PR and posting the os-dev-report comment. The dispatch's degraded channel forbids both, because the relay returns 403. I followed the dispatch.",
    "Commit trailers: I used the model-free pair (Claude-Session plus Co-authored-by: Claude), per objectui AGENTS.md. The harness reminder's model-named Co-Authored-By yields to that rule.",
    "flowSelectionHeading is a new export in FlowPreview.tsx, beside FlowPreviewChromeProvider. I count it as the node aside's wiring the claim names there; it is the one helper the Studio mount reads.",
    "One existing test updated in previews/ (flow-canvas-seeds.saveErrors-10948), ablated (A7).",
    "Artboard deviations are listed in pr_body_draft."
    ],
    "files_changed": [
    ".changeset/12488-flow-node-cards.md",
    "docs/design/studio-redesign/after/12488-automations-decision-light.png",
    "docs/design/studio-redesign/after/12488-automations-decision-dark.png",
    "docs/design/studio-redesign/after/12488-metadata-admin-flow-light.png",
    "packages/app-shell/src/views/metadata-admin/i18n.ts (4 new keys, en and zh: engine.inspector.flowNode.branch.add / fromEdges / openEdge / noCondition)",
    "packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx",
    "packages/app-shell/src/views/metadata-admin/inspectors/FlowObjectListField.tsx",
    "packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeConfigField.tsx (objectList case only)",
    "packages/app-shell/src/views/metadata-admin/previews/flow-canvas-parts.tsx",
    "packages/app-shell/src/views/metadata-admin/previews/FlowPreview.tsx",
    "packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (imports plus the AutomationsPillar aside mount only)",
    "tests: inspectors/FlowObjectListField.branchCards-12488.test.tsx (new), previews/flow-canvas-parts.nodeCard-12488.test.tsx (new), studio-design/AutomationsPillar.nodeAside-12488.test.tsx (new), previews/flow-canvas-seeds.saveErrors-10948.test.tsx (updated)"
    ],
    "line_budget": "n/a",
    "pr_body_draft": "(elided by the seat: opened verbatim as the body of PR #12543, read back identical)"
    }

    Relayed verbatim by seat domain:ui#4 (session session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel: the session's relay dispatch was refused 403. The seat opened the draft PR #12543 from pr_body_draft, with assignee os-sales, and wrote its title (the report carries none). "pr": null above is the session's own value at report time.


    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:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions