carson 3.23.0 → 3.23.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b09da719cacfca1da3e00f8c542719283e0f1e0d73554371449fa01d3d1b331
4
- data.tar.gz: 33d2ab2f5881e8f96934037f715893492b495354eda5c77c64b47ded8d1ee710
3
+ metadata.gz: b22f115d99fffc1ed689ceb214523b8a33da08fc84a955fcc766d778be21e40c
4
+ data.tar.gz: 6a270c175cb8affabba0d6d747e08701a50c61124a4c5486525113ff350cedbf
5
5
  SHA512:
6
- metadata.gz: d5c0dbbae4f9424054cbed42c0d6ed6430643a7f974252bf532f98c8aeeafb5e0962c9204e3e9d806752ac5ee063c98d005b4693d570c2b3d9699b2ac1808bef
7
- data.tar.gz: d49e6eef6854bf55eb3fe788e6f549585806a03d9266ef5aefdd42b960b3ed9e9f8750d48c3d893d94415e43a56df685701989724d1ff8af439375218f5af743
6
+ metadata.gz: 00ac0ad1897f1d04520f4653aec941e6d9c0dfeb7d13656a4ecb1eff6a3cffe24af0b839447f1324ac11c6eb6c40e00b01e1cba6c3fa86a5d21871576b4bb2d2
7
+ data.tar.gz: fad5645c4a8befb085b4617db5542117bf593f1475cc5a65ccbf29b207e4c5ea9fd938bb503d2fa35e66b652ecc01fae269d68c5c60f2e895055cd8c3b0190b3
data/RELEASE.md CHANGED
@@ -5,6 +5,27 @@ 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.23.2
9
+
10
+ ### What changed
11
+
12
+ - **Fix: onboard audit exception silently swallowed** — `onboard_run_audit!` referenced an undefined variable `e` instead of `exception` in its rescue clause. When `audit!` raised during `carson onboard`, the NameError was caught by the ensure block's return, giving the user no diagnostic. Now correctly captures the exception and reports "Audit skipped."
13
+ - **Fix: `sync_after_merge!` always reported success** — `Open3.capture3` returns a `Process::Status` object which is always truthy. The sync-failure branch was unreachable. Now correctly calls `.success?` on the status object.
14
+ - **Test coverage: Ledger revision recording** — unit tests for `record_revision` (numbering, `finished_at`, counter bump), `revisions_for_delivery` ordering, and behavioural verification that `active_deliveries` returns exactly the states `Delivery` considers active.
15
+ - **Test coverage: govern reconciliation** — tests for PR MERGED (integrated), PR CLOSED (failed), head-advanced (superseded), and no-agent-provider (escalated) state transitions that were previously stubbed out.
16
+ - **Test coverage: deliver error paths** — push failure and PR creation failure tests exercising actual error handling at the adapter boundary.
17
+ - **Housekeeping** — `Ledger::ACTIVE_DELIVERY_STATES` now references `Delivery::ACTIVE_STATES` instead of maintaining a duplicate. Fixed indentation in `upsert_delivery` and `supersede_branch!`. Corrected "deliver/delivers" to "delivery/deliveries" in govern output.
18
+
19
+ ### No migration required
20
+
21
+ ## 3.23.1
22
+
23
+ ### What changed
24
+
25
+ - **Govern no longer depends on cache-report writes** — `carson govern` no longer writes `govern_latest.md/json` under `~/.carson/cache` as part of its normal execution. Govern now advances deliveries directly from the ledger and prints results to stdout/JSON only.
26
+
27
+ ### No migration required
28
+
8
29
  ## 3.23.0
9
30
 
10
31
  ### What changed
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.23.0
1
+ 3.23.2
data/lib/carson/ledger.rb CHANGED
@@ -6,7 +6,7 @@ require "time"
6
6
  module Carson
7
7
  class Ledger
8
8
  UNSET = Object.new
9
- ACTIVE_DELIVERY_STATES = %w[preparing gated queued integrating escalated].freeze
9
+ ACTIVE_DELIVERY_STATES = Delivery::ACTIVE_STATES
10
10
 
11
11
  def initialize( path: )
12
12
  @path = File.expand_path( path )
@@ -75,16 +75,16 @@ module Carson
75
75
  [ repository.path, branch_name, head ]
76
76
  )
77
77
 
78
- if row
79
- database.execute(
80
- <<~SQL,
81
- UPDATE deliveries
82
- SET worktree_path = ?, authority = ?, status = ?, pr_number = ?, pr_url = ?,
83
- cause = ?, summary = ?, updated_at = ?
84
- WHERE id = ?
85
- SQL
86
- [ worktree_path, authority, status, pr_number, pr_url, cause, summary, timestamp, row.fetch( "id" ) ]
87
- )
78
+ if row
79
+ database.execute(
80
+ <<~SQL,
81
+ UPDATE deliveries
82
+ SET worktree_path = ?, authority = ?, status = ?, pr_number = ?, pr_url = ?,
83
+ cause = ?, summary = ?, updated_at = ?
84
+ WHERE id = ?
85
+ SQL
86
+ [ worktree_path, authority, status, pr_number, pr_url, cause, summary, timestamp, row.fetch( "id" ) ]
87
+ )
88
88
  return fetch_delivery( database: database, id: row.fetch( "id" ), repository: repository )
89
89
  end
90
90
 
@@ -283,15 +283,15 @@ module Carson
283
283
  )
284
284
  end
285
285
 
