@windyroad/itil 0.54.1 → 0.54.2-preview.786
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/resolve-governance-plugin-dirs.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/resolve-governance-plugin-dirs.sh" "$@"
|
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolve --plugin-dir arguments for the governance plugins an AFK iter
|
|
3
|
+
# subprocess needs.
|
|
4
|
+
#
|
|
5
|
+
# P382: headless `claude -p` loads only USER-scoped enabledPlugins, NOT
|
|
6
|
+
# project-scoped ones. When the windyroad plugins are enabled at project scope
|
|
7
|
+
# (the common adopter setup), an iter subprocess dispatched by work-problems
|
|
8
|
+
# Step 5 receives no windyroad agents/hooks — it commits ungated and cannot run
|
|
9
|
+
# retro-on-exit. `--setting-sources user,project` is insufficient (project
|
|
10
|
+
# activation is trust-gated; headless skips trust). Passing `--plugin-dir
|
|
11
|
+
# <plugin-root>` for each governance plugin DOES make it available. This script
|
|
12
|
+
# emits those `--plugin-dir` argument pairs for the Step 5 dispatch to splice
|
|
13
|
+
# into its `claude -p` invocation.
|
|
14
|
+
#
|
|
15
|
+
# Resolution: each plugin's cache parent is located from its bin dir on $PATH
|
|
16
|
+
# (ADR-049 bin-on-PATH precedent — present in adopter marketplace-cache trees
|
|
17
|
+
# and source-dev alike), then the HIGHEST-SEMVER sibling under that cache parent
|
|
18
|
+
# is selected (ADR-080 highest-version-wins). $PATH order is frozen at session
|
|
19
|
+
# init and goes stale mid-session (P343 / ADR-080 / ADR-081), so it MUST NOT be
|
|
20
|
+
# trusted for version selection — only to discover the cache parent.
|
|
21
|
+
#
|
|
22
|
+
# Output: two lines per resolvable plugin — `--plugin-dir` then the plugin root
|
|
23
|
+
# dir — for `mapfile -t` consumption (one token per line tolerates paths with
|
|
24
|
+
# spaces). Unresolvable plugins are skipped silently: graceful degradation when
|
|
25
|
+
# an adopter has not installed the full governance surface. (This is a
|
|
26
|
+
# deliberate divergence from the canonical shim's loud exit-127 — a missing
|
|
27
|
+
# governance plugin must not abort the whole AFK orchestrator dispatch.)
|
|
28
|
+
#
|
|
29
|
+
# Override the plugin set via WR_GOVERNANCE_PLUGINS (space-separated).
|
|
30
|
+
#
|
|
31
|
+
# @adr ADR-032 (governance skill invocation — Step 5 dispatch contract, P382 amendment)
|
|
32
|
+
# @adr ADR-049 (plugin-bundled scripts resolve via bin/ on $PATH)
|
|
33
|
+
# @adr ADR-080 (highest-version-wins resolution — $PATH order is not authoritative)
|
|
34
|
+
# @adr ADR-052 (behavioural test — resolve-governance-plugin-dirs.bats)
|
|
35
|
+
# @problem P382
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
|
|
38
|
+
PLUGINS="${WR_GOVERNANCE_PLUGINS:-wr-architect wr-jtbd wr-risk-scorer wr-voice-tone wr-itil wr-retrospective wr-style-guide}"
|
|
39
|
+
SEMVER_RE='^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$'
|
|
40
|
+
|
|
41
|
+
IFS=':' read -r -a path_entries <<< "$PATH"
|
|
42
|
+
|
|
43
|
+
for plugin in $PLUGINS; do
|
|
44
|
+
# Discover this plugin's cache parent from any of its bin dirs on $PATH.
|
|
45
|
+
# A bin entry has the shape <cache_parent>/<version>/bin.
|
|
46
|
+
cache_parent=""
|
|
47
|
+
for entry in "${path_entries[@]}"; do
|
|
48
|
+
case "$entry" in
|
|
49
|
+
*/"$plugin"/*/bin)
|
|
50
|
+
cache_parent="$(dirname "$(dirname "$entry")")"
|
|
51
|
+
break
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
done
|
|
55
|
+
[ -n "$cache_parent" ] || continue
|
|
56
|
+
|
|
57
|
+
# Select the highest-semver sibling under <cache_parent> (ADR-080). PATH order
|
|
58
|
+
# may name a stale version after a mid-session /install-updates, so re-resolve
|
|
59
|
+
# the version here rather than reading it from the PATH entry.
|
|
60
|
+
highest=""
|
|
61
|
+
while IFS= read -r dir; do
|
|
62
|
+
name="$(basename "$dir")"
|
|
63
|
+
[[ "$name" =~ $SEMVER_RE ]] || continue
|
|
64
|
+
if [ -z "$highest" ] || [ "$(printf '%s\n%s\n' "$highest" "$name" | sort -V | tail -1)" = "$name" ]; then
|
|
65
|
+
highest="$name"
|
|
66
|
+
fi
|
|
67
|
+
done < <(find "$cache_parent" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
|
|
68
|
+
|
|
69
|
+
[ -n "$highest" ] || continue
|
|
70
|
+
printf '%s\n%s\n' "--plugin-dir" "$cache_parent/$highest"
|
|
71
|
+
done
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# @problem P382 — AFK work-problems `claude -p` iter subprocesses load only
|
|
4
|
+
# USER-scoped enabledPlugins; project-scoped governance plugins
|
|
5
|
+
# must be passed explicitly via --plugin-dir. This helper emits
|
|
6
|
+
# the --plugin-dir argument pairs the Step 5 dispatch splices
|
|
7
|
+
# into its claude -p invocation.
|
|
8
|
+
#
|
|
9
|
+
# Contract: `resolve-governance-plugin-dirs.sh` reads $PATH + WR_GOVERNANCE_PLUGINS
|
|
10
|
+
# and emits, per resolvable plugin, two stdout lines — `--plugin-dir` then the
|
|
11
|
+
# plugin root dir — for `mapfile -t` consumption.
|
|
12
|
+
#
|
|
13
|
+
# Load-bearing behaviour (ADR-080): the version is selected by HIGHEST-SEMVER
|
|
14
|
+
# walk of the cache parent, NOT by $PATH order (which is frozen-stale
|
|
15
|
+
# mid-session per P343). The fixture deliberately places a LOWER version's bin
|
|
16
|
+
# earlier on $PATH and asserts the HIGHER version's dir is still emitted.
|
|
17
|
+
#
|
|
18
|
+
# @adr ADR-049 (bin/ on PATH shim — adopter-safe script resolution)
|
|
19
|
+
# @adr ADR-080 (highest-version-wins resolution)
|
|
20
|
+
# @adr ADR-052 (behavioural test per P081)
|
|
21
|
+
# @adr ADR-032 (Step 5 dispatch contract — P382 amendment)
|
|
22
|
+
# @jtbd JTBD-001 (Enforce Governance — iter commits ship gated)
|
|
23
|
+
# @jtbd JTBD-006 (Progress Backlog AFK — full governance surface inside iters)
|
|
24
|
+
|
|
25
|
+
setup() {
|
|
26
|
+
SCRIPTS_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
27
|
+
SCRIPT="$SCRIPTS_DIR/resolve-governance-plugin-dirs.sh"
|
|
28
|
+
CACHE="$(mktemp -d)"
|
|
29
|
+
# Synthetic marketplace-cache layout (ADR-080 SQ-080-5 — no source-repo
|
|
30
|
+
# cohabitation). wr-architect has two cached versions; wr-itil has one.
|
|
31
|
+
mkdir -p "$CACHE/windyroad/wr-architect/0.16.0/bin"
|
|
32
|
+
mkdir -p "$CACHE/windyroad/wr-architect/0.17.3/bin"
|
|
33
|
+
mkdir -p "$CACHE/windyroad/wr-itil/0.51.0/bin"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
teardown() {
|
|
37
|
+
rm -rf "$CACHE"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@test "resolve-governance-plugin-dirs: script exists and is executable" {
|
|
41
|
+
[ -f "$SCRIPT" ]
|
|
42
|
+
[ -x "$SCRIPT" ]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@test "resolve-governance-plugin-dirs: emits --plugin-dir pairs for resolvable plugins" {
|
|
46
|
+
PATH="$CACHE/windyroad/wr-architect/0.17.3/bin:$CACHE/windyroad/wr-itil/0.51.0/bin:/usr/bin:/bin" \
|
|
47
|
+
WR_GOVERNANCE_PLUGINS="wr-architect wr-itil" run "$SCRIPT"
|
|
48
|
+
[ "$status" -eq 0 ]
|
|
49
|
+
[ "${lines[0]}" = "--plugin-dir" ]
|
|
50
|
+
[ "${lines[1]}" = "$CACHE/windyroad/wr-architect/0.17.3" ]
|
|
51
|
+
[ "${lines[2]}" = "--plugin-dir" ]
|
|
52
|
+
[ "${lines[3]}" = "$CACHE/windyroad/wr-itil/0.51.0" ]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@test "resolve-governance-plugin-dirs: picks highest semver, NOT \$PATH order (ADR-080)" {
|
|
56
|
+
# Lower version (0.16.0) earlier on PATH than higher (0.17.3) — the resolver
|
|
57
|
+
# must still emit 0.17.3 (cache-parent highest-semver walk, not PATH order).
|
|
58
|
+
PATH="$CACHE/windyroad/wr-architect/0.16.0/bin:$CACHE/windyroad/wr-architect/0.17.3/bin:/usr/bin:/bin" \
|
|
59
|
+
WR_GOVERNANCE_PLUGINS="wr-architect" run "$SCRIPT"
|
|
60
|
+
[ "$status" -eq 0 ]
|
|
61
|
+
[ "${lines[0]}" = "--plugin-dir" ]
|
|
62
|
+
[ "${lines[1]}" = "$CACHE/windyroad/wr-architect/0.17.3" ]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@test "resolve-governance-plugin-dirs: skips unresolvable plugins silently" {
|
|
66
|
+
PATH="$CACHE/windyroad/wr-itil/0.51.0/bin:/usr/bin:/bin" \
|
|
67
|
+
WR_GOVERNANCE_PLUGINS="wr-architect wr-itil wr-not-installed" run "$SCRIPT"
|
|
68
|
+
[ "$status" -eq 0 ]
|
|
69
|
+
# Only wr-itil is on PATH → exactly one pair, no wr-architect / wr-not-installed.
|
|
70
|
+
[ "${#lines[@]}" -eq 2 ]
|
|
71
|
+
[ "${lines[1]}" = "$CACHE/windyroad/wr-itil/0.51.0" ]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@test "resolve-governance-plugin-dirs: no governance plugins on PATH → empty output, exit 0" {
|
|
75
|
+
PATH="/usr/bin:/bin" WR_GOVERNANCE_PLUGINS="wr-architect wr-itil" run "$SCRIPT"
|
|
76
|
+
[ "$status" -eq 0 ]
|
|
77
|
+
[ -z "$output" ]
|
|
78
|
+
}
|
|
@@ -538,9 +538,22 @@ export WR_SUPPRESS_PENDING_QUESTIONS=1
|
|
|
538
538
|
# pending-questions guard above (JTBD-006 friction guard).
|
|
539
539
|
export WR_SUPPRESS_OVERSIGHT_NUDGE=1
|
|
540
540
|
|
|
541
|
+
# Project-scoped governance plugins are NOT loaded by headless `claude -p`
|
|
542
|
+
# (P382): it activates only USER-scoped enabledPlugins, and project activation
|
|
543
|
+
# is trust-gated (headless skips trust), so `--setting-sources user,project`
|
|
544
|
+
# alone does not attach them. Without this, the iter subprocess has no
|
|
545
|
+
# windyroad architect/jtbd/risk-scorer/voice-tone agents or gate hooks — it
|
|
546
|
+
# commits ungated and cannot run retro-on-exit. Pass each governance plugin
|
|
547
|
+
# explicitly via `--plugin-dir`, resolved portably from the installed
|
|
548
|
+
# marketplace cache (highest-version-wins, ADR-080; adopter-safe via the
|
|
549
|
+
# ADR-049 bin-on-PATH shim, NOT a repo-relative path). Unresolvable plugins are
|
|
550
|
+
# skipped silently. <!-- @jtbd JTBD-001 (iter commits ship gated) @jtbd JTBD-006 (full governance surface inside AFK iters) -->
|
|
551
|
+
mapfile -t PLUGIN_DIR_ARGS < <(wr-itil-resolve-governance-plugin-dirs)
|
|
552
|
+
|
|
541
553
|
claude -p \
|
|
542
554
|
--permission-mode bypassPermissions \
|
|
543
555
|
--output-format json \
|
|
556
|
+
"${PLUGIN_DIR_ARGS[@]}" \
|
|
544
557
|
"$ITERATION_PROMPT" \
|
|
545
558
|
< /dev/null \
|
|
546
559
|
> "$ITER_JSON" 2>&1 &
|
|
@@ -595,6 +608,7 @@ rm -f "$ITER_JSON"
|
|
|
595
608
|
|
|
596
609
|
- `--permission-mode bypassPermissions` — handles non-interactive permission prompts. Without this, Bash/Edit/Write calls inside the subprocess halt on approval prompts (no TTY). Alternative modes (`acceptEdits`, `auto`, `dontAsk`) are acceptable if adopters need narrower permission scopes; `bypassPermissions` is the broadest and the empirically-verified path.
|
|
597
610
|
- `--output-format json` — deterministic structured output. The subprocess's final agent message lands in the JSON response's `.result` field; orchestrator extracts `ITERATION_SUMMARY` from that field. Plain-text output would require fragile scraping.
|
|
611
|
+
- `"${PLUGIN_DIR_ARGS[@]}"` — `--plugin-dir <root>` pairs for each governance plugin, emitted by `wr-itil-resolve-governance-plugin-dirs` (the `mapfile` line above). **Load-bearing (P382).** Headless `claude -p` activates only USER-scoped `enabledPlugins`; project-scoped plugins stay inactive because project-plugin activation is trust-gated and headless skips the trust prompt. Empirically (verified 2026-06-21) `--setting-sources user,project` does NOT fix this — only `--plugin-dir` makes a project-scoped plugin's agents/hooks/skills available. Without these args an iter in a project-scope adopter tree commits ungated (architect/jtbd/risk-scorer/voice-tone agents resolve to "not found") and cannot run retro-on-exit. The resolver derives each plugin's root from its `bin/` dir on `$PATH` (ADR-049 — present in adopter marketplace-cache trees and source-dev alike) and selects the highest-semver cached version (ADR-080 — `$PATH` order is frozen at session init and goes stale mid-session, so it is NOT trusted for version selection). Behavioural second-source: `packages/itil/scripts/test/resolve-governance-plugin-dirs.bats`. The expansion is empty (no-op) when no governance plugins resolve, so source-repo dev sessions and minimal adopters degrade gracefully.
|
|
598
612
|
- `< /dev/null` — explicit stdin-closed redirect (P089 Gap 1). Without this, `claude -p` waits up to 3s for stdin data in non-TTY contexts and then prints `Warning: no stdin data received in 3s, proceeding without it. If piping from a slow command, redirect stdin explicitly: < /dev/null to skip, or wait longer.` to stderr. The warning is on stderr — if the caller separates stderr and stdout streams, the warning is harmless. But the orchestrator captures via `2>&1` (required because the CLI emits progress prose on stderr that must not interleave between JSON responses when multiple invocations chain). Under the `2>&1` merge the stderr warning prefixes the stdout JSON and breaks `jq` / `json.load` / `JSON.parse` extraction at "line 1, column 1: Expecting value". The redirect suppresses the warning at source. First observed AFK-iter-7 iter 1 (2026-04-21); workaround is the Anthropic CLI help's own suggestion.
|
|
599
613
|
|
|
600
614
|
**No per-iteration budget cap.** The dispatch deliberately omits `--max-budget-usd`. Per user direction 2026-04-21: the natural stop condition for an AFK loop is quota exhaustion, not an arbitrary per-iteration dollar cap. A cap would halt iterations before quota is actually exhausted, wasting remaining budget. Runaway-iteration risk is bounded by quota + the orchestrator's Step 6.75 halt on unexpected dirty state + exit-code handling below.
|