14 Commits

Author SHA1 Message Date
Paul O'Reilly
562be1faf1 feat(spawn-session): skill wrapping claude-tmux to spawn Remote-Control sessions
Thin /spawn-session skill that resolves a project name and shells out to the
claude-tmux script to launch a new detached tmux Claude Code session with
Remote Control. Installed into the active profile; README catalogue updated.

Claude-Session: https://claude.ai/code/session_014oa7T7z1h5n34vu8vX9zGT
2026-07-25 16:29:46 +12:00
Paul O'Reilly
a71959c1c6 feat: upgrade ask-minimax skills from MiniMax-M2.7 to M3
M3 is now available on both api.minimax.io and api.minimaxi.chat
and has been verified working. Bump --model flag and all prose
references in both skills.

Claude-Session: https://claude.ai/code/session_01XtWRazqRbMnMRa4iV3zbVx
2026-07-17 21:22:36 +12:00
Paul O'Reilly
1b62c256f6 feat(end-session): wire RCA document write-back into planning-store step
Adds Step 2.4: when a session root-causes a real incident and the
project's planning store supports an incident type + rca-<date>-<slug>.md
convention, write the postmortem there via cp-cli and link affected
items via impacted/impacted_by. Skips cleanly for projects without
that convention. Adds a scoped cp_cli Bash allowlist entry.
2026-07-13 12:54:03 +12:00
Paul O'Reilly
36aa3d26ee feat(end-session): Step 2.3 planning-store status write-back
Session isn't finished until delivered work is written back to the project's
canonical planning store (cp-cli / local checkout / offline staging), and
roll-up docs are checked for contradictions. Motivated by the agent-runtimes
work-items repo drifting 6 weeks behind delivered reality.
2026-07-02 21:27:58 +12:00
Paul O'Reilly
ca77d3873a orchestrate: add dispatch pre-flight gate and 5-iteration stall circuit-breaker 2026-06-12 21:38:05 +12:00
Paul O'Reilly
574bd12c64 feat: add scan-ralph skill for polling ralph_code overnight results
Wraps scripts/scan-ralph-candidates to inject scan output at load time.
Shows unmerged agent-fork branches with changed files and inferred spec IDs.
Designed for /loop 5m /scan-ralph continuous polling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 18:06:58 +12:00
Paul O'Reilly
e5dbe95acb Add /ask-minimax-with-context skill
Walks CONTEXT.md and CLAUDE.md files from a given folder up to the git
root, assembles them into a context bundle via shell redirects (file
contents never enter the calling session), then delegates a one-line
question to MiniMax M2.7. Token cost to the main session: paths only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 21:43:29 +12:00
Paul O'Reilly
0d4b43dd5e Add /dispatch skill (F114): wave-based CP task submission
Implements DS-1..DS-13 from spec/dispatch-skill.md. Reads
.agent-tasks.json, validates structure, submits tasks to the control
plane in dependency-ordered waves, and records cp_task_ids atomically
after each submission. Supports --wait-wave, --continue-on-failure, and
--as-manifest flags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 13:45:11 +12:00
Paul O'Reilly
28ae10ea90 feat(review-spec): add mechanical-test-generation focus + tighten conditional topics
The skill now loads mechanical-test-generation.md as a 6th core file alongside
spec-driven-development, test-driven-development, api-design, llm-code-security,
and security-architecture. The previous "load related files based on the stack"
hint is replaced with an explicit conditional triage table covering agent-repos,
ai-parallel-agents, api-integration, secrets-management, database-selection,
docker, kubernetes, and validation, plus a skip list of implementation-time
topics that don't add value at spec-review time.

Adds a new "Review: mechanical test derivation" section with seven checks
(module layout, integration boundaries, library semantics, concrete interfaces,
error messages as test data, parametric pattern tables, pre-dispatch testability)
and a corresponding scorecard row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 08:24:06 +12:00
Paul O'Reilly
7d7856a079 Update /log skill to use transcript backups via Sonnet subagent; add switch-mode
- skills/log/SKILL.md: pre-gathers unprocessed transcript metadata (compact
  JSON via list-transcripts-here.sh) rather than full content; spawns a
  Sonnet subagent (not Haiku — gotcha detection requires judgment) to read
  JSONL backups, write a companion -transcripts.md log, and mark backups
  processed in tracking.json; adds Agent and Bash(bash|pwd|python3) to
  allowed-tools
- skills/switch-mode/SKILL.md: add previously untracked skill to repo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:06:20 +12:00
Paul O'Reilly
a95197b162 add manual-workflow skill
Run a workflow template from a Claude Code session via the local control
plane. Handles topological wave execution, artifact passing, sentinel
resolution, and human review gates locally — no CP-side workflow
expansion required.

The pre-gathered "Available workflows" context uses python3 (not a
ls|sed pipe) so the Bash permission checker accepts it without needing
sed in allowed-tools — piped commands fail the check even when both
sides match an allowed pattern individually.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:33:36 +12:00
Paul O'Reilly
3b954ff02a Add review-plan and review-spec skills
Two new read-only skills that review plans and specs against best practices:
- /review-plan: Checks plans against 6 areas (security, API design, LLM code
  security, spec-driven dev, TDD, operational readiness). Outputs scorecard
  with critical gaps and recommendations.
- /review-spec: Checks specs for structure quality, requirement testability,
  security coverage, and API design patterns. Scores 5 dimensions and
  identifies missing requirements and scenarios.

Both load api-design.md, llm-code-security.md, spec-driven-development.md,
test-driven-development.md, and security-architecture.md from best-practices.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 11:11:28 +13:00
Paul O'Reilly
e03b00843a Add reflected memory and update decompose/orchestrate skills
Adds decisions and process-lessons from recent reflections.
Updates decompose and orchestrate SKILL.md with operational improvements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:38:02 +13:00
Paul O'Reilly
ec36f05e86 Update distill-best-practices to target skynet/best-practices repo
All paths now point to ~/dev/claude/projects/best-practices/ instead of
claude-foundations/best-practices/. The settings.yaml remains in
claude-foundations (where the pipeline config lives).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 17:48:58 +13:00
22 changed files with 1948 additions and 79 deletions

8
.reflection-state.json Normal file
View File

@@ -0,0 +1,8 @@
{
"version": 1,
"last_run": "2026-03-24T10:36:46Z",
"processed": {
"memory/log/2026-03-17.110845.md": "5ac7f82c96613876a4ecf7f2b5506edf",
"memory/log/2026-03-24.215106.md": "3e039830da10338af26d31fc0959dff1"
}
}

View File

