@windyroad/itil 0.61.1 → 0.61.2
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.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +7 -2
- package/agents/test/fixtures/proceed-new-genuinely-new.md +0 -43
- package/agents/test/fixtures/proceed-new-subtle-sibling.md +0 -47
- package/agents/test/fixtures/regression-p347-vs-p346.md +0 -54
- package/agents/test/hang-off-check.bats +0 -225
- package/hooks/test/block-list.bats +0 -153
- package/hooks/test/command-detect.bats +0 -181
- package/hooks/test/itil-assistant-output-gate.bats +0 -114
- package/hooks/test/itil-assistant-output-review.bats +0 -224
- package/hooks/test/itil-bash-polling-antipattern-detect.bats +0 -154
- package/hooks/test/itil-changeset-discipline.bats +0 -603
- package/hooks/test/itil-claude-space-protection.bats +0 -294
- package/hooks/test/itil-commit-trailer-transition-advisory.bats +0 -80
- package/hooks/test/itil-correction-detect.bats +0 -189
- package/hooks/test/itil-deferral-cadence-gate.bats +0 -187
- package/hooks/test/itil-fictional-defer-detect.bats +0 -292
- package/hooks/test/itil-fix-title-lifecycle-advisory.bats +0 -129
- package/hooks/test/itil-mid-loop-ask-detect.bats +0 -220
- package/hooks/test/itil-no-implement-draft-gate.bats +0 -210
- package/hooks/test/itil-pending-questions-surface.bats +0 -307
- package/hooks/test/itil-readme-refresh-discipline.bats +0 -642
- package/hooks/test/itil-rfc-oversight-nudge.bats +0 -83
- package/hooks/test/itil-rfc-trailer-advisory.bats +0 -375
- package/hooks/test/itil-story-mirror-migration-nudge.bats +0 -122
- package/hooks/test/manage-problem-enforce-create.bats +0 -424
- package/hooks/test/p057-staging-trap-detect.bats +0 -231
- package/hooks/test/pre-publish-intake-gate.bats +0 -144
- package/hooks/test/runtime-sid-marker.bats +0 -90
- package/hooks/test/session-id.bats +0 -370
- package/scripts/migrate-problems-add-type.sh +0 -116
- package/scripts/test/catchup-scan.bats +0 -274
- package/scripts/test/check-afk-accept-eligible.bats +0 -310
- package/scripts/test/check-fail-soft-skip-discipline.bats +0 -172
- package/scripts/test/check-fix-rfc-trace.bats +0 -164
- package/scripts/test/check-locale-discipline.bats +0 -291
- package/scripts/test/check-problems-readme-budget.bats +0 -192
- package/scripts/test/check-rfc-has-stories.bats +0 -69
- package/scripts/test/check-rfc-rejected-alternatives.bats +0 -108
- package/scripts/test/check-rfc-stories-ratified.bats +0 -82
- package/scripts/test/check-upstream-responses.bats +0 -503
- package/scripts/test/classify-readme-drift.bats +0 -392
- package/scripts/test/derive-first-dispatch.bats +0 -274
- package/scripts/test/derive-release-vehicle.bats +0 -454
- package/scripts/test/detect-unratified-stories-maps.bats +0 -60
- package/scripts/test/dual-tolerant-glob-rfc-002-t2.bats +0 -309
- package/scripts/test/effort-tally.bats +0 -170
- package/scripts/test/evaluate-relevance.bats +0 -773
- package/scripts/test/kind-a-commands.bats +0 -46
- package/scripts/test/mark-create-gate.bats +0 -59
- package/scripts/test/mark-story-oversight-confirmed.bats +0 -53
- package/scripts/test/migrate-problems-add-type.bats +0 -280
- package/scripts/test/migrate-story-status-mirror.bats +0 -167
- package/scripts/test/no-type-regression-guard.bats +0 -74
- package/scripts/test/oversight-mirror-corpus-lint.bats +0 -150
- package/scripts/test/plugin-exercise-index.bats +0 -386
- package/scripts/test/plugin-maturity-doc-lint.bats +0 -470
- package/scripts/test/plugin-maturity-populate.bats +0 -631
- package/scripts/test/plugin-maturity-render.bats +0 -469
- package/scripts/test/plugin-validate-ci-gate.bats +0 -208
- package/scripts/test/reconcile-readme.bats +0 -1007
- package/scripts/test/reconcile-rfcs.bats +0 -496
- package/scripts/test/reconcile-stories.bats +0 -173
- package/scripts/test/reconcile-story-maps.bats +0 -74
- package/scripts/test/release-watch-poll-loop.bats +0 -180
- package/scripts/test/resolve-governance-plugin-dirs.bats +0 -78
- package/scripts/test/rfc-stories-extension.bats +0 -175
- package/scripts/test/skill-invocations.bats +0 -535
- package/scripts/test/skill-md-dual-tolerant-coverage-rfc-002-t3.bats +0 -374
- package/scripts/test/story-oversight-lib.bats +0 -190
- package/scripts/test/update-jtbd-references-related-problems.bats +0 -178
- package/scripts/test/update-problem-references-section.bats +0 -242
- package/scripts/test/update-problem-rfcs-section.bats +0 -242
- package/scripts/test/update-references-section-sibling-helpers.bats +0 -80
- package/scripts/test/update-rfc-commits-section.bats +0 -77
- package/scripts/test/verify-iter-summary.bats +0 -196
- package/scripts/test/working-the-problem-traversal.bats +0 -116
- package/skills/capture-problem/test/capture-problem-step-1-5b-jtbd-trace.bats +0 -398
- package/skills/capture-problem/test/capture-problem.bats +0 -389
- package/skills/capture-story/test/capture-story-behavioural.bats +0 -317
- package/skills/capture-story-map/test/capture-story-map-behavioural.bats +0 -98
- package/skills/close-incident/test/close-incident-contract.bats +0 -146
- package/skills/link-incident/test/link-incident-contract.bats +0 -125
- package/skills/list-incidents/test/list-incidents-contract.bats +0 -115
- package/skills/list-problems/test/list-problems-contract.bats +0 -100
- package/skills/list-stories/test/list-stories-contract.bats +0 -127
- package/skills/list-story-maps/test/list-story-maps-contract.bats +0 -46
- package/skills/manage-incident/test/manage-incident-adr-044-contract.bats +0 -206
- package/skills/manage-incident/test/manage-incident-close-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-link-forwarder.bats +0 -67
- package/skills/manage-incident/test/manage-incident-list-forwarder.bats +0 -68
- package/skills/manage-incident/test/manage-incident-mitigate-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident-restore-forwarder.bats +0 -66
- package/skills/manage-incident/test/manage-incident.bats +0 -171
- package/skills/manage-problem/test/manage-problem-adr-044-step4-derive-first.bats +0 -151
- package/skills/manage-problem/test/manage-problem-auto-migrate-step.bats +0 -53
- package/skills/manage-problem/test/manage-problem-concern-boundary.bats +0 -64
- package/skills/manage-problem/test/manage-problem-effort-buckets.bats +0 -56
- package/skills/manage-problem/test/manage-problem-external-root-cause-detection.bats +0 -119
- package/skills/manage-problem/test/manage-problem-first-run-intake-prompt.bats +0 -43
- package/skills/manage-problem/test/manage-problem-git-mv-restage-reminder.bats +0 -48
- package/skills/manage-problem/test/manage-problem-id-collision-guard.bats +0 -39
- package/skills/manage-problem/test/manage-problem-list-forwarder.bats +0 -66
- package/skills/manage-problem/test/manage-problem-next-id-origin-lookup.bats +0 -50
- package/skills/manage-problem/test/manage-problem-no-prose-options.bats +0 -74
- package/skills/manage-problem/test/manage-problem-output-formatting.bats +0 -30
- package/skills/manage-problem/test/manage-problem-parked-and-cache.bats +0 -73
- package/skills/manage-problem/test/manage-problem-readme-refresh-on-transition.bats +0 -62
- package/skills/manage-problem/test/manage-problem-readme-tie-break-order.bats +0 -318
- package/skills/manage-problem/test/manage-problem-readme-vq-sort-order.bats +0 -206
- package/skills/manage-problem/test/manage-problem-release-vehicle-seed.bats +0 -83
- package/skills/manage-problem/test/manage-problem-review-forwarder.bats +0 -71
- package/skills/manage-problem/test/manage-problem-step-9d-recovery-path.bats +0 -97
- package/skills/manage-problem/test/manage-problem-transition-forwarder.bats +0 -96
- package/skills/manage-problem/test/manage-problem-transitive-dependencies.bats +0 -365
- package/skills/manage-problem/test/manage-problem-verification-detection.bats +0 -64
- package/skills/manage-problem/test/manage-problem-verification-pending.bats +0 -114
- package/skills/manage-problem/test/manage-problem-work-forwarder.bats +0 -88
- package/skills/manage-story/test/manage-story-contract.bats +0 -174
- package/skills/manage-story-map/test/manage-story-map-contract.bats +0 -63
- package/skills/mitigate-incident/test/mitigate-incident-contract.bats +0 -258
- package/skills/reconcile-readme/test/reconcile-readme-contract.bats +0 -201
- package/skills/report-upstream/test/report-upstream-contract.bats +0 -394
- package/skills/restore-incident/test/restore-incident-contract.bats +0 -170
- package/skills/review-problems/test/inbound-channels-cache-shape.bats +0 -127
- package/skills/review-problems/test/inbound-discovery-contract.bats +0 -383
- package/skills/review-problems/test/jtbd-301-verdict-shape-contract.bats +0 -225
- package/skills/review-problems/test/review-problems-contract.bats +0 -235
- package/skills/review-problems/test/review-problems-likely-verified-cell-shape.bats +0 -229
- package/skills/scaffold-intake/test/scaffold-intake-contract.bats +0 -126
- package/skills/scaffold-intake/test/scaffold-intake-fixture.bats +0 -164
- package/skills/scaffold-intake/test/scaffold-intake-secrets-absent.bats +0 -87
- package/skills/transition-problem/test/transition-problem-contract.bats +0 -285
- package/skills/transition-problems/test/transition-problems-contract.bats +0 -331
- package/skills/update-upstream/test/update-upstream-contract.bats +0 -375
- package/skills/work-problem/test/work-problem-contract.bats +0 -294
- package/skills/work-problems/test/work-problems-above-appetite-remediation.bats +0 -139
- package/skills/work-problems/test/work-problems-adr-013-rule-6-p352-amendment.bats +0 -166
- package/skills/work-problems/test/work-problems-auto-migrate-step.bats +0 -57
- package/skills/work-problems/test/work-problems-cost-logging.bats +0 -109
- package/skills/work-problems/test/work-problems-deviation-candidate-shape.bats +0 -121
- package/skills/work-problems/test/work-problems-first-run-intake-prompt.bats +0 -36
- package/skills/work-problems/test/work-problems-inter-iteration-verify.bats +0 -59
- package/skills/work-problems/test/work-problems-mid-loop-userpromptsubmit-handler.bats +0 -69
- package/skills/work-problems/test/work-problems-no-mid-loop-asking.bats +0 -192
- package/skills/work-problems/test/work-problems-p341-pre-all-done-gate.bats +0 -122
- package/skills/work-problems/test/work-problems-p342-r007-derive-then-pass-flags.bats +0 -170
- package/skills/work-problems/test/work-problems-p342-retro-auto-ticket-carveout.bats +0 -120
- package/skills/work-problems/test/work-problems-preflight-failure-handling.bats +0 -248
- package/skills/work-problems/test/work-problems-preflight-session-continuity.bats +0 -139
- package/skills/work-problems/test/work-problems-preflight.bats +0 -69
- package/skills/work-problems/test/work-problems-release-cadence.bats +0 -63
- package/skills/work-problems/test/work-problems-step-0-iter-error-staleness.bats +0 -93
- package/skills/work-problems/test/work-problems-step-0b-cache-staleness-behavioural.bats +0 -138
- package/skills/work-problems/test/work-problems-step-0c-deferred-placeholder-staleness-behavioural.bats +0 -238
- package/skills/work-problems/test/work-problems-step-0d-outbound-responses-staleness-behavioural.bats +0 -174
- package/skills/work-problems/test/work-problems-step-2-5-routing.bats +0 -80
- package/skills/work-problems/test/work-problems-step-2-5b-cross-halt-routing.bats +0 -149
- package/skills/work-problems/test/work-problems-step-3-5-jtbd-ratification-predicate.bats +0 -191
- package/skills/work-problems/test/work-problems-step-5-bats-polling-discipline.bats +0 -91
- package/skills/work-problems/test/work-problems-step-5-delegation.bats +0 -333
- package/skills/work-problems/test/work-problems-step-5-idle-timeout-sigterm.bats +0 -421
- package/skills/work-problems/test/work-problems-step-5-is-error-transient-halt.bats +0 -278
- package/skills/work-problems/test/work-problems-step-5-iter-changeset-required.bats +0 -95
- package/skills/work-problems/test/work-problems-step-5-prompt-body-re-grounding.bats +0 -128
- package/skills/work-problems/test/work-problems-step-5-stream-timeout-salvage.bats +0 -251
- package/skills/work-problems/test/work-problems-step-6-5-always-drain.bats +0 -203
- package/skills/work-problems/test/work-problems-step-6-5-cache-refresh-chain.bats +0 -174
- package/skills/work-problems/test/work-problems-step-6-5-fix-and-continue.bats +0 -254
- package/skills/work-problems/test/work-problems-step-6-5-postrelease-kv-callback.bats +0 -209
- package/skills/work-problems/test/work-problems-stop-condition-questions.bats +0 -85
|
@@ -1,642 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# P165: itil-readme-refresh-discipline.sh PreToolUse:Bash hook must deny
|
|
4
|
-
# `git commit` invocations whose staged set includes any
|
|
5
|
-
# docs/problems/<state>/NNN-*.md (or legacy docs/problems/NNN-*.md) but
|
|
6
|
-
# does NOT also stage docs/problems/README.md. Hook-level enforcement
|
|
7
|
-
# closes the P094/P062 README-refresh enforcement gap — iter subprocess
|
|
8
|
-
# commits could previously ship a `.verifying.md` rename or Status edit
|
|
9
|
-
# without the corresponding Verification Queue / WSJF Rankings row in
|
|
10
|
-
# the README.
|
|
11
|
-
#
|
|
12
|
-
# Detection logic (per ticket Fix Strategy + architect verdict):
|
|
13
|
-
# On `git commit` invocations, run `git diff --staged --name-only`.
|
|
14
|
-
# If any path matches docs/problems/(open|verifying|closed|known-error|parked)/NNN-*.md
|
|
15
|
-
# OR docs/problems/NNN-*.<state>.md (legacy flat layout) AND
|
|
16
|
-
# docs/problems/README.md is NOT staged, emit a deny with recovery
|
|
17
|
-
# directive `git add docs/problems/README.md` and the P165 cite.
|
|
18
|
-
# Allow when README is staged alongside, when no ticket file is
|
|
19
|
-
# staged at all (README-only / retro-only / ADR-only / source-only
|
|
20
|
-
# commits), or when BYPASS_README_REFRESH_GATE=1 is set.
|
|
21
|
-
#
|
|
22
|
-
# Per ADR-005 (plugin testing strategy) — hook bats live under
|
|
23
|
-
# packages/<plugin>/hooks/test/ and assert behaviour on emitted JSON,
|
|
24
|
-
# not source content. Per P081 — no source-grep on hook text. Simulate
|
|
25
|
-
# the PreToolUse:Bash payload on stdin and assert on the emitted
|
|
26
|
-
# permissionDecision.
|
|
27
|
-
#
|
|
28
|
-
# Per ADR-045 Pattern 1 (silent-on-pass) — allow paths emit 0 bytes.
|
|
29
|
-
# Per ADR-045 deny-band — deny messages target ~245 bytes; cap at 300.
|
|
30
|
-
|
|
31
|
-
setup() {
|
|
32
|
-
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
33
|
-
HOOK="$SCRIPT_DIR/itil-readme-refresh-discipline.sh"
|
|
34
|
-
ORIG_DIR="$PWD"
|
|
35
|
-
TEST_DIR=$(mktemp -d)
|
|
36
|
-
cd "$TEST_DIR"
|
|
37
|
-
git init --quiet -b main
|
|
38
|
-
git config user.email "test@example.com"
|
|
39
|
-
git config user.name "Test"
|
|
40
|
-
mkdir -p docs/problems/open docs/problems/verifying docs/problems/closed \
|
|
41
|
-
docs/problems/known-error docs/problems/parked docs/retros \
|
|
42
|
-
docs/decisions packages/itil/skills/foo .changeset
|
|
43
|
-
echo "seed" > seed.txt
|
|
44
|
-
git add seed.txt
|
|
45
|
-
git -c commit.gpgsign=false commit --quiet -m "initial"
|
|
46
|
-
# README must exist for the "stage it alongside" tests to work.
|
|
47
|
-
echo "# Problem Backlog" > docs/problems/README.md
|
|
48
|
-
git add docs/problems/README.md
|
|
49
|
-
git -c commit.gpgsign=false commit --quiet -m "seed readme"
|
|
50
|
-
unset BYPASS_README_REFRESH_GATE
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
teardown() {
|
|
54
|
-
cd "$ORIG_DIR"
|
|
55
|
-
rm -rf "$TEST_DIR"
|
|
56
|
-
unset BYPASS_README_REFRESH_GATE
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
run_bash_hook() {
|
|
60
|
-
local cmd="$1"
|
|
61
|
-
local json
|
|
62
|
-
json=$(printf '{"tool_name":"Bash","tool_input":{"command":"%s"}}' "$cmd")
|
|
63
|
-
echo "$json" | bash "$HOOK"
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
# --- Trap detection: the canonical P165 shape ---
|
|
67
|
-
|
|
68
|
-
@test "deny: staged docs/problems/open/NNN-*.md without README refresh triggers deny on git commit" {
|
|
69
|
-
echo "# Problem 999" > docs/problems/open/999-some-new-ticket.md
|
|
70
|
-
git add docs/problems/open/999-some-new-ticket.md
|
|
71
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
72
|
-
[ "$status" -eq 0 ]
|
|
73
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
74
|
-
[[ "$output" == *"P165"* ]]
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@test "deny: staged docs/problems/verifying/NNN-*.md without README refresh triggers deny" {
|
|
78
|
-
echo "# Problem 999 verifying" > docs/problems/verifying/999-some-ticket.md
|
|
79
|
-
git add docs/problems/verifying/999-some-ticket.md
|
|
80
|
-
run run_bash_hook "git commit -m 'fix'"
|
|
81
|
-
[ "$status" -eq 0 ]
|
|
82
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
83
|
-
[[ "$output" == *"P165"* ]]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
@test "deny: staged docs/problems/closed/NNN-*.md without README refresh triggers deny" {
|
|
87
|
-
echo "# Problem 999 closed" > docs/problems/closed/999-some-ticket.md
|
|
88
|
-
git add docs/problems/closed/999-some-ticket.md
|
|
89
|
-
run run_bash_hook "git commit -m 'close'"
|
|
90
|
-
[ "$status" -eq 0 ]
|
|
91
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@test "deny: staged docs/problems/known-error/NNN-*.md without README refresh triggers deny" {
|
|
95
|
-
echo "# Problem 999 known error" > docs/problems/known-error/999-some-ticket.md
|
|
96
|
-
git add docs/problems/known-error/999-some-ticket.md
|
|
97
|
-
run run_bash_hook "git commit -m 'transition'"
|
|
98
|
-
[ "$status" -eq 0 ]
|
|
99
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@test "deny: staged docs/problems/parked/NNN-*.md without README refresh triggers deny" {
|
|
103
|
-
echo "# Problem 999 parked" > docs/problems/parked/999-some-ticket.md
|
|
104
|
-
git add docs/problems/parked/999-some-ticket.md
|
|
105
|
-
run run_bash_hook "git commit -m 'park'"
|
|
106
|
-
[ "$status" -eq 0 ]
|
|
107
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@test "deny: staged legacy flat-layout docs/problems/NNN-*.<state>.md without README triggers deny" {
|
|
111
|
-
echo "# Problem 999 flat" > docs/problems/999-some-legacy.open.md
|
|
112
|
-
git add docs/problems/999-some-legacy.open.md
|
|
113
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
114
|
-
[ "$status" -eq 0 ]
|
|
115
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@test "deny message names offending ticket ID, recovery command, P165 cite" {
|
|
119
|
-
echo "# Problem 999" > docs/problems/open/999-some-new-ticket.md
|
|
120
|
-
git add docs/problems/open/999-some-new-ticket.md
|
|
121
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
122
|
-
[ "$status" -eq 0 ]
|
|
123
|
-
# Deny names the ticket as `P<NNN>` (not full path — see hook
|
|
124
|
-
# comment: full descriptive ticket slugs exceed ADR-045 deny-band).
|
|
125
|
-
[[ "$output" == *"P999"* ]]
|
|
126
|
-
[[ "$output" == *"docs/problems/README.md"* ]]
|
|
127
|
-
[[ "$output" == *"P165"* ]]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@test "deny message stays under ADR-045 deny-band (<300 bytes)" {
|
|
131
|
-
echo "# Problem 999" > docs/problems/open/999-some-ticket.md
|
|
132
|
-
git add docs/problems/open/999-some-ticket.md
|
|
133
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
134
|
-
[ "$status" -eq 0 ]
|
|
135
|
-
[ "${#output}" -lt 300 ]
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
# --- Allow paths: each non-trap shape must NOT deny ---
|
|
139
|
-
|
|
140
|
-
@test "allow: staged ticket file WITH docs/problems/README.md allows the commit" {
|
|
141
|
-
echo "# Problem 999" > docs/problems/open/999-new.md
|
|
142
|
-
echo "# Problem Backlog updated" > docs/problems/README.md
|
|
143
|
-
git add docs/problems/open/999-new.md docs/problems/README.md
|
|
144
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
145
|
-
[ "$status" -eq 0 ]
|
|
146
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
@test "allow: README-only commit (reconcile-readme path) allows without ticket change" {
|
|
150
|
-
echo "# Problem Backlog reconciled" > docs/problems/README.md
|
|
151
|
-
git add docs/problems/README.md
|
|
152
|
-
run run_bash_hook "git commit -m 'docs: reconcile readme'"
|
|
153
|
-
[ "$status" -eq 0 ]
|
|
154
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@test "allow: retro-only commit allows without ticket change or README refresh" {
|
|
158
|
-
echo "# Retro 2026-05-11" > docs/retros/2026-05-11-iter.md
|
|
159
|
-
git add docs/retros/2026-05-11-iter.md
|
|
160
|
-
run run_bash_hook "git commit -m 'docs(retros): iter'"
|
|
161
|
-
[ "$status" -eq 0 ]
|
|
162
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
@test "allow: ADR-only commit allows without ticket change or README refresh" {
|
|
166
|
-
echo "# ADR 999" > docs/decisions/999-some-decision.proposed.md
|
|
167
|
-
git add docs/decisions/999-some-decision.proposed.md
|
|
168
|
-
run run_bash_hook "git commit -m 'docs(decisions): adr-999'"
|
|
169
|
-
[ "$status" -eq 0 ]
|
|
170
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@test "allow: source-only commit (packages/) allows without ticket change or README refresh" {
|
|
174
|
-
echo "skill body" > packages/itil/skills/foo/SKILL.md
|
|
175
|
-
git add packages/itil/skills/foo/SKILL.md
|
|
176
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
177
|
-
[ "$status" -eq 0 ]
|
|
178
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
@test "allow: BYPASS_README_REFRESH_GATE=1 env var allows ticket commit without README refresh" {
|
|
182
|
-
echo "# Problem 999" > docs/problems/open/999-bypass.md
|
|
183
|
-
git add docs/problems/open/999-bypass.md
|
|
184
|
-
BYPASS_README_REFRESH_GATE=1 run run_bash_hook "git commit -m 'feat'"
|
|
185
|
-
[ "$status" -eq 0 ]
|
|
186
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
@test "allow: docs/problems/README-history.md edit alone does NOT trigger deny (not a ticket file)" {
|
|
190
|
-
echo "# History" > docs/problems/README-history.md
|
|
191
|
-
git add docs/problems/README-history.md
|
|
192
|
-
run run_bash_hook "git commit -m 'docs: rotate history'"
|
|
193
|
-
[ "$status" -eq 0 ]
|
|
194
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
# --- Allow path silence (ADR-045 Pattern 1) ---
|
|
198
|
-
|
|
199
|
-
@test "allow path emits 0 bytes (ADR-045 Pattern 1 silent-on-pass)" {
|
|
200
|
-
echo "# Retro" > docs/retros/2026-05-11-iter.md
|
|
201
|
-
git add docs/retros/2026-05-11-iter.md
|
|
202
|
-
run run_bash_hook "git commit -m 'docs'"
|
|
203
|
-
[ "$status" -eq 0 ]
|
|
204
|
-
[ "${#output}" -eq 0 ]
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
# --- Tool-name and command-shape filters ---
|
|
208
|
-
|
|
209
|
-
@test "allow: non-Bash tool exits 0 without deny" {
|
|
210
|
-
run bash -c "echo '{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"foo.md\"}}' | bash $HOOK"
|
|
211
|
-
[ "$status" -eq 0 ]
|
|
212
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
@test "allow: Bash command that is NOT git commit (e.g., git status) bypasses detection" {
|
|
216
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
217
|
-
git add docs/problems/open/999-x.md
|
|
218
|
-
run run_bash_hook "git status"
|
|
219
|
-
[ "$status" -eq 0 ]
|
|
220
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
# --- Mixed staged sets ---
|
|
224
|
-
|
|
225
|
-
@test "deny: staged ticket + ADR (no README) still triggers deny (mixed surface dominance)" {
|
|
226
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
227
|
-
echo "# ADR 999" > docs/decisions/999-x.proposed.md
|
|
228
|
-
git add docs/problems/open/999-x.md docs/decisions/999-x.proposed.md
|
|
229
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
230
|
-
[ "$status" -eq 0 ]
|
|
231
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
@test "allow: staged ticket + ADR + README allows (mixed set with README)" {
|
|
235
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
236
|
-
echo "# ADR 999" > docs/decisions/999-x.proposed.md
|
|
237
|
-
echo "# Problem Backlog updated" > docs/problems/README.md
|
|
238
|
-
git add docs/problems/open/999-x.md docs/decisions/999-x.proposed.md docs/problems/README.md
|
|
239
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
240
|
-
[ "$status" -eq 0 ]
|
|
241
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
# --- Parse / fail-open contracts ---
|
|
245
|
-
|
|
246
|
-
@test "allow: empty JSON exits 0 without deny (fail-open on parse-incomplete)" {
|
|
247
|
-
run bash -c "echo '{}' | bash $HOOK"
|
|
248
|
-
[ "$status" -eq 0 ]
|
|
249
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
@test "allow: outside a git work tree exits 0 without deny (fail-open)" {
|
|
253
|
-
cd "$ORIG_DIR"
|
|
254
|
-
TEMP_NONGIT=$(mktemp -d)
|
|
255
|
-
cd "$TEMP_NONGIT"
|
|
256
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
257
|
-
cd "$TEST_DIR"
|
|
258
|
-
rm -rf "$TEMP_NONGIT"
|
|
259
|
-
[ "$status" -eq 0 ]
|
|
260
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
# --- P230: narrative-only short-circuit (reconcile-readme is authority) ---
|
|
264
|
-
#
|
|
265
|
-
# When all staged ticket edits are purely narrative (Change Log entries,
|
|
266
|
-
# Investigation Task checkbox ticks, prose edits — no ranking-bearing
|
|
267
|
-
# field change, no rename between state subdirs, no creation/deletion)
|
|
268
|
-
# AND `packages/itil/scripts/reconcile-readme.sh` reports exit=0 against
|
|
269
|
-
# the current README, the hook silently passes. Ranking-bearing edits
|
|
270
|
-
# still fall through to existing detection per ADR-014 single-commit
|
|
271
|
-
# grain (architect verdict: reconcile is robustness layer, not
|
|
272
|
-
# supersession of per-operation refresh).
|
|
273
|
-
|
|
274
|
-
seed_valid_readme_p999_open() {
|
|
275
|
-
cat > docs/problems/README.md <<EOF
|
|
276
|
-
# Problem Backlog
|
|
277
|
-
|
|
278
|
-
## WSJF Rankings
|
|
279
|
-
|
|
280
|
-
| ID | Title | WSJF |
|
|
281
|
-
|---|---|---|
|
|
282
|
-
| P999 | Test ticket | 1.0 |
|
|
283
|
-
|
|
284
|
-
## Verification Queue
|
|
285
|
-
|
|
286
|
-
(none)
|
|
287
|
-
|
|
288
|
-
## Closed
|
|
289
|
-
|
|
290
|
-
(none)
|
|
291
|
-
EOF
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
@test "P230 allow: narrative-only edit + reconcile-readme exit=0 → allow silently" {
|
|
295
|
-
cat > docs/problems/open/999-narrative.md <<EOF
|
|
296
|
-
# Problem 999: Test ticket
|
|
297
|
-
**Status**: Open
|
|
298
|
-
**Priority**: 1
|
|
299
|
-
EOF
|
|
300
|
-
seed_valid_readme_p999_open
|
|
301
|
-
git add docs/problems/open/999-narrative.md docs/problems/README.md
|
|
302
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
303
|
-
# Narrative-only edit: append a Change Log line
|
|
304
|
-
echo "- 2026-05-16 — narrative tweak" >> docs/problems/open/999-narrative.md
|
|
305
|
-
git add docs/problems/open/999-narrative.md
|
|
306
|
-
run run_bash_hook "git commit -m 'docs(problems): narrative tweak'"
|
|
307
|
-
[ "$status" -eq 0 ]
|
|
308
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
@test "P230 allow: Investigation Task checkbox tick (narrative-only) + reconcile=0 → allow silently" {
|
|
312
|
-
cat > docs/problems/open/999-checkbox.md <<EOF
|
|
313
|
-
# Problem 999: Test ticket
|
|
314
|
-
**Status**: Open
|
|
315
|
-
**Priority**: 1
|
|
316
|
-
|
|
317
|
-
## Investigation Tasks
|
|
318
|
-
|
|
319
|
-
- [ ] First task
|
|
320
|
-
EOF
|
|
321
|
-
seed_valid_readme_p999_open
|
|
322
|
-
git add docs/problems/open/999-checkbox.md docs/problems/README.md
|
|
323
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
324
|
-
# Narrative-only edit: tick a checkbox
|
|
325
|
-
sed -i.bak 's/- \[ \] First task/- [x] First task/' docs/problems/open/999-checkbox.md
|
|
326
|
-
rm docs/problems/open/999-checkbox.md.bak
|
|
327
|
-
git add docs/problems/open/999-checkbox.md
|
|
328
|
-
run run_bash_hook "git commit -m 'docs(problems): tick task'"
|
|
329
|
-
[ "$status" -eq 0 ]
|
|
330
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
@test "P230 deny: ranking-bearing Status field change + reconcile=0 → still deny per ADR-014 single-commit grain" {
|
|
334
|
-
cat > docs/problems/open/999-ranking.md <<EOF
|
|
335
|
-
# Problem 999: Test ticket
|
|
336
|
-
**Status**: Open
|
|
337
|
-
**Priority**: 1
|
|
338
|
-
EOF
|
|
339
|
-
seed_valid_readme_p999_open
|
|
340
|
-
git add docs/problems/open/999-ranking.md docs/problems/README.md
|
|
341
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
342
|
-
# Ranking-bearing edit: change Status
|
|
343
|
-
sed -i.bak 's/\*\*Status\*\*: Open/\*\*Status\*\*: Verifying/' docs/problems/open/999-ranking.md
|
|
344
|
-
rm docs/problems/open/999-ranking.md.bak
|
|
345
|
-
git add docs/problems/open/999-ranking.md
|
|
346
|
-
run run_bash_hook "git commit -m 'transition'"
|
|
347
|
-
[ "$status" -eq 0 ]
|
|
348
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
@test "P230 deny: ranking-bearing Priority field change + reconcile=0 → still deny" {
|
|
352
|
-
cat > docs/problems/open/999-priority.md <<EOF
|
|
353
|
-
# Problem 999: Test ticket
|
|
354
|
-
**Status**: Open
|
|
355
|
-
**Priority**: 1
|
|
356
|
-
EOF
|
|
357
|
-
seed_valid_readme_p999_open
|
|
358
|
-
git add docs/problems/open/999-priority.md docs/problems/README.md
|
|
359
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
360
|
-
# Ranking-bearing edit: change Priority
|
|
361
|
-
sed -i.bak 's/\*\*Priority\*\*: 1/\*\*Priority\*\*: 5/' docs/problems/open/999-priority.md
|
|
362
|
-
rm docs/problems/open/999-priority.md.bak
|
|
363
|
-
git add docs/problems/open/999-priority.md
|
|
364
|
-
run run_bash_hook "git commit -m 're-rate'"
|
|
365
|
-
[ "$status" -eq 0 ]
|
|
366
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
@test "P230 deny: git mv between state subdirs (open→verifying) + no README refresh → deny (canonical iter-subprocess case)" {
|
|
370
|
-
cat > docs/problems/open/999-rename.md <<EOF
|
|
371
|
-
# Problem 999: Test ticket
|
|
372
|
-
**Status**: Open
|
|
373
|
-
EOF
|
|
374
|
-
seed_valid_readme_p999_open
|
|
375
|
-
git add docs/problems/open/999-rename.md docs/problems/README.md
|
|
376
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
377
|
-
# Rename to verifying state subdir
|
|
378
|
-
git mv docs/problems/open/999-rename.md docs/problems/verifying/999-rename.md
|
|
379
|
-
run run_bash_hook "git commit -m 'transition'"
|
|
380
|
-
[ "$status" -eq 0 ]
|
|
381
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
@test "P230 deny: narrative-only edit + reconcile-readme drift (README missing ticket) → still deny per existing logic" {
|
|
385
|
-
cat > docs/problems/open/999-narrative-drift.md <<EOF
|
|
386
|
-
# Problem 999: Test ticket
|
|
387
|
-
**Status**: Open
|
|
388
|
-
EOF
|
|
389
|
-
# README does NOT list P999 → reconcile detects MISSING drift → exit=1
|
|
390
|
-
cat > docs/problems/README.md <<EOF
|
|
391
|
-
# Problem Backlog
|
|
392
|
-
|
|
393
|
-
## WSJF Rankings
|
|
394
|
-
|
|
395
|
-
| ID | Title | WSJF |
|
|
396
|
-
|---|---|---|
|
|
397
|
-
EOF
|
|
398
|
-
git add docs/problems/open/999-narrative-drift.md docs/problems/README.md
|
|
399
|
-
git -c commit.gpgsign=false commit --quiet -m "seed p999"
|
|
400
|
-
# Narrative-only edit
|
|
401
|
-
echo "- 2026-05-16 — narrative line" >> docs/problems/open/999-narrative-drift.md
|
|
402
|
-
git add docs/problems/open/999-narrative-drift.md
|
|
403
|
-
run run_bash_hook "git commit -m 'docs(problems): narrative'"
|
|
404
|
-
[ "$status" -eq 0 ]
|
|
405
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
# --- P231: deny message advertises correct bypass syntax (Option A) ---
|
|
409
|
-
#
|
|
410
|
-
# Deny message advertises `.claude/settings.json env` rather than inline
|
|
411
|
-
# prefix (which doesn't propagate to PreToolUse hooks per P173).
|
|
412
|
-
|
|
413
|
-
@test "P231 deny message advertises .claude/settings.json bypass path + P173 reference" {
|
|
414
|
-
echo "# Problem 999" > docs/problems/open/999-bypass-msg.md
|
|
415
|
-
git add docs/problems/open/999-bypass-msg.md
|
|
416
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
417
|
-
[ "$status" -eq 0 ]
|
|
418
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
419
|
-
[[ "$output" == *".claude/settings.json"* ]]
|
|
420
|
-
[[ "$output" == *"P173"* ]]
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
# --- P268: substring-vs-invocation regression coverage ---
|
|
424
|
-
#
|
|
425
|
-
# Prior to P268, the hook used `case "$COMMAND" in *"git commit"*) ;;`
|
|
426
|
-
# which fired on ANY Bash command whose text contained the literal
|
|
427
|
-
# phrase "git commit" — including grep patterns, sed substitutions,
|
|
428
|
-
# cat heredoc bodies, echo strings, and `git log --grep` queries.
|
|
429
|
-
# Workaround was stage-README-first, observed ≥3 times per session.
|
|
430
|
-
# P268 replaces that match with a leading-executable-token check via
|
|
431
|
-
# `lib/command-detect.sh::command_invokes_git_commit`. The tests
|
|
432
|
-
# below stage a ticket file (which would trigger deny if the gate
|
|
433
|
-
# fired) and run various non-commit Bash commands whose argument
|
|
434
|
-
# vectors mention `git commit`. The hook MUST pass silently.
|
|
435
|
-
|
|
436
|
-
@test "P268 allow: grep with 'git commit' pattern does NOT trigger gate" {
|
|
437
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
438
|
-
git add docs/problems/open/999-x.md
|
|
439
|
-
run run_bash_hook "grep -n 'git commit' file.md"
|
|
440
|
-
[ "$status" -eq 0 ]
|
|
441
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
442
|
-
# Silent pass per ADR-045 Pattern 1.
|
|
443
|
-
[ "${#output}" -eq 0 ]
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
@test "P268 allow: grep -rn 'git commit' packages/ (the recurring orchestrator surface)" {
|
|
447
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
448
|
-
git add docs/problems/open/999-x.md
|
|
449
|
-
run run_bash_hook "grep -rn 'git commit' packages/"
|
|
450
|
-
[ "$status" -eq 0 ]
|
|
451
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
452
|
-
[ "${#output}" -eq 0 ]
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
@test "P268 allow: sed -i 's/git commit/.../' substitution does NOT trigger gate" {
|
|
456
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
457
|
-
git add docs/problems/open/999-x.md
|
|
458
|
-
run run_bash_hook "sed -i 's/git commit/git push/' file.md"
|
|
459
|
-
[ "$status" -eq 0 ]
|
|
460
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
461
|
-
[ "${#output}" -eq 0 ]
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
@test "P268 allow: echo with 'git commit' inside string does NOT trigger gate" {
|
|
465
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
466
|
-
git add docs/problems/open/999-x.md
|
|
467
|
-
run run_bash_hook "echo 'the git commit gate fires here'"
|
|
468
|
-
[ "$status" -eq 0 ]
|
|
469
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
470
|
-
[ "${#output}" -eq 0 ]
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
@test "P268 allow: git log --grep 'git commit' does NOT trigger gate (git log is leading)" {
|
|
474
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
475
|
-
git add docs/problems/open/999-x.md
|
|
476
|
-
run run_bash_hook "git log --grep 'git commit'"
|
|
477
|
-
[ "$status" -eq 0 ]
|
|
478
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
479
|
-
[ "${#output}" -eq 0 ]
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
@test "P268 allow: cat heredoc whose body contains 'git commit' does NOT trigger gate (iter-1 retro write surface)" {
|
|
483
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
484
|
-
git add docs/problems/open/999-x.md
|
|
485
|
-
# Inline-build JSON with embedded newlines via printf to mimic the
|
|
486
|
-
# Bash tool's multi-line command payload (the canonical retro-write
|
|
487
|
-
# surface that misfired in P268).
|
|
488
|
-
local payload
|
|
489
|
-
payload=$(python3 -c "import json,sys; print(json.dumps({'tool_name':'Bash','tool_input':{'command':'cat >> docs/problems/README-history.md <<EOF\nFlow note: the git commit gate fires here.\nEOF'}}))")
|
|
490
|
-
run bash -c "echo '$payload' | bash $HOOK"
|
|
491
|
-
[ "$status" -eq 0 ]
|
|
492
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
493
|
-
[ "${#output}" -eq 0 ]
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
@test "P268 deny: actual git commit invocation with staged ticket still triggers gate (positive regression)" {
|
|
497
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
498
|
-
git add docs/problems/open/999-x.md
|
|
499
|
-
run run_bash_hook "git commit -m 'feat'"
|
|
500
|
-
[ "$status" -eq 0 ]
|
|
501
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
502
|
-
[[ "$output" == *"P165"* ]]
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
@test "P268 deny: cd <path> && git commit (prefix-strip path) still triggers gate" {
|
|
506
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
507
|
-
git add docs/problems/open/999-x.md
|
|
508
|
-
run run_bash_hook "cd . && git commit -m 'feat'"
|
|
509
|
-
[ "$status" -eq 0 ]
|
|
510
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
@test "P268 deny: GIT_AUTHOR_NAME=Test git commit (env-prefix path) still triggers gate" {
|
|
514
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
515
|
-
git add docs/problems/open/999-x.md
|
|
516
|
-
run run_bash_hook "GIT_AUTHOR_NAME=Test git commit -m 'feat'"
|
|
517
|
-
[ "$status" -eq 0 ]
|
|
518
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
@test "P268 allow: git commit-tree (boundary check — commit-tree is a different plumbing command)" {
|
|
522
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
523
|
-
git add docs/problems/open/999-x.md
|
|
524
|
-
run run_bash_hook "git commit-tree HEAD^{tree}"
|
|
525
|
-
[ "$status" -eq 0 ]
|
|
526
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
527
|
-
[ "${#output}" -eq 0 ]
|
|
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
|
-
}
|
|
584
|
-
|
|
585
|
-
# --- P262: capture-deferred-readme RISK_BYPASS trailer allow-list bypass ---
|
|
586
|
-
#
|
|
587
|
-
# /wr-itil:capture-problem SKILL.md Step 6 deliberately stages ONLY the new
|
|
588
|
-
# ticket file and NOT docs/problems/README.md — the deferred-README-refresh
|
|
589
|
-
# contract is the load-bearing capture-time-speed distinction from
|
|
590
|
-
# /wr-itil:manage-problem (ADR-032). The P165 gate treats any new ticket
|
|
591
|
-
# file (status A) as ranking-bearing, so a bare capture commit was denied
|
|
592
|
-
# 4×/session. Architect verdict (P262): register a reason-named token
|
|
593
|
-
# `capture-deferred-readme` into the same P265 allow-list mechanism; the
|
|
594
|
-
# capture commit carries `RISK_BYPASS: capture-deferred-readme` in its
|
|
595
|
-
# message body. The carve-out is scoped to the TOKEN, not the commit
|
|
596
|
-
# subject — a `docs(problems): capture` subject WITHOUT the trailer still
|
|
597
|
-
# denies (proves the bypass cannot be self-claimed by message prose, and
|
|
598
|
-
# manage-problem's full-intake `docs(problems): open` path still enforces
|
|
599
|
-
# the refresh).
|
|
600
|
-
|
|
601
|
-
@test "P262 allow: capture commit with RISK_BYPASS: capture-deferred-readme trailer → allow silently" {
|
|
602
|
-
echo "# Problem 999" > docs/problems/open/999-capture.md
|
|
603
|
-
git add docs/problems/open/999-capture.md
|
|
604
|
-
run run_bash_hook "git commit -m 'docs(problems): capture P999 some-thing' -m 'RISK_BYPASS: capture-deferred-readme'"
|
|
605
|
-
[ "$status" -eq 0 ]
|
|
606
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
607
|
-
# Bypass is an allow path — silent per ADR-045 Pattern 1.
|
|
608
|
-
[ "${#output}" -eq 0 ]
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
@test "P262 deny: capture-subject commit WITHOUT the trailer still denies (token-scoped, not subject-scoped)" {
|
|
612
|
-
echo "# Problem 999" > docs/problems/open/999-capture.md
|
|
613
|
-
git add docs/problems/open/999-capture.md
|
|
614
|
-
run run_bash_hook "git commit -m 'docs(problems): capture P999 some-thing'"
|
|
615
|
-
[ "$status" -eq 0 ]
|
|
616
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
617
|
-
[[ "$output" == *"P165"* ]]
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
@test "P262 deny: subject merely contains the word capture but no registered trailer still denies" {
|
|
621
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
622
|
-
git add docs/problems/open/999-x.md
|
|
623
|
-
run run_bash_hook "git commit -m 'fix(itil): handle the capture edge case'"
|
|
624
|
-
[ "$status" -eq 0 ]
|
|
625
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
@test "P262 deny: docs(problems): open (manage-problem full intake) without README still denies" {
|
|
629
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
630
|
-
git add docs/problems/open/999-x.md
|
|
631
|
-
run run_bash_hook "git commit -m 'docs(problems): open P999 some-thing'"
|
|
632
|
-
[ "$status" -eq 0 ]
|
|
633
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
@test "P262 deny: unregistered capture-ish token does NOT bypass (allow-list scope)" {
|
|
637
|
-
echo "# Problem 999" > docs/problems/open/999-x.md
|
|
638
|
-
git add docs/problems/open/999-x.md
|
|
639
|
-
run run_bash_hook "git commit -m 'docs(problems): capture P999 x' -m 'RISK_BYPASS: capture-something-else'"
|
|
640
|
-
[ "$status" -eq 0 ]
|
|
641
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
642
|
-
}
|