Run the military extension: from contact to occupation
This tutorial runs the military reference starter. It creates a field force, recruits reinforcements, marches into a defended territory, resolves the battle, and occupies the territory, then checks that a saved and a replayed copy match. You will learn how canwu-military accepts commands, where its randomness comes from, and which state it leaves to other extensions.
The military simulation extension is split into three crates:
canwu-military: military domain records, theMilitaryCommanddomain command, boundary systems, draw evidence, and commander reports, built oncanwu-api.canwu-military-reference-content: two versioned rulesets.canwu-military-reference: a replaceable reference integration that adds a military catalog to the reference world.
Run it
Section titled “Run it”From the repository root:
cargo run -p canwu-military-reference --example military_startermilitary_gameplan=complete riverine=riverine_preindustrial industrial=industrial_front checkpoint=3b647676eb58a48594665edc09d190b25eb7932e4d630496c3f57fc6ddc2b691military_gameplan=complete means every assertion passed: the battle reached a result, the occupation exists, and the restored and replayed copies have the same checkpoint hash as the live run. riverine and industrial print the names of the two reference rulesets.
How a military command runs
Section titled “How a military command runs”View diagram source
flowchart TB
Cmd["military_command()<br/>Command::Plugin"] --> Queue["enqueue_command()<br/>canonical ingress"]
Queue --> Admit["Admission checks<br/>commander, revision,<br/>branch, tactic"]
Admit --> Packet["military_command_v1<br/>ingress packet"]
Packet --> Apply["Phase 7<br/>write records, draw"]
Apply --> Report["Phase 13<br/>report to commanders"]
The starter sends every command through one helper:
fn submit( canwu: &mut Canwu, sequence: u64, issuer: Issuer, command: MilitaryCommand,) -> Result<(), Box<dyn std::error::Error>> { let envelope = CommandEnvelope::new(issuer, military_command(command)?); canwu.enqueue_command( canwu.time(), i32::try_from(sequence)?, CommandRequest::new(CommandRequestId::new(sequence), canwu.revision(), envelope), )?; canwu.advance_canonical(SimDuration::minutes(1))?; Ok(())}Military commands must use canonical ingress (enqueue_command); the direct submit path returns MixedCommandIngress. At admission, the handler checks that the issuer commands the force, that expected_force_revision matches the force record, and that the branch or tactic exists in the active ruleset. It then queues a military_command_v1 packet, which the phase-7 system apply-military-ingress-v1 applies.
Every command carries a MilitaryOperationKey. The same key with different input fails with IdempotencyConflict. Resending the same key with the same input is a no-op only when admission accepts the copy. Once the first copy of a command that carries expected_force_revision has raised the force revision, a resend fails admission as stale (DomainRecordVersionConflict). That fails the boundary and leaves the input in the queue.
What the starter does
Section titled “What the starter does”demo_military_scenario() starts from the reference world’s demo scenario and adds a military catalog that uses the riverine_preindustrial ruleset. The example registers ReferenceWorldPlugin and military_plugin(), then sends five commands:
| Step | Command | Result |
|---|---|---|
| 1 | CreateForce |
General Shen’s field force, 2,000 of 2,500 authorized, at the central node. |
| 2 | Recruit |
A 400-soldier reserve subunit. The force record moves to revision 2. |
| 3 | PlanOperation |
A strategic operation record in the Planned phase. |
| 4 | CreateForce |
A 100-soldier defending force at the eastern node, commanded by Minister Luo. |
| 5 | OrderMarch |
The field force marches on the eastern node with the defender as its opponent. |
The march order, as the starter writes it:
MilitaryCommand::OrderMarch { operation: canwu_military::MilitaryOperationKey::new("canwu.military:op:march-front")?, force: force.clone(), operation_id: OperationId::new("canwu.military:operation:march-front")?, destination: MilitaryNodeId::new(format!( "canwu.military:node:{}", ids.eastern_territory ))?, objective: "secure the eastern route".to_owned(), tactic: "crossing_assault".to_owned(), opposing_force: Some(ForceId::new("canwu.military:force:defender")?), expected_force_revision: 2,}Then canwu.advance_canonical(SimDuration::days(5)) runs five days:
- One minute after the order, the force reaches the destination and contact creates a combat record. The reference plugin models the march as a one-minute step and records a route digest (a hash of force and destination); see Compose movement.
- Each day an engaged operation resolves one combat round, using one draw from
military_random_stream()that is recorded as draw evidence. In this run the 2,400-strong attacker loses 7 soldiers and destroys the 100-strong defender in the first round, so the combat ends inAttackerVictory. - A victory creates an occupation of the destination at stage
MilitaryControl. Daily occupation ticks raise security and administrative reach; when the run ends they stand at 600 and 80 per mille.
Commands and ownership
Section titled “Commands and ownership”MilitaryCommand has 13 variants, including the internal AdvanceTick. canwu-military writes only its own force, operation, combat, occupation, ledger, and provider-outcome records; routes, resources, population, law, and fiscal procedure stay with their own extensions. Military lists what each command checks and writes, the combat and occupation rules, and the special-operation known issue.
Reference rulesets are synthetic
Section titled “Reference rulesets are synthetic”canwu-military-reference-content provides two rulesets, riverine_preindustrial and industrial_front. Both are marked synthetic_reference: they exercise different branch, tactic, terrain, recruitment, combat, and occupation assumptions and make no historical claims. The current plugin reads the active ruleset to validate branch and tactic names; its combat and occupation arithmetic is built into canwu-military.
Compose movement
Section titled “Compose movement”The current canwu-military plugin completes a march one minute after the order and records only a route digest. A host that needs routes and travel time composes canwu-routing with canwu-transport or canwu-movement around the march in its own integration. See Route planning and delivery execution.
Read the full starter source for every command and the snapshot and replay checks.