carson 3.19.0 → 3.21.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/RELEASE.md +25 -0
  4. data/VERSION +1 -1
  5. data/exe/carson +3 -3
  6. data/hooks/command-guard +56 -0
  7. data/hooks/pre-push +37 -1
  8. data/lib/carson/adapters/agent.rb +1 -0
  9. data/lib/carson/adapters/claude.rb +2 -0
  10. data/lib/carson/adapters/codex.rb +2 -0
  11. data/lib/carson/adapters/git.rb +2 -0
  12. data/lib/carson/adapters/github.rb +2 -0
  13. data/lib/carson/adapters/prompt.rb +2 -0
  14. data/lib/carson/cli.rb +415 -414
  15. data/lib/carson/config.rb +4 -3
  16. data/lib/carson/runtime/audit.rb +84 -84
  17. data/lib/carson/runtime/deliver.rb +27 -24
  18. data/lib/carson/runtime/govern.rb +29 -29
  19. data/lib/carson/runtime/housekeep.rb +15 -15
  20. data/lib/carson/runtime/local/hooks.rb +20 -0
  21. data/lib/carson/runtime/local/onboard.rb +17 -17
  22. data/lib/carson/runtime/local/prune.rb +13 -13
  23. data/lib/carson/runtime/local/sync.rb +6 -6
  24. data/lib/carson/runtime/local/template.rb +26 -25
  25. data/lib/carson/runtime/local/worktree.rb +76 -33
  26. data/lib/carson/runtime/local.rb +1 -0
  27. data/lib/carson/runtime/repos.rb +1 -1
  28. data/lib/carson/runtime/review/data_access.rb +1 -0
  29. data/lib/carson/runtime/review/gate_support.rb +15 -14
  30. data/lib/carson/runtime/review/query_text.rb +1 -0
  31. data/lib/carson/runtime/review/sweep_support.rb +5 -4
  32. data/lib/carson/runtime/review/utility.rb +2 -1
  33. data/lib/carson/runtime/review.rb +10 -8
  34. data/lib/carson/runtime/setup.rb +12 -10
  35. data/lib/carson/runtime/status.rb +20 -20
  36. data/lib/carson/runtime.rb +39 -25
  37. data/lib/carson/version.rb +1 -0
  38. data/lib/carson.rb +1 -0
  39. data/templates/.github/carson.md +7 -4
  40. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36c68c06630159e0dca3560f62993befe8b50dd324fac5a6f8232895b2a667bd
4
- data.tar.gz: 1a4462a8b56b6b7fe505705c30ded47e24a279f4fa775baf56b05ae2e48f450a
3
+ metadata.gz: 8274b4268c0fc93eeb9323f2b9fd0d4e042001d1633ccde431afcb7f61448bae
4
+ data.tar.gz: cff9a441857c256c82bf0f395c92a29a2879c5f1da45c4de3ae1232803e0ea5f
5
5
  SHA512:
6
- metadata.gz: 3f644435f5ca88527edc597a7b19134b701bc970748b5ef075f9b1e3c600b3be60724d80c8567bb919cf378a9a5da2dc54fcc27f29b0da811c0f3880c990f016
7
- data.tar.gz: 12e3651de47f5f741115c58308c2fe344d542bcc94c3f1234e08990b506b6ffa2c518e5f782ea9cb0a86a699111926c6df1f6571724b5ef3d318666889f2a13f
6
+ metadata.gz: 375300bdb2789594fa0a088c3f2d983a31aa492884b7fd399e76f0531885c191b81d855dd35c9d79a488cca6a3fdbdc3da35a9ac24d820901a34558d60bbc4f2
7
+ data.tar.gz: a5f3ddb10f668b7003578c62bdec28b95c0c584cfe5c93da38d50ffc01e4a03f4a91c31053d02a7a97af396baa247917ce3a5edf95608d801c9ce274024cde04
data/README.md CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  Named after the head of household in Downton Abbey, Carson is your repositories' autonomous governance runtime — you write the code, Carson manages everything else. From commit-time checks through PR triage, agent dispatch, merge, and cleanup, Carson runs the household with discipline and professional standards. Carson itself has no intelligence — it follows a deterministic decision tree. The intelligence comes from the coding agents it dispatches (Codex, Claude) to fix problems.
8
8
 
9
- ## The Problem to Solve
9
+ ## The Problem
10
10
 
11
11
  Managing a growing portfolio of repositories is rewarding work — but the operational overhead scales faster than the code itself. PR templates go stale, reviewer feedback gets quietly buried, and what passes on a developer's laptop fails in CI. When coding agents start producing PRs across multiple projects, the coordination load multiplies: checking results, dispatching fixes, clicking merge, cleaning up branches.
