@windyroad/itil 0.47.16 → 0.48.0-preview.654
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/package.json
CHANGED
|
@@ -802,6 +802,32 @@ The refresh uses the same rendering rules as Step 9e (dual-tolerant glob per RFC
|
|
|
802
802
|
|
|
803
803
|
**Fast-path interaction**: the Step 9 fast-path freshness check (`git log -1 --format=%H -- docs/problems/README.md` followed by `git log --oneline "${readme_commit}..HEAD" -- 'docs/problems/*.md'`) remains the authoritative staleness test. When this refresh fires on every transition, that check should return empty on any subsequent invocation — the cache stays fresh by construction. If the check still reports "stale", something skipped the refresh (bug) and the slow-path is the correct recovery.
|
|
804
804
|
|
|
805
|
+
#### Bidirectional upstream lifecycle update (P080 — advisory, copy-not-move from transition-problem Step 7b)
|
|
806
|
+
|
|
807
|
+
After the rename + README refresh land but BEFORE the Step 11 commit, fire the bidirectional lifecycle-update sibling skill so the upstream issue (if any) receives the lifecycle update comment in the SAME commit as the transition per ADR-014 single-commit grain. This is the **outbound-lifecycle-update leg** of the reporter loop (the inbound-discovery leg is owned by ADR-062's assessment pipeline; together they close the reporter relationship per JTBD-301 + JTBD-201).
|
|
808
|
+
|
|
809
|
+
This subsection is the **copy-not-move sibling** of `transition-problem` SKILL.md Step 7b per [ADR-010](../../../docs/decisions/010-rename-wr-problem-to-wr-itil.proposed.md) amended "Split-skill execution ownership" rule (P093). The user-initiated transition path lives in `/wr-itil:transition-problem`; the in-skill callers (Step 9b auto-transition, Step 9d closure inside review, the Parked path) need the same lifecycle-update trigger and carry their own scoped copy. Drift between the two copies re-opens P080's bidirectional gap on the in-skill paths.
|
|
810
|
+
|
|
811
|
+
The trigger is **unconditional** — fire on every transition regardless of whether the ticket carries `## Reported Upstream`. The sibling skill's no-op exit (Step 1 of `/wr-itil:update-upstream`) absorbs the misses cheaply; per-transition decision cost is bounded.
|
|
812
|
+
|
|
813
|
+
Invoke via the Skill tool:
|
|
814
|
+
|
|
815
|
+
```
|
|
816
|
+
/wr-itil:update-upstream <NNN>
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
Behaviour matrix:
|
|
820
|
+
|
|
821
|
+
- **No `## Reported Upstream` section on the local ticket** → the sibling skill no-op-exits with a one-line `Nothing to update` message. Proceed to Step 11.
|
|
822
|
+
- **`## Reported Upstream` present AND both gates within appetite** → the sibling skill posts via `gh issue comment` (and on Verifying → Closed, also `gh issue close`), back-writes to `## Upstream Lifecycle Updates`, and stages the back-write into the index. The Step 11 commit captures the back-write alongside the transition.
|
|
823
|
+
- **`## Reported Upstream` present AND above-appetite (after silent risk-reduce + re-score)** → the sibling skill saves the drafted comment to `## Queued Upstream Update` and queues an `outstanding_questions` entry. The Step 11 commit captures the `## Queued Upstream Update` appendage alongside the transition. **The orchestrator continues per P352 queue-and-continue** — do NOT halt the transition on an above-appetite upstream update.
|
|
824
|
+
|
|
825
|
+
If `/wr-itil:update-upstream` is not installed (the `@windyroad/itil` package version pre-dates P080 shipping), the Skill tool returns a not-found error. Log a one-line warning (`update-upstream skill not available; skipping upstream lifecycle update`) and proceed to Step 11 — do NOT halt the transition.
|
|
826
|
+
|
|
827
|
+
Per [ADR-013 Rule 6](../../../docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md) (AFK fail-safe), AFK orchestrators MUST NOT halt this transition path on a queued upstream-update — the queued entry surfaces at the existing batched-`AskUserQuestion` end-of-loop gate.
|
|
828
|
+
|
|
829
|
+
Authority: [ADR-024](../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) amendment (P080) — bidirectional lifecycle-update sibling skill. The advisory ALSO lives in `/wr-itil:transition-problem` Step 7b per ADR-010 amended "copy, not move" (P093) — both copies must move in lockstep.
|
|
830
|
+
|
|
805
831
|
### 8. For list: Show summary
|
|
806
832
|
|
|
807
833
|
Read all open + known-error tickets via the dual-tolerant glob `ls docs/problems/*.open.md docs/problems/*.known-error.md docs/problems/open/*.md docs/problems/known-error/*.md 2>/dev/null` (RFC-002 migration window). Extract ID, title, priority, and status. Sort by priority (highest first). Display as a markdown table.
|
|
@@ -229,6 +229,30 @@ The refresh uses the same rendering rules as `/wr-itil:review-problems` Step 9e
|
|
|
229
229
|
|
|
230
230
|
Canonical rationale anchor: `manage-problem` SKILL.md Step 5 § Last-reviewed line discipline (P134). The cross-reference is preserved for the "why"; the "what" is inlined above for execution-time legibility per P331.
|
|
231
231
|
|
|
232
|
+
### 7b. Bidirectional upstream lifecycle update (P080 — advisory)
|
|
233
|
+
|
|
234
|
+
After the rename + README refresh land but BEFORE the Step 8 commit, fire the bidirectional lifecycle-update sibling skill so the upstream issue (if any) receives the lifecycle update comment in the SAME commit as the transition per ADR-014 single-commit grain. This is the **outbound-lifecycle-update leg** of the reporter loop (the inbound-discovery leg is owned by ADR-062's assessment pipeline; together they close the reporter relationship per JTBD-301 + JTBD-201).
|
|
235
|
+
|
|
236
|
+
The trigger is **unconditional** — fire on every transition regardless of whether the ticket carries `## Reported Upstream`. The sibling skill's no-op exit (Step 1 of `/wr-itil:update-upstream`) absorbs the misses cheaply; per-transition decision cost is bounded.
|
|
237
|
+
|
|
238
|
+
Invoke via the Skill tool:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
/wr-itil:update-upstream <NNN>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Behaviour matrix:
|
|
245
|
+
|
|
246
|
+
- **No `## Reported Upstream` section on the local ticket** → the sibling skill no-op-exits with a one-line `Nothing to update` message. Proceed to Step 8.
|
|
247
|
+
- **`## Reported Upstream` present AND both gates within appetite** → the sibling skill posts via `gh issue comment` (and on Verifying → Closed, also `gh issue close`), back-writes to `## Upstream Lifecycle Updates`, and stages the back-write into the index. The Step 8 commit captures the back-write alongside the transition.
|
|
248
|
+
- **`## Reported Upstream` present AND above-appetite (after silent risk-reduce + re-score)** → the sibling skill saves the drafted comment to `## Queued Upstream Update` and queues an `outstanding_questions` entry. The Step 8 commit captures the `## Queued Upstream Update` appendage alongside the transition. **The orchestrator continues per P352 queue-and-continue** — do NOT halt the transition on an above-appetite upstream update.
|
|
249
|
+
|
|
250
|
+
If `/wr-itil:update-upstream` is not installed (the `@windyroad/itil` package version pre-dates P080 shipping), the Skill tool returns a not-found error. Log a one-line warning (`update-upstream skill not available; skipping upstream lifecycle update`) and proceed to Step 8 — do NOT halt the transition.
|
|
251
|
+
|
|
252
|
+
Per [ADR-013 Rule 6](../../../docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md) (AFK fail-safe), AFK orchestrators MUST NOT halt this transition path on a queued upstream-update — the queued entry surfaces at the existing batched-`AskUserQuestion` end-of-loop gate.
|
|
253
|
+
|
|
254
|
+
Authority: [ADR-024](../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) amendment (P080) — bidirectional lifecycle-update sibling skill. Per [ADR-010](../../../docs/decisions/010-rename-wr-problem-to-wr-itil.proposed.md) amended "Split-skill execution ownership" (P093), an equivalent advisory subsection ALSO lives in `/wr-itil:manage-problem`'s in-skill Step 7 block ("copy, not move") so the in-skill callers (Step 9b auto-transition, Step 9d closure inside review, the Parked path) fire the same lifecycle update.
|
|
255
|
+
|
|
232
256
|
### 8. Commit per ADR-014
|
|
233
257
|
|
|
234
258
|
Governance skills commit their own work. Transition commits include the renamed ticket file + any content edits + the refreshed `docs/problems/README.md`.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wr-itil:update-upstream
|
|
3
|
+
description: Post a lifecycle-update comment to an upstream issue when a local problem ticket transitions. Drafts a transition-specific update (root-cause confirmed / fix released / closed), composes the prose through the external-comms risk gate + voice-tone gate, auto-posts within appetite, queues above-appetite. Reciprocal sibling to /wr-itil:report-upstream — initial-filing vs lifecycle-update split per ADR-024 amendment (P080).
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Skill, Agent
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
@jtbd JTBD-301 (Report a Problem Without Pre-Classifying It — reporter feedback loop)
|
|
9
|
+
@jtbd JTBD-001 (Enforce Governance Without Slowing Down — no manual policing of upstream issues)
|
|
10
|
+
@jtbd JTBD-201 (Restore Service Fast with an Audit Trail — symmetric local/upstream audit trail)
|
|
11
|
+
@jtbd JTBD-101 (Extend the Suite with Clear Patterns — downstream adopters inherit bidirectional contract)
|
|
12
|
+
@problem P080
|
|
13
|
+
@adr ADR-024 (amended P080 — bidirectional lifecycle updates)
|
|
14
|
+
@adr ADR-028 (voice-tone gate on `gh issue comment` / `gh issue close`)
|
|
15
|
+
@adr ADR-013 (Rule 1 AskUserQuestion; Rule 6 AFK fail-safe)
|
|
16
|
+
@adr ADR-014 (single-commit grain — transition + back-write + upstream comment)
|
|
17
|
+
@adr ADR-010 amended (sibling-skill naming; split execution ownership)
|
|
18
|
+
@adr ADR-044 (decision-delegation contract — framework-resolution boundary)
|
|
19
|
+
@adr ADR-075 (Amendment 2026-06-02 — paired promptfoo eval discharges R009 prose floor)
|
|
20
|
+
@adr ADR-061 Rule 4 (evidence-floor — paired Tier-A/B eval ships in same commit as SKILL)
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
# Update Upstream — Lifecycle-Update Skill
|
|
24
|
+
|
|
25
|
+
Post a lifecycle-update comment to an upstream issue (or close it) when the local problem ticket transitions. Reads the local ticket's `## Reported Upstream` section, drafts a transition-specific update from the templates below, composes the draft through the external-comms risk gate (`wr-risk-scorer:external-comms`) and voice-tone gate (`wr-voice-tone:external-comms`), auto-posts via `gh issue comment` (or `gh issue close` on Verifying → Closed) when both gates pass within appetite, and queues an `outstanding_questions` entry when either gate scores above appetite.
|
|
26
|
+
|
|
27
|
+
This skill is the **reciprocal sibling** to [`/wr-itil:report-upstream`](../report-upstream/SKILL.md) — that skill files the initial upstream report; this skill keeps the upstream record in sync as the local ticket walks its lifecycle. The split is per [ADR-010](../../../docs/decisions/010-rename-wr-problem-to-wr-itil.proposed.md) amended Skill Granularity rule (one skill per distinct user intent) — initial-filing and lifecycle-update are distinct user intents with distinct autocomplete surfaces.
|
|
28
|
+
|
|
29
|
+
This skill implements the bidirectional extension to ADR-024's outbound contract — see the **ADR-024 amendment (P080)** entry in the ADR's `## Amendments` section. The amendment authorises this sibling skill, defines the transition-template shape, and pins the same external-comms + voice-tone gate composition that the post-P270 amendment uses for the initial-filing path.
|
|
30
|
+
|
|
31
|
+
## Invocation
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
/wr-itil:update-upstream <NNN>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- `<NNN>`: the three-digit local ticket ID (e.g. `080`). The ticket file is discovered via the same dual-tolerant lookup as [`/wr-itil:report-upstream`](../report-upstream/SKILL.md) (flat layout + per-state subdir per RFC-002 migration window).
|
|
38
|
+
|
|
39
|
+
The skill is typically invoked from `/wr-itil:transition-problem` Step 7's advisory subsection when the transitioning ticket carries a `## Reported Upstream` section (per ADR-024 Confirmation criterion 3a — the back-write that `/wr-itil:report-upstream` Step 7 writes). User-initiated invocation is also supported for retroactive catch-up on existing `.verifying.md` / `.closed.md` tickets that pre-date this skill landing.
|
|
40
|
+
|
|
41
|
+
## Scope
|
|
42
|
+
|
|
43
|
+
**In scope:**
|
|
44
|
+
- Read the local ticket's `## Reported Upstream` section and extract each upstream URL + matched template + disclosure path recorded there.
|
|
45
|
+
- Determine the local ticket's current Status from the filename suffix.
|
|
46
|
+
- Draft a transition-specific lifecycle-update comment per the templates below (Open→KE / KE→Verifying / Verifying→Closed).
|
|
47
|
+
- Compose the drafted prose through `wr-risk-scorer:external-comms` + `wr-voice-tone:external-comms` gates.
|
|
48
|
+
- Within appetite → post via `gh issue comment <n>`; on Verifying→Closed also run `gh issue close <n>`.
|
|
49
|
+
- Above appetite → AskUserQuestion (interactive) / queue `outstanding_questions` (AFK, per P352 queue-and-continue).
|
|
50
|
+
- Back-write a `## Upstream Lifecycle Updates` log entry to the local ticket recording the transition, the matched URL, the posted comment URL, and the disclosure path.
|
|
51
|
+
|
|
52
|
+
**Out of scope:**
|
|
53
|
+
- Initial upstream filing — that's `/wr-itil:report-upstream`.
|
|
54
|
+
- Historical catch-up migration (one-shot retroactive scan of all closed/verifying tickets with `## Reported Upstream`) — that's a separate orchestration concern; per-ticket invocation suffices for the Phase 1 contract. A future amendment may add a `--catchup` mode.
|
|
55
|
+
- Cross-tracker propagation (linking the upstream update back into a different upstream's parallel issue) — out of scope; one local ticket → N upstream URLs is supported, but each URL update is independent.
|
|
56
|
+
|
|
57
|
+
## Step-0 deferral (ADR-027)
|
|
58
|
+
|
|
59
|
+
This skill does NOT implement ADR-027's Step-0 auto-delegation pattern. Same rationale as [`/wr-itil:report-upstream`](../report-upstream/SKILL.md) Step-0 deferral: the local ticket and the `## Reported Upstream` extraction must stay in main-agent context for the gate composition and the back-write, so wrapping the flow in a subagent would not reduce main-agent context cost. Trigger to revisit per ADR-024's Reassessment Criteria — if a third skill that reads `## Reported Upstream` lands, factor the read into a Step-0-delegated subagent.
|
|
60
|
+
|
|
61
|
+
## Voice-tone gate interaction (ADR-028)
|
|
62
|
+
|
|
63
|
+
The skill's `gh issue comment` and `gh issue close` calls are **on the gated surface list per [ADR-028](../../../docs/decisions/028-voice-tone-gate-external-comms.proposed.md)** (Voice-tone gate on external communications). Expected behaviour during these tool calls:
|
|
64
|
+
|
|
65
|
+
1. The voice-tone gate fires `PreToolUse:Bash` with a deny-plus-delegate response.
|
|
66
|
+
2. The hook delegates to `wr-voice-tone:agent` to review the drafted body for brand-voice + tone alignment against `docs/VOICE-AND-TONE.md`.
|
|
67
|
+
3. Once the agent's marker lands, the same `gh issue comment` or `gh issue close` call retries and proceeds.
|
|
68
|
+
|
|
69
|
+
The skill should treat this transient deny-plus-delegate as the expected path, not as an error.
|
|
70
|
+
|
|
71
|
+
If `wr-voice-tone:agent` is not installed in the project, the gate is dormant and the skill proceeds without delegation.
|
|
72
|
+
|
|
73
|
+
## Steps
|
|
74
|
+
|
|
75
|
+
### 1. Read the local problem ticket
|
|
76
|
+
|
|
77
|
+
Dual-tolerant lookup spans flat layout AND per-state subdir layout (RFC-002 migration window):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
LOCAL_TICKET=$(ls docs/problems/${LOCAL_ID}-*.{open,known-error,verifying,closed,parked}.md docs/problems/*/${LOCAL_ID}-*.md 2>/dev/null | head -1)
|
|
81
|
+
[ -n "$LOCAL_TICKET" ] || { echo "Error: local ticket P${LOCAL_ID} not found in docs/problems/"; exit 1; }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Extract:
|
|
85
|
+
- Title (from H1).
|
|
86
|
+
- Current Status (from filename suffix — `.open.md` / `.known-error.md` / `.verifying.md` / `.closed.md` / `.parked.md`).
|
|
87
|
+
- The `## Reported Upstream` section (zero or more upstream entries, each with URL + disclosure path).
|
|
88
|
+
- For `.verifying.md` tickets: the `## Fix Released` section (release marker, version, commit SHA, PR number).
|
|
89
|
+
- For `.known-error.md` tickets: the `## Fix Strategy` section (planned fix path; cited in KE updates so the reporter knows the direction).
|
|
90
|
+
|
|
91
|
+
If the ticket has no `## Reported Upstream` section, exit cleanly with a one-line message: `No ## Reported Upstream section in P${LOCAL_ID}; nothing to update.` This is the **no-op exit** — most local tickets never reported upstream, and the skill's invocation from `transition-problem` Step 7 is unconditional; a missing section is the common case, not an error.
|
|
92
|
+
|
|
93
|
+
### 2. Parse each upstream entry
|
|
94
|
+
|
|
95
|
+
For each `- **URL**: <url>` line under `## Reported Upstream`, extract:
|
|
96
|
+
- The upstream URL.
|
|
97
|
+
- The matched template name (`- **Template used**: <name>`).
|
|
98
|
+
- The disclosure path (`- **Disclosure path**: <path>`).
|
|
99
|
+
- The reported date (`- **Reported**: <YYYY-MM-DD>`).
|
|
100
|
+
|
|
101
|
+
Skip entries whose disclosure path is `drafted-and-saved (mailbox / out-of-band)` — those reports were never filed via `gh`, so there is no issue to comment on. Log a one-line skip note: `Skipping upstream entry <url> — disclosure path is out-of-band; user follow-up required.` The skip is **not** queued as an `outstanding_questions` entry (the user already owns the out-of-band channel per the ADR-024 no-infra-for-email constraint).
|
|
102
|
+
|
|
103
|
+
Multiple `## Reported Upstream` entries are supported (the local ticket may have been filed to multiple upstream trackers). Process each entry independently — the gate composition + post + back-write all run per-entry; one above-appetite entry queues only that entry, the rest proceed.
|
|
104
|
+
|
|
105
|
+
### 3. Determine the transition that fired this invocation
|
|
106
|
+
|
|
107
|
+
The skill is typically invoked AFTER the local ticket's filename suffix has changed (the `transition-problem` Step 7 advisory subsection fires AFTER the `git mv` + Status edit + re-stage). Compare the current suffix against the most recent `## Upstream Lifecycle Updates` log entry (if any) to identify which transition just fired:
|
|
108
|
+
|
|
109
|
+
| Last logged Status | Current suffix | Transition fired |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| (none) | `.known-error.md` | Open → Known Error |
|
|
112
|
+
| (none) | `.verifying.md` | Open → Known Error (skipped) → Verification Pending |
|
|
113
|
+
| Open | `.known-error.md` | Open → Known Error |
|
|
114
|
+
| Known Error | `.verifying.md` | Known Error → Verification Pending |
|
|
115
|
+
| Verification Pending | `.closed.md` | Verification Pending → Closed |
|
|
116
|
+
| any | matching last logged | no transition since last update — exit no-op |
|
|
117
|
+
|
|
118
|
+
If the current suffix matches the last logged Status, exit clean: `No transition since last upstream update; nothing to post.` This guards against re-firing on a manage-problem update that doesn't change Status (Priority/Effort/WSJF edits, root-cause refinement edits).
|
|
119
|
+
|
|
120
|
+
If the current suffix is `.parked.md`, exit clean: `Local ticket is Parked; no upstream lifecycle update applies.` Parking is a transient hold (per ADR-022) and does not warrant an upstream comment.
|
|
121
|
+
|
|
122
|
+
### 4. Draft the lifecycle-update comment
|
|
123
|
+
|
|
124
|
+
Per-transition templates. Each template's `<placeholders>` are filled from the local ticket's sections (per Step 1's extraction).
|
|
125
|
+
|
|
126
|
+
#### Open → Known Error template
|
|
127
|
+
|
|
128
|
+
```markdown
|
|
129
|
+
Update from <downstream-repo-url>/<local-ticket-relative-path>:
|
|
130
|
+
|
|
131
|
+
**Status**: Root cause identified (local ticket transitioned to Known Error).
|
|
132
|
+
|
|
133
|
+
**Investigation findings**:
|
|
134
|
+
|
|
135
|
+
<one-paragraph synthesis from the local ticket's Root Cause Analysis section — substantive, not just "we found it">
|
|
136
|
+
|
|
137
|
+
**Planned fix path**:
|
|
138
|
+
|
|
139
|
+
<from the local ticket's Fix Strategy section, or "Fix path under design" if absent>
|
|
140
|
+
|
|
141
|
+
**Workaround**:
|
|
142
|
+
|
|
143
|
+
<from the local ticket's Workaround section, or "None identified yet" if absent>
|
|
144
|
+
|
|
145
|
+
We'll post here again when the fix releases. Local tracking: P<NNN>.
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Known Error → Verification Pending template
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
Update from <downstream-repo-url>/<local-ticket-relative-path>:
|
|
152
|
+
|
|
153
|
+
**Status**: Fix released (local ticket transitioned to Verification Pending).
|
|
154
|
+
|
|
155
|
+
**Release**:
|
|
156
|
+
|
|
157
|
+
- **Package**: `<package>@<version>` (or "see release notes" if not derivable)
|
|
158
|
+
- **Merge PR**: #<N> (or commit SHA if direct-to-main)
|
|
159
|
+
- **Released**: <YYYY-MM-DD>
|
|
160
|
+
|
|
161
|
+
**Fix summary**:
|
|
162
|
+
|
|
163
|
+
<one-sentence summary from the local ticket's ## Fix Released section>
|
|
164
|
+
|
|
165
|
+
Please upgrade and verify when convenient. We'll close this issue after your confirmation OR after a 14-day quiet period (per P048 default). Local tracking: P<NNN>.
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Verification Pending → Closed template
|
|
169
|
+
|
|
170
|
+
```markdown
|
|
171
|
+
Update from <downstream-repo-url>/<local-ticket-relative-path>:
|
|
172
|
+
|
|
173
|
+
**Status**: Closed locally after user-side verification.
|
|
174
|
+
|
|
175
|
+
Closing this issue to match. Thanks for the report — your filing is what got this on the queue. Local tracking: P<NNN>.
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
After posting the Verifying → Closed comment, the skill also runs `gh issue close <n>` (per Step 5b below) so the upstream tracker matches local state.
|
|
179
|
+
|
|
180
|
+
#### Template-filling rules
|
|
181
|
+
|
|
182
|
+
- **No invention**: if a section the template cites is absent from the local ticket, write the explicit "absent" phrasing ("Fix path under design", "None identified yet") rather than synthesising content. The risk gate and voice-tone gate cannot guard against invented technical claims; the no-invention rule does.
|
|
183
|
+
- **Source-citation**: every template starts with `Update from <downstream-repo-url>/<local-ticket-relative-path>:` so the upstream maintainer can navigate back to the source ticket without ambiguity. The cross-reference URL uses the same shape as `/wr-itil:report-upstream` Step 5's `## Cross-reference` section.
|
|
184
|
+
- **No "we" assumptions about the upstream maintainer**: the lifecycle templates are written from the downstream-reporter perspective. Use "we" only when referring to the downstream team; do not use it to suggest joint authorship of the upstream fix unless the local ticket's Fix Strategy explicitly records an upstream-collaborative path.
|
|
185
|
+
|
|
186
|
+
### 5. Compose through external-comms + voice-tone gates
|
|
187
|
+
|
|
188
|
+
#### 5a. External-comms risk gate
|
|
189
|
+
|
|
190
|
+
Score the drafted comment body via the `wr-risk-scorer:external-comms` agent (shipped per [ADR-028](../../../docs/decisions/028-voice-tone-gate-external-comms.proposed.md) — the same agent the post-P270 amendment uses for the initial-filing path's pre-fire gate). Invocation: delegate via the Agent tool with `subagent_type: "wr-risk-scorer:external-comms"` passing the drafted body + the upstream URL + the transition type as context.
|
|
191
|
+
|
|
192
|
+
The agent returns a structured verdict:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
EXTERNAL_COMMS_RISK_VERDICT
|
|
196
|
+
band: Low (<=4/25) | Medium (5..16) | High (17+)
|
|
197
|
+
score: <0..25>
|
|
198
|
+
pass: true | false
|
|
199
|
+
reason: <one-line rationale>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- **`pass: true` AND band ≤ Low (4/25)**: within appetite per RISK-POLICY.md commit-layer. Proceed to 5b (voice-tone).
|
|
203
|
+
- **`pass: false` OR band > Low**: above appetite. Branch to 5c (above-appetite handling).
|
|
204
|
+
|
|
205
|
+
#### 5b. Voice-tone gate
|
|
206
|
+
|
|
207
|
+
`gh issue comment` and `gh issue close` are on the ADR-028 gated surface list. The PreToolUse:Bash hook fires deny-plus-delegate to `wr-voice-tone:agent`. The agent reads the drafted body against `docs/VOICE-AND-TONE.md` and writes the bypass marker on PASS; the original `gh` call retries automatically.
|
|
208
|
+
|
|
209
|
+
A FAIL verdict on the voice-tone gate is treated identically to an above-appetite risk verdict — branch to 5c.
|
|
210
|
+
|
|
211
|
+
#### 5c. Above-appetite handling
|
|
212
|
+
|
|
213
|
+
The decision policy here is **framework-resolved** per [ADR-044](../../../docs/decisions/044-decision-delegation-contract.proposed.md) (decision-delegation contract) and ADR-013 Rule 6 (AFK fail-safe). No per-transition `AskUserQuestion` for the GATE FIRING — the gate scoring is itself the framework. The above-appetite handling differs by orchestrator context:
|
|
214
|
+
|
|
215
|
+
- **Interactive context** (per ADR-013 Rule 1): use `AskUserQuestion` to surface the drafted comment + the gate verdict + the matched URL, with options:
|
|
216
|
+
- `Post the comment anyway (Recommended after review)` — user has read the draft and judged the post warranted; the skill bypasses the gate for this single post.
|
|
217
|
+
- `Risk-reduce and re-score` — invoke a tighter draft (shorter / fewer claims / stricter source-citation) and re-run the gate.
|
|
218
|
+
- `Queue for later review` — save the draft to `## Queued Upstream Update` on the local ticket; user acts on return.
|
|
219
|
+
- `Skip this update` — exit no-op for this upstream entry; the next transition's invocation re-considers.
|
|
220
|
+
|
|
221
|
+
- **AFK / non-interactive context** (per ADR-013 Rule 6 + P352 queue-and-continue): the skill applies **silent risk-reduce + re-score** first — re-draft the comment with tighter source-citation + shorter prose, then re-invoke the external-comms gate. If the re-scored verdict is within appetite, proceed via 5b. Otherwise, save the drafted comment to the local ticket's `## Queued Upstream Update` section (shape below) and queue an `outstanding_questions` entry (category: `deviation-approval`) naming the local ticket ID + the matched URL + the residual band + the risk-reduce attempts taken. **The orchestrator continues per P352** — do NOT halt the loop on an above-appetite upstream update.
|
|
222
|
+
|
|
223
|
+
The silent risk-reduce step is **mechanical** per ADR-044 framework-resolution boundary — the skill owns the re-draft; per-iter `AskUserQuestion` for risk-reduce vocabulary is the lazy-deferral anti-pattern P132 closes.
|
|
224
|
+
|
|
225
|
+
#### Queued Upstream Update save format
|
|
226
|
+
|
|
227
|
+
```markdown
|
|
228
|
+
## Queued Upstream Update
|
|
229
|
+
|
|
230
|
+
- **Drafted**: <YYYY-MM-DD>
|
|
231
|
+
- **Transition**: Open → Known Error | Known Error → Verification Pending | Verification Pending → Closed
|
|
232
|
+
- **Target URL**: <upstream-issue-url>
|
|
233
|
+
- **Halt reason**: above-appetite external-comms gate (band: <verdict band>; score: <verdict score>; reason: <verdict reason>) | above-appetite voice-tone gate (reason: <verdict reason>)
|
|
234
|
+
- **Risk-reduce attempts**: <count, e.g. "1 — tighter source-citation; re-scored band Medium">
|
|
235
|
+
- **Drafted comment body**:
|
|
236
|
+
|
|
237
|
+
<the body that would have been posted as a `gh issue comment`, ready for manual review>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Per [ADR-024](../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) amendment (P080), the section name `## Queued Upstream Update` is the lifecycle-update analogue of `## Queued Upstream Report` (the initial-filing analogue per the 2026-06-04 second-amendment leaf (c) rename). Same shape; distinct section so a single local ticket can carry both a queued report (initial filing held) and a queued update (lifecycle update held) without collision.
|
|
241
|
+
|
|
242
|
+
### 5b. (final). Post via gh issue comment
|
|
243
|
+
|
|
244
|
+
Within-appetite path. Post the drafted comment:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
gh issue comment "${UPSTREAM_ISSUE_NUMBER}" \
|
|
248
|
+
--repo "${UPSTREAM_OWNER_REPO}" \
|
|
249
|
+
--body "${DRAFTED_COMMENT_BODY}"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Capture the returned comment URL (gh prints `https://github.com/<owner>/<repo>/issues/<n>#issuecomment-<id>`).
|
|
253
|
+
|
|
254
|
+
On the **Verifying → Closed** transition, after posting the comment, also close the upstream issue:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
gh issue close "${UPSTREAM_ISSUE_NUMBER}" \
|
|
258
|
+
--repo "${UPSTREAM_OWNER_REPO}" \
|
|
259
|
+
--comment "" \
|
|
260
|
+
--reason completed
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`--comment ""` suppresses gh's own auto-comment (we already posted the closing comment above); `--reason completed` matches the `.closed.md` semantics.
|
|
264
|
+
|
|
265
|
+
If the issue is already closed upstream (someone else closed it manually), `gh issue close` returns a benign error — capture the existing state and continue to Step 6 with `closed-already-upstream` recorded in the back-write disclosure path.
|
|
266
|
+
|
|
267
|
+
### 6. Back-write to local ticket
|
|
268
|
+
|
|
269
|
+
Append a log entry to the local ticket's `## Upstream Lifecycle Updates` section (create the section if absent — never inserted mid-document; appended after all existing sections per the same discipline as `## Reported Upstream` in `/wr-itil:report-upstream` Step 7):
|
|
270
|
+
|
|
271
|
+
```markdown
|
|
272
|
+
## Upstream Lifecycle Updates
|
|
273
|
+
|
|
274
|
+
- **<YYYY-MM-DD>** — Open → Known Error
|
|
275
|
+
- **Target URL**: <upstream-issue-url>
|
|
276
|
+
- **Comment URL**: <posted-comment-url> (or "queued — see ## Queued Upstream Update" when above-appetite)
|
|
277
|
+
- **Disclosure path**: posted-comment | posted-comment-and-closed (Verifying → Closed) | queued-above-appetite | closed-already-upstream | skipped-out-of-band
|
|
278
|
+
- **Gate verdict**: external-comms <band/score> + voice-tone <pass|fail>
|
|
279
|
+
|
|
280
|
+
- **<YYYY-MM-DD>** — Known Error → Verification Pending
|
|
281
|
+
- ... (next entry appends; never replaces earlier entries)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The log is append-only — each transition adds an entry; earlier entries are never overwritten. The log is the audit trail per JTBD-201's symmetric-audit-trail outcome and per ADR-024 Confirmation criterion 3a's `## Reported Upstream` back-write pattern (extended for the bidirectional case).
|
|
285
|
+
|
|
286
|
+
### 7. Commit per ADR-014
|
|
287
|
+
|
|
288
|
+
When invoked from `transition-problem` Step 7's advisory subsection, the upstream comment + back-write + the ticket rename + the README refresh all join the **same single commit** per ADR-014's single-commit grain — never split across commits. The transition-problem skill owns the commit; this skill's edits ride that commit as additional staged changes.
|
|
289
|
+
|
|
290
|
+
When invoked user-initiatedly (no transition in this session, e.g. retroactive catch-up), the skill commits its own work:
|
|
291
|
+
|
|
292
|
+
1. `git add docs/problems/<state>/<NNN>-<title>.md` (the back-write + any `## Queued Upstream Update` appendage).
|
|
293
|
+
2. Score commit/push/release risk via `wr-risk-scorer:pipeline` subagent (or fall back to `/wr-risk-scorer:assess-release` skill per ADR-015).
|
|
294
|
+
3. `git commit -m "docs(problems): P<NNN> upstream lifecycle update — <transition>"`.
|
|
295
|
+
|
|
296
|
+
If the cumulative pipeline risk lands above appetite and `AskUserQuestion` is unavailable, apply the [ADR-013 Rule 6](../../../docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md) non-interactive fail-safe: skip the commit and report the uncommitted state. Do NOT auto-commit above appetite without the user's call.
|
|
297
|
+
|
|
298
|
+
## AFK behaviour summary
|
|
299
|
+
|
|
300
|
+
Four distinct AFK branches. Per the [ADR-024](../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) amendment (P080) — same composition shape as the post-P270 initial-filing path — ALL pre-post branches route through the `wr-risk-scorer:external-comms` + `wr-voice-tone:external-comms` gates. Below-appetite proceeds; above-appetite silent risk-reduces + re-scores; if still above, queues per P352 queue-and-continue without halting the loop.
|
|
301
|
+
|
|
302
|
+
| Branch | AFK behaviour | Authority |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| Below-appetite post (Step 5b final) | Post via `gh issue comment`; on Verifying→Closed also `gh issue close`. Back-write to `## Upstream Lifecycle Updates`. Voice-tone gate per ADR-028 may delegate-and-retry on the post; treat as expected. | ADR-024 amendment (P080); ADR-028 |
|
|
305
|
+
| Above-appetite — silent risk-reduce + re-score within appetite | Re-draft with tighter source-citation + shorter prose; re-invoke `wr-risk-scorer:external-comms`. If within → post per the below-appetite branch. | ADR-024 amendment (P080); ADR-044 framework-resolution boundary; ADR-042 within-axis precedent (open-vocabulary risk-reducing measures) |
|
|
306
|
+
| Above-appetite — silent risk-reduce did not bring within appetite | Save drafted comment to `## Queued Upstream Update` + queue `outstanding_questions` entry (category: `deviation-approval`). Orchestrator continues per P352. | ADR-024 amendment (P080); ADR-013 Rule 6; P352 |
|
|
307
|
+
| Above-appetite commit (Step 7) | Skip the commit, report uncommitted state. | ADR-013 Rule 6 |
|
|
308
|
+
|
|
309
|
+
The pre-amendment "halt-the-orchestrator on above-appetite" semantics are **superseded** by queue-and-continue per P352 — same shape as the post-P270 initial-filing path.
|
|
310
|
+
|
|
311
|
+
## Triggered from transition-problem Step 7
|
|
312
|
+
|
|
313
|
+
[`/wr-itil:transition-problem`](../transition-problem/SKILL.md) Step 7 hosts an advisory subsection that fires this skill when the transitioning ticket carries a `## Reported Upstream` section. The advisory wires the trigger; this skill owns the execution. The same "copy, not move" pattern (per [ADR-010](../../../docs/decisions/010-rename-wr-problem-to-wr-itil.proposed.md) amended Split-skill execution ownership rule, P093) replicates the advisory in `/wr-itil:manage-problem`'s in-skill Step 7 block so in-skill callers (Step 9b auto-transition, Step 9d closure, the Parked path) also fire the lifecycle update.
|
|
314
|
+
|
|
315
|
+
Both call sites delegate via the Skill tool:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
/wr-itil:update-upstream <NNN>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The skill's no-op exit (Step 1) means firing the trigger unconditionally on every transition is cheap — most tickets have no `## Reported Upstream` section and exit immediately.
|
|
322
|
+
|
|
323
|
+
## References
|
|
324
|
+
|
|
325
|
+
- [ADR-024](../../../docs/decisions/024-cross-project-problem-reporting-contract.proposed.md) — primary contract this skill extends. The P080 amendment in `## Amendments` authorises the bidirectional lifecycle-update sibling skill, the transition-template shape, and the external-comms + voice-tone gate composition.
|
|
326
|
+
- [ADR-028](../../../docs/decisions/028-voice-tone-gate-external-comms.proposed.md) — voice-tone gate on `gh issue comment` and `gh issue close`.
|
|
327
|
+
- [ADR-013](../../../docs/decisions/013-structured-user-interaction-for-governance-decisions.proposed.md) — interaction policy; Rule 1 governs the interactive above-appetite path; Rule 6 governs the AFK fail-safe.
|
|
328
|
+
- [ADR-014](../../../docs/decisions/014-governance-skills-commit-their-own-work.proposed.md) — single-commit grain for transition + back-write + upstream post.
|
|
329
|
+
- [ADR-010](../../../docs/decisions/010-rename-wr-problem-to-wr-itil.proposed.md) amended — sibling-skill naming + split execution ownership (P093 "copy, not move").
|
|
330
|
+
- [ADR-044](../../../docs/decisions/044-decision-delegation-contract.proposed.md) — framework-resolution boundary; the gate verdict IS the framework, no per-transition AskUserQuestion for the gate firing itself.
|
|
331
|
+
- [ADR-042](../../../docs/decisions/042-auto-apply-scorer-remediations-open-vocabulary.proposed.md) — within-axis precedent for open-vocabulary risk-reducing measures.
|
|
332
|
+
- [ADR-075](../../../docs/decisions/075-promptfoo-agent-prose-verdict-eval-harness.proposed.md) Amendment 2026-06-02 — paired promptfoo Tier-A/B eval discharges the R009 prose-floor for SKILL surfaces.
|
|
333
|
+
- [ADR-061](../../../docs/decisions/061-dogfood-graduation-criteria.proposed.md) Rule 4 — evidence-floor; the paired eval ships in the same commit as this SKILL prose for atomic R009 discharge.
|
|
334
|
+
- **P080** — driving problem ticket (No bidirectional update of upstream-reported problems).
|
|
335
|
+
- **P079** — sibling problem (inbound-discovery leg); together P079 + P080 close the reporter-loop end-to-end.
|
|
336
|
+
- **P078** — capture-on-correction; the manage-problem trap this skill closes (manual upstream-update step gets forgotten under load).
|
|
337
|
+
- [`packages/itil/skills/report-upstream/SKILL.md`](../report-upstream/SKILL.md) — reciprocal sibling (initial-filing path); shares the `## Reported Upstream` contract this skill consumes.
|
|
338
|
+
- [`packages/itil/skills/transition-problem/SKILL.md`](../transition-problem/SKILL.md) — Step 7 advisory subsection fires this skill.
|
|
339
|
+
- [`packages/itil/skills/manage-problem/SKILL.md`](../manage-problem/SKILL.md) — in-skill Step 7 copy fires this skill (per ADR-010 amended "copy, not move").
|
|
340
|
+
|
|
341
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# tdd-review: structural-permitted (justification: P012 skill testing
|
|
4
|
+
# harness scope is open; no behavioural alternative for SKILL.md prose-
|
|
5
|
+
# template structural assertions today. ADR-052 Migration clause permits
|
|
6
|
+
# structural retrofit-via-justification when the linked harness-gap ticket
|
|
7
|
+
# has not yet shipped the primitives. The paired promptfoo eval at
|
|
8
|
+
# packages/itil/skills/update-upstream/eval/promptfooconfig.yaml carries
|
|
9
|
+
# the behavioural Tier-A/B coverage per ADR-075 Amendment 2026-06-02.)
|
|
10
|
+
#
|
|
11
|
+
# Doc-lint structural test (Permitted Exception per ADR-005 — structural
|
|
12
|
+
# SKILL.md content checks, not behavioural). Mirrors the doc-lint pattern
|
|
13
|
+
# established in ADR-011 and ADR-027 Confirmation tests and used by the
|
|
14
|
+
# sibling report-upstream contract test.
|
|
15
|
+
#
|
|
16
|
+
# Asserts the update-upstream skill's SKILL.md encodes the contract
|
|
17
|
+
# documented in ADR-024 amendment (P080):
|
|
18
|
+
# - sibling-skill split per ADR-010 amended
|
|
19
|
+
# - reads ## Reported Upstream from the local ticket
|
|
20
|
+
# - three transition templates (Open→KE, KE→Verifying, Verifying→Closed)
|
|
21
|
+
# - composes through external-comms + voice-tone gates (AND, not OR)
|
|
22
|
+
# - within appetite → gh issue comment; on Verifying→Closed also gh issue close
|
|
23
|
+
# - above appetite → silent risk-reduce + re-score; if still above, queue
|
|
24
|
+
# to ## Queued Upstream Update + outstanding_questions (NOT halt the loop)
|
|
25
|
+
# - back-writes to ## Upstream Lifecycle Updates (append-only log)
|
|
26
|
+
# - ADR-014 single-commit grain when fired from transition-problem Step 7
|
|
27
|
+
|
|
28
|
+
setup() {
|
|
29
|
+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../../.." && pwd)"
|
|
30
|
+
SKILL_MD="$REPO_ROOT/packages/itil/skills/update-upstream/SKILL.md"
|
|
31
|
+
EVAL_CONFIG="$REPO_ROOT/packages/itil/skills/update-upstream/eval/promptfooconfig.yaml"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@test "update-upstream: SKILL.md exists" {
|
|
35
|
+
[ -f "$SKILL_MD" ]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@test "update-upstream: SKILL.md declares sibling-skill split per ADR-010 amended (P080 user direction (a))" {
|
|
39
|
+
# User direction (a) — new sibling skill, not a mode flag on report-upstream.
|
|
40
|
+
run grep -F 'sibling' "$SKILL_MD"
|
|
41
|
+
[ "$status" -eq 0 ]
|
|
42
|
+
run grep -F 'ADR-010' "$SKILL_MD"
|
|
43
|
+
[ "$status" -eq 0 ]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@test "update-upstream: SKILL.md cross-references ADR-024 amendment (P080)" {
|
|
47
|
+
run grep -F 'ADR-024' "$SKILL_MD"
|
|
48
|
+
[ "$status" -eq 0 ]
|
|
49
|
+
run grep -iE 'P080' "$SKILL_MD"
|
|
50
|
+
[ "$status" -eq 0 ]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@test "update-upstream: SKILL.md reads ## Reported Upstream from the local ticket (P080 contract)" {
|
|
54
|
+
# The skill reads the section that /wr-itil:report-upstream Step 7
|
|
55
|
+
# writes (per ADR-024 Confirmation criterion 3a). Without this read, the
|
|
56
|
+
# skill has no upstream URL to comment on.
|
|
57
|
+
run grep -F '## Reported Upstream' "$SKILL_MD"
|
|
58
|
+
[ "$status" -eq 0 ]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@test "update-upstream: SKILL.md documents three transition templates (Open→KE, KE→Verifying, Verifying→Closed)" {
|
|
62
|
+
# Each transition has its own template — investigation findings vs release
|
|
63
|
+
# info vs closure thanks. ADR-024 amendment authority pins all three.
|
|
64
|
+
run grep -iE 'Open[[:space:]]*[—\-][[:space:]]*[>›]?[[:space:]]*Known Error|Open[[:space:]]*→[[:space:]]*Known Error' "$SKILL_MD"
|
|
65
|
+
[ "$status" -eq 0 ]
|
|
66
|
+
run grep -iE 'Known Error[[:space:]]*[—\-][[:space:]]*[>›]?[[:space:]]*Verification Pending|Known Error[[:space:]]*→[[:space:]]*Verification Pending' "$SKILL_MD"
|
|
67
|
+
[ "$status" -eq 0 ]
|
|
68
|
+
run grep -iE 'Verification Pending[[:space:]]*[—\-][[:space:]]*[>›]?[[:space:]]*Closed|Verification Pending[[:space:]]*→[[:space:]]*Closed' "$SKILL_MD"
|
|
69
|
+
[ "$status" -eq 0 ]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@test "update-upstream: SKILL.md composes through external-comms gate (P080 user direction (b) + ADR-028)" {
|
|
73
|
+
# User direction (b) — risk-gated. The gate is wr-risk-scorer:external-comms
|
|
74
|
+
# per ADR-028's third-evaluator extension point (the same gate the post-
|
|
75
|
+
# P270 amendment uses for the initial-filing path).
|
|
76
|
+
run grep -F 'wr-risk-scorer:external-comms' "$SKILL_MD"
|
|
77
|
+
[ "$status" -eq 0 ]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@test "update-upstream: SKILL.md composes through voice-tone gate (P080 user direction (b) + ADR-028)" {
|
|
81
|
+
# User direction (b) — voice-tone gated. The gate is wr-voice-tone:agent
|
|
82
|
+
# firing on gh issue comment / gh issue close per ADR-028.
|
|
83
|
+
run grep -F 'ADR-028' "$SKILL_MD"
|
|
84
|
+
[ "$status" -eq 0 ]
|
|
85
|
+
run grep -iE 'voice-tone gate' "$SKILL_MD"
|
|
86
|
+
[ "$status" -eq 0 ]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@test "update-upstream: SKILL.md gates compose AND (both must pass) not OR (P080 user direction (c))" {
|
|
90
|
+
# User direction (c) — same external-comms gate composition pattern as
|
|
91
|
+
# inbound (P079). That pattern is AND, not OR — one fail blocks the post.
|
|
92
|
+
# The SKILL.md must frame composition explicitly so adopters do not read
|
|
93
|
+
# it as independent gates.
|
|
94
|
+
run grep -iE 'voice-tone fail.*queue|voice-tone.*FAIL.*queue|FAIL.*identically' "$SKILL_MD"
|
|
95
|
+
[ "$status" -eq 0 ]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@test "update-upstream: SKILL.md within-appetite posts via gh issue comment" {
|
|
99
|
+
# Within-appetite path posts via gh issue comment with the matched URL's
|
|
100
|
+
# issue number — same gh CLI surface as report-upstream Step 5c.
|
|
101
|
+
run grep -F 'gh issue comment' "$SKILL_MD"
|
|
102
|
+
[ "$status" -eq 0 ]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@test "update-upstream: SKILL.md Verifying→Closed also runs gh issue close (symmetric audit trail)" {
|
|
106
|
+
# JTBD-201 symmetric audit trail outcome — when the local ticket goes
|
|
107
|
+
# .closed.md the upstream issue must also close. Without gh issue close
|
|
108
|
+
# the upstream tracker accumulates stale-looking open issues.
|
|
109
|
+
run grep -F 'gh issue close' "$SKILL_MD"
|
|
110
|
+
[ "$status" -eq 0 ]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@test "update-upstream: SKILL.md above-appetite saves to ## Queued Upstream Update (not the report rename)" {
|
|
114
|
+
# ADR-024 amendment (P080) — the lifecycle-update queue section is named
|
|
115
|
+
# ## Queued Upstream Update, distinct from the initial-filing ## Queued
|
|
116
|
+
# Upstream Report (renamed per the 2026-06-04 second-amendment leaf (c)).
|
|
117
|
+
# Distinct sections so a single local ticket can carry both without collision.
|
|
118
|
+
run grep -F '## Queued Upstream Update' "$SKILL_MD"
|
|
119
|
+
[ "$status" -eq 0 ]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@test "update-upstream: SKILL.md above-appetite queues outstanding_questions (does NOT halt the orchestrator per P352)" {
|
|
123
|
+
# P352 queue-and-continue — same shape as the post-P270 initial-filing
|
|
124
|
+
# path. The above-appetite branch must NOT halt the loop; the queued
|
|
125
|
+
# entry surfaces at the existing batched-AskUserQuestion end-of-loop gate.
|
|
126
|
+
run grep -iE 'outstanding_questions|queue-and-continue|P352' "$SKILL_MD"
|
|
127
|
+
[ "$status" -eq 0 ]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@test "update-upstream: SKILL.md silent risk-reduce + re-score is mechanical per ADR-044 (no per-iter AskUserQuestion)" {
|
|
131
|
+
# ADR-044 framework-resolution boundary — the risk-reduce + re-score is
|
|
132
|
+
# silent (mechanical); per-iter AskUserQuestion for risk-reduce vocabulary
|
|
133
|
+
# is the lazy-deferral anti-pattern P132 closes.
|
|
134
|
+
run grep -iE 'silent risk-reduce|mechanical.*ADR-044|ADR-044.*mechanical' "$SKILL_MD"
|
|
135
|
+
[ "$status" -eq 0 ]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@test "update-upstream: SKILL.md back-writes to ## Upstream Lifecycle Updates (append-only log)" {
|
|
139
|
+
# The audit-trail back-write mirrors the ## Reported Upstream back-write
|
|
140
|
+
# in report-upstream Step 7 (per ADR-024 Confirmation criterion 3a),
|
|
141
|
+
# extended to log every transition.
|
|
142
|
+
run grep -F '## Upstream Lifecycle Updates' "$SKILL_MD"
|
|
143
|
+
[ "$status" -eq 0 ]
|
|
144
|
+
run grep -iE 'append-only|never overwritten|append.*entry' "$SKILL_MD"
|
|
145
|
+
[ "$status" -eq 0 ]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@test "update-upstream: SKILL.md AFK behaviour summary table exists (architect review)" {
|
|
149
|
+
# Mirrors report-upstream's AFK behaviour summary table — the canonical
|
|
150
|
+
# place where adopters look up the AFK routing for each branch.
|
|
151
|
+
run grep -F 'AFK behaviour summary' "$SKILL_MD"
|
|
152
|
+
[ "$status" -eq 0 ]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@test "update-upstream: SKILL.md cites ADR-014 single-commit grain (transition + back-write + upstream post)" {
|
|
156
|
+
# When fired from transition-problem Step 7, the upstream comment +
|
|
157
|
+
# back-write + ticket rename + README refresh ride the SAME commit per
|
|
158
|
+
# ADR-014 single-commit grain.
|
|
159
|
+
run grep -F 'ADR-014' "$SKILL_MD"
|
|
160
|
+
[ "$status" -eq 0 ]
|
|
161
|
+
run grep -iE 'single-commit|same single commit|same commit' "$SKILL_MD"
|
|
162
|
+
[ "$status" -eq 0 ]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@test "update-upstream: SKILL.md no-op exit on missing ## Reported Upstream (cheap unconditional firing)" {
|
|
166
|
+
# The trigger fires on every transition unconditionally; the no-op exit
|
|
167
|
+
# absorbs the misses (most tickets have no ## Reported Upstream). Without
|
|
168
|
+
# the no-op exit, every transition pays a decision cost.
|
|
169
|
+
run grep -iE 'no-op exit|nothing to update|exit cleanly' "$SKILL_MD"
|
|
170
|
+
[ "$status" -eq 0 ]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@test "update-upstream: SKILL.md no-invention rule on template-filling (reporter-trust guard)" {
|
|
174
|
+
# If a cited local-ticket section is absent, write the explicit "absent"
|
|
175
|
+
# phrasing rather than invent content. The risk + voice-tone gates cannot
|
|
176
|
+
# guard against invented technical claims; the no-invention rule does.
|
|
177
|
+
run grep -iE 'no.invention|do not invent|never invent|rather than invent|absent.*phrasing|"absent"' "$SKILL_MD"
|
|
178
|
+
[ "$status" -eq 0 ]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@test "update-upstream: SKILL.md annotates JTBD-301 + JTBD-001 (jtbd-lead review)" {
|
|
182
|
+
# JTBD-301 plugin-user reporter feedback loop AND JTBD-001 solo-developer
|
|
183
|
+
# without-slowing-down are the load-bearing JTBDs. The jtbd-lead review
|
|
184
|
+
# required both annotations.
|
|
185
|
+
run grep -F 'JTBD-301' "$SKILL_MD"
|
|
186
|
+
[ "$status" -eq 0 ]
|
|
187
|
+
run grep -F 'JTBD-001' "$SKILL_MD"
|
|
188
|
+
[ "$status" -eq 0 ]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@test "update-upstream: SKILL.md documents transition-problem Step 7 trigger wire-in (P080 invocation contract)" {
|
|
192
|
+
# The advisory subsection in transition-problem Step 7 fires this skill
|
|
193
|
+
# when ## Reported Upstream is present. The SKILL.md must reference the
|
|
194
|
+
# trigger surface so adopters understand the invocation contract.
|
|
195
|
+
run grep -iE 'transition-problem.*Step 7|Step 7.*transition-problem|fired from.*transition' "$SKILL_MD"
|
|
196
|
+
[ "$status" -eq 0 ]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# ─── Paired promptfoo eval (ADR-075 Amendment 2026-06-02 + ADR-061 Rule 4) ────
|
|
200
|
+
#
|
|
201
|
+
# The paired promptfoo eval at packages/itil/skills/update-upstream/eval/
|
|
202
|
+
# discharges the R009 prose-floor for this SKILL surface atomically per
|
|
203
|
+
# ADR-061 Rule 4 evidence-floor — without the paired eval, ADR-042 Rule 2
|
|
204
|
+
# move-to-holding would apply per the P080 iter contract.
|
|
205
|
+
|
|
206
|
+
@test "update-upstream: paired promptfoo eval config exists (ADR-075 Amendment 2026-06-02)" {
|
|
207
|
+
[ -f "$EVAL_CONFIG" ]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@test "update-upstream: paired eval uses exec:bash provider per ADR-075 §6 (no API key)" {
|
|
211
|
+
# ADR-075 §6 — subscription auth via claude -p, no ANTHROPIC_API_KEY.
|
|
212
|
+
# The provider must be exec:bash invoking ./run-skill-eval.sh.
|
|
213
|
+
run grep -F "exec:bash ./run-skill-eval.sh" "$EVAL_CONFIG"
|
|
214
|
+
[ "$status" -eq 0 ]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@test "update-upstream: paired eval ships Tier-B llm-rubric grader for negative clauses" {
|
|
218
|
+
# Negative clauses (must NOT halt; must NOT invent) cannot be Tier-A
|
|
219
|
+
# graded — a not-regex for "halt" false-fails the correct "does NOT halt".
|
|
220
|
+
# The grader script + llm-rubric assertions discharge those clauses.
|
|
221
|
+
GRADER="$REPO_ROOT/packages/itil/skills/update-upstream/eval/grade-llm-rubric.sh"
|
|
222
|
+
[ -f "$GRADER" ]
|
|
223
|
+
[ -x "$GRADER" ]
|
|
224
|
+
run grep -F 'llm-rubric' "$EVAL_CONFIG"
|
|
225
|
+
[ "$status" -eq 0 ]
|
|
226
|
+
}
|