@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,294 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# P131: itil-claude-space-protection.sh PreToolUse:Write|Edit hook must
|
|
4
|
-
# block agent writes to project-scoped `.claude/` paths NOT in the
|
|
5
|
-
# user-space allow-list, unless an approval marker is present.
|
|
6
|
-
#
|
|
7
|
-
# Per ADR-037 + P081 (feedback_behavioural_tests.md): behavioural
|
|
8
|
-
# assertions — simulate the hook's payload on stdin and assert on
|
|
9
|
-
# emitted JSON permissionDecision and exit status. No source-grep on
|
|
10
|
-
# hook content.
|
|
11
|
-
#
|
|
12
|
-
# References:
|
|
13
|
-
# ADR-009 — marker lifecycle (this hook adds a new persistent class)
|
|
14
|
-
# ADR-013 Rule 6 — non-interactive fail-safe
|
|
15
|
-
# ADR-038 — progressive disclosure (deny message <500 bytes)
|
|
16
|
-
# ADR-045 — hook injection budget (silent on allow path)
|
|
17
|
-
# P131 — user-space write protection driver
|
|
18
|
-
|
|
19
|
-
setup() {
|
|
20
|
-
SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
|
21
|
-
HOOK="$SCRIPT_DIR/itil-claude-space-protection.sh"
|
|
22
|
-
ORIG_DIR="$PWD"
|
|
23
|
-
TEST_DIR=$(mktemp -d)
|
|
24
|
-
cd "$TEST_DIR"
|
|
25
|
-
mkdir -p .claude/skills/myskill .claude/commands .claude/agents \
|
|
26
|
-
.claude/hooks .claude/projects/abc/memory .claude/worktrees \
|
|
27
|
-
docs/plans
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
teardown() {
|
|
31
|
-
cd "$ORIG_DIR"
|
|
32
|
-
rm -rf "$TEST_DIR"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
run_hook() {
|
|
36
|
-
local tool="$1"
|
|
37
|
-
local file_path="$2"
|
|
38
|
-
local json
|
|
39
|
-
json=$(printf '{"tool_name":"%s","tool_input":{"file_path":"%s"},"session_id":"test-sid"}' \
|
|
40
|
-
"$tool" "$file_path")
|
|
41
|
-
echo "$json" | bash "$HOOK"
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
# Helper: compute approval-marker filename for a project-relative path
|
|
45
|
-
marker_path_for() {
|
|
46
|
-
local rel_path="$1"
|
|
47
|
-
local hash
|
|
48
|
-
if command -v shasum >/dev/null 2>&1; then
|
|
49
|
-
hash=$(printf '%s' "$rel_path" | shasum -a 256 | awk '{print $1}')
|
|
50
|
-
else
|
|
51
|
-
hash=$(printf '%s' "$rel_path" | sha256sum | awk '{print $1}')
|
|
52
|
-
fi
|
|
53
|
-
echo ".claude/.agent-write-approved-${hash}"
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# --- Core deny path: protected .claude/ writes without marker ---
|
|
57
|
-
|
|
58
|
-
@test "deny: Write to .claude/plans/foo.md without marker" {
|
|
59
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
60
|
-
[ "$status" -eq 0 ]
|
|
61
|
-
[[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
|
|
62
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@test "deny: Write to .claude/audits/2026-04-28.md without marker" {
|
|
66
|
-
run run_hook "Write" "$PWD/.claude/audits/2026-04-28.md"
|
|
67
|
-
[ "$status" -eq 0 ]
|
|
68
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@test "deny: Edit to existing agent-introduced .claude/plans file" {
|
|
72
|
-
mkdir -p .claude/plans
|
|
73
|
-
echo "stub" > .claude/plans/foo.md
|
|
74
|
-
run run_hook "Edit" "$PWD/.claude/plans/foo.md"
|
|
75
|
-
[ "$status" -eq 0 ]
|
|
76
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@test "deny: Write to .claude/scratch/state.json without marker" {
|
|
80
|
-
run run_hook "Write" "$PWD/.claude/scratch/state.json"
|
|
81
|
-
[ "$status" -eq 0 ]
|
|
82
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# --- Allow paths: user-space allow-list ---
|
|
86
|
-
|
|
87
|
-
@test "allow: Write to .claude/settings.json" {
|
|
88
|
-
run run_hook "Write" "$PWD/.claude/settings.json"
|
|
89
|
-
[ "$status" -eq 0 ]
|
|
90
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
91
|
-
[[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@test "allow: Write to .claude/settings.local.json" {
|
|
95
|
-
run run_hook "Write" "$PWD/.claude/settings.local.json"
|
|
96
|
-
[ "$status" -eq 0 ]
|
|
97
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@test "allow: Write to .claude/MEMORY.md" {
|
|
101
|
-
run run_hook "Write" "$PWD/.claude/MEMORY.md"
|
|
102
|
-
[ "$status" -eq 0 ]
|
|
103
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@test "allow: Write to .claude/.install-updates-consent" {
|
|
107
|
-
run run_hook "Write" "$PWD/.claude/.install-updates-consent"
|
|
108
|
-
[ "$status" -eq 0 ]
|
|
109
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@test "allow: Write to .claude/scheduled_tasks.lock" {
|
|
113
|
-
run run_hook "Write" "$PWD/.claude/scheduled_tasks.lock"
|
|
114
|
-
[ "$status" -eq 0 ]
|
|
115
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
@test "allow: Write to .claude/skills/install-updates/SKILL.md (skills subtree)" {
|
|
119
|
-
run run_hook "Write" "$PWD/.claude/skills/install-updates/SKILL.md"
|
|
120
|
-
[ "$status" -eq 0 ]
|
|
121
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
@test "allow: Write to .claude/commands/foo.md (commands subtree)" {
|
|
125
|
-
run run_hook "Write" "$PWD/.claude/commands/foo.md"
|
|
126
|
-
[ "$status" -eq 0 ]
|
|
127
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@test "allow: Write to .claude/agents/foo.md (agents subtree)" {
|
|
131
|
-
run run_hook "Write" "$PWD/.claude/agents/foo.md"
|
|
132
|
-
[ "$status" -eq 0 ]
|
|
133
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
@test "allow: Write to .claude/hooks/foo.sh (hooks subtree)" {
|
|
137
|
-
run run_hook "Write" "$PWD/.claude/hooks/foo.sh"
|
|
138
|
-
[ "$status" -eq 0 ]
|
|
139
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
@test "allow: Write to .claude/projects/abc/memory/MEMORY.md (Claude Code state)" {
|
|
143
|
-
run run_hook "Write" "$PWD/.claude/projects/abc/memory/MEMORY.md"
|
|
144
|
-
[ "$status" -eq 0 ]
|
|
145
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
@test "allow: Write to .claude/worktrees/something (worktrees subtree)" {
|
|
149
|
-
run run_hook "Write" "$PWD/.claude/worktrees/something"
|
|
150
|
-
[ "$status" -eq 0 ]
|
|
151
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
# --- Allow paths: outside .claude/ ---
|
|
155
|
-
|
|
156
|
-
@test "allow: Write to docs/plans/foo.md (not under .claude/)" {
|
|
157
|
-
run run_hook "Write" "$PWD/docs/plans/foo.md"
|
|
158
|
-
[ "$status" -eq 0 ]
|
|
159
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@test "allow: Write to packages/itil/hooks/foo.sh" {
|
|
163
|
-
mkdir -p packages/itil/hooks
|
|
164
|
-
run run_hook "Write" "$PWD/packages/itil/hooks/foo.sh"
|
|
165
|
-
[ "$status" -eq 0 ]
|
|
166
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
@test "allow: Write to absolute path outside PWD project root" {
|
|
170
|
-
run run_hook "Write" "/Users/someone/.claude/plans/foo.md"
|
|
171
|
-
[ "$status" -eq 0 ]
|
|
172
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
@test "allow: Write to ~/.claude path (user home, outside project)" {
|
|
176
|
-
run run_hook "Write" "$HOME/.claude/projects/xyz/foo.md"
|
|
177
|
-
[ "$status" -eq 0 ]
|
|
178
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
# --- Approval-marker bypass ---
|
|
182
|
-
|
|
183
|
-
@test "allow: Write to .claude/plans/foo.md WHEN approval marker exists" {
|
|
184
|
-
marker=$(marker_path_for ".claude/plans/foo.md")
|
|
185
|
-
: > "$marker"
|
|
186
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
187
|
-
[ "$status" -eq 0 ]
|
|
188
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
@test "deny: marker for one path does not authorize a different path" {
|
|
192
|
-
marker=$(marker_path_for ".claude/plans/foo.md")
|
|
193
|
-
: > "$marker"
|
|
194
|
-
run run_hook "Write" "$PWD/.claude/plans/bar.md"
|
|
195
|
-
[ "$status" -eq 0 ]
|
|
196
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
@test "allow: user can Write the approval marker itself" {
|
|
200
|
-
marker=$(marker_path_for ".claude/plans/foo.md")
|
|
201
|
-
run run_hook "Write" "$PWD/$marker"
|
|
202
|
-
[ "$status" -eq 0 ]
|
|
203
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
# --- Tool-name and edge cases ---
|
|
207
|
-
|
|
208
|
-
@test "allow: Read tool on protected .claude/ path is unaffected" {
|
|
209
|
-
mkdir -p .claude/plans
|
|
210
|
-
echo "stub" > .claude/plans/foo.md
|
|
211
|
-
run bash -c "echo '{\"tool_name\":\"Read\",\"tool_input\":{\"file_path\":\"$PWD/.claude/plans/foo.md\"}}' | bash $HOOK"
|
|
212
|
-
[ "$status" -eq 0 ]
|
|
213
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
@test "allow: Glob tool on .claude/ is unaffected (pattern, not file_path)" {
|
|
217
|
-
run bash -c "echo '{\"tool_name\":\"Glob\",\"tool_input\":{\"pattern\":\".claude/**/*.md\"}}' | bash $HOOK"
|
|
218
|
-
[ "$status" -eq 0 ]
|
|
219
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
@test "allow: empty file_path exits 0 without action" {
|
|
223
|
-
run bash -c "echo '{\"tool_name\":\"Write\",\"tool_input\":{}}' | bash $HOOK"
|
|
224
|
-
[ "$status" -eq 0 ]
|
|
225
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
# --- Allow-list anchor depth (architect note) ---
|
|
229
|
-
|
|
230
|
-
@test "deny: .claude/plans/foo.local.json (deeper than root) — *.local.json must not pass at arbitrary depth" {
|
|
231
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.local.json"
|
|
232
|
-
[ "$status" -eq 0 ]
|
|
233
|
-
[[ "$output" == *"BLOCKED"* ]]
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
@test "allow: .claude/foo.local.json (root depth) — *.local.json convention" {
|
|
237
|
-
run run_hook "Write" "$PWD/.claude/foo.local.json"
|
|
238
|
-
[ "$status" -eq 0 ]
|
|
239
|
-
[[ "$output" != *"BLOCKED"* ]]
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# --- Deny message contract (ADR-038 progressive disclosure) ---
|
|
243
|
-
|
|
244
|
-
@test "deny message names P131" {
|
|
245
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
246
|
-
[[ "$output" == *"P131"* ]]
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
@test "deny message suggests docs/ alternative" {
|
|
250
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
251
|
-
[[ "$output" == *"docs/"* ]]
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
@test "deny message names the approval-marker bypass" {
|
|
255
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
256
|
-
[[ "$output" == *".agent-write-approved-"* ]]
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
@test "deny message references project CLAUDE.md MANDATORY rule" {
|
|
260
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
261
|
-
[[ "$output" == *"CLAUDE.md"* ]]
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
@test "deny message stays under ADR-038 progressive-disclosure 500-byte cap" {
|
|
265
|
-
run run_hook "Write" "$PWD/.claude/plans/foo.md"
|
|
266
|
-
# Extract the permissionDecisionReason value and check its byte length.
|
|
267
|
-
reason=$(echo "$output" | python3 -c "
|
|
268
|
-
import sys, json
|
|
269
|
-
try:
|
|
270
|
-
data = json.load(sys.stdin)
|
|
271
|
-
print(data['hookSpecificOutput']['permissionDecisionReason'])
|
|
272
|
-
except Exception as e:
|
|
273
|
-
print('')
|
|
274
|
-
")
|
|
275
|
-
byte_len=$(printf '%s' "$reason" | wc -c)
|
|
276
|
-
# Bound: the deny message must be discoverable + actionable but
|
|
277
|
-
# progressive — < 500 bytes per ADR-038.
|
|
278
|
-
[ "$byte_len" -lt 500 ]
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
# --- ADR-045 silent-on-pass: allow path emits zero bytes ---
|
|
282
|
-
|
|
283
|
-
@test "allow path emits no output (ADR-045 Pattern 1 silent-on-pass)" {
|
|
284
|
-
run run_hook "Write" "$PWD/.claude/settings.json"
|
|
285
|
-
[ "$status" -eq 0 ]
|
|
286
|
-
# Empty stdout — silent on allow.
|
|
287
|
-
[ -z "$output" ]
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
@test "non-Write|Edit tool emits no output (silent-on-pass)" {
|
|
291
|
-
run bash -c "echo '{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"ls\"}}' | bash $HOOK"
|
|
292
|
-
[ "$status" -eq 0 ]
|
|
293
|
-
[ -z "$output" ]
|
|
294
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
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 ACCEPTED 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 accepted story → in-progress on a non-capture Refs commit (ADR-096: draft is blocked, accepted is advised)" {
|
|
50
|
-
mkdir -p docs/stories/accepted
|
|
51
|
-
echo "---" > docs/stories/accepted/STORY-201-y.md; git add -A; commit "$(printf 'feat(itil): capture STORY-201 y\n\nRefs: STORY-201')"
|
|
52
|
-
commit "$(printf 'feat(itil): implement\n\nRefs: STORY-201')"
|
|
53
|
-
run run_hook
|
|
54
|
-
[ "$status" -eq 0 ]
|
|
55
|
-
[[ "$output" == *"STORY-201"* ]]
|
|
56
|
-
[[ "$output" == *"manage-story"* ]]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
@test "silent for a DRAFT story on a non-capture Refs commit (ADR-096: draft is gate-blocked, not advised to advance)" {
|
|
60
|
-
mkdir -p docs/stories/draft
|
|
61
|
-
echo "---" > docs/stories/draft/STORY-202-z.md; git add -A; commit "$(printf 'feat(itil): capture STORY-202 z\n\nRefs: STORY-202')"
|
|
62
|
-
commit "$(printf 'feat(itil): implement\n\nRefs: STORY-202')"
|
|
63
|
-
run run_hook
|
|
64
|
-
[ "$status" -eq 0 ]
|
|
65
|
-
[[ "$output" != *"STORY-202"* ]]
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
@test "bypass env var suppresses the advisory" {
|
|
69
|
-
echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "$(printf 'feat: s\n\nRefs: RFC-201')"
|
|
70
|
-
run env BYPASS_TRANSITION_ADVISORY=1 bash -c 'printf "{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":\"git commit -m x\"}}" | bash "$0"' "$HOOK"
|
|
71
|
-
[ "$status" -eq 0 ]
|
|
72
|
-
[ -z "$output" ]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@test "silent when commit carries no Refs trailer" {
|
|
76
|
-
echo "---" > docs/rfcs/RFC-201-x.proposed.md; git add -A; commit "chore: untagged"
|
|
77
|
-
run run_hook
|
|
78
|
-
[ "$status" -eq 0 ]
|
|
79
|
-
[ -z "$output" ]
|
|
80
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
|
|
3
|
-
# P078: itil-correction-detect.sh UserPromptSubmit hook must detect
|
|
4
|
-
# strong-signal correction patterns in the incoming user prompt
|
|
5
|
-
# (FFS / DO NOT / direct contradiction / exasperation markers /
|
|
6
|
-
# meta-correction "you always|you never|you keep") and inject a
|
|
7
|
-
# MANDATORY systemMessage instructing the assistant to OFFER
|
|
8
|
-
# /wr-itil:capture-problem (with /wr-itil:manage-problem fallback)
|
|
9
|
-
# BEFORE addressing the operational request.
|
|
10
|
-
#
|
|
11
|
-
# Per ADR-038: full block emits once per session; subsequent prompts
|
|
12
|
-
# emit a terse reminder (<250 bytes) preserving the MANDATORY signal,
|
|
13
|
-
# the gate name, and the capture-problem affordance.
|
|
14
|
-
#
|
|
15
|
-
# Per feedback_behavioural_tests.md (P081): these are behavioural
|
|
16
|
-
# assertions — they simulate the hook payload on stdin and assert on
|
|
17
|
-
# what the hook emits, not on the source text of the hook file.
|
|
18
|
-
|
|
19
|
-
setup() {
|
|
20
|
-
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../../../.." && pwd)"
|
|
21
|
-
HOOK="$REPO_ROOT/packages/itil/hooks/itil-correction-detect.sh"
|
|
22
|
-
SID="itil-correction-test-$$-$RANDOM"
|
|
23
|
-
# An AFK iter exports WR_SUPPRESS_CORRECTION_DETECT=1, which the hook
|
|
24
|
-
# self-suppresses on. Unset it so the positive-detection cases below do not
|
|
25
|
-
# false-RED when this suite runs inside such an iter (the P391 hermeticity
|
|
26
|
-
# class). Suppression is asserted per-invocation via `run env ...` instead.
|
|
27
|
-
unset WR_SUPPRESS_CORRECTION_DETECT
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
teardown() {
|
|
31
|
-
unset WR_SUPPRESS_CORRECTION_DETECT
|
|
32
|
-
rm -f "/tmp/itil-correction-detect-announced-${SID}"
|
|
33
|
-
rm -f "/tmp/itil-correction-detect-announced-${SID}-alt"
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
run_hook() {
|
|
37
|
-
local sid="$1"
|
|
38
|
-
local prompt="$2"
|
|
39
|
-
echo "{\"session_id\":\"$sid\",\"prompt\":$(printf '%s' "$prompt" | jq -Rs .)}" | bash "$HOOK"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@test "correction-detect: 'FFS' triggers full MANDATORY block on first emission" {
|
|
43
|
-
run run_hook "$SID" "FFS, you didn't run the test"
|
|
44
|
-
[ "$status" -eq 0 ]
|
|
45
|
-
[ "${#output}" -gt 300 ]
|
|
46
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
47
|
-
[[ "$output" == *"capture-problem"* ]] || [[ "$output" == *"manage-problem"* ]]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
@test "correction-detect: writes the announcement marker on first emission" {
|
|
51
|
-
run run_hook "$SID" "FFS stop framing it that way"
|
|
52
|
-
[ "$status" -eq 0 ]
|
|
53
|
-
[ -f "/tmp/itil-correction-detect-announced-${SID}" ]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@test "correction-detect: all-caps imperative 'DO NOT' triggers the block" {
|
|
57
|
-
run run_hook "$SID" "DO NOT TELL ME the cache is stale when you haven't installed it"
|
|
58
|
-
[ "$status" -eq 0 ]
|
|
59
|
-
[ "${#output}" -gt 300 ]
|
|
60
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@test "correction-detect: meta-correction 'you always' triggers the block" {
|
|
64
|
-
run run_hook "$SID" "you always do this — frame your own failures as external"
|
|
65
|
-
[ "$status" -eq 0 ]
|
|
66
|
-
[ "${#output}" -gt 300 ]
|
|
67
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@test "correction-detect: direct contradiction 'that's wrong' triggers the block" {
|
|
71
|
-
run run_hook "$SID" "no, that's wrong — the cache wasn't refreshed because you didn't install"
|
|
72
|
-
[ "$status" -eq 0 ]
|
|
73
|
-
[ "${#output}" -gt 300 ]
|
|
74
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@test "correction-detect: exasperation '!!!' triggers the block" {
|
|
78
|
-
run run_hook "$SID" "stop framing your own failure as external!!!"
|
|
79
|
-
[ "$status" -eq 0 ]
|
|
80
|
-
[ "${#output}" -gt 300 ]
|
|
81
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
@test "correction-detect: 'you're not listening' triggers the block" {
|
|
85
|
-
run run_hook "$SID" "you're not listening — install first then check the cache"
|
|
86
|
-
[ "$status" -eq 0 ]
|
|
87
|
-
[ "${#output}" -gt 300 ]
|
|
88
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
@test "correction-detect: second correction prompt in same session emits terse reminder only" {
|
|
92
|
-
run_hook "$SID" "FFS that's not right" >/dev/null
|
|
93
|
-
run run_hook "$SID" "DO NOT do that again"
|
|
94
|
-
[ "$status" -eq 0 ]
|
|
95
|
-
[ "${#output}" -lt 250 ]
|
|
96
|
-
[[ "$output" == *"capture-problem"* ]] || [[ "$output" == *"manage-problem"* ]]
|
|
97
|
-
[[ "$output" != *"NON-OPTIONAL RULES"* ]]
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@test "correction-detect: terse reminder preserves MANDATORY / REQUIRED signal word" {
|
|
101
|
-
run_hook "$SID" "FFS" >/dev/null
|
|
102
|
-
run run_hook "$SID" "you keep doing this"
|
|
103
|
-
[ "$status" -eq 0 ]
|
|
104
|
-
[[ "$output" == *"MANDATORY"* ]] || [[ "$output" == *"REQUIRED"* ]] || [[ "$output" == *"NON-OPTIONAL"* ]]
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
@test "correction-detect: emitted block names capture-problem and manage-problem fallback" {
|
|
108
|
-
run run_hook "$SID" "FFS!"
|
|
109
|
-
[ "$status" -eq 0 ]
|
|
110
|
-
[[ "$output" == *"capture-problem"* ]]
|
|
111
|
-
[[ "$output" == *"manage-problem"* ]]
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
@test "correction-detect: different session_id re-emits the full block" {
|
|
115
|
-
run_hook "$SID" "FFS" >/dev/null
|
|
116
|
-
local SID2="${SID}-alt"
|
|
117
|
-
run run_hook "$SID2" "FFS"
|
|
118
|
-
[ "$status" -eq 0 ]
|
|
119
|
-
[ "${#output}" -gt 300 ]
|
|
120
|
-
rm -f "/tmp/itil-correction-detect-announced-${SID2}"
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
@test "correction-detect: empty session_id emits the full block and writes no marker" {
|
|
124
|
-
run run_hook "" "FFS, that's wrong"
|
|
125
|
-
[ "$status" -eq 0 ]
|
|
126
|
-
[ "${#output}" -gt 300 ]
|
|
127
|
-
[ ! -f "/tmp/itil-correction-detect-announced-" ]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@test "correction-detect: plain conversational prompt does not emit a block" {
|
|
131
|
-
run run_hook "$SID" "what does ADR-013 say about ambiguous decisions?"
|
|
132
|
-
[ "$status" -eq 0 ]
|
|
133
|
-
[ -z "$output" ]
|
|
134
|
-
[ ! -f "/tmp/itil-correction-detect-announced-${SID}" ]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@test "correction-detect: direction-pin 'yes' alone does not trigger" {
|
|
138
|
-
run run_hook "$SID" "yes, go ahead and update the ticket"
|
|
139
|
-
[ "$status" -eq 0 ]
|
|
140
|
-
[ -z "$output" ]
|
|
141
|
-
[ ! -f "/tmp/itil-correction-detect-announced-${SID}" ]
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
# --- P430 / STORY-047: prompt-authorship guard --------------------------------
|
|
145
|
-
# A framework-authored iter prompt carrying an ordinary imperative ("DO NOT
|
|
146
|
-
# skip the gate") is not a correction — nobody is correcting anything, and the
|
|
147
|
-
# absent user cannot act on the nudge. Provenance is a property of the spawning
|
|
148
|
-
# process, so the dispatcher asserts it via env var rather than the hook
|
|
149
|
-
# guessing from content.
|
|
150
|
-
|
|
151
|
-
run_hook_env() {
|
|
152
|
-
local guard="$1" sid="$2" prompt="$3"
|
|
153
|
-
echo "{\"session_id\":\"$sid\",\"prompt\":$(printf '%s' "$prompt" | jq -Rs .)}" \
|
|
154
|
-
| env WR_SUPPRESS_CORRECTION_DETECT="$guard" bash "$HOOK"
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@test "correction-detect: WR_SUPPRESS_CORRECTION_DETECT=1 suppresses a correction prompt" {
|
|
158
|
-
run run_hook_env 1 "$SID" "DO NOT skip the architect gate"
|
|
159
|
-
[ "$status" -eq 0 ]
|
|
160
|
-
[ -z "$output" ]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@test "correction-detect: suppressed path writes no announcement marker" {
|
|
164
|
-
run run_hook_env 1 "$SID" "FFS, you didn't run the test"
|
|
165
|
-
[ "$status" -eq 0 ]
|
|
166
|
-
[ -z "$output" ]
|
|
167
|
-
# The once-per-session budget must survive a prompt that produced no output,
|
|
168
|
-
# or the first real correction in the orchestrator session emits only the
|
|
169
|
-
# terse reminder.
|
|
170
|
-
[ ! -f "/tmp/itil-correction-detect-announced-${SID}" ]
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@test "correction-detect: same prompt still fires with the guard unset" {
|
|
174
|
-
run run_hook "$SID" "DO NOT skip the architect gate"
|
|
175
|
-
[ "$status" -eq 0 ]
|
|
176
|
-
[ "${#output}" -gt 300 ]
|
|
177
|
-
[[ "$output" == *"MANDATORY"* ]]
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
@test "correction-detect: only the literal value 1 suppresses" {
|
|
181
|
-
for v in 0 true yes ""; do
|
|
182
|
-
run run_hook_env "$v" "$SID-$RANDOM" "FFS, that's wrong"
|
|
183
|
-
[ "$status" -eq 0 ]
|
|
184
|
-
[ -n "$output" ] || {
|
|
185
|
-
echo "guard value '$v' wrongly suppressed"
|
|
186
|
-
false
|
|
187
|
-
}
|
|
188
|
-
done
|
|
189
|
-
}
|