Commit Graph
25 Commits
Author SHA1 Message Date
scotttongandClaude Fable 5 9445f4fbbe feat(ui): interstitial polish — hover only on gerund line, no leading icon, held line slides away after dwell
- Expand button moves inside TaskChatStatusPill wrapping only the status
  line (new onToggle prop); the interstitial row sits outside the hover/
  click target
- Drop the Responding icon prepending the interstitial text (lead spacer
  keeps column alignment)
- useHeldSelfTalk: a finished update now slides out of the 1lh viewport
  after --motion-interstitial-dwell (~4s) with no successor, instead of
  sitting until pushed out; same .tc-line-scroll transition, unmounts to
  the reserved empty slot

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:57:14 -07:00
scotttongandClaude Fable 5 04397ac526 fix(ui): insert comment-less settled turns chronologically at run start — assembleThreadItems replaces bottom-append of unanchored Worked rows
A terminal run with no UI-linked reply comment fell into the unanchored
bucket and out.push(...unanchored) pinned its "Worked · Ns · N tools" row
under every new user bubble. Unanchored turns now interleave into the
backbone at meta.startedAt ?? createdAt (Infinity → tail when no meta yet);
anchored turns and live-turn-last ordering unchanged. 5 regression tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:57:06 -07:00
scotttongandClaude Fable 5 eef40c0347 feat(ui): round-9 polish — reserved interstitial slot + Worked summary attached to the reply timestamp
The interstitial row above the gerund status line is now permanently
reserved while a turn is live: text slides in/out of the always-mounted
1lh slot, so the layout above never jumps as messages appear/disappear.

On settle, a turn directly following its own agent's reply bubble
attaches to it (attachSettledTurns, after coalesceSettledTurns): the
"✓ Worked · …" summary renders appended to the bubble's timestamp line
— "2:34 PM · ✓ Worked · 38s · 2 tools" — still expandable to the tool
history. Bubble timestamps are always visible (hover-only reveal
removed). Turns without a reply bubble keep the standalone folded row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:57:06 -07:00
scotttongandClaude Fable 5 354bb822e0 feat(ui): coalesce back-to-back settled agent turns into one "Worked" row — coalesceSettledTurns final pass + buildMergedTurnSummary (summed per-run durations, re-derived counts); user/interaction/live-turn boundaries preserved
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:55 -07:00
scotttongandClaude Fable 5 39e30b51ab feat(ui): interstitial updates on the live line — F10 line-scroll revived (LiveSelfTalkLine, .tc-line-scroll, --motion-line-scroll), ephemeral messages, settled turns slim to tool rows only (thinking rows removed, TaskChatThinkingBlock deleted)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:55 -07:00
scotttongandClaude Fable 5 ad1ed3cca3 feat(ui): mobile document-flow layout for chat-style task thread — thread renders via scroll={false} in the page scroll with window auto-follow, sticky safe-area composer, 16px mobile editor text; desktop bounded chain untouched
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:55 -07:00
scotttongandClaude Fable 5 b3c082735c feat(ui): ambient self-talk — narration signals via status-word shimmer + Responding icon swap, interstitial text fully suppressed; revert F10 line-scroll and settled nesting, retire P5 interleave
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:55 -07:00
scotttongandClaude Fable 5 7eadf663ad feat(ui): self-talk as a live element of the parent row — streaming text takes the line with 1lh line-scroll, finished self-talk nests like tool rows, settled turns follow
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:50 -07:00
scotttongandClaude Fable 5 2714832c3d feat(ui): task-chat de-emphasized self-talk — bubble-less muted narration with CSS typewriter entry
Mid-run agent messages are tagged interstitial by the transcript adapter and
render bubble-less/avatar-less in muted text; the run's posted reply keeps its
bubble. Settled history keeps the self-talk interleaved at its streamed
timestamp (deduped against posted comment bodies). New --motion-typewriter-*
tokens drive a CSS-only steps() clip reveal per streamed block, zeroed under
prefers-reduced-motion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:44 -07:00
scotttongandClaude Fable 5 76770ace54 feat(ui): task-chat live turn collapses to one expandable parent row owning all activity
While a run streams, the status line (whimsy/taxonomy gerund + elapsed +
tokens) is now the live turn's single visible row: TaskChatTurnItem gains
liveStatus, TaskChatTurn renders it as an expandable header (chevron, same
grammar as tool rows / the settled Worked line), and expanding nests the
chronological tool/thinking history via the existing .tc-turn-fold motion
with live append while open. On settle the header morphs in place into the
'Worked · …' summary, preserving expand state. Only tool/thinking/usage rows
nest (isNestableLiveChild); mid-run agent text, markers and interaction cards
stay in the thread. Retires the  liveTurnFoldedIds window + tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:37 -07:00
scotttongandClaude Fable 5 8bbef4177a feat(ui): task-chat composer swaps textarea for shared MarkdownEditor
Rich composer inside the v7 comp-bar chrome: @-mentions (wired through
TaskChatThread, previously dropped), data-driven /-command autocomplete via
EditorAutocompleteContext, rich lists, and inline pasted/dropped images at
the caret through the editor's image plugin -> issue attachments POST.
Non-image files keep the chip row; the round-13 object-URL thumbnail path
is superseded. Cmd/Ctrl+Enter posts natively; Shift+Tab mode-cycling is
captured on the wrapper ahead of Lexical. Motion-gate comment fix in
TaskChatStatusPill (doc said 100ms).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:56:14 -07:00
scotttongandClaude Fable 5 15eb30bb64 feat(ui): task-chat collapsed turn summary lands below the reply bubble
Settled run turns now anchor after the run's last comment instead of
before its first, so the folded "Worked · …" line appears where the
live "Running…" pill sat — the pill reads as being replaced by the
summary in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:54 -07:00
scotttongandClaude Fable 5 ced262a0da feat(ui): task-chat tool taxonomy — pill verbs/icons, Shift+Enter to send, baseline-aligned elapsed
- new shared tool-taxonomy.ts (family/icon/verb per raw ACP tool name,
  mcp__ prefix collapse) used by the status pill, chat tool rows, and the
  classic transcript's previously hardcoded Wrench sites
