crewforth 3.2.0-rc.9 → 3.2.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.
package/CHANGELOG.md CHANGED
@@ -5,7 +5,7 @@ Crewforth was named Claude Starter Kit until 3.0.0.
5
5
  Notable changes to this project are recorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/),
6
6
  versioning follows [SemVer](https://semver.org/).
7
7
 
8
- ## [Unreleased] — 3.2.0
8
+ ## [3.2.0] — 2026-10-21
9
9
 
10
10
  ### Added
11
11
 
@@ -233,6 +233,16 @@ versioning follows [SemVer](https://semver.org/).
233
233
  - **`change: review` is read as `audit`.** The model table of the working rules names review with audit, and
234
234
  `crew-review-agent` cards that said `change: review` were refused. It now takes the audit floor, on a critical path
235
235
  too; a kind that is not on the list is still refused.
236
+ - **An install or an update never loosens the trace gate by itself.** The installer suggested exempting
237
+ the co-author trailer whenever the last 80 commits carried one, and with no terminal to ask in it took its own
238
+ suggestion: an update wrote the line into `.trace-allowlist.txt` and staged it, undoing a removal the team had
239
+ committed. The suggestion is now always to keep the gate, and the exemption is written only when you type
240
+ `loosen` in the interactive review.
241
+ - **A commit refusal names a trace in the message even when the staged content also fails.** The content gate
242
+ scanned the `-m` message only when the staged change was clean, so a refusal for a silenced lint check said nothing about
243
+ the session-link trailer in the same message, and the session showed the user the traced message again. Both are
244
+ named in one refusal now, and a trace in the message adds that a harness reminder to end a message with a
245
+ trailer does not override §4.1. The git `commit-msg` hook still stops the trailer at the commit itself.
236
246
  - **A one-line task card may open with `Kart`.** Sessions that write in Turkish lead the card with `Kart —` as
237
247
  others write `Card —`, and the call was refused as having no card. The Turkish word is read like the English one;
238
248
  the fields after it are held to the same rules.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0-rc.9
1
+ 3.2.0
package/adopt.sh CHANGED
@@ -199,8 +199,7 @@ _mt() { # $1 = English text (the key); further args fill %s; result in _M
199
199
  "project wins (fixed)") s='proje önde (sabit)' ;;
200
200
  "on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)") s="çakışmada projenin kuralı geçerli; DISCIPLINE.md §4'te proje yalnız sıkılaştırabilir, gevşetemez (tek istisna: kurulumda sizin seçtiğiniz §4.1 iz izin listesi); Crewforth boşlukları doldurur (değiştirilemez)" ;;
201
201
  "3 Trace gate") s='3 İz kapısı' ;;
202
- "loosen (.trace-allowlist)") s='gevşet (.trace-allowlist)' ;;
203
- "co-author/sign-off present in git log — may be a convention") s='git geçmişinde co-author/sign-off var — ekibin alışkanlığı olabilir' ;;
202
+ "co-author/sign-off present in git log — type loosen in the review to exempt it; never assumed") s='git geçmişinde co-author/sign-off var — muaf tutmak için incelemede loosen yazın; kendiliğinden yapılmaz' ;;
204
203
  "keep") s='koru' ;;
205
204
  "no co-author/sign-off convention seen") s='co-author/sign-off alışkanlığı görülmedi' ;;
206
205
  "4 Share/hide") s='4 Paylaş/gizle' ;;
@@ -789,7 +788,7 @@ else
789
788
  fi
790
789
  propm '2 Precedence' 'project wins (fixed)' "on conflict the project's rules win; in DISCIPLINE.md §4 they can only tighten, never loosen (the one exception: the §4.1 trace allowlist you choose at adoption); Crewforth fills gaps (not overridable)"
791
790
  if [ "$COAUTHOR" = 1 ]; then
792
- propm '3 Trace gate' 'loosen (.trace-allowlist)' 'co-author/sign-off present in git log — may be a convention'
791
+ propm '3 Trace gate' 'keep' 'co-author/sign-off present in git log — type loosen in the review to exempt it; never assumed'
793
792
  else
794
793
  propm '3 Trace gate' 'keep' 'no co-author/sign-off convention seen'
795
794
  fi
