Files
Gabriel Vidal e2842e27e0 refactor(complete): replace the CTA system with a complete skill
Completing a conversation is now a skill the session runs on itself before it
ends, instead of a button that resumes it afterwards. A resume is a new process,
so it re-creates the whole transcript as fresh input tokens — the same tidy-up
costs several times more once the process has exited. The spawn guidelines point
the session at `.claude/skills/complete/SKILL.md` after its final notification;
that pass waits 30s (a window to redirect after reading the result), publishes
the summary via conv-scaffold + one subagent, tidies up, and signs off with
COMPLETED.

With the prompts living in a skill, the whole CTA layer goes:

- backend: ctas.py, /api/ctas* (+ /run, /prompt, /reorder), the seeded prompt
  writer, the Cta* schemas, meta.ctas and its merge path. /api/claude-hooks
  stays — it just no longer lives in a CTA-shaped section.
- frontend: Settings -> CTAs page, the CTA buttons under a finished thread,
  the CTA badge, ctaIcons, the cta:<id> composer tags, and the ctas SSE event.
- the native Claude Code hooks list moves onto the main Settings page
  (/settings#hooks), where it is the only hooks surface left.

The COMPLETED seal now reads `meta.completedAt`, derived from the transcript's
COMPLETED marker the parser already flags, rather than the meta.ctas["complete"]
ledger — nothing has to stamp it. What survives of the old pass is unchanged:
conv-scaffold, the published summary on meta.summary, and the Summary card
(ConversationCtas -> ConversationSummary).

Existing meta.ctas data is left alone in the store; it is simply no longer read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 00:15:34 +02:00

4.2 KiB

name, description
name description
complete Close out a finished conversation — wait 30 s for a change of mind, then publish its summary (conv-scaffold + one subagent) and tidy up (memories, docs, artefacts, worktrees, changelog). Use when the task is done, the final notification has been sent, and the conversation guidelines say to complete it. Not for starting new work.

complete — the end-of-task pass

The last thing a conversation does. Everything about the task is already finished and notified; this pass turns the conversation into a record and leaves the machine tidy.

It runs inside the still-running session, not as a resume. That is the whole point: a --resume is a new process, so it re-creates the entire transcript as fresh input tokens (see the ai-agent repo's CLAUDE.md). Running the pass before the session ends reuses the prompt cache it already built.

When to run it

After the task is done and the final notify-done has gone out — the conversation guidelines say so explicitly. Never as a way to start new work.

0. Wait 30 seconds first

Gabriel reads the finished conversation on his phone as the notification lands. The pause is his window to interrupt and redirect before the pass starts — without it, the tidy-up races his reply and he ends up correcting a conversation that has already summarised itself.

Arm a Monitor and end the turn. Do not run a foreground sleep (it is blocked), and do not skip the wait:

Monitor: command     = "sleep 30; echo 'window over — completing'"
         description = "30 s grace period before the completion pass"
         timeout_ms  = 120000
         persistent  = false

The monitor emits one line and exits. If Gabriel sends something in the meantime, that wins: drop the completion pass, do what he asked, and only come back here once that is finished too.

1. Scaffold + summary

Run conv-scaffold — it writes a markdown scaffold of this conversation (metadata and tags, the squashed diff of everything that changed, merged bash/tool/task summaries) and prints its path:

conv-scaffold            # → /tmp/conv-scaffold-<sessionId>.md

Then spawn one subagent whose whole job is that file: it reads the scaffold, fills in the ## Summary and ## Analysis sections (what was built, the key decisions, what is worth remembering, what is still open), and publishes it:

conv-scaffold publish <path>

Everything the subagent needs is in the scaffold — it does not need this conversation's history, which is why the analysis costs a fraction of what re-reading the transcript would. An untouched scaffold is refused (HTTP 422).

The published summary lands on the conversation's meta.summary and renders as the folded Summary card under the thread in the ai-agent viewer.

2. Tidy up

While the subagent works, do the housekeeping this task earned — only what genuinely applies, and keep it short:

  • Memory. Anything non-obvious here — a gotcha, a wrong turn, a constraint invisible in the code? One fact per file in your memory directory, plus its line in MEMORY.md. Skip what the repo already records.
  • Docs. If the change altered how something is used, built or deployed, update the affected README.md / CLAUDE.md / GOAL.md. Describe the current state — no changelog prose.
  • Artefacts. Copy anything worth keeping (screenshots, diagrams, reports) into the canonical project's .ai/artefacts/<sessionId>/. Leave scratch behind.
  • Cleanup. Remove worktrees you created once their branch is merged (scripts/new-worktree.sh -r <path>), and kill background processes, dev servers or standby containers you no longer need.
  • Changelog. If the project keeps one, add the user-facing entry.

Do not re-notify (no notify-done) and do not start new feature work.

3. Sign off with COMPLETED

End your reply with COMPLETED on its own line — not DONE.

That marker is load-bearing, not decoration. The ai-agent backend parses the transcript's last message for it (completedMarkermeta.completedAt) and uses it to tell "the task finished" apart from "the task finished and was tidied up after": the spinning COMPLETED seal on the conversation card is that timestamp, and nothing else writes it.