@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.
Files changed (43) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +9 -1
  3. package/bin/wr-itil-reconcile-stories +2 -0
  4. package/bin/wr-itil-reconcile-story-maps +2 -0
  5. package/hooks/hooks.json +4 -0
  6. package/hooks/itil-readme-refresh-discipline.sh +118 -0
  7. package/hooks/lib/readme-refresh-detect.sh +161 -0
  8. package/hooks/test/itil-readme-refresh-discipline.bats +261 -0
  9. package/lib/migrate-problems-layout.sh +128 -0
  10. package/package.json +1 -1
  11. package/scripts/reconcile-stories.sh +236 -0
  12. package/scripts/reconcile-story-maps.sh +98 -0
  13. package/scripts/test/reconcile-stories.bats +173 -0
  14. package/scripts/test/reconcile-story-maps.bats +74 -0
  15. package/scripts/test/rfc-stories-extension.bats +173 -0
  16. package/scripts/test/update-problem-references-section.bats +195 -0
  17. package/scripts/test/update-references-section-sibling-helpers.bats +80 -0
  18. package/scripts/test/working-the-problem-traversal.bats +109 -0
  19. package/scripts/update-jtbd-references-section.sh +131 -0
  20. package/scripts/update-problem-references-section.sh +284 -0
  21. package/scripts/update-rfc-references-section.sh +152 -0
  22. package/scripts/update-story-references-section.sh +128 -0
  23. package/skills/capture-rfc/SKILL.md +28 -3
  24. package/skills/capture-story/SKILL.md +373 -0
  25. package/skills/capture-story/test/capture-story-behavioural.bats +227 -0
  26. package/skills/capture-story-map/SKILL.md +229 -0
  27. package/skills/capture-story-map/test/capture-story-map-behavioural.bats +98 -0
  28. package/skills/list-stories/SKILL.md +151 -0
  29. package/skills/list-stories/test/list-stories-contract.bats +127 -0
  30. package/skills/list-story-maps/SKILL.md +93 -0
  31. package/skills/list-story-maps/test/list-story-maps-contract.bats +46 -0
  32. package/skills/manage-problem/SKILL.md +42 -4
  33. package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +53 -0
  34. package/skills/manage-rfc/SKILL.md +12 -0
  35. package/skills/manage-story/SKILL.md +242 -0
  36. package/skills/manage-story/test/manage-story-contract.bats +171 -0
  37. package/skills/manage-story-map/SKILL.md +158 -0
  38. package/skills/manage-story-map/test/manage-story-map-contract.bats +63 -0
  39. package/skills/reconcile-stories/SKILL.md +110 -0
  40. package/skills/reconcile-story-maps/SKILL.md +70 -0
  41. package/skills/work-problem/SKILL.md +1 -1
  42. package/skills/work-problems/SKILL.md +25 -0
  43. package/skills/work-problems/test/work-problems-auto-migrate-step.bats +57 -0
