mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-26 18:31:34 +01:00
docs(runner): add approved status/attention operator UX reference
Extract the board-approved PAP-16712 UX design into spec/paperclip-runner-status-attention-ux.md: layered outcome vocabulary (turn/run/claim/status), OutcomeChipRow / StatusDecisionCard / AttentionRequestCard / FinalizationErrorCard, five desktop board flows, the v1 reason-code copy table, and token-system notes. Core spec changes are pointers only: 18.3 names the reference doc as the normative Phase 4 presentation, and the 18.11 UI-surfaces row now requires the Phase 4 read models to carry every field that design renders. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
35e1f4cd8c
commit
b3699eb78f
2 files changed
+201
-1
No files matched your search
@@ -3377,6 +3377,12 @@ Issue status is a server-owned projection of validated work facts, not an agent
|
||||
| Agent/model | Work-report authority | Reported disposition, completion claim, evidence, remaining work, blocker and attention candidates | Authoritative issue transition, governed approval, cross-company routing |
|
||||
| Status arbiter | Organizational status authority | Legal issue transition/no-op and atomic side effects | Rewriting the original agent report or fabricating evidence |
|
||||
|
||||
The approved operator-facing presentation of these four authorities — the
|
||||
layered outcome vocabulary, components, desktop board flows, reason-code copy,
|
||||
and token mapping — is maintained in
|
||||
[`paperclip-runner-status-attention-ux.md`](./paperclip-runner-status-attention-ux.md)
|
||||
(PAP-16712) and is normative for Phase 4 UI work.
|
||||
|
||||
The arbiter consumes a durable assessment rather than raw prose:
|
||||
|
||||
```ts
|
||||
@@ -4178,7 +4184,7 @@ Implementation-ready file map:
|
||||
| Heartbeat integration | In `server/src/services/heartbeat.ts`, replace the native-mode exit-code outcome branch and the later sequence of independent issue liveness handlers with the coordinator. Keep usage/cost/session/runtime diagnostics and the legacy branch intact. Workspace finalization completes before native run success and arbitration. |
|
||||
| Routes/OpenAPI | Add company-authorized read routes in `server/src/routes/issues.ts` and `server/src/routes/agents.ts`; document them in `server/src/routes/openapi.ts`. Native ingestion stays on the runner transport/internal service boundary. |
|
||||
| UI API | Add read types/fetchers in `ui/src/api/issues.ts` and `ui/src/api/heartbeats.ts`; invalidate issue, run, assessment, and decision queries from the existing live-update provider. |
|
||||
| UI surfaces | Phase 4 renders run outcome separately from issue status, the agent claim separately from the arbiter decision, reason/evidence classification, supersession history, finalization failure, and the concrete current liveness owner/path. This phase supplies data contracts only; it does not pre-empt the approved UX task. |
|
||||
| UI surfaces | Phase 4 renders run outcome separately from issue status, the agent claim separately from the arbiter decision, reason/evidence classification, supersession history, finalization failure, and the concrete current liveness owner/path. This phase supplies data contracts only; the approved operator UX for these surfaces is [`paperclip-runner-status-attention-ux.md`](./paperclip-runner-status-attention-ux.md), and the read models above MUST carry every field that design renders (turn/run/claim/decision layers, criterion assessments, side-effect links, attention owner/authority/scope/attempts/expiry, resume consequence, supersession chain). |
|
||||
| CLI/MCP | Expose read-only finalization/decision inspection if operator parity requires it. Do not expose status-decision creation or arbitrary replay inputs. |
|
||||
|
||||
Migration and rollout sequence:
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
# Status and Attention Operator UX Reference
|
||||
|
||||
**Document status:** Approved Phase 4 UX input (PAP-16712, approved by the board 2026-08-07)<br>
|
||||
**Relationship to the spike specification:** [`paperclip-native-runner-spike-spec.md`](./paperclip-native-runner-spike-spec.md) §18.3–§18.11 remains the normative status-authority, arbitration, and attention-resolution contract. This document is the normative operator-facing presentation of that contract: vocabulary, components, board flows, copy, and token mapping. Phase 4 data contracts (spec §18.11) MUST satisfy the read requirements here; UI implementation tasks consume this document directly.<br>
|
||||
**Scope:** desktop board (1440×900) — task page, issues board, attention inbox. Mobile is deferred by the source ticket.<br>
|
||||
**Mockups:** six flow screenshots and one inspectable single-file HTML mockup (per-flow rendering via `?only=a|b|c|d|e|board`) are attached to PAP-16712.
|
||||
|
||||
---
|
||||
|
||||
## 1. The layered-outcome model (vocabulary system)
|
||||
|
||||
The contract yields four independent facts. A single status badge collapses them into exactly the "agent succeeded" lie the parent issue names. Phase 4 renders them as four distinct visual layers with a strict vocabulary, so a stranger can tell in two seconds *what happened to the process* versus *what Paperclip decided about the work* (Mental Models; Nielsen #1 visibility of system status; #2 match to the real world).
|
||||
|
||||
| Layer | Source of truth (spec §18.3 authority table) | Values | Vocabulary rule | Visual treatment |
|
||||
|---|---|---|---|---|
|
||||
| **Turn outcome** | Harness driver | completed / failed / interrupted / cancelled | "Turn completed" — always the noun *turn* | Tiny mono-caption chip on the run row; never colored green |
|
||||
| **Run outcome** | Runner / finalizer | succeeded / failed / cancelled | "Run succeeded" — always the noun *run* | Small neutral chip on the run row (existing `IssueRunLedger` liveness copy) |
|
||||
| **Agent report** | Model claim (`ReportedWorkDisposition`) | done / blocked / needs_review / yielded | "reported done", "claimed complete" — always claim verbs, never bare "done" | **Claim chip**: outlined, `border-dashed`, `text-muted-foreground`, quote glyph. Never status-colored fill |
|
||||
| **Issue status** | Status arbiter only (`StatusDecision`) | backlog … done / cancelled | The only element allowed to say "Done" plainly | Existing `StatusBadge` / `StatusIcon`, unchanged — remains the single authoritative badge |
|
||||
|
||||
Copy laws (enforceable in review):
|
||||
|
||||
1. Never compose "agent" + a success verb. Banned: "Agent succeeded", "Agent completed the task". Allowed: "Run succeeded", "UXDesigner reported done".
|
||||
2. A claim is always attributed and past-tense: "*UXDesigner reported done · 12m ago*".
|
||||
3. The arbiter is always the subject of status sentences: "*Paperclip kept In Progress — evidence incomplete*".
|
||||
4. Filled status colors (green done, violet review, red blocked) are reserved for arbiter output. Claims and process outcomes use neutral/outline treatments. This makes disagreement visible pre-attentively (Selective Attention; color-independence: shape differs too — dashed vs solid).
|
||||
|
||||
---
|
||||
|
||||
## 2. Components
|
||||
|
||||
Three new composites plus surgical extensions to existing ones. All live in `ui/src/components/`, get CVA variants, and must be added to `/design-guide`.
|
||||
|
||||
### 2.1 `OutcomeChipRow` (new)
|
||||
|
||||
One row of the three non-authoritative layers, rendered on every finalized native run in `IssueRunLedger` and in the Live Run Console terminal card (spec §23.5 "final result visually distinct"):
|
||||
|
||||
```
|
||||
Turn completed · Run succeeded · “reported done” [decision →]
|
||||
```
|
||||
|
||||
- Turn/run chips: `text-xs text-muted-foreground`, plain text with mid-dots — they are metadata, not signals (Cognitive Load: don't chip-ify everything).
|
||||
- Claim chip: `rounded-full border border-dashed border-border px-2 text-xs text-muted-foreground`, leading `MessageSquareQuote` (14px). Tooltip: "What the agent reported. Paperclip decides issue status separately."
|
||||
- Run `failed` / turn `failed` use `text-red-400` text (not a red fill) — a failed run with preserved status must not look like a blocked issue.
|
||||
|
||||
### 2.2 `StatusDecisionCard` (new — the arbiter explanation)
|
||||
|
||||
Renders one `StatusDecision` plus its `WorkAssessment` summary from `GET /api/issues/:id/status-decisions` (spec §18.11). Placement: run ledger directly under the run that produced it; the latest decision also appears as a one-line property in the task properties panel ("Decision · Kept In Progress · 12m").
|
||||
|
||||
**Collapsed row (default, Progressive Disclosure):**
|
||||
|
||||
```
|
||||
[glyph] Kept In Progress — completion claim not accepted: evidence incomplete
|
||||
by Paperclip arbiter · triggered by run finalization · 12m ago [∨]
|
||||
```
|
||||
|
||||
- Glyph encodes the decision class (see §5 tone map). "Kept/Moved to {Status}" is the verb: `transitionApplied ? "Moved to" : "Kept"`.
|
||||
- Reason code renders as plain-language copy (table in §4); the raw code is available in the expanded audit footer.
|
||||
|
||||
**Expanded body, in order (Inverted Pyramid):**
|
||||
|
||||
1. **Claim vs decision strip** — two cells side by side: left `“UXDesigner reported done”` (claim chip style), right authoritative `StatusBadge` + reason sentence. Only rendered when they disagree; this is the Von Restorff element of the card.
|
||||
2. **Criterion table** — one row per `criterionAssessments` entry: criterion description · claim (`satisfied`/`unknown`) · arbiter classification (`accepted` green check / `missing` amber dash / `rejected` red x / `unverifiable` slate ?) · reason code · evidence links. Reuses Cost-table styling (`text-xs`, `bg-accent/20` header).
|
||||
3. **What happens next** — side-effects list as actionable links, one per `sideEffects` entry: "Queued continuation for UXDesigner", "Created review interaction → open", "Bound blocker PAP-XXXX". This is the liveness answer to "who owns the next move" (Goal-Gradient; Nielsen #1).
|
||||
4. **Audit footer** — `text-xs font-mono text-muted-foreground`: reason code, decision id, policy version, contract revision, supersession chain ("superseded by decision …" links both directions).
|
||||
|
||||
### 2.3 `AttentionRequestCard` (new — canonical attention record)
|
||||
|
||||
Renders one `CanonicalAttentionRequest` (spec §18.3.1). Placement: task page thread (where interaction cards render today, as an `IssueThreadInteractionCard` sibling) and, for board-routed pending requests only, the attention inbox as a new `AttentionQueueRow` source.
|
||||
|
||||
Anatomy top-to-bottom:
|
||||
|
||||
1. **Header:** classification icon + request summary/question (`text-sm font-medium`) + state chip (`pending / routed / resolved / expired / superseded / rejected / exhausted`) + urgency flag (`high` = amber dot, never a red fill).
|
||||
2. **Meta chip row** (the required operator fields, each a labeled chip, `text-xs`):
|
||||
- **Owner** — selected resolver: avatar + "Waiting on: Dotta (board)" / "Routed to: CodexCoder". Owner is always a named person/agent/system, never "someone".
|
||||
- **Authority** — derived `minimumAuthority` in plain words: "needs a board decision", "needs governed approval". When the agent asked for more authority than policy derived, show the correction inline: `asked: human → resolved as: expertise` with tooltip "Paperclip routes by policy, not by the agent's request" (trust signal; prevents learned helplessness about spurious escalations).
|
||||
- **Scope** — "blocks this turn only" / "blocks one track" / "blocks the whole task". When narrowed, strike the claim: `~~task-wide~~ → this turn` with the alternate live track linked.
|
||||
- **Attempts** — "attempt 2 of 6" from `AttentionResolutionBudget` (spec §18.3.3); expandable route history (context ✓ → retry ✗ → agent…) so a human landing here sees escalation was earned, not first-resort.
|
||||
- **Expiry** — relative countdown "expires in 3h 40m"; switches to amber text under 25% remaining; the expired state renders the card inert with "expired unanswered · one fallback wake sent".
|
||||
3. **Response affordance** — only for `route: board` + state `pending/routed`: the existing typed interaction control (`AttentionInteractionResolver`) — answer / choose / approve / decline. All other routes render read-only.
|
||||
4. **Resume line (footer, always present):** the exact consequence of responding, generated from the response binding (spec §18.3.6): "*Answering wakes UXDesigner and resumes this turn*" / "*Answer recorded; UXDesigner wakes on its next scheduled turn*" / "*Recorded for audit only — this request was superseded*". Fitts/forgiveness: the operator knows what the button fires before pressing it.
|
||||
|
||||
### 2.4 Extensions to existing components
|
||||
|
||||
- **`IssueRunLedger`** — insert `OutcomeChipRow` + nested `StatusDecisionCard` per finalized native run. Legacy runs render exactly as today (Jakob's Law; spec §28.2 compatibility).
|
||||
- **`AttentionQueueRow`** — new source kind for board-routed canonical attention requests; reuse `DecisionTriageStrip` actions; suppressed duplicates and agent-routed requests never enter the queue (see flows C/D).
|
||||
- **Properties panel (`IssueDetail`)** — two added property rows: "Decision" (latest `StatusDecisionCard` collapsed line, click scrolls to card) and "Waiting on" (owner chip of the oldest pending board-routed attention/liveness path). No new panel.
|
||||
- **Issues board rows** — no new column. A single secondary chip slot after the title (existing `BlockedReasonChip` pattern) for exactly two conditions: `Needs you` (pending board-routed attention, violet) and `Reconciliation` (finalization error, cyan). Everything else stays on the task page (Miller / Choice Overload — the board stays scannable).
|
||||
- **`FinalizationErrorCard`** — small variant of `StatusDecisionCard` for `finalization_failed_claim_preserved` / `result_schema_rejected` / `attention_budget_exhausted`: cyan "recovery" tone; names the recovery owner and retry state from `GET /api/heartbeat-runs/:runId/finalization`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Desktop board flows
|
||||
|
||||
### Flow A — Completion disagreement (`completion_evidence_incomplete`)
|
||||
|
||||
> Run succeeded, agent reported done, arbiter kept In Progress and queued a continuation.
|
||||
|
||||
1. Board: issue stays in **In Progress** column — no movement, no false "Done" flicker (system honesty beats optimistic UI here).
|
||||
2. Task page: run row shows `Turn completed · Run succeeded · “reported done”`; directly beneath, collapsed decision card: **"Kept In Progress — completion claim not accepted: evidence incomplete"**.
|
||||
3. Expanding shows the claim-vs-decision strip, the criterion table (e.g. `tests pass` → claim satisfied → **missing**: no persisted test record), and "Queued continuation for UXDesigner".
|
||||
4. Operator actions: open evidence refs; add guidance comment; or use the normal board status override — copy on the override confirm: "Your change is recorded as a board decision and supersedes the arbiter's" (the override becomes a `board_user` trigger for a superseding decision; the old card shows the supersession chain).
|
||||
|
||||
**Anti-goal:** no red/error styling. Disagreement is a *normal governed outcome*, not a failure — amber "downgraded" tone only on the decision glyph.
|
||||
|
||||
### Flow B — Real human-needed interaction (`attention_requires_human_authority`)
|
||||
|
||||
> Subjective judgment routed to the board; issue in_progress with a response wake (or in_review for contract review).
|
||||
|
||||
1. Attention inbox: one `AttentionQueueRow` — "UXDesigner needs a decision on PAP-16712 · expires in 3h · attempt 1". Issues board shows the violet `Needs you` chip.
|
||||
2. Task page card shows the full anatomy: owner **you**, authority "needs a board decision", scope "blocks this turn only", attempts history proving context/agent routes were tried or skipped-by-classification, expiry, and the typed answer control.
|
||||
3. Resume line: "*Answering wakes UXDesigner and resumes this turn.*"
|
||||
4. On answer: card flips to `resolved` with the response pinned; a system marker "UXDesigner woken · continuation queued" appears; the `Needs you` chip clears. Peak-End: the operator sees their answer take effect without refreshing (spec §23.9 item 7).
|
||||
|
||||
### Flow C — Agent-routed resolution (`attention_routed_to_agent`)
|
||||
|
||||
> Agent asked for a human; resolver reclassified to expertise and delegated in-company.
|
||||
|
||||
1. **Not** in the attention inbox, no `Needs you` chip — humans are only interrupted for human-authority work (the parent issue's core ask).
|
||||
2. Task page renders the card read-only: authority chip shows the correction `asked: human → resolved as: expertise`; owner "Routed to: CodexCoder"; link to the delegated issue; resume line "*CodexCoder's answer resumes this track — no action needed from you.*"
|
||||
3. If delegation later exhausts, the card's attempt history grows and only then can a human route appear (budget rules, spec §18.3.3) — the UI never shows a human affordance on an agent-routed card.
|
||||
|
||||
### Flow D — Duplicate suppression (`attention_duplicate_suppressed`)
|
||||
|
||||
> Repeated/paraphrased request, same equivalence family.
|
||||
|
||||
1. Exactly **one** canonical card ever renders. Duplicates render as a one-line collapsed link row under it: "↳ asked again (reworded) · 8m ago · merged — no new notification", and increment the visible attempts/budget counter.
|
||||
2. The inbox row count does **not** increase; no re-notification (protects trust in the queue; alarm fatigue is the failure mode).
|
||||
3. The counter doubles as agent-quality telemetry the operator can cite ("this agent re-asked 3×").
|
||||
|
||||
### Flow E — Finalization / reconciliation errors (`finalization_failed_claim_preserved`, `result_schema_rejected`)
|
||||
|
||||
> Turn completed, then workspace/transport/finalization failed — or the report was invalid.
|
||||
|
||||
1. Board: issue status unchanged; cyan `Reconciliation` chip on the row.
|
||||
2. Task page run row: `Turn completed · Run failed` (run failure text in red-400) + `“reported done”` claim chip still visible — the work report is preserved and must not disappear (spec §18.5).
|
||||
3. `FinalizationErrorCard`: "**Finalization failed — completion claim preserved, not accepted.** Reconciliation scheduled · owner: Paperclip recovery · retry 1 of 3." For `result_schema_rejected`: "Agent report couldn't be read — status unchanged" + preserved raw payload link.
|
||||
4. Explicit anti-copy: never "Agent failed" (the agent may have done the work) and never auto-"Done" (the claim is unverified). This row is the clearest case for the four-layer vocabulary.
|
||||
|
||||
---
|
||||
|
||||
## 4. Reason-code copy table (complete v1 enum)
|
||||
|
||||
Plain-language, ≤ 70 chars, agent-name-free; raw code in the audit footer. (Plain Language / Recognition over recall.)
|
||||
|
||||
| Reason code | Operator copy | Tone |
|
||||
|---|---|---|
|
||||
| completion_contract_satisfied | All acceptance criteria verified | accepted |
|
||||
| completion_claim_policy_accepted | Claim accepted under low-risk policy | accepted |
|
||||
| completion_evidence_incomplete | Completion claim not accepted: evidence incomplete | downgraded |
|
||||
| completion_review_required | Completion needs review before Done | review |
|
||||
| governed_gate_pending | Waiting on a governed approval | review |
|
||||
| live_continuation_registered | More work queued — continuing | progress |
|
||||
| turn_waiting_other_track_live | One track waiting; another continues | progress |
|
||||
| task_wide_blocker_bound | Blocked task-wide — unblock owner assigned | blocked |
|
||||
| attention_resolved_from_context | Answered from existing context | progress |
|
||||
| attention_routed_to_agent | Routed to a qualified agent | progress |
|
||||
| attention_requires_human_authority | Needs a human decision | review |
|
||||
| attention_duplicate_suppressed | Repeat request merged — no new ask | preserved |
|
||||
| attention_budget_exhausted | Escalation budget exhausted — recovery recorded | recovery |
|
||||
| run_failed_partial_evidence_preserved | Run failed — partial work preserved | recovery |
|
||||
| finalization_failed_claim_preserved | Finalization failed — claim preserved, not accepted | recovery |
|
||||
| result_schema_rejected | Agent report couldn't be read — status unchanged | recovery |
|
||||
| cancellation_turn_only / _run_only | Turn/Run cancelled — task unaffected | preserved |
|
||||
| cancellation_issue_authorized | Task cancelled by authorized decision | blocked-tone gray |
|
||||
| prior_status_preserved_no_live_path | Status preserved — no live path could be created | recovery |
|
||||
| authorized_resume / dependency_resolved | Resumed by authorization / dependency resolved | progress |
|
||||
| decision_superseded_by_new_evidence | Superseded by newer evidence | preserved |
|
||||
|
||||
New reason codes added to the v1 enum after this document's approval MUST land with a row in this table before they can be emitted on a UI-visible surface.
|
||||
|
||||
---
|
||||
|
||||
## 5. Token-system notes
|
||||
|
||||
**No new raw values.** Everything maps to existing tokens; two small semantic proposals.
|
||||
|
||||
1. **Decision tone map** (CVA variant on `StatusDecisionCard` glyph/border) reuses the PAP-75 status palette + `BlockedReasonChip` variants: accepted → `--status-task-done` (#22c55e); review → `--status-task-in_review` (#7c3aed); progress → `--status-task-in_progress` (#2563eb); downgraded → amber-500 family (BlockedReasonChip `stalled`); recovery → cyan family (BlockedReasonChip `recovery_required`); preserved → slate family (`external_wait`); blocked → `--status-task-blocked` (#dc2626).
|
||||
2. **Claim treatment is a pattern, not a color:** `border border-dashed border-border rounded-full text-muted-foreground` + quote glyph. Document it as a named pattern ("claim chip") in `/design-guide` so it is reused for any future agent-asserted-but-unverified value. Color-independent by construction (dashed ≠ solid).
|
||||
3. **Proposed semantic tokens** (only if implementation finds repeated inline pairs): `--tone-recovery` → cyan-500-based bg/fg pair and `--tone-downgraded` → amber-500-based pair, defined like the existing `--status-*` vars with `.status-chip` color-mix derivation. If declined, components use the BlockedReasonChip literal classes, which already pass AA in both modes.
|
||||
4. Typography/spacing: entirely from the existing scale — card title `text-sm font-medium`, meta `text-xs text-muted-foreground`, audit `text-xs font-mono`, chips `rounded-full`, cards `rounded-lg border-border`, `space-y-1` property rows. Shadows ≤ `shadow-sm`.
|
||||
5. Accessibility: every tone pairs color with a distinct glyph (check/dash/x/?/arrow); countdown text never relies on color alone ("expires in 40m" text turns amber *and* gains a clock glyph); all chips ≥ 24px hit target when interactive; reduced-motion: no pulse on `Needs you`, static dot.
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 4 acceptance criteria (UX)
|
||||
|
||||
1. On any finalized native run, a stranger can answer separately: did the turn finish, did the run succeed, what did the agent claim, what did Paperclip decide — without opening a drawer (spec §18.11 consistency gate, UI bullet).
|
||||
2. The strings "agent succeeded/failed/completed" appear nowhere; claims are attributed and quoted.
|
||||
3. Every non-terminal decision card names its live path owner as a link.
|
||||
4. A board-routed attention card always shows owner, authority, scope, attempts, expiry, and the resume consequence before the answer control.
|
||||
5. Duplicates never add inbox rows; agent-routed requests never show human affordances.
|
||||
6. Finalization errors preserve and display the claim while keeping status unchanged.
|
||||
7. All five flows verified at 1440×900 against the mockups attached to PAP-16712.
|
||||
Reference in new issue
Block a user