crewforth 3.2.0-rc.5 → 3.2.0-rc.6

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
@@ -220,6 +220,13 @@ versioning follows [SemVer](https://semver.org/).
220
220
 
221
221
  ### Fixed
222
222
 
223
+ - **`change: review` is read as `audit`.** The model table of the working rules names review with audit, and
224
+ `crew-review-agent` cards that said `change: review` were refused. It now takes the audit floor, on a critical path
225
+ too; a kind that is not on the list is still refused.
226
+ - **`above:` is read anywhere among a task card's three lines.** A card with `above: <why>` between `change:` and
227
+ `verify:` was refused ("line 3 of the task is not one of files:, change:, verify:") and the call was sent again
228
+ with the line moved down. It is now read in any position and keeps the model as it does after the three; the three
229
+ are still all required.
223
230
  - **A click on the approval question is recorded whatever order its keys come back in.** Claude Code hands the
224
231
  answered question back either as it was asked or with its keys sorted, and the answer side matched it byte for
225
232
  byte: in real `auto` sessions 30 of 30 answers in the asked order were recorded and 7 of 7 sorted ones refused
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0-rc.5
1
+ 3.2.0-rc.6
@@ -8598,6 +8598,26 @@ for _pr in 'files: a · change: archi (planning only) · verify: none' 'files: a
8598
8598
  done
8599
8599
  [ -z "$_v2b" ] && pass "a card on one line (· ; | separators, a Card: lead, above: on the line) is read by its labels and held to the three-line rules: a critical path still needs opus; a kind with a note in parentheses is the kind, on one line or three; a bad change, labels out of order, a missing verify or prose first are refused" \
8600
8600
  || fail "the one-line card:$_v2b"
8601
+ # `above:` among the three lines, not only after them (2026-10-19: 2 of 19 line refusals had it between change: and
8602
+ # verify:; the retry moved it down and passed). It keeps the model the same way; the three are still all required.
8603
+ _v2b=""
8604
+ for _pr in 'files: src/ui/a.ts\nchange: feature\nabove: the parser is subtle\nverify: true' 'above: the parser is subtle\nfiles: src/ui/a.ts\nchange: feature\nverify: true'; do
8605
+ _v2one "$_pr" opus; { [ "$_rc" = 0 ] && [ ! -s "$_v2/oneout" ]; } || _v2b="$_v2b [above: among the three did not keep opus: $_pr → $_rc $(head -c 100 "$_v2/oneout")]"
8606
+ done
8607
+ _v2one 'files: src/ui/c.ts\nchange: feature\nverify: true' opus
8608
+ grep -q '"updatedInput":' "$_v2/oneout" || _v2b="$_v2b [without above: opus was not lowered (rc $_rc)]"
8609
+ for _pr in 'files: src/ui/a.ts\nchange: feature\nabove: x\nDo it.' 'files: src/ui/a.ts\nabove: x\nabove: y\nverify: true'; do
8610
+ _v2one "$_pr" sonnet; { [ "$_rc" = 2 ] && grep -q 'opens with its task card' "$_v2/err"; } || _v2b="$_v2b [passed: $_pr → $_rc]"
8611
+ done
8612
+ # `change: review` is an audit (the discipline's table lists it with them): same floor, a near miss still refused.
8613
+ _v2one 'files: src/ui/a.ts\nchange: review\nverify: none' haiku; { [ "$_rc" = 2 ] && grep -q 'runs on sonnet' "$_v2/err"; } || _v2b="$_v2b [review on haiku was not held to sonnet as audit (rc $_rc)]"
8614
+ for _pr in 'files: src/ui/a.ts\nchange: review\nverify: none' 'files: src/ui/a.ts · change: review (the staged diff) · verify: none'; do
8615
+ _v2one "$_pr" sonnet; [ "$_rc" = 0 ] || _v2b="$_v2b [refused: $_pr → $_rc $(head -c 100 "$_v2/err")]"
8616
+ done
8617
+ _v2one 'files: src/payments/p.ts\nchange: review\nverify: none' sonnet; { [ "$_rc" = 2 ] && grep -q 'runs on opus' "$_v2/err"; } || _v2b="$_v2b [review on a critical path was not held to opus (rc $_rc)]"
8618
+ _v2one 'files: src/ui/a.ts\nchange: reviews\nverify: none' sonnet; { [ "$_rc" = 2 ] && grep -q 'opens with its task card' "$_v2/err"; } || _v2b="$_v2b [change: reviews passed (rc $_rc)]"
8619
+ [ -z "$_v2b" ] && pass "above: may stand anywhere among the three card lines and keeps the model; without it the model is lowered; above: does not stand in for files:, change: or verify:; change: review is an audit, held to the same floors, and a near miss is still refused" \
8620
+ || fail "above: in the card:$_v2b"
8601
8621
 
8602
8622
  # ---- at write time: the card can leave a file out --------------------------------------------------------------
8603
8623
  _v2new; _v2b=""
@@ -459,7 +459,7 @@ _am_card_line(){ # $1 = the first line of the task -> 0 and CARD_* set (uncheck
459
459
  return 0
460
460
  }
461
461
  _am_card(){ # $1 = a task text -> 0 and CARD_FILES, CARD_CHANGE, CARD_VERIFY (CARD_ABOVE); 1 and CARD_WHY
462
- local s="$1" l k v n=0 seen="" one=0
462
+ local s="$1" l k v n=0 ln=0 seen="" one=0
463
463
  CARD_FILES=""; CARD_CHANGE=""; CARD_VERIFY=""; CARD_WHY=""; CARD_ABOVE=""
464
464
  s="${s//$'\r'/}"
465
465
  # One line first: the first line that is not empty, read by its labels; the three-line reading otherwise.
@@ -472,17 +472,20 @@ _am_card(){ # $1 = a task text -> 0 and CARD_FILES, CARD_CHANGE, CARD_VERIFY (C
472
472
  l="${s%%$'\n'*}"; if [ "$l" = "$s" ]; then s=""; else s="${s#*$'\n'}"; fi
473
473
  while :; do case "$l" in [$' \t']*) l="${l:1}" ;; *) break ;; esac; done
474
474
  [ -n "$l" ] || continue
475
- n=$((n+1))
476
- case "$l" in *:*) ;; *) CARD_WHY="line $n of the task is not one of files:, change:, verify:"; return 1 ;; esac
475
+ ln=$((ln+1))
476
+ case "$l" in *:*) ;; *) CARD_WHY="line $ln of the task is not one of files:, change:, verify:"; return 1 ;; esac
477
477
  k="${l%%:*}"; v="${l#*:}"