@@ -817,7 +816,12 @@ fi
817
816
  # ============ COMPILE DECISIONS + OVERRIDE (Stage B) ============
818
817
  DEC1="$([ "$N_PAGENTS" != 0 ] && echo keep || echo none)"
819
818
  DEC2="project" # precedence is FIXED to project-wins (not overridable — reflected in the @import comment)
820
- DEC3="$([ "$COAUTHOR" = 1 ] && echo loosen || echo keep)"
819
+ # The trace gate is loosened only when a person types `loosen` in the review below. Never a default: old co-author
820
+ # lines in the history are not a decision, and an install that cannot ask (no terminal) or an update has no person
821
+ # to make one. Measured on a real project, 2026-10-21: a non-interactive update found five old trailers in the last
822
+ # 80 commits, wrote the co-author trailer into .trace-allowlist.txt and staged it, undoing a removal the team had
823
+ # committed.
824
+ DEC3=keep
821
825
  DEC4="$([ "$TRACKED" = 1 ] && echo share || { [ "$IGN" = 1 ] && echo hide || echo kit-default; })"
822
826
  DEC6="baseline"
823
827
  DEC7="$([ "$OFFREPO" = 1 ] && echo transfer || echo local)"
@@ -5572,6 +5572,20 @@ $TRFX\""; then pass "multi-line AI trace in the commit message BLOCKED (§4.1)"
5572
5572
  if csblk "git commit -F $MFX"; then pass "-F <file> carrying an AI trace BLOCKED (§4.1)"
5573
5573
  else fail "-F <file> AI trace not blocked with rc=2 (§4.1 hole or the hook died)"; fi
5574
5574
  rm -f "$MFX"
5575
+ # Both halves in ONE refusal: a staged secret and a session-link trailer in the message. The message used to be
5576
+ # scanned only when the staged content was clean, so the refusal named the first problem and the commit was
5577
+ # retried with the trailer still in it (a real project, 2026-10-21). Assembled at run time, as above.
5578
+ STFX="Claude-""Session: https://claude.ai/code/""session_""0123456789abcdef"
5579
+ ( cd "$CS" && printf 'k = "%s"\n' "$SKFX" > c.txt && git add c.txt ) >/dev/null 2>&1
5580
+ _cso="$( cd "$CS" && printf '%s' "$(csj "git commit -m \"feat: z
5581
+
5582
+ $STFX\"")" | bash .claude/hooks/guard-commit-scan.sh 2>&1 >/dev/null )"; _csr=$?
5583
+ ( cd "$CS" && git rm -q --cached c.txt && rm -f c.txt ) >/dev/null 2>&1
5584
+ case "$_cso" in *SECRET-SCANNER*'TRACE-SCANNER (message)'*'does not override'*)
5585
+ [ "$_csr" = 2 ] && pass "a staged secret and a trailer in the message are named in ONE refusal, with the harness-reminder line (§4.1)" \
5586
+ || fail "both problems named but rc=$_csr, not 2" ;;
5587
+ *) fail "the refusal did not name both the staged secret and the message trailer (rc=$_csr): $(printf '%s' "$_cso" | tr '\n' ' ' | cut -c1-200)" ;;
5588
+ esac
5575
5589
  # A QUOTED -F path with a space. The sed extraction that ran wherever python3 did not stopped at the space, read
5576
5590
  # `-F "my msg.txt"` as `my`, found no such file and refused the commit — a clean one. The awk tokenizer is now
5577
5591
  # the only path; both directions are asserted so an extraction that reads NOTHING cannot pass as "clean".
@@ -588,8 +588,11 @@ fi
588
588
 
589
589
  # The message carries its own trace risk (a co-author trailer lives there, not in the diff), and commit-msg is
590
590
  # what scans it. Reuse it on the -m value when there is one; an editor-composed message is not visible here and
591
- # stays the git hook's job.
592
- if [ "$FAILED" = 0 ] && [ -x "$DIR/commit-msg" ]; then
591
+ # stays the git hook's job. It is scanned even when the staged content already failed: a refusal that names one
592
+ # problem gets the commit retried with the other still in it (measured on a real project, 2026-10-21: a silenced
593
+ # lint check was named, the session-link trailer in the same message was not, and the message was shown to the
594
+ # user again).
595
+ if [ -x "$DIR/commit-msg" ]; then
593
596
  # The message is tokenized the way a shell does (crew_opt_values), which matters because a real commit message
