Skip to main content

Canwu

Struct Canwu 

Source
pub struct Canwu { /* private fields */ }
Expand description

Main in-process API. All returned world values are detached snapshots.

Implementations§

Source§

impl Canwu

Source

pub const fn version() -> &'static str

Source

pub fn new(seed: u64, scenario: Scenario) -> Result<Self, CanwuError>

Source

pub fn into_compacted(self) -> Result<CompactedCanwu, CanwuError>

Enters the explicit compact-journal interface without discarding evidence.

Source

pub fn new_with_plugins( seed: u64, scenario: Scenario, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Examples found in repository?
examples/governance_transition.rs (line 555)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn new_with_manifest( seed: u64, scenario: Scenario, run_manifest: RunManifest, ) -> Result<Self, CanwuError>

Source

pub fn new_with_manifest_and_plugins( seed: u64, scenario: Scenario, run_manifest: RunManifest, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Source

pub fn new_with_run_configuration( seed: u64, scenario: Scenario, run_manifest: RunManifest, run_configuration: RunConfiguration, ) -> Result<Self, CanwuError>

Source

pub fn new_with_run_configuration_and_plugins( seed: u64, scenario: Scenario, run_manifest: RunManifest, run_configuration: RunConfiguration, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Source

pub fn demo(seed: u64) -> Result<Self, CanwuError>

Deprecated compatibility scenario. New hosts should use an integration-owned scenario.

Examples found in repository?
examples/phased_boundary.rs (line 150)
149fn main() -> Result<(), CanwuError> {
150    let mut canwu = Canwu::demo(35)?;
151    canwu.register_plugin(&SupplyPlugin)?;
152    canwu.register_plugin(&DemandPlugin)?;
153
154    let receipt = canwu
155        .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;
156    assert_eq!(receipt.allocations[0].granted, 6);
157    assert_eq!(canwu.boundaries()[0].emissions.len(), 2);
158    Ok(())
159}
More examples
Hide additional examples
examples/plugin.rs (line 89)
88fn main() -> Result<(), CanwuError> {
89    let mut canwu = Canwu::demo(35)?;
90    let ids = Canwu::demo_ids();
91    canwu.register_plugin(&StancePlugin)?;
92    canwu.submit(CommandEnvelope::new(
93        Issuer::Actor(ids.commander),
94        Command::Plugin {
95            plugin: "example-stance".to_owned(),
96            command: "set_stance".to_owned(),
97            payload: json!({ "army": ids.army, "stance": "hold" }),
98        },
99    ))?;
100    Ok(())
101}
examples/decision_ticket.rs (line 12)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    let mut canwu = Canwu::demo(1918)?;
13    let ids = Canwu::demo_ids();
14
15    open_aid_request(&mut canwu, ids)?;
16    refresh_aid_options(&mut canwu, ids)?;
17    resolve_aid_request(&mut canwu)?;
18
19    let trace = canwu
20        .decision_trace(canwu_api::DecisionTraceId::new(1))
21        .expect("utility decision trace");
22    println!("{}", serde_json::to_string_pretty(trace)?);
23
24    verify_persistence_and_replay(&canwu)?;
25    println!("snapshot_restore=ok exact_replay=ok");
26    Ok(())
27}
examples/continuous_game_loop.rs (line 182)
181    fn new(verbose: bool) -> Result<Self, CanwuError> {
182        let authority = Canwu::demo(35)?;
183        let ids = Canwu::demo_ids();
184        let presentation = PresentationState::from_world(&authority.world(), ids.army);
185        Ok(Self {
186            authority,
187            ids,
188            presentation,
189            accumulated_sim_nanos: 0,
190            wall_elapsed: Duration::ZERO,
191            frame: 0,
192            reported_events: 0,
193            next_progress_milestone: 25,
194            canonical_boundaries: 0,
195            verbose,
196        })
197    }
examples/uncertainty_resolution.rs (line 147)
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
173
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
Source

pub fn demo_ids() -> DemoIds

IDs for the deprecated compatibility scenario.

Examples found in repository?
examples/plugin.rs (line 90)
88fn main() -> Result<(), CanwuError> {
89    let mut canwu = Canwu::demo(35)?;
90    let ids = Canwu::demo_ids();
91    canwu.register_plugin(&StancePlugin)?;
92    canwu.submit(CommandEnvelope::new(
93        Issuer::Actor(ids.commander),
94        Command::Plugin {
95            plugin: "example-stance".to_owned(),
96            command: "set_stance".to_owned(),
97            payload: json!({ "army": ids.army, "stance": "hold" }),
98        },
99    ))?;
100    Ok(())
101}
More examples
Hide additional examples
examples/decision_ticket.rs (line 13)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    let mut canwu = Canwu::demo(1918)?;
13    let ids = Canwu::demo_ids();
14
15    open_aid_request(&mut canwu, ids)?;
16    refresh_aid_options(&mut canwu, ids)?;
17    resolve_aid_request(&mut canwu)?;
18
19    let trace = canwu
20        .decision_trace(canwu_api::DecisionTraceId::new(1))
21        .expect("utility decision trace");
22    println!("{}", serde_json::to_string_pretty(trace)?);
23
24    verify_persistence_and_replay(&canwu)?;
25    println!("snapshot_restore=ok exact_replay=ok");
26    Ok(())
27}
examples/continuous_game_loop.rs (line 183)
181    fn new(verbose: bool) -> Result<Self, CanwuError> {
182        let authority = Canwu::demo(35)?;
183        let ids = Canwu::demo_ids();
184        let presentation = PresentationState::from_world(&authority.world(), ids.army);
185        Ok(Self {
186            authority,
187            ids,
188            presentation,
189            accumulated_sim_nanos: 0,
190            wall_elapsed: Duration::ZERO,
191            frame: 0,
192            reported_events: 0,
193            next_progress_milestone: 25,
194            canonical_boundaries: 0,
195            verbose,
196        })
197    }
198
199    fn run_phase(&mut self, phase: SpeedPhase, profile: FrameProfile) -> Result<(), CanwuError> {
200        let mut remaining_millis = phase.wall_millis;
201        let mut pattern_index = 0;
202        let mut first_frame = true;
203
204        while remaining_millis > 0 {
205            let frame_millis = profile.frame_millis[pattern_index % profile.frame_millis.len()]
206                .min(remaining_millis);
207            remaining_millis -= frame_millis;
208            self.render_frame(
209                Duration::from_millis(frame_millis),
210                phase.speed,
211                first_frame || remaining_millis == 0,
212            )?;
213            first_frame = false;
214            pattern_index += 1;
215        }
216        Ok(())
217    }
218
219    fn render_frame(
220        &mut self,
221        wall_dt: Duration,
222        speed: GameSpeed,
223        phase_edge: bool,
224    ) -> Result<(), CanwuError> {
225        self.frame += 1;
226        self.wall_elapsed += wall_dt;
227
228        let converted_wall_nanos = wall_dt.as_nanos() * BASE_SIM_SECONDS_PER_WALL_SECOND;
229        self.accumulated_sim_nanos += converted_wall_nanos * speed.multiplier();
230
231        let mut authority_changed = false;
232        while self.accumulated_sim_nanos >= SIMULATION_MINUTE_NANOS {
233            let receipts = self.authority.advance_canonical(SIMULATION_QUANTUM)?;
234            self.canonical_boundaries += receipts.len();
235            authority_changed |= !receipts.is_empty();
236            self.accumulated_sim_nanos -= SIMULATION_MINUTE_NANOS;
237        }
238
239        if authority_changed {
240            self.refresh_presentation_from_authority();
241        }
242
243        let progress = self.presentation.render(self.presentation_time());
244        let crossed_milestone = self.crossed_progress_milestone(progress);
245        let has_new_events = self.reported_events < self.authority.events().len();
246
247        if self.verbose && (phase_edge || crossed_milestone || has_new_events) {
248            self.print_frame(speed);
249        }
250        self.report_new_events();
251        Ok(())
252    }
253
254    /// This host never backdates mid-quantum input. It deterministically rounds
255    /// up to the next representable Canwu minute; another host may choose a
256    /// different policy, but it must apply the same policy across render FPS.
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
303
304    fn refresh_presentation_from_authority(&mut self) {
305        self.presentation
306            .refresh_from_authority(&self.authority.world(), self.ids.army);
307    }
308
309    fn presentation_time(&self) -> PresentationTime {
310        PresentationTime {
311            canonical: self.authority.time(),
312            sub_minute: Duration::from_nanos(
313                u64::try_from(self.accumulated_sim_nanos)
314                    .expect("the sub-minute accumulator must fit a Duration"),
315            ),
316        }
317    }
318
319    fn crossed_progress_milestone(&mut self, progress: Option<f64>) -> bool {
320        let Some(progress) = progress else {
321            return false;
322        };
323        let mut crossed = false;
324        while self.next_progress_milestone <= 100
325            && progress * 100.0 + f64::EPSILON >= f64::from(self.next_progress_milestone)
326        {
327            crossed = true;
328            self.next_progress_milestone += 25;
329        }
330        crossed
331    }
332
333    fn print_frame(&self, speed: GameSpeed) {
334        println!(
335            "frame={:>5} speed={:<6} wall={:>7.3}s visual_time={} canwu_time={} {}",
336            self.frame,
337            speed.label(),
338            self.wall_elapsed.as_secs_f64(),
339            self.presentation_time(),
340            self.authority.time(),
341            self.presentation.describe(),
342        );
343    }
344
345    fn report_new_events(&mut self) {
346        let events = self.authority.events();
347        if self.verbose {
348            for event in &events[self.reported_events..] {
349                println!(
350                    "EVENT: at={} type={} {}",
351                    event.timestamp,
352                    event.kind.qualified_event_type(),
353                    event.summary,
354                );
355            }
356        }
357        self.reported_events = events.len();
358    }
359
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
373}
374
375struct RunOutcome {
376    profile: &'static str,
377    frames: u64,
378    time: SimTime,
379    world: WorldSnapshot,
380    events: Vec<SimEvent>,
381    commands: Vec<CommandRecord>,
382    authoritative_state_hash: String,
383    checkpoint_hash: String,
384    canonical_boundaries: usize,
385}
386
387fn run(profile: FrameProfile, verbose: bool) -> Result<RunOutcome, CanwuError> {
388    let mut host = GameHost::new(verbose)?;
389    if verbose {
390        println!("\n{} render loop", profile.name);
391    }
392
393    host.run_phase(SPEED_SCHEDULE[0], profile)?;
394    host.submit_player_command()?;
395    host.run_phase(SPEED_SCHEDULE[1], profile)?;
396    host.run_phase(SPEED_SCHEDULE[2], profile)?;
397
398    let time_before_pause = host.authority.time();
399    let remainder_before_pause = host.accumulated_sim_nanos;
400    let presentation_before_pause = host.presentation_time();
401    let frames_before_pause = host.frame;
402    assert!(
403        remainder_before_pause > 0,
404        "the pause fixture must preserve a fractional simulation minute"
405    );
406    host.run_phase(SPEED_SCHEDULE[3], profile)?;
407    assert!(
408        host.frame > frames_before_pause,
409        "pause must keep rendering"
410    );
411    assert_eq!(
412        host.authority.time(),
413        time_before_pause,
414        "pause must not advance Canwu"
415    );
416    assert_eq!(
417        host.accumulated_sim_nanos, remainder_before_pause,
418        "pause must not add desired simulation time"
419    );
420    assert_eq!(
421        host.presentation_time(),
422        presentation_before_pause,
423        "pause must freeze presentation time"
424    );
425
426    host.run_phase(SPEED_SCHEDULE[4], profile)?;
427    assert_eq!(
428        host.accumulated_sim_nanos, 0,
429        "the scripted schedule should end on a simulation-minute boundary"
430    );
431    host.outcome(profile)
432}
433
434fn std_duration(duration: SimDuration) -> Duration {
435    let minutes = u64::try_from(duration.as_minutes())
436        .expect("presentation interpolation requires a non-negative duration");
437    Duration::from_secs(
438        minutes
439            .checked_mul(60)
440            .expect("presentation duration must remain representable"),
441    )
442}
443
444fn assert_fps_independent(left: &RunOutcome, right: &RunOutcome) {
445    assert_ne!(
446        left.frames, right.frames,
447        "the render segmentations must differ"
448    );
449    assert_eq!(left.time, right.time, "Canwu time changed with render FPS");
450    assert_eq!(
451        left.world, right.world,
452        "world state changed with render FPS"
453    );
454    assert_eq!(left.events, right.events, "events changed with render FPS");
455    assert_eq!(
456        left.commands, right.commands,
457        "command ordering changed with render FPS"
458    );
459    assert_eq!(
460        left.authoritative_state_hash, right.authoritative_state_hash,
461        "authoritative state hash changed with render FPS"
462    );
463    assert_eq!(
464        left.checkpoint_hash, right.checkpoint_hash,
465        "checkpoint hash changed with render FPS"
466    );
467    assert_eq!(
468        left.canonical_boundaries, right.canonical_boundaries,
469        "canonical boundary count changed with render FPS"
470    );
471}
472
473fn assert_expected_outcome(outcome: &RunOutcome) {
474    let ids = Canwu::demo_ids();
475    let command_at = SimTime::from_minutes(1);
476    let arrival_at = SimTime::from_minutes(1_081);
477    let final_time = SimTime::from_minutes(1_082);
478
479    assert_eq!(outcome.time, final_time, "the scripted final time changed");
480    let army = outcome
481        .world
482        .army(ids.army)
483        .expect("the demo army must remain present");
484    assert_eq!(
485        army.location, ids.eastern_territory,
486        "the scheduled arrival must execute"
487    );
488    assert!(
489        army.transit.is_none(),
490        "the army must no longer be in authoritative transit"
491    );
492
493    assert_eq!(
494        outcome.commands.len(),
495        1,
496        "the move command must be accepted"
497    );
498    let command = &outcome.commands[0];
499    assert_eq!(command.accepted_at, command_at);
500    assert_eq!(command.envelope.expected_time, Some(command_at));
501    assert!(matches!(
502        &command.envelope.command,
503        Command::OrderMovement {
504            subject: EntityRef::Army(army),
505            destination,
506            cargo,
507        } if *army == ids.army
508            && *destination == ids.eastern_territory
509            && cargo.is_empty()
510    ));
511
512    let event_timeline = outcome
513        .events
514        .iter()
515        .map(|event| (event.kind.qualified_event_type(), event.timestamp))
516        .collect::<Vec<_>>();
517    assert_eq!(
518        event_timeline,
519        vec![
520            ("move_ordered".to_owned(), command_at),
521            ("army_arrived".to_owned(), arrival_at),
522            ("knowledge_updated".to_owned(), arrival_at),
523            ("report_dispatched".to_owned(), arrival_at),
524        ],
525        "the expected command and scheduled-arrival event timeline changed"
526    );
527    assert_eq!(
528        outcome.canonical_boundaries, 2,
529        "the command and arrival should settle at two canonical boundaries"
530    );
531}
examples/uncertainty_resolution.rs (line 122)
95fn enqueue_ticket(
96    canwu: &mut Canwu,
97    ticket_id: DecisionTicketId,
98    request_offset: u64,
99    controller: DecisionControllerBinding,
100) -> Result<(), CanwuError> {
101    let now = canwu.time();
102    let controller_id = controller.id.clone();
103    canwu.enqueue_decision(
104        now,
105        0,
106        DecisionIngressRequest::new(
107            DecisionRequestId::new(request_offset),
108            canwu.revision(),
109            DecisionMutation::RegisterController { controller },
110        ),
111    )?;
112    canwu.enqueue_decision(
113        now,
114        0,
115        DecisionIngressRequest::new(
116            DecisionRequestId::new(request_offset + 1),
117            canwu.revision(),
118            DecisionMutation::Open {
119                ticket: DecisionTicketDraft {
120                    id: ticket_id,
121                    definition: "example.law-passage".to_owned(),
122                    decision_maker: EntityRef::Person(Canwu::demo_ids().commander),
123                    assigned_controller: controller_id,
124                    summary: "Will the proposed law pass?".to_owned(),
125                    context: DecisionContext::new(
126                        "example.law-passage.v1",
127                        json!({
128                            "supporting_seats": 72,
129                            "opposing_seats": 28,
130                            "public_pressure": "high"
131                        }),
132                    ),
133                    options: vec![
134                        DecisionOption::new("pass", "Pass the law"),
135                        DecisionOption::new("fail", "Reject the law"),
136                    ],
137                    deadline: None,
138                },
139            },
140        ),
141    )?;
142    canwu.settle_boundary(canwu_api::BoundaryRequest::at(now))?;
143    Ok(())
144}
145
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
173
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
Source

