@windyroad/itil 0.47.12-preview.598 → 0.47.12-preview.617
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/bin/wr-itil-check-outbound-responses-staleness +51 -0
- package/bin/wr-itil-enumerate-postrelease-kv-candidates +51 -0
- package/lib/check-outbound-responses-staleness.sh +93 -0
- package/lib/enumerate-postrelease-kv-candidates.sh +106 -0
- package/package.json +1 -1
- package/scripts/run-check-outbound-responses-staleness.sh +21 -0
- package/scripts/run-enumerate-postrelease-kv-candidates.sh +29 -0
- package/skills/check-upstream-responses/SKILL.md +5 -2
- package/skills/review-problems/SKILL.md +28 -4
- package/skills/review-problems/test/jtbd-301-verdict-shape-contract.bats +225 -0
- package/skills/work-problems/SKILL.md +92 -11
- package/skills/work-problems/test/work-problems-step-0d-outbound-responses-staleness-behavioural.bats +174 -0
- package/skills/work-problems/test/work-problems-step-5-is-error-transient-halt.bats +278 -0
- package/skills/work-problems/test/work-problems-step-6-5-postrelease-kv-callback.bats +209 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generated by scripts/sync-shim-wrappers.sh from
|
|
3
|
+
# packages/shared/lib/shim-wrapper-template.sh. DO NOT EDIT individual
|
|
4
|
+
# shim files in packages/*/bin/wr-* directly; edit the template + run
|
|
5
|
+
# `npm run sync:shim-wrappers` to regenerate.
|
|
6
|
+
#
|
|
7
|
+
# Resolution (ADR-080):
|
|
8
|
+
# 1. If the wrapper's parent dir is semver-shaped, treat as installed-
|
|
9
|
+
# cache execution and resolve to the highest-version sibling's
|
|
10
|
+
# scripts/ entry below.
|
|
11
|
+
# 2. Otherwise (parent dir is e.g. `architect`), treat as source-
|
|
12
|
+
# monorepo execution and dispatch to own scripts/. The source-repo-
|
|
13
|
+
# guard `exec` is the anchor parsed by
|
|
14
|
+
# packages/retrospective/scripts/check-tarball-shipped-shims.sh.
|
|
15
|
+
# 3. If the cache parent contains zero semver-shaped siblings, exit
|
|
16
|
+
# 127 with a stderr message naming the cache parent (per SQ-080-2).
|
|
17
|
+
#
|
|
18
|
+
# @adr ADR-080 (highest-version-wins shim wrapper plugin scaffold)
|
|
19
|
+
# @adr ADR-049 (plugin-bundled scripts resolve via bin/ on $PATH — amended)
|
|
20
|
+
# @problem P343 (mid-session staleness window)
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
SHIM_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
25
|
+
OWN_VERSION_DIR="$(dirname "$SHIM_DIR")"
|
|
26
|
+
OWN_VERSION_NAME="$(basename "$OWN_VERSION_DIR")"
|
|
27
|
+
CACHE_PARENT="$(dirname "$OWN_VERSION_DIR")"
|
|
28
|
+
|
|
29
|
+
SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'
|
|
30
|
+
|
|
31
|
+
# Source-repo guard: own parent dir is NOT semver → dispatch to own scripts/.
|
|
32
|
+
if ! [[ "$OWN_VERSION_NAME" =~ $SEMVER_RE ]]; then
|
|
33
|
+
exec "$SHIM_DIR/../scripts/run-check-outbound-responses-staleness.sh" "$@"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Cache execution: pick the highest-semver sibling under CACHE_PARENT.
|
|
37
|
+
HIGHEST=""
|
|
38
|
+
while IFS= read -r dir; do
|
|
39
|
+
name="$(basename "$dir")"
|
|
40
|
+
[[ "$name" =~ $SEMVER_RE ]] || continue
|
|
41
|
+
if [[ -z "$HIGHEST" ]] || [[ "$(printf '%s\n%s\n' "$HIGHEST" "$name" | sort -V | tail -1)" == "$name" ]]; then
|
|
42
|
+
HIGHEST="$name"
|
|
43
|
+
fi
|
|
44
|
+
done < <(find "$CACHE_PARENT" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
45
|
+
|
|
46
|
+
if [[ -z "$HIGHEST" ]]; then
|
|
47
|
+
printf 'wr-shim: no cached versions in %s\n' "$CACHE_PARENT" >&2
|
|
48
|
+
exit 127
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exec "$CACHE_PARENT/$HIGHEST/scripts/run-check-outbound-responses-staleness.sh" "$@"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Generated by scripts/sync-shim-wrappers.sh from
|
|
3
|
+
# packages/shared/lib/shim-wrapper-template.sh. DO NOT EDIT individual
|
|
4
|
+
# shim files in packages/*/bin/wr-* directly; edit the template + run
|
|
5
|
+
# `npm run sync:shim-wrappers` to regenerate.
|
|
6
|
+
#
|
|
7
|
+
# Resolution (ADR-080):
|
|
8
|
+
# 1. If the wrapper's parent dir is semver-shaped, treat as installed-
|
|
9
|
+
# cache execution and resolve to the highest-version sibling's
|
|
10
|
+
# scripts/ entry below.
|
|
11
|
+
# 2. Otherwise (parent dir is e.g. `architect`), treat as source-
|
|
12
|
+
# monorepo execution and dispatch to own scripts/. The source-repo-
|
|
13
|
+
# guard `exec` is the anchor parsed by
|
|
14
|
+
# packages/retrospective/scripts/check-tarball-shipped-shims.sh.
|
|
15
|
+
# 3. If the cache parent contains zero semver-shaped siblings, exit
|
|
16
|
+
# 127 with a stderr message naming the cache parent (per SQ-080-2).
|
|
17
|
+
#
|
|
18
|
+
# @adr ADR-080 (highest-version-wins shim wrapper plugin scaffold)
|
|
19
|
+
# @adr ADR-049 (plugin-bundled scripts resolve via bin/ on $PATH — amended)
|
|
20
|
+
# @problem P343 (mid-session staleness window)
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
SHIM_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
25
|
+
OWN_VERSION_DIR="$(dirname "$SHIM_DIR")"
|
|
26
|
+
OWN_VERSION_NAME="$(basename "$OWN_VERSION_DIR")"
|
|
27
|
+
CACHE_PARENT="$(dirname "$OWN_VERSION_DIR")"
|
|
28
|
+
|
|
29
|
+
SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'
|
|
30
|
+
|
|
31
|
+
# Source-repo guard: own parent dir is NOT semver → dispatch to own scripts/.
|
|
32
|
+
if ! [[ "$OWN_VERSION_NAME" =~ $SEMVER_RE ]]; then
|
|
33
|
+
exec "$SHIM_DIR/../scripts/run-enumerate-postrelease-kv-candidates.sh" "$@"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Cache execution: pick the highest-semver sibling under CACHE_PARENT.
|
|
37
|
+
HIGHEST=""
|
|
38
|
+
while IFS= read -r dir; do
|
|
39
|
+
name="$(basename "$dir")"
|
|
40
|
+
[[ "$name" =~ $SEMVER_RE ]] || continue
|
|
41
|
+
if [[ -z "$HIGHEST" ]] || [[ "$(printf '%s\n%s\n' "$HIGHEST" "$name" | sort -V | tail -1)" == "$name" ]]; then
|
|
42
|
+
HIGHEST="$name"
|
|
43
|
+
fi
|
|
44
|
+
done < <(find "$CACHE_PARENT" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
45
|
+
|
|
46
|
+
if [[ -z "$HIGHEST" ]]; then
|
|
47
|
+
printf 'wr-shim: no cached versions in %s\n' "$CACHE_PARENT" >&2
|
|
48
|
+
exit 127
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exec "$CACHE_PARENT/$HIGHEST/scripts/run-enumerate-postrelease-kv-candidates.sh" "$@"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Outbound-responses cache staleness check — Step 0d of /wr-itil:work-problems.
|
|
3
|
+
# P220 + ADR-062 § JTBD-006 driver: work-problems should pre-flight
|
|
4
|
+
# /wr-itil:check-upstream-responses when the outbound-responses cache is
|
|
5
|
+
# stale or missing AND local tickets carry `## Reported Upstream` back-link
|
|
6
|
+
# sections, so AFK loops keep upstream-reporter responses visible without
|
|
7
|
+
# the maintainer remembering to invoke check-upstream-responses first.
|
|
8
|
+
#
|
|
9
|
+
# The staleness comparison is the outbound symmetric counterpart of
|
|
10
|
+
# check-upstream-cache-staleness.sh's inbound-discovery contract. Any
|
|
11
|
+
# change to TTL semantics MUST update this helper, the check-upstream-
|
|
12
|
+
# responses SKILL.md Confirmation, and the Step 0d block in work-problems
|
|
13
|
+
# SKILL.md in the same commit.
|
|
14
|
+
# <!-- OUTBOUND-RESPONSES-STALENESS-CONTRACT-SOURCE: packages/itil/skills/check-upstream-responses/SKILL.md ## Confirmation -->
|
|
15
|
+
#
|
|
16
|
+
# Source this file, then call `should_promote_outbound_responses_preflight`:
|
|
17
|
+
# . packages/itil/lib/check-outbound-responses-staleness.sh
|
|
18
|
+
# reason="$(should_promote_outbound_responses_preflight "$PWD")"
|
|
19
|
+
#
|
|
20
|
+
# Output (one of):
|
|
21
|
+
# no-back-link-tickets → no problem tickets carry a `## Reported Upstream`
|
|
22
|
+
# section; nothing to poll. Skip silently.
|
|
23
|
+
# Downstream-adopter non-obligation; analogue
|
|
24
|
+
# to inbound's `no-channels-config`.
|
|
25
|
+
# first-run-cache-absent → back-link tickets exist, cache file absent.
|
|
26
|
+
# Dispatch check-upstream-responses.
|
|
27
|
+
# first-run-last-checked-null → cache present but last_checked is null.
|
|
28
|
+
# Dispatch check-upstream-responses.
|
|
29
|
+
# fresh-within-ttl → cache within TTL; silent-pass.
|
|
30
|
+
# ttl-expiry age=<N>s ttl=<M>s → cache older than TTL; dispatch.
|
|
31
|
+
#
|
|
32
|
+
# TTL source: cache.ttl_seconds if present, else 86400 (24h) — symmetric
|
|
33
|
+
# with the inbound axis default at packages/itil/lib/check-upstream-cache-staleness.sh.
|
|
34
|
+
#
|
|
35
|
+
# Dependencies: bash 4+, jq, python3 (for ISO-8601 parsing — portable across
|
|
36
|
+
# Linux/BSD date implementations).
|
|
37
|
+
|
|
38
|
+
should_promote_outbound_responses_preflight() {
|
|
39
|
+
local repo_root="${1:-$PWD}"
|
|
40
|
+
local problems_dir="$repo_root/docs/problems"
|
|
41
|
+
local cache_file="$problems_dir/.outbound-responses-cache.json"
|
|
42
|
+
|
|
43
|
+
if [ ! -d "$problems_dir" ]; then
|
|
44
|
+
echo "no-back-link-tickets"
|
|
45
|
+
return 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Scan for `## Reported Upstream` back-link sections. Dual-tolerant per
|
|
49
|
+
# RFC-002: flat layout `<NNN>-*.<status>.md` AND per-state subdir layout
|
|
50
|
+
# `<status>/<NNN>-*.md`. Use grep -l (files-with-match); silent on
|
|
51
|
+
# zero matches via the `|| true` short-circuit.
|
|
52
|
+
local back_link_count
|
|
53
|
+
back_link_count="$(
|
|
54
|
+
grep -lE '^## Reported Upstream$' \
|
|
55
|
+
"$problems_dir"/[0-9][0-9][0-9]-*.md \
|
|
56
|
+
"$problems_dir"/*/[0-9][0-9][0-9]-*.md \
|
|
57
|
+
2>/dev/null | wc -l | tr -d ' '
|
|
58
|
+
)"
|
|
59
|
+
|
|
60
|
+
if [ "${back_link_count:-0}" -eq 0 ]; then
|
|
61
|
+
echo "no-back-link-tickets"
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [ ! -f "$cache_file" ]; then
|
|
66
|
+
echo "first-run-cache-absent"
|
|
67
|
+
return 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
local last_checked
|
|
71
|
+
last_checked="$(jq -r '.last_checked // ""' "$cache_file")"
|
|
72
|
+
|
|
73
|
+
if [ -z "$last_checked" ] || [ "$last_checked" = "null" ]; then
|
|
74
|
+
echo "first-run-last-checked-null"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
local ttl_seconds
|
|
79
|
+
ttl_seconds="$(jq -r '.ttl_seconds // 86400' "$cache_file")"
|
|
80
|
+
|
|
81
|
+
local last_checked_epoch now_epoch cache_age
|
|
82
|
+
last_checked_epoch="$(python3 -c "import datetime,sys; ts=sys.argv[1].replace('Z','+00:00'); print(int(datetime.datetime.fromisoformat(ts).timestamp()))" "$last_checked" 2>/dev/null || echo "0")"
|
|
83
|
+
now_epoch="$(date +%s)"
|
|
84
|
+
cache_age=$((now_epoch - last_checked_epoch))
|
|
85
|
+
|
|
86
|
+
if [ "$cache_age" -gt "$ttl_seconds" ]; then
|
|
87
|
+
echo "ttl-expiry age=${cache_age}s ttl=${ttl_seconds}s"
|
|
88
|
+
return 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo "fresh-within-ttl"
|
|
92
|
+
return 0
|
|
93
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Enumerate Known Error problem tickets whose Release-vehicle citation
|
|
3
|
+
# points at a just-shipped changeset — input to the work-problems
|
|
4
|
+
# Step 6.5 Post-release K→V auto-transition callback (P228).
|
|
5
|
+
#
|
|
6
|
+
# Driver: ADR-022 prescribes K→V transition on release, but until P228
|
|
7
|
+
# there was no auto-fire surface to back-fill the transition once a fix
|
|
8
|
+
# ships. Iter subprocesses defer K→V to "the next session" citing a
|
|
9
|
+
# misapplied P143 amendment, and tickets accumulate in
|
|
10
|
+
# `docs/problems/known-error/` with `## Fix Released` populated but no
|
|
11
|
+
# transition. The 2026-06-08 P220 empirical witness confirmed the gap.
|
|
12
|
+
#
|
|
13
|
+
# Composes with `wr-itil-derive-release-vehicle` (P267) — the derive
|
|
14
|
+
# helper's exit-code contract is the filter:
|
|
15
|
+
# exit 0 → changeset has been deleted from the working tree AND has a
|
|
16
|
+
# deletion commit in git history (released to npm). Emit as
|
|
17
|
+
# KV_CANDIDATE.
|
|
18
|
+
# exit 2 → no `.changeset/<name>.md` reference in ticket body
|
|
19
|
+
# (legacy ticket pre-P330). Skip silently.
|
|
20
|
+
# exit 3 → changeset still present in working tree (unreleased).
|
|
21
|
+
# Skip silently.
|
|
22
|
+
# other → log to stderr, skip.
|
|
23
|
+
#
|
|
24
|
+
# Source this file, then call `enumerate_postrelease_kv_candidates`:
|
|
25
|
+
# . packages/itil/lib/enumerate-postrelease-kv-candidates.sh
|
|
26
|
+
# enumerate_postrelease_kv_candidates "$PWD/docs/problems" \
|
|
27
|
+
# "wr-itil-derive-release-vehicle"
|
|
28
|
+
#
|
|
29
|
+
# Stdout (multi-line):
|
|
30
|
+
# KV_CANDIDATE: P<NNN> | <changeset-path>
|
|
31
|
+
# ...
|
|
32
|
+
# KV_CANDIDATES_SUMMARY: total=<N>
|
|
33
|
+
#
|
|
34
|
+
# Exit: 0 always (idempotent — safe to invoke when the directory is
|
|
35
|
+
# empty or absent). Stderr carries non-fatal warnings.
|
|
36
|
+
#
|
|
37
|
+
# Glob — targets `docs/problems/known-error/*.md` directly (per-state
|
|
38
|
+
# subdir layout per ADR-031). Flat-layout tickets are NOT in scope — the
|
|
39
|
+
# adopter has already migrated when this callback fires (work-problems
|
|
40
|
+
# Step 0a auto-migrate runs before Step 6.5).
|
|
41
|
+
#
|
|
42
|
+
# Cross-references:
|
|
43
|
+
# @problem P228 (K→V auto-transition gap)
|
|
44
|
+
# @problem P220 (empirical witness)
|
|
45
|
+
# @problem P267 (derive-release-vehicle composed helper)
|
|
46
|
+
# @problem P330 (Release vehicle seed reference — input signal)
|
|
47
|
+
# @adr ADR-022 (Verifying lifecycle)
|
|
48
|
+
# @adr ADR-018 (release-cadence host of the callback)
|
|
49
|
+
# @adr ADR-031 (per-state subdir layout — glob target)
|
|
50
|
+
# @adr ADR-049 (bin/ PATH shim — adopter-safe script resolution)
|
|
51
|
+
# @adr ADR-005 (behavioural bats per P081)
|
|
52
|
+
# @jtbd JTBD-006 (Progress the Backlog While I'm Away — primary driver)
|
|
53
|
+
# @jtbd JTBD-001 (Enforce Governance Without Slowing Down — audit trail)
|
|
54
|
+
|
|
55
|
+
enumerate_postrelease_kv_candidates() {
|
|
56
|
+
local problems_dir="${1:-docs/problems}"
|
|
57
|
+
local derive_cmd="${2:-wr-itil-derive-release-vehicle}"
|
|
58
|
+
local kedir="$problems_dir/known-error"
|
|
59
|
+
local total=0
|
|
60
|
+
|
|
61
|
+
if [ ! -d "$kedir" ]; then
|
|
62
|
+
printf 'KV_CANDIDATES_SUMMARY: total=0\n'
|
|
63
|
+
return 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
local saved_nullglob
|
|
67
|
+
saved_nullglob="$(shopt -p nullglob)"
|
|
68
|
+
shopt -s nullglob
|
|
69
|
+
|
|
70
|
+
local f
|
|
71
|
+
for f in "$kedir"/*.md; do
|
|
72
|
+
local bn
|
|
73
|
+
bn="$(basename "$f")"
|
|
74
|
+
[ "$bn" = "README.md" ] && continue
|
|
75
|
+
|
|
76
|
+
local nnn
|
|
77
|
+
nnn="$(printf '%s\n' "$bn" | grep -oE '^[0-9]+' | head -1)"
|
|
78
|
+
[ -z "$nnn" ] && continue
|
|
79
|
+
|
|
80
|
+
local derive_out derive_exit
|
|
81
|
+
derive_out="$("$derive_cmd" "$nnn" "$problems_dir" 2>/dev/null)"
|
|
82
|
+
derive_exit=$?
|
|
83
|
+
|
|
84
|
+
case "$derive_exit" in
|
|
85
|
+
0)
|
|
86
|
+
local changeset
|
|
87
|
+
changeset="$(printf '%s\n' "$derive_out" \
|
|
88
|
+
| grep -oE '\.changeset/[a-z0-9._-]+\.md' \
|
|
89
|
+
| head -1)"
|
|
90
|
+
printf 'KV_CANDIDATE: P%03d | %s\n' "$((10#$nnn))" "$changeset"
|
|
91
|
+
total=$((total + 1))
|
|
92
|
+
;;
|
|
93
|
+
2|3)
|
|
94
|
+
;;
|
|
95
|
+
*)
|
|
96
|
+
printf 'enumerate-postrelease-kv-candidates: derive helper exit=%d for P%03d (skipped)\n' \
|
|
97
|
+
"$derive_exit" "$((10#$nnn))" >&2
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
done
|
|
101
|
+
|
|
102
|
+
eval "$saved_nullglob"
|
|
103
|
+
|
|
104
|
+
printf 'KV_CANDIDATES_SUMMARY: total=%d\n' "$total"
|
|
105
|
+
return 0
|
|
106
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windyroad/itil",
|
|
3
|
-
"version": "0.47.12-preview.
|
|
3
|
+
"version": "0.47.12-preview.617",
|
|
4
4
|
"description": "ITIL-aligned IT service management for Claude Code (problem, and future incident/change skills)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"windyroad-itil": "./bin/install.mjs"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wr-itil — echo the outbound-responses-preflight promotion reason, or empty
|
|
3
|
+
# if promotion is not warranted (P220 + P317/RFC-009).
|
|
4
|
+
#
|
|
5
|
+
# Adopter-safe wrapper: sources the canonical lib RELATIVE TO THIS SCRIPT
|
|
6
|
+
# (`$(dirname)/../lib`), then echoes the function's result on stdout (the SKILL
|
|
7
|
+
# captures it via `$(...)`). Replaces any SKILL-inline
|
|
8
|
+
# `source packages/itil/lib/check-outbound-responses-staleness.sh;
|
|
9
|
+
# preflight_reason="$(should_promote_outbound_responses_preflight "$PWD")"`,
|
|
10
|
+
# which only resolves in the source monorepo (P317). SKILLs invoke this by name
|
|
11
|
+
# via the `wr-itil-check-outbound-responses-staleness` PATH shim (ADR-049).
|
|
12
|
+
# Operates on the directory given as $1 (defaults to $PWD).
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
|
|
15
|
+
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")/../lib" 2>/dev/null && pwd)" || {
|
|
16
|
+
echo "wr-itil-check-outbound-responses-staleness: cannot locate lib next to the script" >&2
|
|
17
|
+
exit 2
|
|
18
|
+
}
|
|
19
|
+
# shellcheck source=/dev/null
|
|
20
|
+
source "$LIB/check-outbound-responses-staleness.sh"
|
|
21
|
+
should_promote_outbound_responses_preflight "${1:-$PWD}"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# wr-itil — emit `KV_CANDIDATE` lines for Known Error tickets whose
|
|
3
|
+
# Release-vehicle citation matches a just-shipped (deleted-from-tree)
|
|
4
|
+
# changeset (P228 / P317 RFC-009 adopter-safe).
|
|
5
|
+
#
|
|
6
|
+
# Adopter-safe wrapper: sources the canonical lib RELATIVE TO THIS SCRIPT
|
|
7
|
+
# (`$(dirname)/../lib`), then invokes the function. The SKILL parses the
|
|
8
|
+
# emitted lines via `$(...)`. Mirrors the
|
|
9
|
+
# `run-check-deferred-placeholder-staleness.sh` precedent — never `source
|
|
10
|
+
# packages/...` repo-relative from a SKILL; those paths only resolve in
|
|
11
|
+
# the source monorepo, not adopter installs.
|
|
12
|
+
#
|
|
13
|
+
# SKILLs invoke this by name via the
|
|
14
|
+
# `wr-itil-enumerate-postrelease-kv-candidates` PATH shim (ADR-049 +
|
|
15
|
+
# ADR-080).
|
|
16
|
+
#
|
|
17
|
+
# Arguments (positional, all optional):
|
|
18
|
+
# $1 — problems-dir (defaults to ./docs/problems)
|
|
19
|
+
# $2 — derive-helper command name (defaults to
|
|
20
|
+
# `wr-itil-derive-release-vehicle`)
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
|
|
23
|
+
LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")/../lib" 2>/dev/null && pwd)" || {
|
|
24
|
+
echo "wr-itil-enumerate-postrelease-kv-candidates: cannot locate lib next to the script" >&2
|
|
25
|
+
exit 2
|
|
26
|
+
}
|
|
27
|
+
# shellcheck source=/dev/null
|
|
28
|
+
source "$LIB/enumerate-postrelease-kv-candidates.sh"
|
|
29
|
+
enumerate_postrelease_kv_candidates "$@"
|
|
@@ -37,7 +37,7 @@ Out of scope:
|
|
|
37
37
|
[--force-recheck] ignore cache; treat all as new
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
`/wr-itil:work-problems` Step 0d pre-flights this skill when the outbound-responses cache is stale or missing AND back-link tickets exist (sibling to Step 0b inbound cache check per ADR-062 Confirmation #5; P220 closed the cadence gap). Direct user invocation remains a first-class surface.
|
|
41
41
|
|
|
42
42
|
## AFK behaviour
|
|
43
43
|
|
|
@@ -101,11 +101,13 @@ If the cumulative pipeline risk lands above appetite and `AskUserQuestion` is un
|
|
|
101
101
|
Three invocation surfaces:
|
|
102
102
|
|
|
103
103
|
1. **Direct user invocation** — `/wr-itil:check-upstream-responses` (or with flags). The default user-facing surface.
|
|
104
|
-
2. **AFK orchestrator pre-flight**
|
|
104
|
+
2. **AFK orchestrator pre-flight** — `/wr-itil:work-problems` Step 0d invokes this skill when the outbound-responses cache is stale, missing, or has `last_checked: null` AND back-link tickets exist, mirroring Step 0b's inbound staleness check per ADR-062 Confirmation #5. Cache TTL defaults to 86400s (24h) symmetric with the inbound axis. P220 closed the cadence gap.
|
|
105
105
|
3. **Manual investigation during a problem-management session** — when a maintainer wants to see if any upstream reports moved before transitioning a `verifying` ticket back to `closed`. Foreground synchronous; the maintainer reads the inline summary and decides next steps.
|
|
106
106
|
|
|
107
107
|
## Confirmation
|
|
108
108
|
|
|
109
|
+
<!-- OUTBOUND-RESPONSES-STALENESS-CONTRACT-SOURCE: packages/itil/skills/check-upstream-responses/SKILL.md ## Confirmation -->
|
|
110
|
+
|
|
109
111
|
This skill's contract holds when:
|
|
110
112
|
|
|
111
113
|
1. The script `packages/itil/scripts/check-upstream-responses.sh` is read-only externally — only `gh issue view` (no `gh issue comment`, no `gh issue create`, no `gh api`).
|
|
@@ -114,6 +116,7 @@ This skill's contract holds when:
|
|
|
114
116
|
4. After a successful pass, the audit-log file exists and has a new `## YYYY-MM-DDTHH:MM:SSZ` heading appended.
|
|
115
117
|
5. The skill is AFK-safe: zero `AskUserQuestion` calls, zero external-comms gate triggers.
|
|
116
118
|
6. The exit code distinguishes success (0), error (1), and partial failure (2) so AFK orchestrators can branch correctly.
|
|
119
|
+
7. **Staleness contract (P220)**: TTL defaults to 86400s (24h) symmetric with the inbound axis. Override via `ttl_seconds` field in the cache file. The Step 0d pre-flight helper at `packages/itil/lib/check-outbound-responses-staleness.sh` MUST stay symmetric with this contract — any change to TTL semantics MUST update the helper, the Step 0d SKILL block in `/wr-itil:work-problems`, and this Confirmation #7 in the same commit.
|
|
117
120
|
|
|
118
121
|
## ADR alignment
|
|
119
122
|
|
|
@@ -204,12 +204,36 @@ Write the polled results to `docs/problems/.upstream-cache.json`, updating `last
|
|
|
204
204
|
|
|
205
205
|
For each fresh report (not present in the prior cache snapshot under the same `body_hash`), invoke P070's semantic-comparator infrastructure (the same comparator used by `/wr-itil:report-upstream` outbound dedup, per ADR-062 § Reassessment composes-with).
|
|
206
206
|
|
|
207
|
-
**Semantic-comparator hit** → record `matched_local_ticket: P<NNN>` on the cache entry AND post a gated `gh issue comment`
|
|
207
|
+
**Semantic-comparator hit** → record `matched_local_ticket: P<NNN>` on the cache entry AND post a gated `gh issue comment` carrying the **duplicate** verdict per the JTBD-301 contract briefed at `#### 4.5e-comment-shape` below. The matched-local-ticket cross-reference comment template is the one named below at 4.5e-comment-shape § Branch templates → "Duplicate verdict (matched-local-ticket)". Do NOT post the legacy boilerplate *"Tracked locally as `docs/problems/<state>/<NNN>-<title>.md`"* — that was the P229 leak shape (framework-vocab path; bureaucratic; no verdict). The acknowledgement comment fires through the external-comms gate (P064 risk + P038 voice-tone per ADR-028 amended). This comment is the JTBD-301 acknowledgement that the report has been received and routed; silent-skip on matched-local-ticket would break the contract per ADR-062 § Decision Drivers row 1 (every submitted report receives a verdict, even if the verdict is "duplicate of P<NNN>").
|
|
208
208
|
|
|
209
209
|
**Semantic-comparator ambiguity** (multiple plausible matches) → annotate `cache_audit_note: ambiguous-match-candidates-P<X>-P<Y>-...` and DO NOT auto-route. The ambiguity surfaces at the next interactive `review-problems` invocation (the maintainer disambiguates from the cache_audit_note channel; this is the documented user-attention surface under the mechanical-stage carve-out).
|
|
210
210
|
|
|
211
211
|
**No comparator hit** → continue to 4.5e.
|
|
212
212
|
|
|
213
|
+
#### 4.5e-comment-shape — JTBD-301 verdict-shape contract (P229)
|
|
214
|
+
|
|
215
|
+
The four ack-comment branches below (4.5d cross-reference + 4.5e steps 4 / 5 / 6) post **reporter-facing** comments on the upstream surface (`gh issue comment <id> --repo <owner>/<repo>`). Per JTBD-301 Desired Outcome row 6 — *"Submitted reports receive a predictable acknowledgement: labelled, routed into the maintainers' problem-management queue, and eventually responded to with a verdict (fix released / parked / duplicate / won't-fix)"* — every ack body MUST carry a **verdict** in **plain-language** so the reporter (plugin-user persona — low context on repo internals) can act on it without reading ADRs or source.
|
|
216
|
+
|
|
217
|
+
**Symmetry with `/wr-itil:report-upstream` (ADR-024 / ADR-036)**: outbound report bodies use structured human-language (e.g. report-upstream § Step 5 structured-default body shape). The inbound ack mirrors the same shape: human-language verdict + actionable expectation + no maintainer-internal jargon. The two surfaces (outbound report, inbound ack) are the two sides of one trust contract; both ride the same external-comms gate (ADR-028) and the same persona constraints.
|
|
218
|
+
|
|
219
|
+
**Anti-leakage rule (load-bearing)**: comment bodies MUST NOT contain **maintainer-internal framework vocab** — Step IDs (e.g. "Step 4.5e"), branch names (e.g. "safe-and-valid branch"), classification tokens (e.g. "safe-low-fix-risk", "out-of-scope-for-documented-personas"), or path syntax (e.g. `docs/problems/<state>/<NNN>-<title>.md`). The maintainer-internal vocabulary belongs in the **audit-log surface** (4.5f — `docs/audits/inbound-discovery-log.md`) and the **cache entry classification** (4.5g — `docs/problems/.upstream-cache.json`), not in user-facing comments. The P229 root-cause leak — *"classified via /wr-itil:review-problems Step 4.5e safe-and-valid branch with safe-low-fix-risk"* posted across 31 ack comments — is the canonical anti-pattern this contract forbids. Architect Issue C2: **the external-comms gate fires on the substituted body, not on the template** — when a template author substitutes `P<NNN>`, `<reason>`, `<classification>`, `<plugin>` etc., they MUST ensure the substituted values stay plain-language (e.g. plain-language gloss for `<classification>`, not raw verdict token). Substitution-time leak is the residual failure mode the gate cannot catch at template-author time.
|
|
220
|
+
|
|
221
|
+
**Verdict vocabulary (JTBD-301 four-verdict + fifth implicit)**:
|
|
222
|
+
|
|
223
|
+
| Verdict | Branch | When | Plain-language ack |
|
|
224
|
+
|---|---|---|---|
|
|
225
|
+
| **fix released** | (future — `/wr-itil:transition-problem` Known Error → Verifying, separate surface) | when the local ticket transitions to Verifying | (existing transition-time comment; out of scope for this contract — surfaces verdict at fix-ship time) |
|
|
226
|
+
| **accepted into backlog** | 4.5e Step 6 (safe-and-valid) | report passes JTBD-alignment + dual-axis risk; local ticket created | "Thanks for the report. We're tracking this as a real bug — local ticket P<NNN>. The fix will ship in a future release of `@windyroad/<plugin>`; we don't have a firm date yet. Watch this issue for updates, or check our [release notes](<link>) — we'll comment here when the fix lands." (Architect C3: the **fix released** JTBD-301 verdict is post-release; this branch fires at accept-into-backlog time. Name the branch **accepted into backlog**; the **fix released** verdict surfaces later at the Known Error → Verifying transition.) |
|
|
227
|
+
| **duplicate** | 4.5d (matched-local-ticket cross-reference) | semantic-comparator hits a local ticket | "Thanks for the report. We're tracking this as a duplicate of P<NNN> — see that ticket for the verdict trail. Future updates will appear on this issue when the local ticket transitions." |
|
|
228
|
+
| **won't-fix** | 4.5e Step 4 (above-threshold-pushback) | JTBD-not-aligned OR above-threshold Request-risk | "We don't plan to fix this — here's why: <plain-language reason>. We track problems labelled `problem` in this repo; this report falls outside that scope. The issue stays open for your reference; close it whenever you're done." |
|
|
229
|
+
| **policy-violation close** (fifth implicit verdict — architect C4) | 4.5e Step 5 (clear-malicious) | clear-malicious-request classifier hit | "We're closing this report. Reason: <plain-language gloss of the policy class — e.g. 'this matches behaviour we've classified as spam / off-topic / disclosure-bypass'>, not the raw `wr-risk-scorer:inbound-report` verdict token. If you believe this is a mistake, please file a new report describing what you're trying to accomplish." |
|
|
230
|
+
|
|
231
|
+
The first four (fix-released / accepted-into-backlog / duplicate / won't-fix) map to JTBD-301's four documented verdicts. The fifth (policy-violation close) is a **stronger** verdict than won't-fix (it's an immediate close, not "we considered it and declined") and is named here precisely so the SKILL prose doesn't conflate clear-malicious with won't-fix. clear-malicious is a maintainer-side intervention; won't-fix is a deliberation outcome. The reporter sees different verdict language for the two cases.
|
|
232
|
+
|
|
233
|
+
**Maintainer-side audit-log unchanged**: the classification tokens (`safe-and-valid-local-ticket-created`, `above-threshold-pushback`, `clear-malicious-closed`, `matched-local-ticket`) remain on the audit-log surface (4.5f) and on the cache-entry `Classification` column (4.5g) verbatim per ADR-062 § Audit-log surface shape — only the reporter-facing comment body strips them. This preserves replay determinism for the inbound-discovery audit trail; the user-side template change is reporter-facing only.
|
|
234
|
+
|
|
235
|
+
Architect Issue A1 / behavioural bats: each branch template MUST preserve its existing **gate-denial sub-branch** (`cache_audit_note: gate-denied-<branch>`). The verdict-shape rewrite is body-content only; the gate-denial fall-through wiring stays intact across all four branches so JTBD-301 acknowledgement is preserved on the next discovery pass when the gate denies.
|
|
236
|
+
|
|
213
237
|
#### 4.5e. Six-step assessment pipeline
|
|
214
238
|
|
|
215
239
|
For each unmatched fresh report, run these steps in order; record the outcome in the cache + audit-log.
|
|
@@ -227,11 +251,11 @@ For each unmatched fresh report, run these steps in order; record the outcome in
|
|
|
227
251
|
- `clear-malicious-request` → step 5 (clear-malicious branch).
|
|
228
252
|
- `above-threshold-risk` → step 4 (above-threshold-pushback branch).
|
|
229
253
|
|
|
230
|
-
4. **Above-threshold-pushback branch**: post a gated `gh issue comment`
|
|
254
|
+
4. **Above-threshold-pushback branch (won't-fix verdict)**: post a gated `gh issue comment` carrying the **won't-fix verdict** per the 4.5e-comment-shape contract above — body uses the "We don't plan to fix this — here's why: <reason>" template. The `<reason>` substitution MUST be a plain-language gloss (e.g. *"this request would require us to expose internal-only API surface area we deliberately don't expose"*, not the raw `out-of-scope-for-documented-personas` token). The raw token belongs on the maintainer-side audit-log (4.5f), not in the reporter-facing comment body. Comment fires through the external-comms gate (P064 + P038 evaluators per ADR-028 amended). Upstream issue is NOT closed by the pipeline — maintainer decides closure manually after reading the pushback. Cache entry classification: `above-threshold-pushback` (maintainer-internal token; remains on cache + audit-log surface). Audit-log append. **Gate-denial sub-branch**: if the external-comms gate denies the comment write (either evaluator FAILs), record `cache_audit_note: gate-denied-pushback` and continue to the next report.
|
|
231
255
|
|
|
232
|
-
5. **Clear-malicious branch**: post a brief gated verdict comment
|
|
256
|
+
5. **Clear-malicious branch (policy-violation close verdict — fifth implicit verdict per 4.5e-comment-shape table)**: post a brief gated verdict comment per the 4.5e-comment-shape contract — body uses the "We're closing this report. Reason: <plain-language gloss>" template. The `<plain-language gloss>` MUST translate the `wr-risk-scorer:inbound-report` verdict into reporter-readable language (e.g. *"spam / off-topic / disclosure-bypass"*, not the raw verdict-class token). Architect C4: name this verdict as **policy-violation close**, NOT won't-fix — clear-malicious is a stronger close than won't-fix (immediate close vs. deliberated decline). The classification gloss is a load-bearing anti-leakage requirement (JTBD non-blocking advisory): the SKILL contract explicitly forbids substituting the raw `wr-risk-scorer:inbound-report` verdict token into the reporter-facing body. The raw token still ships to the maintainer-side audit-log surface (4.5f) verbatim per ADR-062 § Audit-log surface shape. JTBD-301 acknowledgement contract — silent close is forbidden per ADR-062 Decision Drivers row 1. External-comms gates ride. Then close the upstream issue via `gh issue close <id>`. Append the reporter handle + classification to `docs/audits/inbound-discovery-log.md` for P123 block-list consumption when that ticket lands. Cache entry classification: `clear-malicious-closed`. **Gate-denial sub-branch**: if the verdict-comment gate denies, record `cache_audit_note: gate-denied-clear-malicious-pre-close` and do NOT close the upstream issue (silent close is forbidden — preserve the report for the next pass).
|
|
233
257
|
|
|
234
|
-
6. **Safe-and-valid branch**: invoke `/wr-itil:capture-problem --no-prompt <report-body-verbatim>` to create the local ticket. The `--no-prompt` flag defaults to `type=technical`; the maintainer re-classifies at next interactive `review-problems` re-rate. **Stamp the inbound origin (ADR-076)**: the skeleton writes `**Origin**: internal` by default — Edit it on the freshly-created ticket to `**Origin**: inbound-reported (#<id>)` (the upstream issue/discussion `<id>` polled this pass) so the ADR-076 reported-first tier ranks it ahead of internal tickets. The on-ticket `**Origin**` field, not the regenerable cache, is the authoritative rank input (ADR-076); the cache's `matched_local_ticket` remains the audit/replay record. Rationale: a default of `user-business` would mis-classify security-advisory-channel reports as user-business when they're often deep technical bugs; the maintainer-re-classify path is the safety net. Verbatim body preservation honors JTBD-301 persona constraint "capture context faithfully without cognitive re-shaping" and JTBD-201 audit-trail fidelity. Then post a gated `gh issue comment`
|
|
258
|
+
6. **Safe-and-valid branch (accepted-into-backlog verdict)**: invoke `/wr-itil:capture-problem --no-prompt <report-body-verbatim>` to create the local ticket. The `--no-prompt` flag defaults to `type=technical`; the maintainer re-classifies at next interactive `review-problems` re-rate. **Stamp the inbound origin (ADR-076)**: the skeleton writes `**Origin**: internal` by default — Edit it on the freshly-created ticket to `**Origin**: inbound-reported (#<id>)` (the upstream issue/discussion `<id>` polled this pass) so the ADR-076 reported-first tier ranks it ahead of internal tickets. The on-ticket `**Origin**` field, not the regenerable cache, is the authoritative rank input (ADR-076); the cache's `matched_local_ticket` remains the audit/replay record. Rationale: a default of `user-business` would mis-classify security-advisory-channel reports as user-business when they're often deep technical bugs; the maintainer-re-classify path is the safety net. Verbatim body preservation honors JTBD-301 persona constraint "capture context faithfully without cognitive re-shaping" and JTBD-201 audit-trail fidelity. Then post a gated `gh issue comment` carrying the **accepted-into-backlog verdict** per the 4.5e-comment-shape contract above — body uses the "Thanks for the report. We're tracking this as a real bug — local ticket P<NNN>. The fix will ship in a future release of `@windyroad/<plugin>`; we don't have a firm date yet. Watch this issue for updates, or check our [release notes](<link>) — we'll comment here when the fix lands." template. The `<plugin>` substitution is the package name (e.g. `itil`, `architect`, `retrospective`); the `<link>` substitution is the package's CHANGELOG URL on npm or GitHub. Do NOT include the legacy P229 leak phrasing *"classified via /wr-itil:review-problems Step 4.5e safe-and-valid branch with safe-low-fix-risk"* in the comment body — that's the canonical anti-leakage failure mode (see 4.5e-comment-shape above). The token `safe-low-fix-risk` belongs to the maintainer-side audit-log only and the cache-entry classification column (4.5g), not in user-facing comments. Cache entry classification: `safe-and-valid-local-ticket-created`; populate `matched_local_ticket: P<NNN>` with the freshly-allocated ID. **Gate-denial sub-branch**: if the acknowledgement comment gate denies, the local ticket already exists — record `cache_audit_note: gate-denied-safe-and-valid-acknowledgement` and continue. The acknowledgement comment will retry on the next discovery pass.
|
|
235
259
|
|
|
236
260
|
#### 4.5f. Audit-log append
|
|
237
261
|
|