carson 2.10.0 → 2.11.1
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/MANUAL.md +100 -1
- data/README.md +13 -1
- data/RELEASE.md +24 -0
- data/VERSION +1 -1
- data/carson.gemspec +1 -7
- data/lib/carson/runtime/audit.rb +2 -2
- data/lib/carson/runtime/setup.rb +54 -3
- metadata +3 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c1366b08108b8d3891fdf7b5a408c6fa48fc1f09fee3e7078f6740e775fb113
|
|
4
|
+
data.tar.gz: 7cfc57e6b7cd66909ea92f9241423e99312cc368c8395c33d8c6b367e8fbaf60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9453360cbbc88577104b25a29373f987e4b8e3e881ec1d21127cbe0e8944beb0b479a617c9d8d83ca970a506b2bb76ceaeec6f264b25b00ae178f772be3a8ea1
|
|
7
|
+
data.tar.gz: f73ced7d9c9114ab395da8a348809ca17944b97cf6012b43c1dda23aa415d40a376c4e0ba01ba28eb3c8fbc74dc6ea7dad711850534ae10014b9a68cdd32dc40
|
data/MANUAL.md
CHANGED
|
@@ -5,7 +5,7 @@ For the mental model and command overview, see `README.md`. For formal interface
|
|
|
5
5
|
|
|
6
6
|
## Install Carson
|
|
7
7
|
|
|
8
|
-
Prerequisites: Ruby `>= 4
|
|
8
|
+
Prerequisites: Ruby `>= 3.4`, `gem` and `git` in `PATH`. `gh` (GitHub CLI) recommended for full review governance.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
gem install --user-install carson
|
|
@@ -176,6 +176,105 @@ Carson's `govern.merge.method` controls how `carson govern` merges ready PRs. Th
|
|
|
176
176
|
|
|
177
177
|
**Important:** Carson's merge method must match your GitHub repository's allowed merge types. If your repo only allows squash merges and Carson is set to `merge`, govern will fail when it tries to auto-merge. Check your repository settings under Settings > General > Pull Requests.
|
|
178
178
|
|
|
179
|
+
## Defaults and Why
|
|
180
|
+
|
|
181
|
+
### Principles (iron rules)
|
|
182
|
+
|
|
183
|
+
These define what Carson *is*. They are not configurable.
|
|
184
|
+
|
|
185
|
+
- **Outsider boundary** — Carson never places its own artefacts inside a governed repository.
|
|
186
|
+
- **Centralised lint** — one policy source at `~/.carson/lint/`, shared across all repos, zero per-repo drift.
|
|
187
|
+
- **Active review** — undisposed reviewer findings block merge; feedback must be acknowledged.
|
|
188
|
+
- **Self-diagnosing output** — every message names the cause and the fix.
|
|
189
|
+
- **Transparent governance** — Carson prepares everything for merge but never makes decisions without telling you.
|
|
190
|
+
|
|
191
|
+
### Configurable defaults
|
|
192
|
+
|
|
193
|
+
These are starting points chosen during `carson setup`. Every default has a reason, but all can be changed.
|
|
194
|
+
|
|
195
|
+
#### Workflow style
|
|
196
|
+
|
|
197
|
+
How code reaches main.
|
|
198
|
+
|
|
199
|
+
- **`branch`** (default) — every change goes through a PR. Hooks block direct commits and pushes to main/master. PRs enforce review, scope integrity, and CI gates before code reaches main.
|
|
200
|
+
- **`trunk`** — commit directly to main. Hooks allow all commits. Suits solo projects or flat teams that don't need PR-based review.
|
|
201
|
+
|
|
202
|
+
Change: `carson setup` or `CARSON_WORKFLOW_STYLE`.
|
|
203
|
+
|
|
204
|
+
#### Merge method
|
|
205
|
+
|
|
206
|
+
How `carson govern` merges ready PRs.
|
|
207
|
+
|
|
208
|
+
- **`squash`** (default) — one PR = one commit on main. Linear, bisectable history. Every commit is individually revertable. Branch commits are preserved in the PR on GitHub.
|
|
209
|
+
- **`rebase`** — preserves individual branch commits on main. Linear history. Use when commit-level attribution matters.
|
|
210
|
+
- **`merge`** — creates merge commits. Non-linear graph but preserves branch topology. Use when branch structure is meaningful.
|
|
211
|
+
|
|
212
|
+
Must match your GitHub repo's allowed merge types. Change: `carson setup` or `govern.merge.method` in config.
|
|
213
|
+
|
|
214
|
+
#### Git remote
|
|
215
|
+
|
|
216
|
+
Which remote Carson checks for main sync and PR operations.
|
|
217
|
+
|
|
218
|
+
- Default: **`origin`**. Setup detects your actual remotes and presents them — pick the one that points to GitHub.
|
|
219
|
+
- If multiple remotes share the same URL, setup warns about the duplicate.
|
|
220
|
+
|
|
221
|
+
Change: `carson setup` or `git.remote` in config.
|
|
222
|
+
|
|
223
|
+
#### Main branch
|
|
224
|
+
|
|
225
|
+
Which branch Carson treats as the canonical baseline.
|
|
226
|
+
|
|
227
|
+
- Default: **`main`**. Setup detects whether `main` or `master` exists and offers both.
|
|
228
|
+
|
|
229
|
+
Change: `carson setup` or `git.main_branch` in config.
|
|
230
|
+
|
|
231
|
+
#### Hooks location
|
|
232
|
+
|
|
233
|
+
Where Carson installs git hooks.
|
|
234
|
+
|
|
235
|
+
- Default: **`~/.carson/hooks/<version>/`**. Outsider principle: hooks live outside your repo, versioned per Carson release, never committed to your repository.
|
|
236
|
+
|
|
237
|
+
Change: `CARSON_HOOKS_BASE_PATH`.
|
|
238
|
+
|
|
239
|
+
#### Lint policy source
|
|
240
|
+
|
|
241
|
+
Where lint configuration files live.
|
|
242
|
+
|
|
243
|
+
- Default: **`~/.carson/lint/`**. Centralised: one policy source governs all repos consistently. Repo-local `.rubocop.yml` is forbidden in outsider mode to prevent per-repo drift.
|
|
244
|
+
|
|
245
|
+
Change the source: `carson lint setup --source <path-or-git-url>`.
|
|
246
|
+
|
|
247
|
+
#### Scope integrity
|
|
248
|
+
|
|
249
|
+
Whether cross-module changes are flagged.
|
|
250
|
+
|
|
251
|
+
- Default: **advisory** (attention, not block). Carson informs you when staged files span multiple module groups (e.g. both `domain` and `ui`), but doesn't prevent the commit. Useful for awareness; not a hard gate.
|
|
252
|
+
|
|
253
|
+
Customise groups: `scope.path_groups` in config.
|
|
254
|
+
|
|
255
|
+
#### Review disposition
|
|
256
|
+
|
|
257
|
+
Whether reviewer findings require acknowledgement.
|
|
258
|
+
|
|
259
|
+
- Default: **required**. Comments containing risk keywords (`bug`, `security`, `regression`, etc.) must have a `Disposition:` response from the PR author before merge. Prevents feedback from being buried.
|
|
260
|
+
|
|
261
|
+
Change prefix: `CARSON_REVIEW_DISPOSITION_PREFIX`.
|
|
262
|
+
|
|
263
|
+
#### Merge authority
|
|
264
|
+
|
|
265
|
+
Whether `carson govern` can merge PRs autonomously.
|
|
266
|
+
|
|
267
|
+
- Default: **enabled**. Carson merges PRs that pass all gates (CI green, review clean, audit clean). PRs that need human judgement are escalated, never silently merged.
|
|
268
|
+
|
|
269
|
+
Disable: `govern.merge_authority: false` in config.
|
|
270
|
+
|
|
271
|
+
#### Output verbosity
|
|
272
|
+
|
|
273
|
+
How much Carson prints.
|
|
274
|
+
|
|
275
|
+
- Default: **concise**. A healthy audit prints one line. Problems print actionable summaries with cause and fix.
|
|
276
|
+
- `--verbose` restores full diagnostic key-value output for debugging.
|
|
277
|
+
|
|
179
278
|
## Agent Discovery
|
|
180
279
|
|
|
181
280
|
Carson writes managed files that help interactive agents (Claude Code, Codex, Copilot) discover the governance system when they work in a governed repository.
|
data/README.md
CHANGED
|
@@ -40,6 +40,18 @@ Carson is an autonomous governance runtime that lives on your workstation and in
|
|
|
40
40
|
|
|
41
41
|
This separation is Carson's defining trait — the **outsider boundary**: no Carson scripts, config files, or governance payloads are ever placed inside a governed repository.
|
|
42
42
|
|
|
43
|
+
## Opinions
|
|
44
|
+
|
|
45
|
+
Carson is opinionated about governance. These are non-negotiable principles, not configurable defaults:
|
|
46
|
+
|
|
47
|
+
- **Outsider boundary** — Carson lives outside your repo, never inside. No Carson-owned artefacts in your repository. Offboarding leaves no trace.
|
|
48
|
+
- **Centralised lint** — lint policy at `~/.carson/lint/`, shared across all repos. Repo-local config files are forbidden — one source of truth, zero drift.
|
|
49
|
+
- **Active review** — undisposed reviewer findings block merge. Feedback must be acknowledged, not buried.
|
|
50
|
+
- **Self-diagnosing output** — every message names the cause and the fix. If you need to debug Carson's output, the output failed.
|
|
51
|
+
- **Transparent governance** — Carson prepares everything for merge but never oversteps. It does not make decisions for you without telling you.
|
|
52
|
+
|
|
53
|
+
Everything else — workflow style, merge method, remote name, main branch — is a configurable default chosen during `carson setup`. See `MANUAL.md` for the full list of defaults and why each was chosen.
|
|
54
|
+
|
|
43
55
|
The data flow:
|
|
44
56
|
|
|
45
57
|
1. You maintain a **policy source** — a directory or git repository containing your lint rules (e.g. `CODING/rubocop.yml`). Carson copies these to `~/.carson/lint/` via `carson lint setup`.
|
|
@@ -95,7 +107,7 @@ The data flow:
|
|
|
95
107
|
|
|
96
108
|
## Quickstart
|
|
97
109
|
|
|
98
|
-
Prerequisites: Ruby `>= 4
|
|
110
|
+
Prerequisites: Ruby `>= 3.4`, `git`, and `gem` in your PATH.
|
|
99
111
|
`gh` (GitHub CLI) is recommended for full review governance features.
|
|
100
112
|
|
|
101
113
|
```bash
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,30 @@ 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
|
+
## 2.11.1 — Document Philosophy, Opinions, and Configurable Defaults
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **README.md** — added "Opinions" section stating Carson's five iron-rule principles (outsider boundary, centralised lint, active review, self-diagnosing output, transparent governance).
|
|
13
|
+
- **MANUAL.md** — added "Defaults and Why" section: principles recap plus comprehensive reference for all ten configurable defaults with options, rationale, and how to change each one.
|
|
14
|
+
- Fixed stale Ruby prerequisite in both files: `>= 4.0` → `>= 3.4`.
|
|
15
|
+
|
|
16
|
+
### What users must do now
|
|
17
|
+
|
|
18
|
+
Nothing. Documentation only.
|
|
19
|
+
|
|
20
|
+
## 2.11.0 — Self-Diagnosing Audit and Duplicate-Remote Prevention
|
|
21
|
+
|
|
22
|
+
### What changed
|
|
23
|
+
|
|
24
|
+
- **Audit concise output now names the remote and suggests recovery actions.** "Main sync (origin): ahead by 1 — git fetch origin, or carson setup to switch remote." instead of the opaque "Main sync: ahead by 1 — reset local drift." The remote name is visible; the fix is embedded.
|
|
25
|
+
- **Setup warns when multiple remotes share the same URL.** Interactive mode annotates duplicates with `[duplicate]` and prints a warning. Silent mode logs a `duplicate_remotes:` verbose line. URL normalisation treats SSH and HTTPS variants as equal (`git@github.com:user/repo.git` matches `https://github.com/user/repo`).
|
|
26
|
+
|
|
27
|
+
### What users must do now
|
|
28
|
+
|
|
29
|
+
1. Upgrade Carson to `2.11.0`.
|
|
30
|
+
2. If you have duplicate remotes (e.g. both `origin` and `github` pointing to the same URL), remove the stale one with `git remote remove <name>`.
|
|
31
|
+
|
|
8
32
|
## 2.10.0 — Lower Ruby Requirement to 3.4
|
|
9
33
|
|
|
10
34
|
### What changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.11.1
|
data/carson.gemspec
CHANGED
|
@@ -20,15 +20,9 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
spec.post_install_message = <<~MSG
|
|
23
|
-
|
|
24
23
|
\u29D3 Carson at your service.
|
|
25
|
-
|
|
26
|
-
Step into your project directory and run:
|
|
27
|
-
|
|
28
|
-
carson onboard
|
|
29
|
-
|
|
24
|
+
Step into your project directory and run: carson onboard
|
|
30
25
|
I'll walk you through everything from there.
|
|
31
|
-
|
|
32
26
|
MSG
|
|
33
27
|
|
|
34
28
|
spec.bindir = "exe"
|
data/lib/carson/runtime/audit.rb
CHANGED
|
@@ -46,13 +46,13 @@ module Carson
|
|
|
46
46
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
47
47
|
puts_verbose "ACTION: local #{config.main_branch} is ahead of #{config.git_remote}/#{config.main_branch} by #{ahead_count} commit#{plural_suffix( count: ahead_count )}; reset local drift before commit/push workflows."
|
|
48
48
|
audit_state = "block"
|
|
49
|
-
audit_concise_problems << "Main sync: ahead by #{ahead_count} —
|
|
49
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): ahead by #{ahead_count} — git fetch #{config.git_remote}, or carson setup to switch remote."
|
|
50
50
|
elsif behind_count.positive?
|
|
51
51
|
puts_verbose "main_vs_remote_main_ahead: #{ahead_count}"
|
|
52
52
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
53
53
|
puts_verbose "ACTION: local #{config.main_branch} is behind #{config.git_remote}/#{config.main_branch} by #{behind_count} commit#{plural_suffix( count: behind_count )}; run carson sync."
|
|
54
54
|
audit_state = "attention" if audit_state == "ok"
|
|
55
|
-
audit_concise_problems << "Main sync: behind by #{behind_count} — run carson sync."
|
|
55
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): behind by #{behind_count} — run carson sync."
|
|
56
56
|
else
|
|
57
57
|
puts_verbose "main_vs_remote_main_ahead: 0"
|
|
58
58
|
puts_verbose "main_vs_remote_main_behind: 0"
|
data/lib/carson/runtime/setup.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "uri"
|
|
3
|
+
|
|
1
4
|
module Carson
|
|
2
5
|
class Runtime
|
|
3
6
|
module Setup
|
|
@@ -51,6 +54,15 @@ module Carson
|
|
|
51
54
|
puts_verbose "detected_remote: none"
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
remotes = list_git_remotes
|
|
58
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
59
|
+
unless duplicates.empty?
|
|
60
|
+
duplicates.each_value do |group|
|
|
61
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
62
|
+
puts_verbose "duplicate_remotes: #{names} share the same URL"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
54
66
|
branch = detect_main_branch
|
|
55
67
|
if branch && branch != config.main_branch
|
|
56
68
|
choices[ "git.main_branch" ] = branch
|
|
@@ -69,9 +81,18 @@ module Carson
|
|
|
69
81
|
return nil
|
|
70
82
|
end
|
|
71
83
|
|
|
84
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
85
|
+
duplicate_names = duplicates.values.flatten.map { it.fetch( :name ) }.to_set
|
|
86
|
+
unless duplicates.empty?
|
|
87
|
+
duplicates.each_value do |group|
|
|
88
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
89
|
+
puts_line "Remotes #{names} share the same URL. Consider removing the duplicate."
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
72
93
|
puts_line ""
|
|
73
94
|
puts_line "Git remote"
|
|
74
|
-
options = build_remote_options( remotes: remotes )
|
|
95
|
+
options = build_remote_options( remotes: remotes, duplicate_names: duplicate_names )
|
|
75
96
|
options << { label: "Other (enter name)", value: :other }
|
|
76
97
|
|
|
77
98
|
default_index = 0
|
|
@@ -151,12 +172,13 @@ module Carson
|
|
|
151
172
|
value.empty? ? nil : value
|
|
152
173
|
end
|
|
153
174
|
|
|
154
|
-
def build_remote_options( remotes: )
|
|
175
|
+
def build_remote_options( remotes:, duplicate_names: Set.new )
|
|
155
176
|
sorted = sort_remotes( remotes: remotes )
|
|
156
177
|
sorted.map do |entry|
|
|
157
178
|
name = entry.fetch( :name )
|
|
158
179
|
url = entry.fetch( :url )
|
|
159
|
-
|
|
180
|
+
tag = duplicate_names.include?( name ) ? " [duplicate]" : ""
|
|
181
|
+
{ label: "#{name} (#{url})#{tag}", value: name }
|
|
160
182
|
end
|
|
161
183
|
end
|
|
162
184
|
|
|
@@ -173,6 +195,35 @@ module Carson
|
|
|
173
195
|
well_known.sort_by { |e| WELL_KNOWN_REMOTES.index( e.fetch( :name ) ) || 999 } + others.sort_by { |e| e.fetch( :name ) }
|
|
174
196
|
end
|
|
175
197
|
|
|
198
|
+
# Normalises a remote URL so SSH and HTTPS variants of the same host/path compare equal.
|
|
199
|
+
# Strips trailing .git, lowercases, converts git@host:path to https://host/path.
|
|
200
|
+
def normalise_remote_url( url: )
|
|
201
|
+
text = url.to_s.strip
|
|
202
|
+
return "" if text.empty?
|
|
203
|
+
|
|
204
|
+
# Convert SSH shorthand (git@host:owner/repo) to HTTPS form.
|
|
205
|
+
if text.match?( /\A[\w.-]+@[\w.-]+:/ )
|
|
206
|
+
text = text.sub( /\A[\w.-]+@([\w.-]+):/, 'https://\1/' )
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
text = text.delete_suffix( ".git" )
|
|
210
|
+
text = text.chomp( "/" )
|
|
211
|
+
text.downcase
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Groups remotes that share the same normalised URL. Returns a hash of
|
|
215
|
+
# normalised_url => [remote entries] for groups with more than one member.
|
|
216
|
+
def duplicate_remote_groups( remotes: )
|
|
217
|
+
by_url = {}
|
|
218
|
+
remotes.each do |entry|
|
|
219
|
+
key = normalise_remote_url( url: entry.fetch( :url ) )
|
|
220
|
+
next if key.empty?
|
|
221
|
+
|
|
222
|
+
( by_url[ key ] ||= [] ) << entry
|
|
223
|
+
end
|
|
224
|
+
by_url.select { |_url, entries| entries.length > 1 }
|
|
225
|
+
end
|
|
226
|
+
|
|
176
227
|
def build_main_branch_options
|
|
177
228
|
options = []
|
|
178
229
|
main_exists = branch_exists_locally_or_remote?( branch: "main" )
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: carson
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.11.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hailei Wang
|
|
@@ -71,16 +71,10 @@ metadata:
|
|
|
71
71
|
changelog_uri: https://github.com/wanghailei/carson/blob/main/RELEASE.md
|
|
72
72
|
bug_tracker_uri: https://github.com/wanghailei/carson/issues
|
|
73
73
|
documentation_uri: https://github.com/wanghailei/carson/blob/main/MANUAL.md
|
|
74
|
-
post_install_message: |
|
|
75
|
-
|
|
74
|
+
post_install_message: |
|
|
76
75
|
⧓ Carson at your service.
|
|
77
|
-
|
|
78
|
-
Step into your project directory and run:
|
|
79
|
-
|
|
80
|
-
carson onboard
|
|
81
|
-
|
|
76
|
+
Step into your project directory and run: carson onboard
|
|
82
77
|
I'll walk you through everything from there.
|
|
83
|
-
|
|
84
78
|
rdoc_options: []
|
|
85
79
|
require_paths:
|
|
86
80
|
- lib
|
|
@@ -99,4 +93,3 @@ rubygems_version: 4.0.3
|
|
|
99
93
|
specification_version: 4
|
|
100
94
|
summary: Outsider governance runtime for repository hygiene and merge readiness.
|
|
101
95
|
test_files: []
|
|
102
|
-
...
|