pub const fn time(&self) -> SimTime

Examples found in repository?
examples/phased_boundary.rs (line 155)
149fn main() -> Result<(), CanwuError> {
150    let mut canwu = Canwu::demo(35)?;
151    canwu.register_plugin(&SupplyPlugin)?;
152    canwu.register_plugin(&DemandPlugin)?;
153
154    let receipt = canwu
155        .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;
156    assert_eq!(receipt.allocations[0].granted, 6);
157    assert_eq!(canwu.boundaries()[0].emissions.len(), 2);
158    Ok(())
159}
More examples
Hide additional examples
examples/uncertainty_resolution.rs (line 101)
95fn enqueue_ticket(
96    canwu: &mut Canwu,
97    ticket_id: DecisionTicketId,
98    request_offset: u64,
99    controller: DecisionControllerBinding,
100) -> Result<(), CanwuError> {
101    let now = canwu.time();
102    let controller_id = controller.id.clone();
103    canwu.enqueue_decision(
104        now,
105        0,
106        DecisionIngressRequest::new(
107            DecisionRequestId::new(request_offset),
108            canwu.revision(),
109            DecisionMutation::RegisterController { controller },
110        ),
111    )?;
112    canwu.enqueue_decision(
113        now,
114        0,
115        DecisionIngressRequest::new(
116            DecisionRequestId::new(request_offset + 1),
117            canwu.revision(),
118            DecisionMutation::Open {
119                ticket: DecisionTicketDraft {
120                    id: ticket_id,
121                    definition: "example.law-passage".to_owned(),
122                    decision_maker: EntityRef::Person(Canwu::demo_ids().commander),
123                    assigned_controller: controller_id,
124                    summary: "Will the proposed law pass?".to_owned(),
125                    context: DecisionContext::new(
126                        "example.law-passage.v1",
127                        json!({
128                            "supporting_seats": 72,
129                            "opposing_seats": 28,
130                            "public_pressure": "high"
131                        }),
132                    ),
133                    options: vec![
134                        DecisionOption::new("pass", "Pass the law"),
135                        DecisionOption::new("fail", "Reject the law"),
136                    ],
137                    deadline: None,
138                },
139            },
140        ),
141    )?;
142    canwu.settle_boundary(canwu_api::BoundaryRequest::at(now))?;
143    Ok(())
144}
145
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
173
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
examples/continuous_game_loop.rs (line 260)
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
303
304    fn refresh_presentation_from_authority(&mut self) {
305        self.presentation
306            .refresh_from_authority(&self.authority.world(), self.ids.army);
307    }
308
309    fn presentation_time(&self) -> PresentationTime {
310        PresentationTime {
311            canonical: self.authority.time(),
312            sub_minute: Duration::from_nanos(
313                u64::try_from(self.accumulated_sim_nanos)
314                    .expect("the sub-minute accumulator must fit a Duration"),
315            ),
316        }
317    }
318
319    fn crossed_progress_milestone(&mut self, progress: Option<f64>) -> bool {
320        let Some(progress) = progress else {
321            return false;
322        };
323        let mut crossed = false;
324        while self.next_progress_milestone <= 100
325            && progress * 100.0 + f64::EPSILON >= f64::from(self.next_progress_milestone)
326        {
327            crossed = true;
328            self.next_progress_milestone += 25;
329        }
330        crossed
331    }
332
333    fn print_frame(&self, speed: GameSpeed) {
334        println!(
335            "frame={:>5} speed={:<6} wall={:>7.3}s visual_time={} canwu_time={} {}",
336            self.frame,
337            speed.label(),
338            self.wall_elapsed.as_secs_f64(),
339            self.presentation_time(),
340            self.authority.time(),
341            self.presentation.describe(),
342        );
343    }
344
345    fn report_new_events(&mut self) {
346        let events = self.authority.events();
347        if self.verbose {
348            for event in &events[self.reported_events..] {
349                println!(
350                    "EVENT: at={} type={} {}",
351                    event.timestamp,
352                    event.kind.qualified_event_type(),
353                    event.summary,
354                );
355            }
356        }
357        self.reported_events = events.len();
358    }
359
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
373}
374
375struct RunOutcome {
376    profile: &'static str,
377    frames: u64,
378    time: SimTime,
379    world: WorldSnapshot,
380    events: Vec<SimEvent>,
381    commands: Vec<CommandRecord>,
382    authoritative_state_hash: String,
383    checkpoint_hash: String,
384    canonical_boundaries: usize,
385}
386
387fn run(profile: FrameProfile, verbose: bool) -> Result<RunOutcome, CanwuError> {
388    let mut host = GameHost::new(verbose)?;
389    if verbose {
390        println!("\n{} render loop", profile.name);
391    }
392
393    host.run_phase(SPEED_SCHEDULE[0], profile)?;
394    host.submit_player_command()?;
395    host.run_phase(SPEED_SCHEDULE[1], profile)?;
396    host.run_phase(SPEED_SCHEDULE[2], profile)?;
397
398    let time_before_pause = host.authority.time();
399    let remainder_before_pause = host.accumulated_sim_nanos;
400    let presentation_before_pause = host.presentation_time();
401    let frames_before_pause = host.frame;
402    assert!(
403        remainder_before_pause > 0,
404        "the pause fixture must preserve a fractional simulation minute"
405    );
406    host.run_phase(SPEED_SCHEDULE[3], profile)?;
407    assert!(
408        host.frame > frames_before_pause,
409        "pause must keep rendering"
410    );
411    assert_eq!(
412        host.authority.time(),
413        time_before_pause,
414        "pause must not advance Canwu"
415    );
416    assert_eq!(
417        host.accumulated_sim_nanos, remainder_before_pause,
418        "pause must not add desired simulation time"
419    );
420    assert_eq!(
421        host.presentation_time(),
422        presentation_before_pause,
423        "pause must freeze presentation time"
424    );
425
426    host.run_phase(SPEED_SCHEDULE[4], profile)?;
427    assert_eq!(
428        host.accumulated_sim_nanos, 0,
429        "the scripted schedule should end on a simulation-minute boundary"
430    );
431    host.outcome(profile)
432}
examples/decision_ticket.rs (line 30)
29fn open_aid_request(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
30    let now = canwu.time();
31    let controller = DecisionControllerBinding::new(
32        "warlord-b-ai",
33        DecisionPolicyIdentity::new(DecisionPolicyKind::Utility, "aid-utility", "1"),
34        DecisionAuthority::Actor {
35            actor: ids.commander,
36        },
37    )
38    .with_command_subject(EntityRef::Army(ids.army));
39
40    canwu.enqueue_decision(
41        now,
42        0,
43        DecisionIngressRequest::new(
44            DecisionRequestId::new(1),
45            canwu.revision(),
46            DecisionMutation::RegisterController { controller },
47        ),
48    )?;
49    canwu.enqueue_decision(
50        now,
51        0,
52        DecisionIngressRequest::new(
53            DecisionRequestId::new(2),
54            canwu.revision(),
55            DecisionMutation::Open {
56                ticket: DecisionTicketDraft {
57                    id: DecisionTicketId::new(1),
58                    definition: "beiyang.request-military-aid".to_owned(),
59                    decision_maker: EntityRef::Person(ids.commander),
60                    assigned_controller: "warlord-b-ai".to_owned(),
61                    summary: "Neighboring warlord requests immediate military aid".to_owned(),
62                    context: DecisionContext::new(
63                        "beiyang.aid-request.v1",
64                        json!({
65                            "requester": "warlord-a",
66                            "battle": "ongoing-front",
67                            "common_enemy": true
68                        }),
69                    ),
70                    options: vec![DecisionOption {
71                        action: DecisionAction::None,
72                        utility_inputs: BTreeMap::from([
73                            ("home_defense".to_owned(), 80),
74                            ("alliance".to_owned(), -40),
75                        ]),
76                        ..DecisionOption::new("decline", "Decline aid")
77                    }],
78                    deadline: Some(now + SimDuration::days(2)),
79                },
80            },
81        ),
82    )?;
83    canwu.step_canonical()?.expect("decision intake boundary");
84    Ok(())
85}
86
87fn refresh_aid_options(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
88    canwu.enqueue_decision(
89        canwu.time(),
90        0,
91        DecisionIngressRequest::new(
92            DecisionRequestId::new(3),
93            canwu.revision(),
94            DecisionMutation::ReplaceOptions {
95                ticket_id: DecisionTicketId::new(1),
96                expected_version: 1,
97                context: DecisionContext::new(
98                    "beiyang.aid-request.v1",
99                    json!({
100                        "requester": "warlord-a",
101                        "battle": "ongoing-front",
102                        "common_enemy": true,
103                        "route_confirmed": true
104                    }),
105                ),
106                options: vec![
107                    DecisionOption {
108                        action: DecisionAction::None,
109                        utility_inputs: BTreeMap::from([
110                            ("home_defense".to_owned(), 80),
111                            ("alliance".to_owned(), -40),
112                        ]),
113                        ..DecisionOption::new("decline", "Decline aid")
114                    },
115                    DecisionOption {
116                        action: DecisionAction::Command {
117                            command: serde_json::to_value(Command::OrderMovement {
118                                subject: EntityRef::Army(ids.army),
119                                destination: ids.eastern_territory,
120                                cargo: Vec::new(),
121                            })?,
122                        },
123                        utility_inputs: BTreeMap::from([
124                            ("home_defense".to_owned(), -20),
125                            ("alliance".to_owned(), 90),
126                        ]),
127                        ..DecisionOption::new("send-aid", "Send the neighboring army")
128                    },
129                ],
130            },
131        ),
132    )?;
133    canwu
134        .step_canonical()?
135        .expect("dynamic option refresh boundary");
136    Ok(())
137}
138
139fn resolve_aid_request(canwu: &mut Canwu) -> Result<(), Box<dyn std::error::Error>> {
140    let policy = WeightedUtilityPolicy::new(
141        "aid-utility",
142        "1",
143        UtilityProfile {
144            weights: BTreeMap::from([("alliance".to_owned(), 3), ("home_defense".to_owned(), 1)]),
145        },
146    );
147    let evaluation = canwu.drive_decision(
148        canwu.time(),
149        0,
150        DecisionRequestId::new(4),
151        Some(CommandRequestId::new(1)),
152        DecisionTicketId::new(1),
153        &policy,
154    )?;
155    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
156    canwu
157        .step_canonical()?
158        .expect("decision resolution boundary");
159    Ok(())
160}
Source

