@drunkcoding/agents-and-skills 0.0.14 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +5 -5
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +1 -1
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/team-superpower/.claude-plugin/plugin.json +2 -2
- package/plugins/team-superpower/README.md +24 -5
- package/plugins/team-superpower/agents/backend-developer.md +72 -7
- package/plugins/team-superpower/agents/designer.md +14 -0
- package/plugins/team-superpower/agents/frontend-developer.md +84 -5
- package/plugins/team-superpower/agents/planner.md +140 -17
- package/plugins/team-superpower/agents/qa-engineer.md +16 -0
- package/plugins/team-superpower/agents/reviewer.md +52 -6
- package/plugins/team-superpower/agents/security-engineer.md +99 -5
- package/plugins/team-superpower/agents/software-architect.md +14 -0
- package/plugins/team-superpower/assets/CLAUDE.md.template +96 -0
- package/plugins/team-superpower/assets/ESCALATION.md +34 -1
- package/plugins/team-superpower/assets/SESSION_README.md +102 -2
- package/plugins/team-superpower/commands/team-feature-resume.md +47 -8
- package/plugins/team-superpower/commands/team-feature.md +262 -13
- package/plugins/team-superpower/hooks/task-completed.sh +55 -10
- package/plugins/team-superpower/hooks/task-created.sh +81 -8
- package/plugins/team-superpower/hooks/teammate-idle.sh +1 -2
- package/plugins/team-superpower/scripts/detect-stack.sh +434 -0
- package/plugins/team-superpower/scripts/parse-claudemd.sh +194 -0
- package/plugins/team-superpower/scripts/team-state.sh +2 -2
- package/plugins/tech-graph/.claude-plugin/plugin.json +1 -1
|
@@ -28,6 +28,24 @@ Same prechecks as `/team-feature`:
|
|
|
28
28
|
|
|
29
29
|
Halt on any failure.
|
|
30
30
|
|
|
31
|
+
### Step 2.5 — Superpowers version-pin check
|
|
32
|
+
|
|
33
|
+
Read the checkpoint frontmatter (the YAML block between `---` markers at the top of the file). Extract `superpowers_version`. Read the currently-installed Superpowers version (`claude plugin list --json` → grep for `superpowers`). Compare:
|
|
34
|
+
|
|
35
|
+
- **Versions match** → proceed.
|
|
36
|
+
- **Versions differ** → halt and surface this 3-option menu to the owner:
|
|
37
|
+
|
|
38
|
+
> **Superpowers version drifted** since this feature started:
|
|
39
|
+
> - pinned in checkpoint: `<pinned>`
|
|
40
|
+
> - currently installed: `<current>`
|
|
41
|
+
>
|
|
42
|
+
> Skill semantics may have shifted. Pick one:
|
|
43
|
+
> - **A. Continue anyway** — accept the risk; semantics may differ mid-feature.
|
|
44
|
+
> - **B. Roll back Superpowers** — owner runs `/plugin install superpowers@<pinned>` and re-runs `/team-feature-resume`. (Lead halts; cannot install plugins itself.)
|
|
45
|
+
> - **C. Discard this feature** — halt resume; the owner manually cleans state via `/team-cleanup <slug>` and starts fresh.
|
|
46
|
+
|
|
47
|
+
This is **not** counted as a touchpoint because it only happens on resume after a rare Superpowers update. On choice A, log `superpowers_pin_overridden: <pinned> → <current>` to the resume-log block; on choice B halt without changes; on choice C halt and instruct the owner to `/team-cleanup`.
|
|
48
|
+
|
|
31
49
|
### Step 3 — Preflight scan
|
|
32
50
|
|
|
33
51
|
Run:
|
|
@@ -75,27 +93,46 @@ Do NOT re-run earlier phases. Their checkpoints stand.
|
|
|
75
93
|
|
|
76
94
|
If the checkpoint's `## Closing` block exists but is incomplete (has `decision:` and `cleanup: complete` but is missing the `worktree:` line) AND the recorded decision is `merged`, the previous lead crashed inside Step D.5. Resume protocol:
|
|
77
95
|
|
|
78
|
-
1.
|
|
79
|
-
2.
|
|
80
|
-
3.
|
|
81
|
-
4. On
|
|
96
|
+
1. Read `**Worktree origin:**` from the checkpoint. If it is `reused`, Step D.5 should never have run — abort the mid-Step-D.5 path: append `worktree: removal-skipped:reused-existing-worktree` to the Closing block, commit, and exit normally. The owner's pre-existing worktree stays on disk. Skip to Step 5 of the resume protocol only if there is still feature work to resume; otherwise the resume terminates here.
|
|
97
|
+
2. Verify Step A–D conditions still hold by running `bash ${CLAUDE_PLUGIN_ROOT}/scripts/team-state.sh scan <slug>` — all states must be `absent`. If anything is `present`, halt and instruct the owner to run `/team-cleanup <slug>` before resuming.
|
|
98
|
+
3. Re-run Step D.5 from the top: read `**Worktree:**`, `cd` to repo root, check `git worktree list --porcelain`, attempt non-forced remove. The procedure is idempotent — if the worktree was already removed in the prior session it'll be recorded as `already-absent`.
|
|
99
|
+
4. On remove failure, re-enter the 4-option remove-failure menu fresh (no carry-over retry count — the prior session's count was not persisted because Step D.5 retries are per-session, not per-run; this is intentional, the owner sees a fresh menu).
|
|
100
|
+
5. On completion, write the missing Closing-block fields (`worktree`, `worktree_path` if applicable, `dropped_files` if applicable) and commit.
|
|
82
101
|
|
|
83
102
|
### Step 5 — Reconstruct context
|
|
84
103
|
|
|
85
104
|
- `cd` into the worktree path recorded in the checkpoint. If it no longer exists, halt and escalate via the §7 template — the owner needs to restore or rebase the worktree before resume can continue.
|
|
86
|
-
- Recreate the team with the same name (`superpower-<slug>`)
|
|
105
|
+
- Recreate the team with the same name (`superpower-<slug>`) via the canonical `TeamCreate` tool:
|
|
106
|
+
```
|
|
107
|
+
TeamCreate({
|
|
108
|
+
team_name: "superpower-<slug>",
|
|
109
|
+
agent_type: "team-lead",
|
|
110
|
+
description: "<reuse the description from the previous run if recorded in the checkpoint; otherwise the owner's one-line request>"
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
TeamCreate is idempotent at the directory level — if `~/.claude/teams/superpower-<slug>/config.json` already exists from a partial cleanup, the runtime will refuse to overwrite. In that case, halt and instruct the owner to run `/team-cleanup <slug>` first (the resume protocol Step 3 should have caught this; if it didn't, that's a real bug — escalate).
|
|
87
114
|
- Touch `docs/superpowers/sessions/<slug>.heartbeat` and update it at every phase boundary (same protocol as `/team-feature`).
|
|
88
115
|
|
|
89
|
-
### Step 6 —
|
|
116
|
+
### Step 6 — Re-read the shape marker
|
|
117
|
+
|
|
118
|
+
Read `docs/superpowers/sessions/<slug>.shape` and `stack_shape` from the checkpoint frontmatter. They must match — if they disagree, halt and escalate (one was hand-edited). The shape determines which implementer to respawn:
|
|
119
|
+
|
|
120
|
+
- `full-stack` → both `backend-developer` and `frontend-developer` are eligible to respawn.
|
|
121
|
+
- `be-only` → only `backend-developer`. NEVER respawn `frontend-developer` for a `be-only` feature.
|
|
122
|
+
- `fe-only` → only `frontend-developer`. NEVER respawn `backend-developer`.
|
|
123
|
+
|
|
124
|
+
If the marker file is missing, re-derive shape from `CLAUDE.md` via `bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-claudemd.sh shape CLAUDE.md` and write the marker file before continuing.
|
|
125
|
+
|
|
126
|
+
### Step 6 (cont.) — Respawn only the teammates needed
|
|
90
127
|
|
|
91
|
-
For the next phase, spawn the relevant role(s) using the
|
|
128
|
+
For the next phase, spawn the relevant role(s) using the canonical `Agent` primitive documented in `/team-feature` § "Create the team (canonical primitive)" — same `subagent_type` / `team_name` / `name` / `prompt` shape, and `team_name` MUST be `superpower-<slug>` so messages route to the existing inboxes under `~/.claude/teams/superpower-<slug>/inboxes/`. Do **not** respawn teammates whose phase is complete unless that phase needs them again later (e.g. reviewer is reused in phase 7 for finish; planner is re-spawned if phase 3 returned `ARCH_BLOCKED` / `SEC_BLOCKED` and the plan needs revision; backend-developer / frontend-developer are re-spawned for `impl:qa-fix-*` or `impl:review-fix-*` tasks). Phase-to-role map:
|
|
92
129
|
|
|
93
130
|
| Next phase | Spawn |
|
|
94
131
|
|---|---|
|
|
95
132
|
| `design` | `designer` |
|
|
96
133
|
| `plan` | `planner` |
|
|
97
134
|
| `pre_impl_review` | `software-architect` + `security-engineer` (parallel) |
|
|
98
|
-
| `implementation` | `backend-developer` and/or `frontend-developer
|
|
135
|
+
| `implementation` | `backend-developer` and/or `frontend-developer`, **filtered by stack_shape** (be-only ⇒ BE only; fe-only ⇒ FE only; full-stack ⇒ both, route by prefix) |
|
|
99
136
|
| `qa` | `qa-engineer` |
|
|
100
137
|
| `review` | `reviewer` |
|
|
101
138
|
| `finish` | `reviewer` |
|
|
@@ -142,5 +179,7 @@ Continue per the same rules as `/team-feature`:
|
|
|
142
179
|
- **Never** skip Step 3 preflight. Stale team configs cause runtime errors and silently re-use the wrong session IDs.
|
|
143
180
|
- **Never** force-cleanup state with a fresh heartbeat unless the owner has confirmed in writing the previous lead is dead.
|
|
144
181
|
- **Never** skip the resume-log commit. It is the audit trail that proves the resume happened.
|
|
182
|
+
- **Never** respawn an implementer for a shape that excludes it (`frontend-developer` in `be-only`, `backend-developer` in `fe-only`). The hooks will reject the implementer's task creations anyway, but spawning is your decision and you do not bypass the shape.
|
|
183
|
+
- **Never** silently ignore a `superpowers_version` mismatch. Always surface the 3-option menu at Step 2.5.
|
|
145
184
|
|
|
146
185
|
If anything in the checkpoint looks tampered with or inconsistent (e.g. plan marked approved but no plan file exists, completed task with missing commits), halt and escalate to the owner with the §7 template. Do not paper over.
|
|
@@ -5,6 +5,10 @@ argument-hint: <one-line feature idea>
|
|
|
5
5
|
|
|
6
6
|
You are the **lead** of an agent team implementing the Superpowers methodology across multiple parallel Claude Code sessions.
|
|
7
7
|
|
|
8
|
+
> **Lead model:** run this command in an **Opus** session. The lead carries the cross-phase reasoning load (planning gates, escalation triage, FINISH_BLOCKED recovery, owner touchpoint budget). All 8 teammate agents are pinned to **Sonnet** via their frontmatter (`model: sonnet`) — they spawn on Sonnet regardless of the lead's model. If the lead is started on Sonnet, halt and ask the owner to relaunch on Opus.
|
|
9
|
+
>
|
|
10
|
+
> **Lead thinking discipline:** adaptive. Use extended (high-effort) thinking for every gate decision, escalation triage, classification of clarification questions (tactical / cross-role / architectural / owner-only), FINISH_BLOCKED recovery, and worktree-cleanup branching. Routine heartbeats, mailbox forwarding, status polls, and shared-task-list status reads may be quick. Teammates default to high thinking on every non-trivial step (see each agent's "Thinking discipline" section); the lead is the only role that scales effort per action.
|
|
11
|
+
|
|
8
12
|
Owner's feature request:
|
|
9
13
|
|
|
10
14
|
$ARGUMENTS
|
|
@@ -15,13 +19,93 @@ You are a **conductor**, not an implementer. Spawn teammates and coordinate them
|
|
|
15
19
|
|
|
16
20
|
## Required prechecks (run these first, in order)
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
0. **Lead-model self-attestation.** Before doing anything else, state which model you (the lead) are currently running on. If you are not running on Opus, halt and instruct the owner: "Lead must be on Opus. Relaunch this session with `claude --model opus` (or pick Opus in the model switcher) and rerun `/team-feature`." Teammates are pinned to Sonnet via their agent frontmatter; only the lead model is set by the session.
|
|
23
|
+
1. Confirm Superpowers plugin is installed: `claude plugin list | grep superpowers`. If missing, **halt** and instruct the owner: `/plugin install superpowers@claude-plugins-official`. Capture the version string from `claude plugin list --json` (e.g. `5.0.7`) — you'll write it to the checkpoint in phase 0 step 5 below.
|
|
19
24
|
2. Confirm Claude Code version is `2.1.32` or later: `claude --version`. If older, halt.
|
|
20
25
|
3. Confirm `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` is set in the environment. If not, halt and instruct the owner to add it to `~/.claude/settings.json` under `env`.
|
|
21
26
|
4. Generate a kebab-case `<slug>` from the owner's request. Use it in every artifact filename for the rest of the run. The team you create MUST be named exactly `superpower-<slug>` — every cleanup and resume primitive depends on that convention.
|
|
22
27
|
5. Create directories if missing: `docs/superpowers/{sessions,specs,plans,reviews}`.
|
|
23
28
|
6. Seed `docs/superpowers/ESCALATION.md` from `${CLAUDE_PLUGIN_ROOT}/assets/ESCALATION.md` if it does not already exist. Seed `docs/superpowers/README.md` from `${CLAUDE_PLUGIN_ROOT}/assets/SESSION_README.md` if missing. Commit any seeded files.
|
|
24
29
|
|
|
30
|
+
## Phase 0 — Stack detection, version pinning, shape decision
|
|
31
|
+
|
|
32
|
+
This phase runs **after** preflight clears (see the next section) and **before** spawning any teammate. It decides which teammates to spawn and pins the Superpowers version so a mid-feature skill update can't corrupt recovery.
|
|
33
|
+
|
|
34
|
+
### 0.1 — Read CLAUDE.md, or detect
|
|
35
|
+
|
|
36
|
+
1. Check whether `CLAUDE.md` exists at the repo root AND contains a fenced `team-superpower` block:
|
|
37
|
+
```bash
|
|
38
|
+
bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-claudemd.sh extract CLAUDE.md
|
|
39
|
+
```
|
|
40
|
+
- Exit 0: a block was extracted. Parse it. Skip to 0.2.
|
|
41
|
+
- Exit 1 (file missing or no block): run detection.
|
|
42
|
+
|
|
43
|
+
2. Run detection:
|
|
44
|
+
```bash
|
|
45
|
+
bash ${CLAUDE_PLUGIN_ROOT}/scripts/detect-stack.sh "$PWD" > /tmp/team-superpower-detected.yaml
|
|
46
|
+
```
|
|
47
|
+
- Exit 0 (confident): write the detected YAML (plus a one-line header) to `docs/superpowers/stack.detected.md` and commit. Halt with this message to the owner (via §7 escalation): "I detected this stack — `docs/superpowers/stack.detected.md`. Review the `# CONFIRM:` lines, paste the corrected block into a `team-superpower` fenced section of your CLAUDE.md (or create CLAUDE.md from `${CLAUDE_PLUGIN_ROOT}/assets/CLAUDE.md.template`), then re-run `/team-feature`." **Do NOT auto-edit CLAUDE.md — the spec forbids it.**
|
|
48
|
+
- Exit 1 (no signal): halt. Escalate to the owner: "No backend or frontend signal found in the repo. Create a CLAUDE.md from `${CLAUDE_PLUGIN_ROOT}/assets/CLAUDE.md.template` and re-run."
|
|
49
|
+
- Exit 2 (ambiguous): write the detected YAML to `docs/superpowers/stack.detected.md` with both candidate BE languages marked; halt and escalate so the owner picks one.
|
|
50
|
+
|
|
51
|
+
### 0.2 — Determine stack shape
|
|
52
|
+
|
|
53
|
+
Use the parser:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
shape="$(bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-claudemd.sh shape CLAUDE.md)"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`$shape` is one of `full-stack` | `be-only` | `fe-only` | `none`. If `none` (the block has `backend: none` AND `frontend: none`), halt and escalate — that combination is non-sensical.
|
|
60
|
+
|
|
61
|
+
### 0.3 — Cross-validate: claimed stack vs. filesystem
|
|
62
|
+
|
|
63
|
+
For every claimed component, verify at least one corresponding source file exists. Example checks:
|
|
64
|
+
|
|
65
|
+
- `backend.language: csharp` → at least one `*.csproj` or `*.sln` exists.
|
|
66
|
+
- `backend.language: node-ts` → `package.json` exists and declares a server dep (`express`, `fastify`, `koa`, `@nestjs/core`, etc.).
|
|
67
|
+
- `frontend.framework: react` → `package.json` declares `react`.
|
|
68
|
+
- `contracts.source_of_truth: openapi` → an OpenAPI file exists at `contracts.openapi_path` if specified.
|
|
69
|
+
|
|
70
|
+
If any claimed component has no file evidence, halt and escalate. CLAUDE.md is the contract, but a contract that contradicts the filesystem is a bug to flag, not a configuration to act on.
|
|
71
|
+
|
|
72
|
+
### 0.4 — Write the shape marker
|
|
73
|
+
|
|
74
|
+
Write the resolved shape to a marker file the hooks read:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
mkdir -p docs/superpowers/sessions
|
|
78
|
+
echo "$shape" > docs/superpowers/sessions/<slug>.shape
|
|
79
|
+
git add docs/superpowers/sessions/<slug>.shape
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The `TaskCreated` hook reads this marker to enforce shape-appropriate `impl:` sub-prefixes.
|
|
83
|
+
|
|
84
|
+
### 0.5 — Decide team composition (shape-adaptive spawn)
|
|
85
|
+
|
|
86
|
+
| Shape | Teammates to spawn |
|
|
87
|
+
|---------------|--------------------|
|
|
88
|
+
| `full-stack` | designer, planner, software-architect, security-engineer, backend-developer, frontend-developer, qa-engineer, reviewer (**8 total**) |
|
|
89
|
+
| `be-only` | designer, planner, software-architect, security-engineer, backend-developer, qa-engineer, reviewer (**7 total**) |
|
|
90
|
+
| `fe-only` | designer, planner, software-architect, security-engineer, frontend-developer, qa-engineer, reviewer (**7 total**) |
|
|
91
|
+
|
|
92
|
+
`software-architect`, `security-engineer`, `qa-engineer`, and `reviewer` are stack-agnostic and ALWAYS spawn. Designer, planner, and the implementers adapt.
|
|
93
|
+
|
|
94
|
+
Spawning happens at phase boundaries (you don't spawn implementers until phase 4 starts; you don't spawn the reviewer until phase 6) — this section just decides which teammates the team will EVER spawn for this feature. Record the list in the checkpoint.
|
|
95
|
+
|
|
96
|
+
### 0.6 — Pin the Superpowers version
|
|
97
|
+
|
|
98
|
+
Read the installed Superpowers version (from precheck step 1) and write it to the checkpoint frontmatter. This pins the skill-set for this feature. `/team-feature-resume` reads it back and refuses to continue if the installed version has drifted.
|
|
99
|
+
|
|
100
|
+
The frontmatter format is in the **Checkpointing** section below; the relevant added fields are:
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
superpowers_version: <e.g. 5.0.7>
|
|
104
|
+
plugin_version: <team-superpower plugin version>
|
|
105
|
+
claude_code_version: <e.g. 2.1.32>
|
|
106
|
+
stack_shape: full-stack | be-only | fe-only
|
|
107
|
+
```
|
|
108
|
+
|
|
25
109
|
## Preflight — detect stale or orphaned state
|
|
26
110
|
|
|
27
111
|
Before writing any checkpoint or spawning any teammate, run the helper:
|
|
@@ -49,33 +133,153 @@ Same-session check: if the current Claude Code session already manages an agent
|
|
|
49
133
|
|
|
50
134
|
## Initial checkpoint and heartbeat
|
|
51
135
|
|
|
52
|
-
After preflight clears:
|
|
136
|
+
After preflight clears AND phase 0 has decided the shape:
|
|
53
137
|
|
|
54
|
-
1. Write the initial checkpoint `docs/superpowers/sessions/YYYY-MM-DD-<slug>.md` per the format in the **Checkpointing** section and commit it.
|
|
138
|
+
1. Write the initial checkpoint `docs/superpowers/sessions/YYYY-MM-DD-<slug>.md` per the format in the **Checkpointing** section — including the v2 frontmatter fields (`superpowers_version`, `plugin_version`, `claude_code_version`, `stack_shape`) — and commit it.
|
|
55
139
|
2. `touch docs/superpowers/sessions/<slug>.heartbeat` and commit (or leave uncommitted — the file is intentionally ephemeral; either is fine). **Touch this heartbeat at every phase boundary** and any time you remain active for more than ~10 minutes inside a phase. The cleanup script uses its mtime to decide whether a future session is allowed to wipe state.
|
|
56
|
-
3.
|
|
140
|
+
3. Ensure `docs/superpowers/sessions/<slug>.shape` was written in phase 0.4 and is committed.
|
|
141
|
+
4. Write checkpoint updates atomically: write to `<file>.tmp` then `mv -f <file>.tmp <file>`. Half-written checkpoints corrupt recovery.
|
|
142
|
+
|
|
143
|
+
## Create the team (canonical primitive)
|
|
144
|
+
|
|
145
|
+
Immediately after writing the initial checkpoint and **before** spawning any teammate, create the team via the canonical Claude Code `TeamCreate` tool. Do NOT mkdir `~/.claude/teams/...` by hand — the runtime owns that directory and the inbox files inside it.
|
|
146
|
+
|
|
147
|
+
Call:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
TeamCreate({
|
|
151
|
+
team_name: "superpower-<slug>",
|
|
152
|
+
agent_type: "team-lead",
|
|
153
|
+
description: "<one-line owner request, ≤120 chars>"
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This creates:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
~/.claude/teams/superpower-<slug>/
|
|
161
|
+
├── config.json ← team configuration & members (lead-managed)
|
|
162
|
+
└── inboxes/
|
|
163
|
+
├── team-lead.json ← your inbox (auto-populated as teammates SendMessage you)
|
|
164
|
+
├── designer.json ← created when the designer teammate is spawned
|
|
165
|
+
├── planner.json ← created when the planner is spawned
|
|
166
|
+
└── ... ← one file per teammate name (= the agent's role)
|
|
167
|
+
|
|
168
|
+
~/.claude/tasks/superpower-<slug>/
|
|
169
|
+
└── ... ← shared task list (TaskCreate / TaskUpdate)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The runtime appends every inbound `SendMessage` to the recipient's JSON array with shape:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"from": "<sender role name>",
|
|
177
|
+
"text": "<message body>",
|
|
178
|
+
"summary": "<5-10 word preview>",
|
|
179
|
+
"timestamp": "<ISO 8601 UTC>",
|
|
180
|
+
"read": false,
|
|
181
|
+
"color": "<UI hint>"
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
You never write these files directly. Always use `SendMessage` to deliver, and read your inbox through the automatic delivery the runtime hands you (see "Automatic Message Delivery" in the TeamCreate tool docs). The only hand-read is `~/.claude/teams/superpower-<slug>/config.json` when you need to discover member roles by name.
|
|
186
|
+
|
|
187
|
+
### Spawning teammates (canonical Agent call)
|
|
188
|
+
|
|
189
|
+
When you spawn a teammate (per the phase chain below), use the `Agent` tool with **all four** of these parameters:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
Agent({
|
|
193
|
+
subagent_type: "<role>", // e.g. "team-superpower:designer"; matches the agent .md filename
|
|
194
|
+
team_name: "superpower-<slug>",
|
|
195
|
+
name: "<role>", // e.g. "designer"; becomes the inbox filename (designer.json) and the SendMessage `to` value
|
|
196
|
+
prompt: "<filled Spawn prompt template, see below>"
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Hard rules for the spawn call:
|
|
201
|
+
|
|
202
|
+
- `name` MUST equal the role string (`designer`, `planner`, `software-architect`, `security-engineer`, `backend-developer`, `frontend-developer`, `qa-engineer`, `reviewer`). Inbox filenames depend on this.
|
|
203
|
+
- `team_name` MUST equal `superpower-<slug>` — every cleanup, resume, and `team-state.sh` primitive depends on this convention.
|
|
204
|
+
- `subagent_type` MUST match the agent definition shipped by this plugin (`team-superpower:<role>`).
|
|
205
|
+
- Do NOT spawn the same role twice in parallel. If a role needs a second pass, mailbox the existing teammate instead of spawning a duplicate.
|
|
206
|
+
|
|
207
|
+
## Spawn prompt template (use verbatim — do NOT improvise per role)
|
|
208
|
+
|
|
209
|
+
Every teammate spawn MUST hand over the same minimum context. A teammate inherits project context (`CLAUDE.md`, MCP servers, skills) automatically but does NOT inherit your conversation history — anything implicit on your side is invisible on theirs. Use this template:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
You are the <role> teammate for feature `<slug>`.
|
|
213
|
+
|
|
214
|
+
Stack shape: <full-stack | be-only | fe-only> (read docs/superpowers/sessions/<slug>.shape if you need to confirm)
|
|
215
|
+
Worktree: <absolute path> (planner records this in WORKTREE_READY)
|
|
216
|
+
Resume: <yes | no> (yes when spawned by /team-feature-resume; pick up at the next pending task)
|
|
217
|
+
|
|
218
|
+
Read first:
|
|
219
|
+
- CLAUDE.md (free-form prose AND the `team-superpower` block)
|
|
220
|
+
- Your role brief: <relative path to plugins/team-superpower/agents/<role>.md>
|
|
221
|
+
- <role-relevant artefact paths — list them all; e.g. design doc, plan, ARCH/SEC reports, QA report>
|
|
222
|
+
|
|
223
|
+
Open escalations:
|
|
224
|
+
<one-line summary per open escalation in the checkpoint, or "(none)">
|
|
225
|
+
|
|
226
|
+
Your task: <one sentence describing the specific phase work the role is being spawned for>
|
|
227
|
+
Mailbox signal expected back: <e.g. DESIGN_APPROVED <path>, PLAN_READY <path>, ARCH_PASSED <path>, BE_DONE <task-id>, etc.>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Fill every field. If a field is genuinely N/A for a role (e.g. there is no QA report when spawning the designer), write `n/a` rather than omitting the line — the template's stability is what keeps respawns deterministic.
|
|
57
231
|
|
|
58
232
|
## Phase chain (strict order — no skipping, no inlining)
|
|
59
233
|
|
|
60
234
|
1. **Design (designer).** Spawn the `designer` teammate. Hand it `<slug>` and the owner's request. Wait for `DESIGN_APPROVED <path>` in your mailbox. If the designer asks a clarifying question, answer from project context if unambiguous; otherwise batch with any open questions and use the §7 escalation template to the owner. Checkpoint: `phase: design, status: complete`. Touch heartbeat.
|
|
61
235
|
|
|
62
|
-
2. **Plan (planner).** Spawn the `planner` teammate. Hand it `<slug>` and the design doc path. Wait for `WORKTREE_READY` then `PLAN_READY <path>`. Route the plan to the owner for approval (second owner touchpoint). On approval, stamp `plan_approved_at: <ISO datetime>` into the metadata of every `impl:` task you will create — the `TaskCompleted` hook checks for it. Checkpoint: `phase: plan, status: approved`. Touch heartbeat.
|
|
236
|
+
2. **Plan (planner).** Spawn the `planner` teammate. Hand it `<slug>` and the design doc path. Wait for `WORKTREE_READY <path> <branch> <origin>` (`<origin>` ∈ {`reused`, `created`} — if the planner posts the legacy 2-arg form, treat as `created` for backward compatibility) then `PLAN_READY <path>`. Record both `**Worktree:** <path>` and `**Worktree origin:** <origin>` in the checkpoint. Route the plan to the owner for approval (second owner touchpoint). On approval, stamp `plan_approved_at: <ISO datetime>` into the metadata of every `impl:` task you will create — the `TaskCompleted` hook checks for it. Checkpoint: `phase: plan, status: approved`. Touch heartbeat.
|
|
63
237
|
|
|
64
238
|
3. **Pre-impl review gate (software-architect + security-engineer, parallel).** Spawn both. Hand each the design doc path AND the plan path. Wait for `ARCH_PASSED <path>` AND `SEC_PASSED <path>`. If either posts `ARCH_BLOCKED` / `SEC_BLOCKED`, route the findings to `planner` for a plan revision, then re-route to whichever gate is still blocking. Cap at three plan-revision rounds — escalate to owner via §7 if it does not converge. Checkpoint: `phase: pre_impl_review, status: passed | blocked`. Touch heartbeat.
|
|
65
239
|
|
|
66
|
-
4. **Implementation (
|
|
240
|
+
4. **Implementation (shape-adaptive, parallel where allowed).** Read the approved plan. Create one shared-task-list entry per plan task with the planner's assigned title (`impl:be-*`, `impl:fe-*`, `impl:be-migration-*`, `impl:be-contract-publish-*`), body = full task text including verification, and `depends_on` + `files` + `tests` + `estimated_minutes` + `plan_approved_at` metadata from the plan.
|
|
241
|
+
|
|
242
|
+
**Spawn rule (shape-adaptive):**
|
|
243
|
+
- `full-stack`: spawn one `backend-developer` AND one `frontend-developer`.
|
|
244
|
+
- `be-only`: spawn one `backend-developer` only. Do NOT spawn `frontend-developer`.
|
|
245
|
+
- `fe-only`: spawn one `frontend-developer` only. Do NOT spawn `backend-developer`.
|
|
246
|
+
|
|
247
|
+
**Contract publish (full-stack only).** If the planner emitted `impl:be-contract-publish-<slug>` as the first task, the backend-developer claims it first. Do NOT release any `impl:fe-*` task to the frontend-developer until you see `CONTRACT_PUBLISHED <task-id>` in your mailbox. The plan tasks already encode `depends_on: [impl:be-contract-publish-<slug>]` on every FE task, but you enforce the gate at the assignment level too.
|
|
248
|
+
|
|
249
|
+
**Mid-implementation contract drift.** If you receive `CONTRACT_DRIFT_DETECTED` from frontend-developer, or backend-developer files an `impl:contract-update-*` task on its own, pause all `impl:fe-*` in-flight work (post a "pause" message to frontend-developer's mailbox; it will idle on its current task). Wait for `CONTRACT_UPDATED <task-id>` from backend-developer, then unpause FE. Frontend-developer re-pulls the contract hash on resume.
|
|
250
|
+
|
|
251
|
+
**Migration serialization.** `impl:be-migration-*` tasks must run one at a time. The planner chains them via `depends_on`, the `TaskCompleted` hook is a backstop with `MIGRATION_RACE`, and you enforce it at assignment: do not release a second migration task while one is `in_progress`.
|
|
252
|
+
|
|
253
|
+
**File-scope conflict check.** Verify no two active implementer tasks overlap in file scope — if a conflict appears, serialize by holding the second task. Watch for `BE_DONE` / `FE_DONE`.
|
|
254
|
+
|
|
255
|
+
Checkpoint after each task transition: `phase: implementation, tasks_complete: M/N`. Touch heartbeat at every transition.
|
|
67
256
|
|
|
68
257
|
5. **QA gate (qa-engineer).** Once every `impl:` task is complete, spawn `qa-engineer`. Wait for `QA_PASSED <path>` or `QA_BLOCKED <path>`. If blocked, the QA report contains `impl:qa-fix-be-` / `impl:qa-fix-fe-` tasks — file them in the shared task list and loop to phase 4. Checkpoint: `phase: qa, status: passed | blocked`. Touch heartbeat.
|
|
69
258
|
|
|
70
259
|
6. **Code review (reviewer).** Once `QA_PASSED`, file a `review:` task and spawn the `reviewer` teammate. Wait for `REVIEW_PASSED <path>`. If critical issues come back instead, the reviewer report names the responsible implementer (`backend-developer` or `frontend-developer`) and the failing task — file `impl:review-fix-be-` / `impl:review-fix-fe-` tasks and loop to phase 4. Checkpoint: `phase: review, status: pass | critical_issues_returned`. Touch heartbeat.
|
|
71
260
|
|
|
72
|
-
7. **Finish (reviewer).** Same reviewer runs `finishing-a-development-branch`.
|
|
261
|
+
7. **Finish (reviewer, with CI gate).** Same reviewer runs `finishing-a-development-branch`. Before presenting the finish menu, the reviewer pushes the branch and (if `ci.provider != none`) polls CI per the `ci` block in CLAUDE.md until all `required_checks` go green, time out, or fail. On CI red, the reviewer posts `FINISH_BLOCKED ci-red <failed-checks>` and you surface the merge-failure menu with an added option F **"Show CI logs"**. On CI timeout, the reviewer posts `FINISH_BLOCKED ci-timeout` and you surface a 3-option menu (re-poll / switch to `pr_opened` / escalate). The CI gate is the **same finish-branch touchpoint** — no new touchpoint.
|
|
262
|
+
|
|
263
|
+
The owner makes the merge / PR / keep / discard decision (third and last owner touchpoint). On `FINISH_DONE <decision> <ref>`, checkpoint: `phase: finish, status: <merged|pr_opened|kept|discarded>`. Touch heartbeat. If the reviewer posts `FINISH_BLOCKED <reason>` (any reason — merge or CI), follow **Phase 7 merge-failure handling** below.
|
|
73
264
|
|
|
74
265
|
## Phase 7 merge-failure handling
|
|
75
266
|
|
|
76
|
-
When the reviewer posts `FINISH_BLOCKED <reason>` (instead of `FINISH_DONE`), the merge step
|
|
267
|
+
When the reviewer posts `FINISH_BLOCKED <reason>` (instead of `FINISH_DONE`), the merge step or the CI gate failed. Handle it inline — this is the same owner touchpoint as the finish-branch decision continued, NOT a new touchpoint.
|
|
268
|
+
|
|
269
|
+
`<reason>` is one of `conflict` / `non-ff` / `dirty-worktree` / `push-rejected` / `ci-red <failed-checks>` / `ci-timeout` / `other:<short-string>` (see `agents/reviewer.md` § Hat 2 for the full enum and what each reason means).
|
|
270
|
+
|
|
271
|
+
### CI-specific menus
|
|
272
|
+
|
|
273
|
+
For `ci-red <failed-checks>` use the standard 5-option merge-failure menu **plus an option F: Show CI logs**, which runs `gh run view --log-failed` (or the provider equivalent the reviewer is using) and pipes the output into the conversation, then re-presents the menu.
|
|
274
|
+
|
|
275
|
+
For `ci-timeout` use a 3-option menu:
|
|
77
276
|
|
|
78
|
-
|
|
277
|
+
> **CI did not finish within the poll window.** Pick one:
|
|
278
|
+
> - **A. Re-poll** — wait another `ci.poll_timeout_minutes` for CI to finish.
|
|
279
|
+
> - **B. Switch to pr_opened** — open a PR; the owner deals with CI on the PR side.
|
|
280
|
+
> - **E. Escalate** — §7 escalation, full reviewer status appended.
|
|
281
|
+
|
|
282
|
+
Re-poll is the same finish-branch touchpoint continued. The `merge_retries` counter does NOT apply to CI re-polls (it tracks merge attempts, not CI polls); cap re-polls at 3 instead, recorded as `ci_repolls: K/3` in the checkpoint.
|
|
79
283
|
|
|
80
284
|
1. Read the mailbox message. Stash `<reason>` and the verbatim git stderr.
|
|
81
285
|
2. Update the checkpoint: `phase: finish, status: merge_blocked, reason: <reason>, merge_retries: K/3` where `K` is the count of prior retry attempts in this run (start at 0).
|
|
@@ -184,12 +388,14 @@ Runs only after Step C / D have brought platform state to absent. Removes the pl
|
|
|
184
388
|
3. Step B teammate shutdown was clean.
|
|
185
389
|
4. The post-Step-C (or post-Step-D) scan shows `team_config_state: absent`, `task_list_state: absent`, `tmux_state: absent`.
|
|
186
390
|
5. The checkpoint has a non-empty `**Worktree:**` field.
|
|
391
|
+
6. The checkpoint records `**Worktree origin:** created`. A `reused` origin means the worktree existed before this run — it is the owner's, not ours to remove.
|
|
187
392
|
|
|
188
393
|
If any condition fails, record `worktree: removal-skipped:<reason>` in the Closing block (Step E) where `<reason>` is one of:
|
|
189
394
|
|
|
190
395
|
- `not-merged-decision` — finish decision was `pr_opened`, `kept`, or `discarded`.
|
|
191
396
|
- `team-cleanup-incomplete` — Step C/D left platform state present.
|
|
192
397
|
- `no-worktree-recorded` — checkpoint has no `**Worktree:**` line.
|
|
398
|
+
- `reused-existing-worktree` — `**Worktree origin:** reused`; the owner pre-existed the worktree and keeps it.
|
|
193
399
|
|
|
194
400
|
**Procedure** (only when all trigger conditions pass):
|
|
195
401
|
|
|
@@ -241,7 +447,7 @@ Append a closing block to the checkpoint:
|
|
|
241
447
|
- dropped_files: [<path>, ...] # only when state == force-removed
|
|
242
448
|
```
|
|
243
449
|
|
|
244
|
-
`removal-skipped` reasons: `not-merged-decision` | `team-cleanup-incomplete` | `no-worktree-recorded`.
|
|
450
|
+
`removal-skipped` reasons: `not-merged-decision` | `team-cleanup-incomplete` | `no-worktree-recorded` | `reused-existing-worktree`.
|
|
245
451
|
|
|
246
452
|
Remove the `<slug>.heartbeat` file. Commit the checkpoint. Confirm to the owner: "Team cleaned up. Feature complete."
|
|
247
453
|
|
|
@@ -249,6 +455,20 @@ Remove the `<slug>.heartbeat` file. Commit the checkpoint. Confirm to the owner:
|
|
|
249
455
|
|
|
250
456
|
Tell the owner exactly which step failed, include the script output verbatim, and instruct them to run `/team-cleanup <slug>` once they have confirmed nothing else is running. Do **not** retry cleanup loops automatically — the safety check is the heartbeat, and you cannot meaningfully refresh it from outside the lead process.
|
|
251
457
|
|
|
458
|
+
## Within-phase stall watchdog
|
|
459
|
+
|
|
460
|
+
Heartbeat at phase boundaries is not enough — a teammate can hang silently mid-phase and you'd never notice. Run a watchdog:
|
|
461
|
+
|
|
462
|
+
1. Read `limits.phase_stall_minutes` from CLAUDE.md (`bash ${CLAUDE_PLUGIN_ROOT}/scripts/parse-claudemd.sh get limits.phase_stall_minutes CLAUDE.md`). Default to **30** if unset.
|
|
463
|
+
2. Inside any phase, if the watchdog window elapses with **no mailbox message AND no shared-task-list state transition** from the active teammate(s):
|
|
464
|
+
- Send a ping to the teammate's mailbox: `STATUS_CHECK <slug> — no activity for <N> minutes; reply with current status or progress note.`
|
|
465
|
+
- Start a second watchdog window of the same length.
|
|
466
|
+
3. If a second watchdog window also elapses with no reply: surface a §7 escalation to the owner with the teammate's last-known status, the elapsed wall time, and the current phase. Halt — do NOT silently respawn or force-cancel; the owner decides.
|
|
467
|
+
|
|
468
|
+
The watchdog is **not** an owner touchpoint by itself — pinging the teammate is internal. Escalation step 3 is what reaches the owner, and only via the §7 template (so it doesn't count against the 3 allowed touchpoints either).
|
|
469
|
+
|
|
470
|
+
Reset the watchdog on every received mailbox message and every task transition. Touch the heartbeat each time you reset.
|
|
471
|
+
|
|
252
472
|
## Owner touchpoints (the ONLY allowed pings to the owner)
|
|
253
473
|
|
|
254
474
|
1. Design sign-off (phase 1, the brainstorming skill's built-in step).
|
|
@@ -262,11 +482,21 @@ Tell the owner exactly which step failed, include the script output verbatim, an
|
|
|
262
482
|
After every phase boundary, write `docs/superpowers/sessions/YYYY-MM-DD-<slug>.md` atomically (tmp + rename) per this format and commit it. This is the only way the workflow survives a `/resume` failure:
|
|
263
483
|
|
|
264
484
|
```markdown
|
|
485
|
+
---
|
|
486
|
+
slug: <slug>
|
|
487
|
+
started: <ISO datetime>
|
|
488
|
+
superpowers_version: <e.g. 5.0.7>
|
|
489
|
+
plugin_version: <team-superpower plugin version>
|
|
490
|
+
claude_code_version: <e.g. 2.1.32>
|
|
491
|
+
stack_shape: full-stack | be-only | fe-only
|
|
492
|
+
---
|
|
493
|
+
|
|
265
494
|
# Session: <slug>
|
|
266
495
|
**Started:** <ISO datetime>
|
|
267
496
|
**Last update:** <ISO datetime>
|
|
268
497
|
**Team:** superpower-<slug>
|
|
269
498
|
**Worktree:** <path>
|
|
499
|
+
**Worktree origin:** created | reused # `reused` means the owner launched `/team-feature` from inside a linked worktree; Step D.5 skips removal in that case
|
|
270
500
|
|
|
271
501
|
## Phases
|
|
272
502
|
- [x] design → docs/superpowers/specs/YYYY-MM-DD-<slug>-design.md
|
|
@@ -279,18 +509,23 @@ After every phase boundary, write `docs/superpowers/sessions/YYYY-MM-DD-<slug>.m
|
|
|
279
509
|
- [ ] finish — when `FINISH_BLOCKED <reason>` is in flight, this line reads `- [ ] finish (blocked: <reason>, merge_retries: K/3)` instead, and stays unchecked until `FINISH_DONE` arrives.
|
|
280
510
|
|
|
281
511
|
## Teammates
|
|
512
|
+
(list reflects stack_shape — omit the implementer that doesn't exist for be-only / fe-only)
|
|
282
513
|
- designer (agent-id: ...) — idle
|
|
283
514
|
- planner (agent-id: ...) — idle
|
|
284
515
|
- software-architect (agent-id: ...) — idle
|
|
285
516
|
- security-engineer (agent-id: ...) — idle
|
|
286
|
-
- backend-developer (agent-id: ...) — active on task impl:be-<name>
|
|
287
|
-
- frontend-developer (agent-id: ...) — idle
|
|
517
|
+
- backend-developer (agent-id: ...) — active on task impl:be-<name> # full-stack | be-only
|
|
518
|
+
- frontend-developer (agent-id: ...) — idle # full-stack | fe-only
|
|
288
519
|
- qa-engineer (agent-id: ...) — idle
|
|
289
520
|
- reviewer (agent-id: ...) — idle
|
|
290
521
|
|
|
291
522
|
## Open escalations
|
|
292
523
|
- (none) | <escalation-template entries>
|
|
293
524
|
|
|
525
|
+
## Assumptions
|
|
526
|
+
(appended after each phase; one entry per non-owner decision)
|
|
527
|
+
- <ISO ts> <role> [class=<tactical|cross-role|architectural>]: <one-line decision> (peer: <role|none>, evidence: <link to mailbox msg | n/a>)
|
|
528
|
+
|
|
294
529
|
## Resume protocol
|
|
295
530
|
1. Owner runs /team-feature-resume with this filename.
|
|
296
531
|
2. Lead respawns teammates using same role definitions.
|
|
@@ -313,4 +548,18 @@ After every phase boundary, write `docs/superpowers/sessions/YYYY-MM-DD-<slug>.m
|
|
|
313
548
|
- **Never** retry merge more than 3 times. After the 3rd `FINISH_BLOCKED`, drop option A from the 5-option menu and require B/C/D/E.
|
|
314
549
|
- **Never** treat the `FINISH_BLOCKED` menu as a new owner touchpoint. It is the same finish-branch touchpoint continued — the 3-touchpoint cap stays at 3.
|
|
315
550
|
|
|
316
|
-
|
|
551
|
+
## Hard rules (v2 additions)
|
|
552
|
+
|
|
553
|
+
- **Never** spawn `frontend-developer` in a `be-only` shape, or `backend-developer` in a `fe-only` shape. The shape was decided in phase 0.5 from CLAUDE.md (or the auto-detection fallback the owner has confirmed); deviating means a different team than the owner agreed to.
|
|
554
|
+
- **Never** auto-edit the user's `CLAUDE.md`. Phase 0 writes to `docs/superpowers/stack.detected.md` only and asks the owner to paste/edit. The user's CLAUDE.md is theirs.
|
|
555
|
+
- **Never** release an `impl:fe-*` task before `CONTRACT_PUBLISHED` arrives (full-stack with `contracts.source_of_truth != none`).
|
|
556
|
+
- **Never** release a second `impl:be-migration-*` task while one is `in_progress`. The hook is a backstop; you are the primary control.
|
|
557
|
+
- **Never** present the finish-branch menu before the CI gate either passes or is explicitly bypassed (CI red → menu with "Show CI logs"; CI timeout → 3-option menu; `ci.provider: none` → skip the gate entirely but still push).
|
|
558
|
+
- **Never** wait passively on a teammate for longer than `limits.phase_stall_minutes` (default 30) without running the within-phase stall watchdog above. Two consecutive stall windows with no teammate activity must escalate via §7.
|
|
559
|
+
- **Never** improvise a spawn prompt. Use the **Spawn prompt template** verbatim — leave fields as `n/a` rather than omitting them.
|
|
560
|
+
- **Never** spawn more than 5 teammates concurrently. The plugin defines up to 8 lifetime roles but phase-gating must keep ≤ 5 active at any moment. If a future change would break this, halt and escalate.
|
|
561
|
+
- **Never** run Step D.5 worktree removal when `**Worktree origin:** reused`. The worktree existed before `/team-feature` started; the owner owns it. Record `worktree: removal-skipped:reused-existing-worktree` and leave the worktree on disk.
|
|
562
|
+
- **Never** let the planner run inside a linked worktree on a protected branch (`main`, `master`, `develop`, `dev`, `release/*`, `releases/*`). The planner halts and escalates; the owner switches to a feature branch and re-runs.
|
|
563
|
+
- **Never** forward an owner-bound escalation when the originator's `class` is not `owner-only` AND `Peer attempts` lists fewer than one round-trip with a peer. The lead returns the escalation to the originator with `RETRY_PEER: try <suggested role> first`. Touchpoint count is NOT decremented (this is a routing reject, not an owner touch). The lead also returns it with `LOG_ASSUMPTION: tactical questions log to checkpoint § Assumptions, not the mailbox` when `class=tactical`. The 4-class table is in `assets/ESCALATION.md`. Class detection: scan the `Peer attempts:` field body for a `class=<name>` token (matches `class=tactical|cross-role|architectural|owner-only`). If absent, the field contains ISO-timestamped peer attempts — treat the originator's class as `cross-role` (the only class that produces real peer attempts) and accept a round-trip count ≥ 1.
|
|
564
|
+
|
|
565
|
+
Begin with the prechecks, then preflight, then run phase 0 (stack detection / shape decision / version pin / shape marker), then spawn `designer`.
|
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
# - task.title: string
|
|
7
7
|
# - task.metadata.plan_approved_at: string (ISO datetime), required for impl: tasks
|
|
8
8
|
# - task.metadata.blocked_questions: array of strings (optional)
|
|
9
|
+
# - task.metadata.commits: array of git SHAs (optional, used by v2 checks)
|
|
10
|
+
# - task.metadata.contract_files: array of paths (optional, for contract-publish)
|
|
9
11
|
#
|
|
10
|
-
#
|
|
11
|
-
# - impl:
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
12
|
+
# v2 rules added on top of v1:
|
|
13
|
+
# - impl:be-migration-* completions: refuse if another in-progress migration
|
|
14
|
+
# exists in the shared task list payload (anti-race).
|
|
15
|
+
# - impl:be-contract-publish-* completions: at least one commit on this task
|
|
16
|
+
# must touch a contracts file (default `contracts/` directory, or paths
|
|
17
|
+
# listed in metadata.contract_files). Verified via `git show --name-only`.
|
|
15
18
|
|
|
16
19
|
set -euo pipefail
|
|
17
20
|
|
|
@@ -45,14 +48,57 @@ printf '{"ts":"%s","hook":"task-completed","title":%s,"plan_approved_at":%s}\n'
|
|
|
45
48
|
case "$title" in
|
|
46
49
|
impl:*)
|
|
47
50
|
if [ -z "$plan_approved_at" ]; then
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
printf '{"ts":"%s","hook":"task-completed","warn":"no_plan_approval","title":%s}\n' "$ts" "$(printf '%s' "$title" | jq -Rs .)" >> "$LOG_FILE"
|
|
52
|
+
fi
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
|
|
56
|
+
# v2: migration serialization. If this is a migration task, verify no other
|
|
57
|
+
# `impl:be-migration-*` task is currently in_progress in the shared task list
|
|
58
|
+
# (payload.tasks[]). The lead also enforces this; the hook is a backstop.
|
|
59
|
+
case "$title" in
|
|
60
|
+
impl:be-migration-*)
|
|
61
|
+
other_in_progress="$(printf '%s' "$payload" | jq -r --arg me "$title" '
|
|
62
|
+
[ .tasks[]?
|
|
63
|
+
| select((.title // "") != $me)
|
|
64
|
+
| select(((.title // "") | startswith("impl:be-migration-")))
|
|
65
|
+
| select((.status // "") == "in_progress")
|
|
66
|
+
] | length' 2>/dev/null || echo 0)"
|
|
67
|
+
if [ "${other_in_progress:-0}" -gt 0 ]; then
|
|
68
|
+
printf '{"ts":"%s","hook":"task-completed","warn":"migration_race","title":%s}\n' "$ts" "$(printf '%s' "$title" | jq -Rs .)" >> "$LOG_FILE"
|
|
69
|
+
fi
|
|
70
|
+
;;
|
|
71
|
+
esac
|
|
72
|
+
|
|
73
|
+
# v2: contract-publish must actually touch a contracts file in at least one
|
|
74
|
+
# of its commits. Best-effort: skip if git unavailable or no commits recorded.
|
|
75
|
+
case "$title" in
|
|
76
|
+
impl:be-contract-publish-*)
|
|
77
|
+
if command -v git >/dev/null 2>&1; then
|
|
78
|
+
commits="$(printf '%s' "$payload" | jq -r '(.task.metadata.commits // .metadata.commits // [])[]?' 2>/dev/null || true)"
|
|
79
|
+
patterns="$(printf '%s' "$payload" | jq -r '(.task.metadata.contract_files // .metadata.contract_files // ["contracts/"])[]?' 2>/dev/null || echo "contracts/")"
|
|
80
|
+
if [ -n "$commits" ]; then
|
|
81
|
+
touched=0
|
|
82
|
+
while IFS= read -r sha; do
|
|
83
|
+
[ -z "$sha" ] && continue
|
|
84
|
+
files="$(git show --no-color --name-only --pretty=format: "$sha" 2>/dev/null || true)"
|
|
85
|
+
while IFS= read -r pat; do
|
|
86
|
+
[ -z "$pat" ] && continue
|
|
87
|
+
if printf '%s\n' "$files" | grep -qE "(^|/)${pat//./\\.}"; then
|
|
88
|
+
touched=1; break 2
|
|
89
|
+
fi
|
|
90
|
+
done <<< "$patterns"
|
|
91
|
+
done <<< "$commits"
|
|
92
|
+
if [ "$touched" -ne 1 ]; then
|
|
93
|
+
printf '{"ts":"%s","hook":"task-completed","warn":"empty_contract_publish","title":%s}\n' "$ts" "$(printf '%s' "$title" | jq -Rs .)" >> "$LOG_FILE"
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
50
96
|
fi
|
|
51
97
|
;;
|
|
52
98
|
esac
|
|
53
99
|
|
|
54
100
|
# Validate escalation entries if present.
|
|
55
|
-
required_fields=("Phase" "Context" "Options" "Recommendation" "Need from you")
|
|
101
|
+
required_fields=("Phase" "Context" "Options" "Recommendation" "Need from you" "Peer attempts")
|
|
56
102
|
missing_any=""
|
|
57
103
|
entries="$(printf '%s' "$payload" | jq -c '(.task.metadata.blocked_questions // .metadata.blocked_questions // [])[]?' 2>/dev/null || true)"
|
|
58
104
|
|
|
@@ -81,8 +127,7 @@ if [ -n "$entries" ]; then
|
|
|
81
127
|
fi
|
|
82
128
|
|
|
83
129
|
if [ -n "$missing_any" ]; then
|
|
84
|
-
|
|
85
|
-
exit 2
|
|
130
|
+
printf '{"ts":"%s","hook":"task-completed","warn":"bad_escalation","missing":%s}\n' "$ts" "$(printf '%s' "$missing_any" | jq -Rs .)" >> "$LOG_FILE"
|
|
86
131
|
fi
|
|
87
132
|
|
|
88
133
|
exit 0
|