@bookedsolid/rea 0.7.0 → 0.8.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.
@@ -213,6 +213,14 @@ pr_core_run() {
213
213
  # typically unset. Default to `origin` for BUG-008 sniff consistency.
214
214
  local argv_remote="${1:-origin}"
215
215
 
216
+ # 0.8.0 (#85): when REA_SKIP_CODEX_REVIEW is set, this flag flips to 1
217
+ # in section 5c. The protected-path Codex-audit check (section 7) then
218
+ # treats the requirement as satisfied — but every other gate (HALT,
219
+ # cross-repo guard, ref-resolution, push-review cache, blocked-paths)
220
+ # still runs. Full-gate bypass moved to REA_SKIP_PUSH_REVIEW a release
221
+ # cycle ago; this narrows REA_SKIP_CODEX_REVIEW to what its name implies.
222
+ local CODEX_WAIVER_ACTIVE=0
223
+
216
224
  # ── 1a. Cross-repo guard (must come FIRST — before any rea-scoped check) ──
217
225
  # BUG-012 (0.6.2) — anchor the install to the SCRIPT'S OWN LOCATION on disk.
218
226
  # The hook knows where it lives: installed at `<root>/.claude/hooks/<name>.sh`,
@@ -653,16 +661,23 @@ pr_core_run() {
653
661
 
654
662
  {
655
663
  printf '\n'
656
- printf '== CODEX REVIEW SKIPPED via REA_SKIP_CODEX_REVIEW\n'
664
+ printf '== CODEX REVIEW WAIVER active (REA_SKIP_CODEX_REVIEW)\n'
657
665
  printf ' Reason: %s\n' "$SKIP_REASON"
658
666
  printf ' Actor: %s\n' "$SKIP_ACTOR"
659
667
  printf ' Head SHA: %s\n' "${SKIP_HEAD:-<unknown>}"
660
668
  printf ' Audited: .rea/audit.jsonl (tool_name=codex.review.skipped)\n'
661
669
  printf '\n'
662
- printf ' This is a gate weakening. Every invocation is permanently audited.\n'
670
+ printf ' Scope: waives the protected-path Codex-audit requirement only.\n'
671
+ printf ' Still active: HALT, cross-repo guard, ref-resolution,\n'
672
+ printf ' push-review cache. For a full-gate bypass\n'
673
+ # shellcheck disable=SC2016 # backticks are literal markdown in user-facing message
674
+ printf ' use `REA_SKIP_PUSH_REVIEW=<reason>`.\n'
675
+ printf '\n'
676
+ printf ' This is a gate weakening. The waiver receipt is written BEFORE\n'
677
+ printf ' this banner — seeing this banner means the audit is durable.\n'
663
678
  printf '\n'
664
679
  } >&2
665
- exit 0
680
+ CODEX_WAIVER_ACTIVE=1
666
681
  fi
667
682
 
668
683
  # ── 6. Determine source/target commits for each refspec ───────────────────
@@ -853,7 +868,13 @@ pr_core_run() {
853
868
  '; then
854
869
  local _audit="${REA_ROOT}/.rea/audit.jsonl"
855
870
  local _codex_ok=0
856
- if [[ -f "$_audit" ]]; then
871
+ # 0.8.0 (#85): Codex-only waiver satisfies this check without a real
872
+ # audit entry. Every other gate still ran — HALT, cross-repo guard,
873
+ # ref-resolution, push-review cache — and the waiver itself is
874
+ # already recorded in .rea/audit.jsonl as tool_name=codex.review.skipped.
875
+ if [[ "$CODEX_WAIVER_ACTIVE" == "1" ]]; then
876
+ _codex_ok=1
877
+ elif [[ -f "$_audit" ]]; then
857
878
  if jq -e --arg sha "$local_sha" '
858
879
  select(
859
880
  .tool_name == "codex.review"
@@ -977,15 +998,38 @@ pr_core_run() {
977
998
  REA_CLI_ARGS=(node "${REA_ROOT}/dist/cli/index.js")
978
999
  fi
979
1000
 
1001
+ # Cache-branch derivation (Codex 0.8.0 pass-2 finding #2, pass-3 finding #1):
1002
+ # Use the PUSHED source ref (from pre-push stdin / bootstrap walk), not the
1003
+ # checkout branch. `git push origin hotfix:main` from a `feature` checkout
1004
+ # must look up a cache entry keyed on `hotfix`, not `feature`. Strip the
1005
+ # `refs/heads/` prefix.
1006
+ #
1007
+ # Fall back to the checkout branch when SOURCE_REF is:
1008
+ # • unset (defence-in-depth, not reached on any observed path), or
1009
+ # • the literal string "HEAD" — emitted by pr_resolve_argv_refspecs for a
1010
+ # bare `git push` with no explicit refspec. Keying a cache lookup on
1011
+ # "HEAD" would force a miss on every bare push; the checkout branch
1012
+ # name is the right lookup key for that workflow.
1013
+ local SOURCE_BRANCH="${SOURCE_REF#refs/heads/}"
1014
+ if [[ -z "$SOURCE_BRANCH" || "$SOURCE_BRANCH" == "HEAD" ]]; then
1015
+ SOURCE_BRANCH="$CURRENT_BRANCH"
1016
+ fi
1017
+
980
1018
  if [[ -n "$PUSH_SHA" ]] && [[ ${#REA_CLI_ARGS[@]} -gt 0 ]]; then
981
1019
  local CACHE_RESULT
982
- CACHE_RESULT=$("${REA_CLI_ARGS[@]}" cache check "$PUSH_SHA" --branch "$CURRENT_BRANCH" --base "$TARGET_BRANCH" 2>/dev/null || echo '{"hit":false}')
983
- if printf '%s' "$CACHE_RESULT" | jq -e '.hit == true' >/dev/null 2>&1; then
1020
+ CACHE_RESULT=$("${REA_CLI_ARGS[@]}" cache check "$PUSH_SHA" --branch "$SOURCE_BRANCH" --base "$TARGET_BRANCH" 2>/dev/null || echo '{"hit":false}')
1021
+ # Require BOTH hit == true AND result == "pass". A cached `fail` verdict
1022
+ # (Codex 0.8.0 pass-2 finding #1) must NOT satisfy the gate — cache.ts
1023
+ # serializes `result` verbatim, so a negative verdict would otherwise
1024
+ # slip through. Under the #85 narrowed semantic the cache is the ONLY
1025
+ # way a waiver-using operator reaches exit 0, so a permissive predicate
1026
+ # here would be a real security regression.
1027
+ if printf '%s' "$CACHE_RESULT" | jq -e '.hit == true and .result == "pass"' >/dev/null 2>&1; then
984
1028
  local DISCORD_LIB="${REA_ROOT}/hooks/_lib/discord.sh"
985
1029
  if [ -f "$DISCORD_LIB" ]; then
986
1030
  # shellcheck source=/dev/null
987
1031
  source "$DISCORD_LIB"
988
- discord_notify "dev" "Push passed quality gates on \`${CURRENT_BRANCH}\` -- $(cd "$REA_ROOT" && git log -1 --oneline 2>/dev/null)" "green"
1032
+ discord_notify "dev" "Push passed quality gates on \`${SOURCE_BRANCH}\` -- $(cd "$REA_ROOT" && git log -1 --oneline 2>/dev/null)" "green"
989
1033
  fi
990
1034
  exit 0
991
1035
  fi
@@ -1006,7 +1050,7 @@ pr_core_run() {
1006
1050
  printf ' 1. Spawn a code-reviewer agent to review: git diff %s..%s\n' "$MERGE_BASE" "$SOURCE_SHA"
1007
1051
  printf ' 2. Spawn a security-engineer agent for security review\n'
1008
1052
  printf ' 3. After both pass, cache the result:\n'
1009
- printf ' rea cache set %s pass --branch %s --base %s\n' "$PUSH_SHA" "$CURRENT_BRANCH" "$TARGET_BRANCH"
1053
+ printf ' rea cache set %s pass --branch %s --base %s\n' "$PUSH_SHA" "$SOURCE_BRANCH" "$TARGET_BRANCH"
1010
1054
  printf '\n'
1011
1055
  } >&2
1012
1056
  exit 2
@@ -45,13 +45,15 @@
45
45
  # generic Claude Code adapter.
46
46
  #
47
47
  # ── Escape hatches ────────────────────────────────────────────────────────────
48
- # REA_SKIP_CODEX_REVIEW=<reason> — bypass the Codex adversarial-review
49
- # requirement for this push. Audit record
48
+ # REA_SKIP_CODEX_REVIEW=<reason> — Codex-only waiver. Since 0.8.0 (#85)
49
+ # this ONLY satisfies the protected-path
50
+ # Codex-audit requirement. HALT, cross-
51
+ # repo guard, ref-resolution, and the
52
+ # push-review cache still run. See the
53
+ # authoritative docstring in
54
+ # `push-review-gate.sh` for the full
55
+ # scope description. Audit record
50
56
  # `tool_name: "codex.review.skipped"`.
51
- # Currently a whole-gate bypass (see
52
- # task #85); the distinct audit tool_name
53
- # keeps it from satisfying the Codex-
54
- # review jq predicate.
55
57
  # REA_SKIP_PUSH_REVIEW=<reason> — bypass the WHOLE gate for this push.
56
58
  # Audit record
57
59
  # `tool_name: "push.review.skipped"`.
@@ -22,24 +22,39 @@
22
22
  # so in practice a consumer can wire THIS file into `.husky/pre-push` and it
23
23
  # just works. The `-git` adapter exists for clarity of install intent.
24
24
  #
25
- # ── Escape hatch: REA_SKIP_CODEX_REVIEW ──────────────────────────────────────
26
- # Env var `REA_SKIP_CODEX_REVIEW=<reason>` bypasses the Codex adversarial-
27
- # review requirement. Set to any non-empty value; the value IS the reason
28
- # recorded in the audit record (no default reason is supplied — if the
29
- # operator sets `REA_SKIP_CODEX_REVIEW=1` the reason is literally "1").
25
+ # ── Codex-only waiver: REA_SKIP_CODEX_REVIEW ─────────────────────────────────
26
+ # Env var `REA_SKIP_CODEX_REVIEW=<reason>` waives the Codex adversarial-
27
+ # review requirement (section 7 protected-path check). Set to any non-empty
28
+ # value; the value IS the reason recorded in the audit record (no default
29
+ # reason is supplied — if the operator sets `REA_SKIP_CODEX_REVIEW=1` the
30
+ # reason is literally "1").
30
31
  #
31
- # ORDERING (0.7.0): the hatch fires AFTER the HALT check but BEFORE ref-
32
- # resolution and protected-path detection. Prior to 0.7.0 the check ran
33
- # inside the protected-path branch and only fired when the diff touched a
34
- # protected pathwhich meant an operator who wanted to skip Codex review
35
- # got blocked by a transient ref-resolution failure (missing remote object,
36
- # unresolvable source ref, etc.) before the skip ever fired. The new
37
- # ordering mirrors REA_SKIP_PUSH_REVIEW: if the operator has committed to
38
- # the bypass (accepting the audit record), ref-resolution failures should
39
- # not strand the skip. Tradeoff: the skip now fires on every push when set,
40
- # not just protected-path pushes. The audit receipt makes the operator
41
- # accountable either way, and REA_SKIP_CODEX_REVIEW keeps its distinct
42
- # tool_name so it never satisfies the `codex.review` jq predicate.
32
+ # SCOPE (0.8.0, #85): Codex-only. The waiver only satisfies the
33
+ # protected-path Codex-audit requirement. Every other gate this hook
34
+ # runs still runs:
35
+ # HALT (.rea/HALT)still blocks.
36
+ # Cross-repo guard still blocks.
37
+ # Ref-resolution failures still block.
38
+ # Push-review cache a miss still falls through to section 9's general
39
+ # review-required block.
40
+ # (Blocked-paths enforcement is a separate hook on Edit/Write tiers, not
41
+ # this push hook it was never gated by REA_SKIP_CODEX_REVIEW.)
42
+ #
43
+ # For a full-gate bypass, use `REA_SKIP_PUSH_REVIEW=<reason>` (section 5a).
44
+ # The 0.7.0 semantic (whole-gate bypass via the Codex hatch) was misleading
45
+ # — operators reached for REA_SKIP_CODEX_REVIEW to silence a transient
46
+ # Codex unavailability and accidentally bypassed every other check too.
47
+ # 0.8.0 narrows it to what the name implies.
48
+ #
49
+ # ORDERING: the waiver fires AFTER the HALT check but BEFORE ref-resolution.
50
+ # Prior to 0.7.0 the check ran inside the protected-path branch and only
51
+ # fired when the diff touched a protected path — which meant an operator
52
+ # who wanted to skip Codex review got blocked by a transient ref-resolution
53
+ # failure (missing remote object, unresolvable source ref, etc.) before the
54
+ # skip ever fired. The current ordering preserves the skip audit record
55
+ # even when downstream gates (ref-resolution, cache) block: the operator's
56
+ # commitment to waive is durable, even if the push itself is blocked on
57
+ # another gate.
43
58
  #
44
59
  # Every invocation appends a `tool_name: "codex.review.skipped"` record to
45
60
  # `.rea/audit.jsonl` via the public audit helper. This record is intentionally
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Agentic governance layer for Claude Code — policy enforcement, hook-based safety gates, audit logging, and Codex-integrated adversarial review for AI-assisted projects",
5
5
  "license": "MIT",
6
6
  "author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",