@windyroad/itil 0.25.0 → 0.26.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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "wr-itil",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "ITIL-aligned IT service management for Claude Code"
5
5
  }
package/README.md CHANGED
@@ -86,6 +86,8 @@ See [ADR-011](../../docs/decisions/011-manage-incident-skill.proposed.md) for th
86
86
  | `/wr-itil:review-problems` | Re-rate every open and known-error ticket and refresh the WSJF ranking |
87
87
  | `/wr-itil:reconcile-readme` | Detect and correct drift between `docs/problems/README.md` and on-disk ticket inventory |
88
88
  | `/wr-itil:report-upstream` | Report a local problem as a structured issue against an upstream repository (ADR-024) |
89
+ | `/wr-itil:capture-rfc` | Lightweight RFC-capture skill — mandatory problem-trace per ADR-060 I1 invariant; opens a coordinated multi-commit change traceable to ≥ 1 driving problem (Phase 1 of the Problem-RFC-Story framework, P170 / ADR-060) |
90
+ | `/wr-itil:manage-rfc` | Heavyweight RFC intake + lifecycle management — proposed → accepted → in-progress → verifying → closed; sibling to `manage-problem` at the RFC tier (ADR-060) |
89
91
  | `/wr-itil:manage-incident` | Declare, triage, mitigate, and close an incident with evidence-first discipline |
90
92
  | `/wr-itil:list-incidents` | Read-only display of active incidents by severity |
91
93
  | `/wr-itil:mitigate-incident` / `/wr-itil:restore-incident` / `/wr-itil:close-incident` / `/wr-itil:link-incident` | Incident lifecycle transitions (ADR-011) |
@@ -106,6 +108,7 @@ This plugin serves the [Jobs to be Done](../../docs/jtbd/) below. Per [ADR-051](
106
108
  ### Solo developer
107
109
 
108
110
  - **[JTBD-006 Progress the Backlog While I'm Away](../../docs/jtbd/solo-developer/JTBD-006-work-backlog-afk.proposed.md)** — `/wr-itil:work-problems` is the AFK orchestrator that loops through the WSJF-ranked backlog, working tickets without interactive input until quota or a stop condition fires.
111
+ - **[JTBD-008 Decompose a Fix Into Coordinated Changes](../../docs/jtbd/solo-developer/JTBD-008-decompose-fix-into-coordinated-changes.proposed.md)** — `/wr-itil:capture-rfc` + `/wr-itil:manage-rfc` are the capture-time decomposition surface for multi-commit coordinated changes traced to a driving problem; the I1 trace-to-problem invariant is gate-enforced at capture-rfc time (P170 / ADR-060).
109
112
 
110
113
  ### Plugin user (currency anchor)
111
114
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ exec "$(dirname "$0")/../scripts/reconcile-rfcs.sh" "$@"
package/hooks/hooks.json CHANGED
@@ -37,6 +37,12 @@
37
37
  "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-changeset-discipline.sh" }]
38
38
  }
39
39
  ],
40
+ "PostToolUse": [
41
+ {
42
+ "matcher": "Bash",
43
+ "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-rfc-trailer-advisory.sh" }]
44
+ }
45
+ ],
40
46
  "Stop": [
41
47
  { "hooks": [{ "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/hooks/itil-assistant-output-review.sh" }] }
42
48
  ]
