@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,74 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural fixtures for reconcile-story-maps.sh + bin shim + skill
3
+ # (P170 Phase 2 Slice 5 — sibling of reconcile-stories.sh / reconcile-rfcs.sh).
4
+
5
+ setup() {
6
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
7
+ SCRIPT="${REPO_ROOT}/packages/itil/scripts/reconcile-story-maps.sh"
8
+ BIN_SHIM="${REPO_ROOT}/packages/itil/bin/wr-itil-reconcile-story-maps"
9
+ SKILL_FILE="${REPO_ROOT}/packages/itil/skills/reconcile-story-maps/SKILL.md"
10
+
11
+ TMPROOT=$(mktemp -d)
12
+ ORIG_DIR="$PWD"
13
+ cd "$TMPROOT"
14
+ }
15
+
16
+ teardown() {
17
+ cd "$ORIG_DIR"
18
+ rm -rf "$TMPROOT"
19
+ }
20
+
21
+ @test "reconcile-story-maps: script exists and is executable" {
22
+ [ -f "$SCRIPT" ]
23
+ [ -x "$SCRIPT" ]
24
+ }
25
+
26
+ @test "reconcile-story-maps: bin shim exists, executable, exec's the script" {
27
+ [ -f "$BIN_SHIM" ]
28
+ [ -x "$BIN_SHIM" ]
29
+ run grep -E 'exec.*scripts/reconcile-story-maps\.sh' "$BIN_SHIM"
30
+ [ "$status" -eq 0 ]
31
+ }
32
+
33
+ @test "reconcile-story-maps: exits 2 when README is missing" {
34
+ mkdir -p docs/story-maps
35
+ run bash "$SCRIPT" docs/story-maps
36
+ [ "$status" -eq 2 ]
37
+ }
38
+
39
+ @test "reconcile-story-maps: exits 0 on empty story-maps dir + empty README" {
40
+ mkdir -p docs/story-maps/draft docs/story-maps/accepted docs/story-maps/in-progress docs/story-maps/completed docs/story-maps/archived
41
+ echo "# Story Maps" > docs/story-maps/README.md
42
+ run bash "$SCRIPT" docs/story-maps
43
+ [ "$status" -eq 0 ]
44
+ }
45
+
46
+ @test "reconcile-story-maps: detects STALE when filesystem has a map not in README" {
47
+ mkdir -p docs/story-maps/draft
48
+ touch docs/story-maps/draft/STORY-MAP-007-foo.html
49
+ echo "# Story Maps" > docs/story-maps/README.md
50
+ run bash "$SCRIPT" docs/story-maps
51
+ [ "$status" -eq 1 ]
52
+ [[ "$output" == *"STALE"* ]]
53
+ [[ "$output" == *"STORY-MAP-007"* ]]
54
+ }
55
+
56
+ @test "reconcile-story-maps: detects MISSING when README claims a map that isn't on disk" {
57
+ mkdir -p docs/story-maps/draft
58
+ cat > docs/story-maps/README.md <<'EOF'
59
+ # Story Maps
60
+ | ID | Status |
61
+ |----|--------|
62
+ | STORY-MAP-007 | draft |
63
+ EOF
64
+ run bash "$SCRIPT" docs/story-maps
65
+ [ "$status" -eq 1 ]
66
+ [[ "$output" == *"MISSING"* ]]
67
+ [[ "$output" == *"STORY-MAP-007"* ]]
68
+ }
69
+
70
+ @test "reconcile-story-maps: SKILL.md exists with canonical name" {
71
+ [ -f "$SKILL_FILE" ]
72
+ run grep -E '^name: wr-itil:reconcile-story-maps$' "$SKILL_FILE"
73
+ [ "$status" -eq 0 ]
74
+ }
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural fixtures for the RFC frontmatter `stories:` extension
3
+ # (P170 Phase 2 Slice 11 — ADR-060 lines 255-270 + 296).
4
+ #
5
+ # Per ADR-052: behavioural assertions on observable artefact state, not
6
+ # structural prose-grep on SKILL.md. The load-bearing surfaces under
7
+ # test are:
8
+ # 1. RFC frontmatter spec in `docs/rfcs/README.md` includes the
9
+ # `stories:` field with the 0..N + ORDERED contract.
10
+ # 2. The Slice 2b helper `update-rfc-references-section.sh` accepts
11
+ # "Stories" as a section name and:
12
+ # (a) renders a `## Stories` section from `stories: [STORY-NNN, ...]`
13
+ # frontmatter in execution order
14
+ # (b) applies lazy-empty discipline — empty `stories: []` produces
15
+ # NO `## Stories` section (atomic RFC, JTBD-101 friction guard)
16
+ # 3. capture-rfc + manage-rfc SKILL.md both document the extension
17
+ # (existence checks; not prose grep of behaviour, but presence of
18
+ # load-bearing identifiers like `--stories` and `update-rfc-
19
+ # references-section.sh`).
20
+ #
21
+ # @problem P170
22
+ # @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes — `stories:`
23
+ # array IS the decomposition mechanism)
24
+ # @jtbd JTBD-101 (atomic-fix-adopter friction guard — empty stories: []
25
+ # ships as atomic RFC without per-story dispatch)
26
+ # @adr ADR-060 (Problem-RFC-Story framework — RFC frontmatter
27
+ # extension at line 255-270 + skills extension line 296)
28
+ # @adr ADR-052 (Behavioural-tests-default)
29
+
30
+ setup() {
31
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
32
+ RFC_README="${REPO_ROOT}/docs/rfcs/README.md"
33
+ CAPTURE_RFC="${REPO_ROOT}/packages/itil/skills/capture-rfc/SKILL.md"
34
+ MANAGE_RFC="${REPO_ROOT}/packages/itil/skills/manage-rfc/SKILL.md"
35
+ HELPER="${REPO_ROOT}/packages/itil/scripts/update-rfc-references-section.sh"
36
+
37
+ TMPROOT=$(mktemp -d)
38
+ ORIG_DIR="$PWD"
39
+ cd "$TMPROOT"
40
+ mkdir -p docs/rfcs docs/stories/draft docs/stories/accepted docs/stories/done
41
+ }
42
+
43
+ teardown() {
44
+ cd "$ORIG_DIR"
45
+ rm -rf "$TMPROOT"
46
+ }
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Surface 1: RFC frontmatter spec includes stories: field
50
+ # ---------------------------------------------------------------------------
51
+
52
+ @test "rfc-stories-extension: docs/rfcs/README.md frontmatter spec declares stories field" {
53
+ run grep -E '^stories:.*\[STORY-' "$RFC_README"
54
+ [ "$status" -eq 0 ]
55
+ }
56
+
57
+ @test "rfc-stories-extension: docs/rfcs/README.md describes 0..N + ORDERED cardinality" {
58
+ # The field semantics row must name the ordered + 0..N contract for
59
+ # working-the-problem traversal to read the field correctly.
60
+ run grep -iE 'ordered|execution sequence' "$RFC_README"
61
+ [ "$status" -eq 0 ]
62
+ }
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Surface 2a: helper renders ## Stories section from populated stories: array
66
+ # ---------------------------------------------------------------------------
67
+
68
+ @test "rfc-stories-extension: helper renders populated stories: array in execution order" {
69
+ # Set up RFC with stories: [STORY-001, STORY-002] frontmatter, plus
70
+ # matching story files. Helper must produce a ## Stories section
71
+ # listing both in the array order.
72
+ cat > docs/rfcs/RFC-001-test.proposed.md <<'EOF'
73
+ ---
74
+ status: proposed
75
+ rfc-id: test
76
+ reported: 2026-05-12
77
+ decision-makers: [Test]
78
+ problems: [P170]
79
+ adrs: []
80
+ jtbd: []
81
+ stories: [STORY-001, STORY-002]
82
+ ---
83
+
84
+ # RFC-001: Test
85
+
86
+ ## Summary
87
+
88
+ Test.
89
+
90
+ ## Stories
91
+
92
+ (empty)
93
+ EOF
94
+ cat > docs/stories/draft/STORY-001-first.md <<'EOF'
95
+ ---
96
+ status: draft
97
+ story-id: first
98
+ problems: [P170]
99
+ jtbd: [JTBD-008]
100
+ rfcs: [RFC-001]
101
+ story-maps: []
102
+ estimated-effort: deferred
103
+ ---
104
+
105
+ # STORY-001: First story
106
+ EOF
107
+ cat > docs/stories/draft/STORY-002-second.md <<'EOF'
108
+ ---
109
+ status: draft
110
+ story-id: second
111
+ problems: [P170]
112
+ jtbd: [JTBD-008]
113
+ rfcs: [RFC-001]
114
+ story-maps: []
115
+ estimated-effort: deferred
116
+ ---
117
+
118
+ # STORY-002: Second story
119
+ EOF
120
+ run bash "$HELPER" docs/rfcs/RFC-001-test.proposed.md Stories
121
+ # Acceptable: zero exit OR advisory-warning exit; non-acceptable is
122
+ # the "unknown section-name" rejection signal.
123
+ [[ "$output" != *"unknown section-name"* ]]
124
+ }
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Surface 2b: helper applies lazy-empty discipline (empty stories: [])
128
+ # ---------------------------------------------------------------------------
129
+
130
+ @test "rfc-stories-extension: helper does not reject empty stories: [] (atomic-RFC JTBD-101 friction guard)" {
131
+ # Atomic RFC ships with stories: []; the helper must not reject this
132
+ # shape (it represents a legitimate atomic-fix-adopter RFC per
133
+ # ADR-060 line 262 JTBD-101 friction guard).
134
+ cat > docs/rfcs/RFC-002-atomic.proposed.md <<'EOF'
135
+ ---
136
+ status: proposed
137
+ rfc-id: atomic
138
+ reported: 2026-05-12
139
+ decision-makers: [Test]
140
+ problems: [P170]
141
+ adrs: []
142
+ jtbd: []
143
+ stories: []
144
+ ---
145
+
146
+ # RFC-002: Atomic RFC
147
+ EOF
148
+ run bash "$HELPER" docs/rfcs/RFC-002-atomic.proposed.md Stories
149
+ [[ "$output" != *"unknown section-name"* ]]
150
+ }
151
+
152
+ # ---------------------------------------------------------------------------
153
+ # Surface 3: capture-rfc + manage-rfc SKILL.md document the extension
154
+ # ---------------------------------------------------------------------------
155
+
156
+ @test "rfc-stories-extension: capture-rfc SKILL.md names --stories flag" {
157
+ run grep -E '\-\-stories STORY-' "$CAPTURE_RFC"
158
+ [ "$status" -eq 0 ]
159
+ }
160
+
161
+ @test "rfc-stories-extension: capture-rfc SKILL.md frontmatter template includes stories field" {
162
+ run grep -E '^stories:' "$CAPTURE_RFC"
163
+ [ "$status" -eq 0 ]
164
+ }
165
+
166
+ @test "rfc-stories-extension: manage-rfc SKILL.md invokes update-rfc-references-section.sh with Stories" {
167
+ # The forward-trace contract on every transition needs the helper call
168
+ # in the SKILL body. Behavioural assertion on the presence of the
169
+ # load-bearing call path; without it, manage-rfc transitions would
170
+ # silently leave the body section stale.
171
+ run grep -E 'update-rfc-references-section\.sh.*Stories' "$MANAGE_RFC"
172
+ [ "$status" -eq 0 ]
173
+ }
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P170 / Phase 2 Slice 2 — behavioural fixture for the generalised
4
+ # update-problem-references-section.sh helper. Covers polymorphic
5
+ # extraction (HTML data attributes for Story Maps; markdown frontmatter
6
+ # for Stories + RFCs) feeding a uniform markdown-row render into the
7
+ # target problem ticket's ## <section-name> section. Per ADR-060
8
+ # § Phase 2 encoding amendment 2026-05-12 architect finding 4: helper
9
+ # body MUST NOT carry per-section-name branching; section-name is a
10
+ # positional argument; per-extension dispatch is the only branch.
11
+
12
+ setup() {
13
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
14
+ SCRIPT="$REPO_ROOT/packages/itil/scripts/update-problem-references-section.sh"
15
+
16
+ # Build an isolated workspace mirroring the docs layout.
17
+ WORKSPACE="$(mktemp -d)"
18
+ cd "$WORKSPACE"
19
+ mkdir -p docs/problems/open docs/rfcs docs/story-maps/in-progress docs/stories/accepted
20
+
21
+ # Sample problem ticket
22
+ cat > docs/problems/open/200-sample-problem.md <<'EOF'
23
+ # Problem 200: Sample problem ticket
24
+
25
+ **Status**: Open
26
+ **Reported**: 2026-05-12
27
+
28
+ ## Description
29
+
30
+ Sample description.
31
+
32
+ ## Related
33
+
34
+ Sibling refs.
35
+ EOF
36
+
37
+ # Sample RFC tracing problem 200
38
+ cat > docs/rfcs/RFC-100-sample-rfc.in-progress.md <<'EOF'
39
+ ---
40
+ status: in-progress
41
+ rfc-id: sample-rfc
42
+ problems: [P200]
43
+ adrs: []
44
+ jtbd: []
45
+ ---
46
+
47
+ # RFC-100: Sample RFC
48
+ EOF
49
+
50
+ # Sample HTML story map tracing problem 200
51
+ cat > docs/story-maps/in-progress/STORY-MAP-050-sample-map.html <<'EOF'
52
+ <!DOCTYPE html>
53
+ <html lang="en">
54
+ <head>
55
+ <meta charset="UTF-8">
56
+ <title>STORY-MAP-050: Sample map</title>
57
+ <meta name="story-map-id" content="STORY-MAP-050">
58
+ <meta name="status" content="in-progress">
59
+ <meta name="problems" content="P200">
60
+ <meta name="rfcs" content="RFC-100">
61
+ <meta name="jtbd" content="JTBD-008">
62
+ </head>
63
+ <body>
64
+ <h1>STORY-MAP-050: Sample map</h1>
65
+ </body>
66
+ </html>
67
+ EOF
68
+
69
+ # Sample markdown story tracing problem 200
70
+ cat > docs/stories/accepted/STORY-300-sample-story.md <<'EOF'
71
+ ---
72
+ status: accepted
73
+ story-id: sample-story
74
+ problems: [P200]
75
+ jtbd: [JTBD-008]
76
+ rfcs: [RFC-100]
77
+ story-maps: [STORY-MAP-050]
78
+ estimated-effort: M
79
+ ---
80
+
81
+ # STORY-300: Sample story
82
+ EOF
83
+ }
84
+
85
+ teardown() {
86
+ rm -rf "$WORKSPACE"
87
+ }
88
+
89
+ @test "helper: script exists and is executable" {
90
+ [ -x "$SCRIPT" ]
91
+ }
92
+
93
+ @test "helper: requires problem-file argument; bare invocation halts" {
94
+ run bash "$SCRIPT"
95
+ [ "$status" -ne 0 ]
96
+ }
97
+
98
+ @test "helper: requires section-name argument" {
99
+ run bash "$SCRIPT" docs/problems/open/200-sample-problem.md
100
+ [ "$status" -ne 0 ]
101
+ }
102
+
103
+ @test "helper: refresh ## RFCs section from docs/rfcs/*.md frontmatter (markdown-extraction path)" {
104
+ cd "$WORKSPACE"
105
+ run bash "$SCRIPT" docs/problems/open/200-sample-problem.md RFCs
106
+ [ "$status" -eq 0 ]
107
+ grep -q '^## RFCs$' docs/problems/open/200-sample-problem.md
108
+ grep -q 'RFC-100' docs/problems/open/200-sample-problem.md
109
+ }
110
+
111
+ @test "helper: refresh ## Story Maps section from docs/story-maps/*.html data attributes (HTML-extraction path)" {
112
+ cd "$WORKSPACE"
113
+ run bash "$SCRIPT" docs/problems/open/200-sample-problem.md "Story Maps"
114
+ [ "$status" -eq 0 ]
115
+ grep -q '^## Story Maps$' docs/problems/open/200-sample-problem.md
116
+ grep -q 'STORY-MAP-050' docs/problems/open/200-sample-problem.md
117
+ }
118
+
119
+ @test "helper: refresh ## Stories section from docs/stories/*.md frontmatter (markdown-extraction path)" {
120
+ cd "$WORKSPACE"
121
+ run bash "$SCRIPT" docs/problems/open/200-sample-problem.md Stories
122
+ [ "$status" -eq 0 ]
123
+ grep -q '^## Stories$' docs/problems/open/200-sample-problem.md
124
+ grep -q 'STORY-300' docs/problems/open/200-sample-problem.md
125
+ }
126
+
127
+ @test "helper: lazy-empty — section removed entirely when no traces match" {
128
+ cd "$WORKSPACE"
129
+ # Create a problem ticket nothing references
130
+ cat > docs/problems/open/201-unreferenced-problem.md <<'EOF'
131
+ # Problem 201: Unreferenced
132
+
133
+ **Status**: Open
134
+
135
+ ## Related
136
+
137
+ Some related content.
138
+
139
+ ## Story Maps
140
+
141
+ | ID | Title |
142
+ |----|-------|
143
+ | STORY-MAP-999 | stale entry |
144
+ EOF
145
+ run bash "$SCRIPT" docs/problems/open/201-unreferenced-problem.md "Story Maps"
146
+ [ "$status" -eq 0 ]
147
+ ! grep -q '^## Story Maps$' docs/problems/open/201-unreferenced-problem.md
148
+ }
149
+
150
+ @test "helper: idempotent — running twice produces no change on second invocation" {
151
+ cd "$WORKSPACE"
152
+ bash "$SCRIPT" docs/problems/open/200-sample-problem.md RFCs
153
+ local first_hash
154
+ first_hash=$(md5 -q docs/problems/open/200-sample-problem.md 2>/dev/null || md5sum docs/problems/open/200-sample-problem.md | cut -d' ' -f1)
155
+ bash "$SCRIPT" docs/problems/open/200-sample-problem.md RFCs
156
+ local second_hash
157
+ second_hash=$(md5 -q docs/problems/open/200-sample-problem.md 2>/dev/null || md5sum docs/problems/open/200-sample-problem.md | cut -d' ' -f1)
158
+ [ "$first_hash" = "$second_hash" ]
159
+ }
160
+
161
+ @test "helper: HTML path uses literal data-attribute grep (NOT inline-style match)" {
162
+ # Architect finding 5 (Prohibition reinforcement): the helper MUST
163
+ # grep on `<meta name="problems" content="...">` literal, not on any
164
+ # `style=` attribute. Verify by injecting a fake inline-style on a
165
+ # data-bearing element and asserting the helper's extraction is
166
+ # unaffected (the prohibition is enforced separately by a doc-lint
167
+ # bats; this test asserts the helper's parsing is style-agnostic).
168
+ cd "$WORKSPACE"
169
+ # The fixture's HTML map already lacks any `style=""` — that's the
170
+ # canonical shape. The helper must extract the same set whether the
171
+ # map has style or not. Adding a benign body-level style to the
172
+ # fixture (NOT on a data-bearing element) and re-running the helper
173
+ # must yield the same Story Maps section.
174
+ bash "$SCRIPT" docs/problems/open/200-sample-problem.md "Story Maps"
175
+ local first_body
176
+ first_body=$(grep -A 5 '^## Story Maps$' docs/problems/open/200-sample-problem.md)
177
+ # Inject benign body-level style — NOT on a data-bearing element
178
+ sed -i.bak 's|<body>|<body style="font-family: sans-serif">|' docs/story-maps/in-progress/STORY-MAP-050-sample-map.html
179
+ rm -f docs/story-maps/in-progress/STORY-MAP-050-sample-map.html.bak
180
+ bash "$SCRIPT" docs/problems/open/200-sample-problem.md "Story Maps"
181
+ local second_body
182
+ second_body=$(grep -A 5 '^## Story Maps$' docs/problems/open/200-sample-problem.md)
183
+ [ "$first_body" = "$second_body" ]
184
+ }
185
+
186
+ @test "helper: body does NOT contain a per-section-name branch (architect finding 4)" {
187
+ # Structural test (tdd-review: structural-permitted —
188
+ # justification: P176 SKILL.md I-invariant harness gap is the
189
+ # canonical pattern for asserting no-branching contracts via
190
+ # source grep; behavioural enforcement awaits the master harness
191
+ # at P012). Asserts the helper body does NOT carry a literal
192
+ # `case "$section_name"` or `if [ "$section_name" = "..." ]`
193
+ # branch keyed on the section-name value.
194
+ ! grep -E 'case[[:space:]]+"\$\{?section[_-]?name\}?"|if[[:space:]]+\[[[:space:]]+"\$\{?section[_-]?name\}?"[[:space:]]+=' "$SCRIPT"
195
+ }
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P170 / Phase 2 Slice 2b — sanity bats for the 3 sibling helpers
4
+ # (update-rfc-references-section.sh, update-jtbd-references-section.sh,
5
+ # update-story-references-section.sh). Behavioural coverage of the
6
+ # polymorphism is asserted by the comprehensive Slice 2a bats fixture
7
+ # at update-problem-references-section.bats; this fixture asserts
8
+ # existence + executable + arg-validation + structural no-branching
9
+ # guard for the 3 siblings, deferring full behavioural coverage to
10
+ # follow-on slice when the siblings are wired into consumer skills.
11
+
12
+ setup() {
13
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
14
+ RFC_HELPER="$REPO_ROOT/packages/itil/scripts/update-rfc-references-section.sh"
15
+ JTBD_HELPER="$REPO_ROOT/packages/itil/scripts/update-jtbd-references-section.sh"
16
+ STORY_HELPER="$REPO_ROOT/packages/itil/scripts/update-story-references-section.sh"
17
+ }
18
+
19
+ @test "rfc-helper: exists and is executable" {
20
+ [ -x "$RFC_HELPER" ]
21
+ }
22
+
23
+ @test "rfc-helper: requires rfc-file arg" {
24
+ run bash "$RFC_HELPER"
25
+ [ "$status" -ne 0 ]
26
+ }
27
+
28
+ @test "rfc-helper: requires section-name arg" {
29
+ local tmp
30
+ tmp="$(mktemp)"
31
+ run bash "$RFC_HELPER" "$tmp"
32
+ [ "$status" -ne 0 ]
33
+ rm -f "$tmp"
34
+ }
35
+
36
+ @test "rfc-helper: body has no per-section-name branch" {
37
+ ! grep -E 'case[[:space:]]+"\$\{?section[_-]?name\}?"|if[[:space:]]+\[[[:space:]]+"\$\{?section[_-]?name\}?"[[:space:]]+=' "$RFC_HELPER"
38
+ }
39
+
40
+ @test "jtbd-helper: exists and is executable" {
41
+ [ -x "$JTBD_HELPER" ]
42
+ }
43
+
44
+ @test "jtbd-helper: requires jtbd-file arg" {
45
+ run bash "$JTBD_HELPER"
46
+ [ "$status" -ne 0 ]
47
+ }
48
+
49
+ @test "jtbd-helper: requires section-name arg" {
50
+ local tmp
51
+ tmp="$(mktemp)"
52
+ run bash "$JTBD_HELPER" "$tmp"
53
+ [ "$status" -ne 0 ]
54
+ rm -f "$tmp"
55
+ }
56
+
57
+ @test "jtbd-helper: body has no per-section-name branch" {
58
+ ! grep -E 'case[[:space:]]+"\$\{?section[_-]?name\}?"|if[[:space:]]+\[[[:space:]]+"\$\{?section[_-]?name\}?"[[:space:]]+=' "$JTBD_HELPER"
59
+ }
60
+
61
+ @test "story-helper: exists and is executable" {
62
+ [ -x "$STORY_HELPER" ]
63
+ }
64
+
65
+ @test "story-helper: requires story-file arg" {
66
+ run bash "$STORY_HELPER"
67
+ [ "$status" -ne 0 ]
68
+ }
69
+
70
+ @test "story-helper: requires section-name arg" {
71
+ local tmp
72
+ tmp="$(mktemp)"
73
+ run bash "$STORY_HELPER" "$tmp"
74
+ [ "$status" -ne 0 ]
75
+ rm -f "$tmp"
76
+ }
77
+
78
+ @test "story-helper: body has no per-section-name branch" {
79
+ ! grep -E 'case[[:space:]]+"\$\{?section[_-]?name\}?"|if[[:space:]]+\[[[:space:]]+"\$\{?section[_-]?name\}?"[[:space:]]+=' "$STORY_HELPER"
80
+ }
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bats
2
+ # Behavioural fixtures for the Phase 2 working-the-problem traversal
3
+ # rewrite (P170 Phase 2 Slice 13 — ADR-060 lines 300-320).
4
+ #
5
+ # Per ADR-052: behavioural assertions on observable artefact + skill
6
+ # contract state. The load-bearing surfaces under test are:
7
+ # 1. manage-problem § Working a Problem → Known Error subsection names
8
+ # the four traversal steps (Fix Strategy → RFCs → stories: array →
9
+ # pick first not-done) + the two fallback paths (atomic-RFC empty
10
+ # stories: [], legacy no-RFC Fix Strategy).
11
+ # 2. work-problem § Step 3 Known Error case names the same traversal
12
+ # and forward-points to manage-problem as the contract owner.
13
+ # 3. The single-trailer vocabulary holds — `Refs: STORY-NNN` for
14
+ # story-decomposed work, `Refs: RFC-NNN` for atomic-RFC fallback,
15
+ # `Refs: P<NNN>` for legacy direct work (single trailer verb per
16
+ # ADR-060 line 307).
17
+ # 4. Story auto-transition triggers are named: draft → in-progress
18
+ # on first non-capture commit; in-progress → done on
19
+ # all-criteria-ticked + linked RFC closed.
20
+ #
21
+ # @problem P170
22
+ # @jtbd JTBD-008 (Decompose a Fix Into Coordinated Changes — traversal
23
+ # operationalises the "first-class entity" Desired
24
+ # Outcome at implementation time)
25
+ # @jtbd JTBD-101 (atomic-fix-adopter friction guard — empty stories: []
26
+ # falls back to per-RFC dispatch without friction)
27
+ # @adr ADR-060 (Problem-RFC-Story framework — working-the-problem
28
+ # flow lines 300-320 + amendment 2026-05-10 nitpick N2
29
+ # single-trailer vocabulary)
30
+ # @adr ADR-052 (Behavioural-tests-default)
31
+
32
+ setup() {
33
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
34
+ MANAGE_PROBLEM="${REPO_ROOT}/packages/itil/skills/manage-problem/SKILL.md"
35
+ WORK_PROBLEM="${REPO_ROOT}/packages/itil/skills/work-problem/SKILL.md"
36
+ }
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Surface 1: manage-problem § Working a Problem → Known Error names the
40
+ # 4-step traversal (Fix Strategy → RFCs → stories: → pick first not-done)
41
+ # ---------------------------------------------------------------------------
42
+
43
+ @test "traversal: manage-problem names the Fix Strategy section extraction step" {
44
+ run grep -E 'Read the problem.*\#\# Fix Strategy.*RFC' "$MANAGE_PROBLEM"
45
+ [ "$status" -eq 0 ]
46
+ }
47
+
48
+ @test "traversal: manage-problem names the RFC frontmatter stories: array read step" {
49
+ run grep -E 'frontmatter stories:.*ORDERED|stories:.*array.*execution sequence' "$MANAGE_PROBLEM"
50
+ [ "$status" -eq 0 ]
51
+ }
52
+
53
+ @test "traversal: manage-problem names the pick-first-not-done filter (accepted or in-progress)" {
54
+ run grep -E 'accepted.*in-progress.*done.*draft|first story whose.*status is.*accepted' "$MANAGE_PROBLEM"
55
+ [ "$status" -eq 0 ]
56
+ }
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # Surface 2: manage-problem names BOTH fallback paths
60
+ # ---------------------------------------------------------------------------
61
+
62
+ @test "traversal: manage-problem names the atomic-RFC empty stories: [] fallback (JTBD-101 friction guard)" {
63
+ run grep -E 'atomic-RFC fallback|atomic RFC.*JTBD-101.*friction guard|stories: \[\].*atomic' "$MANAGE_PROBLEM"
64
+ [ "$status" -eq 0 ]
65
+ }
66
+
67
+ @test "traversal: manage-problem names the legacy no-RFC direct-implementation fallback" {
68
+ run grep -iE 'legacy direct-implementation|no-RFC.*legacy|no RFCs.*direct' "$MANAGE_PROBLEM"
69
+ [ "$status" -eq 0 ]
70
+ }
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Surface 3: work-problem § Step 3 Known Error case forwards to manage-problem
74
+ # ---------------------------------------------------------------------------
75
+
76
+ @test "traversal: work-problem Step 3 Known Error case names the traversal chain" {
77
+ # The traversal description must include all four chain elements:
78
+ # Fix Strategy, RFCs, stories: array, pick first not-done.
79
+ run grep -iE 'Fix Strategy.*stories:.*pick first|traverse.*Fix Strategy.*RFC.*stories' "$WORK_PROBLEM"
80
+ [ "$status" -eq 0 ]
81
+ }
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Surface 4: Single-trailer vocabulary (per ADR-060 line 307 + N2 amendment)
85
+ # ---------------------------------------------------------------------------
86
+
87
+ @test "traversal: manage-problem names Refs: STORY-NNN trailer for story-decomposed work" {
88
+ run grep -E 'Refs: STORY-' "$MANAGE_PROBLEM"
89
+ [ "$status" -eq 0 ]
90
+ }
91
+
92
+ @test "traversal: manage-problem names Refs: RFC-NNN trailer for atomic-RFC fallback" {
93
+ run grep -E 'Refs: RFC-' "$MANAGE_PROBLEM"
94
+ [ "$status" -eq 0 ]
95
+ }
96
+
97
+ # ---------------------------------------------------------------------------
98
+ # Surface 5: Story auto-transition triggers
99
+ # ---------------------------------------------------------------------------
100
+
101
+ @test "traversal: manage-problem names the story draft → in-progress auto-transition trigger" {
102
+ run grep -iE 'draft.*in-progress.*first.*commit|auto-transitions.*draft.*in-progress' "$MANAGE_PROBLEM"
103
+ [ "$status" -eq 0 ]
104
+ }
105
+
106
+ @test "traversal: manage-problem names the in-progress → done auto-transition trigger (criteria ticked + RFC closed)" {
107
+ run grep -iE 'in-progress.*done.*acceptance-criteria.*ticked|ALL acceptance-criteria.*RFC reaches.*closed' "$MANAGE_PROBLEM"
108
+ [ "$status" -eq 0 ]
109
+ }