@windyroad/itil 0.35.7-preview.387 → 0.35.8-preview.389
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.
|
@@ -92,8 +92,10 @@ command_invokes_git_commit "$COMMAND" || exit 0
|
|
|
92
92
|
|
|
93
93
|
# Run detection. Helper echoes offending ticket path on stdout when
|
|
94
94
|
# detected; returns 1 in that case. Returns 0 (allow) on no-trap,
|
|
95
|
-
# bypass env,
|
|
96
|
-
|
|
95
|
+
# bypass env, a registered RISK_BYPASS commit-message trailer (P265 —
|
|
96
|
+
# `$COMMAND` is threaded in so the helper can inspect the trailer), or
|
|
97
|
+
# fail-open (non-git tree, parse error).
|
|
98
|
+
TRAPPED_TICKET=$(detect_readme_refresh_required "$COMMAND" 2>/dev/null) && exit 0
|
|
97
99
|
|
|
98
100
|
# Extract the leading ticket-ID digits from the basename so the deny
|
|
99
101
|
# names the ticket as `P<NNN>` rather than the full descriptive path
|
|
@@ -42,6 +42,16 @@
|
|
|
42
42
|
# `.claude/settings.json` env field or shell `export` before
|
|
43
43
|
# launching `claude` — inline-prefix syntax (`VAR=1 git commit ...`)
|
|
44
44
|
# does NOT propagate from a Bash subshell to PreToolUse hooks (P173).
|
|
45
|
+
# - Registered `RISK_BYPASS: <token>` commit-message trailer (P265) →
|
|
46
|
+
# return 0 (allow). Narrow allow-list (currently only
|
|
47
|
+
# `adr-031-migration`, the standalone ADR-031 layout-migration
|
|
48
|
+
# commit, which is a rename-only change that legitimately stages no
|
|
49
|
+
# README refresh). The trailer is read from the live `git commit`
|
|
50
|
+
# command string at PreToolUse time (the commit message is not yet
|
|
51
|
+
# written), matching the sibling `risk-score-commit-gate.sh`
|
|
52
|
+
# recognition (P170 T11) so one logical migration commit clears both
|
|
53
|
+
# gates. Registry of record: ADR-014 commit-message bypass-token
|
|
54
|
+
# table.
|
|
45
55
|
#
|
|
46
56
|
# Narrative-only short-circuit (P230):
|
|
47
57
|
# - When all staged ticket edits are purely narrative — no
|
|
@@ -101,21 +111,74 @@
|
|
|
101
111
|
# shape — per-invocation deterministic, no markers).
|
|
102
112
|
# P141 — sibling changeset-discipline helper (same shape).
|
|
103
113
|
# P165 — this helper.
|
|
114
|
+
# P265 — RISK_BYPASS trailer allow-list bypass (this addition).
|
|
115
|
+
|
|
116
|
+
# Allow-list of registered RISK_BYPASS commit-message trailer tokens
|
|
117
|
+
# (P265). A policy-authorised commit may carry `RISK_BYPASS: <token>` in
|
|
118
|
+
# its message body; when <token> is registered here, the README-refresh
|
|
119
|
+
# gate allows the commit even though no README refresh is staged. The
|
|
120
|
+
# allow-list keeps the bypass narrow and auditable — a generic
|
|
121
|
+
# `RISK_BYPASS:` match would let any commit self-exempt.
|
|
122
|
+
#
|
|
123
|
+
# Registered tokens:
|
|
124
|
+
# adr-031-migration — the standalone per-state-subdir layout-migration
|
|
125
|
+
# commit written by lib/migrate-problems-layout.sh. It is a pure
|
|
126
|
+
# rename (no README content change — the table references tickets by
|
|
127
|
+
# ID, not path), so requiring a README refresh would deadlock the
|
|
128
|
+
# migration (P265). The same token clears the sibling
|
|
129
|
+
# risk-score-commit-gate.sh (P170 T11); both gates recognise it via
|
|
130
|
+
# the identical grep below so one logical migration commit clears
|
|
131
|
+
# both. Registry of record: ADR-014 commit-message bypass-token table.
|
|
132
|
+
_README_REFRESH_BYPASS_TRAILERS=("adr-031-migration")
|
|
133
|
+
|
|
134
|
+
# Returns 0 if the given `git commit` command string carries a
|
|
135
|
+
# registered RISK_BYPASS trailer from the allow-list above. The grep
|
|
136
|
+
# pattern is kept byte-identical to risk-score-commit-gate.sh so both
|
|
137
|
+
# commit gates recognise the token the same way (P265 architect verdict).
|
|
138
|
+
_readme_refresh_command_has_bypass_trailer() {
|
|
139
|
+
local command="${1:-}"
|
|
140
|
+
[ -n "$command" ] || return 1
|
|
141
|
+
local token
|
|
142
|
+
for token in "${_README_REFRESH_BYPASS_TRAILERS[@]}"; do
|
|
143
|
+
if printf '%s' "$command" \
|
|
144
|
+
| grep -qE "RISK_BYPASS:[[:space:]]*${token}([^A-Za-z0-9_-]|\$)"; then
|
|
145
|
+
return 0
|
|
146
|
+
fi
|
|
147
|
+
done
|
|
148
|
+
return 1
|
|
149
|
+
}
|
|
104
150
|
|
|
105
151
|
# Detect whether the current staged set requires a README refresh that
|
|
106
152
|
# is not staged.
|
|
107
153
|
#
|
|
154
|
+
# $1 (optional) — the `git commit` command string. Inspected for a
|
|
155
|
+
# registered RISK_BYPASS trailer (P265). Empty/absent → no trailer
|
|
156
|
+
# bypass (fail-safe; preserves pre-P265 behaviour for any caller that
|
|
157
|
+
# does not thread the command through).
|
|
158
|
+
#
|
|
108
159
|
# Echoes the offending ticket path on stdout when detected.
|
|
109
160
|
#
|
|
110
161
|
# Returns:
|
|
111
|
-
# 0 — no change required,
|
|
162
|
+
# 0 — no change required, BYPASS env set, a registered RISK_BYPASS
|
|
163
|
+
# trailer is present, or fail-open (allow)
|
|
112
164
|
# 1 — ticket change staged + README not staged (caller should deny)
|
|
113
165
|
detect_readme_refresh_required() {
|
|
166
|
+
local command="${1:-}"
|
|
167
|
+
|
|
114
168
|
# Bypass via env var — single most-common legitimate escape.
|
|
115
169
|
if [ "${BYPASS_README_REFRESH_GATE:-}" = "1" ]; then
|
|
116
170
|
return 0
|
|
117
171
|
fi
|
|
118
172
|
|
|
173
|
+
# Bypass via registered RISK_BYPASS commit-message trailer (P265).
|
|
174
|
+
# The ADR-031 layout-migration commit is a rename-only change that
|
|
175
|
+
# legitimately stages no README refresh; its `RISK_BYPASS:
|
|
176
|
+
# adr-031-migration` trailer carries the policy authorisation
|
|
177
|
+
# (ADR-031 § Backward Compatibility + ADR-013 Rule 6).
|
|
178
|
+
if _readme_refresh_command_has_bypass_trailer "$command"; then
|
|
179
|
+
return 0
|
|
180
|
+
fi
|
|
181
|
+
|
|
119
182
|
# Fail-open if not inside a git working tree.
|
|
120
183
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0
|
|
121
184
|
|
|
@@ -526,3 +526,58 @@ EOF
|
|
|
526
526
|
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
527
527
|
[ "${#output}" -eq 0 ]
|
|
528
528
|
}
|
|
529
|
+
|
|
530
|
+
# --- P265: RISK_BYPASS commit-message trailer allow-list bypass ---
|
|
531
|
+
#
|
|
532
|
+
# The ADR-031 layout-migration commit (lib/migrate-problems-layout.sh)
|
|
533
|
+
# is a pure rename (flat docs/problems/NNN-*.<state>.md → per-state
|
|
534
|
+
# subdir docs/problems/<state>/NNN-*.md) that legitimately stages NO
|
|
535
|
+
# README refresh — the rename does not change README content (the table
|
|
536
|
+
# references tickets by ID, not path). Its `RISK_BYPASS: adr-031-migration`
|
|
537
|
+
# trailer (written via sequential `-m` paragraphs, so the literal token
|
|
538
|
+
# appears in the `git commit` command argv) carries the policy
|
|
539
|
+
# authorisation (ADR-031 § Backward Compatibility + ADR-013 Rule 6).
|
|
540
|
+
# The hook must allow such commits silently. The bypass is an allow-list:
|
|
541
|
+
# only the registered token bypasses; an unregistered RISK_BYPASS token
|
|
542
|
+
# still denies. The recognition grep is kept identical to the sibling
|
|
543
|
+
# risk-score-commit-gate.sh (P170 T11 precedent) so both commit gates
|
|
544
|
+
# recognise the token the same way.
|
|
545
|
+
|
|
546
|
+
@test "P265 allow: migration rename + RISK_BYPASS: adr-031-migration trailer → allow silently" {
|
|
547
|
+
echo "# Problem 999 flat" > docs/problems/999-mig.open.md
|
|
548
|
+
git add docs/problems/999-mig.open.md
|
|
549
|
+
git -c commit.gpgsign=false commit --quiet -m "seed flat ticket"
|
|
550
|
+
git mv docs/problems/999-mig.open.md docs/problems/open/999-mig.md
|
|
551
|
+
run run_bash_hook "git commit -m 'docs(problems): auto-migrate to per-state subdirectory layout (ADR-031)' -m 'RISK_BYPASS: adr-031-migration'"
|
|
552
|
+
[ "$status" -eq 0 ]
|
|
553
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
554
|
+
# Bypass is an allow path — silent per ADR-045 Pattern 1.
|
|
555
|
+
[ "${#output}" -eq 0 ]
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
@test "P265 deny: same migration rename WITHOUT the trailer still denies (negative control)" {
|
|
559
|
+
echo "# Problem 999 flat" > docs/problems/999-mig.open.md
|
|
560
|
+
git add docs/problems/999-mig.open.md
|
|
561
|
+
git -c commit.gpgsign=false commit --quiet -m "seed flat ticket"
|
|
562
|
+
git mv docs/problems/999-mig.open.md docs/problems/open/999-mig.md
|
|
563
|
+
run run_bash_hook "git commit -m 'docs(problems): auto-migrate'"
|
|
564
|
+
[ "$status" -eq 0 ]
|
|
565
|
+
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
@test "P265 deny: unregistered RISK_BYPASS token does NOT bypass (allow-list scope)" {
|
|
569
|
+
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
570
|
+
git add docs/problems/open/999-x.md
|
|
571
|
+
run run_bash_hook "git commit -m 'feat' -m 'RISK_BYPASS: some-other-thing'"
|
|
572
|
+
[ "$status" -eq 0 ]
|
|
573
|
+
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
@test "P265 allow: registered trailer bypasses a newly-staged ticket too (bypass is staged-shape-agnostic)" {
|
|
577
|
+
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
578
|
+
git add docs/problems/open/999-x.md
|
|
579
|
+
run run_bash_hook "git commit -m 'docs(problems): auto-migrate' -m 'RISK_BYPASS: adr-031-migration'"
|
|
580
|
+
[ "$status" -eq 0 ]
|
|
581
|
+
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
582
|
+
[ "${#output}" -eq 0 ]
|
|
583
|
+
}
|
package/package.json
CHANGED