@@ -0,0 +1,198 @@
1
+ #!/bin/bash
2
+ # P170: PostToolUse:Bash hook — detects `git commit` invocations whose
3
+ # HEAD commit message carries a `Refs: RFC-<NNN>` trailer (the
4
+ # commit-message RFC trailer convention introduced by ADR-060 Phase 1
5
+ # item 12 + finding 8). For each such trailer, the hook checks whether
6
+ # the driving problem ticket(s) `## RFCs` reverse-trace section lists
7
+ # the RFC; emits a stderr advisory when stale.
8
+ #
9
+ # This is the SECONDARY surface for the auto-maintained `## RFCs` section
10
+ # contract. The PRIMARY surface is skill-side inline refresh in
11
+ # `/wr-itil:capture-rfc` Step 6 + `/wr-itil:manage-rfc` Step 7+9 (architect
12
+ # Q1 verdict). The hook is the drift-detection backstop for ARBITRARY
13
+ # commits — `feat(...)` / `fix(...)` / `chore(...)` commits that carry
14
+ # `Refs: RFC-<NNN>` trailers but were authored OUTSIDE the RFC skills
15
+ # and therefore did not run the inline refresh.
16
+ #
17
+ # Advisory-only per architect Q2 verdict + ADR-014 single-commit grain:
18
+ # the hook NEVER auto-fixes (no follow-up commit; no working-tree edit
19
+ # after the commit lands). It emits a stderr advisory pointing the
20
+ # user at `/wr-itil:manage-rfc <RFC-NNN>` to refresh the reverse-trace
21
+ # in a subsequent commit, OR at `wr-itil-reconcile-rfcs docs/rfcs
22
+ # docs/problems` to verify drift in batch.
23
+ #
24
+ # Allow paths (silent-on-pass per ADR-045 Pattern 1):
25
+ # - tool_name != "Bash" (only Bash invocations gated)
26
+ # - command lacks `git commit` (non-commit Bash bypasses)
27
+ # - BYPASS_RFC_TRAILER_ADVISORY=1 (env-var escape)
28
+ # - outside git work tree
29
+ # - no `./docs/rfcs/` (RFC framework not adopted)
30
+ # - no `./docs/problems/` (problem framework not adopted)
31
+ # - HEAD commit lacks Refs: RFC trailer
32
+ # - all referenced RFCs' driving problems' `## RFCs` tables are current
33
+ #
34
+ # Multi-`Refs:` malformed-per-finding-8 advisory: when HEAD's commit
35
+ # message carries multiple `Refs: RFC-<NNN>` trailer lines, the hook
36
+ # emits a malformed-per-finding-8 advisory directing the user to split
37
+ # the commit (one commit advances at most one RFC per ADR-060 finding
38
+ # 8). The split itself is user-side work; the hook flags only.
39
+ #
40
+ # Failmode (per ADR-013 Rule 6 fail-open): on parse error in the
41
+ # trailer, on a Refs: trailer pointing to a non-existent RFC file
42
+ # (could be a capture-rfc invocation in flight), on `git interpret-trailers`
43
+ # failure — exit 0 silently. The reconcile-rfcs.sh batch surface
44
+ # catches these cases at the next manage-rfc Step 0 preflight per
45
+ # Confirmation criterion 3.
46
+ #
47
+ # Cost: one git invocation per `git commit` Bash call (~30-60ms). No
48
+ # marker (per-invocation deterministic; mirrors P125 staging-detect.sh
49
+ # and P141 itil-changeset-discipline.sh precedent).
50
+ #
51
+ # References:
52
+ # ADR-005 — plugin testing strategy (hook bats live under hooks/test/).
53
+ # ADR-013 Rule 6 — fail-open on missing inputs / parse errors.
54
+ # ADR-014 — single-commit grain (this hook never auto-fixes via
55
+ # follow-up commit; advisory-only).
56
+ # ADR-038 — progressive disclosure / advisory band ≤300 bytes.
57
+ # ADR-045 — hook injection budget; Pattern 1 silent-on-pass.
58
+ # ADR-051 — load-bearing-from-the-start (drift detection ships at
59
+ # the same time as the convention; never deferred).
60
+ # ADR-052 — behavioural-tests default; bats live alongside.
61
+ # ADR-060 — Phase 1 item 12 + Confirmation criterion 3.
62
+ # P170 — driving problem ticket.
63
+ # P081 — behavioural tests preferred over structural greps.
64
+ # P125 — sibling per-invocation no-marker hook precedent.
65
+ # P141 — sibling commit-time gate hook precedent.
66
+
67
+ INPUT=$(cat)
68
+
69
+ TOOL_NAME=$(echo "$INPUT" | python3 -c "
70
+ import sys, json
71
+ try:
72
+ data = json.load(sys.stdin)
73
+ print(data.get('tool_name', ''))
74
+ except:
75
+ print('')
76
+ " 2>/dev/null || echo "")
77
+
78
+ # Only fire on Bash.
79
+ if [ "$TOOL_NAME" != "Bash" ]; then
80
+ exit 0
81
+ fi
82
+
83
+ COMMAND=$(echo "$INPUT" | python3 -c "
84
+ import sys, json
85
+ try:
86
+ data = json.load(sys.stdin)
87
+ print(data.get('tool_input', {}).get('command', ''))
88
+ except:
89
+ print('')
90
+ " 2>/dev/null || echo "")
91
+
92
+ # Only fire on `git commit` invocations.
93
+ case "$COMMAND" in
94
+ *"git commit"*) ;;
95
+ *) exit 0 ;;
96
+ esac
97
+
98
+ # Bypass via env var.
99
+ if [ "${BYPASS_RFC_TRAILER_ADVISORY:-}" = "1" ]; then
100
+ exit 0
101
+ fi
102
+
103
+ # Fail-open if not in a git work tree.
104
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
105
+
106
+ # Fail-open if RFC / problem framework not adopted.
107
+ [ -d "./docs/rfcs" ] || exit 0
108
+ [ -d "./docs/problems" ] || exit 0
109
+
110
+ # Read HEAD commit message.
111
+ COMMIT_MSG=$(git log -1 --format='%B' HEAD 2>/dev/null) || exit 0
112
+ [ -n "$COMMIT_MSG" ] || exit 0
113
+
114
+ # Parse `Refs:` trailers via git's native parser. The `key=Refs` filter
115
+ # extracts every Refs: line; we then keep only those naming RFC-<NNN>.
116
+ TRAILERS=$(printf '%s\n' "$COMMIT_MSG" | git interpret-trailers --parse 2>/dev/null \
117
+ | grep -E '^Refs:[[:space:]]+RFC-[0-9]{3}' || true)
118
+
119
+ # No RFC trailer → silent.
120
+ [ -n "$TRAILERS" ] || exit 0
121
+
122
+ # Multi-Refs: malformed-per-finding-8 advisory.
123
+ TRAILER_COUNT=$(printf '%s\n' "$TRAILERS" | wc -l | tr -d ' ')
124
+ if [ "$TRAILER_COUNT" -gt 1 ]; then
125
+ echo "P170 ADVISORY: HEAD commit carries ${TRAILER_COUNT} Refs: RFC trailers (malformed-per-finding-8 — one commit advances at most one RFC per ADR-060). Recovery: split the commit; rerun /wr-itil:manage-rfc on each RFC. Bypass: BYPASS_RFC_TRAILER_ADVISORY=1." >&2
126
+ exit 0
127
+ fi
128
+
129
+ # Single trailer — extract RFC ID.
130
+ RFC_ID=$(printf '%s' "$TRAILERS" | grep -oE 'RFC-[0-9]{3}' | head -1)
131
+ [ -n "$RFC_ID" ] || exit 0
132
+ RFC_NUM="${RFC_ID#RFC-}"
133
+
134
+ # Locate the RFC file (any status suffix).
135
+ shopt -s nullglob
136
+ RFC_FILES=(./docs/rfcs/RFC-${RFC_NUM}-*.md)
137
+ shopt -u nullglob
138
+ if [ ${#RFC_FILES[@]} -eq 0 ]; then
139
+ # RFC file not yet on disk (capture-rfc may be in flight). Fail-open.
140
+ exit 0
141
+ fi
142
+ RFC_FILE="${RFC_FILES[0]}"
143
+
144
+ # Parse RFC frontmatter `problems: [P<NNN>, P<NNN>, ...]`.
145
+ RAW_PROBLEMS=$(awk '/^problems:/ { print; exit }' "$RFC_FILE")
146
+ INNER=$(echo "$RAW_PROBLEMS" | sed -E 's/^[[:space:]]*problems:[[:space:]]*\[//; s/\][[:space:]]*$//')
147
+
148
+ # Tokenise.
149
+ PIDS=()
150
+ while IFS= read -r tok; do
151
+ tok=$(echo "$tok" | tr -d ' "'\''')
152
+ case "$tok" in
153
+ P[0-9][0-9][0-9]) PIDS+=("$tok") ;;
154
+ esac
155
+ done <<< "$(echo "$INNER" | tr ',' '\n')"
156
+
157
+ # No claims → fail-open (RFC has no problem trace; trailer hook can't
158
+ # verify anything).
159
+ if [ ${#PIDS[@]} -eq 0 ]; then
160
+ exit 0
161
+ fi
162
+
163
+ # For each PID, check whether the problem's `## RFCs` table lists RFC_ID.
164
+ STALE_PIDS=""
165
+ for pid in "${PIDS[@]}"; do
166
+ pnum="${pid#P}"
167
+ shopt -s nullglob
168
+ PFILES=(./docs/problems/${pnum}-*.md)
169
+ shopt -u nullglob
170
+ if [ ${#PFILES[@]} -eq 0 ]; then
171
+ # Problem file missing — fail-open (could be a stale frontmatter
172
+ # claim or a problem in flight).
173
+ continue
174
+ fi
175
+ pfile="${PFILES[0]}"
176
+
177
+ # Locate `## RFCs` section.
178
+ sec_start=$(awk '/^## RFCs[[:space:]]*$/ { print NR; exit }' "$pfile")
179
+ if [ -z "$sec_start" ]; then
180
+ STALE_PIDS="${STALE_PIDS:+$STALE_PIDS,}$pid"
181
+ continue
182
+ fi
183
+
184
+ # Check if the section lists RFC_ID.
185
+ if ! awk -v start="$sec_start" -v rid="$RFC_ID" '
186
+ NR > start && /^## / { exit }
187
+ NR > start && index($0, rid) > 0 { print "found"; exit }
188
+ ' "$pfile" | grep -q found; then
189
+ STALE_PIDS="${STALE_PIDS:+$STALE_PIDS,}$pid"
190
+ fi
191
+ done
192
+
193
+ # No drift → silent.
194
+ [ -n "$STALE_PIDS" ] || exit 0
195
+
196
+ # Emit advisory (per ADR-045 ≤300 byte band; stderr; exit 0).
197
+ echo "P170 ADVISORY: HEAD commit ${RFC_ID} trailer; problem(s) ${STALE_PIDS} ## RFCs section stale (skill-side refresh missed). Recovery: /wr-itil:manage-rfc ${RFC_ID} OR wr-itil-reconcile-rfcs docs/rfcs docs/problems. Bypass: BYPASS_RFC_TRAILER_ADVISORY=1." >&2
198
+ exit 0
@@ -52,6 +52,36 @@ mark_step2_complete() {
52
52
  : > "/tmp/manage-problem-grep-${SESSION_ID}"
53
53
  }
54
54
 
55
+ # Returns 0 if the RFC capture-step marker exists for SESSION_ID; 1 otherwise.
56
+ # Empty SESSION_ID => returns 1 (no marker).
57
+ #
58
+ # Sibling marker per architect verdict on capture-rfc sub-decision (a) —
59
+ # preserves audit-trail per-surface granularity (problem-tier vs RFC-tier
60
+ # capture). Marker name: /tmp/wr-itil-rfc-capture-grep-${SESSION_ID}.
61
+ #
62
+ # Usage: if check_rfc_capture_gate "$SESSION_ID"; then exit 0; fi
63
+ check_rfc_capture_gate() {
64
+ local SESSION_ID="$1"
65
+ [ -n "$SESSION_ID" ] || return 1
66
+ [ -f "/tmp/wr-itil-rfc-capture-grep-${SESSION_ID}" ]
67
+ }
68
+
69
+ # Writes the RFC capture-step marker for SESSION_ID. Empty SESSION_ID => no-op.
70
+ # Idempotent — safe to call more than once per session.
71
+ #
72
+ # Per ADR-060 + capture-rfc Step 2: the marker records that capture-rfc has
73
+ # run its problem-trace validation pass (the RFC-tier analogue of the
74
+ # manage-problem Step 2 duplicate-grep). Per-session scope so a single
75
+ # capture-rfc invocation may write multiple RFC files (e.g. multi-problem
76
+ # trace splits) without re-validating.
77
+ #
78
+ # Usage: mark_rfc_capture_complete "$SESSION_ID"
79
+ mark_rfc_capture_complete() {
80
+ local SESSION_ID="$1"
81
+ [ -n "$SESSION_ID" ] || return 0
82
+ : > "/tmp/wr-itil-rfc-capture-grep-${SESSION_ID}"
83
+ }
84
+
55
85
  # Emit fail-closed deny JSON for PreToolUse hooks.
56
86
  # Usage: create_gate_deny "BLOCKED: <reason>"
57
87
  create_gate_deny() {
@@ -1,35 +1,53 @@
1
1
  #!/bin/bash
2
2
  # P119: PreToolUse:Write enforcement hook for new problem ticket creation.
3
+ # P170 / ADR-060: extended to also gate new RFC ticket creation under docs/rfcs/.
3
4
  #
4
- # BLOCKS Write to docs/problems/<NNN>-*.<status>.md when the file does not
5
- # yet exist on disk and the per-session Step-2 grep marker is absent.
6
- # Marker is set by /wr-itil:manage-problem Step 2 (duplicate-check) at the
7
- # end of its grep pass — present marker means the agent has run the
8
- # duplicate-check for this session and may write new tickets.
5
+ # BLOCKS Write to:
6
+ # - docs/problems/<NNN>-*.<status>.md (when file does not yet exist
7
+ # and Step-2 grep marker absent)
8
+ # - docs/rfcs/RFC-<NNN>-*.<status>.md (when file does not yet exist
9
+ # and capture-rfc marker absent)
10
+ #
11
+ # Marker convention:
12
+ # - /tmp/manage-problem-grep-${SESSION_ID} (problems tier;
13
+ # set by manage-problem /
14
+ # capture-problem Step 2)
15
+ # - /tmp/wr-itil-rfc-capture-grep-${SESSION_ID} (RFC tier;
16
+ # set by capture-rfc Step 2 /
17
+ # manage-rfc creation flow)
9
18
  #
10
19
  # Out of scope (allow-listed):
11
- # - docs/problems/README.md — regenerated by Steps 5/6/7 (chicken-and-egg)
20
+ # - docs/problems/README.md — regenerated by manage-problem Steps 5/6/7
21
+ # - docs/rfcs/README.md — regenerated by manage-rfc transitions / review
12
22
  # - Existing files — Edit-flow / status transitions are
13
- # governed by /wr-itil:transition-problem,
14
- # not new-ticket creation
23
+ # governed by /wr-itil:transition-problem and
24
+ # /wr-itil:manage-rfc respectively
15
25
  # - Edit tool — only Write is gated; Edit on existing
16
26
  # tickets is normal status-transition shape
17
- # - Non-ticket basenames — only docs/problems/<NNN>-*.md ticket-shaped
18
- # paths are gated (NOTES.md, archive/, etc.
19
- # are project housekeeping, not tickets)
27
+ # - Non-ticket basenames — only ticket-shaped paths are gated:
28
+ # problems: NNN-*.md (3-digit prefix)
29
+ # rfcs: RFC-NNN-*.md
30
+ # Other docs (NOTES.md, archive/, etc.)
31
+ # are project housekeeping, not tickets
20
32
  #
21
- # ADR-031 forward-compat: matcher uses docs/problems/ prefix + numeric
22
- # basename, agnostic to current suffix-based layout (docs/problems/NNN-*.<status>.md)
23
- # vs. future per-state subdirectory layout (docs/problems/<state>/NNN-*.md).
33
+ # ADR-031 forward-compat: matcher uses path-prefix + ticket-shape basename,
34
+ # agnostic to current suffix-based layout vs. future per-state subdirectory
35
+ # layout. Branch deny message names the right skill (manage-problem /
36
+ # capture-problem for problems-tier; capture-rfc / manage-rfc for RFC-tier).
24
37
  #
25
38
  # References:
26
- # ADR-009 — gate marker lifecycle (per-session /tmp markers).
27
- # ADR-013 Rule 1 — deny redirects to /wr-itil:manage-problem where
28
- # Step 2 fires AskUserQuestion if duplicates exist.
29
- # ADR-022 — problem lifecycle (status suffixes covered: open / known-error /
30
- # verifying / parked).
31
- # ADR-038 — progressive disclosure (deny message stays terse + actionable).
32
- # P119 agent bypasses Step 2 by writing tickets directly.
39
+ # ADR-009 — gate marker lifecycle (per-session /tmp markers).
40
+ # ADR-013 Rule 1 — deny redirects to the relevant skill (manage-problem
41
+ # for problems; capture-rfc for RFCs) where Step 2 fires.
42
+ # ADR-022 — problem lifecycle (status suffixes covered: open / known-error /
43
+ # verifying / parked).
44
+ # ADR-038 — progressive disclosure (deny message stays terse + actionable).
45
+ # ADR-051 load-bearing-from-the-start (RFC I1 trace-to-problem invariant
46
+ # ships as a hard-block from day one, not advisory-then-escalate).
47
+ # ADR-060 — Problem-RFC-Story framework; introduces RFC tier + I1
48
+ # hard-block at capture-rfc.
49
+ # P119 — agent bypasses Step 2 by writing tickets directly.
50
+ # P170 — driver problem ticket for the RFC framework introduction.
33
51
 
34
52
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
35
53
  # shellcheck source=lib/create-gate.sh
@@ -81,29 +99,44 @@ if [ -z "$SESSION_ID" ]; then
81
99
  exit 0
82
100
  fi
83
101
 
84
- # Match docs/problems/ paths only. Both absolute (project-root prefixed)
85
- # and relative shapes are accepted because Claude Code passes absolute
86
- # paths in tool_input.file_path but tests and direct invocations may
87
- # use relative paths.
102
+ # Match docs/problems/ OR docs/rfcs/ paths only. Both absolute
103
+ # (project-root prefixed) and relative shapes are accepted because
104
+ # Claude Code passes absolute paths in tool_input.file_path but tests
105
+ # and direct invocations may use relative paths.
106
+ TIER=""
88
107
  case "$FILE_PATH" in
89
- *docs/problems/*) ;;
108
+ *docs/problems/*) TIER="problems" ;;
109
+ *docs/rfcs/*) TIER="rfcs" ;;
90
110
  *) exit 0 ;;
91
111
  esac
92
112
 
93
113
  BASENAME=$(basename "$FILE_PATH")
94
114
 
95
- # Allow-list: docs/problems/README.md is regenerated by Steps 5/6/7.
96
- # Gating it would chicken-and-egg the skill itself.
115
+ # Allow-list: README.md regenerated by skill flows (chicken-and-egg).
116
+ # Applies to both tiers: docs/problems/README.md (manage-problem Steps 5/6/7)
117
+ # and docs/rfcs/README.md (manage-rfc transitions / review).
97
118
  if [ "$BASENAME" = "README.md" ]; then
98
119
  exit 0
99
120
  fi
100
121
 
101
- # Only gate ticket-shaped basenames: NNN-... (3-digit prefix).
122
+ # Only gate ticket-shaped basenames per tier:
123
+ # problems: NNN-... (3-digit prefix)
124
+ # rfcs: RFC-NNN-...
102
125
  # Non-ticket files (NOTES.md, archive/index.md, etc.) are project
103
- # housekeeping and don't need the duplicate-check.
104
- case "$BASENAME" in
105
- [0-9][0-9][0-9]-*) ;;
106
- *) exit 0 ;;
126
+ # housekeeping and don't need the gate.
127
+ case "$TIER" in
128
+ problems)
129
+ case "$BASENAME" in
130
+ [0-9][0-9][0-9]-*) ;;
131
+ *) exit 0 ;;
132
+ esac
133
+ ;;
134
+ rfcs)
135
+ case "$BASENAME" in
136
+ RFC-[0-9][0-9][0-9]-*) ;;
137
+ *) exit 0 ;;
138
+ esac
139
+ ;;
107
140
  esac
108
141
 
109
142
  # Existing file — Edit-flow / status-transition path. Only block
@@ -112,19 +145,31 @@ if [ -f "$FILE_PATH" ]; then
112
145
  exit 0
113
146
  fi
114
147
 
115
- # New ticket Write: gate on the Step-2 grep marker.
116
- if check_create_gate "$SESSION_ID"; then
117
- exit 0
118
- fi
119
-
120
148
  # P142 / ADR-050: the runtime-SID instrumentation hook
121
149
  # (itil-runtime-sid-marker.sh) writes the runtime stdin session_id to a
122
150
  # per-machine marker on every PreToolUse:Bash|Write|Edit|Read event. The
123
151
  # `get_current_session_id` helper reads that marker as the authoritative
124
- # SID, so the marker `mark_step2_complete` writes is bound to the same
125
- # session_id this hook will see on the subsequent Write. SID-mismatch
126
- # denial is structurally eliminated; the only remaining deny path is
127
- # the routine "Step 2 grep has not run yet for this session" case, for
128
- # which the deny message stays focused and skill-pointing.
129
- create_gate_deny "BLOCKED: Cannot Write '${BASENAME}' under docs/problems/ without running /wr-itil:manage-problem Step 2 (duplicate-check) first. New problem tickets MUST be created via the skill so the duplicate-prevention grep fires before the file lands. Invoke the Skill tool with skill='wr-itil:manage-problem' and a description of the new problem; Step 2 will grep for related existing tickets and surface any matches via AskUserQuestion before creating the new ticket. (P119)"
152
+ # SID, so the marker `mark_step2_complete` / `mark_rfc_capture_complete`
153
+ # writes is bound to the same session_id this hook will see on the
154
+ # subsequent Write. SID-mismatch denial is structurally eliminated; the
155
+ # only remaining deny path is the routine "duplicate-check / capture
156
+ # step has not run yet for this session" case.
157
+
158
+ # New ticket Write: gate on the tier-specific marker.
159
+ case "$TIER" in
160
+ problems)
161
+ if check_create_gate "$SESSION_ID"; then
162
+ exit 0
163
+ fi
164
+ create_gate_deny "BLOCKED: Cannot Write '${BASENAME}' under docs/problems/ without running /wr-itil:manage-problem Step 2 (duplicate-check) first. New problem tickets MUST be created via the skill so the duplicate-prevention grep fires before the file lands. Invoke the Skill tool with skill='wr-itil:manage-problem' and a description of the new problem; Step 2 will grep for related existing tickets and surface any matches via AskUserQuestion before creating the new ticket. (P119)"
165
+ exit 0
166
+ ;;
167
+ rfcs)
168
+ if check_rfc_capture_gate "$SESSION_ID"; then
169
+ exit 0
170
+ fi
171
+ create_gate_deny "BLOCKED: Cannot Write '${BASENAME}' under docs/rfcs/ without running /wr-itil:capture-rfc Step 2 (problem-trace validation) first. New RFC tickets MUST be created via the skill so the I1 trace-to-problem invariant is enforced before the file lands. Invoke the Skill tool with skill='wr-itil:capture-rfc' supplying the leading problem-trace argument (P<NNN> or P<NNN>,P<NNN>,...); Step 2 will validate that each traced problem exists. Without a problem-trace, capture-rfc emits a deny log entry to logs/rfc-capture-denials.jsonl per ADR-060 § Confirmation criterion 1 + § Reassessment trace-violation-rate criterion. (P170 / ADR-060)"
172
+ exit 0
173
+ ;;
174
+ esac
130
175
  exit 0