@windyroad/itil 0.27.1 → 0.28.0-preview.304
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/README.md +9 -1
- package/bin/wr-itil-reconcile-stories +2 -0
- package/bin/wr-itil-reconcile-story-maps +2 -0
- package/hooks/hooks.json +4 -0
- package/hooks/itil-readme-refresh-discipline.sh +118 -0
- package/hooks/lib/readme-refresh-detect.sh +161 -0
- package/hooks/test/itil-readme-refresh-discipline.bats +261 -0
- package/lib/migrate-problems-layout.sh +128 -0
- package/package.json +1 -1
- package/scripts/reconcile-stories.sh +236 -0
- package/scripts/reconcile-story-maps.sh +98 -0
- package/scripts/test/reconcile-stories.bats +173 -0
- package/scripts/test/reconcile-story-maps.bats +74 -0
- package/scripts/test/rfc-stories-extension.bats +173 -0
- package/scripts/test/update-problem-references-section.bats +195 -0
- package/scripts/test/update-references-section-sibling-helpers.bats +80 -0
- package/scripts/test/working-the-problem-traversal.bats +109 -0
- package/scripts/update-jtbd-references-section.sh +131 -0
- package/scripts/update-problem-references-section.sh +284 -0
- package/scripts/update-rfc-references-section.sh +152 -0
- package/scripts/update-story-references-section.sh +128 -0
- package/skills/capture-rfc/SKILL.md +28 -3
- package/skills/capture-story/SKILL.md +373 -0
- package/skills/capture-story/test/capture-story-behavioural.bats +227 -0
- package/skills/capture-story-map/SKILL.md +229 -0
- package/skills/capture-story-map/test/capture-story-map-behavioural.bats +98 -0
- package/skills/list-stories/SKILL.md +151 -0
- package/skills/list-stories/test/list-stories-contract.bats +127 -0
- package/skills/list-story-maps/SKILL.md +93 -0
- package/skills/list-story-maps/test/list-story-maps-contract.bats +46 -0
- package/skills/manage-problem/SKILL.md +42 -4
- package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +53 -0
- package/skills/manage-rfc/SKILL.md +12 -0
- package/skills/manage-story/SKILL.md +242 -0
- package/skills/manage-story/test/manage-story-contract.bats +171 -0
- package/skills/manage-story-map/SKILL.md +158 -0
- package/skills/manage-story-map/test/manage-story-map-contract.bats +63 -0
- package/skills/reconcile-stories/SKILL.md +110 -0
- package/skills/reconcile-story-maps/SKILL.md +70 -0
- package/skills/work-problem/SKILL.md +1 -1
- package/skills/work-problems/SKILL.md +25 -0
- package/skills/work-problems/test/work-problems-auto-migrate-step.bats +57 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-itil:list-stories
|
|
3
|
+
description: List INVEST-shaped story tickets from docs/stories/ as a markdown table. Read-only display — no edits, no interaction. Optional `--rfc RFC-<NNN>` filter to surface a specific RFC's ordered story list per ADR-060 Phase 2.
|
|
4
|
+
allowed-tools: Read, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# List Stories
|
|
8
|
+
|
|
9
|
+
Display the story corpus from `docs/stories/` as a markdown table. Read-only view of the story tier per ADR-060 Phase 2; this skill does not edit, transition, close, or create stories. For those operations, use the dedicated skills (`/wr-itil:capture-story`, `/wr-itil:manage-story`).
|
|
10
|
+
|
|
11
|
+
Mirrors the `/wr-itil:list-problems` precedent (P071 phased-landing split per ADR-010 amended Skill Granularity rule: one skill per distinct user intent). The list-stories surface separates the read-only view from the heavyweight `/wr-itil:manage-story list` subcommand route (which is itself a candidate for phased-landing split in a future slice).
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
Stories live under `docs/stories/<state>/STORY-<NNN>-<slug>.md` in lifecycle subdirectories:
|
|
16
|
+
|
|
17
|
+
- `docs/stories/draft/*.md` — draft (captured via `/wr-itil:capture-story`; pre-INVEST-acceptance)
|
|
18
|
+
- `docs/stories/accepted/*.md` — accepted (INVEST-shape verified per I10; pre-implementation)
|
|
19
|
+
- `docs/stories/in-progress/*.md` — in-progress (implementation underway; auto-transitioned from accepted on first `Refs: STORY-<NNN>` commit AFTER the capture commit per ADR-060 line 292)
|
|
20
|
+
- `docs/stories/done/*.md` — done (acceptance-criteria all-ticked + linked RFC closes; auto-transitioned from in-progress)
|
|
21
|
+
- `docs/stories/archived/*.md` — archived (closed without completion; manual transition)
|
|
22
|
+
|
|
23
|
+
Per ADR-060 I11 invariant (Phase 2 deferred): stories MUST NOT carry a WSJF field. Ordering inside an RFC is per the RFC's frontmatter `stories: [STORY-<NNN>, ...]` array (ordered = execution sequence per ADR-060 line 259), NOT per any per-story WSJF.
|
|
24
|
+
|
|
25
|
+
## Argument grammar
|
|
26
|
+
|
|
27
|
+
**Positional (optional)**: `--rfc RFC-<NNN>` flag-style filter. When provided, the display lists ONLY stories that trace to the named RFC, IN THE ORDER specified by the RFC's frontmatter `stories:` array per ADR-060 line 259. Without the flag, all stories across all lifecycle states are listed grouped by state.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
/wr-itil:list-stories # All stories, grouped by lifecycle state
|
|
31
|
+
/wr-itil:list-stories --rfc RFC-002 # Only stories under RFC-002, in execution order
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Steps
|
|
35
|
+
|
|
36
|
+
### 1. Check `docs/stories/README.md` cache freshness
|
|
37
|
+
|
|
38
|
+
Reuse the same `git log`-based freshness test as `/wr-itil:list-problems` Step 1 (per P031 — filesystem mtime is unreliable in worktrees and fresh checkouts):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
readme_commit=$(git log -1 --format=%H -- docs/stories/README.md 2>/dev/null)
|
|
42
|
+
if [ -z "$readme_commit" ] || \
|
|
43
|
+
git log --oneline "${readme_commit}..HEAD" -- 'docs/stories/*/*.md' ':!docs/stories/README.md' 2>/dev/null | grep -q .; then
|
|
44
|
+
echo "stale"
|
|
45
|
+
fi
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Cache fresh** (no output AND no `--rfc` filter): read `docs/stories/README.md` directly — display the Story Rankings section + Done section as-is. Note in the output: "Using cached ranking from [timestamp in README.md]".
|
|
49
|
+
|
|
50
|
+
**Cache stale OR `--rfc` filter provided OR `README.md` missing**: run the live scan in Step 2. (The filter case always live-scans because the README cache is whole-corpus ranking, not a per-RFC filtered view.)
|
|
51
|
+
|
|
52
|
+
### 2. Live scan (cache-stale fallback OR filter mode)
|
|
53
|
+
|
|
54
|
+
**Unfiltered live scan** — enumerate every state directory:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
ls docs/stories/draft/*.md docs/stories/accepted/*.md docs/stories/in-progress/*.md docs/stories/done/*.md docs/stories/archived/*.md 2>/dev/null
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For each story file, parse the YAML frontmatter to extract: `story-id`, `status`, `problems`, `jtbd`, `rfcs`, `story-maps`, `estimated-effort`. Read the H1 line for the title.
|
|
61
|
+
|
|
62
|
+
**Filtered live scan** (`--rfc RFC-<NNN>` provided) — resolve the RFC file first, then enumerate its ordered `stories:` array:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
rfc_file=$(ls docs/rfcs/RFC-<NNN>-*.md 2>/dev/null | head -1)
|
|
66
|
+
[ -z "$rfc_file" ] && echo "RFC-<NNN> not found" >&2 && exit 1
|
|
67
|
+
|
|
68
|
+
# Extract the ordered stories: array from RFC frontmatter
|
|
69
|
+
# (Phase 2 Slice 11 extension — see ADR-060 RFC frontmatter extension)
|
|
70
|
+
stories_list=$(awk '/^stories:/,/^[a-z]/' "$rfc_file" | grep -oE 'STORY-[0-9]+')
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For each `STORY-<NNN>` in the ordered list, resolve to a file under `docs/stories/*/STORY-<NNN>-*.md` and parse the frontmatter as above. Preserve the RFC's array ordering in the output.
|
|
74
|
+
|
|
75
|
+
### 3. Display
|
|
76
|
+
|
|
77
|
+
**Unfiltered mode** — render lifecycle-grouped sections:
|
|
78
|
+
|
|
79
|
+
```markdown
|
|
80
|
+
## Draft
|
|
81
|
+
|
|
82
|
+
| ID | Title | Problems | JTBD | RFCs | Story Maps |
|
|
83
|
+
|----|-------|----------|------|------|------------|
|
|
84
|
+
| STORY-<NNN> | <title> | <P<NNN>...> | <JTBD-<NNN>...> | <RFC-<NNN>...> | <STORY-MAP-<NNN>...> |
|
|
85
|
+
|
|
86
|
+
## Accepted
|
|
87
|
+
|
|
88
|
+
| ID | Title | Problems | JTBD | RFCs | Story Maps | Effort |
|
|
89
|
+
|----|-------|----------|------|------|------------|--------|
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
## In Progress
|
|
93
|
+
|
|
94
|
+
(same shape as Accepted)
|
|
95
|
+
|
|
96
|
+
## Done
|
|
97
|
+
|
|
98
|
+
| ID | Title | Done date | Driving problems |
|
|
99
|
+
|----|-------|-----------|------------------|
|
|
100
|
+
...
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Omit empty sections rather than rendering empty headers. The Estimated Effort column is omitted from the Draft section because effort is deferred at capture and only required at accepted per I10 INVEST Estimable.
|
|
104
|
+
|
|
105
|
+
**Filtered mode** (`--rfc RFC-<NNN>`) — render a single ordered table:
|
|
106
|
+
|
|
107
|
+
```markdown
|
|
108
|
+
## Stories under RFC-<NNN>
|
|
109
|
+
|
|
110
|
+
(In execution order per RFC frontmatter `stories:` array.)
|
|
111
|
+
|
|
112
|
+
| Order | ID | Title | Status | Effort |
|
|
113
|
+
|-------|----|-------|--------|--------|
|
|
114
|
+
| 1 | STORY-<NNN> | <title> | <status> | <effort> |
|
|
115
|
+
| 2 | STORY-<NNN> | <title> | <status> | <effort> |
|
|
116
|
+
...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The Order column makes the execution sequence visible — critical for the working-the-problem flow per ADR-060 line 314 ("read frontmatter `stories:` array (ordered) → pick first not-done story").
|
|
120
|
+
|
|
121
|
+
### 4. Trailing suggestions
|
|
122
|
+
|
|
123
|
+
After the table(s), print one short pointer depending on output:
|
|
124
|
+
|
|
125
|
+
- **Filtered mode + first not-done story exists**: `Run /wr-itil:work-problem to advance the next story under RFC-<NNN> (STORY-<NNN> — <title>).` (Note: working-the-problem traversal per ADR-060 line 300-320 lands in Slice 13.)
|
|
126
|
+
- **Filtered mode + all stories done**: `All stories under RFC-<NNN> are done. Run /wr-itil:manage-rfc <RFC-<NNN>> verifying to transition the RFC.`
|
|
127
|
+
- **Unfiltered mode + Draft section non-empty**: `Run /wr-itil:manage-story <STORY-<NNN>> accepted to advance a draft through INVEST gates.`
|
|
128
|
+
- **Unfiltered mode + only Done section non-empty**: `No active stories. Run /wr-itil:capture-story to draft a new story.`
|
|
129
|
+
|
|
130
|
+
## Ownership boundary
|
|
131
|
+
|
|
132
|
+
`list-stories` does not modify, rename, or commit any files. If the README.md cache is stale, list-stories performs a live scan but does NOT rewrite `docs/stories/README.md` — refreshing the cache is `/wr-itil:manage-story review`'s ownership (Slice 8 lands the review surface). The trailing-suggestion pointer surfaces this boundary.
|
|
133
|
+
|
|
134
|
+
## Related
|
|
135
|
+
|
|
136
|
+
- **ADR-060** — Problem-RFC-Story framework + Phase 2 amendment 2026-05-12 (story tier).
|
|
137
|
+
- **ADR-060 line 259** — RFC frontmatter `stories:` ORDERED array (execution sequence).
|
|
138
|
+
- **ADR-060 line 294** — `/wr-itil:list-stories` skill description with `--rfc RFC-<NNN>` filter.
|
|
139
|
+
- **ADR-060 lines 300-320** — working-the-problem flow (Slice 13 lands the traversal).
|
|
140
|
+
- **ADR-060 line 253** — I11 no-WSJF-leak invariant (Phase 2).
|
|
141
|
+
- **P071** — phased-landing split precedent (list-problems split from manage-problem list).
|
|
142
|
+
- **ADR-010 amended** — Skill Granularity rule.
|
|
143
|
+
- **ADR-022** — lifecycle conventions (story lifecycle mirrors problem lifecycle).
|
|
144
|
+
- **ADR-037** — contract-assertion bats pattern.
|
|
145
|
+
- **P031** — git-history freshness check rationale.
|
|
146
|
+
- **JTBD-008** — Decompose a Fix Into Coordinated Changes. The list view supports the working-the-problem flow that operationalises JTBD-008's "first-class entity" Desired Outcome.
|
|
147
|
+
- **JTBD-006** — Progress the Backlog While I'm Away. Filtered mode (`--rfc`) feeds the AFK orchestrator's per-RFC iter dispatch (Slice 13).
|
|
148
|
+
- `packages/itil/skills/list-problems/SKILL.md` — direct precedent shape.
|
|
149
|
+
- `packages/itil/skills/list-incidents/SKILL.md` — sibling list-* skill at the incident tier.
|
|
150
|
+
|
|
151
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Behavioural contract fixtures for /wr-itil:list-stories (P170 Phase 2 Slice 10).
|
|
3
|
+
#
|
|
4
|
+
# Per ADR-037 + ADR-052: behavioural tests over structural prose-grep.
|
|
5
|
+
# These tests exercise the read-only display surface contract — read
|
|
6
|
+
# story files, render markdown tables, no edits.
|
|
7
|
+
#
|
|
8
|
+
# Behavioural surfaces under test:
|
|
9
|
+
# 1. SKILL.md presence + canonical name (minimum discoverability).
|
|
10
|
+
# 2. Read-only contract — no Write/Edit tools in frontmatter.
|
|
11
|
+
# 3. Lifecycle enumeration — list-stories enumerates all five state
|
|
12
|
+
# subdirectories (draft / accepted / in-progress / done / archived).
|
|
13
|
+
# 4. RFC-filter ordering — when --rfc filter is provided, ordering
|
|
14
|
+
# follows the RFC frontmatter `stories:` array, not lexical /
|
|
15
|
+
# filesystem order.
|
|
16
|
+
#
|
|
17
|
+
# @problem P170
|
|
18
|
+
# @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes — list view
|
|
19
|
+
# supports the working-the-problem flow's per-RFC iter
|
|
20
|
+
# dispatch in Slice 13)
|
|
21
|
+
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — filtered mode
|
|
22
|
+
# feeds the AFK orchestrator)
|
|
23
|
+
# @adr ADR-060 (Problem-RFC-Story framework — story tier line 294
|
|
24
|
+
# list-stories description)
|
|
25
|
+
# @adr ADR-037 (Skill testing strategy — contract bats)
|
|
26
|
+
# @adr ADR-052 (Behavioural-tests-default)
|
|
27
|
+
# @adr ADR-010 (Amended skill granularity — phased-landing split
|
|
28
|
+
# precedent from list-problems / P071)
|
|
29
|
+
|
|
30
|
+
setup() {
|
|
31
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
32
|
+
SKILL_FILE="${REPO_ROOT}/packages/itil/skills/list-stories/SKILL.md"
|
|
33
|
+
|
|
34
|
+
TMPROOT=$(mktemp -d)
|
|
35
|
+
ORIG_DIR="$PWD"
|
|
36
|
+
cd "$TMPROOT"
|
|
37
|
+
# Set up minimal story corpus fixture
|
|
38
|
+
mkdir -p docs/stories/draft docs/stories/accepted docs/stories/in-progress docs/stories/done docs/stories/archived
|
|
39
|
+
mkdir -p docs/rfcs
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
teardown() {
|
|
43
|
+
cd "$ORIG_DIR"
|
|
44
|
+
rm -rf "$TMPROOT"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# Surface 0: SKILL.md exists with correct name (minimum discoverability)
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
@test "list-stories: SKILL.md exists" {
|
|
52
|
+
[ -f "$SKILL_FILE" ]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@test "list-stories: SKILL.md frontmatter declares wr-itil:list-stories name" {
|
|
56
|
+
run grep -E '^name: wr-itil:list-stories$' "$SKILL_FILE"
|
|
57
|
+
[ "$status" -eq 0 ]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Surface 1: Read-only contract — no Write/Edit in allowed-tools
|
|
62
|
+
# (ADR-010 phased-landing split rule: list-* skills are pure read views)
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
@test "list-stories: SKILL.md allowed-tools does NOT include Write or Edit" {
|
|
66
|
+
# Behavioural: extract the allowed-tools line and check it omits the
|
|
67
|
+
# write tools. The list-* family is read-only by contract per ADR-010.
|
|
68
|
+
run grep '^allowed-tools:' "$SKILL_FILE"
|
|
69
|
+
[ "$status" -eq 0 ]
|
|
70
|
+
[[ "$output" != *"Write"* ]]
|
|
71
|
+
[[ "$output" != *"Edit"* ]]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Surface 2: Lifecycle enumeration — all 5 state subdirectories
|
|
76
|
+
# (story lifecycle per ADR-060 mirrors problem lifecycle ADR-022)
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
@test "list-stories: SKILL.md names all 5 lifecycle state subdirectories" {
|
|
80
|
+
# The Scope section enumerates the lifecycle states. Without all five,
|
|
81
|
+
# the list view would miss stories in some lifecycle state silently —
|
|
82
|
+
# a behavioural defect not a prose preference.
|
|
83
|
+
for state in draft accepted in-progress done archived; do
|
|
84
|
+
run grep -E "docs/stories/${state}" "$SKILL_FILE"
|
|
85
|
+
[ "$status" -eq 0 ]
|
|
86
|
+
done
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# Surface 3: --rfc filter ordering follows RFC frontmatter `stories:` array
|
|
91
|
+
# (load-bearing for the working-the-problem flow per ADR-060 line 314)
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
@test "list-stories: --rfc filter mode enumerates stories via RFC frontmatter stories array" {
|
|
95
|
+
# The SKILL.md filter-mode pseudocode reads the RFC's frontmatter and
|
|
96
|
+
# extracts STORY-NNN tokens in order. This is the load-bearing
|
|
97
|
+
# behaviour for Slice 13's working-the-problem traversal — verify
|
|
98
|
+
# the SKILL prescribes RFC-frontmatter-driven order, not filesystem
|
|
99
|
+
# / lexical order.
|
|
100
|
+
run grep -E 'stories_list.*RFC|stories: array|RFC frontmatter' "$SKILL_FILE"
|
|
101
|
+
[ "$status" -eq 0 ]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# Surface 4: Cache-freshness check pattern — same git log shape as list-problems
|
|
106
|
+
# (P031: filesystem mtime unreliable in worktrees; git history is authoritative)
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
@test "list-stories: SKILL.md uses git log cache-freshness pattern per P031" {
|
|
110
|
+
run grep -E 'git log -1 --format=%H -- docs/stories/README\.md' "$SKILL_FILE"
|
|
111
|
+
[ "$status" -eq 0 ]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
# Surface 5: No-WSJF-leak — I11 invariant
|
|
116
|
+
# (ADR-060 line 253: stories MUST NOT carry a WSJF field in Phase 2)
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
@test "list-stories: SKILL.md does NOT render a WSJF column (I11 invariant)" {
|
|
120
|
+
# Phase 2 invariant: no story-level WSJF. The display tables must
|
|
121
|
+
# not include a WSJF column header. This is a behavioural contract
|
|
122
|
+
# at the SKILL output surface, not a prose-grep.
|
|
123
|
+
# The output tables specified in SKILL.md should have NO 'WSJF' header.
|
|
124
|
+
# Find any markdown table header line and verify none include WSJF.
|
|
125
|
+
run grep -E '^\| WSJF\b|\| WSJF \|' "$SKILL_FILE"
|
|
126
|
+
[ "$status" -ne 0 ]
|
|
127
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-itil:list-story-maps
|
|
3
|
+
description: List story-map artefacts from docs/story-maps/ as a markdown table. Read-only display — no edits, no interaction. Renders <meta> block data (problems / rfcs / jtbd / status) from each HTML map per ADR-060 § Phase 2 encoding amendment 2026-05-12.
|
|
4
|
+
allowed-tools: Read, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# List Story Maps
|
|
8
|
+
|
|
9
|
+
Display the story-map corpus from `docs/story-maps/` as a markdown table. Read-only view per ADR-060 Phase 2; does not edit, transition, or create maps.
|
|
10
|
+
|
|
11
|
+
Mirrors `/wr-itil:list-stories` precedent (P071 phased-landing split per ADR-010). I5 invariant: story-maps MUST NOT carry WSJF (no Story Rankings table — maps are planning artefacts, not work items per ADR-060 line 145).
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
Story-maps live under `docs/story-maps/<state>/STORY-MAP-<NNN>-<slug>.html`:
|
|
16
|
+
- `draft/` — captured (problem + JTBD traces present); pre-acceptance authoring
|
|
17
|
+
- `accepted/` — backbone/ribs/slices authored; ready for implementation dispatch
|
|
18
|
+
- `in-progress/` — slices being implemented; stories transitioning
|
|
19
|
+
- `completed/` — all slices done
|
|
20
|
+
- `archived/` — closed without completion
|
|
21
|
+
|
|
22
|
+
## Argument grammar
|
|
23
|
+
|
|
24
|
+
No arguments (read-only display).
|
|
25
|
+
|
|
26
|
+
## Steps
|
|
27
|
+
|
|
28
|
+
### 1. Check `docs/story-maps/README.md` cache freshness
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
readme_commit=$(git log -1 --format=%H -- docs/story-maps/README.md 2>/dev/null)
|
|
32
|
+
if [ -z "$readme_commit" ] || \
|
|
33
|
+
git log --oneline "${readme_commit}..HEAD" -- 'docs/story-maps/*/*.html' ':!docs/story-maps/README.md' 2>/dev/null | grep -q .; then
|
|
34
|
+
echo "stale"
|
|
35
|
+
fi
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Cache fresh**: read `docs/story-maps/README.md` directly. **Cache stale**: live-scan in Step 2.
|
|
39
|
+
|
|
40
|
+
### 2. Live scan
|
|
41
|
+
|
|
42
|
+
Enumerate each lifecycle subdir:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
ls docs/story-maps/draft/*.html docs/story-maps/accepted/*.html docs/story-maps/in-progress/*.html docs/story-maps/completed/*.html docs/story-maps/archived/*.html 2>/dev/null
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For each map file, parse the `<meta>` block to extract: `story-map-id`, `status`, `problems`, `rfcs`, `jtbd`. Use `xmllint --xpath` when available; fall back to `grep` on `<meta>` lines:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
status=$(xmllint --xpath 'string(//meta[@name="status"]/@content)' "$map" 2>/dev/null || \
|
|
52
|
+
grep -oE '<meta name="status" content="[^"]*"' "$map" | grep -oE 'content="[^"]*"' | sed 's/content="//;s/"$//')
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Display
|
|
56
|
+
|
|
57
|
+
Render lifecycle-grouped sections:
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
## Draft
|
|
61
|
+
|
|
62
|
+
| ID | Title | Problems | RFCs | JTBD |
|
|
63
|
+
|----|-------|----------|------|------|
|
|
64
|
+
| STORY-MAP-<NNN> | <title> | <P<NNN>...> | <RFC-<NNN>...> | <JTBD-<NNN>...> |
|
|
65
|
+
|
|
66
|
+
## Accepted / In Progress / Completed / Archived
|
|
67
|
+
|
|
68
|
+
(same shape; sections omitted when empty)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
NO WSJF column per I5. NO ranking table per the "story-maps are planning artefacts, not work items" principle (ADR-060 line 145).
|
|
72
|
+
|
|
73
|
+
### 4. Trailing suggestions
|
|
74
|
+
|
|
75
|
+
- Draft section non-empty: `Run /wr-itil:manage-story-map <STORY-MAP-<NNN>> accepted to author backbone/ribs/slices and advance the draft.`
|
|
76
|
+
- In-progress section non-empty: `Run /wr-itil:list-stories --rfc <RFC-<NNN>> to see the next story under each in-progress map's referenced RFC.`
|
|
77
|
+
- All sections empty: `No story maps captured yet. Run /wr-itil:capture-story-map <P-<NNN>> <JTBD-<NNN>> <description> to capture the first.`
|
|
78
|
+
|
|
79
|
+
## Ownership boundary
|
|
80
|
+
|
|
81
|
+
Does not modify, rename, or commit files. Cache-stale path performs live scan only; never rewrites `docs/story-maps/README.md` — refresh is `/wr-itil:manage-story-map review`'s ownership.
|
|
82
|
+
|
|
83
|
+
## Related
|
|
84
|
+
|
|
85
|
+
- **ADR-060** — Problem-RFC-Story framework; Phase 2 amendment lines 145-189 (story-map tier spec).
|
|
86
|
+
- **ADR-060 line 145** — story-maps are planning artefacts; no WSJF (I5).
|
|
87
|
+
- **ADR-060 lines 381-435** — HTML encoding schema; `<meta>` block parse target.
|
|
88
|
+
- **`docs/story-maps/README.md`** — story-map directory index.
|
|
89
|
+
- **`/wr-itil:list-stories`** — sibling read-only display at the story tier.
|
|
90
|
+
- **`/wr-itil:list-problems`** — sibling at the problem tier (P071 precedent).
|
|
91
|
+
- **JTBD-008** — Decompose a Fix Into Coordinated Changes.
|
|
92
|
+
|
|
93
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
# Behavioural contract fixtures for /wr-itil:list-story-maps (P170 Phase 2 Slice 6).
|
|
3
|
+
|
|
4
|
+
setup() {
|
|
5
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
6
|
+
SKILL_FILE="${REPO_ROOT}/packages/itil/skills/list-story-maps/SKILL.md"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@test "list-story-maps: SKILL.md exists" {
|
|
10
|
+
[ -f "$SKILL_FILE" ]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@test "list-story-maps: SKILL.md frontmatter declares wr-itil:list-story-maps name" {
|
|
14
|
+
run grep -E '^name: wr-itil:list-story-maps$' "$SKILL_FILE"
|
|
15
|
+
[ "$status" -eq 0 ]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@test "list-story-maps: SKILL.md allowed-tools does NOT include Write or Edit (read-only contract)" {
|
|
19
|
+
run grep '^allowed-tools:' "$SKILL_FILE"
|
|
20
|
+
[ "$status" -eq 0 ]
|
|
21
|
+
[[ "$output" != *"Write"* ]]
|
|
22
|
+
[[ "$output" != *"Edit"* ]]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@test "list-story-maps: SKILL.md names all 5 lifecycle state subdirectories" {
|
|
26
|
+
for state in draft accepted in-progress completed archived; do
|
|
27
|
+
run grep -E "docs/story-maps/${state}|${state}/" "$SKILL_FILE"
|
|
28
|
+
[ "$status" -eq 0 ]
|
|
29
|
+
done
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@test "list-story-maps: SKILL.md does NOT render a WSJF column (I5 invariant)" {
|
|
33
|
+
# Story-maps are planning artefacts, not work items — no WSJF per ADR-060 line 145.
|
|
34
|
+
run grep -E '^\| WSJF\b|\| WSJF \|' "$SKILL_FILE"
|
|
35
|
+
[ "$status" -ne 0 ]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@test "list-story-maps: SKILL.md names <meta> block parse target per ADR-060 lines 381-435" {
|
|
39
|
+
run grep -E '<meta name=|xmllint.*meta' "$SKILL_FILE"
|
|
40
|
+
[ "$status" -eq 0 ]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@test "list-story-maps: SKILL.md uses git log cache-freshness pattern per P031" {
|
|
44
|
+
run grep -E 'git log -1 --format=%H -- docs/story-maps/README\.md' "$SKILL_FILE"
|
|
45
|
+
[ "$status" -eq 0 ]
|
|
46
|
+
}
|
|
@@ -166,10 +166,23 @@ What "work" means depends on the problem's status:
|
|
|
166
166
|
8. If the fix is small enough, continue straight to implementing it (becoming a Known Error → Closed flow in one session)
|
|
167
167
|
|
|
168
168
|
**Known Error (root cause confirmed, fix path clear):**
|
|
169
|
-
|
|
170
|
-
2.
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
|
|
170
|
+
The Phase 2 working-the-problem traversal makes "implement the fix" concretely traceable via stories (per ADR-060 lines 300-320). Replaces the prior vague "implement the fix following the project's development workflow" with a deterministic problem → RFC → story dispatch:
|
|
171
|
+
|
|
172
|
+
1. **Read the problem's `## Fix Strategy` section** — extract referenced RFC IDs (anchor links / inline references like `RFC-NNN`). If no RFCs are referenced, the problem is non-decomposed Phase 1 work: fall through to the legacy direct-implementation path (step 6 below).
|
|
173
|
+
2. **For each referenced RFC** (in the order they appear in the Fix Strategy section), read its frontmatter `stories:` array (per ADR-060 line 259, the array is ORDERED — array position IS execution sequence):
|
|
174
|
+
- **Non-empty `stories:` array** (story-decomposed RFC): pick the first story whose lifecycle status is `accepted` or `in-progress` — skip `done` stories that already shipped, skip `draft` stories that aren't ready (the `manage-story <NNN> accepted` gate enforces INVEST shape; a draft story is structurally unready). Continue to step 3.
|
|
175
|
+
- **Empty `stories: []`** (atomic RFC per JTBD-101 friction guard, ADR-060 line 262): fall back to Phase 1 per-RFC iter dispatch — read the RFC body's `## Tasks` section directly; pick the first unticked task; no per-story scoping needed. Skip to step 5 (commit + trailer).
|
|
176
|
+
3. **Read the picked story's body** — `## User value` statement (INVEST Valuable), `## Acceptance criteria` (INVEST Testable observable behaviours), `## Implementation notes` (architecture sketches, library decisions). The story's frontmatter `estimated-effort` field (set at `manage-story accepted` transition per I10 INVEST Estimable) sets the appetite for the iteration.
|
|
177
|
+
4. **Implement the story scope** — follow the project's standard development workflow (plan if needed, architect/JTBD review, behavioural tests per ADR-052, single-commit grain per ADR-014). Confine the implementation to the picked story's acceptance criteria; deviating into adjacent unscoped work is a scope-expansion signal — surface it via the `## Scope expansion` AskUserQuestion below.
|
|
178
|
+
5. **Commit with the `Refs: STORY-<NNN>` trailer** (single-trailer vocabulary per ADR-060 line 307 + amendment 2026-05-10 nitpick N2 — same trailer verb whether the commit is the story's first implementation commit or a continuation). On the FIRST commit AFTER the capture commit (subject prefix discriminates: `feat(itil): capture STORY-NNN ...` is the capture; any other subject prefix is an implementation commit), `/wr-itil:manage-story` auto-transitions the story `draft → in-progress`. As acceptance criteria checkboxes are ticked across multiple commits, the same trailer continues to attribute the work.
|
|
179
|
+
6. **Story `done` auto-transition**: when ALL acceptance-criteria checkboxes in the story body are ticked AND the linked RFC reaches `closed`, `/wr-itil:manage-story` auto-transitions the story `in-progress → done`. (When a story's RFC is still `in-progress` but the acceptance criteria are all ticked, the story stays at `in-progress` until the RFC closes — this preserves the trace coupling per ADR-060 line 309.)
|
|
180
|
+
7. **Pick the next not-done story** from the RFC's `stories:` array (or the next unticked task from the RFC body for the atomic-RFC fallback path). Repeat from step 3.
|
|
181
|
+
8. **When all stories under all referenced RFCs are done** — the problem is fix-released. Include the problem doc closure in the final commit (`git mv` to `.verifying.md`, update Status) per ADR-022. Push, create changeset, release per the lean release principle.
|
|
182
|
+
|
|
183
|
+
**Atomic-RFC fallback path** (step 2 empty-stories case, in detail): a Phase 1-shape problem whose Fix Strategy references RFCs that have not been decomposed into stories continues to work via the existing per-RFC iter dispatch — read the RFC body's tasks/steps section, implement each task as a single-commit per ADR-014, attribute via `Refs: RFC-<NNN>` trailer (still single-trailer vocabulary). This preserves Phase 1 atomic-fix-adopter behaviour: an adopter who hasn't adopted Phase 2 story tooling has zero new friction; their RFCs continue to ship with `stories: []` and their problems continue to close via per-RFC iter dispatch.
|
|
184
|
+
|
|
185
|
+
**Legacy direct-implementation path** (step 1 no-RFCs case): a Phase 1-shape Known Error whose Fix Strategy references no RFCs continues to work via the pre-Phase-2 flow — read the root cause analysis and fix strategy, implement the fix following the project's development workflow, include the problem doc closure in the fix commit (`git mv` to `.verifying.md`, update Status), push + changeset + release. This preserves backwards compatibility with all existing Known Error problems (which were captured before the RFC framework was Phase-1-graduated).
|
|
173
186
|
|
|
174
187
|
**Scope expansion during work:** If investigation or architect review reveals that the problem's scope has grown significantly (e.g., effort re-sized from S to L, additional files discovered), use `AskUserQuestion` before continuing:
|
|
175
188
|
- Option 1: `Continue with expanded scope` — keep working this problem at its new size
|
|
@@ -181,6 +194,31 @@ What "work" means depends on the problem's status:
|
|
|
181
194
|
|
|
182
195
|
## Steps
|
|
183
196
|
|
|
197
|
+
### 0a. Auto-migrate adopter layout (P170 / RFC-002 / ADR-031)
|
|
198
|
+
|
|
199
|
+
Before the README-reconciliation preflight (Step 0) and any other layout-dependent logic, source the shared shell migration routine and call the idempotent entrypoint:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Source the synced copy from this package's lib/ (canonical lives at
|
|
203
|
+
# packages/shared/lib/migrate-problems-layout.sh per ADR-017 sync pattern).
|
|
204
|
+
source packages/itil/lib/migrate-problems-layout.sh
|
|
205
|
+
migrate_problems_to_per_state_layout "$PWD"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The routine is **idempotent and partial-migration-safe**. It no-ops when no flat-layout files (`docs/problems/*.<state>.md` at the top level of `docs/problems/`) are detected — the common case in this monorepo (post-Slice-5 T5a 2026-05-10) and in freshly-migrated adopter repos.
|
|
209
|
+
|
|
210
|
+
On a flat-layout adopter repo (first invocation post-update — JTBD-101 plugin-developer auto-migration path), the routine:
|
|
211
|
+
|
|
212
|
+
1. Creates the five state subdirectories (`docs/problems/open/`, `/known-error/`, `/verifying/`, `/parked/`, `/closed/`).
|
|
213
|
+
2. Runs `git mv docs/problems/<NNN>-<slug>.<state>.md docs/problems/<state>/<NNN>-<slug>.md` for every existing ticket. `nullglob` is enabled so partial-migration tails don't trip on literal-glob expansion.
|
|
214
|
+
3. Emits a standalone commit (per ADR-031 § Backward Compatibility line 124 "not folded into other work — so adopters can audit / revert in isolation") with subject `docs(problems): auto-migrate to per-state subdirectory layout (ADR-031)` and footer trailer `RISK_BYPASS: adr-031-migration` (recognised by the commit-gate hook per T11; allows the migration to skip the full risk-score overhead while preserving the audit trail).
|
|
215
|
+
|
|
216
|
+
**AFK authorisation per ADR-013 Rule 6**: this fires unconditionally even in AFK / non-interactive / orchestrated mode. Pure-rename + pure-mkdir + standalone-commit actions are policy-authorised under ADR-019 precedent — they are fully reversible (`git revert`), have no external-comms surface, no secrets, no destructive overwrite. No `AskUserQuestion` gate.
|
|
217
|
+
|
|
218
|
+
**First-fire signal (JTBD-006 AFK transparency per T8 jtbd-review nitpick c)**: the routine emits a single stderr line `migrate-problems-layout: relocated N tickets to per-state subdirs (ADR-031)` on the migrating invocation; silent on no-op re-invocations.
|
|
219
|
+
|
|
220
|
+
After Step 0a completes (whether no-op or migration), proceed to Step 0 README reconciliation preflight. The reconcile-readme script reads the post-migration layout; the in-flow Step 5 / Step 7 README refresh paths re-render the README from the per-state subdir shape.
|
|
221
|
+
|
|
184
222
|
### 0. README reconciliation preflight (P118)
|
|
185
223
|
|
|
186
224
|
Before parsing the request, run the diagnose-only reconciliation check. The contract here catches **cross-session drift** that per-operation refresh paths (P094 refresh-on-create + P062 refresh-on-transition) cannot retroactively see — if any past session committed a ticket change without staging the README refresh, the next manage-problem invocation reads a stale README that lies about what is open / verifying / closed.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# P170 / RFC-002 / ADR-031 Open-Execution Q1 resolution: manage-problem
|
|
4
|
+
# SKILL.md wires the shared migration routine at Step 0a (before
|
|
5
|
+
# Step 0 README reconciliation preflight). Doc-lint structural test —
|
|
6
|
+
# the wiring is a SKILL.md preamble integration point; behavioural
|
|
7
|
+
# assertions for the routine itself live at
|
|
8
|
+
# packages/shared/test/sync-migrate-problems-layout.bats (T7) and the
|
|
9
|
+
# end-to-end behavioural fixture
|
|
10
|
+
# packages/itil/skills/manage-problem/test/manage-problem-auto-migrate.bats (T10).
|
|
11
|
+
|
|
12
|
+
setup() {
|
|
13
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
14
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/manage-problem/SKILL.md"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@test "manage-problem: SKILL.md declares Step 0a auto-migrate (T8 wiring point)" {
|
|
18
|
+
run grep -E '^### 0a\.|^## Step 0a|Step 0a:' "$SKILL_MD"
|
|
19
|
+
[ "$status" -eq 0 ]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@test "manage-problem: SKILL.md Step 0a cites P170 / RFC-002 / ADR-031" {
|
|
23
|
+
run grep -F 'P170' "$SKILL_MD"
|
|
24
|
+
[ "$status" -eq 0 ]
|
|
25
|
+
run grep -F 'ADR-031' "$SKILL_MD"
|
|
26
|
+
[ "$status" -eq 0 ]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@test "manage-problem: SKILL.md Step 0a sources packages/itil/lib/migrate-problems-layout.sh" {
|
|
30
|
+
run grep -F 'packages/itil/lib/migrate-problems-layout.sh' "$SKILL_MD"
|
|
31
|
+
[ "$status" -eq 0 ]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@test "manage-problem: SKILL.md Step 0a calls migrate_problems_to_per_state_layout entrypoint" {
|
|
35
|
+
run grep -F 'migrate_problems_to_per_state_layout' "$SKILL_MD"
|
|
36
|
+
[ "$status" -eq 0 ]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@test "manage-problem: SKILL.md Step 0a fires before Step 0 README reconciliation" {
|
|
40
|
+
# Assert the literal line order in the file: Step 0a appears before
|
|
41
|
+
# the existing Step 0 README reconciliation heading.
|
|
42
|
+
local step_0a_line step_0_line
|
|
43
|
+
step_0a_line=$(grep -nE '^### 0a\.|^## Step 0a|Step 0a:' "$SKILL_MD" | head -1 | cut -d: -f1)
|
|
44
|
+
step_0_line=$(grep -nE '^### 0\. README' "$SKILL_MD" | head -1 | cut -d: -f1)
|
|
45
|
+
[ -n "$step_0a_line" ]
|
|
46
|
+
[ -n "$step_0_line" ]
|
|
47
|
+
[ "$step_0a_line" -lt "$step_0_line" ]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@test "manage-problem: SKILL.md Step 0a cites ADR-013 Rule 6 (AFK auto-fire authorisation)" {
|
|
51
|
+
run grep -F 'ADR-013' "$SKILL_MD"
|
|
52
|
+
[ "$status" -eq 0 ]
|
|
53
|
+
}
|
|
@@ -157,6 +157,18 @@ The helper (`packages/itil/scripts/update-problem-rfcs-section.sh`) is idempoten
|
|
|
157
157
|
|
|
158
158
|
The trailer hook (`itil-rfc-trailer-advisory.sh`) sits on top of this skill-side contract as a drift-detection backstop for ARBITRARY commits (e.g. `feat(...)` commits with `Refs: RFC-<NNN>` trailers authored outside the RFC skills) — it never auto-fixes; it advises.
|
|
159
159
|
|
|
160
|
+
#### Forward trace — `## Stories` body section (Phase 2)
|
|
161
|
+
|
|
162
|
+
Per ADR-060 line 270 + line 296: every transition that touches the RFC body refreshes the RFC's own `## Stories` body section from its frontmatter `stories:` array. The forward-trace surface renders the ordered execution sequence as inline links to the story files, lazy-empty when `stories: []` (atomic RFC — JTBD-101 friction guard). The helper is the Slice 2b sibling `update-rfc-references-section.sh`:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-rfc-references-section.sh" "$rfc_file" "Stories"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Idempotent + lazy-empty per the Slice 2a/2b contract. Run after the rename + frontmatter edit so the section reflects the post-transition `stories:` shape. Stage the RFC file (already staged for the lifecycle transition; the helper modifies the same file in-place).
|
|
169
|
+
|
|
170
|
+
This composes with the existing `## Story Maps` refresh that the same helper handles via `update-rfc-references-section.sh "$rfc_file" "Story Maps"` — when the RFC traces story-maps via the `story-maps:` frontmatter field (Phase 2+).
|
|
171
|
+
|
|
160
172
|
### 8. List flow (`list`)
|
|
161
173
|
|
|
162
174
|
Read all `.proposed.md`, `.accepted.md`, `.in-progress.md` files in `docs/rfcs/`. Extract ID, title, status, traced problems. Sort by Status priority (Accepted > In-Progress > Proposed) then by `Reported` ASC. Display as a markdown table.
|