Class: Carson::Courier

Inherits:
Object
  • Object
show all
Defined in:
lib/carson/courier.rb

Overview

The delivery person — picks up parcels and delivers them to the registry.

The courier is a Carson employee assigned to a warehouse. They pick up a parcel, ship it to the bureau, file a waybill, and wait at the registry while the bureaucrats check it.

The courier is a thin orchestrator: it creates a Waybill and sends it messages. The domain logic lives in the objects, not the courier.

The bureau

The bureau is a registry (GitHub) where bureaucrats work. They check parcels (CI, review, mergeability) and either accept them into the registry or hold them with a reason.

Situations the courier encounters

Each numbered situation is handled by a specific guard or branch in the delivery flow. The number appears in the code comment where it’s handled.

01. Parcel on main — cannot deliver from the destination.
02. Parcel behind standard — not based on client's latest standard.
03. Shipping fails — warehouse couldn't push to the bureau.
04. Waybill filing fails — bureau rejected the paperwork.
05. Pending at registry — bureaucrats still checking (CI running).
06. Failed at registry — bureaucrats rejected (CI failed).
07. Review pending — review still in progress.
08. Review changes requested — reviewer wants corrections.
09. Merge conflict — parcel conflicts with registry contents.
10. Behind standard (post-filing) — standard changed since shipping.
11. Policy block — bureau regulation prevents acceptance.
12. Draft waybill — form not finalised.
13. Mergeability pending — bureau still processing eligibility.
14. Acceptance succeeds — parcel enters the registry. Delivered.
15. Acceptance fails — classify why, report.
16. Bureau unreachable — cannot contact the bureau.
17. Parcel already delivered — already in registry.
18. Waybill closed — cancelled by someone externally.

Design: wait and poll at the registry

The courier waits at the registry while the bureaucrats check the parcel. It polls up to MAX_CHECKS_AT_REGISTRY times, pausing between each check. If the bureaucrats give a definitive answer (accepted or rejected), the courier acts immediately. If the checks are exhausted without a definitive answer, the courier reports “filed” — the parcel is still at the registry.

Future: destination modes

Currently remote-centred (ship → waybill → registry → acceptance). A future local-centred mode merges locally; remote is a synced backup. The destination mode should be injectable, not baked in.

Constant Summary collapse

OK =

Exit codes — shared contract between Carson employees and the CLI.

0
ERROR =
1
BLOCKED =
2
MAX_CHECKS_AT_REGISTRY =

The courier checks the registry up to 6 times before leaving.

6

Instance Method Summary collapse

Constructor Details

#initialize(warehouse, ledger: nil, merge_method: "rebase", poll_interval_at_registry: 30) ⇒ Courier

Returns a new instance of Courier.



64
65
66
67
68
69
# File 'lib/carson/courier.rb', line 64

def initialize( warehouse, ledger: nil, merge_method: "rebase", poll_interval_at_registry: 30 )
	@warehouse = warehouse
	@ledger = ledger
	@merge_method = merge_method
	@poll_interval_at_registry = poll_interval_at_registry
end

Instance Method Details

#deliver(parcel, title: nil, body_file: nil, commit_message: nil) ⇒ Object

Deliver a parcel to the registry. Ships it, files a waybill, waits at the registry for the bureaucrats.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/carson/courier.rb', line 73

def deliver( parcel, title: nil, body_file: nil, commit_message: nil )
	result = {
		command: "deliver",
		label: parcel.label,
		remote_main: "#{@warehouse.bureau_address}/#{@warehouse.main_label}"
	}

	# 01. Parcel on main — cannot deliver from the destination.
	if parcel.on_main?( @warehouse.main_label )
		return blocked( result,
			"cannot deliver from #{@warehouse.main_label}",
			recovery: "carson worktree create <name>" )
	end

	# Dirty tree guard — the warehouse knows if its floor is clean.
	if commit_message && @warehouse.clean?
		return blocked( result,
			"working tree is already clean",
			recovery: "carson deliver" )
	end
	if !commit_message && !@warehouse.clean?
		return blocked( result,
			"working tree is dirty",
			recovery: "carson deliver --commit \"describe this delivery\"" )
	end

	# Submit compliance — ensure templates are in sync before delivery.
	compliance = @warehouse.submit_compliance!
	unless compliance[ :compliant ]
		return error( result, compliance[ :error ] || "compliance check failed" )
	end

	# Pack the parcel if the sender provided a commit message.
	# Skip if compliance already committed everything (tree is now clean).
	if commit_message && !@warehouse.clean?
		unless @warehouse.pack!( message: commit_message )
			return error( result, "packing failed — nothing to commit?" )
		end
	end
	# Refresh parcel head — compliance or pack may have created commits.
	parcel = Parcel.new( label: parcel.label, head: @warehouse.current_head, shelf: parcel.shelf )

	# 02. Parcel behind standard — not based on client's latest standard.
	@warehouse.fetch_latest( registry: @warehouse.main_label )
	unless @warehouse.based_on_latest_standard?( parcel )
		remote_main = "#{@warehouse.bureau_address}/#{@warehouse.main_label}"
		return blocked( result,
			"branch is behind #{remote_main}",
			recovery: "git rebase #{remote_main}, then carson deliver" )
	end

	# The courier picks up the parcel — start tracking.
	record( parcel, status: "preparing", summary: "delivery accepted" )

	# 03. Shipping fails — warehouse couldn't push to the bureau.
	unless @warehouse.ship( parcel )
		return error( result, "push failed" )
	end

	# File a waybill with the bureau.
	waybill = Waybill.new(
		label: parcel.label,
		warehouse_path: @warehouse.path
	)
	waybill.file!( title: title, body_file: body_file )

	# 04. Waybill filing fails — bureau rejected the paperwork.
	unless waybill.filed?
		return error( result, "PR creation failed", recovery: "carson deliver" )
	end

	result[ :tracking_number ] = waybill.tracking_number
	result[ :url ] = waybill.url

	# Wait at the registry while the bureaucrats check the parcel.
	wait_and_poll_at_registry( waybill, result )

	# Update the ledger with the final outcome.
	record( parcel, status: result[ :outcome ] || "filed", summary: result[ :hold_reason ] )

	result[ :exit ] ||= OK
	result
end