carson 4.3.1 → 4.3.3
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.
- checksums.yaml +4 -4
- data/.github/workflows/carson_policy.yml +0 -4
- data/API.md +2 -3
- data/MANUAL.md +8 -8
- data/README.md +1 -1
- data/RELEASE.md +28 -0
- data/VERSION +1 -1
- data/config/hooks/pre-commit +2 -17
- data/lib/carson/adapters/agent.rb +1 -1
- data/lib/carson/config.rb +0 -10
- data/lib/carson/courier.rb +10 -10
- data/lib/carson/parcel.rb +11 -2
- data/lib/carson/runtime/local/onboard.rb +9 -43
- data/lib/carson/runtime/receive.rb +2 -2
- data/lib/carson/runtime/recover.rb +3 -3
- data/lib/carson/runtime/review/sweep_support.rb +1 -1
- data/lib/carson/runtime.rb +2 -5
- data/lib/carson/warehouse/bureau.rb +8 -11
- data/lib/carson/warehouse/seal.rb +17 -16
- data/lib/carson/warehouse/vault.rb +51 -36
- data/lib/carson/warehouse/workbench.rb +21 -94
- data/lib/carson/warehouse.rb +68 -77
- data/lib/carson/worktree.rb +1 -1
- data/lib/cli.rb +4 -38
- metadata +1 -2
- data/lib/carson/runtime/audit.rb +0 -603
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c6a03fe7387ab7c3df9dc7d1bc7de0b716dbb38b68b18cf9f103f8df9a7114c
|
|
4
|
+
data.tar.gz: 57e656339b9a4307684fae58ed6d10f147e8c23d4cd2a45731dd85905c4f7b05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc0ed4785b032dba61497a99ff09e29d890777d69d0dd59b1d632955a2b92a294a66571c7d04caa45b61ed0b81d56c65ce5a4550c13dfe6de32670f0103c0c54
|
|
7
|
+
data.tar.gz: 2419020c706bf3b1d94233c077ffdeb05475d2f443d1b8d27a1386c368d1535b797707f171270d18d2707bc98a3daf10d4c4add82d298e694375cecd27ff848e
|
data/API.md
CHANGED
|
@@ -21,7 +21,7 @@ Repo-scoped commands: `carson <repo> <command>` or `carson <command>` when CWD i
|
|
|
21
21
|
|---|---|
|
|
22
22
|
| `carson setup` | Interactive quiz to configure remote, main branch, workflow, and canonical lint-policy path. Writes `~/.carson/config.json`. |
|
|
23
23
|
| `carson onboard <repo_path>` | Apply one-command baseline setup for a target git repository. Auto-triggers `setup` on first run. Installs or refreshes Carson-managed global hooks. |
|
|
24
|
-
| `carson refresh` | Re-apply hooks
|
|
24
|
+
| `carson refresh` | Re-apply hooks and templates across all governed repos after upgrading Carson. Auto-propagates template updates to the remote via worktree (branch workflow: PR on `carson/template-sync`; trunk workflow: push to main). Skips repos with active worktrees or uncommitted changes. |
|
|
25
25
|
| `carson offboard <repo_path>` | Remove Carson-managed host artefacts, detach Carson hooks path, and deregister from `govern.repos`. |
|
|
26
26
|
| `carson list [--json]` | List all governed repositories. |
|
|
27
27
|
|
|
@@ -29,9 +29,8 @@ Repo-scoped commands: `carson <repo> <command>` or `carson <command>` when CWD i
|
|
|
29
29
|
|
|
30
30
|
| Command | Purpose |
|
|
31
31
|
|---|---|
|
|
32
|
-
| `carson audit` | Evaluate governance status and generate report output. |
|
|
33
32
|
| `carson deliver [--commit MESSAGE]` | Run Carson-owned branch delivery for the current checkout. Plain `deliver` transports existing commits only; `--commit` creates one all-dirty delivery commit first. Before push, Carson verifies the branch is fresh against the configured remote `main`; behind or unknown freshness blocks delivery without creating or refreshing a PR. If freshness is good, Carson pushes, creates or refreshes the PR, watches the delivery for a bounded settle window, merges when clear, syncs local `main`, and reports merge proof for the delivered branch. Non-integrated exits report `Merge deferred` or `Merge blocked` with explicit handoff commands. |
|
|
34
|
-
| `carson recover --check NAME [--json]` | Run the exceptional governed recovery path when one governance-owned required check is already red on the default branch. Carson proves the named baseline failure, keeps every other gate intact, merges through the recovery path
|
|
33
|
+
| `carson recover --check NAME [--json]` | Run the exceptional governed recovery path when one governance-owned required check is already red on the default branch. Carson proves the named baseline failure, keeps every other gate intact, and merges through the recovery path. |
|
|
35
34
|
| `carson sync` | Fast-forward local `main` from configured remote when tree is clean. |
|
|
36
35
|
| `carson prune` | Remove stale local branches whose upstream refs no longer exist. |
|
|
37
36
|
| `carson housekeep [--json] [--dry-run]` | Attempt to sync the current repo, then reap worktrees with strong abandonment evidence, reconcile integrated delivery worktree records from the ledger, and prune stale branches. Safe cleanup still runs when sync is blocked. |
|
data/MANUAL.md
CHANGED
|
@@ -40,7 +40,7 @@ On first run (no `~/.carson/config.json` exists), `onboard` launches `carson set
|
|
|
40
40
|
- Repository `core.hooksPath` alignment to Carson global hooks.
|
|
41
41
|
- Commit-time governance gate via managed `pre-commit` hook.
|
|
42
42
|
- Canonical `.github/*` template synchronisation (when `lint.canonical` is configured).
|
|
43
|
-
- Initial governance
|
|
43
|
+
- Initial governance status check.
|
|
44
44
|
|
|
45
45
|
### Reconfigure later
|
|
46
46
|
|
|
@@ -78,7 +78,7 @@ jobs:
|
|
|
78
78
|
Notes:
|
|
79
79
|
- When upgrading Carson, update both `carson_ref` and `carson_version` together.
|
|
80
80
|
- `CARSON_READ_TOKEN` must have read access to your policy source repository.
|
|
81
|
-
- The reusable workflow installs a pinned RuboCop gem before
|
|
81
|
+
- The reusable workflow installs a pinned RuboCop gem before lint checks; mirror the same pin in host governance workflows for deterministic checks.
|
|
82
82
|
|
|
83
83
|
### Canonical Templates
|
|
84
84
|
|
|
@@ -112,7 +112,7 @@ Carson discovers files in this directory and syncs them to governed repos. Root
|
|
|
112
112
|
|
|
113
113
|
## Operating Strategies
|
|
114
114
|
|
|
115
|
-
These strategies are the
|
|
115
|
+
These strategies are the design lens for Carson. If behaviour departs from them, either the product model or the implementation needs attention.
|
|
116
116
|
|
|
117
117
|
### Git Strategist
|
|
118
118
|
|
|
@@ -181,7 +181,7 @@ When a governance-owned required check is already red on the default branch, the
|
|
|
181
181
|
carson recover --check "Carson governance"
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
-
Recovery is narrow. Carson proves that the named check is red on the default branch, verifies that the current branch is repairing the governance surface, requires every other required check and the review gate to pass, then records a
|
|
184
|
+
Recovery is narrow. Carson proves that the named check is red on the default branch, verifies that the current branch is repairing the governance surface, requires every other required check and the review gate to pass, then records a recovery event before reporting success.
|
|
185
185
|
|
|
186
186
|
If Carson refuses recovery, the message explains the exact missing proof or remaining gate and tells you what to do next.
|
|
187
187
|
|
|
@@ -255,13 +255,13 @@ The two tools serve different layers. EnterWorktree owns the session (CWD switch
|
|
|
255
255
|
|
|
256
256
|
```bash
|
|
257
257
|
carson sync # fast-forward local main
|
|
258
|
-
carson
|
|
258
|
+
carson status # check repository state
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
**Before push or PR update:**
|
|
262
262
|
|
|
263
263
|
```bash
|
|
264
|
-
carson
|
|
264
|
+
carson status
|
|
265
265
|
carson template check
|
|
266
266
|
```
|
|
267
267
|
|
|
@@ -287,7 +287,7 @@ carson list --json # machine-readable output
|
|
|
287
287
|
**Portfolio maintenance:**
|
|
288
288
|
|
|
289
289
|
```bash
|
|
290
|
-
carson refresh # re-apply hooks
|
|
290
|
+
carson refresh # re-apply hooks and templates across all repos
|
|
291
291
|
carson list # list all governed repositories
|
|
292
292
|
```
|
|
293
293
|
|
|
@@ -411,7 +411,7 @@ Change: `CARSON_REVIEW_DISPOSITION`.
|
|
|
411
411
|
|
|
412
412
|
How much Carson prints.
|
|
413
413
|
|
|
414
|
-
- Default: **concise**. A healthy
|
|
414
|
+
- Default: **concise**. A healthy run prints one line. Problems print actionable summaries with cause and fix.
|
|
415
415
|
- `--verbose` restores full diagnostic key-value output for debugging.
|
|
416
416
|
|
|
417
417
|
## Configuration
|
data/README.md
CHANGED
|
@@ -66,7 +66,7 @@ carson housekeep
|
|
|
66
66
|
|
|
67
67
|
`carson deliver` runs Carson-owned branch delivery. Before any push, Carson verifies that the branch is fresh against the configured remote `main` using local git. If freshness is behind or unknown, delivery stops with an explicit block and no PR side effect. After a PR exists, Carson delegates merge eligibility to GitHub's `mergeStateStatus` — if GitHub reports `CLEAN`, Carson proceeds regardless of local ancestor status. Plain `deliver` transports existing commits only; `carson deliver --commit "..."` creates one all-dirty delivery commit first, then continues the same flow. If the branch is fresh, Carson pushes it, creates or refreshes the PR, watches the delivery for a bounded settle window, merges when clear, and syncs local `main`. If the settle window expires without integration, Carson exits with an explicit `Merge deferred` or `Merge blocked` handoff instead of leaving the PR mysteriously open. Deferred and blocked exits say whether Carson attempted merge and list the next commands in order.
|
|
68
68
|
|
|
69
|
-
When one Carson-governed required check is already red on the default branch and the current PR is the repair, use `carson recover --check "..."`. Recovery is the explicit exceptional path: Carson proves the baseline failure, keeps every other gate intact, records
|
|
69
|
+
When one Carson-governed required check is already red on the default branch and the current PR is the repair, use `carson recover --check "..."`. Recovery is the explicit exceptional path: Carson proves the baseline failure, keeps every other gate intact, records a recovery event, and never teaches operators to step outside Carson first.
|
|
70
70
|
|
|
71
71
|
`carson worktree list` is the visibility surface for cleanup: it shows every registered worktree, the branch, PR state, whether the content is already on `main`, and Carson's keep or reap recommendation. Content matching `main` is diagnostic, not standalone proof that a worktree is abandoned. `carson housekeep` is the main cleanup pass: sync main, reap worktrees with strong abandonment evidence (for example a missing directory, merged PR, or closed abandoned PR), and prune stale branches. When work needs to be abandoned instead of landed, use `carson abandon <pr-number|pr-url|branch>` to close the PR and clean up the branch/worktree safely.
|
|
72
72
|
|
data/RELEASE.md
CHANGED
|
@@ -7,6 +7,34 @@ Release-note scope rule:
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
### Removed
|
|
11
|
+
|
|
12
|
+
- **`carson audit` dissolved.** The audit command and its pre-commit gate have been removed entirely. Three of its seven checks were already enforced by the commands that need them (sealed workbench by `pack!`, working tree by Courier, main sync by `sync!`). The remaining four (PR status, CI baseline, hooks health, outsider fingerprints) were either unproven at commit time or already called by individual commands. Bare `carson` now defaults to `status` instead of `audit`. The pre-commit hook is now a no-op reserved for future use.
|
|
13
|
+
|
|
14
|
+
### Why
|
|
15
|
+
|
|
16
|
+
Audit was a remote-centred pre-commit gate — it blocked commits based on delivery-time concerns (PR checks, CI baseline) that don't belong at commit time. In local-centred mode it was purely dead weight. Rather than relocate it to bureau, we verified that none of its unique checks had proven value at any gate, and dissolved the whole thing. Scars, not speculation.
|
|
17
|
+
|
|
18
|
+
## 4.3.3
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- **`carson checkin` is 13× faster.** Removed two redundant `git fetch` calls that hit GitHub on every checkin. `checkin` now branches from local main — zero network, 0.7s instead of 9.4s. Local main is the standard; GitHub is a backup, not a gating dependency.
|
|
23
|
+
|
|
24
|
+
### Why
|
|
25
|
+
|
|
26
|
+
Local-centred mode means local main is the source of truth. Fetching from GitHub on every checkin was a remote-centred assumption baked into the command. The two sequential network round-trips accounted for 90% of the wall time.
|
|
27
|
+
|
|
28
|
+
## 4.3.2
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **Vault gives correct recovery when main worktree is dirty.** `vault_blocked` now inspects stderr from `git merge --ff-only` to distinguish dirty-tree conflicts from diverged-history blocks. Previously, both cases got the generic "rebase" advice — misleading when the branch was already a valid fast-forward descendant and the real problem was uncommitted files in the main worktree.
|
|
33
|
+
|
|
34
|
+
### Why
|
|
35
|
+
|
|
36
|
+
An agent rebased three times on an already-correct branch because Carson misdiagnosed a dirty-tree block as divergence. The fix: read stderr, give the right advice. Scars, not speculation.
|
|
37
|
+
|
|
10
38
|
## 4.3.1
|
|
11
39
|
|
|
12
40
|
### Changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.3.
|
|
1
|
+
4.3.3
|
data/config/hooks/pre-commit
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
carson_command=( "$CARSON_BIN" )
|
|
6
|
-
elif command -v carson >/dev/null 2>&1; then
|
|
7
|
-
carson_command=( "carson" )
|
|
8
|
-
else
|
|
9
|
-
echo "Carson policy: 'carson' command is required for pre-commit governance checks." >&2
|
|
10
|
-
echo "Install Carson and rerun 'carson prepare'." >&2
|
|
11
|
-
exit 1
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
if ! "${carson_command[@]}" audit; then
|
|
15
|
-
echo "Carson policy: commit blocked by governance audit." >&2
|
|
16
|
-
echo "Resolve reported policy blocks before committing." >&2
|
|
17
|
-
echo "If lint policy files are missing, run: carson lint policy --source <path-or-git-url>" >&2
|
|
18
|
-
exit 1
|
|
19
|
-
fi
|
|
3
|
+
# Pre-commit hook — reserved for future use.
|
|
4
|
+
exit 0
|
|
@@ -3,7 +3,7 @@ module Carson
|
|
|
3
3
|
module Adapters
|
|
4
4
|
module Agent
|
|
5
5
|
WorkOrder = Struct.new( :repo, :branch, :pr_number, :objective, :context, :acceptance_checks, keyword_init: true )
|
|
6
|
-
# objective: "fix_ci" | "address_review"
|
|
6
|
+
# objective: "fix_ci" | "address_review"
|
|
7
7
|
# context: String (legacy — PR title) or Hash with structured evidence:
|
|
8
8
|
# fix_ci: { title:, ci_logs:, ci_run_url:, prior_attempt: { summary:, dispatched_at: } }
|
|
9
9
|
# address_review: { title:, review_findings: [{ kind:, url:, body: }], prior_attempt: ... }
|
data/lib/carson/config.rb
CHANGED
|
@@ -28,7 +28,6 @@ module Carson
|
|
|
28
28
|
:review_wait_seconds, :review_poll_seconds, :review_max_polls, :review_sweep_window_days,
|
|
29
29
|
:review_sweep_states, :review_disposition, :review_risk_keywords,
|
|
30
30
|
:review_tracking_issue_title, :review_tracking_issue_label, :review_bot_usernames,
|
|
31
|
-
:audit_advisory_check_names,
|
|
32
31
|
:workflow_style,
|
|
33
32
|
:govern_repos, :govern_merge_method,
|
|
34
33
|
:govern_agent_provider, :govern_state_path,
|
|
@@ -80,9 +79,6 @@ module Carson
|
|
|
80
79
|
"label" => "carson-review-sweep"
|
|
81
80
|
}
|
|
82
81
|
},
|
|
83
|
-
"audit" => {
|
|
84
|
-
"advisory_check_names" => [ "Scheduled review sweep", "Carson governance", "Tag, release, publish" ]
|
|
85
|
-
},
|
|
86
82
|
"deliver" => {
|
|
87
83
|
"poll_interval_at_bureau" => 30
|
|
88
84
|
},
|
|
@@ -170,9 +166,6 @@ module Carson
|
|
|
170
166
|
sweep[ "states" ] = states unless states.empty?
|
|
171
167
|
bot_usernames = env_string_array( key: "CARSON_REVIEW_BOT_USERNAMES" )
|
|
172
168
|
review[ "bot_usernames" ] = bot_usernames unless bot_usernames.empty?
|
|
173
|
-
audit = fetch_hash_section( data: copy, key: "audit" )
|
|
174
|
-
advisory_names = env_string_array( key: "CARSON_AUDIT_ADVISORY_CHECK_NAMES" )
|
|
175
|
-
audit[ "advisory_check_names" ] = advisory_names unless advisory_names.empty?
|
|
176
169
|
deliver = fetch_hash_section( data: copy, key: "deliver" )
|
|
177
170
|
deliver[ "poll_interval_at_bureau" ] = env_integer( key: "CARSON_POLL_INTERVAL_AT_BUREAU", fallback: deliver.fetch( "poll_interval_at_bureau" ) )
|
|
178
171
|
govern = fetch_hash_section( data: copy, key: "govern" )
|
|
@@ -243,9 +236,6 @@ module Carson
|
|
|
243
236
|
@review_tracking_issue_title = fetch_string( hash: tracking_issue_hash, key: "title" )
|
|
244
237
|
@review_tracking_issue_label = fetch_string( hash: tracking_issue_hash, key: "label" )
|
|
245
238
|
@review_bot_usernames = fetch_optional_string_array( hash: review_hash, key: "bot_usernames" )
|
|
246
|
-
audit_hash = fetch_hash( hash: data, key: "audit" )
|
|
247
|
-
@audit_advisory_check_names = fetch_optional_string_array( hash: audit_hash, key: "advisory_check_names" )
|
|
248
|
-
|
|
249
239
|
deliver_hash = fetch_hash( hash: data, key: "deliver" )
|
|
250
240
|
@poll_interval_at_bureau = fetch_non_negative_integer( hash: deliver_hash, key: "poll_interval_at_bureau" )
|
|
251
241
|
|
data/lib/carson/courier.rb
CHANGED
|
@@ -137,15 +137,15 @@ module Carson
|
|
|
137
137
|
|
|
138
138
|
# 02. Parcel behind standard — not based on client's latest standard.
|
|
139
139
|
# The courier rebases automatically. Only blocks on conflict.
|
|
140
|
-
unless @warehouse.
|
|
140
|
+
unless @warehouse.receive_latest!
|
|
141
141
|
return blocked( result,
|
|
142
142
|
"cannot verify freshness — fetch failed",
|
|
143
143
|
recovery: "carson sync, then carson deliver" )
|
|
144
144
|
end
|
|
145
|
-
unless @warehouse.
|
|
145
|
+
unless @warehouse.based_on_latest?( parcel )
|
|
146
146
|
remote_main = "#{@warehouse.bureau_address}/#{@warehouse.main_label}"
|
|
147
147
|
say "Branch is behind #{remote_main} — rebasing..."
|
|
148
|
-
unless @warehouse.
|
|
148
|
+
unless @warehouse.rebase!
|
|
149
149
|
return blocked( result,
|
|
150
150
|
"rebase conflict onto #{remote_main}",
|
|
151
151
|
recovery: "resolve conflicts, then carson deliver" )
|
|
@@ -175,8 +175,8 @@ module Carson
|
|
|
175
175
|
result[ :tracking_number ] = waybill.tracking_number
|
|
176
176
|
result[ :url ] = waybill.url
|
|
177
177
|
|
|
178
|
-
# Seal the
|
|
179
|
-
@warehouse.
|
|
178
|
+
# Seal the workbench — no more packing until the outcome is confirmed.
|
|
179
|
+
@warehouse.seal!( tracking: waybill.tracking_number )
|
|
180
180
|
|
|
181
181
|
# Wait at the bureau while the bureaucrats check the parcel.
|
|
182
182
|
wait_and_poll_at_bureau( waybill, result )
|
|
@@ -185,7 +185,7 @@ module Carson
|
|
|
185
185
|
# delivered/held/rejected → unseal (shelf done or parcel returned)
|
|
186
186
|
# filed → stay sealed (parcel still in flight)
|
|
187
187
|
outcome = result[ :outcome ]
|
|
188
|
-
@warehouse.
|
|
188
|
+
@warehouse.unseal! if outcome == "delivered" || outcome == "held" || outcome == "rejected"
|
|
189
189
|
|
|
190
190
|
# Update the ledger with the final outcome and PR identity.
|
|
191
191
|
record( parcel, status: outcome || "filed", summary: result[ :hold_reason ], waybill: waybill )
|
|
@@ -201,12 +201,12 @@ module Carson
|
|
|
201
201
|
# checks are exhausted.
|
|
202
202
|
def wait_and_poll_at_bureau( waybill, result )
|
|
203
203
|
MAX_CHECKS_AT_BUREAU.times do |check|
|
|
204
|
-
@warehouse.
|
|
204
|
+
@warehouse.check_parcel_with( waybill )
|
|
205
205
|
|
|
206
206
|
# 14/17. Already accepted — parcel is in the registry.
|
|
207
207
|
if waybill.accepted?
|
|
208
208
|
result[ :outcome ] = "delivered"
|
|
209
|
-
result[ :synced ] = @warehouse.
|
|
209
|
+
result[ :synced ] = @warehouse.receive_latest!
|
|
210
210
|
return
|
|
211
211
|
end
|
|
212
212
|
|
|
@@ -219,11 +219,11 @@ module Carson
|
|
|
219
219
|
|
|
220
220
|
# Cleared or mergeability pending — ask the warehouse to register.
|
|
221
221
|
if waybill.cleared? || waybill.mergeability_pending?
|
|
222
|
-
@warehouse.
|
|
222
|
+
@warehouse.register_with!( waybill, method: @merge_method )
|
|
223
223
|
|
|
224
224
|
if waybill.accepted?
|
|
225
225
|
result[ :outcome ] = "delivered"
|
|
226
|
-
result[ :synced ] = @warehouse.
|
|
226
|
+
result[ :synced ] = @warehouse.receive_latest!
|
|
227
227
|
return
|
|
228
228
|
end
|
|
229
229
|
end
|
data/lib/carson/parcel.rb
CHANGED
|
@@ -11,12 +11,13 @@ module Carson
|
|
|
11
11
|
# head (commit SHA), and shelf (worktree). The protagonist of every
|
|
12
12
|
# delivery — without a parcel, there is nothing to deliver.
|
|
13
13
|
class Parcel
|
|
14
|
-
attr_reader :label, :head, :shelf
|
|
14
|
+
attr_reader :label, :head, :shelf, :origin
|
|
15
15
|
|
|
16
|
-
def initialize( label:, head:, shelf: nil )
|
|
16
|
+
def initialize( label:, head:, shelf: nil, origin: nil )
|
|
17
17
|
@label = label
|
|
18
18
|
@head = head
|
|
19
19
|
@shelf = shelf
|
|
20
|
+
@origin = origin
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
# Is this parcel sitting on the main shelf?
|
|
@@ -24,5 +25,13 @@ module Carson
|
|
|
24
25
|
def on_main?( main_label )
|
|
25
26
|
label == main_label
|
|
26
27
|
end
|
|
28
|
+
|
|
29
|
+
# Does this parcel carry anything?
|
|
30
|
+
# A parcel with no commits ahead of its origin is empty —
|
|
31
|
+
# nothing was packed, nothing to deliver.
|
|
32
|
+
def empty?
|
|
33
|
+
return false unless origin
|
|
34
|
+
head == origin
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
37
|
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Repository onboarding and refresh lifecycle.
|
|
2
|
-
# Onboard: detect remote, install hooks, apply templates
|
|
2
|
+
# Onboard: detect remote, install hooks, apply templates.
|
|
3
3
|
# Refresh: re-apply hooks and templates after Carson upgrade.
|
|
4
4
|
# Refresh all: batch refresh across governed portfolio with safety checks.
|
|
5
5
|
module Carson
|
|
6
6
|
class Runtime
|
|
7
7
|
module Local
|
|
8
8
|
# One-command onboarding for new repositories: detect remote, install hooks,
|
|
9
|
-
# apply templates
|
|
9
|
+
# and apply templates.
|
|
10
10
|
def onboard!
|
|
11
11
|
fingerprint_status = block_if_outsider_fingerprints!
|
|
12
12
|
return fingerprint_status unless fingerprint_status.nil?
|
|
@@ -32,7 +32,7 @@ module Carson
|
|
|
32
32
|
onboard_apply!
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
# Re-applies hooks
|
|
35
|
+
# Re-applies hooks and templates after upgrading Carson.
|
|
36
36
|
def refresh!
|
|
37
37
|
fingerprint_status = block_if_outsider_fingerprints!
|
|
38
38
|
return fingerprint_status unless fingerprint_status.nil?
|
|
@@ -55,13 +55,8 @@ module Carson
|
|
|
55
55
|
|
|
56
56
|
@template_sync_result = template_propagate!( drift_count: drift_count + stale_count )
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
puts_line "OK: Carson refresh completed for #{repo_root}."
|
|
61
|
-
elsif audit_status == EXIT_BLOCK
|
|
62
|
-
puts_line "Refresh complete — some checks need attention. Run carson audit for details."
|
|
63
|
-
end
|
|
64
|
-
return audit_status
|
|
58
|
+
puts_line "OK: Carson refresh completed for #{repo_root}."
|
|
59
|
+
return EXIT_OK
|
|
65
60
|
end
|
|
66
61
|
|
|
67
62
|
puts_line "Refresh"
|
|
@@ -82,12 +77,11 @@ module Carson
|
|
|
82
77
|
|
|
83
78
|
@template_sync_result = template_propagate!( drift_count: total_drift )
|
|
84
79
|
|
|
85
|
-
audit_status = audit!
|
|
86
80
|
puts_line "Refresh complete."
|
|
87
|
-
|
|
81
|
+
EXIT_OK
|
|
88
82
|
end
|
|
89
83
|
|
|
90
|
-
# Re-applies hooks
|
|
84
|
+
# Re-applies hooks and templates across all governed repositories.
|
|
91
85
|
# Checks each repo for safety (active worktrees, uncommitted changes) and
|
|
92
86
|
# marks unsafe repos as pending to avoid disrupting active work.
|
|
93
87
|
def refresh_all!
|
|
@@ -202,7 +196,7 @@ module Carson
|
|
|
202
196
|
|
|
203
197
|
private
|
|
204
198
|
|
|
205
|
-
# Concise onboard orchestration: hooks, templates, remote,
|
|
199
|
+
# Concise onboard orchestration: hooks, templates, remote, guidance.
|
|
206
200
|
def onboard_apply!
|
|
207
201
|
hook_status = with_captured_output { prepare! }
|
|
208
202
|
return hook_status unless hook_status == EXIT_OK
|
|
@@ -218,7 +212,6 @@ module Carson
|
|
|
218
212
|
end
|
|
219
213
|
|
|
220
214
|
onboard_report_remote!
|
|
221
|
-
audit_status = onboard_run_audit!
|
|
222
215
|
|
|
223
216
|
puts_line ""
|
|
224
217
|
puts_line "Carson at your service."
|
|
@@ -235,7 +228,7 @@ module Carson
|
|
|
235
228
|
puts_line ""
|
|
236
229
|
puts_line "To adjust any setting: carson setup"
|
|
237
230
|
|
|
238
|
-
|
|
231
|
+
EXIT_OK
|
|
239
232
|
end
|
|
240
233
|
|
|
241
234
|
# Friendly remote status for onboard output.
|
|
@@ -247,33 +240,6 @@ module Carson
|
|
|
247
240
|
end
|
|
248
241
|
end
|
|
249
242
|
|
|
250
|
-
# Runs audit with captured output; reports summary instead of full detail.
|
|
251
|
-
def onboard_run_audit!
|
|
252
|
-
audit_error = nil
|
|
253
|
-
audit_status = with_captured_output { audit! }
|
|
254
|
-
rescue StandardError => exception
|
|
255
|
-
audit_error = exception
|
|
256
|
-
audit_status = EXIT_OK
|
|
257
|
-
ensure
|
|
258
|
-
return onboard_print_audit_result( status: audit_status, error: audit_error )
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def onboard_print_audit_result( status:, error: )
|
|
262
|
-
if error
|
|
263
|
-
if error.message.to_s.match?( /HEAD|rev-parse/ )
|
|
264
|
-
puts_line "No commits yet — run carson audit after your first commit."
|
|
265
|
-
else
|
|
266
|
-
puts_line "Audit skipped — run carson audit for details."
|
|
267
|
-
end
|
|
268
|
-
return EXIT_OK
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
if status == EXIT_BLOCK
|
|
272
|
-
puts_line "Some checks need attention — run carson audit for details."
|
|
273
|
-
end
|
|
274
|
-
status
|
|
275
|
-
end
|
|
276
|
-
|
|
277
243
|
# Verifies configured remote exists and logs status without mutating remotes.
|
|
278
244
|
def report_detected_remote!
|
|
279
245
|
if git_remote_exists?( remote_name: config.git_remote )
|
|
@@ -87,7 +87,7 @@ module Carson
|
|
|
87
87
|
# Unseal worktrees that were filed — receive now owns the delivery lifecycle.
|
|
88
88
|
unless dry_run
|
|
89
89
|
filed_worktree_paths.each do |worktree_path|
|
|
90
|
-
Warehouse.new( path: worktree_path ).
|
|
90
|
+
Warehouse.new( path: worktree_path ).unseal!
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
@@ -365,7 +365,7 @@ module Carson
|
|
|
365
365
|
case cause
|
|
366
366
|
when "ci" then "fix_ci"
|
|
367
367
|
when "review" then "address_review"
|
|
368
|
-
else "
|
|
368
|
+
else "fix_ci"
|
|
369
369
|
end
|
|
370
370
|
end
|
|
371
371
|
|
|
@@ -84,14 +84,14 @@ module Carson
|
|
|
84
84
|
}
|
|
85
85
|
if baseline.fetch( :status ) == "skipped"
|
|
86
86
|
result[ :error ] = "unable to verify the default-branch baseline: #{baseline.fetch( :skip_reason )}"
|
|
87
|
-
result[ :recovery ] = "run carson
|
|
87
|
+
result[ :recovery ] = "run carson status after fixing GitHub access"
|
|
88
88
|
return recover_finish( result: result, exit_code: EXIT_ERROR, json_output: json_output )
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
baseline_entry = recovery_baseline_entry( baseline: baseline, check_name: check_name )
|
|
92
92
|
if baseline_entry.nil?
|
|
93
93
|
result[ :error ] = "#{check_name} is not red on #{baseline.fetch( :default_branch, config.main_branch )}"
|
|
94
|
-
result[ :recovery ] = "run carson
|
|
94
|
+
result[ :recovery ] = "run carson status to confirm the baseline check state"
|
|
95
95
|
return recover_finish( result: result, exit_code: EXIT_BLOCK, json_output: json_output )
|
|
96
96
|
end
|
|
97
97
|
|
|
@@ -408,7 +408,7 @@ module Carson
|
|
|
408
408
|
elsif result[ :synced ]
|
|
409
409
|
puts_line "Synced local #{result.fetch( :main_branch )}."
|
|
410
410
|
end
|
|
411
|
-
puts_line "Recorded recovery
|
|
411
|
+
puts_line "Recorded recovery for #{result.fetch( :check )}."
|
|
412
412
|
puts_line "Check back with #{result.fetch( :next_step )}" if result[ :next_step ]
|
|
413
413
|
end
|
|
414
414
|
end
|
|
@@ -163,7 +163,7 @@ module Carson
|
|
|
163
163
|
}
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
# When sweep is clear, close prior tracking issue and add
|
|
166
|
+
# When sweep is clear, close prior tracking issue and add a clear comment.
|
|
167
167
|
def close_review_sweep_issue_if_open( repo_slug:, issue: )
|
|
168
168
|
return { action: "none", issue: nil } if issue.nil?
|
|
169
169
|
return { action: "none", issue: issue } unless issue.fetch( :state ) == "OPEN"
|
data/lib/carson/runtime.rb
CHANGED
|
@@ -14,8 +14,6 @@ module Carson
|
|
|
14
14
|
EXIT_ERROR = 1
|
|
15
15
|
EXIT_BLOCK = 2
|
|
16
16
|
|
|
17
|
-
REPORT_MD = "pr_report_latest.md".freeze
|
|
18
|
-
REPORT_JSON = "pr_report_latest.json".freeze
|
|
19
17
|
REVIEW_GATE_REPORT_MD = "review_gate_latest.md".freeze
|
|
20
18
|
REVIEW_GATE_REPORT_JSON = "review_gate_latest.json".freeze
|
|
21
19
|
REVIEW_SWEEP_REPORT_MD = "review_sweep_latest.md".freeze
|
|
@@ -43,7 +41,7 @@ module Carson
|
|
|
43
41
|
attr_reader :template_sync_result
|
|
44
42
|
|
|
45
43
|
# Lazy ledger: only constructed when a command actually needs delivery state.
|
|
46
|
-
# Read-only commands (worktree list,
|
|
44
|
+
# Read-only commands (worktree list, prune, sync) never touch the
|
|
47
45
|
# govern state lock file.
|
|
48
46
|
def ledger
|
|
49
47
|
@ledger ||= Ledger.new( path: @config.govern_state_path )
|
|
@@ -115,7 +113,7 @@ module Carson
|
|
|
115
113
|
success
|
|
116
114
|
end
|
|
117
115
|
|
|
118
|
-
# Human-readable plural suffix helper
|
|
116
|
+
# Human-readable plural suffix helper.
|
|
119
117
|
def plural_suffix( count: )
|
|
120
118
|
count.to_i == 1 ? "" : "s"
|
|
121
119
|
end
|
|
@@ -366,7 +364,6 @@ module Carson
|
|
|
366
364
|
end
|
|
367
365
|
|
|
368
366
|
require_relative "runtime/local"
|
|
369
|
-
require_relative "runtime/audit"
|
|
370
367
|
require_relative "runtime/loop_runner"
|
|
371
368
|
require_relative "runtime/housekeep"
|
|
372
369
|
require_relative "runtime/list"
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
# The warehouse's bureau
|
|
2
|
-
# The
|
|
3
|
-
#
|
|
1
|
+
# The warehouse's bureau concern — backup and optional PR/CI.
|
|
2
|
+
# The bureau is just a backup. These methods exist for when
|
|
3
|
+
# the courier needs the warehouse to interact with it.
|
|
4
4
|
require "json"
|
|
5
5
|
|
|
6
6
|
module Carson
|
|
7
7
|
class Warehouse
|
|
8
8
|
module Bureau
|
|
9
9
|
|
|
10
|
-
# Check
|
|
11
|
-
|
|
12
|
-
def check_parcel_at_bureau_with( waybill )
|
|
10
|
+
# Check a parcel's status using the waybill.
|
|
11
|
+
def check_parcel_with( waybill )
|
|
13
12
|
state = fetch_pr_state_for( waybill.tracking_number )
|
|
14
13
|
ci, ci_diagnostic = fetch_ci_state_for( waybill.tracking_number )
|
|
15
14
|
waybill.record( state: state, ci: ci, ci_diagnostic: ci_diagnostic )
|
|
@@ -47,15 +46,13 @@ module Carson
|
|
|
47
46
|
Waybill.new( label: parcel.label, tracking_number: tracking_number, url: url )
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
# Register
|
|
51
|
-
|
|
52
|
-
def register_parcel_at_bureau_with!( waybill, method: )
|
|
49
|
+
# Register a parcel using the waybill.
|
|
50
|
+
def register_with!( waybill, method: )
|
|
53
51
|
_, _, status = gh( "pr", "merge", waybill.tracking_number.to_s, "--#{method}" )
|
|
54
52
|
if status.success?
|
|
55
53
|
waybill.stamp( :accepted )
|
|
56
54
|
else
|
|
57
|
-
|
|
58
|
-
check_parcel_at_bureau_with( waybill )
|
|
55
|
+
check_parcel_with( waybill )
|
|
59
56
|
end
|
|
60
57
|
end
|
|
61
58
|
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
# The warehouse's
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
1
|
+
# The warehouse's seal concern — bureau enhancement only.
|
|
2
|
+
#
|
|
3
|
+
# The seal is inactive in the base (local-centred) model.
|
|
4
|
+
# There is no "in flight" period — the parcel goes directly
|
|
5
|
+
# into the vault.
|
|
6
|
+
#
|
|
7
|
+
# The seal only activates with bureau enhancement, where there's
|
|
8
|
+
# a waiting period between shipping and bureau acceptance. During
|
|
9
|
+
# that period, the workbench is sealed — no more packing until
|
|
10
|
+
# the delivery outcome is confirmed.
|
|
11
|
+
#
|
|
12
|
+
# The seal marker lives outside the worktree (~/.carson/seals/)
|
|
13
|
+
# so it does not pollute git status.
|
|
6
14
|
require "digest"
|
|
7
15
|
require "fileutils"
|
|
8
16
|
|
|
@@ -10,17 +18,15 @@ module Carson
|
|
|
10
18
|
class Warehouse
|
|
11
19
|
module Seal
|
|
12
20
|
|
|
13
|
-
# Seal the workbench — no more packing until
|
|
14
|
-
|
|
15
|
-
def seal_workbench!( tracking_number: )
|
|
21
|
+
# Seal the workbench — no more packing until the bureau answers.
|
|
22
|
+
def seal!( tracking: )
|
|
16
23
|
marker = delivering_marker_path
|
|
17
24
|
FileUtils.mkdir_p( File.dirname( marker ) )
|
|
18
|
-
File.write( marker, "#{
|
|
25
|
+
File.write( marker, "#{tracking}\n#{@path}" )
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
# Unseal the workbench — the courier brought back the parcel.
|
|
22
|
-
|
|
23
|
-
def unseal_workbench!
|
|
29
|
+
def unseal!
|
|
24
30
|
File.delete( delivering_marker_path ) if File.exist?( delivering_marker_path )
|
|
25
31
|
end
|
|
26
32
|
|
|
@@ -35,11 +41,6 @@ module Carson
|
|
|
35
41
|
File.read( delivering_marker_path ).lines.first.strip
|
|
36
42
|
end
|
|
37
43
|
|
|
38
|
-
# --- Transitional aliases ---
|
|
39
|
-
# Keep old names working until all callers are updated.
|
|
40
|
-
alias seal_shelf! seal_workbench!
|
|
41
|
-
alias unseal_shelf! unseal_workbench!
|
|
42
|
-
|
|
43
44
|
private
|
|
44
45
|
|
|
45
46
|
# Path to the delivery marker file.
|