12
12
 
13
13
  Carson exists so you can focus on what matters — building — while governance runs itself.
14
14
 
15
- ## How Carson Works
15
+ ## What Carson Does
16
16
 
17
17
  Carson is an autonomous governance runtime that lives on your workstation and in CI, never inside the repositories it governs. It operates at two levels:
18
18
 
@@ -40,7 +40,7 @@ Carson orchestrates a closed governance loop across two layers:
40
40
 
41
41
  Carson's role is governance orchestration — gating on results and dispatching action. The actual CI runs and code fixes are delegated to specialised tools: GitHub Actions for CI and coding agents for remediation.
42
42
 
43
- ## Opinions
43
+ ### Principles
44
44
 
45
45
  Carson is opinionated about governance. These are non-negotiable principles, not configurable defaults:
46
46
 
@@ -51,6 +51,14 @@ Carson is opinionated about governance. These are non-negotiable principles, not
51
51
 
52
52
  Everything else bends to your preference. Which branch is main, how PRs are merged, which repositories to govern, which coding agent to dispatch — Carson asks during setup and remembers. Sensible defaults are provided; you only change what matters to you. See `MANUAL.md` for the full list.
53
53
 
54
+ ## When to Use Carson
55
+
56
+ - **You run coding agents across multiple repositories** and need a single command that triages every open PR, merges what's ready, dispatches agents to fix what's broken, and reports what needs your attention.
57
+ - **Your PR feedback gets buried.** Carson blocks merge until every reviewer comment is explicitly acknowledged — accepted, rejected, or deferred — so nothing is silently ignored.
58
+ - **CI breaks and nobody notices.** `carson audit` runs on every commit via managed hooks, and `carson govern` watches CI status across your portfolio continuously.
59
+ - **You onboard new repositories often** and want consistent hooks, templates, and governance from the first commit without manual setup.
60
+ - **You want agent-safe worktree management.** `carson worktree create` auto-syncs, branches, and isolates; `carson worktree remove` guards against unpushed work and active shells before cleanup.
61
+
54
62
  ## Quickstart
55
63
 
56
64
  Prerequisites: Ruby `>= 3.4`, `git`, and `gem` in your PATH. `gh` (GitHub CLI) is recommended for full review governance features.
data/RELEASE.md CHANGED
@@ -5,6 +5,31 @@ Release-note scope rule:
5
5
  - `RELEASE.md` records only version deltas, breaking changes, and migration actions.
6
6
  - Operational usage guides live in `MANUAL.md` and `API.md`.
7
7
 
8
+ ## 3.21.0
9
+
10
+ ### What changed
11
+
12
+ - **Command guard for governed repositories** — Carson now detects and blocks raw `git push` and `gh pr create/merge` commands in governed repos, redirecting agents to `carson deliver` instead. Three enforcement layers:
13
+ - **Pre-push hook** — blocks raw `git push` in governed repos. Carson sets `CARSON_PUSH=1` internally so its own pushes pass through.
14
+ - **Command guard script** — a Claude Code `PreToolUse` hook that intercepts `gh pr create` and `gh pr merge` Bash commands in governed repos.
15
+ - **Existing pre-push guard** — the main/master branch push block now uses the same `BLOCKED` message format with explicit recovery guidance.
16
+ - **`with_env_var` helper** — new Runtime utility for temporarily setting environment variables with guaranteed cleanup.
17
+
18
+ ### UX improvement
19
+
20
+ - Agents that forget to use `carson deliver` are caught at push time or tool-call time with a clear message: what happened, why, and what to use instead. Born from scar: a preflight agent used raw `git push github main` and `gh pr create` in a governed repo, wasting work and requiring manual recovery (2026-03-09).
21
+
22
+ ## 3.20.0
23
+
24
+ ### What changed
25
+
26
+ - **Cross-process CWD guard for worktree removal** — `worktree_remove!` and `sweep_stale_worktrees!` now detect when another process (e.g. an agent session) has its working directory inside a worktree before attempting removal. Uses `lsof -d cwd` to scan for cross-process CWD holds. Blocks removal with a clear recovery message instead of silently destroying the directory and crashing the other session's shell.
27
+ - **Code quality sweep** — fixed indentation errors in `deliver.rb` (`check_pr_ci` method). Converted single-parameter blocks to Ruby 3.4 `it` implicit parameter across `deliver.rb`, `govern.rb`, `setup.rb`, and all review submodules (`gate_support.rb`, `sweep_support.rb`, `utility.rb`). Added file-level purpose comments to review submodules (`gate_support.rb`, `sweep_support.rb`, `utility.rb`, `data_access.rb`, `query_text.rb`).
28
+
29
+ ### UX improvement
30
+
31
+ - Worktree removal is now safe across process boundaries. Previously, Carson only checked if the *current* process's CWD was inside the worktree. A separate cleanup process (hook, batch sweep, or another agent) could still remove a worktree while another session's shell was inside it, permanently crashing that shell. The new guard detects this scenario and refuses removal with actionable recovery advice. Fails safe: if `lsof` is unavailable, removal proceeds as before.
32
+
8
33
  ## 3.19.0
