@windyroad/itil 0.46.0-preview.514 → 0.47.0-preview.516
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/package.json +1 -1
- package/skills/capture-problem/SKILL.md +31 -23
- package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +189 -19
- package/skills/capture-problem/test/capture-problem.bats +12 -6
- package/skills/work-problems/SKILL.md +10 -2
- package/skills/work-problems/test/work-problems-p342-r007-derive-then-pass-flags.bats +170 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wr-itil:capture-problem
|
|
3
3
|
description: Lightweight problem-capture skill for aside-invocation during foreground work — minimal duplicate-check, skeleton ticket file, single commit per capture, no inline README refresh. Defers full duplicate analysis and README refresh to /wr-itil:review-problems. Use this when the user (or agent mid-iter) wants to capture an observation quickly without disrupting current task flow. For full-intake new-problem creation, use /wr-itil:manage-problem.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Capture Problem Skill
|
|
@@ -23,7 +23,7 @@ This skill is the foreground-lightweight-capture variant of `/wr-itil:manage-pro
|
|
|
23
23
|
|
|
24
24
|
## Rule 6 audit (per ADR-032 + ADR-013)
|
|
25
25
|
|
|
26
|
-
This skill has **at most one
|
|
26
|
+
This skill has **at most one direction-setting AskUserQuestion (the I12 derive-then-ratify proposal, fired only when persona/JTBD derivation fails or is ambiguous) and zero control-flow branches keyed on the answer's substance**. Each potentially-interactive decision is framework-mediated per ADR-044:
|
|
27
27
|
|
|
28
28
|
| Decision | Resolution |
|
|
29
29
|
|----------|-----------|
|
|
@@ -33,12 +33,12 @@ This skill has **at most one classification-only AskUserQuestion (persona, on JT
|
|
|
33
33
|
| Effort default | Framework-policy: `M` flagged "deferred — re-rate at next /wr-itil:review-problems". |
|
|
34
34
|
| Multi-concern split | Out of scope: capture-problem creates one ticket per invocation. Multi-concern observations route to `/wr-itil:manage-problem` (its Step 4b owns the split). |
|
|
35
35
|
| Empty `$ARGUMENTS` | Halt-with-stderr-directive: print "capture-problem requires a description in $ARGUMENTS — invoke /wr-itil:manage-problem instead for the full intake flow" and exit. AFK orchestrators MUST NOT invoke capture-problem with empty arguments — caller-side contract. |
|
|
36
|
-
| JTBD-trace derivation (
|
|
37
|
-
|
|
|
36
|
+
| JTBD-trace + persona derivation (I12 derive-then-ratify — ADR-060 Amendment 2026-06-02) | **Derive-then-ratify per ADR-044 category 1 (direction-setting) on the AskUserQuestion fallback path; silent-framework category 4 on the derive-success path.** Step 1.5b runs lexical detection (`\bJTBD-[0-9]+\b`) + flag pre-resolution (`--jtbd=` / `--persona=`) + cited-JTBD persona derivation. **Derive-success** → silent-proceed with derived values + stderr advisory. **Derive-failure or ambiguity** → AskUserQuestion proposes up-to-3 candidate persona+JTBD pairs + a Reject option (4-option cap per ADR-044 Rule 1). User response semantics: **REJECT** → halt-with-stderr-directive + exit non-zero (REJECT of proposed persona/JTBD = REJECT of the problem; no ticket created). **Option-pick** (acceptance of a proposed candidate as-is) → silent-proceed with picked values. **Free-text correction** → silent-proceed with corrected values (correction-as-acceptance). |
|
|
37
|
+
| AFK halt-without-flags (NEW per Amendment 2026-06-02) | **Halt-with-stderr-directive per ADR-044 silent-framework category 4 — caller-side contract.** When invoked with `--no-prompt` (AFK mode marker) AND derivation fails (no JTBD-NNN citations + no `--persona=` flag + no `--jtbd=` flag, OR cited-JTBD persona-disagreement) → capture HALTS with stderr message `capture-problem: cannot derive persona/JTBD interactively under AFK and no --persona/--jtbd flags supplied; capture refused — re-invoke with explicit anchoring`. Exit non-zero. AFK orchestrators (`/wr-itil:work-problems` capture-on-correction sub-flow, agent-mid-iter `capture-problem` invocations via the Agent tool) MUST pre-resolve persona+JTBD via flags before invoking. JTBD-006 compatibility: halt-with-stderr is the audit-trail-preserving form of "queued"; the directive surfaces on user return; the AFK loop continues to the next problem. |
|
|
38
38
|
|
|
39
|
-
**P287
|
|
39
|
+
**P287 / ADR-060 Amendment 2026-06-02 — type-classification retired + I12 hard-block replaced with derive-then-ratify**: the maintainer-side type-classification dispatch (technical vs user-business) was REMOVED per twice-confirmed user direction (2026-05-25 P287 base + 2026-06-02 ADR-060 amendment substance — *"I12 hard-block was wrong. Replacement: the persona and jtbd should be derived by the LLM. And if a persona/jtbd cannot be found then it should be proposed for User ratification. If the user rejects the persona or job to be done then that is should be treated as a rejection of the problem; corrections to the persona or job to be done are treated as acceptance and acceptance is treated as acceptance. Applies to ALL problems."*). The redundant type axis was already covered by RFC/Story persona-anchoring per ADR-060 Phase 4. The original I12 hard-block (type-keyed JTBD-required halt) was REPLACED wholesale in ADR-060 Amendment 2026-06-02 with the derive-then-ratify contract codified in Step 1.5b below. The contract applies to ALL problems (no type-keyed gating; the type axis itself is GONE).
|
|
40
40
|
|
|
41
|
-
Per ADR-013 Rule 6 fail-safe: every decision above resolves without interactive user input in non-interactive contexts. The
|
|
41
|
+
Per ADR-013 Rule 6 fail-safe: every decision above resolves without interactive user input in non-interactive contexts. The derive-then-ratify AskUserQuestion fires only on derivation-failure (interactive surface) AND only when `--no-prompt` is absent (AFK callers pre-resolve via flags or halt-with-stderr-directive — never silently swallow the ratification gate).
|
|
42
42
|
|
|
43
43
|
## Steps
|
|
44
44
|
|
|
@@ -65,34 +65,42 @@ fi
|
|
|
65
65
|
|
|
66
66
|
| Flag | Effect on Step 1.5b |
|
|
67
67
|
|------|-------------------|
|
|
68
|
-
| `--jtbd=JTBD-NNN[,JTBD-NNN...]` | Pre-resolves the JTBD-trace value. Step 1.5b skips
|
|
68
|
+
| `--jtbd=JTBD-NNN[,JTBD-NNN...]` | Pre-resolves the JTBD-trace value. Step 1.5b skips JTBD-trace derivation. Comma-separated list of JTBD IDs (no spaces). |
|
|
69
69
|
| `--persona=<value>` | Pre-resolves the persona value. Step 1.5b skips persona derivation. Value MUST be one of: `developer`, `tech-lead`, `plugin-developer`, `plugin-user`. |
|
|
70
|
+
| `--no-prompt` | **AFK mode marker** (REVIVED 2026-06-02 per ADR-060 Amendment 2026-06-02). Suppresses the I12 derive-then-ratify AskUserQuestion fallback. When set AND derivation fails (no JTBD-NNN citations + no `--persona=` flag + no `--jtbd=` flag, OR cited-JTBD persona-disagreement) → capture halts-with-stderr-directive and exits non-zero. AFK orchestrators (`/wr-itil:work-problems` capture-on-correction sub-flow, agent-mid-iter `capture-problem` invocations) MUST pass this flag PLUS pre-resolved `--persona` + `--jtbd` flags to avoid the halt. |
|
|
70
71
|
|
|
71
|
-
Strip recognised leading flags from `$ARGUMENTS`; the remainder (after flags) is the free-text description. Unknown leading flags halt-with-stderr-directive: print "capture-problem: unknown flag '<flag>' — recognised flags: --jtbd=JTBD-NNN, --persona=<value
|
|
72
|
+
Strip recognised leading flags from `$ARGUMENTS`; the remainder (after flags) is the free-text description. Unknown leading flags halt-with-stderr-directive: print "capture-problem: unknown flag '<flag>' — recognised flags: --jtbd=JTBD-NNN, --persona=<value>, --no-prompt" and exit.
|
|
72
73
|
|
|
73
|
-
**P287
|
|
74
|
+
**P287 + ADR-060 Amendment 2026-06-02 note**: `--type=technical` and `--type=user-business` are RETIRED (P287, 2026-06-02 — type-classification axis removed). `--no-prompt` was retired in P287 base alongside the type-axis removal AND is now REVIVED 2026-06-02 per the ADR-060 amendment's I12 derive-then-ratify contract — its semantic is now the AFK-mode marker for the persona/JTBD AskUserQuestion fallback, NOT the historical type-classification suppress.
|
|
74
75
|
|
|
75
76
|
Empty description (post-flag-strip) halts per the Rule 6 audit above.
|
|
76
77
|
|
|
77
78
|
Derive a kebab-case title slug from the first 8-10 non-stopword tokens of the description (matching the existing `manage-problem` slug derivation pattern).
|
|
78
79
|
|
|
79
|
-
### 1.5b JTBD-trace + persona dispatch
|
|
80
|
+
### 1.5b JTBD-trace + persona dispatch — I12 derive-then-ratify (ADR-060 Amendment 2026-06-02)
|
|
80
81
|
|
|
81
|
-
Per ADR-060 § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by P287 (2026-06-02 — type-classification retired)
|
|
82
|
+
Per ADR-060 § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by P287 (2026-06-02 base — type-classification retired) AND by ADR-060 Amendment 2026-06-02 (I12 hard-block REPLACED with derive-then-ratify; applies to ALL problems; no type-keyed gating). Fires UNCONDITIONALLY. Both `jtbd_trace_value` and `persona_value` are REQUIRED on every captured ticket via the derive-then-ratify contract; on derivation failure or ambiguity, the dispatch proposes candidates for user ratification via `AskUserQuestion` (direction-setting per ADR-044 category 1).
|
|
82
83
|
|
|
83
|
-
**Resolve `jtbd_trace_value`** (an ORDERED list of JTBD IDs
|
|
84
|
+
**Resolve `jtbd_trace_value`** (an ORDERED list of JTBD IDs) via the following derive-then-ratify dispatch:
|
|
84
85
|
|
|
85
|
-
1. **If `--jtbd=JTBD-NNN[,JTBD-NNN...]` was set in Step 1**: parse comma-separated list; assign to `jtbd_trace_value`; do NOT run the lexical detector; do NOT fire AskUserQuestion (silent-
|
|
86
|
-
2. **Else** run the **lexical JTBD-trace detector** against the description: `grep -oE '\bJTBD-[0-9]+\b' | sort -u`. If
|
|
87
|
-
3. **Else (no flag, no lexical detection
|
|
86
|
+
1. **If `--jtbd=JTBD-NNN[,JTBD-NNN...]` was set in Step 1**: parse comma-separated list; assign to `jtbd_trace_value`; do NOT run the lexical detector; do NOT fire `AskUserQuestion` (silent-framework per ADR-044 category 4). Caller pre-resolved.
|
|
87
|
+
2. **Else** run the **lexical JTBD-trace detector** against the description: `grep -oE '\bJTBD-[0-9]+\b' | sort -u`. If ≥1 match found, set `jtbd_trace_value` to the matched IDs (de-duplicated, sorted ascending) and emit stderr advisory: `capture-problem: derived jtbd-trace=<id-list> from description JTBD-NNN citations; re-invoke with --jtbd= to override`. Do NOT fire `AskUserQuestion` (silent-framework).
|
|
88
|
+
3. **Else (no flag, no lexical detection — derive-failure path)**: enter the **derive-then-ratify dispatch**. Propose up-to-3 candidate JTBD IDs to the user via `AskUserQuestion`. The candidates come from LLM analysis of the description's domain signals (e.g. "AFK loop" + "iter dispatch" → propose JTBD-006; "ADR / governance" → propose JTBD-001; "plugin discoverability" → propose JTBD-101). The 4th option is **Reject** (4-option cap per ADR-044 Rule 1). User response semantics:
|
|
89
|
+
- **REJECT** → halt-with-stderr-directive (`capture-problem: user rejected proposed JTBD trace; per I12 derive-then-ratify (ADR-060 Amendment 2026-06-02), rejection of proposed persona/JTBD = rejection of the problem; no ticket created`); exit non-zero.
|
|
90
|
+
- **Option-pick** (acceptance of a proposed JTBD as-is) → assign `jtbd_trace_value` to the picked ID; proceed silently.
|
|
91
|
+
- **Free-text correction** (user supplies a different JTBD-NNN ID via the AskUserQuestion free-text path) → validate the supplied ID matches `\bJTBD-[0-9]+\b` AND a `docs/jtbd/<persona>/JTBD-<NNN>-*.md` file exists; assign `jtbd_trace_value` to the corrected ID; proceed silently (correction-as-acceptance).
|
|
92
|
+
4. **AFK halt (the `--no-prompt` branch)**: if `--no-prompt` was set in Step 1 AND control reaches step 3 (no flag + no lexical detection) → SKIP the `AskUserQuestion`; halt-with-stderr-directive (`capture-problem: cannot derive JTBD interactively under AFK and no --jtbd= flag supplied; capture refused — re-invoke with explicit anchoring`); exit non-zero.
|
|
88
93
|
|
|
89
|
-
**Resolve `persona_value`** (a scalar enum value
|
|
94
|
+
**Resolve `persona_value`** (a scalar enum value drawn from `{developer, tech-lead, plugin-developer, plugin-user}`) via the following derive-then-ratify dispatch:
|
|
90
95
|
|
|
91
|
-
1. **If `--persona=<value>` was set in Step 1**: validate `<value>`
|
|
92
|
-
2. **Else if `jtbd_trace_value` is non-empty
|
|
93
|
-
3. **Else (no JTBDs cited, no
|
|
96
|
+
1. **If `--persona=<value>` was set in Step 1**: validate `<value>` against the enum; halt-with-directive if invalid; otherwise assign and proceed silently. Caller pre-resolved.
|
|
97
|
+
2. **Else if `jtbd_trace_value` is non-empty AND cited JTBDs agree on a single persona**: derive `persona_value` from the cited JTBDs' `persona:` (and optionally `secondary-persona:`) frontmatter; emit stderr advisory: `capture-problem: derived persona=<value> from cited JTBD <id> frontmatter`; proceed silently.
|
|
98
|
+
3. **Else (no flag + (no cited JTBDs OR cited-JTBD persona-disagreement) — derive-failure / ambiguity path)**: enter the **derive-then-ratify dispatch**. Propose up-to-3 candidate persona values to the user via `AskUserQuestion`. Candidate generation: on cited-JTBD persona-disagreement, the candidates are the union-of-derived-personas from the cited JTBDs. On no-cited-JTBDs, the candidates come from LLM analysis of the description's persona signals (e.g. "ADR / governance" → propose `developer`; "plugin install / autocomplete" → propose `plugin-user`; "plugin maintainer / scaffold" → propose `plugin-developer`). The 4th option is **Reject** (4-option cap per ADR-044 Rule 1). User response semantics same as JTBD-trace step 3: REJECT → halt-with-stderr-directive + exit non-zero (rejection of proposed persona = rejection of the problem); option-pick → assign + proceed silently (acceptance); free-text correction → validate against enum + assign + proceed silently (correction-as-acceptance).
|
|
99
|
+
4. **AFK halt (the `--no-prompt` branch)**: if `--no-prompt` was set in Step 1 AND control reaches step 3 → SKIP the `AskUserQuestion`; halt-with-stderr-directive (`capture-problem: cannot derive persona interactively under AFK and no --persona= flag supplied; capture refused — re-invoke with explicit anchoring`); exit non-zero.
|
|
94
100
|
|
|
95
|
-
**JTBD-301 scope preservation**: this dispatch fires on the maintainer-side `/wr-itil:capture-problem` only. Plugin-user-side `.github/ISSUE_TEMPLATE/problem-report.yml` MUST NOT prompt for JTBD trace or persona — preserves the JTBD-301 firewall per ADR-060 P4.3 maintainer-side / plugin-user-side asymmetry clarifier. Triage during `/wr-itil:manage-problem` ingestion assigns both fields from the reporter's symptom signals (per the JTBD-301 maintainer-side-complement extension
|
|
101
|
+
**JTBD-301 scope preservation**: this dispatch fires on the maintainer-side `/wr-itil:capture-problem` only. Plugin-user-side `.github/ISSUE_TEMPLATE/problem-report.yml` MUST NOT prompt for JTBD trace or persona — preserves the JTBD-301 firewall per ADR-060 P4.3 maintainer-side / plugin-user-side asymmetry clarifier. Triage during `/wr-itil:manage-problem` ingestion assigns both fields from the reporter's symptom signals (per the JTBD-301 maintainer-side-complement extension, amended 2026-06-02 to remove the type-axis residue).
|
|
102
|
+
|
|
103
|
+
**ADR-044 authority taxonomy**: silent-framework (category 4) on the derive-success paths (flag pre-resolution, lexical detection, cited-JTBD agreement); **direction-setting (category 1)** on the derive-failure AskUserQuestion fallback paths (the user is being asked to ratify the captured ticket's substance — persona + JTBD trace are direction-setting for the ticket's future trace per ADR-060 amendment's I12 reframe — NOT a taste preference between equally-valid options).
|
|
96
104
|
|
|
97
105
|
### 2. Minimal-grep duplicate check (3-keyword title-only) + hang-off-check subagent dispatch (P346 Phase 3 amendment, 2026-05-31)
|
|
98
106
|
|
|
@@ -320,10 +328,10 @@ The trailing pointer is **not optional** — it is the user-visible signal that
|
|
|
320
328
|
|---------|----------------|-----------------|
|
|
321
329
|
| Duplicate-check | Wide-net grep + AskUserQuestion branch on matches | 3-keyword title-only grep, list-only (no branch) |
|
|
322
330
|
| Multi-concern split | Step 4b AskUserQuestion | Out of scope (one ticket per invocation) |
|
|
323
|
-
| Skeleton-fill | Full-intake; AskUserQuestion for missing fields | Deferred-placeholder pattern;
|
|
331
|
+
| Skeleton-fill | Full-intake; AskUserQuestion for missing fields | Deferred-placeholder pattern; I12 derive-then-ratify AskUserQuestion fires on derivation-failure (REJECT/CORRECTION/ACCEPT semantics) |
|
|
324
332
|
| Type-tag prompt | RETIRED (P287, 2026-06-02) | RETIRED (P287, 2026-06-02) — the technical/user-business axis was removed as redundant with RFC/Story persona-anchoring per ADR-060 Phase 4 |
|
|
325
|
-
| JTBD-trace + persona | Step 4-equivalent ingestion path | Step 1.5b derive-
|
|
326
|
-
| AskUserQuestion authority | Multiple branches (deviation-approval / direction-setting / taste / mechanical) |
|
|
333
|
+
| JTBD-trace + persona | Step 4-equivalent ingestion path | Step 1.5b I12 derive-then-ratify dispatch (ADR-060 Amendment 2026-06-02) — flag pre-resolution (`--jtbd=` / `--persona=`) silent-proceeds; lexical detection of JTBD-NNN citations silent-proceeds; cited-JTBD persona derivation silent-proceeds; derivation-failure → AskUserQuestion proposal with REJECT (= problem rejected; no ticket) / option-pick (acceptance) / free-text correction (correction-as-acceptance); AFK callers pre-resolve via flags or halt-with-stderr-directive when `--no-prompt` |
|
|
334
|
+
| AskUserQuestion authority | Multiple branches (deviation-approval / direction-setting / taste / mechanical) | One direction-setting branch on the I12 derive-failure fallback (ADR-044 category 1); silent-framework (category 4) on derive-success paths; zero control-flow branches keyed on the answer's substance (REJECT/option-pick/correction are uniform handlers) |
|
|
327
335
|
| README refresh | P094 inline (regenerate + stage in same commit) | Deferred to next `/wr-itil:review-problems` |
|
|
328
336
|
| Status transitions | Step 7 owns Open → Known Error → Verifying → Closed | Out of scope (creation only) |
|
|
329
337
|
| Commit grain | One commit per intake (or per split-concern set) | One commit per capture |
|
|
@@ -3,20 +3,32 @@
|
|
|
3
3
|
# P170 / Phase 3 P3.1 + Phase 4 P4.2 — behavioural fixture for
|
|
4
4
|
# capture-problem Step 1.5b JTBD-trace + persona dispatch. Per ADR-060
|
|
5
5
|
# § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by
|
|
6
|
-
# P287 (2026-06-02 — type-classification
|
|
6
|
+
# P287 (2026-06-02 base — type-classification retired) AND ADR-060
|
|
7
|
+
# Amendment 2026-06-02 (I12 hard-block REPLACED with derive-then-ratify;
|
|
8
|
+
# applies to ALL problems; no type-keyed gating):
|
|
7
9
|
#
|
|
8
10
|
# - Lexical JTBD-trace detection: description-contains-JTBD-NNN-ID →
|
|
9
11
|
# silent-resolve jtbd_trace_value to the matched IDs.
|
|
10
12
|
# - --jtbd=JTBD-NNN[,...] flag pre-resolves jtbd_trace_value silently.
|
|
11
13
|
# - --persona=<value> flag pre-resolves persona_value silently.
|
|
12
|
-
# -
|
|
13
|
-
#
|
|
14
|
+
# - Derive-failure (no flag + no lexical detection + no cited-JTBD
|
|
15
|
+
# agreement) → AskUserQuestion proposal with REJECT/option-pick/
|
|
16
|
+
# free-text correction semantics (REJECT = problem rejected; no
|
|
17
|
+
# ticket; option-pick = acceptance; correction = correction-as-
|
|
18
|
+
# acceptance).
|
|
19
|
+
# - --no-prompt + derive-failure → halt-with-stderr-directive (AFK
|
|
20
|
+
# callers MUST pre-resolve via flags).
|
|
21
|
+
# - Skeleton template carries **JTBD**: and **Persona**: body fields.
|
|
14
22
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
23
|
+
# i12_should_halt_afk predicate (NEW per ADR-060 Amendment 2026-06-02)
|
|
24
|
+
# encodes the AFK halt-without-flags branch. The historical
|
|
25
|
+
# i12_should_block predicate is preserved as a regression guard
|
|
26
|
+
# (never returns 0) against re-introduction of the type-keyed hard-block.
|
|
27
|
+
#
|
|
28
|
+
# Persona enum aligned 2026-06-02 to `docs/jtbd/<persona>/` directory
|
|
29
|
+
# names: developer / tech-lead / plugin-developer / plugin-user
|
|
30
|
+
# (architect AMEND finding 1 — historical `solo-developer` value was
|
|
31
|
+
# stale ADR-060 P4.2 spec text and is corrected in the amendment).
|
|
20
32
|
#
|
|
21
33
|
# Reference-impl pattern: this fixture exercises the algorithm directly
|
|
22
34
|
# via shell helpers; the SKILL.md prose at runtime executes the same
|
|
@@ -44,6 +56,51 @@ i12_should_block() {
|
|
|
44
56
|
return 1
|
|
45
57
|
}
|
|
46
58
|
|
|
59
|
+
# ADR-060 Amendment 2026-06-02 — NEW positive predicate for I12 derive-
|
|
60
|
+
# then-ratify AFK halt-without-flags branch. Returns 0 (halt) when:
|
|
61
|
+
# - --no-prompt is set AND
|
|
62
|
+
# - derivation failed (no flag pre-resolution + no lexical detection
|
|
63
|
+
# + no cited-JTBD agreement).
|
|
64
|
+
# Returns 1 (proceed) otherwise. Inputs:
|
|
65
|
+
# $1: no_prompt_flag ("1" if --no-prompt set, "" otherwise)
|
|
66
|
+
# $2: derivation_resolved ("1" if persona+JTBD resolved by any of
|
|
67
|
+
# flag/lexical/cited-JTBD path; "" otherwise)
|
|
68
|
+
i12_should_halt_afk() {
|
|
69
|
+
local no_prompt="$1"
|
|
70
|
+
local derivation_resolved="$2"
|
|
71
|
+
if [ "$no_prompt" = "1" ] && [ -z "$derivation_resolved" ]; then
|
|
72
|
+
return 0 # halt
|
|
73
|
+
fi
|
|
74
|
+
return 1 # proceed (interactive ratification fires, or derivation succeeded)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ADR-060 Amendment 2026-06-02 — reference impl for AskUserQuestion
|
|
78
|
+
# response semantics in the I12 derive-then-ratify dispatch. Returns:
|
|
79
|
+
# "REJECT" when user picked the Reject option
|
|
80
|
+
# "ACCEPT:<v>" when user picked a proposed option <v> as-is
|
|
81
|
+
# "CORRECT:<v>" when user supplied free-text correction <v>
|
|
82
|
+
# Behaviourally the SKILL must treat REJECT as halt-with-stderr-directive
|
|
83
|
+
# (no ticket); ACCEPT and CORRECT both yield ticket-with-value.
|
|
84
|
+
classify_ratification_response() {
|
|
85
|
+
local response="$1"
|
|
86
|
+
case "$response" in
|
|
87
|
+
REJECT) echo "REJECT" ;;
|
|
88
|
+
OPTION:*) echo "ACCEPT:${response#OPTION:}" ;;
|
|
89
|
+
FREETEXT:*) echo "CORRECT:${response#FREETEXT:}" ;;
|
|
90
|
+
*) echo "UNKNOWN:$response" ;;
|
|
91
|
+
esac
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Returns 0 when the response yields a ticket; 1 when it halts capture.
|
|
95
|
+
ratification_creates_ticket() {
|
|
96
|
+
local classified="$1"
|
|
97
|
+
case "$classified" in
|
|
98
|
+
REJECT) return 1 ;; # no ticket; capture halts
|
|
99
|
+
ACCEPT:*|CORRECT:*) return 0 ;; # ticket created
|
|
100
|
+
*) return 1 ;;
|
|
101
|
+
esac
|
|
102
|
+
}
|
|
103
|
+
|
|
47
104
|
# Reference implementation of --jtbd= flag parser. Accepts CSV; returns
|
|
48
105
|
# space-separated IDs (canonicalised) OR empty if the flag wasn't set.
|
|
49
106
|
parse_jtbd_flag() {
|
|
@@ -55,15 +112,29 @@ parse_jtbd_flag() {
|
|
|
55
112
|
}
|
|
56
113
|
|
|
57
114
|
# Reference implementation of --persona= validator. Returns the value
|
|
58
|
-
# if it's in the closed enum; halts (returns 1) otherwise.
|
|
115
|
+
# if it's in the closed enum; halts (returns 1) otherwise. Enum aligned
|
|
116
|
+
# 2026-06-02 to docs/jtbd/<persona>/ directory names (architect AMEND
|
|
117
|
+
# finding 1 — `solo-developer` was stale ADR-060 P4.2 text).
|
|
59
118
|
validate_persona() {
|
|
60
119
|
local val="$1"
|
|
61
120
|
case "$val" in
|
|
62
|
-
|
|
121
|
+
developer|tech-lead|plugin-developer|plugin-user) echo "$val"; return 0 ;;
|
|
63
122
|
*) return 1 ;;
|
|
64
123
|
esac
|
|
65
124
|
}
|
|
66
125
|
|
|
126
|
+
# Reference implementation of --no-prompt flag detector. Returns "1" if
|
|
127
|
+
# any of the supplied args is --no-prompt; empty otherwise. AFK marker
|
|
128
|
+
# per ADR-060 Amendment 2026-06-02 I12 derive-then-ratify contract.
|
|
129
|
+
parse_no_prompt_flag() {
|
|
130
|
+
for arg in "$@"; do
|
|
131
|
+
case "$arg" in
|
|
132
|
+
--no-prompt) echo "1"; return 0 ;;
|
|
133
|
+
esac
|
|
134
|
+
done
|
|
135
|
+
echo ""
|
|
136
|
+
}
|
|
137
|
+
|
|
67
138
|
@test "P3.1 detect_jtbd_trace: description with single JTBD-NNN citation extracts ID" {
|
|
68
139
|
result=$(detect_jtbd_trace "Adopters want JTBD-101 to scale down for atomic fixes")
|
|
69
140
|
[ "$result" = "JTBD-101" ]
|
|
@@ -117,9 +188,9 @@ validate_persona() {
|
|
|
117
188
|
[ -z "$result" ]
|
|
118
189
|
}
|
|
119
190
|
|
|
120
|
-
@test "P4.2 validate_persona: closed enum accepts solo-developer" {
|
|
121
|
-
result=$(validate_persona "
|
|
122
|
-
[ "$result" = "
|
|
191
|
+
@test "P4.2 validate_persona: closed enum accepts developer (architect AMEND 2026-06-02 — was solo-developer)" {
|
|
192
|
+
result=$(validate_persona "developer")
|
|
193
|
+
[ "$result" = "developer" ]
|
|
123
194
|
}
|
|
124
195
|
|
|
125
196
|
@test "P4.2 validate_persona: closed enum accepts tech-lead" {
|
|
@@ -138,6 +209,85 @@ validate_persona() {
|
|
|
138
209
|
! validate_persona "maintainer"
|
|
139
210
|
}
|
|
140
211
|
|
|
212
|
+
@test "P4.2 validate_persona: rejects stale solo-developer (architect AMEND 2026-06-02 regression guard)" {
|
|
213
|
+
# Pre-Amendment-2026-06-02 ADR-060 P4.2 text named `solo-developer` but
|
|
214
|
+
# docs/jtbd/ directory layout uses `developer/`. The amendment reconciled
|
|
215
|
+
# the enum. This test guards against drift back to the stale value.
|
|
216
|
+
! validate_persona "solo-developer"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
# ADR-060 Amendment 2026-06-02 — I12 derive-then-ratify positive controls.
|
|
221
|
+
# These exercise the new contract: AskUserQuestion fires on derivation-
|
|
222
|
+
# failure with REJECT/option-pick/free-text-correction semantics; AFK
|
|
223
|
+
# callers pre-resolve via flags or halt-with-stderr-directive on
|
|
224
|
+
# --no-prompt + derive-failure.
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
@test "I12 derive-then-ratify: i12_should_halt_afk halts on --no-prompt + derive-failure" {
|
|
228
|
+
# AFK caller passed --no-prompt; derivation failed (no flag pre-resolution,
|
|
229
|
+
# no lexical detection, no cited-JTBD agreement). MUST halt.
|
|
230
|
+
i12_should_halt_afk "1" ""
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@test "I12 derive-then-ratify: i12_should_halt_afk proceeds when --no-prompt set but derivation succeeded" {
|
|
234
|
+
# AFK caller passed --no-prompt AND pre-resolved via flags. Derivation
|
|
235
|
+
# succeeded; proceed silently with derived values.
|
|
236
|
+
! i12_should_halt_afk "1" "1"
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@test "I12 derive-then-ratify: i12_should_halt_afk proceeds when no --no-prompt (interactive mode)" {
|
|
240
|
+
# Interactive caller; derivation failed; AskUserQuestion fires (proceed
|
|
241
|
+
# past the AFK halt gate, into the ratification dispatch).
|
|
242
|
+
! i12_should_halt_afk "" ""
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@test "I12 derive-then-ratify: i12_should_halt_afk proceeds when interactive AND derivation succeeded" {
|
|
246
|
+
! i12_should_halt_afk "" "1"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@test "I12 derive-then-ratify: REJECT response halts capture (no ticket created)" {
|
|
250
|
+
classified=$(classify_ratification_response "REJECT")
|
|
251
|
+
[ "$classified" = "REJECT" ]
|
|
252
|
+
! ratification_creates_ticket "$classified"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@test "I12 derive-then-ratify: option-pick (ACCEPTANCE) yields ticket with proposed values" {
|
|
256
|
+
classified=$(classify_ratification_response "OPTION:developer")
|
|
257
|
+
[ "$classified" = "ACCEPT:developer" ]
|
|
258
|
+
ratification_creates_ticket "$classified"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@test "I12 derive-then-ratify: free-text correction (CORRECTION-AS-ACCEPTANCE) yields ticket with corrected values" {
|
|
262
|
+
classified=$(classify_ratification_response "FREETEXT:plugin-user")
|
|
263
|
+
[ "$classified" = "CORRECT:plugin-user" ]
|
|
264
|
+
ratification_creates_ticket "$classified"
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@test "I12 derive-then-ratify: parse_no_prompt_flag detects --no-prompt anywhere in args" {
|
|
268
|
+
result=$(parse_no_prompt_flag "--persona=developer" "--no-prompt" "description text")
|
|
269
|
+
[ "$result" = "1" ]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@test "I12 derive-then-ratify: parse_no_prompt_flag empty when --no-prompt absent" {
|
|
273
|
+
result=$(parse_no_prompt_flag "--persona=developer" "description text")
|
|
274
|
+
[ -z "$result" ]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@test "I12 derive-then-ratify: flag pre-resolution short-circuits derive-failure (AFK-safe path)" {
|
|
278
|
+
# AFK orchestrator pattern: pass --no-prompt PLUS --persona + --jtbd to
|
|
279
|
+
# avoid the AFK halt. Verifies the load-bearing caller-side contract.
|
|
280
|
+
no_prompt=$(parse_no_prompt_flag "--persona=developer" "--jtbd=JTBD-006" "--no-prompt" "fix work-problems iter halt")
|
|
281
|
+
[ "$no_prompt" = "1" ]
|
|
282
|
+
persona=$(validate_persona "developer")
|
|
283
|
+
[ "$persona" = "developer" ]
|
|
284
|
+
jtbd=$(parse_jtbd_flag "--jtbd=JTBD-006")
|
|
285
|
+
[ "$jtbd" = "JTBD-006" ]
|
|
286
|
+
# Derivation resolved (both flags present); halt predicate proceeds.
|
|
287
|
+
derivation_resolved="1"
|
|
288
|
+
! i12_should_halt_afk "$no_prompt" "$derivation_resolved"
|
|
289
|
+
}
|
|
290
|
+
|
|
141
291
|
@test "SKILL.md: Step 1.5b section header exists for JTBD-trace + persona dispatch" {
|
|
142
292
|
grep -qE '^### 1\.5b JTBD-trace \+ persona dispatch' "$SKILL_FILE"
|
|
143
293
|
}
|
|
@@ -162,14 +312,34 @@ validate_persona() {
|
|
|
162
312
|
grep -qE '^\*\*Persona\*\*:' "$SKILL_FILE"
|
|
163
313
|
}
|
|
164
314
|
|
|
165
|
-
@test "SKILL.md: Step 1.5b
|
|
166
|
-
grep -qE '
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
@test "SKILL.md: Step 1.5b cites I12 + ADR-060 amendment 2026-05-13" {
|
|
170
|
-
grep -qE 'ADR-060 § Phase 3 \+ Phase 4 in-scope amendment' "$SKILL_FILE"
|
|
315
|
+
@test "SKILL.md: Step 1.5b cites ADR-060 Amendment 2026-06-02 (I12 derive-then-ratify)" {
|
|
316
|
+
grep -qE 'ADR-060 Amendment 2026-06-02' "$SKILL_FILE"
|
|
171
317
|
}
|
|
172
318
|
|
|
173
319
|
@test "SKILL.md: Step 1.5b preserves JTBD-301 firewall on plugin-user-side intake" {
|
|
174
320
|
grep -qE 'plugin-user-side .* MUST NOT (prompt|carry)' "$SKILL_FILE"
|
|
175
321
|
}
|
|
322
|
+
|
|
323
|
+
@test "SKILL.md: Step 1.5b names derive-then-ratify contract" {
|
|
324
|
+
grep -qE 'derive-then-ratify' "$SKILL_FILE"
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@test "SKILL.md: --no-prompt flag declared in flag table (AFK mode marker)" {
|
|
328
|
+
grep -qE '\| `--no-prompt`' "$SKILL_FILE"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
@test "SKILL.md: Step 1.5b names REJECT-as-problem-rejection semantics" {
|
|
332
|
+
grep -qE 'REJECT.*=.*[Rr]ejection of the problem|rejection of proposed persona/JTBD = (rejection|REJECT) of the problem' "$SKILL_FILE"
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
@test "SKILL.md: Step 1.5b names AFK halt-with-stderr-directive on --no-prompt + derive-failure" {
|
|
336
|
+
grep -qE 'halt-with-stderr-directive.*AFK|AFK.*halt-with-stderr-directive|cannot derive .* under AFK' "$SKILL_FILE"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@test "SKILL.md: allowed-tools includes AskUserQuestion (for I12 ratification dispatch)" {
|
|
340
|
+
grep -qE '^allowed-tools:.*AskUserQuestion' "$SKILL_FILE"
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@test "SKILL.md: ADR-044 authority taxonomy names direction-setting (category 1) for ratification fallback" {
|
|
344
|
+
grep -qE 'direction-setting.*category 1|category 1.*direction-setting' "$SKILL_FILE"
|
|
345
|
+
}
|
|
@@ -284,15 +284,21 @@ EOF
|
|
|
284
284
|
# the runtime consumes.
|
|
285
285
|
# ---------------------------------------------------------------------------
|
|
286
286
|
|
|
287
|
-
@test "capture-problem: allowed-tools
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
#
|
|
291
|
-
#
|
|
287
|
+
@test "capture-problem: allowed-tools includes AskUserQuestion (I12 derive-then-ratify dispatch — ADR-060 Amendment 2026-06-02)" {
|
|
288
|
+
# Amended 2026-06-02: the I12 derive-then-ratify contract (ADR-060
|
|
289
|
+
# Amendment 2026-06-02) requires AskUserQuestion for the derive-failure
|
|
290
|
+
# ratification fallback (REJECT / option-pick / free-text correction
|
|
291
|
+
# semantics). Pre-amendment contract was "no AskUserQuestion at all";
|
|
292
|
+
# the amendment promoted persona+JTBD anchoring from optional to
|
|
293
|
+
# required-via-ratification, applies to ALL problems (no type-keyed
|
|
294
|
+
# gating). The historical no-AskUserQuestion contract is preserved at
|
|
295
|
+
# the derive-success paths (silent-framework per ADR-044 cat 4); the
|
|
296
|
+
# AskUserQuestion fires only on derive-failure (ADR-044 cat 1
|
|
297
|
+
# direction-setting). See SKILL.md Step 1.5b dispatch + Rule 6 row.
|
|
292
298
|
run grep -E '^allowed-tools:' "$SKILL_FILE"
|
|
293
299
|
[ "$status" -eq 0 ]
|
|
294
300
|
run grep -E '^allowed-tools:.*AskUserQuestion' "$SKILL_FILE"
|
|
295
|
-
[ "$status" -
|
|
301
|
+
[ "$status" -eq 0 ]
|
|
296
302
|
}
|
|
297
303
|
|
|
298
304
|
@test "capture-problem: allowed-tools includes Bash (for create-gate marker write)" {
|
|
@@ -414,9 +414,17 @@ rm -f "$ITER_JSON"
|
|
|
414
414
|
|
|
415
415
|
**P342 classification taxonomy — retro-surfaced observations.** When the iter-retro's Step 4b Stage 1 surfaces a ticketable observation, the routing depends on classification:
|
|
416
416
|
|
|
417
|
-
- **Recurring class-of-behaviour observation** (sibling iters hit same pattern; SKILL-contract drift; hook misbehaviour; framework-gap; pipeline instability with concrete fix path): **auto-ticket via `/wr-itil:capture-problem
|
|
417
|
+
- **Recurring class-of-behaviour observation** (sibling iters hit same pattern; SKILL-contract drift; hook misbehaviour; framework-gap; pipeline instability with concrete fix path): **auto-ticket via `/wr-itil:capture-problem` with pre-resolved persona + JTBD flags** (or `/wr-itil:manage-problem` if capture-problem sibling not yet available). This is the **mechanical-stage carve-out per run-retro Step 4a precedent** — the retro IS the system designed to mechanically observe and surface recurring class-of-behaviour, so its output ticketing is policy-authorised silent proceed per ADR-013 Rule 5. The capture-problem dispatch commits its own ticket per ADR-014; the ticket enters the WSJF queue on the orchestrator's next Step 1 scan. This is the routing that closes the silent-queue-accumulation gap P342 names.
|
|
418
|
+
|
|
419
|
+
**Dispatch shape under the I12 derive-then-ratify contract (ADR-060 Amendment 2026-06-02; R007 paired-capability gap)**: AFK callers MUST pre-resolve persona + JTBD via flags or capture-problem halts-with-stderr-directive (per capture-problem SKILL.md Step 1.5b AFK halt clause). The halt stderr is unobservable to the AFK user — silent loop-stall, violating JTBD-006's audit-trail guarantee. The iter subprocess derives both values from iter context BEFORE invoking capture-problem:
|
|
420
|
+
|
|
421
|
+
1. **Persona derivation from iter context**: the iter is dispatched against a specific ticket carrying Origin + RFC trace + story trace; derive persona from those signals. Default to `developer` when context is ambiguous — it is the dominant persona across this monorepo's JTBD corpus. **Validate the derived value against the persona enum `{developer | tech-lead | plugin-developer | plugin-user}` BEFORE dispatch** (capture-problem halts-with-directive on invalid `--persona=` per its SKILL.md Step 1.5b validation rule). On invalid-derivation, route to `outstanding_questions` (genuinely-ambiguous branch below) instead of dispatching with a bad value.
|
|
422
|
+
2. **JTBD derivation from iter context**: read the iter-prompt content. Cite `JTBD-006` for AFK-loop-continuity / iter-dispatch / orchestrator-mechanic contexts; `JTBD-001` for governance / ADR / decision-record contexts; `JTBD-101` for plugin-discoverability / plugin-developer / suite-extension contexts. Multi-JTBD entries are allowed (comma-separated, no spaces — per capture-problem's `--jtbd=` flag grammar).
|
|
423
|
+
3. **Dispatch shape**: `/wr-itil:capture-problem --no-prompt --persona=<derived> --jtbd=<derived-list> "<description>"`. The `--no-prompt` flag is the AFK-mode marker that suppresses the I12 derive-then-ratify `AskUserQuestion` fallback inside capture-problem (per its SKILL.md Step 1.5b AFK halt clause); combined with the pre-resolved `--persona` + `--jtbd` flags, the derive-success silent-proceed path fires per ADR-044 category 4 silent-framework.
|
|
424
|
+
4. **Genuinely-ambiguous derivation** (cannot pick persona/JTBD cleanly from iter context; signals contradict; derived persona fails enum validation): do NOT invoke capture-problem (would halt-with-stderr-directive into the iter subprocess's unobservable stderr; the observation is lost). Instead, queue the observation as an `outstanding_questions` entry with `category: "direction"`, naming the candidate-anchoring options for the orchestrator main-turn Step 2.5 surface. The orchestrator's `AskUserQuestion` on user return resolves the anchoring, then the user (or a future retro pass) creates the ticket.
|
|
425
|
+
|
|
418
426
|
- **Direction-setting observation** (genuine user-judgment-bound question — design choice, deviation-approval, framework boundary): route to `outstanding_questions` entry per the ITERATION_SUMMARY schema. Orchestrator-level Step 2.5 surfaces these at loop end per the existing batched `AskUserQuestion` flow. These observations preserve the user's authority surface and MUST NOT auto-ticket.
|
|
419
|
-
- **Ambiguous** (retro cannot cleanly distinguish recurring-class from direction-setting): **default to auto-ticket** per the P342 trust-boundary asymmetry. The ticket lifecycle (`/wr-itil:manage-problem` Step 9d / `/wr-itil:review-problems` Step 4) will surface any embedded direction-setting question through the standard problem-review flow. Defaulting to queue would re-introduce the silent-queue-accumulation hazard P342 closes; defaulting to ticket has zero observation-drop risk.
|
|
427
|
+
- **Ambiguous** (retro cannot cleanly distinguish recurring-class from direction-setting): **default to auto-ticket** per the P342 trust-boundary asymmetry, using the same persona + JTBD derivation contract above. The ticket lifecycle (`/wr-itil:manage-problem` Step 9d / `/wr-itil:review-problems` Step 4) will surface any embedded direction-setting question through the standard problem-review flow. Defaulting to queue would re-introduce the silent-queue-accumulation hazard P342 closes; defaulting to ticket has zero observation-drop risk. If persona/JTBD derivation itself fails (the recurring-class derivation branch's step 4), fall through to `outstanding_questions` rather than dispatch a halt-bound capture-problem.
|
|
420
428
|
|
|
421
429
|
The classification is silent agent judgement (no `AskUserQuestion` per observation — that would re-route mechanical decisions back to the user, the lazy-deferral surface P135 / ADR-044 close). The mirror locus is run-retro `Step 4b` — same trust-boundary applies whether retro fires in iter context (this surface) OR standalone in main turn (run-retro Step 4b).
|
|
422
430
|
5. **Output**: end the final message with the `ITERATION_SUMMARY` block defined below — this is how the orchestrator consumes the iteration's result.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# R007 paired-capability gap (post-commit 54ecf83 — ADR-060 Amendment
|
|
4
|
+
# 2026-06-02 + I12 derive-then-ratify in capture-problem):
|
|
5
|
+
#
|
|
6
|
+
# The P342 mechanical-stage carve-out at Step 5 iter-prompt authorises
|
|
7
|
+
# retro-surfaced recurring class-of-behaviour observations to auto-ticket
|
|
8
|
+
# via `/wr-itil:capture-problem`. The new capture-problem contract
|
|
9
|
+
# requires AFK callers (iter subprocesses) to pre-resolve persona + JTBD
|
|
10
|
+
# via `--no-prompt --persona=<value> --jtbd=JTBD-NNN` flags, OR capture
|
|
11
|
+
# halts-with-stderr-directive. The halt stderr is unobservable to the
|
|
12
|
+
# AFK user — silent loop-stall.
|
|
13
|
+
#
|
|
14
|
+
# The fix shape amends the work-problems Step 5 iter-prompt body's P342
|
|
15
|
+
# classification taxonomy bullet for "Recurring class-of-behaviour":
|
|
16
|
+
# - Derive persona from iter context; default `developer` on ambiguity.
|
|
17
|
+
# - Validate persona against the enum
|
|
18
|
+
# `{developer | tech-lead | plugin-developer | plugin-user}` before
|
|
19
|
+
# dispatch.
|
|
20
|
+
# - Derive JTBD from iter context. Cite JTBD-006 (AFK-loop-continuity),
|
|
21
|
+
# JTBD-001 (governance), JTBD-101 (plugin-developer).
|
|
22
|
+
# - Dispatch shape:
|
|
23
|
+
# /wr-itil:capture-problem --no-prompt --persona=<derived>
|
|
24
|
+
# --jtbd=<derived> "<description>"
|
|
25
|
+
# - On genuine derive-failure (cannot cleanly resolve, invalid enum):
|
|
26
|
+
# route to outstanding_questions, NOT capture-problem dispatch.
|
|
27
|
+
#
|
|
28
|
+
# Doc-lint contract assertions per ADR-037 Permitted Exception.
|
|
29
|
+
#
|
|
30
|
+
# @problem P342 (originating mechanical-stage carve-out)
|
|
31
|
+
# @problem P078 (capture-on-correction; sibling caller for the same dispatch)
|
|
32
|
+
# @adr ADR-060 Amendment 2026-06-02 (I12 derive-then-ratify)
|
|
33
|
+
# @adr ADR-044 (Decision-Delegation Contract — silent-framework cat 4 on
|
|
34
|
+
# derive-success; direction cat 1 on outstanding_questions route)
|
|
35
|
+
# @adr ADR-014 (single-commit grain — SKILL + bats + changeset)
|
|
36
|
+
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — audit-trail outcome:
|
|
37
|
+
# halt-with-stderr in iter is invisible to AFK user; route to
|
|
38
|
+
# outstanding_questions or pre-resolve flags to preserve trail)
|
|
39
|
+
|
|
40
|
+
setup() {
|
|
41
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
42
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/work-problems/SKILL.md"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@test "work-problems R007: SKILL.md exists" {
|
|
46
|
+
[ -f "$SKILL_MD" ]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ── Dispatch shape: --no-prompt + --persona + --jtbd flags ─────────────────
|
|
50
|
+
|
|
51
|
+
@test "work-problems R007: iter-prompt dispatch shape names --no-prompt flag" {
|
|
52
|
+
# AFK-mode marker. Without --no-prompt, capture-problem's I12 derive-
|
|
53
|
+
# then-ratify AskUserQuestion fallback could fire inside the iter
|
|
54
|
+
# subprocess where the user is absent.
|
|
55
|
+
run grep -nE '\-\-no-prompt' "$SKILL_MD"
|
|
56
|
+
[ "$status" -eq 0 ]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@test "work-problems R007: iter-prompt dispatch shape names --persona flag" {
|
|
60
|
+
# Pre-resolves persona to silent-proceed path. Required by capture-
|
|
61
|
+
# problem SKILL.md Step 1.5b silent-framework branch.
|
|
62
|
+
run grep -nE '\-\-persona=' "$SKILL_MD"
|
|
63
|
+
[ "$status" -eq 0 ]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@test "work-problems R007: iter-prompt dispatch shape names --jtbd flag" {
|
|
67
|
+
# Pre-resolves JTBD-trace to silent-proceed path.
|
|
68
|
+
run grep -nE '\-\-jtbd=' "$SKILL_MD"
|
|
69
|
+
[ "$status" -eq 0 ]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@test "work-problems R007: iter-prompt cites I12 derive-then-ratify contract authority" {
|
|
73
|
+
# The dispatch contract's upstream authority is ADR-060 Amendment
|
|
74
|
+
# 2026-06-02 + I12 derive-then-ratify. Cite so future authors don't
|
|
75
|
+
# drift on the rationale.
|
|
76
|
+
run grep -nE 'I12 derive-then-ratify|derive-then-ratify.*ADR-060|ADR-060 Amendment 2026-06-02' "$SKILL_MD"
|
|
77
|
+
[ "$status" -eq 0 ]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@test "work-problems R007: iter-prompt cites R007 paired-capability gap" {
|
|
81
|
+
# R007 names the gap this amendment closes. Cited so the iter-prompt
|
|
82
|
+
# ties back to the originating risk-register entry.
|
|
83
|
+
run grep -nE 'R007.*paired-capability|paired-capability.*R007|R007 paired-capability gap' "$SKILL_MD"
|
|
84
|
+
[ "$status" -eq 0 ]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ── Persona derivation contract ────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
@test "work-problems R007: iter-prompt derives persona from iter context" {
|
|
90
|
+
# Derive-don't-ask: persona signals come from the ticket the iter
|
|
91
|
+
# is dispatched against (Origin + RFC + story trace).
|
|
92
|
+
run grep -nE 'derive persona|Persona derivation' "$SKILL_MD"
|
|
93
|
+
[ "$status" -eq 0 ]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@test "work-problems R007: iter-prompt defaults persona to developer on ambiguity" {
|
|
97
|
+
# Default chosen per JTBD-006 + dominant-persona in this monorepo.
|
|
98
|
+
run grep -nE 'Default to .?developer.?|default to .?developer.? if|default to .?developer.? when' "$SKILL_MD"
|
|
99
|
+
[ "$status" -eq 0 ]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@test "work-problems R007: iter-prompt names the persona enum for pre-dispatch validation" {
|
|
103
|
+
# capture-problem halts-with-directive on invalid --persona= value
|
|
104
|
+
# (SKILL.md Step 1.5b). Iter must validate against the enum BEFORE
|
|
105
|
+
# dispatch, or fall through to outstanding_questions.
|
|
106
|
+
run grep -nE 'developer.*tech-lead.*plugin-developer.*plugin-user|persona enum' "$SKILL_MD"
|
|
107
|
+
[ "$status" -eq 0 ]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# ── JTBD derivation contract ───────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
@test "work-problems R007: iter-prompt derives JTBD from iter context" {
|
|
113
|
+
# Derive-don't-ask: JTBD signals come from iter-prompt content.
|
|
114
|
+
run grep -nE 'derive.*JTBD|JTBD derivation' "$SKILL_MD"
|
|
115
|
+
[ "$status" -eq 0 ]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@test "work-problems R007: iter-prompt cites JTBD-006 for AFK-loop-continuity contexts" {
|
|
119
|
+
run grep -nE 'JTBD-006.*AFK|AFK.*JTBD-006|JTBD-006.*loop-continuity|JTBD-006.*iter-dispatch' "$SKILL_MD"
|
|
120
|
+
[ "$status" -eq 0 ]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@test "work-problems R007: iter-prompt cites JTBD-001 for governance contexts" {
|
|
124
|
+
run grep -nE 'JTBD-001.*governance|governance.*JTBD-001|JTBD-001.*ADR|JTBD-001.*decision' "$SKILL_MD"
|
|
125
|
+
[ "$status" -eq 0 ]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@test "work-problems R007: iter-prompt cites JTBD-101 for plugin-developer contexts" {
|
|
129
|
+
run grep -nE 'JTBD-101.*plugin|plugin.*JTBD-101|JTBD-101.*discoverability|JTBD-101.*suite-extension' "$SKILL_MD"
|
|
130
|
+
[ "$status" -eq 0 ]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ── Fall-through to outstanding_questions on derive-failure ────────────────
|
|
134
|
+
|
|
135
|
+
@test "work-problems R007: iter-prompt routes genuinely-ambiguous derivation to outstanding_questions" {
|
|
136
|
+
# Architect AMEND closed: when derivation cannot cleanly resolve (or
|
|
137
|
+
# persona fails enum validation), do NOT invoke capture-problem with
|
|
138
|
+
# a bad value (would halt-with-stderr-directive into unobservable iter
|
|
139
|
+
# subprocess stderr); instead, queue for orchestrator main-turn Step
|
|
140
|
+
# 2.5 surfacing.
|
|
141
|
+
run grep -nE 'Genuinely-ambiguous|cannot pick persona/JTBD cleanly|fall through to .?outstanding_questions' "$SKILL_MD"
|
|
142
|
+
[ "$status" -eq 0 ]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@test "work-problems R007: iter-prompt names the unobservable-stderr failure mode" {
|
|
146
|
+
# Documents WHY the dispatch contract matters: the halt stderr does
|
|
147
|
+
# not surface to the AFK user; the loop silently stalls. JTBD-006
|
|
148
|
+
# audit-trail violation framing.
|
|
149
|
+
run grep -nE 'stderr is unobservable|unobservable.*stderr|silent loop-stall|halt stderr' "$SKILL_MD"
|
|
150
|
+
[ "$status" -eq 0 ]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# ── Composition with the existing P342 carve-out ───────────────────────────
|
|
154
|
+
|
|
155
|
+
@test "work-problems R007: amendment preserves the P342 mechanical-stage carve-out framing" {
|
|
156
|
+
# The R007 fix is ADDITIVE — it carries the new dispatch shape into
|
|
157
|
+
# the existing Recurring-class auto-ticket bullet. The P342 carve-out
|
|
158
|
+
# itself (mechanical-stage / Step 4a precedent / ADR-013 Rule 5)
|
|
159
|
+
# remains the authority for auto-ticketing at all.
|
|
160
|
+
run grep -nE 'mechanical-stage carve-out|policy-authorised silent proceed' "$SKILL_MD"
|
|
161
|
+
[ "$status" -eq 0 ]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@test "work-problems R007: Ambiguous bullet reuses the persona+JTBD derivation contract" {
|
|
165
|
+
# The Ambiguous-classification default-to-auto-ticket path also goes
|
|
166
|
+
# through the new dispatch contract (same flag-resolution discipline);
|
|
167
|
+
# otherwise the Ambiguous branch becomes a halt vector.
|
|
168
|
+
run grep -nE 'same persona \+ JTBD derivation contract|using the same persona \+ JTBD derivation' "$SKILL_MD"
|
|
169
|
+
[ "$status" -eq 0 ]
|
|
170
|
+
}
|