pub const fn run_manifest(&self) -> &RunManifest

Source

pub const fn run_configuration(&self) -> &RunConfigurationSnapshot

Source

pub fn revision(&self) -> u64

Returns the persisted authoritative transaction revision.

Accepted commands, persisted expected rejections, and completed settlement boundaries each advance it exactly once. Failed work, exact retries, bare clock movement, queued but unadmitted ingress, and plugin setup do not advance it; combine it with command expected-time guards.

Examples found in repository?
examples/uncertainty_resolution.rs (line 108)
95fn enqueue_ticket(
96    canwu: &mut Canwu,
97    ticket_id: DecisionTicketId,
98    request_offset: u64,
99    controller: DecisionControllerBinding,
100) -> Result<(), CanwuError> {
101    let now = canwu.time();
102    let controller_id = controller.id.clone();
103    canwu.enqueue_decision(
104        now,
105        0,
106        DecisionIngressRequest::new(
107            DecisionRequestId::new(request_offset),
108            canwu.revision(),
109            DecisionMutation::RegisterController { controller },
110        ),
111    )?;
112    canwu.enqueue_decision(
113        now,
114        0,
115        DecisionIngressRequest::new(
116            DecisionRequestId::new(request_offset + 1),
117            canwu.revision(),
118            DecisionMutation::Open {
119                ticket: DecisionTicketDraft {
120                    id: ticket_id,
121                    definition: "example.law-passage".to_owned(),
122                    decision_maker: EntityRef::Person(Canwu::demo_ids().commander),
123                    assigned_controller: controller_id,
124                    summary: "Will the proposed law pass?".to_owned(),
125                    context: DecisionContext::new(
126                        "example.law-passage.v1",
127                        json!({
128                            "supporting_seats": 72,
129                            "opposing_seats": 28,
130                            "public_pressure": "high"
131                        }),
132                    ),
133                    options: vec![
134                        DecisionOption::new("pass", "Pass the law"),
135                        DecisionOption::new("fail", "Reject the law"),
136                    ],
137                    deadline: None,
138                },
139            },
140        ),
141    )?;
142    canwu.settle_boundary(canwu_api::BoundaryRequest::at(now))?;
143    Ok(())
144}
More examples
Hide additional examples
examples/continuous_game_loop.rs (line 278)
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
examples/decision_ticket.rs (line 45)
29fn open_aid_request(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
30    let now = canwu.time();
31    let controller = DecisionControllerBinding::new(
32        "warlord-b-ai",
33        DecisionPolicyIdentity::new(DecisionPolicyKind::Utility, "aid-utility", "1"),
34        DecisionAuthority::Actor {
35            actor: ids.commander,
36        },
37    )
38    .with_command_subject(EntityRef::Army(ids.army));
39
40    canwu.enqueue_decision(
41        now,
42        0,
43        DecisionIngressRequest::new(
44            DecisionRequestId::new(1),
45            canwu.revision(),
46            DecisionMutation::RegisterController { controller },
47        ),
48    )?;
49    canwu.enqueue_decision(
50        now,
51        0,
52        DecisionIngressRequest::new(
53            DecisionRequestId::new(2),
54            canwu.revision(),
55            DecisionMutation::Open {
56                ticket: DecisionTicketDraft {
57                    id: DecisionTicketId::new(1),
58                    definition: "beiyang.request-military-aid".to_owned(),
59                    decision_maker: EntityRef::Person(ids.commander),
60                    assigned_controller: "warlord-b-ai".to_owned(),
61                    summary: "Neighboring warlord requests immediate military aid".to_owned(),
62                    context: DecisionContext::new(
63                        "beiyang.aid-request.v1",
64                        json!({
65                            "requester": "warlord-a",
66                            "battle": "ongoing-front",
67                            "common_enemy": true
68                        }),
69                    ),
70                    options: vec![DecisionOption {
71                        action: DecisionAction::None,
72                        utility_inputs: BTreeMap::from([
73                            ("home_defense".to_owned(), 80),
74                            ("alliance".to_owned(), -40),
75                        ]),
76                        ..DecisionOption::new("decline", "Decline aid")
77                    }],
78                    deadline: Some(now + SimDuration::days(2)),
79                },
80            },
81        ),
82    )?;
83    canwu.step_canonical()?.expect("decision intake boundary");
84    Ok(())
85}
86
87fn refresh_aid_options(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
88    canwu.enqueue_decision(
89        canwu.time(),
90        0,
91        DecisionIngressRequest::new(
92            DecisionRequestId::new(3),
93            canwu.revision(),
94            DecisionMutation::ReplaceOptions {
95                ticket_id: DecisionTicketId::new(1),
96                expected_version: 1,
97                context: DecisionContext::new(
98                    "beiyang.aid-request.v1",
99                    json!({
100                        "requester": "warlord-a",
101                        "battle": "ongoing-front",
102                        "common_enemy": true,
103                        "route_confirmed": true
104                    }),
105                ),
106                options: vec![
107                    DecisionOption {
108                        action: DecisionAction::None,
109                        utility_inputs: BTreeMap::from([
110                            ("home_defense".to_owned(), 80),
111                            ("alliance".to_owned(), -40),
112                        ]),
113                        ..DecisionOption::new("decline", "Decline aid")
114                    },
115                    DecisionOption {
116                        action: DecisionAction::Command {
117                            command: serde_json::to_value(Command::OrderMovement {
118                                subject: EntityRef::Army(ids.army),
119                                destination: ids.eastern_territory,
120                                cargo: Vec::new(),
121                            })?,
122                        },
123                        utility_inputs: BTreeMap::from([
124                            ("home_defense".to_owned(), -20),
125                            ("alliance".to_owned(), 90),
126                        ]),
127                        ..DecisionOption::new("send-aid", "Send the neighboring army")
128                    },
129                ],
130            },
131        ),
132    )?;
133    canwu
134        .step_canonical()?
135        .expect("dynamic option refresh boundary");
136    Ok(())
137}
Source

