crewforth 3.2.0-rc.6 → 3.2.0-rc.7

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,11 @@ versioning follows [SemVer](https://semver.org/).
220
220
 
221
221
  ### Fixed
222
222
 
223
+ - **A push approval covers a tag on the approved commit.** `git push <remote> <tag>` had no way to be approved: the
224
+ click and `/crew-approve push` both record a push of the branch, and the tag push of a release was refused after
225
+ either, so the session handed the user the command to run. It now passes when the tag points at the commit the
226
+ approval covers (after a commit + push, the new one) and no branch has its name; a tag on another commit, a full
227
+ ref, a refspec and `--tags` are still refused.
223
228
  - **`change: review` is read as `audit`.** The model table of the working rules names review with audit, and
224
229
  `crew-review-agent` cards that said `change: review` were refused. It now takes the audit floor, on a critical path
225
230
  too; a kind that is not on the list is still refused.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0-rc.6
1
+ 3.2.0-rc.7
@@ -7689,6 +7689,25 @@ _pa_say auto 'approve: push' >/dev/null; ( cd "$_paw" && git checkout -q -b othe
7689
7689
  _pa_run auto 'git push origin feat/x'; [ "$_par" = 2 ] || _pabad="$_pabad other-branch-checked-out:rc=$_par"
7690
7690
  [ -z "$_pabad" ] && pass "a push approval is for the commit HEAD was on and the branch that was checked out: one more commit, or another branch, and it is refused" \
7691
7691
  || fail "push binding:$_pabad"
7692
+ # A tag on the approved commit is part of that push (a release: `/crew-approve push`, then `git push origin v2.0.0`
7693
+ # was refused on a real project). A tag elsewhere, a name that is also a branch, or a full ref is not.
7694
+ _pa_new; _pabad=""; ( cd "$_paw" && git commit -qm second && git tag old HEAD^ && git tag v1 && git tag -a v1a -m v1a && git tag both && git branch both ) >/dev/null 2>&1
7695
+ _pa_say auto 'approve: commit' >/dev/null
7696
+ _pa_run auto 'git push origin v1'; [ "$_par" = 2 ] || _pabad="$_pabad [a commit approval pushed a tag: rc=$_par]"
7697
+ _pa_say auto 'approve: push' >/dev/null
7698
+ for _pc in 'git push origin v1' 'git push origin v1a' 'git push -q origin v1'; do
7699
+ _pa_run auto "$_pc"; [ "$_par" = 0 ] || _pabad="$_pabad [$_pc → $_par $(head -c 120 "$_PA/err")]"
7700
+ done
7701
+ _pa_run auto 'git push origin old'; { [ "$_par" = 2 ] && grep -q 'the tag old is on' "$_PA/err"; } || _pabad="$_pabad [a tag on another commit → $_par]"
7702
+ for _pc in 'git push origin both' 'git push origin refs/tags/v1' 'git push origin nosuch' 'git push origin v1:v2' 'git push other v1'; do
7703
+ _pa_run auto "$_pc"; [ "$_par" = 2 ] || _pabad="$_pabad [$_pc → $_par]"
7704
+ done
7705
+ _pa_new; ( cd "$_paw" && git tag before ) >/dev/null 2>&1; _pa_say auto 'approve: commit+push' >/dev/null
7706
+ _pa_run auto 'git commit -m x' >/dev/null; ( cd "$_paw" && git commit -qm approved && git tag after ) >/dev/null 2>&1
7707
+ _pa_run auto 'git push origin after'; [ "$_par" = 0 ] || _pabad="$_pabad [commit+push: a tag on the new commit → $_par $(head -c 120 "$_PA/err")]"
7708
+ _pa_run auto 'git push origin before'; { [ "$_par" = 2 ] && grep -q 'the tag before is on' "$_PA/err"; } || _pabad="$_pabad [commit+push: a tag on the old commit → $_par]"
7709
+ [ -z "$_pabad" ] && pass "a push approval covers a tag on the approved commit (lightweight or annotated; after commit+push, on the new commit): a tag on another commit, a name that is also a branch, a full ref, a refspec, another remote or a commit-only approval is refused" \
7710
+ || fail "tag push:$_pabad"
7692
7711
 
7693
7712
  # ---- commit+push: the push is for the commit the approved diff became -----------------------------------------
7694
7713
  _pa_new; _pabad=""
@@ -2437,7 +2437,7 @@ _p44_scan(){ # $1 = the collapsed `git push …` -> 0 when it is `git push [-u]
2437
2437
  return 0
2438
2438
  }
2439
2439
  _approval_ok(){ # 0 = the user's recorded approval covers THIS call. Otherwise _APW says why not, for the message.
2440
- local k v op="" tr="" h="" b="" r="" u="" sid="" ts="" now age dir="${_CWD:-.}" hc=1 hp=1 cur tool
2440
+ local k v op="" tr="" h="" b="" r="" u="" sid="" ts="" now age dir="${_CWD:-.}" hc=1 hp=1 cur tool tag _h2
2441
2441
  _APW="no approval from the user is on record"
2442
2442
  git_has "$CMD_SEEN" 'commit' && hc=0; git_has "$CMD_SEEN" 'push' && hp=0
2443
2443
  if [ "$hc" = 0 ] && [ "$hp" = 0 ]; then
@@ -2478,7 +2478,17 @@ _approval_ok(){ # 0 = the user's recorded approval covers THIS call. Otherwise
2478
2478
  _p44_scan "$_A44_S" || { _APW="the push is not in the one form an approval covers, git push <remote> <branch> ($_P44_WHY)"; return 1; }
2479
2479
  [ -n "$b" ] && [ -n "$r" ] && [ -n "$u" ] || { _APW="the approval record could not be read"; return 1; }
2480
2480
  if [ "$_P44_REMOTE" != "$r" ]; then _APW="the user approved a push to $r, not to $_P44_REMOTE"; return 1; fi
2481
- case "$_P44_REF" in "$b"|HEAD) ;; *) _APW="the user approved a push of $b, not of $_P44_REF"; return 1 ;; esac
2481
+ # A TAG ON THE APPROVED COMMIT is part of that push (a release is tagged on what was approved): `git push <remote>
2482
+ # <tag>` passes when <tag> is a tag and not also a branch, and it points at the commit the approval covers. A tag
2483
+ # on any other commit, --tags, and a refspec are refused as before.
2484
+ tag=""
2485
+ case "$_P44_REF" in
2486
+ "$b"|HEAD) ;;
2487
+ *[!A-Za-z0-9._/-]*|-*|*..*|*/) _APW="the user approved a push of $b, not of $_P44_REF"; return 1 ;;
2488
+ *) if git -C "$dir" show-ref --verify --quiet "refs/tags/$_P44_REF" 2>/dev/null \
2489
+ && ! git -C "$dir" show-ref --verify --quiet "refs/heads/$_P44_REF" 2>/dev/null; then tag="$_P44_REF"
2490
+ else _APW="the user approved a push of $b, not of $_P44_REF"; return 1; fi ;;
2491
+ esac
2482
2492
  cur="$(git -C "$dir" symbolic-ref --short -q HEAD 2>/dev/null || true)"
