diff --git a/claude-plugins/plugins/base/skills/async-task-hygiene/SKILL.md b/claude-plugins/plugins/base/skills/async-task-hygiene/SKILL.md new file mode 100644 index 00000000..7f86b13e --- /dev/null +++ b/claude-plugins/plugins/base/skills/async-task-hygiene/SKILL.md @@ -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. diff --git a/claude-plugins/plugins/base/skills/choosing-a-comms-channel/SKILL.md b/claude-plugins/plugins/base/skills/choosing-a-comms-channel/SKILL.md new file mode 100644 index 00000000..e0f512cb --- /dev/null +++ b/claude-plugins/plugins/base/skills/choosing-a-comms-channel/SKILL.md @@ -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. diff --git a/claude-plugins/plugins/base/skills/planning-gate/SKILL.md b/claude-plugins/plugins/base/skills/planning-gate/SKILL.md new file mode 100644 index 00000000..89bbb7f9 --- /dev/null +++ b/claude-plugins/plugins/base/skills/planning-gate/SKILL.md @@ -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. diff --git a/claude-plugins/plugins/base/skills/state-not-tmp/SKILL.md b/claude-plugins/plugins/base/skills/state-not-tmp/SKILL.md new file mode 100644 index 00000000..858008d0 --- /dev/null +++ b/claude-plugins/plugins/base/skills/state-not-tmp/SKILL.md @@ -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. diff --git a/claude-plugins/plugins/base/skills/status-and-blockers/SKILL.md b/claude-plugins/plugins/base/skills/status-and-blockers/SKILL.md new file mode 100644 index 00000000..17842a1d --- /dev/null +++ b/claude-plugins/plugins/base/skills/status-and-blockers/SKILL.md @@ -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.