pub fn run_manifest_hash(&self) -> &str

Source

pub fn checkpoint_hash(&self) -> &str

Examples found in repository?
examples/continuous_game_loop.rs (line 369)
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
Source

pub fn authoritative_state_hash(&self) -> Result<String, CanwuError>

Examples found in repository?
examples/continuous_game_loop.rs (line 368)
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
Source

pub fn entities(&self) -> impl Iterator<Item = &EntityRef>

Source

pub fn entity_exists(&self, entity: &EntityRef) -> bool

Source

pub fn world(&self) -> WorldSnapshot

Deprecated detached format-5 compatibility projection.

Examples found in repository?
examples/continuous_game_loop.rs (line 184)
181    fn new(verbose: bool) -> Result<Self, CanwuError> {
182        let authority = Canwu::demo(35)?;
183        let ids = Canwu::demo_ids();
184        let presentation = PresentationState::from_world(&authority.world(), ids.army);
185        Ok(Self {
186            authority,
187            ids,
188            presentation,
189            accumulated_sim_nanos: 0,
190            wall_elapsed: Duration::ZERO,
191            frame: 0,
192            reported_events: 0,
193            next_progress_milestone: 25,
194            canonical_boundaries: 0,
195            verbose,
196        })
197    }
198
199    fn run_phase(&mut self, phase: SpeedPhase, profile: FrameProfile) -> Result<(), CanwuError> {
200        let mut remaining_millis = phase.wall_millis;
201        let mut pattern_index = 0;
202        let mut first_frame = true;
203
204        while remaining_millis > 0 {
205            let frame_millis = profile.frame_millis[pattern_index % profile.frame_millis.len()]
206                .min(remaining_millis);
207            remaining_millis -= frame_millis;
208            self.render_frame(
209                Duration::from_millis(frame_millis),
210                phase.speed,
211                first_frame || remaining_millis == 0,
212            )?;
213            first_frame = false;
214            pattern_index += 1;
215        }
216        Ok(())
217    }
218
219    fn render_frame(
220        &mut self,
221        wall_dt: Duration,
222        speed: GameSpeed,
223        phase_edge: bool,
224    ) -> Result<(), CanwuError> {
225        self.frame += 1;
226        self.wall_elapsed += wall_dt;
227
228        let converted_wall_nanos = wall_dt.as_nanos() * BASE_SIM_SECONDS_PER_WALL_SECOND;
229        self.accumulated_sim_nanos += converted_wall_nanos * speed.multiplier();
230
231        let mut authority_changed = false;
232        while self.accumulated_sim_nanos >= SIMULATION_MINUTE_NANOS {
233            let receipts = self.authority.advance_canonical(SIMULATION_QUANTUM)?;
234            self.canonical_boundaries += receipts.len();
235            authority_changed |= !receipts.is_empty();
236            self.accumulated_sim_nanos -= SIMULATION_MINUTE_NANOS;
237        }
238
239        if authority_changed {
240            self.refresh_presentation_from_authority();
241        }
242
243        let progress = self.presentation.render(self.presentation_time());
244        let crossed_milestone = self.crossed_progress_milestone(progress);
245        let has_new_events = self.reported_events < self.authority.events().len();
246
247        if self.verbose && (phase_edge || crossed_milestone || has_new_events) {
248            self.print_frame(speed);
249        }
250        self.report_new_events();
251        Ok(())
252    }
253
254    /// This host never backdates mid-quantum input. It deterministically rounds
255    /// up to the next representable Canwu minute; another host may choose a
256    /// different policy, but it must apply the same policy across render FPS.
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
303
304    fn refresh_presentation_from_authority(&mut self) {
305        self.presentation
306            .refresh_from_authority(&self.authority.world(), self.ids.army);
307    }
308
309    fn presentation_time(&self) -> PresentationTime {
310        PresentationTime {
311            canonical: self.authority.time(),
312            sub_minute: Duration::from_nanos(
313                u64::try_from(self.accumulated_sim_nanos)
314                    .expect("the sub-minute accumulator must fit a Duration"),
315            ),
316        }
317    }
318
319    fn crossed_progress_milestone(&mut self, progress: Option<f64>) -> bool {
320        let Some(progress) = progress else {
321            return false;
322        };
323        let mut crossed = false;
324        while self.next_progress_milestone <= 100
325            && progress * 100.0 + f64::EPSILON >= f64::from(self.next_progress_milestone)
326        {
327            crossed = true;
328            self.next_progress_milestone += 25;
329        }
330        crossed
331    }
332
333    fn print_frame(&self, speed: GameSpeed) {
334        println!(
335            "frame={:>5} speed={:<6} wall={:>7.3}s visual_time={} canwu_time={} {}",
336            self.frame,
337            speed.label(),
338            self.wall_elapsed.as_secs_f64(),
339            self.presentation_time(),
340            self.authority.time(),
341            self.presentation.describe(),
342        );
343    }
344
345    fn report_new_events(&mut self) {
346        let events = self.authority.events();
347        if self.verbose {
348            for event in &events[self.reported_events..] {
349                println!(
350                    "EVENT: at={} type={} {}",
351                    event.timestamp,
352                    event.kind.qualified_event_type(),
353                    event.summary,
354                );
355            }
356        }
357        self.reported_events = events.len();
358    }
359
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
Source

pub fn knowledge(&self) -> &KnowledgeSnapshot

Trusted host/admin access to the complete knowledge snapshot.

Do not expose this public API to player, agent, observer, or remote clients; use Canwu::viewer or Canwu::viewer_for_actor instead.

Source

pub fn events(&self) -> &[SimEvent]

