@windyroad/itil 0.57.3 → 0.58.0-preview.940
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 +1 -1
- package/hooks/hooks.json +4 -0
- package/hooks/itil-commit-trailer-transition-advisory.sh +9 -3
- package/hooks/itil-no-implement-draft-gate.sh +69 -0
- package/hooks/test/itil-no-implement-draft-gate.bats +82 -0
- package/package.json +1 -1
- package/skills/capture-rfc/SKILL.md +7 -6
- package/skills/capture-story/SKILL.md +22 -18
- package/skills/capture-story/test/capture-story-behavioural.bats +22 -1
- package/skills/manage-story/SKILL.md +4 -4
- package/skills/work-problem/SKILL.md +1 -1
package/hooks/hooks.json
CHANGED
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
"matcher": "Bash",
|
|
38
38
|
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/p057-staging-trap-detect.sh" }]
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
"matcher": "Bash",
|
|
42
|
+
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-no-implement-draft-gate.sh" }]
|
|
43
|
+
},
|
|
40
44
|
{
|
|
41
45
|
"matcher": "Bash",
|
|
42
46
|
"hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-bash-polling-antipattern-detect.sh" }]
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
# - is NOT the artefact's capture commit (subject does not start with
|
|
18
18
|
# `docs(rfcs): capture RFC-` / `feat(itil): capture STORY-`), AND
|
|
19
19
|
# - the referenced artefact is in a pre-in-progress status
|
|
20
|
-
# (RFC: .proposed/.accepted ; story: .
|
|
20
|
+
# (RFC: .proposed/.accepted ; story: .accepted — per ADR-096 a draft
|
|
21
|
+
# story is blocked from implementation, not advised to advance) on disk.
|
|
21
22
|
# → emit a stderr advisory naming the transition command. Silent otherwise.
|
|
22
23
|
#
|
|
23
24
|
# Advisory-only (ADR-013 Rule 6 fail-open; ADR-045 ≤300-byte band; exit 0).
|
|
@@ -69,9 +70,14 @@ while IFS= read -r id; do
|
|
|
69
70
|
;;
|
|
70
71
|
STORY-*)
|
|
71
72
|
[ -d "./docs/stories" ] || continue
|
|
72
|
-
|
|
73
|
+
# ADR-096: a draft story is NEVER implementable — the PreToolUse
|
|
74
|
+
# itil-no-implement-draft-gate blocks a commit referencing a draft story.
|
|
75
|
+
# So this advisory now fires for an ACCEPTED story carrying its first
|
|
76
|
+
# implementing commit: advise accepted -> in-progress (draft -> in-progress
|
|
77
|
+
# no longer exists; a draft story must be accepted first).
|
|
78
|
+
shopt -s nullglob; files=(./docs/stories/accepted/${id}-*.md); shopt -u nullglob
|
|
73
79
|
[ ${#files[@]} -gt 0 ] || continue
|
|
74
|
-
advise "${id} carries a non-capture commit
|
|
80
|
+
advise "${id} carries a non-capture implementing commit and is accepted. It should advance to in-progress — run /wr-itil:manage-story ${id} in-progress. Bypass: BYPASS_TRANSITION_ADVISORY=1."
|
|
75
81
|
;;
|
|
76
82
|
esac
|
|
77
83
|
done <<< "$TRAILERS"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# wr-itil — PreToolUse:Bash gate (ADR-096 / P404). A story in `draft` is NEVER
|
|
3
|
+
# implementable. Blocks a `git commit` whose `Refs: STORY-NNN` trailer names a
|
|
4
|
+
# story still in docs/stories/draft/ — implementation requires `accepted` (where
|
|
5
|
+
# the INVEST + RFC-trace gates + ADR-090 ratification fire). Capture commits are
|
|
6
|
+
# exempt (they CREATE the draft story). Bootstrap-exempt commits bypass
|
|
7
|
+
# (ADR-060 A4). Fail-open on every abnormal path (ADR-013 Rule 6).
|
|
8
|
+
#
|
|
9
|
+
# This is the enforcement locus the architect named as the ONLY one that catches
|
|
10
|
+
# the exact P404 bypass — a direct implementing commit against a draft story,
|
|
11
|
+
# whether from the orchestrator or by hand.
|
|
12
|
+
#
|
|
13
|
+
# Bypass: BYPASS_NO_IMPLEMENT_DRAFT=1.
|
|
14
|
+
#
|
|
15
|
+
# @adr ADR-096 (no-implement-while-draft) ADR-060 (lifecycle) ADR-013 (Rule 6)
|
|
16
|
+
# ADR-095 (sibling capture-time gates) ADR-052 (bats)
|
|
17
|
+
# @problem P404
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
+
# shellcheck source=lib/command-detect.sh
|
|
20
|
+
source "$SCRIPT_DIR/lib/command-detect.sh" 2>/dev/null || exit 0
|
|
21
|
+
|
|
22
|
+
INPUT=$(cat)
|
|
23
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | python3 -c "import sys,json
|
|
24
|
+
try: print(json.load(sys.stdin).get('tool_name',''))
|
|
25
|
+
except: print('')" 2>/dev/null || echo "")
|
|
26
|
+
[ "$TOOL_NAME" = "Bash" ] || exit 0
|
|
27
|
+
|
|
28
|
+
COMMAND=$(printf '%s' "$INPUT" | python3 -c "import sys,json
|
|
29
|
+
try: print(json.load(sys.stdin).get('tool_input',{}).get('command',''))
|
|
30
|
+
except: print('')" 2>/dev/null || echo "")
|
|
31
|
+
command_invokes_git_commit "$COMMAND" || exit 0
|
|
32
|
+
[ "${BYPASS_NO_IMPLEMENT_DRAFT:-}" = "1" ] && exit 0
|
|
33
|
+
|
|
34
|
+
# Capture commits create the draft story — exempt. Bootstrap migrations bypass.
|
|
35
|
+
case "$COMMAND" in
|
|
36
|
+
*"capture STORY-"*) exit 0 ;;
|
|
37
|
+
*"bootstrap-exempt"*) exit 0 ;;
|
|
38
|
+
esac
|
|
39
|
+
|
|
40
|
+
# Extract `Refs: STORY-NNN` trailers from the command text (present literally
|
|
41
|
+
# regardless of -m / heredoc form).
|
|
42
|
+
STORIES=$(printf '%s' "$COMMAND" | grep -oE 'Refs:[[:space:]]*STORY-[0-9]{3}' | grep -oE 'STORY-[0-9]{3}' | sort -u)
|
|
43
|
+
[ -n "$STORIES" ] || exit 0
|
|
44
|
+
|
|
45
|
+
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
46
|
+
[ -d "./docs/stories" ] || exit 0
|
|
47
|
+
|
|
48
|
+
deny() {
|
|
49
|
+
cat <<JSON
|
|
50
|
+
{
|
|
51
|
+
"hookSpecificOutput": {
|
|
52
|
+
"hookEventName": "PreToolUse",
|
|
53
|
+
"permissionDecision": "deny",
|
|
54
|
+
"permissionDecisionReason": "$1"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
JSON
|
|
58
|
+
exit 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
while IFS= read -r id; do
|
|
62
|
+
[ -n "$id" ] || continue
|
|
63
|
+
shopt -s nullglob; draftfiles=(./docs/stories/draft/${id}-*.md); shopt -u nullglob
|
|
64
|
+
if [ ${#draftfiles[@]} -gt 0 ]; then
|
|
65
|
+
deny "BLOCKED (ADR-096 / P404): this commit references ${id} via a Refs: trailer, but ${id} is still in draft. A draft story cannot be implemented — accept it first via /wr-itil:manage-story ${id} accepted (it runs the INVEST + RFC-trace gates + ratification), then re-commit. Bypass: BYPASS_NO_IMPLEMENT_DRAFT=1."
|
|
66
|
+
fi
|
|
67
|
+
done <<< "$STORIES"
|
|
68
|
+
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Behavioural tests for itil-no-implement-draft-gate.sh (ADR-096 / P404).
|
|
3
|
+
# PreToolUse:Bash — reads a JSON envelope on stdin, emits permissionDecision:deny
|
|
4
|
+
# (exit 0, deny in the JSON) when a commit references a DRAFT story.
|
|
5
|
+
# @adr ADR-096 @adr ADR-052 @problem P404
|
|
6
|
+
|
|
7
|
+
setup() {
|
|
8
|
+
HOOK="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/itil-no-implement-draft-gate.sh"
|
|
9
|
+
TMP="$(mktemp -d)"; cd "$TMP"; git init -q .
|
|
10
|
+
mkdir -p docs/stories/draft docs/stories/accepted
|
|
11
|
+
}
|
|
12
|
+
teardown() { cd /; rm -rf "$TMP"; }
|
|
13
|
+
|
|
14
|
+
# write env.json carrying the given command string
|
|
15
|
+
env_cmd() { python3 -c 'import json,sys; open("env.json","w").write(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$1"; }
|
|
16
|
+
|
|
17
|
+
@test "blocks a commit referencing a DRAFT story" {
|
|
18
|
+
touch docs/stories/draft/STORY-042-foo.md
|
|
19
|
+
env_cmd 'git commit -m "fix: thing
|
|
20
|
+
|
|
21
|
+
Refs: STORY-042"'
|
|
22
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
23
|
+
[ "$status" -eq 0 ]
|
|
24
|
+
echo "$output" | grep -q '"permissionDecision": "deny"'
|
|
25
|
+
echo "$output" | grep -q 'STORY-042'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@test "allows a commit referencing an ACCEPTED story" {
|
|
29
|
+
touch docs/stories/accepted/STORY-042-foo.md
|
|
30
|
+
env_cmd 'git commit -m "fix: thing
|
|
31
|
+
|
|
32
|
+
Refs: STORY-042"'
|
|
33
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
34
|
+
[ "$status" -eq 0 ]
|
|
35
|
+
! echo "$output" | grep -q 'deny'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@test "exempts a capture commit even for a draft story" {
|
|
39
|
+
touch docs/stories/draft/STORY-042-foo.md
|
|
40
|
+
env_cmd 'git commit -m "feat(itil): capture STORY-042 foo
|
|
41
|
+
|
|
42
|
+
Refs: STORY-042"'
|
|
43
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
44
|
+
[ "$status" -eq 0 ]
|
|
45
|
+
! echo "$output" | grep -q 'deny'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@test "bootstrap-exempt commit bypasses" {
|
|
49
|
+
touch docs/stories/draft/STORY-042-foo.md
|
|
50
|
+
env_cmd 'git commit -m "migrate
|
|
51
|
+
|
|
52
|
+
Refs: STORY-042
|
|
53
|
+
bootstrap-exempt: STORY-MAP-001"'
|
|
54
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
55
|
+
[ "$status" -eq 0 ]
|
|
56
|
+
! echo "$output" | grep -q 'deny'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "non-commit command is a no-op" {
|
|
60
|
+
touch docs/stories/draft/STORY-042-foo.md
|
|
61
|
+
env_cmd 'echo Refs: STORY-042'
|
|
62
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
63
|
+
[ "$status" -eq 0 ]
|
|
64
|
+
! echo "$output" | grep -q 'deny'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@test "commit with no story trailer is a no-op" {
|
|
68
|
+
env_cmd 'git commit -m "fix: no story ref"'
|
|
69
|
+
run bash -c "bash '$HOOK' < env.json"
|
|
70
|
+
[ "$status" -eq 0 ]
|
|
71
|
+
! echo "$output" | grep -q 'deny'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@test "BYPASS env allows the commit" {
|
|
75
|
+
touch docs/stories/draft/STORY-042-foo.md
|
|
76
|
+
env_cmd 'git commit -m "fix
|
|
77
|
+
|
|
78
|
+
Refs: STORY-042"'
|
|
79
|
+
BYPASS_NO_IMPLEMENT_DRAFT=1 run bash -c "BYPASS_NO_IMPLEMENT_DRAFT=1 bash '$HOOK' < env.json"
|
|
80
|
+
[ "$status" -eq 0 ]
|
|
81
|
+
! echo "$output" | grep -q 'deny'
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -35,7 +35,7 @@ When referencing RFC IDs, problem IDs, ADR IDs, JTBD IDs, or story IDs in prose
|
|
|
35
35
|
|
|
36
36
|
**Optional flag (Phase 2)**: `--stories STORY-<NNN>,STORY-<NNN>,...` — ORDERED execution sequence per ADR-060 line 262. Cardinality 0..N: an RFC whose work is not decomposed into stories OMITS the flag and capture-rfc populates `stories: []` in frontmatter (a structural state, NOT a reduced-ceremony path — every fix goes through an RFC per ADR-071); story-decomposed RFCs supply the ordered list. The flag accepts STORY-IDs that don't yet resolve to files (forward-reference is permitted at capture; the existence check happens at `manage-rfc <NNN> accepted` transition per ADR-060 working-the-problem flow line 304).
|
|
37
37
|
|
|
38
|
-
**Optional flag (fix-time authoring, ADR-073 P399)**: `--fix-time` — switches Step 5 from the deferred-placeholder template to **full authoring**: capture-rfc reads the traced problem ticket(s) and authors a populated `## Scope` (the fix being proposed + chosen implementation approach as prose) and a
|
|
38
|
+
**Optional flag (fix-time authoring, ADR-073 P399)**: `--fix-time` — switches Step 5 from the deferred-placeholder template to **full authoring**: capture-rfc reads the traced problem ticket(s) and authors a populated `## Scope` (the fix being proposed + chosen implementation approach as prose) and, per ADR-089, a **story** work-breakdown on a story map — NOT a `## Tasks` decomposition (superseded; the fix's stories are authored via capture-story-map + capture-story and listed in `stories:`), instead of the `(deferred — populate at manage-rfc accepted)` placeholders. Set by the I13 propose-fix gate's auto-create branch (`/wr-itil:manage-problem` + `/wr-itil:work-problems`); the user-aside path omits it. The authored RFC still carries NO Considered-Options block (ADR-070 — chosen-path prose only) and is still born `human-oversight: unconfirmed` (ratified at `manage-rfc accepted`). Composes with `--stories`.
|
|
39
39
|
|
|
40
40
|
```
|
|
41
41
|
/wr-itil:capture-rfc P168 Pipeline consume-catalog and bootstrap-from-reports — multi-commit retrofit
|
|
@@ -56,7 +56,7 @@ This skill has **one direction-setting AskUserQuestion** (problem-trace, when ar
|
|
|
56
56
|
| RFC ID allocation | Mechanical: `max(local, origin) + 1`, three-digit padded | silent-mechanical |
|
|
57
57
|
| Title kebab-slug | Mechanical: first 8-10 non-stopword tokens of description | silent-mechanical |
|
|
58
58
|
| Title prose / scope summary refinement | Optional `AskUserQuestion`; silent-default to derived form when unavailable | taste |
|
|
59
|
-
| Fix-time Scope/
|
|
59
|
+
| Fix-time Scope/Stories authoring (`--fix-time`) | Framework-mediated: author `## Scope` + a **story** work-breakdown on a story map (NOT `## Tasks` — superseded per ADR-089) from the traced problem's RCA + Fix Strategy. NO `AskUserQuestion` — the scope is *derived* from already-pinned ADR-071 direction, not new direction-setting (ADR-073 § ADR-044 reclassification, P399). Born `unconfirmed`; ratified at `manage-rfc accepted` | silent-framework |
|
|
60
60
|
| File write / frontmatter | Mechanical: shape per `docs/rfcs/README.md` § RFC body structure | silent-mechanical |
|
|
61
61
|
| Single commit | Mechanical: `docs(rfcs): capture RFC-<NNN> <title>` | silent-mechanical |
|
|
62
62
|
| Empty arguments | Halt-with-stderr-directive: print "capture-rfc requires `<problem-trace> <description>` — invoke /wr-itil:manage-rfc instead for the full intake flow" and exit. AFK orchestrators MUST NOT invoke capture-rfc with empty arguments. | n/a |
|
|
@@ -210,10 +210,11 @@ stories: [<from --stories flag — ordered execution sequence; or [] if --storie
|
|
|
210
210
|
<default (user-aside path): (deferred — populate at /wr-itil:manage-rfc accepted transition)>
|
|
211
211
|
<--fix-time path (ADR-073 P399): AUTHOR this from the traced problem — a real paragraph describing the fix being proposed (the change(s) the Known Error needs) plus the chosen implementation approach as prose. Derive it from the problem's `## Root Cause Analysis` + `## Fix Strategy`. Do NOT enumerate rejected alternatives or add a Considered-Options block (ADR-070 — chosen-path prose only).>
|
|
212
212
|
|
|
213
|
-
##
|
|
213
|
+
## Stories
|
|
214
214
|
|
|
215
|
-
<
|
|
216
|
-
|
|
215
|
+
<RFC work-breakdown is **stories on a story map**, NOT tasks (ADR-089 / ADR-095 / ADR-096). Do NOT scaffold a `## Tasks` body.>
|
|
216
|
+
<default (user-aside path): populate the ordered `stories:` frontmatter array via `--stories STORY-<NNN>,...` (each story must exist on a story map); rendered here at /wr-itil:manage-rfc.>
|
|
217
|
+
<--fix-time path (ADR-073 P399): AUTHOR the fix's stories on a story map — route to /wr-itil:capture-story-map (or /wr-itil:manage-story-map to extend one), capture each story via /wr-itil:capture-story onto that map, and list them in `stories:`. A draft story cannot be implemented until accepted (ADR-096). Do NOT emit a `## Tasks` decomposition.>
|
|
217
218
|
|
|
218
219
|
## Commits
|
|
219
220
|
|
|
@@ -227,7 +228,7 @@ stories: [<from --stories flag — ordered execution sequence; or [] if --storie
|
|
|
227
228
|
**Mode branch (`fix_time` from Step 1):**
|
|
228
229
|
|
|
229
230
|
- **`fix_time=0` (user-aside path, default)**: write the deferred placeholders verbatim. The deferred-section pattern matches `capture-problem`'s placeholder approach — the captured RFC is intentionally minimal; full scope and task decomposition land at the manage-rfc accepted-transition step.
|
|
230
|
-
- **`fix_time=1` (fix-time auto-create path, ADR-073 P399)**: BEFORE writing the file, Read each traced problem ticket (`docs/problems/.../<NNN>-*.md`) and author the `## Scope` + `## Tasks`
|
|
231
|
+
- **`fix_time=1` (fix-time auto-create path, ADR-073 P399)**: BEFORE writing the file, Read each traced problem ticket (`docs/problems/.../<NNN>-*.md`) and author the `## Scope` prose + the fix's **stories on a story map** (NOT a `## Tasks` decomposition — superseded per ADR-089/ADR-095/ADR-096: route to capture-story-map, capture each story via capture-story, list in `stories:`) from its `## Root Cause Analysis` + `## Fix Strategy`. The RFC is NOT a skeleton on this path — the deferred "flesh out later" step never self-fires (P375), so the scope is authored now while the fix context is in hand. Keep it ADR-070-compliant: chosen-path prose in `## Scope`, NO Considered-Options / alternatives-rejected block (enumerating ≥2 options and choosing among them would be cat-1 direction-setting; deriving the single fix the problem already implies is framework-mediated — ADR-073 § ADR-044 reclassification). `human-oversight` stays `unconfirmed` (Step 7 oversight discipline holds — no per-RFC substance-confirm at fix-time; ratified at `manage-rfc accepted`).
|
|
231
232
|
|
|
232
233
|
### 6. Single commit — `## RFCs` reverse-trace refresh; no rfcs README refresh
|
|
233
234
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wr-itil:capture-story
|
|
3
|
-
description: Lightweight story-capture skill for aside-invocation during foreground work — mandatory leading problem-trace AND JTBD-trace per ADR-060 I6 + I9 invariants,
|
|
3
|
+
description: Lightweight story-capture skill for aside-invocation during foreground work — mandatory leading problem-trace AND JTBD-trace per ADR-060 I6 + I9 invariants, mandatory `--story-map` trace (I8 enforced AT CAPTURE per ADR-095, refuse-and-route if absent) + optional `--rfc` (I7 at accepted) + real user-value + >=1 acceptance criterion at capture (I10 content subset / ADR-095), skeleton story file at `docs/stories/draft/STORY-NNN-<slug>.md`, single commit per capture, no inline README refresh. Defers full INVEST shape + acceptance transition to /wr-itil:manage-story. Use when the user (or agent) wants to capture a story quickly with clear problem + JTBD anchoring. For full lifecycle management, use /wr-itil:manage-story.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Capture Story Skill
|
|
8
8
|
|
|
9
|
-
Capture an INVEST-shaped story ticket quickly during foreground work. Lightweight aside-invocation surface that complements the heavyweight `/wr-itil:manage-story` flow. Mirrors `/wr-itil:capture-rfc` shape per ADR-032 lightweight + heavyweight skill split, extended for the story tier's stricter trace-mandate (
|
|
9
|
+
Capture an INVEST-shaped story ticket quickly during foreground work. Lightweight aside-invocation surface that complements the heavyweight `/wr-itil:manage-story` flow. Mirrors `/wr-itil:capture-rfc` shape per ADR-032 lightweight + heavyweight skill split, extended for the story tier's stricter trace-mandate (problem, JTBD, AND story-map at capture — I8 enforced at capture per ADR-095; RFC deferred to accepted).
|
|
10
10
|
|
|
11
11
|
This skill is one half of the capture-then-manage story framework introduced by ADR-060 (Problem-RFC-Story framework with mandatory problem-trace and unified problem ontology, accepted 2026-05-05; Phase 2 amendment 2026-05-12 introducing the story tier). The other half is `/wr-itil:manage-story` (heavyweight intake + INVEST-gated lifecycle management).
|
|
12
12
|
|
|
@@ -15,8 +15,8 @@ This skill is one half of the capture-then-manage story framework introduced by
|
|
|
15
15
|
## When to invoke
|
|
16
16
|
|
|
17
17
|
- **Slicing an RFC into INVEST-shaped sub-workstreams**: agent / user has captured an RFC and is now decomposing its scope into the ordered `stories:` array per ADR-060's working-the-problem flow (line 300-320). Each slice on a story-map's backbone → ribs → slices grid becomes one story.
|
|
18
|
-
- **
|
|
19
|
-
- **Retrospective bootstrap migration** (Slice 15 of P170 Phase 2): extracting existing slices from `docs/plans/170-rfc-framework-story-map.md` into individual story files. The
|
|
18
|
+
- **Placing a story on its map at capture**: per ADR-095 a story is born on a story map — `--story-map` is MANDATORY at capture (I8 refuse-and-route if absent). `--rfc` stays optional (I7 enforces at the `draft → accepted` transition); a draft story may exist with NO `rfcs:` until the design firms up, but NEVER with an empty `story-maps:`.
|
|
19
|
+
- **Retrospective bootstrap migration** (Slice 15 of P170 Phase 2): extracting existing slices from `docs/plans/170-rfc-framework-story-map.md` into individual story files. The bootstrap-exempt marker (ADR-060 A4 / ADR-053) permits the retrospective sequence to bypass the capture-time I8 hard-block for migration stories ONLY; `rfcs:` still fills in before the manage-story <NNN> accepted gate (I7).
|
|
20
20
|
- **Forward dogfood capture**: a new story for in-flight work, captured at the start of implementation, runs to `done` via `Refs: STORY-NNN` trailer detection + acceptance-criteria all-ticked.
|
|
21
21
|
|
|
22
22
|
**Use `/wr-itil:manage-story` instead** when:
|
|
@@ -153,21 +153,25 @@ jtbd_file=$(ls docs/jtbd/*/JTBD-<NNN>-*.md 2>/dev/null | head -1)
|
|
|
153
153
|
|
|
154
154
|
JTBD lifecycle states (`.proposed.md` / `.accepted.md` / `.archived.md`) all pass silently — a story may anchor on a proposed JTBD per the dogfood pattern (Phase 2 itself is being captured against proposed JTBDs).
|
|
155
155
|
|
|
156
|
-
### 2.6. Validate optional `--rfc`
|
|
156
|
+
### 2.6. Validate story-map trace (I8 hard-block AT CAPTURE per ADR-095) + optional `--rfc`
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
**I8 story-map membership is enforced at CAPTURE (ADR-095) — a story is born on a map.** `--story-map` is MANDATORY:
|
|
159
|
+
|
|
160
|
+
- **`--story-map` absent**: hard-block with **refuse-and-route** (parity with the I6/I9 mandatory-trace gates). Do NOT scaffold `story-maps: []`. Emit the deny log (`reason: missing-story-map-trace`) and halt with:
|
|
161
|
+
> `/wr-itil:capture-story` requires a story-map trace (ADR-095 / I8): every story is born on a story map. Create a map first via `/wr-itil:capture-story-map` (or extend one via `/wr-itil:manage-story-map`), then re-invoke capture-story with `--story-map STORY-MAP-<NNN>`.
|
|
162
|
+
|
|
163
|
+
AFK orchestrators author the map first (born `human-oversight: unconfirmed` per ADR-090, drained later), then capture the story onto it — nothing halts silently; the map is a prerequisite step.
|
|
164
|
+
- **`--story-map` present**: for each `STORY-MAP-<NNN>`, existence check `ls docs/story-maps/*/STORY-MAP-<NNN>-*.html 2>/dev/null`. Malformed or unresolved → hard-block (`reason: unresolved-story-map-trace`). Lifecycle advisory-warn on `draft` / `in-progress` maps; pass silently on `accepted` / `completed`.
|
|
165
|
+
- **Bootstrap exemption (ADR-060 A4 / ADR-053)**: a capture carrying the inline `<!-- bootstrap-exempt: ... -->` marker bypasses the I8 hard-block for migration stories ONLY. A **non-bootstrap** capture carrying the marker is rejected (the marker is not a general capture-time escape hatch — behavioural test asserts).
|
|
166
|
+
|
|
167
|
+
`--rfc` stays **OPTIONAL** at capture (I7 enforces at the `accepted` transition — a story can legitimately precede its RFC firming up):
|
|
159
168
|
|
|
160
169
|
```bash
|
|
161
170
|
rfc_file=$(ls docs/rfcs/RFC-<NNN>-*.md 2>/dev/null | head -1)
|
|
162
171
|
[ -z "$rfc_file" ] && unresolved_rfcs+=("RFC-<NNN>")
|
|
163
172
|
```
|
|
164
173
|
|
|
165
|
-
-
|
|
166
|
-
- Token present but malformed (`--rfc RFC-99`, etc.) OR resolves to no file: hard-block. Emit deny log with `reason: unresolved-rfc-trace`. The optional-vs-malformed distinction is load-bearing — absence is permitted, malformed input is not.
|
|
167
|
-
|
|
168
|
-
Same shape for `--story-map`. Story-maps are HTML; existence check uses `ls docs/story-maps/*/STORY-MAP-<NNN>-*.html 2>/dev/null`.
|
|
169
|
-
|
|
170
|
-
Lifecycle classification on resolved files: advisory-warn on `proposed` / `draft` / `in-progress` states (the design isn't firm yet — captured story will reference work that may itself drift); pass silently on `accepted` / `closed` / `verifying` states.
|
|
174
|
+
- `--rfc` token absent: skip (the optional path). Present-but-malformed OR resolves to no file: hard-block (`reason: unresolved-rfc-trace`).
|
|
171
175
|
|
|
172
176
|
### 3. Compute next STORY ID
|
|
173
177
|
|
|
@@ -200,7 +204,7 @@ decision-makers: [<git config user.name>]
|
|
|
200
204
|
problems: [P<NNN>, P<NNN>, ...]
|
|
201
205
|
jtbd: [JTBD-<NNN>, JTBD-<NNN>, ...]
|
|
202
206
|
rfcs: [<RFC-<NNN>, ...> or empty]
|
|
203
|
-
story-maps: [<STORY-MAP-<NNN>, ...>
|
|
207
|
+
story-maps: [<STORY-MAP-<NNN>, ...>] # >=1 REQUIRED at capture (I8 / ADR-095)
|
|
204
208
|
estimated-effort: <S|M|L|XL — derived at capture per ADR-067 (real best-effort value, no deferral marker)>
|
|
205
209
|
human-oversight: unconfirmed
|
|
206
210
|
---
|
|
@@ -212,16 +216,16 @@ human-oversight: unconfirmed
|
|
|
212
216
|
**Problems**: <P<NNN> [, P<NNN>, ...]>
|
|
213
217
|
**JTBD**: <JTBD-<NNN> [, ...]>
|
|
214
218
|
**RFCs**: <RFC-<NNN> [, ...]> or (none — populate at accepted transition per I7)
|
|
215
|
-
**Story Maps**: <STORY-MAP-<NNN> [, ...]>
|
|
219
|
+
**Story Maps**: <STORY-MAP-<NNN> [, ...]> (>=1 required at capture — I8 / ADR-095)
|
|
216
220
|
**Estimated effort**: <S|M|L|XL> — derived at capture as a real best-effort value (P375 / ADR-032 amendment 2026-06-24; ADR-067 silent-derivation). NO `deferred` default and no "not estimated" marker — those are deferrals (user correction 2026-06-24). Confirmed/refined at the accepted transition per I10 INVEST Estimable.
|
|
217
221
|
|
|
218
222
|
## User value (required, INVEST Valuable)
|
|
219
223
|
|
|
220
|
-
|
|
224
|
+
<REQUIRED at capture (I10 Valuable subset / ADR-095): a real one-paragraph value-first user-facing statement — NOT a placeholder. "In order to <value>, as a <persona>, I want <capability>.">
|
|
221
225
|
|
|
222
226
|
## Acceptance criteria (accepted-gate, INVEST Testable)
|
|
223
227
|
|
|
224
|
-
- [ ]
|
|
228
|
+
- [ ] <REQUIRED at capture (I10 Testable subset / ADR-095): >=1 real observable behavioural acceptance criterion — NOT a placeholder>
|
|
225
229
|
|
|
226
230
|
## Driving problem trace (required — I6 invariant)
|
|
227
231
|
|
|
@@ -335,7 +339,7 @@ The trailing pointer is **not optional** — it is the user-visible signal that
|
|
|
335
339
|
| Problem-trace I6 enforcement | Re-validated at every lifecycle transition | Hard-block at capture-time; deny logged to `logs/story-capture-denials.jsonl` |
|
|
336
340
|
| JTBD-trace I9 enforcement | Re-validated at every lifecycle transition | Hard-block at capture-time |
|
|
337
341
|
| RFC-trace I7 enforcement | Hard-block at `accepted` transition (allows draft stories to exist before RFC reference firms up) | Advisory-warn at capture-time if `--rfc` provided and resolves to draft/proposed lifecycle |
|
|
338
|
-
| Story-map-trace I8 enforcement | Hard-block
|
|
342
|
+
| Story-map-trace I8 enforcement | Hard-block AT CAPTURE — refuse-and-route to `/wr-itil:capture-story-map` if absent (ADR-095) | direction-setting (caller must supply or author a map first) |
|
|
339
343
|
| INVEST shape (I10) | Behavioural checks at `accepted` transition | Out of scope: capture produces a skeleton with deferred-placeholder sections |
|
|
340
344
|
| Skeleton-fill | Full-intake; AskUserQuestion for User value + Acceptance criteria + Estimated effort | Deferred-placeholder pattern; one optional taste prompt only |
|
|
341
345
|
| Status transitions | Step 7 owns draft → accepted → in-progress → done | Out of scope (creation only) |
|
|
@@ -373,4 +377,4 @@ The two skills share the `/tmp/wr-itil-story-capture-grep-${SESSION_ID}` create-
|
|
|
373
377
|
|
|
374
378
|
## Phase-out-of-order note
|
|
375
379
|
|
|
376
|
-
This skill ships BEFORE `/wr-itil:capture-story-map` (Slice 3 of P170 Phase 2) due to the voice-tone-hook-on-HTML blocker documented at P170 line 297. Building capture-story first
|
|
380
|
+
This skill ships BEFORE `/wr-itil:capture-story-map` (Slice 3 of P170 Phase 2) due to the voice-tone-hook-on-HTML blocker documented at P170 line 297. Building capture-story first was structurally permitted at the time (story-map traces were then optional at capture). **Superseded by ADR-095** — I8 now hard-blocks at capture, and `/wr-itil:capture-story-map` has since shipped, so a map must exist before a story is captured. When Slices 3-6 eventually ship the story-map skills, `manage-story <NNN> accepted` will validate the I8 invariant against the then-existing story-map corpus. The deviation from ADR-060's recommended commit-grain order (line 449-454 — sub-slice 3 story-map skills then sub-slice 4 story skills) is auditable here and in this commit's Slice 7 commit message.
|
|
@@ -197,7 +197,7 @@ decision-makers: [Test]
|
|
|
197
197
|
problems: [P170]
|
|
198
198
|
jtbd: [JTBD-008]
|
|
199
199
|
rfcs: []
|
|
200
|
-
story-maps: []
|
|
200
|
+
story-maps: [STORY-MAP-001]
|
|
201
201
|
estimated-effort: M
|
|
202
202
|
---
|
|
203
203
|
|
|
@@ -212,6 +212,27 @@ EOF
|
|
|
212
212
|
done
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
@test "capture-story: I8-at-capture invariant — a valid captured story has a NON-EMPTY story-maps: (ADR-095)" {
|
|
216
|
+
mkdir -p docs/stories/draft
|
|
217
|
+
cat > docs/stories/draft/STORY-001-mapped.md <<EOF
|
|
218
|
+
---
|
|
219
|
+
status: draft
|
|
220
|
+
story-maps: [STORY-MAP-002]
|
|
221
|
+
---
|
|
222
|
+
EOF
|
|
223
|
+
run grep -E '^story-maps: \[STORY-MAP-[0-9]' docs/stories/draft/STORY-001-mapped.md
|
|
224
|
+
[ "$status" -eq 0 ]
|
|
225
|
+
run grep -E '^story-maps: \[\]' docs/stories/draft/STORY-001-mapped.md
|
|
226
|
+
[ "$status" -ne 0 ]
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@test "capture-story: SKILL prescribes refuse-and-route on a missing story-map trace (I8/ADR-095)" {
|
|
230
|
+
run grep -E 'I8 hard-block AT CAPTURE|refuse-and-route|missing-story-map-trace' "$SKILL_FILE"
|
|
231
|
+
[ "$status" -eq 0 ]
|
|
232
|
+
run grep -E 'story-map traces are optional at capture' "$SKILL_FILE"
|
|
233
|
+
[ "$status" -ne 0 ]
|
|
234
|
+
}
|
|
235
|
+
|
|
215
236
|
@test "capture-story: captured story file lands at docs/stories/draft/ per ADR-060 lines 145-147" {
|
|
216
237
|
# Story-maps use HTML and lifecycle subdirs (draft/accepted/in-progress/
|
|
217
238
|
# completed/archived); stories use markdown and lifecycle subdirs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wr-itil:manage-story
|
|
3
|
-
description: Heavyweight story intake + lifecycle management following ADR-060 Phase 2. Creates and updates story tickets, transitions through draft → accepted → in-progress → done → archived lifecycle, enforces I7 + I8 trace-gate at the accepted transition, runs INVEST checks per I10 at acceptance, auto-
|
|
3
|
+
description: Heavyweight story intake + lifecycle management following ADR-060 Phase 2. Creates and updates story tickets, transitions through draft → accepted → in-progress → done → archived lifecycle, enforces I7 + I8 trace-gate at the accepted transition, runs INVEST checks per I10 at acceptance, auto-detects accepted→in-progress on the first implementing commit and in-progress→done on all-criteria-ticked + linked RFC closes (per ADR-096 a draft story is NEVER implementable — draft→in-progress is removed; the itil-no-implement-draft-gate blocks implementing a draft story), and refreshes docs/stories/README.md per the P062 / P094 contract pattern. Companion to /wr-itil:capture-story (lightweight aside surface).
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -117,7 +117,7 @@ For any transition `<from> → <to>`:
|
|
|
117
117
|
|
|
118
118
|
The auto-transition logic fires in two contexts:
|
|
119
119
|
|
|
120
|
-
- **`
|
|
120
|
+
- **`accepted → in-progress`**: when the FIRST implementing commit lands with a `Refs: STORY-<NNN>` trailer AND a commit subject NOT prefixed with `feat(itil): capture STORY-`, on a story that is already `accepted`. **Per ADR-096 a `draft` story is NEVER implementable — there is NO `draft → in-progress` transition**; the `itil-no-implement-draft-gate` PreToolUse hook blocks a commit referencing a draft story (accept it first). **Detected by `itil-commit-trailer-transition-advisory.sh`** (PostToolUse:Bash; P378/RFC-030 Piece 2). Per ADR-014 the hook DETECTS + emits a stderr advisory; it does NOT perform the `git mv` (that would land outside the commit grain). The transition is performed by `manage-story <NNN> in-progress` (run on the advisory) or by an AFK orchestrator acting on it.
|
|
121
121
|
|
|
122
122
|
- **`in-progress → done`**: when all `- [ ]` lines in `## Acceptance criteria` are ticked AND the linked RFC is `closed`. Detected at manage-rfc close-fire (the RFC's transition triggers a sweep of its `stories:` array; each in-progress story with all-criteria-ticked auto-transitions to `done`). Manual `manage-story <NNN> done` invocation works in the interim.
|
|
123
123
|
|
|
@@ -229,7 +229,7 @@ After commit, report:
|
|
|
229
229
|
| I10 INVEST shape | **Primary surface** — fires at `manage-story <NNN> accepted` | Out of scope (capture produces skeleton) |
|
|
230
230
|
| Status transitions | Owns draft → accepted → in-progress → done → archived | Out of scope (creation only) |
|
|
231
231
|
| README refresh | Inline per transition (P094 mirror) | Deferred to `manage-story review` or `wr-itil-reconcile-stories` |
|
|
232
|
-
| Auto-transition triggers | Fires on first
|
|
232
|
+
| Auto-transition triggers | Fires on first implementing commit against an ACCEPTED story (accepted→in-progress; draft→in-progress REMOVED per ADR-096) + criteria-ticked + RFC-closed (in-progress→done) | n/a |
|
|
233
233
|
| Reverse-trace refresh on parents | Inline per transition | Inline per capture |
|
|
234
234
|
| Commit grain | One commit per transition / per intake | One commit per capture |
|
|
235
235
|
|
|
@@ -238,7 +238,7 @@ After commit, report:
|
|
|
238
238
|
- **ADR-060** — Problem-RFC-Story framework + Phase 2 amendment 2026-05-10 (story tier).
|
|
239
239
|
- **ADR-060 lines 248-253** — I6-I11 story-tier invariants.
|
|
240
240
|
- **ADR-060 line 252** — I10 INVEST shape (Testable/Valuable/Independent/Estimable; Small SHOULD per architect-amendment-2026-05-10 nitpick N3).
|
|
241
|
-
- **ADR-060 line 292** — auto-transition triggers (draft→in-progress on first
|
|
241
|
+
- **ADR-060 line 292** — auto-transition triggers (**amended by ADR-096**: draft→in-progress REMOVED — a draft story is blocked from implementation by the itil-no-implement-draft-gate; accepted→in-progress on the first implementing commit; in-progress→done on criteria-ticked + RFC-closed).
|
|
242
242
|
- **ADR-060 line 339 + ADR-053 Bootstrapping precedent** — bootstrap-exemption marker contract for STORY-MAP-001 migration retrofit.
|
|
243
243
|
- **P170** — driver problem ticket.
|
|
244
244
|
- **JTBD-008** — Decompose a Fix Into Coordinated Changes. Primary persona-anchor.
|
|
@@ -81,7 +81,7 @@ When a single ticket is the strict top, rungs 2-5 are not consulted. When multip
|
|
|
81
81
|
Invoke `/wr-itil:manage-problem <NNN>` via the Skill tool with the selected ticket's ID as the argument. The delegated skill runs the full Working a Problem flow appropriate to the ticket's status:
|
|
82
82
|
|
|
83
83
|
- **Open ticket**: investigate root cause; document findings; create reproduction test; identify workaround; auto-transition to Known Error when root cause + workaround are documented; if the fix is small, proceed straight into implementation.
|
|
84
|
-
- **Known Error**: traverse problem `## Fix Strategy` → referenced RFCs → each RFC's frontmatter `stories:` array (ordered per ADR-060 line 259) → pick first not-done story (status `accepted` or `in-progress`, skipping `done`
|
|
84
|
+
- **Known Error**: traverse problem `## Fix Strategy` → referenced RFCs → each RFC's frontmatter `stories:` array (ordered per ADR-060 line 259) → pick first not-done story (status `accepted` or `in-progress`, skipping `done`). **Per ADR-096 a `draft` story is NOT silently skipped**: if the first not-done story is `draft`, refuse-and-route — surface `accept STORY-<NNN> via /wr-itil:manage-story <NNN> accepted first (it runs the INVEST + RFC-trace gates + ratification)` and (under AFK) queue it to `outstanding_questions` rather than skipping past it or implementing it (the itil-no-implement-draft-gate blocks the implementing commit regardless) → implement the picked story scope. An empty `stories: []` is a legacy/back-fill state (per **ADR-089** every RFC has ≥1 story — the empty-stories atomic fallback is removed): back-fill a story onto the RFC, don't fall back to a per-RFC atomic dispatch. No-RFC Fix Strategy (Phase 1-shape legacy problems) falls back to direct fix implementation. Each commit carries `Refs: STORY-<NNN>` (or `Refs: RFC-<NNN>` for cross-cutting RFC work with no single story / `Refs: P<NNN>` for legacy direct path). Stories auto-transition `accepted → in-progress` on the first implementing commit (draft → in-progress REMOVED per ADR-096 — a draft story is blocked from implementation); auto-transition `in-progress → done` on all-criteria-ticked + linked RFC closed. When all stories under all referenced RFCs are done, include the Known Error → Verification Pending `git mv` in the final commit per ADR-022. The full traversal contract lives in `/wr-itil:manage-problem` § Working a Problem → Known Error subsection (post-Phase-2 rewrite per ADR-060 lines 300-320).
|
|
85
85
|
|
|
86
86
|
**Why delegate rather than re-implement:** the full investigation / transition / fix / release pipeline is a long-lived, policy-governed flow that must stay on a single authoritative workflow. Re-hosting it on a sibling skill would fork the ownership contract and compound maintenance cost. The split skill (this file) owns the *selection* of the next ticket; `/wr-itil:manage-problem <NNN>` owns the *execution*.
|
|
87
87
|
|