@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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +9 -1
- package/bin/wr-itil-reconcile-stories +2 -0
- package/bin/wr-itil-reconcile-story-maps +2 -0
- package/hooks/hooks.json +4 -0
- package/hooks/itil-readme-refresh-discipline.sh +118 -0
- package/hooks/lib/readme-refresh-detect.sh +161 -0
- package/hooks/test/itil-readme-refresh-discipline.bats +261 -0
- package/lib/migrate-problems-layout.sh +128 -0
- package/package.json +1 -1
- package/scripts/reconcile-stories.sh +236 -0
- package/scripts/reconcile-story-maps.sh +98 -0
- package/scripts/test/reconcile-stories.bats +173 -0
- package/scripts/test/reconcile-story-maps.bats +74 -0
- package/scripts/test/rfc-stories-extension.bats +173 -0
- package/scripts/test/update-problem-references-section.bats +195 -0
- package/scripts/test/update-references-section-sibling-helpers.bats +80 -0
- package/scripts/test/working-the-problem-traversal.bats +109 -0
- package/scripts/update-jtbd-references-section.sh +131 -0
- package/scripts/update-problem-references-section.sh +284 -0
- package/scripts/update-rfc-references-section.sh +152 -0
- package/scripts/update-story-references-section.sh +128 -0
- package/skills/capture-rfc/SKILL.md +28 -3
- package/skills/capture-story/SKILL.md +373 -0
- package/skills/capture-story/test/capture-story-behavioural.bats +227 -0
- package/skills/capture-story-map/SKILL.md +229 -0
- package/skills/capture-story-map/test/capture-story-map-behavioural.bats +98 -0
- package/skills/list-stories/SKILL.md +151 -0
- package/skills/list-stories/test/list-stories-contract.bats +127 -0
- package/skills/list-story-maps/SKILL.md +93 -0
- package/skills/list-story-maps/test/list-story-maps-contract.bats +46 -0
- package/skills/manage-problem/SKILL.md +42 -4
- package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +53 -0
- package/skills/manage-rfc/SKILL.md +12 -0
- package/skills/manage-story/SKILL.md +242 -0
- package/skills/manage-story/test/manage-story-contract.bats +171 -0
- package/skills/manage-story-map/SKILL.md +158 -0
- package/skills/manage-story-map/test/manage-story-map-contract.bats +63 -0
- package/skills/reconcile-stories/SKILL.md +110 -0
- package/skills/reconcile-story-maps/SKILL.md +70 -0
- package/skills/work-problem/SKILL.md +1 -1
- package/skills/work-problems/SKILL.md +25 -0
- package/skills/work-problems/test/work-problems-auto-migrate-step.bats +57 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# packages/itil/scripts/update-story-references-section.sh
|
|
3
|
+
#
|
|
4
|
+
# Generalised reverse-trace section updater for story files. Lookup
|
|
5
|
+
# table supports ## RFCs, ## Story Maps sections on STORY-NNN-*.md
|
|
6
|
+
# files (a story's parent RFC + parent story-map traces).
|
|
7
|
+
#
|
|
8
|
+
# Per ADR-060 § Phase 2 encoding amendment 2026-05-12 architect
|
|
9
|
+
# finding 4: no per-section-name branching; lookup-table dispatch.
|
|
10
|
+
#
|
|
11
|
+
# @adr ADR-060 (Phase 2 encoding amendment 2026-05-12)
|
|
12
|
+
# @problem P170 (Phase 2 Slice 2b)
|
|
13
|
+
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
|
|
16
|
+
STORY_FILE="${1:-}"
|
|
17
|
+
SECTION_NAME="${2:-}"
|
|
18
|
+
|
|
19
|
+
[ -n "$STORY_FILE" ] || { echo "ERROR: missing story-file argument" >&2; exit 1; }
|
|
20
|
+
[ -n "$SECTION_NAME" ] || { echo "ERROR: missing section-name argument" >&2; exit 1; }
|
|
21
|
+
[ -f "$STORY_FILE" ] || { echo "ERROR: story file not found: $STORY_FILE" >&2; exit 1; }
|
|
22
|
+
|
|
23
|
+
declare -A SECTION_GLOB SECTION_MODE SECTION_ID_PATTERN
|
|
24
|
+
|
|
25
|
+
SECTION_GLOB["RFCs"]="docs/rfcs/RFC-*.md"
|
|
26
|
+
SECTION_MODE["RFCs"]="markdown-frontmatter-stories"
|
|
27
|
+
SECTION_ID_PATTERN["RFCs"]="RFC-[0-9]+"
|
|
28
|
+
|
|
29
|
+
SECTION_GLOB["Story Maps"]="docs/story-maps/*/STORY-MAP-*.html"
|
|
30
|
+
SECTION_MODE["Story Maps"]="html-data-story-id"
|
|
31
|
+
SECTION_ID_PATTERN["Story Maps"]="STORY-MAP-[0-9]+"
|
|
32
|
+
|
|
33
|
+
glob_pattern="${SECTION_GLOB[$SECTION_NAME]:-}"
|
|
34
|
+
extraction_mode="${SECTION_MODE[$SECTION_NAME]:-}"
|
|
35
|
+
id_pattern="${SECTION_ID_PATTERN[$SECTION_NAME]:-}"
|
|
36
|
+
|
|
37
|
+
[ -n "$glob_pattern" ] || { echo "ERROR: unknown section-name '$SECTION_NAME'. Supported: RFCs, Story Maps" >&2; exit 1; }
|
|
38
|
+
|
|
39
|
+
story_basename=$(basename "$STORY_FILE")
|
|
40
|
+
story_id=$(echo "$story_basename" | grep -oE '^STORY-[0-9]+' | head -1)
|
|
41
|
+
[ -n "$story_id" ] || { echo "ERROR: cannot extract STORY ID from filename: $story_basename" >&2; exit 1; }
|
|
42
|
+
|
|
43
|
+
declare -a matched_ids=() matched_titles=() matched_statuses=()
|
|
44
|
+
|
|
45
|
+
extract_from_markdown_frontmatter_stories() {
|
|
46
|
+
local file="$1"
|
|
47
|
+
awk '/^---$/{f=!f;next} f && /^stories:/' "$file" | head -1 | grep -qE "\\b${story_id}\\b"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
extract_from_html_data_story_id() {
|
|
51
|
+
local file="$1"
|
|
52
|
+
# Story maps reference stories via <a data-story-id="STORY-NNN"> per ADR-060
|
|
53
|
+
# amendment schema; grep on the literal attribute match.
|
|
54
|
+
grep -qE "data-story-id=\"${story_id}\"" "$file"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
extract_id_from_filename() { basename "$1" | grep -oE "$id_pattern" | head -1; }
|
|
58
|
+
extract_title_md() { awk '/^# / { sub(/^# /, ""); print; exit }' "$1"; }
|
|
59
|
+
extract_title_html() { grep -oE '<title>[^<]+</title>' "$1" | head -1 | sed -E 's|<title>([^<]+)</title>|\1|'; }
|
|
60
|
+
extract_status_md() { awk '/^---$/{f=!f;next} f && /^status:/{ sub(/^status:[[:space:]]*/, ""); gsub(/"/, ""); print; exit }' "$1"; }
|
|
61
|
+
extract_status_html() { grep -oE '<meta[[:space:]]+name="status"[[:space:]]+content="[^"]+"' "$1" | head -1 | sed -E 's|.*content="([^"]+)".*|\1|'; }
|
|
62
|
+
|
|
63
|
+
case "$extraction_mode" in
|
|
64
|
+
markdown-frontmatter-stories)
|
|
65
|
+
extract_match=extract_from_markdown_frontmatter_stories
|
|
66
|
+
extract_title=extract_title_md
|
|
67
|
+
extract_status=extract_status_md
|
|
68
|
+
;;
|
|
69
|
+
html-data-story-id)
|
|
70
|
+
extract_match=extract_from_html_data_story_id
|
|
71
|
+
extract_title=extract_title_html
|
|
72
|
+
extract_status=extract_status_html
|
|
73
|
+
;;
|
|
74
|
+
*)
|
|
75
|
+
echo "ERROR: unknown extraction-mode '$extraction_mode'" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
80
|
+
shopt -s nullglob
|
|
81
|
+
for artefact in $glob_pattern; do
|
|
82
|
+
[ -e "$artefact" ] || continue
|
|
83
|
+
if "$extract_match" "$artefact"; then
|
|
84
|
+
aid=$(extract_id_from_filename "$artefact")
|
|
85
|
+
[ -n "$aid" ] || continue
|
|
86
|
+
matched_ids+=("$aid")
|
|
87
|
+
matched_titles+=("$("$extract_title" "$artefact" 2>/dev/null || echo "")")
|
|
88
|
+
matched_statuses+=("$("$extract_status" "$artefact" 2>/dev/null || echo "unknown")")
|
|
89
|
+
fi
|
|
90
|
+
done
|
|
91
|
+
shopt -u nullglob
|
|
92
|
+
|
|
93
|
+
new_section=""
|
|
94
|
+
if [ ${#matched_ids[@]} -gt 0 ]; then
|
|
95
|
+
new_section="## ${SECTION_NAME}"$'\n\n| ID | Title | Status |\n|----|-------|--------|\n'
|
|
96
|
+
for i in "${!matched_ids[@]}"; do
|
|
97
|
+
new_section+="| ${matched_ids[$i]} | ${matched_titles[$i]} | ${matched_statuses[$i]} |"$'\n'
|
|
98
|
+
done
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
tmp_file="$(mktemp)"
|
|
102
|
+
awk -v sec="## $SECTION_NAME" '
|
|
103
|
+
BEGIN { in_target=0; blank_buffer="" }
|
|
104
|
+
$0 == sec { in_target=1; blank_buffer=""; next }
|
|
105
|
+
in_target && /^## / && $0 != sec { in_target=0 }
|
|
106
|
+
!in_target {
|
|
107
|
+
if ($0 ~ /^[[:space:]]*$/) { if (blank_buffer == "") blank_buffer="\n"; next }
|
|
108
|
+
if (blank_buffer != "") { printf "%s", blank_buffer; blank_buffer="" }
|
|
109
|
+
print
|
|
110
|
+
}
|
|
111
|
+
END { if (blank_buffer != "") printf "%s", blank_buffer }
|
|
112
|
+
' "$STORY_FILE" > "$tmp_file"
|
|
113
|
+
|
|
114
|
+
tmp_file2="$(mktemp)"
|
|
115
|
+
awk 'BEGIN{c=0} /^[[:space:]]*$/{c++; next} {for(i=0;i<c;i++)print ""; c=0; print} END{print ""}' "$tmp_file" > "$tmp_file2"
|
|
116
|
+
mv "$tmp_file2" "$tmp_file"
|
|
117
|
+
|
|
118
|
+
if [ -n "$new_section" ]; then
|
|
119
|
+
printf '\n%s' "$new_section" >> "$tmp_file"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if ! cmp -s "$tmp_file" "$STORY_FILE"; then
|
|
123
|
+
mv "$tmp_file" "$STORY_FILE"
|
|
124
|
+
else
|
|
125
|
+
rm -f "$tmp_file"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
exit 0
|
|
@@ -27,9 +27,12 @@ This skill is one half of the capture-then-manage RFC framework introduced by AD
|
|
|
27
27
|
|
|
28
28
|
**Positional**: `<problem-trace> <description>` where `<problem-trace>` is `P<NNN>` or `P<NNN>,P<NNN>,...` (no spaces inside the trace; multiple problems comma-separated).
|
|
29
29
|
|
|
30
|
+
**Optional flag (Phase 2)**: `--stories STORY-<NNN>,STORY-<NNN>,...` — ORDERED execution sequence per ADR-060 line 262. Cardinality 0..N: atomic RFCs OMIT the flag and capture-rfc populates `stories: []` in frontmatter (JTBD-101 friction guard — atomic RFCs are first-class); story-decomposed RFCs supply the ordered list. The flag accepts STORY-IDs that don't yet resolve to files (forward-reference is permitted at capture; the existence check happens at `manage-rfc <NNN> accepted` transition per ADR-060 working-the-problem flow line 304).
|
|
31
|
+
|
|
30
32
|
```
|
|
31
33
|
/wr-itil:capture-rfc P168 Pipeline consume-catalog and bootstrap-from-reports — multi-commit retrofit
|
|
32
34
|
/wr-itil:capture-rfc P038,P064 Voice-and-tone gates on external comms — coordinated rollout across changeset/PR/release-notes
|
|
35
|
+
/wr-itil:capture-rfc P170 --stories STORY-007,STORY-010,STORY-011 Phase 2 story-tier framework — capture-story + list-stories + RFC frontmatter extension
|
|
33
36
|
```
|
|
34
37
|
|
|
35
38
|
**ADR-060 § Phase 1 item 2 phrasing footnote**: ADR-060 names "mandatory `--problem P<NNN>` flag" verbatim. This skill uses the **positional** form (no `--problem` prefix) to match the lightweight aside-invocation grammar of `capture-problem` (per ADR-032) and because Claude Code skill arguments don't carry a proper CLI flag parser. The hard-block intent (ADR-060 § Confirmation criterion 1: "without a problem trace") is preserved verbatim — only the surface syntax differs. The `--problem` phrasing in ADR-060 reads as exemplar, not contract.
|
|
@@ -75,13 +78,24 @@ fi
|
|
|
75
78
|
The arguments must begin with a problem-trace token (`P<NNN>` or comma-separated `P<NNN>,P<NNN>,...`). The remainder is the description.
|
|
76
79
|
|
|
77
80
|
```bash
|
|
78
|
-
# Tokenise: first token = problem-trace; rest = description
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
# Tokenise: first non-flag token = problem-trace; rest = description.
|
|
82
|
+
# Optional --stories STORY-NNN,STORY-NNN,... flag may appear anywhere.
|
|
83
|
+
stories_trace=""
|
|
84
|
+
positional=()
|
|
85
|
+
while [ $# -gt 0 ]; do
|
|
86
|
+
case "$1" in
|
|
87
|
+
--stories) stories_trace="$2"; shift 2 ;;
|
|
88
|
+
*) positional+=("$1"); shift ;;
|
|
89
|
+
esac
|
|
90
|
+
done
|
|
91
|
+
problem_trace="${positional[0]}"
|
|
92
|
+
description="${positional[*]:1}"
|
|
81
93
|
```
|
|
82
94
|
|
|
83
95
|
If `$problem_trace` does not match `^P[0-9]{3}(,P[0-9]{3})*$` (regex), this is an I1 violation — go to Step 2's deny path. If `$description` is empty, halt with the empty-arguments directive from the Rule 6 audit table above.
|
|
84
96
|
|
|
97
|
+
If `$stories_trace` is non-empty, validate the format matches `^STORY-[0-9]{3}(,STORY-[0-9]{3})*$`. Forward-reference is permitted (STORY-IDs that don't yet resolve to files at capture time) — the existence check happens at `manage-rfc <NNN> accepted` transition. Malformed format (e.g. `--stories foo`) is an argument error: halt with stderr directive naming the expected shape.
|
|
98
|
+
|
|
85
99
|
Derive a kebab-case title slug from the first 8-10 non-stopword tokens of `$description` (matching `capture-problem` slug derivation).
|
|
86
100
|
|
|
87
101
|
### 2. Validate problem trace + I1 hard-block enforcement
|
|
@@ -152,6 +166,7 @@ decision-makers: [<git config user.name>]
|
|
|
152
166
|
problems: [P<NNN>, P<NNN>, ...]
|
|
153
167
|
adrs: []
|
|
154
168
|
jtbd: []
|
|
169
|
+
stories: [<from --stories flag — ordered execution sequence; or [] if --stories absent>]
|
|
155
170
|
---
|
|
156
171
|
|
|
157
172
|
# RFC-<NNN>: <Title>
|
|
@@ -210,6 +225,16 @@ done
|
|
|
210
225
|
|
|
211
226
|
The helper (`packages/itil/scripts/update-problem-rfcs-section.sh`) is idempotent: running over a current section is a no-op. Lazy-empty discipline applies (zero traced RFCs → section absent) — capture-rfc invocations always have ≥ 1 trace at this step, so this surface always emits a populated section. The `git add` is conditional on the helper actually modifying the file — `cmp -s` no-op-on-current is the helper's idempotency contract; `git add` of an unchanged file is also a no-op.
|
|
212
227
|
|
|
228
|
+
**Phase 2 — render `## Stories` body section on the new RFC** (when `--stories` was provided): the just-written RFC file carries `stories: [STORY-NNN, ...]` in frontmatter; the helper `update-rfc-references-section.sh <rfc-file> "Stories"` renders the forward-trace `## Stories` body section from that frontmatter array in execution order per ADR-060 line 270. Lazy-empty discipline applies — when `stories: []` (atomic RFC, JTBD-101 friction guard), the helper omits the section entirely:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
if [ -n "$stories_trace" ]; then
|
|
232
|
+
bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-rfc-references-section.sh" "docs/rfcs/RFC-${next}-${slug}.proposed.md" "Stories"
|
|
233
|
+
fi
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
(`git add` of the new RFC file in the next step picks up the section render.)
|
|
237
|
+
|
|
213
238
|
Stage the new RFC file:
|
|
214
239
|
|
|
215
240
|
```bash
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-itil:capture-story
|
|
3
|
+
description: Lightweight story-capture skill for aside-invocation during foreground work — mandatory leading problem-trace AND JTBD-trace per ADR-060 I6 + I9 invariants, optional `--rfc` and `--story-map` flag args (I7 + I8 enforce at `accepted` transition not at capture), skeleton story file at `docs/stories/draft/STORY-NNN-<slug>.md`, single commit per capture, no inline README refresh. Defers full INVEST shape + acceptance transition to /wr-itil:manage-story. Use when the user (or agent) wants to capture a story quickly with clear problem + JTBD anchoring. For full lifecycle management, use /wr-itil:manage-story.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Capture Story Skill
|
|
8
|
+
|
|
9
|
+
Capture an INVEST-shaped story ticket quickly during foreground work. Lightweight aside-invocation surface that complements the heavyweight `/wr-itil:manage-story` flow. Mirrors `/wr-itil:capture-rfc` shape per ADR-032 lightweight + heavyweight skill split, extended for the story tier's stricter trace-mandate (both problem AND JTBD at capture; RFC AND story-map deferred to accepted).
|
|
10
|
+
|
|
11
|
+
This skill is one half of the capture-then-manage story framework introduced by ADR-060 (Problem-RFC-Story framework with mandatory problem-trace and unified problem ontology, accepted 2026-05-05; Phase 2 amendment 2026-05-12 introducing the story tier). The other half is `/wr-itil:manage-story` (heavyweight intake + INVEST-gated lifecycle management).
|
|
12
|
+
|
|
13
|
+
**Related JTBDs**: JTBD-008 (primary — Decompose a Fix Into Coordinated Changes; story is the INVEST-shaped sub-workstream entity JTBD-008 line 20 names), JTBD-001 (extended scope — story-level governance via INVEST gates at acceptance + auto-transition on RFC-closes + acceptance-criteria all-ticked), JTBD-101 (atomic-fix-adopter friction guard — capture-story remains opt-in; atomic-RFC fallback per ADR-060 line 262 means atomic adopters never invoke capture-story).
|
|
14
|
+
|
|
15
|
+
## When to invoke
|
|
16
|
+
|
|
17
|
+
- **Slicing an RFC into INVEST-shaped sub-workstreams**: agent / user has captured an RFC and is now decomposing its scope into the ordered `stories:` array per ADR-060's working-the-problem flow (line 300-320). Each slice on a story-map's backbone → ribs → slices grid becomes one story.
|
|
18
|
+
- **Capturing a story before its placement on a story-map**: per ADR-060 line 291, RFC + story-map traces are optional at capture; I7 / I8 enforce only at the `draft → accepted` transition. Draft stories may exist with NO `rfcs:` and NO `story-maps:` until the design firms up.
|
|
19
|
+
- **Retrospective bootstrap migration** (Slice 15 of P170 Phase 2): extracting existing slices from `docs/plans/170-rfc-framework-story-map.md` into individual story files. The bounded-escape carve-out for I7 / I8 enforce-at-accepted permits the retrospective sequence (capture draft, design fills in `rfcs:` + `story-maps:`, manage-story <NNN> accepted gate fires).
|
|
20
|
+
- **Forward dogfood capture**: a new story for in-flight work, captured at the start of implementation, runs to `done` via `Refs: STORY-NNN` trailer detection + acceptance-criteria all-ticked.
|
|
21
|
+
|
|
22
|
+
**Use `/wr-itil:manage-story` instead** when:
|
|
23
|
+
- The work is advancing an existing story through its lifecycle (draft → accepted → in-progress → done).
|
|
24
|
+
- The user wants the full INVEST intake flow with structured value-statement + acceptance-criteria + effort-estimation prompts.
|
|
25
|
+
- The story needs the I10 INVEST shape behavioural test to fire at accepted.
|
|
26
|
+
|
|
27
|
+
## Argument grammar
|
|
28
|
+
|
|
29
|
+
**Positional (both mandatory)**: `<problem-trace> <jtbd-trace> <description>` where:
|
|
30
|
+
- `<problem-trace>` is `P<NNN>` or comma-separated `P<NNN>,P<NNN>,...` (no spaces inside the trace; multiple problems comma-separated).
|
|
31
|
+
- `<jtbd-trace>` is `JTBD-<NNN>` or comma-separated `JTBD-<NNN>,JTBD-<NNN>,...`.
|
|
32
|
+
|
|
33
|
+
**Optional flags** (any order, before or after positional args):
|
|
34
|
+
- `--rfc RFC-<NNN>[,RFC-<NNN>,...]` — RFC(s) this story will be referenced by once design firms up.
|
|
35
|
+
- `--story-map STORY-MAP-<NNN>[,STORY-MAP-<NNN>,...]` — story-map(s) this story will be placed on once design firms up.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
/wr-itil:capture-story P170 JTBD-008 Build /wr-itil:capture-story-map skill scaffold
|
|
39
|
+
/wr-itil:capture-story P170 JTBD-008,JTBD-001 --rfc RFC-002 Ship hook exemption globs across 4 enforce-edit hooks
|
|
40
|
+
/wr-itil:capture-story P170 JTBD-008 --story-map STORY-MAP-001 --rfc RFC-002 Extract Slice 5 T7 shared-migration-routine into STORY-NNN
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**ADR-060 § Skills line 291 phrasing footnote**: ADR-060 names "Mandatory: ≥1 problem trace, ≥1 JTBD trace" verbatim. This skill uses the **positional** form (no `--problem` / `--jtbd` prefix on the mandatory pair) to match the lightweight aside-invocation grammar of `capture-rfc` (per ADR-032) and because Claude Code skill arguments don't carry a proper CLI flag parser. The optional `--rfc` / `--story-map` flags exist BECAUSE they are optional — fully positional would require sentinel values for absent traces. The hard-block intent (ADR-060 § Confirmation criterion implied by I6 + I9 — capture fails without both mandatory traces) is preserved verbatim — only the surface syntax differs.
|
|
44
|
+
|
|
45
|
+
## Rule 6 audit (per ADR-032 + ADR-013 + ADR-060)
|
|
46
|
+
|
|
47
|
+
This skill has **two direction-setting AskUserQuestion fires** (problem-trace AND JTBD-trace, when arguments are non-empty but malformed) and **one optional taste AskUserQuestion** (title/scope summary, silent-default if unavailable). Every other potentially-interactive decision is framework-mediated per ADR-044:
|
|
48
|
+
|
|
49
|
+
| Decision | Resolution | Authority class |
|
|
50
|
+
|----------|-----------|-----------------|
|
|
51
|
+
| Problem trace presence | I6 hard-block — refuse on missing trace; emit deny log + halt-with-stderr-directive | direction-setting |
|
|
52
|
+
| Problem trace validation | Mechanical: each `P<NNN>` must exist in `docs/problems/`. Open / Known Error / Verifying = pass; Closed / Parked = advisory-warn but proceed (bounded-escape carve-out — see Step 2) | silent-mechanical |
|
|
53
|
+
| JTBD trace presence | I9 hard-block — refuse on missing JTBD trace; emit deny log + halt-with-stderr-directive | direction-setting |
|
|
54
|
+
| JTBD trace validation | Mechanical: each `JTBD-<NNN>` must resolve to a file under `docs/jtbd/<persona>/JTBD-<NNN>-*.md` (any lifecycle status) | silent-mechanical |
|
|
55
|
+
| Optional `--rfc` trace validation | Mechanical: each provided `RFC-<NNN>` must resolve to a file under `docs/rfcs/`; advisory-warn on `proposed` / `verifying` lifecycle states; missing entirely = hard-block on the provided arg (the absence-from-args case is the "optional" path — the malformed-arg case is not) | silent-mechanical |
|
|
56
|
+
| Optional `--story-map` trace validation | Same mechanical pattern against `docs/story-maps/*/STORY-MAP-*.html` (HTML data-attribute existence check); advisory-warn on `draft` / `in-progress` story-maps | silent-mechanical |
|
|
57
|
+
| STORY ID allocation | Mechanical: `max(local, origin) + 1`, three-digit padded; enumerates `docs/stories/*/STORY-*.md` + `git ls-tree origin/main docs/stories/`. ADR-019 collision-guard inline per Slice 3 design review architect approval (finding 3 option a — inline-only path) | silent-mechanical |
|
|
58
|
+
| Title kebab-slug | Mechanical: first 8-10 non-stopword tokens of description | silent-mechanical |
|
|
59
|
+
| Title prose / scope summary refinement | Optional `AskUserQuestion`; silent-default to derived form when unavailable | taste |
|
|
60
|
+
| File write / frontmatter | Mechanical: shape per `docs/stories/README.md` § Frontmatter shape + ADR-060 lines 220-228 | silent-mechanical |
|
|
61
|
+
| Single commit | Mechanical: `feat(itil): capture STORY-<NNN> <title>` + `Refs: STORY-<NNN>` trailer | silent-mechanical |
|
|
62
|
+
| Empty arguments | Halt-with-stderr-directive: print "capture-story requires `<problem-trace> <jtbd-trace> <description>` — invoke /wr-itil:manage-story instead for the full intake flow" and exit. AFK orchestrators MUST NOT invoke capture-story with empty arguments. | n/a |
|
|
63
|
+
|
|
64
|
+
Per ADR-013 Rule 6 fail-safe + ADR-044 + P132 + inverse-P078: every silent-mechanical branch above resolves without user input, so AFK and interactive contexts behave identically modulo the optional taste prompt.
|
|
65
|
+
|
|
66
|
+
## Steps
|
|
67
|
+
|
|
68
|
+
### 0. Preflight (Phase 2 cross-directory)
|
|
69
|
+
|
|
70
|
+
This skill's preflight uses `wr-itil-reconcile-readme docs/problems` (the existing problems-README reconciliation contract per P118). Sibling reconcile-stories + reconcile-story-maps scripts land in Slice 5 + Slice 9 of P170 Phase 2 — once those ship, swap this preflight to call all three reconciliations (cross-tier integrity holds at all three surfaces).
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
wr-itil-reconcile-readme docs/problems > /tmp/wr-itil-drift-$$.txt
|
|
74
|
+
reconcile_exit=$?
|
|
75
|
+
if [ "$reconcile_exit" -eq 1 ]; then
|
|
76
|
+
wr-itil-classify-readme-drift /tmp/wr-itil-drift-$$.txt docs/problems
|
|
77
|
+
classify_exit=$?
|
|
78
|
+
rm -f /tmp/wr-itil-drift-$$.txt
|
|
79
|
+
# classify_exit 0 (INLINE_REFRESH): proceed (no inline refresh in this skill).
|
|
80
|
+
# classify_exit 1 (HALT_ROUTE_RECONCILE): halt; invoke /wr-itil:reconcile-readme.
|
|
81
|
+
# classify_exit 2 (parse error): conservative halt-and-route.
|
|
82
|
+
fi
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 1. Parse arguments
|
|
86
|
+
|
|
87
|
+
Tokenise the argument string. Optional flags (`--rfc <ids>`, `--story-map <ids>`) may appear in any position. The remaining positional tokens are `<problem-trace> <jtbd-trace> <description>` in that order.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Pseudo:
|
|
91
|
+
rfc_trace=""
|
|
92
|
+
story_map_trace=""
|
|
93
|
+
positional=()
|
|
94
|
+
while [ $# -gt 0 ]; do
|
|
95
|
+
case "$1" in
|
|
96
|
+
--rfc) rfc_trace="$2"; shift 2 ;;
|
|
97
|
+
--story-map) story_map_trace="$2"; shift 2 ;;
|
|
98
|
+
*) positional+=("$1"); shift ;;
|
|
99
|
+
esac
|
|
100
|
+
done
|
|
101
|
+
problem_trace="${positional[0]}"
|
|
102
|
+
jtbd_trace="${positional[1]}"
|
|
103
|
+
description="${positional[*]:2}"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If `$problem_trace` does not match `^P[0-9]{3}(,P[0-9]{3})*$` (regex), this is an I6 violation — go to Step 2's deny path. If `$jtbd_trace` does not match `^JTBD-[0-9]{3}(,JTBD-[0-9]{3})*$`, this is an I9 violation. If `$description` is empty, halt with the empty-arguments directive from the Rule 6 audit table.
|
|
107
|
+
|
|
108
|
+
Derive a kebab-case title slug from the first 8-10 non-stopword tokens of `$description` (matching `capture-rfc` slug derivation).
|
|
109
|
+
|
|
110
|
+
### 2. Validate problem trace + I6 hard-block enforcement
|
|
111
|
+
|
|
112
|
+
For each `P<NNN>` in the trace list:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Dual-tolerant ticket discovery (RFC-002 migration window):
|
|
116
|
+
# BOTH flat `docs/problems/<NNN>-<title>.<state>.md` AND per-state
|
|
117
|
+
# subdir `docs/problems/<state>/<NNN>-<title>.md` layouts.
|
|
118
|
+
trace_files=$(ls docs/problems/<NNN>-*.md docs/problems/*/<NNN>-*.md 2>/dev/null)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**I6 hard-block (per ADR-060 line 248)**:
|
|
122
|
+
|
|
123
|
+
- **Trace token absent OR malformed**: emit deny log entry + halt with stderr directive:
|
|
124
|
+
```bash
|
|
125
|
+
mkdir -p logs
|
|
126
|
+
printf '{"timestamp":"%s","session_id":"%s","reason":"%s","args":%s}\n' \
|
|
127
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(get_current_session_id 2>/dev/null || echo unknown)" \
|
|
128
|
+
"<missing|malformed|unresolved>-trace" \
|
|
129
|
+
"$(printf '%s' "$ARGUMENTS" | jq -Rs .)" \
|
|
130
|
+
>> logs/story-capture-denials.jsonl
|
|
131
|
+
echo "/wr-itil:capture-story requires a leading problem-trace argument (P<NNN> or P<NNN>,P<NNN>...). Open the driving problem via /wr-itil:capture-problem first, then re-invoke capture-story with the trace." >&2
|
|
132
|
+
exit 1
|
|
133
|
+
```
|
|
134
|
+
The deny log feeds the trace-violation-rate reassessment criterion (sibling to RFC's `logs/rfc-capture-denials.jsonl`).
|
|
135
|
+
|
|
136
|
+
- **Each `P<NNN>` must resolve to a file in `docs/problems/`**. If any does not, emit deny log entry with `reason: unresolved-trace` + the unresolved IDs, halt, exit 1.
|
|
137
|
+
|
|
138
|
+
**Bounded-escape carve-out for Closed/Verifying/Parked traces**: classify by suffix or path; `.open.md` / `.known-error.md` (or `open/` / `known-error/` subdirs) pass silently; `.verifying.md` (or `verifying/` subdir) passes with advisory note; `.closed.md` (or `closed/` subdir) and `.parked.md` (or `parked/` subdir) pass with advisory-warn (story may be a retrospective extraction).
|
|
139
|
+
|
|
140
|
+
### 2.5. Validate JTBD trace + I9 hard-block enforcement
|
|
141
|
+
|
|
142
|
+
For each `JTBD-<NNN>` in the JTBD trace list:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
jtbd_file=$(ls docs/jtbd/*/JTBD-<NNN>-*.md 2>/dev/null | head -1)
|
|
146
|
+
[ -z "$jtbd_file" ] && unresolved_jtbds+=("JTBD-<NNN>")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**I9 hard-block (per ADR-060 line 251)**:
|
|
150
|
+
|
|
151
|
+
- **JTBD trace token absent OR malformed**: emit deny log entry + halt with directive: `/wr-itil:capture-story requires a JTBD trace argument (JTBD-<NNN> or JTBD-<NNN>,JTBD-<NNN>...). Author the driving JTBD via /wr-jtbd:update-guide first, then re-invoke capture-story.`
|
|
152
|
+
- **Each `JTBD-<NNN>` must resolve to a file in `docs/jtbd/`**. If any does not, emit deny log with `reason: unresolved-jtbd-trace`, halt, exit 1.
|
|
153
|
+
|
|
154
|
+
JTBD lifecycle states (`.proposed.md` / `.accepted.md` / `.archived.md`) all pass silently — a story may anchor on a proposed JTBD per the dogfood pattern (Phase 2 itself is being captured against proposed JTBDs).
|
|
155
|
+
|
|
156
|
+
### 2.6. Validate optional `--rfc` and `--story-map` traces
|
|
157
|
+
|
|
158
|
+
If `$rfc_trace` is non-empty, for each `RFC-<NNN>`:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
rfc_file=$(ls docs/rfcs/RFC-<NNN>-*.md 2>/dev/null | head -1)
|
|
162
|
+
[ -z "$rfc_file" ] && unresolved_rfcs+=("RFC-<NNN>")
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- Token absent: skip (the "optional" path).
|
|
166
|
+
- Token present but malformed (`--rfc RFC-99`, etc.) OR resolves to no file: hard-block. Emit deny log with `reason: unresolved-rfc-trace`. The optional-vs-malformed distinction is load-bearing — absence is permitted, malformed input is not.
|
|
167
|
+
|
|
168
|
+
Same shape for `--story-map`. Story-maps are HTML; existence check uses `ls docs/story-maps/*/STORY-MAP-<NNN>-*.html 2>/dev/null`.
|
|
169
|
+
|
|
170
|
+
Lifecycle classification on resolved files: advisory-warn on `proposed` / `draft` / `in-progress` states (the design isn't firm yet — captured story will reference work that may itself drift); pass silently on `accepted` / `closed` / `verifying` states.
|
|
171
|
+
|
|
172
|
+
### 3. Compute next STORY ID
|
|
173
|
+
|
|
174
|
+
Inline `max(local, origin) + 1` formula (per Slice 3 design review architect finding 3 option a — inline-only path; no separate `check-id-collision.sh` script per capture-rfc / capture-problem precedent):
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
local_max=$(ls docs/stories/*/STORY-*.md 2>/dev/null | sed 's|.*/STORY-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
|
|
178
|
+
origin_max=$(git ls-tree -r --name-only origin/main docs/stories/ 2>/dev/null | sed 's|.*/STORY-||;s|-.*||' | grep -oE '^[0-9]+' | sort -n | tail -1)
|
|
179
|
+
next=$(printf '%03d' $(( 10#$(echo -e "${local_max:-0}\n${origin_max:-0}" | sort -n | tail -1) + 1 )))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Log the renumber decision in the operation report if origin and local diverged. The `git ls-tree -r` recursive flag enumerates the per-state subdir layout — `docs/stories/draft/`, `docs/stories/accepted/`, etc.
|
|
183
|
+
|
|
184
|
+
### 4. Optional taste prompt for title / scope summary
|
|
185
|
+
|
|
186
|
+
If interactive (AskUserQuestion available) AND the description is short enough that the derived title slug may not capture intent, fire one `AskUserQuestion` with `header: "Story title"` offering: (a) the derived kebab-slug as default, (b) "edit". This is **taste** authority per ADR-044 — silent-default to (a) when AskUserQuestion is unavailable.
|
|
187
|
+
|
|
188
|
+
### 5. Write the story file
|
|
189
|
+
|
|
190
|
+
**File path**: `docs/stories/draft/STORY-<NNN>-<kebab-title>.md`
|
|
191
|
+
|
|
192
|
+
**Template** (mirrors `docs/stories/README.md` § Story frontmatter + body structure):
|
|
193
|
+
|
|
194
|
+
```markdown
|
|
195
|
+
---
|
|
196
|
+
status: draft
|
|
197
|
+
story-id: <kebab-slug>
|
|
198
|
+
reported: <YYYY-MM-DD>
|
|
199
|
+
decision-makers: [<git config user.name>]
|
|
200
|
+
problems: [P<NNN>, P<NNN>, ...]
|
|
201
|
+
jtbd: [JTBD-<NNN>, JTBD-<NNN>, ...]
|
|
202
|
+
rfcs: [<RFC-<NNN>, ...> or empty]
|
|
203
|
+
story-maps: [<STORY-MAP-<NNN>, ...> or empty]
|
|
204
|
+
estimated-effort: deferred
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
# STORY-<NNN>: <Title>
|
|
208
|
+
|
|
209
|
+
**Status**: draft
|
|
210
|
+
**Reported**: <YYYY-MM-DD>
|
|
211
|
+
**Problems**: <P<NNN> [, P<NNN>, ...]>
|
|
212
|
+
**JTBD**: <JTBD-<NNN> [, ...]>
|
|
213
|
+
**RFCs**: <RFC-<NNN> [, ...]> or (none — populate at accepted transition per I7)
|
|
214
|
+
**Story Maps**: <STORY-MAP-<NNN> [, ...]> or (none — populate at accepted transition per I8)
|
|
215
|
+
**Estimated effort**: deferred (populate at accepted transition per I10 INVEST Estimable)
|
|
216
|
+
|
|
217
|
+
## User value (required, INVEST Valuable)
|
|
218
|
+
|
|
219
|
+
(populate at /wr-itil:manage-story accepted transition — one-paragraph user-facing value statement)
|
|
220
|
+
|
|
221
|
+
## Acceptance criteria (accepted-gate, INVEST Testable)
|
|
222
|
+
|
|
223
|
+
- [ ] (populate at /wr-itil:manage-story accepted transition — observable behavioural criteria)
|
|
224
|
+
|
|
225
|
+
## Driving problem trace (required — I6 invariant)
|
|
226
|
+
|
|
227
|
+
<description from arguments — one-line summary linking the story scope to the problem's symptom or RCA finding for each driving problem>
|
|
228
|
+
|
|
229
|
+
## JTBD trace (required — I9 invariant)
|
|
230
|
+
|
|
231
|
+
<one-line summary linking each JTBD-<NNN> to the persona-job's desired outcome that this story serves>
|
|
232
|
+
|
|
233
|
+
## Implementation notes (optional)
|
|
234
|
+
|
|
235
|
+
(deferred — populate at /wr-itil:manage-story accepted transition or during implementation)
|
|
236
|
+
|
|
237
|
+
## Dependencies
|
|
238
|
+
|
|
239
|
+
- **Blocks**: (none — populate at /wr-itil:manage-story if applicable)
|
|
240
|
+
- **Blocked by**: (none — populate at /wr-itil:manage-story; Phase 2 I-invariant prohibits Blocked-by references to unaccepted stories at acceptance time per INVEST Independent)
|
|
241
|
+
|
|
242
|
+
## Related
|
|
243
|
+
|
|
244
|
+
(captured via /wr-itil:capture-story; expand at next /wr-itil:manage-story invocation)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The deferred-section pattern matches `capture-rfc`'s placeholder approach — the captured story is intentionally minimal; full INVEST shape lands at the manage-story accepted-transition step.
|
|
248
|
+
|
|
249
|
+
### 6. Single commit — `## Stories` reverse-trace refresh; no stories README refresh
|
|
250
|
+
|
|
251
|
+
**Stage list**: the new story file PLUS each driving problem ticket file (refresh `## Stories` reverse-trace section) PLUS each driving JTBD file (refresh `## Stories` reverse-trace section) PLUS each driving RFC file IF `--rfc` was provided (refresh `## Stories` reverse-trace section). **Do NOT** stage `docs/stories/README.md` (deferred). **Do NOT** stage any story-map HTML files — story-maps are spatially-authored HTML; new stories must be placed on the relevant map manually via `/wr-itil:manage-story-map` (when that skill lands per Slice 4 of P170 Phase 2). Capture-story emits an advisory stderr line naming the unplaced-on-map state.
|
|
252
|
+
|
|
253
|
+
The reverse-trace refresh on driving artefacts IS in-commit per ADR-014 single-commit grain — the cross-tier `## Stories` table on a problem / JTBD / RFC must stay current the moment a new story traces it. The same justification as capture-rfc's inline `## RFCs` refresh applies.
|
|
254
|
+
|
|
255
|
+
For each problem ID in `$problem_trace`:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
for pid_token in $(echo "$problem_trace" | tr ',' ' '); do
|
|
259
|
+
pid_num="${pid_token#P}"
|
|
260
|
+
# Dual-tolerant ticket discovery (RFC-002 migration window).
|
|
261
|
+
problem_file=$(ls docs/problems/${pid_num}-*.md docs/problems/*/${pid_num}-*.md 2>/dev/null | head -1)
|
|
262
|
+
[ -z "$problem_file" ] && continue
|
|
263
|
+
bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-problem-references-section.sh" "$problem_file" "Stories"
|
|
264
|
+
git add "$problem_file"
|
|
265
|
+
done
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Same shape for each JTBD in `$jtbd_trace`:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
for jid_token in $(echo "$jtbd_trace" | tr ',' ' '); do
|
|
272
|
+
jid_num="${jid_token#JTBD-}"
|
|
273
|
+
jtbd_file=$(ls docs/jtbd/*/JTBD-${jid_num}-*.md 2>/dev/null | head -1)
|
|
274
|
+
[ -z "$jtbd_file" ] && continue
|
|
275
|
+
bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-jtbd-references-section.sh" "$jtbd_file" "Stories"
|
|
276
|
+
git add "$jtbd_file"
|
|
277
|
+
done
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Same shape for each RFC in `$rfc_trace` (only if non-empty):
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
for rid_token in $(echo "$rfc_trace" | tr ',' ' '); do
|
|
284
|
+
[ -z "$rid_token" ] && continue
|
|
285
|
+
rfc_file=$(ls docs/rfcs/${rid_token}-*.md 2>/dev/null | head -1)
|
|
286
|
+
[ -z "$rfc_file" ] && continue
|
|
287
|
+
bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-rfc-references-section.sh" "$rfc_file" "Stories"
|
|
288
|
+
git add "$rfc_file"
|
|
289
|
+
done
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The helpers (`update-problem-references-section.sh`, `update-jtbd-references-section.sh`, `update-rfc-references-section.sh`) all support `"Stories"` as a section-name token per Slice 2a/2b verified lookup tables. Each helper is idempotent: a no-op section is a no-op stage.
|
|
293
|
+
|
|
294
|
+
Stage the new story file:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
git add docs/stories/draft/STORY-<NNN>-<slug>.md
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Satisfy the commit gate per ADR-014:
|
|
301
|
+
|
|
302
|
+
- **Primary**: delegate to subagent type `wr-risk-scorer:pipeline` via the Agent tool.
|
|
303
|
+
- **Fallback**: invoke `/wr-risk-scorer:assess-release` via the Skill tool when the subagent type is unavailable.
|
|
304
|
+
|
|
305
|
+
Commit message:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
feat(itil): capture STORY-<NNN> <title>
|
|
309
|
+
|
|
310
|
+
Refs: STORY-<NNN>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
The `capture` verb mirrors `capture-rfc`'s audit signal (lightweight aside path vs. heavyweight `manage-story` intake). The single `Refs: STORY-<NNN>` trailer is the universal story-trailer vocabulary per ADR-060 line 307 + amendment 2026-05-10 nitpick N2 — capture commits and implementation commits both use `Refs:`; the manage-story skill's `draft → in-progress` auto-transition trigger discriminates by "is this the capture commit (subject starts with `capture`) or a subsequent commit" rather than by trailer verb.
|
|
314
|
+
|
|
315
|
+
### 7. Report
|
|
316
|
+
|
|
317
|
+
After the commit, report:
|
|
318
|
+
|
|
319
|
+
- The new story file path and ID.
|
|
320
|
+
- The traced problems with their lifecycle states.
|
|
321
|
+
- The traced JTBDs with their lifecycle states.
|
|
322
|
+
- Any traced RFCs / story-maps if provided, with lifecycle-state advisory warnings.
|
|
323
|
+
- Any unplaced-on-story-map advisory (always emit when `--story-map STORY-MAP-<NNN>` was provided — the HTML placement is manual per architect finding 2 on Slice 7).
|
|
324
|
+
- Trailing pointer: `Run /wr-itil:manage-story <STORY-<NNN>> next to populate User value + Acceptance criteria + Estimated effort, then advance draft → accepted; refresh docs/stories/README.md.`
|
|
325
|
+
|
|
326
|
+
The trailing pointer is **not optional** — it is the user-visible signal that the story is intentionally skeleton-only and how to advance it.
|
|
327
|
+
|
|
328
|
+
## Composition with manage-story
|
|
329
|
+
|
|
330
|
+
| Concern | manage-story | capture-story |
|
|
331
|
+
|---------|--------------|---------------|
|
|
332
|
+
| Problem-trace I6 enforcement | Re-validated at every lifecycle transition | Hard-block at capture-time; deny logged to `logs/story-capture-denials.jsonl` |
|
|
333
|
+
| JTBD-trace I9 enforcement | Re-validated at every lifecycle transition | Hard-block at capture-time |
|
|
334
|
+
| RFC-trace I7 enforcement | Hard-block at `accepted` transition (allows draft stories to exist before RFC reference firms up) | Advisory-warn at capture-time if `--rfc` provided and resolves to draft/proposed lifecycle |
|
|
335
|
+
| Story-map-trace I8 enforcement | Hard-block at `accepted` transition | Same advisory-warn pattern at capture-time |
|
|
336
|
+
| INVEST shape (I10) | Behavioural checks at `accepted` transition | Out of scope: capture produces a skeleton with deferred-placeholder sections |
|
|
337
|
+
| Skeleton-fill | Full-intake; AskUserQuestion for User value + Acceptance criteria + Estimated effort | Deferred-placeholder pattern; one optional taste prompt only |
|
|
338
|
+
| Status transitions | Step 7 owns draft → accepted → in-progress → done | Out of scope (creation only) |
|
|
339
|
+
| `## Stories` README refresh | P094 / P062 inline (regenerate + stage in same commit) | Deferred to `/wr-itil:manage-story review` or `wr-itil-reconcile-stories` (Slice 9) |
|
|
340
|
+
| Commit grain | One commit per intake / per transition | One commit per capture |
|
|
341
|
+
| Use case | Full lifecycle management | Aside-invocation; capture-and-continue |
|
|
342
|
+
|
|
343
|
+
The two skills share the `/tmp/wr-itil-story-capture-grep-${SESSION_ID}` create-gate marker (sibling to the capture-rfc marker per architect verdict on capture-rfc sub-decision (a)).
|
|
344
|
+
|
|
345
|
+
## Related
|
|
346
|
+
|
|
347
|
+
- **ADR-060** — Problem-RFC-Story framework with mandatory problem-trace and unified problem ontology + Phase 2 amendment 2026-05-12 (story tier).
|
|
348
|
+
- **ADR-060 lines 220-228** — Story frontmatter shape spec.
|
|
349
|
+
- **ADR-060 lines 248-253** — I6-I11 story-tier invariants.
|
|
350
|
+
- **ADR-060 line 291** — capture-story description (this skill's source-of-truth contract).
|
|
351
|
+
- **ADR-060 line 307 + amendment 2026-05-10 nitpick N2** — single-trailer vocabulary (`Refs: STORY-<NNN>`).
|
|
352
|
+
- **P170** — driver problem ticket.
|
|
353
|
+
- **JTBD-008** — Decompose a Fix Into Coordinated Changes. Primary persona-anchor.
|
|
354
|
+
- **JTBD-001** (extended scope) — change-set-level governance composition.
|
|
355
|
+
- **JTBD-101** (atomic-fix-adopter friction guard) — capture-story remains opt-in aside-invocation; atomic-RFC fallback per ADR-060 line 262.
|
|
356
|
+
- **`docs/stories/README.md`** — story tier lifecycle index + frontmatter/body shape spec (P170 Phase 2 Slice 1 — committed `8562bbc`).
|
|
357
|
+
- **ADR-010** — amended skill-granularity: capture-story + manage-story are two skills, not one.
|
|
358
|
+
- **ADR-014** — single-commit grain per capture. Commit-message convention.
|
|
359
|
+
- **ADR-022** — problem lifecycle conventions; story lifecycle mirrors (draft / accepted / in-progress / done / archived).
|
|
360
|
+
- **ADR-032** — governance-skill aside-invocation pattern. Lightweight + heavyweight split.
|
|
361
|
+
- **ADR-038** — progressive disclosure. SKILL.md (this file) + future REFERENCE.md split deferred per ADR-054.
|
|
362
|
+
- **ADR-044** — decision delegation contract. Authority classes named in the Rule 6 audit table.
|
|
363
|
+
- **ADR-049** — plugin-bundled scripts via `bin/` on `$PATH`. `wr-itil-reconcile-stories` shim follows this grammar (Slice 9).
|
|
364
|
+
- **ADR-051** — load-bearing-from-the-start. I6 + I9 hard-block ship behaviourally on day one.
|
|
365
|
+
- **ADR-052** — behavioural-tests default. Bats coverage at `packages/itil/skills/capture-story/test/capture-story-behavioural.bats` (this slice).
|
|
366
|
+
- **ADR-060 Phase 2 Slice 2a/2b reverse-trace helpers** — `update-problem-references-section.sh`, `update-jtbd-references-section.sh`, `update-rfc-references-section.sh` all support `"Stories"` section-name token (verified lookup-table entries).
|
|
367
|
+
- **Capture-rfc precedent** — `packages/itil/skills/capture-rfc/SKILL.md` — sibling skill at the RFC tier; structurally near-identical surface.
|
|
368
|
+
- **P078** capture-on-correction — capture-story may be the correct response to a strong-signal user correction that names a single INVEST-shaped sub-workstream within an existing RFC.
|
|
369
|
+
- **P132 + inverse-P078** — mechanical-stage carve-outs prevent over-asking; named in the Rule 6 audit table.
|
|
370
|
+
|
|
371
|
+
## Phase-out-of-order note
|
|
372
|
+
|
|
373
|
+
This skill ships BEFORE `/wr-itil:capture-story-map` (Slice 3 of P170 Phase 2) due to the voice-tone-hook-on-HTML blocker documented at P170 line 297. Building capture-story first is structurally permitted per ADR-060 line 291 (story-map traces optional at capture; I8 enforce only at accepted transition). When Slices 3-6 eventually ship the story-map skills, `manage-story <NNN> accepted` will validate the I8 invariant against the then-existing story-map corpus. The deviation from ADR-060's recommended commit-grain order (line 449-454 — sub-slice 3 story-map skills then sub-slice 4 story skills) is auditable here and in this commit's Slice 7 commit message.
|