Examples found in repository?
examples/continuous_game_loop.rs (line 245)
219    fn render_frame(
220        &mut self,
221        wall_dt: Duration,
222        speed: GameSpeed,
223        phase_edge: bool,
224    ) -> Result<(), CanwuError> {
225        self.frame += 1;
226        self.wall_elapsed += wall_dt;
227
228        let converted_wall_nanos = wall_dt.as_nanos() * BASE_SIM_SECONDS_PER_WALL_SECOND;
229        self.accumulated_sim_nanos += converted_wall_nanos * speed.multiplier();
230
231        let mut authority_changed = false;
232        while self.accumulated_sim_nanos >= SIMULATION_MINUTE_NANOS {
233            let receipts = self.authority.advance_canonical(SIMULATION_QUANTUM)?;
234            self.canonical_boundaries += receipts.len();
235            authority_changed |= !receipts.is_empty();
236            self.accumulated_sim_nanos -= SIMULATION_MINUTE_NANOS;
237        }
238
239        if authority_changed {
240            self.refresh_presentation_from_authority();
241        }
242
243        let progress = self.presentation.render(self.presentation_time());
244        let crossed_milestone = self.crossed_progress_milestone(progress);
245        let has_new_events = self.reported_events < self.authority.events().len();
246
247        if self.verbose && (phase_edge || crossed_milestone || has_new_events) {
248            self.print_frame(speed);
249        }
250        self.report_new_events();
251        Ok(())
252    }
253
254    /// This host never backdates mid-quantum input. It deterministically rounds
255    /// up to the next representable Canwu minute; another host may choose a
256    /// different policy, but it must apply the same policy across render FPS.
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
303
304    fn refresh_presentation_from_authority(&mut self) {
305        self.presentation
306            .refresh_from_authority(&self.authority.world(), self.ids.army);
307    }
308
309    fn presentation_time(&self) -> PresentationTime {
310        PresentationTime {
311            canonical: self.authority.time(),
312            sub_minute: Duration::from_nanos(
313                u64::try_from(self.accumulated_sim_nanos)
314                    .expect("the sub-minute accumulator must fit a Duration"),
315            ),
316        }
317    }
318
319    fn crossed_progress_milestone(&mut self, progress: Option<f64>) -> bool {
320        let Some(progress) = progress else {
321            return false;
322        };
323        let mut crossed = false;
324        while self.next_progress_milestone <= 100
325            && progress * 100.0 + f64::EPSILON >= f64::from(self.next_progress_milestone)
326        {
327            crossed = true;
328            self.next_progress_milestone += 25;
329        }
330        crossed
331    }
332
333    fn print_frame(&self, speed: GameSpeed) {
334        println!(
335            "frame={:>5} speed={:<6} wall={:>7.3}s visual_time={} canwu_time={} {}",
336            self.frame,
337            speed.label(),
338            self.wall_elapsed.as_secs_f64(),
339            self.presentation_time(),
340            self.authority.time(),
341            self.presentation.describe(),
342        );
343    }
344
345    fn report_new_events(&mut self) {
346        let events = self.authority.events();
347        if self.verbose {
348            for event in &events[self.reported_events..] {
349                println!(
350                    "EVENT: at={} type={} {}",
351                    event.timestamp,
352                    event.kind.qualified_event_type(),
353                    event.summary,
354                );
355            }
356        }
357        self.reported_events = events.len();
358    }
359
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
Source

pub fn commands(&self) -> &[CommandRecord]

Examples found in repository?
examples/continuous_game_loop.rs (line 367)
360    fn outcome(self, profile: FrameProfile) -> Result<RunOutcome, CanwuError> {
361        Ok(RunOutcome {
362            profile: profile.name,
363            frames: self.frame,
364            time: self.authority.time(),
365            world: self.authority.world(),
366            events: self.authority.events().to_vec(),
367            commands: self.authority.commands().to_vec(),
368            authoritative_state_hash: self.authority.authoritative_state_hash()?,
369            checkpoint_hash: self.authority.checkpoint_hash().to_owned(),
370            canonical_boundaries: self.canonical_boundaries,
371        })
372    }
Source

pub fn boundaries(&self) -> &[BoundaryRecord]

Examples found in repository?
examples/phased_boundary.rs (line 157)
149fn main() -> Result<(), CanwuError> {
150    let mut canwu = Canwu::demo(35)?;
151    canwu.register_plugin(&SupplyPlugin)?;
152    canwu.register_plugin(&DemandPlugin)?;
153
154    let receipt = canwu
155        .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;
156    assert_eq!(receipt.allocations[0].granted, 6);
157    assert_eq!(canwu.boundaries()[0].emissions.len(), 2);
158    Ok(())
159}
Source

pub fn command_attempts(&self) -> &[CommandAttemptRecord]

Source

pub fn ingress_log(&self) -> &[IngressRecord]

Source

pub fn domain_record( &self, reference: &DomainRecordRef, ) -> Option<&DomainRecord>

Source

pub fn domain_record_version_evidence_exists( &self, reference: &DomainRecordVersionRef, ) -> bool

Returns whether an exact domain-record version exists in current or retained evidence.

Source

pub fn evidence_exists(&self, reference: &EvidenceRef) -> bool

Returns whether a generic evidence identity is retained or archived.

Source

pub fn evidence_time(&self, reference: &EvidenceRef) -> Option<SimTime>

Returns when retained evidence first became authoritative.

Compacted identity-only receipts return None; load the archived evidence body before making decisions that require temporal ordering.

Source

pub fn domain_record_version( &self, reference: &DomainRecordVersionRef, ) -> Option<DomainRecord>

Resolves the retained record body for one exact domain-record version.

A compacted archive receipt proves existence but does not expose the version body through this trusted-host query.

Source

pub fn current_domain_record_version( &self, reference: &DomainRecordRef, ) -> Result<Option<DomainRecordVersionRef>, CanwuError>

Returns the retained or archive-resolvable exact identity for the authoritative current record version. Missing provenance fails closed.

Source

pub fn typed_domain_record<T: DomainRecordType>( &self, reference: &TypedDomainRecordRef<T>, ) -> Option<&DomainRecord>

Examples found in repository?
examples/governance_transition.rs (line 566)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn domain_records(&self) -> impl Iterator<Item = &DomainRecord>

Source

pub fn domain_record_page( &self, kind: &DomainRecordKind, after: Option<&DomainRecordRef>, limit: usize, expected_revision: Option<u64>, ) -> Result<DomainRecordPage, CanwuError>

Returns one trusted-host page bound to an authoritative revision.

Use the returned revision as expected_revision on subsequent pages.

Source

pub fn decision_ticket(&self, id: DecisionTicketId) -> Option<&DecisionTicket>

Examples found in repository?
examples/uncertainty_resolution.rs (line 198)
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
Source

pub fn decision_controller( &self, id: &str, ) -> Option<&DecisionControllerBinding>

Source

pub fn decision_trace(&self, id: DecisionTraceId) -> Option<&DecisionTrace>

Examples found in repository?
examples/decision_ticket.rs (line 20)
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12    let mut canwu = Canwu::demo(1918)?;
13    let ids = Canwu::demo_ids();
14
15    open_aid_request(&mut canwu, ids)?;
16    refresh_aid_options(&mut canwu, ids)?;
17    resolve_aid_request(&mut canwu)?;
18
19    let trace = canwu
20        .decision_trace(canwu_api::DecisionTraceId::new(1))
21        .expect("utility decision trace");
22    println!("{}", serde_json::to_string_pretty(trace)?);
23
24    verify_persistence_and_replay(&canwu)?;
25    println!("snapshot_restore=ok exact_replay=ok");
26    Ok(())
27}
More examples
Hide additional examples
examples/uncertainty_resolution.rs (line 168)
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
Source

pub fn decision_attempt( &self, id: DecisionRequestId, ) -> Option<&DecisionAttemptRecord>

Source

pub fn decision_hot_state(&self) -> DecisionHotState

Source

pub fn decision_history_location( &self, key: &DecisionHistoryKey, ) -> DecisionHistoryLocation

Source

pub fn decision_history_location_with_provider( &self, key: &DecisionHistoryKey, provider: &dyn DecisionArchiveProvider, ) -> Result<DecisionHistoryLocation, CanwuError>

Source

pub fn random_draws(&self) -> &[RandomDrawRecord]

Source

pub fn boundary_head_hash(&self) -> Option<&str>

Source

pub const fn schema(&self) -> &SchemaRegistry

Source

pub fn plugin_descriptors(&self) -> impl Iterator<Item = &PluginDescriptor>

Source

pub fn replay_journal(&self) -> ReplayJournal

Examples found in repository?
examples/decision_ticket.rs (line 180)
176fn verify_journal_replay(
177    canwu: &Canwu,
178    snapshot: &canwu_api::SimulationSnapshot,
179) -> Result<(), Box<dyn std::error::Error>> {
180    let replayed = Canwu::replay_from_journal(&[], &canwu.replay_journal())?;
181    assert_eq!(replayed.snapshot(), *snapshot);
182    Ok(())
183}
More examples
Hide additional examples
examples/governance_transition.rs (line 577)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn outbox_entries(&self) -> Result<Vec<OutboxEntry>, CanwuError>

Source

pub fn evidence_cursor(&self) -> Result<EvidenceCursor, CanwuError>

Source

pub fn checkpoint(&self) -> Result<SimulationCheckpoint, CanwuError>

Source

pub fn archive_reachability_manifest( &self, retained_checkpoints: &[SimulationCheckpoint], page_retention: &StatePageRetentionLedger, decision_provider: &dyn DecisionArchiveProvider, plugin_provider: &dyn PluginArchiveObjectProvider, ) -> Result<ArchiveReachabilityManifest, CanwuError>

Source

pub fn journal_segment_since( &self, start: EvidenceCursor, ) -> Result<EvidenceJournalSegment, CanwuError>

Source

pub fn checkpoint_journal(&self) -> Result<CheckpointJournal, CanwuError>

Source

pub fn checkpoint_journal_json(&self) -> Result<String, CanwuError>

Source

pub fn register_plugin<P: SimulationPlugin + ?Sized>( &mut self, plugin: &P, ) -> Result<(), CanwuError>

Examples found in repository?
examples/phased_boundary.rs (line 151)
149fn main() -> Result<(), CanwuError> {
150    let mut canwu = Canwu::demo(35)?;
151    canwu.register_plugin(&SupplyPlugin)?;
152    canwu.register_plugin(&DemandPlugin)?;
153
154    let receipt = canwu
155        .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;
156    assert_eq!(receipt.allocations[0].granted, 6);
157    assert_eq!(canwu.boundaries()[0].emissions.len(), 2);
158    Ok(())
159}
More examples
Hide additional examples
examples/plugin.rs (line 91)
88fn main() -> Result<(), CanwuError> {
89    let mut canwu = Canwu::demo(35)?;
90    let ids = Canwu::demo_ids();
91    canwu.register_plugin(&StancePlugin)?;
92    canwu.submit(CommandEnvelope::new(
93        Issuer::Actor(ids.commander),
94        Command::Plugin {
95            plugin: "example-stance".to_owned(),
96            command: "set_stance".to_owned(),
97            payload: json!({ "army": ids.army, "stance": "hold" }),
98        },
99    ))?;
100    Ok(())
101}
examples/uncertainty_resolution.rs (line 148)
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
Source

pub fn set_plugin_archive_object_provider( &mut self, provider: Rc<dyn PluginArchiveObjectProvider>, )

Attaches caller-owned package archive storage for authenticated cold history resolution during normal admissions, settlement, and queries.

Source

