Files
ai-agent/PROJECT_CLAUDE.md
Gabriel Vidal 74393fa474 feat(hub): deliver a follow-up into a live run through its inbox
_send_message now POSTs the prompt to the sidecar's /message first: a
run that is still alive keeps its background work (a build, a subagent,
a Monitor on a form) and reads the message between tool calls, or at
once when idle. Only 404 (nothing live) or 409 (the run can't take it —
a pi run, an older CLI) falls back to the old stop-then-resume.

Claude Code records such a message as a peer's ("Another Claude session
sent a message … not typed by your user"); the sidecar prefixes the text
to say it is the user's, and conversations._unwrap_inbox shows the plain
user turn. SpawnResult gains `delivered: "inbox"`.

Docs: the headless background-task semantics in CLAUDE.md and
PROJECT_CLAUDE.md; FakeWorker learns /message for the routing tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 23:24:50 +02:00

6.0 KiB

Project working conventions

Generic, host-agnostic conventions for working in a repo that the ai-agent tool manages. These are the portable rules — worktree workflow, artefacts, plans, committing, and how to signal a task is finished — with none of the homelab's Traefik/Compose/registry specifics baked in. A concrete repo (e.g. the homelab) keeps its own CLAUDE.md for its architecture and layers this on top; the ai-agent ships this file as the starting point for a new project's root CLAUDE.md.

Anything wrapped in {{…}} is a placeholder to fill in per repo.

Before you edit: isolate your work

Spin up an isolated git worktree and work there, rather than editing files on the checked-out branch. Before creating the worktree, pull the latest main so the worktree starts from current state.

  • {{worktree_command}} — create the worktree (e.g. scripts/new-worktree.sh).
  • {{pull_command}} — pull main first (e.g. git pull, or an auth-wrapped variant if a proxy fronts your git host).

Skip the worktree only for trivial, read-only inspection; any change to tracked files goes through one.

Directory conventions

  • data/ — file-backed working data: notes, logs, plans, boards. Not tracked in git. Write a plan to data/log/<YYYY-MM-DD>-[title].md.

  • Generated artefacts (images, screenshots, renders, charts, exports — any binary/media file) tied to a project go into that project's artefact folder:

    <projectFolder>/.ai/artefacts/<YYYY-MM-DD>/<sessionId>/<file>
    

    where <projectFolder> is the canonical directory (never a worktree copy — worktrees get deleted and aren't mounted into the viewer) and <sessionId> is the current conversation id ($CLAUDE_SESSION_ID, else resolve it via the session-id helper). The ai-agent viewer serves these folders: images you Read from there render inline in the tool call, and the conversation details page lists the whole set in its Artefacts card. Add .ai/ to the repo's .gitignore (or .git/info/exclude) if it isn't already ignored.

  • Artefacts tied to no particular project still go under data/ — e.g. data/artifacts/<YYYY-MM-DD>-[title].png — which is likewise mounted into the viewer and previewed inline. Files written anywhere else can't be served or previewed.

Waiting on background work in a spawned run

A spawned session is a headless claude -p run: the process exits when you end your turn. Ending a turn with "I'll pick this up when it reports back" is fine only because the runner guards it:

  • a background subagent or Monitor is waited for up to the runner's ceiling (2 h by default) — long enough for a build or a form answer;
  • a plain background shell (Bash run_in_background) would die at once; a Stop hook holds the turn until it has exited and then hands you its output file. If you are told a task is still running after the hold, either TaskStop it or end your turn again.

Prefer a Monitor for anything you wait on for more than a few minutes, and never park work on a sleep loop. A follow-up message the user sends while the run is alive arrives in the thread as a user turn — it does not restart the session.

Committing changes

Commit after each change. When you finish a logical unit of work — a source edit, a config change, a script, docs — commit it right away in its own focused commit rather than batching unrelated changes, and push as you go.

Use {{commit_command}} to commit and push (e.g. the commit-project skill for per-project Gitea/GitHub repos, or a repo-specific commit skill).

Finishing a feature: open a documented PR

When the task's guidelines ask for a PR instead of a deploy, finish feature work by pushing the branch and opening a pull request with {{pr_command}} (e.g. the open-pr skill), assigned to the repo owner. The PR body is the feature's documentation — write it with these sections:

  • Summary — what changed and why, for someone reading history later.
  • Key changes — one bullet per meaningful change, not per commit.
  • Key decisions — non-obvious choices and their trade-offs.
  • Changelog — user-facing Keep-a-Changelog bullets (Added/Changed/Fixed/ Removed); these entries are harvested to generate the project's changelog.
  • Test notes — how it was verified.

Attach a screenshot of any visible result. Deployment happens after the PR is reviewed and merged, not as part of the task.

When work is done

Send a push notification after finishing any task the user kicked off — not just long-running ones but every completed task — so they know it's done:

{{notify_command}} "✅ Task finished" "short outcome" --type {{type}}

Keep it short: say what finished and the outcome. Pass --type <type> to pick a fitting icon and --url <url> when the task has a more relevant page than the default (the conversation in the ai-agent viewer).

Then end the turn with DONE. After sending that notification, your final message must end with the literal marker DONE on its own line. The ai-agent conversation viewer keys "finished" off this: a conversation that has both recorded a notification and ended its last message with DONE is shown as finished, instead of being stuck on the stale running badge the worktree/select-project step set at the start.

Completing the conversation

When the task guidelines ask you to complete the conversation, read .claude/skills/complete/SKILL.md after that final notification and follow it. It waits 30 s (a window for the user to redirect after reading the result), then publishes the conversation's summary (conv-scaffold + one subagent) and tidies up — memories, docs, artefacts, worktrees, changelog. That pass signs off with COMPLETED instead of DONE, which is what puts the COMPLETED seal on the conversation card.

It runs in this session, before it ends: resuming a finished conversation re-creates its whole transcript as fresh input tokens, so the same work costs several times more once the process has exited.