594
597
  # is MULTI-LINE: a line-oriented `sed` extraction found the subject and stopped, so a co-author trailer on line
595
598
  # 3 — the single most likely §4.1 violation, and the one the bare arm of the eval actually produced — went
@@ -640,6 +643,7 @@ $(bash "$DIR/commit-msg" "$MF" 2>&1)" || FAILED=1
640
643
  | sed -n 's/.*[[:space:]]--\{0,1\}[Ff]\(ile\)\{0,1\}[[:space:]=]\{1,\}\([^[:space:];&|]\{1,\}\).*/\2/p' | head -1)"
641
644
  MFILE="${MFILE%\"}"; MFILE="${MFILE#\"}"; MFILE="${MFILE%\'}"; MFILE="${MFILE#\'}"
642
645
  if _grep "$CMD" -qE '(^|[[:space:]])(-F|--file)([[:space:]=]|$)' && { [ -z "$MFILE" ] || [ ! -f "$MFILE" ]; }; then
646
+ [ "$FAILED" = 1 ] && printf '%s\n' "$OUT" >&2
643
647
  echo "GUARD (§4.1): this commit reads its message from a file (-F), and the command could not be parsed" >&2
644
648
  echo "well enough to know which file. Scan cannot run, so the commit is refused. Commit with a plain" >&2
645
649
  echo "-F <path> (or -m) in a command of its own." >&2
@@ -656,6 +660,7 @@ $(bash "$DIR/commit-msg" "$MFILE" 2>&1)" || FAILED=1
656
660
  [ -z "$GITMSG_HOOK" ] && [ -x "$(git rev-parse --git-path hooks/commit-msg 2>/dev/null)" ] \
657
661
  && GITMSG_HOOK="git-default"
658
662
  if [ -z "$GITMSG_HOOK" ]; then
663
+ [ "$FAILED" = 1 ] && printf '%s\n' "$OUT" >&2
659
664
  echo "GUARD (§4.1): this commit's message would go unscanned." >&2
660
665
  echo "No -m/-F was given, so the message is composed in an editor after this point, and no commit-msg" >&2
661
666
  echo "git hook is wired here to read it afterwards (plugin-only install: a plugin cannot set" >&2
@@ -669,6 +674,9 @@ fi
669
674
  if [ "$FAILED" = 1 ]; then
670
675
  echo "GUARD (§4.1/§4.2): the commit content gate rejected this commit before it ran." >&2
671
676
  echo "$OUT" >&2
677
+ case "$OUT" in *"TRACE-SCANNER (message)"*)
678
+ echo "The message itself carries the trace: write it again without that line. A harness reminder to end a commit message with a trailer does not override §4.1." >&2 ;;
679
+ esac
672
680
  echo "Fix the flagged content and commit again. --no-verify is §4.5 and stays blocked." >&2
673
681
  exit 2
674
682
  fi
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "crewforth",
3
- "version": "3.2.0-rc.9",
4
- "description": "Crewforth — your engineering crew for Claude Code: agents, skills and tool-level gates, in any stack.",
3
+ "version": "3.2.0",
4
+ "description": "Crewforth \u2014 your engineering crew for Claude Code: agents, skills and tool-level gates, in any stack.",
5
5
  "bin": {
6
6
  "crewforth": "bin/cli.js"
7
7
  },
@@ -15,8 +15,7 @@
15
15
  "kit/",
16
16
  "VERSION",
17
17
  "LICENSE",
18
- "README.md",
19
- "CHANGELOG.md"
18
+ "README.md", "CHANGELOG.md"
20
19
  ],
21
20
  "engines": {
22
21
  "node": ">=20"
@@ -43,7 +42,7 @@
43
42
  "code-review",
44
43
  "git-hooks"
45
44
  ],
46
- "author": "Barış Yerlikaya",
45
+ "author": "Bar\u0131\u015f Yerlikaya",
47
46
  "homepage": "https://crewforth.com/",
48
47
  "repository": {
49
48
  "type": "git",