pub fn plugin_archive_object( &self, namespace: &str, object_id: &str, ) -> Result<Option<Vec<u8>>, CanwuError>

Loads one opaque package archive object from the attached provider. Package integrations authenticate the bytes against their committed archive roots before use.

Source

pub fn submit( &mut self, command: CommandEnvelope, ) -> Result<CommandReceipt, CanwuError>

Examples found in repository?
examples/plugin.rs (lines 92-99)
88fn main() -> Result<(), CanwuError> {
89    let mut canwu = Canwu::demo(35)?;
90    let ids = Canwu::demo_ids();
91    canwu.register_plugin(&StancePlugin)?;
92    canwu.submit(CommandEnvelope::new(
93        Issuer::Actor(ids.commander),
94        Command::Plugin {
95            plugin: "example-stance".to_owned(),
96            command: "set_stance".to_owned(),
97            payload: json!({ "army": ids.army, "stance": "hold" }),
98        },
99    ))?;
100    Ok(())
101}
Source

pub fn process_command( &mut self, request: CommandRequest, ) -> Result<CommandOutcome, CanwuError>

Source

pub fn enqueue_command( &mut self, due_at: SimTime, priority: i32, request: CommandRequest, ) -> Result<IngressReceipt, CanwuError>

Examples found in repository?
examples/continuous_game_loop.rs (line 281)
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
Source

pub fn enqueue_plugin_ingress( &mut self, request: PluginIngressRequest, ) -> Result<IngressReceipt, CanwuError>

Examples found in repository?
examples/governance_transition.rs (lines 557-562)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn enqueue_permitted_plugin_ingress( &mut self, request: PluginIngressRequest, permit: &PluginIngressPermit, ) -> Result<IngressReceipt, CanwuError>

Source

pub fn prepare_decision( &self, decision_request_id: DecisionRequestId, command_request_id: Option<CommandRequestId>, ticket_id: DecisionTicketId, policy: &dyn DecisionPolicy, ) -> Result<DecisionEvaluation, CanwuError>

Source

pub fn prepare_decision_at( &self, due_at: SimTime, decision_request_id: DecisionRequestId, command_request_id: Option<CommandRequestId>, ticket_id: DecisionTicketId, policy: &dyn DecisionPolicy, ) -> Result<DecisionEvaluation, CanwuError>

Source

pub fn enqueue_decision( &mut self, due_at: SimTime, priority: i32, request: DecisionIngressRequest, ) -> Result<IngressReceipt, CanwuError>

Examples found in repository?
examples/uncertainty_resolution.rs (lines 103-111)
95fn enqueue_ticket(
96    canwu: &mut Canwu,
97    ticket_id: DecisionTicketId,
98    request_offset: u64,
99    controller: DecisionControllerBinding,
100) -> Result<(), CanwuError> {
101    let now = canwu.time();
102    let controller_id = controller.id.clone();
103    canwu.enqueue_decision(
104        now,
105        0,
106        DecisionIngressRequest::new(
107            DecisionRequestId::new(request_offset),
108            canwu.revision(),
109            DecisionMutation::RegisterController { controller },
110        ),
111    )?;
112    canwu.enqueue_decision(
113        now,
114        0,
115        DecisionIngressRequest::new(
116            DecisionRequestId::new(request_offset + 1),
117            canwu.revision(),
118            DecisionMutation::Open {
119                ticket: DecisionTicketDraft {
120                    id: ticket_id,
121                    definition: "example.law-passage".to_owned(),
122                    decision_maker: EntityRef::Person(Canwu::demo_ids().commander),
123                    assigned_controller: controller_id,
124                    summary: "Will the proposed law pass?".to_owned(),
125                    context: DecisionContext::new(
126                        "example.law-passage.v1",
127                        json!({
128                            "supporting_seats": 72,
129                            "opposing_seats": 28,
130                            "public_pressure": "high"
131                        }),
132                    ),
133                    options: vec![
134                        DecisionOption::new("pass", "Pass the law"),
135                        DecisionOption::new("fail", "Reject the law"),
136                    ],
137                    deadline: None,
138                },
139            },
140        ),
141    )?;
142    canwu.settle_boundary(canwu_api::BoundaryRequest::at(now))?;
143    Ok(())
144}
More examples
Hide additional examples
examples/decision_ticket.rs (lines 40-48)
29fn open_aid_request(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
30    let now = canwu.time();
31    let controller = DecisionControllerBinding::new(
32        "warlord-b-ai",
33        DecisionPolicyIdentity::new(DecisionPolicyKind::Utility, "aid-utility", "1"),
34        DecisionAuthority::Actor {
35            actor: ids.commander,
36        },
37    )
38    .with_command_subject(EntityRef::Army(ids.army));
39
40    canwu.enqueue_decision(
41        now,
42        0,
43        DecisionIngressRequest::new(
44            DecisionRequestId::new(1),
45            canwu.revision(),
46            DecisionMutation::RegisterController { controller },
47        ),
48    )?;
49    canwu.enqueue_decision(
50        now,
51        0,
52        DecisionIngressRequest::new(
53            DecisionRequestId::new(2),
54            canwu.revision(),
55            DecisionMutation::Open {
56                ticket: DecisionTicketDraft {
57                    id: DecisionTicketId::new(1),
58                    definition: "beiyang.request-military-aid".to_owned(),
59                    decision_maker: EntityRef::Person(ids.commander),
60                    assigned_controller: "warlord-b-ai".to_owned(),
61                    summary: "Neighboring warlord requests immediate military aid".to_owned(),
62                    context: DecisionContext::new(
63                        "beiyang.aid-request.v1",
64                        json!({
65                            "requester": "warlord-a",
66                            "battle": "ongoing-front",
67                            "common_enemy": true
68                        }),
69                    ),
70                    options: vec![DecisionOption {
71                        action: DecisionAction::None,
72                        utility_inputs: BTreeMap::from([
73                            ("home_defense".to_owned(), 80),
74                            ("alliance".to_owned(), -40),
75                        ]),
76                        ..DecisionOption::new("decline", "Decline aid")
77                    }],
78                    deadline: Some(now + SimDuration::days(2)),
79                },
80            },
81        ),
82    )?;
83    canwu.step_canonical()?.expect("decision intake boundary");
84    Ok(())
85}
86
87fn refresh_aid_options(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
88    canwu.enqueue_decision(
89        canwu.time(),
90        0,
91        DecisionIngressRequest::new(
92            DecisionRequestId::new(3),
93            canwu.revision(),
94            DecisionMutation::ReplaceOptions {
95                ticket_id: DecisionTicketId::new(1),
96                expected_version: 1,
97                context: DecisionContext::new(
98                    "beiyang.aid-request.v1",
99                    json!({
100                        "requester": "warlord-a",
101                        "battle": "ongoing-front",
102                        "common_enemy": true,
103                        "route_confirmed": true
104                    }),
105                ),
106                options: vec![
107                    DecisionOption {
108                        action: DecisionAction::None,
109                        utility_inputs: BTreeMap::from([
110                            ("home_defense".to_owned(), 80),
111                            ("alliance".to_owned(), -40),
112                        ]),
113                        ..DecisionOption::new("decline", "Decline aid")
114                    },
115                    DecisionOption {
116                        action: DecisionAction::Command {
117                            command: serde_json::to_value(Command::OrderMovement {
118                                subject: EntityRef::Army(ids.army),
119                                destination: ids.eastern_territory,
120                                cargo: Vec::new(),
121                            })?,
122                        },
123                        utility_inputs: BTreeMap::from([
124                            ("home_defense".to_owned(), -20),
125                            ("alliance".to_owned(), 90),
126                        ]),
127                        ..DecisionOption::new("send-aid", "Send the neighboring army")
128                    },
129                ],
130            },
131        ),
132    )?;
133    canwu
134        .step_canonical()?
135        .expect("dynamic option refresh boundary");
136    Ok(())
137}
Source

pub fn drive_decision( &mut self, due_at: SimTime, priority: i32, decision_request_id: DecisionRequestId, command_request_id: Option<CommandRequestId>, ticket_id: DecisionTicketId, policy: &dyn DecisionPolicy, ) -> Result<DecisionEvaluation, CanwuError>

Examples found in repository?
examples/decision_ticket.rs (lines 147-154)
139fn resolve_aid_request(canwu: &mut Canwu) -> Result<(), Box<dyn std::error::Error>> {
140    let policy = WeightedUtilityPolicy::new(
141        "aid-utility",
142        "1",
143        UtilityProfile {
144            weights: BTreeMap::from([("alliance".to_owned(), 3), ("home_defense".to_owned(), 1)]),
145        },
146    );
147    let evaluation = canwu.drive_decision(
148        canwu.time(),
149        0,
150        DecisionRequestId::new(4),
151        Some(CommandRequestId::new(1)),
152        DecisionTicketId::new(1),
153        &policy,
154    )?;
155    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
156    canwu
157        .step_canonical()?
158        .expect("decision resolution boundary");
159    Ok(())
160}
More examples
Hide additional examples
examples/uncertainty_resolution.rs (lines 215-222)
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
Source

pub fn schedule_calendar_boundary( &mut self, due_at: SimTime, cadences: Vec<SystemCadence>, ) -> Result<IngressReceipt, CanwuError>

Examples found in repository?
examples/uncertainty_resolution.rs (line 162)
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
Source

pub fn advance_canonical( &mut self, duration: SimDuration, ) -> Result<Vec<BoundaryReceipt>, CanwuError>

