@windyroad/retrospective 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "wr-retrospective",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Session retrospective reminders and plan review for Claude Code"
5
5
  }
package/hooks/hooks.json CHANGED
@@ -19,6 +19,17 @@
19
19
  ]
20
20
  }
21
21
  ],
22
+ "PreToolUse": [
23
+ {
24
+ "matcher": "Bash",
25
+ "hooks": [
26
+ {
27
+ "type": "command",
28
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/retrospective-readme-jtbd-currency.sh"
29
+ }
30
+ ]
31
+ }
32
+ ],
22
33
  "Stop": [
23
34
  {
24
35
  "hooks": [
@@ -0,0 +1,203 @@
1
+ #!/bin/bash
2
+ # P159: PreToolUse:Bash hook — denies `git commit` invocations whose
3
+ # post-commit working tree exhibits JTBD-currency drift in any
4
+ # packages/<plugin>/README.md (no JTBD-NNN anchor, stale or
5
+ # deprecated-only citation, or skill directory missing from README).
6
+ #
7
+ # Hook-level enforcement at commit time replaces ADR-051 Phase 1's
8
+ # retro-time advisory surface (shipped under P158, df47ad1). The user
9
+ # correction (P159) and the architect verdict identified the retro
10
+ # surface as too late: the most-common drift class (contributor adds
11
+ # skill/hook/agent and forgets the README) ships in a commit that
12
+ # does not touch README.md, so a retro-time consumer sees the drift
13
+ # only after the contributor has already committed.
14
+ #
15
+ # Detection delegates to the existing detector script
16
+ # (`packages/retrospective/scripts/check-readme-jtbd-currency.sh`),
17
+ # invoked against the project's working tree (`./packages/` +
18
+ # `./docs/jtbd/`). The hook reads the detector's
19
+ # `TOTAL packages=<N> with_jtbd=<M> drift_instances=<K>` summary and
20
+ # denies when `drift_instances > 0`.
21
+ #
22
+ # Allow paths (exit 0 silently per ADR-045 Pattern 1):
23
+ # - tool_name != "Bash" (only Bash invocations are gated)
24
+ # - command does not contain `git commit` substring (non-commit
25
+ # Bash bypasses entirely — `git
26
+ # status`, `git log`, etc.)
27
+ # - BYPASS_JTBD_CURRENCY=1 (single-most-common legitimate
28
+ # escape — bypass-traceable via
29
+ # shell history)
30
+ # - outside a git work tree (adopter sessions outside the
31
+ # plugin monorepo)
32
+ # - no `./packages/` directory (project does not have ADR-051's
33
+ # structural anchor — adopter
34
+ # project shape; gate is a no-op)
35
+ # - no `./docs/jtbd/` directory (project has not run
36
+ # /wr-jtbd:update-guide; gate is a
37
+ # no-op)
38
+ # - detector exits non-zero (parse error / hostile env;
39
+ # fail-open per ADR-013 Rule 6)
40
+ # - detector emits no TOTAL line (no packages found; nothing to
41
+ # gate)
42
+ # - drift_instances == 0 (clean tree)
43
+ #
44
+ # Deny shape (per ADR-013 Rule 1 — deny redirects with mechanical
45
+ # recovery; ADR-045 deny-band ≤300 bytes):
46
+ # - Names the first offending plugin slug + drift hint vocabulary.
47
+ # - Names the wr-jtbd:agent recovery path AND the hand-edit fallback
48
+ # (graceful degradation when @windyroad/jtbd is not installed).
49
+ # - Names BYPASS_JTBD_CURRENCY=1 as the env-var escape.
50
+ # - Cites P159 for traceability.
51
+ # - Truncates the drift_hints CSV to the first hint to keep the
52
+ # deny-band ≤300 bytes for worst-case slug + hint combinations.
53
+ #
54
+ # Cost: one invocation of `check-readme-jtbd-currency.sh` per `git
55
+ # commit` (~80–150ms in the worst case across 12 plugin READMEs +
56
+ # ~30 JTBD job files; per the architect's ADR-023 perf review at
57
+ # Phase 1 design time). Per-invocation deterministic; no marker
58
+ # (mirrors P125 `staging-detect.sh` and P141
59
+ # `itil-changeset-discipline.sh` precedent — architect-approved
60
+ # no-marker design when detection cost stays under ~150ms).
61
+ #
62
+ # References:
63
+ # ADR-005 — plugin testing strategy (hook bats live under
64
+ # `packages/<plugin>/hooks/test/`).
65
+ # ADR-013 Rule 1 — deny redirects with mechanical recovery (the
66
+ # deny names the wr-jtbd:agent recovery, the hand-edit
67
+ # fallback, and the BYPASS env override).
68
+ # ADR-013 Rule 6 — non-interactive fail-safe (fail-open outside a
69
+ # git work tree, on parse error, in projects lacking
70
+ # ADR-051 anchors, and on detector failure).
71
+ # ADR-014 — governance skills commit their own work (this hook
72
+ # keeps iter commits self-contained).
73
+ # ADR-018 — inter-iteration release cadence (the hook strengthens
74
+ # release-cadence integrity by ensuring every publishable
75
+ # iter has a current README before commit).
76
+ # ADR-038 — progressive disclosure / deny-message terseness budget.
77
+ # ADR-045 — hook injection budget (Pattern 1 silent-on-pass; deny
78
+ # band ≤300 bytes for this hook).
79
+ # ADR-051 — JTBD-anchored README rule (this hook is the load-
80
+ # bearing-from-the-start commit-gate surface; supersedes
81
+ # retro-time advisory consumption as primary).
82
+ # ADR-052 — behavioural-tests default (bats fixture asserts on
83
+ # emitted JSON, not source content).
84
+ # P081 — behavioural tests preferred over structural greps.
85
+ # P125 — sibling staging-trap helper (per-invocation no-marker).
86
+ # P141 — sibling changeset-discipline gate on `git commit` (same
87
+ # hook shape).
88
+ # P158 — retro Step 2b wiring (backup advisory; survives this
89
+ # hook's primary-surface migration).
90
+ # P159 — this hook.
91
+
92
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
93
+ DETECTOR="$SCRIPT_DIR/../scripts/check-readme-jtbd-currency.sh"
94
+
95
+ INPUT=$(cat)
96
+
97
+ TOOL_NAME=$(echo "$INPUT" | python3 -c "
98
+ import sys, json
99
+ try:
100
+ data = json.load(sys.stdin)
101
+ print(data.get('tool_name', ''))
102
+ except:
103
+ print('')
104
+ " 2>/dev/null || echo "")
105
+
106
+ # Only gate Bash. Non-Bash tools bypass entirely.
107
+ if [ "$TOOL_NAME" != "Bash" ]; then
108
+ exit 0
109
+ fi
110
+
111
+ COMMAND=$(echo "$INPUT" | python3 -c "
112
+ import sys, json
113
+ try:
114
+ data = json.load(sys.stdin)
115
+ print(data.get('tool_input', {}).get('command', ''))
116
+ except:
117
+ print('')
118
+ " 2>/dev/null || echo "")
119
+
120
+ # Only fire on `git commit` invocations. Substring match catches common
121
+ # shapes (`git commit -m`, `git commit --amend`, leading `cd && git
122
+ # commit`, `chore: version packages` release commits routed via
123
+ # `git commit -m 'chore: version packages'`, etc.) without
124
+ # over-matching unrelated bash.
125
+ case "$COMMAND" in
126
+ *"git commit"*) ;;
127
+ *) exit 0 ;;
128
+ esac
129
+
130
+ # Bypass via env var — single most-common legitimate escape.
131
+ if [ "${BYPASS_JTBD_CURRENCY:-}" = "1" ]; then
132
+ exit 0
133
+ fi
134
+
135
+ # Fail-open if not inside a git working tree.
136
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
137
+
138
+ # Fail-open if the project lacks ADR-051's structural anchors.
139
+ # Adopter projects without `./packages/` or `./docs/jtbd/` are not
140
+ # subject to the rule; the hook is a no-op for them.
141
+ [ -d "./packages" ] || exit 0
142
+ [ -d "./docs/jtbd" ] || exit 0
143
+
144
+ # Fail-open if the detector script itself is missing (defensive —
145
+ # hook + detector ship together, but install-time corruption or
146
+ # adopter-side patching should not block legitimate commits).
147
+ [ -x "$DETECTOR" ] || exit 0
148
+
149
+ # Run the detector. Capture exit code + output. Fail-open on detector
150
+ # error (exit != 0).
151
+ DETECTOR_OUTPUT=$(bash "$DETECTOR" "./packages" "./docs/jtbd" 2>/dev/null) || exit 0
152
+
153
+ # Parse the TOTAL summary line. If absent, no packages were
154
+ # enumerated — fail-open (no drift to report).
155
+ TOTAL_LINE=$(echo "$DETECTOR_OUTPUT" | grep -E '^TOTAL packages=' | tail -n1)
156
+ [ -n "$TOTAL_LINE" ] || exit 0
157
+
158
+ # Extract drift_instances=<K>.
159
+ DRIFT_INSTANCES=$(echo "$TOTAL_LINE" | grep -oE 'drift_instances=[0-9]+' | head -n1 | cut -d'=' -f2)
160
+ [ -n "$DRIFT_INSTANCES" ] || exit 0
161
+
162
+ # Allow path: clean tree.
163
+ if [ "$DRIFT_INSTANCES" -eq 0 ]; then
164
+ exit 0
165
+ fi
166
+
167
+ # Drift detected — extract first offending package + its drift hints
168
+ # for the deny message. The detector emits one "README package=<name>
169
+ # ... drift_hints=<csv>" line per package; we name the first one with
170
+ # a non-empty drift_hints.
171
+ OFFENDING_LINE=$(echo "$DETECTOR_OUTPUT" | grep -E '^README package=' | grep -vE 'drift_hints=$' | head -n1)
172
+ OFFENDING_SLUG=$(echo "$OFFENDING_LINE" | grep -oE 'package=[A-Za-z0-9_-]+' | head -n1 | cut -d'=' -f2)
173
+ OFFENDING_HINTS=$(echo "$OFFENDING_LINE" | grep -oE 'drift_hints=[A-Za-z0-9,_-]+' | head -n1 | cut -d'=' -f2)
174
+
175
+ # Fall back to a generic name if parsing failed (shouldn't happen but
176
+ # defensive).
177
+ [ -n "$OFFENDING_SLUG" ] || OFFENDING_SLUG="(unknown)"
178
+ [ -n "$OFFENDING_HINTS" ] || OFFENDING_HINTS="drift"
179
+
180
+ # Truncate the hints CSV to the first hint. Multi-hint cases (e.g.
181
+ # both `missing-jtbd-section` and `skill-inventory-drift` on one
182
+ # package) are bounded so the deny-band stays under 300 bytes for
183
+ # worst-case slug + hint combinations.
184
+ PRIMARY_HINT="${OFFENDING_HINTS%%,*}"
185
+
186
+ # Deny — voice/tone budget per ADR-045 deny-band ≤300 bytes total
187
+ # (envelope ~137 bytes; REASON ~163 bytes for worst-case slug +
188
+ # hint). Names the offending plugin slug, the primary drift hint,
189
+ # the wr-jtbd:agent recovery path with hand-edit fallback (graceful
190
+ # degradation per architect F advisory), the BYPASS env, and the
191
+ # P159 cite.
192
+ REASON="BLOCKED: P159 JTBD drift in ${OFFENDING_SLUG} (${PRIMARY_HINT}). Recovery: wr-jtbd:agent OR cite a JTBD-NNN in README. Bypass: BYPASS_JTBD_CURRENCY=1."
193
+
194
+ cat <<EOF
195
+ {
196
+ "hookSpecificOutput": {
197
+ "hookEventName": "PreToolUse",
198
+ "permissionDecision": "deny",
199
+ "permissionDecisionReason": "${REASON}"
200
+ }
201
+ }
202
+ EOF
203
+ exit 0
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env bats
2
+
3
+ # P159: retrospective-readme-jtbd-currency.sh PreToolUse:Bash hook must
4
+ # deny `git commit` invocations whose post-commit working tree exhibits
5
+ # JTBD-currency drift (no JTBD-NNN anchor in a plugin README, stale or
6
+ # deprecated-only citations, or skills/<dir>/ missing from the README).
7
+ # Hook-level enforcement at commit time replaces ADR-051 Phase 1's
8
+ # retro-time advisory surface, which the user correction (P159) and the
9
+ # architect verdict identify as too late: the most-common drift class
10
+ # (contributor adds skill/hook/agent and forgets the README) ships in a
11
+ # commit that doesn't touch README.md, so a retro-time consumer sees the
12
+ # drift only after the contributor has already committed.
13
+ #
14
+ # Detection delegates to the existing
15
+ # `packages/retrospective/scripts/check-readme-jtbd-currency.sh`
16
+ # detector, which the hook invokes against the project's working tree
17
+ # (`./packages/` + `./docs/jtbd/`). The hook reads the detector's
18
+ # `TOTAL packages=<N> with_jtbd=<M> drift_instances=<K>` summary line
19
+ # and denies when `drift_instances > 0`.
20
+ #
21
+ # Per ADR-005 (plugin testing strategy) — hook bats live under
22
+ # packages/<plugin>/hooks/test/ and assert on emitted JSON, not source
23
+ # content. Per ADR-052 / P081 — behavioural; no source greps. Per
24
+ # ADR-045 Pattern 1 — allow paths emit 0 bytes; deny-band ≤300 bytes.
25
+ # Per ADR-013 Rule 1 — deny redirects to mechanical recovery (here: the
26
+ # wr-jtbd:agent for prose-weaving guidance, with hand-edit fallback).
27
+ # Per ADR-013 Rule 6 — fail-open outside a git work tree, on parse
28
+ # errors, or in projects without ADR-051's structural anchors
29
+ # (./packages/ or ./docs/jtbd/) so adopter projects are not blocked.
30
+
31
+ setup() {
32
+ SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
33
+ HOOK="$SCRIPT_DIR/retrospective-readme-jtbd-currency.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
+ echo "seed" > seed.txt
41
+ git add seed.txt
42
+ git -c commit.gpgsign=false commit --quiet -m "initial"
43
+ unset BYPASS_JTBD_CURRENCY
44
+ }
45
+
46
+ teardown() {
47
+ cd "$ORIG_DIR"
48
+ rm -rf "$TEST_DIR"
49
+ unset BYPASS_JTBD_CURRENCY
50
+ }
51
+
52
+ run_bash_hook() {
53
+ local cmd="$1"
54
+ local json
55
+ json=$(printf '{"tool_name":"Bash","tool_input":{"command":"%s"}}' "$cmd")
56
+ echo "$json" | bash "$HOOK"
57
+ }
58
+
59
+ # Helper: build a stub project layout with a drifted plugin README.
60
+ # README has no JTBD-NNN citation; jtbd dir has one resolving job.
61
+ make_drifted_project() {
62
+ mkdir -p packages/stub docs/jtbd/plugin-user
63
+ printf '%s\n' "# @windyroad/stub" "no jtbd anchor here" > packages/stub/README.md
64
+ cat > docs/jtbd/plugin-user/JTBD-302-trust-readme.proposed.md <<'EOF'
65
+ ---
66
+ status: proposed
67
+ job-id: trust-readme
68
+ persona: plugin-user
69
+ date-created: 2026-05-04
70
+ ---
71
+
72
+ # JTBD-302
73
+ EOF
74
+ }
75
+
76
+ # Helper: build a stub project layout with a clean plugin README.
77
+ # README cites a resolving JTBD-NNN; no skill drift.
78
+ make_clean_project() {
79
+ mkdir -p packages/stub docs/jtbd/plugin-user
80
+ printf '%s\n' "# @windyroad/stub" "Serves JTBD-302." > packages/stub/README.md
81
+ cat > docs/jtbd/plugin-user/JTBD-302-trust-readme.proposed.md <<'EOF'
82
+ ---
83
+ status: proposed
84
+ job-id: trust-readme
85
+ persona: plugin-user
86
+ date-created: 2026-05-04
87
+ ---
88
+
89
+ # JTBD-302
90
+ EOF
91
+ }
92
+
93
+ # Helper: build a stub project with skill-inventory-drift —
94
+ # packages/stub/skills/orphan/ exists but README doesn't name "orphan".
95
+ make_skill_drift_project() {
96
+ mkdir -p packages/stub/skills/orphan docs/jtbd/plugin-user
97
+ printf '%s\n' "# @windyroad/stub" "Serves JTBD-302." > packages/stub/README.md
98
+ cat > docs/jtbd/plugin-user/JTBD-302-trust-readme.proposed.md <<'EOF'
99
+ ---
100
+ status: proposed
101
+ job-id: trust-readme
102
+ persona: plugin-user
103
+ date-created: 2026-05-04
104
+ ---
105
+
106
+ # JTBD-302
107
+ EOF
108
+ }
109
+
110
+ # ── Trap detection: deny when drift detected ───────────────────────────────
111
+
112
+ @test "deny: drifted README (no JTBD-NNN cite) on git commit triggers deny" {
113
+ make_drifted_project
114
+ run run_bash_hook "git commit -m 'feat'"
115
+ [ "$status" -eq 0 ]
116
+ [[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
117
+ [[ "$output" == *"P159"* ]]
118
+ }
119
+
120
+ @test "deny: skill-inventory-drift on git commit triggers deny" {
121
+ make_skill_drift_project
122
+ run run_bash_hook "git commit -m 'feat'"
123
+ [ "$status" -eq 0 ]
124
+ [[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
125
+ }
126
+
127
+ @test "deny message names the offending plugin slug" {
128
+ make_drifted_project
129
+ run run_bash_hook "git commit -m 'feat'"
130
+ [ "$status" -eq 0 ]
131
+ [[ "$output" == *"stub"* ]]
132
+ }
133
+
134
+ @test "deny message names the wr-jtbd:agent recovery path" {
135
+ make_drifted_project
136
+ run run_bash_hook "git commit -m 'feat'"
137
+ [ "$status" -eq 0 ]
138
+ [[ "$output" == *"wr-jtbd"* ]]
139
+ }
140
+
141
+ @test "deny message stays under ADR-045 deny-band (<300 bytes)" {
142
+ make_drifted_project
143
+ run run_bash_hook "git commit -m 'feat'"
144
+ [ "$status" -eq 0 ]
145
+ [ "${#output}" -lt 300 ]
146
+ }
147
+
148
+ @test "deny: chore release commit (chore: version packages) is subject to the gate" {
149
+ make_drifted_project
150
+ run run_bash_hook "chore: version packages"
151
+ # Not a `git commit` invocation — should NOT trigger deny because the
152
+ # command field is the message, not the invocation. The actual release
153
+ # path runs `git commit` which IS gated; verify that shape:
154
+ [ "$status" -eq 0 ]
155
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
156
+
157
+ # Now the canonical release commit shape:
158
+ run run_bash_hook "git commit -m 'chore: version packages'"
159
+ [ "$status" -eq 0 ]
160
+ [[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
161
+ }
162
+
163
+ @test "deny: git commit --amend on drifted tree also triggers deny" {
164
+ make_drifted_project
165
+ run run_bash_hook "git commit --amend --no-edit"
166
+ [ "$status" -eq 0 ]
167
+ [[ "$output" == *"\"permissionDecision\": \"deny\""* ]]
168
+ }
169
+
170
+ # ── Allow paths: each non-trap shape must NOT deny ─────────────────────────
171
+
172
+ @test "allow: clean README (cites resolving JTBD-NNN) on git commit allows the commit" {
173
+ make_clean_project
174
+ run run_bash_hook "git commit -m 'feat'"
175
+ [ "$status" -eq 0 ]
176
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
177
+ }
178
+
179
+ @test "allow: BYPASS_JTBD_CURRENCY=1 env var allows drifted commit" {
180
+ make_drifted_project
181
+ BYPASS_JTBD_CURRENCY=1 run run_bash_hook "git commit -m 'feat'"
182
+ [ "$status" -eq 0 ]
183
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
184
+ }
185
+
186
+ @test "allow: non-Bash tool exits 0 without deny" {
187
+ make_drifted_project
188
+ run bash -c "echo '{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"foo.md\"}}' | bash $HOOK"
189
+ [ "$status" -eq 0 ]
190
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
191
+ }
192
+
193
+ @test "allow: Bash command that is NOT git commit (e.g., git status) bypasses detection" {
194
+ make_drifted_project
195
+ run run_bash_hook "git status"
196
+ [ "$status" -eq 0 ]
197
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
198
+ }
199
+
200
+ # ── Fail-open contracts (ADR-013 Rule 6) ───────────────────────────────────
201
+
202
+ @test "allow: outside a git work tree exits 0 without deny (fail-open)" {
203
+ cd "$ORIG_DIR"
204
+ TEMP_NONGIT=$(mktemp -d)
205
+ cd "$TEMP_NONGIT"
206
+ run run_bash_hook "git commit -m 'feat'"
207
+ cd "$TEST_DIR"
208
+ rm -rf "$TEMP_NONGIT"
209
+ [ "$status" -eq 0 ]
210
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
211
+ }
212
+
213
+ @test "allow: project without packages/ dir exits 0 without deny (fail-open)" {
214
+ # No packages/, no docs/jtbd/ — adopter project shape.
215
+ run run_bash_hook "git commit -m 'feat'"
216
+ [ "$status" -eq 0 ]
217
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
218
+ }
219
+
220
+ @test "allow: project with packages/ but no docs/jtbd/ exits 0 without deny (fail-open)" {
221
+ mkdir -p packages/stub
222
+ echo "# stub" > packages/stub/README.md
223
+ run run_bash_hook "git commit -m 'feat'"
224
+ [ "$status" -eq 0 ]
225
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
226
+ }
227
+
228
+ @test "allow: empty JSON exits 0 without deny (fail-open on parse-incomplete)" {
229
+ make_drifted_project
230
+ run bash -c "echo '{}' | bash $HOOK"
231
+ [ "$status" -eq 0 ]
232
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
233
+ }
234
+
235
+ @test "allow: malformed JSON exits 0 without deny (fail-open on parse error)" {
236
+ make_drifted_project
237
+ run bash -c "echo 'not-json' | bash $HOOK"
238
+ [ "$status" -eq 0 ]
239
+ [[ "$output" != *"\"permissionDecision\": \"deny\""* ]]
240
+ }
241
+
242
+ # ── Allow path silence (ADR-045 Pattern 1) ─────────────────────────────────
243
+
244
+ @test "allow path on clean tree emits 0 bytes (ADR-045 Pattern 1 silent-on-pass)" {
245
+ make_clean_project
246
+ run run_bash_hook "git commit -m 'feat'"
247
+ [ "$status" -eq 0 ]
248
+ [ "${#output}" -eq 0 ]
249
+ }
250
+
251
+ @test "allow path on non-Bash tool emits 0 bytes (silent-on-pass)" {
252
+ make_drifted_project
253
+ run bash -c "echo '{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"foo.md\"}}' | bash $HOOK"
254
+ [ "$status" -eq 0 ]
255
+ [ "${#output}" -eq 0 ]
256
+ }
257
+
258
+ @test "allow path on non-commit Bash emits 0 bytes (silent-on-pass)" {
259
+ make_drifted_project
260
+ run run_bash_hook "git status"
261
+ [ "$status" -eq 0 ]
262
+ [ "${#output}" -eq 0 ]
263
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/retrospective",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Session retrospectives that update briefings and create problem tickets",
5
5
  "bin": {
6
6
  "windyroad-retrospective": "./bin/install.mjs"