@windyroad/voice-tone 0.5.12 → 0.5.13

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.
@@ -123,5 +123,5 @@
123
123
  }
124
124
  },
125
125
  "name": "wr-voice-tone",
126
- "version": "0.5.12"
126
+ "version": "0.5.13"
127
127
  }
@@ -318,6 +318,28 @@ if [ "$EXTERNAL_COMMS_LEAK_PREFILTER" = "yes" ]; then
318
318
  fi
319
319
  fi
320
320
 
321
+ # ---------- Repo-visibility precondition: git-commit-message surface (P365) ----------
322
+ # A commit message only becomes external-facing prose when it lands in a PUBLIC
323
+ # GitHub repo (git log / PR commits tab / release-page auto-notes / CHANGELOG).
324
+ # In private or internal repos the marker-review delegation deny below is a pure
325
+ # false-positive (P365 — user direction 2026-06-11: "this MUST NOT fire for
326
+ # private repos"). Confirm visibility authoritatively via gh and silent-pass the
327
+ # marker gate on any non-PUBLIC result. Any INDETERMINATE result (gh absent,
328
+ # unauthenticated, no remote, API error → empty $REPO_VISIBILITY) is treated as
329
+ # non-public: a commit message is only demonstrably external when the repo is
330
+ # confirmably PUBLIC, so the conservative direction for THIS surface is to not
331
+ # fire. This is a fail-open on the voice/tone-and-prose review ONLY — the
332
+ # leak-pattern pre-filter above (credentials / prod-URLs) has already run for
333
+ # every surface in every repo, so the high-stakes secrecy net is unaffected.
334
+ # Scoped to git-commit-message only; the gh-issue/pr/api, npm-publish, and
335
+ # changeset-author surfaces are inherently external and stay gated regardless.
336
+ if [ "$SURFACE" = "git-commit-message" ]; then
337
+ REPO_VISIBILITY=$(gh repo view --json visibility -q .visibility 2>/dev/null || echo "")
338
+ if [ "$REPO_VISIBILITY" != "PUBLIC" ]; then
339
+ exit 0
340
+ fi
341
+ fi
342
+
321
343
  # ---------- Marker-based gate (per-evaluator marker per ADR-028 amended 2026-05-14) ----------
322
344
  SESSION_DIR="${TMPDIR:-/tmp}/claude-risk-${SESSION_ID}"
323
345
  mkdir -p "$SESSION_DIR"
