Skip to content

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, the MilitaryCommand domain command, boundary systems, draw evidence, and commander reports, built on canwu-api.
  • canwu-military-reference-content: two versioned rulesets.
  • canwu-military-reference: a replaceable reference integration that adds a military catalog to the reference world.

From the repository root:

Terminal window
cargo run -p canwu-military-reference --example military_starter
military_gameplan=complete riverine=riverine_preindustrial industrial=industrial_front checkpoint=3b647676eb58a48594665edc09d190b25eb7932e4d630496c3f57fc6ddc2b691

military_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.

A military command is admitted through canonical ingress, applied in phase 7, and reported to commanders in phase 13. View diagram source.
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.

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:

  1. 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.
  2. 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 in AttackerVictory.
  3. 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.

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.

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.

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.