478
478
  while :; do case "$v" in [$' \t']*) v="${v:1}" ;; *) break ;; esac; done
479
479
  while :; do case "$v" in *[$' \t']) v="${v%?}" ;; *) break ;; esac; done
480
+ # `above:` may stand anywhere among the three (2 of 19 line refusals, 2026-10-19: between change: and verify:).
480
481
  case "$k" in
482
+ above) [ -n "$CARD_ABOVE" ] || CARD_ABOVE="${v:0:200}"; continue ;;
481
483
  files) CARD_FILES="$v" ;;
482
484
  change) CARD_CHANGE="$v" ;;
483
485
  verify) CARD_VERIFY="$v" ;;
484
- *) CARD_WHY="line $n of the task is not one of files:, change:, verify:"; return 1 ;;
486
+ *) CARD_WHY="line $ln of the task is not one of files:, change:, verify:"; return 1 ;;
485
487
  esac
488
+ n=$((n+1))
486
489
  case "$seen" in *" $k "*) CARD_WHY="the card names $k twice"; return 1 ;; esac
487
490
  seen="$seen $k "
488
491
  done
@@ -491,6 +494,9 @@ _am_card(){ # $1 = a task text -> 0 and CARD_FILES, CARD_CHANGE, CARD_VERIFY (C
491
494
  # A note in parentheses after the kind is the session's own comment, as `none (…)` is for verify: `architecture
492
495
  # (planning only)` is architecture (4 of the cards refused on this machine, 2026-10-18). Only a kind on the list, and
493
496
  # only a note that is the whole rest of the value in parentheses.
497
+ # `review` is an audit: the discipline's model table names it with them, and 4 crew-review-agent cards were refused
498
+ # for it (2026-10-19).
499
+ case "$CARD_CHANGE" in review|'review ('*')') CARD_CHANGE=audit ;; esac
494
500
  case "$CARD_CHANGE" in *' ('*')') v="${CARD_CHANGE%% (*}"; case "$_AM_CHANGES" in *" $v "*) CARD_CHANGE="$v" ;; esac ;; esac
495
501
  case "$_AM_CHANGES" in *" $CARD_CHANGE "*) ;; *) CARD_WHY="change: is '${CARD_CHANGE:0:40}', not one of:$_AM_CHANGES"; return 1 ;; esac
496
502
  [ -n "$CARD_VERIFY" ] || { CARD_WHY="verify: is empty (a command, or none)"; return 1; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewforth",
3
- "version": "3.2.0-rc.5",
3
+ "version": "3.2.0-rc.6",
4
4
  "description": "Crewforth — your engineering crew for Claude Code: agents, skills and tool-level gates, in any stack.",
5
5
  "bin": {
6
6
  "crewforth": "bin/cli.js"