claude-plugins: split agent-hygiene guidance into five focused skills

This commit is contained in:
damocles 2026-07-27 16:07:31 +02:00 committed by mara
commit c236c16c52
5 changed files with 201 additions and 0 deletions

View file

@ -0,0 +1,48 @@
---
name: async-task-hygiene
description: Habits for background tasks and waiting. Don't truncate a background task's captured output with a tail/head pipe - the full output is saved regardless, so piping it away only throws away what you might need, slice it after the fact instead. Don't assume a background task from before a restart is still exactly where you left it - check its status rather than blindly re-running it. Prefer ending your turn (or a short bounded wait) over a sleep-based polling loop when waiting on something in progress. Use this whenever you launch, check on, or wait for background work.
---
# Async Task Hygiene
Background tasks (a build, a long command, anything you fire off and
check on later) have a few sharp edges worth knowing.
## Don't pipe away captured output
Don't do `some-long-command | tail -N` (or `| head -N`) when launching
a background task. The runner captures the **full** stdout/stderr to a
file regardless of what you piped through - so truncating the live
output doesn't save anything, it just means you can't slice the part
you didn't think you'd need until you needed it. Run the full command,
then read/grep the captured file however you want afterward. (Genuinely
unbounded streams you'll never read in full are the rare exception to
this - not normal command output.)
## Don't blindly re-run a task after a restart
If your environment restarted (container rebuild, harness restart) and
you had background tasks in flight, don't assume they're gone and
re-launch them from scratch. Check their status first - a task may
still be running, may have finished while you were down, or may
genuinely need restarting. Re-running blind can duplicate work or step
on a task that's still making progress.
## Prefer ending the turn over a sleep-loop
When you're waiting on something (a build, a task you started, a fixed
delay before retrying), a `sleep`-then-check loop blocks you from
reacting to anything else for that whole window. Prefer:
- **Between units of work:** just end the turn. Whatever wakes you next
(the task's own completion, a new message) drives the follow-up -
there's nothing to poll for.
- **Within a turn, if you must wait:** a short bounded wait that can be
interrupted by new input beats a blind sleep, since it lets you react
immediately if something more urgent shows up instead of only after
your poll interval elapses.
Concretely: if you started something and plan to check back, don't
`sleep N && check-status` in a loop. Either end the turn and let the
task's own completion (or the next message) drive the next step, or use
an interruptible wait if you genuinely need to stay in-turn.

View file

@ -0,0 +1,37 @@
---
name: choosing-a-comms-channel
description: Pick the right channel for a message instead of defaulting to whichever one is easiest to reach for. A fire-and-forget message is fine for pure notification with no expected reply. A tracked question needing a recorded decision belongs in a mechanism that threads the answer back to you, not a channel with no reply path. An issue-shaped question or decision belongs where the surrounding context already lives (the tracked item itself), not a side channel a future reader won't find. Ongoing back-and-forth belongs in a real conversational channel, not a one-shot send. Use this whenever you're about to communicate and there's more than one way to do it.
---
# Choosing a Comms Channel
Different messages want different channels. Picking the wrong one
doesn't just look sloppy - it actively loses information (a reply that
never reaches you) or buries context (a decision made somewhere a
future reader won't think to look).
## Match the channel to what you actually need
- **Pure fire-and-forget notification, no reply expected** - a
dashboard-visible one-way message is fine. Don't reach for a
conversational channel just to say something that needs no response.
- **A specific decision you need recorded, that threads an answer back
to you** - use a mechanism built for exactly that (queues cleanly,
the answer comes back attached to the question). This matters for
anything you'll need to point back to later - a gated approval, a
go/no-go call.
- **An issue-shaped question or decision** - post it where the
surrounding context already lives (the tracked item itself), not in
a side channel. A future reader looking at that item should find the
reasoning there, not have to go hunting through chat history for why
a decision was made.
- **Quick back-and-forth, status checks, anything conversational** -
use a real two-way conversational channel if you have one, rather
than a one-shot message that doesn't invite a reply.
## The underlying test
Before sending, ask: "if I need an answer, does this channel actually
deliver one back to me? If someone reads this in a week, will they find
it where the rest of the context already is?" If either answer is no,
you've picked the wrong channel for what you're trying to do.

View file

@ -0,0 +1,44 @@
---
name: planning-gate
description: Wait for explicit approval before implementing a proposed design or feature - posting a plan for review is not itself a go-ahead. Reading existing code to inform a plan is fine; writing new files or editing existing ones is an implementation action that needs an explicit yes from whoever owns the decision first. Use this whenever you've sketched an approach bigger than an obvious one-line fix and are about to start writing code, or whenever you're unsure if silence means consent.
---
# Planning Gate
A plan is not a decision. Posting a proposed design, and someone reading
it, are two different events - don't collapse them.
## Before writing any code for a non-trivial change
1. **Read and analyze freely.** Looking at existing code, tracing a bug,
forming a hypothesis - none of that needs permission.
2. **Post the plan, then stop.** Once you have a proposed approach,
share it and wait. Don't start editing files on the strength of your
own plan looking good to you.
3. **Writing files is the implementation action.** Creating a new file
or editing an existing one is where the gate applies - not the
thinking that led up to it.
4. **Silence isn't consent.** If it's unclear whether a plan was
approved (nobody replied, or the reply address a different point),
ask explicitly rather than assuming you're clear to proceed.
## Where a proposal belongs
Put a proposed design or solution in a **comment** on the task/issue,
never in the task's own description. The description states the
problem and should stay stable; a proposal is a point-in-time idea that
gets revised, superseded, or rejected. A proposal baked into the
description reads as settled fact to a later reader, when it might not
even have survived the first round of feedback. This applies to any
sub-tasks filed off a parent too: description states the problem,
comments carry the evolving design.
## What doesn't need the gate
- Obvious one-line fixes with no design ambiguity.
- Direct, explicit feedback on your own already-in-flight change (a
reviewer's comment on your open PR is actionable immediately - it's
not a new feature proposal, it's correcting work already approved to
exist).
- Anything the requester has already explicitly said "go ahead" on,
even if the exact implementation detail is still yours to fill in.

View file

@ -0,0 +1,34 @@
---
name: state-not-tmp
description: Never put work that needs to persist under /tmp - it's cleared on restart, so anything written there is lost the moment your environment restarts. Durable work (clones, notes, intermediate artifacts, anything you might need again) belongs under your own persistent state directory instead. /tmp is fine only for genuinely disposable scratch you're happy to lose. Use this whenever you're about to write a file and deciding where it should live.
---
# State, Not /tmp
`/tmp` is cleared on restart. Anything you put there that you'd actually
miss later is gone the next time your environment restarts - and a
restart isn't a rare event, it can happen mid-task.
## The rule
- **Durable work** - clones you're actively developing in, notes,
intermediate artifacts, anything you'd want back after a restart -
goes under your own persistent state directory.
- **`/tmp` is for genuinely disposable scratch** - a one-shot
verification build you'll tear down in the same turn, a throwaway
intermediate file you're done with before you stop. If losing it
would cost you nothing, `/tmp` is fine and even preferable (keeps
your persistent state directory from accumulating cruft).
## The test
Before writing a file, ask: "if this disappeared right now, would I
care?" If yes, it doesn't belong in `/tmp`. This matters most for
anything that took real work to produce - a clone you've made several
commits in, a report you'll want to reference later, config you're
mid-way through editing.
You can delete files from your own state directory freely once they're
no longer needed - storage isn't the concern there, persistence is. The
asymmetry is the point: `/tmp` disappears on a schedule you don't
control, your state directory disappears only when you decide it should.

View file

@ -0,0 +1,38 @@
---
name: status-and-blockers
description: Keep your visible status current and surface blockers proactively instead of parking silently. Call set_status at the start of every task describing what you're working on (or clear it to idle when there's nothing to do), update it the moment the work changes rather than leaving a stale description, and report a genuine blocker explicitly (file it, flag it) rather than waiting quietly for someone to notice. Use this at the start of any task and again whenever what you're doing actually changes.
---
# Status and Blockers
A stale status is worse than no status - it actively misleads whoever's
watching. Keeping it honest costs one tool call; the alternative costs
someone else's trust that your status means anything.
## Keep status current
- Set it at the **start** of a task: what you're working on, in enough
words that someone skimming a dashboard understands the current
activity without asking.
- Update it **the moment the work changes** - don't let "surveying
before answering" sit there long after you've already answered.
Ending a turn or waiting on something doesn't fix a stale status;
only calling the status update does.
- Clear it to idle (or an equivalent "nothing to do") when you
genuinely have nothing in flight. An idle agent showing a stale
"working on X" status wastes the next person's time chasing a ghost.
## Respond honestly to status checks
If asked directly what you're doing or whether you're blocked, answer
promptly and accurately - don't let a check-in go stale in your own
inbox while you keep grinding on something unrelated.
## Report blockers, don't just wait
If you're stuck on something outside your control - waiting on a
decision, a dependency, missing information - **surface it explicitly**
(file it as a tracked item, flag the specific person who can unblock
you) rather than silently sitting on it until someone happens to check
in. A blocker nobody knows about doesn't get resolved faster by staying
quiet about it.