@windyroad/itil 0.56.0 → 0.56.1
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/hooks/itil-assistant-output-review.sh +28 -13
- package/hooks/lib/detectors.sh +78 -0
- package/hooks/test/itil-assistant-output-review.bats +48 -0
- package/package.json +1 -1
- package/skills/capture-problem/SKILL.md +39 -24
- package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +146 -93
- package/skills/restore-incident/SKILL.md +13 -3
- package/skills/restore-incident/test/restore-incident-contract.bats +28 -0
- package/skills/review-problems/SKILL.md +9 -7
- package/skills/review-problems/test/review-problems-contract.bats +28 -0
- package/skills/work-problems/SKILL.md +13 -0
- package/skills/work-problems/test/work-problems-adr-013-rule-6-p352-amendment.bats +9 -3
|
@@ -51,22 +51,37 @@ if [ -z "$ASSISTANT_TEXT" ]; then
|
|
|
51
51
|
exit 0
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
-
# Scan for prose-ask patterns. If
|
|
54
|
+
# Scan for prose-ask patterns. If one matches, emit the prose-ask nudge.
|
|
55
55
|
MATCH=$(echo "$ASSISTANT_TEXT" | detect_prose_ask 2>/dev/null) || true
|
|
56
|
-
if [ -
|
|
56
|
+
if [ -n "$MATCH" ]; then
|
|
57
|
+
# Emit stopReason. Structured JSON so Claude Code injects the nudge
|
|
58
|
+
# into the next assistant context. The user does not see this — the
|
|
59
|
+
# next turn does.
|
|
60
|
+
jq -n --arg match "$MATCH" '{
|
|
61
|
+
stopReason: (
|
|
62
|
+
"PROSE-ASK DETECTED in your last turn (pattern: \"" + $match + "\"). " +
|
|
63
|
+
"If the decision is obvious from direction / policy / session context, ACT — do not ask. " +
|
|
64
|
+
"If genuinely ambiguous, re-emit via the AskUserQuestion tool. " +
|
|
65
|
+
"Never prose-ask. See ADR-013 Rule 1 + feedback_act_on_obvious_decisions.md."
|
|
66
|
+
)
|
|
67
|
+
}'
|
|
57
68
|
exit 0
|
|
58
69
|
fi
|
|
59
70
|
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
# P403: scan for mechanical-step-framed-as-user-optional. When a skill
|
|
72
|
+
# contract mandates a mechanical step, re-surfacing it as a user decision
|
|
73
|
+
# (or a budget-caution skip) reintroduces the friction the mechanical-stage
|
|
74
|
+
# carve-out (P132 / ADR-044) was engineered to remove.
|
|
75
|
+
MECH_MATCH=$(echo "$ASSISTANT_TEXT" | detect_mechanical_optional 2>/dev/null) || true
|
|
76
|
+
if [ -n "$MECH_MATCH" ]; then
|
|
77
|
+
jq -n --arg match "$MECH_MATCH" '{
|
|
78
|
+
stopReason: (
|
|
79
|
+
"MECHANICAL-STEP-FRAMED-AS-OPTIONAL detected in your last turn (closer: \"" + $match + "\"). " +
|
|
80
|
+
"A skill contract that mandates a mechanical step has already resolved that decision — do not re-surface it as a user choice or skip it on budget-caution grounds. " +
|
|
81
|
+
"Run the step, then report it done. See P132 / ADR-044 mechanical-stage carve-out."
|
|
82
|
+
)
|
|
83
|
+
}'
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
71
86
|
|
|
72
87
|
exit 0
|
package/hooks/lib/detectors.sh
CHANGED
|
@@ -79,6 +79,84 @@ CORRECTION_SIGNAL_PATTERNS=(
|
|
|
79
79
|
'\bno\b.*\bwrong\b'
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
+
# P403: mechanical-step-framed-as-user-optional detection. When a skill
|
|
83
|
+
# contract mandates a mechanical step (review-problems Step 7 auto-release,
|
|
84
|
+
# a full re-rank, etc.), the agent must not re-surface the step as a user
|
|
85
|
+
# decision — or as a budget-caution skip — in end-of-turn prose. That
|
|
86
|
+
# reintroduces the exact friction the mechanical-stage carve-out (P132 /
|
|
87
|
+
# ADR-044 category 4 silent-framework) was engineered to remove.
|
|
88
|
+
#
|
|
89
|
+
# The detector fires only when ALL THREE co-occur in the turn text: a
|
|
90
|
+
# step-skip verb, a step / mechanical-pass reference, AND an
|
|
91
|
+
# offloaded-justification closer. The three-way AND is the anti-over-fire
|
|
92
|
+
# guard (inverse-P078 / P132): a legitimately user-owned decision, or a
|
|
93
|
+
# skipped step reported with a substantive reason, does not fire. Same
|
|
94
|
+
# accepted false-positive posture as CORRECTION_SIGNAL_PATTERNS above — a
|
|
95
|
+
# rare over-fire costs only a non-blocking next-turn nudge.
|
|
96
|
+
#
|
|
97
|
+
# Case-insensitive (grep -Eqi). grep matches per line; the two P403
|
|
98
|
+
# evidence phrasings are single-line, so per-line matching is sufficient.
|
|
99
|
+
|
|
100
|
+
# 1. Step-skip verbs — the step was not run.
|
|
101
|
+
MECHANICAL_STEP_SKIP_PATTERNS=(
|
|
102
|
+
'\bskip(ped|ping|s)?\b'
|
|
103
|
+
'\bdefer(red|ring|s)?\b'
|
|
104
|
+
'\bomit(ted|ting|s)?\b'
|
|
105
|
+
'\bleft out\b'
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# 2. Step / mechanical-pass reference — the skipped thing is a skill step.
|
|
109
|
+
MECHANICAL_STEP_REF_PATTERNS=(
|
|
110
|
+
'\bstep[[:space:]]+[0-9]+'
|
|
111
|
+
'\bre-rank\b'
|
|
112
|
+
'\bauto-release\b'
|
|
113
|
+
'\binbound-discovery\b'
|
|
114
|
+
'\brelevance-close\b'
|
|
115
|
+
'\bverification queue\b'
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# 3. Offloaded-justification closer — the mandate handed back to the user
|
|
119
|
+
# ("your call") or self-authorised away on budget grounds ("context budget").
|
|
120
|
+
MECHANICAL_OPTIONAL_PATTERNS=(
|
|
121
|
+
'your call'
|
|
122
|
+
'up to you'
|
|
123
|
+
'worth doing'
|
|
124
|
+
"user'?s call"
|
|
125
|
+
'if you (want|prefer|would like)'
|
|
126
|
+
'feel free to'
|
|
127
|
+
'context budget'
|
|
128
|
+
'to stay within'
|
|
129
|
+
'budget[- ]?caution'
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# detect_mechanical_optional: scans text on stdin. Exits 0 (and echoes the
|
|
133
|
+
# matched optional-closer phrase) only when a step-skip verb, a step
|
|
134
|
+
# reference, AND an offloaded-justification closer ALL match somewhere in
|
|
135
|
+
# the text. Exits 1 otherwise. Mirrors detect_prose_ask's shape.
|
|
136
|
+
detect_mechanical_optional() {
|
|
137
|
+
local text
|
|
138
|
+
text=$(cat)
|
|
139
|
+
local pattern
|
|
140
|
+
local skip_hit="" ref_hit="" opt_hit=""
|
|
141
|
+
for pattern in "${MECHANICAL_STEP_SKIP_PATTERNS[@]}"; do
|
|
142
|
+
if echo "$text" | grep -Eqi -- "$pattern"; then skip_hit=1; break; fi
|
|
143
|
+
done
|
|
144
|
+
[ -n "$skip_hit" ] || return 1
|
|
145
|
+
for pattern in "${MECHANICAL_STEP_REF_PATTERNS[@]}"; do
|
|
146
|
+
if echo "$text" | grep -Eqi -- "$pattern"; then ref_hit=1; break; fi
|
|
147
|
+
done
|
|
148
|
+
[ -n "$ref_hit" ] || return 1
|
|
149
|
+
for pattern in "${MECHANICAL_OPTIONAL_PATTERNS[@]}"; do
|
|
150
|
+
if echo "$text" | grep -Eqi -- "$pattern"; then
|
|
151
|
+
echo "$pattern"
|
|
152
|
+
opt_hit=1
|
|
153
|
+
break
|
|
154
|
+
fi
|
|
155
|
+
done
|
|
156
|
+
[ -n "$opt_hit" ] || return 1
|
|
157
|
+
return 0
|
|
158
|
+
}
|
|
159
|
+
|
|
82
160
|
# detect_prose_ask: scans text on stdin for canonical prose-ask
|
|
83
161
|
# phrasings. Exits 0 if any pattern matches, 1 otherwise. Writes the
|
|
84
162
|
# first matched phrase to stdout (for observability in the Stop hook
|
|
@@ -174,3 +174,51 @@ JSON
|
|
|
174
174
|
[ "$status" -eq 0 ]
|
|
175
175
|
[[ "$output" == *"stopReason"* ]]
|
|
176
176
|
}
|
|
177
|
+
|
|
178
|
+
# P403: mechanical-step-framed-as-user-optional. When a skill contract
|
|
179
|
+
# mandates a mechanical step, the agent must not re-surface it as a user
|
|
180
|
+
# decision (or a budget-caution skip) in end-of-turn prose. The detector
|
|
181
|
+
# fires only when a step-skip signal, a step/mechanical-pass reference,
|
|
182
|
+
# AND an offloaded-justification closer all co-occur (the AND-discriminator).
|
|
183
|
+
|
|
184
|
+
@test "review: P403 evidence 1 — 'Step 7 auto-release skipped — your call' triggers stopReason" {
|
|
185
|
+
write_transcript "review the backlog" "Done. Step 7 auto-release skipped — your call whether to drain via /wr-risk-scorer:assess-release."
|
|
186
|
+
run run_hook
|
|
187
|
+
[ "$status" -eq 0 ]
|
|
188
|
+
[[ "$output" == *"stopReason"* ]]
|
|
189
|
+
[[ "$output" == *"mechanical"* ]]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@test "review: P403 evidence 2 — 'Skipped the full re-rank (Step 2) ... to stay within context budget' triggers stopReason" {
|
|
193
|
+
write_transcript "review problems" "Skipped the full 40-ticket re-rank (Step 2), inbound-discovery pipeline (Step 4.5), and relevance-close pass (Step 4.6) to stay within context budget."
|
|
194
|
+
run run_hook
|
|
195
|
+
[ "$status" -eq 0 ]
|
|
196
|
+
[[ "$output" == *"stopReason"* ]]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@test "review: AND-discriminator — optional closer WITHOUT a step-skip does not trigger stopReason" {
|
|
200
|
+
# "up to you" is a legitimately user-owned decision with no skipped
|
|
201
|
+
# mechanical step in context — must NOT fire (the inverse-P078 guard).
|
|
202
|
+
write_transcript "which theme" "Both palettes meet contrast AA. It is up to you which one reads better for your brand."
|
|
203
|
+
run run_hook
|
|
204
|
+
[ "$status" -eq 0 ]
|
|
205
|
+
[[ "$output" != *"stopReason"* ]]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@test "review: AND-discriminator — skipped step with a real reason (no offload) does not trigger stopReason" {
|
|
209
|
+
# A skipped step reported with a substantive reason (not offloaded to the
|
|
210
|
+
# user, not budget-caution) is a legitimate report, not the P403 defect.
|
|
211
|
+
write_transcript "run it" "Step 2 skipped because its precondition is already satisfied by Step 1. Proceeding to Step 3."
|
|
212
|
+
run run_hook
|
|
213
|
+
[ "$status" -eq 0 ]
|
|
214
|
+
[[ "$output" != *"stopReason"* ]]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@test "review: P403 nudge stays within the ADR-045 hook injection budget" {
|
|
218
|
+
write_transcript "review the backlog" "Done. Step 7 auto-release skipped — your call whether to drain."
|
|
219
|
+
run run_hook
|
|
220
|
+
[ "$status" -eq 0 ]
|
|
221
|
+
[[ "$output" == *"stopReason"* ]]
|
|
222
|
+
# ADR-045 advisory band: keep the injected nudge well under 1000 bytes.
|
|
223
|
+
[ "${#output}" -lt 1000 ]
|
|
224
|
+
}
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ This skill is the foreground-lightweight-capture variant of `/wr-itil:manage-pro
|
|
|
14
14
|
|
|
15
15
|
When referencing problem, JTBD, ADR, or RFC IDs in prose output (stderr advisories, capture-report messages), always include the human-readable title or substance on first mention. Use the format `JTBD-001 (Enforce Governance Without Slowing Down)`, not bare `JTBD-001`.
|
|
16
16
|
|
|
17
|
-
**Brief-before-ID discipline at the Step 1.5b
|
|
17
|
+
**Brief-before-ID discipline at the Step 1.5b low-confidence interview surface (P350).** Per the P401 correction the low-confidence path does NOT surface candidate `JTBD-NNN` / `persona-slug` IDs at all — it **interviews** the human about *who* hits the problem and *what job* they are trying to get done, in plain language, so the brief-before-ID discipline is satisfied by construction (no ID is a carrier of meaning). If any option or prose in the interview references an existing job/persona for context, it MUST inline the job statement + the persona's key constraint BEFORE any ID. The user reads the prompt without project filesystem access (mobile clients, accessibility tooling, notification surfaces) and cannot follow links into `docs/jtbd/`. Mirrors the canonical `/wr-architect:create-adr` Step 5 § 5a Rule 3 ("No IDs as explainers"). See also session memory `feedback_brief_before_id.md`.
|
|
18
18
|
|
|
19
19
|
## When to invoke
|
|
20
20
|
|
|
@@ -29,7 +29,7 @@ When referencing problem, JTBD, ADR, or RFC IDs in prose output (stderr advisori
|
|
|
29
29
|
|
|
30
30
|
## Rule 6 audit (per ADR-032 + ADR-013)
|
|
31
31
|
|
|
32
|
-
This skill has **
|
|
32
|
+
This skill has **one direction-setting AskUserQuestion surface — the low-confidence persona/JTBD elicitation interview (fired only when derivation is weak; it may span more than one question to elicit who + why), plus the ADR-068/P288 human-ratify surface when the elicited who/why warrants a NEW persona/JTBD**. Each potentially-interactive decision is framework-mediated per ADR-044:
|
|
33
33
|
|
|
34
34
|
| Decision | Resolution |
|
|
35
35
|
|----------|-----------|
|
|
@@ -39,14 +39,14 @@ This skill has **at most one direction-setting AskUserQuestion (the I12 derive-t
|
|
|
39
39
|
| Effort | **Derived at capture** silently per Step 4a (ADR-067 t-shirt buckets + ADR-026 grounding/sentinel). No deferred placeholder. |
|
|
40
40
|
| 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). |
|
|
41
41
|
| 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. |
|
|
42
|
-
| JTBD-trace + persona derivation (I12 derive-
|
|
43
|
-
| AFK
|
|
42
|
+
| JTBD-trace + persona derivation (I12 derive → interview-on-low-confidence → classify → ratify-creation-only — ADR-060 Amendment 2026-06-02 as corrected by P401 2026-06-29/2026-07-02) | **Derive-first per ADR-044 category 4 (silent-framework); interview + creation-ratify per category 1 (direction-setting).** 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. **Low-confidence** → **INTERVIEW** the human to elicit the real who/why (substantive non-leading questions, NOT propose an ID) → the agent classifies: elicited job/persona matches an EXISTING artefact → map + proceed autonomously; NO existing fit → human ratifies the **creation** of a new persona/JTBD (ADR-068/P288) → map + proceed. **A real problem is NEVER discarded over anchoring uncertainty.** Scope-rejection (elicited who/why out of scope) is **external-report-only** at `/wr-itil:manage-problem` ingestion — never here. |
|
|
43
|
+
| AFK low-confidence (`--no-prompt`) — preserve the finding | **Create-with-unconfirmed-anchoring + queue the elicitation per P401 never-discard + JTBD-006 save-and-continue.** 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) → do NOT fire `AskUserQuestion`, do NOT shoehorn a best-fit, do NOT auto-create a new artefact. Capture the ticket with the greppable `(unconfirmed — elicitation queued)` anchoring sentinel + stderr advisory, and queue the elicitation interview for the next interactive session (the AFK orchestrator surfaces it via `outstanding_questions`). ADR-074 is preserved by the existing downstream oversight gates (ADR-068/P288, ADR-090, ADR-060 I13), not by refusing the capture. AFK orchestrators still SHOULD pre-resolve persona+JTBD via flags to skip the sentinel path entirely. |
|
|
44
44
|
|
|
45
|
-
**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).
|
|
45
|
+
**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). **P401 supersession (2026-06-29/2026-07-02):** the 2026-06-02 amendment's *"reject of the proposed persona/JTBD = rejection of the problem; no ticket created"* clause (quoted above) is REVERSED — low-confidence now interviews to elicit the real who/why (never proposes an ID), the agent classifies existing-vs-new, the human ratifies only the CREATION of a new persona/JTBD, and a real problem is NEVER discarded over anchoring uncertainty; scope-rejection is external-report-only. See the P401 supersession note in Step 1.5b.
|
|
46
46
|
|
|
47
|
-
Per ADR-013 Rule 6 fail-safe: every decision above resolves without interactive user input in non-interactive contexts. The
|
|
47
|
+
Per ADR-013 Rule 6 fail-safe: every decision above resolves without interactive user input in non-interactive contexts. The low-confidence **interview** fires only on derivation-failure (interactive surface) AND only when `--no-prompt` is absent; under `--no-prompt` the AFK path captures with the unconfirmed-anchoring sentinel + queues the elicitation (P401) — never silently shoehorning a best-fit or discarding the finding.
|
|
48
48
|
|
|
49
|
-
**ADR-013 Rule 6 carve-out audit (P352, 2026-06-06 amendment)**: the universal AFK default is **queue-and-continue** (queue the decision, continue the loop). The
|
|
49
|
+
**ADR-013 Rule 6 carve-out audit (P352, 2026-06-06 amendment; corrected by P401 2026-06-29/2026-07-02)**: the universal AFK default is **queue-and-continue** (queue the decision, continue the loop). The AFK low-confidence path now **conforms** to that default rather than carving out from it: it captures the ticket with the `(unconfirmed — elicitation queued)` anchoring sentinel and queues the elicitation interview — the finding is preserved (P401 never-discard + JTBD-006 "save findings and move to the next problem"), NOT refused. ADR-074 (Confirm decision substance before building dependent work) is still honoured, but by a different tactic than the retired no-ticket halt: the ticket's anchoring is **explicitly unconfirmed** (a greppable sentinel, never a shoehorned best-fit masquerading as confirmed — that would be the P395 harm), and dependent RFC/story/fix work stays gated on anchoring confirmation via the existing downstream oversight markers — **ADR-068/P288** (a new persona/JTBD needs human ratification before it exists), **ADR-090** (story maps/stories carry a drift-invalidated oversight marker), and the **ADR-060 I13** propose-fix RFC-trace guard. Until the deferred `/wr-itil:work-problems` caller-side wiring lands (route the queued elicitation into the orchestrator's `outstanding_questions` batch — tracked on P401), that downstream protection rests on those existing markers, not on refusing the capture. (The Empty `$ARGUMENTS` halt remains a genuine caller-contract halt — there is no description to capture.)
|
|
50
50
|
|
|
51
51
|
## Steps
|
|
52
52
|
|
|
@@ -75,7 +75,7 @@ fi
|
|
|
75
75
|
|------|-------------------|
|
|
76
76
|
| `--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). |
|
|
77
77
|
| `--persona=<value>` | Pre-resolves the persona value. Step 1.5b skips persona derivation. Value MUST be one of the directory names under `docs/jtbd/*/` (the adopter's real persona corpus — e.g. `maintainer`, `smb-owner` in adopter repos), falling back to the home-repo set `{developer, tech-lead, plugin-developer, plugin-user}` only when no `docs/jtbd/` directories exist. P383 — never hardcode the home-repo enum (P151/P317 adopter-portability). |
|
|
78
|
-
| `--no-prompt` | **AFK mode marker** (REVIVED 2026-06-02 per ADR-060 Amendment 2026-06-02). Suppresses the
|
|
78
|
+
| `--no-prompt` | **AFK mode marker** (REVIVED 2026-06-02 per ADR-060 Amendment 2026-06-02; behaviour corrected by P401 2026-06-29/2026-07-02). Suppresses the low-confidence interview. When set AND derivation fails (no JTBD-NNN citations + no `--persona=` flag + no `--jtbd=` flag, OR cited-JTBD persona-disagreement) → capture the ticket with the `(unconfirmed — elicitation queued)` anchoring sentinel + stderr advisory and queue the elicitation (P401 never-discard) — it no longer halts-refuses. AFK orchestrators (`/wr-itil:work-problems` capture-on-correction sub-flow, agent-mid-iter `capture-problem` invocations) SHOULD still pass this flag PLUS pre-resolved `--persona` + `--jtbd` flags to skip the sentinel path entirely. |
|
|
79
79
|
|
|
80
80
|
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.
|
|
81
81
|
|
|
@@ -85,30 +85,44 @@ Empty description (post-flag-strip) halts per the Rule 6 audit above.
|
|
|
85
85
|
|
|
86
86
|
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).
|
|
87
87
|
|
|
88
|
-
### 1.5b JTBD-trace + persona dispatch — I12 derive-
|
|
88
|
+
### 1.5b JTBD-trace + persona dispatch — I12 derive → interview-on-low-confidence → classify (ADR-060 Amendment 2026-06-02, corrected by P401)
|
|
89
89
|
|
|
90
|
-
Per ADR-060 § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by P287 (2026-06-02 base — type-classification retired)
|
|
90
|
+
Per ADR-060 § Phase 3 + Phase 4 in-scope amendment (2026-05-13), as amended by P287 (2026-06-02 base — type-classification retired), by ADR-060 Amendment 2026-06-02 (I12 hard-block REPLACED with derive-then-ratify; applies to ALL problems; no type-keyed gating), AND by the **P401 correction (user direction 2026-06-29, sharpened 2026-07-02)** — the low-confidence path is now **interview-to-elicit-who/why**, NOT propose-candidate-IDs-for-ratification; and a real problem is **never discarded** over anchoring uncertainty. Fires UNCONDITIONALLY. Both `jtbd_trace_value` and `persona_value` are REQUIRED on every captured ticket.
|
|
91
91
|
|
|
92
|
-
**
|
|
92
|
+
**The corrected contract (one principle, both fields):**
|
|
93
|
+
|
|
94
|
+
1. The agent **derives** persona + JTBD from the description.
|
|
95
|
+
2. **Confident** → map to the existing persona/JTBD and **proceed autonomously** (no human — ADR-044 category-4 silent-framework).
|
|
96
|
+
3. **Not confident** → do **NOT** shoehorn into the nearest existing ID, and do **NOT** ask *"is it JTBD-XXXX?"*. **Interview** the human (`AskUserQuestion`, ADR-013 Rule 1 structured form; may be more than one question) with substantive, non-leading questions about *who* hits this and *what they are trying to get done* — requirements elicitation of the real persona/job, **not** ratification of a guessed ID (this is *more* brief-before-ID-compliant than the old prompt — P350 — since no ID is surfaced at all).
|
|
97
|
+
4. From the elicited who/why the **agent classifies**: matches an **existing** persona/JTBD → map and proceed (autonomous — the ADR-068 boundary applies only to *creation*, not mapping); **no existing fit** → a **new** persona/JTBD is warranted → route the **creation** to the ADR-068/P288 human-ratify surface (`/wr-jtbd:update-guide` then `/wr-jtbd:confirm-jobs-and-personas`); on ratification, map to the new ID and proceed.
|
|
98
|
+
|
|
99
|
+
The human is involved only for **substance** — the interview when derivation is weak, and ratifying the **creation** of a genuinely new persona/JTBD — never to bless an ID. **A real problem is NEVER discarded over anchoring uncertainty**; anchoring uncertainty is resolved by elicitation, not by rejecting the problem. Scope-rejection (deciding the elicited who/why is out of scope to support) is an **external-report-only** decision handled at `/wr-itil:manage-problem` ingestion — maintainer-side captures here always anchor to an existing or a new job. This is the same `covered → agent proceeds / uncovered → human ratifies a new artefact` boundary ADR-073 draws for the fix-approach axis, applied to the who/why axis.
|
|
100
|
+
|
|
101
|
+
> **P401 supersession note (user direction 2026-06-29 / 2026-07-02).** This reverses the 2026-06-02 ADR-060 amendment substance that framed *"REJECT of the proposed persona/JTBD = rejection of the problem; no ticket created."* That framing shoehorned problems into the nearest ID and discarded legitimate problems over anchoring uncertainty. The reversal is deliberate and user-directed, NOT drift. The corresponding **ADR-060 body amendment** and the **ADR-073 additive symmetric-escalation note** are **queued on P401** for the interactive ratification drain (per P357 — user direction is not itself ADR-body-substance ratification); until they land, this SKILL leads and the ADR bodies follow — the same governed trade-off as the P287 type-axis retirement (see the `## Related` note below). The AFK caller-side wiring in `/wr-itil:work-problems` (route the queued elicitation into its `outstanding_questions` batch) is likewise queued on P401.
|
|
102
|
+
|
|
103
|
+
**Resolve `jtbd_trace_value`** (an ORDERED list of JTBD IDs) via the following derive → interview-on-low-confidence → classify dispatch:
|
|
93
104
|
|
|
94
105
|
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.
|
|
95
106
|
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).
|
|
96
|
-
3. **Else (no flag, no lexical detection —
|
|
97
|
-
- **
|
|
98
|
-
- **
|
|
99
|
-
- **
|
|
100
|
-
4. **AFK
|
|
107
|
+
3. **Else (no flag, no lexical detection — low-confidence path)**: do **NOT** shoehorn a best-fit JTBD and do **NOT** propose candidate `JTBD-NNN` IDs for ratification. **Interview** the human via `AskUserQuestion` (may span who + why in one dispatch shared with the persona resolution below) with substantive, non-leading questions about *what job* the affected user is trying to get done. From the elicited answer the **agent classifies**:
|
|
108
|
+
- **Elicited job matches an existing JTBD** → set `jtbd_trace_value` to that ID and proceed (autonomous mapping — no further ratification; the ADR-068 human-oversight boundary applies only to *creation*).
|
|
109
|
+
- **No existing JTBD fits** → a new JTBD is warranted → route the **creation** to the ADR-068/P288 human-ratify surface (`/wr-jtbd:update-guide` then `/wr-jtbd:confirm-jobs-and-personas`); on ratification, set `jtbd_trace_value` to the new ID and proceed.
|
|
110
|
+
- **The problem is never discarded** over anchoring uncertainty. (Scope-rejection — deciding the elicited who/why is out of scope to support — is an external-report-only decision at `/wr-itil:manage-problem` ingestion, never here.)
|
|
111
|
+
4. **AFK (the `--no-prompt` branch) — preserve the finding, queue the elicitation**: if `--no-prompt` was set in Step 1 AND control reaches step 3 (no flag + no lexical detection) → SKIP the `AskUserQuestion`, do **NOT** shoehorn a best-fit ID, and do **NOT** auto-create a new JTBD. Set `jtbd_trace_value` to the greppable unconfirmed-anchoring sentinel `(unconfirmed — elicitation queued)`, capture the ticket, and **queue the persona/JTBD elicitation interview** for the next interactive session (the AFK orchestrator surfaces it via its `outstanding_questions` batch). Emit stderr advisory `capture-problem: JTBD low-confidence under AFK; ticket captured with unconfirmed anchoring; elicitation queued — do not build dependent RFC/story/fix work until anchoring is confirmed`. The ticket IS created (P401 never-discard + JTBD-006 save-and-continue); ADR-074's "don't build on unconfirmed substance" is preserved by the existing downstream oversight gates (ADR-068/P288 new-persona/JTBD ratification; ADR-090 story-oversight; ADR-060 I13 propose-fix RFC-trace guard), NOT by refusing the capture.
|
|
101
112
|
|
|
102
|
-
**Resolve `persona_value`** (a scalar persona value — validated against the **adopter's persona corpus**: the directory names under `docs/jtbd/*/`, falling back to the home-repo set `{developer, tech-lead, plugin-developer, plugin-user}` only when no `docs/jtbd/` directories exist; P383, never hardcode the home-repo enum — P151/P317 adopter-portability) via the following derive-
|
|
113
|
+
**Resolve `persona_value`** (a scalar persona value — validated against the **adopter's persona corpus**: the directory names under `docs/jtbd/*/`, falling back to the home-repo set `{developer, tech-lead, plugin-developer, plugin-user}` only when no `docs/jtbd/` directories exist; P383, never hardcode the home-repo enum — P151/P317 adopter-portability) via the following derive → interview-on-low-confidence → classify dispatch:
|
|
103
114
|
|
|
104
115
|
1. **If `--persona=<value>` was set in Step 1**: validate `<value>` against the persona corpus (the `docs/jtbd/*/` directory names, or the home-repo fallback set when no jtbd dirs exist — e.g. `ls -d docs/jtbd/*/ | xargs -n1 basename`); halt-with-directive if invalid; otherwise assign and proceed silently. Caller pre-resolved.
|
|
105
116
|
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.
|
|
106
|
-
3. **Else (no flag + (no cited JTBDs OR cited-JTBD persona-disagreement) —
|
|
107
|
-
|
|
117
|
+
3. **Else (no flag + (no cited JTBDs OR cited-JTBD persona-disagreement) — low-confidence / ambiguity path)**: do **NOT** shoehorn a best-fit persona and do **NOT** propose candidate persona IDs for ratification. **Interview** the human via `AskUserQuestion` (share the dispatch with the JTBD-trace interview above — one elicitation covers *who* + *what job*) with substantive, non-leading questions about *who* actually hits this problem. From the elicited answer the **agent classifies**:
|
|
118
|
+
- **Elicited persona matches an existing persona** (a `docs/jtbd/*/` directory) → assign `persona_value` to it and proceed (autonomous mapping — ADR-068 boundary is creation-only).
|
|
119
|
+
- **No existing persona fits** → a new persona is warranted → route the **creation** to the ADR-068/P288 human-ratify surface (`/wr-jtbd:update-guide` then `/wr-jtbd:confirm-jobs-and-personas`); on ratification assign `persona_value` to the new persona and proceed.
|
|
120
|
+
- **The problem is never discarded** over persona uncertainty. Scope-rejection remains external-report-only (`/wr-itil:manage-problem` ingestion).
|
|
121
|
+
4. **AFK (the `--no-prompt` branch) — preserve the finding, queue the elicitation**: if `--no-prompt` was set in Step 1 AND control reaches step 3 → SKIP the `AskUserQuestion`, do **NOT** shoehorn a best-fit persona, and do **NOT** auto-create a new persona. Set `persona_value` to the greppable unconfirmed-anchoring sentinel `(unconfirmed — elicitation queued)`, capture the ticket, and queue the elicitation interview for the next interactive session. Emit stderr advisory `capture-problem: persona low-confidence under AFK; ticket captured with unconfirmed anchoring; elicitation queued — do not build dependent RFC/story/fix work until anchoring is confirmed`. The ticket IS created (P401 never-discard); ADR-074 preserved via the existing downstream oversight gates, not by refusing the capture.
|
|
108
122
|
|
|
109
123
|
**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).
|
|
110
124
|
|
|
111
|
-
**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
|
|
125
|
+
**ADR-044 authority taxonomy**: silent-framework (category 4) on the derive-success paths (flag pre-resolution, lexical detection, cited-JTBD agreement) AND on the agent's existing-vs-new classification of the elicited who/why (mechanical framework resolution — the agent maps the elicited job to an existing artefact without a consent gate); **direction-setting (category 1)** on the low-confidence **interview** (requirements elicitation of the real persona/job) and on the human ratification of the **creation** of a new persona/JTBD (per ADR-068/P288) — NOT a taste preference between equally-valid options, and NOT ratification of a guessed ID (P401).
|
|
112
126
|
|
|
113
127
|
### 2. Minimal-grep duplicate check (3-keyword title-only) + hang-off-check subagent dispatch (P346 Phase 3 amendment, 2026-05-31)
|
|
114
128
|
|
|
@@ -372,10 +386,10 @@ The trailing pointer is **not optional** — it is the user-visible signal for u
|
|
|
372
386
|
|---------|----------------|-----------------|
|
|
373
387
|
| Duplicate-check | Wide-net grep + AskUserQuestion branch on matches | 3-keyword title-only grep, list-only (no branch) |
|
|
374
388
|
| Multi-concern split | Step 4b AskUserQuestion | Out of scope (one ticket per invocation) |
|
|
375
|
-
| Skeleton-fill | Full-intake; AskUserQuestion for missing fields | Deferred-placeholder pattern;
|
|
389
|
+
| Skeleton-fill | Full-intake; AskUserQuestion for missing fields | Deferred-placeholder pattern; low-confidence persona/JTBD → interview-to-elicit-who/why (never propose an ID; never discard the problem) |
|
|
376
390
|
| 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 |
|
|
377
|
-
| JTBD-trace + persona | Step 4-equivalent ingestion path | Step 1.5b
|
|
378
|
-
| AskUserQuestion authority | Multiple branches (deviation-approval / direction-setting / taste / mechanical) |
|
|
391
|
+
| JTBD-trace + persona | Step 4-equivalent ingestion path | Step 1.5b derive → interview → classify dispatch (ADR-060 Amendment 2026-06-02 as corrected by P401) — flag pre-resolution (`--jtbd=` / `--persona=`) silent-proceeds; lexical detection of JTBD-NNN citations silent-proceeds; cited-JTBD persona derivation silent-proceeds; low-confidence → INTERVIEW to elicit who/why (not propose an ID) → agent classifies existing:map+proceed / no-fit:human-ratifies-CREATION (ADR-068/P288); problem NEVER discarded over anchoring; AFK `--no-prompt` → capture with unconfirmed-anchoring sentinel + queue elicitation |
|
|
392
|
+
| AskUserQuestion authority | Multiple branches (deviation-approval / direction-setting / taste / mechanical) | Direction-setting (ADR-044 category 1) on the low-confidence interview + on the new-artefact creation-ratify; silent-framework (category 4) on derive-success paths AND on the agent's existing-vs-new classification of the elicited who/why |
|
|
379
393
|
| README refresh | P094 inline (regenerate + stage in same commit) | P094 inline (regenerate + stage in same commit) — P199 Option 2 amendment 2026-06-05; previously deferred to next `/wr-itil:review-problems` |
|
|
380
394
|
| Status transitions | Step 7 owns Open → Known Error → Verifying → Closed | Out of scope (creation only) |
|
|
381
395
|
| Commit grain | One commit per intake (or per split-concern set) | One commit per capture |
|
|
@@ -404,7 +418,8 @@ The two skills share the `/tmp/manage-problem-grep-${SESSION_ID}` create-gate ma
|
|
|
404
418
|
- **P185** — `/wr-itil:capture-problem` historical: asked a classification question (type) it could answer itself; the Step 1.5 derive-first refactor (lexical-signal classifier + stderr advisory) shipped the fix in 2026-05-15. P287 then retired the entire surface in 2026-06-02 as the classification axis itself was redundant with RFC/Story persona-anchoring.
|
|
405
419
|
- **ADR-049** — bin/ on PATH; capture-problem reuses the existing `wr-itil-reconcile-readme` shim.
|
|
406
420
|
- **ADR-052** — behavioural-tests-default for skill testing; SKILL.md I2 surface coverage gap is named, not silent (P176 + ADR-052 § Surface 2).
|
|
407
|
-
- **ADR-060** (`docs/decisions/060-...accepted.md`) — body currently encodes the type-tag schema (Decision Outcome item 1, I2 type-uniformity, I12 hard-block, Phase-4 persona+jtbd machinery keyed on `type:user-business`). P287 retires the SKILL implementation of
|
|
421
|
+
- **ADR-060** (`docs/decisions/060-...accepted.md`) — body currently encodes the type-tag schema (Decision Outcome item 1, I2 type-uniformity, I12 hard-block, Phase-4 persona+jtbd machinery keyed on `type:user-business`) AND the 2026-06-02 amendment's REJECT=discard clause. P287 retires the SKILL implementation of the type clauses unilaterally per twice-confirmed user direction; **P401 (2026-06-29/2026-07-02) reverses the REJECT=discard clause to interview-on-low-confidence + never-discard + ratify-creation-only** (see the Step 1.5b supersession note). Both ADR body amendment substances (the P287 type rework; the P401 I12 reshape) plus the **ADR-073 additive symmetric covered→agent / uncovered→human-ratifies-new-persona/JTBD escalation note** are queued for user re-confirmation per ADR-074/P357. Until they land, ADR-060/ADR-073 bodies and SKILL implementation are intentionally inconsistent — the same governed SKILL-leads-ADR-follows trade-off the user accepted for P287.
|
|
422
|
+
- **P401** (`docs/problems/open/401-capture-persona-jtbd-shoehorns-or-discards-instead-of-interviewing.md`) — the driver: the old propose-candidate-IDs + REJECT=discard flow shoehorned problems into the nearest persona/JTBD and discarded legitimate problems over anchoring uncertainty. This SKILL amendment lands the corrected interview-based shape; the ADR-060/ADR-073 body amendments + the `/wr-itil:work-problems` AFK caller-side wiring (route the queued elicitation into `outstanding_questions`) remain tracked on P401's Investigation Tasks.
|
|
408
423
|
- **JTBD-301** (`docs/jtbd/plugin-user/JTBD-301-...md`) — plugin-user no-pre-classification persona constraint; the Step 1.5b maintainer-side scope guard preserves the firewall on the JTBD-trace + persona axis. The type-axis firewall is moot (axis retired).
|
|
409
424
|
- `packages/itil/skills/manage-problem/SKILL.md` — heavyweight intake counterpart.
|
|
410
425
|
- `packages/itil/skills/review-problems/SKILL.md` — re-rates the deferred placeholders + refreshes README.md.
|
|
@@ -11,17 +11,23 @@
|
|
|
11
11
|
# silent-resolve jtbd_trace_value to the matched IDs.
|
|
12
12
|
# - --jtbd=JTBD-NNN[,...] flag pre-resolves jtbd_trace_value silently.
|
|
13
13
|
# - --persona=<value> flag pre-resolves persona_value silently.
|
|
14
|
-
# -
|
|
15
|
-
# agreement) →
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
14
|
+
# - Low-confidence (no flag + no lexical detection + no cited-JTBD
|
|
15
|
+
# agreement) → P401 CORRECTED shape (user direction 2026-06-29,
|
|
16
|
+
# sharpened 2026-07-02): INTERVIEW the human to elicit the real
|
|
17
|
+
# who/why (NOT propose an ID) → agent classifies existing-vs-new →
|
|
18
|
+
# existing:map+proceed autonomously / no-fit:human-ratifies-CREATION
|
|
19
|
+
# of a new persona/JTBD (ADR-068/P288). A real problem is NEVER
|
|
20
|
+
# discarded over anchoring uncertainty. Scope-rejection (elicited
|
|
21
|
+
# who/why out of scope) is EXTERNAL-report-only, at manage-problem
|
|
22
|
+
# ingestion — never here.
|
|
23
|
+
# - --no-prompt + low-confidence → preserve the finding: CREATE the
|
|
24
|
+
# ticket with the `(unconfirmed — elicitation queued)` anchoring
|
|
25
|
+
# sentinel + queue the elicitation (P401 never-discard + JTBD-006
|
|
26
|
+
# save-and-continue). It no longer halt-refuses.
|
|
21
27
|
# - Skeleton template carries **JTBD**: and **Persona**: body fields.
|
|
22
28
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
29
|
+
# afk_low_confidence_action predicate (P401 2026-06-29) encodes the AFK
|
|
30
|
+
# create-with-unconfirmed-sentinel branch. The historical
|
|
25
31
|
# i12_should_block predicate is preserved as a regression guard
|
|
26
32
|
# (never returns 0) against re-introduction of the type-keyed hard-block.
|
|
27
33
|
#
|
|
@@ -56,51 +62,67 @@ i12_should_block() {
|
|
|
56
62
|
return 1
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
+
# P401 (2026-06-29 / 2026-07-02) — AFK low-confidence action. The
|
|
66
|
+
# corrected shape PRESERVES THE FINDING: under --no-prompt + low-
|
|
67
|
+
# confidence, capture the ticket with the unconfirmed-anchoring sentinel
|
|
68
|
+
# and queue the elicitation. It NO LONGER halt-refuses (that discarded
|
|
69
|
+
# legitimate problems over anchoring uncertainty). Returns the action.
|
|
70
|
+
# Inputs:
|
|
65
71
|
# $1: no_prompt_flag ("1" if --no-prompt set, "" otherwise)
|
|
66
72
|
# $2: derivation_resolved ("1" if persona+JTBD resolved by any of
|
|
67
73
|
# flag/lexical/cited-JTBD path; "" otherwise)
|
|
68
|
-
|
|
74
|
+
AFK_SENTINEL="(unconfirmed — elicitation queued)"
|
|
75
|
+
afk_low_confidence_action() {
|
|
69
76
|
local no_prompt="$1"
|
|
70
77
|
local derivation_resolved="$2"
|
|
71
78
|
if [ "$no_prompt" = "1" ] && [ -z "$derivation_resolved" ]; then
|
|
72
|
-
|
|
79
|
+
echo "CREATE_UNCONFIRMED_QUEUE_ELICITATION"
|
|
80
|
+
else
|
|
81
|
+
echo "PROCEED" # derivation succeeded, or interactive interview fires
|
|
73
82
|
fi
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
# "
|
|
80
|
-
#
|
|
81
|
-
# "
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
local
|
|
86
|
-
case "$
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*) echo "UNKNOWN:$response" ;;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# P401 — reference impl for the corrected low-confidence resolution of a
|
|
86
|
+
# MAINTAINER-INTERNAL capture. The agent interviews to elicit who/why,
|
|
87
|
+
# then classifies the elicited fit. Returns the resolution action:
|
|
88
|
+
# "MAP_EXISTING" elicited job/persona matches an existing artefact
|
|
89
|
+
# → map autonomously (ADR-068 boundary is creation-only)
|
|
90
|
+
# "RATIFY_CREATE_NEW" no existing fit → human ratifies the CREATION of a
|
|
91
|
+
# new persona/JTBD (ADR-068/P288), then map
|
|
92
|
+
# The problem is NEVER discarded over anchoring uncertainty.
|
|
93
|
+
resolve_low_confidence_internal() {
|
|
94
|
+
local elicited_fit="$1"
|
|
95
|
+
case "$elicited_fit" in
|
|
96
|
+
existing) echo "MAP_EXISTING" ;;
|
|
97
|
+
none) echo "RATIFY_CREATE_NEW" ;;
|
|
98
|
+
*) echo "INTERVIEW" ;; # elicit who/why first
|
|
91
99
|
esac
|
|
92
100
|
}
|
|
93
101
|
|
|
94
|
-
#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
case "$
|
|
98
|
-
|
|
99
|
-
ACCEPT:*|CORRECT:*) return 0 ;; # ticket created
|
|
102
|
+
# P401 — every maintainer-internal low-confidence resolution yields a
|
|
103
|
+
# ticket (never discard). CREATE_UNCONFIRMED is the AFK sentinel path.
|
|
104
|
+
internal_resolution_creates_ticket() {
|
|
105
|
+
case "$1" in
|
|
106
|
+
MAP_EXISTING|RATIFY_CREATE_NEW|CREATE_UNCONFIRMED_QUEUE_ELICITATION) return 0 ;;
|
|
100
107
|
*) return 1 ;;
|
|
101
108
|
esac
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
# P401 — scope-rejection is EXTERNAL-report-only (at manage-problem
|
|
112
|
+
# ingestion, NOT capture-problem). Through the elicitation interview the
|
|
113
|
+
# maintainer may decide the elicited who/why is out of scope to support
|
|
114
|
+
# and decline. This is the ONLY path that yields no ticket, and only for
|
|
115
|
+
# external reports. A maintainer-internal capture ALWAYS anchors.
|
|
116
|
+
# Inputs: $1 origin ("internal"|"external"); $2 in_scope ("yes"|"no").
|
|
117
|
+
external_scope_disposition() {
|
|
118
|
+
local origin="$1" in_scope="$2"
|
|
119
|
+
if [ "$origin" = "external" ] && [ "$in_scope" = "no" ]; then
|
|
120
|
+
echo "DECLINE_SCOPE" # deliberate product-scope decline; no ticket
|
|
121
|
+
else
|
|
122
|
+
echo "ANCHOR" # internal always anchors; external in-scope anchors
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
104
126
|
# Reference implementation of --jtbd= flag parser. Accepts CSV; returns
|
|
105
127
|
# space-separated IDs (canonicalised) OR empty if the flag wasn't set.
|
|
106
128
|
parse_jtbd_flag() {
|
|
@@ -217,77 +239,96 @@ parse_no_prompt_flag() {
|
|
|
217
239
|
}
|
|
218
240
|
|
|
219
241
|
# ---------------------------------------------------------------------------
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
242
|
+
# P401 (2026-06-29 / 2026-07-02) — corrected low-confidence contract.
|
|
243
|
+
# Low-confidence INTERVIEWS to elicit who/why (never proposes an ID); the
|
|
244
|
+
# agent classifies existing-vs-new; existing→map+proceed, no-fit→human-
|
|
245
|
+
# ratifies-CREATION; a real problem is NEVER discarded over anchoring;
|
|
246
|
+
# scope-rejection is external-report-only; AFK captures with the
|
|
247
|
+
# unconfirmed-anchoring sentinel + queues the elicitation.
|
|
225
248
|
# ---------------------------------------------------------------------------
|
|
226
249
|
|
|
227
|
-
@test "
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
|
|
250
|
+
@test "P401 low-confidence interviews (elicits who/why) rather than proposing an ID" {
|
|
251
|
+
# Before classification, the resolution action is INTERVIEW — the agent
|
|
252
|
+
# elicits the real who/why, NOT a candidate JTBD-NNN to ratify.
|
|
253
|
+
result=$(resolve_low_confidence_internal "unknown")
|
|
254
|
+
[ "$result" = "INTERVIEW" ]
|
|
231
255
|
}
|
|
232
256
|
|
|
233
|
-
@test "
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
|
|
257
|
+
@test "P401 elicited who/why matching an existing artefact maps autonomously" {
|
|
258
|
+
# Classification: elicited job matches an existing JTBD/persona → map and
|
|
259
|
+
# proceed with no further human ratification (ADR-068 boundary is
|
|
260
|
+
# creation-only). Yields a ticket.
|
|
261
|
+
result=$(resolve_low_confidence_internal "existing")
|
|
262
|
+
[ "$result" = "MAP_EXISTING" ]
|
|
263
|
+
internal_resolution_creates_ticket "$result"
|
|
237
264
|
}
|
|
238
265
|
|
|
239
|
-
@test "
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
|
|
266
|
+
@test "P401 elicited who/why with no existing fit routes to human-ratified new-artefact creation" {
|
|
267
|
+
# No existing fit → a new persona/JTBD is warranted → human ratifies the
|
|
268
|
+
# CREATION (ADR-068/P288), then map. Still yields a ticket (never discard).
|
|
269
|
+
result=$(resolve_low_confidence_internal "none")
|
|
270
|
+
[ "$result" = "RATIFY_CREATE_NEW" ]
|
|
271
|
+
internal_resolution_creates_ticket "$result"
|
|
243
272
|
}
|
|
244
273
|
|
|
245
|
-
@test "
|
|
246
|
-
|
|
274
|
+
@test "P401 a maintainer-internal problem is NEVER discarded over anchoring uncertainty" {
|
|
275
|
+
# Every internal low-confidence resolution — map-existing, ratify-new, or
|
|
276
|
+
# the AFK create-with-unconfirmed-sentinel — yields a ticket.
|
|
277
|
+
internal_resolution_creates_ticket "MAP_EXISTING"
|
|
278
|
+
internal_resolution_creates_ticket "RATIFY_CREATE_NEW"
|
|
279
|
+
internal_resolution_creates_ticket "CREATE_UNCONFIRMED_QUEUE_ELICITATION"
|
|
247
280
|
}
|
|
248
281
|
|
|
249
|
-
@test "
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
282
|
+
@test "P401 AFK low-confidence creates ticket with unconfirmed sentinel + queues elicitation (not halt-refuse)" {
|
|
283
|
+
# --no-prompt + derivation-failure → preserve the finding.
|
|
284
|
+
result=$(afk_low_confidence_action "1" "")
|
|
285
|
+
[ "$result" = "CREATE_UNCONFIRMED_QUEUE_ELICITATION" ]
|
|
286
|
+
# The AFK sentinel is a real (non-empty) anchoring value written to the
|
|
287
|
+
# ticket — the finding is preserved, not discarded.
|
|
288
|
+
[ -n "$AFK_SENTINEL" ]
|
|
289
|
+
internal_resolution_creates_ticket "$result"
|
|
253
290
|
}
|
|
254
291
|
|
|
255
|
-
@test "
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
292
|
+
@test "P401 AFK proceeds normally when derivation succeeded (flags pre-resolved)" {
|
|
293
|
+
# AFK orchestrator pattern: pass --no-prompt PLUS --persona + --jtbd to
|
|
294
|
+
# skip the sentinel path entirely.
|
|
295
|
+
no_prompt=$(parse_no_prompt_flag "--persona=developer" "--jtbd=JTBD-006" "--no-prompt" "fix work-problems iter halt")
|
|
296
|
+
[ "$no_prompt" = "1" ]
|
|
297
|
+
persona=$(validate_persona "developer")
|
|
298
|
+
[ "$persona" = "developer" ]
|
|
299
|
+
jtbd=$(parse_jtbd_flag "--jtbd=JTBD-006")
|
|
300
|
+
[ "$jtbd" = "JTBD-006" ]
|
|
301
|
+
result=$(afk_low_confidence_action "$no_prompt" "1")
|
|
302
|
+
[ "$result" = "PROCEED" ]
|
|
259
303
|
}
|
|
260
304
|
|
|
261
|
-
@test "
|
|
262
|
-
|
|
263
|
-
[ "$
|
|
264
|
-
ratification_creates_ticket "$classified"
|
|
305
|
+
@test "P401 interactive low-confidence proceeds into the interview (no --no-prompt)" {
|
|
306
|
+
result=$(afk_low_confidence_action "" "")
|
|
307
|
+
[ "$result" = "PROCEED" ]
|
|
265
308
|
}
|
|
266
309
|
|
|
267
|
-
@test "
|
|
310
|
+
@test "P401 scope-rejection is external-report-only; maintainer-internal always anchors" {
|
|
311
|
+
# External report whose elicited who/why we do NOT want to support →
|
|
312
|
+
# deliberate scope decline (no ticket) — the ONLY no-ticket path.
|
|
313
|
+
[ "$(external_scope_disposition external no)" = "DECLINE_SCOPE" ]
|
|
314
|
+
# External report in scope → anchors.
|
|
315
|
+
[ "$(external_scope_disposition external yes)" = "ANCHOR" ]
|
|
316
|
+
# Maintainer-internal ALWAYS anchors, regardless of scope signal —
|
|
317
|
+
# internal captures are never scope-rejected.
|
|
318
|
+
[ "$(external_scope_disposition internal no)" = "ANCHOR" ]
|
|
319
|
+
[ "$(external_scope_disposition internal yes)" = "ANCHOR" ]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@test "P401 parse_no_prompt_flag detects --no-prompt anywhere in args" {
|
|
268
323
|
result=$(parse_no_prompt_flag "--persona=developer" "--no-prompt" "description text")
|
|
269
324
|
[ "$result" = "1" ]
|
|
270
325
|
}
|
|
271
326
|
|
|
272
|
-
@test "
|
|
327
|
+
@test "P401 parse_no_prompt_flag empty when --no-prompt absent" {
|
|
273
328
|
result=$(parse_no_prompt_flag "--persona=developer" "description text")
|
|
274
329
|
[ -z "$result" ]
|
|
275
330
|
}
|
|
276
331
|
|
|
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
|
-
|
|
291
332
|
@test "SKILL.md: Step 1.5b section header exists for JTBD-trace + persona dispatch" {
|
|
292
333
|
grep -qE '^### 1\.5b JTBD-trace \+ persona dispatch' "$SKILL_FILE"
|
|
293
334
|
}
|
|
@@ -328,18 +369,30 @@ parse_no_prompt_flag() {
|
|
|
328
369
|
grep -qE '\| `--no-prompt`' "$SKILL_FILE"
|
|
329
370
|
}
|
|
330
371
|
|
|
331
|
-
@test "SKILL.md: Step 1.5b names
|
|
332
|
-
grep -
|
|
372
|
+
@test "SKILL.md: Step 1.5b names P401 never-discard-over-anchoring rule" {
|
|
373
|
+
grep -qiE 'never discarded over anchoring uncertainty' "$SKILL_FILE"
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
@test "SKILL.md: Step 1.5b names interview-to-elicit-who/why (not propose an ID)" {
|
|
377
|
+
grep -qiE '[Ii]nterview.*(who|why|elicit)|elicit the real who/why' "$SKILL_FILE"
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@test "SKILL.md: Step 1.5b names scope-rejection as external-report-only" {
|
|
381
|
+
grep -qiE 'external-report-only' "$SKILL_FILE"
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
@test "SKILL.md: Step 1.5b names AFK unconfirmed-anchoring sentinel + queued elicitation" {
|
|
385
|
+
grep -qiE 'unconfirmed — elicitation queued|unconfirmed-anchoring sentinel' "$SKILL_FILE"
|
|
333
386
|
}
|
|
334
387
|
|
|
335
|
-
@test "SKILL.md: Step 1.5b
|
|
336
|
-
grep -qE '
|
|
388
|
+
@test "SKILL.md: Step 1.5b routes new persona/JTBD creation to ADR-068/P288 human ratify" {
|
|
389
|
+
grep -qE 'ratif.*(creation|CREATION).*(ADR-068|P288)|(ADR-068|P288).*creation' "$SKILL_FILE"
|
|
337
390
|
}
|
|
338
391
|
|
|
339
|
-
@test "SKILL.md: allowed-tools includes AskUserQuestion (for
|
|
392
|
+
@test "SKILL.md: allowed-tools includes AskUserQuestion (for the low-confidence interview)" {
|
|
340
393
|
grep -qE '^allowed-tools:.*AskUserQuestion' "$SKILL_FILE"
|
|
341
394
|
}
|
|
342
395
|
|
|
343
|
-
@test "SKILL.md: ADR-044 authority taxonomy names direction-setting (category 1) for
|
|
396
|
+
@test "SKILL.md: ADR-044 authority taxonomy names direction-setting (category 1) for interview + creation-ratify" {
|
|
344
397
|
grep -qE 'direction-setting.*category 1|category 1.*direction-setting' "$SKILL_FILE"
|
|
345
398
|
}
|
|
@@ -18,7 +18,7 @@ This skill is the P071 phased-landing split of `/wr-itil:manage-incident <I> res
|
|
|
18
18
|
|
|
19
19
|
- `<I###>` — the incident ID (e.g. `I007` or bare `007`). Resolves to `docs/incidents/<I###>-*.mitigating.md` (primary path) or `docs/incidents/<I###>-*.restored.md` (idempotent re-invocation).
|
|
20
20
|
|
|
21
|
-
If `$ARGUMENTS` is empty or malformed,
|
|
21
|
+
If `$ARGUMENTS` is empty or malformed, fail-fast with a usage message and exit (per ADR-044 Framework-Mediated Surface; matches the `mitigate-incident` Surface 1 / `transition-problem` / `work-problem` precedent). Argument malformation is a typo-class signal, not a decision — the slash command is the input contract; re-typing is faster than a multi-turn `AskUserQuestion` dialogue, and the user-memory direction `feedback_act_on_obvious_decisions.md` pins this. This scopes to the `<I###>` ID only — the verification signal (Pre-flight / Step 3) and the problem-handoff decision (Step 5) remain genuine interactive user-authority surfaces. The exact usage block is in Step 1.
|
|
22
22
|
|
|
23
23
|
## Pre-flight (ADR-011)
|
|
24
24
|
|
|
@@ -31,12 +31,21 @@ If either pre-flight fails, block the transition and ask via `AskUserQuestion` w
|
|
|
31
31
|
|
|
32
32
|
## Steps
|
|
33
33
|
|
|
34
|
-
### 1. Parse arguments
|
|
34
|
+
### 1. Parse arguments (fail-fast on typos — ADR-044 Surface 1)
|
|
35
35
|
|
|
36
36
|
Extract `<I###>` from `$ARGUMENTS`. Normalise:
|
|
37
37
|
|
|
38
38
|
- Accept `I007`, `i007`, `007`, `7` → canonicalise to `I007` (uppercase I + zero-padded 3 digits).
|
|
39
|
-
- If missing,
|
|
39
|
+
- If missing, malformed, or unrecognisable, emit the usage block below and exit. **Do not** fire `AskUserQuestion` for argument backfill — argument shape is a typo-class signal, not a decision. The framework-mediated answer per ADR-044 is fail-fast + exit; the user re-types in 1 second. Matches the `mitigate-incident` Step 1 + `transition-problem` Step 1 + `work-problem` singular precedent for consistency across the suite (JTBD-101 — clear patterns).
|
|
40
|
+
|
|
41
|
+
**Usage block** (emitted on any malformed-argument case; copy verbatim so adopters get a consistent shape):
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
Usage: /wr-itil:restore-incident <I###>
|
|
45
|
+
<I###> — incident ID (e.g. I007 or bare 007); must resolve to docs/incidents/<I###>-*.{mitigating,restored}.md
|
|
46
|
+
|
|
47
|
+
Run /wr-itil:list-incidents to see active incidents if you don't know the ID.
|
|
48
|
+
```
|
|
40
49
|
|
|
41
50
|
### 2. Locate the incident file
|
|
42
51
|
|
|
@@ -181,6 +190,7 @@ If the user wants any of the above, the skill reports the appropriate sibling an
|
|
|
181
190
|
- **ADR-011** (`docs/decisions/011-manage-incident-skill.proposed.md`) — incident lifecycle file-suffix conventions (`.investigating.md` / `.mitigating.md` / `.restored.md` / `.closed.md`) + Decision Outcome point 4 (direct Skill-tool invocation of `/wr-itil:manage-problem` for problem handoff).
|
|
182
191
|
- **ADR-013** Rule 1 — structured user interaction (verification-signal and handoff prompts use AskUserQuestion; deprecation notice uses systemMessage).
|
|
183
192
|
- **ADR-013** Rule 6 — policy-within-appetite non-interactive actions (release drain).
|
|
193
|
+
- **ADR-044** (`docs/decisions/044-decision-delegation-contract.proposed.md`) — Framework-Mediated Surface: `<I###>` argument-shape backfill is a typo-class signal resolved by fail-fast + exit (Arguments + Step 1), NOT a user decision. The genuine user-authority asks remain interactive per the 6-class taxonomy: verification-signal + mitigation-attempts pre-flight (cat-2 deviation-approval), problem-handoff + no-problem justification (cat-1 direction-setting). P136 Phase-remediation surface (mirrors the shipped `mitigate-incident` Surface 1).
|
|
184
194
|
- **ADR-014** — governance skills commit their own work.
|
|
185
195
|
- **ADR-015** — release scorer delegation pattern.
|
|
186
196
|
- **ADR-020** — auto-release when changesets are queued.
|
|
@@ -140,3 +140,31 @@ setup() {
|
|
|
140
140
|
run grep -inE "If arguments start with \"(list|mitigate|restore|close|link)\"|If arguments contain \"(list|mitigate|restore|close|link)\"" "$SKILL_FILE"
|
|
141
141
|
[ "$status" -ne 0 ]
|
|
142
142
|
}
|
|
143
|
+
|
|
144
|
+
# --- P136 ADR-044 alignment: argument-backfill fail-fast ---
|
|
145
|
+
# tdd-review: structural-permitted — P081 bridge per the P136 Fix Strategy.
|
|
146
|
+
# Pins the lazy-AskUserQuestion removal so a future reword cannot silently
|
|
147
|
+
# reintroduce the typo-class <I###> argument-backfill ask (the exact P136
|
|
148
|
+
# regression class). Behavioural retrofit owned by P081 Phase 2.
|
|
149
|
+
|
|
150
|
+
@test "SKILL.md fails fast on missing/malformed <I###> — no AskUserQuestion for ID backfill (P136 / ADR-044 Surface 1)" {
|
|
151
|
+
# ADR-044 framework-mediated: argument shape is a typo-class signal resolved
|
|
152
|
+
# by fail-fast + usage block, NOT a user decision. Mirrors mitigate-incident
|
|
153
|
+
# Surface 1. Regression guard: the old "ask via AskUserQuestion for the
|
|
154
|
+
# incident ID" backfill prose must NOT return.
|
|
155
|
+
run grep -inE "ask via .AskUserQuestion. for the incident ID" "$SKILL_FILE"
|
|
156
|
+
[ "$status" -ne 0 ]
|
|
157
|
+
run grep -inE "fail-fast|emit the usage block below and exit" "$SKILL_FILE"
|
|
158
|
+
[ "$status" -eq 0 ]
|
|
159
|
+
run grep -inE "fire .AskUserQuestion. for argument backfill" "$SKILL_FILE"
|
|
160
|
+
[ "$status" -eq 0 ]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@test "SKILL.md keeps the genuine user-authority asks after the fail-fast scope-down (P136 / ADR-044)" {
|
|
164
|
+
# The fail-fast scopes to the <I###> ID ONLY. The cat-2 evidence pre-flight
|
|
165
|
+
# and cat-1 problem-handoff asks remain interactive — do not over-remove.
|
|
166
|
+
run grep -inE "ADR-044" "$SKILL_FILE"
|
|
167
|
+
[ "$status" -eq 0 ]
|
|
168
|
+
run grep -nE "^allowed-tools:.*AskUserQuestion" "$SKILL_FILE"
|
|
169
|
+
[ "$status" -eq 0 ]
|
|
170
|
+
}
|
|
@@ -86,7 +86,7 @@ After re-scoring, present three sections matching the README.md format (same ren
|
|
|
86
86
|
- `no — not observed` — fix released but no session-observable evidence yet. Default for newly-released tickets. Aging is preserved separately via the `Released` column — the Released column is the aging signal, `Likely verified?` is the evidence signal.
|
|
87
87
|
- `no — observed regression` — fix released and the bug recurred this session. Cite the recurrence inline (≤ 120 chars).
|
|
88
88
|
|
|
89
|
-
Any change to the canonical cell shape MUST update this rendering block, Step 5's README template, AND every co-located render site listed in the VQ-SORT-DIRECTION drift-tripwire above — drift re-opens P186.
|
|
89
|
+
Any change to the canonical cell shape MUST update this rendering block, Step 5's README template, AND every co-located render site listed in the VQ-SORT-DIRECTION drift-tripwire above — drift re-opens P186. Step 4 routes each cell value to a distinct bucket: `yes — observed: …` rows close **on evidence** (mechanical, no `AskUserQuestion` — framework-mediated per ADR-044); `no — not observed` rows route to the verification ask (genuine user-authority); `no — observed regression` rows must NOT be batch-closed (they may signal a botched fix and warrant a flip-back to `.known-error.md`).
|
|
90
90
|
|
|
91
91
|
```
|
|
92
92
|
| ID | Title | Released | Fix summary | Likely verified? |
|
|
@@ -111,15 +111,17 @@ Omit an empty section rather than rendering an empty header.
|
|
|
111
111
|
|
|
112
112
|
### 4. Verification prompt (Verification Pending → Closed)
|
|
113
113
|
|
|
114
|
-
Target the dual-tolerant glob `docs/problems/*.verifying.md docs/problems/verifying/*.md` (RFC-002 migration window) — do NOT scan `.known-error.md` bodies for a `## Fix Released` section (per ADR-022, Verification Pending is a first-class status, not a substring marker).
|
|
114
|
+
Target the dual-tolerant glob `docs/problems/*.verifying.md docs/problems/verifying/*.md` (RFC-002 migration window) — do NOT scan `.known-error.md` bodies for a `## Fix Released` section (per ADR-022, Verification Pending is a first-class status, not a substring marker). Bucket each verifying ticket by its Step-3 `Likely verified?` cell (the P186 evidence-first cell shape: `yes — observed: <evidence>` / `no — not observed` / `no — observed regression`) and route each bucket differently. **Do NOT fire one `AskUserQuestion` per ticket** — the evidence-backed subset closes on evidence; only the unobserved subset asks.
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
**Bucket 1 — `yes — observed: <evidence>` → close-on-evidence (framework-mediated, silent agent action per ADR-044 + P135).** These rows carry ADR-026-grounded cited evidence (a prior Step 4 user confirmation, an in-session test invocation + observable outcome, or a `run-retro` Step 4a close-on-evidence citation — see the Step 3 cell definition). The framework has resolved this decision: a `.verifying.md` ticket with specific cited evidence IS verified per ADR-022's evidence semantics, so close it mechanically WITHOUT `AskUserQuestion` — mirroring the shipped `run-retro` Step 4a step 5 close-on-evidence. A per-candidate ask here is lazy deferral (sub-contracting a framework-resolved decision back to the user) per the Step 2d Ask Hygiene Pass. For each such ticket: close the problem (`git mv` from `.verifying.md` to `.closed.md`, update Status to "Closed", re-stage per the P057 staging trap) and update the `Likely verified?` cell to `yes — observed: closed-on-evidence <YYYY-MM-DD> — <citation>`. Report each closure in the review output with a documented reversible recovery path: `Recovery: rerun /wr-itil:transition-problem <NNN> known-error to reopen` — closes are cheap and reversible; user disagreement surfaces via authentic-correction (ADR-044 category 6), not a pre-close consent gate.
|
|
117
117
|
|
|
118
|
-
-
|
|
119
|
-
- If the user confirms: close the problem (`git mv` from `.verifying.md` to `.closed.md`, update Status to "Closed", re-stage per the P057 staging trap). Update the `Likely verified?` cell
|
|
120
|
-
- If the user says no or is unsure: leave the ticket as Verification Pending. If the user reports recurrence, update the cell to `no — observed regression — <one-line citation>` and
|
|
118
|
+
**Bucket 2 — `no — not observed` → ask (genuine user-authority).** No session-observable evidence yet, so the user may hold out-of-band production knowledge the agent cannot observe — this is the genuine user-input surface. Use `AskUserQuestion` to ask whether the fix has been verified in production. The question MUST include a fix summary extracted from the `## Fix Released` section — inline the first sentence (or first bullet list) of that section in the question body or option description per the brief-before-ID discipline, so the user can answer without reading the full problem file. Do NOT ask with only the problem ID + title + version.
|
|
119
|
+
- If the user confirms: close the problem (`git mv` from `.verifying.md` to `.closed.md`, update Status to "Closed", re-stage per the P057 staging trap). Update the `Likely verified?` cell to `yes — observed: user confirmed <YYYY-MM-DD>`.
|
|
120
|
+
- If the user says no or is unsure: leave the ticket as Verification Pending. If the user reports recurrence, update the cell to `no — observed regression — <one-line citation>` and route it to Bucket 3.
|
|
121
121
|
|
|
122
|
-
**
|
|
122
|
+
**Bucket 3 — `no — observed regression` → flip-back, never batch-close (unchanged per P186).** The fix recurred; this is a botched fix, not a closure candidate. Do NOT close it. Flag for `.verifying.md` → `.known-error.md` flip-back via `/wr-itil:transition-problem <NNN> known-error` with the recurrence citation.
|
|
123
|
+
|
|
124
|
+
**AFK / non-interactive branch (ADR-013 Rule 6; ADR-044 framework-mediated-verification-close):** the Bucket 1 close-on-evidence fires in AFK too — evidence-backed closure is framework-mediated, not gated on `AskUserQuestion` availability (exactly parallel to `run-retro` Step 4a, which closes silently even in AFK-by-construction subprocesses); report each close + recovery path in the review output. Only Bucket 2 (`no — not observed`) defers when `AskUserQuestion` is unavailable — record those rows in the Verification Queue and skip the prompt; do NOT auto-close them (no evidence → genuine user call). Bucket 3 flip-backs proceed. The user sees the queued Bucket-2 rows on next interactive invocation. This supersedes the prior blanket "do NOT auto-close verifying tickets — only the user can make that call", which pre-dated the P186 evidence-first cell.
|
|
123
125
|
|
|
124
126
|
<!-- ADR-062-step-naming-reconciliation: this skill's current numbering has 7 steps; ADR-062 was authored against a stale view that called the inbound-discovery sub-step "Step 8.5" and the README renderer "Step 9e". Both names appear verbatim in headers below so ADR-062 § Confirmation criterion 1 ("Step 8.5") and § Confirmation criterion final bullet ("Step 9e") remain string-anchorable. Do NOT strip the "Step 8.5" / "Step 9e" substrings on rename. -->
|
|
125
127
|
|
|
@@ -205,3 +205,31 @@ setup() {
|
|
|
205
205
|
run grep -inE "Effort.*→.*transitive via|transitive via.*P[0-9]" "$SKILL_FILE"
|
|
206
206
|
[ "$status" -eq 0 ]
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
# --- P136 ADR-044 alignment: Step 4 close-on-evidence bucket routing ---
|
|
210
|
+
# tdd-review: structural-permitted — P081 bridge per the P136 Fix Strategy.
|
|
211
|
+
# Pins the Step 4 lazy-ask removal (close the evidence-backed subset
|
|
212
|
+
# mechanically; ask only the unobserved subset) so a future reword cannot
|
|
213
|
+
# silently revert to a per-ticket AskUserQuestion (the exact P136 regression
|
|
214
|
+
# class). Behavioural retrofit owned by P081 Phase 2.
|
|
215
|
+
|
|
216
|
+
@test "SKILL.md Step 4 closes the yes-observed subset on evidence without AskUserQuestion (P136 / ADR-044 + run-retro Step 4a)" {
|
|
217
|
+
run grep -inE "close-on-evidence" "$SKILL_FILE"
|
|
218
|
+
[ "$status" -eq 0 ]
|
|
219
|
+
run grep -inE "Do NOT fire one .AskUserQuestion. per ticket" "$SKILL_FILE"
|
|
220
|
+
[ "$status" -eq 0 ]
|
|
221
|
+
run grep -inE "ADR-044" "$SKILL_FILE"
|
|
222
|
+
[ "$status" -eq 0 ]
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@test "SKILL.md Step 4 keeps the three-way split — ask no-not-observed, flip-back regressions (P136)" {
|
|
226
|
+
# Bucket 2 (no — not observed) still asks; Bucket 3 (no — observed
|
|
227
|
+
# regression) still flips back and is never batch-closed. Do not collapse
|
|
228
|
+
# the three-way routing to a two-way one (JTBD-201 audit-trail invariant).
|
|
229
|
+
run grep -inE "no — not observed" "$SKILL_FILE"
|
|
230
|
+
[ "$status" -eq 0 ]
|
|
231
|
+
run grep -inE "no — observed regression" "$SKILL_FILE"
|
|
232
|
+
[ "$status" -eq 0 ]
|
|
233
|
+
run grep -inE "flip-back" "$SKILL_FILE"
|
|
234
|
+
[ "$status" -eq 0 ]
|
|
235
|
+
}
|
|
@@ -1142,6 +1142,17 @@ The skill should produce a final summary when the loop ends:
|
|
|
1142
1142
|
|---------|---------------------|--------|
|
|
1143
1143
|
| P016 (Multi-concern splitting) | user-answerable (verification) | Awaiting user verification |
|
|
1144
1144
|
|
|
1145
|
+
### Reported Upstream
|
|
1146
|
+
|
|
1147
|
+
<!-- @jtbd JTBD-006 (Progress the Backlog While I'm Away — the summary reports ACTUAL upstream filings so the AFK audit trail is honest, never a re-run-to-file checklist) -->
|
|
1148
|
+
|
|
1149
|
+
(Renders when ≥1 upstream-blocked ticket was auto-filed or queued this loop by the Step 4 `upstream-blocked` row's per-iter auto-invoke of `/wr-itil:report-upstream`. Reports ACTUAL filings and queued drafts — never a to-do list of "re-run to file". Omitted entirely when no ticket was upstream-reported this loop.)
|
|
1150
|
+
|
|
1151
|
+
| Ticket | Upstream action | Result |
|
|
1152
|
+
|--------|-----------------|--------|
|
|
1153
|
+
| P067 (adopter hook path bug) | Filed to windyroad/agent-plugins#142 | Sent (below appetite) |
|
|
1154
|
+
| P071 (security-classified regression) | Queued to `## Queued Upstream Report` | Above appetite — surfaced as outstanding question |
|
|
1155
|
+
|
|
1145
1156
|
### Outstanding Design Questions
|
|
1146
1157
|
|
|
1147
1158
|
(Emitted only when stop-condition #2 fires AND at least one skipped ticket has a `user-answerable (design/direction/pacing/scope)` skip-reason. Populated by Step 2.5 in non-interactive / AFK mode per ADR-013 Rule 6.)
|
|
@@ -1182,6 +1193,8 @@ ALL_DONE
|
|
|
1182
1193
|
|
|
1183
1194
|
When every skipped ticket is in the `upstream-blocked` category (stop-condition #3) or there are no skipped tickets (stop-condition #1), omit the Outstanding Design Questions section entirely rather than rendering an empty heading. The Session Cost section always renders when at least one iteration ran.
|
|
1184
1195
|
|
|
1196
|
+
**Upstream reports are auto-filed per-iter, never deferred to a wrap-time batch choice (P413).** The `### Reported Upstream` section reports ACTUAL filings and queued drafts produced by the Step 4 `upstream-blocked` row's per-iter auto-invoke of `/wr-itil:report-upstream` (below-appetite → sent during the loop; above-appetite → risk-reduced then sent-or-queued per P352). There is **no "batch-report upstream" mode** and no wrap-time reporting decision: the summary MUST NOT emit a "N upstream-blocked tickets are unreported — re-run `/wr-itil:work-problems` and choose batch-report upstream" nudge. That nudge is an agent-invented permission gate the framework did not authorise — the same class as P390 / P341 / P175 (agent-invented loop-control the framework already resolved per ADR-044), and it directly contradicts the ADR-024 2026-06-04 (P270) auto-fire contract the Step 4 row (lines 503 / 511) and decision table (line 1077) mandate. A below-appetite report just sends during the loop; an above-appetite report queues to `## Queued Upstream Report` and surfaces at Step 2.4 gate (a) as an `outstanding_questions` entry — never as a "re-run to report" instruction the user must action. If the loop ends with upstream-blocked tickets that were NOT reported, that is a bug (the auto-fire did not fire), not a batch the user should be asked to trigger.
|
|
1197
|
+
|
|
1185
1198
|
## Related
|
|
1186
1199
|
|
|
1187
1200
|
- **P341** (`docs/problems/open/341-work-problems-skill-must-surface-outstanding-questions-then-run-retro-before-emitting-all-done.md`) — driver for Step 2.4 Pre-`ALL_DONE` gate sequence (UNCONDITIONAL fire of outstanding-questions surface + session-level retro before `ALL_DONE` emit). 2026-05-31 user direction (verbatim in ticket Description): *"The work-problems skill MUST surface the outstanding questions at the end before emitting ALL_DONE. It MUST then run a retro. Only then should it emit ALL_DONE."* Closes the structural gap that allowed `ALL_DONE` to emit while direction-class observations remained queued AND without a session-level retro running. Behavioural second-source: `test/work-problems-p341-pre-all-done-gate.bats`. Composes with P086 (extends iter-level retro-on-exit to orchestrator-level), P126 (preserves `halt-paths-must-route-design-questions-through-Step-2.5b` principle), ADR-014 (retro commits its own work), ADR-044 (framework-resolution boundary for when to surface — now framework-resolved as unconditional pre-`ALL_DONE`).
|
|
@@ -114,13 +114,19 @@ setup() {
|
|
|
114
114
|
# Per-SKILL carve-out audit annotations
|
|
115
115
|
# ----------------------------------------------------------------------
|
|
116
116
|
|
|
117
|
-
@test "capture-problem SKILL.md carries the P352 carve-out audit (
|
|
117
|
+
@test "capture-problem SKILL.md carries the P352 carve-out audit (P401-corrected: conforms to queue-and-continue; ADR-074 preserved via downstream gating)" {
|
|
118
118
|
SKILL="${REPO_ROOT}/packages/itil/skills/capture-problem/SKILL.md"
|
|
119
119
|
[ -f "$SKILL" ]
|
|
120
120
|
run grep -nE "ADR-013 Rule 6 carve-out audit \(P352" "$SKILL"
|
|
121
121
|
[ "$status" -eq 0 ]
|
|
122
|
-
#
|
|
123
|
-
|
|
122
|
+
# Post-P401 (2026-06-29/2026-07-02) the AFK low-confidence path no longer
|
|
123
|
+
# HALTs — it CONFORMS to the queue-and-continue default: capture the
|
|
124
|
+
# ticket with the unconfirmed-anchoring sentinel + queue the elicitation.
|
|
125
|
+
run grep -niE "conform.*queue-and-continue|unconfirmed — elicitation queued" "$SKILL"
|
|
126
|
+
[ "$status" -eq 0 ]
|
|
127
|
+
# ADR-074 is still named as the honoured constraint (preserved by
|
|
128
|
+
# downstream oversight gating, not by a no-ticket halt).
|
|
129
|
+
run grep -nE "ADR-074" "$SKILL"
|
|
124
130
|
[ "$status" -eq 0 ]
|
|
125
131
|
}
|
|
126
132
|
|