Examples found in repository?
examples/continuous_game_loop.rs (line 233)
219    fn render_frame(
220        &mut self,
221        wall_dt: Duration,
222        speed: GameSpeed,
223        phase_edge: bool,
224    ) -> Result<(), CanwuError> {
225        self.frame += 1;
226        self.wall_elapsed += wall_dt;
227
228        let converted_wall_nanos = wall_dt.as_nanos() * BASE_SIM_SECONDS_PER_WALL_SECOND;
229        self.accumulated_sim_nanos += converted_wall_nanos * speed.multiplier();
230
231        let mut authority_changed = false;
232        while self.accumulated_sim_nanos >= SIMULATION_MINUTE_NANOS {
233            let receipts = self.authority.advance_canonical(SIMULATION_QUANTUM)?;
234            self.canonical_boundaries += receipts.len();
235            authority_changed |= !receipts.is_empty();
236            self.accumulated_sim_nanos -= SIMULATION_MINUTE_NANOS;
237        }
238
239        if authority_changed {
240            self.refresh_presentation_from_authority();
241        }
242
243        let progress = self.presentation.render(self.presentation_time());
244        let crossed_milestone = self.crossed_progress_milestone(progress);
245        let has_new_events = self.reported_events < self.authority.events().len();
246
247        if self.verbose && (phase_edge || crossed_milestone || has_new_events) {
248            self.print_frame(speed);
249        }
250        self.report_new_events();
251        Ok(())
252    }
253
254    /// This host never backdates mid-quantum input. It deterministically rounds
255    /// up to the next representable Canwu minute; another host may choose a
256    /// different policy, but it must apply the same policy across render FPS.
257    fn submit_player_command(&mut self) -> Result<(), CanwuError> {
258        let captured_at = self.presentation_time();
259        let due_at = if self.accumulated_sim_nanos == 0 {
260            self.authority.time()
261        } else {
262            self.authority
263                .time()
264                .checked_add(SIMULATION_QUANTUM)
265                .expect("the scripted command time must remain representable")
266        };
267        let envelope = CommandEnvelope::new(
268            Issuer::Actor(self.ids.commander),
269            Command::OrderMovement {
270                subject: EntityRef::Army(self.ids.army),
271                destination: self.ids.eastern_territory,
272                cargo: Vec::new(),
273            },
274        )
275        .at_time(due_at);
276        let request = CommandRequest::new(
277            CommandRequestId::new(1),
278            self.authority.revision(),
279            envelope,
280        );
281        let ingress = self.authority.enqueue_command(due_at, 0, request)?;
282
283        if self.verbose {
284            println!(
285                "INPUT: queued commander move as {:?}, captured_at={captured_at}, quantized_due_at={due_at}",
286                ingress.ingress_id,
287            );
288        }
289
290        // This drains ingress due at the current canonical time only. The
291        // mid-quantum command remains queued until normal accumulation reaches
292        // its quantized minute; future scheduled work is never jumped to.
293        let receipts = self.authority.advance_canonical(SimDuration::ZERO)?;
294        self.canonical_boundaries += receipts.len();
295        self.refresh_presentation_from_authority();
296        self.presentation.render(self.presentation_time());
297        if self.verbose {
298            self.print_frame(GameSpeed::X1);
299        }
300        self.report_new_events();
301        Ok(())
302    }
Source

pub fn step_canonical(&mut self) -> Result<Option<BoundaryReceipt>, CanwuError>

Examples found in repository?
examples/uncertainty_resolution.rs (line 163)
146fn random_branch() -> Result<(), Box<dyn std::error::Error>> {
147    let mut canwu = Canwu::demo(202)?;
148    canwu.register_plugin(&UncertaintyPlugin)?;
149    enqueue_ticket(
150        &mut canwu,
151        RANDOM_TICKET,
152        1,
153        DecisionControllerBinding::new(
154            "law-random-controller",
155            DecisionPolicyIdentity::new(DecisionPolicyKind::Random, "weighted-random", "1"),
156            DecisionAuthority::Actor {
157                actor: Canwu::demo_ids().commander,
158            },
159        ),
160    )?;
161    let selection_at = SimTime::EPOCH + SimDuration::days(1);
162    canwu.schedule_calendar_boundary(selection_at, vec![SystemCadence::Daily])?;
163    canwu.step_canonical()?.expect("random source boundary");
164    canwu
165        .step_canonical()?
166        .expect("generated decision resolution boundary");
167    let trace = canwu
168        .decision_trace(canwu_api::DecisionTraceId::new(1))
169        .expect("random decision trace");
170    println!("random_trace={}", serde_json::to_string(trace)?);
171    Ok(())
172}
173
174fn llm_interface_branch() -> Result<(), Box<dyn std::error::Error>> {
175    let mut canwu = Canwu::demo(202)?;
176    let ticket_id = DecisionTicketId::new(2);
177    enqueue_ticket(
178        &mut canwu,
179        ticket_id,
180        10,
181        DecisionControllerBinding::new(
182            "law-llm-controller",
183            DecisionPolicyIdentity::new(DecisionPolicyKind::Llm, "strict-law-selector", "1"),
184            DecisionAuthority::Actor {
185                actor: Canwu::demo_ids().commander,
186            },
187        ),
188    )?;
189    let mut policy = QueuedLlmPolicy::new(
190        "strict-law-selector",
191        "1",
192        LlmModelIdentity {
193            provider: "not-connected".to_owned(),
194            model: "host-selected-model".to_owned(),
195            prompt_contract: "return one existing option_id and no new action".to_owned(),
196        },
197    );
198    let ticket = canwu.decision_ticket(ticket_id).expect("LLM ticket");
199    let request = policy.external_request(ticket);
200    println!("llm_request={}", serde_json::to_string(&request)?);
201
202    // A real host sends `request` to its model and parses a strict structured
203    // answer. This example supplies the same response object without making a
204    // network call.
205    policy.submit(
206        ticket_id,
207        ExternalDecisionResponse {
208            ticket_version: request.ticket_version,
209            option_id: "pass".to_owned(),
210            provider: "not-connected".to_owned(),
211            request_id: "example-response-1".to_owned(),
212            metadata: BTreeMap::new(),
213        },
214    )?;
215    let evaluation = canwu.drive_decision(
216        canwu.time(),
217        0,
218        DecisionRequestId::new(12),
219        None,
220        ticket_id,
221        &policy,
222    )?;
223    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
224    canwu.step_canonical()?.expect("LLM decision boundary");
225    Ok(())
226}
More examples
Hide additional examples
examples/decision_ticket.rs (line 83)
29fn open_aid_request(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
30    let now = canwu.time();
31    let controller = DecisionControllerBinding::new(
32        "warlord-b-ai",
33        DecisionPolicyIdentity::new(DecisionPolicyKind::Utility, "aid-utility", "1"),
34        DecisionAuthority::Actor {
35            actor: ids.commander,
36        },
37    )
38    .with_command_subject(EntityRef::Army(ids.army));
39
40    canwu.enqueue_decision(
41        now,
42        0,
43        DecisionIngressRequest::new(
44            DecisionRequestId::new(1),
45            canwu.revision(),
46            DecisionMutation::RegisterController { controller },
47        ),
48    )?;
49    canwu.enqueue_decision(
50        now,
51        0,
52        DecisionIngressRequest::new(
53            DecisionRequestId::new(2),
54            canwu.revision(),
55            DecisionMutation::Open {
56                ticket: DecisionTicketDraft {
57                    id: DecisionTicketId::new(1),
58                    definition: "beiyang.request-military-aid".to_owned(),
59                    decision_maker: EntityRef::Person(ids.commander),
60                    assigned_controller: "warlord-b-ai".to_owned(),
61                    summary: "Neighboring warlord requests immediate military aid".to_owned(),
62                    context: DecisionContext::new(
63                        "beiyang.aid-request.v1",
64                        json!({
65                            "requester": "warlord-a",
66                            "battle": "ongoing-front",
67                            "common_enemy": true
68                        }),
69                    ),
70                    options: vec![DecisionOption {
71                        action: DecisionAction::None,
72                        utility_inputs: BTreeMap::from([
73                            ("home_defense".to_owned(), 80),
74                            ("alliance".to_owned(), -40),
75                        ]),
76                        ..DecisionOption::new("decline", "Decline aid")
77                    }],
78                    deadline: Some(now + SimDuration::days(2)),
79                },
80            },
81        ),
82    )?;
83    canwu.step_canonical()?.expect("decision intake boundary");
84    Ok(())
85}
86
87fn refresh_aid_options(canwu: &mut Canwu, ids: DemoIds) -> Result<(), Box<dyn std::error::Error>> {
88    canwu.enqueue_decision(
89        canwu.time(),
90        0,
91        DecisionIngressRequest::new(
92            DecisionRequestId::new(3),
93            canwu.revision(),
94            DecisionMutation::ReplaceOptions {
95                ticket_id: DecisionTicketId::new(1),
96                expected_version: 1,
97                context: DecisionContext::new(
98                    "beiyang.aid-request.v1",
99                    json!({
100                        "requester": "warlord-a",
101                        "battle": "ongoing-front",
102                        "common_enemy": true,
103                        "route_confirmed": true
104                    }),
105                ),
106                options: vec![
107                    DecisionOption {
108                        action: DecisionAction::None,
109                        utility_inputs: BTreeMap::from([
110                            ("home_defense".to_owned(), 80),
111                            ("alliance".to_owned(), -40),
112                        ]),
113                        ..DecisionOption::new("decline", "Decline aid")
114                    },
115                    DecisionOption {
116                        action: DecisionAction::Command {
117                            command: serde_json::to_value(Command::OrderMovement {
118                                subject: EntityRef::Army(ids.army),
119                                destination: ids.eastern_territory,
120                                cargo: Vec::new(),
121                            })?,
122                        },
123                        utility_inputs: BTreeMap::from([
124                            ("home_defense".to_owned(), -20),
125                            ("alliance".to_owned(), 90),
126                        ]),
127                        ..DecisionOption::new("send-aid", "Send the neighboring army")
128                    },
129                ],
130            },
131        ),
132    )?;
133    canwu
134        .step_canonical()?
135        .expect("dynamic option refresh boundary");
136    Ok(())
137}
138
139fn resolve_aid_request(canwu: &mut Canwu) -> Result<(), Box<dyn std::error::Error>> {
140    let policy = WeightedUtilityPolicy::new(
141        "aid-utility",
142        "1",
143        UtilityProfile {
144            weights: BTreeMap::from([("alliance".to_owned(), 3), ("home_defense".to_owned(), 1)]),
145        },
146    );
147    let evaluation = canwu.drive_decision(
148        canwu.time(),
149        0,
150        DecisionRequestId::new(4),
151        Some(CommandRequestId::new(1)),
152        DecisionTicketId::new(1),
153        &policy,
154    )?;
155    assert!(matches!(evaluation, DecisionEvaluation::Prepared(_)));
156    canwu
157        .step_canonical()?
158        .expect("decision resolution boundary");
159    Ok(())
160}
Source

