@tianhai/pi-workflow-kit 1.4.0 → 1.6.0
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/README.md +21 -8
- package/agents/pwk-hazard-reviewer.md +23 -4
- package/agents/pwk-recon-scout.md +11 -10
- package/agents/pwk-smell-reviewer.md +26 -5
- package/agents/pwk-spec-reviewer.md +18 -3
- package/agents/pwk-tracing-reviewer.md +19 -4
- package/docs/developer-usage-guide.md +14 -8
- package/docs/oversight-model.md +4 -4
- package/docs/provider-delegation-contract.md +120 -0
- package/docs/workflow-phases.md +3 -3
- package/extensions/workflow-guard.ts +394 -1
- package/package.json +2 -1
- package/skills/pwk-brainstorming/SKILL.md +1 -1
- package/skills/pwk-executing-tasks/SKILL.md +33 -21
- package/skills/pwk-finalizing/SKILL.md +2 -1
- package/skills/pwk-writing-plans/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
AI coding agents tend to skip design and jump straight into implementation, producing over-engineered or misaligned code. **pi-workflow-kit** solves this by hard-blocking write operations during brainstorm and planning phases — the agent *literally cannot modify your source files* until you approve the design.
|
|
6
6
|
|
|
7
|
-
[pi](https://github.com/badlogic/pi-mono) package.
|
|
7
|
+
[pi](https://github.com/badlogic/pi-mono) package. Skills are portable; the workflow guard and `/pwk-setup` command are Pi integrations.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -12,7 +12,13 @@ AI coding agents tend to skip design and jump straight into implementation, prod
|
|
|
12
12
|
pi install npm:@tianhai/pi-workflow-kit
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
For Pi delegation providers that discover project agents, install the canonical PWK roles before starting a gated workflow:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
/pwk-setup
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This creates the five role definitions under `.agents/agents/`. It does not install or configure a provider. Existing customized files are preserved; use `/pwk-setup --force` only when you explicitly want to replace differing role files. Run setup before `/skill:pwk-brainstorming`; the command is refused during brainstorm and plan phases.
|
|
16
22
|
|
|
17
23
|
**Want to try before committing?**
|
|
18
24
|
|
|
@@ -20,13 +26,19 @@ No setup needed — skills and guards activate automatically after install.
|
|
|
20
26
|
pi -e npm:@tianhai/pi-workflow-kit
|
|
21
27
|
```
|
|
22
28
|
|
|
23
|
-
**Optional —
|
|
29
|
+
**Optional — delegated recon and review.** The skills request logical capabilities rather than a specific agent tool. If the host has no safe compatible provider, they perform recon and review inline. In Pi, [`@tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents) is one compatible provider:
|
|
24
30
|
|
|
25
31
|
```bash
|
|
26
|
-
pi install npm
|
|
32
|
+
pi install npm:@tintinweb/pi-subagents
|
|
27
33
|
```
|
|
28
34
|
|
|
29
|
-
|
|
35
|
+
After `/pwk-setup`, Tintinweb can discover the five named roles from `.agents/agents/`; recon can also use its built-in read-only `Explore` agent. No running subagents need to be pre-created. Other Pi extensions require the documented capabilities or a separate adapter; arbitrary extensions are not automatically compatible.
|
|
36
|
+
|
|
37
|
+
### Using the roles on other hosts
|
|
38
|
+
|
|
39
|
+
The five role files define portable logical roles, not a required provider API. Claude Code can map them to its native read-only task/subagent mechanism, but must enforce its own permissions or hooks because the Pi workflow guard does not transfer outside Pi. A different Pi extension can use the same roles when it provides the documented capabilities or an adapter; otherwise PWK performs recon and review inline.
|
|
40
|
+
|
|
41
|
+
See [`docs/provider-delegation-contract.md`](docs/provider-delegation-contract.md) for the normalized capability and outcome contract. The core kit does not install a provider, require `@tintinweb/pi-subagents`, or provide automatic compatibility with every Pi subagent extension.
|
|
30
42
|
|
|
31
43
|
## What You Get
|
|
32
44
|
|
|
@@ -58,10 +70,10 @@ A **design doc is one PR**; a **requirement is one testable slice within it**. A
|
|
|
58
70
|
|
|
59
71
|
| Phase | Trigger | What Happens |
|
|
60
72
|
|-------|---------|--------------|
|
|
61
|
-
| **Brainstorm** | `/skill:pwk-brainstorming` | Explore approaches, produce a design doc with a `## Requirements` list. On non-trivial topics,
|
|
73
|
+
| **Brainstorm** | `/skill:pwk-brainstorming` | Explore approaches, produce a design doc with a `## Requirements` list. On non-trivial topics, requests the logical `codebase-recon` capability; if unavailable or unsafe, performs the `pwk-recon-scout` role inline. |
|
|
62
74
|
| **Plan** | `/skill:pwk-writing-plans` | Turn each requirement into **acceptance criteria + integration tests** — a behavioral spec (no implementation code) |
|
|
63
75
|
| **Execute** | `/skill:pwk-executing-tasks` | Write the feature E2E (red) → **checkpoint: feature-spec** → implement requirements → **checkpoint: feature-complete** → feature review |
|
|
64
|
-
| **Code review** | `/skill:pwk-code-review` | Feature-level (default) or per-requirement: code tracing, spec alignment, code smells (applies fixes), production hazard check |
|
|
76
|
+
| **Code review** | `/skill:pwk-code-review` | Feature-level (default) or per-requirement: code tracing, spec alignment, code smells (applies fixes), production hazard check. Delegated review uses four tiered logical roles (smell/hazard on a fast model via `/pwk-setup --fast-model`) over a script-assembled review packet when a safe provider is available; otherwise it runs inline. |
|
|
65
77
|
| **Finalize** | `/skill:pwk-finalizing` | Delete consumed plan docs, update README/CHANGELOG, create PR |
|
|
66
78
|
| **Diagnose** | `/skill:pwk-diagnose` | Debugging loop: reproduce → hypothesise → instrument → fix → cleanup. **Exits the gated phase** (debugging writes tests/instrumentation) |
|
|
67
79
|
| **Status** | `/skill:pwk-status` | Read-only overview of all active design topics — phase + progress. Use when resuming or juggling several designs in parallel worktrees. Not a pipeline phase; **does not exit the gated phase**. |
|
|
@@ -177,11 +189,12 @@ pi-workflow-kit/
|
|
|
177
189
|
│ ├── pwk-finalizing/SKILL.md
|
|
178
190
|
│ ├── pwk-status/SKILL.md
|
|
179
191
|
│ └── pwk-diagnose/SKILL.md
|
|
180
|
-
├── agents/ #
|
|
192
|
+
├── agents/ # canonical role contracts; /pwk-setup copies them to .agents/agents/
|
|
181
193
|
├── docs/
|
|
182
194
|
│ ├── developer-usage-guide.md
|
|
183
195
|
│ ├── workflow-phases.md
|
|
184
196
|
│ ├── oversight-model.md
|
|
197
|
+
│ ├── provider-delegation-contract.md
|
|
185
198
|
│ ├── lessons.md
|
|
186
199
|
│ ├── adr/ # permanent architectural decisions (never archived)
|
|
187
200
|
│ └── plans/ # active design/plan/progress docs (deleted after finalization)
|
|
@@ -3,13 +3,30 @@ name: pwk-hazard-reviewer
|
|
|
3
3
|
description: Production-hazard reviewer — audits for unbounded ops, missing indexes, unbounded concurrency, long transactions, injection, silent swallowing loops. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
# model: <fast-tier> — set yours via /pwk-setup
|
|
7
|
+
thinking: low
|
|
8
|
+
max_turns: 20
|
|
6
9
|
---
|
|
7
10
|
|
|
8
|
-
# PWK
|
|
11
|
+
# PWK Reviewer
|
|
9
12
|
|
|
10
|
-
You are a
|
|
13
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
14
|
|
|
12
|
-
##
|
|
15
|
+
## Authority boundary
|
|
16
|
+
|
|
17
|
+
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
18
|
+
|
|
19
|
+
## Reporting contract
|
|
20
|
+
|
|
21
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
22
|
+
|
|
23
|
+
## Working from the packet
|
|
24
|
+
|
|
25
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
26
|
+
|
|
27
|
+
## Your checklist
|
|
28
|
+
|
|
29
|
+
### Production hazards — audit each changed file
|
|
13
30
|
|
|
14
31
|
For each item below, write `[SAFE]` (1-line justification) or `[TRIGGERED]` (concrete mitigation):
|
|
15
32
|
|
|
@@ -21,4 +38,6 @@ For each item below, write `[SAFE]` (1-line justification) or `[TRIGGERED]` (con
|
|
|
21
38
|
6. **Unrestricted uploads / temp flooding** — uploads to local temp without limits or `finally` cleanup
|
|
22
39
|
7. **Silent swallowing loops** — background workers catching/suppressing exceptions without logging/back-off
|
|
23
40
|
|
|
24
|
-
Also check the design doc
|
|
41
|
+
Also check the design doc’s `## Production-risk areas`, if any.
|
|
42
|
+
|
|
43
|
+
Include file and line evidence for each finding. If there are no findings, report `No findings` explicitly.
|
|
@@ -7,21 +7,21 @@ systemPromptMode: replace
|
|
|
7
7
|
|
|
8
8
|
# PWK Recon Scout
|
|
9
9
|
|
|
10
|
-
You are a codebase recon scout
|
|
10
|
+
You are a codebase recon scout requested during brainstorming before design work. Your job is to map how a repository handles a topic today so the main agent can design against prior art instead of loading the relevant files into its own context.
|
|
11
11
|
|
|
12
12
|
**You are observations only.** No design recommendations, no preferred-approach opinion, no code beyond one-line excerpts. Every claim must cite a `file:line` so the main agent can drill in if it needs to.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Authority boundary
|
|
15
15
|
|
|
16
|
-
You
|
|
16
|
+
You are a read-only reporter. The host must enforce the requested read-only boundary; do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
17
17
|
|
|
18
18
|
## Inputs
|
|
19
19
|
|
|
20
|
-
The
|
|
20
|
+
The host provides three things:
|
|
21
21
|
|
|
22
22
|
- a `<topic>` (one short phrase, the new feature or change)
|
|
23
23
|
- a one-line `<intent>` (what the new thing does, in plain words)
|
|
24
|
-
- the
|
|
24
|
+
- the repository root
|
|
25
25
|
|
|
26
26
|
If any of these is missing, ask for it before proceeding.
|
|
27
27
|
|
|
@@ -47,7 +47,9 @@ Where similar tests live, what harness they use (vitest, jest, go test, etc.), a
|
|
|
47
47
|
|
|
48
48
|
### Gotchas
|
|
49
49
|
|
|
50
|
-
Anything that bit a previous change, in this layer of the code or in the topic area specifically. A migrations folder that must run in order, a feature flag that gates the new path, a known deadlock with another subsystem, an environment variable that has to be set, a CI hook that runs before tests. The point is to surface landmines before the
|
|
50
|
+
Anything that bit a previous change, in this layer of the code or in the topic area specifically. A migrations folder that must run in order, a feature flag that gates the new path, a known deadlock with another subsystem, an environment variable that has to be set, a CI hook that runs before tests. The point is to surface landmines before the planning phase.
|
|
51
|
+
|
|
52
|
+
End with `Scout: complete` when the five sections are present. If the host cannot complete the report, return `Scout: unavailable` with the reason instead of inventing observations.
|
|
51
53
|
|
|
52
54
|
## Hard rules
|
|
53
55
|
|
|
@@ -59,10 +61,9 @@ Anything that bit a previous change, in this layer of the code or in the topic a
|
|
|
59
61
|
|
|
60
62
|
## When you finish
|
|
61
63
|
|
|
62
|
-
Return the report as your final message. The main agent reads it into its context and uses it as the grounding for
|
|
64
|
+
Return the report as your final message. The main agent reads it into its context and uses it as the grounding context for approach exploration and design presentation.
|
|
63
65
|
|
|
64
66
|
## Failure modes
|
|
65
67
|
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
- You return a wrong-shaped report: the main agent will downweight the findings but still proceed. Better to ship the 5-section shape than to improvise.
|
|
68
|
+
- If no compatible read-only delegation worker is available, the main agent performs this role inline.
|
|
69
|
+
- If the role cannot complete, return `Scout: unavailable` with the reason rather than returning an empty or invented report.
|
|
@@ -3,15 +3,36 @@ name: pwk-smell-reviewer
|
|
|
3
3
|
description: Code-smell reviewer — flags shallow modules, duplication, missing seams, premature abstraction, poor naming, magic values, dead code. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
# model: <fast-tier> — set yours via /pwk-setup
|
|
7
|
+
thinking: low
|
|
8
|
+
max_turns: 20
|
|
6
9
|
---
|
|
7
10
|
|
|
8
|
-
# PWK
|
|
11
|
+
# PWK Reviewer
|
|
9
12
|
|
|
10
|
-
You are a
|
|
13
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
14
|
|
|
12
|
-
##
|
|
15
|
+
## Authority boundary
|
|
16
|
+
|
|
17
|
+
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
18
|
+
|
|
19
|
+
## Reporting contract
|
|
20
|
+
|
|
21
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
22
|
+
|
|
23
|
+
## Working from the packet
|
|
24
|
+
|
|
25
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
26
|
+
|
|
27
|
+
## Your checklist
|
|
28
|
+
|
|
29
|
+
### Code smells
|
|
30
|
+
|
|
31
|
+
Review the changed code and affected files against the assigned requirement and feature scope. Flag only smells that require large refactors risky to the requirement; everything else is for the main agent to fix.
|
|
13
32
|
|
|
14
33
|
- Shallow modules (interface nearly as complex as implementation)
|
|
15
34
|
- Duplication
|
|
16
|
-
- Missing seams
|
|
17
|
-
- Poor naming, magic values, dead code
|
|
35
|
+
- Missing seams or premature abstraction
|
|
36
|
+
- Poor naming, magic values, dead code
|
|
37
|
+
|
|
38
|
+
Include file and line evidence for each finding. If there are no findings, report `No findings` explicitly.
|
|
@@ -3,12 +3,27 @@ name: pwk-spec-reviewer
|
|
|
3
3
|
description: Spec-alignment reviewer — checks each acceptance criterion has covering code and tests; flags gaps and scope creep. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
max_turns: 40
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
# PWK
|
|
9
|
+
# PWK Reviewer
|
|
9
10
|
|
|
10
|
-
You are a
|
|
11
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Authority boundary
|
|
14
|
+
|
|
15
|
+
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
16
|
+
|
|
17
|
+
## Reporting contract
|
|
18
|
+
|
|
19
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
20
|
+
|
|
21
|
+
## Working from the packet
|
|
22
|
+
|
|
23
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
24
|
+
|
|
25
|
+
## Your checklist
|
|
26
|
+
|
|
27
|
+
### Spec alignment
|
|
13
28
|
|
|
14
29
|
For each acceptance criterion, point to the code and the test that satisfy it. A criterion with no covering code or no test is a **gap**. Code that does more than the criteria specify is **scope creep** — flag it.
|
|
@@ -3,12 +3,27 @@ name: pwk-tracing-reviewer
|
|
|
3
3
|
description: Code-tracing reviewer — traces new/changed paths end-to-end against tests; flags untested branches, dead branches, and broken traces. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
max_turns: 40
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
# PWK
|
|
9
|
+
# PWK Reviewer
|
|
9
10
|
|
|
10
|
-
You are a
|
|
11
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## Authority boundary
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
16
|
+
|
|
17
|
+
## Reporting contract
|
|
18
|
+
|
|
19
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
20
|
+
|
|
21
|
+
## Working from the packet
|
|
22
|
+
|
|
23
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
24
|
+
|
|
25
|
+
## Your checklist
|
|
26
|
+
|
|
27
|
+
### Code tracing
|
|
28
|
+
|
|
29
|
+
Trace the new or changed code paths end-to-end against the integration tests. For each path, determine whether data flows correctly from entry to the asserted outcome. Note any branch the tests do not exercise, any dead branch, or any path where the trace breaks.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Developer Usage Guide
|
|
2
2
|
|
|
3
|
-
How to install and use `pi-workflow-kit` with
|
|
3
|
+
How to install and use `pi-workflow-kit` with Pi, and how its workflow roles map to other agent hosts.
|
|
4
4
|
|
|
5
5
|
## What you get
|
|
6
6
|
|
|
@@ -40,11 +40,19 @@ You control each phase by invoking the skill. A design doc is one PR; a requirem
|
|
|
40
40
|
|
|
41
41
|
### 1. Brainstorm
|
|
42
42
|
|
|
43
|
+
Before entering the gated phase in Pi, optionally install the canonical role definitions:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
/pwk-setup
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The command creates `.agents/agents/` and installs the five PWK roles. It preserves differing files unless `--force` is supplied and is refused during brainstorm and plan phases. It does not install a delegation provider.
|
|
50
|
+
|
|
43
51
|
```
|
|
44
52
|
/skill:pwk-brainstorming
|
|
45
53
|
```
|
|
46
54
|
|
|
47
|
-
Explore the idea through collaborative dialogue. The agent reads code, asks questions, proposes approaches, and presents the design for your review. On non-trivial topics with prior art, the
|
|
55
|
+
Explore the idea through collaborative dialogue. The agent reads code, asks questions, proposes approaches, and presents the design for your review. On non-trivial topics with prior art, the skill requests the logical `codebase-recon` capability using the `pwk-recon-scout` role. A compatible host may dispatch that role in a fresh, bounded, read-only worker; otherwise the skill reports `Scout: unavailable` and performs the same five-section recon inline.
|
|
48
56
|
|
|
49
57
|
Outcome: `docs/plans/YYYY-MM-DD-<topic>-design.md` — descriptive, opening with a `## Requirements` list. For a too-big requirement, may start an **umbrella** (writes a status-free overview + the first part's design doc). ADRs go to `docs/adr/` (permanent).
|
|
50
58
|
|
|
@@ -68,13 +76,11 @@ Implement via the **feature-gate flow** with full autonomy: write the feature-ac
|
|
|
68
76
|
|
|
69
77
|
### 4. Code review (feature level)
|
|
70
78
|
|
|
71
|
-
The `pwk-executing-tasks` skill
|
|
79
|
+
The `pwk-executing-tasks` skill requests the `parallel-review` capability for four logical roles over the whole feature diff: spec alignment, code tracing, code smells, and production hazards. The scope is a script-assembled review packet (diff + acceptance criteria verbatim) handed to every role via a one-liner pointer — the packet never rides in spawn arguments. The roles are independent, fresh-context, read-only reporters; the main agent collects their results, applies smell fixes itself, runs the tests, and flags other findings for the human.
|
|
72
80
|
|
|
73
|
-
|
|
81
|
+
In Pi, `/pwk-setup` installs the canonical role definitions into `.agents/agents/`, where compatible providers such as `@tintinweb/pi-subagents` can discover them. `/pwk-setup --fast-model <model>` (or the interactive picker) sets the fast-tier model for the smell/hazard reviewers — an advisory hint hosts may honor. Tintinweb may run the roles through its native `Agent` mechanism or map recon to its built-in read-only `Explore` type. The core kit does not require Tintinweb or any other provider.
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
pi install npm:pi-subagents
|
|
77
|
-
```
|
|
83
|
+
*Fallback:* if no host/provider can guarantee the requested capabilities, the skill performs the missing recon or review work inline. Other Pi extensions are supported only when they expose the documented capabilities or have a separate adapter; arbitrary extensions are not automatically compatible. See `docs/provider-delegation-contract.md` for the integration contract.
|
|
78
84
|
|
|
79
85
|
### 5. Finalize
|
|
80
86
|
|
|
@@ -102,7 +108,7 @@ A read-only overview of all active design topics — which phase each is in and
|
|
|
102
108
|
|
|
103
109
|
## What the extension does
|
|
104
110
|
|
|
105
|
-
The `workflow-guard` extension watches `write`/`edit` and `bash` tool calls:
|
|
111
|
+
The `workflow-guard` extension registers `/pwk-setup` and watches `write`/`edit` and `bash` tool calls:
|
|
106
112
|
|
|
107
113
|
- **During brainstorm and writing-plans**: blocks writes outside `docs/plans/`, and blocks destructive bash via a simple common-blacklist (a command is allowed unless it matches a destructive pattern). A short phase reminder is shown once when the gated phase begins so the model self-restricts.
|
|
108
114
|
- **During executing-tasks, code-review, finalizing, diagnose**: no restrictions.
|
package/docs/oversight-model.md
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
Skills teach the agent the workflow. There are 5 pipeline skills:
|
|
8
8
|
|
|
9
|
-
- **pwk-brainstorming** — explore ideas, produce a descriptive design doc that opens with a `## Requirements` list. For a requirement too big for one design doc, may start an **umbrella** (multiple design docs under one status-free overview, shipping as one PR)
|
|
9
|
+
- **pwk-brainstorming** — explore ideas, produce a descriptive design doc that opens with a `## Requirements` list. For a requirement too big for one design doc, may start an **umbrella** (multiple design docs under one status-free overview, shipping as one PR). On non-trivial topics, requests the logical `codebase-recon` capability and falls back to the `pwk-recon-scout` role inline when unavailable or unsafe.
|
|
10
10
|
- **pwk-writing-plans** — turn each requirement into acceptance criteria + integration-test cases (a behavioral spec, no implementation code)
|
|
11
11
|
- **pwk-executing-tasks** — feature-gate flow: write the feature E2E first, implement the requirements, then one feature-level review; two mandatory checkpoints at the feature level, per-requirement ceremony opt-in
|
|
12
|
-
- **pwk-code-review** — the inline reviewer (code tracing, spec alignment, code smells, production hazards). During `pwk-executing-tasks`, the
|
|
12
|
+
- **pwk-code-review** — the inline reviewer (code tracing, spec alignment, code smells, production hazards). During `pwk-executing-tasks`, the feature-level review requests the `parallel-review` capability for four logical fresh-context, read-only roles; successful reports are retained and missing roles are retried or completed inline. It falls back to inline review when no safe compatible provider exists. The canonical provider contract is documented in `docs/provider-delegation-contract.md`.
|
|
13
13
|
- **pwk-finalizing** — dispose consumed plan docs (archive or delete; for an umbrella, the overview + every part), curate lessons, update docs, create PR or merge
|
|
14
14
|
|
|
15
15
|
Plus 2 on-demand skills:
|
|
@@ -21,7 +21,7 @@ They explain *what* to do and *when* to do it. Phase control is manual — you i
|
|
|
21
21
|
|
|
22
22
|
## Extension
|
|
23
23
|
|
|
24
|
-
The `workflow-guard` extension enforces one rule:
|
|
24
|
+
The `workflow-guard` extension registers the Pi-only `/pwk-setup` command and enforces one workflow rule:
|
|
25
25
|
|
|
26
26
|
> During brainstorm and plan phases, `write` and `edit` are **hard-blocked** outside `docs/plans/`.
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ The agent can still use `read` and `bash` for investigation. During those gated
|
|
|
29
29
|
|
|
30
30
|
During executing-tasks, code-review, finalizing, **and diagnose**, nothing is restricted (diagnosis needs to write failing tests and debug instrumentation, so it exits the gate). `pwk-status` stays inside the gate.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Canonical role contracts live in `agents/pwk-*.md` (single source of truth) and can be installed into `.agents/agents/` with `/pwk-setup`. `pwk-executing-tasks` requests logical review roles through the host’s delegation capabilities and passes each role a one-liner pointer to a script-assembled review packet — the packet defines the scope per review level (feature review: the whole feature diff; per-requirement: just that slice).
|
|
33
33
|
|
|
34
34
|
Phases follow the skill you invoke — there is no message-keyword unlock. Invoking `/skill:pwk-executing-tasks`, `pwk-finalizing`, `pwk-code-review`, or `pwk-diagnose` exits the gated phase (those skills write source); `pwk-status` deliberately does **not** (read-only orientation). `/pwk-guard on|off|auto` manually overrides the guard.
|
|
35
35
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Provider Delegation Contract
|
|
2
|
+
|
|
3
|
+
This document defines the provider-neutral contract for running PWK roles outside the main agent. It is an integration contract for host adapters and extensions. The workflow skills describe the same behavior in portable language; they do not require this document’s TypeScript notation or any specific transport.
|
|
4
|
+
|
|
5
|
+
## Logical operations
|
|
6
|
+
|
|
7
|
+
A provider may support either operation independently:
|
|
8
|
+
|
|
9
|
+
- `codebase-recon` — run one `pwk-recon-scout` role and return its five-section observation map.
|
|
10
|
+
- `feature-review` — run the requested review roles over one feature scope and return one outcome per role.
|
|
11
|
+
|
|
12
|
+
The logical role name is not the provider’s concrete agent type. A provider may map `pwk-recon-scout` to a safe built-in explorer, a custom agent definition, or a host-native read-only task.
|
|
13
|
+
|
|
14
|
+
## Required capabilities
|
|
15
|
+
|
|
16
|
+
A provider advertises capabilities independently from its name:
|
|
17
|
+
|
|
18
|
+
| Capability | Meaning |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `codebase-recon` | Can run one fresh, read-only recon worker and collect its report. |
|
|
21
|
+
| `named-role-dispatch` | Can map each requested logical role to an appropriate worker. |
|
|
22
|
+
| `parallel-review` | Can run independent review roles concurrently when requested. |
|
|
23
|
+
| `result-collection` | Returns one distinguishable outcome for every requested role. |
|
|
24
|
+
| `read-only-enforcement` | Prevents delegated workers from writing files or running destructive commands. |
|
|
25
|
+
| `fresh-context` | Starts each requested worker without reusing an unrelated prior conversation. |
|
|
26
|
+
| `bounded-execution` | Applies a timeout, turn limit, or equivalent resource bound. |
|
|
27
|
+
|
|
28
|
+
A provider must not claim `read-only-enforcement` when it only adds a prompt instruction. Providers may support `codebase-recon` without supporting `parallel-review`.
|
|
29
|
+
|
|
30
|
+
## Role resource hints
|
|
31
|
+
|
|
32
|
+
Role definition frontmatter may declare optional resource hints: `model` (a host-resolvable model name), `thinking` (a reasoning-effort level), and `max_turns` (a turn budget — the per-role instance of `bounded-execution`). Hints are advisory:
|
|
33
|
+
|
|
34
|
+
- Hosts that support per-role resources SHOULD honor them; hosts that do not ignore them without failing the operation.
|
|
35
|
+
- If a `model` hint cannot be resolved to an available model, the host runs the role on its default model — an unresolvable hint MUST NOT fail the review.
|
|
36
|
+
- `max_turns` is a graceful backstop: the role wraps up and reports rather than running unbounded. A capped role still follows the normalized outcome rules — a non-empty report is required for completion.
|
|
37
|
+
|
|
38
|
+
## Request shape
|
|
39
|
+
|
|
40
|
+
The following TypeScript is illustrative. Implementations may use Pi events, tool calls, CLI processes, native task APIs, or another transport. A reference implementation of the outcome normalization lives in `extensions/workflow-guard.ts` (`assessDelegationCoverage`), exported as a pure helper so future adapters and tests share one definition of complete coverage.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
type DelegationOperation = 'codebase-recon' | 'feature-review';
|
|
44
|
+
type DelegationRole =
|
|
45
|
+
| 'pwk-recon-scout'
|
|
46
|
+
| 'pwk-spec-reviewer'
|
|
47
|
+
| 'pwk-tracing-reviewer'
|
|
48
|
+
| 'pwk-smell-reviewer'
|
|
49
|
+
| 'pwk-hazard-reviewer';
|
|
50
|
+
|
|
51
|
+
type DelegationRequest = {
|
|
52
|
+
operation: DelegationOperation;
|
|
53
|
+
roles: DelegationRole[];
|
|
54
|
+
prompt: string;
|
|
55
|
+
cwd: string;
|
|
56
|
+
constraints: {
|
|
57
|
+
readOnly: true;
|
|
58
|
+
freshContext: true;
|
|
59
|
+
parallel: boolean;
|
|
60
|
+
bounded: true;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The provider must preserve the logical roles and the repository root when translating a request. `parallel: true` requests concurrency; it does not permit unbounded concurrency. If the provider cannot satisfy a requested constraint, it must reject the delegated operation so the host can use inline fallback.
|
|
66
|
+
|
|
67
|
+
## Normalized outcome shape
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
type DelegationStatus = 'completed' | 'failed' | 'timed-out' | 'skipped';
|
|
71
|
+
|
|
72
|
+
type DelegationResult = {
|
|
73
|
+
role: DelegationRole;
|
|
74
|
+
status: DelegationStatus;
|
|
75
|
+
report?: string;
|
|
76
|
+
error?: string;
|
|
77
|
+
provider?: string;
|
|
78
|
+
runId?: string;
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Rules:
|
|
83
|
+
|
|
84
|
+
- `role` is required and must identify one requested logical role.
|
|
85
|
+
- `completed` requires a non-empty `report` that follows the role contract.
|
|
86
|
+
- `failed` and `timed-out` require an `error` or equivalent failure explanation.
|
|
87
|
+
- `skipped` is explicit and is not equivalent to completion.
|
|
88
|
+
- `provider` identifies the adapter or host that produced the outcome when known.
|
|
89
|
+
- `runId` is an opaque provider-local identifier when available; consumers must not interpret its format.
|
|
90
|
+
- A multi-role operation is complete only when every requested role has a completed result or an explicit fallback result approved by the workflow.
|
|
91
|
+
|
|
92
|
+
An empty report is not a successful result. A missing role is not silently discarded.
|
|
93
|
+
|
|
94
|
+
## Fallback protocol
|
|
95
|
+
|
|
96
|
+
If no provider satisfies the requested capabilities, the host performs the role inline using the same logical contract. For recon, it reports:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
Scout: unavailable — inline recon used.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
For feature review, successful delegated reports remain usable. A failed or timed-out role is retried or performed inline. The host does not report a complete review while a required role has neither a delegated result nor an inline result.
|
|
103
|
+
|
|
104
|
+
## Provider discovery and selection
|
|
105
|
+
|
|
106
|
+
Provider discovery and transport are host-specific. A future Pi adapter may use a capability registry, an event-bus handshake, a shared extension RPC, or explicit configuration. The core PWK package does not assume that all Pi extensions are discoverable or that a package name identifies a compatible provider.
|
|
107
|
+
|
|
108
|
+
If multiple providers are available, selection must be deterministic. A host may use explicit configuration or a documented priority order. It must not select a provider based only on extension load order when that changes safety or result semantics.
|
|
109
|
+
|
|
110
|
+
## Role setup and provider loading
|
|
111
|
+
|
|
112
|
+
The Pi-only `/pwk-setup` command copies the canonical role definitions into `.agents/agents/`. This makes named roles available to providers that discover the shared directory, including `@tintinweb/pi-subagents`. Setup creates role definitions; it does not install or configure a provider.
|
|
113
|
+
|
|
114
|
+
Providers may cache role definitions. Hosts should tell the user when `/reload` or a new session is needed. Setup must not reload a session implicitly.
|
|
115
|
+
|
|
116
|
+
## Safety boundary
|
|
117
|
+
|
|
118
|
+
The provider is responsible for enforcing any capability it advertises. The role prompt is defense in depth, not a security boundary. The Pi workflow guard protects the main Pi session’s brainstorm and plan phases; it does not automatically protect Claude Code or another host, and it does not replace delegated-worker tool restrictions.
|
|
119
|
+
|
|
120
|
+
The `/pwk-setup` installer itself is hardened in depth: it opens the target with `O_NOFOLLOW` and does every subsequent check, read, and write through that one descriptor (`fstat` regular-file check, content comparison, and post-write verification), so nothing swapped in on the path afterward can affect what is read or written. On platforms where `O_NOFOLLOW` is unavailable, a symlink swapped in before the initial open remains a narrow advisory window; setup is a user-invoked development command, not a security boundary.
|
package/docs/workflow-phases.md
CHANGED
|
@@ -43,7 +43,7 @@ Write boundary: only `docs/plans/` is writable.
|
|
|
43
43
|
/skill:pwk-executing-tasks
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
- **Feature-gate flow:** write the feature-acceptance E2E test (red) → **⏸ checkpoint: feature-spec** (human confirms the E2E proves the feature) → implement the requirements back-to-back with full autonomy (the executor chooses structure/signatures/internals) → **⏸ checkpoint: feature-complete** (full suite + feature E2E green) → **feature review** (
|
|
46
|
+
- **Feature-gate flow:** write the feature-acceptance E2E test (red) → **⏸ checkpoint: feature-spec** (human confirms the E2E proves the feature) → implement the requirements back-to-back with full autonomy (the executor chooses structure/signatures/internals) → **⏸ checkpoint: feature-complete** (full suite + feature E2E green) → **feature review** (request the `parallel-review` capability for four logical read-only roles when the host supports it; otherwise run `/skill:pwk-code-review` inline — see [code-review](#code-review)).
|
|
47
47
|
- Per-requirement checkpoints/reviews are **opt-in** — they fire only for requirements the plan tags (default off); see [Proportionality](#proportionality).
|
|
48
48
|
- **Regression check after each commit** — run the full existing suite to catch cross-requirement regressions immediately. The feature E2E stays red until the last requirement and is gated only at `feature-complete` (the old integration gate folds into it).
|
|
49
49
|
- Progress tracked in `docs/plans/*-progress.md` (feature phase + requirement checklist).
|
|
@@ -56,7 +56,7 @@ The **feature-gate flow** is the default: write the feature E2E first, implement
|
|
|
56
56
|
|
|
57
57
|
- **Checkpoints** — `none` (no per-requirement stop, **default**) | `full` (both stops) | `spec` (tests stop only — cheap spec-correctness gate, implementation covered by review). Test-first is preserved either way: even `none` writes a meaningful test first (red) and implements to green; only the human *stops* are optional. `spec` requires at least `inline` review (never combine with `skip`).
|
|
58
58
|
- **Review** — `skip` (no per-requirement review, **default**) | `parallel` (four fresh-context reviewers) | `inline` (single `pwk-code-review` pass).
|
|
59
|
-
- **Feature review** — `parallel` (four reviewers over the whole feature diff, **default**) | `inline` (one pass, small features). Always on.
|
|
59
|
+
- **Feature review** — `parallel` (four reviewers over the whole feature diff, **default**) | `inline` (one pass, small features). Always on. The review scope is a script-assembled review packet (diff + criteria verbatim), so reviewers never re-derive scope; smell/hazard reviewers run on the fast tier set via `/pwk-setup --fast-model` (advisory hint).
|
|
60
60
|
|
|
61
61
|
Flag a requirement for a checkpoint when it has complex logic or is the main part of the feature; for a review when it touches production-risk. A trivial fix can also skip the multi-turn brainstorm dialogue via the brainstorming trivial fast-path (compress to one turn, minimal design doc) — the guard still enforces read-only.
|
|
62
62
|
|
|
@@ -68,7 +68,7 @@ Flag a requirement for a checkpoint when it has complex logic or is the main par
|
|
|
68
68
|
|
|
69
69
|
The **inline reviewer**: code tracing, spec alignment (vs acceptance criteria), code smells (applies fixes), production hazard check. Unlocked — may modify code to fix smells.
|
|
70
70
|
|
|
71
|
-
**Not a phase you drive manually.** During `pwk-executing-tasks`, the
|
|
71
|
+
**Not a phase you drive manually.** During `pwk-executing-tasks`, the feature-level review requests four logical roles (`pwk-spec-reviewer`, `pwk-tracing-reviewer`, `pwk-smell-reviewer`, `pwk-hazard-reviewer`) through the host’s `parallel-review` capability. Roles are fresh-context, read-only reporters; successful reports are retained and failed roles are retried or completed inline. If no compatible provider is available, the whole review runs inline. In Pi, `/pwk-setup` installs the canonical role definitions into `.agents/agents/`; [`@tintinweb/pi-subagents`](https://github.com/tintinweb/pi-subagents) is one compatible provider. See `docs/provider-delegation-contract.md` for the provider contract. You can also invoke `/skill:pwk-code-review` standalone for an ad-hoc review of any diff.
|
|
72
72
|
|
|
73
73
|
No write restrictions.
|
|
74
74
|
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
closeSync,
|
|
3
|
+
constants,
|
|
4
|
+
fstatSync,
|
|
5
|
+
ftruncateSync,
|
|
6
|
+
lstatSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
openSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
readSync,
|
|
11
|
+
writeSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
2
15
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
16
|
|
|
4
17
|
/**
|
|
@@ -12,6 +25,331 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
12
25
|
|
|
13
26
|
type Phase = "brainstorm" | "plan" | null;
|
|
14
27
|
|
|
28
|
+
type DelegationStatus = "completed" | "failed" | "timed-out" | "skipped";
|
|
29
|
+
|
|
30
|
+
export interface DelegationOutcome {
|
|
31
|
+
role: string;
|
|
32
|
+
status: DelegationStatus;
|
|
33
|
+
report?: string;
|
|
34
|
+
error?: string;
|
|
35
|
+
provider?: string;
|
|
36
|
+
runId?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DelegationCoverage {
|
|
40
|
+
complete: boolean;
|
|
41
|
+
missing: string[];
|
|
42
|
+
retainedReports: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Summarize role outcomes without treating failed or empty outcomes as coverage. */
|
|
46
|
+
export function assessDelegationCoverage(
|
|
47
|
+
requiredRoles: readonly string[],
|
|
48
|
+
outcomes: readonly DelegationOutcome[],
|
|
49
|
+
): DelegationCoverage {
|
|
50
|
+
const completedReports = new Map<string, string>();
|
|
51
|
+
for (const outcome of outcomes) {
|
|
52
|
+
if (outcome.status === "completed" && outcome.report) {
|
|
53
|
+
completedReports.set(outcome.role, outcome.report);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const missing = requiredRoles.filter((role) => !completedReports.has(role));
|
|
58
|
+
return {
|
|
59
|
+
complete: missing.length === 0,
|
|
60
|
+
missing,
|
|
61
|
+
retainedReports: [...completedReports.values()],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const ROLE_NAMES = [
|
|
66
|
+
"pwk-recon-scout",
|
|
67
|
+
"pwk-spec-reviewer",
|
|
68
|
+
"pwk-tracing-reviewer",
|
|
69
|
+
"pwk-smell-reviewer",
|
|
70
|
+
"pwk-hazard-reviewer",
|
|
71
|
+
] as const;
|
|
72
|
+
|
|
73
|
+
const REVIEWER_ROLES = ROLE_NAMES.filter((role) => role !== "pwk-recon-scout");
|
|
74
|
+
const FAST_TIER_ROLES = ["pwk-smell-reviewer", "pwk-hazard-reviewer"];
|
|
75
|
+
|
|
76
|
+
const FAST_MODEL_PLACEHOLDER = "# model: <fast-tier> — set yours via /pwk-setup";
|
|
77
|
+
|
|
78
|
+
/** Split a role file into its frontmatter block (with fences) and body. */
|
|
79
|
+
function splitFrontmatter(content: string): { frontmatter: string; rest: string } {
|
|
80
|
+
const match = content.match(/^(---\n[\s\S]*?\n---\n)([\s\S]*)$/);
|
|
81
|
+
return match ? { frontmatter: match[1], rest: match[2] } : { frontmatter: "", rest: content };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A model hint is a `model:` key line — one definition shared by every consumer
|
|
85
|
+
* (apply, detect, conflict-compare) so the three can never drift apart. Scoped to
|
|
86
|
+
* frontmatter: a body line that happens to start `model: ` is content, not config. */
|
|
87
|
+
const MODEL_HINT_LINE = /^model: /m;
|
|
88
|
+
|
|
89
|
+
/** Apply a fast-tier model hint to a role definition.
|
|
90
|
+
*
|
|
91
|
+
* Replaces an existing `model:` line; else swaps the commented placeholder for
|
|
92
|
+
* `model: <model>`; else inserts after `systemPromptMode:` when `insertIfAbsent`
|
|
93
|
+
* (the all-four path for judgment roles, which ship no placeholder). Pure: same
|
|
94
|
+
* input always yields the same output, so install comparisons stay byte-exact.
|
|
95
|
+
*/
|
|
96
|
+
export function applyFastModelHint(content: string, model: string, opts?: { insertIfAbsent?: boolean }): string {
|
|
97
|
+
const trimmed = model.trim();
|
|
98
|
+
if (!trimmed || /\s/.test(trimmed)) throw new Error(`Invalid fast model name: ${JSON.stringify(model)}`);
|
|
99
|
+
const { frontmatter, rest } = splitFrontmatter(content);
|
|
100
|
+
if (!frontmatter) return content;
|
|
101
|
+
const hintedFrontmatter = MODEL_HINT_LINE.test(frontmatter)
|
|
102
|
+
? frontmatter.replace(/^model: .*$/m, `model: ${trimmed}`)
|
|
103
|
+
: frontmatter.includes(FAST_MODEL_PLACEHOLDER)
|
|
104
|
+
? frontmatter.replace(FAST_MODEL_PLACEHOLDER, `model: ${trimmed}`)
|
|
105
|
+
: opts?.insertIfAbsent
|
|
106
|
+
? frontmatter.replace("systemPromptMode: replace\n", `systemPromptMode: replace\nmodel: ${trimmed}\n`)
|
|
107
|
+
: frontmatter;
|
|
108
|
+
return hintedFrontmatter + rest;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** True when the only difference between two contents is the model hint line
|
|
112
|
+
* (an uncommented `model:` line or the commented placeholder). Such deltas are
|
|
113
|
+
* kit-managed config and auto-update without --force; anything else conflicts.
|
|
114
|
+
*/
|
|
115
|
+
function differsOnlyByHint(a: string, b: string): boolean {
|
|
116
|
+
const strip = (content: string) => {
|
|
117
|
+
const { frontmatter, rest } = splitFrontmatter(content);
|
|
118
|
+
const stripped = frontmatter
|
|
119
|
+
.split("\n")
|
|
120
|
+
.filter((line) => !MODEL_HINT_LINE.test(line) && line !== FAST_MODEL_PLACEHOLDER)
|
|
121
|
+
.join("\n");
|
|
122
|
+
return stripped + rest;
|
|
123
|
+
};
|
|
124
|
+
return strip(a) === strip(b);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const CANONICAL_AGENTS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "agents");
|
|
128
|
+
|
|
129
|
+
function setupUsageError(): Error {
|
|
130
|
+
return new Error("Usage: /pwk-setup [--force] [--fast-model <model>] [--all-roles]");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseSetupArgs(args: string): { force: boolean; fastModel?: string; allRoles: boolean } {
|
|
134
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
135
|
+
let force = false;
|
|
136
|
+
let allRoles = false;
|
|
137
|
+
let fastModel: string | undefined;
|
|
138
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
139
|
+
const token = tokens[i];
|
|
140
|
+
if (token === "--force") {
|
|
141
|
+
force = true;
|
|
142
|
+
} else if (token === "--all-roles") {
|
|
143
|
+
allRoles = true;
|
|
144
|
+
} else if (token.startsWith("--fast-model=")) {
|
|
145
|
+
fastModel = token.slice("--fast-model=".length);
|
|
146
|
+
} else if (token === "--fast-model") {
|
|
147
|
+
const next = tokens[i + 1];
|
|
148
|
+
if (next === undefined || next.startsWith("--")) throw setupUsageError();
|
|
149
|
+
fastModel = next;
|
|
150
|
+
i += 1;
|
|
151
|
+
} else {
|
|
152
|
+
throw setupUsageError();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (fastModel !== undefined && !fastModel.trim()) throw setupUsageError();
|
|
156
|
+
if (allRoles && fastModel === undefined) throw setupUsageError(); // --all-roles pairs with --fast-model
|
|
157
|
+
return { force, fastModel, allRoles };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function ensureDirectory(path: string): void {
|
|
161
|
+
const stats = statNoFollow(path);
|
|
162
|
+
if (!stats) {
|
|
163
|
+
mkdirSync(path);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (stats.isSymbolicLink()) throw new Error(`Refusing symlink destination: ${path}`);
|
|
167
|
+
if (!stats.isDirectory()) throw new Error(`Destination is not a directory: ${path}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** lstat without following symlinks; null when the path does not exist. */
|
|
171
|
+
function statNoFollow(path: string): ReturnType<typeof lstatSync> | null {
|
|
172
|
+
try {
|
|
173
|
+
return lstatSync(path);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Read exactly `size` bytes from `fd` starting at position 0, regardless of the fd's cursor. */
|
|
181
|
+
function readAllFromFd(fd: number, size: number): string {
|
|
182
|
+
const buffer = Buffer.alloc(size);
|
|
183
|
+
let offset = 0;
|
|
184
|
+
while (offset < size) {
|
|
185
|
+
const bytesRead = readSync(fd, buffer, offset, size - offset, offset);
|
|
186
|
+
if (bytesRead === 0) break;
|
|
187
|
+
offset += bytesRead;
|
|
188
|
+
}
|
|
189
|
+
return buffer.toString("utf8");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Write `content` to `fd` at position 0 (truncating first) and verify by reading the same fd back. */
|
|
193
|
+
function overwriteFd(fd: number, content: string, path: string): void {
|
|
194
|
+
ftruncateSync(fd, 0);
|
|
195
|
+
const buffer = Buffer.from(content, "utf8");
|
|
196
|
+
writeSync(fd, buffer, 0, buffer.length, 0);
|
|
197
|
+
if (readAllFromFd(fd, buffer.length) !== content) throw new Error(`Verification failed after writing: ${path}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function writeNewFile(path: string, content: string): void {
|
|
201
|
+
const noFollow = constants.O_NOFOLLOW ?? 0;
|
|
202
|
+
const flags = constants.O_RDWR | noFollow | constants.O_CREAT | constants.O_EXCL;
|
|
203
|
+
const fd = openSync(path, flags, 0o644);
|
|
204
|
+
try {
|
|
205
|
+
overwriteFd(fd, content, path);
|
|
206
|
+
} finally {
|
|
207
|
+
closeSync(fd);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function installRoleFiles(
|
|
212
|
+
cwd: string,
|
|
213
|
+
opts: { force: boolean; hint?: { model: string; allRoles: boolean } },
|
|
214
|
+
): { installed: string[]; skipped: string[] } {
|
|
215
|
+
const projectAgentsDir = join(cwd, ".agents");
|
|
216
|
+
const targetDir = join(projectAgentsDir, "agents");
|
|
217
|
+
ensureDirectory(projectAgentsDir);
|
|
218
|
+
ensureDirectory(targetDir);
|
|
219
|
+
|
|
220
|
+
const installed: string[] = [];
|
|
221
|
+
const skipped: string[] = [];
|
|
222
|
+
const failures: string[] = [];
|
|
223
|
+
const noFollow = constants.O_NOFOLLOW ?? 0;
|
|
224
|
+
|
|
225
|
+
for (const roleName of ROLE_NAMES) {
|
|
226
|
+
const sourcePath = join(CANONICAL_AGENTS_DIR, `${roleName}.md`);
|
|
227
|
+
const targetPath = join(targetDir, `${roleName}.md`);
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
// Read inside the try: one broken canonical source becomes a per-role failure
|
|
231
|
+
// instead of aborting the whole install and hiding other roles' results.
|
|
232
|
+
const canonical = readFileSync(sourcePath, "utf8");
|
|
233
|
+
const hintApplies =
|
|
234
|
+
opts.hint !== undefined && (opts.hint.allRoles ? REVIEWER_ROLES : FAST_TIER_ROLES).includes(roleName);
|
|
235
|
+
const content =
|
|
236
|
+
hintApplies && opts.hint ? applyFastModelHint(canonical, opts.hint.model, { insertIfAbsent: true }) : canonical;
|
|
237
|
+
|
|
238
|
+
// Open the existing target (if any) once and do every check/read/write through
|
|
239
|
+
// that single fd — the fd names one fixed inode, so nothing swapped in on the
|
|
240
|
+
// path between checks (TOCTOU) can affect what gets read or written.
|
|
241
|
+
let fd: number | null;
|
|
242
|
+
try {
|
|
243
|
+
fd = openSync(targetPath, constants.O_RDWR | noFollow);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const err = error as NodeJS.ErrnoException;
|
|
246
|
+
if (err.code === "ENOENT") {
|
|
247
|
+
fd = null;
|
|
248
|
+
} else if (err.code === "ELOOP") {
|
|
249
|
+
throw new Error(`Refusing symlink destination: ${targetPath}`);
|
|
250
|
+
} else {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (fd === null) {
|
|
256
|
+
writeNewFile(targetPath, content);
|
|
257
|
+
installed.push(roleName);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const stats = fstatSync(fd);
|
|
263
|
+
if (!stats.isFile()) throw new Error(`Refusing non-regular destination: ${targetPath}`);
|
|
264
|
+
|
|
265
|
+
const existing = readAllFromFd(fd, stats.size);
|
|
266
|
+
if (existing === content) {
|
|
267
|
+
skipped.push(roleName);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (opts.hint !== undefined && differsOnlyByHint(existing, content)) {
|
|
271
|
+
// Kit-managed only while a hint choice is active this run: bare runs treat
|
|
272
|
+
// any delta (including hand-added model lines) as content — conflict rules.
|
|
273
|
+
overwriteFd(fd, content, targetPath);
|
|
274
|
+
installed.push(roleName);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (!opts.force) {
|
|
278
|
+
failures.push(`${targetPath}: conflict (use /pwk-setup --force to replace it)`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
overwriteFd(fd, content, targetPath);
|
|
283
|
+
installed.push(roleName);
|
|
284
|
+
} finally {
|
|
285
|
+
closeSync(fd);
|
|
286
|
+
}
|
|
287
|
+
} catch (error) {
|
|
288
|
+
failures.push(`${targetPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (failures.length > 0) {
|
|
293
|
+
const partial =
|
|
294
|
+
installed.length + skipped.length > 0
|
|
295
|
+
? ` (${installed.length} installed, ${skipped.length} skipped before failure — installation is partial)`
|
|
296
|
+
: "";
|
|
297
|
+
throw new Error(`PWK setup incomplete${partial}:\n${failures.join("\n")}`);
|
|
298
|
+
}
|
|
299
|
+
return { installed, skipped };
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Minimal structural view of the command context the fast-model prompt needs. */
|
|
303
|
+
interface FastModelPromptContext {
|
|
304
|
+
cwd: string;
|
|
305
|
+
hasUI?: boolean;
|
|
306
|
+
scopedModels?: { model?: string }[];
|
|
307
|
+
ui?: {
|
|
308
|
+
select?: (title: string, options: { value: string; label: string; description: string }[]) => Promise<string>;
|
|
309
|
+
confirm?: (title: string, message: string) => Promise<boolean>;
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** True when an installed fast-tier role already carries a model hint. */
|
|
314
|
+
function installedHint(cwd: string): { model: string; allRoles: boolean } | undefined {
|
|
315
|
+
const frontmatterOf = (role: string): string => {
|
|
316
|
+
try {
|
|
317
|
+
return splitFrontmatter(readFileSync(join(cwd, ".agents", "agents", `${role}.md`), "utf8")).frontmatter;
|
|
318
|
+
} catch {
|
|
319
|
+
return "";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const modelOf = (frontmatter: string): string | undefined =>
|
|
323
|
+
frontmatter.match(/^model: (\S.*)$/m)?.[1]?.trim() || undefined;
|
|
324
|
+
const model = FAST_TIER_ROLES.map(frontmatterOf).map(modelOf).find(Boolean);
|
|
325
|
+
if (!model) return undefined;
|
|
326
|
+
const allRoles = REVIEWER_ROLES.some((role) => modelOf(frontmatterOf(role)) !== undefined);
|
|
327
|
+
return { model, allRoles };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Ask for the fast-tier model once, only when a picker is available, no hint is
|
|
331
|
+
* installed, and no --fast-model argument was given. Headless hosts skip silently
|
|
332
|
+
* and reviewers stay on default models. */
|
|
333
|
+
async function promptFastModelChoice(
|
|
334
|
+
ctx: FastModelPromptContext,
|
|
335
|
+
): Promise<{ model: string; allRoles: boolean } | undefined> {
|
|
336
|
+
const ui = ctx.ui;
|
|
337
|
+
if (typeof ui?.select !== "function" || ctx.hasUI === false) return undefined;
|
|
338
|
+
if (installedHint(ctx.cwd) !== undefined) return undefined;
|
|
339
|
+
const scoped = Array.isArray(ctx.scopedModels) ? ctx.scopedModels : [];
|
|
340
|
+
const models = scoped
|
|
341
|
+
.map((entry) => (typeof entry?.model === "string" ? entry.model : undefined))
|
|
342
|
+
.filter((model): model is string => model !== undefined && model.length > 0);
|
|
343
|
+
const options = [
|
|
344
|
+
...models.map((model) => ({ value: model, label: model, description: "fast-tier reviewer model" })),
|
|
345
|
+
{ value: "skip", label: "skip", description: "reviewers run on default models" },
|
|
346
|
+
];
|
|
347
|
+
const choice = await ui.select("Fast-tier model for smell/hazard reviewers", options);
|
|
348
|
+
if (!choice || choice === "skip") return undefined;
|
|
349
|
+
const allRoles = (await ui.confirm?.("Apply to all four reviewers?", "No = smell+hazard only")) ?? false;
|
|
350
|
+
return { model: choice, allRoles };
|
|
351
|
+
}
|
|
352
|
+
|
|
15
353
|
// Destructive commands blocked in brainstorm/plan phases (simple common blacklist)
|
|
16
354
|
const DESTRUCTIVE_PATTERNS = [
|
|
17
355
|
/\brm\b/i,
|
|
@@ -181,6 +519,16 @@ function enforceLabel(): string {
|
|
|
181
519
|
return guardOverride === "on" ? "GUARD ON" : phase ? phase.toUpperCase() : "";
|
|
182
520
|
}
|
|
183
521
|
|
|
522
|
+
/**
|
|
523
|
+
* Is `/pwk-setup` blocked right now? Deliberately NOT `enforceActive()`: setup must
|
|
524
|
+
* refuse during a gated phase even when the tool-call guard is manually disabled
|
|
525
|
+
* (`/pwk-guard off`), since its own banner promises writes stay confined to
|
|
526
|
+
* docs/plans/ for the whole gated phase, override or not.
|
|
527
|
+
*/
|
|
528
|
+
function setupBlocked(): boolean {
|
|
529
|
+
return phase !== null || guardOverride === "on";
|
|
530
|
+
}
|
|
531
|
+
|
|
184
532
|
export default function (pi: ExtensionAPI) {
|
|
185
533
|
pi.on("session_start", () => {
|
|
186
534
|
phase = null;
|
|
@@ -188,6 +536,51 @@ export default function (pi: ExtensionAPI) {
|
|
|
188
536
|
guardOverride = null;
|
|
189
537
|
});
|
|
190
538
|
|
|
539
|
+
// --- Project role setup -------------------------------------------------
|
|
540
|
+
// This command writes through Node rather than the write tool, so it enforces
|
|
541
|
+
// the gated-phase boundary itself instead of relying on tool_call interception.
|
|
542
|
+
pi.registerCommand("pwk-setup", {
|
|
543
|
+
description: "Install PWK role agents into .agents/agents/",
|
|
544
|
+
handler: async (args, ctx) => {
|
|
545
|
+
// Refuse whenever the session is read-only in fact: gated phase (even with the
|
|
546
|
+
// tool-call guard manually disabled — the design mandates that) or the manual
|
|
547
|
+
// read-only lock, whose banner promises "writes only under docs/plans/".
|
|
548
|
+
if (setupBlocked()) {
|
|
549
|
+
const scope =
|
|
550
|
+
guardOverride === "on"
|
|
551
|
+
? "the manual read-only lock (/pwk-guard on)"
|
|
552
|
+
: `${(phase as string).toUpperCase()} phase`;
|
|
553
|
+
const message = `Cannot run /pwk-setup during ${scope}. Run it before entering the gated workflow or after leaving it (guard auto/off).`;
|
|
554
|
+
ctx.ui.notify(message, "warning");
|
|
555
|
+
throw new Error(message);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const { force, fastModel, allRoles } = parseSetupArgs(args ?? "");
|
|
559
|
+
try {
|
|
560
|
+
// Explicit arg wins; else the picker (only while no hint is installed);
|
|
561
|
+
// else re-apply the installed hint — the recorded choice — so bare runs
|
|
562
|
+
// are no-ops rather than stripping or conflicting on kit-managed lines.
|
|
563
|
+
const hint =
|
|
564
|
+
fastModel !== undefined
|
|
565
|
+
? { model: fastModel, allRoles }
|
|
566
|
+
: ((await promptFastModelChoice(ctx)) ?? installedHint(ctx.cwd));
|
|
567
|
+
const result = installRoleFiles(ctx.cwd, { force, hint });
|
|
568
|
+
const parts = [`PWK setup complete: ${result.installed.length} installed`];
|
|
569
|
+
if (result.skipped.length > 0) parts.push(`${result.skipped.length} skipped`);
|
|
570
|
+
if (force) parts.push("forced conflicts replaced");
|
|
571
|
+
if (hint) {
|
|
572
|
+
const scope = hint.allRoles ? "all four reviewers" : "smell+hazard reviewers";
|
|
573
|
+
parts.push(`fast model ${hint.model} (${scope})`);
|
|
574
|
+
}
|
|
575
|
+
ctx.ui.notify(`${parts.join(", ")}. Providers may require /reload to discover updated roles.`, "info");
|
|
576
|
+
} catch (error) {
|
|
577
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
578
|
+
ctx.ui.notify(message, "error");
|
|
579
|
+
throw error;
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
|
|
191
584
|
// --- Manual override (escape hatch) -----------------------------------
|
|
192
585
|
// Phases are driven by `/skill:` commands; `/pwk-guard` lets the user pin the
|
|
193
586
|
// guard regardless of phase. `/pwk-guard auto` returns control to skill transitions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tianhai/pi-workflow-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Enforce structured brainstorm→plan→execute→finalize workflow with TDD discipline in AI coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"docs/developer-usage-guide.md",
|
|
33
33
|
"docs/workflow-phases.md",
|
|
34
34
|
"docs/oversight-model.md",
|
|
35
|
+
"docs/provider-delegation-contract.md",
|
|
35
36
|
"LICENSE",
|
|
36
37
|
"README.md"
|
|
37
38
|
],
|
|
@@ -54,7 +54,7 @@ The whole umbrella is one branch and one PR: `pwk-writing-plans` creates the bra
|
|
|
54
54
|
1. **Check git state** — `git status` + `git log --oneline -5`. Uncommitted work? Ask the user what to do first.
|
|
55
55
|
2. **Discovery** *(skip in a brand-new repo with no `docs/plans/`)* — glob `docs/plans/*-design.md` and `*-overview.md`; report in-flight topics and any active umbrella. If the new idea continues an existing topic, ask whether to extend it or start fresh. Part of an umbrella? An existing `*-overview.md` means the split is already decided — read it for the roster and design this part's `-design.md` against it (see [Umbrella](#umbrella)).
|
|
56
56
|
3. **Understand the idea** — read only enough code/docs/commits to ground the design. **Check `docs/lessons.md`** — known constraints may shape it. Ask questions one at a time, prefer multiple choice. Once you can articulate what/why/constraints, present a short summary and ask: "Should I proceed, or is there more?" The human decides when to move on.
|
|
57
|
-
4. **(skipped on trivial changes)** **Codebase recon** — for non-trivial topics with prior art,
|
|
57
|
+
4. **(skipped on trivial changes)** **Codebase recon** — for non-trivial topics with prior art, request the host’s `codebase-recon` capability using the logical `pwk-recon-scout` role. Require a fresh-context, read-only, bounded worker and pass the topic, one-line intent, and repo root. Use the returned 5-section codebase map (Relevant files, Existing patterns, Call sites, Test layout, Gotchas) as the grounding context for the next two steps instead of reading those files inline. The scout is observations only — no design recommendations. Skip this step on trivial changes (typo, version bump, single-function edit per the proportionality rule). If no compatible capability is available or the provider cannot enforce the requested constraints, report `Scout: unavailable` and do the same recon inline, preserving the five-section map and `file:line` citations; do not silently omit recon.
|
|
58
58
|
5. **Explore approaches** — propose 2–3, leading with your recommendation. Sketch the concrete interface (types, signatures, example caller) for each so the comparison is grounded in code, not abstractions.
|
|
59
59
|
6. **Present the design** in one pass, organized into sections (architecture, components, data flow, error handling, testing) — the human comments on any section; re-present only revised sections.
|
|
60
60
|
|
|
@@ -7,7 +7,7 @@ description: "Implement a plan via the feature-gate flow: write the feature-acce
|
|
|
7
7
|
|
|
8
8
|
Implement the plan from `docs/plans/*-implementation.md` via the **feature-gate flow**. The plan is a behavioral spec (acceptance criteria + integration tests) — you choose structure, signatures, internals; the criteria define *what*, you decide *how*.
|
|
9
9
|
|
|
10
|
-
The feature-acceptance E2E test is the primary enforced gate. The flow is always on: write the E2E first (red), implement the requirements back-to-back, then run one feature-level review over the whole diff. Per-requirement checkpoints and reviews are **opt-in** — they fire only for requirements the plan tags (default off); the feature gate covers everything else.
|
|
10
|
+
The feature-acceptance E2E test is the primary enforced gate and the primary enforced spec for the feature. The flow is always on: write the E2E first (red), implement the requirements back-to-back, then run one feature-level review over the whole diff. Per-requirement checkpoints and reviews are **opt-in** — they fire only for requirements the plan tags (default off); the feature gate covers everything else.
|
|
11
11
|
|
|
12
12
|
## Before you start
|
|
13
13
|
|
|
@@ -68,7 +68,7 @@ Set `Feature phase: implementing (0/N)` and work the requirements in listed orde
|
|
|
68
68
|
|
|
69
69
|
### Per-requirement review (opt-in)
|
|
70
70
|
|
|
71
|
-
If the requirement's `### Review` tag is `parallel` or `inline` (default `skip`), review that slice now — same mechanics as the [feature review](#feature-review), scoped to the requirement
|
|
71
|
+
If the requirement's `### Review` tag is `parallel` or `inline` (default `skip`), review that slice now — same mechanics as the [feature review](#feature-review), with a requirement-scoped packet: the same recipe limited to the commits and criteria sections of that requirement, written to `docs/plans/<dated-stem>-review-packet.md`. With `skip`, no per-requirement review; the feature-level review covers it.
|
|
72
72
|
|
|
73
73
|
`Checkpoints: spec` requires at least `inline` review — dropping the complete checkpoint is only safe when review covers implementation quality; never combine `spec` with `Review: skip` (use `Checkpoints: none` instead).
|
|
74
74
|
|
|
@@ -93,25 +93,37 @@ The old "integration gate" is gone — the feature E2E at `feature-complete` *is
|
|
|
93
93
|
|
|
94
94
|
After `feature-complete` is approved, run **one** review over the **whole feature diff**, driven by the plan's feature-level `### Feature review` tag. This is the single thorough review — per-requirement reviews, if any, only saw slices in isolation.
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
|
|
96
|
+
**Assemble the review packet first** — once, by script, so that no packet byte passes through model output (spawn arguments are model output; file reads are not). If commits land while the review is in flight, re-run the recipe before spawning any replacement role so the packet matches HEAD:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
PACKET="docs/plans/<dated-stem>-review-packet.md" # same dated stem as the plan docs
|
|
100
|
+
{
|
|
101
|
+
echo "# Review packet: <topic> — feature review"
|
|
102
|
+
echo
|
|
103
|
+
echo "## Commits"
|
|
104
|
+
git log --oneline <merge-base>..HEAD
|
|
105
|
+
echo
|
|
106
|
+
echo "## Changed files"
|
|
107
|
+
git diff --stat <merge-base>...HEAD
|
|
108
|
+
echo
|
|
109
|
+
echo "## Acceptance criteria (verbatim from the plan)"
|
|
110
|
+
sed -n '/^## Requirement 1/,/^## Feature acceptance/p' docs/plans/<dated-stem>-implementation.md | sed '/^## Feature acceptance/,$d'
|
|
111
|
+
echo
|
|
112
|
+
echo "## Feature acceptance (verbatim)"
|
|
113
|
+
sed -n '/^## Feature acceptance/,/^### Feature review/p' docs/plans/<dated-stem>-implementation.md | sed '/^### Feature review/,$d'
|
|
114
|
+
echo
|
|
115
|
+
echo "## Production-risk notes (verbatim, if any)"
|
|
116
|
+
sed -n '/^### Production-risk notes/,/^## /p' docs/plans/<dated-stem>-implementation.md | sed '/^## /d'
|
|
117
|
+
echo
|
|
118
|
+
echo "## Diff"
|
|
119
|
+
git diff <merge-base>...HEAD
|
|
120
|
+
} > "$PACKET"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- **`parallel`** (default) — request the host’s `parallel-review` capability for four fresh-context, read-only logical roles: `pwk-spec-reviewer`, `pwk-tracing-reviewer`, `pwk-smell-reviewer`, and `pwk-hazard-reviewer`. Spawn each role with a **one-liner** — a pointer to the packet file with the role framing appended last: the checklist name of the role (`spec alignment`, `code tracing`, `code smells`, or `production hazards`). For example: `Read docs/plans/<dated-stem>-review-packet.md. Your role: spec alignment.` The packet never appears in spawn arguments. Require independent execution and one collected outcome per role. The reviewer role contracts live in `agents/pwk-*-reviewer.md`; do not duplicate their checklists in the workflow instructions. Reviewers are read-only reporters; you apply smell fixes yourself (full suite + E2E must stay green, commit) and flag trace/spec/hazard findings as follow-ups for the human.
|
|
124
|
+
|
|
125
|
+
- **`inline`** — perform `/skill:pwk-code-review` over the whole diff as a single pass.
|
|
126
|
+
- **Fallback** — if the host has no compatible parallel-review capability, cannot prove the requested read-only/fresh-context/bounded constraints, or delegation fails, perform the missing review work inline. Retain successful delegated reports and do not mark the feature fully reviewed while a required role is missing.
|
|
115
127
|
|
|
116
128
|
On success, set `Feature phase: done`.
|
|
117
129
|
|
|
@@ -27,7 +27,7 @@ Ship the completed work.
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# for each <topic> in the set:
|
|
30
|
-
rm -f docs/plans/????-??-??-<topic>-design.md docs/plans/????-??-??-<topic>-implementation.md docs/plans/????-??-??-<topic>-progress.md
|
|
30
|
+
rm -f docs/plans/????-??-??-<topic>-design.md docs/plans/????-??-??-<topic>-implementation.md docs/plans/????-??-??-<topic>-progress.md docs/plans/????-??-??-<topic>-review-packet.md
|
|
31
31
|
# umbrella only:
|
|
32
32
|
rm -f docs/plans/????-??-??-<umbrella>-overview.md
|
|
33
33
|
git add -A docs/plans/ && git commit -m "chore: delete planning docs for <topic-or-umbrella>"
|
|
@@ -41,6 +41,7 @@ Ship the completed work.
|
|
|
41
41
|
mv docs/plans/????-??-??-<topic>-design.md docs/plans/completed/ 2>/dev/null || true
|
|
42
42
|
mv docs/plans/????-??-??-<topic>-implementation.md docs/plans/completed/ 2>/dev/null || true
|
|
43
43
|
mv docs/plans/????-??-??-<topic>-progress.md docs/plans/completed/ 2>/dev/null || true
|
|
44
|
+
mv docs/plans/????-??-??-<topic>-review-packet.md docs/plans/completed/ 2>/dev/null || true
|
|
44
45
|
# umbrella only:
|
|
45
46
|
mv docs/plans/????-??-??-<umbrella>-overview.md docs/plans/completed/ 2>/dev/null || true
|
|
46
47
|
git add docs/plans/ && git commit -m "chore: archive planning docs for <topic-or-umbrella>"
|
|
@@ -23,7 +23,7 @@ Your writes go into `docs/plans/` and nowhere else. Source code and configuratio
|
|
|
23
23
|
- **Integration tests** — test name + what each asserts. This is the spec the executor writes tests from.
|
|
24
24
|
- **Meaningful tests** — write acceptance criteria and tests as observable behavior: (1) **Test observable behavior** — assert on what the feature produces or changes (a return value, persisted/updated data, an emitted event, an HTTP response) through its public interface; these assertions keep passing as the implementation changes. (2) **Write a per-slice test when the slice has its own observable behavior** — when a slice is pure config or a trivial extraction, the feature E2E covers it and a per-slice test is unnecessary.
|
|
25
25
|
- **`### Checkpoints: none | full | spec`** — how many human stops. `none` = no per-requirement stop (default — the feature gate covers it); `full` = tests + complete stops; `spec` = tests stop only. Flag a requirement `full` or `spec` when it contains complex logic or is the main part of the feature — where a human look at the slice is worth the stop.
|
|
26
|
-
- **`### Review: skip | parallel | inline`** — `skip` = no per-requirement review (default — the feature-level review covers it); `parallel` = four reviewers via
|
|
26
|
+
- **`### Review: skip | parallel | inline`** — `skip` = no per-requirement review (default — the feature-level review covers it); `parallel` = four reviewers via delegated parallel roles; `inline` = one `pwk-code-review` pass. The auto-tag bullet below is the single source of truth for risky-requirement tagging.
|
|
27
27
|
- **`### Feature review: parallel | inline`** — one review over the **whole feature diff**, always present (the single thorough pass). `parallel` (default — thoroughness lives here, since it is the only review in the common case); `inline` for small features.
|
|
28
28
|
- Tag every requirement — missing tags default to `none` / `skip`. **`spec` requires at least `inline` review** — dropping the complete checkpoint is only safe when review covers implementation quality; never combine `spec` with `Review: skip` (use `Checkpoints: none` instead).
|
|
29
29
|
- **Production-risk notes** — carry forward the design's `## Production-risk areas`, if any.
|