9
34
 
10
35
  ### What changed
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.19.0
1
+ 3.21.0
data/exe/carson CHANGED
@@ -5,9 +5,9 @@ $LOAD_PATH.unshift( File.expand_path( "../lib", __dir__ ) )
5
5
  require "carson"
6
6
 
7
7
  exit Carson::CLI.start(
8
- argv: ARGV.dup,
8
+ arguments: ARGV.dup,
9
9
  repo_root: Dir.pwd,
10
10
  tool_root: File.expand_path( "..", __dir__ ),
11
- out: $stdout,
12
- err: $stderr
11
+ output: $stdout,
12
+ error: $stderr
13
13
  )
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # Carson command guard — Claude Code PreToolUse hook.
3
+ #
4
+ # Blocks raw `gh pr create` and `gh pr merge` in Carson-governed repositories.
5
+ # Agents should use `carson deliver` instead.
6
+ #
7
+ # Claude Code sends JSON on stdin: {"tool_name": "Bash", "tool_input": {"command": "..."}}
8
+ # To block: print reason to stderr, exit 2.
9
+ # To allow: exit 0.
10
+ #
11
+ # Registration in ~/.claude/settings.json:
12
+ # "PreToolUse": [
13
+ # {
14
+ # "matcher": "Bash",
15
+ # "hooks": [
16
+ # {
17
+ # "type": "command",
18
+ # "command": "~/.carson/hooks/command-guard"
19
+ # }
20
+ # ]
21
+ # }
22
+ # ]
23
+ set -euo pipefail
24
+
25
+ # Read the tool call JSON from stdin.
26
+ input="$(cat)"
27
+
28
+ # Only inspect Bash tool calls.
29
+ tool_name="$(echo "$input" | jq -r '.tool_name // empty' 2>/dev/null)"
30
+ [ "$tool_name" = "Bash" ] || exit 0
31
+
32
+ command_text="$(echo "$input" | jq -r '.tool_input.command // empty' 2>/dev/null)"
33
+ [ -n "$command_text" ] || exit 0
34
+
35
+ # Check for gh pr commands that Carson replaces.
36
+ guarded_pattern='gh\s+pr\s+(create|merge)'
37
+ if ! echo "$command_text" | grep -qE "$guarded_pattern"; then
38
+ exit 0
39
+ fi
40
+
41
+ # Check if the current directory is inside a governed repository.
42
+ config_file="${HOME}/.carson/config.json"
43
+ [ -f "$config_file" ] || exit 0
44
+
45
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
46
+ [ -n "$repo_root" ] || exit 0
47
+
48
+ normalised="$(cd "$repo_root" && pwd -P)"
49
+ if ! grep -qF "\"$normalised\"" "$config_file" 2>/dev/null; then
50
+ exit 0
51
+ fi
52
+
53
+ # This is a raw gh pr command in a governed repo — block it.
54
+ echo "BLOCKED: raw \`gh pr create/merge\` in a Carson-governed repository." >&2
55
+ echo "Use \`carson deliver\` instead — it handles push, PR, and merge with safety guards." >&2
56
+ exit 2
data/hooks/pre-push CHANGED
@@ -1,23 +1,59 @@
1
1
  #!/usr/bin/env bash
2
+ # Carson pre-push hook — enforces push policy in governed repositories.
3
+ #
4
+ # Guards:
5
+ # 1. Blocks direct push to main/master refs.
6
+ # 2. Blocks raw git push in governed repos — agents must use `carson deliver`.
7
+ # Carson sets CARSON_PUSH=1 internally so its own pushes pass through.
8
+ #
9
+ # Bypass (emergency only): git push --no-verify
2
10
  set -euo pipefail
3
11
 
4
12
  hooks_dir="$(cd "$(dirname "$0")" && pwd)"
