@windyroad/itil 0.27.1 → 0.28.0-preview.303

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,227 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural fixtures for /wr-itil:capture-story (P170 Phase 2 Slice 7).
3
+ #
4
+ # Per ADR-052 (Behavioural-tests-default for skill testing), these tests
5
+ # exercise the load-bearing primitives the skill dispatches and assert
6
+ # observable state — NOT the prose contents of SKILL.md.
7
+ #
8
+ # Behavioural surfaces under test:
9
+ # 1. Next-ID computation — capture-story uses the inline
10
+ # max(local, origin) + 1 formula scanning docs/stories/*/STORY-*.md
11
+ # + git ls-tree -r origin/main docs/stories/ per ADR-019 inline
12
+ # collision-guard approved at Slice 3 design review (option a).
13
+ # 2. ID collision-on-origin renumber — when an origin RFC IS not yet
14
+ # pulled locally, the formula must compute the higher of the two
15
+ # and increment from there (no silent collision).
16
+ # 3. Reverse-trace helper "Stories" section-name support — the three
17
+ # Slice 2a/2b helpers (update-problem / update-jtbd / update-rfc
18
+ # -references-section.sh) must accept "Stories" as a section name.
19
+ # 4. Frontmatter shape — a captured story file conforms to ADR-060
20
+ # lines 220-228 (status: draft / story-id / problems / jtbd / etc.).
21
+ # 5. NO update-story-references-section.sh "Stories" path —
22
+ # story-maps are HTML; the story-tier reverse-trace helper does NOT
23
+ # accept "Stories" (per architect amend finding 2 on Slice 7).
24
+ #
25
+ # @problem P170
26
+ # @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes — capture-time
27
+ # decomposition surface)
28
+ # @jtbd JTBD-001 (extended scope — change-set-level governance)
29
+ # @adr ADR-060 (Problem-RFC-Story framework — story tier)
30
+ # @adr ADR-052 (Behavioural-tests-default)
31
+ # @adr ADR-019 (ID collision-on-origin renumber)
32
+ # @adr ADR-014 (single-commit grain)
33
+
34
+ setup() {
35
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
36
+ SKILL_FILE="${REPO_ROOT}/packages/itil/skills/capture-story/SKILL.md"
37
+ HELPER_PROBLEM="${REPO_ROOT}/packages/itil/scripts/update-problem-references-section.sh"
38
+ HELPER_JTBD="${REPO_ROOT}/packages/itil/scripts/update-jtbd-references-section.sh"
39
+ HELPER_RFC="${REPO_ROOT}/packages/itil/scripts/update-rfc-references-section.sh"
40
+ HELPER_STORY="${REPO_ROOT}/packages/itil/scripts/update-story-references-section.sh"
41
+
42
+ TMPROOT=$(mktemp -d)
43
+ ORIG_DIR="$PWD"
44
+ cd "$TMPROOT"
45
+ }
46
+
47
+ teardown() {
48
+ cd "$ORIG_DIR"
49
+ rm -rf "$TMPROOT"
50
+ }
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Surface 0: SKILL.md exists with correct name (minimum discoverability)
54
+ # ---------------------------------------------------------------------------
55
+
56
+ @test "capture-story: SKILL.md exists" {
57
+ [ -f "$SKILL_FILE" ]
58
+ }
59
+
60
+ @test "capture-story: SKILL.md frontmatter declares wr-itil:capture-story name" {
61
+ # Discoverable on / autocomplete depends on the canonical name.
62
+ run grep -E '^name: wr-itil:capture-story$' "$SKILL_FILE"
63
+ [ "$status" -eq 0 ]
64
+ }
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Surface 1: Next-ID computation — inline formula matches capture-rfc precedent
68
+ # (verified against a fixture stories directory).
69
+ # ---------------------------------------------------------------------------
70
+
71
+ @test "capture-story: next-ID formula computes 001 for empty stories directory" {
72
+ mkdir -p docs/stories/draft
73
+ local_max=$(ls docs/stories/*/STORY-*.md 2>/dev/null | sed 's|.*/STORY-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
74
+ origin_max="" # no git fixture; treat as empty
75
+ next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort -n | tail -1) + 1 )))
76
+ [ "$next" = "001" ]
77
+ }
78
+
79
+ @test "capture-story: next-ID formula computes 003 when STORY-002 exists locally" {
80
+ mkdir -p docs/stories/draft docs/stories/in-progress
81
+ touch docs/stories/draft/STORY-002-foo.md
82
+ touch docs/stories/in-progress/STORY-001-bar.md
83
+ local_max=$(ls docs/stories/*/STORY-*.md 2>/dev/null | sed 's|.*/STORY-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
84
+ next=$(printf '%03d' $(( 10#${local_max:-0} + 1 )))
85
+ [ "$next" = "003" ]
86
+ }
87
+
88
+ @test "capture-story: next-ID formula picks max(local, origin) when origin is higher (collision-on-origin renumber)" {
89
+ # Simulates the renumber: local has STORY-002, origin has STORY-005.
90
+ # Formula must pick 005 + 1 = 006, not 002 + 1 = 003 (silent collision).
91
+ local_max=2
92
+ origin_max=5
93
+ next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort -n | tail -1) + 1 )))
94
+ [ "$next" = "006" ]
95
+ }
96
+
97
+ @test "capture-story: next-ID formula picks max(local, origin) when local is higher" {
98
+ # Reverse case: local STORY-007, origin STORY-003. Local wins → 008.
99
+ local_max=7
100
+ origin_max=3
101
+ next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort -n | tail -1) + 1 )))
102
+ [ "$next" = "008" ]
103
+ }
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Surface 2: Reverse-trace helpers accept "Stories" section name
107
+ # (Slice 2a/2b shipped these — verify they're still wired correctly).
108
+ # ---------------------------------------------------------------------------
109
+
110
+ @test "capture-story: update-problem-references-section.sh accepts 'Stories' section name without rejecting" {
111
+ # Set up minimal fixture: a problem file with a Story Maps placeholder
112
+ # section. The helper should accept "Stories" as a valid lookup key
113
+ # (architect finding on Slice 7: the absence of Stories support here
114
+ # would block the inline reverse-trace refresh in capture-story Step 6).
115
+ mkdir -p docs/problems/known-error docs/stories/draft
116
+ cat > docs/problems/known-error/170-test-problem.md <<EOF
117
+ # P170: Test problem
118
+
119
+ ## Story Maps
120
+
121
+ (empty)
122
+
123
+ ## Stories
124
+
125
+ (empty)
126
+ EOF
127
+ run bash "$HELPER_PROBLEM" docs/problems/known-error/170-test-problem.md Stories
128
+ # Acceptable outcomes: success (0) or no-op-with-stderr; rejection (e.g.
129
+ # "unknown section-name") would fail this assertion.
130
+ [[ "$output" != *"unknown section-name"* ]]
131
+ }
132
+
133
+ @test "capture-story: update-jtbd-references-section.sh accepts 'Stories' section name" {
134
+ mkdir -p docs/jtbd/solo-developer docs/stories/draft
135
+ cat > docs/jtbd/solo-developer/JTBD-008-test.proposed.md <<EOF
136
+ # JTBD-008: Test
137
+
138
+ ## Stories
139
+
140
+ (empty)
141
+ EOF
142
+ run bash "$HELPER_JTBD" docs/jtbd/solo-developer/JTBD-008-test.proposed.md Stories
143
+ [[ "$output" != *"unknown section-name"* ]]
144
+ }
145
+
146
+ @test "capture-story: update-rfc-references-section.sh accepts 'Stories' section name" {
147
+ mkdir -p docs/rfcs docs/stories/draft
148
+ cat > docs/rfcs/RFC-002-test.verifying.md <<EOF
149
+ # RFC-002: Test
150
+
151
+ ## Stories
152
+
153
+ (empty)
154
+ EOF
155
+ run bash "$HELPER_RFC" docs/rfcs/RFC-002-test.verifying.md Stories
156
+ [[ "$output" != *"unknown section-name"* ]]
157
+ }
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # Surface 3: NO update-story-references-section.sh "Stories" path
161
+ # (architect amend finding 2 on Slice 7 — story-maps are HTML, no markdown
162
+ # reverse-trace section auto-maintained on the HTML map; capture-story does
163
+ # NOT stage story-map files.)
164
+ # ---------------------------------------------------------------------------
165
+
166
+ @test "capture-story: update-story-references-section.sh does NOT accept 'Stories' section name (architect finding 2 — story-maps are HTML, manually authored)" {
167
+ mkdir -p docs/stories/draft
168
+ cat > docs/stories/draft/STORY-001-test.md <<EOF
169
+ # STORY-001: Test
170
+ EOF
171
+ run bash "$HELPER_STORY" docs/stories/draft/STORY-001-test.md Stories
172
+ # The helper's lookup table supports RFCs + Story Maps but NOT Stories
173
+ # (per architect finding 2). The helper MUST exit non-zero AND name
174
+ # the supported sections in its error stream.
175
+ [ "$status" -ne 0 ]
176
+ [[ "$output" == *"unknown section-name"* ]] || [[ "$output" == *"Supported"* ]]
177
+ }
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # Surface 4: Frontmatter shape — verify ADR-060 lines 220-228 fields
181
+ # round-trip through a minimal capture sequence.
182
+ # ---------------------------------------------------------------------------
183
+
184
+ @test "capture-story: captured frontmatter contains required fields per ADR-060 lines 220-228" {
185
+ # Construct the frontmatter the way the skill prescribes (Step 5).
186
+ mkdir -p docs/stories/draft
187
+ next_id="001"
188
+ slug="test-capture"
189
+ description="Test capture"
190
+ reported=$(date -u +%Y-%m-%d)
191
+ cat > "docs/stories/draft/STORY-${next_id}-${slug}.md" <<EOF
192
+ ---
193
+ status: draft
194
+ story-id: ${slug}
195
+ reported: ${reported}
196
+ decision-makers: [Test]
197
+ problems: [P170]
198
+ jtbd: [JTBD-008]
199
+ rfcs: []
200
+ story-maps: []
201
+ estimated-effort: deferred
202
+ ---
203
+
204
+ # STORY-${next_id}: ${description}
205
+ EOF
206
+ # Assert each required frontmatter field appears (grep is sufficient —
207
+ # ADR-052 prefers behavioural assertion on the observable file state).
208
+ story_file="docs/stories/draft/STORY-${next_id}-${slug}.md"
209
+ for field in status story-id reported decision-makers problems jtbd rfcs story-maps estimated-effort; do
210
+ run grep -E "^${field}:" "$story_file"
211
+ [ "$status" -eq 0 ]
212
+ done
213
+ }
214
+
215
+ @test "capture-story: captured story file lands at docs/stories/draft/ per ADR-060 lines 145-147" {
216
+ # Story-maps use HTML and lifecycle subdirs (draft/accepted/in-progress/
217
+ # completed/archived); stories use markdown and lifecycle subdirs
218
+ # (draft/accepted/in-progress/done/archived). Captured story lands at
219
+ # draft/ per the skill's Step 5 prescription.
220
+ mkdir -p docs/stories/draft
221
+ touch docs/stories/draft/STORY-001-test.md
222
+ [ -f docs/stories/draft/STORY-001-test.md ]
223
+ # Negative assertion: NOT in any other subdir at capture time.
224
+ [ ! -f docs/stories/accepted/STORY-001-test.md ]
225
+ [ ! -f docs/stories/in-progress/STORY-001-test.md ]
226
+ [ ! -f docs/stories/done/STORY-001-test.md ]
227
+ }
@@ -0,0 +1,229 @@
1
+ ---
2
+ name: wr-itil:capture-story-map
3
+ description: Lightweight story-map-capture skill for aside-invocation during foreground work — mandatory leading problem-trace AND JTBD-trace per ADR-060 I3 + I4 invariants, skeleton HTML file at `docs/story-maps/draft/STORY-MAP-NNN-<slug>.html` per ADR-060 § Phase 2 encoding amendment 2026-05-12, single commit per capture, no inline README refresh. Defers full backbone/ribs/slices authoring + lifecycle transitions to /wr-itil:manage-story-map. Use when the user (or agent) wants to capture a new story-map quickly with clear problem + JTBD anchoring.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Capture Story Map Skill
8
+
9
+ Capture a story-map (HTML artefact representing Patton's backbone × ribs × slices layout) quickly during foreground work. Lightweight aside-invocation surface that complements the heavyweight `/wr-itil:manage-story-map` flow. Mirrors `/wr-itil:capture-story` shape per ADR-032 lightweight + heavyweight skill split, applied at the story-map tier with HTML encoding.
10
+
11
+ **Related JTBDs**: JTBD-008 (primary — Decompose a Fix Into Coordinated Changes; story-maps represent the journey-context decomposition), JTBD-001 (extended scope), JTBD-302 (README-currency rule for `docs/story-maps/README.md`).
12
+
13
+ ## When to invoke
14
+
15
+ - **Decomposing a problem or RFC into a journey-shaped layout** — agent / user observes that the fix decomposes into multiple coordinated changes that map onto a user-journey backbone × ribs × slices spatial layout (per Patton's User Story Mapping). Capture the story-map BEFORE individual stories so the spatial-placement context informs story decomposition.
16
+ - **Retrospective story-map for shipped work** — lifting an existing multi-commit decomposition into a story-map artefact (e.g. STORY-MAP-001 retro on P170 Phase 1 + Phase 2 framework code — Slice 14 of P170 Phase 2).
17
+ - **Cross-RFC journey lens** — a single story-map can reference stories from multiple RFCs (the map is a journey-context lens on the story corpus per ADR-060 line 317).
18
+
19
+ **Use `/wr-itil:manage-story-map` instead** when:
20
+ - The work is moving an existing story-map through its lifecycle (draft → accepted → in-progress → completed → archived).
21
+ - The user wants to author or refine the backbone/ribs/slices structure with full intake.
22
+ - Cross-map coordination decisions need to be captured.
23
+
24
+ ## Argument grammar
25
+
26
+ **Positional (both mandatory)**: `<problem-trace> <jtbd-trace> <description>` where:
27
+ - `<problem-trace>` is `P<NNN>` or `P<NNN>,P<NNN>,...`
28
+ - `<jtbd-trace>` is `JTBD-<NNN>` or `JTBD-<NNN>,JTBD-<NNN>,...`
29
+
30
+ ```
31
+ /wr-itil:capture-story-map P170 JTBD-008 RFC framework Phase 1 + Phase 2 bootstrap
32
+ /wr-itil:capture-story-map P170 JTBD-008,JTBD-001 Story map for the P170 RFC framework work
33
+ ```
34
+
35
+ Positional grammar mirrors `/wr-itil:capture-story` shape (footnote per ADR-060 line 285 phrasing — `--problem` / `--jtbd` flag-form was the ADR-exemplar but positional is the lightweight-aside grammar that Claude Code skills support natively).
36
+
37
+ ## Rule 6 audit (per ADR-032 + ADR-013 + ADR-060)
38
+
39
+ | Decision | Resolution | Authority class |
40
+ |----------|-----------|-----------------|
41
+ | Problem-trace presence | I3 hard-block — refuse on missing trace; emit deny log + halt | direction-setting |
42
+ | Problem-trace validation | Mechanical: each `P<NNN>` exists in `docs/problems/`; dual-tolerant lookup | silent-mechanical |
43
+ | JTBD-trace presence | I4 hard-block — refuse on missing trace; emit deny log + halt | direction-setting |
44
+ | JTBD-trace validation | Mechanical: each `JTBD-<NNN>` resolves to a file in `docs/jtbd/` | silent-mechanical |
45
+ | STORY-MAP ID allocation | Mechanical: `max(local, origin) + 1` enumerating `docs/story-maps/*/STORY-MAP-*.html` (ADR-019 inline collision-guard) | silent-mechanical |
46
+ | Title kebab-slug | Mechanical: first 8-10 non-stopword tokens of description | silent-mechanical |
47
+ | Title prose refinement | Optional taste AskUserQuestion; silent-default to derived form | taste |
48
+ | HTML file write | Mechanical: schema per ADR-060 § Phase 2 encoding amendment 2026-05-12 lines 381-435 | silent-mechanical |
49
+ | Reverse-trace `## Story Maps` refresh | Mechanical: inline on driving problem + JTBD files via Slice 2a/2b helpers | silent-mechanical |
50
+ | README refresh | Mechanical: deferred to `/wr-itil:manage-story-map review` or `wr-itil-reconcile-story-maps` | silent-mechanical |
51
+ | Empty arguments | Halt-with-stderr-directive | n/a |
52
+
53
+ ## Steps
54
+
55
+ ### 0. Preflight
56
+
57
+ ```bash
58
+ wr-itil-reconcile-readme docs/problems > /tmp/wr-itil-drift-$$.txt
59
+ reconcile_exit=$?
60
+ # Halt-and-route on drift per the standard pattern.
61
+ ```
62
+
63
+ ### 1. Parse arguments
64
+
65
+ ```bash
66
+ problem_trace="$1"; shift
67
+ jtbd_trace="$1"; shift
68
+ description="$*"
69
+ ```
70
+
71
+ Validate `$problem_trace` matches `^P[0-9]{3}(,P[0-9]{3})*$`. Validate `$jtbd_trace` matches `^JTBD-[0-9]{3}(,JTBD-[0-9]{3})*$`. If `$description` is empty, halt with empty-arguments directive.
72
+
73
+ Derive kebab-case title slug from first 8-10 non-stopword tokens of `$description`.
74
+
75
+ ### 2. Validate problem trace + I3 hard-block
76
+
77
+ For each `P<NNN>`:
78
+
79
+ ```bash
80
+ # Dual-tolerant ticket discovery (RFC-002 migration window).
81
+ trace_files=$(ls docs/problems/<NNN>-*.md docs/problems/*/<NNN>-*.md 2>/dev/null)
82
+ ```
83
+
84
+ **I3 hard-block** (per ADR-060 line 187): trace absent / malformed / unresolved → emit deny log entry to `logs/story-map-capture-denials.jsonl`, halt with stderr directive naming `/wr-itil:capture-problem` as the open-the-driving-problem-first surface.
85
+
86
+ ### 2.5. Validate JTBD trace + I4 hard-block
87
+
88
+ For each `JTBD-<NNN>`:
89
+
90
+ ```bash
91
+ jtbd_file=$(ls docs/jtbd/*/JTBD-<NNN>-*.md 2>/dev/null | head -1)
92
+ ```
93
+
94
+ **I4 hard-block** (per ADR-060 line 188): trace absent / malformed / unresolved → emit deny log + halt. Story-maps without JTBD trace are structurally meaningless per ADR-060 ("a map with no JTBD trace is structurally meaningless"; Patton's central thesis is journey-around-user-value).
95
+
96
+ ### 3. Compute next STORY-MAP ID
97
+
98
+ Inline `max(local, origin) + 1` per ADR-019 collision-guard (architect Slice 3 design review option a — inline-only path, mirrors capture-rfc + capture-story precedent):
99
+
100
+ ```bash
101
+ local_max=$(ls docs/story-maps/*/STORY-MAP-*.html 2>/dev/null | sed 's|.*/STORY-MAP-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
102
+ origin_max=$(git ls-tree -r --name-only origin/main docs/story-maps/ 2>/dev/null | sed 's|.*/STORY-MAP-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
103
+ next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort -n | tail -1) + 1 )))
104
+ ```
105
+
106
+ ### 4. Optional taste prompt for title
107
+
108
+ Same shape as capture-story Step 4 — silent-default when unavailable.
109
+
110
+ ### 5. Write the story-map file
111
+
112
+ **File path**: `docs/story-maps/draft/STORY-MAP-<NNN>-<kebab-title>.html`
113
+
114
+ **Template** (per ADR-060 § Phase 2 encoding amendment 2026-05-12 lines 381-420 + `docs/STYLE-GUIDE.md` rules):
115
+
116
+ ```html
117
+ <!DOCTYPE html>
118
+ <html lang="en">
119
+ <head>
120
+ <meta charset="UTF-8">
121
+ <title>STORY-MAP-<NNN>: <Title></title>
122
+ <meta name="story-map-id" content="STORY-MAP-<NNN>">
123
+ <meta name="status" content="draft">
124
+ <meta name="problems" content="<P<NNN>[,P<NNN>...]>">
125
+ <meta name="rfcs" content="">
126
+ <meta name="jtbd" content="<JTBD-<NNN>[,JTBD-<NNN>...]>">
127
+ <meta name="adrs" content="">
128
+ <meta name="reported" content="<YYYY-MM-DD>">
129
+ <meta name="decision-makers" content="<git config user.name>">
130
+ <style>
131
+ body { font-family: system-ui, sans-serif; max-width: 1200px; margin: 1rem auto; padding: 0 1rem; }
132
+ h1 { font-size: 1.5rem; }
133
+ h2 { font-size: 1.125rem; margin-top: 1.5rem; }
134
+ .backbone { display: grid; grid-template-columns: repeat(var(--cols), 1fr); gap: 1rem; margin-bottom: 2rem; }
135
+ .rib-header { grid-column: 1 / -1; border-bottom: 1px solid #ccc; padding-bottom: 0.25rem; }
136
+ .rib { display: contents; }
137
+ .slice { border: 1px solid #ccc; padding: 0.5rem; text-decoration: none; color: inherit; display: block; }
138
+ .slice:hover { border-color: #666; }
139
+ </style>
140
+ </head>
141
+ <body>
142
+ <h1>STORY-MAP-<NNN>: <Title></h1>
143
+
144
+ <p>(Story-map purpose paragraph — populated at /wr-itil:manage-story-map accepted transition.)</p>
145
+
146
+ <section class="backbone" style="--cols: 1">
147
+ <header class="rib-header">
148
+ <h2 data-rib="placeholder">Backbone — populate at /wr-itil:manage-story-map accepted transition</h2>
149
+ </header>
150
+ <div class="rib">
151
+ <!-- Slice cards as <a class="slice" href="../../stories/<state>/STORY-NNN-<slug>.md"
152
+ data-story-id="STORY-NNN" data-rfc="RFC-NNN" data-jtbd="JTBD-NNN"
153
+ data-status="<draft|accepted|in-progress|done|archived>">Story title</a>
154
+ per docs/story-maps/README.md schema. Populated by manage-story-map.
155
+ -->
156
+ </div>
157
+ </section>
158
+ </body>
159
+ </html>
160
+ ```
161
+
162
+ Per `docs/STYLE-GUIDE.md`: NO inline `style=""` on `<a class="slice">` or `<h2 data-rib>` data-bearing elements; embedded `<style>` block in `<head>` is the only permitted styling source; `--cols` custom-property on `.backbone` is the layout-container exception.
163
+
164
+ ### 6. Single commit — `## Story Maps` reverse-trace refresh
165
+
166
+ **Stage list**: new HTML file PLUS driving problem files (refresh `## Story Maps` section via `update-problem-references-section.sh <file> "Story Maps"`) PLUS driving JTBD files (refresh `## Story Maps` section via `update-jtbd-references-section.sh <file> "Story Maps"`). Do NOT stage `docs/story-maps/README.md` (deferred).
167
+
168
+ ```bash
169
+ for pid_token in $(echo "$problem_trace" | tr ',' ' '); do
170
+ pid_num="${pid_token#P}"
171
+ problem_file=$(ls docs/problems/${pid_num}-*.md docs/problems/*/${pid_num}-*.md 2>/dev/null | head -1)
172
+ [ -z "$problem_file" ] && continue
173
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-problem-references-section.sh" "$problem_file" "Story Maps"
174
+ git add "$problem_file"
175
+ done
176
+
177
+ for jid_token in $(echo "$jtbd_trace" | tr ',' ' '); do
178
+ jtbd_file=$(ls docs/jtbd/*/${jid_token}-*.md 2>/dev/null | head -1)
179
+ [ -z "$jtbd_file" ] && continue
180
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-jtbd-references-section.sh" "$jtbd_file" "Story Maps"
181
+ git add "$jtbd_file"
182
+ done
183
+
184
+ git add docs/story-maps/draft/STORY-MAP-<NNN>-<slug>.html
185
+ ```
186
+
187
+ Commit message:
188
+
189
+ ```
190
+ feat(itil): capture STORY-MAP-<NNN> <title>
191
+
192
+ Refs: STORY-MAP-<NNN>
193
+ ```
194
+
195
+ ### 7. Report
196
+
197
+ After commit, report:
198
+ - New story-map file path + ID.
199
+ - Traced problems + JTBDs.
200
+ - Trailing pointer: `Run /wr-itil:manage-story-map <STORY-MAP-<NNN>> next to author backbone/ribs/slices structure and advance draft → accepted; refresh docs/story-maps/README.md.`
201
+
202
+ ## Composition with manage-story-map
203
+
204
+ | Concern | manage-story-map | capture-story-map |
205
+ |---------|------------------|-------------------|
206
+ | I3 + I4 enforcement | Re-validated at every lifecycle transition | Hard-block at capture-time |
207
+ | I5 no-WSJF-leak | Behavioural test asserts no WSJF field at every transition | Already absent at capture (frontmatter has no WSJF) |
208
+ | Backbone/ribs/slices authoring | Step 7-9 author the spatial layout | Deferred-placeholder pattern; one rib placeholder only |
209
+ | Status transitions | draft → accepted → in-progress → completed → archived | Out of scope (creation only) |
210
+ | README refresh | Inline per transition | Deferred to `/wr-itil:manage-story-map review` or `wr-itil-reconcile-story-maps` |
211
+ | Commit grain | One commit per intake / per transition | One commit per capture |
212
+
213
+ ## Related
214
+
215
+ - **ADR-060** — Problem-RFC-Story framework + Phase 2 amendment 2026-05-10 + encoding amendment 2026-05-12.
216
+ - **ADR-060 lines 145-189** — story-map tier spec + I3-I5 invariants.
217
+ - **ADR-060 lines 381-435** — HTML encoding schema (the source-of-truth for the file template).
218
+ - **`docs/STYLE-GUIDE.md`** — story-map HTML style rules (prohibited inline `style=""` on data-bearing elements).
219
+ - **`docs/VOICE-AND-TONE.md`** — story-map prose guidance (HTML content section).
220
+ - **`docs/story-maps/README.md`** — story-map tier lifecycle index + schema spec.
221
+ - **P170** — driver problem ticket.
222
+ - **JTBD-008** — Decompose a Fix Into Coordinated Changes. Primary persona-anchor.
223
+ - **JTBD-302** — Trust That the README Describes the Plugin I Just Installed (README-currency rule for `docs/story-maps/README.md`).
224
+ - **ADR-032** — governance-skill aside-invocation pattern.
225
+ - **ADR-049** — bin/ on PATH; `wr-itil-reconcile-story-maps` shim ships in Slice 5.
226
+ - **ADR-052** — behavioural-tests default. Bats at `packages/itil/skills/capture-story-map/test/capture-story-map-behavioural.bats`.
227
+ - **Capture-story precedent** — `packages/itil/skills/capture-story/SKILL.md` — sibling skill at the story tier; capture-story-map mirrors with story-map-tier extensions (HTML encoding, no optional --rfc / --story-map flags).
228
+
229
+ $ARGUMENTS
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural fixtures for /wr-itil:capture-story-map (P170 Phase 2 Slice 3 — ADR-060).
3
+ #
4
+ # Mirrors capture-story-behavioural.bats with story-map-tier adjustments
5
+ # (HTML encoding, I3 + I4 invariants instead of I6 + I9, no optional
6
+ # --rfc / --story-map flags).
7
+
8
+ setup() {
9
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
10
+ SKILL_FILE="${REPO_ROOT}/packages/itil/skills/capture-story-map/SKILL.md"
11
+ HELPER_PROBLEM="${REPO_ROOT}/packages/itil/scripts/update-problem-references-section.sh"
12
+ HELPER_JTBD="${REPO_ROOT}/packages/itil/scripts/update-jtbd-references-section.sh"
13
+
14
+ TMPROOT=$(mktemp -d)
15
+ ORIG_DIR="$PWD"
16
+ cd "$TMPROOT"
17
+ }
18
+
19
+ teardown() {
20
+ cd "$ORIG_DIR"
21
+ rm -rf "$TMPROOT"
22
+ }
23
+
24
+ @test "capture-story-map: SKILL.md exists" {
25
+ [ -f "$SKILL_FILE" ]
26
+ }
27
+
28
+ @test "capture-story-map: SKILL.md frontmatter declares wr-itil:capture-story-map name" {
29
+ run grep -E '^name: wr-itil:capture-story-map$' "$SKILL_FILE"
30
+ [ "$status" -eq 0 ]
31
+ }
32
+
33
+ @test "capture-story-map: SKILL.md names I3 trace-to-problem invariant" {
34
+ run grep -E 'I3 hard-block|I3.*trace-to-problem' "$SKILL_FILE"
35
+ [ "$status" -eq 0 ]
36
+ }
37
+
38
+ @test "capture-story-map: SKILL.md names I4 trace-to-JTBD invariant" {
39
+ run grep -E 'I4 hard-block|I4.*trace-to-JTBD' "$SKILL_FILE"
40
+ [ "$status" -eq 0 ]
41
+ }
42
+
43
+ @test "capture-story-map: next-ID formula computes 001 for empty story-maps directory" {
44
+ mkdir -p docs/story-maps/draft
45
+ local_max=$(ls docs/story-maps/*/STORY-MAP-*.html 2>/dev/null | sed 's|.*/STORY-MAP-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
46
+ next=$(printf '%03d' $(( 10#${local_max:-0} + 1 )))
47
+ [ "$next" = "001" ]
48
+ }
49
+
50
+ @test "capture-story-map: next-ID formula computes 003 when STORY-MAP-002 exists locally" {
51
+ mkdir -p docs/story-maps/draft docs/story-maps/in-progress
52
+ touch docs/story-maps/draft/STORY-MAP-002-foo.html
53
+ touch docs/story-maps/in-progress/STORY-MAP-001-bar.html
54
+ local_max=$(ls docs/story-maps/*/STORY-MAP-*.html 2>/dev/null | sed 's|.*/STORY-MAP-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
55
+ next=$(printf '%03d' $(( 10#${local_max:-0} + 1 )))
56
+ [ "$next" = "003" ]
57
+ }
58
+
59
+ @test "capture-story-map: update-problem-references-section.sh accepts 'Story Maps' section name" {
60
+ mkdir -p docs/problems/known-error docs/story-maps/draft
61
+ cat > docs/problems/known-error/170-test.md <<'EOF'
62
+ # P170: Test
63
+
64
+ ## Story Maps
65
+
66
+ (empty)
67
+ EOF
68
+ run bash "$HELPER_PROBLEM" docs/problems/known-error/170-test.md "Story Maps"
69
+ [[ "$output" != *"unknown section-name"* ]]
70
+ }
71
+
72
+ @test "capture-story-map: update-jtbd-references-section.sh accepts 'Story Maps' section name" {
73
+ mkdir -p docs/jtbd/solo-developer docs/story-maps/draft
74
+ cat > docs/jtbd/solo-developer/JTBD-008-test.proposed.md <<'EOF'
75
+ # JTBD-008: Test
76
+
77
+ ## Story Maps
78
+
79
+ (empty)
80
+ EOF
81
+ run bash "$HELPER_JTBD" docs/jtbd/solo-developer/JTBD-008-test.proposed.md "Story Maps"
82
+ [[ "$output" != *"unknown section-name"* ]]
83
+ }
84
+
85
+ @test "capture-story-map: SKILL.md prescribes HTML encoding per ADR-060 amendment 2026-05-12" {
86
+ run grep -E 'HTML5|<!DOCTYPE html>|encoding amendment 2026-05-12' "$SKILL_FILE"
87
+ [ "$status" -eq 0 ]
88
+ }
89
+
90
+ @test "capture-story-map: SKILL.md prescribes draft/ landing subdir" {
91
+ run grep -E 'docs/story-maps/draft/STORY-MAP' "$SKILL_FILE"
92
+ [ "$status" -eq 0 ]
93
+ }
94
+
95
+ @test "capture-story-map: SKILL.md prohibits inline style on data-bearing elements per STYLE-GUIDE.md" {
96
+ run grep -iE 'NO inline.*style.*data-bearing|prohibited inline.*style' "$SKILL_FILE"
97
+ [ "$status" -eq 0 ]
98
+ }