@windyroad/retrospective 0.2.0 → 0.3.0-preview.116
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/plugin.json +2 -2
- package/README.md +55 -0
- package/bin/install.mjs +5 -4
- package/hooks/hooks.json +1 -4
- package/lib/install-utils.mjs +146 -0
- package/package.json +3 -2
- package/skills/run-retro/SKILL.md +174 -0
- package/skills/run-retro/test/run-retro-codification-candidates.bats +168 -0
- package/skills/run-retro/test/run-retro-skill-candidates.bats +104 -0
- package/hooks/lib/gate-helpers.sh +0 -174
- package/hooks/lib/review-gate.sh +0 -102
- package/hooks/review-plan-enforce.sh +0 -73
- package/skills/wr:retrospective/SKILL.md +0 -72
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @windyroad/retrospective
|
|
2
|
+
|
|
3
|
+
**Session retrospectives for Claude Code.** Captures learnings at the end of each session and creates problem tickets for failures and friction.
|
|
4
|
+
|
|
5
|
+
Part of [Windy Road Agent Plugins](../../README.md).
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
Every coding session produces learnings -- things that went well, things that broke, things that were harder than expected. Without a retrospective, those learnings evaporate.
|
|
10
|
+
|
|
11
|
+
The retrospective plugin:
|
|
12
|
+
|
|
13
|
+
- **Reminds** you to run a retro when a session ends
|
|
14
|
+
- **Updates** `docs/BRIEFING.md` with session learnings so future sessions start with context
|
|
15
|
+
- **Creates problem tickets** (via [`@windyroad/itil`](../itil/)) for failures and friction encountered during the session
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @windyroad/retrospective
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Restart Claude Code after installing.
|
|
24
|
+
|
|
25
|
+
> **Requires:** [`@windyroad/itil`](../itil/) and [`@windyroad/risk-scorer`](../risk-scorer/). The installer warns if they're missing.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
**Run a session retrospective:**
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/wr-retrospective:run-retro
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This walks through the session's work, identifies what went well and what didn't, updates `docs/BRIEFING.md`, and creates problem tickets for any failures.
|
|
36
|
+
|
|
37
|
+
The plugin also triggers a reminder via a `Stop` hook when a session ends naturally.
|
|
38
|
+
|
|
39
|
+
## How It Works
|
|
40
|
+
|
|
41
|
+
| Hook | Trigger | What it does |
|
|
42
|
+
|------|---------|-------------|
|
|
43
|
+
| `check-deps.sh` | Session start | Verifies that `wr-itil` and `wr-risk-scorer` are installed |
|
|
44
|
+
| `retrospective-reminder.sh` | Session end | Reminds you to run a retrospective |
|
|
45
|
+
|
|
46
|
+
## Updating and Uninstalling
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @windyroad/retrospective --update
|
|
50
|
+
npx @windyroad/retrospective --uninstall
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Licence
|
|
54
|
+
|
|
55
|
+
[MIT](../../LICENSE)
|
package/bin/install.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import { resolve, dirname } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const utils = await import(resolve(__dirname, "
|
|
7
|
+
const utils = await import(resolve(__dirname, "../lib/install-utils.mjs"));
|
|
8
8
|
|
|
9
9
|
const PLUGIN = "wr-retrospective";
|
|
10
|
-
const DEPS = ["wr-
|
|
10
|
+
const DEPS = ["wr-itil", "wr-risk-scorer"];
|
|
11
11
|
|
|
12
12
|
const flags = utils.parseStandardArgs(process.argv);
|
|
13
13
|
|
|
@@ -20,6 +20,7 @@ Session retrospectives that update briefings and create problem tickets
|
|
|
20
20
|
Options:
|
|
21
21
|
--update Update this plugin and its skills
|
|
22
22
|
--uninstall Remove this plugin
|
|
23
|
+
--scope Installation scope: project (default) or user
|
|
23
24
|
--dry-run Show what would be done without executing
|
|
24
25
|
--help, -h Show this help
|
|
25
26
|
`);
|
|
@@ -36,7 +37,7 @@ utils.checkPrerequisites();
|
|
|
36
37
|
if (flags.uninstall) {
|
|
37
38
|
utils.uninstallPackage(PLUGIN);
|
|
38
39
|
} else if (flags.update) {
|
|
39
|
-
utils.updatePackage(PLUGIN);
|
|
40
|
+
utils.updatePackage(PLUGIN, { scope: flags.scope });
|
|
40
41
|
} else {
|
|
41
|
-
utils.installPackage(PLUGIN, { deps: DEPS });
|
|
42
|
+
utils.installPackage(PLUGIN, { deps: DEPS, scope: flags.scope });
|
|
42
43
|
}
|
package/hooks/hooks.json
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
3
|
"SessionStart": [
|
|
4
|
-
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/bin/check-deps.sh wr-retrospective wr-
|
|
5
|
-
],
|
|
6
|
-
"PreToolUse": [
|
|
7
|
-
{ "matcher": "ExitPlanMode", "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/review-plan-enforce.sh" }] }
|
|
4
|
+
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/bin/check-deps.sh wr-retrospective wr-itil wr-risk-scorer" }] }
|
|
8
5
|
],
|
|
9
6
|
"Stop": [
|
|
10
7
|
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/retrospective-reminder.sh" }] }
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared install utilities for @windyroad/* packages.
|
|
3
|
+
* Used by both per-plugin installers and the meta-installer.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const MARKETPLACE_REPO = "windyroad/agent-plugins";
|
|
9
|
+
const MARKETPLACE_NAME = "windyroad";
|
|
10
|
+
|
|
11
|
+
let _dryRun = false;
|
|
12
|
+
|
|
13
|
+
export { MARKETPLACE_REPO, MARKETPLACE_NAME };
|
|
14
|
+
|
|
15
|
+
export function setDryRun(value) {
|
|
16
|
+
_dryRun = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isDryRun() {
|
|
20
|
+
return _dryRun;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function run(cmd, label) {
|
|
24
|
+
console.log(` ${label}...`);
|
|
25
|
+
if (_dryRun) {
|
|
26
|
+
console.log(` [dry-run] ${cmd}`);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
execSync(cmd, { stdio: "inherit" });
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
console.error(` FAILED: ${label}`);
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function checkPrerequisites() {
|
|
39
|
+
if (_dryRun) return;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
execSync("claude --version", { stdio: "pipe" });
|
|
43
|
+
} catch {
|
|
44
|
+
console.error(
|
|
45
|
+
"Error: 'claude' CLI not found. Install Claude Code first:\n https://docs.anthropic.com/en/docs/claude-code\n"
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function addMarketplace() {
|
|
52
|
+
return run(
|
|
53
|
+
`claude plugin marketplace add ${MARKETPLACE_REPO}`,
|
|
54
|
+
`Marketplace: ${MARKETPLACE_NAME}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function installPlugin(pluginName, { scope = "project" } = {}) {
|
|
59
|
+
return run(
|
|
60
|
+
`claude plugin install ${pluginName}@${MARKETPLACE_NAME} --scope ${scope}`,
|
|
61
|
+
pluginName
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function updatePlugin(pluginName, { scope = "project" } = {}) {
|
|
66
|
+
return run(
|
|
67
|
+
`claude plugin update "${pluginName}@${MARKETPLACE_NAME}" --scope ${scope}`,
|
|
68
|
+
pluginName
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function uninstallPlugin(pluginName) {
|
|
73
|
+
return run(`claude plugin uninstall ${pluginName}`, `Removing ${pluginName}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Install a single package: marketplace add + plugin install.
|
|
78
|
+
*/
|
|
79
|
+
export function installPackage(pluginName, { deps = [], scope = "project" } = {}) {
|
|
80
|
+
console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")} (${scope} scope)...\n`);
|
|
81
|
+
|
|
82
|
+
addMarketplace();
|
|
83
|
+
installPlugin(pluginName, { scope });
|
|
84
|
+
|
|
85
|
+
if (deps.length > 0) {
|
|
86
|
+
console.log(`\nNote: This plugin works best with:`);
|
|
87
|
+
for (const dep of deps) {
|
|
88
|
+
console.log(` - @windyroad/${dep.replace("wr-", "")} (npx @windyroad/${dep.replace("wr-", "")})`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(
|
|
93
|
+
`\nDone! Restart Claude Code to activate.\n`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update a single package.
|
|
99
|
+
*/
|
|
100
|
+
export function updatePackage(pluginName, { scope = "project" } = {}) {
|
|
101
|
+
console.log(`\nUpdating @windyroad/${pluginName.replace("wr-", "")}...\n`);
|
|
102
|
+
|
|
103
|
+
run(
|
|
104
|
+
`claude plugin marketplace update ${MARKETPLACE_NAME}`,
|
|
105
|
+
"Updating marketplace"
|
|
106
|
+
);
|
|
107
|
+
updatePlugin(pluginName, { scope });
|
|
108
|
+
|
|
109
|
+
console.log("\nDone! Restart Claude Code to apply updates.\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Uninstall a single package.
|
|
114
|
+
*/
|
|
115
|
+
export function uninstallPackage(pluginName) {
|
|
116
|
+
console.log(`\nUninstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
|
|
117
|
+
|
|
118
|
+
uninstallPlugin(pluginName);
|
|
119
|
+
|
|
120
|
+
console.log("\nDone. Restart Claude Code to apply changes.\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Parse standard flags used by all per-plugin installers.
|
|
125
|
+
*/
|
|
126
|
+
export function parseStandardArgs(argv) {
|
|
127
|
+
const args = argv.slice(2);
|
|
128
|
+
const flags = {
|
|
129
|
+
help: args.includes("--help") || args.includes("-h"),
|
|
130
|
+
uninstall: args.includes("--uninstall"),
|
|
131
|
+
update: args.includes("--update"),
|
|
132
|
+
dryRun: args.includes("--dry-run"),
|
|
133
|
+
scope: "project",
|
|
134
|
+
};
|
|
135
|
+
const scopeIdx = args.indexOf("--scope");
|
|
136
|
+
if (scopeIdx !== -1 && args[scopeIdx + 1]) {
|
|
137
|
+
const val = args[scopeIdx + 1];
|
|
138
|
+
if (["project", "user", "local"].includes(val)) {
|
|
139
|
+
flags.scope = val;
|
|
140
|
+
} else {
|
|
141
|
+
console.error("--scope requires: project, user, or local");
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return flags;
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windyroad/retrospective",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-preview.116",
|
|
4
4
|
"description": "Session retrospectives that update briefings and create problem tickets",
|
|
5
5
|
"bin": {
|
|
6
6
|
"windyroad-retrospective": "./bin/install.mjs"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"agents/",
|
|
24
24
|
"hooks/",
|
|
25
25
|
"skills/",
|
|
26
|
-
".claude-plugin/"
|
|
26
|
+
".claude-plugin/",
|
|
27
|
+
"lib/"
|
|
27
28
|
]
|
|
28
29
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-retrospective:run-retro
|
|
3
|
+
description: Run a session retrospective. Updates docs/BRIEFING.md with learnings and creates problem tickets for failures and friction.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Session Retrospective
|
|
8
|
+
|
|
9
|
+
Reflect on the current session, update the project briefing, and create problem tickets for failures and friction.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
### 1. Read the current briefing
|
|
14
|
+
|
|
15
|
+
Read `docs/BRIEFING.md` to understand what previous sessions already captured.
|
|
16
|
+
|
|
17
|
+
### 2. Reflect on this session
|
|
18
|
+
|
|
19
|
+
Consider the work done in this session and identify:
|
|
20
|
+
|
|
21
|
+
**What you wish you'd been told up front** — things that were non-obvious and caused wasted effort or wrong assumptions. These should be added to BRIEFING.md "What You Need to Know" if they aren't already there.
|
|
22
|
+
|
|
23
|
+
**What surprised you** — things that contradicted reasonable expectations. These should be added to BRIEFING.md "What Will Surprise You" if they aren't already there.
|
|
24
|
+
|
|
25
|
+
**What was harder than it should have been** — friction points, tool limitations, process overhead, confusing code. These should become problem tickets via the `/problem` skill.
|
|
26
|
+
|
|
27
|
+
**What failed** — things that broke, bugs encountered, hooks that errored, tests that failed unexpectedly. These should become problem tickets via the `/problem` skill.
|
|
28
|
+
|
|
29
|
+
**What should we make easier or automate** — repetitive manual steps, missing tooling, things that could be scripted. These should become problem tickets via the `/problem` skill.
|
|
30
|
+
|
|
31
|
+
**What recurring pattern did I (or the assistant) observe that would be better codified?** — a pattern that (a) was invoked multiple times in one session or across sessions, (b) has a deterministic action order or a clear invariant, and (c) is reusable beyond one project. These are **codification candidates** and route through Step 4b below. Do not treat them as problem tickets unless the user explicitly picks that routing option.
|
|
32
|
+
|
|
33
|
+
**What existing skill, agent, hook, ADR, guide, or other codifiable showed a flaw, gap, or friction this session that a targeted edit would fix?** — the **improvement axis** of the codification surface. Criteria: (a) the flaw is reproducible and specific, (b) the fix is a bounded edit to an existing file, (c) no new concept is being invented. Improvement observations flow through the same Step 4b `AskUserQuestion` call as creation candidates, but their options name the improvement shape (e.g. `Skill — improvement stub`, `ADR — supersede or amend`) and the resulting Step 5 row records `Kind: improve` rather than `Kind: create`. An improvement that touches multiple unrelated concerns must be split using the P016 / P017 concern-boundary pattern before routing. If a single output accumulates ≥ 3 improvements in one session, prefer a single coordinating problem ticket over N separate tickets.
|
|
34
|
+
|
|
35
|
+
For each codification candidate, also identify the **Kind** (`create` for a new output, `improve` for a targeted edit to an existing output) and the **best shape** for the codification. The Windy Road suite supports many shapes — pick the one that fits the pattern, not the one you happened to learn first:
|
|
36
|
+
|
|
37
|
+
- **Skill** — deterministic multi-step sequence the user invokes by name (e.g. `wr-itil:ship-fix`). Worked example: `fetch origin → check changesets → score risk → commit → push → release → sync manifest → mark Fix Released`.
|
|
38
|
+
- **Agent** — bounded investigation or review the main agent should delegate to (e.g. a performance-specialist the architect calls in for runtime-path changes). Place under `packages/<plugin>/agents/`.
|
|
39
|
+
- **Hook** — event-driven enforcement or prompt injection (PreToolUse, PostToolUse, UserPromptSubmit). Use when "I keep forgetting to X before Y" — hooks make X unmissable without adding memory load.
|
|
40
|
+
- **Settings entry** — `.claude/settings.json` changes: allowlisted commands, env vars, hook wiring. Best fit when a session repeatedly hits permission prompts for the same benign tool.
|
|
41
|
+
- **Shell or Node script** — reusable repo-level tooling in `scripts/` (e.g. `sync-install-utils.sh`, `sync-plugin-manifests.mjs`). Best fit for multi-step shell sequences worth scripting.
|
|
42
|
+
- **CI step** — `.github/workflows/*.yml` insertion. Best fit for "we'd have caught that earlier with a CI check".
|
|
43
|
+
- **ADR** — architectural decision worth recording. Route to `/wr-architect:create-adr`.
|
|
44
|
+
- **JTBD** — job-to-be-done record for a persona. Route to `/wr-jtbd:update-guide`.
|
|
45
|
+
- **Guide** — voice, style, or risk policy edit. Route to `/wr-voice-tone:update-guide`, `/wr-style-guide:update-guide`, or `/wr-risk-scorer:update-policy`.
|
|
46
|
+
- **Problem ticket** — diagnostic, project-specific friction (the default for flaws). Route to `/wr-itil:manage-problem`.
|
|
47
|
+
- **Test fixture** — regression test for a recurring failure pattern (bats fixture, unit test). Best fit when the observation is "this kept breaking the same way".
|
|
48
|
+
- **Memory** — per-user or per-project memory note in `~/.claude/.../memory/`. Best fit for short, user-habit observations that aren't a codifiable sequence (e.g. "I always forget to run `npm run verify` before pushing").
|
|
49
|
+
|
|
50
|
+
If no shape fits — the observation is a one-off learning, not a repeating pattern — it belongs in BRIEFING.md (Step 3), not Step 4b.
|
|
51
|
+
|
|
52
|
+
Counter-examples (what does **not** become a codification candidate):
|
|
53
|
+
- "The commit gate rejected my work twice because X was misconfigured" — diagnostic, project-specific → **problem ticket** shape (route via Step 4b).
|
|
54
|
+
- "I always forget to run `npm run verify` before pushing" — short, user-habit rather than codifiable sequence → **memory** shape or **BRIEFING.md** note.
|
|
55
|
+
|
|
56
|
+
### 3. Update BRIEFING.md
|
|
57
|
+
|
|
58
|
+
Edit `docs/BRIEFING.md`:
|
|
59
|
+
|
|
60
|
+
- **Add** new learnings to the appropriate section ("What You Need to Know" or "What Will Surprise You")
|
|
61
|
+
- **Remove** stale items that are no longer true. A learning is stale when:
|
|
62
|
+
- The issue has been fixed (e.g., "CI doesn't test v2" after v2 tests are added)
|
|
63
|
+
- It's now documented elsewhere (e.g., in an ADR, CLAUDE.md, or README)
|
|
64
|
+
- The codebase has changed enough that it's no longer relevant
|
|
65
|
+
- **Update** items where the details have changed
|
|
66
|
+
- Keep the file concise — under 2000 tokens. Each item should be 1-2 lines.
|
|
67
|
+
|
|
68
|
+
Use the AskUserQuestion tool to confirm any removals: "I'd like to remove [item] from BRIEFING.md because [reason]. Is this correct?"
|
|
69
|
+
|
|
70
|
+
### 4. Create or update problem tickets
|
|
71
|
+
|
|
72
|
+
For each item identified in "What was harder than it should have been", "What failed", and "What should we make easier or automate", use the `/problem` skill to:
|
|
73
|
+
|
|
74
|
+
- Check if a problem ticket already exists in `docs/problems/`
|
|
75
|
+
- If yes: update it with new evidence from this session
|
|
76
|
+
- If no: create a new problem ticket
|
|
77
|
+
|
|
78
|
+
### 4b. Recommend new codifications
|
|
79
|
+
|
|
80
|
+
For each **codification candidate** identified in Step 2, route the decision through a single `AskUserQuestion` call. This is the ADR-013 Rule 1 structured-interaction pattern — do not present the choices as prose enumeration in the skill output. The shape and Kind identified in Step 2 determine which option rows the user picks from; every shape and Kind routes through the same `AskUserQuestion` so the decision stays one structured interaction (architect decision: flat shape-prefixed options, not a two-step type-then-action or Kind-then-shape flow).
|
|
81
|
+
|
|
82
|
+
For each candidate, invoke `AskUserQuestion` with:
|
|
83
|
+
- `header: "Codification candidate"`
|
|
84
|
+
- `multiSelect: false`
|
|
85
|
+
- Options (a flat list; each option names the shape and Kind up front so the decision is auditable):
|
|
86
|
+
|
|
87
|
+
**Creation axis (Kind: create)** — new outputs:
|
|
88
|
+
1. `Skill — create stub` — description: "Record a stub candidate (suggested name, scope, triggers, prior uses) for a future scaffolding flow. Skill scaffolding itself is out of scope for this retrospective."
|
|
89
|
+
2. `Agent — create stub` — description: "Record a stub candidate for a new agent (suggested name, scope, trigger conditions, delegating skill). Place under `packages/<plugin>/agents/` when scaffolded."
|
|
90
|
+
3. `Hook — create stub` — description: "Record a stub candidate for a new hook (event: PreToolUse / PostToolUse / UserPromptSubmit; trigger; action summary)."
|
|
91
|
+
4. `Settings — propose entry` — description: "Record a proposed `.claude/settings.json` entry (allowlist / env / hook wiring) for later review."
|
|
92
|
+
5. `Script — create stub` — description: "Record a stub `scripts/*.sh` or `scripts/*.mjs` candidate (shebang + TODO + scope)."
|
|
93
|
+
6. `CI — propose step` — description: "Record a proposed `.github/workflows/ci.yml` insertion."
|
|
94
|
+
7. `ADR — invoke create-adr` — description: "Delegate to `/wr-architect:create-adr` so the decision is captured with proper MADR structure. Routing skill, not a stub."
|
|
95
|
+
8. `JTBD — invoke update-guide` — description: "Delegate to `/wr-jtbd:update-guide` to add or amend a job-to-be-done record. Routing skill, not a stub."
|
|
96
|
+
9. `Guide — invoke update-guide / update-policy` — description: "Delegate to `/wr-voice-tone:update-guide`, `/wr-style-guide:update-guide`, or `/wr-risk-scorer:update-policy` depending on the guide touched."
|
|
97
|
+
10. `Problem — invoke manage-problem` — description: "Delegate to `/wr-itil:manage-problem` so the candidate is WSJF-ranked against other backlog items. Routing skill, not a stub."
|
|
98
|
+
11. `Test fixture — create stub` — description: "Record a candidate bats / unit-test fixture for the recurring failure pattern."
|
|
99
|
+
12. `Memory — propose note` — description: "Record a proposed memory note (per-user or per-project) for a short user-habit observation that isn't a codifiable sequence."
|
|
100
|
+
|
|
101
|
+
**Improvement axis (Kind: improve)** — targeted edits to existing outputs (P051):
|
|
102
|
+
13. `Skill — improvement stub` — description: "Record a proposed targeted edit to an existing skill's SKILL.md (file path, observed flaw, evidence, edit summary). Use when an existing skill has a bounded, reproducible gap."
|
|
103
|
+
14. `Agent — improvement stub` — description: "Record a proposed targeted edit to an existing agent file (path, observed flaw, edit summary)."
|
|
104
|
+
15. `Hook — improvement stub` — description: "Record a proposed targeted edit to an existing hook script or `.claude/settings.json` wiring."
|
|
105
|
+
16. `ADR — supersede or amend` — description: "Delegate to `/wr-architect:create-adr` with a `supersedes ADR-N` hint so the new ADR explicitly replaces or amends the outdated one. Routing skill, not a stub."
|
|
106
|
+
17. `Guide — improvement edit` — description: "Delegate to `/wr-voice-tone:update-guide`, `/wr-style-guide:update-guide`, `/wr-jtbd:update-guide`, or `/wr-risk-scorer:update-policy` for a targeted edit to an existing guide (voice / style / JTBD / risk policy)."
|
|
107
|
+
18. `Problem — edit existing ticket` — description: "Delegate to `/wr-itil:manage-problem <NNN>` update flow to amend an existing open or known-error ticket with new observations from this session."
|
|
108
|
+
|
|
109
|
+
**Default:**
|
|
110
|
+
19. `Skip — not codify-worthy` — description: "Neither stub nor route. The observation is too small, too ambiguous, or a one-off learning that belongs in BRIEFING.md."
|
|
111
|
+
|
|
112
|
+
If a single output has accumulated ≥ 3 improvement candidates in one session, prefer offering a single coordinating ticket (`Problem — invoke manage-problem` with an "apply N improvements to X" scope) over recording N separate improvement stubs — this reduces ticket churn and keeps the affected output's improvement queue coherent.
|
|
113
|
+
|
|
114
|
+
If an improvement candidate touches multiple unrelated concerns, apply the P016 / P017 concern-boundary split before routing: re-run the `AskUserQuestion` once per concern, each with its own shape + Kind selection. This mirrors the concern-boundary analysis used when creating new problem tickets.
|
|
115
|
+
|
|
116
|
+
If the option count is impractical for a single `AskUserQuestion` payload in a given Claude Code version, fall back to a two-question flow: (1) `"Which shape fits?"` with the shape list, (2) `"Create, improve, or skip?"` with `Create stub / Improvement stub / Invoke dedicated skill / Skip` — but prefer the single call when the surface allows it.
|
|
117
|
+
|
|
118
|
+
When the user chooses any of the **Create stub** shapes (skill / agent / hook / settings / script / CI / test / memory), record a candidate entry in the Step 5 summary under "Codification Candidates" with:
|
|
119
|
+
- **Kind** — `create`
|
|
120
|
+
- **Shape** — which codification type (skill, agent, hook, etc.)
|
|
121
|
+
- **Suggested name** — for skills: `wr-<plugin>:<action>`; for agents: `<plugin>:<name>`; for hooks: `<event>:<trigger>`; for scripts: `scripts/<name>.<ext>`; etc.
|
|
122
|
+
- **Scope** — one sentence on what the codification does and when it should fire
|
|
123
|
+
- **Triggers** — example user prompts or events that should invoke it
|
|
124
|
+
- **Prior uses** — 2-3 observed invocations from this session
|
|
125
|
+
|
|
126
|
+
When the user chooses any of the **Improvement stub** shapes (skill / agent / hook), record a candidate entry in the Step 5 summary under "Codification Candidates" with:
|
|
127
|
+
- **Kind** — `improve`
|
|
128
|
+
- **Shape** — which existing codifiable is being edited (skill, agent, hook)
|
|
129
|
+
- **Target file** — the existing file path (e.g. `packages/itil/skills/manage-problem/SKILL.md`)
|
|
130
|
+
- **Observed flaw** — one-sentence description of the gap, friction, or defect
|
|
131
|
+
- **Edit summary** — one-sentence description of the proposed targeted edit
|
|
132
|
+
- **Evidence** — 1-3 observations from this session showing the flaw
|
|
133
|
+
|
|
134
|
+
When the user chooses any of the **Invoke <dedicated skill>** routes (ADR create / JTBD / Guide / Problem) OR the improvement routing options (ADR supersede or amend / Guide improvement edit / Problem edit existing ticket), delegate to the named skill with a context hand-off describing the candidate. Record the routing decision in the Step 5 summary under "Codification Candidates" with Kind (`create` or `improve`), Shape = the routing target, and a `routed to <skill>` marker. For `ADR — supersede or amend`, include the `supersedes ADR-N` hint in the hand-off so create-adr produces the correct MADR header.
|
|
135
|
+
|
|
136
|
+
When the user chooses **Skip**, record the candidate in the Step 5 summary under "Codification Candidates" with a `skipped` marker so the pattern is still visible in the session audit trail.
|
|
137
|
+
|
|
138
|
+
**Non-interactive fallback (per ADR-013 Rule 6):** if `AskUserQuestion` is unavailable, record each candidate in the Step 5 summary under "Codification Candidates" with a `flagged — not actioned (non-interactive)` marker, noting the identified Kind alongside Shape (e.g. `Kind: improve, Shape: skill, flagged — not actioned (non-interactive)`). Do not create stubs, route to dedicated skills, or scaffold. The user can review the flags and decide when they return. Improvement candidates flagged this way retain the Target file and Observed flaw fields so the user has enough to act on without re-deriving the context.
|
|
139
|
+
|
|
140
|
+
**Backward compatibility**: "Skill" is retained as one shape among many so existing P044 muscle memory and `run-retro-skill-candidates.bats` continue to hold. Use the singular shape name in the summary (e.g. `Shape: skill`) so legacy greps still match. Improvement-axis rows use the same singular shape names (`Shape: skill, Kind: improve`) so the Shape column stays consistent across both axes.
|
|
141
|
+
|
|
142
|
+
### 5. Summary
|
|
143
|
+
|
|
144
|
+
Present a summary to the user:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
## Session Retrospective
|
|
148
|
+
|
|
149
|
+
### BRIEFING.md Changes
|
|
150
|
+
- Added: [items added]
|
|
151
|
+
- Removed: [items removed with reasons]
|
|
152
|
+
- Updated: [items modified]
|
|
153
|
+
|
|
154
|
+
### Problems Created/Updated
|
|
155
|
+
- [problem ticket]: [summary]
|
|
156
|
+
|
|
157
|
+
### Codification Candidates
|
|
158
|
+
|
|
159
|
+
| Kind | Shape | Suggested name / Target file | Scope / Flaw | Triggers / Evidence | Decision |
|
|
160
|
+
|------|-------|-----------------------------|--------------|----------------------|----------|
|
|
161
|
+
| create | skill | [suggested name] | [scope] | [examples] | created stub / routed to <skill> / skipped / flagged (non-interactive) |
|
|
162
|
+
| create | agent | ... | ... | ... | ... |
|
|
163
|
+
| improve | skill | [target file path] | [observed flaw] | [1-3 session observations] | improvement stub / routed to <skill> / skipped / flagged (non-interactive) |
|
|
164
|
+
| improve | hook | ... | ... | ... | ... |
|
|
165
|
+
|
|
166
|
+
### No Action Needed
|
|
167
|
+
- [learnings that were already captured]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The `Kind` column takes values `create` or `improve` — the create / improve axis defined in Step 2 and Step 4b. Creation rows use the `Suggested name` / `Scope` / `Triggers` field semantics; improvement rows reuse the same columns with `Target file` / `Observed flaw` / `Evidence` semantics (per the stub-recording guidance in Step 4b). The decision column carries the same vocabulary for both Kinds, with `improvement stub` replacing `created stub` for Kind=improve rows.
|
|
171
|
+
|
|
172
|
+
If the "Codification Candidates" table has no rows, omit it rather than rendering an empty header. The legacy "Skill Candidates" heading is preserved as a worked-example row in the Shape column so downstream tooling that grepped for "Skill Candidates" continues to find skill-shaped entries within the unified table.
|
|
173
|
+
|
|
174
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Doc-lint guard: run-retro SKILL.md must include a generalised codification
|
|
3
|
+
# branch that recommends agents, hooks, and other codifiable outputs — not
|
|
4
|
+
# only skills. This is the P050 generalisation of P044's single-output-type
|
|
5
|
+
# recommendation surface, extended by P051 with an improvement axis for
|
|
6
|
+
# existing codifiables.
|
|
7
|
+
#
|
|
8
|
+
# Structural assertion — Permitted Exception to the source-grep ban (ADR-005 / P011).
|
|
9
|
+
# These tests assert that the skill specification document includes the
|
|
10
|
+
# multi-shape codification branch introduced by P050 and the improvement-axis
|
|
11
|
+
# extension introduced by P051.
|
|
12
|
+
#
|
|
13
|
+
# Cross-reference:
|
|
14
|
+
# P051: docs/problems/051-run-retro-does-not-recommend-improvements-to-existing-codifiables.open.md
|
|
15
|
+
# P050: docs/problems/050-run-retro-does-not-recommend-other-codifiable-outputs.known-error.md
|
|
16
|
+
# P044: docs/problems/044-run-retro-does-not-recommend-new-skills.known-error.md (predecessor)
|
|
17
|
+
# ADR-013 Rule 1 / Rule 6 (docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md)
|
|
18
|
+
# @jtbd JTBD-101 (extend the suite with clear patterns)
|
|
19
|
+
# @jtbd JTBD-006 (progress the backlog while I'm away — AFK-safe Rule 6 fallback)
|
|
20
|
+
# @jtbd JTBD-001 (enforce governance without slowing down)
|
|
21
|
+
|
|
22
|
+
setup() {
|
|
23
|
+
SKILL_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
24
|
+
SKILL_FILE="${SKILL_DIR}/SKILL.md"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@test "SKILL.md Step 2 includes a generalised codification reflection category (P050)" {
|
|
28
|
+
# P050 fix: Step 2 must prompt for recurring patterns that would be better
|
|
29
|
+
# codified — not only as skills. The generalised wording names "codif"
|
|
30
|
+
# (codification / codifiable / codified) so reviewers and agents can tell
|
|
31
|
+
# P050 shipped.
|
|
32
|
+
run grep -in "codification candidate\|codifiable\|codify\|codified" "$SKILL_FILE"
|
|
33
|
+
[ "$status" -eq 0 ]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@test "SKILL.md Step 2 names at least three codification shapes beyond skills (P050)" {
|
|
37
|
+
# The shape question is the point of P050. Names at minimum: agent, hook,
|
|
38
|
+
# and one of: settings, script, CI step, ADR, JTBD, guide, test. "skill"
|
|
39
|
+
# stays in the list as a worked example so P044 muscle memory survives.
|
|
40
|
+
run grep -ic "agent" "$SKILL_FILE"
|
|
41
|
+
[ "$status" -eq 0 ]
|
|
42
|
+
[ "$output" -ge 1 ]
|
|
43
|
+
run grep -ic "hook" "$SKILL_FILE"
|
|
44
|
+
[ "$status" -eq 0 ]
|
|
45
|
+
[ "$output" -ge 1 ]
|
|
46
|
+
run grep -icE "settings|script|ci step|ADR|JTBD|guide|test fixture" "$SKILL_FILE"
|
|
47
|
+
[ "$status" -eq 0 ]
|
|
48
|
+
[ "$output" -ge 1 ]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test "SKILL.md Step 4b recommendation branch covers multiple shapes (P050)" {
|
|
52
|
+
# Step 4b must route per-shape. The recommendation branch names more than
|
|
53
|
+
# just "skill" as an output type.
|
|
54
|
+
run grep -inE "(agent|hook).*stub|stub.*(agent|hook)|create.*(agent|hook)|(agent|hook).*candidate" "$SKILL_FILE"
|
|
55
|
+
[ "$status" -eq 0 ]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@test "SKILL.md Step 4b uses a single AskUserQuestion with shape-prefixed options (ADR-013 Rule 1)" {
|
|
59
|
+
# Architect decision: flat AskUserQuestion with type-prefixed labels, not a
|
|
60
|
+
# two-step chained flow. At least three shape-prefixed option lines must
|
|
61
|
+
# appear in the Step 4b block. Match both plain and backticked forms:
|
|
62
|
+
# "Skill — ..." / "`Skill — ...`" / "**Skill** — ..."
|
|
63
|
+
run grep -cE "(\`|\*\*)?(Skill|Agent|Hook|Settings|Script|CI|ADR|JTBD|Guide|Problem|Test fixture|Memory)(\`|\*\*)? +(—|-) " "$SKILL_FILE"
|
|
64
|
+
[ "$status" -eq 0 ]
|
|
65
|
+
[ "$output" -ge 3 ]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@test "SKILL.md Step 4b routes ADR candidates to wr-architect:create-adr (P050)" {
|
|
69
|
+
# Dedicated codification skills already exist — Step 4b must route to them,
|
|
70
|
+
# not duplicate intake.
|
|
71
|
+
run grep -in "wr-architect:create-adr\|/wr-architect:create-adr" "$SKILL_FILE"
|
|
72
|
+
[ "$status" -eq 0 ]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@test "SKILL.md Step 4b routes JTBD candidates to wr-jtbd:update-guide (P050)" {
|
|
76
|
+
run grep -in "wr-jtbd:update-guide\|/wr-jtbd:update-guide" "$SKILL_FILE"
|
|
77
|
+
[ "$status" -eq 0 ]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@test "SKILL.md Step 4b preserves non-interactive fallback for generalised shapes (ADR-013 Rule 6)" {
|
|
81
|
+
# The Rule 6 fallback that P044 introduced must cover the generalised surface.
|
|
82
|
+
# When AskUserQuestion is unavailable, all shape candidates are flagged rather
|
|
83
|
+
# than silently chosen.
|
|
84
|
+
run grep -in "non-interactive\|Rule 6" "$SKILL_FILE"
|
|
85
|
+
[ "$status" -eq 0 ]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@test "SKILL.md Step 5 summary has a Codification Candidates section with Shape column (P050)" {
|
|
89
|
+
# P050 candidate fix (3): unified table. Either:
|
|
90
|
+
# - a "### Codification Candidates" heading AND a "Shape" column, OR
|
|
91
|
+
# - a "### Codification Candidates" heading with per-shape rows.
|
|
92
|
+
# Accept either shape but require the heading.
|
|
93
|
+
run grep -n "### Codification Candidates\|## Codification Candidates" "$SKILL_FILE"
|
|
94
|
+
[ "$status" -eq 0 ]
|
|
95
|
+
run grep -in "shape" "$SKILL_FILE"
|
|
96
|
+
[ "$status" -eq 0 ]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@test "SKILL.md retains 'skill' as a worked example within the generalised category (backward compat with P044)" {
|
|
100
|
+
# Architect advisory: keep 'skill' in the shape list as one worked example so
|
|
101
|
+
# the existing P044 muscle memory and the run-retro-skill-candidates.bats
|
|
102
|
+
# line-30 grep still pass. This is the compatibility test.
|
|
103
|
+
run grep -in "would be better as a skill\|better as a skill\|as a skill\|skill candidate" "$SKILL_FILE"
|
|
104
|
+
[ "$status" -eq 0 ]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
# P051 improvement-axis assertions
|
|
109
|
+
#
|
|
110
|
+
# P051 extends the P050 codification surface with an improvement axis so
|
|
111
|
+
# existing skills, agents, hooks, ADRs, and guides can be recommended for
|
|
112
|
+
# targeted edits (not only new creation). The architect decision requires:
|
|
113
|
+
# - flat shape-prefixed option list (no two-step create-vs-improve question)
|
|
114
|
+
# - parallel naming (e.g. `Skill — improvement stub` mirrors
|
|
115
|
+
# `Skill — create stub`)
|
|
116
|
+
# - Kind column in the Step 5 summary table (create / improve)
|
|
117
|
+
# - non-interactive fallback records the improvement Kind alongside Shape
|
|
118
|
+
# ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
@test "SKILL.md Step 2 includes an improvement reflection category for existing codifiables (P051)" {
|
|
121
|
+
# P051 fix: Step 2 must prompt for flaws observed in existing skills /
|
|
122
|
+
# agents / hooks / ADRs / guides — the improvement axis. The generalised
|
|
123
|
+
# phrasing names "improvement" or "improve" alongside "codification" so
|
|
124
|
+
# reviewers and agents can tell P051 shipped.
|
|
125
|
+
run grep -inE "existing (skill|agent|hook|codifiable).*(flaw|friction|gap|improve|improvement)|improvement(-| )shaped|improvement reflection|improvement candidate|improve an existing" "$SKILL_FILE"
|
|
126
|
+
[ "$status" -eq 0 ]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@test "SKILL.md Step 4b names improvement-shaped options for multiple shapes (P051)" {
|
|
130
|
+
# P051 fix: the flat option list must carry `Skill — improvement ...`,
|
|
131
|
+
# `Agent — improvement ...`, and `Hook — improvement ...` rows in addition
|
|
132
|
+
# to the create-stub rows introduced by P050. Match at least three
|
|
133
|
+
# shape-prefixed improvement options.
|
|
134
|
+
run grep -cE "(\`|\*\*)?(Skill|Agent|Hook|ADR|Guide|Problem)(\`|\*\*)? +(—|-) +(improvement|supersede|amend|edit)" "$SKILL_FILE"
|
|
135
|
+
[ "$status" -eq 0 ]
|
|
136
|
+
[ "$output" -ge 3 ]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@test "SKILL.md Step 4b routes improvement-axis ADR candidates to create-adr with supersede hint (P051)" {
|
|
140
|
+
# ADR improvement shape is "supersede or amend an existing ADR". The
|
|
141
|
+
# routing target stays `/wr-architect:create-adr` (it writes the new ADR
|
|
142
|
+
# with a supersedes reference) — P051 adds the supersede/amend wording as
|
|
143
|
+
# a shape-prefixed option so the improvement axis is recognisable.
|
|
144
|
+
# Require both: an `ADR — supersede` (or equivalent) shape-prefixed option
|
|
145
|
+
# AND a route through `wr-architect:create-adr` near that option.
|
|
146
|
+
run grep -inE "(\`|\*\*)?ADR(\`|\*\*)? +(—|-) +(supersede|amend)" "$SKILL_FILE"
|
|
147
|
+
[ "$status" -eq 0 ]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@test "SKILL.md Step 5 summary distinguishes create from improve via a Kind column (P051)" {
|
|
151
|
+
# P051 candidate fix (3): the Codification Candidates table gains a `Kind`
|
|
152
|
+
# column carrying `create` / `improve`. Accept either a literal "Kind"
|
|
153
|
+
# column header or explicit `create / improve` values cited in the summary
|
|
154
|
+
# template. Both forms signal that the summary separates the two axes.
|
|
155
|
+
run grep -inE "\| *Kind *\||Kind column|Kind.*create.*improve|create / improve|create/improve" "$SKILL_FILE"
|
|
156
|
+
[ "$status" -eq 0 ]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@test "SKILL.md Step 4b non-interactive fallback covers improvement candidates (P051 + ADR-013 Rule 6)" {
|
|
160
|
+
# Rule 6 fallback must mention the improvement axis explicitly so an AFK
|
|
161
|
+
# loop that flags an improvement candidate records Kind=improve alongside
|
|
162
|
+
# Shape (per architect advisory — the audit trail needs both axes for
|
|
163
|
+
# improvements). Accept either explicit Kind=improve language in the
|
|
164
|
+
# fallback block, OR a statement that the fallback records the Kind
|
|
165
|
+
# alongside Shape.
|
|
166
|
+
run grep -inE "flagged.*improve|improvement.*flagged|Kind.*(Shape|alongside)|(Shape|alongside).*Kind" "$SKILL_FILE"
|
|
167
|
+
[ "$status" -eq 0 ]
|
|
168
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Doc-lint guard: run-retro SKILL.md must include the skill-recommendation branch.
|
|
3
|
+
#
|
|
4
|
+
# Structural assertion — Permitted Exception to the source-grep ban (ADR-005 / P011).
|
|
5
|
+
# These tests do not assert hook behaviour; they assert that the skill specification
|
|
6
|
+
# document includes the skill-candidate branch added in P044.
|
|
7
|
+
#
|
|
8
|
+
# Cross-reference:
|
|
9
|
+
# P044 (docs/problems/044-run-retro-does-not-recommend-new-skills.known-error.md)
|
|
10
|
+
# ADR-013 Rule 1 / Rule 6 (docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md)
|
|
11
|
+
# @jtbd JTBD-001 (enforce governance without slowing down)
|
|
12
|
+
# @jtbd JTBD-101 (extend the suite with clear patterns)
|
|
13
|
+
|
|
14
|
+
setup() {
|
|
15
|
+
SKILL_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
16
|
+
SKILL_FILE="${SKILL_DIR}/SKILL.md"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@test "SKILL.md exists and has frontmatter" {
|
|
20
|
+
[ -f "$SKILL_FILE" ]
|
|
21
|
+
run head -1 "$SKILL_FILE"
|
|
22
|
+
[ "$status" -eq 0 ]
|
|
23
|
+
[ "$output" = "---" ]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@test "SKILL.md Step 2 includes the skill-candidate reflection category (P044, updated by P050)" {
|
|
27
|
+
# P044 fix: Step 2 must prompt for recurring workflows that would be better
|
|
28
|
+
# as skills. P050 generalises this to a codification category, with "skill"
|
|
29
|
+
# retained as one worked example within the shape list. This test accepts
|
|
30
|
+
# either the original P044 phrasing OR the P050 generalised phrasing that
|
|
31
|
+
# still names "skill" as a shape.
|
|
32
|
+
run grep -in "recurring workflow.*better as a skill\|would be better as a skill\|recurring pattern.*better codified\|\*\*Skill\*\* — " "$SKILL_FILE"
|
|
33
|
+
[ "$status" -eq 0 ]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@test "SKILL.md includes a Step 4b Recommend new skills branch (P044)" {
|
|
37
|
+
# P044 fix: a dedicated output branch for skill candidates, distinct from Step 4
|
|
38
|
+
# (problem tickets) and Step 5 (summary).
|
|
39
|
+
run grep -n "Recommend new skills\|Step 4b" "$SKILL_FILE"
|
|
40
|
+
[ "$status" -eq 0 ]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@test "SKILL.md Step 4b uses AskUserQuestion (ADR-013 Rule 1)" {
|
|
44
|
+
# ADR-013 Rule 1: the skill-candidate decision branch must use AskUserQuestion,
|
|
45
|
+
# not prose '(a)/(b)/(c)' enumeration. Architect review flagged this as the
|
|
46
|
+
# gotcha to avoid when implementing P044.
|
|
47
|
+
run grep -n "AskUserQuestion" "$SKILL_FILE"
|
|
48
|
+
[ "$status" -eq 0 ]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@test "SKILL.md Step 4b header matches ADR-013 structured-interaction pattern (P044, updated by P050)" {
|
|
52
|
+
# P044 used "Skill candidate" as the AskUserQuestion header. P050 generalises
|
|
53
|
+
# to "Codification candidate" with "Skill" as one shape option. Accept either —
|
|
54
|
+
# both preserve the ADR-013 Rule 1 structured-interaction shape.
|
|
55
|
+
run grep -in "Skill candidate\|Codification candidate" "$SKILL_FILE"
|
|
56
|
+
[ "$status" -eq 0 ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "SKILL.md Step 4b names the structured options for skill-shaped candidates (P044, updated by P050)" {
|
|
60
|
+
# P044 required three specific option labels (Create a new skill / Track as a
|
|
61
|
+
# problem / Skip — not skill-worthy). P050 generalises: the skill shape now
|
|
62
|
+
# appears as a row in the flat shape-prefixed option list ("Skill — create
|
|
63
|
+
# stub"). The "track as problem" path becomes the explicit "Problem" row.
|
|
64
|
+
# Skip path becomes "Skip — not codify-worthy". This test accepts either
|
|
65
|
+
# pattern so the P044 regression guard survives P050's generalisation.
|
|
66
|
+
run grep -in "Create a new skill\|Skill — create stub\|Skill - create stub" "$SKILL_FILE"
|
|
67
|
+
[ "$status" -eq 0 ]
|
|
68
|
+
run grep -in "Track as a problem ticket\|Problem — invoke manage-problem\|Problem - invoke manage-problem" "$SKILL_FILE"
|
|
69
|
+
[ "$status" -eq 0 ]
|
|
70
|
+
run grep -in "Skip — not skill-worthy\|Skip - not skill-worthy\|Skip — not codify-worthy\|Skip - not codify-worthy" "$SKILL_FILE"
|
|
71
|
+
[ "$status" -eq 0 ]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@test "SKILL.md Step 4b has non-interactive fallback per ADR-013 Rule 6" {
|
|
75
|
+
# ADR-013 Rule 6: if AskUserQuestion is unavailable, flag candidates instead of
|
|
76
|
+
# silently choosing. P044 implementation uses "flagged — not actioned" wording.
|
|
77
|
+
run grep -n "non-interactive\|Rule 6" "$SKILL_FILE"
|
|
78
|
+
[ "$status" -eq 0 ]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@test "SKILL.md Step 5 summary template has a Skill / Codification Candidates slot (P044, updated by P050)" {
|
|
82
|
+
# P044 fix: the summary template must include a Skill Candidates section so
|
|
83
|
+
# recommendations are visible in the session audit alongside BRIEFING changes
|
|
84
|
+
# and problem tickets. P050 generalises this to a unified "Codification
|
|
85
|
+
# Candidates" table with a Shape column; skill-shaped candidates still appear
|
|
86
|
+
# (as Shape: skill rows). Accept either heading.
|
|
87
|
+
run grep -n "### Skill Candidates\|### Codification Candidates" "$SKILL_FILE"
|
|
88
|
+
[ "$status" -eq 0 ]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@test "SKILL.md does not contain 'Options: (a)' prose option list (ADR-013)" {
|
|
92
|
+
# ADR-013 Rule 1: user-facing decisions must use AskUserQuestion, not prose
|
|
93
|
+
# "Options: (a)/(b)/(c)". This matches the narrow pattern used by
|
|
94
|
+
# manage-problem-no-prose-options.bats so criteria lists using (a)/(b)/(c)
|
|
95
|
+
# as internal enumeration (which are fine) don't trip the test.
|
|
96
|
+
run grep -n "Options: (a)" "$SKILL_FILE"
|
|
97
|
+
[ "$status" -ne 0 ]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@test "SKILL.md does not contain 'Your call:' prose option prompt (ADR-013)" {
|
|
101
|
+
# ADR-013 Rule 1: mirrors the manage-problem regression guard.
|
|
102
|
+
run grep -n "Your call:" "$SKILL_FILE"
|
|
103
|
+
[ "$status" -ne 0 ]
|
|
104
|
+
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Shared portable helpers for gate enforcement hooks.
|
|
3
|
-
# Sourced by architect-gate.sh, risk-gate.sh, and all hook scripts.
|
|
4
|
-
# Provides: _mtime, _hashcmd, _doc_exclusions, _err_trap, _get_*
|
|
5
|
-
|
|
6
|
-
# ---------------------------------------------------------------------------
|
|
7
|
-
# Portable utilities
|
|
8
|
-
# ---------------------------------------------------------------------------
|
|
9
|
-
|
|
10
|
-
# Portable mtime: tries GNU stat, falls back to macOS stat
|
|
11
|
-
_mtime() { stat -c%Y "$1" 2>/dev/null || /usr/bin/stat -f%m "$1" 2>/dev/null || echo 0; }
|
|
12
|
-
|
|
13
|
-
# Portable hash: tries md5sum, falls back to md5 -r, then shasum
|
|
14
|
-
_hashcmd() { md5sum 2>/dev/null || md5 -r 2>/dev/null || shasum 2>/dev/null; }
|
|
15
|
-
|
|
16
|
-
# Paths excluded from pipeline state hashing and docs-only detection.
|
|
17
|
-
_doc_exclusions() {
|
|
18
|
-
echo ':!docs/' ':!.risk-reports/' ':!.changeset/' ':!governance/' ':!.claude/plans/' ':!CLAUDE.md' ':!AGENTS.md' ':!PRINCIPLES.md' ':!DECISION-MANAGEMENT.md' ':!AGENTIC_RISK_REGISTER.md' ':!PROBLEM-MANAGEMENT.md'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
# ---------------------------------------------------------------------------
|
|
22
|
-
# ERR trap: outputs diagnostic JSON on hook errors (P010)
|
|
23
|
-
# Usage: source gate-helpers.sh at top of hook, then call _enable_err_trap
|
|
24
|
-
# ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
_enable_err_trap() {
|
|
27
|
-
trap '_err_trap_handler "$BASH_SOURCE" "$LINENO" "$BASH_COMMAND"' ERR
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
_err_trap_handler() {
|
|
31
|
-
local script="$1" line="$2" cmd="$3"
|
|
32
|
-
local name
|
|
33
|
-
name=$(basename "$script" 2>/dev/null || echo "$script")
|
|
34
|
-
# Output diagnostic as systemMessage so it's visible in conversation
|
|
35
|
-
cat <<EOF
|
|
36
|
-
{
|
|
37
|
-
"systemMessage": "Hook error in ${name} at line ${line}: ${cmd}"
|
|
38
|
-
}
|
|
39
|
-
EOF
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
# ---------------------------------------------------------------------------
|
|
43
|
-
# JSON input parsing: standardised helpers replacing inline python3
|
|
44
|
-
# Each reads from _HOOK_INPUT (set by the hook before calling these)
|
|
45
|
-
# ---------------------------------------------------------------------------
|
|
46
|
-
|
|
47
|
-
# Store hook input for reuse by parsing helpers
|
|
48
|
-
_HOOK_INPUT=""
|
|
49
|
-
|
|
50
|
-
_parse_input() {
|
|
51
|
-
_HOOK_INPUT=$(cat)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
_get_tool_name() {
|
|
55
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
56
|
-
import sys, json
|
|
57
|
-
try:
|
|
58
|
-
data = json.load(sys.stdin)
|
|
59
|
-
print(data.get('tool_name', ''))
|
|
60
|
-
except:
|
|
61
|
-
print('')
|
|
62
|
-
" 2>/dev/null || echo ""
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
_get_session_id() {
|
|
66
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
67
|
-
import sys, json
|
|
68
|
-
try:
|
|
69
|
-
data = json.load(sys.stdin)
|
|
70
|
-
print(data.get('session_id', ''))
|
|
71
|
-
except:
|
|
72
|
-
print('')
|
|
73
|
-
" 2>/dev/null || echo ""
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
_get_command() {
|
|
77
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
78
|
-
import sys, json
|
|
79
|
-
try:
|
|
80
|
-
data = json.load(sys.stdin)
|
|
81
|
-
print(data.get('tool_input', {}).get('command', ''))
|
|
82
|
-
except:
|
|
83
|
-
print('')
|
|
84
|
-
" 2>/dev/null || echo ""
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
_get_file_path() {
|
|
88
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
89
|
-
import sys, json
|
|
90
|
-
try:
|
|
91
|
-
data = json.load(sys.stdin)
|
|
92
|
-
ti = data.get('tool_input', {})
|
|
93
|
-
print(ti.get('file_path', ti.get('path', '')))
|
|
94
|
-
except:
|
|
95
|
-
print('')
|
|
96
|
-
" 2>/dev/null || echo ""
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
_get_subagent_type() {
|
|
100
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
101
|
-
import sys, json
|
|
102
|
-
try:
|
|
103
|
-
data = json.load(sys.stdin)
|
|
104
|
-
print(data.get('tool_input', {}).get('subagent_type', ''))
|
|
105
|
-
except:
|
|
106
|
-
print('')
|
|
107
|
-
" 2>/dev/null || echo ""
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
_get_user_prompt() {
|
|
111
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
112
|
-
import sys, json
|
|
113
|
-
try:
|
|
114
|
-
data = json.load(sys.stdin)
|
|
115
|
-
print(data.get('user_prompt', ''))
|
|
116
|
-
except:
|
|
117
|
-
print('')
|
|
118
|
-
" 2>/dev/null || echo ""
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
_get_tool_output() {
|
|
122
|
-
echo "$_HOOK_INPUT" | python3 -c "
|
|
123
|
-
import sys, json
|
|
124
|
-
try:
|
|
125
|
-
data = json.load(sys.stdin)
|
|
126
|
-
# PostToolUse provides tool_response (dict with content array), not tool_output
|
|
127
|
-
tr = data.get('tool_response', {})
|
|
128
|
-
if isinstance(tr, dict):
|
|
129
|
-
content = tr.get('content', [])
|
|
130
|
-
if isinstance(content, list):
|
|
131
|
-
texts = [c.get('text', '') for c in content if isinstance(c, dict) and c.get('type') == 'text']
|
|
132
|
-
if texts:
|
|
133
|
-
print('\n'.join(texts))
|
|
134
|
-
sys.exit(0)
|
|
135
|
-
# Fallback for older/different hook formats
|
|
136
|
-
print(data.get('tool_output', ''))
|
|
137
|
-
except:
|
|
138
|
-
print('')
|
|
139
|
-
" 2>/dev/null || echo ""
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
# ---------------------------------------------------------------------------
|
|
143
|
-
# Session-scoped tmp directory for risk files
|
|
144
|
-
# ---------------------------------------------------------------------------
|
|
145
|
-
|
|
146
|
-
# Returns the session-scoped directory for risk temp files.
|
|
147
|
-
# Creates the directory if it doesn't exist.
|
|
148
|
-
# Usage: DIR=$(_risk_dir "$SESSION_ID"); echo "1" > "$DIR/commit"
|
|
149
|
-
_risk_dir() {
|
|
150
|
-
local sid="$1"
|
|
151
|
-
local dir="${TMPDIR:-/tmp}/claude-risk-${sid}"
|
|
152
|
-
mkdir -p "$dir"
|
|
153
|
-
echo "$dir"
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
# ---------------------------------------------------------------------------
|
|
157
|
-
# Non-doc file detection for WIP gating
|
|
158
|
-
# ---------------------------------------------------------------------------
|
|
159
|
-
|
|
160
|
-
_is_doc_file() {
|
|
161
|
-
local file_path="$1"
|
|
162
|
-
local EXCL
|
|
163
|
-
EXCL=$(_doc_exclusions)
|
|
164
|
-
for pattern in $EXCL; do
|
|
165
|
-
local clean="${pattern#:!}"
|
|
166
|
-
case "$file_path" in
|
|
167
|
-
*"$clean"*) return 0 ;;
|
|
168
|
-
esac
|
|
169
|
-
done
|
|
170
|
-
case "$file_path" in
|
|
171
|
-
*.claude/*|*.risk-reports/*|*RISK-POLICY.md) return 0 ;;
|
|
172
|
-
esac
|
|
173
|
-
return 1
|
|
174
|
-
}
|
package/hooks/lib/review-gate.sh
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Shared gate logic for review enforcement hooks (a11y, voice-tone, style-guide).
|
|
3
|
-
# Sourced by *-enforce-edit.sh hooks and review-plan-enforce.sh.
|
|
4
|
-
# Provides: check_review_gate, review_gate_deny, review_gate_parse_error
|
|
5
|
-
|
|
6
|
-
# Source shared portable helpers (_mtime, _hashcmd)
|
|
7
|
-
_REVIEW_GATE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
-
source "$_REVIEW_GATE_DIR/gate-helpers.sh"
|
|
9
|
-
|
|
10
|
-
# Check review gate marker. Returns 0 if marker is valid (allow), 1 if invalid (deny).
|
|
11
|
-
# Sets REVIEW_GATE_REASON on failure.
|
|
12
|
-
# Usage: check_review_gate "$SESSION_ID" "style-guide" "docs/STYLE-GUIDE.md"
|
|
13
|
-
check_review_gate() {
|
|
14
|
-
local SESSION_ID="$1"
|
|
15
|
-
local SYSTEM="$2" # e.g., "a11y", "voice-tone", "style-guide"
|
|
16
|
-
local POLICY_FILE="$3" # e.g., "docs/STYLE-GUIDE.md"
|
|
17
|
-
local MARKER="/tmp/${SYSTEM}-reviewed-${SESSION_ID}"
|
|
18
|
-
local HASH_FILE="/tmp/${SYSTEM}-reviewed-${SESSION_ID}.hash"
|
|
19
|
-
local TTL_SECONDS="${REVIEW_TTL:-600}"
|
|
20
|
-
|
|
21
|
-
# 1. Marker must exist
|
|
22
|
-
if [ ! -f "$MARKER" ]; then
|
|
23
|
-
REVIEW_GATE_REASON="No ${SYSTEM} review marker found. The ${SYSTEM} agent must review first."
|
|
24
|
-
return 1
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# 2. TTL check — marker mtime must be within TTL
|
|
28
|
-
local NOW=$(date +%s)
|
|
29
|
-
local MARKER_TIME=$(_mtime "$MARKER")
|
|
30
|
-
local AGE=$(( NOW - MARKER_TIME ))
|
|
31
|
-
if [ "$AGE" -ge "$TTL_SECONDS" ]; then
|
|
32
|
-
rm -f "$MARKER" "$HASH_FILE"
|
|
33
|
-
REVIEW_GATE_REASON="${SYSTEM} review expired (${AGE}s old, TTL ${TTL_SECONDS}s). Re-run the ${SYSTEM} agent."
|
|
34
|
-
return 1
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
# 3. Drift detection — policy file hash must match
|
|
38
|
-
if [ -f "$HASH_FILE" ] && [ -n "$POLICY_FILE" ]; then
|
|
39
|
-
local STORED_HASH=$(cat "$HASH_FILE")
|
|
40
|
-
local CURRENT_HASH=""
|
|
41
|
-
if [ -f "$POLICY_FILE" ]; then
|
|
42
|
-
CURRENT_HASH=$(cat "$POLICY_FILE" | _hashcmd | cut -d' ' -f1)
|
|
43
|
-
elif [ -d "$POLICY_FILE" ]; then
|
|
44
|
-
# Directory (e.g., docs/decisions/) — hash all .md files
|
|
45
|
-
CURRENT_HASH=$(find "$POLICY_FILE" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
|
|
46
|
-
else
|
|
47
|
-
CURRENT_HASH="missing"
|
|
48
|
-
fi
|
|
49
|
-
if [ "$STORED_HASH" != "$CURRENT_HASH" ]; then
|
|
50
|
-
rm -f "$MARKER" "$HASH_FILE"
|
|
51
|
-
REVIEW_GATE_REASON="${SYSTEM} policy file changed since last review. Re-run the ${SYSTEM} agent."
|
|
52
|
-
return 1
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# Slide TTL window forward
|
|
57
|
-
touch "$MARKER"
|
|
58
|
-
return 0
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
# Store policy file hash after a successful review.
|
|
62
|
-
# Usage: store_review_hash "$SESSION_ID" "style-guide" "docs/STYLE-GUIDE.md"
|
|
63
|
-
store_review_hash() {
|
|
64
|
-
local SESSION_ID="$1"
|
|
65
|
-
local SYSTEM="$2"
|
|
66
|
-
local POLICY_FILE="$3"
|
|
67
|
-
local HASH_FILE="/tmp/${SYSTEM}-reviewed-${SESSION_ID}.hash"
|
|
68
|
-
|
|
69
|
-
if [ -n "$POLICY_FILE" ]; then
|
|
70
|
-
local HASH=""
|
|
71
|
-
if [ -f "$POLICY_FILE" ]; then
|
|
72
|
-
HASH=$(cat "$POLICY_FILE" | _hashcmd | cut -d' ' -f1)
|
|
73
|
-
elif [ -d "$POLICY_FILE" ]; then
|
|
74
|
-
HASH=$(find "$POLICY_FILE" -name '*.md' -not -name 'README.md' -print0 | sort -z | xargs -0 cat 2>/dev/null | _hashcmd | cut -d' ' -f1)
|
|
75
|
-
else
|
|
76
|
-
HASH="missing"
|
|
77
|
-
fi
|
|
78
|
-
echo "$HASH" > "$HASH_FILE"
|
|
79
|
-
fi
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
# Emit fail-closed deny JSON for PreToolUse hooks.
|
|
83
|
-
review_gate_deny() {
|
|
84
|
-
local REASON="$1"
|
|
85
|
-
cat <<EOF
|
|
86
|
-
{
|
|
87
|
-
"hookSpecificOutput": {
|
|
88
|
-
"hookEventName": "PreToolUse",
|
|
89
|
-
"permissionDecision": "deny",
|
|
90
|
-
"permissionDecisionReason": "$REASON"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
EOF
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# Emit fail-closed deny JSON for parse failures.
|
|
97
|
-
review_gate_parse_error() {
|
|
98
|
-
cat <<'EOF'
|
|
99
|
-
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny",
|
|
100
|
-
"permissionDecisionReason": "BLOCKED: Could not parse hook input. Gate is fail-closed." } }
|
|
101
|
-
EOF
|
|
102
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# PreToolUse hook: Denies ExitPlanMode until review specialists have
|
|
3
|
-
# reviewed the plan. Skips UI specialists (a11y, voice-tone, style-guide,
|
|
4
|
-
# jtbd) when the plan only touches non-UI files (P008 optimization).
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
source "$SCRIPT_DIR/lib/review-gate.sh"
|
|
8
|
-
source "$SCRIPT_DIR/lib/gate-helpers.sh"
|
|
9
|
-
|
|
10
|
-
INPUT=$(cat)
|
|
11
|
-
|
|
12
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty') || true
|
|
13
|
-
|
|
14
|
-
if [ -z "$SESSION_ID" ]; then
|
|
15
|
-
review_gate_parse_error
|
|
16
|
-
exit 0
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
# Detect if the plan touches UI files by checking uncommitted changes
|
|
20
|
-
# UI patterns: *.html, *.jsx, *.tsx, *.vue, *.svelte, *.astro, *.css, *.scss
|
|
21
|
-
HAS_UI_FILES=false
|
|
22
|
-
UI_PATTERNS='\.html$|\.jsx$|\.tsx$|\.vue$|\.svelte$|\.astro$|\.css$|\.scss$|\.ejs$|\.hbs$|\.erb$|\.leaf$'
|
|
23
|
-
if git diff --cached --name-only 2>/dev/null | grep -qE "$UI_PATTERNS"; then
|
|
24
|
-
HAS_UI_FILES=true
|
|
25
|
-
elif git diff --name-only 2>/dev/null | grep -qE "$UI_PATTERNS"; then
|
|
26
|
-
HAS_UI_FILES=true
|
|
27
|
-
elif git ls-files --others --exclude-standard 2>/dev/null | grep -qE "$UI_PATTERNS"; then
|
|
28
|
-
HAS_UI_FILES=true
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# Also check the plan file itself for mentions of UI files
|
|
32
|
-
PLAN_DIR="$HOME/.claude/plans"
|
|
33
|
-
if [ -d "$PLAN_DIR" ] && [ "$HAS_UI_FILES" = false ]; then
|
|
34
|
-
LATEST_PLAN=$(ls -t "$PLAN_DIR"/*.md 2>/dev/null | head -1)
|
|
35
|
-
if [ -n "$LATEST_PLAN" ] && grep -qiE '\.html|\.jsx|\.tsx|\.vue|\.svelte|\.css|component|page|form|modal|dialog' "$LATEST_PLAN" 2>/dev/null; then
|
|
36
|
-
HAS_UI_FILES=true
|
|
37
|
-
fi
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
MISSING=""
|
|
41
|
-
|
|
42
|
-
if [ "$HAS_UI_FILES" = true ]; then
|
|
43
|
-
# UI files detected — require all specialists
|
|
44
|
-
for SYSTEM in a11y voice-tone style-guide jtbd; do
|
|
45
|
-
MARKER="/tmp/${SYSTEM}-plan-reviewed-${SESSION_ID}"
|
|
46
|
-
if [ ! -f "$MARKER" ]; then
|
|
47
|
-
case "$SYSTEM" in
|
|
48
|
-
a11y) AGENT="accessibility-agents:accessibility-lead" ;;
|
|
49
|
-
voice-tone) AGENT="voice-and-tone-lead" ;;
|
|
50
|
-
style-guide) AGENT="style-guide-lead" ;;
|
|
51
|
-
jtbd) AGENT="jtbd-lead" ;;
|
|
52
|
-
esac
|
|
53
|
-
if [ -z "$MISSING" ]; then
|
|
54
|
-
MISSING="$AGENT"
|
|
55
|
-
else
|
|
56
|
-
MISSING="${MISSING}, ${AGENT}"
|
|
57
|
-
fi
|
|
58
|
-
fi
|
|
59
|
-
done
|
|
60
|
-
else
|
|
61
|
-
# No UI files — skip a11y, voice-tone, style-guide, jtbd
|
|
62
|
-
# Auto-create their markers so the gate passes
|
|
63
|
-
for SYSTEM in a11y voice-tone style-guide jtbd; do
|
|
64
|
-
touch "/tmp/${SYSTEM}-plan-reviewed-${SESSION_ID}"
|
|
65
|
-
done
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
if [ -n "$MISSING" ]; then
|
|
69
|
-
review_gate_deny "BLOCKED: Cannot approve plan without specialist review. Missing: ${MISSING}. Delegate to each agent to review the plan."
|
|
70
|
-
exit 0
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
exit 0
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: wr:retrospective
|
|
3
|
-
description: Run a session retrospective. Updates docs/BRIEFING.md with learnings and creates problem tickets for failures and friction.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Session Retrospective
|
|
8
|
-
|
|
9
|
-
Reflect on the current session, update the project briefing, and create problem tickets for failures and friction.
|
|
10
|
-
|
|
11
|
-
## Steps
|
|
12
|
-
|
|
13
|
-
### 1. Read the current briefing
|
|
14
|
-
|
|
15
|
-
Read `docs/BRIEFING.md` to understand what previous sessions already captured.
|
|
16
|
-
|
|
17
|
-
### 2. Reflect on this session
|
|
18
|
-
|
|
19
|
-
Consider the work done in this session and identify:
|
|
20
|
-
|
|
21
|
-
**What you wish you'd been told up front** — things that were non-obvious and caused wasted effort or wrong assumptions. These should be added to BRIEFING.md "What You Need to Know" if they aren't already there.
|
|
22
|
-
|
|
23
|
-
**What surprised you** — things that contradicted reasonable expectations. These should be added to BRIEFING.md "What Will Surprise You" if they aren't already there.
|
|
24
|
-
|
|
25
|
-
**What was harder than it should have been** — friction points, tool limitations, process overhead, confusing code. These should become problem tickets via the `/problem` skill.
|
|
26
|
-
|
|
27
|
-
**What failed** — things that broke, bugs encountered, hooks that errored, tests that failed unexpectedly. These should become problem tickets via the `/problem` skill.
|
|
28
|
-
|
|
29
|
-
**What should we make easier or automate** — repetitive manual steps, missing tooling, things that could be scripted. These should become problem tickets via the `/problem` skill.
|
|
30
|
-
|
|
31
|
-
### 3. Update BRIEFING.md
|
|
32
|
-
|
|
33
|
-
Edit `docs/BRIEFING.md`:
|
|
34
|
-
|
|
35
|
-
- **Add** new learnings to the appropriate section ("What You Need to Know" or "What Will Surprise You")
|
|
36
|
-
- **Remove** stale items that are no longer true. A learning is stale when:
|
|
37
|
-
- The issue has been fixed (e.g., "CI doesn't test v2" after v2 tests are added)
|
|
38
|
-
- It's now documented elsewhere (e.g., in an ADR, CLAUDE.md, or README)
|
|
39
|
-
- The codebase has changed enough that it's no longer relevant
|
|
40
|
-
- **Update** items where the details have changed
|
|
41
|
-
- Keep the file concise — under 2000 tokens. Each item should be 1-2 lines.
|
|
42
|
-
|
|
43
|
-
Use the AskUserQuestion tool to confirm any removals: "I'd like to remove [item] from BRIEFING.md because [reason]. Is this correct?"
|
|
44
|
-
|
|
45
|
-
### 4. Create or update problem tickets
|
|
46
|
-
|
|
47
|
-
For each item identified in "What was harder than it should have been", "What failed", and "What should we make easier or automate", use the `/problem` skill to:
|
|
48
|
-
|
|
49
|
-
- Check if a problem ticket already exists in `docs/problems/`
|
|
50
|
-
- If yes: update it with new evidence from this session
|
|
51
|
-
- If no: create a new problem ticket
|
|
52
|
-
|
|
53
|
-
### 5. Summary
|
|
54
|
-
|
|
55
|
-
Present a summary to the user:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
## Session Retrospective
|
|
59
|
-
|
|
60
|
-
### BRIEFING.md Changes
|
|
61
|
-
- Added: [items added]
|
|
62
|
-
- Removed: [items removed with reasons]
|
|
63
|
-
- Updated: [items modified]
|
|
64
|
-
|
|
65
|
-
### Problems Created/Updated
|
|
66
|
-
- [problem ticket]: [summary]
|
|
67
|
-
|
|
68
|
-
### No Action Needed
|
|
69
|
-
- [learnings that were already captured]
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
$ARGUMENTS
|