@@ -21,7 +21,9 @@ custom-claude-skills/
├── log/SKILL.md # End-of-session logging
├── orchestrate/SKILL.md # Container agent dispatch with git worktrees
├── reflect/SKILL.md # Milestone reflection
── reflect-logs/SKILL.md # Process logs into memory
── reflect-logs/SKILL.md # Process logs into memory
├── review-plan/SKILL.md # Review plan against best practices
└── review-spec/SKILL.md # Review spec against best practices
```
## Conventions

View File

@@ -11,6 +11,12 @@
- **Broad `Bash(git *)` is acceptable when justified:** orchestrate skill needs worktree/branch/merge/checkout — listing 6+ specific subcommands is worse than the broad pattern with a validator warning
- **Skills that launch containers need careful entrypoint handling:** Use `--entrypoint uid-wrapper.sh` when running `claude --print` in agent containers — the default entrypoint expects AGENT_PAYLOAD
## Topic Files
- [Process Lessons](memory/process-lessons.md) — Container agent workflow patterns, skill development rules
- [Decisions](memory/decisions.md) — Task orchestration architecture decisions, harness design rationale
- [skill-ask-minimax-with-context](memory/skill-ask-minimax-with-context.md) — Walk CONTEXT.md+CLAUDE.md up to git root, bundle via shell redirects, delegate question to MiniMax
## Task Orchestration Skills (2026-03-24)
Added `/decompose` and `/orchestrate` for parallel container agent work:

View File

@@ -25,6 +25,7 @@ The install script creates symlinks from `~/.claude/skills/` to this repo, makin
| housekeeping | `/housekeeping` | Cross-project health check: git status, unreflected logs, skill validation, pipeline recommendations |
| decompose | `/decompose <task>` | Break a task into subtasks with dependency graph. Writes `.agent-tasks.json` for container agent orchestration. |
| orchestrate | `/orchestrate` | Check task state, launch container agents in git worktrees. Use `/loop 2m /orchestrate` for auto-polling. |
| spawn-session | `/spawn-session <project>` | Spawn a new detached tmux Claude Code session with Remote Control, named after the project (wraps `claude-tmux`). Attach later via `tmux attach` over VPN or drive from the Claude app. |
## Adding a New Skill

21
memory/decisions.md Normal file
View File

@@ -0,0 +1,21 @@
# Architecture Decisions
## Git worktrees for container agent task isolation
Each container agent gets its own git worktree branch. This prevents file conflicts between parallel agents and makes merging results straightforward with standard git operations.
## No hardcoded concurrency limit
The `max_concurrent` value is set by the user during `/decompose`, not baked into the skill. Different tasks have different parallelism needs — a refactor across 10 files can run 10 agents, while a sequential pipeline needs 1-2.
## Container agents launched with docker run -d (no --rm)
Using `-d` without `--rm` ensures container logs survive for debugging. The orchestrator checks exit codes and retrieves logs from stopped containers. Cleanup is explicit, not automatic.
## Harness design is highest-leverage for agent quality
Research showed LangChain benchmark scores jumped from 52.8% to 66.5% from harness improvements alone (context injection, tool selection, prompt structure). Model choice matters less than giving the model good context and tools.
## Keep harness layers to 3-5 (95% step problem)
Each harness layer that must succeed is a multiplicative failure point. At 95% reliability per step, 10 steps = 60% overall success. Keep the critical path short — 3-5 layers max.

View File

@@ -0,0 +1,39 @@
# Session Log — 2026-03-24
## Summary
Designed the M3 composable agent harness architecture for agent-runtimes, wrote the full plan and had a container agent write the harness spec. Created two new skills (`/decompose` and `/orchestrate`) for task decomposition and container agent dispatch with git worktree isolation.
## Decisions
- Decision: Harnesses live in a separate `agent-harnesses` repo under skynet org — Rationale: Versioned independently from agent-runtimes, allows different teams/projects to share harness definitions
- Decision: Three harness kinds (capability, context, composite) — Rationale: Separation of concerns between container overlays (tools) and session config (identity, context, skills)
- Decision: Context files mount at unique paths per layer, use `--append-system-prompt-file` — Rationale: Claude Code's native mechanism, no lossy merging, preserves all context layers distinctly
- Decision: Heavy capability layers use OCI mod images (LinuxServer.io pattern) — Rationale: Runtime install too slow for JDK/Rust; single-layer OCI images with modcache provide fast cached extraction
- Decision: Harness-injected actions with payload suppress — Rationale: Cross-cutting concerns (session logging) belong in harness, but payload must be able to override
- Decision: Git worktrees for container agent task isolation — Rationale: No file conflicts between concurrent agents, clean per-task branches, dependency chains branch from parent output
- Decision: No hardcoded concurrency limit — user approves `max_concurrent` during `/decompose` — Rationale: Token usage happens regardless of parallelism; more agents = faster, not more expensive
- Decision: Container agents launched with `docker run -d` (no `--rm`) — Rationale: Logs must survive for inspection after container exit
## Gotchas Discovered
- **[docker]** Symptom: Container agent failed with "No payload" error when launched with `docker run ... agent-claude:latest claude --print ...` — Fix: Must use `--entrypoint uid-wrapper.sh` to override the default entrypoint (which expects AGENT_PAYLOAD env var). The `claude-container.sh` script handles this correctly.
- **[skills]** Symptom: validate-skill failed on decompose skill with "Command binary 'ls' not covered" — Fix: Bang-command `!`ls spec/`` requires `Bash(ls *)` in allowed-tools. Every binary in bang-commands must be explicitly covered.
- **[skills]** Symptom: validate-skill warned about `Bash(git *)` being too broad in orchestrate skill — Fix: Acceptable warning — orchestrate needs worktree add/remove, branch, merge, and checkout. Specific subcommand patterns would need 6+ entries.
## Key Context
- LangChain research showed 52.8% → 66.5% improvement on Terminal Bench by modifying only the harness, not the model — validates harness design as highest-leverage work
- Claude Code's `--append-system-prompt-file` is the native context injection mechanism (one flag per file, preserves built-in prompt)
- Skills auto-discovered from `.claude/skills/` — just mount them into containers
- Anthropic's reference devcontainer uses iptables firewall allowlisting (adopted into our harness design)
- OpenCode reads `CLAUDE.md` and `~/.claude/skills/` by default — cross-tool compatibility is free
- K8s Agent Sandbox CRD (SIG Apps, March 2026) worth evaluating for M9
- 95% step problem: 20 steps at 95% each = 36% success — keep harness layers to 3-5
## Process Notes
- Container agent successfully wrote a 426-line spec from a detailed prompt — validates the pattern of using container agents for substantial spec/code work
- Research phase used 3 parallel agents effectively: LinuxServer.io patterns, broader container composition, and existing codebase analysis
- Second research round (Claude devcontainers, OpenCode, 2026 best practices) surfaced important design refinements that improved the plan
- The `/decompose` + `/orchestrate` + `/loop` workflow creates a "manager session" pattern where the human drives strategy while agents execute in parallel

17
memory/process-lessons.md Normal file
View File

@@ -0,0 +1,17 @@
# Process Lessons
## Container agent prompts need high detail for quality output
Container agents produce substantial output (e.g., 426-line spec) when given detailed, structured prompts. Invest time in prompt crafting during `/decompose` — the agent has no conversation history to draw from.
## Use 3+ parallel research agents before planning
Survey different domains (competing tools, best practices, user patterns) in parallel before committing to an architecture. The breadth of input prevents tunnel vision during planning.
## The decompose/orchestrate/loop pattern creates a manager session
Human drives strategy via `/decompose`, agents execute via `/orchestrate`, and `/loop 2m /orchestrate` auto-polls progress. The human's role shifts from doing to reviewing and steering.
## Always run validate-skill before committing skill changes
The validator catches restriction violations (uncovered binaries, `$()` substitution, `${VAR}` syntax) that silently break skills at load time. Run it as a pre-commit gate, not an afterthought.

View File

@@ -0,0 +1,35 @@
# Skill: ask-minimax-with-context
## Purpose
Delegate a focused question to MiniMax M2.7 with automatically gathered project context, without loading any file contents into the current session.
## Usage
```
/ask-minimax-with-context <folder-path> <question>
```
Example: `/ask-minimax-with-context octopus/staging/ Has build 1.4.202 been deployed?`
## How It Works
1. Parses args: first token = folder path, rest = question
2. Walks from the given folder up to the git root collecting `CONTEXT.md` and `CLAUDE.md` at each level (deepest-first)
3. Assembles a context bundle at `/tmp/ask-minimax-ctx-<TS>.md` via shell redirects — file contents never enter the calling session's context
4. Writes a prompt file telling MiniMax to read the bundle and answer the question
5. Runs `ask-minimax-run` (MiniMax M2.7, read+write tools only)
6. Reads the result file and summarises concisely
7. Cleans up temp files (keeps result file)
## When to Use
- Any question where the answer lives in CONTEXT.md or CLAUDE.md files in a project tree
- Even simple questions — overhead is one line from the user, context savings are real
- Alternative to loading large context hierarchies into the main session just to answer a single question
## Gotchas
- Path must resolve to a directory that is inside a git repo (used to find the walk ceiling)
- If no CONTEXT.md or CLAUDE.md files are found, the skill warns and asks whether to proceed with the question only
- Requires the MiniMax profile at `~/.claude-oreillyit-minimax/provider.env`

View File

@@ -0,0 +1,170 @@
---
name: ask-minimax-with-context
description: >
Walk CONTEXT.md and CLAUDE.md files from a folder up to the git root, assemble them into
a context bundle via shell redirects (contents never enter this session), then delegate a
one-line question to MiniMax M3. Saves context and Anthropic tokens even for simple questions.
Usage: /ask-minimax-with-context <folder-path> <question>
user_invocable: true
allowed-tools: Read, Write, Bash(ask-minimax-run *), Bash(date *), Bash(rm -f *), Bash(test *), Bash(pwd), Bash(bash *)
---
<command-name>ask-minimax-with-context</command-name>
# /ask-minimax-with-context Skill
Walk `CONTEXT.md` and `CLAUDE.md` files from a given folder up to the git root. Assemble them into a context bundle via shell redirects — file contents never appear in this session. Then delegate a one-line question to MiniMax; only a concise answer flows back.
**Usage:** `/ask-minimax-with-context <folder-path> <question>`
**Example:** `/ask-minimax-with-context octopus/staging/ Has build 1.4.202 been deployed?`
## Pre-gathered context
### Current date
!`date +%Y-%m-%d`
### Working directory
!`pwd`
## Instructions
### Step 0 — Verify profile readiness
```bash
test -f ~/.claude-oreillyit-minimax/provider.env && echo "minimax profile: ready" || echo "minimax profile: MISSING"
```
If MISSING, tell the user the profile is not configured and stop.
### Step 1 — Parse arguments
From `$ARGUMENTS`:
- **Path**: the first whitespace-delimited token
- **Question**: everything after the first token
If either is missing, tell the user the correct usage format and stop.
### Step 2 — Generate a timestamp
```bash
date +%s
```
Note the integer as `TS`. Use it as a suffix on all temp files.
### Step 3 — Walk the tree and assemble the context bundle
Run the following bash script, substituting the literal values of `PATH_ARG` (from Step 1) and `TS` (from Step 2) before executing. Do not use shell variables as placeholders — write the actual values inline.
The script resolves the path to absolute, walks up to the git root collecting `CONTEXT.md` and `CLAUDE.md` at each level (deepest first), and shell-redirects each file into the bundle. File contents never appear in stdout — only a summary line is returned.
```bash
bash -c '
set -e
path="PATH_ARG"
ts="TS"
bundle="/tmp/ask-minimax-ctx-${ts}.md"
# Resolve to absolute path
case "$path" in
/*) ;;
*) path="$(pwd)/$path" ;;
esac
path="${path%/}"
# Find git root (stop walking here)
git_root=$(git -C "$path" rev-parse --show-toplevel 2>/dev/null || echo "")
[ -z "$git_root" ] && git_root="/"
# Walk from given dir up to git root
dir="$path"
count=0
while true; do
for fname in CONTEXT.md CLAUDE.md; do
fpath="$dir/$fname"
if [ -f "$fpath" ]; then
echo "## Context from: $fpath" >> "$bundle"
echo "" >> "$bundle"
cat "$fpath" >> "$bundle"
echo "" >> "$bundle"
echo "---" >> "$bundle"
echo "" >> "$bundle"
count=$((count + 1))
fi
done
[ "$dir" = "$git_root" ] && break
parent=$(dirname "$dir")
[ "$parent" = "$dir" ] && break
dir="$parent"
done
echo "Bundle assembled: $bundle ($count files)"
'
```
If the output says `(0 files)`, warn the user that no context files were found and ask whether to proceed with the question only.
### Step 4 — Write the prompt file
Use the Write tool to create `/tmp/ask-minimax-TS-prompt.txt` (replace `TS` with the actual timestamp integer). Fill in all values — no placeholders in the written file.
```
You are running in non-interactive (-p) mode. Read the context bundle and answer
the question concisely. Write your full response to the output file specified below.
## Context bundle
Read this file fully before answering: /tmp/ask-minimax-ctx-TS.md
The bundle contains CONTEXT.md and CLAUDE.md files collected from a project directory
tree, deepest-first. Each section is prefixed with "## Context from: <path>" so you
know where each piece of context comes from.
## Question
QUESTION
## Output file
Write your complete response to: /tmp/ask-minimax-TS-result.md
Use the Write tool. Overwrite if it already exists.
## Constraints
- Read the context bundle fully before answering
- Keep your answer concise — this is a focused question, not an open-ended task
- Write the complete result to the output file
- One-line stdout acknowledgement is fine; content goes in the output file
```
### Step 5 — Run the sub-session
Substitute `TS` with the actual timestamp integer before running:
```bash
ask-minimax-run \
--model MiniMax-M3 \
--allowedTools "Read,Write" \
--dangerously-skip-permissions \
-p "$(cat /tmp/ask-minimax-TS-prompt.txt)" \
> /tmp/ask-minimax-TS-stdout.txt 2>&1
```
Show the exit code after it completes.
### Step 6 — Collect and present results
1. Read `/tmp/ask-minimax-TS-result.md` using the Read tool
2. If not found, read `/tmp/ask-minimax-TS-stdout.txt` as fallback
3. Present a concise summary — do not dump the full file into the conversation
4. Tell the user the result file path if they want the full output
### Step 7 — Clean up
```bash
rm -f /tmp/ask-minimax-ctx-TS.md /tmp/ask-minimax-TS-prompt.txt /tmp/ask-minimax-TS-stdout.txt
```
Keep the result file.

127
skills/ask-minimax/SKILL.md Normal file
View File

@@ -0,0 +1,127 @@
---
name: ask-minimax
description: >
Delegate a file-heavy task to a MiniMax M3 sub-session. The sub-session reads and
writes files directly; only a concise result returns to this session. Saves context and
Anthropic tokens. Pass a task description as arguments, or run without args to be prompted.
user_invocable: true
allowed-tools: Read, Write, Bash(ask-minimax-run *), Bash(date *), Bash(rm -f *), Bash(test *), Bash(pwd)
---
<command-name>ask-minimax</command-name>
# /ask-minimax Skill
Offload a file-heavy task to a MiniMax M3 sub-session. Large file contents stay out of this session — only the task prompt and a concise result summary flow back.
## Pre-gathered context
### Current date
!`date +%Y-%m-%d`
## Instructions
**Goal:** Run a scoped task in MiniMax so its file reads and writes never load into this context. You write a prompt file, invoke the sub-session, read the result file, and summarise.
### Step 0 — Verify profile readiness
Run via Bash:
```bash
test -f ~/.claude-oreillyit-minimax/provider.env && echo "minimax profile: ready" || echo "minimax profile: MISSING"
```
If MISSING, tell the user the profile is not configured and stop.
### Step 1 — Clarify the task
Use `$ARGUMENTS` as the task description. If empty or incomplete, ask the user:
- What should the sub-session do? (transform, extract, rewrite, summarise, generate?)
- Which file paths should it read? (give absolute paths)
- Where should it write output? (default: `/tmp/ask-minimax-result.md`)
- Any constraints on format, length, or structure?
Do **not** read the input files yourself — pass paths only. The sub-session reads them.
### Step 2 — Generate a timestamp
Run the following via Bash and note the integer value as `TS`:
```bash
date +%s
```
Use `TS` as a suffix on all temp files: `/tmp/ask-minimax-<TS>-prompt.txt`, `/tmp/ask-minimax-<TS>-result.md`, `/tmp/ask-minimax-<TS>-stdout.txt`.
### Step 3 — Write the prompt file
Use the Write tool to create `/tmp/ask-minimax-<TS>-prompt.txt`. Substitute `<TS>` with the actual timestamp integer. Structure the content as:
```
You are running in non-interactive (-p) mode. Complete the following task and write
your full response to the output file path specified below. Do not rely on stdout for
structured output — use the Write tool.
## Task
<task description from the user>
## Files to read
<list each absolute file path on its own line>
## Output file
Write your complete response to: /tmp/ask-minimax-<TS>-result.md
Use the Write tool to create that file. Overwrite if it already exists.
## Constraints
- Read each input file fully before starting
- Write the complete result to the output file
- One-line stdout acknowledgement is fine; bulk content goes in the output file
```
Fill in the actual task description, file paths, and timestamp — no placeholders in the written file.
### Step 4 — Run the sub-session
Execute via Bash (substitute `<TS>` with the actual value):
```bash
ask-minimax-run \
--model MiniMax-M3 \
--allowedTools "Read,Write,Edit,Glob,Grep" \
--permission-mode acceptEdits \
-p "$(cat /tmp/ask-minimax-<TS>-prompt.txt)" \
> /tmp/ask-minimax-<TS>-stdout.txt 2>&1
```
This runs synchronously. Warn the user if the task involves very large files — it may take a minute or two. Show the exit code after it completes.
**Permissions:** do **not** pass `--dangerously-skip-permissions`. The sub-session is scoped by `--allowedTools` — only those tools run without a prompt. `--permission-mode acceptEdits` lets it write/edit files non-interactively within that allowlist without a blanket bypass. In headless `-p` mode any tool **outside** the allowlist is denied (the sub-session continues; it does not hang), so the allowlist is the security boundary — keep it minimal.
**Adjusting `--allowedTools`**: Default covers read + write tasks. For read-only analysis, use `"Read,Glob,Grep"` (and drop `--permission-mode acceptEdits`). For heavy generation, keep `"Read,Write,Edit,Glob,Grep"`. If a task genuinely needs the sub-session to run commands, add a **scoped** Bash pattern (e.g. `Bash(pytest *)`) to the allowlist rather than reaching for the skip flag.
### Step 5 — Collect and present results
1. Read `/tmp/ask-minimax-<TS>-result.md` using the Read tool
2. If not found: read `/tmp/ask-minimax-<TS>-stdout.txt` as fallback
3. Present a concise summary to the user — do **not** dump the full file contents into the conversation
4. Tell the user the result file path so they can read it themselves if they want the full output
### Step 6 — Clean up
Remove the prompt and stdout temp files (keep the result file):
```bash
rm -f /tmp/ask-minimax-<TS>-prompt.txt /tmp/ask-minimax-<TS>-stdout.txt
```
## Usage patterns
- **Large file transformation**: Pass the source path; sub-session reads, transforms, writes output
- **Multi-file summarisation**: Pass all file paths; get a single consolidated result
- **Bulk generation**: Describe what to write and where; full generated content lands in the result file without touching this context
- **Format conversion**: Pass input path and target format; result file gets the converted content

View File

@@ -63,6 +63,8 @@ For each subtask, determine:
- **End every prompt with:** "Run `pytest tests/ -v --tb=short` and fix any failures before finishing. Write a session log to memory/log/ when done."
- **State import conventions explicitly** in prompts — e.g., "Use `from module import X`, not `from .module import X`" when source dirs aren't packages
- **Before writing .agent-tasks.json, warn the user to commit WIP** if there are untracked/uncommitted files that agents will need. Worktrees only see committed content.
- **Set the `model` field** for each task. Use `claude-opus-4-20250514` for research, architecture, and complex reasoning tasks. Use `claude-sonnet-4-20250514` (or omit for default) for code generation, testing, and mechanical tasks. Ask the user if unsure.
- **Do NOT include "use web search" in prompts.** Container agents cannot web search. If a task requires current data verification, note this in the task description so the user can validate from their main session after the agent completes.
### Step 3: Present the task graph
@@ -102,6 +104,7 @@ Once approved, write `.agent-tasks.json` in the project root:
"<task-id>": {
"name": "<human readable name>",
"prompt": "<full prompt for container agent>",
"model": "<optional model override, e.g. claude-opus-4-20250514>",
"depends_on": ["<task-id>", ...],
"reads": ["<file paths the agent should read>"],
"writes": ["<file paths the agent will create/modify>"],

372
skills/dispatch/SKILL.md Normal file
View File

@@ -0,0 +1,372 @@
---
name: dispatch
description: >
Submit .agent-tasks.json to the control plane in dependency-ordered waves. Records
cp_task_ids atomically. Use after /decompose to fire a task batch at the CP.
user_invocable: true
allowed-tools: Read, Write, Bash(python3 *), Bash(scripts/dispatch-task *), Bash(scripts/wait-for-tasks *), Bash(ls *), Bash(date *), Bash(cat *)
---
# /dispatch Skill
<command-name>dispatch</command-name>
You are dispatching a batch of tasks to the agent-runtimes control plane.
## Pre-gathered context
### Task file
!`cat .agent-tasks.json 2>/dev/null || echo "NO_TASK_FILE"`
### Available templates
!`ls task-templates/ 2>/dev/null || echo "NO_TEMPLATES_DIR"`
### Current date
!`date +%Y-%m-%dT%H:%M:%S`
---
## Instructions
Parse `$ARGUMENTS` for flags:
- `--wait-wave` → block until each wave reaches a terminal state before submitting the next
- `--continue-on-failure` → submit the next wave even if the current wave had failures (only relevant with `--wait-wave`)
- `--as-manifest` → translate `.agent-tasks.json` to a `POST /manifests` payload and print to stdout; do NOT submit tasks
### Step 1: Check task file exists
If the pre-gathered task file output is `NO_TASK_FILE`, say:
```
No .agent-tasks.json found. Run /decompose to create one.
```
Stop.
### Step 2: Validate token file (DS-10)
Run:
```bash
python3 -c "
import os, stat, sys, json
path = os.path.expanduser('~/.config/agent-runtimes/tokens.json')
parent = os.path.dirname(path)
errors = []
if not os.path.exists(path):
errors.append({'type': 'token_missing', 'detail': f'Token file not found at {path}. Run \"scripts/dispatch-task --login\" to obtain a token.'})
else:
mode = stat.S_IMODE(os.stat(path).st_mode)
if mode != 0o600:
errors.append({'type': 'token_perms', 'detail': f'Token file mode is {oct(mode)}; expected 0600. Run \"chmod 600 {path}\".'})
if os.path.isdir(parent):
dmode = stat.S_IMODE(os.stat(parent).st_mode)
if dmode != 0o700:
errors.append({'type': 'dir_perms', 'detail': f'Token directory mode is {oct(dmode)}; expected 0700. Run \"chmod 700 {parent}\".'})
if errors:
print(json.dumps({'type': 'validation_error', 'errors': errors}, indent=2))
sys.exit(1)
print('ok')
"
```
If the output is not `ok`, display the error JSON and stop. Do not attempt any submissions.
### Step 3: Parse and validate .agent-tasks.json (DS-1, DS-2)
Run:
```bash
python3 -c "
import json, sys, os
with open('.agent-tasks.json') as f:
data = json.load(f)
errors = []
# Check top-level structure
required_top = {'params', 'tasks'}
known_top = {'params', 'tasks'}
unknown_keys = set(data.keys()) - known_top
if unknown_keys:
errors.append({'field': '<top-level>', 'reason': f'Unknown top-level key(s): {sorted(unknown_keys)}'})
# Validate params
params = data.get('params', {})
for field in ['repo_url', 'agent_repo_url', 'project_id']:
if not params.get(field):
errors.append({'field': f'params.{field}', 'reason': 'Required field missing or empty'})
# Check for userinfo in URLs (DS-6)
import urllib.parse
for url_field in ['repo_url', 'agent_repo_url']:
url = params.get(url_field, '')
try:
p = urllib.parse.urlparse(url)
if p.password or (p.username and '@' in url):
errors.append({'field': f'params.{url_field}', 'reason': f'{url_field} contains userinfo. Pass credentials via SSH keys or env vars, not URL userinfo.'})
except Exception:
pass
# Validate tasks array
tasks = data.get('tasks', [])
if not isinstance(tasks, list) or len(tasks) == 0:
errors.append({'field': 'tasks', 'reason': 'Must be a non-empty array'})
# Validate each task
task_ids = set()
for i, task in enumerate(tasks):
prefix = f'tasks[{i}]'
for field in ['id', 'name', 'prompt']:
if not task.get(field):
errors.append({'field': f'{prefix}.{field}', 'reason': 'Required field missing or empty'})
tid = task.get('id', '')
if tid:
if tid in task_ids:
errors.append({'field': f'{prefix}.id', 'reason': f'Duplicate task id: {tid!r}'})
task_ids.add(tid)
if not task.get('template') and not task.get('model'):
errors.append({'field': f'{prefix}.template/model', 'reason': 'One of template or model is required'})
if task.get('template') and task.get('model'):
errors.append({'field': f'{prefix}.template/model', 'reason': 'Only one of template or model is allowed, not both'})
if not isinstance(task.get('depends_on', []), list):
errors.append({'field': f'{prefix}.depends_on', 'reason': 'Must be an array'})
# Validate depends_on references
for task in tasks:
for dep in task.get('depends_on', []):
if dep not in task_ids:
errors.append({'field': f'tasks[{task[\"id\"]}].depends_on', 'reason': f'Unknown task id reference: {dep!r}'})
# Validate templates exist (DS-7)
templates_dir = 'task-templates'
if os.path.isdir(templates_dir):
valid_templates = {f.replace('.yaml', '') for f in os.listdir(templates_dir) if f.endswith('.yaml')}
for task in tasks:
tmpl = task.get('template')
if tmpl and tmpl not in valid_templates:
errors.append({'field': f'tasks[{task.get(\"id\",\"?\")}].template', 'reason': f'Unknown template {tmpl!r}. Valid: {sorted(valid_templates)}'})
if errors:
print(json.dumps({'type': 'validation_error', 'title': 'Validation failed', 'invalid-params': errors}, indent=2))
sys.exit(1)
print('ok')
"
```
If not `ok`, display the error JSON and stop.
### Step 3b: --as-manifest translation (DS-13)
If `$ARGUMENTS` contains `--as-manifest`, run:
```bash
python3 -c "
import json, sys
RESERVED_KEYS = {'manifest_id', 'workflow_id', 'definition_version'}
with open('.agent-tasks.json') as f:
data = json.load(f)
params = data.get('params', {})
# Check reserved keys (MN-31)
reserved_found = set(params.keys()) & RESERVED_KEYS
if reserved_found:
for k in reserved_found:
print(json.dumps({'type': 'validation_error', 'detail': f'params contains reserved key {k!r}. CP injects this automatically (MN-31).'}), file=sys.stderr)
sys.exit(1)
# Build manifest
nodes = []
for task in data.get('tasks', []):
node = {
'node_id': task['id'],
'prompt': task['prompt'],
'depends_on': task.get('depends_on', []),
}
if task.get('template'):
node['template'] = task['template']
if task.get('model'):
node['model'] = task['model']
nodes.append(node)
manifest = {
'definition_version': 1,
'params': params,
'nodes': nodes,
}
print(json.dumps(manifest, indent=2))
"
```
Print the output and stop. Do not submit any tasks.
### Step 4: Build wave structure
Run:
```bash
python3 -c "
import json, sys
with open('.agent-tasks.json') as f:
data = json.load(f)
tasks = data['tasks']
task_map = {t['id']: t for t in tasks}
# Topological sort into waves
waves = []
resolved = set()
remaining = set(t['id'] for t in tasks)
while remaining:
wave = []
for tid in list(remaining):
task = task_map[tid]
deps = set(task.get('depends_on', []))
if deps.issubset(resolved):
wave.append(tid)
if not wave:
print(json.dumps({'error': 'Circular dependency detected in tasks'}), file=sys.stderr)
sys.exit(1)
waves.append(sorted(wave))
for tid in wave:
resolved.add(tid)
remaining.discard(tid)
# Output wave assignments
result = {}
for wave_num, wave_ids in enumerate(waves, 1):
for tid in wave_ids:
result[tid] = wave_num
print(json.dumps({'waves': waves, 'wave_for_task': result}))
"
```
Store the wave structure. Present it to the user as:
```
Wave 1: <task ids>
Wave 2: <task ids> (depends on wave 1)
...
```
### Step 5: Check CP_URL
Use `CP_URL` from the environment if set. Default: `https://agents.oreillyit.nz/api`
### Step 6: Submit tasks wave by wave
For each wave in order:
**6a. Identify tasks to submit in this wave:**
- Skip tasks where `cp_task_id` is already set (non-null) — these are already submitted (DS-11)
- But first verify each already-submitted task still exists on the CP:
```bash
scripts/dispatch-task status <cp_task_id> 2>&1
```
If any returns 404: print structured error and stop:
```
Recorded cp_task_id '<id>' for task '<task_id>' returned 404 from CP. Clear cp_task_id in .agent-tasks.json to re-submit.
```
**6b. Submit each remaining task in the wave:**
For each task, build the dispatch-task command:
```bash
CP_URL=<cp_url> scripts/dispatch-task \
--name "<task.name>" \
--project-id "<params.project_id>" \
--prompt "<task.prompt>" \
[--template "<task.template>" | --model "<task.model>"] \
--template-param "repo_url=<params.repo_url>" \
--template-param "agent_repo_url=<params.agent_repo_url>"
```
Run the submission. Capture stdout — it contains the returned `cp_task_id` (UUID on its own line, or JSON).
**6c. Extract cp_task_id from output:**
Parse the dispatch-task output to extract the task UUID. Look for a UUID pattern: `[0-9a-f-]{36}`.
**6d. Record cp_task_id atomically (DS-3):**
After each successful submission, atomically update `.agent-tasks.json`:
```bash
python3 -c "
import json, os, tempfile
with open('.agent-tasks.json') as f:
data = json.load(f)
for task in data['tasks']:
if task['id'] == '<task_id>':
task['cp_task_id'] = '<cp_task_id>'
break
# Atomic write: tempfile + rename (DS-3)
with tempfile.NamedTemporaryFile(mode='w', dir='.', delete=False, suffix='.tmp') as tf:
json.dump(data, tf, indent=2)
tmpname = tf.name
os.rename(tmpname, '.agent-tasks.json')
print('recorded')
"
```
**6e. If submission fails (non-zero exit from dispatch-task):**
- Record the per-task error: `Task '<id>' submission failed: <error output>`
- Continue with the rest of the wave (DS-5)
- Set an overall failure flag (exit 1 at end)
**6f. If --wait-wave flag is set (DS-4):**
After submitting all tasks in the wave, collect all cp_task_ids for this wave and run:
```bash
CP_URL=<cp_url> scripts/wait-for-tasks <cp_task_id_1> <cp_task_id_2> ...
```
If wait-for-tasks exits non-zero (any task failed/cancelled/timed_out):
- Report the failure: show which tasks failed
- Unless `--continue-on-failure` is set, **stop here** and do not submit the next wave
- Exit 1
### Step 7: Print monitoring command (DS-8)
After all waves are submitted, print:
```
Run: CP_URL=<cp_url> scripts/agent-monitor --login --filter "project=<project_id>" --filter "age<2h"
```
### Step 8: Final exit
- If any submission failed → exit 1
- If `--wait-wave` and any wave task reached a non-success terminal state → exit 1
- Otherwise → exit 0
---
## Error output format
All errors are printed as JSON to stderr:
```json
{
"type": "https://agent-runtimes.oreillyit.nz/errors/dispatch-validation",
"title": "Validation failed",
"detail": "...",
"invalid-params": [{"name": "field", "reason": "reason"}]
}
```

View File

@@ -2,12 +2,10 @@
name: distill-best-practices
description: >
Cross-project best practices distillation. Reads changed memory files from all tracked
projects and proposes additions, updates, or removals to the skynet/best-practices repo
(gitea.oreillyit.nz/skynet/best-practices). Approved changes are committed and pushed to
that repo, then synced back to claude-foundations/best-practices/ for backward compatibility.
Run from any project -- always targets skynet/best-practices as output. Interactive -- presents
proposals for approval before making changes.
allowed-tools: Read, Edit, Write, Glob, Grep, Bash(git clone *), Bash(git -C *), Bash(git rev-parse *), Bash(ls *), Bash(cat *), Bash(head *), Bash(date *), Bash(mktemp *), Bash(rm -rf *), Bash(cp *)
projects and proposes additions, updates, or removals to ~/dev/claude/projects/best-practices/.
Run from any project -- always targets skynet/best-practices repo as output. Interactive --
presents proposals for approval before making changes.
allowed-tools: Read, Edit, Write, Glob, Grep, Bash(git -C *), Bash(git rev-parse *), Bash(ls *), Bash(cat *), Bash(head *), Bash(date *)
---
# Distill Best Practices Skill
@@ -18,39 +16,23 @@ You are extracting generalisable best practices from project-specific memory fil
### Pre-gathered context (read at runtime)
The following files should be read using the Read tool at the start of execution:
- `~/dev/claude/projects/claude-foundations/best-practices/.distill-state.json` — Distill state (if not found, treat as `{}`)
- `~/dev/claude/projects/best-practices/.distill-state.json` — Distill state (if not found, treat as `{}`)
- `~/dev/claude/projects/claude-foundations/settings.yaml` — Settings (if not found, use defaults from instructions)
- `~/dev/claude/projects/best-practices/BESTPRACTICES.md` — Current best-practices index
- Use Glob to list `~/dev/claude/projects/best-practices/*.md` for available topic files
- Use Glob to list `~/dev/claude/projects/*/` for the projects directory listing
**Then clone the skynet/best-practices repo** (see Step 0 below) and read from it:
- `<tmpdir>/BESTPRACTICES.md` — Current best-practices index (from cloned repo)
- Use Glob to list `<tmpdir>/*.md` for available topic files (from cloned repo)
## Instructions
### Step 0: Clone skynet/best-practices
Before reading any best-practices content, clone the dedicated repo to a temp directory:
```
tmpdir=$(mktemp -d)
git clone https://gitea.oreillyit.nz/skynet/best-practices "$tmpdir/best-practices"
```
The working path for the cloned repo is `$tmpdir/best-practices`. Use this path throughout — referred to below as `<bp-repo>`.
Read `<bp-repo>/BESTPRACTICES.md` and list `<bp-repo>/*.md` to discover available topic files.
### Step 1: Discover changes per project
The project root is `~/dev/claude` (hardcoded — change at the top of the `!`command`` blocks if your layout differs).
Key paths:
- **Settings**: `~/dev/claude/projects/claude-foundations/settings.yaml`
- **Best practices repo (primary)**: `<bp-repo>/` (cloned skynet/best-practices)
- **Best practices local copy (backward compat)**: `~/dev/claude/projects/claude-foundations/best-practices/`
- **Best practices index**: `<bp-repo>/BESTPRACTICES.md`
- **Distill state**: `~/dev/claude/projects/claude-foundations/best-practices/.distill-state.json`
- **Best practices dir**: `~/dev/claude/projects/best-practices/`
- **Best practices index**: `~/dev/claude/projects/best-practices/BESTPRACTICES.md`
- **Distill state**: `~/dev/claude/projects/best-practices/.distill-state.json`
Read `settings.yaml` (pre-gathered above).
@@ -83,7 +65,7 @@ Also identify which existing best-practices topic files cover the same domain. U
- `decisions.md` → various (match by content)
- Other gotchas → match by topic or propose a new file
Read the matching best-practices files from `<bp-repo>/` so you can compare.
Read the matching best-practices files so you can compare.
### Step 3: Analyse and propose
@@ -120,38 +102,18 @@ Wait for the user to approve, modify, or reject proposals. The user may say "all
### Step 5: Apply approved changes
For each approved proposal, apply changes to `<bp-repo>/`:
- **ADD**: Append the new entry to the target file in `<bp-repo>/`, matching the existing style (heading level, bullet format, explanation depth)
- **UPDATE**: Edit the existing entry in place in `<bp-repo>/`
- **REMOVE**: Delete the entry from the file in `<bp-repo>/`
For each approved proposal:
- **ADD**: Append the new entry to the target file, matching the existing style (heading level, bullet format, explanation depth)
- **UPDATE**: Edit the existing entry in place
- **REMOVE**: Delete the entry from the file
If a new best-practices topic file is needed:
- Create it in `<bp-repo>/` following the format of existing files (top-level heading, subheadings per entry, 2-6 lines per entry)
- Add it to `<bp-repo>/BESTPRACTICES.md` with a one-line description
- Create it following the format of existing files (top-level heading, subheadings per entry, 2-6 lines per entry)
- Add it to `~/dev/claude/projects/best-practices/BESTPRACTICES.md` with a one-line description
**Sync to local copy for backward compatibility**: After applying all changes to `<bp-repo>/`, copy the modified files to `~/dev/claude/projects/claude-foundations/best-practices/` using `cp`:
- `cp <bp-repo>/BESTPRACTICES.md ~/dev/claude/projects/claude-foundations/BESTPRACTICES.md`
- For each modified topic file: `cp <bp-repo>/<file>.md ~/dev/claude/projects/claude-foundations/best-practices/<file>.md`
### Step 6: Update distill state
### Step 6: Commit and push to skynet/best-practices
Stage and commit the changes in the cloned repo, then push:
```
git -C <bp-repo> add -A
git -C <bp-repo> commit -m "distill: apply best practice updates from project memory
Applied: <count> additions, <count> updates, <count> removals
Sources: <comma-separated list of source projects>"
git -C <bp-repo> push
```
Report the outcome (commit SHA, push success/failure).
### Step 7: Update distill state
Write `~/dev/claude/projects/claude-foundations/best-practices/.distill-state.json`:
Write `~/dev/claude/projects/best-practices/.distill-state.json`:
```json
{
@@ -169,22 +131,13 @@ Write `~/dev/claude/projects/claude-foundations/best-practices/.distill-state.js
Preserve entries for projects that weren't processed this run (no changes).
### Step 8: Clean up temp directory
Remove the temp directory used for the clone:
```
rm -rf "$tmpdir"
```
### Step 9: Summary
### Step 7: Summary
Print:
- Projects scanned and number of changed memory files per project
- Number of proposals (add/update/remove)
- Number approved and applied
- Any new best-practices files created
- Commit SHA pushed to skynet/best-practices
## Quality checks
@@ -193,4 +146,3 @@ Print:
- Entries must be **deduplicated** against existing best-practices content
- The **BESTPRACTICES.md** must stay accurate after any file additions
- Proposals are **always presented before applying** — never auto-apply
- Changes go to **skynet/best-practices** (primary) and are synced to **claude-foundations/best-practices/** (backward compat)

269
skills/end-session/SKILL.md Normal file
View File

@@ -0,0 +1,269 @@
---
name: end-session
description: >
End-of-session wrap-up. Captures a session log, updates CONTEXT.md for seamless
resumption, and refreshes project docs (README, CLAUDE.md, FUTURE.md) with anything
that changed this session. Just run /end-session when you're done working.
allowed-tools: Read, Edit, Write, Grep, Glob, Bash(git log *), Bash(git diff *), Bash(date *), Bash(ls *), Bash(cat *), Bash(find *), Bash(rm *), Bash(mkdir *), Bash(sed *), Bash(python3 -m lib.cp_cli *)
---
# End-of-Session Skill
You are wrapping up a session. This skill captures a session log, updates CONTEXT.md
for resumption, and refreshes project documentation with changes from this session.
## Pre-gathered context
### Current date and session timestamp
!`date +%Y-%m-%d`
!`date +%Y%m%d-%H%M%S`
### Git log (last 30 commits)
!`git log --oneline -30 2>/dev/null || echo "Not a git repo"`
### Git status
!`git status --short 2>/dev/null || echo "Not a git repo"`
### Current MEMORY.md
!`cat MEMORY.md 2>/dev/null || echo "No MEMORY.md found"`
### Current CONTEXT.md
!`cat CONTEXT.md 2>/dev/null || echo "No CONTEXT.md found"`
### Current FUTURE.md
!`cat FUTURE.md 2>/dev/null || echo "No FUTURE.md found"`
### Current README.md (scripts section)
!`sed -n '/## Scripts/,/^## /p' README.md 2>/dev/null | head -60 || echo "No Scripts section found"`
### Current README.md (milestone table)
!`sed -n '/## Milestones/,/^## /p' README.md 2>/dev/null | head -40 || echo "No Milestones section found"`
### Available scripts
!`ls -1 scripts/ 2>/dev/null || echo "No scripts directory"`
### Existing log files
!`ls -1 memory/log/ 2>/dev/null || echo "No log directory yet"`
### Reflection state
!`cat .reflection-state.json 2>/dev/null || echo "No reflection state yet"`
### Settings
(Read `~/dev/claude/projects/claude-foundations/settings.yaml` using the Read tool. If not found, use defaults: retention_days=7, warn_unreflected_days=14)
---
## Phase 1: Session Log
### Step 1.1: Create the log directory if needed
If `memory/log/` does not exist, create it with `mkdir -p memory/log/`.
### Step 1.2: Write the log file
Review the **full conversation history** and extract key points worth preserving. If the session was trivial (a typo fix, a quick question), note that and write a minimal log.
Generate the filename as `memory/log/YYYY-MM-DD.<HHMMSS>.md` using the pre-gathered date and timestamp values.
Write the file using this format:
```markdown
# Session Log -- YYYY-MM-DD
## Summary
<!-- 1-2 sentence overview of what was accomplished -->
## Decisions
<!-- Omit section if none -->
- Decision: <what> -- Rationale: <why>
## Gotchas Discovered
<!-- Omit section if none -->
- **[topic]** Symptom: <what happened> -- Fix: <what resolved it>
## Open Questions
<!-- Omit section if none -->
- <question>
## Key Context
<!-- Omit section if none -->
- <fact>
## Process Notes
<!-- Omit section if none -->
- <note>
```
**Guidelines:**
- Omit empty sections entirely
- `[topic]` tags on gotchas should match existing memory topic names where possible
- Keep entries concise -- raw material for `/reflect-logs`
### Step 1.3: Prune old logs
1. Read `retention_days` and `warn_unreflected_days` from settings (defaults: 7 and 14)
2. Read `.reflection-state.json` to know which logs have been reflected on
3. For each file in `memory/log/`:
- Parse the date from the filename (first 10 characters: `YYYY-MM-DD`)
- If **older than `retention_days`** AND **present in `.reflection-state.json`** -- delete it
- If **older than `warn_unreflected_days`** AND **NOT in `.reflection-state.json`** -- warn: `WARNING: <filename> is N days old and has NOT been reflected on. Run /reflect-logs.`
- Otherwise -- leave it alone
---
## Phase 2: Update CONTEXT.md
This is the most important phase for session resumption. CONTEXT.md tells the next session what to focus on.
### Step 2.1: Assess current state
Based on the full conversation, determine:
- What work is actively in progress (not yet complete)?
- What was completed this session that changes the project's focus?
- What should the next session pick up first?
- Are there blockers, pending decisions, or things to verify?
### Step 2.2: Update CONTEXT.md and context/ files
Follow the thin-index pattern:
1. **Remove completed entries** from CONTEXT.md that are no longer active.
2. **Add or update entries** for active work streams, linking to detail files in `context/`.
3. **Create or update `context/<topic>.md`** files with enough detail for a fresh session to resume without re-reading the full conversation:
- What's the current state?
- What was the last thing done?
- What should be done next?
- Any gotchas, blockers, or decisions pending?
- Key file paths, commands, or references needed to continue.
4. If `context/` directory doesn't exist and there are active work streams, create it with `mkdir -p context/`.
**Principles:**
- Write for a Claude session that has zero conversation history -- it only has CONTEXT.md and the linked files.
- Be specific: file paths, branch names, error messages, command sequences.
- Don't duplicate what's in CLAUDE.md or MEMORY.md -- reference those instead.
- If there's nothing active (session wrapped up cleanly with no follow-up), say so in CONTEXT.md: `No active work streams. Project is at a clean stopping point.`
### Step 2.3: Planning-store status write-back
If the project's CLAUDE.md declares a canonical planning store outside the repo (e.g. a
work-items repo managed via `cp-cli planner items …`, like `agent-runtimes-work-items`),
the session is **not finished until delivered work is written back to it**. Stale planning
stores mislead every future session and agent that treats them as canonical.
1. **List what this session delivered or advanced**: milestones completed or progressed,
epics affected, specs shipped, pipelines deployed.
2. **For each affected item**, update its status in the planning store:
- CP reachable: update the item's flow state / status document via
`cp-cli planner items …` / `documents put` (see the project CLAUDE.md for the exact
workflow).
- CP unreachable but the store has a local git checkout: edit the status/plan/CONTEXT
docs there directly and note in CONTEXT.md that the edit needs upload/push.
- Neither: stage the update in the project's offline staging dir (e.g.
`local-planning/<item-uuid>/`) and record the pending upload in CONTEXT.md.
3. **Check roll-up documents for contradictions**: any epic overview, delivery map, or
roadmap doc that summarises the affected milestones must not now contradict reality
(e.g. still saying "pending" for work that shipped this session). Fix the rows you have
ground truth for; flag the ones you don't.
4. If nothing this session touched milestone/epic-level state, say so and move on.
Skip this step entirely only when the project has no external planning store.
### Step 2.4: RCA document write-back
If this session root-caused a real incident — a bug, outage, or regression with a clear
symptom, root cause, and fix, not just routine debugging — and the project's planning store
supports an `incident` item type (check the store's `DATA_VALUES.md`/`CLAUDE.md` for an
`incident` type and an `rca-<date>-<slug>.md` naming convention; most projects will not have
this, in which case skip this step entirely):
1. **Find or create the `incident` item.** Search the planning store for an existing
`type: incident` item covering this incident before creating a new one.
2. **Write the full postmortem** as `rca-<date>-<slug>.md`, where `<date>` is the date the
incident *occurred* (not today, if determinable from git history/logs) and `<slug>` is a
short kebab-case description. Cover symptom, root cause, fix, and impact — this is the
durable narrative; the item's `CONTEXT.md` body stays a short summary.
3. **Upload it to the `incident` item**:
- CP reachable: `python3 -m lib.cp_cli planner items documents put <incident-item-uuid> rca-<date>-<slug>.md --file <path>`
- CP unreachable but the store has a local git checkout: write the file directly under
`by-uuid/<incident-item-uuid>/rca-<date>-<slug>.md` there and note in CONTEXT.md that it
needs a commit/push.
- Neither: stage it in the project's offline staging dir (e.g. `local-planning/`) and
record the pending upload in CONTEXT.md.
4. **Link every affected item** to the incident via the `impacted`/`impacted_by`
relationship verb (or whatever the store's `DATA_VALUES.md` names it) — one document on
the `incident` item, one relationship edge per affected item, not copies of the document.
5. If nothing this session rises to postmortem-worthy, skip this step and say so.
---
## Phase 3: Update Project Documentation
Review the conversation for changes that should be reflected in project docs. Only update files where this session actually changed something relevant -- don't touch files that are already accurate.
### Step 3.1: FUTURE.md
If new improvement ideas, feature requests, or tech debt surfaced during this session, add them to FUTURE.md using the standard format:
- **Problem:** What's painful or manual today
- **Idea:** What the improvement looks like
- **Open questions:** Unknowns to research before starting
- **Depends on:** Other items or milestones that should come first
Skip this step if no new ideas emerged.
### Step 3.2: README.md
Update if any of these changed this session:
- **Milestone table** -- update status if a milestone progressed or completed
- **Scripts section** -- add any new scripts created this session (with purpose and usage)
- **Setup instructions** -- if new dependencies, environment changes, or setup steps were introduced
Skip this step if README.md is already accurate.
### Step 3.3: CLAUDE.md (and `claude/` detail files)
Update if any of these changed this session:
- **Repo structure** -- new directories, moved files, renamed components
- **Conventions** -- new patterns or rules discovered during implementation
- **Environment details** -- new URLs, IPs, service endpoints, credentials references
- **Common operations** -- new how-to recipes worth preserving
If the project follows the thin-index pattern (a `claude/` directory with per-topic detail
files referenced from CLAUDE.md), put per-subsystem changes in the appropriate
`claude/<topic>.md` file rather than the root CLAUDE.md. Only edit the root CLAUDE.md for
always-applies guardrails, the architecture overview, or the pointer table itself (e.g.,
adding a row for a new detail file).
Skip this step if CLAUDE.md and the relevant detail file are already accurate.
---
## Phase 4: Summary
Print a concise summary:
**Session log:**
- Log file path created
- Number of entries by section
- Any files pruned or warnings issued
**Context update:**
- What CONTEXT.md now points to (active work streams)
- What the next session should pick up
**Doc updates:**
- Which files were updated (FUTURE.md, README.md, CLAUDE.md) and what changed
- Or "No doc updates needed" if everything was already accurate
---
## Quality Checks
- Gotchas include **symptom AND fix**, not just "X was tricky"
- Process lessons are phrased as **rules** ("Always X before Y")
- CONTEXT.md entries are **specific enough to resume from** without conversation history
- Context detail files include **file paths, branch names, and next steps**
- Future items have all four fields (Problem/Idea/Open questions/Depends on)
- Doc updates are **minimal and accurate** -- only touch what actually changed this session
- README scripts section lists every script with purpose and usage

View File

@@ -4,7 +4,7 @@ description: >
End-of-session logging. Captures key decisions, gotchas, open questions, and discussion
points into memory/log/ for later reflection. Run before ending a session to preserve
context. Fast and low-friction -- just run /log.
allowed-tools: Read, Write, Glob, Bash(date *), Bash(ls *), Bash(cat *), Bash(find *), Bash(rm *), Bash(mkdir *)
allowed-tools: Read, Write, Glob, Agent, Bash(date *), Bash(pwd), Bash(ls *), Bash(cat *), Bash(find *), Bash(rm *), Bash(mkdir *), Bash(python3 *), Bash(bash *)
---
# Session Log Skill
@@ -17,6 +17,9 @@ You are capturing key points from the current session into a structured log file
!`date +%Y-%m-%d`
!`date +%Y%m%d-%H%M%S`
### Current project directory
!`pwd`
### Existing log files
!`ls -1 memory/log/ 2>/dev/null || echo "No log directory yet"`
@@ -29,16 +32,19 @@ You are capturing key points from the current session into a structured log file
### Current MEMORY.md index
!`cat MEMORY.md 2>/dev/null || echo "No MEMORY.md found"`
### Unprocessed transcript backups for this project (metadata only)
!`bash ~/.claude/scripts/list-transcripts-here.sh 2>/dev/null || echo "[]"`
## Instructions
Review the **full conversation history** in your context window and extract the key points worth preserving. Not every session needs a log — if the session was trivial (a typo fix, a quick question), say so and skip.
Review the **full conversation history** in your context window and extract key points worth preserving. Not every session needs a log — if the session was trivial (a typo fix, a quick question), say so and skip.
### Step 1: Create the log directory if needed
The "Unprocessed transcript backups" above shows metadata (filenames, timestamps) for any pre-compaction snapshots not yet captured in a log. **Do NOT read the transcript content yourself** — that is handled by a subagent in Step 2 to avoid context overflow.
### Step 1: Create the log directory and write the in-context log
If `memory/log/` does not exist, create it with `mkdir -p memory/log/`.
### Step 2: Write the log file
Generate the filename as `memory/log/YYYY-MM-DD.<HHMMSS>.md` using the pre-gathered date and timestamp values.
Write the file using this format:
@@ -72,9 +78,82 @@ Write the file using this format:
**Guidelines:**
- Omit empty sections entirely rather than leaving them blank
- The `[topic]` tag on gotchas should match existing memory topic names where possible (e.g., `[k8s]`, `[cilium]`, `[ansible]`, `[sops]`, `[helm]`). Use new tags for new topics.
- The `[topic]` tag on gotchas should match existing memory topic names where possible (e.g., `[k8s]`, `[cilium]`, `[ansible]`, `[sops]`, `[helm]`)
- Keep entries concise — this is raw material for `/reflect-logs`, not a polished document
- Include enough context that each entry makes sense on its own without the full conversation
- Include enough context that each entry makes sense without the full conversation
### Step 2: Dispatch transcript analysis subagent (if unprocessed transcripts exist)
If the pre-gathered transcript list is non-empty and contains backup files that `exists: true`:
Spawn a **Sonnet** subagent (model: sonnet) with the following prompt. Sonnet is used (not Haiku) because gotcha detection requires judgment about backtracking, failed attempts, and domain-specific failure modes — Haiku tends to miss these. Fill in the bracketed values from the pre-gathered data:
---
**Subagent prompt template:**
You are processing pre-compaction transcript backups into a structured session log.
**Project directory:** [CWD from pre-gathered context]
**Log directory:** memory/log/ (relative to project dir — write files there using absolute path)
**Log filename:** [SAME HHMMSS timestamp as Step 1, but with suffix -transcripts, e.g. memory/log/YYYY-MM-DD.HHMMSS-transcripts.md]
**Transcripts to process:** [paste the JSON array from the pre-gathered list]
## Your task
For each backup file listed above (where `exists: true`):
1. Extract the conversation using:
```
python3 ~/.claude/scripts/extract-transcripts.py --extract <backup_name>
```
Run this as a Bash command and read the output.
2. If the transcript has >40 turns, process it in two passes:
- First half of turns in one read
- Second half in a second run (re-run --extract and skip to turn N)
Note: the --extract command outputs all turns; read the full output but focus analysis on substance.
3. After reading all transcripts, write a single log file at the absolute path:
`[PROJECT_ABS_PATH]/memory/log/YYYY-MM-DD.HHMMSS-transcripts.md`
Use this format:
```markdown
# Transcript Log — YYYY-MM-DD (pre-compaction backups)
## Sources
- <backup_name> (session <session_id>, saved <saved_at>)
## Summary
<!-- What was worked on across the captured sessions -->
## Decisions
- Decision: <what> — Rationale: <why>
## Gotchas Discovered
- **[topic]** Symptom: <what happened> — Fix: <what resolved it>
## Open Questions
- <question>
## Key Context
- <fact>
## Process Notes
- <note>
```
4. After writing the log, mark all processed transcripts:
```
python3 ~/.claude/scripts/extract-transcripts.py --mark-all-processed "[CWD]" --log-file "memory/log/YYYY-MM-DD.HHMMSS-transcripts.md"
```
5. Report: transcript(s) processed, log file written, any issues encountered.
**Allowed tools for the subagent:** Read, Write, Bash(python3 *), Bash(mkdir *)
---
Spawn this subagent with `model: sonnet` and wait for it to complete before continuing.
### Step 3: Prune old logs
@@ -91,6 +170,6 @@ After writing the log file, check for old logs that should be pruned:
### Step 4: Summary
Print a brief summary:
- Log file created (with path)
- Number of entries by section
- In-context log file created (with path)
- Whether a transcript analysis subagent was dispatched (and which backups it processed)
- Any files pruned or warnings issued

View File

@@ -0,0 +1,174 @@
---
name: manual-workflow
description: >
Run a workflow template from a Claude Code session using the control plane for task execution.
Handles artifact passing, sentinel resolution, and human review gates locally.
Usage: /manual-workflow <template-name> [param=value ...]
user_invocable: true
allowed-tools: Read, Glob, Grep, Bash(curl *), Bash(python3 *), Bash(sleep *), Bash(date *), Bash(cat *), Bash(ls *), Bash(kill *), Bash(kubectl port-forward *), AskUserQuestion, Write, Edit
---
# /manual-workflow Skill
<command-name>manual-workflow</command-name>
Run a workflow template through the control plane, managing artifact passing and human review gates from this Claude session.
## Arguments
`$ARGUMENTS` — Template name followed by optional `key=value` params.
Examples:
- `/manual-workflow spec-planning` — interactive param prompting
- `/manual-workflow spec-planning project_id=agent-runtimes task_description="Design artifact passing"`
## Pre-gathered context
### Available workflows
!`python3 -c "import glob,os; [print(os.path.basename(p).removesuffix('.yaml')) for p in sorted(glob.glob(os.path.expanduser('~/dev/claude/projects/agent-runtimes/workflows/*.yaml')))]"`
### Control plane access
!`curl -s --connect-timeout 2 http://localhost:8100/health 2>/dev/null || echo "CP_NOT_AVAILABLE"`
## Instructions
### Step 0: Parse arguments and validate
Parse `$ARGUMENTS` to extract the template name (first word) and any `key=value` params.
If no template name provided, list available workflows and ask the user to pick one.
### Step 1: Load and validate template
Read the workflow YAML from `~/dev/claude/projects/agent-runtimes/workflows/<name>.yaml`.
Parse it to extract: `params`, `nodes`, node `depends_on`, and `model_override` fields.
Display a summary:
```
Workflow: <name> v<version>
Description: <description>
Nodes: <count> (<node_ids>)
DAG: <visual showing phases>
```
### Step 2: Resolve parameters
For each required param not provided via arguments, ask the user with `AskUserQuestion`.
For each optional param, show the default and ask if they want to override.
For `model_a` / `model_b`: if not provided and the user doesn't override, set sensible defaults:
- `model_a` = the harness context that runs via MiniMax (use `minimax/v1` harness)
- `model_b` = the default Claude model (use `anthropic-cloud/v1` harness)
Or let the user specify model names and you'll map them to harnesses.
### Step 3: Ensure CP access
Check if `http://localhost:8100/health` responds. If not:
```bash
kubectl port-forward -n agent-runtimes svc/controlplane 8100:8100 &
```
Wait for health check to pass.
### Step 4: Execute in topological waves
Process nodes in dependency order. Nodes with no deps (or all deps satisfied) form a "wave" and run in parallel.
For each wave:
1. **Resolve sentinels** in each node's prompt: replace `<<ARTIFACT:node_id:key>>` with the collected output from that node. Wrap injected content in `[BEGIN ARTIFACT: node_id:key]` / `[END ARTIFACT: node_id:key]` markers.
2. **Render Jinja2** — replace `{{ param }}` references with resolved param values. For Jinja2 conditionals (`{% if %}`, `{% for %}`), render them with the params dict. Use Python:
```python
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
rendered = env.from_string(prompt).render(**params)
```
3. **Determine harness** for each node:
- If `model_override` resolves to a value containing "model_a" and model_a is minimax → use `minimax/v1`
- If `model_override` resolves to a value containing "model_b" or is empty → use `anthropic-cloud/v1`
- The harness controls which provider's auth the agent gets
4. **Submit tasks** to the CP via:
```bash
curl -s -X POST http://localhost:8100/tasks \
-H "Content-Type: application/json" \
-d '{"name": "<node_name>", "project_id": "<project_id>", "prompt": "<rendered_prompt>", "harness": "<harness>", "runtime": {"cli": "claude", "model": "<model_if_applicable>", "timeout": 3600}}'
```
5. **Monitor tasks** — poll every 30s:
```bash
curl -s http://localhost:8100/tasks/<task_id>
```
Display progress updates to the user: which nodes are running, which completed, duration.
6. **Collect outputs** — when a task succeeds, extract the assistant's output from the task logs:
```python
# Parse NDJSON logs, find assistant message content
for line in logs.split('\n'):
entry = json.loads(line)
if entry.get('type') == 'assistant':
content = entry['message']['content']
for block in content:
if block['type'] == 'text':
result += block['text']
```
Store the output as `artifacts[node_id]['output']` for sentinel resolution in downstream nodes.
7. **Handle failures** — if a task fails:
- Show the error and logs to the user
- Ask: "Retry this node, skip it, or abort the workflow?"
- On retry: resubmit with the same prompt
- On skip: mark the artifact as `[NODE FAILED — no output available]`
- On abort: stop the workflow
### Step 5: Human review gate
When the `escalate` node completes (or any node whose name contains "human" or "escalat"):
1. **Display the full escalation output** to the user
2. **Ask for decisions** on each "Decision needed: YES" item using `AskUserQuestion`
3. **Append the human decisions** to the escalation artifact:
```
## Human Decisions (from manual review)
### Decision 1: [topic]
**Choice:** [user's answer]
**Rationale:** [user's explanation if provided]
### Decision 2: [topic]
...
```
4. The updated artifact (original + human decisions) is then used for sentinel resolution in the `synthesize` node.
### Step 6: Final output
When the `synthesize` node completes:
1. Display a summary: total nodes, succeeded/failed, wall-clock time per phase
2. Write the final spec output to a file in the project:
- Ask the user where to save it (suggest `spec/<name>.md`)
- Write the file
3. Show the file path and suggest next steps: "Review the spec, then commit when ready."
### Error handling
- If the CP goes offline mid-workflow, pause and tell the user to fix it
- If a node times out (>3600s), treat as failure
- If ALL nodes in a wave fail, ask the user before continuing
- Never leave orphan tasks running — cancel them on abort
### Progress display
Keep the user informed with a compact status line after each check:
```
[Phase 2] spec_review_a: running (2m) | spec_review_b: running (1m45s) | security_review_a: succeeded (3m) | ...
```
### Important notes
- This skill runs the workflow LOCALLY from your Claude session — it does NOT use the CP's workflow expansion (Phase 2 artifact passing isn't implemented yet)
- Artifacts are passed by embedding the full text in downstream prompts — this works but means large artifacts consume context
- The harness mapping (model name → harness context) is specific to the agent-runtimes project's current provider setup
- Monitor the agent-monitor in another terminal for richer progress: `scripts/agent-monitor --filter "project=<project_id>" --filter "age<30m"`

View File

@@ -28,6 +28,19 @@ You are the task orchestrator. Check task state, update statuses, and launch con
If the task state file doesn't exist, say "No .agent-tasks.json found. Run /decompose first." and stop.
### Step 0: Dispatch pre-flight (first invocation only)
On the very first invocation of a dispatch session, run the pre-flight checklist from `~/dev/claude/claude/agent-dispatch-preflight.md` before proceeding. All four checks must pass:
1. CP reachable — confirm the health endpoint returns 200 (see `claude/agent-runtimes-cp.md` for `CP_URL`).
2. A dispatcher is polling — verify at least one dispatcher is active at the CP dispatchers endpoint.
3. Scaffolding present — `.agent-tasks.json` passes `jq empty`, referenced templates and repos are resolvable, and agent push keys are loaded.
4. Auth done this session — `CLAUDE_CODE_OAUTH_TOKEN` is set or readable from secrets.
If any check fails, report which check failed and wait for the user to remediate. Do not proceed to Step 1 until all four pass.
"First invocation" means the first time this skill runs in a loop session. On subsequent loop invocations, skip Step 0.
### Step 1: Check running containers
For each task with `status: "running"`, check if the container is still alive:
@@ -98,6 +111,11 @@ This step is critical — worktrees only contain committed content. Without it,
#### 3c. Launch the container
Determine the model to use:
- Read the task's `model` field from `.agent-tasks.json`
- If `model` is set, use that value (e.g., `claude-opus-4-20250514`, `claude-sonnet-4-20250514`)
- If `model` is not set or null, default to `claude-sonnet-4-20250514`
```bash
docker run -d \
-e CLAUDE_CODE_OAUTH_TOKEN="<token>" \
@@ -106,7 +124,7 @@ docker run -d \
-w /project \
--entrypoint uid-wrapper.sh \
agent-claude:latest \
claude --print --dangerously-skip-permissions --model claude-sonnet-4-20250514 "<task prompt>"
claude --print --dangerously-skip-permissions --model <model> "<task prompt>"
```
**Important:**
@@ -115,6 +133,7 @@ docker run -d \
- Capture the container ID from docker run output
- The CLAUDE_CODE_OAUTH_TOKEN must be available in the current environment. If not set, read it from `~/dev/claude/secrets/claude/long_lived_oauth_token` (extract the value after `value: `)
- If the task's `reads` list references paths outside the project (e.g., `/foundations` for best practices), mount those as additional read-only volumes
- Container agents do NOT have web search capability. Do not include "use web search" in task prompts unless web search support has been explicitly configured for the container.
After launching, update the task in `.agent-tasks.json`:
- Set `status` to `running`
@@ -164,6 +183,8 @@ Do NOT auto-merge — the user should review and decide.
- Keep output concise when called in a loop — just the status table unless something changed
- On first run, if `CLAUDE_CODE_OAUTH_TOKEN` is not in the environment, read it once and export it for subsequent runs
**Stall circuit-breaker:** Track whether any task changed state (pending→running, running→completed/failed, etc.) across each invocation. After 5 consecutive invocations with no state change, stop dispatching, print a stuck-queue report listing every task and its current status, and wait for the user. Do not continue the loop automatically. Never run an unattended dispatch loop without this guard.
### Error Recovery
- If a task fails, its dependents are marked `blocked`. The user can:

123
skills/review-plan/SKILL.md Normal file
View File

@@ -0,0 +1,123 @@
---
name: review-plan
description: >
Review a plan file against best practices: API design, LLM code security, spec-driven
development, test-driven development, and security architecture. Flags gaps, missing
considerations, and anti-patterns before implementation begins. Invoke with the plan
filename, e.g. /review-plan M2-auth-PLAN.md
allowed-tools: Read, Glob, Grep, Bash(cat *), Bash(ls *), Bash(find *)
---
# Plan Review Skill
You are reviewing the plan file **$ARGUMENTS** against established best practices.
## Pre-gathered context
### Best practices index
!`cat ~/dev/claude/BESTPRACTICES.md 2>/dev/null || echo "BESTPRACTICES.md not found"`
### Plan file to review
(Use the Read tool to read the plan file specified in $ARGUMENTS. If no filename is given, look for *-PLAN.md files in the project root and ask which one to review.)
## Best practice files to load
Read ALL of the following best practice files before starting the review:
1. `~/dev/claude/projects/best-practices/api-design.md` -- Transport security, auth, API patterns, input validation, zero-trust
2. `~/dev/claude/projects/best-practices/llm-code-security.md` -- LLM-generated code vulnerabilities, review checklists
3. `~/dev/claude/projects/best-practices/spec-driven-development.md` -- Spec structure, requirements, scenarios
4. `~/dev/claude/projects/best-practices/test-driven-development.md` -- Test derivation, edge cases, property testing
5. `~/dev/claude/projects/best-practices/security-architecture.md` -- Server boundary rule, credential proxying
Also read any additional best practice files relevant to the plan's technology stack (check the index for Kubernetes, Helm, Docker, secrets management, etc.).
## Review checklist
Evaluate the plan against each area below. For each area, report one of:
- **Covered** -- the plan explicitly addresses this
- **Partially covered** -- mentioned but lacks detail or has gaps
- **Missing** -- not addressed and should be
- **N/A** -- not relevant to this plan
### 1. Security by design
- [ ] Authentication model defined (who authenticates, how, what protocol)
- [ ] Authorization model defined (who can do what, how enforced)
- [ ] Transport security specified (TLS, mTLS, or explicit justification for plaintext)
- [ ] Secrets handling defined (how injected, never in payloads/URLs/logs, rotation plan)
- [ ] No credentials crossing server boundary to clients (server boundary rule)
- [ ] Input validation strategy at system boundaries
- [ ] Error responses don't leak internals (stack traces, paths, SQL)
### 2. API design (if the plan involves APIs)
- [ ] API versioning strategy
- [ ] Pagination on list endpoints
- [ ] Idempotency for state-changing operations
- [ ] Rate limiting considered
- [ ] Structured error responses with stable codes
- [ ] Health checks split into liveness and readiness
- [ ] Request size limits
- [ ] CORS policy (explicit origins, not wildcard)
### 3. LLM code security awareness
- [ ] Plan acknowledges that LLM-generated code needs security review
- [ ] Input validation is planned at all external boundaries (not deferred)
- [ ] Dependency versions will be verified from live sources (not LLM memory)
- [ ] Infrastructure manifests include security defaults (securityContext, NetworkPolicy, resource limits)
- [ ] No over-permissive defaults (0.0.0.0 binding, CORS *, chmod 777, verify=False)
- [ ] Secrets never hardcoded -- plan specifies how they're injected
### 4. Spec-driven development
- [ ] Plan references or will produce specs before implementation
- [ ] Subsystem boundaries identified (what gets its own spec)
- [ ] Data models and interfaces described (not just "we'll figure it out")
- [ ] Requirements are testable and unambiguous
- [ ] Scenarios included or planned (given/when/then)
- [ ] Spec dependencies mapped (which specs need to be read together)
### 5. Test-driven development
- [ ] Test strategy defined (what's tested, how, what tools)
- [ ] Tests derived from spec requirements (requirement IDs in test names)
- [ ] Edge cases and failure modes considered (not just happy path)
- [ ] Integration test plan (not just unit tests)
- [ ] Verification script planned for milestone completion
### 6. Operational readiness
- [ ] Logging and observability considered
- [ ] Deployment strategy (how it gets deployed, rollback plan)
- [ ] Configuration via environment variables (not baked in)
- [ ] Health checks and monitoring
- [ ] Backward compatibility considered (existing consumers)
## Output format
Structure your review as:
### Summary
One paragraph: overall assessment of the plan's readiness.
### Scorecard
| Area | Status | Notes |
|------|--------|-------|
| Security by design | Covered/Partial/Missing | ... |
| API design | Covered/Partial/Missing/N/A | ... |
| LLM code security | Covered/Partial/Missing | ... |
| Spec-driven development | Covered/Partial/Missing | ... |
| Test-driven development | Covered/Partial/Missing | ... |
| Operational readiness | Covered/Partial/Missing | ... |
### Critical gaps
Numbered list of issues that should be addressed before implementation begins. Include the specific best practice being violated and a concrete suggestion.
### Recommendations
Numbered list of improvements that would strengthen the plan but aren't blockers.
### What's done well
Brief acknowledgment of areas the plan handles correctly -- reinforces good patterns.

179
skills/review-spec/SKILL.md Normal file
View File

@@ -0,0 +1,179 @@
---
name: review-spec
description: >
Review a spec file against best practices: API design, LLM code security, spec-driven
development, test-driven development, security architecture, and mechanical test
generation. Checks spec structure, requirement quality, security coverage, testability,
and whether the spec enables mechanical test derivation. Invoke with the spec filename,
e.g. /review-spec spec/authentication.md
allowed-tools: Read, Glob, Grep, Bash(cat *), Bash(ls *), Bash(find *)
---
# Spec Review Skill
You are reviewing the spec file **$ARGUMENTS** against established best practices.
## Pre-gathered context
### Best practices index
!`cat ~/dev/claude/BESTPRACTICES.md 2>/dev/null || echo "BESTPRACTICES.md not found"`
### Spec index (if exists)
!`cat SPEC.md 2>/dev/null || echo "No SPEC.md found"`
### Spec file to review
(Use the Read tool to read the spec file specified in $ARGUMENTS. If no filename is given, read SPEC.md and ask which spec to review.)
## Best practice files to load
Read ALL of the following core best practice files before starting the review:
1. `~/dev/claude/projects/best-practices/spec-driven-development.md` -- Spec structure, requirements, scenarios, writing guidelines
2. `~/dev/claude/projects/best-practices/test-driven-development.md` -- Test derivation, edge cases, property testing
3. `~/dev/claude/projects/best-practices/mechanical-test-generation.md` -- Spec properties that enable mechanical test writing (module layout, integration boundaries, error messages as test data, pattern tables as parametric matrices, pre-dispatch testability review)
4. `~/dev/claude/projects/best-practices/api-design.md` -- Transport security, auth, API patterns, input validation, zero-trust, RFC 9457 errors, contract testing
5. `~/dev/claude/projects/best-practices/llm-code-security.md` -- LLM-generated code vulnerabilities, review checklists
6. `~/dev/claude/projects/best-practices/security-architecture.md` -- Server boundary rule, credential proxying
### Conditional topic files
Read these ONLY when the spec under review touches that area. Skim the spec first, then load the matching files:
| Load when the spec involves... | File |
|---|---|
| Agent task dispatch, branch-per-task persistence, agent repo lifecycle | `agent-repos.md` |
| Parallel/wave dispatch, multi-agent orchestration, comparative dispatch | `ai-parallel-agents.md` |
| Third-party APIs (consumer side: capability verification, polling sync, SoR mapping) | `api-integration.md` |
| Credential handling beyond the security-architecture basics (SOPS, age, secret scoping, env vs file injection) | `secrets-management.md` |
| Persistence (DB selection, concurrent access, FQDNs/services backed by storage) | `database-selection.md` |
| Container lifecycle, image pinning, UID matching for mounted volumes | `docker.md`, `docker-uid-matching.md` |
| K8s resources, NetworkPolicy, securityContext, probe behaviour, Cilium/Istio | `kubernetes.md` |
| Pre-deploy validation, integration failure categorisation, migration safety | `validation.md` |
Skip for spec review (these are implementation-time concerns, not spec-quality concerns): `debugging`, `linting`, `scripting`, `documentation`, `milestones`, `networking`, `ansible`, `octopus-process-templates`, `git-source-control`, `python-patterns`, `helm`, `ci-container-builds`, `skills-development`. If the spec under review *is* about one of those areas, load the corresponding file then — but don't load them by default.
## Review: spec structure quality
Evaluate the spec against the required structure from spec-driven-development.md:
### Required sections
- [ ] **Overview** -- 2-3 sentences, clear purpose. An agent knows if this spec is relevant after reading this.
- [ ] **Responsibilities** -- What this subsystem owns AND what it delegates. Prevents scope creep.
- [ ] **Dependencies** -- Which other specs to read. Links present and correct.
- [ ] **Data Model** -- Types, schemas, state machines, interfaces with concrete examples (not just abstract schemas).
- [ ] **Requirements** -- Numbered with a consistent prefix (e.g., AU-1, CP-1). Each independently testable.
- [ ] **Scenarios** -- Given/when/then format. Cover happy path AND failure modes.
### Optional sections (flag if missing but relevant)
- [ ] **Interface** -- API surface, endpoints, signatures (required if the subsystem has an external API)
- [ ] **Extension Points** -- How to add capabilities without modifying existing code
- [ ] **Error Handling** -- Failure modes and expected behaviour (prevents agents inventing strategies)
## Review: requirement quality
For each numbered requirement, check:
- [ ] **Testable** -- Can an agent write a test that unambiguously passes or fails?
- [ ] **Unambiguous** -- No "should", "appropriate", "handle errors gracefully". Specific exit codes, status codes, timeouts.
- [ ] **Includes rationale** -- Why this requirement exists (the "Why:" line). Without it, agents follow mechanically and can't judge edge cases.
- [ ] **No duplicates** -- Same requirement doesn't appear under different numbers.
- [ ] **Complete coverage** -- Are there obvious behaviours that lack requirements?
Count the requirements and verify any summary counts in the spec are accurate.
## Review: security coverage
Check the spec against API design and security best practices:
### Authentication and authorization
- [ ] Auth model specified for every endpoint (who can call it, what credential, how validated)
- [ ] Service-to-service auth uses mTLS or short-lived tokens (not shared static keys)
- [ ] Human auth uses OIDC/OAuth2 with PKCE (not implicit flow, not password grant)
- [ ] Token validation is complete (signature, expiry, issuer, audience, algorithm pinned)
### Transport and data protection
- [ ] TLS required (or explicit justification for plaintext)
- [ ] Secrets never in payloads, URLs, query params, or logs
- [ ] Secrets passed via env vars or mounted files
- [ ] Error responses don't expose internals
### Input validation
- [ ] All external inputs validated (types, lengths, ranges, formats)
- [ ] Parameterized queries for database access (no string concatenation)
- [ ] Request size limits specified
### API patterns (if the spec defines an API)
- [ ] Pagination on list endpoints with enforced max page size
- [ ] Idempotency for POST endpoints
- [ ] Rate limiting mentioned or deferred with a reference
- [ ] Structured error responses with stable codes
- [ ] API versioning strategy
### Infrastructure security (if the spec involves K8s/containers)
- [ ] securityContext specified (runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities)
- [ ] Resource limits defined
- [ ] NetworkPolicy specified or referenced
- [ ] No privileged containers
- [ ] Images pinned to digest or specific version
## Review: testability
Evaluate how well this spec supports test-driven development:
- [ ] Every requirement maps to at least one testable assertion
- [ ] Scenarios cover both happy path and failure modes
- [ ] Edge cases identified (boundary values, empty inputs, concurrent access, timeout)
- [ ] Data model examples are concrete enough to use as test fixtures
- [ ] Extension points describe how to test new extensions
## Review: mechanical test derivation
Evaluate whether a tester (human or agent) could write the test suite from the spec without inventing structure. Source: `mechanical-test-generation.md`.
- [ ] **Module layout is explicit** -- A table maps subsystems → source files → test files (or the spec names them inline). Without this, testers invent module boundaries.
- [ ] **Integration boundaries are marked** -- Requirements that cross a process/network boundary are flagged as integration tests. Pure-logic requirements are flagged as unit tests. No ambiguity about which level a requirement belongs at.
- [ ] **Library/framework semantics are explicit** -- If the spec says "use X library", it states which behaviours the library guarantees vs which the spec adds on top. Testers should not have to read library source to know what to test.
- [ ] **Concrete interfaces over "implementation detail"** -- Function signatures, data shapes, and error types are spelled out. Avoid "the implementation handles this" hand-waves.
- [ ] **Error messages are test data** -- Where the spec specifies error responses, the exact string/code/structure is given (or referenced from a stable source). Tests can assert against these without guessing.
- [ ] **Pattern tables work as parametric matrices** -- Tables of inputs → expected outputs are formatted such that a `@pytest.mark.parametrize` can be derived directly. Avoid prose lists where a table would do.
- [ ] **Pre-dispatch testability review** -- Each requirement has been read with "could I write a failing test for this right now?" If not, the requirement is too vague to dispatch to an implementation agent.
## Output format
Structure your review as:
### Summary
One paragraph: overall quality of the spec and its readiness for implementation.
### Scorecard
| Area | Score | Notes |
|------|-------|-------|
| Structure completeness | 1-5 | ... |
| Requirement quality | 1-5 | ... |
| Security coverage | 1-5 | ... |
| Testability | 1-5 | ... |
| Mechanical test derivation | 1-5 | ... |
| Clarity for AI agents | 1-5 | ... |
(1 = major gaps, 3 = adequate, 5 = exemplary)
### Critical issues
Numbered list of problems that would cause implementation failures or security vulnerabilities. Each includes:
- The specific section/requirement with the issue
- What best practice it violates
- A concrete fix
### Missing requirements
Requirements that should exist but don't. Suggest a requirement ID and text for each.
### Missing scenarios
Scenarios that should exist but don't. Provide given/when/then for each.
### Recommendations
Non-blocking improvements that would strengthen the spec.
### What's done well
Specific sections or requirements that are exemplary -- reinforces good patterns for future specs.

View File

@@ -0,0 +1,82 @@
---
name: scan-ralph
description: >
Scan agent-fork branches for unmerged implementations from ralph_code overnight runs.
Reports changed files and inferred spec IDs. Use with "/loop 5m /scan-ralph" to poll
continuously for new work.
allowed-tools: Read, Edit, Bash(git *), Bash(python3 *), Bash(scripts/scan-ralph-candidates *), Bash(grep *), Bash(date *)
---
# /scan-ralph
<command-name>scan-ralph</command-name>
You are reporting on unmerged implementations from overnight ralph_code agent runs.
## Pre-gathered context
### Scan timestamp
!`date +"%Y-%m-%d %H:%M"`
### Candidate branches (fetched from agent-fork now)
!`scripts/scan-ralph-candidates 2>&1`
### Recent main commits (last 5)
!`git log --oneline -5 main 2>/dev/null`
---
## Instructions
### Step 1: Check scan output
The "Candidate branches" block above contains either:
- Lines of the form `=== task-<uuid> ===` followed by file paths and spec IDs
- Or the message "No unmerged candidate branches found..."
If the scan failed (e.g. "not in a git repository" or a Python traceback), report the error
and stop. The user should `cd` into the `agent-runtimes` repo first.
### Step 2: Report results
**No candidates:**
> No new implementations found. All recent agent-fork branches are already merged or empty.
**Candidates found** — present as a compact list:
```
Branch: task-<uuid>
path/to/file.py → SPEC-ID, SPEC-ID
```
Then say: "**N branch(es) with unmerged implementations found.**"
### Step 3: Suggest next action
If candidates exist:
> To integrate, follow the workflow in `prompts/ralph_integration.md`.
> Start with the branch covering the highest-priority spec IDs.
>
> Quick recap:
> 1. `git checkout agent-fork/task-XXXX -- path/to/file.py`
> 2. Run the xfail test: `python -m pytest tests/test_foo.py::test_bar -v`
> 3. If XPASS(strict): remove the `@pytest.mark.xfail` decorator, commit
>
> Run `/scan-ralph` again after each integration to see the updated candidate list.
If no candidates:
> Run `scripts/scan-ralph-candidates --hours 168` to look back 7 days if you expect
> more results, or check `git fetch agent-fork` ran successfully.
---
## Loop usage
This skill is designed for:
```
/loop 5m /scan-ralph
```
Each firing re-fetches `agent-fork` and rescans, so new overnight branches appear
automatically without manual intervention.

View File

@@ -0,0 +1,61 @@
---
name: spawn-session
description: >
Spawn a new detached tmux session running Claude Code with Remote Control enabled,
in a project's directory, named after the project (e.g. "Agent Runtimes #2"). Use when
the user wants to start a separate Claude Code session for another project that they can
attach to over VPN/tmux later or drive from the Claude app. Wraps the claude-tmux script.
allowed-tools: Bash(claude-tmux *), Bash(ls *), Bash(tmux ls), Bash(tmux list-sessions *)
---
# Spawn Session Skill
Spawn a new, independent Claude Code session inside a detached `tmux` session with
Remote Control enabled, so the user can attach to it later (`tmux attach`) or control it
from the Claude app. This wraps the `claude-tmux` script — you do not reimplement its logic.
## Pre-gathered context
### Available projects (folders under ~/dev/claude and ~/dev/claude/projects)
!`ls -d "$HOME"/dev/claude/*/ "$HOME"/dev/claude/projects/*/ 2>/dev/null`
### Existing tmux sessions
!`tmux list-sessions -F '#{session_name}' 2>/dev/null || echo "(no tmux server running)"`
## Instructions
1. **Determine the project.** The user's argument is: `$ARGUMENTS`.
- If it names a project (a folder basename from the list above, e.g. `agent-runtimes`
or `cluster-bootstrap`), use it.
- If it is empty, ask the user which project to spawn a session for, offering the list
above. Do not guess.
- Flags the user may include and you should pass straight through: `--mode <name>`,
`--profile <name>`, `--name "<display>"`, `--attach`, `--dryrun`.
2. **Spawn it.** Run:
```
claude-tmux <project> [flags]
```
The script resolves the folder, picks the engagement mode (the profile's `last-mode`
unless `--mode` is given), Title-Cases the project into a session name, auto-increments
with a ` #N` suffix if that name is already taken, creates the detached tmux session in
the project's directory, and launches Claude Code with `--remote-control` under it.
Do **not** pass `--attach` unless the user explicitly asked to attach — the point is
usually to leave it running detached for later. The default profile is
`oreillyit-anthropic`; only override with `--profile` if the user asks.
3. **Report back** exactly what the script prints: the session name, how to attach
(`tmux attach -t "<name>"`), and the Remote-Control name to look for in the Claude app.
If the user is on a machine without power (the common reason for this), remind them the
session persists on the host and is reachable over the VPN via `tmux attach` whenever
they reconnect.
## Notes
- This starts a **new, separate** Claude Code process — it is not a sub-agent and does not
share this conversation's context. It is a full interactive session the user drives.
- Remote Control registers in the Claude app only once `claude` is actually running inside
the tmux session; `claude-tmux` guarantees this by passing the mode explicitly (never
leaving the launcher stalled at an interactive picker).
- To preview without creating anything, pass `--dryrun`.

128
skills/switch-mode/SKILL.md Normal file
View File

@@ -0,0 +1,128 @@
---
name: switch-mode
description: >
Change engagement mode mid-session. Updates active-mode.env and last-mode so
the next session launches in the new mode. Injects the new mode's workflow
stance into the current conversation so it takes effect immediately (driver
model cannot change without a relaunch).
allowed-tools: Read, Write, Glob, Bash(ls *), Bash(cat *)
---
# Switch Mode Skill
You are helping the user change their engagement mode.
## Pre-gathered context
### Available modes
!`ls ~/dev/claude/small-scripts/data/claude-profile/modes/`
### Current active-mode.env
!`cat ~/.claude-oreillyit/active-mode.env`
### Current last-mode
!`cat ~/.claude-oreillyit/last-mode`
## Instructions
The user wants to switch engagement mode. Follow these steps exactly.
### Step 1: Build the mode menu
Read each `.md` file in `/home/paul/dev/claude/small-scripts/data/claude-profile/modes/` using the Read tool.
From each file's frontmatter, extract:
- `name` — the mode identifier
- `tag` — display tag (same as name usually)
- `driver` — the driver model
- `escalates_to` — escalation model (or "none")
From the body, read the `## Purpose` section to get a one-line description.
Present a numbered menu like this (order: quick, deep, hybrid, orch, chat):
```
Available modes:
1. quick [Sonnet] One small focused job — get in, get out
2. deep [Sonnet -> Opus] Hard design/debug, plan-first, spec-driven
3. hybrid [Haiku -> Opus] Long sessions, cheap driver, escalate on demand
4. orch [Sonnet] Parallel container agents, orchestration focus
5. chat [Haiku] No project, just conversation
```
Mark the current mode (from `active-mode.env`'s `CLAUDE_MODE`) with `(current)`.
If `$ARGUMENTS` is non-empty and matches a mode name, skip the menu and go directly to Step 2 with that mode.
### Step 2: Confirm selection
Ask the user which mode number (or name) they want to switch to.
Wait for their response. If they already specified a mode in `$ARGUMENTS` or the menu was skipped, confirm it: "Switching to `<name>` mode — confirm? (y/n)"
### Step 3: Read the target mode file
Use the Read tool to read the full content of `/home/paul/dev/claude/small-scripts/data/claude-profile/modes/<name>.md`.
Extract all frontmatter fields:
- `name`
- `tag`
- `driver`
- `async_ok`
- `autoloop`
- `plan_mode_auto`
- `spec_driven`
- `escalates_to`
Compute the full mode file path: `/home/paul/dev/claude/small-scripts/data/claude-profile/modes/<name>.md`
### Step 4: Write active-mode.env
Write `/home/paul/.claude-oreillyit/active-mode.env` with this exact format (substitute the values from the frontmatter):
```
CLAUDE_MODE=<name>
CLAUDE_MODE_TAG=<tag>
CLAUDE_DRIVER=<driver>
CLAUDE_ESCALATES_TO=<escalates_to>
CLAUDE_PROJECT=
CLAUDE_TIME_HORIZON=2
CLAUDE_ASYNC_OK=<async_ok>
CLAUDE_AUTOLOOP=<autoloop>
CLAUDE_PLAN_MODE_AUTO=<plan_mode_auto>
CLAUDE_SPEC_DRIVEN=<spec_driven>
CLAUDE_MODE_FILE=/home/paul/dev/claude/small-scripts/data/claude-profile/modes/<name>.md
```
For `CLAUDE_PROJECT=` — leave the value empty (no project context is carried across a mode switch initiated mid-session; the user will set it at relaunch).
### Step 5: Write last-mode
Write `/home/paul/.claude-oreillyit/last-mode` with just the mode name and a trailing newline:
```
<name>
```
### Step 6: Adopt the new workflow stance
Read the full body of the new mode's `.md` file (everything after the closing `---` of the frontmatter). This is the mode's instructions to Claude.
Tell the user:
> Mode updated to `<name>`. The new mode will be fully active after `/clear` + relaunch (driver model `<current-driver>` cannot change mid-session).
>
> In this session, the following changes are active immediately:
> - **Workflow stance**: [summarise the key stance change in 1-2 sentences based on the mode body]
> - **Subagent policy**: [summarise from the Subagent policy section]
> - **Async policy**: [enabled / disabled based on async_ok]
> - **Plan mode**: [auto / manual / never, from plan_mode_auto]
>
> Adopting new stance now.
Then behave according to the new mode's body prose for the remainder of the session. The driver model (`<current-driver>`) is fixed — acknowledge this if the new mode's driver differs.
### Error handling
- If `$ARGUMENTS` is set but does not match any mode name: say "Unknown mode: `<arg>`. Available modes: quick, deep, hybrid, orch, chat." and show the menu.
- If the user picks their current mode: say "You are already in `<name>` mode. No change made." and stop.
- If a file write fails: report the error and tell the user to check permissions on `/home/paul/.claude-oreillyit/`.