## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task composer is where operators direct running agents. > - Operators need to stop work without leaving the conversation. > - Existing pause controls already hold task trees and interrupt both runner types. > - This pull request connects the composer to those controls and removes repeated feedback. > - Operators can pause work quickly and still queue messages while agents run. ## Linked Issues or Issue Description **What existing behavior does this improve?** Task pause, resume, and cancellation in the task page and composer. **Current behavior** The empty composer cannot stop a running task. Task controls require extra confirmation and reason text. Pause can show several notifications for the task already on screen. **Proposed behavior** Show Stop while this task runs and the composer is empty. Text or attachments switch it to Send. Stop and the menu use the same manual pause hold. Parent pauses include descendants. Keep task cancellation in the menu with a compact confirmation. Show one quiet pause row and gray cancelled-run details. **Reason and benefit** Operators can interrupt execution with one click. Drafts and queued messages keep their existing behavior. The UI waits for actual termination, including native cancellation acknowledgment. **Breaking changes** No endpoint, schema, or task-status change. Pause no longer asks for confirmation or a reason. Resume now honors the existing wake-agents option. Task notifications are suppressed for the task and subtree currently in view. Related UI work: #8228 changes navigation and composer shortcuts. This PR covers execution controls. No duplicate Stop-button PR was found. The change improves existing controls and does not duplicate a roadmap milestone. ## What Changed - Add Stop, pending feedback, duplicate-click protection, and inline errors to the composer. - Share the pause mutation across the composer, active-run controls, and menu. - Poll affected runs after a pause request. Require native cancellation acknowledgment. - Remove pause confirmation and shared reason fields. Reduce cancel confirmation to its task count and actions. - Honor wake-agents for executable tasks only. Preserve the pause when recovery review is needed; show partial wake failures inline. - Preserve explicit legacy reconciliation decisions while their continuation waits for dispatch. - Suppress notifications for visible task trees. Use quiet pause and cancellation feedback. - Add interactive stories using production controls and native/legacy end-to-end tests. ## Verification - User reviewed the running feature and revised Storybooks in the browser. - Rebased focused checks passed: 295 original targeted tests, 161 updated route/page/notification/status tests, and 26 recovery integration tests. - Both isolated runner journeys pass on the final revision (1.7 minutes). Coverage includes queueing, parent and child interruption, persisted holds, no automatic continuation, reconciled resume, cancellation, terminal exclusions, and no Stop toast. - Native coverage uses real runnerd with a deterministic provider fixture. Legacy coverage checks actual process termination. Live hosted-provider execution was not tested. - Repository typecheck and build, Storybook build, and token gates passed after rebase. The final server typecheck/build also passed. - The broad local run completed its general-server stage with 7,219 passing tests, 48 skipped, and two failures from cached pre-fix source and a stale native provider fixture. Both failed tests pass in fresh final-head reruns after rebuilding the fixture; the script did not continue to its later local stages. CI runs all test groups on the final revision. - Final revision: all 31 applicable CI checks passed; Storybook visual regression was skipped by its workflow conditions. Greptile: 5/5, zero unresolved comments. - Review `Tasks / Execution Controls` in Storybook. Type and clear a draft, stop a run, expand cancellation details, and test the menu on desktop and mobile. ## Risks - Stop pauses descendants for a parent task. This is the existing pause contract. - A held task can remain active if interruption fails. The UI shows an error instead of claiming termination. - Resume can start multiple assignees when wake-agents is selected. Backlog, blocked, and terminal tasks stay excluded. Existing execution reconciliation remains mandatory where required; Resume never invents action-outcome evidence. - Notification suppression uses the visible task and cached subtree. Notifications for unrelated work remain enabled. ## Model Used OpenAI GPT-6 through Codex. The exact runtime snapshot and context-window limit are not exposed in this session. Used reasoning, tool calls, code execution, and browser inspection. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
9.9 KiB
Paperclip Design Principles
Status: v0.3 — anchor document for design-language simplification. Governs structure, not brand. Brand values (color, type, iconography) are intentionally unspecified: they are being redesigned and will land as token values only. Nothing in ui/ may hardcode them. Spacing/radius scales are likewise TBD pending the token audit (see Principle 3).
Changes from v0.2: token layer location corrected to the repo's real source (ui/src/index.css); existing token tiers inventoried; snapshot-coverage scope bounded for Run 1; the issue→task copy rename moved out of the zero-visual-change run.
What this document is for
Agents and humans modifying ui/ treat this file as the source of truth for design decisions. Storybook is the verification surface — it documents the system; it does not define it. If a change conflicts with this document, change this document first (with review) or change the code.
Product stance
Paperclip is an operational control plane: org charts, tasks, heartbeat runs, budgets, approvals, audit logs. The user is an operator scanning state and making decisions. Every screen should answer, in order: what is happening, does it need me, what do I do about it. Density in service of scanning beats whitespace in service of aesthetics — but density comes from information, never from chrome.
The token layer (where visual values live)
The single token source is ui/src/index.css (Tailwind v4; there is no tailwind config file — tokens are CSS custom properties consumed via @theme). Do NOT create a parallel token source such as ui/src/tokens/ — that would produce two sources of truth. If index.css grows unwieldy, extracted values may live in a tokens.css imported by index.css so the pipeline still has one root.
Tailwind v4 gotcha: @theme inline bakes literal values at build time. Any token that must be runtime-tunable (theme editor, dark mode overrides) must be defined in a NON-inline block.
Existing tiers already in index.css (~80+ tokens) — extraction maps to these on exact value match before minting anything new:
- Semantic tier — shadcn core set:
--background,--foreground,--card,--primary,--secondary,--muted,--accent,--destructive,--border,--input,--ring,--sidebar-*,--chart-1..5(OKLCH, light/dark overrides). - Brand tier — agent gradients
--agent-1a/1b..10a/10b(fixed hex) and status hues--status-task-*/--status-agent-*(WCAG-tuned; see inline comments). - Domain tier — match-chip tokens
--chip-match-*, annotation highlights--paperclip-doc-annotation-highlight-*, plus motion/typography tokens.
Principles
- One way to say each thing. One component per job. One Button, one Card, one Badge, one Table, one EmptyState. Variants are props, not new components. Before creating a component, prove no existing one covers the job.
- Tokens are the only source of visual values. All color, spacing, radius, type size/weight, shadow, and motion values come from the token layer. No hex, no raw px, no ad-hoc Tailwind arbitrary values (
p-[13px]) in components. If a needed value doesn't exist, add a token — don't inline it. Tailwind palette classes (bg-red-500,text-zinc-400, etc.) ARE hardcoded values in spirit: they name a literal color, not a semantic role. They are in-scope debt scheduled for a dedicated future run (Run 4, cluster-by-cluster mapping to semantic tokens per doc/design/DECISION-SHEET.md B2) and are not currently gated by check-token-gates. Exception (doc/design/DECISION-SHEET.md B1 user ruling): first-party intentional one-off decoration on demo/UX-lab surfaces stays inline and allowlisted rather than minted as singleton tokens. - Spacing routes through tokens; the scale comes later. During simplification, extract every spacing and radius value verbatim into tokens — do not normalize, round, or invent a scale. The final scale is a design decision made by a human after reviewing the token audit. Structural rules apply now: vertical rhythm within a container uses one gap value, not per-element margins, and siblings never carry both margin and gap.
- Hierarchy through structure, not decoration. Prefer position, size, and weight over borders, backgrounds, and dividers. Every border, divider, and background fill must justify itself; when in doubt, remove it. A screen should survive the removal of one visual layer.
- Status is systematic. States like running / paused / blocked / awaiting-approval / over-budget map to a single semantic status token set used identically everywhere (badge, row, chart, log). An operator learns the vocabulary once.
- Machine values look machine-made. IDs, costs, token counts, timestamps, and log output use the monospace token and consistent formatting helpers. Never format these ad hoc per screen.
- Words are part of the system. One name per concept across the entire UI — the canonical term is task (never issue or ticket in copy, labels, or empty states). Buttons name the action ("Approve hire," not "Submit"). Errors say what happened and what to do. Empty states say what to do first. Note: enforcing the task rename is a visible change and is explicitly OUT of the zero-visual-change extraction run; it happens in its own follow-up run.
- Agent-modifiable by design. The system must be changeable via instructions: single token source, lint rules that enforce it, and this document kept current. A correct change should be expressible as "edit tokens + run checks," not "visit 40 files."
Contextual feedback
Do not show a toast for task or run state already visible on the current screen. This includes descendant runs represented by the open subtree. Show local action results in place; keep failures actionable inline. Notifications for other work remain useful. Expected cancellation is neutral gray, not an error. A paused subtree needs only “Subtree is paused.” and “Resume subtree.”
Enforcement (what "compliant" means for the extraction run)
- Zero visual change is proven, not promised: Storybook visual snapshots are baselined before any refactor, and all snapshots match baseline after it. A change that alters rendered output must be intentional and human-approved.
- Baseline scope for Run 1: the shared primitives in
ui/src/components/ui/(each gets a story if missing — there are only ~24) plus the ~46 existing stories underui/storybook/stories/. Do NOT attempt a story for every feature component (~277) in this run; full coverage is a later effort. - Mechanical rewrites (value extraction, renames) are done via committed codemod scripts in
scripts/, not hand-edits — reviewable once, repeatable forever. - Token layer is the single source (
ui/src/index.css, per above) consumed via CSS variables / Tailwind theme — never values copied into components. - Lint/grep gates pass: zero hardcoded hex values, zero arbitrary spacing values, zero raw font-size declarations in
ui/src/components/**andui/src/pages/**outside the token layer and a documented allowlist (third-party overrides, intentional opt-outs commented inline). pnpm build,pnpm typecheck, andpnpm build-storybookpass.- AGENTS.md links here and states the token-only rule.
Aspirational (NOT gating this run): no duplicate components; every component has exactly one story covering its variants; all UI copy says "task".
Out of scope (do not do during simplification)
No visual redesign, no new colors or typefaces, no layout restructuring, no new dependencies beyond snapshot tooling, no component consolidation/merges (audit + recommend only), no copy renames, no changes to server code or app logic. Simplification means fewer parts, same product.
Prior art (read before auditing)
See doc/design/PRIOR-ART.md — a previous audit pass (PAP-280/283/284, on the PAP-282-playground branch, NOT on master) found that of ~220 hardcoded drift sites, only 6 were exact-value-mappable to existing tokens; expect the verbatim extraction to mint many new tokens that the human scale-collapse step later merges. It also drafted usage rules (radius tiers, CTA tiers, named type styles) that are good candidates for the post-audit scale decision.
How-to guide for day-to-day UI changes: see doc/design/CHANGING-THE-UI.md.
Motion tokens (Task Chat Redesign)
The redesigned task thread (flag enableTaskChatRedesign) is the first surface to
tokenize motion. Principles — reasoning only; values live in ui/src/index.css:
- One home, and it is
:root, not@theme inline.@theme inlinebakes literals at build time, so a value placed there cannot be moved at runtime. The dev tweak panel tunes motion by writing CSS custom properties live, so every motion token must resolve at runtime — hence:root. - Two tiers. Primitives (
--motion-duration-*,--motion-ease-*) express the app's baseline motion feel; state/component-scoped tokens (--motion-<state>-*) reference the primitives so the whole thread retunes from a few knobs. Scoped tokens exist so the tweak panel can group controls by the state they affect. - Reuse the house curves. New easing defaults point at the two curves already used across the app rather than inventing a third feel.
- No hardcoded timing in components. Durations, easings, delays, and staggers used by
the redesigned thread must reference these tokens; a check script rejects raw
ms/cubic-beziervalues outsideui/src/index.css. This discipline is what makes the tweak panel structurally possible. - Values are placeholders. The committed numbers are sensible starting points, tuned live by a human and pasted back from the tweak panel's export — never treated as final during the baseline build.
- Reduced motion is honored at the token layer. A
prefers-reduced-motion: reduceblock collapses the duration/stagger tokens to zero, cascading to every scoped token, in addition to each animation's own component-level guard.