286
- def supersede_branch!( database:, repository:, branch_name:, timestamp: )
287
- database.execute(
288
- <<~SQL,
289
- UPDATE deliveries
290
- SET status = ?, superseded_at = ?, updated_at = ?
291
- WHERE repo_path = ? AND branch_name = ? AND status IN ( #{active_state_placeholders} )
292
- SQL
293
- [ "superseded", timestamp, timestamp, repository.path, branch_name, *ACTIVE_DELIVERY_STATES ]
294
- )
286
+ def supersede_branch!( database:, repository:, branch_name:, timestamp: )
287
+ database.execute(
288
+ <<~SQL,
289
+ UPDATE deliveries
290
+ SET status = ?, superseded_at = ?, updated_at = ?
291
+ WHERE repo_path = ? AND branch_name = ? AND status IN ( #{active_state_placeholders} )
292
+ SQL
293
+ [ "superseded", timestamp, timestamp, repository.path, branch_name, *ACTIVE_DELIVERY_STATES ]
294
+ )
295
295
  end
296
296
 
297
297
  def active_state_placeholders
@@ -311,10 +311,10 @@ module Carson
311
311
  # Syncs main after a successful merge.
312
312
  def sync_after_merge!( remote:, main:, result: )
313
313
  main_root = main_worktree_root
314
- _, pull_stderr, pull_success, = Open3.capture3(
314
+ _, pull_stderr, pull_status, = Open3.capture3(
315
315
  "git", "-C", main_root, "pull", "--ff-only", remote, main
316
316
  )
317
- if pull_success
317
+ if pull_status.success?
318
318
  result[ :synced ] = true
319
319
  puts_verbose "synced #{main} in #{main_root} from #{remote}"
320
320
  else
@@ -2,14 +2,10 @@
2
2
  # Govern reassesses queued/gated deliveries, records revision cycles, and integrates one ready delivery at a time.
3
3
  require "json"
4
4
  require "time"
5
- require "fileutils"
6
5
 
7
6
  module Carson
8
7
  class Runtime
9
8
  module Govern
10
- GOVERN_REPORT_MD = "govern_latest.md".freeze
11
- GOVERN_REPORT_JSON = "govern_latest.json".freeze
12
-
13
9
  # Portfolio-level entry point. Scans governed repos (or the current repo) and advances deliveries.
14
10
  def govern!( dry_run: false, json_output: false, loop_seconds: nil )
15
11
  if loop_seconds
@@ -31,7 +27,6 @@ module Carson
31
27
  repositories: repositories.map { |path| govern_repo!( repo_path: path, dry_run: dry_run ) }
32
28
  }
33
29
 
34
- write_govern_report( report: report )
35
30
  if json_output
36
31
  output.puts JSON.pretty_generate( report )
37
32
  else
@@ -86,7 +81,7 @@ module Carson
86
81
  return repo_report
87
82
  end
88
83
 
89
- puts_line "#{repository.name}: #{deliveries.length} active deliver#{plural_suffix( count: deliveries.length )}"
84
+ puts_line "#{repository.name}: #{deliveries.length} active deliver#{deliveries.length == 1 ? 'y' : 'ies'}"
90
85
 
91
86
  reconciled = deliveries.map { |item| scoped_runtime.send( :reconcile_delivery!, delivery: item ) }
92
87
  next_integration_id = reconciled.find( &:ready? )&.id
@@ -423,51 +418,6 @@ module Carson
423
418
  ""
424
419
  end
425
420
 
426
- def write_govern_report( report: )
427
- report_dir = report_dir_path
428
- FileUtils.mkdir_p( report_dir )
429
- File.write( File.join( report_dir, GOVERN_REPORT_JSON ), JSON.pretty_generate( report ) )
430
- File.write( File.join( report_dir, GOVERN_REPORT_MD ), render_govern_markdown( report: report ) )
431
- end
432
-
433
- def render_govern_markdown( report: )
434
- lines = []
435
- lines << "# Carson Govern Report"
436
- lines << ""
437
- lines << "**Cycle**: #{report[ :cycle_at ]}"
438
- lines << "**Dry run**: #{report[ :dry_run ]}"
439
- lines << ""
440
-
441
- Array( report[ :repositories ] ).each do |repo_report|
442
- lines << "## #{repo_report[ :path ]}"
443
- lines << ""
444
- if repo_report[ :error ]
445
- lines << "**Error**: #{repo_report[ :error ]}"
446
- lines << ""
447
- next
448
- end
449
-
450
- deliveries = Array( repo_report[ :deliveries ] )
451
- if deliveries.empty?
452
- lines << "No active deliveries."
453
- lines << ""
454
- next
455
- end
456
-
457
- deliveries.each do |delivery|
458
- lines << "### #{delivery[ :branch ]}"
459
- lines << ""
460
- lines << "- **Status**: #{delivery[ :status ]}"
461
- lines << "- **Action**: #{delivery[ :action ]}"
462
- lines << "- **Summary**: #{delivery[ :summary ]}" unless delivery[ :summary ].to_s.empty?
463
- lines << "- **Revision count**: #{delivery[ :revision_count ]}"
464
- lines << ""
465
- end
466
- end
467
-
468
- lines.join( "\n" )
469
- end
470
-
471
421
  def print_govern_summary( report: )
472
422
  Array( report[ :repositories ] ).each do |repo_report|
473
423
  if repo_report[ :error ]
@@ -295,7 +295,7 @@ module Carson
295
295
  audit_error = nil
296
296
  audit_status = with_captured_output { audit! }
297
297
  rescue StandardError => exception
298
- audit_error = e
298
+ audit_error = exception
299
299
  audit_status = EXIT_OK
300
300
  ensure
301
301
  return onboard_print_audit_result( status: audit_status, error: audit_error )
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: 3.23.0
4
+ version: 3.23.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hailei Wang