- deriveRunStatusLabel: tail tool_call now yields the taxonomy verb
  ("Searching", "Running a command") with "Tool · target" detail and
  the raw toolName so the pill shows the family icon beside the pulse dot
- live status line: label/elapsed/detail/tokens share one baseline row
- TaskChatComposer: Shift+Enter posts, plain Enter inserts a newline
- tests: tool-taxonomy unit tests, deriveRunStatusLabel/toolDisplayName
  tests, composer Enter-behavior tests updated

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:54 -07:00
scotttongandClaude Fable 5 67e0713a2a feat(ui): task-chat live status elapsed sits beside the label and ticks in tenths
The running/working status line now shows elapsed time directly to the
right of the label (was pushed to the far right edge) and renders it
with one decimal (24.3s) so the readout visibly moves. The pill ticks
itself every 100ms from the run's start timestamp (new startedAtMs on
TaskChatStatusItem), replacing the 1s whole-thread re-render tick in
TaskChatThread.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:49 -07:00
scotttongandClaude Fable 5 61aae371bd feat(ui): task-chat per-request mode chips — drop the header mode badge, tag each agent reply with the mode its request ran under
The header's Ask/Plan badge read as a task-global setting, but mode is a
per-request choice: with the redesign flag ON the badge is gone from the
issue header, and each agent reply's avatar row now shows the work mode
that was in effect when its run started, reconstructed from the activity
feed's workMode change history (issue.updated details + _previous).
Flag OFF is unchanged: the legacy header badge stays and the new
workModeChanges prop is ignored by IssueChatThread.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:49 -07:00
scotttongandClaude Fable 5 9a5f7e26e4 feat(ui): task-chat phase 2e — pane tabs in header bar, chat-only center, non-sticky header, plan CTA action bar
Flag-gated (enableTaskChatRedesign), flag OFF byte-identical:

- Properties | Plan | Artifacts tab strip portals from IssueProperties into
  the pane's header bar (new slot left of the maximize/close controls).
- Center Chat/Activity/Related-work tab strip hidden — the thread is the page;
  deep links that switched tabs stay on chat.
- Issue header (parent breadcrumb, title/badge block, plugin toolbars, load-
  earlier button) renders INSIDE the thread scroll viewport, so it scrolls
  away with the messages instead of staying pinned.
- --tc-thread-max-h reserve 21rem → 8rem: composer now sits ~26px above the
  viewport bottom.
- New IssuePlanConfirmationActionBar: while a plan confirmation is pending,
  its Confirm/Decline CTAs (with decline-reason flow) pin to a footer slot
  below the pane's scroll area on the Plan tab.

Verified: ui tsc clean; motion gate (25 files) + token gates clean; 218 tests
across task-chat/pane/IssueDetail/IssueChatThread (1 pre-existing TZ flake in
IssueProperties monitor row, fails on base); Playwright on :3093 light+dark —
tabs in pane header, header scrolls away/returns, thread bottom 923/950, plan
bar pinned at pane bottom in footer slot, zero page errors; flag-off run shows
legacy tabs/pane title, 0 redesign testids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:49 -07:00
scotttongandClaude Fable 5 a5bdb851ea feat(ui): task-chat phase 2c — center/widen shell, inline interaction cards, plan doc in Plan tab
All flag-gated behind enableTaskChatRedesign; flag OFF stays byte-identical.

- --tc-shell-max-w (60rem) token; IssueDetail wrapper + skeleton become
  mx-auto w-full max-w-(--tc-shell-max-w) when the flag is on, and the
  thread body + pinned composer caps switch to the same token
- interaction kind added to the task-chat model; TaskChatThread merges
  interactions chronologically (same sort keys + same-run handoff shift
  as buildIssueChatMessages) and renders them via TaskChatInteractionCard,
  a v7-grammar wrapper over the shared IssueThreadInteractionCard
  (expired confirmations demote to a marker row)
- useIssuePlanDocument hook (404 → null) shared by the Plan tab, which
  now renders the plan document via MarkdownBody with a revision line
  above the decompositions, and by the thread's Plan updated/created
  marker

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:31 -07:00
scotttongandClaude Fable 5 e76584b95b feat(ui): task-chat phase 2b — v7 pixel parity + live streaming
Close the gaps between the flagged redesign and the accepted v7 prototype,
verified against the v7 artifact side-by-side and a REAL streaming agent run:

- Bubbles: human = --liveness-blue #2563eb (was bg-primary near-black) white
  text right; agent comment = borderless gray --bubble-agent bubble (new token,
  light+dark) with v7 header (capsule avatar · bold name · one capitalized
  mode chip). Bubble bodies now render markdown via MarkdownBody (code chips,
  links, mentions) instead of raw text.
- Composer: per-mode v7 placeholders ("Message {agent} — describe what you
  want done…"), neutral focus-within ring, borderless + attach iconbtn,
  full "Agent/Plan/Ask mode" label on the status-chip trigger, icon-only
  32px ArrowUp send with hover scale.
- Full-page chat shell: with enableTaskChatRedesign ON, IssueDetail gates the
  legacy title (compact single-line editor instead) / description / sub-tasks
  table / plan decompositions / Documents (pinned plan) / Output / attachments
  wall / workspace card / artifact chips — thread owns the center column,
  bounded by new --tc-thread-max-h so TaskMessageScroller owns scrolling with
  the composer pinned (scroll viewport = absolute inset-0; percentage heights
  don't resolve against flex-sized block parents). Flag OFF proven legacy:
  0 redesign testids, all legacy sections present.
- Tool rows (v7 flat grammar): [glyph · name · mono target · hover chevron ·
  status]; transcript adapter now emits real tool names (toolDisplayName —
  mcp__ collapse + capitalization, tool_result.toolName backfill), summarized
  primary params (summarizeToolInput probes file_path/command/pattern/…),
  clipped mono result detail, generic "Tool" fallback for legacy stored logs.
- Streaming states: shimmer (.shimmer-text-muted variant) on live Thinking…
  header + flat pulse-dot statusline (running/working no longer card chrome);
  thinking settles to "Thought for Ns" (ts-span derived) and arrives collapsed
  in settled history; turn summary label + mono metrics per v7 runsum.

Verified: motion gate (24 files, seams), token gates, workspace tsc, 177 ui
tests incl. task-chat/PropertiesPanel/IssueChatThread/IssueDetail; live
streaming captured on a real claude_local run on the worktree instance
(:3093 scratch task) — thinking/text deltas, live statusline with elapsed,
auto-follow, and fold-to-summary on completion; flag-off isolation screenshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:27 -07:00
scotttongandClaude Fable 5 474c4138ac feat(ui): task-chat redesign phase 2 — v5–v7 reconciliation + live-thread completeness
Reconcile the baseline with the accepted v5–v7 prototype decisions and finish
the live-thread work items (, plan §5), all behind enableTaskChatRedesign:

- Composer: v7 comp-bar (attach + far left, rectangle status-chip mode trigger
  via --tc-mode-* hues incl. ask-blue #3b82f6, assignee combobox left of send,
  all controls 32px, no shortcut hints), per-message mode with ⇧⇥ cycling,
  attachments via button/paste/drag-drop, reassign-on-send with implicit reopen.
- Scroll pill: stock shadcn MessageScrollerButton (32px round, arrow-only) with
  tokenized entry/exit motion and easing-aware re-follow (smooth glide cannot
  unpin the follow state; wheel/touch cancels).
- Turn fold: run activity grouped into TaskChatTurn — live turns stream
  expanded, settling folds to a one-line '✓ Worked · 38s · 3 tools · +34 −3'
  summary (--motion-turn-fold 380ms, instant for settled history, reduced-motion
  honored), settled turns interleave before their run's reply comment.
- Bubbles: agent header gains the textless brand-gradient capsule avatar +
  name · mode chip (deterministic agent→gradient hash).
- Properties pane: tab label Plans→Plan; drag-resizable (clamp ≥260px,
  dbl-click reset 322px, width remembered) + maximizable (320ms glide, spacer
  holds flex slot, 840px content cap) — flag-off DOM untouched.
- Motion: new tokens (turn-fold, scroll-pill enter/exit + easings, pane-glide)
  in index.css :root + reduced-motion zeroing + tweak-panel catalog entries.

Verified: workspace tsc clean; check-task-chat-motion + token gates clean;
53 task-chat/PropertiesPanel/gate tests, 29 experimental-settings tests and
21 server instance-settings tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:27 -07:00
scotttongandClaude Fable 5 d270ee47fd feat(ui): task-chat redesign baseline behind enableTaskChatRedesign
New ui/src/components/task-chat/ surface (model, TASK_CHAT_STATES inventory,
fixtures, adapter, Bubble/Marker/StatusPill/ToolCard/ThinkingBlock/UsageReadout/
Scroller/PlanView/ThreadView, TweakPanel), motion tokens in index.css :root,
useTaskChatRedesignEnabled hook + TaskChatRedesignGate, one-line ThreadComponent
seam in IssueDetail.tsx, flag-scoped tabs in IssueProperties.tsx, dev harness
/dev/task-chat-lab, gate script scripts/check-task-chat-motion.mjs.

Flag wired through the 3 lockstep shared files (types/validators/feature-catalog),
preference tier, default off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 16:55:20 -07:00
Nicky LeachandPaperclip 01b51dc0e5 fix(runtime): stop Live badge and Working shimmer after task teardown (#10985)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task list and the chat views show a Live badge and a Working
shimmer for an issue that has an active run.
> - A finished task kept the Live badge and the Working shimmer after
the run ended and the sandbox stopped.
> - The user interface reads run liveness from the
`heartbeat_runs.status` row. The run finalizer writes the terminal
status in a step that is separate from the agent `status=done` update.
When the sandbox or the run process stops between the two steps,
`heartbeat_runs.status` stays `running` forever.
> - A run row that stays `running` makes a finished task look
perpetually Live, and the user interface has no guard for an issue that
already reached a terminal status.
> - This pull request closes the invariant "environment lease released
implies the run is terminal" on the server, and adds a user interface
guard that suppresses live state for a terminal issue.
> - The benefit is that a finished task stops showing Live and Working,
both at the source (the run row) and at the surface (the badge and the
shimmer).

## Linked Issues or Issue Description

**Bug description**

- A completed task kept the Live badge and the Working shimmer after its
run ended and the sandbox was torn down.

**Steps to reproduce**

- Run an agent task to completion. Let the sandbox tear down while the
run finalizer is between the `status=done` update and the terminal
run-status write.
- Open the task list or the chat view for the finished task.

**Expected behavior**

- A finished task shows no Live badge and no Working shimmer.

**Actual behavior (before this change)**

- The finished task showed the Live badge and the Working shimmer
because its `heartbeat_runs.status` row stayed `running`.

This pull request supersedes the two separate pull requests #10954
(frontend) and #10955 (backend). It carries all of their changes for the
same race.

## What Changed

Server:

- Run teardown terminalizes a still-running or still-queued run before
it releases the environment lease. It writes `succeeded` when the issue
already reached `done`, `cancelled` when the issue is `cancelled`, and
`interrupted` otherwise. It never overwrites a status that another path
already made terminal.
- The recovery stale-lock sweep terminalizes an orphaned running run to
`interrupted` after it confirms the process and the sandbox are both
gone. It requires recorded process metadata, so it never terminalizes a
live run, a queued run, or a scheduled retry.
- Each terminal transition writes a run event.
- The stale-lock sweep continues and clears the lock when the audit
write fails. It logs the failure loudly.
- New server tests cover both invariants.

User interface:

- A shared guard suppresses the Live badge and the Working shimmer when
the issue status is terminal.
- The guard keeps non-terminal `queued` and `running` issues live.
- The guard prefers the newest issue live-status snapshot.
- New user interface tests cover the guard and the snapshot preference.

## Verification

Server:

- `pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc
--noEmit` in `server/` — 0 errors.
- `pnpm --filter server test
heartbeat-run-lease-release-terminalization.test.ts
recovery-stale-issue-lock-sweep.test.ts` — 12 tests pass.

User interface:

- `pnpm --filter @paperclipai/ui typecheck` — 0 errors.
- `pnpm exec vitest run ui/src/lib/liveIssueIds.test.ts
ui/src/lib/issue-chat-messages.test.ts` — 40 tests pass.

## Risks

- Low risk. The server change only forces a still-live run row to a
terminal status when the lease releases or when the recovery sweep
confirms the process is dead. It never overwrites an existing terminal
status, and it guards the recovery path with process metadata to avoid
terminalizing a live run.
- The user interface change is additive. The guard only suppresses live
state for a terminal issue and keeps queued and running issues live.
- No database migration. No change to any external endpoint.

## Model Used

- Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, tool use, and
code execution.

## 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>
2026-08-07 07:23:56 -07:00
scotttongandClaude Opus 4.8 ea83c5c822 feat(task-chat): bring back copy/👍/👎 actions on the agent bubble footer (#11025)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task view shows a threaded conversation between the user and the
agent, with agent replies rendered as bubbles that carry a "✓ Worked · N
tools" summary line.
> - The conference-room chat already offers per-message copy and
thumbs-up / thumbs-down feedback, but the redesigned task thread dropped
these controls from the agent bubble footer.
> - Users lose a quick way to copy an agent reply or send feedback on
it, and the redesign silently ignored the feedback-vote props it was
already given.
> - This pull request prepends a copy · thumbs-up · thumbs-down cluster
to the bubble summary line and wires the existing feedback-vote props
through.
> - The benefit is a consistent feedback surface across both chat views,
with no new API.

## Linked Issues or Issue Description

No public GitHub issue exists for this change; the underlying issue is
described inline below following the feature template
(`.github/ISSUE_TEMPLATE/feature_request.yml`).

#### Problem or motivation

The redesigned task thread renders each agent reply with a "✓ Worked · N
tools · <timestamp>" summary line, but it dropped the copy and thumbs-up
/ thumbs-down controls that the conference-room chat still shows. Users
can no longer copy an agent reply or vote feedback from the task thread.
The redesign component already received `feedbackVotes` and `onVote`
props but ignored them.

#### Proposed solution

Prepend a copy · 👍 · 👎 cluster to the summary line, leading the
always-visible timestamp, reusing the shared `IssueChatFeedbackButtons`
so both chat views speak the same feedback language. Anchor the cluster
to the turn's summary row (a sibling of the expandable tool-history
fold) so it stays on the summary line whether the tool history is
collapsed or expanded.

#### Alternatives considered

Placing the cluster inside the expandable fold — rejected because
expanding the tool history then re-centered the actions to the middle of
the tall fold.

#### Roadmap alignment

UI polish to the task thread; no core-roadmap overlap.

## What Changed

- Add `TaskChatBubbleActions`: a copy · thumbs-up · thumbs-down cluster
built on the shared `IssueChatFeedbackButtons`.
- Render the cluster on the agent bubble's "✓ Worked · …" summary line,
leading the timestamp; runless agent replies get the same cluster with
the timestamp trailing. Human and system bubbles are unchanged.
- Add a `leading` slot to `TaskChatTurn` so the actions sit on the
summary row, a sibling of the tool-history fold, and stay anchored when
the fold expands.
- Wire the redesign to the `feedbackVotes` / `onVote` props it already
received.
- Add a demo binding in the `TaskChatLab` dev harness.

## Verification

- `pnpm check:token-gates` — 3/3 gates CLEAN.
- `pnpm typecheck` — clean across all packages.
- `cd ui && pnpm vitest run
src/components/task-chat/TaskChatBubble.test.tsx
src/components/task-chat/TaskChatTurn.test.tsx` — 31/31 pass.
- Manual: open a task thread, confirm the copy / 👍 / 👎 cluster shows on
the agent bubble summary line before the timestamp, copy works, votes
toggle, and the cluster stays on the summary line when the tool history
is expanded.

Visual change: snapshot baselines are intentionally not updated, per the
`doc/design/DECISION-SHEET.md` entry "Per-change snapshot verification
demoted to dormant (Jul 13 2026)".

## Risks

Low risk. UI-only change scoped to the redesigned task-chat bubble
footer. It reuses an existing shared feedback component and existing
vote props; no API, schema, or server change. Human and system bubbles
are untouched.

## Model Used

Claude Opus 4.8 (Anthropic), model id `claude-opus-4-8`, extended
thinking enabled, tool use.

## 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: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 18:26:44 -07:00
DottaandClaude Opus 5 e8ae5286eb feat(issues): explain cross-task agent writes with attribution, audit receipts, and actionable denials (#10843)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents write to tasks they do not own. They comment, they change
fields, and the control plane now permits this by default for
standard-trust agents on any task they can read
> - This makes a task thread ambiguous. A reader sees a comment from an
agent that is not the assignee, but no surface says whose authority that
write rode
> - The same gap applies to field edits. The activity stream named the
verb, but it did not show the before value, the after value, or the
reason the write was permitted
> - The remaining refusals are also opaque. An agent that hits a wall
receives a 403 with no boundary name, no actor who can act, and no
sanctioned path. One real incident spent a full detour to find the
workaround
> - This pull request adds the three surfaces that make open cross-task
writes legible: an attribution chip, a field-level audit receipt, and an
actionable denial contract shared by the API and the UI
> - The benefit is that a reader can answer "who did this, on whose
authority, and was it allowed?" on the task itself, and a blocked writer
is told what to do next

## Linked Issues or Issue Description

No public issue exists for this work, so the enhancement is described
here.

**What existing behavior does this improve?**

Cross-task agent writes are permitted, but they are not explained. A
task thread can hold comments from agents that are not the assignee, and
the activity stream can hold field changes made by those agents. Neither
surface names the responsible user behind the write. When a write is
refused, the error text does not name the boundary or the way forward.

**Subsystem affected**

Issue detail UI (comment thread and activity stream), the issue write
authorization responses in the server, and the shared copy contract that
both consume.

**Current behavior**

- An agent comment on a task the agent does not own looks the same as an
assignee comment.
- An `issue.updated` activity row states the verb only. It does not show
the field-level before and after values, the responsible user, or the
authorization reason.
- A refused write returns a short message such as an ownership error.
The message does not state which rule fired, who is able to perform the
action, or which alternative path is sanctioned.

**Proposed behavior**

- An agent comment on a task the agent does not own carries a chip that
reads "for {user}". The chip names the responsible user. Its tooltip
states that the author is not the assignee and cannot exceed that user's
permissions.
- Each `issue.updated` row shows a receipt: the changed fields with
before and after values, the responsible user, and the authorization
reason. This applies to board edits as well as agent edits.
- Each refusal states three things: the boundary that fired, who is able
to act, and the sanctioned path. The API error body and the in-app
notice use the same words, because both read one shared contract.

Related pull requests, found by searching this repository:

- Refs #10837 — merged. It added the default-open cross-task write rule,
the comment attribution data, and the per-run containment cap that this
pull request makes visible.
- Refs #10114 — open. It proposes a narrower authorization change in the
same area.
- Refs #7998 — open. It proposes append-only cross-assignee comments as
an alternative to opening writes.

## What Changed

- Adds `packages/shared/src/issue-write-denial.ts`. This is one copy
contract for eight ways an issue write can be refused: not visible,
responsible-user ceiling, responsible user unavailable, excluded actor
class, assignee run lock, per-run cross-task cap, missing run context,
and rejected attribution. Each entry names the boundary, who can act,
and the sanctioned path.
- Maps server authorization decisions onto that contract in
`server/src/routes/issues.ts` and
`server/src/services/cross-issue-influence-limit.ts`. The flattened
`error` string carries all three obligations, and `details.code` lets
the UI render the same words. The two cap codes keep the names they
already ship under.
- Adds `CommentAttributionChip`. It renders "for {user}" beside the
author name on agent comments where the author is not the assignee. It
renders nothing when no responsible user is recorded, so older rows stay
clean. It is wired into both `IssueChatThread` and the flagged
`TaskChatThread` redesign.
- Adds `IssueFieldChangeReceipt`. It renders the change receipt under
`issue.updated` rows in the activity stream. Ids resolve to agent and
user names where the directory is loaded. Server-truncated text is
labelled as a preview, so the receipt never implies that it shows a
whole value.
- Adds `IssueWriteDenialNotice`. It renders the shared copy in the app,
keyed off the denial events the server logs on a task.
- Adds a public `/ux-lab/cross-issue-collaboration` page. It renders all
three surfaces and their edge cases for review without a seeded thread.
This follows the existing `ux-lab` pages.

## Verification

Automated, all green:

```
pnpm --filter @paperclipai/shared exec vitest run src/issue-write-denial.test.ts        # 17 tests
pnpm --filter @paperclipai/ui exec vitest run src/components/IssueWriteDenialNotice.test.tsx \
  src/components/IssueFieldChangeReceipt.test.tsx src/components/CommentAttributionChip.test.tsx \
  src/lib/issue-change-receipt.test.ts src/lib/comment-attribution.test.ts                # 46 tests
pnpm --filter @paperclipai/server exec vitest run src/__tests__/cross-issue-influence-limit.test.ts \
  src/__tests__/issue-comment-attribution-audit-routes.test.ts \
  src/__tests__/issue-agent-mutation-ownership-routes.test.ts \
  src/__tests__/low-trust-red-team-routes.test.ts                                        # 98 tests
```

`tsc --noEmit` passes for the shared, ui, and server packages.

Manual, in a browser:

1. Start the UI only: `pnpm --filter @paperclipai/ui exec vite`.
2. Open `/ux-lab/cross-issue-collaboration`. No session is needed,
because `ux-lab` routes are public.
3. All three surfaces were captured at 1440x900 in light mode and dark
mode, and at 390x844. The page reported no errors.
4. The chip tooltip was opened by a hover and by a keyboard focus.

Rendering the page found defects that the tests had missed. Three copy
and contrast defects were fixed, and two of them are now pinned by a
test. A design review then found three layout defects, which are also
fixed: the denial notice orphaned its label when a value wrapped, the
receipt icon wrapped onto its own line at narrow widths, and the chip
tooltip was reachable by hover only.

## Risks

Low risk, and additive.

- Every new surface renders nothing when its data is absent. Comments
without a recorded responsible user show no chip, and activity events
without a receipt show no receipt, so existing rows do not change.
- No migration is included. The data these surfaces read already ships.
- The wire values of the two per-run cap denial codes are unchanged.
Only the human-readable text changes, plus six codes that had no
`details.code` before.
- The denial copy is read by agents as well as people. If wording must
change later, one shared module is the only place to change it.
- Roadmap check: this extends the completed "Activity log & action
attribution" area rather than duplicating planned core work.

## Model Used

Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context
window, extended thinking, with tool use and code execution. It ran as
an agent in Claude Code and drove a real browser to capture the review
screenshots.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 23:02:51 -05:00
9b9631b724 feat(ui): chat-style tasks polish — rich-text composer, attachment chips, live-turn interstitials, mobile layout (#10707)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators talk to their agents on the issue detail page. An
experimental "Chat-Style Tasks" view (#10606) makes that page read as a
conversation instead of a ticket form.
> - The first release of that view shipped with a plain-text composer,
no visible narration while an agent works, and a desktop-only layout.
> - Users write formatted replies, paste screenshots, and follow long
agent runs from their phones. The experimental view should support all
of that before it can graduate.
> - This pull request is the next iteration of the same experiment: a
rich-text composer with attachments, live-turn narration on the status
line, cleaner settled-turn history, and a mobile layout.
> - The benefit is a chat view that feels alive while the agent works
and stays readable after it finishes, on desktop and mobile, still fully
behind the existing opt-in flag.

## Linked Issues or Issue Description

Refs #49 (chat with agents is a much-wanted feature).
Refs #10606 (the merged first release of the experimental chat-style
task view; this PR iterates on it).

Related PRs found in the dedup search:
- #8228 — open PR that polishes the classic issue chat composer. It
targets the flag-off legacy path; this PR only changes the flag-on
experimental view.
- #10466 — merged blockquote-recovery fix in the shared MarkdownEditor.
This PR now reuses that editor inside the chat composer.

**What existing behavior does this improve?**

The experimental "Chat-Style Tasks" view on the issue detail page
(Settings → Experimental, `enableTaskChatRedesign`, default off).

**Subsystem affected**

UI (issue detail page, chat-style task view).

**Current behavior**

With the experiment enabled, the composer is a plain textarea with no
formatting, no attachment preview, and no mention support. While an
agent runs, the status line shows only a static label, and the agent's
narration text is hidden. Finished runs render one settled row per turn,
so a run with many short turns produces a long list of near-duplicate
"Worked" rows, and turns without a comment append at the bottom out of
order. On mobile, the desktop bounded-height thread makes the page
scroll poorly.

**Proposed behavior**

The composer uses the shared MarkdownEditor: markdown formatting,
mentions, image paste with thumbnail previews, and non-image attachment
chips. Sending posts on Cmd/Ctrl+Enter. While an agent runs, the status
line rotates playful status words and surfaces the agent's own narration
as short interstitial updates: each update holds for a minimum dwell, is
replaced only when superseded, and slides through a one-line viewport
with tokenized motion. Back-to-back settled turns coalesce into one
"Worked" row with summed durations and re-derived tool counts, and
comment-less settled turns insert chronologically at their run's start
time. On mobile, the thread renders in the document flow with window
auto-follow and a sticky safe-area composer; the desktop layout is
unchanged.

**Reason and benefit**

The chat view is only convincing if it feels like a conversation with a
working agent. Rich text and screenshots are table stakes for chat
input. Live narration gives moment-to-moment feedback without opening
transcripts. Coalesced history keeps long-running tasks readable. Mobile
support lets operators follow runs away from their desks.

**Breaking changes**

None. Every change is gated behind the existing `enableTaskChatRedesign`
flag, which is off by default. The flag-off page is unchanged.

## What Changed

- `TaskChatComposer` swaps its textarea for the shared `MarkdownEditor`:
markdown formatting, mentions, image paste with object-URL thumbnail
previews (revoked on clear and unmount), and posting on Cmd/Ctrl+Enter.
- Non-image attachments render as chips on a new shared
`ui/attachment.tsx` primitive (adds the `@base-ui/react` dependency it
builds on).
- New `status-whimsy.ts`: deterministic rotation of playful status words
on the live status line.
- Live interstitial narration: the transcript adapter tags agent
self-talk, and the live status line shows it as ephemeral one-line
updates with a ~4s minimum dwell, hold-until-superseded replacement, and
a slide transition driven by new `--motion-line-scroll` tokens
(cataloged in `motion-tokens.ts`, which a test keeps 1:1 with
`index.css`). Hover affordance applies only to the status line, with no
leading icon.
- Settled-turn history: `coalesceSettledTurns` merges back-to-back
settled agent turns into one "Worked" row (summed per-run durations,
tool counts re-derived from the merged turn); `assembleThreadItems`
inserts comment-less settled turns chronologically at run start instead
of appending them at the bottom; settled turns render tool rows only
(the separate thinking block component is removed).
- The "Worked" summary attaches to the reply timestamp row, and thread
timestamps are always visible.
- Mobile layout: the thread renders with `scroll={false}` in the page
scroll, a new `useWindowAutoFollow` hook keeps the window pinned to new
content, the composer is sticky with safe-area padding, and the editor
uses 16px text so iOS does not zoom on focus. The desktop bounded chain
is untouched.
- New `TaskChatDescriptionBubble` renders the issue description as the
first chat bubble, and `McpIcon` gives MCP tools a distinct icon.
- `IssueDetail.test.tsx` stubs `TaskChatThread`: the composer's
`@mdxeditor` dependency cannot load under jsdom's CSSOM, and the suite
exercises the flag-off path.

## Verification

- `pnpm check:token-gates` — 3/3 CLEAN.
- `node scripts/check-task-chat-motion.mjs` — OK (30 files scanned,
seams present).
- `cd ui && npx tsc -b` — clean.
- `cd ui && pnpm vitest run` — 3,482 of 3,483 tests pass locally. The
one failure is the `IssueProperties.test.tsx` monitor-row
time-formatting test, which is timezone-sensitive: it fails identically
on unmodified `origin/master` in a non-UTC timezone and passes with
`TZ=UTC`. It is not related to this change.
- Manual: enable "Chat-Style Tasks" in Settings → Experimental and open
an issue with an assigned agent. Comment to start a run: the status line
rotates status words and shows the agent's narration as short held
updates. After the run, consecutive turns fold into one "Worked" row
under the reply timestamp. Paste an image into the composer to see a
thumbnail chip; attach a non-image file to see a file chip; send with
Cmd+Enter. Open the same issue in a narrow viewport to see the
document-flow layout with the sticky composer.
- Visual snapshot baselines are intentionally not updated: per
`doc/design/DECISION-SHEET.md`, "Per-change snapshot verification
demoted to dormant (Jul 13 2026)".

## Risks

- The composer now loads the shared MarkdownEditor inside the chat view.
The editor is already used across the app (issue descriptions,
comments), so its behavior is well exercised; composer-specific handling
(paste, attachments, submit keys) is covered by new tests.
- The transcript adapter changes how live narration and settled turns
are derived from run logs. Malformed or legacy logs degrade to generic
rows rather than crashing, and the adapter suites cover the merge and
ordering rules.
- Object URLs for paste previews are revoked on send-clear and unmount
to avoid leaks; jsdom environments without `URL.createObjectURL` are
guarded.
- All changes are behind the default-off `enableTaskChatRedesign` flag.
Overall risk with the flag off is low.

## Model Used

- Claude (Anthropic), model id `claude-fable-5` (Claude Fable 5),
extended thinking enabled, agentic tool use (file editing, shell, test
execution) via Claude Code / Claude Agent SDK.

## 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 16:25:31 -07:00
scotttongandClaude Fable 5 c185e64b77 feat(ui): chat-style task view behind an experimental flag (#10606)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators spend most of their time on the issue detail page. They
talk to the assigned agent there through comments.
> - The current page reads as a ticket form. The thread sits below
properties, the composer sits mid-page, and live agent activity renders
as dense transcript logs.
> - Talking to an agent is a conversation. A chat-first layout matches
that mental model better than a ticket form.
> - A layout change this large must not disrupt current users. It needs
a safe opt-in path and full parity with the existing thread features.
> - This pull request adds a chat-style task view behind a new
"Chat-Style Tasks" experiment toggle. The flag is off by default and the
existing page is unchanged when it is off.
> - The benefit is a focused, readable conversation with the agent: live
tool activity folds into compact summaries, the composer stays at the
bottom, and properties, plan, and artifacts move into header tabs.

## Linked Issues or Issue Description

Refs #49 (chat with agents is a much-wanted feature).

Related PRs found in the dedup search:
- #4489 — an earlier, closed attempt to promote the conversation to the
primary surface on issue detail. This PR is a fresh, flag-gated take on
the same goal.
- #8837 — an open PR that proposes a two-column task layout. It
restructures the same page but keeps the ticket paradigm; this PR is
orthogonal because it is opt-in and chat-first.

**Subsystem affected**

UI (issue detail page).

**Problem or motivation**

The issue detail page presents agent conversations as a ticket:
properties first, thread below, composer in the middle of the page, and
raw transcript noise during live runs. Users who mainly converse with
their agents must scroll past chrome to follow the conversation, and
live activity is hard to read.

**Proposed solution**

An opt-in chat-style view of the issue detail page, gated by a new
"Chat-Style Tasks" experiment toggle in Settings → Experimental. With
the flag on, the thread fills the center pane, the composer docks to the
bottom of the viewport, Properties / Plan / Artifacts become header
tabs, live turns show a status pill with the current tool action and
elapsed time, and settled turns collapse to a "Worked · N tools" summary
that expands into per-tool rows. With the flag off, nothing changes.

**Alternatives considered**

Restyling the existing layout in place (rejected: too disruptive without
an opt-out), and a separate chat page beside the issue page (rejected:
splits the task's single source of truth). A per-request lab page
(`/task-chat-lab`, dev-only) was kept for design iteration instead.

**Roadmap alignment**

ROADMAP.md "CEO Chat" wants lighter conversations that still resolve to
real work objects. This PR keeps the core task-and-comments model — it
only changes presentation, opt-in — so it does not duplicate that
planned work.

## What Changed

- New `enableTaskChatRedesign` instance setting, exposed as a
"Chat-Style Tasks" experiment card in Settings → Experimental (shared
feature catalog, validators, server instance-settings service, and UI
settings page).
- New `ui/src/components/task-chat/` component family: chat thread with
turn grouping, agent reply bubbles, live status pill, collapsible turn
summaries with per-tool rows, plan tab with a sticky CTA action bar,
inline interaction cards, per-request mode chips, and a bottom-docked
composer.
- A shared tool taxonomy (`tool-taxonomy.ts`) maps tool names to verbs
and icons; the status pill, tool rows, and the classic transcript view
all use it.
- A transcript adapter converts stored run logs into chat turns; it
dedupes tool-call updates by `toolUseId` so tool counts match the
expanded rows, and it keeps a tool row's first real name when later
generic updates arrive.
- Composer: posts on Cmd/Ctrl+Enter, supports image paste with
object-URL thumbnail previews (revoked on clear/unmount), and uploads
through the issue attachments route.
- `IssueDetail.tsx`: with the flag on, pane tabs move to the header bar,
the header is not sticky, and the chat fills the center; with the flag
off, the previous layout renders unchanged.
- Motion tokens for the new animations live in `ui/src/index.css` with a
`motion-tokens.ts` catalog and a test that keeps the two in sync (the
catalog now also covers the shared enter/exit/swap tokens that the
decision/quicklook block declares).
- A dev-only `/task-chat-lab` page with fixtures and a tweak panel for
motion tuning.

## Verification

- `pnpm typecheck` — clean across the workspace.
- `pnpm check:token-gates` — 3/3 CLEAN.
- `cd ui && pnpm vitest run` — 3,344 of 3,345 tests pass locally. The
one failure is `IssueProperties.test.tsx` monitor-row time formatting,
which is timezone-sensitive: it also fails on unmodified `origin/master`
in a non-UTC timezone and passes with `TZ=UTC`. It is not related to
this change.
- `cd server && pnpm vitest run
src/__tests__/instance-settings-service.test.ts` — 21/21 pass (covers
the new setting).
- Manual: start the dev server, open Settings → Experimental, enable
"Chat-Style Tasks", and open any issue. The thread fills the page, the
composer docks to the bottom, and Properties / Plan / Artifacts appear
as header tabs. Assign an agent and comment to watch a live run: the
status pill shows the current tool action with elapsed time, and the
finished turn folds into a "Worked · N tools" summary. Disable the
toggle and confirm the classic page is unchanged.
- Visual snapshot baselines are intentionally not updated: per
`doc/design/DECISION-SHEET.md`, "Per-change snapshot verification
demoted to dormant (Jul 13 2026)".

## Risks

- The flag-off path goes through the same `IssueDetail.tsx` file, so a
regression there would affect current users. Mitigation: the classic
markup renders through the same components as before behind explicit
flag conditionals, and the full UI suite passes.
- The transcript adapter interprets stored run-log formats, including
legacy entries without `toolUseId`. Malformed logs degrade to generic
tool rows rather than crashing.
- The new view changes no server behavior other than one additive
instance setting; it is additive and default-off. Overall risk with the
flag off is low.

## Model Used

- Claude (Anthropic), model id `claude-fable-5` (Claude Fable 5),
extended thinking enabled, agentic tool use (file editing, shell, test
execution) via Claude Code / Claude Agent SDK.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 02:26:47 -07:00