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