pub fn advance( &mut self, duration: SimDuration, ) -> Result<Vec<SimEvent>, CanwuError>

Source

pub fn settle_boundary( &mut self, request: BoundaryRequest, ) -> Result<BoundaryReceipt, CanwuError>

Examples found in repository?
examples/phased_boundary.rs (line 155)
149fn main() -> Result<(), CanwuError> {
150    let mut canwu = Canwu::demo(35)?;
151    canwu.register_plugin(&SupplyPlugin)?;
152    canwu.register_plugin(&DemandPlugin)?;
153
154    let receipt = canwu
155        .settle_boundary(BoundaryRequest::at(canwu.time()).with_cadence(SystemCadence::Daily))?;
156    assert_eq!(receipt.allocations[0].granted, 6);
157    assert_eq!(canwu.boundaries()[0].emissions.len(), 2);
158    Ok(())
159}
More examples
Hide additional examples
examples/governance_transition.rs (line 564)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
examples/uncertainty_resolution.rs (line 142)
95fn enqueue_ticket(
96    canwu: &mut Canwu,
97    ticket_id: DecisionTicketId,
98    request_offset: u64,
99    controller: DecisionControllerBinding,
100) -> Result<(), CanwuError> {
101    let now = canwu.time();
102    let controller_id = controller.id.clone();
103    canwu.enqueue_decision(
104        now,
105        0,
106        DecisionIngressRequest::new(
107            DecisionRequestId::new(request_offset),
108            canwu.revision(),
109            DecisionMutation::RegisterController { controller },
110        ),
111    )?;
112    canwu.enqueue_decision(
113        now,
114        0,
115        DecisionIngressRequest::new(
116            DecisionRequestId::new(request_offset + 1),
117            canwu.revision(),
118            DecisionMutation::Open {
119                ticket: DecisionTicketDraft {
120                    id: ticket_id,
121                    definition: "example.law-passage".to_owned(),
122                    decision_maker: EntityRef::Person(Canwu::demo_ids().commander),
123                    assigned_controller: controller_id,
124                    summary: "Will the proposed law pass?".to_owned(),
125                    context: DecisionContext::new(
126                        "example.law-passage.v1",
127                        json!({
128                            "supporting_seats": 72,
129                            "opposing_seats": 28,
130                            "public_pressure": "high"
131                        }),
132                    ),
133                    options: vec![
134                        DecisionOption::new("pass", "Pass the law"),
135                        DecisionOption::new("fail", "Reject the law"),
136                    ],
137                    deadline: None,
138                },
139            },
140        ),
141    )?;
142    canwu.settle_boundary(canwu_api::BoundaryRequest::at(now))?;
143    Ok(())
144}
Source

pub fn wait( &mut self, duration: SimDuration, ) -> Result<Vec<SimEvent>, CanwuError>

Source

pub fn step(&mut self) -> Result<Vec<SimEvent>, CanwuError>

Source

pub fn snapshot(&self) -> SimulationSnapshot

Examples found in repository?
examples/decision_ticket.rs (line 163)
162fn verify_persistence_and_replay(canwu: &Canwu) -> Result<(), Box<dyn std::error::Error>> {
163    let snapshot = canwu.snapshot();
164    verify_snapshot_round_trip(&snapshot)?;
165    verify_journal_replay(canwu, &snapshot)
166}
167
168fn verify_snapshot_round_trip(
169    snapshot: &canwu_api::SimulationSnapshot,
170) -> Result<(), Box<dyn std::error::Error>> {
171    let restored = Canwu::from_snapshot_json(&serde_json::to_string(&snapshot)?)?;
172    assert_eq!(restored.snapshot(), *snapshot);
173    Ok(())
174}
175
176fn verify_journal_replay(
177    canwu: &Canwu,
178    snapshot: &canwu_api::SimulationSnapshot,
179) -> Result<(), Box<dyn std::error::Error>> {
180    let replayed = Canwu::replay_from_journal(&[], &canwu.replay_journal())?;
181    assert_eq!(replayed.snapshot(), *snapshot);
182    Ok(())
183}
More examples
Hide additional examples
examples/governance_transition.rs (line 578)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn snapshot_json(&self) -> Result<String, CanwuError>

Examples found in repository?
examples/governance_transition.rs (line 575)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn from_snapshot_json(json: &str) -> Result<Self, CanwuError>

Examples found in repository?
examples/decision_ticket.rs (line 171)
168fn verify_snapshot_round_trip(
169    snapshot: &canwu_api::SimulationSnapshot,
170) -> Result<(), Box<dyn std::error::Error>> {
171    let restored = Canwu::from_snapshot_json(&serde_json::to_string(&snapshot)?)?;
172    assert_eq!(restored.snapshot(), *snapshot);
173    Ok(())
174}
Source

pub fn from_snapshot_json_with_plugins( json: &str, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Examples found in repository?
examples/governance_transition.rs (line 576)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn from_checkpoint_and_journal( checkpoint: SimulationCheckpoint, segments: Vec<EvidenceJournalSegment>, ) -> Result<Self, CanwuError>

Source

pub fn from_checkpoint_journal( bundle: CheckpointJournal, ) -> Result<Self, CanwuError>

Source

pub fn from_checkpoint_journal_with_plugins( bundle: CheckpointJournal, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Source

pub fn from_checkpoint_journal_json(json: &str) -> Result<Self, CanwuError>

Source

pub fn from_checkpoint_journal_json_with_plugins( json: &str, plugins: &[&dyn SimulationPlugin], ) -> Result<Self, CanwuError>

Source

pub fn replay_from_journal( plugins: &[&dyn SimulationPlugin], journal: &ReplayJournal, ) -> Result<Self, CanwuError>

Examples found in repository?
examples/decision_ticket.rs (line 180)
176fn verify_journal_replay(
177    canwu: &Canwu,
178    snapshot: &canwu_api::SimulationSnapshot,
179) -> Result<(), Box<dyn std::error::Error>> {
180    let replayed = Canwu::replay_from_journal(&[], &canwu.replay_journal())?;
181    assert_eq!(replayed.snapshot(), *snapshot);
182    Ok(())
183}
More examples
Hide additional examples
examples/governance_transition.rs (line 577)
549fn main() -> Result<(), CanwuError> {
550    let central = CentralPlugin;
551    let treasury = TreasuryPlugin;
552    let county = CountyPlugin;
553    let plugins: [&dyn SimulationPlugin; 3] = [&central, &treasury, &county];
554    let mut canwu =
555        Canwu::new_with_plugins(11, Scenario::new(SimTime::EPOCH, Vec::new()), &plugins)?;
556
557    canwu.enqueue_plugin_ingress(canwu_api::PluginIngressRequest::new(
558        CENTRAL_PLUGIN,
559        ISSUE_INGRESS,
560        SimTime::EPOCH,
561        json!({"order_id": ORDER_ID}),
562    ))?;
563
564    let first = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
565    assert_eq!(first.generated_ingress.len(), 2);
566    assert!(canwu.typed_domain_record(&order_reference()).is_some());
567    assert!(canwu.typed_domain_record(&treasury_reference()).is_none());
568    assert!(canwu.typed_domain_record(&county_reference()).is_none());
569
570    let second = canwu.settle_boundary(BoundaryRequest::at(SimTime::EPOCH))?;
571    assert_eq!(second.record_change_count, 2);
572    assert!(canwu.typed_domain_record(&treasury_reference()).is_some());
573    assert!(canwu.typed_domain_record(&county_reference()).is_some());
574
575    let snapshot = canwu.snapshot_json()?;
576    let restored = Canwu::from_snapshot_json_with_plugins(&snapshot, &plugins)?;
577    let replayed = Canwu::replay_from_journal(&plugins, &canwu.replay_journal())?;
578    assert_eq!(restored.snapshot(), canwu.snapshot());
579    assert_eq!(replayed.snapshot(), canwu.snapshot());
580
581    println!(
582        "relief_order={} treasury_grain={} county_grain={} exact_replay=ok",
583        ORDER_ID, 600, 600
584    );
585    Ok(())
586}
Source

pub fn replay_from_journal_with_archive_provider( plugins: &[&dyn SimulationPlugin], journal: &ReplayJournal, archive_provider: Rc<dyn PluginArchiveObjectProvider>, ) -> Result<Self, CanwuError>

Replays with package archive storage attached before the first recorded boundary, so package-owned cold history participates in ordinary admission and settlement throughout replay.

Source

pub fn replay_from_journal_json( plugins: &[&dyn SimulationPlugin], json: &str, ) -> Result<Self, CanwuError>

Source

pub fn fork(&self) -> Self

Source

pub fn admin_query_knowledge( &self, holder: KnowledgeHolderRef, query: &KnowledgeQuery, ) -> Result<KnowledgeQueryResult, CanwuError>

Trusted host/admin holder query. Player-facing callers must use a restricted CanwuViewer.

Source

pub fn viewer(&self) -> Result<CanwuViewer<'_>, CanwuError>

Creates the restricted viewer dictated entirely by the persisted run policy and seat binding.

Source

pub fn viewer_for_actor( &self, actor: PersonId, ) -> Result<CanwuViewer<'_>, CanwuError>

Character-seat and compatibility convenience. It never upgrades an institution, public, research, or developer policy to a person.

Source

pub fn viewer_context( &self, actor: PersonId, ) -> Result<ViewerContext, CanwuError>

Source

pub fn explain(&self, request: &ExplanationRequest) -> Explanation

Trait Implementations§

Auto Trait Implementations§

§

impl !RefUnwindSafe for Canwu

§

impl !Send for Canwu

§

impl !Sync for Canwu

§

impl !UnwindSafe for Canwu

§

impl Freeze for Canwu

§

impl Unpin for Canwu

§

impl UnsafeUnpin for Canwu

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.