@windyroad/itil 0.27.1 → 0.28.0-preview.304

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +9 -1
  3. package/bin/wr-itil-reconcile-stories +2 -0
  4. package/bin/wr-itil-reconcile-story-maps +2 -0
  5. package/hooks/hooks.json +4 -0
  6. package/hooks/itil-readme-refresh-discipline.sh +118 -0
  7. package/hooks/lib/readme-refresh-detect.sh +161 -0
  8. package/hooks/test/itil-readme-refresh-discipline.bats +261 -0
  9. package/lib/migrate-problems-layout.sh +128 -0
  10. package/package.json +1 -1
  11. package/scripts/reconcile-stories.sh +236 -0
  12. package/scripts/reconcile-story-maps.sh +98 -0
  13. package/scripts/test/reconcile-stories.bats +173 -0
  14. package/scripts/test/reconcile-story-maps.bats +74 -0
  15. package/scripts/test/rfc-stories-extension.bats +173 -0
  16. package/scripts/test/update-problem-references-section.bats +195 -0
  17. package/scripts/test/update-references-section-sibling-helpers.bats +80 -0
  18. package/scripts/test/working-the-problem-traversal.bats +109 -0
  19. package/scripts/update-jtbd-references-section.sh +131 -0
  20. package/scripts/update-problem-references-section.sh +284 -0
  21. package/scripts/update-rfc-references-section.sh +152 -0
  22. package/scripts/update-story-references-section.sh +128 -0
  23. package/skills/capture-rfc/SKILL.md +28 -3
  24. package/skills/capture-story/SKILL.md +373 -0
  25. package/skills/capture-story/test/capture-story-behavioural.bats +227 -0
  26. package/skills/capture-story-map/SKILL.md +229 -0
  27. package/skills/capture-story-map/test/capture-story-map-behavioural.bats +98 -0
  28. package/skills/list-stories/SKILL.md +151 -0
  29. package/skills/list-stories/test/list-stories-contract.bats +127 -0
  30. package/skills/list-story-maps/SKILL.md +93 -0
  31. package/skills/list-story-maps/test/list-story-maps-contract.bats +46 -0
  32. package/skills/manage-problem/SKILL.md +42 -4
  33. package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +53 -0
  34. package/skills/manage-rfc/SKILL.md +12 -0
  35. package/skills/manage-story/SKILL.md +242 -0
  36. package/skills/manage-story/test/manage-story-contract.bats +171 -0
  37. package/skills/manage-story-map/SKILL.md +158 -0
  38. package/skills/manage-story-map/test/manage-story-map-contract.bats +63 -0
  39. package/skills/reconcile-stories/SKILL.md +110 -0
  40. package/skills/reconcile-story-maps/SKILL.md +70 -0
  41. package/skills/work-problem/SKILL.md +1 -1
  42. package/skills/work-problems/SKILL.md +25 -0
  43. package/skills/work-problems/test/work-problems-auto-migrate-step.bats +57 -0
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: wr-itil:reconcile-stories
3
+ description: Detect and correct drift between docs/stories/README.md and the on-disk story inventory. Wraps the diagnose-only packages/itil/scripts/reconcile-stories.sh script with an agent-applied-edits pattern that preserves narrative content (the "Last reviewed" prose paragraph). Use when docs/stories/README.md Story Rankings or Done sections drift from filesystem state — typically detected by manage-story Step 0 preflight or work-problems preflight on RFC iters with story-tier traces.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Reconcile Stories Skill
8
+
9
+ Sibling to `/wr-itil:reconcile-readme` (P118 / ADR-014) and `/wr-itil:reconcile-rfcs` (ADR-060 Phase 1 item 5), applied at the story tier per P170 Phase 2 Slice 9.
10
+
11
+ **Diagnose-only mechanic** — wraps `packages/itil/scripts/reconcile-stories.sh` (resolved via `wr-itil-reconcile-stories` `$PATH` shim per ADR-049). The script reads `docs/stories/<state>/STORY-NNN-*.md` files across all 5 lifecycle subdirs (draft, accepted, in-progress, done, archived), parses `docs/stories/README.md`'s Story Rankings + Done tables, and reports each disagreement. Exit codes: `0` clean, `1` drift detected (structured stdout), `2` parse error.
12
+
13
+ **Reverse-trace pass** — when `docs/problems/`, `docs/rfcs/`, and `docs/jtbd/` exist on disk (the default project layout), the reconciler ALSO checks the auto-maintained `## Stories` section on each parent artefact against the story frontmatter's `problems:` / `rfcs:` / `jtbd:` claims. Three drift kinds per parent tier (mirrors the RFC-tier reverse-trace contract):
14
+ - `MISSING_REVERSE_TRACE STORY-NNN in <PARENT-ID> ## Stories` — story claims parent but parent's `## Stories` table doesn't list the story
15
+ - `STALE_REVERSE_TRACE STORY-NNN in <PARENT-ID> ## Stories` — parent lists the story but story no longer claims the parent
16
+ - `STATUS_MISMATCH STORY-NNN in <PARENT-ID> ## Stories claims=<X> actual=<Y>` — parent's row claims one lifecycle status; story's filesystem subdir is a different state
17
+
18
+ ## When to invoke
19
+
20
+ - **Drift detected by another skill's preflight** — `/wr-itil:manage-story` or `/wr-itil:work-problems` Step 0 preflight may surface `docs/stories/README.md` drift; this skill is the recovery path.
21
+ - **Manual drift recovery** — user notices the README is stale (e.g. story moved between lifecycle subdirs without README refresh; reverse-trace `## Stories` section out of date on a problem ticket).
22
+ - **CI drift gate** — a CI step running `wr-itil-reconcile-stories` against the merge target would surface drift before merge; this skill is the in-repo recovery path.
23
+
24
+ ## Composition with manage-story
25
+
26
+ `manage-story` (P170 Phase 2 Slice 8) owns the inline P094 / P062 README refresh on every lifecycle transition (draft → accepted → in-progress → done). When the inline refresh is satisfied, the README stays current and `reconcile-stories` reports clean. The reconcile skill is the *recovery* path when inline refresh was missed (typically when story files are moved manually, or when frontmatter trace edits don't ride through `manage-story`).
27
+
28
+ ## Steps
29
+
30
+ ### 1. Run the diagnose script
31
+
32
+ ```bash
33
+ wr-itil-reconcile-stories docs/stories docs/problems docs/rfcs docs/jtbd > /tmp/wr-itil-stories-drift-$$.txt
34
+ reconcile_exit=$?
35
+ ```
36
+
37
+ - **Exit 0**: README is clean. Report "no drift detected" and exit.
38
+ - **Exit 1**: drift detected. Continue to Step 2.
39
+ - **Exit 2**: parse error (README missing or malformed). Halt; the README needs structural repair which this skill doesn't own (the `manage-story review` flow handles structural-rebuild semantics).
40
+
41
+ ### 2. Read drift entries + plan edits
42
+
43
+ Read `/tmp/wr-itil-stories-drift-$$.txt` line by line. Each line is one of:
44
+ - `DRIFT STORY-NNN rankings: claims=<X> actual=<Y>` — Story Rankings row has wrong Status; update the row.
45
+ - `STALE STORY-NNN rankings: actual=<state>` — Story Rankings table is missing a row; add it.
46
+ - `MISMATCH STORY-NNN done: actual=<state>` — Done table has wrong row OR an extra row; remove/adjust.
47
+ - `MISSING_REVERSE_TRACE STORY-NNN in <PARENT-ID> ## Stories` — parent's `## Stories` section needs the story added; call `update-<parent-kind>-references-section.sh <parent-file> "Stories"` to refresh.
48
+ - `STALE_REVERSE_TRACE STORY-NNN in <PARENT-ID> ## Stories` — parent's `## Stories` section needs the story removed; same helper call (idempotent, lazy-empty discipline removes when no traces remain).
49
+ - `STATUS_MISMATCH STORY-NNN in <PARENT-ID> ## Stories claims=<X> actual=<Y>` — same helper call refreshes the status column.
50
+
51
+ ### 3. Apply edits
52
+
53
+ For README drift entries — edit `docs/stories/README.md` in-place preserving the "Last reviewed" prose paragraph at the top. Use the Edit tool with narrow `old_string` / `new_string` pairs targeting only the table row(s) affected.
54
+
55
+ For reverse-trace drift entries — invoke the appropriate Slice 2a/2b helper for each parent file:
56
+
57
+ ```bash
58
+ # Problem parent
59
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-problem-references-section.sh" "$problem_file" "Stories"
60
+ # RFC parent
61
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-rfc-references-section.sh" "$rfc_file" "Stories"
62
+ # JTBD parent
63
+ bash "$(wr-itil-script-path 2>/dev/null || echo packages/itil/scripts)/update-jtbd-references-section.sh" "$jtbd_file" "Stories"
64
+ ```
65
+
66
+ ### 4. Verify + commit
67
+
68
+ Re-run `wr-itil-reconcile-stories` after edits. Exit 0 expected. Stage all modified files (README + parent files) and commit per ADR-014 single-commit grain.
69
+
70
+ Commit message:
71
+
72
+ ```
73
+ docs(stories): reconcile docs/stories/README.md drift (N entries)
74
+
75
+ Refs: <relevant problem/RFC/story IDs derived from the drift entries>
76
+ ```
77
+
78
+ The ADR-014 commit grain is "one reconciliation pass per commit" — covers README + N parent files all in one commit since they're all reconciling to filesystem truth, a single coherent action.
79
+
80
+ ### 5. Report
81
+
82
+ After commit, report:
83
+ - Number of drift entries reconciled.
84
+ - Files modified (README + each parent reverse-trace surface touched).
85
+ - Commit SHA.
86
+ - Trailing pointer: `Run /wr-itil:manage-story review next to refresh story rankings + INVEST scoring if any stories crossed the accepted gate during the reconciliation window.`
87
+
88
+ ## Ownership boundary
89
+
90
+ `reconcile-stories` owns drift DETECTION and MECHANICAL REPAIR of `docs/stories/README.md` + reverse-trace sections. It does NOT:
91
+ - Move story files between lifecycle subdirs (that's `manage-story` § Status transitions).
92
+ - Edit story frontmatter (that's `manage-story` at lifecycle transitions OR `capture-story` at capture-time).
93
+ - Refresh story body sections (User value / Acceptance criteria / Implementation notes are owned by `manage-story` lifecycle transitions per I10 INVEST gates).
94
+ - Run WSJF computation (I11 invariant: stories MUST NOT carry a WSJF field in Phase 2 per ADR-060 line 253).
95
+
96
+ ## Related
97
+
98
+ - **P170** — driver problem ticket.
99
+ - **ADR-060** — Problem-RFC-Story framework. Phase 2 amendment 2026-05-10 introduces the story tier; line 270 names the auto-maintained `## Stories` reverse-trace section contract.
100
+ - **ADR-049** — plugin-bundled scripts via `bin/` on `$PATH`. `wr-itil-reconcile-stories` shim follows this grammar.
101
+ - **ADR-014** — single-commit grain. The reconciliation pass is a single coherent action; one commit per pass.
102
+ - **ADR-052** — behavioural-tests default. Bats coverage at `packages/itil/scripts/test/reconcile-stories.bats` (P170 Phase 2 Slice 9).
103
+ - **ADR-040** — diagnose-only advisory-exit contract. `reconcile-stories.sh` is exit-1 on drift, exit-0 on clean, exit-2 on parse error.
104
+ - **P118** / `reconcile-readme.sh` — sibling at the problems tier.
105
+ - **ADR-060 Phase 1 item 5** / `reconcile-rfcs.sh` — sibling at the RFC tier.
106
+ - **Slice 2a/2b helpers** — `update-problem-references-section.sh`, `update-rfc-references-section.sh`, `update-jtbd-references-section.sh` are the load-bearing reverse-trace refresh helpers this skill invokes; all three accept `"Stories"` as a section-name token per their lookup tables.
107
+ - **JTBD-001** — Enforce Governance Without Slowing Down. Drift detection is an automated governance enforcement surface; mechanical repair preserves the spirit while removing manual toil.
108
+ - **JTBD-008** — Decompose a Fix Into Coordinated Changes. Story tier reverse-trace integrity is load-bearing for the working-the-problem flow's per-story dispatch (Slice 13 traversal).
109
+
110
+ $ARGUMENTS
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: wr-itil:reconcile-story-maps
3
+ description: Detect and correct drift between docs/story-maps/README.md and the on-disk story-map HTML inventory. Wraps the diagnose-only packages/itil/scripts/reconcile-story-maps.sh script with an agent-applied-edits pattern preserving narrative content.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Reconcile Story Maps Skill
8
+
9
+ Sibling to `/wr-itil:reconcile-stories` (P170 Phase 2 Slice 9), `/wr-itil:reconcile-rfcs` (ADR-060 Phase 1 item 5), and `/wr-itil:reconcile-readme` (P118 / ADR-014), applied at the story-map tier per P170 Phase 2 Slice 5.
10
+
11
+ **Diagnose-only mechanic** — wraps `packages/itil/scripts/reconcile-story-maps.sh` via the `wr-itil-reconcile-story-maps` `$PATH` shim per ADR-049. Script reads `docs/story-maps/<state>/STORY-MAP-NNN-*.html` files across 5 lifecycle subdirs (draft, accepted, in-progress, completed, archived), parses `docs/story-maps/README.md`, and reports disagreements. Exit codes: `0` clean, `1` drift detected, `2` parse error.
12
+
13
+ ## When to invoke
14
+
15
+ - Drift surfaced by another skill's preflight (e.g. `/wr-itil:manage-story-map`).
16
+ - Manual drift recovery (e.g. story-map moved between lifecycle subdirs without README refresh).
17
+ - CI drift gate against merge target.
18
+
19
+ ## Steps
20
+
21
+ ### 1. Run the diagnose script
22
+
23
+ ```bash
24
+ wr-itil-reconcile-story-maps docs/story-maps > /tmp/wr-itil-story-maps-drift-$$.txt
25
+ reconcile_exit=$?
26
+ ```
27
+
28
+ Exit 0 → clean; exit 1 → drift to address in Step 2; exit 2 → parse error (halt).
29
+
30
+ ### 2. Read drift entries + plan edits
31
+
32
+ Each line is one of:
33
+ - `MISSING STORY-MAP-NNN README claims it exists but no file on disk` — remove README row.
34
+ - `STALE STORY-MAP-NNN README missing entry; actual=<state>` — add README row.
35
+
36
+ ### 3. Apply edits
37
+
38
+ Edit `docs/story-maps/README.md` in-place preserving narrative. Use Edit tool with narrow `old_string`/`new_string` pairs targeting only the affected rows.
39
+
40
+ ### 4. Verify + commit
41
+
42
+ Re-run `wr-itil-reconcile-story-maps` (expect exit 0). Stage README + commit per ADR-014 single-commit grain.
43
+
44
+ Commit message: `docs(story-maps): reconcile docs/story-maps/README.md drift (N entries)`.
45
+
46
+ ### 5. Report
47
+
48
+ Report drift entries reconciled + files modified + commit SHA + trailing pointer to `/wr-itil:manage-story-map review` if any maps crossed lifecycle states during the window.
49
+
50
+ ## Ownership boundary
51
+
52
+ Detects + mechanically repairs `docs/story-maps/README.md` drift. Does NOT:
53
+ - Move story-map files between lifecycle subdirs (manage-story-map's surface).
54
+ - Edit `<meta>` blocks or `<style>` blocks or HTML body content (manage-story-map's surface).
55
+ - Run WSJF (I5 invariant: no WSJF on story-maps).
56
+
57
+ ## Related
58
+
59
+ - **P170** — driver problem ticket.
60
+ - **ADR-060** — Problem-RFC-Story framework; Phase 2 amendment 2026-05-10 introduces story-map tier.
61
+ - **ADR-049** — bin/ on PATH.
62
+ - **ADR-014** — single-commit grain.
63
+ - **ADR-052** — behavioural-tests default; bats at `packages/itil/scripts/test/reconcile-story-maps.bats`.
64
+ - **ADR-040** — advisory-exit contract; exit 0 clean / exit 1 drift / exit 2 parse error.
65
+ - **`/wr-itil:reconcile-stories`** — sibling at the story tier.
66
+ - **`/wr-itil:reconcile-rfcs`** — sibling at the RFC tier.
67
+ - **JTBD-008** — Decompose a Fix Into Coordinated Changes.
68
+ - **JTBD-302** — Trust That the README Describes the Plugin I Just Installed.
69
+
70
+ $ARGUMENTS
@@ -81,7 +81,7 @@ When a single ticket is the strict top, rungs 2-5 are not consulted. When multip
81
81
  Invoke `/wr-itil:manage-problem <NNN>` via the Skill tool with the selected ticket's ID as the argument. The delegated skill runs the full Working a Problem flow appropriate to the ticket's status:
82
82
 
83
83
  - **Open ticket**: investigate root cause; document findings; create reproduction test; identify workaround; auto-transition to Known Error when root cause + workaround are documented; if the fix is small, proceed straight into implementation.
84
- - **Known Error**: read the root cause analysis; implement the fix following the project's standard workflow (plan if needed, architect review, tests, changeset); include the Known Error → Verification Pending `git mv` in the fix commit per ADR-022.
84
+ - **Known Error**: traverse problem `## Fix Strategy` → referenced RFCs → each RFC's frontmatter `stories:` array (ordered per ADR-060 line 259) → pick first not-done story (status `accepted` or `in-progress`, skipping `done` and `draft`) → implement the picked story scope. Empty `stories: []` (atomic RFC, JTBD-101 friction guard) falls back to per-RFC iter dispatch on the RFC body's tasks. No-RFC Fix Strategy (Phase 1-shape legacy problems) falls back to direct fix implementation. Each commit carries `Refs: STORY-<NNN>` (or `Refs: RFC-<NNN>` for atomic-RFC fallback / `Refs: P<NNN>` for legacy direct path). Stories auto-transition `draft → in-progress` on first non-capture commit; auto-transition `in-progress → done` on all-criteria-ticked + linked RFC closed. When all stories under all referenced RFCs are done, include the Known Error → Verification Pending `git mv` in the final commit per ADR-022. The full traversal contract lives in `/wr-itil:manage-problem` § Working a Problem → Known Error subsection (post-Phase-2 rewrite per ADR-060 lines 300-320).
85
85
 
86
86
  **Why delegate rather than re-implement:** the full investigation / transition / fix / release pipeline is a long-lived, policy-governed flow that must stay on a single authoritative workflow. Re-hosting it on a sibling skill would fork the ownership contract and compound maintenance cost. The split skill (this file) owns the *selection* of the next ticket; `/wr-itil:manage-problem <NNN>` owns the *execution*.
87
87
 
@@ -122,6 +122,31 @@ Classifier exit-code routing:
122
122
 
123
123
  This is a robustness layer ON TOP of P094 + P062, not a supersession — both per-operation contracts remain in force inside each iteration's manage-problem / transition-problem invocation.
124
124
 
125
+ ### Step 0a: Auto-migrate adopter layout (P170 / RFC-002 / ADR-031)
126
+
127
+ After Step 0's fetch/divergence preflight and the README reconciliation block but **before** Step 1's backlog scan, source the shared shell migration routine and call the idempotent entrypoint:
128
+
129
+ ```bash
130
+ source packages/itil/lib/migrate-problems-layout.sh
131
+ migrate_problems_to_per_state_layout "$PWD"
132
+ ```
133
+
134
+ The routine is **idempotent and partial-migration-safe**. It no-ops when no flat-layout files (`docs/problems/*.<state>.md` at the top level of `docs/problems/`) are detected — the common case post-Slice-5 T5a in this monorepo and in freshly-migrated adopter repos.
135
+
136
+ **Closes the Step 1 false-zero defect** (per ADR-031 § Backward Compatibility line 126 "Why both skills"): Step 1 enumerates BEFORE delegating to manage-problem. On a flat-layout adopter repo, the post-ADR-031 Step 1 glob would return zero matches at the per-state shape and stop-condition #1 would fire incorrectly — the orchestrator would exit with a false "nothing to do" signal, never reaching manage-problem's Step 0a auto-migrate. Wiring auto-migrate here at Step 0a is structurally required, not an optimisation.
137
+
138
+ On a flat-layout adopter repo (first invocation post-update — JTBD-101 plugin-developer auto-migration path), the routine:
139
+
140
+ 1. Creates the five state subdirectories under `docs/problems/`.
141
+ 2. Runs `git mv` to relocate every existing ticket from flat to per-state subdir.
142
+ 3. Emits a standalone commit with subject `docs(problems): auto-migrate to per-state subdirectory layout (ADR-031)` and footer trailer `RISK_BYPASS: adr-031-migration` (recognised by the commit-gate hook per T11).
143
+
144
+ **AFK authorisation per ADR-013 Rule 6**: this fires unconditionally even in AFK / non-interactive / orchestrated mode. Pure-rename + pure-mkdir + standalone-commit actions are policy-authorised under ADR-019 precedent — fully reversible (`git revert`), no external-comms surface, no destructive overwrite. No `AskUserQuestion` gate.
145
+
146
+ **First-fire signal**: the routine emits a single stderr line on the migrating invocation; silent on no-op re-invocations.
147
+
148
+ After Step 0a completes (whether no-op or migration), proceed to Step 1 backlog scan. The dual-tolerant glob at Step 1 (RFC-002 transitional window) continues to match both layouts; post-T6 (single-pattern collapse), Step 1 will tighten to per-state only and the migration commit ensures the adopter tree matches.
149
+
125
150
  ### Step 1: Scan the backlog
126
151
 
127
152
  Read `docs/problems/README.md` if it exists and is fresh (check via git history — see manage-problem step 9 for the cache freshness check). If stale or missing, scan all open + known-error tickets via the dual-tolerant pattern `ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null` (RFC-002 migration window — covers BOTH the flat `<NNN>-<title>.<state>.md` filename-suffix layout AND the per-state subdir `<state>/<NNN>-<title>.md` layout), extract their WSJF scores, and rank them.
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P170 / RFC-002 / ADR-031 Open-Execution Q1 resolution: work-problems
4
+ # SKILL.md wires the shared migration routine at Step 0a (after Step 0
5
+ # fetch/divergence preflight, before Step 1 backlog scan). Closes the
6
+ # Step 1 false-zero defect — flat-layout adopters without auto-migrate
7
+ # at Step 0a would enumerate zero matches at Step 1 and stop-condition
8
+ # would fire incorrectly. Doc-lint structural test — behavioural
9
+ # assertions live at packages/shared/test/sync-migrate-problems-layout.bats
10
+ # (T7) and the end-to-end behavioural fixture
11
+ # packages/itil/skills/work-problems/test/work-problems-auto-migrate.bats (T10).
12
+
13
+ setup() {
14
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
15
+ SKILL_MD="$REPO_ROOT/packages/itil/skills/work-problems/SKILL.md"
16
+ }
17
+
18
+ @test "work-problems: SKILL.md declares Step 0a auto-migrate (T9 wiring point)" {
19
+ run grep -E '^### Step 0a:|^### 0a\.|Step 0a:' "$SKILL_MD"
20
+ [ "$status" -eq 0 ]
21
+ }
22
+
23
+ @test "work-problems: SKILL.md Step 0a cites P170 / RFC-002 / ADR-031" {
24
+ run grep -F 'P170' "$SKILL_MD"
25
+ [ "$status" -eq 0 ]
26
+ run grep -F 'ADR-031' "$SKILL_MD"
27
+ [ "$status" -eq 0 ]
28
+ }
29
+
30
+ @test "work-problems: SKILL.md Step 0a sources packages/itil/lib/migrate-problems-layout.sh" {
31
+ run grep -F 'packages/itil/lib/migrate-problems-layout.sh' "$SKILL_MD"
32
+ [ "$status" -eq 0 ]
33
+ }
34
+
35
+ @test "work-problems: SKILL.md Step 0a calls migrate_problems_to_per_state_layout entrypoint" {
36
+ run grep -F 'migrate_problems_to_per_state_layout' "$SKILL_MD"
37
+ [ "$status" -eq 0 ]
38
+ }
39
+
40
+ @test "work-problems: SKILL.md Step 0a fires AFTER Step 0 fetch/divergence and BEFORE Step 1 backlog scan" {
41
+ local step_0_line step_0a_line step_1_line
42
+ step_0_line=$(grep -nE '^### Step 0:' "$SKILL_MD" | head -1 | cut -d: -f1)
43
+ step_0a_line=$(grep -nE '^### Step 0a:' "$SKILL_MD" | head -1 | cut -d: -f1)
44
+ step_1_line=$(grep -nE '^### Step 1:' "$SKILL_MD" | head -1 | cut -d: -f1)
45
+ [ -n "$step_0_line" ]
46
+ [ -n "$step_0a_line" ]
47
+ [ -n "$step_1_line" ]
48
+ [ "$step_0_line" -lt "$step_0a_line" ]
49
+ [ "$step_0a_line" -lt "$step_1_line" ]
50
+ }
51
+
52
+ @test "work-problems: SKILL.md Step 0a addresses the Step 1 false-zero defect (ADR-031 Backward Compatibility)" {
53
+ # Architect explicitly noted Step 1 enumeration would mis-report
54
+ # "nothing to do" on flat-layout adopters without Step 0a wiring.
55
+ run grep -E 'false.zero|Step 1 enumerat|flat-layout adopter' "$SKILL_MD"
56
+ [ "$status" -eq 0 ]
57
+ }