@@ -0,0 +1,242 @@
1
+ ---
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-transitions draft→in-progress on first non-capture commit and in-progress→done on all-criteria-ticked + linked RFC closes, and refreshes docs/stories/README.md per the P062 / P094 contract pattern. Companion to /wr-itil:capture-story (lightweight aside surface).
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Manage Story Skill
8
+
9
+ Heavyweight story lifecycle management. Mirrors `/wr-itil:manage-rfc` shape per ADR-032 lightweight + heavyweight skill split, extended for the story-tier's INVEST-shape acceptance gate (I10) and auto-transition triggers.
10
+
11
+ ## Story Lifecycle
12
+
13
+ Per ADR-060 Phase 2 amendment 2026-05-10 lines 200-253:
14
+
15
+ | Status | Filename pattern | Meaning | Entry criteria |
16
+ |--------|-----------------|---------|----------------|
17
+ | **draft** | `docs/stories/draft/STORY-<NNN>-<slug>.md` | Captured (problem + JTBD traces present); skeleton body | I6 + I9 satisfied at capture |
18
+ | **accepted** | `docs/stories/accepted/STORY-<NNN>-<slug>.md` | INVEST shape verified; ready for implementation | I7 + I8 + I10 hard-block satisfied |
19
+ | **in-progress** | `docs/stories/in-progress/STORY-<NNN>-<slug>.md` | Implementation underway | Auto-transitioned on first non-capture commit carrying `Refs: STORY-<NNN>` |
20
+ | **done** | `docs/stories/done/STORY-<NNN>-<slug>.md` | All acceptance criteria ticked + linked RFC closed | Auto-transitioned (triggered by RFC close-fire or by manual `manage-story <NNN> done`) |
21
+ | **archived** | `docs/stories/archived/STORY-<NNN>-<slug>.md` | Closed without completion (scope shifted; superseded) | Manual transition only |
22
+
23
+ ## I-invariant enforcement at lifecycle transitions
24
+
25
+ | Invariant | What it asserts | Where it fires |
26
+ |-----------|-----------------|----------------|
27
+ | **I6** trace-to-problem | Every story traces to ≥ 1 problem | Hard-block at `/wr-itil:capture-story` (also verified at every transition) |
28
+ | **I7** trace-to-RFC | Every story traces to ≥ 1 RFC | Hard-block at `manage-story <NNN> accepted` (deferred from capture per ADR-060 line 291) |
29
+ | **I8** trace-to-story-map | Every story traces to ≥ 1 story map | Hard-block at `manage-story <NNN> accepted` (deferred from capture) |
30
+ | **I9** trace-to-JTBD | Every story traces to ≥ 1 JTBD | Hard-block at `/wr-itil:capture-story` (also verified at every transition) |
31
+ | **I10** INVEST shape | At acceptance, INVEST behaviourally: ≥1 acceptance criterion (Testable); user-value statement (Valuable); no Blocked-by-unaccepted refs (Independent); `estimated-effort` field set (Estimable); S/M effort SHOULD; L/XL flagged decomposition-candidate (Small) | Hard-block at `manage-story <NNN> accepted` |
32
+ | **I11** no-WSJF-leak | Phase 2: stories MUST NOT carry a WSJF field | Behavioural test at this skill (no WSJF field added/read) |
33
+
34
+ **Bootstrap-exemption marker** (per ADR-060 line 339 + ADR-053 Bootstrapping precedent): the I7/I8/I9/I10 retrofit on bootstrap-migration stories rides a one-time exemption marker `<!-- bootstrap-exempt: STORY-MAP-001 migration per ADR-060 amendment 2026-05-10 -->` inline with the frontmatter. Non-bootstrap captures with the marker fail per the behavioural test.
35
+
36
+ ## Argument grammar
37
+
38
+ ```
39
+ /wr-itil:manage-story <STORY-NNN> # Update flow — open the story for review
40
+ /wr-itil:manage-story <STORY-NNN> accepted # Transition draft → accepted (gates I7 + I8 + I10)
41
+ /wr-itil:manage-story <STORY-NNN> in-progress # Manual transition (auto-fires on first non-capture commit)
42
+ /wr-itil:manage-story <STORY-NNN> done # Transition in-progress → done (gates all-criteria-ticked + RFC closed)
43
+ /wr-itil:manage-story <STORY-NNN> archived # Close without completion
44
+ /wr-itil:manage-story review # Re-rank all stories + refresh README
45
+ ```
46
+
47
+ ## Rule 6 audit (per ADR-032 + ADR-013 + ADR-060)
48
+
49
+ | Decision | Resolution | Authority class |
50
+ |----------|-----------|-----------------|
51
+ | Story ID resolution | Mechanical: `STORY-<NNN>` regex match against `docs/stories/*/STORY-<NNN>-*.md` | silent-mechanical |
52
+ | Lifecycle transition validation | Mechanical: state machine — draft → accepted → in-progress → done; allow draft → archived; disallow backwards | silent-mechanical |
53
+ | I7 + I8 hard-block at accepted | Mechanical: frontmatter `rfcs:` and `story-maps:` arrays MUST be non-empty AND each ID must resolve to a file in `docs/rfcs/` and `docs/story-maps/` | silent-mechanical |
54
+ | I10 INVEST shape check | Mechanical: `## User value` section non-empty; `## Acceptance criteria` has ≥ 1 `- [ ]` line; `estimated-effort` field set to S/M/L/XL; L/XL flagged as decomposition-candidate (advisory, not blocking per ADR-060 line 252 architect-amendment-2026-05-10 nitpick N3) | silent-mechanical |
55
+ | INVEST shape violation | Halt-with-stderr-directive listing the missing INVEST attributes; user re-invokes after editing the story body | n/a (halt) |
56
+ | README refresh on every transition | Mechanical: regenerate `docs/stories/README.md` Story Rankings + Done tables from FS truth; stage in same commit | silent-mechanical |
57
+ | Reverse-trace refresh on driving artefacts | Mechanical: every transition refreshes `## Stories` section on each driving problem + JTBD + RFC + story-map via the Slice 2a/2b helpers | silent-mechanical |
58
+ | Bootstrap-exemption marker | Mechanical: marker permitted only on stories whose frontmatter problem-trace includes the bootstrap-migration problem ID (P170); non-bootstrap stories with the marker fail at acceptance | silent-mechanical |
59
+
60
+ All decisions framework-mediated per ADR-044 + P132 + inverse-P078.
61
+
62
+ ## Steps
63
+
64
+ ### 0. Preflight
65
+
66
+ ```bash
67
+ wr-itil-reconcile-readme docs/problems > /tmp/wr-itil-drift-$$.txt
68
+ wr-itil-reconcile-stories docs/stories docs/problems docs/rfcs docs/jtbd > /tmp/wr-itil-stories-drift-$$.txt
69
+ # If either reports drift, halt-and-route to the appropriate reconcile skill.
70
+ ```
71
+
72
+ ### 1. Parse arguments
73
+
74
+ ```bash
75
+ story_id="$1"
76
+ action="${2:-update}" # default: update flow
77
+ ```
78
+
79
+ If `$story_id` is `review`, branch to Step 9 (review flow). Otherwise validate `$story_id` matches `^STORY-[0-9]{3}$` and resolve to a file under `docs/stories/<state>/STORY-<NNN>-*.md`. If unresolved, halt with stderr directive.
80
+
81
+ ### 2. Read story frontmatter + body
82
+
83
+ Parse YAML frontmatter (fields: `status`, `story-id`, `reported`, `decision-makers`, `problems`, `jtbd`, `rfcs`, `story-maps`, `estimated-effort`). Read body sections (`## User value`, `## Acceptance criteria`, `## Driving problem trace`, `## JTBD trace`, `## Implementation notes`, `## Dependencies`, `## Related`).
84
+
85
+ ### 3-6. (Update flow — bare `<STORY-NNN>`)
86
+
87
+ Display the current story state. Surface any open gaps:
88
+ - Missing/empty body sections.
89
+ - Frontmatter trace lists that don't resolve to files.
90
+ - Mismatched filename `<status>` subdir vs frontmatter `status:` field.
91
+
92
+ Use `AskUserQuestion` for direction-setting fields (e.g. `## User value` rewrite); silent-mechanical for housekeeping (e.g. trace ID format normalisation).
93
+
94
+ ### 7. Status transitions
95
+
96
+ #### Transition mechanics
97
+
98
+ For any transition `<from> → <to>`:
99
+
100
+ 1. **Verify pre-transition invariants** for `<to>`:
101
+ - `accepted`: I7 + I8 + I10 hard-block (see § I-invariant table).
102
+ - `in-progress`: linked RFC status is `accepted` or `in-progress` (you can't progress a story under a proposed/closed RFC).
103
+ - `done`: ALL `- [ ]` checkboxes in `## Acceptance criteria` are ticked (i.e. zero unticked); linked RFC status is `closed` OR the RFC's other stories have closed (transitive closure check deferred to a per-RFC `manage-rfc done-gate` check in a future slice).
104
+ - `archived`: no invariants (manual close-without-completion).
105
+
106
+ 2. **`git mv` the file** to the new state subdir:
107
+ ```bash
108
+ git mv "docs/stories/${from_state}/STORY-${nnn}-${slug}.md" "docs/stories/${to_state}/STORY-${nnn}-${slug}.md"
109
+ ```
110
+
111
+ 3. **Edit the file** — Status field; for `accepted`, populate any deferred `## User value` / `## Acceptance criteria` / `## Implementation notes` sections via AskUserQuestion if not already filled.
112
+
113
+ 4. **P057 staging-trap** — after the Edit, re-stage: `git add "docs/stories/${to_state}/STORY-${nnn}-${slug}.md"`.
114
+
115
+ #### Auto-transition triggers (ADR-060 line 292)
116
+
117
+ The auto-transition logic fires in two contexts:
118
+
119
+ - **`draft → in-progress`**: when the FIRST commit AFTER the story's capture commit lands with a `Refs: STORY-<NNN>` trailer AND a commit subject NOT prefixed with `feat(itil): capture STORY-`. Detected by a future commit-trailer-trigger hook (deferred to a hook-source slice); manual `manage-story <NNN> in-progress` invocation works in the interim.
120
+
121
+ - **`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.
122
+
123
+ #### README refresh on every transition (P062 mirror)
124
+
125
+ After rename + Edit + re-stage, regenerate `docs/stories/README.md` Story Rankings + Done tables in-place reflecting the new filename set and the transitioned story's new Status. Stage the refreshed README with the same commit.
126
+
127
+ The "Last reviewed" line on `docs/stories/README.md` follows the same P134 truncation discipline as `docs/problems/README.md`: single most-recent fragment on line 3; soft cap ≤ 1024 bytes per fragment; hard ceiling 5120 bytes per ADR-040 Tier 3 envelope.
128
+
129
+ #### Reverse trace on driving problem(s), JTBD(s), RFC(s), story-map(s) — skill-side primary surface
130
+
131
+ Per ADR-060 line 270: every transition refreshes the `## Stories` section on each driving artefact inline in the same commit per ADR-014 single-commit grain.
132
+
133
+ For each parent ID in the story's frontmatter:
134
+
135
+ ```bash
136
+ # Problem parents
137
+ for pid_token in $(awk '/^problems:/{gsub(/[][]/,""); gsub(/,/," "); for(i=2;i<=NF;i++)print $i; exit}' "$story_file"); do
138
+ pid_num="${pid_token#P}"
139
+ problem_file=$(ls docs/problems/${pid_num}-*.md docs/problems/*/${pid_num}-*.md 2>/dev/null | head -1)
140
+ [ -z "$problem_file" ] && continue
141
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-problem-references-section.sh" "$problem_file" "Stories"
142
+ git add "$problem_file"
143
+ done
144
+
145
+ # JTBD parents — sibling shape
146
+ for jid in $(awk '/^jtbd:/{gsub(/[][]/,""); gsub(/,/," "); for(i=2;i<=NF;i++)print $i; exit}' "$story_file"); do
147
+ jtbd_file=$(ls docs/jtbd/*/${jid}-*.md 2>/dev/null | head -1)
148
+ [ -z "$jtbd_file" ] && continue
149
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-jtbd-references-section.sh" "$jtbd_file" "Stories"
150
+ git add "$jtbd_file"
151
+ done
152
+
153
+ # RFC parents — sibling shape (after Slice 11 ships the Stories section helper)
154
+ for rid in $(awk '/^rfcs:/{gsub(/[][]/,""); gsub(/,/," "); for(i=2;i<=NF;i++)print $i; exit}' "$story_file"); do
155
+ rfc_file=$(ls docs/rfcs/${rid}-*.md 2>/dev/null | head -1)
156
+ [ -z "$rfc_file" ] && continue
157
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-rfc-references-section.sh" "$rfc_file" "Stories"
158
+ git add "$rfc_file"
159
+ done
160
+
161
+ # Story-map parents — manually-authored data-attribute traces per
162
+ # architect amend finding 2 on Slice 7; NO automatic refresh here. Emit
163
+ # advisory stderr noting unplaced-on-map state if frontmatter
164
+ # story-maps: is non-empty.
165
+ ```
166
+
167
+ The helpers are idempotent + lazy-empty per the Slice 2a/2b/Slice 11 contract.
168
+
169
+ ### 8. List flow (`list`)
170
+
171
+ Read all `docs/stories/*/STORY-*.md` files. Extract ID, title, status, traced problems / RFCs / story-maps / JTBDs. Sort by lifecycle state (accepted > in-progress > draft > done > archived) then by `Reported` ASC. Display as markdown tables grouped by state.
172
+
173
+ ### 9. Review flow (`review`)
174
+
175
+ Per `/wr-itil:review-problems` precedent at the story tier:
176
+
177
+ 1. For each story, re-validate I6 + I9 (re-resolve problem + JTBD trace files; flag missing).
178
+ 2. For each accepted/in-progress story, re-validate I7 + I8 + I10 (re-resolve RFC + story-map trace files; re-check INVEST shape; flag drift).
179
+ 3. Regenerate `docs/stories/README.md` Story Rankings + Done tables.
180
+ 4. Single commit per ADR-014; commit message names the review pass + count of drift-resolutions.
181
+
182
+ ### 10. Single commit per ADR-014
183
+
184
+ Stage list per transition shape:
185
+ - Renamed story file (new path)
186
+ - README.md refresh
187
+ - All driving problem + JTBD + RFC files whose `## Stories` section refreshed
188
+
189
+ Commit message format:
190
+ ```
191
+ feat(itil): transition STORY-<NNN> <from> → <to> — <title>
192
+
193
+ Refs: STORY-<NNN>
194
+ ```
195
+
196
+ Risk-gate per ADR-014: delegate to subagent type `wr-risk-scorer:pipeline` via the Agent tool; fallback `/wr-risk-scorer:assess-release` via Skill tool.
197
+
198
+ ### 11. Report
199
+
200
+ After commit, report:
201
+ - Story ID + title + new status.
202
+ - Action taken (transition kind + invariant gates that fired).
203
+ - Driving artefacts touched (problem + JTBD + RFC reverse-trace refresh paths).
204
+ - Trailing pointer: `Run /wr-itil:work-problem <P-<NNN>> to continue work on the driving problem, or /wr-itil:list-stories --rfc <RFC-<NNN>> to see the next story in the RFC's execution order.`
205
+
206
+ ## Composition with capture-story
207
+
208
+ | Concern | manage-story | capture-story |
209
+ |---------|--------------|---------------|
210
+ | I6 + I9 hard-block | Re-validated at every lifecycle transition | Hard-block at capture-time |
211
+ | I7 + I8 hard-block | **Primary surface** — fires at `manage-story <NNN> accepted` | Advisory at capture if flags provided |
212
+ | I10 INVEST shape | **Primary surface** — fires at `manage-story <NNN> accepted` | Out of scope (capture produces skeleton) |
213
+ | Status transitions | Owns draft → accepted → in-progress → done → archived | Out of scope (creation only) |
214
+ | README refresh | Inline per transition (P094 mirror) | Deferred to `manage-story review` or `wr-itil-reconcile-stories` |
215
+ | Auto-transition triggers | Fires on first non-capture commit (draft→in-progress) + criteria-ticked + RFC-closed (in-progress→done) | n/a |
216
+ | Reverse-trace refresh on parents | Inline per transition | Inline per capture |
217
+ | Commit grain | One commit per transition / per intake | One commit per capture |
218
+
219
+ ## Related
220
+
221
+ - **ADR-060** — Problem-RFC-Story framework + Phase 2 amendment 2026-05-10 (story tier).
222
+ - **ADR-060 lines 248-253** — I6-I11 story-tier invariants.
223
+ - **ADR-060 line 252** — I10 INVEST shape (Testable/Valuable/Independent/Estimable; Small SHOULD per architect-amendment-2026-05-10 nitpick N3).
224
+ - **ADR-060 line 292** — auto-transition triggers (draft→in-progress on first non-capture commit; in-progress→done on criteria-ticked + RFC-closed).
225
+ - **ADR-060 line 339 + ADR-053 Bootstrapping precedent** — bootstrap-exemption marker contract for STORY-MAP-001 migration retrofit.
226
+ - **P170** — driver problem ticket.
227
+ - **JTBD-008** — Decompose a Fix Into Coordinated Changes. Primary persona-anchor.
228
+ - **JTBD-001** (extended scope) — change-set-level governance composition.
229
+ - **JTBD-006** — AFK orchestrator protection (I11 no-WSJF-leak prevents story-level competition in Step 3 selection).
230
+ - **`docs/stories/README.md`** — story tier lifecycle index + frontmatter/body shape spec.
231
+ - **`/wr-itil:capture-story` SKILL.md** — companion lightweight aside surface.
232
+ - **`/wr-itil:reconcile-stories` SKILL.md** — drift detection + agent-applied edit recovery.
233
+ - **ADR-014** — single-commit grain.
234
+ - **ADR-022** — problem lifecycle conventions; story lifecycle mirrors.
235
+ - **ADR-032** — governance-skill aside-invocation pattern; lightweight + heavyweight split.
236
+ - **ADR-044** — decision delegation contract.
237
+ - **ADR-051** — load-bearing-from-the-start; I7 + I8 + I10 ship on day one.
238
+ - **ADR-052** — behavioural-tests default; `packages/itil/scripts/test/manage-story.bats` (P170 Phase 2 Slice 8).
239
+ - **ADR-060 Phase 2 Slice 2a/2b/Slice 11 reverse-trace helpers** — `update-problem-references-section.sh`, `update-jtbd-references-section.sh`, `update-rfc-references-section.sh` all support `"Stories"` section-name token.
240
+ - **manage-rfc SKILL.md** — direct precedent shape; manage-story mirrors with story-tier extensions for INVEST gate + auto-transitions.
241
+
242
+ $ARGUMENTS
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural contract fixtures for /wr-itil:manage-story (P170 Phase 2
3
+ # Slice 8 — ADR-060 amendment 2026-05-10 lines 200-253 + 270 + 292).
4
+ #
5
+ # Per ADR-052: behavioural assertions on observable SKILL contract
6
+ # surfaces. Skill-prose behaviour for prompt-driven agents isn't
7
+ # directly testable in bats; these tests assert presence of the
8
+ # load-bearing identifiers in the SKILL contract (the closest in-
9
+ # session-reachable approximation per P081 + P012).
10
+ #
11
+ # Behavioural surfaces under test:
12
+ # 1. SKILL.md presence + canonical name.
13
+ # 2. I6-I11 invariant table presence (load-bearing).
14
+ # 3. I7 + I8 hard-block fires at accepted transition (not earlier).
15
+ # 4. I10 INVEST shape gate names all 4 INVEST checks (Testable /
16
+ # Valuable / Independent / Estimable) + L/XL decomposition-
17
+ # candidate advisory per ADR-060 line 252 nitpick N3.
18
+ # 5. Auto-transition triggers named: draft→in-progress on first
19
+ # non-capture commit; in-progress→done on criteria-ticked + RFC
20
+ # closed.
21
+ # 6. Bootstrap-exemption marker contract named per ADR-060 line 339.
22
+ # 7. Reverse-trace refresh on all 4 parent tiers (problem / JTBD /
23
+ # RFC / story-map) named, with story-map manual placement noted
24
+ # per architect amend finding 2 on Slice 7.
25
+ # 8. No-WSJF-leak (I11) — no WSJF field in argument grammar or
26
+ # frontmatter handling.
27
+ #
28
+ # @problem P170
29
+ # @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes)
30
+ # @jtbd JTBD-001 (extended scope — change-set-level governance)
31
+ # @adr ADR-060 (Problem-RFC-Story framework — story tier)
32
+ # @adr ADR-052 (Behavioural-tests-default)
33
+ # @adr ADR-032 (governance-skill aside-invocation pattern)
34
+
35
+ setup() {
36
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
37
+ SKILL_FILE="${REPO_ROOT}/packages/itil/skills/manage-story/SKILL.md"
38
+ }
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Surface 0: SKILL.md presence + canonical name
42
+ # ---------------------------------------------------------------------------
43
+
44
+ @test "manage-story: SKILL.md exists" {
45
+ [ -f "$SKILL_FILE" ]
46
+ }
47
+
48
+ @test "manage-story: SKILL.md frontmatter declares wr-itil:manage-story name" {
49
+ run grep -E '^name: wr-itil:manage-story$' "$SKILL_FILE"
50
+ [ "$status" -eq 0 ]
51
+ }
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Surface 1: I6-I11 invariant table presence
55
+ # ---------------------------------------------------------------------------
56
+
57
+ @test "manage-story: SKILL.md declares I6 trace-to-problem invariant" {
58
+ run grep -E 'I6.*trace-to-problem|trace-to-problem.*I6' "$SKILL_FILE"
59
+ [ "$status" -eq 0 ]
60
+ }
61
+
62
+ @test "manage-story: SKILL.md declares I7 trace-to-RFC invariant" {
63
+ run grep -E 'I7.*trace-to-RFC|trace-to-RFC.*I7' "$SKILL_FILE"
64
+ [ "$status" -eq 0 ]
65
+ }
66
+
67
+ @test "manage-story: SKILL.md declares I8 trace-to-story-map invariant" {
68
+ run grep -E 'I8.*trace-to-story-map|trace-to-story-map.*I8' "$SKILL_FILE"
69
+ [ "$status" -eq 0 ]
70
+ }
71
+
72
+ @test "manage-story: SKILL.md declares I9 trace-to-JTBD invariant" {
73
+ run grep -E 'I9.*trace-to-JTBD|trace-to-JTBD.*I9' "$SKILL_FILE"
74
+ [ "$status" -eq 0 ]
75
+ }
76
+
77
+ @test "manage-story: SKILL.md declares I10 INVEST shape invariant" {
78
+ run grep -E 'I10.*INVEST|INVEST.*I10' "$SKILL_FILE"
79
+ [ "$status" -eq 0 ]
80
+ }
81
+
82
+ @test "manage-story: SKILL.md declares I11 no-WSJF-leak invariant" {
83
+ run grep -E 'I11.*WSJF|WSJF.*I11|no-WSJF-leak' "$SKILL_FILE"
84
+ [ "$status" -eq 0 ]
85
+ }
86
+
87
+ # ---------------------------------------------------------------------------
88
+ # Surface 2: I7 + I8 fire at accepted transition (not earlier)
89
+ # ---------------------------------------------------------------------------
90
+
91
+ @test "manage-story: SKILL.md states I7 + I8 hard-block fires at accepted transition" {
92
+ # The deferred-from-capture contract is load-bearing per ADR-060
93
+ # line 291 — capture-story permits absent traces; manage-story
94
+ # accepted gate enforces them.
95
+ run grep -iE 'I7.*accepted|hard-block at.*accepted|fires at.*accepted.*I7' "$SKILL_FILE"
96
+ [ "$status" -eq 0 ]
97
+ }
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # Surface 3: I10 INVEST 4-axis check
101
+ # ---------------------------------------------------------------------------
102
+
103
+ @test "manage-story: SKILL.md names all 4 INVEST checks (Testable/Valuable/Independent/Estimable)" {
104
+ for axis in Testable Valuable Independent Estimable; do
105
+ run grep -i "$axis" "$SKILL_FILE"
106
+ [ "$status" -eq 0 ]
107
+ done
108
+ }
109
+
110
+ @test "manage-story: SKILL.md names L/XL decomposition-candidate advisory per ADR-060 nitpick N3" {
111
+ run grep -iE 'L/XL.*decomposition|decomposition.candidate|N3' "$SKILL_FILE"
112
+ [ "$status" -eq 0 ]
113
+ }
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # Surface 4: Auto-transition triggers
117
+ # ---------------------------------------------------------------------------
118
+
119
+ @test "manage-story: SKILL.md names draft → in-progress auto-transition on first non-capture commit" {
120
+ run grep -iE 'draft.*in-progress.*first.*non-capture|first commit.*non-capture|first.*after.*capture' "$SKILL_FILE"
121
+ [ "$status" -eq 0 ]
122
+ }
123
+
124
+ @test "manage-story: SKILL.md names in-progress → done auto-transition on criteria-ticked + RFC closed" {
125
+ run grep -iE 'in-progress.*done.*criteria.*ticked|all.*criteria.*ticked.*RFC.*closed|RFC.*closed.*all-criteria' "$SKILL_FILE"
126
+ [ "$status" -eq 0 ]
127
+ }
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # Surface 5: Bootstrap-exemption marker
131
+ # ---------------------------------------------------------------------------
132
+
133
+ @test "manage-story: SKILL.md names bootstrap-exemption marker per ADR-060 line 339" {
134
+ run grep -iE 'bootstrap-exempt|bootstrap.exemption|ADR-053.*Bootstrapping' "$SKILL_FILE"
135
+ [ "$status" -eq 0 ]
136
+ }
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Surface 6: Reverse-trace refresh on 4 parent tiers
140
+ # ---------------------------------------------------------------------------
141
+
142
+ @test "manage-story: SKILL.md names reverse-trace refresh on problem parents via update-problem-references-section.sh" {
143
+ run grep -E 'update-problem-references-section\.sh.*Stories' "$SKILL_FILE"
144
+ [ "$status" -eq 0 ]
145
+ }
146
+
147
+ @test "manage-story: SKILL.md names reverse-trace refresh on JTBD parents via update-jtbd-references-section.sh" {
148
+ run grep -E 'update-jtbd-references-section\.sh.*Stories' "$SKILL_FILE"
149
+ [ "$status" -eq 0 ]
150
+ }
151
+
152
+ @test "manage-story: SKILL.md names reverse-trace refresh on RFC parents via update-rfc-references-section.sh" {
153
+ run grep -E 'update-rfc-references-section\.sh.*Stories' "$SKILL_FILE"
154
+ [ "$status" -eq 0 ]
155
+ }
156
+
157
+ @test "manage-story: SKILL.md names story-map parents as MANUAL placement (no automatic refresh per Slice 7 architect amend finding 2)" {
158
+ run grep -iE 'manually-authored|manual placement|no automatic refresh' "$SKILL_FILE"
159
+ [ "$status" -eq 0 ]
160
+ }
161
+
162
+ # ---------------------------------------------------------------------------
163
+ # Surface 7: I11 no-WSJF-leak — no WSJF field in argument grammar
164
+ # ---------------------------------------------------------------------------
165
+
166
+ @test "manage-story: SKILL.md argument grammar does NOT include a WSJF flag/token (I11 invariant)" {
167
+ # The argument grammar block — extract the fenced block following
168
+ # "## Argument grammar" and verify no WSJF token in it.
169
+ run grep -A 20 '^## Argument grammar' "$SKILL_FILE"
170
+ [[ "$output" != *"WSJF"* ]] && [[ "$output" != *"wsjf"* ]]
171
+ }
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: wr-itil:manage-story-map
3
+ description: Heavyweight story-map intake + lifecycle management following ADR-060 Phase 2. Authors backbone × ribs × slices structure on draft maps, transitions through draft → accepted → in-progress → completed → archived, re-validates I3 + I4 invariants at every transition, and refreshes docs/story-maps/README.md per the P062 / P094 contract pattern. Companion to /wr-itil:capture-story-map (lightweight aside surface).
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Manage Story Map Skill
8
+
9
+ Heavyweight story-map lifecycle management. Mirrors `/wr-itil:manage-story` shape per ADR-032 lightweight + heavyweight split, applied at the story-map tier with HTML encoding.
10
+
11
+ ## Story-Map Lifecycle
12
+
13
+ Per ADR-060 amendment 2026-05-10 lines 145-189 + encoding amendment 2026-05-12:
14
+
15
+ | Status | Filename pattern | Meaning |
16
+ |--------|-----------------|---------|
17
+ | **draft** | `docs/story-maps/draft/STORY-MAP-<NNN>-<slug>.html` | Captured (problem + JTBD traces present); skeleton backbone |
18
+ | **accepted** | `docs/story-maps/accepted/STORY-MAP-<NNN>-<slug>.html` | Backbone × ribs × slices authored; story references in place |
19
+ | **in-progress** | `docs/story-maps/in-progress/STORY-MAP-<NNN>-<slug>.html` | Slices being implemented; stories transitioning |
20
+ | **completed** | `docs/story-maps/completed/STORY-MAP-<NNN>-<slug>.html` | All slices done |
21
+ | **archived** | `docs/story-maps/archived/STORY-MAP-<NNN>-<slug>.html` | Closed without completion |
22
+
23
+ ## I-invariant enforcement
24
+
25
+ | Invariant | What it asserts | Where it fires |
26
+ |-----------|-----------------|----------------|
27
+ | **I3** trace-to-problem | Every map traces to ≥ 1 problem (ADR-060 line 187) | Hard-block at `/wr-itil:capture-story-map` + re-validated at every transition |
28
+ | **I4** trace-to-JTBD | Every map traces to ≥ 1 JTBD (ADR-060 line 188) | Hard-block at `/wr-itil:capture-story-map` + re-validated at every transition |
29
+ | **I5** no-WSJF-leak | Maps MUST NOT carry WSJF (ADR-060 line 189) | Behavioural test: argument grammar + frontmatter carry no WSJF |
30
+
31
+ **Bootstrap-exemption marker** per ADR-060 line 339 + ADR-053 Bootstrapping precedent: STORY-MAP-001 ships with the `<!-- bootstrap-exempt -->` marker during retrofit; non-bootstrap captures with the marker fail.
32
+
33
+ ## Argument grammar
34
+
35
+ ```
36
+ /wr-itil:manage-story-map <STORY-MAP-NNN> # Update flow
37
+ /wr-itil:manage-story-map <STORY-MAP-NNN> accepted # Transition draft → accepted
38
+ /wr-itil:manage-story-map <STORY-MAP-NNN> in-progress # Manual transition
39
+ /wr-itil:manage-story-map <STORY-MAP-NNN> completed # Transition in-progress → completed
40
+ /wr-itil:manage-story-map <STORY-MAP-NNN> archived # Close without completion
41
+ /wr-itil:manage-story-map review # Re-validate all maps + refresh README
42
+ ```
43
+
44
+ No WSJF token in any grammar form (I5 invariant).
45
+
46
+ ## Rule 6 audit
47
+
48
+ | Decision | Resolution | Authority class |
49
+ |----------|-----------|-----------------|
50
+ | Story-map ID resolution | Mechanical: regex match `^STORY-MAP-[0-9]{3}$` against `docs/story-maps/*/STORY-MAP-<NNN>-*.html` | silent-mechanical |
51
+ | Lifecycle transition validation | Mechanical state machine | silent-mechanical |
52
+ | Backbone/ribs/slices authoring | AskUserQuestion (taste) at accepted; agent applies user input as HTML edits | taste |
53
+ | `<meta>` block updates | Mechanical: update status `<meta>` on transitions; preserve all other meta | silent-mechanical |
54
+ | README refresh on every transition | Mechanical: regenerate `docs/story-maps/README.md` lifecycle-grouped section from FS truth | silent-mechanical |
55
+ | Reverse-trace refresh on driving artefacts | Mechanical: refresh `## Story Maps` on problems + JTBDs via Slice 2a/2b helpers | silent-mechanical |
56
+
57
+ ## Steps
58
+
59
+ ### 0. Preflight
60
+
61
+ ```bash
62
+ wr-itil-reconcile-readme docs/problems > /tmp/wr-itil-drift-$$.txt
63
+ wr-itil-reconcile-story-maps docs/story-maps > /tmp/wr-itil-story-maps-drift-$$.txt
64
+ # Halt-and-route on drift to the appropriate reconcile skill.
65
+ ```
66
+
67
+ ### 1. Parse arguments + resolve story-map file
68
+
69
+ ```bash
70
+ story_map_id="$1"
71
+ action="${2:-update}"
72
+ ```
73
+
74
+ If `$story_map_id == "review"`, branch to Step 8 (review flow). Otherwise resolve `docs/story-maps/<state>/STORY-MAP-<NNN>-*.html`; if unresolved, halt.
75
+
76
+ ### 2. Read story-map HTML
77
+
78
+ Parse `<meta>` block (problems, rfcs, jtbd, adrs, status, reported, decision-makers). Read backbone structure (`<section class="backbone">` → `.rib-header` → `.rib` → `<a class="slice">` data-* attributes).
79
+
80
+ ### 3-6. (Update flow — bare `<STORY-MAP-NNN>`)
81
+
82
+ Display current map state. Surface gaps: missing backbone ribs, slices with unresolved `data-story-id` references, mismatched `<meta name="status">` vs filename `<state>` subdir.
83
+
84
+ Use AskUserQuestion for backbone authoring direction (taste class); silent-mechanical for housekeeping (status normalisation).
85
+
86
+ ### 7. Status transitions
87
+
88
+ For any transition `<from> → <to>`:
89
+
90
+ 1. **Verify pre-transition invariants**:
91
+ - `accepted`: I3 + I4 re-validated; backbone authored with ≥ 1 rib + ≥ 1 slice; every slice `data-story-id` resolves to a `docs/stories/*/STORY-NNN-*.md` file.
92
+ - `in-progress`: at least one slice has `data-status="in-progress"` or `"done"`.
93
+ - `completed`: every slice has `data-status="done"`.
94
+ - `archived`: no invariants (manual close).
95
+
96
+ 2. **`git mv` to new state subdir + Edit `<meta name="status">`** + P057 re-stage.
97
+
98
+ 3. **README refresh** — regenerate `docs/story-maps/README.md` lifecycle-grouped section from FS truth. Stage in same commit.
99
+
100
+ 4. **Reverse-trace refresh** — for each problem + JTBD in `<meta>` block:
101
+
102
+ ```bash
103
+ for pid_token in $(grep -oE '<meta name="problems" content="[^"]*"' "$map_file" | grep -oE 'P[0-9]{3}'); do
104
+ pid_num="${pid_token#P}"
105
+ problem_file=$(ls docs/problems/${pid_num}-*.md docs/problems/*/${pid_num}-*.md 2>/dev/null | head -1)
106
+ [ -z "$problem_file" ] && continue
107
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-problem-references-section.sh" "$problem_file" "Story Maps"
108
+ git add "$problem_file"
109
+ done
110
+
111
+ # Same for JTBDs via update-jtbd-references-section.sh "Story Maps"
112
+ ```
113
+
114
+ Per architect amend finding 2 on Slice 7: story-map HTML files do NOT carry an auto-maintained markdown reverse-trace section themselves (the `<a class="slice">` data-attribute traces are authored manually during backbone design). No reverse-trace refresh on the map itself; reverse-trace only flows OUT to problem + JTBD parents.
115
+
116
+ ### 8. List flow (`list`)
117
+
118
+ Forward-points to `/wr-itil:list-story-maps` (read-only sibling).
119
+
120
+ ### 9. Review flow (`review`)
121
+
122
+ Re-validate every map's I3 + I4. Regenerate README. Single commit.
123
+
124
+ ### 10. Single commit per ADR-014
125
+
126
+ Stage: renamed map file + README + driving problem/JTBD files whose `## Story Maps` section refreshed.
127
+
128
+ Commit message: `feat(itil): transition STORY-MAP-<NNN> <from> → <to> — <title>` + `Refs: STORY-MAP-<NNN>` trailer.
129
+
130
+ ### 11. Report
131
+
132
+ Story-map ID + new status + invariants verified + parent artefacts touched + trailing pointer to `/wr-itil:list-story-maps`.
133
+
134
+ ## Composition with capture-story-map
135
+
136
+ | Concern | manage-story-map | capture-story-map |
137
+ |---------|------------------|-------------------|
138
+ | I3 + I4 | Re-validated at every transition | Hard-block at capture |
139
+ | Backbone authoring | Step 7 accepted-transition AskUserQuestion fires | Out of scope (deferred-placeholder rib only) |
140
+ | Status transitions | draft → accepted → in-progress → completed → archived | Out of scope (creation only) |
141
+ | README refresh | Inline per transition (P094 mirror) | Deferred to `manage-story-map review` or `reconcile-story-maps` |
142
+ | Commit grain | One commit per intake / per transition | One commit per capture |
143
+
144
+ ## Related
145
+
146
+ - **ADR-060** — Problem-RFC-Story framework; Phase 2 amendment 2026-05-10 lines 145-189 + encoding amendment 2026-05-12 lines 381-435.
147
+ - **ADR-060 line 145** — I5 no-WSJF-on-maps invariant.
148
+ - **ADR-060 line 339 + ADR-053** — bootstrap-exemption marker contract.
149
+ - **`docs/STYLE-GUIDE.md`** — HTML style rules manage-story-map enforces.
150
+ - **P170** — driver problem ticket.
151
+ - **JTBD-008** — primary anchor.
152
+ - **JTBD-302** — README-currency rule.
153
+ - **`/wr-itil:capture-story-map`** — companion lightweight aside.
154
+ - **`/wr-itil:reconcile-story-maps`** — drift detection + recovery.
155
+ - **manage-story SKILL.md** — sibling at the story tier; manage-story-map mirrors with HTML-encoding adjustments (`<meta>` block parse + backbone-authoring AskUserQuestion + no I7/I8/I9/I10 — those are story-tier invariants).
156
+ - **ADR-052** — behavioural-tests default; `packages/itil/skills/manage-story-map/test/manage-story-map-contract.bats`.
157
+
158
+ $ARGUMENTS
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural contract fixtures for /wr-itil:manage-story-map (P170 Phase 2 Slice 4).
3
+
4
+ setup() {
5
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
6
+ SKILL_FILE="${REPO_ROOT}/packages/itil/skills/manage-story-map/SKILL.md"
7
+ }
8
+
9
+ @test "manage-story-map: SKILL.md exists" {
10
+ [ -f "$SKILL_FILE" ]
11
+ }
12
+
13
+ @test "manage-story-map: SKILL.md frontmatter declares wr-itil:manage-story-map name" {
14
+ run grep -E '^name: wr-itil:manage-story-map$' "$SKILL_FILE"
15
+ [ "$status" -eq 0 ]
16
+ }
17
+
18
+ @test "manage-story-map: SKILL.md declares I3 trace-to-problem invariant" {
19
+ run grep -E 'I3.*trace-to-problem|trace-to-problem.*I3' "$SKILL_FILE"
20
+ [ "$status" -eq 0 ]
21
+ }
22
+
23
+ @test "manage-story-map: SKILL.md declares I4 trace-to-JTBD invariant" {
24
+ run grep -E 'I4.*trace-to-JTBD|trace-to-JTBD.*I4' "$SKILL_FILE"
25
+ [ "$status" -eq 0 ]
26
+ }
27
+
28
+ @test "manage-story-map: SKILL.md declares I5 no-WSJF-leak invariant" {
29
+ run grep -E 'I5.*WSJF|WSJF.*I5|no-WSJF-leak' "$SKILL_FILE"
30
+ [ "$status" -eq 0 ]
31
+ }
32
+
33
+ @test "manage-story-map: SKILL.md names 5 lifecycle states (draft / accepted / in-progress / completed / archived)" {
34
+ for state in draft accepted in-progress completed archived; do
35
+ run grep -E "${state}" "$SKILL_FILE"
36
+ [ "$status" -eq 0 ]
37
+ done
38
+ }
39
+
40
+ @test "manage-story-map: SKILL.md names bootstrap-exemption marker per ADR-060 line 339" {
41
+ run grep -iE 'bootstrap-exempt|ADR-053.*Bootstrapping' "$SKILL_FILE"
42
+ [ "$status" -eq 0 ]
43
+ }
44
+
45
+ @test "manage-story-map: SKILL.md names reverse-trace refresh on problem parents via Story Maps section" {
46
+ run grep -E 'update-problem-references-section\.sh.*Story Maps' "$SKILL_FILE"
47
+ [ "$status" -eq 0 ]
48
+ }
49
+
50
+ @test "manage-story-map: SKILL.md names reverse-trace refresh on JTBD parents via Story Maps section" {
51
+ run grep -E 'update-jtbd-references-section\.sh.*Story Maps' "$SKILL_FILE"
52
+ [ "$status" -eq 0 ]
53
+ }
54
+
55
+ @test "manage-story-map: SKILL.md states map HTML files do NOT carry markdown reverse-trace section themselves (per Slice 7 architect amend finding 2)" {
56
+ run grep -iE 'do NOT carry.*markdown reverse-trace|authored manually|manual.*data-attribute' "$SKILL_FILE"
57
+ [ "$status" -eq 0 ]
58
+ }
59
+
60
+ @test "manage-story-map: argument grammar contains no WSJF token (I5 invariant)" {
61
+ run grep -A 10 '^## Argument grammar' "$SKILL_FILE"
62
+ [[ "$output" != *"WSJF"* ]] && [[ "$output" != *"wsjf"* ]]
63
+ }