Compare commits
1 Commits
main
...
agent/dist
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b21596db2 |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,7 @@ 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
|
||||
├── review-plan/SKILL.md # Review plan against best practices
|
||||
└── review-spec/SKILL.md # Review spec against best practices
|
||||
└── reflect-logs/SKILL.md # Process logs into memory
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
- **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:
|
||||
|
||||
@@ -25,7 +25,6 @@ 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
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,39 +0,0 @@
|
||||
# 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
|
||||
@@ -1,17 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,35 +0,0 @@
|
||||
# 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`
|
||||
@@ -1,170 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -63,8 +63,6 @@ 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
|
||||
|
||||
@@ -104,7 +102,6 @@ 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>"],
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
---
|
||||
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"}]
|
||||
}
|
||||
```
|
||||
@@ -2,10 +2,12 @@
|
||||
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 ~/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 *)
|
||||
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 *)
|
||||
---
|
||||
|
||||
# Distill Best Practices Skill
|
||||
@@ -16,23 +18,39 @@ 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/best-practices/.distill-state.json` — Distill state (if not found, treat as `{}`)
|
||||
- `~/dev/claude/projects/claude-foundations/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 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`
|
||||
- **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`
|
||||
|
||||
Read `settings.yaml` (pre-gathered above).
|
||||
|
||||
@@ -65,7 +83,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 so you can compare.
|
||||
Read the matching best-practices files from `<bp-repo>/` so you can compare.
|
||||
|
||||
### Step 3: Analyse and propose
|
||||
|
||||
@@ -102,18 +120,38 @@ Wait for the user to approve, modify, or reject proposals. The user may say "all
|
||||
|
||||
### Step 5: Apply approved changes
|
||||
|
||||
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
|
||||
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>/`
|
||||
|
||||
If a new best-practices topic file is needed:
|
||||
- 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
|
||||
- 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
|
||||
|
||||
### Step 6: Update distill state
|
||||
**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`
|
||||
|
||||
Write `~/dev/claude/projects/best-practices/.distill-state.json`:
|
||||
### 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`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -131,13 +169,22 @@ Write `~/dev/claude/projects/best-practices/.distill-state.json`:
|
||||
|
||||
Preserve entries for projects that weren't processed this run (no changes).
|
||||
|
||||
### Step 7: Summary
|
||||
### Step 8: Clean up temp directory
|
||||
|
||||
Remove the temp directory used for the clone:
|
||||
|
||||
```
|
||||
rm -rf "$tmpdir"
|
||||
```
|
||||
|
||||
### Step 9: 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
|
||||
|
||||
@@ -146,3 +193,4 @@ 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)
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -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, Agent, Bash(date *), Bash(pwd), Bash(ls *), Bash(cat *), Bash(find *), Bash(rm *), Bash(mkdir *), Bash(python3 *), Bash(bash *)
|
||||
allowed-tools: Read, Write, Glob, Bash(date *), Bash(ls *), Bash(cat *), Bash(find *), Bash(rm *), Bash(mkdir *)
|
||||
---
|
||||
|
||||
# Session Log Skill
|
||||
@@ -17,9 +17,6 @@ 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"`
|
||||
|
||||
@@ -32,19 +29,16 @@ 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 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 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.
|
||||
|
||||
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
|
||||
### Step 1: Create the log directory if needed
|
||||
|
||||
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:
|
||||
@@ -78,82 +72,9 @@ 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]`)
|
||||
- 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.
|
||||
- Keep entries concise — this is raw material for `/reflect-logs`, not a polished document
|
||||
- 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.
|
||||
- Include enough context that each entry makes sense on its own without the full conversation
|
||||
|
||||
### Step 3: Prune old logs
|
||||
|
||||
@@ -170,6 +91,6 @@ After writing the log file, check for old logs that should be pruned:
|
||||
### Step 4: Summary
|
||||
|
||||
Print a brief summary:
|
||||
- In-context log file created (with path)
|
||||
- Whether a transcript analysis subagent was dispatched (and which backups it processed)
|
||||
- Log file created (with path)
|
||||
- Number of entries by section
|
||||
- Any files pruned or warnings issued
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
---
|
||||
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"`
|
||||
@@ -28,19 +28,6 @@ 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:
|
||||
@@ -111,11 +98,6 @@ 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>" \
|
||||
@@ -124,7 +106,7 @@ docker run -d \
|
||||
-w /project \
|
||||
--entrypoint uid-wrapper.sh \
|
||||
agent-claude:latest \
|
||||
claude --print --dangerously-skip-permissions --model <model> "<task prompt>"
|
||||
claude --print --dangerously-skip-permissions --model claude-sonnet-4-20250514 "<task prompt>"
|
||||
```
|
||||
|
||||
**Important:**
|
||||
@@ -133,7 +115,6 @@ 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`
|
||||
@@ -183,8 +164,6 @@ 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:
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,179 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,82 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
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`.
|
||||
@@ -1,128 +0,0 @@
|
||||
---
|
||||
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/`.
|
||||
Reference in New Issue
Block a user