@@ -61,9 +61,23 @@ print(json.dumps({
61
61
  " "$file_path" "$content"
62
62
  }
63
63
 
64
+ # Mock `gh repo view --json visibility` for the git-commit-message surface
65
+ # repo-visibility precondition (P365). vis ∈ {PUBLIC,PRIVATE,INTERNAL}; pass the
66
+ # literal "FAIL" to simulate gh absent / unauthenticated (non-zero exit).
67
+ mock_gh_visibility() {
68
+ local vis="$1"
69
+ mkdir -p "$TEST_PROJECT_DIR/mockbin"
70
+ if [ "$vis" = "FAIL" ]; then
71
+ printf '#!/usr/bin/env bash\nexit 1\n' > "$TEST_PROJECT_DIR/mockbin/gh"
72
+ else
73
+ printf '#!/usr/bin/env bash\necho %s\n' "$vis" > "$TEST_PROJECT_DIR/mockbin/gh"
74
+ fi
75
+ chmod +x "$TEST_PROJECT_DIR/mockbin/gh"
76
+ }
77
+
64
78
  run_hook() {
65
79
  local input="$1"
66
- run bash -c "cd '$TEST_PROJECT_DIR' && printf '%s' \"\$1\" | '$HOOK'" _ "$input"
80
+ run bash -c "cd '$TEST_PROJECT_DIR' && export PATH='$TEST_PROJECT_DIR/mockbin':\$PATH && printf '%s' \"\$1\" | '$HOOK'" _ "$input"
67
81
  }
68
82
 
69
83
  # ---------- Tests ----------
@@ -242,6 +256,7 @@ run_hook() {
242
256
  # ---------------------------------------------------------------------------
243
257
 
244
258
  @test "P082: git commit -m with literal -m body denies and delegates to voice-tone evaluator" {
259
+ mock_gh_visibility PUBLIC
245
260
  INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
246
261
  run_hook "$INPUT"
247
262
  [ "$status" -eq 0 ]
@@ -251,6 +266,7 @@ run_hook() {
251
266
  }
252
267
 
253
268
  @test "P082: git commit --message with literal body denies and delegates" {
269
+ mock_gh_visibility PUBLIC
254
270
  INPUT=$(build_bash_input "git commit --message \"happy to help further with this fix\"")
255
271
  run_hook "$INPUT"
256
272
  [ "$status" -eq 0 ]
@@ -259,6 +275,7 @@ run_hook() {
259
275
  }
260
276
 
261
277
  @test "P082: git commit --amend -m is intercepted (P082 SC2)" {
278
+ mock_gh_visibility PUBLIC
262
279
  INPUT=$(build_bash_input "git commit --amend -m \"rewritten subject\"")
263
280
  run_hook "$INPUT"
264
281
  [ "$status" -eq 0 ]
@@ -270,6 +287,7 @@ run_hook() {
270
287
  # Build a HEREDOC-shaped command. The hook regex pulls the body BETWEEN
271
288
  # the <<'EOF' opener and the closing EOF marker — the extracted DRAFT is
272
289
  # the inner text, NOT the literal `$(cat <<'EOF' ... EOF)` wrapper.
290
+ mock_gh_visibility PUBLIC
273
291
  BODY=$'feat(foo): add bar\n\nWe observed a build failure on Node 20.'
274
292
  CMD=$'git commit -m "$(cat <<\'EOF\'\n'"$BODY"$'\nEOF\n)"'
275
293
  INPUT=$(build_bash_input "$CMD")
@@ -312,6 +330,7 @@ run_hook() {
312
330
  }
313
331
 
314
332
  @test "P082: per-evaluator marker keyed on (body, git-commit-message) permits the call" {
333
+ mock_gh_visibility PUBLIC
315
334
  BODY="docs(retro): close iter 3 ask-hygiene trail"
316
335
  SURFACE="git-commit-message"
317
336
  KEY=$(printf '%s\n%s' "$BODY" "$SURFACE" | shasum -a 256 | cut -d' ' -f1)
@@ -323,6 +342,59 @@ run_hook() {
323
342
  [ -z "$output" ]
324
343
  }
325
344
 
345
+ # ---------------------------------------------------------------------------
346
+ # P365 — repo-visibility precondition on the git-commit-message surface.
347
+ # Shared canonical hook (ADR-017 sync), so the precondition applies to the
348
+ # voice-tone evaluator too: a commit message is external-facing prose ONLY in
349
+ # a PUBLIC repo. In private/internal repos — or any indeterminate gh result —
350
+ # the marker-review deny is a pure false-positive (user direction 2026-06-11:
351
+ # "this MUST NOT fire for private repos"). Scoped to the git-commit-message
352
+ # surface only; the gh-issue/pr/npm/changeset surfaces are inherently external
353
+ # and stay gated regardless of repo visibility.
354
+ # ---------------------------------------------------------------------------
355
+
356
+ @test "P365: git commit -m in a PRIVATE repo silent-passes (no external-comms deny)" {
357
+ mock_gh_visibility PRIVATE
358
+ INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
359
+ run_hook "$INPUT"
360
+ [ "$status" -eq 0 ]
361
+ [ -z "$output" ]
362
+ }
363
+
364
+ @test "P365: git commit -m in an INTERNAL repo silent-passes" {
365
+ mock_gh_visibility INTERNAL
366
+ INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
367
+ run_hook "$INPUT"
368
+ [ "$status" -eq 0 ]
369
+ [ -z "$output" ]
370
+ }
371
+
372
+ @test "P365: git commit -m when gh is unavailable/indeterminate silent-passes (fail-non-public)" {
373
+ mock_gh_visibility FAIL
374
+ INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
375
+ run_hook "$INPUT"
376
+ [ "$status" -eq 0 ]
377
+ [ -z "$output" ]
378
+ }
379
+
380
+ @test "P365: git commit -m in a PUBLIC repo still denies+delegates (gate intact, precondition surface-scoped)" {
381
+ mock_gh_visibility PUBLIC
382
+ INPUT=$(build_bash_input "git commit -m \"I've implemented the feature\"")
383
+ run_hook "$INPUT"
384
+ [ "$status" -eq 0 ]
385
+ [[ "$output" == *"deny"* ]]
386
+ [[ "$output" == *"git-commit-message"* ]]
387
+ }
388
+
389
+ @test "P365: PRIVATE visibility does NOT short-circuit the gh-issue surface (still denies+delegates)" {
390
+ mock_gh_visibility PRIVATE
391
+ INPUT=$(build_bash_input "gh issue create --title x --body 'a clean issue body'")
392
+ run_hook "$INPUT"
393
+ [ "$status" -eq 0 ]
394
+ [[ "$output" == *"deny"* ]]
395
+ [[ "$output" == *"gh-issue-create"* ]]
396
+ }
397
+
326
398
  # ---------------------------------------------------------------------------
327
399
  # P364 — backtick-bearing double-quoted --body marker-key mismatch.
328
400
  # The voice-tone gate shares the byte-identical canonical external-comms-gate.sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/voice-tone",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "description": "Voice and tone enforcement for user-facing copy",
5
5
  "bin": {
6
6
  "windyroad-voice-tone": "./bin/install.mjs"