@windyroad/retrospective 0.24.1 → 0.25.0
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.
|
@@ -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/check-autocreate-rfc-scope.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/check-autocreate-rfc-scope.sh" "$@"
|
package/package.json
CHANGED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# packages/retrospective/scripts/check-autocreate-rfc-scope.sh
|
|
3
|
+
#
|
|
4
|
+
# Diagnose-only advisory script for ADR-073 (Fix-time gate auto-creates a
|
|
5
|
+
# missing RFC). ADR-073's Reassessment Criteria say to revisit the
|
|
6
|
+
# auto-create decision "if auto-created RFCs are systematically under-scoped
|
|
7
|
+
# (a recurring 'the auto-RFC didn't capture the real fix' signal)". The I13
|
|
8
|
+
# fix-time gate auto-creates problem-traced *skeleton* RFCs via
|
|
9
|
+
# /wr-itil:capture-rfc whenever a Known Error has no RFC trace; the
|
|
10
|
+
# auto-create event is otherwise only logged ephemerally to the work-problems
|
|
11
|
+
# iter-summary `notes`. This detector makes the reassessment signal durable
|
|
12
|
+
# and observable: it reads the auto-created RFCs left on disk and surfaces the
|
|
13
|
+
# ones whose traced problem's fix has already shipped while the RFC scope was
|
|
14
|
+
# never fleshed out — exactly the under-scoped population. RFC-005 B9 wires it
|
|
15
|
+
# into /wr-retrospective:run-retro Step 2b as an advisory sub-block (mirroring
|
|
16
|
+
# the README inventory-currency advisory, ADR-069/P294).
|
|
17
|
+
#
|
|
18
|
+
# An RFC is a SKELETON when its `## Scope` section still carries the
|
|
19
|
+
# capture-rfc placeholder `(deferred — populate at /wr-itil:manage-rfc
|
|
20
|
+
# accepted transition)` (capture-rfc/SKILL.md skeleton contract).
|
|
21
|
+
#
|
|
22
|
+
# A skeleton RFC is UNDER-SCOPED (a reassessment candidate) when its
|
|
23
|
+
# `problems:` frontmatter traces at least one FIX-SHIPPED problem, where
|
|
24
|
+
# fix-shipped means any of (broadened so the gate-on-next-touch population —
|
|
25
|
+
# fixes shipped before the gate existed, recorded as `## Fix Strategy` in a
|
|
26
|
+
# `known-error/` ticket — is caught; the canonical P361/RFC-026 dogfood case):
|
|
27
|
+
# - the problem file lives under <problems-dir>/verifying/ or .../closed/, OR
|
|
28
|
+
# - the problem body has a `## Fix Released` heading, OR
|
|
29
|
+
# - the problem body has a POPULATED `## Fix Strategy` section (heading with
|
|
30
|
+
# non-blank body content — a bare stub heading does not count). Fix-
|
|
31
|
+
# *proposed* is sufficient for the under-scoped signal: the RFC should have
|
|
32
|
+
# been fleshed out the moment a fix was scoped (ADR-073 Consequences — the
|
|
33
|
+
# RFC body is fleshed out as fix work proceeds).
|
|
34
|
+
#
|
|
35
|
+
# Output (one line per under-scoped candidate, then a trailing summary):
|
|
36
|
+
# AUTOCREATE-RFC under-scoped rfc=<RFC-NNN> problems=<csv> shipped=<pid:basis>
|
|
37
|
+
# TOTAL proposed_skeletons=<N> under_scoped=<K>
|
|
38
|
+
# where <basis> is one of: verifying | closed | fix-released | fix-strategy.
|
|
39
|
+
#
|
|
40
|
+
# Exit codes:
|
|
41
|
+
# 0 = always (advisory only — count is signal, not failure; ADR-013 Rule 6)
|
|
42
|
+
# 2 = parse error (rfcs-dir missing or unreadable)
|
|
43
|
+
#
|
|
44
|
+
# Usage:
|
|
45
|
+
# check-autocreate-rfc-scope.sh [<rfcs-dir>] [<problems-dir>]
|
|
46
|
+
# Defaults:
|
|
47
|
+
# <rfcs-dir> = docs/rfcs <problems-dir> = docs/problems
|
|
48
|
+
#
|
|
49
|
+
# @problem P314 (rework the fix-time RFC-trace gate — RFC-005 B9)
|
|
50
|
+
# @adr ADR-073 (fix-time gate auto-creates a missing RFC — this script's
|
|
51
|
+
# normative source; its Reassessment Criteria are what B9 wires)
|
|
52
|
+
# @adr ADR-060 (Problem-RFC-Story framework; I13 fix-time trace invariant)
|
|
53
|
+
# @adr ADR-013 Rule 6 (non-interactive fail-safe — advisory script never blocks)
|
|
54
|
+
# @adr ADR-040 (declarative-first / advisory-then-escalate precedent)
|
|
55
|
+
# @adr ADR-049 (bin/-on-PATH script resolution — paired
|
|
56
|
+
# wr-retrospective-check-autocreate-rfc-scope shim)
|
|
57
|
+
# @adr ADR-052 / P081 (behavioural tests via bats; no structural-grep on source)
|
|
58
|
+
|
|
59
|
+
set -uo pipefail
|
|
60
|
+
|
|
61
|
+
RFCS_DIR="${1:-docs/rfcs}"
|
|
62
|
+
PROBLEMS_DIR="${2:-docs/problems}"
|
|
63
|
+
|
|
64
|
+
# The capture-rfc skeleton placeholder (em-dash is U+2014, matching the
|
|
65
|
+
# capture-rfc skeleton contract and the live RFC-026 skeleton).
|
|
66
|
+
PLACEHOLDER='deferred — populate at /wr-itil:manage-rfc accepted transition'
|
|
67
|
+
|
|
68
|
+
# ── Pre-checks ──────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
if [ ! -d "$RFCS_DIR" ]; then
|
|
71
|
+
echo "check-autocreate-rfc-scope: rfcs dir not found: $RFCS_DIR" >&2
|
|
72
|
+
exit 2
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# ── Helpers ─────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
# Extract a markdown section body: lines after the `## <heading>` line up to
|
|
78
|
+
# (but excluding) the next `## ` heading. Heading matched whole-line.
|
|
79
|
+
section_body() {
|
|
80
|
+
local file="$1" heading="$2"
|
|
81
|
+
awk -v h="$heading" '
|
|
82
|
+
$0 ~ "^## " h "([[:space:]]*$)" { f=1; next }
|
|
83
|
+
/^## / && f { f=0 }
|
|
84
|
+
f { print }
|
|
85
|
+
' "$file"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# True when a section has non-blank body content beyond the skeleton
|
|
89
|
+
# placeholder — i.e. it is genuinely populated, not a bare stub heading.
|
|
90
|
+
section_populated() {
|
|
91
|
+
local file="$1" heading="$2"
|
|
92
|
+
section_body "$file" "$heading" \
|
|
93
|
+
| grep -qv -e '^[[:space:]]*$' -e "$PLACEHOLDER"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Echo a fix-shipped basis (verifying|closed|fix-released|fix-strategy) for a
|
|
97
|
+
# problem id, or empty when the problem is not fix-shipped / not found.
|
|
98
|
+
problem_fix_shipped() {
|
|
99
|
+
local pid="$1" num pfile
|
|
100
|
+
num="${pid#P}"
|
|
101
|
+
pfile="$(find "$PROBLEMS_DIR" -type f -name "${num}-*.md" 2>/dev/null | head -1)"
|
|
102
|
+
[ -n "$pfile" ] || { echo ""; return; }
|
|
103
|
+
case "$pfile" in
|
|
104
|
+
*/verifying/*) echo "verifying"; return ;;
|
|
105
|
+
*/closed/*) echo "closed"; return ;;
|
|
106
|
+
esac
|
|
107
|
+
if grep -qE '^## Fix Released([[:space:]]*$)' "$pfile"; then
|
|
108
|
+
echo "fix-released"; return
|
|
109
|
+
fi
|
|
110
|
+
if grep -qE '^## Fix Strategy([[:space:]]*$)' "$pfile" \
|
|
111
|
+
&& section_populated "$pfile" "Fix Strategy"; then
|
|
112
|
+
echo "fix-strategy"; return
|
|
113
|
+
fi
|
|
114
|
+
echo ""
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Echo space-separated problem ids from an RFC's `problems:` frontmatter.
|
|
118
|
+
rfc_problems() {
|
|
119
|
+
grep -m1 '^problems:' "$1" 2>/dev/null | grep -oE 'P[0-9]+'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# ── Scan proposed RFCs ──────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
total_skeletons=0
|
|
125
|
+
under_scoped=0
|
|
126
|
+
|
|
127
|
+
shopt -s nullglob
|
|
128
|
+
rfcs=("$RFCS_DIR"/*.proposed.md)
|
|
129
|
+
shopt -u nullglob
|
|
130
|
+
|
|
131
|
+
# Stable alphabetical order for deterministic output.
|
|
132
|
+
IFS=$'\n' rfcs=($(printf '%s\n' "${rfcs[@]}" | sort))
|
|
133
|
+
unset IFS
|
|
134
|
+
|
|
135
|
+
for rfc in "${rfcs[@]}"; do
|
|
136
|
+
[ -f "$rfc" ] || continue
|
|
137
|
+
|
|
138
|
+
# Skeleton = `## Scope` section still carries the placeholder.
|
|
139
|
+
if ! section_body "$rfc" "Scope" | grep -qF "$PLACEHOLDER"; then
|
|
140
|
+
continue
|
|
141
|
+
fi
|
|
142
|
+
total_skeletons=$(( total_skeletons + 1 ))
|
|
143
|
+
|
|
144
|
+
rfc_id="$(basename "$rfc" | grep -oE '^RFC-[0-9]+')"
|
|
145
|
+
|
|
146
|
+
pids="$(rfc_problems "$rfc")"
|
|
147
|
+
[ -n "$pids" ] || continue
|
|
148
|
+
|
|
149
|
+
csv=""
|
|
150
|
+
shipped_pid=""
|
|
151
|
+
shipped_basis=""
|
|
152
|
+
for pid in $pids; do
|
|
153
|
+
csv="${csv:+$csv,}$pid"
|
|
154
|
+
if [ -z "$shipped_pid" ]; then
|
|
155
|
+
basis="$(problem_fix_shipped "$pid")"
|
|
156
|
+
if [ -n "$basis" ]; then
|
|
157
|
+
shipped_pid="$pid"
|
|
158
|
+
shipped_basis="$basis"
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
done
|
|
162
|
+
|
|
163
|
+
if [ -n "$shipped_pid" ]; then
|
|
164
|
+
under_scoped=$(( under_scoped + 1 ))
|
|
165
|
+
echo "AUTOCREATE-RFC under-scoped rfc=$rfc_id problems=$csv shipped=$shipped_pid:$shipped_basis"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
|
|
169
|
+
echo "TOTAL proposed_skeletons=$total_skeletons under_scoped=$under_scoped"
|
|
170
|
+
exit 0
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
#
|
|
3
|
+
# packages/retrospective/scripts/test/check-autocreate-rfc-scope.bats
|
|
4
|
+
#
|
|
5
|
+
# Behavioural tests for `check-autocreate-rfc-scope.sh` — the ADR-073
|
|
6
|
+
# auto-create-RFC reassessment advisory (RFC-005 B9). The detector scans
|
|
7
|
+
# docs/rfcs/*.proposed.md for skeleton (capture-rfc placeholder-scope) RFCs
|
|
8
|
+
# whose `problems:` trace a fix-shipped problem — the "auto-created RFC
|
|
9
|
+
# under-scoped" signal ADR-073's Reassessment Criteria names. run-retro
|
|
10
|
+
# Step 2b surfaces the candidates so a human can assess whether auto-created
|
|
11
|
+
# RFCs are systematically under-scoped.
|
|
12
|
+
#
|
|
13
|
+
# Tests are behavioural per ADR-005 / ADR-052 / P081 — they exercise the
|
|
14
|
+
# script end-to-end against fixture docs/rfcs + docs/problems trees and
|
|
15
|
+
# assert on stdout / exit-code shape. No structural greps of the script
|
|
16
|
+
# source. Mirrors the sibling `check-readme-jtbd-currency.bats`.
|
|
17
|
+
#
|
|
18
|
+
# @problem P314 (rework the fix-time RFC-trace gate — RFC-005 B9 wires the
|
|
19
|
+
# ADR-073 reassessment criterion into run-retro Step 2b)
|
|
20
|
+
# @problem P081 (Structural-content tests are wasteful — behavioural preferred)
|
|
21
|
+
# @adr ADR-073 (fix-time gate auto-creates a missing RFC; Reassessment
|
|
22
|
+
# Criteria: revisit if auto-created RFCs systematically under-scoped)
|
|
23
|
+
# @adr ADR-013 Rule 6 (non-interactive fail-safe — advisory script never blocks)
|
|
24
|
+
# @adr ADR-040 (declarative-first / advisory-then-escalate precedent)
|
|
25
|
+
# @adr ADR-005 / ADR-052 (behavioural tests via bats)
|
|
26
|
+
|
|
27
|
+
SCRIPT="${BATS_TEST_DIRNAME}/../check-autocreate-rfc-scope.sh"
|
|
28
|
+
|
|
29
|
+
# The capture-rfc skeleton placeholder string (em-dash is U+2014, matching
|
|
30
|
+
# capture-rfc/SKILL.md and the live RFC-026 skeleton).
|
|
31
|
+
PLACEHOLDER='(deferred — populate at /wr-itil:manage-rfc accepted transition)'
|
|
32
|
+
|
|
33
|
+
setup() {
|
|
34
|
+
TEST_DIR="$(mktemp -d)"
|
|
35
|
+
RFCS_DIR="$TEST_DIR/rfcs"
|
|
36
|
+
PROBLEMS_DIR="$TEST_DIR/problems"
|
|
37
|
+
mkdir -p "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
teardown() {
|
|
41
|
+
rm -rf "$TEST_DIR"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Write a proposed RFC. $1 = filename, $2 = problems-csv (e.g. "P361"),
|
|
45
|
+
# $3 = scope-body (use $PLACEHOLDER for a skeleton).
|
|
46
|
+
make_rfc() {
|
|
47
|
+
local fname="$1" probs="$2" scope="$3"
|
|
48
|
+
cat > "$RFCS_DIR/$fname" <<EOF
|
|
49
|
+
---
|
|
50
|
+
status: proposed
|
|
51
|
+
human-oversight: unconfirmed
|
|
52
|
+
problems: [$probs]
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# $fname
|
|
56
|
+
|
|
57
|
+
## Scope
|
|
58
|
+
|
|
59
|
+
$scope
|
|
60
|
+
|
|
61
|
+
## Tasks
|
|
62
|
+
|
|
63
|
+
- [ ] $PLACEHOLDER
|
|
64
|
+
EOF
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Write a problem ticket under a lifecycle subdir. $1 = subdir, $2 = num,
|
|
68
|
+
# $3 = body.
|
|
69
|
+
make_problem() {
|
|
70
|
+
local sub="$1" num="$2" body="$3"
|
|
71
|
+
mkdir -p "$PROBLEMS_DIR/$sub"
|
|
72
|
+
cat > "$PROBLEMS_DIR/$sub/$num-stub.md" <<EOF
|
|
73
|
+
# P$num stub ticket
|
|
74
|
+
|
|
75
|
+
$body
|
|
76
|
+
EOF
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# ── Pre-checks ──────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
@test "script file exists and is executable" {
|
|
82
|
+
[ -f "$SCRIPT" ]
|
|
83
|
+
[ -x "$SCRIPT" ]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@test "missing rfcs dir exits 2 with error message on stderr" {
|
|
87
|
+
run bash "$SCRIPT" "$TEST_DIR/does-not-exist" "$PROBLEMS_DIR"
|
|
88
|
+
[ "$status" -eq 2 ]
|
|
89
|
+
[[ "$output" == *"rfcs dir not found"* ]]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@test "empty rfcs dir exits 0 with a zeroed TOTAL summary" {
|
|
93
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
94
|
+
[ "$status" -eq 0 ]
|
|
95
|
+
[[ "$output" == *"TOTAL proposed_skeletons=0 under_scoped=0"* ]]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# ── Regression anchor: the P361/RFC-026 gate-on-next-touch shape ─────────────
|
|
99
|
+
# A known-error-located problem whose fix is recorded as a populated
|
|
100
|
+
# `## Fix Strategy` (NO `## Fix Released` heading), traced by a
|
|
101
|
+
# placeholder-scope RFC. The original (pre-review) predicate missed this.
|
|
102
|
+
|
|
103
|
+
@test "known-error problem with populated Fix Strategy (no Fix Released) is under-scoped" {
|
|
104
|
+
make_problem "known-error" "361" "## Fix Strategy
|
|
105
|
+
|
|
106
|
+
Implemented in \`packages/itil/scripts/derive-release-vehicle.sh\`. Full 15-test suite green; de-facto-released exit-0 branch added."
|
|
107
|
+
make_rfc "RFC-026-stub.proposed.md" "P361" "$PLACEHOLDER"
|
|
108
|
+
|
|
109
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
110
|
+
[ "$status" -eq 0 ]
|
|
111
|
+
[[ "$output" == *"AUTOCREATE-RFC under-scoped rfc=RFC-026"* ]]
|
|
112
|
+
[[ "$output" == *"problems=P361"* ]]
|
|
113
|
+
[[ "$output" == *"shipped=P361:fix-strategy"* ]]
|
|
114
|
+
[[ "$output" == *"TOTAL proposed_skeletons=1 under_scoped=1"* ]]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# ── verifying/ lifecycle-directory basis (the P215/RFC-021 shape) ───────────
|
|
118
|
+
|
|
119
|
+
@test "skeleton RFC tracing a verifying/ problem is under-scoped (directory basis)" {
|
|
120
|
+
make_problem "verifying" "215" "## Fix Strategy
|
|
121
|
+
|
|
122
|
+
Recovery directive added to the architect gate."
|
|
123
|
+
make_rfc "RFC-021-stub.proposed.md" "P215" "$PLACEHOLDER"
|
|
124
|
+
|
|
125
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
126
|
+
[ "$status" -eq 0 ]
|
|
127
|
+
[[ "$output" == *"AUTOCREATE-RFC under-scoped rfc=RFC-021"* ]]
|
|
128
|
+
[[ "$output" == *"shipped=P215:verifying"* ]]
|
|
129
|
+
[[ "$output" == *"TOTAL proposed_skeletons=1 under_scoped=1"* ]]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# ── Fix Released heading basis ──────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
@test "skeleton RFC tracing a problem with a Fix Released heading is under-scoped" {
|
|
135
|
+
make_problem "known-error" "500" "## Fix Released
|
|
136
|
+
|
|
137
|
+
Shipped in @windyroad/itil@1.2.3."
|
|
138
|
+
make_rfc "RFC-050-stub.proposed.md" "P500" "$PLACEHOLDER"
|
|
139
|
+
|
|
140
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
141
|
+
[ "$status" -eq 0 ]
|
|
142
|
+
[[ "$output" == *"AUTOCREATE-RFC under-scoped rfc=RFC-050"* ]]
|
|
143
|
+
[[ "$output" == *"shipped=P500:fix-released"* ]]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# ── Non-candidate: skeleton RFC tracing an open, no-fix problem ──────────────
|
|
147
|
+
# A legitimately-still-skeleton RFC — the fix has not been scoped yet, so the
|
|
148
|
+
# RFC SHOULD still be a skeleton. Counted in proposed_skeletons, NOT under_scoped.
|
|
149
|
+
|
|
150
|
+
@test "skeleton RFC tracing an open problem with no fix is NOT under-scoped" {
|
|
151
|
+
make_problem "open" "304" "## Symptoms
|
|
152
|
+
|
|
153
|
+
Duplicate-and-sync is fragile. No fix proposed yet."
|
|
154
|
+
make_rfc "RFC-023-stub.proposed.md" "P304" "$PLACEHOLDER"
|
|
155
|
+
|
|
156
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
157
|
+
[ "$status" -eq 0 ]
|
|
158
|
+
[[ "$output" != *"rfc=RFC-023"* ]]
|
|
159
|
+
[[ "$output" == *"TOTAL proposed_skeletons=1 under_scoped=0"* ]]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# ── Robustness: a bare/empty Fix Strategy heading is NOT populated ──────────
|
|
163
|
+
# (architect note — distinguish a populated section from a stub heading.)
|
|
164
|
+
|
|
165
|
+
@test "skeleton RFC tracing a problem with an EMPTY Fix Strategy heading is NOT under-scoped" {
|
|
166
|
+
make_problem "known-error" "600" "## Fix Strategy
|
|
167
|
+
|
|
168
|
+
## Root Cause Analysis
|
|
169
|
+
|
|
170
|
+
Some analysis."
|
|
171
|
+
make_rfc "RFC-060-stub.proposed.md" "P600" "$PLACEHOLDER"
|
|
172
|
+
|
|
173
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
174
|
+
[ "$status" -eq 0 ]
|
|
175
|
+
[[ "$output" != *"rfc=RFC-060"* ]]
|
|
176
|
+
[[ "$output" == *"TOTAL proposed_skeletons=1 under_scoped=0"* ]]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# ── Non-candidate: a fleshed-out (non-skeleton) RFC is never counted ─────────
|
|
180
|
+
|
|
181
|
+
@test "non-skeleton RFC (Scope fleshed out) tracing a shipped problem is NOT counted" {
|
|
182
|
+
make_problem "closed" "700" "## Fix Strategy
|
|
183
|
+
|
|
184
|
+
Done and shipped."
|
|
185
|
+
make_rfc "RFC-070-stub.proposed.md" "P700" "This RFC has a real scope paragraph describing the change."
|
|
186
|
+
|
|
187
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
188
|
+
[ "$status" -eq 0 ]
|
|
189
|
+
[[ "$output" != *"rfc=RFC-070"* ]]
|
|
190
|
+
[[ "$output" == *"TOTAL proposed_skeletons=0 under_scoped=0"* ]]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# ── closed/ lifecycle-directory basis ───────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
@test "skeleton RFC tracing a closed/ problem is under-scoped (directory basis)" {
|
|
196
|
+
make_problem "closed" "800" "## Fix Strategy
|
|
197
|
+
|
|
198
|
+
Resolved and verified."
|
|
199
|
+
make_rfc "RFC-080-stub.proposed.md" "P800" "$PLACEHOLDER"
|
|
200
|
+
|
|
201
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
202
|
+
[ "$status" -eq 0 ]
|
|
203
|
+
[[ "$output" == *"AUTOCREATE-RFC under-scoped rfc=RFC-080"* ]]
|
|
204
|
+
[[ "$output" == *"shipped=P800:closed"* ]]
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# ── Multi-RFC aggregation + TOTAL summary ───────────────────────────────────
|
|
208
|
+
|
|
209
|
+
@test "multi-RFC aggregation: one candidate line per under-scoped skeleton + TOTAL" {
|
|
210
|
+
# under-scoped (known-error + Fix Strategy)
|
|
211
|
+
make_problem "known-error" "361" "## Fix Strategy
|
|
212
|
+
|
|
213
|
+
Shipped."
|
|
214
|
+
make_rfc "RFC-026-stub.proposed.md" "P361" "$PLACEHOLDER"
|
|
215
|
+
# under-scoped (verifying)
|
|
216
|
+
make_problem "verifying" "215" "## Fix Strategy
|
|
217
|
+
|
|
218
|
+
Shipped."
|
|
219
|
+
make_rfc "RFC-021-stub.proposed.md" "P215" "$PLACEHOLDER"
|
|
220
|
+
# legitimately-skeleton (open, no fix)
|
|
221
|
+
make_problem "open" "304" "## Symptoms
|
|
222
|
+
|
|
223
|
+
No fix yet."
|
|
224
|
+
make_rfc "RFC-023-stub.proposed.md" "P304" "$PLACEHOLDER"
|
|
225
|
+
|
|
226
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
227
|
+
[ "$status" -eq 0 ]
|
|
228
|
+
[[ "$output" == *"rfc=RFC-026"* ]]
|
|
229
|
+
[[ "$output" == *"rfc=RFC-021"* ]]
|
|
230
|
+
[[ "$output" != *"rfc=RFC-023"* ]]
|
|
231
|
+
[[ "$output" == *"TOTAL proposed_skeletons=3 under_scoped=2"* ]]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# ── Multi-problem trace: any one shipped problem makes the RFC under-scoped ──
|
|
235
|
+
|
|
236
|
+
@test "skeleton RFC tracing multiple problems is under-scoped if ANY is fix-shipped" {
|
|
237
|
+
make_problem "open" "900" "## Symptoms
|
|
238
|
+
|
|
239
|
+
No fix."
|
|
240
|
+
make_problem "verifying" "901" "## Fix Strategy
|
|
241
|
+
|
|
242
|
+
Shipped."
|
|
243
|
+
make_rfc "RFC-090-stub.proposed.md" "P900, P901" "$PLACEHOLDER"
|
|
244
|
+
|
|
245
|
+
run bash "$SCRIPT" "$RFCS_DIR" "$PROBLEMS_DIR"
|
|
246
|
+
[ "$status" -eq 0 ]
|
|
247
|
+
[[ "$output" == *"AUTOCREATE-RFC under-scoped rfc=RFC-090"* ]]
|
|
248
|
+
[[ "$output" == *"problems=P900,P901"* ]]
|
|
249
|
+
[[ "$output" == *"shipped=P901:verifying"* ]]
|
|
250
|
+
}
|
|
@@ -193,6 +193,21 @@ Drift-hint vocabulary: `skill-inventory-drift` (inventory-only per ADR-069). Alw
|
|
|
193
193
|
|
|
194
194
|
**Already load-bearing (ADR-069)**: the commit-hook `retrospective-readme-jtbd-currency.sh` denies `git commit` when `drift_instances ≥ 1` (skill-inventory-drift), per the carried-forward load-bearing-from-the-start-for-drift-class driver. This Step 2b advisory is the backup / cross-cutting surface — it catches drift in sessions that bypass the commit-hook (`BYPASS_JTBD_CURRENCY=1`) and summarises across packages; it is not the gate.
|
|
195
195
|
|
|
196
|
+
**ADR-073 auto-create-RFC reassessment advisory (RFC-005 B9, P314).** Step 2b also feeds the ADR-073 reassessment loop. The I13 fix-time RFC-trace gate (ADR-072 placement / ADR-073 auto-create-not-block) auto-creates problem-traced **skeleton** RFCs via `/wr-itil:capture-rfc` whenever a fix is proposed on an RFC-less Known Error. ADR-073's Reassessment Criteria say to revisit the auto-create decision "if auto-created RFCs are systematically under-scoped (a recurring 'the auto-RFC didn't capture the real fix' signal)". The auto-create event is otherwise only logged ephemerally to the `/wr-itil:work-problems` iter-summary `notes`; this advisory makes the reassessment signal durable by reading the auto-created RFCs left on disk and surfacing the ones whose traced problem's fix has already shipped while the RFC scope was never fleshed out. The surfacing channel is the retro summary's Pipeline Instability section (same trust-boundary as the README advisory above — surface the evidence; defer the reassessment to the human).
|
|
197
|
+
|
|
198
|
+
**Mechanism**: invoke `wr-retrospective-check-autocreate-rfc-scope` (resolves on `$PATH` to the plugin-bundled detector per ADR-049/ADR-080 — invoke by the `wr-retrospective-…` shim name, never a repo-relative `packages/…` path). The detector scans `docs/rfcs/*.proposed.md` for skeleton RFCs (whose `## Scope` still carries the capture-rfc placeholder `(deferred — populate at /wr-itil:manage-rfc accepted transition)`) whose `problems:` frontmatter traces at least one **fix-shipped** problem (located under `docs/problems/verifying/` or `…/closed/`, OR carrying a `## Fix Released` heading, OR carrying a populated `## Fix Strategy` section). It emits:
|
|
199
|
+
|
|
200
|
+
- Per under-scoped candidate: `AUTOCREATE-RFC under-scoped rfc=<RFC-NNN> problems=<csv> shipped=<pid:basis>`
|
|
201
|
+
- Trailing summary: `TOTAL proposed_skeletons=<N> under_scoped=<K>`
|
|
202
|
+
|
|
203
|
+
Always exits 0 — advisory per ADR-013 Rule 6 / ADR-040 declarative-first; there is no load-bearing gate counterpart (unlike the README inventory advisory, the reassessment signal informs a future human decision, not a blocking commit-hook).
|
|
204
|
+
|
|
205
|
+
**Interpretation**:
|
|
206
|
+
|
|
207
|
+
1. **`under_scoped == 0`** — emit a one-line `ADR-073 auto-create-RFC scope: clean (<N> proposed skeletons, 0 under-scoped)` to the retro summary's Pipeline Instability section.
|
|
208
|
+
2. **`under_scoped ≥ 1`** — emit each `AUTOCREATE-RFC under-scoped …` candidate line into the Pipeline Instability section. Each names a skeleton RFC whose fix has shipped but whose scope was never written — the ADR-073 under-scoped signal. Per ADR-013 Rule 6, the user reviews on return: a one-off is the expected living-RFC pattern (flesh the RFC out via `/wr-itil:manage-rfc accepted`); a **recurring** under-scoped population across retros is the ADR-073 reassessment trigger (revisit whether auto-create produces useful vehicles or noise). Same trust-boundary as the categorical detection above — surface the evidence; defer the decision.
|
|
209
|
+
3. **Detector failure** — if the detector exits non-zero (missing `docs/rfcs/`, runtime exception), log the failure inline as `ADR-073 auto-create-RFC advisory failed: <stderr>` but do NOT halt the retro. Same fail-open contract as the README advisory and Step 3's `check-briefing-budgets.sh` defensive trip — the cheap layer degrades to a one-line pointer rather than blocking.
|
|
210
|
+
|
|
196
211
|
**Interaction with other surfaces:**
|
|
197
212
|
|
|
198
213
|
- **Step 4a (Verification-close housekeeping, P068)** — same evidence-scan shape applied to a different surface. Both share the glob / scan / categorise / specific-citation / interactive-or-AFK pattern. Step 4a scans for successful exercise of `.verifying.md` fixes; Step 2b scans for tool-level friction. They fire independently and produce independent retro-summary sections.
|