5
13
  style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "branch")"
6
14
  [ "$style" = "trunk" ] && exit 0
7
15
 
16
+ # --- Guard 1: block pushes to main/master refs ---
17
+
8
18
  remote_name="${1:-unknown}"
9
19
  remote_url="${2:-unknown}"
10
20
  has_commit_push=false
11
21
  while read -r local_ref local_sha remote_ref remote_sha; do
12
22
  case "$remote_ref" in
13
23
  refs/heads/main|refs/heads/master)
14
- echo "Carson policy: direct push to ${remote_ref#refs/heads/} is blocked on remote ${remote_name} (${remote_url}). Use a pull request." >&2
24
+ echo "BLOCKED: direct push to ${remote_ref#refs/heads/} is not allowed." >&2
25
+ echo "Use \`carson deliver\` instead — it handles push, PR, and merge with safety guards." >&2
26
+ echo "Bypass (emergency only): git push --no-verify" >&2
15
27
  exit 1
16
28
  ;;
17
29
  esac
18
30
  [[ "$local_sha" != "0000000000000000000000000000000000000000" ]] && has_commit_push=true
19
31
  done
20
32
 
33
+ # --- Guard 2: block raw git push in governed repos ---
34
+ # Carson sets CARSON_PUSH=1 when pushing internally via `deliver`.
35
+ # If the variable is absent, this is a raw git push from an agent or human.
36
+
37
+ if [[ -z "${CARSON_PUSH:-}" ]]; then
38
+ config_file="${HOME}/.carson/config.json"
39
+ if [[ -f "$config_file" ]]; then
40
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
41
+ if [[ -n "$repo_root" ]]; then
42
+ # Check if this repo appears in govern.repos.
43
+ # Uses a simple grep against the JSON — no jq dependency required.
44
+ normalised="$(cd "$repo_root" && pwd -P)"
45
+ if grep -qF "\"$normalised\"" "$config_file" 2>/dev/null; then
46
+ echo "BLOCKED: raw \`git push\` in a Carson-governed repository." >&2
47
+ echo "Use \`carson deliver\` instead — it handles push, PR, and merge with safety guards." >&2
48
+ echo "Bypass (emergency only): git push --no-verify" >&2
49
+ exit 1
50
+ fi
51
+ fi
52
+ fi
53
+ fi
54
+
55
+ # --- Template sync on push ---
56
+
21
57
  if $has_commit_push; then
22
58
  if [[ -n "${CARSON_BIN:-}" ]]; then
23
59
  ruby "$CARSON_BIN" template apply --push-prep || exit 1
@@ -1,3 +1,4 @@
1
+ # Defines the WorkOrder and Result data structures for agent dispatch.
1
2
  module Carson
2
3
  module Adapters
3
4
  module Agent
@@ -1,8 +1,10 @@
1
+ # Dispatches coding work to the Claude Code CLI and parses its output.
1
2
  require "open3"
2
3
  require "json"
3
4
 
4
5
  module Carson
5
6
  module Adapters
7
+ # Adapter for dispatching work to Claude Code.
6
8
  class Claude
7
9
  include Prompt
8
10
 
@@ -1,8 +1,10 @@
1
+ # Dispatches coding work to the OpenAI Codex CLI and parses its output.
1
2
  require "open3"
2
3
  require "json"
3
4
 
4
5
  module Carson
5
6
  module Adapters
7
+ # Adapter for dispatching work to OpenAI Codex.
6
8
  class Codex
7
9
  include Prompt
8
10
 
@@ -1,7 +1,9 @@
1
+ # Executes git commands via Open3 and returns structured output.
1
2
  require "open3"
2
3
 
3
4
  module Carson
4
5
  module Adapters
6
+ # Thin wrapper around the git CLI. Runs commands via Open3.
5
7
  class Git
6
8
  def initialize( repo_root: )
7
9
  @repo_root = repo_root
@@ -1,7 +1,9 @@
1
+ # Executes gh CLI commands via Open3 for GitHub API access.
1
2
  require "open3"
2
3
 
3
4
  module Carson
4
5
  module Adapters
6
+ # Thin wrapper around the gh CLI for GitHub API access.
5
7
  class GitHub
6
8
  def initialize( repo_root: )
7
9
  @repo_root = repo_root
@@ -1,5 +1,7 @@
1
+ # Builds structured prompts for dispatching work orders to coding agents.
1
2
  module Carson
2
3
  module Adapters
4
+ # Builds structured prompts for coding agent dispatch.
3
5
  module Prompt
4
6
  private
5
7