@windyroad/itil 0.51.2-preview.757 → 0.52.0-preview.762

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.
@@ -497,5 +497,5 @@
497
497
  }
498
498
  },
499
499
  "name": "wr-itil",
500
- "version": "0.51.2"
500
+ "version": "0.52.0"
501
501
  }
@@ -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/update-rfc-commits-section.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/update-rfc-commits-section.sh" "$@"
package/hooks/hooks.json CHANGED
@@ -5,6 +5,10 @@
5
5
  {
6
6
  "matcher": "startup",
7
7
  "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-pending-questions-surface.sh" }]
8
+ },
9
+ {
10
+ "matcher": "startup",
11
+ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-rfc-oversight-nudge.sh" }]
8
12
  }
9
13
  ],
10
14
  "UserPromptSubmit": [
@@ -50,6 +54,10 @@
50
54
  "matcher": "Bash",
51
55
  "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-rfc-trailer-advisory.sh" }]
52
56
  },
57
+ {
58
+ "matcher": "Bash",
59
+ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-commit-trailer-transition-advisory.sh" }]
60
+ },
53
61
  {
54
62
  "matcher": "Write|Edit|MultiEdit",
55
63
  "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-fictional-defer-detect.sh" }]
@@ -0,0 +1,79 @@
1
+ #!/bin/bash
2
+ # wr-itil — PostToolUse:Bash hook (P378/RFC-030 Piece 2). The SHARED
3
+ # commit-trailer auto-transition trigger that ADR-060 (line 292/307) promised
4
+ # for BOTH the RFC and story tiers and that manage-rfc + manage-story both
5
+ # deferred to "a future commit-trailer-trigger hook" — never built. This is it.
6
+ #
7
+ # Per ADR-014, a hook MUST NOT perform the transition itself (git mv + Status
8
+ # edit + commit lands OUTSIDE the triggering commit's grain). So the hook
9
+ # DETECTS eligibility and ADVISES; the skill (/wr-itil:manage-rfc |
10
+ # /wr-itil:manage-story) — or an AFK orchestrator acting on the advisory —
11
+ # performs the transition. The detection is now self-firing; the false
12
+ # "auto-transitions on first non-capture commit" claims in both skills are
13
+ # corrected to describe this detect-then-perform shape.
14
+ #
15
+ # Trigger: a `git commit` whose HEAD message
16
+ # - carries a `Refs: RFC-<NNN>` or `Refs: STORY-<NNN>` trailer, AND
17
+ # - is NOT the artefact's capture commit (subject does not start with
18
+ # `docs(rfcs): capture RFC-` / `feat(itil): capture STORY-`), AND
19
+ # - the referenced artefact is in a pre-in-progress status
20
+ # (RFC: .proposed/.accepted ; story: .draft) on disk.
21
+ # → emit a stderr advisory naming the transition command. Silent otherwise.
22
+ #
23
+ # Advisory-only (ADR-013 Rule 6 fail-open; ADR-045 ≤300-byte band; exit 0).
24
+ # Bypass: BYPASS_TRANSITION_ADVISORY=1.
25
+ #
26
+ # @adr ADR-060 (line 292/307 auto-transition triggers) ADR-014 (hook detects,
27
+ # skill commits) ADR-045 (budget) ADR-013 (Rule 6 fail-open) ADR-052 (bats)
28
+ # @problem P378 @rfc RFC-030
29
+
30
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
31
+ # shellcheck source=lib/command-detect.sh
32
+ source "$SCRIPT_DIR/lib/command-detect.sh"
33
+
34
+ INPUT=$(cat)
35
+ TOOL_NAME=$(printf '%s' "$INPUT" | python3 -c "import sys,json
36
+ try: print(json.load(sys.stdin).get('tool_name',''))
37
+ except: print('')" 2>/dev/null || echo "")
38
+ [ "$TOOL_NAME" = "Bash" ] || exit 0
39
+
40
+ COMMAND=$(printf '%s' "$INPUT" | python3 -c "import sys,json
41
+ try: print(json.load(sys.stdin).get('tool_input',{}).get('command',''))
42
+ except: print('')" 2>/dev/null || echo "")
43
+ command_invokes_git_commit "$COMMAND" || exit 0
44
+ [ "${BYPASS_TRANSITION_ADVISORY:-}" = "1" ] && exit 0
45
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
46
+
47
+ SUBJECT=$(git log -1 --format='%s' HEAD 2>/dev/null) || exit 0
48
+ BODY=$(git log -1 --format='%B' HEAD 2>/dev/null) || exit 0
49
+
50
+ # Skip capture commits — they CREATE the artefact, not advance it.
51
+ case "$SUBJECT" in
52
+ *"capture RFC-"*|*"capture STORY-"*) exit 0 ;;
53
+ esac
54
+
55
+ TRAILERS=$(printf '%s\n' "$BODY" | git interpret-trailers --parse 2>/dev/null \
56
+ | grep -oE 'RFC-[0-9]{3}|STORY-[0-9]{3}' | sort -u || true)
57
+ [ -n "$TRAILERS" ] || exit 0
58
+
59
+ advise() { echo "P378 ADVISORY: $1" >&2; }
60
+
61
+ while IFS= read -r id; do
62
+ [ -n "$id" ] || continue
63
+ case "$id" in
64
+ RFC-*)
65
+ [ -d "./docs/rfcs" ] || continue
66
+ shopt -s nullglob; files=(./docs/rfcs/${id}-*.proposed.md ./docs/rfcs/${id}-*.accepted.md); shopt -u nullglob
67
+ [ ${#files[@]} -gt 0 ] || continue
68
+ advise "${id} carries a non-capture commit but is still $(basename "${files[0]}" | grep -oE 'proposed|accepted'). It should advance to in-progress — run /wr-itil:manage-rfc ${id} in-progress (the skill performs the transition; a hook cannot, per ADR-014). Bypass: BYPASS_TRANSITION_ADVISORY=1."
69
+ ;;
70
+ STORY-*)
71
+ [ -d "./docs/stories" ] || continue
72
+ shopt -s nullglob; files=(./docs/stories/draft/${id}-*.md ./docs/stories/${id}-*.draft.md); shopt -u nullglob
73
+ [ ${#files[@]} -gt 0 ] || continue
74
+ advise "${id} carries a non-capture commit but is still draft. It should advance to in-progress — run /wr-itil:manage-story ${id} in-progress. Bypass: BYPASS_TRANSITION_ADVISORY=1."
75
+ ;;
76
+ esac
77
+ done <<< "$TRAILERS"
78
+
79
+ exit 0
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ # wr-itil — SessionStart hook (P378/RFC-030; ADR-066/068 oversight-nudge clone)
3
+ #
4
+ # Surfaces a one-line nudge when RFCs lack the human-oversight marker, so the
5
+ # user can ratify them via /wr-itil:manage-rfc <RFC-NNN> (the accepted
6
+ # transition ratifies RFC scope). Sibling of architect-oversight-nudge.sh
7
+ # (ADR-066) and jtbd-oversight-nudge.sh (ADR-068); same class-B SessionStart
8
+ # shape as the ADR-040 session-start surface — so ratification is auto-surfaced
9
+ # every session instead of leaning on the user's memory (P378).
10
+ #
11
+ # Detection is token-cheap: delegates to detect-unoversighted-rfcs.sh (a grep
12
+ # over docs/rfcs/ frontmatter — no body reads, no per-RFC LLM call). Silent
13
+ # when the unoversighted count is zero (steady state once ratified).
14
+ #
15
+ # AFK self-suppress: shares the suite-wide WR_SUPPRESS_OVERSIGHT_NUDGE guard
16
+ # with the architect + jtbd oversight nudges (ADR-068 § shared cross-plugin
17
+ # contracts). AFK orchestrators export it once and every oversight nudge
18
+ # self-suppresses — so this interactive ratify nudge never fires into an
19
+ # absent-user subprocess (JTBD-006 friction guard). Only the literal "1"
20
+ # suppresses. Silent-on-zero; fail-open; ADR-040 ≤2KB budget (one line).
21
+
22
+ set -uo pipefail
23
+
24
+ if [ "${WR_SUPPRESS_OVERSIGHT_NUDGE:-}" = "1" ]; then
25
+ exit 0
26
+ fi
27
+
28
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
29
+ RFCS_DIR="$PROJECT_DIR/docs/rfcs"
30
+
31
+ [ -d "$RFCS_DIR" ] || exit 0
32
+
33
+ DETECT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}/scripts/detect-unoversighted-rfcs.sh"
34
+ [ -x "$DETECT" ] || DETECT="$(dirname "$0")/../scripts/detect-unoversighted-rfcs.sh"
35
+ [ -r "$DETECT" ] || exit 0
36
+
37
+ COUNT="$(bash "$DETECT" "$RFCS_DIR" 2>/dev/null | grep -c . || true)"
38
+ COUNT="${COUNT:-0}"
39
+
40
+ [ "$COUNT" -gt 0 ] 2>/dev/null || exit 0
41
+
42
+ if [ "$COUNT" -eq 1 ]; then
43
+ echo "[wr-itil] 1 RFC lacks human oversight — run /wr-itil:manage-rfc <RFC-NNN> to ratify it (the accepted transition confirms scope)."
44
+ else
45
+ echo "[wr-itil] $COUNT RFCs lack human oversight — run /wr-itil:manage-rfc <RFC-NNN> to ratify them (the accepted transition confirms scope)."
46
+ fi
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P378/RFC-030 Piece 2: itil-commit-trailer-transition-advisory.sh — the shared
4
+ # RFC+story commit-trailer auto-transition DETECTOR. Advises (ADR-014: does not
5
+ # perform) proposed/accepted RFC → in-progress and draft story → in-progress on
6
+ # the first non-capture commit carrying the Refs trailer. Behavioural.
7
+
8
+ setup() {
9
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
10
+ HOOK="$REPO_ROOT/packages/itil/hooks/itil-commit-trailer-transition-advisory.sh"
11
+ DIR="$(mktemp -d)"; cd "$DIR"
12
+ git init -q; git config user.email t@e.x; git config user.name t
13
+ mkdir -p docs/rfcs docs/stories/draft
14
+ echo x > seed; git add -A; git commit -qm "chore: seed"
15
+ }
16
+ teardown() { cd /; rm -rf "$DIR"; }
17
+
18
+ # Feed the hook a PostToolUse Bash payload for a `git commit` command.
19
+ run_hook() {
20
+ printf '{"tool_name":"Bash","tool_input":{"command":"git commit -m x"}}' | bash "$HOOK"
21
+ }
22
+
23
+ commit() { git commit -q --allow-empty -m "$1"; }
24
+
25
+ @test "advises proposed RFC → in-progress on a non-capture Refs commit" {
26
+ echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "$(printf 'docs(rfcs): capture RFC-201 x\n\nRefs: RFC-201')"
27
+ commit "$(printf 'feat(itil): do slice 1\n\nRefs: RFC-201')"
28
+ run run_hook
29
+ [ "$status" -eq 0 ]
30
+ [[ "$output" == *"RFC-201"* ]]
31
+ [[ "$output" == *"in-progress"* ]]
32
+ [[ "$output" == *"manage-rfc"* ]]
33
+ }
34
+
35
+ @test "silent on the capture commit itself" {
36
+ echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "$(printf 'docs(rfcs): capture RFC-201 x\n\nRefs: RFC-201')"
37
+ run run_hook
38
+ [ "$status" -eq 0 ]
39
+ [ -z "$output" ]
40
+ }
41
+
42
+ @test "silent when the RFC is already in-progress (no .proposed/.accepted file)" {
43
+ echo "---" > docs/rfcs/RFC-201-x.in-progress.md; git add -A; commit "$(printf 'feat(itil): more\n\nRefs: RFC-201')"
44
+ run run_hook
45
+ [ "$status" -eq 0 ]
46
+ [ -z "$output" ]
47
+ }
48
+
49
+ @test "advises draft story → in-progress on a non-capture Refs commit" {
50
+ echo "---" > docs/stories/draft/STORY-201-y.md; git add -A; commit "$(printf 'feat(itil): capture STORY-201 y\n\nRefs: STORY-201')"
51
+ commit "$(printf 'feat(itil): implement\n\nRefs: STORY-201')"
52
+ run run_hook
53
+ [ "$status" -eq 0 ]
54
+ [[ "$output" == *"STORY-201"* ]]
55
+ [[ "$output" == *"manage-story"* ]]
56
+ }
57
+
58
+ @test "bypass env var suppresses the advisory" {
59
+ echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "$(printf 'feat: s\n\nRefs: RFC-201')"
60
+ run env BYPASS_TRANSITION_ADVISORY=1 bash -c 'printf "{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"git commit -m x\"}}" | bash "$0"' "$HOOK"
61
+ [ "$status" -eq 0 ]
62
+ [ -z "$output" ]
63
+ }
64
+
65
+ @test "silent when commit carries no Refs trailer" {
66
+ echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "chore: untagged"
67
+ run run_hook
68
+ [ "$status" -eq 0 ]
69
+ [ -z "$output" ]
70
+ }
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P378/RFC-030: itil-rfc-oversight-nudge.sh (SessionStart) emits a one-line
4
+ # nudge when RFCs lack the human-oversight marker, is silent when none do, and
5
+ # self-suppresses under the shared AFK guard (WR_SUPPRESS_OVERSIGHT_NUDGE=1).
6
+ # Behavioural — exercises the hook against fixture docs/rfcs/ trees. Clone of
7
+ # the architect/jtbd oversight-nudge contract (ADR-066/068).
8
+
9
+ setup() {
10
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
11
+ HOOK="$REPO_ROOT/packages/itil/hooks/itil-rfc-oversight-nudge.sh"
12
+ PLUGIN_ROOT="$REPO_ROOT/packages/itil"
13
+ DIR="$(mktemp -d)"
14
+ mkdir -p "$DIR/docs/rfcs"
15
+ }
16
+
17
+ teardown() { rm -rf "$DIR"; }
18
+
19
+ mk_unconfirmed() {
20
+ { echo "---"; echo "status: proposed"; echo "human-oversight: unconfirmed"; echo "---"; echo "# $1"; } \
21
+ > "$DIR/docs/rfcs/$1"
22
+ }
23
+ mk_confirmed() {
24
+ { echo "---"; echo "status: proposed"; echo "human-oversight: confirmed"; echo "---"; echo "# $1"; } \
25
+ > "$DIR/docs/rfcs/$1"
26
+ }
27
+
28
+ run_hook() { run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" "$@" bash "$HOOK"; }
29
+
30
+ @test "emits a count line when there are unoversighted RFCs" {
31
+ mk_unconfirmed "RFC-201-foo.proposed.md"
32
+ mk_unconfirmed "RFC-202-bar.proposed.md"
33
+ run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
34
+ [ "$status" -eq 0 ]
35
+ [[ "$output" == *"2 RFCs lack human oversight"* ]]
36
+ [[ "$output" == *"/wr-itil:manage-rfc"* ]]
37
+ }
38
+
39
+ @test "singular wording for exactly one unoversighted RFC" {
40
+ mk_unconfirmed "RFC-201-foo.proposed.md"
41
+ run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
42
+ [[ "$output" == *"1 RFC lacks human oversight"* ]]
43
+ }
44
+
45
+ @test "silent when every RFC is confirmed" {
46
+ mk_confirmed "RFC-201-foo.proposed.md"
47
+ run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
48
+ [ "$status" -eq 0 ]
49
+ [ -z "$output" ]
50
+ }
51
+
52
+ @test "excludes superseded and closed RFCs from the count" {
53
+ mk_unconfirmed "RFC-201-foo.superseded.md"
54
+ mk_unconfirmed "RFC-202-bar.closed.md"
55
+ run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
56
+ [ "$status" -eq 0 ]
57
+ [ -z "$output" ]
58
+ }
59
+
60
+ @test "shared AFK guard suppresses the nudge entirely" {
61
+ mk_unconfirmed "RFC-201-foo.proposed.md"
62
+ run env WR_SUPPRESS_OVERSIGHT_NUDGE=1 CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
63
+ [ "$status" -eq 0 ]
64
+ [ -z "$output" ]
65
+ }
66
+
67
+ @test "guard value other than 1 does not suppress" {
68
+ mk_unconfirmed "RFC-201-foo.proposed.md"
69
+ run env WR_SUPPRESS_OVERSIGHT_NUDGE=yes CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
70
+ [[ "$output" == *"lack"*"human oversight"* ]] || [[ "$output" == *"lacks human oversight"* ]]
71
+ }
72
+
73
+ @test "silent when docs/rfcs does not exist (framework not adopted)" {
74
+ rm -rf "$DIR/docs/rfcs"
75
+ run env CLAUDE_PROJECT_DIR="$DIR" CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" bash "$HOOK"
76
+ [ "$status" -eq 0 ]
77
+ [ -z "$output" ]
78
+ }
@@ -7,10 +7,12 @@ import { execSync } from "node:child_process";
7
7
 
8
8
  const MARKETPLACE_REPO = "windyroad/agent-plugins";
9
9
  const MARKETPLACE_NAME = "windyroad";
10
+ const CODEX_MARKETPLACE_PATH = ".";
11
+ const CODEX_MARKETPLACE_NAME = "windyroad-local";
10
12
 
11
13
  let _dryRun = false;
12
14
 
13
- export { MARKETPLACE_REPO, MARKETPLACE_NAME };
15
+ export { MARKETPLACE_REPO, MARKETPLACE_NAME, CODEX_MARKETPLACE_PATH, CODEX_MARKETPLACE_NAME };
14
16
 
15
17
  export function setDryRun(value) {
16
18
  _dryRun = value;
@@ -35,16 +37,34 @@ export function run(cmd, label) {
35
37
  }
36
38
  }
37
39
 
38
- export function checkPrerequisites() {
40
+ function runtimesFor(runtime = "claude") {
41
+ if (runtime === "both") return ["claude", "codex"];
42
+ return [runtime];
43
+ }
44
+
45
+ export function checkPrerequisites({ runtime = "claude" } = {}) {
39
46
  if (_dryRun) return;
40
47
 
41
- try {
42
- execSync("claude --version", { stdio: "pipe" });
43
- } catch {
44
- console.error(
45
- "Error: 'claude' CLI not found. Install Claude Code first:\n https://docs.anthropic.com/en/docs/claude-code\n"
46
- );
47
- process.exit(1);
48
+ for (const currentRuntime of runtimesFor(runtime)) {
49
+ if (currentRuntime === "claude") {
50
+ try {
51
+ execSync("claude --version", { stdio: "pipe" });
52
+ } catch {
53
+ console.error(
54
+ "Error: 'claude' CLI not found. Install Claude Code first:\n https://docs.anthropic.com/en/docs/claude-code\n"
55
+ );
56
+ process.exit(1);
57
+ }
58
+ } else if (currentRuntime === "codex") {
59
+ try {
60
+ execSync("codex --version", { stdio: "pipe" });
61
+ } catch {
62
+ console.error(
63
+ "Error: 'codex' CLI not found. Install Codex CLI first:\n https://developers.openai.com/codex\n"
64
+ );
65
+ process.exit(1);
66
+ }
67
+ }
48
68
  }
49
69
  }
50
70
 
@@ -55,6 +75,13 @@ export function addMarketplace() {
55
75
  );
56
76
  }
57
77
 
78
+ export function addCodexMarketplace() {
79
+ return run(
80
+ `codex plugin marketplace add ${CODEX_MARKETPLACE_PATH}`,
81
+ `Codex marketplace: ${CODEX_MARKETPLACE_NAME}`
82
+ );
83
+ }
84
+
58
85
  export function installPlugin(pluginName, { scope = "project" } = {}) {
59
86
  return run(
60
87
  `claude plugin install ${pluginName}@${MARKETPLACE_NAME} --scope ${scope}`,
@@ -62,6 +89,13 @@ export function installPlugin(pluginName, { scope = "project" } = {}) {
62
89
  );
63
90
  }
64
91
 
92
+ export function installCodexPlugin(pluginName) {
93
+ return run(
94
+ `codex plugin add ${pluginName}@${CODEX_MARKETPLACE_NAME}`,
95
+ pluginName
96
+ );
97
+ }
98
+
65
99
  export function updatePlugin(pluginName, { scope = "project" } = {}) {
66
100
  return run(
67
101
  `claude plugin update "${pluginName}@${MARKETPLACE_NAME}" --scope ${scope}`,
@@ -69,18 +103,36 @@ export function updatePlugin(pluginName, { scope = "project" } = {}) {
69
103
  );
70
104
  }
71
105
 
106
+ export function updateCodexMarketplace() {
107
+ return run(
108
+ `codex plugin marketplace add ${CODEX_MARKETPLACE_PATH}`,
109
+ `Codex marketplace: ${CODEX_MARKETPLACE_NAME}`
110
+ );
111
+ }
112
+
72
113
  export function uninstallPlugin(pluginName) {
73
114
  return run(`claude plugin uninstall ${pluginName}`, `Removing ${pluginName}`);
74
115
  }
75
116
 
117
+ export function uninstallCodexPlugin(pluginName) {
118
+ return run(`codex plugin remove ${pluginName}`, `Removing ${pluginName}`);
119
+ }
120
+
76
121
  /**
77
122
  * Install a single package: marketplace add + plugin install.
78
123
  */
79
- export function installPackage(pluginName, { deps = [], scope = "project" } = {}) {
124
+ export function installPackage(pluginName, { deps = [], scope = "project", runtime = "claude" } = {}) {
80
125
  console.log(`\nInstalling @windyroad/${pluginName.replace("wr-", "")} (${scope} scope)...\n`);
81
126
 
82
- addMarketplace();
83
- installPlugin(pluginName, { scope });
127
+ if (runtime === "claude" || runtime === "both") {
128
+ addMarketplace();
129
+ installPlugin(pluginName, { scope });
130
+ }
131
+
132
+ if (runtime === "codex" || runtime === "both") {
133
+ addCodexMarketplace();
134
+ installCodexPlugin(pluginName);
135
+ }
84
136
 
85
137
  if (deps.length > 0) {
86
138
  console.log(`\nNote: This plugin works best with:`);
@@ -90,34 +142,47 @@ export function installPackage(pluginName, { deps = [], scope = "project" } = {}
90
142
  }
91
143
 
92
144
  console.log(
93
- `\nDone! Restart Claude Code to activate.\n`
145
+ `\nDone! Restart ${runtime === "codex" ? "Codex" : runtime === "both" ? "Claude Code and Codex" : "Claude Code"} to activate.\n`
94
146
  );
95
147
  }
96
148
 
97
149
  /**
98
150
  * Update a single package.
99
151
  */
100
- export function updatePackage(pluginName, { scope = "project" } = {}) {
152
+ export function updatePackage(pluginName, { scope = "project", runtime = "claude" } = {}) {
101
153
  console.log(`\nUpdating @windyroad/${pluginName.replace("wr-", "")}...\n`);
102
154
 
103
- run(
104
- `claude plugin marketplace update ${MARKETPLACE_NAME}`,
105
- "Updating marketplace"
106
- );
107
- updatePlugin(pluginName, { scope });
155
+ if (runtime === "claude" || runtime === "both") {
156
+ run(
157
+ `claude plugin marketplace update ${MARKETPLACE_NAME}`,
158
+ "Updating marketplace"
159
+ );
160
+ updatePlugin(pluginName, { scope });
161
+ }
108
162
 
109
- console.log("\nDone! Restart Claude Code to apply updates.\n");
163
+ if (runtime === "codex" || runtime === "both") {
164
+ updateCodexMarketplace();
165
+ installCodexPlugin(pluginName);
166
+ }
167
+
168
+ console.log(`\nDone! Restart ${runtime === "codex" ? "Codex" : runtime === "both" ? "Claude Code and Codex" : "Claude Code"} to apply updates.\n`);
110
169
  }
111
170
 
112
171
  /**
113
172
  * Uninstall a single package.
114
173
  */
115
- export function uninstallPackage(pluginName) {
174
+ export function uninstallPackage(pluginName, { runtime = "claude" } = {}) {
116
175
  console.log(`\nUninstalling @windyroad/${pluginName.replace("wr-", "")}...\n`);
117
176
 
118
- uninstallPlugin(pluginName);
177
+ if (runtime === "claude" || runtime === "both") {
178
+ uninstallPlugin(pluginName);
179
+ }
180
+
181
+ if (runtime === "codex" || runtime === "both") {
182
+ uninstallCodexPlugin(pluginName);
183
+ }
119
184
 
120
- console.log("\nDone. Restart Claude Code to apply changes.\n");
185
+ console.log(`\nDone. Restart ${runtime === "codex" ? "Codex" : runtime === "both" ? "Claude Code and Codex" : "Claude Code"} to apply changes.\n`);
121
186
  }
122
187
 
123
188
  /**
@@ -131,6 +196,7 @@ export function parseStandardArgs(argv) {
131
196
  update: args.includes("--update"),
132
197
  dryRun: args.includes("--dry-run"),
133
198
  scope: "project",
199
+ runtime: "claude",
134
200
  };
135
201
  const scopeIdx = args.indexOf("--scope");
136
202
  if (scopeIdx !== -1 && args[scopeIdx + 1]) {
@@ -142,5 +208,15 @@ export function parseStandardArgs(argv) {
142
208
  process.exit(1);
143
209
  }
144
210
  }
211
+ const runtimeIdx = args.indexOf("--runtime");
212
+ if (runtimeIdx !== -1 && args[runtimeIdx + 1]) {
213
+ const val = args[runtimeIdx + 1];
214
+ if (["claude", "codex", "both"].includes(val)) {
215
+ flags.runtime = val;
216
+ } else {
217
+ console.error("--runtime requires: claude, codex, or both");
218
+ process.exit(1);
219
+ }
220
+ }
145
221
  return flags;
146
222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.51.2-preview.757",
3
+ "version": "0.52.0-preview.762",
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,46 @@
1
+ #!/usr/bin/env bash
2
+ # wr-itil — detect RFCs lacking the human-oversight marker (P378/RFC-030).
3
+ #
4
+ # Clone of the architect/jtbd detect-unoversighted shape (ADR-066/068): greps
5
+ # each RFC's YAML frontmatter for `human-oversight: confirmed`. No body reads,
6
+ # no per-RFC LLM call. An RFC is "unoversighted" when its frontmatter does not
7
+ # carry that marker line (RFCs are born `human-oversight: unconfirmed` at
8
+ # capture-rfc; they are ratified at the /wr-itil:manage-rfc accepted transition
9
+ # where scope is confirmed).
10
+ #
11
+ # Usage:
12
+ # detect-unoversighted-rfcs.sh [RFCS_DIR] (default docs/rfcs)
13
+ # Output: one unoversighted RFC file path per line, sorted. Empty = all
14
+ # confirmed. Always exits 0 (detector, not a gate).
15
+ #
16
+ # Consumed by: itil-rfc-oversight-nudge.sh (SessionStart count). Marker
17
+ # contract: ADR-066 (human-oversight marker), as extended to RFCs by ADR-070/071.
18
+
19
+ set -euo pipefail
20
+
21
+ RFCS_DIR="${1:-docs/rfcs}"
22
+
23
+ [ -d "$RFCS_DIR" ] || exit 0
24
+
25
+ shopt -s nullglob
26
+ for f in "$RFCS_DIR"/*.md "$RFCS_DIR"/*/*.md; do
27
+ base="$(basename "$f")"
28
+ [ "$base" = "README.md" ] && continue
29
+ # Superseded + closed RFCs are retired/done — ratifying their scope is moot,
30
+ # so they are not part of the "needs oversight" set (keeps the nudge focused
31
+ # on live RFCs the user can still act on).
32
+ case "$base" in *.superseded.md|*.closed.md) continue ;; esac
33
+
34
+ fm="$(awk '
35
+ NR==1 && $0 != "---" { exit }
36
+ NR==1 { next }
37
+ /^---[[:space:]]*$/ { exit }
38
+ { print }
39
+ ' "$f")"
40
+
41
+ if printf '%s\n' "$fm" | grep -qiE '^human-oversight:[[:space:]]*confirmed[[:space:]]*$'; then
42
+ continue
43
+ fi
44
+
45
+ echo "$f"
46
+ done | sort
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P378/RFC-030 Piece 1 (ADR-085): update-rfc-commits-section.sh renders the RFC
4
+ # `## Commits` section as a derived view from `git log --grep "Refs: RFC-NNN"`.
5
+ # Behavioural — exercises the renderer against a throwaway git repo.
6
+
7
+ setup() {
8
+ REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
9
+ HELPER="$REPO_ROOT/packages/itil/scripts/update-rfc-commits-section.sh"
10
+ DIR="$(mktemp -d)"
11
+ cd "$DIR"
12
+ git init -q
13
+ git config user.email t@e.x; git config user.name t
14
+ mkdir -p docs/rfcs
15
+ cat > docs/rfcs/RFC-201-thing.proposed.md <<'EOF'
16
+ # RFC-201: thing
17
+
18
+ ## Commits
19
+
20
+ (placeholder)
21
+
22
+ ## Related
23
+
24
+ x
25
+ EOF
26
+ git add -A && git commit -qm "chore: seed"
27
+ }
28
+ teardown() { cd /; rm -rf "$DIR"; }
29
+
30
+ @test "renders commits carrying the Refs: RFC-NNN trailer, newest first" {
31
+ echo a > a; git add a; git commit -qm "$(printf 'feat: a\n\nRefs: RFC-201')"
32
+ echo b > b; git add b; git commit -qm "$(printf 'fix: b\n\nRefs: RFC-201')"
33
+ run bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
34
+ [ "$status" -eq 0 ]
35
+ run sed -n '/## Commits/,/## Related/p' docs/rfcs/RFC-201-thing.proposed.md
36
+ [[ "$output" == *"fix: b"* ]]
37
+ [[ "$output" == *"feat: a"* ]]
38
+ # newest first: fix: b appears before feat: a
39
+ [[ "$output" == *"fix: b"*"feat: a"* ]]
40
+ # placeholder body is gone
41
+ [[ "$output" != *"(placeholder)"* ]]
42
+ }
43
+
44
+ @test "does NOT include commits without the trailer" {
45
+ echo a > a; git add a; git commit -qm "$(printf 'feat: tagged\n\nRefs: RFC-201')"
46
+ echo b > b; git add b; git commit -qm "chore: untagged"
47
+ run bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
48
+ run sed -n '/## Commits/,/## Related/p' docs/rfcs/RFC-201-thing.proposed.md
49
+ [[ "$output" == *"feat: tagged"* ]]
50
+ [[ "$output" != *"chore: untagged"* ]]
51
+ }
52
+
53
+ @test "no trailer-bearing commits → renders a self-describing placeholder, not a false claim" {
54
+ run bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
55
+ [ "$status" -eq 0 ]
56
+ run sed -n '/## Commits/,/## Related/p' docs/rfcs/RFC-201-thing.proposed.md
57
+ [[ "$output" == *"rendered from"* ]] && [[ "$output" == *"git log"* ]]
58
+ [[ "$output" != *"maintained automatically"* ]]
59
+ }
60
+
61
+ @test "idempotent — second render is a byte-stable no-op" {
62
+ echo a > a; git add a; git commit -qm "$(printf 'feat: a\n\nRefs: RFC-201')"
63
+ bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
64
+ h1=$(shasum docs/rfcs/RFC-201-thing.proposed.md | cut -d' ' -f1)
65
+ bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
66
+ h2=$(shasum docs/rfcs/RFC-201-thing.proposed.md | cut -d' ' -f1)
67
+ [ "$h1" = "$h2" ]
68
+ }
69
+
70
+ @test "preserves sections after ## Commits (## Related survives)" {
71
+ echo a > a; git add a; git commit -qm "$(printf 'feat: a\n\nRefs: RFC-201')"
72
+ bash "$HELPER" docs/rfcs/RFC-201-thing.proposed.md
73
+ run grep -c "## Related" docs/rfcs/RFC-201-thing.proposed.md
74
+ [ "$output" -eq 1 ]
75
+ run grep -c "^x$" docs/rfcs/RFC-201-thing.proposed.md
76
+ [ "$output" -eq 1 ]
77
+ }
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ # packages/itil/scripts/update-rfc-commits-section.sh
3
+ #
4
+ # Renders an RFC's `## Commits` section as a DERIVED VIEW from the git history
5
+ # (P378/RFC-030 Piece 1; ADR-085). The commit log is the source of truth; the
6
+ # section is a projection of `git log --grep "Refs: RFC-NNN"`. Because nothing
7
+ # is written per-commit, there is no post-commit working-tree edit and no
8
+ # ADR-014 grain problem — the section is regenerated skill-side (manage-rfc on
9
+ # every transition/review; reconcile-rfcs) exactly like docs/problems/README.md
10
+ # is a rendered index (ADR-031 precedent).
11
+ #
12
+ # Usage: update-rfc-commits-section.sh <rfc-file>
13
+ # Idempotent: rewriting a current section is a byte-stable no-op.
14
+ #
15
+ # @adr ADR-085 (RFC ## Commits is a git-log-derived view, rendered skill-side)
16
+ # @adr ADR-031 (rendered-index precedent) ADR-014 (why not a post-commit hook)
17
+ # @problem P378
18
+ # @rfc RFC-030
19
+
20
+ set -uo pipefail
21
+
22
+ RFC_FILE="${1:-}"
23
+ [ -n "$RFC_FILE" ] || { echo "ERROR: missing rfc-file argument" >&2; exit 1; }
24
+ [ -f "$RFC_FILE" ] || { echo "ERROR: rfc file not found: $RFC_FILE" >&2; exit 1; }
25
+
26
+ # RFC id from filename (RFC-NNN-...).
27
+ RFC_ID=$(basename "$RFC_FILE" | grep -oE 'RFC-[0-9]{3}' | head -1)
28
+ [ -n "$RFC_ID" ] || { echo "ERROR: cannot derive RFC id from $RFC_FILE" >&2; exit 1; }
29
+
30
+ # Render the commit list from git log (newest first). Fail-open to a sentinel
31
+ # line when not in a git tree (e.g. adopter tarball inspection).
32
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
33
+ RENDERED=$(git log --grep="Refs: ${RFC_ID}\b" --format='- `%h` %s — %ad' --date=short 2>/dev/null || true)
34
+ else
35
+ RENDERED=""
36
+ fi
37
+ if [ -z "$RENDERED" ]; then
38
+ RENDERED="(no commits yet — this section is rendered from \`git log --grep \"Refs: ${RFC_ID}\"\`)"
39
+ fi
40
+
41
+ # Rewrite the body of the `## Commits` section in place (everything between the
42
+ # `## Commits` heading and the next `## ` heading). awk preserves the rest. The
43
+ # rendered (multi-line) content is passed via a file — awk -v cannot carry
44
+ # embedded newlines.
45
+ TMP="$(mktemp)"
46
+ RENDERED_FILE="$(mktemp)"
47
+ printf '%s\n' "$RENDERED" > "$RENDERED_FILE"
48
+ awk -v rfile="$RENDERED_FILE" '
49
+ BEGIN { in_sec = 0 }
50
+ /^## Commits[[:space:]]*$/ {
51
+ print
52
+ print ""
53
+ while ((getline line < rfile) > 0) print line
54
+ close(rfile)
55
+ print ""
56
+ in_sec = 1
57
+ next
58
+ }
59
+ in_sec && /^## / { in_sec = 0 } # next section header ends the block
60
+ in_sec { next } # drop old body lines
61
+ { print }
62
+ ' "$RFC_FILE" > "$TMP"
63
+ rm -f "$RENDERED_FILE"
64
+
65
+ # Idempotent: only replace on change.
66
+ if ! cmp -s "$TMP" "$RFC_FILE"; then
67
+ cat "$TMP" > "$RFC_FILE"
68
+ fi
69
+ rm -f "$TMP"
@@ -35,8 +35,8 @@ This skill has **at most one direction-setting AskUserQuestion (the I12 derive-t
35
35
  |----------|-----------|
36
36
  | Duplicate-check | Mechanical 3-keyword title-only grep; matches listed in report; capture proceeds regardless. False-positives are cheaper than false-negatives (P155 line 24). |
37
37
  | Hang-off arbitration (P346 Phase 3) | Mechanical pre-filter (≤5 candidates by shared ADR/RFC/SKILL/file signal) + fresh-context `wr-itil:hang-off-check` subagent dispatch (ADR-032 5th invocation pattern). Verdict-acts: `HANG_OFF: P<NNN>` halts capture + routes orchestrator to amend parent; `PROCEED_NEW` continues + appends rationale to `## Related`. AFK safe-default: ambiguous → PROCEED_NEW per subagent Rule 6 contract. JTBD-301 firewall: maintainer-side only. |
38
- | Priority default | Framework-policy: `3 (Medium) Impact 3 × Likelihood 1` flagged "deferred re-rate at next /wr-itil:review-problems". |
39
- | Effort default | Framework-policy: `M` flagged "deferred re-rate at next /wr-itil:review-problems". |
38
+ | Priority (Impact × Likelihood) | **Derived at capture** silently from the description per Step 4a (ADR-067 silent-derivation + ADR-026 grounding/sentinel; ADR-076 honest-likelihood). No deferred placeholder, no false-low. Category-4 silent-framework per ADR-044. |
39
+ | Effort | **Derived at capture** silently per Step 4a (ADR-067 t-shirt buckets + ADR-026 grounding/sentinel). No deferred placeholder. |
40
40
  | Multi-concern split | Out of scope: capture-problem creates one ticket per invocation. Multi-concern observations route to `/wr-itil:manage-problem` (its Step 4b owns the split). |
41
41
  | Empty `$ARGUMENTS` | Halt-with-stderr-directive: print "capture-problem requires a description in $ARGUMENTS — invoke /wr-itil:manage-problem instead for the full intake flow" and exit. AFK orchestrators MUST NOT invoke capture-problem with empty arguments — caller-side contract. |
42
42
  | JTBD-trace + persona derivation (I12 derive-then-ratify — ADR-060 Amendment 2026-06-02) | **Derive-then-ratify per ADR-044 category 1 (direction-setting) on the AskUserQuestion fallback path; silent-framework category 4 on the derive-success path.** Step 1.5b runs lexical detection (`\bJTBD-[0-9]+\b`) + flag pre-resolution (`--jtbd=` / `--persona=`) + cited-JTBD persona derivation. **Derive-success** → silent-proceed with derived values + stderr advisory. **Derive-failure or ambiguity** → AskUserQuestion proposes up-to-3 candidate persona+JTBD pairs + a Reject option (4-option cap per ADR-044 Rule 1). User response semantics: **REJECT** → halt-with-stderr-directive + exit non-zero (REJECT of proposed persona/JTBD = REJECT of the problem; no ticket created). **Option-pick** (acceptance of a proposed candidate as-is) → silent-proceed with picked values. **Free-text correction** → silent-proceed with corrected values (correction-as-acceptance). |
@@ -239,9 +239,9 @@ Log the renumber decision in the operation report if origin and local diverged.
239
239
 
240
240
  **Status**: Open
241
241
  **Reported**: <YYYY-MM-DD>
242
- **Priority**: 3 (Medium) — Impact: 3 x Likelihood: 1 (deferred re-rate at next /wr-itil:review-problems)
242
+ **Priority**: <Severity = Impact × Likelihood> (<band>) — Impact: <1-5> × Likelihood: <1-5>derived at capture from the description per Step 4a
243
243
  **Origin**: internal
244
- **Effort**: M (deferred re-rate at next /wr-itil:review-problems)
244
+ **Effort**: <S|M|L|XL>derived at capture per Step 4a
245
245
  **JTBD**: <jtbd_trace_value_as_comma_separated_list_OR_omit_line_when_empty>
246
246
  **Persona**: <persona_value_OR_omit_line_when_empty>
247
247
 
@@ -268,7 +268,6 @@ Log the renumber decision in the operation report if origin and local diverged.
268
268
 
269
269
  ### Investigation Tasks
270
270
 
271
- - [ ] Re-rate Priority and Effort at next /wr-itil:review-problems
272
271
  - [ ] Investigate root cause
273
272
  - [ ] Create reproduction test
274
273
 
@@ -283,7 +282,17 @@ Log the renumber decision in the operation report if origin and local diverged.
283
282
  (captured via /wr-itil:capture-problem; expand at next investigation)
284
283
  ```
285
284
 
286
- The deferred-placeholder pattern is load-bearing`/wr-itil:review-problems` keys off the literal string `(deferred re-rate at next /wr-itil:review-problems)` to surface captured tickets for re-rating.
285
+ **Rate at capture, do not defer (P375 / ADR-032 amendment 2026-06-24).** The Priority (Impact × Likelihood) and Effort fields are **derived at capture** per Step 4a below — a real best-effort rating, ALWAYS. No `(deferred — re-rate…)` placeholder and **no "not estimated" / "no prior data" marker** those are deferrals wearing a different hat (user correction 2026-06-24: *"that's just another deferral"*). The old deferred-placeholder default was the single largest uncadenced-deferral rot generator (P375): nothing self-fired `review-problems`, and the `Likelihood: 1` false-low buried every capture at the bottom of WSJF. Estimation under uncertainty produces an estimate, not a deferral: a thin description still gets a real number (rough is fine). `/wr-itil:review-problems` re-rates the whole backlog when it runs — captures need no per-ticket re-rate flag.
286
+
287
+ ### 4a. Derive the rating at capture (P375 / ADR-032 amendment; ADR-067 + ADR-026 + ADR-076)
288
+
289
+ Derive Priority and Effort from the description **silently** — NO `AskUserQuestion` (ADR-067 item 1 silent-derivation, category-4 silent-framework per ADR-044; preserves the lightweight 3-4-turn capture cost shape). This is one inference over text the agent has already read, NOT the `/wr-itil:manage-problem` ceremony.
290
+
291
+ 1. **Impact (1-5)** and **Likelihood (1-5)** — read the description against `RISK-POLICY.md`'s impact bands and assess how often / how broadly the harm manifests. `Severity = Impact × Likelihood`; `Priority` band follows the standard matrix. Likelihood is **honest field-risk only** — never inflate it to lift WSJF rank (ADR-076).
292
+ 2. **Effort (S/M/L/XL)** — derive from the described scope (files touched, cross-package, migration-heavy → larger), per ADR-067's t-shirt buckets.
293
+ 3. **Always a real value (ADR-026 grounding without deferral)** — when a comparable prior ticket informs the estimate, cite it inline (e.g. `— cf. P<NNN>`). When no comparable prior exists, **still commit to a real best-effort number** grounded in the description's own signals — do NOT append "not estimated" / "no prior data" or any other re-rate flag (that is a deferral; user correction 2026-06-24). A rough estimate from a thin description is honest estimation; a marker that says "I didn't really estimate this" is the rot this brick removes.
294
+
295
+ Write the derived values into the template's `**Priority**` and `**Effort**` lines (replacing the `<…>` placeholders). Every captured ticket leaves Step 4a with a real Impact × Likelihood and a real Effort bucket — never a placeholder, never a deferral marker.
287
296
 
288
297
  ### 5. Write the file
289
298
 
@@ -341,19 +350,19 @@ After the commit, report:
341
350
  preflight_reason="$(wr-itil-check-deferred-placeholder-staleness "$PWD")"
342
351
  ```
343
352
 
344
- See `/wr-itil:work-problems` SKILL.md Step 0c for the canonical contract on the two-axis trigger (count ≥ 3 deferred placeholders AND README age > 7 days); the threshold constants live in the helper. <!-- DEFERRED-PLACEHOLDER-STALENESS-CONTRACT-SOURCE: packages/itil/lib/check-deferred-placeholder-staleness.sh -->
353
+ See `/wr-itil:work-problems` SKILL.md Step 0c for the canonical contract on the two-axis trigger (count ≥ 3 ungrounded-rating tickets — the ADR-026 `not estimated` sentinel — AND README age > 7 days); the threshold constants live in the helper. <!-- DEFERRED-PLACEHOLDER-STALENESS-CONTRACT-SOURCE: packages/itil/lib/check-deferred-placeholder-staleness.sh -->
345
354
 
346
355
  | `preflight_reason` | Trailing-pointer shape |
347
356
  |---------------------------------------------------|--------------------------------------------------------------------------------------------------------|
348
- | `no-deferred-placeholders` / `below-threshold ...` / `fresh-readme ...` | Default (low-priority) pointer: *"Run `/wr-itil:review-problems` next to re-rate the deferred Priority/Effort placeholders on P\<NNN\>."* (README is now refreshed inline at Step 6 per P199 Option 2 — the pointer no longer names a README-refresh action in the default shape.) |
349
- | `no-readme count=<N>` | **Highlighted (actionable) pointer**: *"⚠ `<N>` deferred-placeholder ticket(s) have accumulated AND `docs/problems/README.md` is missing/malformed — run `/wr-itil:review-problems` NOW to rebuild the README and re-rate placeholders."* (Fires only when README is genuinely missing/malformed despite this skill's inline refresh — a drift class the helper still surfaces.) |
350
- | `stale-readme count=<N> age=<X>s threshold=<Y>s` | **Highlighted (actionable) pointer**: *"⚠ `<N>` deferred-placeholder ticket(s) have accumulated AND the WSJF Rankings cadence is `<X>` days stale (> 7-day threshold) — run `/wr-itil:review-problems` NOW to re-rate placeholders."* (Inline refresh at Step 6 freshens the README mtime on every capture, so this shape now fires only when captures are absent for > 7 days — a real cadence-stale signal, not capture-driven drift.) |
357
+ | `no-deferred-placeholders` / `below-threshold ...` / `fresh-readme ...` | Default (low-priority) pointer: *"Run `/wr-itil:review-problems` next to re-rate the ungrounded (`not estimated no prior data`) ratings."* (README is now refreshed inline at Step 6 per P199 Option 2 — the pointer no longer names a README-refresh action in the default shape.) |
358
+ | `no-readme count=<N>` | **Highlighted (actionable) pointer**: *"⚠ `<N>` ungrounded-rating ticket(s) (ADR-026 `not estimated` sentinel) have accumulated AND `docs/problems/README.md` is missing/malformed — run `/wr-itil:review-problems` NOW to rebuild the README and re-rate placeholders."* (Fires only when README is genuinely missing/malformed despite this skill's inline refresh — a drift class the helper still surfaces.) |
359
+ | `stale-readme count=<N> age=<X>s threshold=<Y>s` | **Highlighted (actionable) pointer**: *"⚠ `<N>` ungrounded-rating ticket(s) (ADR-026 `not estimated` sentinel) have accumulated AND the WSJF Rankings cadence is `<X>` days stale (> 7-day threshold) — run `/wr-itil:review-problems` NOW to re-rate placeholders."* (Inline refresh at Step 6 freshens the README mtime on every capture, so this shape now fires only when captures are absent for > 7 days — a real cadence-stale signal, not capture-driven drift.) |
351
360
 
352
- **Why conditional, not auto-dispatch** (ADR-032 + JTBD-001): capture-problem is intentionally lightweight per ADR-032 P155 amendment. Auto-dispatching review-problems from capture would re-introduce the ~10-turn ceremony the lightweight aside is engineered to avoid. The conditional pointer surfaces the signal "deferred-placeholder backlog has grown AND the cadence axis hit threshold" when both axes fire. The user picks when to absorb the re-rate cost.
361
+ **Why conditional, not auto-dispatch** (ADR-032 + JTBD-001): capture-problem is intentionally lightweight per ADR-032 P155 amendment. Auto-dispatching review-problems from capture would re-introduce the ~10-turn ceremony the lightweight aside is engineered to avoid. The conditional pointer surfaces the signal "ungrounded-rating backlog has grown AND the cadence axis hit threshold" when both axes fire. The user picks when to absorb the re-rate cost.
353
362
 
354
363
  **Fail-soft**: any error in the helper invocation MUST NOT block the report — fall back to the default pointer shape.
355
364
 
356
- The trailing pointer is **not optional** — it is the user-visible signal for deferred-placeholder re-rating cadence. The conditional highlight (P271) escalates the signal when the deferred-placeholder backlog AND the cadence axis both hit threshold. Drift here re-opens P271.
365
+ The trailing pointer is **not optional** — it is the user-visible signal for ungrounded-rating re-rating cadence (P375 re-point: the count is now ADR-026 `not estimated` sentinels, not blanket deferred placeholders). The conditional highlight (P271) escalates the signal when the ungrounded-rating backlog AND the cadence axis both hit threshold. Drift here re-opens P271.
357
366
 
358
367
  <!-- @jtbd JTBD-001 (Enforce Governance Without Slowing Down — conditional highlight escalates the signal without forcing a flow break) -->
359
368
 
@@ -198,30 +198,30 @@ EOF
198
198
  }
199
199
 
200
200
  # ---------------------------------------------------------------------------
201
- # Skeleton-fill ticket shape — capture-problem writes a deferred-placeholder
202
- # ticket. Default Priority and Effort are flagged for re-rate at next review.
201
+ # Skeleton-fill ticket shape — capture-problem DERIVES the Priority/Effort
202
+ # rating at capture (P375 / ADR-032 amendment 2026-06-24). NO deferred false-
203
+ # low placeholder. The ADR-026 sentinel `not estimated — no prior data` is the
204
+ # residual re-rate signal, emitted only when no comparable prior grounds the
205
+ # rating.
203
206
  # ---------------------------------------------------------------------------
204
207
 
205
- @test "capture-problem: skeleton-filled ticket carries the deferred-placeholder pattern" {
206
- # Fixture mirrors the SKILL.md Step 4-5 prescribed write target — per
207
- # ADR-031 per-state-subdir layout (`docs/problems/open/<NNN>-<slug>.md`),
208
- # NOT the pre-ADR-031 flat shape (`docs/problems/<NNN>-<slug>.open.md`).
209
- # P281 (template-refresh sub-shape) corrected the SKILL.md drift; this
210
- # fixture exercises the now-canonical write path.
208
+ @test "capture-problem: skeleton ticket carries a DERIVED rating, not a deferred false-low" {
211
209
  mkdir -p "$TMPROOT/docs/problems/open"
212
210
  TITLE="example-aside-finding"
213
211
  ID="200"
214
212
  TODAY=$(date -u +%Y-%m-%d)
215
213
  DESCRIPTION="Quick observation worth a ticket but not blocking."
216
214
 
217
- # Mirror the SKILL.md skeleton-fill template.
215
+ # Mirror the SKILL.md Step 4a + Step 4 template: a derived rating (real
216
+ # Impact × Likelihood) — a real best-effort value, NO deferral marker of
217
+ # any kind (no "deferred — re-rate", no "not estimated" sentinel).
218
218
  cat > "$TMPROOT/docs/problems/open/${ID}-${TITLE}.md" <<EOF
219
219
  # Problem ${ID}: ${TITLE}
220
220
 
221
221
  **Status**: Open
222
222
  **Reported**: ${TODAY}
223
- **Priority**: 3 (Medium) — Impact: 3 x Likelihood: 1 (deferred — re-rate at next /wr-itil:review-problems)
224
- **Effort**: M (deferred — re-rate at next /wr-itil:review-problems)
223
+ **Priority**: 6 (Medium) — Impact: 3 × Likelihood: 2
224
+ **Effort**: M
225
225
 
226
226
  ## Description
227
227
 
@@ -231,49 +231,42 @@ ${DESCRIPTION}
231
231
 
232
232
  (deferred to investigation)
233
233
 
234
- ## Workaround
235
-
236
- (deferred to investigation)
237
-
238
- ## Impact Assessment
239
-
240
- - **Who is affected**: (deferred to investigation)
241
- - **Frequency**: (deferred to investigation)
242
- - **Severity**: (deferred to investigation)
243
- - **Analytics**: (deferred to investigation)
244
-
245
234
  ## Root Cause Analysis
246
235
 
247
236
  ### Investigation Tasks
248
237
 
249
- - [ ] Re-rate Priority and Effort at next /wr-itil:review-problems
250
238
  - [ ] Investigate root cause
251
239
  - [ ] Create reproduction test
252
240
 
253
- ## Dependencies
254
-
255
- - **Blocks**: (none)
256
- - **Blocked by**: (none)
257
- - **Composes with**: (none)
258
-
259
241
  ## Related
260
242
 
261
243
  (captured via /wr-itil:capture-problem; expand at next investigation)
262
244
  EOF
263
245
 
264
- # Behavioural assertions: ticket file has the load-bearing fields.
265
246
  TICKET="$TMPROOT/docs/problems/open/${ID}-${TITLE}.md"
266
247
  [ -f "$TICKET" ]
267
248
  run grep -F '**Status**: Open' "$TICKET"
268
249
  [ "$status" -eq 0 ]
269
- # Description survives verbatim
270
250
  run grep -F "$DESCRIPTION" "$TICKET"
271
251
  [ "$status" -eq 0 ]
272
- # Deferred placeholders flag re-rating
252
+
253
+ # The dropped false-low default MUST NOT appear.
273
254
  run grep -F 'deferred — re-rate at next /wr-itil:review-problems' "$TICKET"
274
- [ "$status" -eq 0 ]
275
- # Investigation Tasks nudges user to re-rate
255
+ [ "$status" -ne 0 ]
276
256
  run grep -F 'Re-rate Priority and Effort at next /wr-itil:review-problems' "$TICKET"
257
+ [ "$status" -ne 0 ]
258
+ run grep -F 'Likelihood: 1 (deferred' "$TICKET"
259
+ [ "$status" -ne 0 ]
260
+ # No "not estimated" sentinel either — that is just another deferral
261
+ # (user correction 2026-06-24). Every capture carries a real value.
262
+ run grep -F 'not estimated' "$TICKET"
263
+ [ "$status" -ne 0 ]
264
+
265
+ # A real derived Impact × Likelihood is present (not the buried false-low).
266
+ run grep -E 'Impact: [1-5] (×|x) Likelihood: [1-5]' "$TICKET"
267
+ [ "$status" -eq 0 ]
268
+ # And a real Effort bucket with no trailing deferral marker.
269
+ run grep -E '^\*\*Effort\*\*: (S|M|L|XL)$' "$TICKET"
277
270
  [ "$status" -eq 0 ]
278
271
  }
279
272
 
@@ -210,7 +210,7 @@ stories: [<from --stories flag — ordered execution sequence; or [] if --storie
210
210
 
211
211
  ## Commits
212
212
 
213
- (maintained automatically populated by the commit-message RFC trailer hook per ADR-060 Phase 1 item 12; lands in Slice 3 task B5.T9)
213
+ (rendered from `git log --grep "Refs: RFC-<NNN>"` by `/wr-itil:manage-rfc` + `wr-itil-reconcile-rfcs` per ADR-085 a git-log-derived view, not stored per-commit. At capture there are no commits yet.)
214
214
 
215
215
  ## Related
216
216
 
@@ -201,7 +201,7 @@ problems: [P<NNN>, P<NNN>, ...]
201
201
  jtbd: [JTBD-<NNN>, JTBD-<NNN>, ...]
202
202
  rfcs: [<RFC-<NNN>, ...> or empty]
203
203
  story-maps: [<STORY-MAP-<NNN>, ...> or empty]
204
- estimated-effort: deferred
204
+ estimated-effort: <S|M|L|XL — derived at capture per ADR-067 (real best-effort value, no deferral marker)>
205
205
  ---
206
206
 
207
207
  # STORY-<NNN>: <Title>
@@ -212,7 +212,7 @@ estimated-effort: deferred
212
212
  **JTBD**: <JTBD-<NNN> [, ...]>
213
213
  **RFCs**: <RFC-<NNN> [, ...]> or (none — populate at accepted transition per I7)
214
214
  **Story Maps**: <STORY-MAP-<NNN> [, ...]> or (none — populate at accepted transition per I8)
215
- **Estimated effort**: deferred (populate at accepted transition per I10 INVEST Estimable)
215
+ **Estimated effort**: <S|M|L|XL> — derived at capture as a real best-effort value (P375 / ADR-032 amendment 2026-06-24; ADR-067 silent-derivation). NO `deferred` default and no "not estimated" marker — those are deferrals (user correction 2026-06-24). Confirmed/refined at the accepted transition per I10 INVEST Estimable.
216
216
 
217
217
  ## User value (required, INVEST Valuable)
218
218
 
@@ -198,7 +198,7 @@ problems: [P170]
198
198
  jtbd: [JTBD-008]
199
199
  rfcs: []
200
200
  story-maps: []
201
- estimated-effort: deferred
201
+ estimated-effort: M
202
202
  ---
203
203
 
204
204
  # STORY-${next_id}: ${description}
@@ -305,7 +305,7 @@ Commit the completed work per ADR-014 (governance skills commit their own work):
305
305
  - Incident mitigated: `docs(incidents): I<NNN> mitigated — <mitigation summary>`
306
306
  - Incident restored: `docs(incidents): I<NNN> restored — <action>`
307
307
  - Incident closed: `docs(incidents): close I<NNN>`
308
- 4. If risk is above appetite: use `AskUserQuestion` to ask whether to commit anyway, remediate first, or park the work. This is the ADR-044 **category-3 (one-time-override)** surface in incident-mitigation context the tech lead may need to ship a fix despite higher residual risk to restore service fast (JTBD-201); the rule (RISK-POLICY appetite) still stands but this specific case warrants an exception. The 3-option vocabulary (commit anyway / remediate / park) is the genuine category-3 surface. If `AskUserQuestion` is unavailable, skip the commit and report the uncommitted state clearly per ADR-013 Rule 6.
308
+ 4. If commit risk is above appetite: this is **framework-mediated, NOT a category-3 one-time-override ask** (P377/RFC-029 amendment 2026-06-24 there is no incident carve-out). Per **ADR-042 Rule 1b (incident-context scoring)**: an active incident is a risk being realised (Likelihood already 5), so the incident-response change is scored against that live realised-risk baseline — weighing P(the change increases impact) vs P(it reduces impact & likelihood / restores service) + P(it introduces a new incident). If net risk-reducing, it takes the risk-reducing path (the `reducing` bypass — `RISK_BYPASS: reducing`) and proceeds, no ask. This is *better* for restore-service-fast (JTBD-201): a genuine hotfix clears via scoring with no consent gate mid-outage. If the change is NOT net-reducing, auto-remediate per ADR-042 Rule 1 or halt per Rule 5. **MUST NOT commit above appetite; MUST NOT `AskUserQuestion` "commit anyway".** If `AskUserQuestion` is unavailable, the ADR-013 Rule 6 fail-safe applies — skip the commit and report the uncommitted state.
309
309
 
310
310
  ### 15. Auto-release when changesets are queued (ADR-020)
311
311
 
@@ -1080,7 +1080,7 @@ Commit the completed work per ADR-014 (governance skills commit their own work):
1080
1080
  - Problem closed: `docs(problems): close P<NNN> <title>`
1081
1081
  - Review/re-rank: `docs(problems): review — re-rank priorities`
1082
1082
  - Fix implemented: `fix(<scope>): <description> (closes P<NNN>)` — include problem file changes (rename to `.verifying.md` + `## Fix Released` section) in the same commit per ADR-022
1083
- 5. If risk is above appetite: use `AskUserQuestion` to ask whether to commit anyway, remediate first, or park the work. If `AskUserQuestion` is unavailable, skip the commit and report the uncommitted state clearly (ADR-013 Rule 6 fail-safe). This applies only to the risk-above-appetite branch, not to the delegation-unavailable case above.
1083
+ 5. If commit risk is above appetite: auto-apply scorer remediations per **ADR-042 Rule 1** incrementally until residual commit risk is within appetite (≤ 4/25), OR halt per ADR-042 Rule 5 if the scorer cannot converge. **The skill MUST NOT commit above appetite, and MUST NOT call `AskUserQuestion` to ask whether to commit anyway** (P377/RFC-029 amendment 2026-06-24 — above-appetite is framework-mediated, never a category-3 one-time-override; same invariant the push/release branch at Step 12 already enforces). The ADR-013 Rule 6 fail-safe (no `AskUserQuestion` available / non-interactive → skip the commit and report the uncommitted state) remains the terminal fallback. This applies only to the risk-above-appetite branch, not to the delegation-unavailable case above.
1084
1084
 
1085
1085
  **Multi-commit slice changeset discipline (P141 Phase 2)**: when a single logical fix lands across multiple ADR-014-grain commits targeting the same plugin (e.g. helper extraction in commit 1, callers wired in commit 2, SKILL note + transition in commit 3 — all `packages/<plugin>/`), author ONE changeset on the first commit in the slice. Subsequent same-plugin commits do NOT need their own changeset — the `itil-changeset-discipline.sh` hook's Check 2b recognises any `.changeset/*.md` (or held-window `docs/changesets-holding/*.md` entry) already in the unpushed slice scope (`origin/<base>..HEAD` + untracked + modified-not-staged) that targets `"@windyroad/<plugin>": <any-bump>` and allows. This eliminates the per-commit changeset ceremony that previously produced N redundant `.changeset/*.md` files for one logical release entry (changesets-action collapses bump-class at version-package time, so per-commit changesets rendered N near-identical CHANGELOG bullets for one release). Once a changeset hits `origin/<base>` (drained at release time), it no longer counts — a fresh changeset is required for the next slice. Cross-plugin coverage is NOT permitted: an `@windyroad/itil` changeset does not satisfy a `packages/voice-tone/` commit.
1086
1086
 
@@ -135,6 +135,21 @@ git add docs/rfcs/RFC-<NNN>-<slug>.<new-status>.md
135
135
 
136
136
  **P057 staging trap rule**: re-stage explicitly after the Edit tool runs. `git mv` alone stages only the rename, not subsequent content edits.
137
137
 
138
+ #### Auto-transition detection (P378/RFC-030 Piece 2; ADR-060 line 292)
139
+
140
+ `proposed → in-progress` is **detected** by `itil-commit-trailer-transition-advisory.sh` (PostToolUse:Bash; the shared RFC+story commit-trailer trigger): when the first non-capture commit carrying `Refs: RFC-<NNN>` lands while the RFC is still `proposed`/`accepted`, the hook emits a stderr advisory. Per ADR-014 the hook does NOT perform the `git mv` (that would land outside the commit grain) — this skill (or an AFK orchestrator acting on the advisory) performs the transition. Mirrors the manage-story draft→in-progress trigger; both share the one hook. (This closes the "future commit-trailer-trigger hook (deferred)" that manage-rfc and manage-story both named but never built.)
141
+
142
+ #### Render the `## Commits` section (P378/RFC-030 Piece 1; ADR-085)
143
+
144
+ On every transition AND every `review` (Step 9), render the affected RFC's `## Commits` section from git history — it is a derived view (`git log --grep "Refs: RFC-NNN"`), NOT stored per-commit, so it must be regenerated skill-side (a PostToolUse hook can't write it without breaking the ADR-014 commit grain). Invoke the helper before staging the RFC file:
145
+
146
+ ```bash
147
+ wr-itil-update-rfc-commits-section docs/rfcs/RFC-<NNN>-<slug>.<status>.md
148
+ git add docs/rfcs/RFC-<NNN>-<slug>.<status>.md
149
+ ```
150
+
151
+ `wr-itil-update-rfc-commits-section` is the ADR-049 `$PATH` shim for `packages/itil/scripts/update-rfc-commits-section.sh` (idempotent — a current section is a byte-stable no-op, so the `git add` is a no-op when nothing changed). This closes the never-built ADR-060 item 12; the section is honestly rendered-as-of-this-skill-run, never a false "maintained automatically" claim.
152
+
138
153
  #### README refresh on every transition (P062 mirror)
139
154
 
140
155
  After renaming + Editing + `git add`-ing the transitioned RFC file, regenerate `docs/rfcs/README.md` in-place reflecting the new filename set and the transitioned RFC's new Status. Stage the refreshed README with the same commit.
@@ -116,7 +116,7 @@ For any transition `<from> → <to>`:
116
116
 
117
117
  The auto-transition logic fires in two contexts:
118
118
 
119
- - **`draft → in-progress`**: when the FIRST commit AFTER the story's capture commit lands with a `Refs: STORY-<NNN>` trailer AND a commit subject NOT prefixed with `feat(itil): capture STORY-`. Detected by a future commit-trailer-trigger hook (deferred to a hook-source slice); manual `manage-story <NNN> in-progress` invocation works in the interim.
119
+ - **`draft → in-progress`**: when the FIRST commit AFTER the story's capture commit lands with a `Refs: STORY-<NNN>` trailer AND a commit subject NOT prefixed with `feat(itil): capture STORY-`. **Detected by `itil-commit-trailer-transition-advisory.sh`** (PostToolUse:Bash; the shared RFC+story commit-trailer trigger P378/RFC-030 Piece 2, no longer "a future deferred hook"). Per ADR-014 the hook DETECTS + emits a stderr advisory; it does NOT perform the `git mv` itself (that would land outside the commit grain). The transition is performed by `manage-story <NNN> in-progress` (run on the advisory) or by an AFK orchestrator acting on it.
120
120
 
121
121
  - **`in-progress → done`**: when all `- [ ]` lines in `## Acceptance criteria` are ticked AND the linked RFC is `closed`. Detected at manage-rfc close-fire (the RFC's transition triggers a sweep of its `stories:` array; each in-progress story with all-criteria-ticked auto-transitions to `done`). Manual `manage-story <NNN> done` invocation works in the interim.
122
122
 
@@ -168,7 +168,7 @@ Per ADR-014, governance skills commit their own work.
168
168
  1. `git add` the renamed / modified incident file.
169
169
  2. Delegate to `wr-risk-scorer:pipeline` (subagent_type: `wr-risk-scorer:pipeline`) to assess the staged changes and create a bypass marker. If the subagent type is not available (spawned subagent surface), invoke `/wr-risk-scorer:assess-release` via the Skill tool instead — per ADR-015 it wraps the same pipeline subagent.
170
170
  3. `git commit -m "docs(incidents): I<NNN> mitigated — <action summary>"`.
171
- 4. If risk is above appetite: use `AskUserQuestion` to ask whether to commit anyway, remediate first, or park the work. This is the ADR-044 **category-3 (one-time-override)** surface the rule (RISK-POLICY appetite) still stands but this specific case in incident-mitigation context often warrants an exception (the tech lead may need to ship a mitigation despite higher residual risk to restore service fast — JTBD-201). The 3-option vocabulary is the genuine category-3 surface. If `AskUserQuestion` is unavailable (rare in incident context incident skills are interactive by definition), skip the commit and report the uncommitted state clearly per ADR-013 Rule 6.
171
+ 4. If commit risk is above appetite: this is **framework-mediated, NOT a category-3 one-time-override ask** (P377/RFC-029 no incident carve-out). Per **ADR-042 Rule 1b (incident-context scoring)**: an active incident is a risk being realised (Likelihood already 5); score the mitigation against that live realised-risk baseline P(increases impact) vs P(reduces impact & likelihood / restores service) + P(introduces a new incident). Net risk-reducing → it takes the risk-reducing path (`RISK_BYPASS: reducing`) and proceeds, no ask exactly the restore-service-fast (JTBD-201) case, cleared via scoring not a consent gate. Not net-reducing auto-remediate (ADR-042 Rule 1) or halt (Rule 5). **MUST NOT commit above appetite; MUST NOT `AskUserQuestion` "commit anyway".** If `AskUserQuestion` is unavailable, ADR-013 Rule 6 fail-safe — skip + report.
172
172
 
173
173
  ### 9. Auto-release when changesets are queued (ADR-020)
174
174
 
@@ -137,7 +137,7 @@ Per ADR-014, governance skills commit their own work.
137
137
  1. `git add` the renamed / modified incident file.
138
138
  2. Delegate to `wr-risk-scorer:pipeline` (subagent_type: `wr-risk-scorer:pipeline`) to assess the staged changes and create a bypass marker. If the subagent type is not available (spawned subagent surface), invoke `/wr-risk-scorer:assess-release` via the Skill tool instead — per ADR-015 it wraps the same pipeline subagent.
139
139
  3. `git commit -m "docs(incidents): I<NNN> restored — <verification signal summary>"`.
140
- 4. If risk is above appetite: use `AskUserQuestion` to ask whether to commit anyway, remediate first, or park the work. If `AskUserQuestion` is unavailable, skip the commit and report the uncommitted state clearly.
140
+ 4. If commit risk is above appetite: **framework-mediated, no ask** (P377/RFC-029 no incident carve-out). Per **ADR-042 Rule 1b**: score the restore action against the live realised-risk baseline (incident Likelihood already 5) — net risk-reducing actions take the risk-reducing path (`RISK_BYPASS: reducing`) and proceed; otherwise auto-remediate (Rule 1) or halt (Rule 5). **MUST NOT commit above appetite; MUST NOT `AskUserQuestion` "commit anyway".** If `AskUserQuestion` is unavailable, ADR-013 Rule 6 fail-safe — skip the commit and report the uncommitted state.
141
141
 
142
142
  ### 9. Auto-release when changesets are queued (ADR-020)
143
143
 
@@ -307,7 +307,7 @@ Omit `docs/problems/README-history.md` from the path list when the P134 rotation
307
307
  - Known Error → Verification Pending (standalone, no fix riding with it): `docs(problems): P<NNN> verification pending — <release marker>`
308
308
  - Verification Pending → Closed: `docs(problems): close P<NNN> <title>`
309
309
 
310
- If risk is above appetite and `AskUserQuestion` is available: ask whether to commit anyway, remediate first, or park the work. If `AskUserQuestion` is unavailable (AFK), skip the commit and report the uncommitted state (ADR-013 Rule 6 fail-safe). This applies only to the risk-above-appetite branch, not to the delegation-unavailable case above.
310
+ If commit risk is above appetite: auto-apply scorer remediations per **ADR-042 Rule 1** until residual risk is within appetite, OR halt per ADR-042 Rule 5 if the scorer cannot converge. **MUST NOT commit above appetite; MUST NOT call `AskUserQuestion` to ask whether to commit anyway** (P377/RFC-029 — above-appetite is framework-mediated, not a one-time-override). If `AskUserQuestion` is unavailable (AFK), the ADR-013 Rule 6 fail-safe applies as the terminal fallback: skip the commit and report the uncommitted state. This applies only to the risk-above-appetite branch, not to the delegation-unavailable case above.
311
311
 
312
312
  ### 9. Report the outcome
313
313
 
@@ -212,7 +212,7 @@ ONE commit covers: every surviving renamed ticket file + every Status edit + eve
212
212
  - Mixed-destination batch: `docs(problems): batch transition — P063 close, P070 known-error, P094 verifying (3 tickets)`
213
213
  - If the batch destination is `verifying` and rides with a fix commit, the commit-message scope follows the singular's pattern: `fix(<scope>): <description> (closes P063, P067)` — but the batch surface is unusual in the riding-with-fix shape; the canonical caller is verification-close housekeeping, not fix-release.
214
214
 
215
- If risk is above appetite and `AskUserQuestion` is available: ask whether to commit anyway, remediate first, or park. If `AskUserQuestion` is unavailable (AFK), skip the commit and report the uncommitted state per ADR-013 Rule 6 fail-safe — apply the same rule the singular does at the same gate.
215
+ If commit risk is above appetite: auto-apply scorer remediations per **ADR-042 Rule 1** until within appetite, OR halt per ADR-042 Rule 5. **MUST NOT commit above appetite; MUST NOT call `AskUserQuestion` to ask whether to commit anyway** (P377/RFC-029 — framework-mediated, not a one-time-override). If `AskUserQuestion` is unavailable (AFK), the ADR-013 Rule 6 fail-safe applies — skip the commit and report the uncommitted state — apply the same rule the singular does at the same gate.
216
216
 
217
217
  ### 5. Report the outcome
218
218