2483
2493
  [ "$cur" = "$b" ] || { _APW="the user approved a push of $b, and the branch checked out now is ${cur:-<detached>}"; return 1; }
2484
2494
  # Where the remote pushes TO, with pushurl and pushInsteadOf applied: the name alone can be pointed elsewhere.
@@ -2491,8 +2501,16 @@ _approval_ok(){ # 0 = the user's recorded approval covers THIS call. Otherwise
2491
2501
  cur="$(git -C "$dir" rev-parse --verify --quiet HEAD 2>/dev/null || echo NONE)"
2492
2502
  if [ "$op" = push ]; then
2493
2503
  [ "$cur" = "$h" ] || { _APW="HEAD moved after the user approved the push (approved ${h:0:7}, now ${cur:0:7})"; return 1; }
2504
+ if [ -n "$tag" ]; then
2505
+ cur="$(git -C "$dir" rev-parse --verify --quiet "refs/tags/$tag^{commit}" 2>/dev/null || echo NONE)"
2506
+ [ "$cur" = "$h" ] || { _APW="the tag $tag is on ${cur:0:7}, not on the commit the user approved for push (${h:0:7})"; return 1; }
2507
+ fi
2494
2508
  return 0
2495
2509
  fi
2510
+ if [ -n "$tag" ]; then
2511
+ _h2="$cur"; cur="$(git -C "$dir" rev-parse --verify --quiet "refs/tags/$tag^{commit}" 2>/dev/null || echo NONE)"
2512
+ [ "$cur" = "$_h2" ] || { _APW="the tag $tag is on ${cur:0:7}, not on the commit the user approved for push (${_h2:0:7})"; return 1; }
2513
+ fi
2496
2514
  # commit+push: HEAD has to be the commit the approved index became.
2497
2515
  cur="$(git -C "$dir" rev-parse --verify --quiet 'HEAD^' 2>/dev/null || echo NONE)"
2498
2516
  if [ "$h" = NONE ] || [ "$cur" != "$h" ]; then
@@ -707,9 +707,9 @@ _pa_record(){
707
707
  commit) WHAT="commit of what is staged now (tree $_d) on HEAD $_h"
708
708
  HOW="Run git commit -m with the message, alone in its call: no other option than -q, -s or -v, no paths, no cd, no pipe, nothing chained. Single-quote the message, or read it from a here-document with a quoted delimiter. The review record of §4.6 is still required." ;;
709
709
  push) WHAT="push of $BR at $_h to $RM"
710
- HOW="Run exactly: git push $RM $BR (alone in its call: the remote and the branch written out, no cd, no pipe, nothing chained)." ;;
710
+ HOW="Run exactly: git push $RM $BR (alone in its call: the remote and the branch written out, no cd, no pipe, nothing chained). A tag on that commit goes the same way: git push $RM <tag>." ;;
711
711
  commit+push) WHAT="commit of what is staged now (tree $_d) on HEAD $_h, then push of that commit on $BR to $RM"
712
- HOW="Run git commit -m with the message, alone in its call (no other option than -q, -s or -v, no paths, no cd, no pipe, nothing chained; single-quote the message; §4.6 still applies), then in a separate call exactly: git push $RM $BR" ;;
712
+ HOW="Run git commit -m with the message, alone in its call (no other option than -q, -s or -v, no paths, no cd, no pipe, nothing chained; single-quote the message; §4.6 still applies), then in a separate call exactly: git push $RM $BR (a tag on the new commit: git push $RM <tag>)" ;;
713
713
  esac
714
714
  [ -n "$_u" ] && _u=" ($RM pushes to $_u)"
715
715
  _bw=""; [ "$BARE" = 1 ] && _bw="approved: $OP (what this session was refused). "
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewforth",
3
- "version": "3.2.0-rc.6",
3
+ "version": "3.2.0-rc.7",
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"