Skip to content

Resources and production

canwu-resource, the resource simulation extension, keeps conserved stock such as grain, cloth, or ammunition in accounts and changes it only through recorded operations. canwu-production, the production simulation extension, turns consumed inputs and facility capacity into output that it hands back to canwu-resource for crediting. Read this page when your game has stock that can run out, several claims on the same stock, or work that turns inputs into goods. Production, resources, and transport: who owns what covers the rules of each operation.

Crate What it owns Published
canwu-resource The canwu.resource namespace: resource and unit revisions, accounts, protected floors, demands, reservations, allocation legs, transfers and their escrow, consumption, loss, fulfillment, operation outcomes, access grants, completion leases, and holder reports Published on crates.io
canwu-production The canwu.production namespace: process revisions, sites, facilities, capacity allocations, work orders, executions, work in progress, facility projects, incident receipts, and holder reports. It keeps no stock. Published on crates.io
canwu-economy-reference-content Source-linked model cards, coverage cells, and three fixtures: synthetic_grain_fixture, ming_workshop_fixture, and china_industrialization_fixture. It has no solver or command handler. Published on crates.io
canwu-force-supply-reference A replaceable military consumer: force requirements, consumption intents, readiness and shortage consequences, and the requisition saga Repository only
canwu-economy-reference A replaceable composition: the fourteen-month grain loop (GrainHarness) and the local scarcity and price-pressure projections Repository only
Crate dependencies: canwu-resource depends on canwu-api; canwu-production depends on canwu-resource and canwu-technology; canwu-force-supply-reference depends on canwu-resource and the reference content; canwu-economy-reference composes canwu-force-supply-reference and canwu-production; the host application feeds the two extensions. View diagram source.
View diagram source
flowchart TB
  Host["Host application<br/>(GrainHarness in the reference)"]
  Economy["canwu-economy-reference<br/>grain loop and projections"]
  Force["canwu-force-supply-reference<br/>force demand and consequences"]
  Content["canwu-economy-reference-content<br/>model cards and fixtures"]
  Production["canwu-production<br/>processes, facilities, work orders"]
  Technology["canwu-technology<br/>technique evidence"]
  Resource["canwu-resource<br/>conserved stock"]
  Api["canwu-api"]
  Host -- "scenario records, commands,<br/>allocation passes" --> Resource
  Host -- "processes, sites,<br/>work orders" --> Production
  Economy -- "composes" --> Force
  Economy -- "composes" --> Production
  Force -- "depends on" --> Content
  Force -- "depends on" --> Resource
  Production -- "depends on" --> Resource
  Production -- "depends on" --> Technology
  Resource -- "depends on" --> Api
  Technology -- "depends on" --> Api

canwu-resource depends only on canwu-api. canwu-production adds canwu-resource and canwu-technology. Each execution and facility project carries a TechnologyEvidenceBinding: an exact TechniqueRevision, a CapabilityQualification or ImplementationRecord, and an AdoptionRecord when the process sets adoption_required. When an execution starts or a facility project is created, each exact domain-record version it cites must be the current version of its record, so a run sealed with seal_evidence accepts the same commands as its exact replay. Advancing a project later does not recheck its provider and technology evidence, which its creation validated; it still rechecks the project’s resource evidence. canwu-api re-exports neither extension, so add them to your Cargo.toml yourself.

canwu-resource is the only writer of stock balances. Production, force supply, and your own domains submit resource operations and read the outcomes. canwu-transport and canwu-movement carry cargo and supply evidence about the journey. A transfer links to that evidence through a TransportExecutionLink, and the cargo’s balance, loss, and acceptance stay in canwu-resource.

The two reference crates exist to show that two independent consumers can share one resource lifecycle. The mechanism decision records why that evidence was enough to publish the two extensions as optional crates outside the simulation core. Replace the reference crates with your own domains.

Each ResourceAccount stores one quantity, balance. Available, reserved, and protected amounts are derived from it by ResourceState::account_quantities. Stock that has left its source account and has not yet arrived sits in the escrow field of its ResourceTransfer and still counts. ConservationTotalsV1 accumulates the opening totals and every admitted source and sink, and ResourceState::validate_conservation checks this equation with 128-bit sums:

account balances + transfer escrow
+ admitted consumption + admitted loss + external outflow
= opening balances + opening escrow
+ admitted production + external inflow

Every term has one entry path:

Term Operation that changes it
Opening balances ResourceState::install_opening_account when the scenario is built. An account created during a run with CreateAccount must start at zero.
Admitted production Credit with ResourceCreditSourceV1::Production, which only the production output batch can settle
External inflow Credit with ResourceCreditSourceV1::ExternalInflow, citing its evidence
Admitted consumption Consume of an allocation leg
Admitted loss RecordLoss on an account, a RecordTransferLoss on part of a transfer’s escrow, or the Lose disposition of a transfer
External outflow ExternalOutflow, as a request on an account or as a transfer disposition, citing authority evidence

Three more rules protect the equation. Units never convert implicitly, because every account, demand, and transfer names one exact resource revision and one exact unit revision. Every operation carries a ResourceOperationKey: resending the same request returns the original ResourceOperationOutcome, and reusing the key for a different request fails as an idempotency conflict. An operation that breaks a domain rule settles as a Rejected outcome and leaves every quantity where it was. If a candidate state still breaks the equation, the phase-8 check fails and the whole boundary rolls back.

Resource types, all in canwu-resource:

Type What it represents
ResourceDefinitionRevision, ResourceUnitRevision An immutable resource with its quality and scope, and a unit with its scale
ResourceAccount One balance of one exact resource and unit, held by a custodian (a KnowledgeHolderRef), with an optional capacity, protected floor policy, and place_scope
ProtectedFloorPolicyRevision A floor held back from ordinary allocation, such as seed grain, and the demand classes allowed to draw below it. The scenario authors a revision, or the account’s custodian installs one at runtime
ResourceDemand A requester’s claim: quantity, minimum useful quantity, PartialFulfillmentPolicy, due and expiry times, priority, tie-break key, and source_policy
ResourceReservation, ResourceAllocationLeg One allocated share of one account for one demand; consumers cite the leg as exact evidence
ResourceTransfer Stock in escrow between two accounts, with a ResourceTransferState, an optional TransportExecutionLink (the current link), and its link_history of every link it was advanced through
ResourceConsumption, ResourceLoss, ResourceFulfillment Terminal evidence of use, of loss, and of how much of a demand was met
ResourceOperationOutcome The immutable receipt of every operation, with status Applied, Rejected, or Duplicate
ResourceAccessGrantV1 A grantor custodian’s capped, time-limited consent that a grantee may draw on its stock
CompletionLeaseActivationCertificateV1 Proof that one holder may perform one irreversible operation, locked to the exact records it depends on
ResourceConsumptionBatchRequestV1 Up to 256 exact allocation-leg consumptions settled under one completion lease, each item with its own outcome; see consumption batches
ResourceState The whole resource root in memory; it persists as a live root (ResourceLiveRootV1), one bucket record per group of hot terminal records (ResourceHotTerminalBucketV1), and one bucket record per group of accounts, grants, definitions, units, and custodian index entries (ResourceLiveBucketV1)

Production types, all in canwu-production:

Type What it represents
ProcessRevision An immutable recipe: requirement groups (twelve ProductionRequirementKind values, from Material to FinanceOrganization), inputs, outputs, capacity needs, work units, and realized-output bounds
ProductionSite A holder’s place of production with a ProductionSiteForm, from Household to MultiSiteEnterprise. The form is data; there are no building levels.
FacilityAsset Equipment at a site: generation, FacilityLifecycle, condition in thousandths, capacity per capability, and incident risk
ProductionCapacityAllocation A half-open time slot of facility capacity held by one execution
WorkOrder, ProductionExecution A request to run a process at a site, and one run of it with its evidence, consumed inputs, and pending outputs
WorkInProgress Progress of an execution and the evidence for the inputs it consumed. The stock itself stays in canwu-resource.
FacilityProject Construction or repair of one facility generation
ProductionOperationOutcome The receipt of every production command, with disposition Applied, Duplicate, or Rejected
Lifecycle: a demand is allocated, a completion lease authorizes the debit, stock is consumed or transferred, production cites consumed inputs, and output returns to canwu-resource as a credit acknowledged back to production. View diagram source.
View diagram source
flowchart TB
  Demand["SubmitDemand<br/>requester's tracked command"] --> Alloc["Allocation pass<br/>enqueue_resource_allocation"]
  Alloc --> Leg["Reservation and<br/>allocation leg"]
  Leg --> Lease["Completion lease<br/>activation certificate"]
  Lease --> Consume["Consume<br/>consumption and fulfillment"]
  Lease --> Transfer["BeginTransfer<br/>stock moves into escrow"]
  Transfer --> Arrive["CompleteTransfer<br/>Accept, AcceptLocal, Lose, Return"]
  Consume --> Start["StartExecution<br/>cites consumed inputs"]
  Start --> Complete["CompleteExecution<br/>work in progress finishes"]
  Complete --> Batch["Output batch<br/>credited in canwu-resource"]
  Batch --> Ack["Output acknowledgement<br/>execution Settled"]

Resource operations enter by four paths. A tracked command is sent as a CommandRequest, with a request ID and an expected revision. Each path accepts only some operations:

Path Who sends it Operations
Tracked command apply_resource_operation_v1 A holder acting on stock it controls. The command subject must be the holder that controls the target record. CreateAccount, SubmitDemand, AmendDemand, CancelDemand, BeginTransfer, BeginExchange, CancelTransfer, CompleteTransfer with AcceptLocal, Lose, or Return, RecordLoss, RecordTransferLoss, ExternalOutflow, external-inflow Credit, SetProtectedFloor, IssueAccessGrant, RevokeAccessGrant, and lease acquisition and abort
Adapter ingress resource_adapter_operation_v1 A provider plugin. The request’s evidence field must equal the exact record version the packet cites, and a timed request settles only at its stated time. Consume, AdvanceTransfer, CompleteTransfer with Accept, AcceptLocal, or ExternalOutflow, RecordLoss, RecordTransferLoss, ExternalOutflow, external-inflow Credit, and observations
Host application resource_account_rescope_v1 (enqueue_resource_account_rescope) The host application only; a packet another plugin schedules settles nothing. RescopeAccount
Plugin-owned packets The host through helper functions, or canwu-production Allocation passes (resource_authorized_allocation_v1), lease transitions, including the single-step lease (resource_completion_operation_v1), and production output batches (resource_production_output_batch_v1)
  1. Set up. The host application installs resource definitions, units, opening accounts, floor policies, and report grants in a ResourceState, turns it into scenario domain records with ResourceState::into_records (the live root and the buckets that hold the accounts, grants, definitions, and units; into_record refuses a root that holds any of them), and registers ResourcePlugin::new(adapter_evidence_kinds). The list names the record kinds that may prove adapter operations. Production state enters the same way through ProductionState::into_initial_record and ProductionPlugin.
  2. Demand. The requester sends the tracked domain command apply_resource_operation_v1, built with resource_command. The command handler checks that the issuer controls the command’s subject and that the subject has authority over every target, then queues a resource_command_v1 packet.
  3. Allocation. The host queues one pass for one requester with enqueue_resource_allocation. When a boundary admits the packet, the phase-7 writer expires overdue demands and gathers that requester’s due or changed demands; the pass fails if they exceed the request’s candidate_limit. It serves them in order of descending priority, due time, tie-break key, admission sequence, and demand ID. It writes a reservation and an allocation leg for each share.
  4. Lease. Before any irreversible step, the acting holder needs an active completion lease, taken through enqueue_resource_completion_operation. A lease becomes active through four operations (acquire, grant, prepare, activate), because each participant grants and prepares on its own: for an execution, canwu-production coordinates the lease and canwu-resource joins as a participant. When the resource plugin is the only participant, as for a transfer start, a local acceptance, a loss, or an outflow, one SingleStepLease operation runs all four steps with the same checks and yields the same certificate; see completion leases. The certificate locks the exact accounts, allocation legs, and records the step depends on. Every consumption, transfer start, transfer disposition, credit, loss, and outflow must carry one.
  5. Debit. A consumer plugin sends Consume through adapter ingress with enqueue_resource_adapter_operation, citing the exact version of its own record that authorizes the debit. Or the source custodian starts a transfer with BeginTransfer, and the quantity moves into escrow. Transfer progress and Accept arrive as adapter operations that cite transport evidence. A transfer carried over several legs records each leg and handoff in its link history, and part of its escrow may be lost on the way while it stays open. AcceptLocal, Lose, and Return end a transfer in the other ways. Each transfer step names the transfer’s expected revision, so one arrival cannot be credited twice.
  6. Production. Production commands (apply_production_operation_v1) create and authorize a work order. StartExecution cites inputs that canwu-resource has already consumed: each ResourceInputBinding names the allocation leg, the consumption, and its outcome. It also brings capacity allocations, which phase 7 marks Consumed. AdvanceExecution moves the work in progress, and CompleteExecution leaves the execution CompletedPendingOutputSettlement. Its output quantities may be scaled by a realized output ratio.
  7. Output. In phase 12, production pins the current production record version as the execution’s output_source and schedules one resource_production_output_batch_v1 packet. At a later boundary the resource writer credits every output in the batch or none of them, then schedules production_output_ack_v1 with settled_at, the instant the credits settled. A batch that finds no room in the resource root is deferred, not lost (see skipped packets and deferrals); its credits are retimed to the instant they settle. Production’s phase 7 checks that every outcome’s request digest commits to settled_at, stores the outcomes and output_settled_at, releases the capacity, and marks the execution Settled.
  8. Check and publish. Phase 8 validates each extension’s candidate state; a broken conservation or capacity rule is an engine invariant failure that fails the boundary. Phase 13 publishes holder reports.

StartExecution refuses, as a recorded rejection, an output set the resource plugin could never credit: an output key under the reserved resource:completion: prefix, a key another running or output-pending execution names, a key a resource outcome already holds in the root or its archive, more than MAX_PRODUCTION_OUTPUT_BATCH_CREDITS (64) outputs, or a set the resource root can never hold under its limits (ResourceState::production_output_batch_can_fit). A process may declare at most 64 outputs, refused at scenario time.

Allocation runs inside the resource plugin’s phase-7 writer against persisted accounts and demands. The kernel’s phase-6 reservation primitive in the phased boundary tutorial (ReservationRequest) is a separate mechanism for offers and requests declared within one boundary, and canwu-resource does not use it.

The boundary systems, all event-driven with SameBoundary visibility:

Crate Phase System What it does
canwu-resource 7 DomainDeltaProposal settle-resource-lifecycle-v1 The only writer of the resource root. It applies commands, adapter operations, allocation passes, lease transitions, and output batches, and emits canwu.resource.operation_settled.v1 to the affected actors.
canwu-resource 8 InvariantValidation validate-resource-invariants-v1 Runs ResourceState::validate, which includes the conservation check
canwu-resource 12 StrategicAggregation maintain-resource-summary-v1 Runs the same validation again
canwu-resource 13 PerspectiveAndReportMaterialization materialize-resource-reports-v1 Publishes holder reports as resource_report knowledge
canwu-production 7 DomainDeltaProposal production_lifecycle_apply_v1 The only writer of the production root. It applies commands, lease transitions, and output acknowledgements.
canwu-production 8 InvariantValidation production_capacity_and_lifecycle_validate_v1 Checks record closure, work in progress totals, and that overlapping capacity slots never exceed a facility’s condition-adjusted capacity
canwu-production 10 HistoricalCandidateEvaluation production_incident_candidate_evaluation_v1 Draws facility incidents and stages their transitions
canwu-production 11 ConditionalTransitionCommit production_incident_commit_audit_v1 Validates the state after the incident commit
canwu-production 12 StrategicAggregation production_output_dispatch_v1 Pins output_source and schedules the output batch
canwu-production 13 PerspectiveAndReportMaterialization production_holder_report_publish_v1 Publishes holder reports

Production registers no event audience, so its events stay out of actor-relative views; holders learn about production through reports. See settlement for the phase order.

A provider that settles many small consumptions, such as a settlement office drawing every household’s grain ration from one granary, sends one ConsumeBatch (ResourceConsumptionBatchRequestV1) instead of one lease per household: 1 to 256 items, each one exact allocation leg with its demand, demand revision, expected account revision, and consumption identity, beside one consumer evidence, one instant, and one completion certificate. The certificate binds the batch’s operation key and locks every item’s account, leg, and demand (locked_targets), and the lease’s holder must have requested every item’s demand. A batch enters only through its provider’s adapter ingress; the provider authorizes every item with a typed consumption intent whose operation key is the item’s reserved key, resource_consumption_batch_item_key(batch key, index).

  • Items settle in order. Each makes the checks a single Consume makes; an item that fails them is refused alone (a rejected Consume outcome under its key, the leg still reserved), and one that passes debits its leg and records its consumption, its fulfillment, and an applied outcome. Several items may draw on one account: an item that expects the revision the batch found draws on the account as the batch left it.
  • The batch outcome, under the batch key, totals the consumed quantity, with the refused quantity as remainder; the lease completes once, with one receipt, and leaves the root. A batch with no items, more than 256, a certificate that does not bind exactly its targets, or an item key already recorded is one rejected batch outcome with the lease left active.
  • Room is measured on the whole result: a batch that does not fit the root is skipped (archive_backlog, or reserved_capacity when no commit or release can make room) and records nothing, so check ResourceState::consumption_batch_can_fit(items) before taking the lease. An item that consumes the one leg of its demand leaves six hot terminal records, and the batch two more.
  • ResourceLimitsV1::max_batch_items_per_boundary (canonical 32, what the canonical archive budget holds beside one other lease; hard maximum 2,048) bounds the items one boundary settles. A batch that fits alone but not beside the batches the boundary already applied is deferred, with the boundary_budget skip event, to the next boundary at the same instant, which settles it. Raise max_archive_candidates with it for full batches: a batch of 256 needs 1,539 candidates.
  • A resend of the batch is answered by the batch outcome, hot or archived; item keys are reserved, so no other request can name one. One holder still takes one lease per simulated minute, so batches that settle together belong to different holders or cite records of their own.

The force-supply reference consumer (canwu-force-supply-reference) is a second, independent user of the same lifecycle: a force’s consumption intent becomes a Consume through adapter ingress, and its shortage consequence applies once, after the resource outcome settles. The economy reference plugin authorizes civilian consumption intents and closes each month; the grain, production, and military supply case walks through both consumers. Both reference plugins keep their package archive bounded: an acknowledged archive commit’s receipts fold into a digest chain in the plugin root, and an operation without room for its outcome is skipped with an operation_skipped event and sent again once an archive commit made room, never a failed boundary.

A trusted host can read the whole resource root with resource_state and validate evidence with the exact queries, such as exact_resource_allocation_leg, exact_resource_operation_outcome, and exact_resource_fulfillment. Players and in-world agents read holder reports:

  • ResourceReportGrantV1 is an explicit allowlist for one holder: a scope, accounts, demands, whether transfer details are included, a confidence, a cadence, and a delay. Phase 13 records an observation head per grant, and resource_report builds a ResourceReportDtoV1 from that head. A delayed holder sees the balance recorded at its observation time. Reports never fall back to ground truth, and they do not reveal a demand’s source list. A holder report that a completion lease reserved is given up when its grant has no observation head, or when the engine refuses the holder knowledge by then: the participant archives without it and no report wake is queued; a completion operation that reserves reports for a holder the engine refuses is a recorded invalid_authority rejection, and install_report_grant refuses a holder kind that can never hold knowledge (a core organization, resource, route, or territory, or a person given as an entity).
  • Each stock observation carries the ResourceScopeId of the account’s resource revision, so a consumer cannot relabel distant stock as local.
  • resource_access_grant_status shows a grant and its cap accounting to its grantor or grantee only.
  • ProductionObserverGrant gives a holder a role for a set of sites: Operator, LocalOwner, or RemoteOwner, with a delay. production_report requires an exact grant, and an unauthorized holder gets an authority error.
  • The force-supply reference builds its reports from typed knowledge publications available at the holder’s observation cut.

The economy reference also builds two detached read models for decision-making, the local scarcity projection and the price-pressure projection. Building them changes no state; Scarcity and price in the grain case describes both.

A trusted host that needs only a few accounts or records does not have to read the whole root. ResourceRootReader::load_run(run) reads the live root of any CanwuRun; attach, attach_live, and attach_custodian_accounts bring in only what they name, as in a boundary, and attach_all brings in the whole root.

For the same committed state and requests it holds what ResourceRootReader::load(view) holds. resource_state lists the buckets by record kind instead of walking every record, and queries such as resource_allocation_leg, exact_resource_allocation_leg, resource_consumption, resource_operation_outcome, resource_access_grant_status, and resource_completion_status read only the live root and the records they name. They no longer run the whole root’s integrity checks: a malformed bucket they do not read no longer turns an answer into None or an error, so a host that relies on a query to detect corruption calls resource_state or validate_resource_runtime.

A host that rereads after every step turns its reader into a KeptResourceRoot with keep, which borrows nothing, and calls ResourceRootReader::reattach(run, kept), which decodes only the buckets whose records the run no longer shares with it. A kept resource root is host memory and is never saved. All of this is a trusted-host read of every custodian’s ground truth; an actor still sees only resource_report and the holder report grants above.

canwu-resource, canwu-force-supply-reference, and canwu-economy-reference draw no random numbers. Allocation order comes from data: priority, due time, tie-break key, admission sequence, and ID.

canwu-production declares one random stream, production_incident_random_stream(), which is stream facility-incident version 1 of the plugin. Incident checks run only in a boundary that admitted production ingress, and each draw cites that ingress. For each due facility, phase 10 works like this:

  • An operation-keyed draw from 0 to 999 is compared with the facility’s incident_risk_per_mille; a value below it is a hit.
  • On a hit, a second draw bounded by incident_max_severity_per_mille sets the condition loss.
  • A facility left at a condition of 250 or less becomes Damaged; otherwise it becomes Degraded.

Your scenario supplies both probabilities on the FacilityAsset; a command can never supply a draw. The ProductionIncidentTransitionV1 receipt keeps the samples and the source record version, so restore and replay reject a changed draw.

What to do with a degraded facility is a decision ticket: degraded_facility_decision_ticket builds one with the options ContinueDegraded, StopForRepair, and DeferOrder.

Production stores one ProductionRuntimeRecord per root in canwu.production: the primary root canwu.production:runtime and, for a host that splits its sites into scopes (one per county, say), one scope root canwu.production:runtime:scope:{scope} per scope, each a whole ProductionState with its own limits and scope, installed with the scenario (a run cannot add a scope). Every packet names its root (ProductionCommandEnvelope.scope, the batch envelope, a scope member on completion steps, witnesses, acknowledgements, and wakes, and the _in_scope enqueue helpers), the lifecycle system loads, settles, writes, and journals only the roots a boundary’s packets name, and a small boundary activity record (canwu.production:boundary-activity) tells the later systems which roots changed their production lifecycle and by which packet, so a boundary with no production work reads one small record and a 64,000-site world settles a scope’s batch as fast as a 4,096-site one.

Roots share no identity: a scope root’s lease and output operation keys begin with {scope}/, the primary root’s hold no /, and a scope ID holds no /, so no two roots can name one resource-side key; a key that breaks the rule is a recorded rejection. apply_production_batch_v1 settles many operations of one holder against one root in one tracked command, item by item with per-item outcomes; a batch whose expected_runtime_revision is stale, or whose items would take the root past max_operations_per_boundary, is refused whole with a trace and can be sent again, and one without an expected revision has each item checked against the root the items before it left. Nothing a root settles stays for life: ended work orders, ended leases, and unclaimed outcomes archive as residues (ProductionResidueArchiveRecordV1, three new archive key kinds), incident receipts of earlier boundaries fold into the root’s incident history, and a rolled-out observation head is pruned once a newer head of its holder and site is eligible. A completion step, witness, archive commit, or acknowledgement that production refuses is refused with a trace, whoever scheduled it, and refused packets draw no incident and publish no report.

The resource root persists in parts: the live root resource:runtime:v1 (ResourceRuntimeRecord, payload ResourceLiveRootV1) holds open demands with their reservations and legs, active transfers, leases, report grants, and observation heads, and only counts, in two ledgers, what it does not hold; each hot terminal record is an entry of a hot terminal bucket resource:hot-terminal:{bucket} (ResourceHotTerminalBucketV1, about two records per bucket), and each account, access grant, definition, unit revision, and custodian index entry (ResourceCustodianAccountsV1, the accounts one holder holds) is an entry of a live bucket resource:live:{bucket} (ResourceLiveBucketV1, about four per bucket), each chosen by a hash of its identity. Both bucket families are one record kind, canwu.resource bucket (ResourceBucketRecord). Since 0.20.0 each hot terminal bucket declares the exact evidence of its records identity-only (canwu_identity_evidence_dependencies), the live root declares the versions its completion leases, external participants, and observation heads cite, and the production root declares the pending output source of every execution without output outcomes yet, so a seal or a continuation checkpoint keeps proof of a version a hot record cites after that record moved on; a continuation checkpoint taken while a completion lease is in progress therefore loads, validates, and continues exactly. The resource state format is 6 and both bucket formats are 2. ResourceState::into_records writes a state as those records and from_records reads it back, checking that every account lies in its custodian’s index and every consumption names the fulfillment it recorded (ResourceConsumption::fulfillment). A boundary reads and writes the live root and the buckets of the records it touches: the resource plugin attaches every hot terminal and live record a packet names (a leg’s account, a demand’s source accounts and access grant, an account’s definition and unit), and the records those name, before it settles the packet; settling one operation writes the same records whatever the number of accounts, a pooled demand’s allocation pass attaches every account, and a boundary with nothing for the resource plugin reads nothing of the root. A plugin of yours that reads the root does so with resource_root_in_view(view, keys) or ResourceRootReader, naming the hot terminal records it needs and the live records with attach_live(keys) or attach_custodian_accounts(holder), or with whole_resource_root_in_view, and declares the read of resource_hot_terminal_state_key() beside the live root’s; a reader that names a consumption gets its fulfillment through the closure without a search. Your host reads the whole root with resource_state. A finished lease leaves the root at once (its receipts stay until an archive commit takes them, and its identity still starts no second lease), so resource_completion_status no longer finds it; a host expires grants with enqueue_resource_completion_expiry_tick, and enqueue_resource_completion_operation refuses a host Expire, since a host cannot know which boundary will admit it. Snapshots hold these records next to the kernel state. Restore and replay through the wrappers, which reject forged digests, conservation failures, revision mismatches, and missing exact evidence:

  • resource: from_resource_snapshot_json, from_resource_checkpoint_journal, replay_resource_from_journal, and validate_resource_runtime after another loader;
  • production: from_production_snapshot_json_with_archives, from_production_checkpoint_journal_with_archives, replay_production_from_journal_with_archives, and validate_production_runtime_with_archives whenever archive state is present.

The _with_archive_store and _with_archives restores take each store as Arc<dyn … + Send + Sync> and attach it to the run: the run and its forks share it, and a fork may move to another thread, so keep a held store’s maps behind a Mutex or RwLock. A store you only lend for one call, such as to prepare_resource_archive, is passed by reference and needs no such bound.

Exact replay reads the recorded commands and ingress, so the host code that queued them and any decision controllers do not run again. See Save, replay, and fork.

Finished work leaves terminal records in the resource root: operation outcomes, closed demands and reservations, settled transfers, and receipts. They count against the candidate budget and are validated by every restore, so your host moves them out to a content-addressed archive, one archive commit at a time:

  1. ResourceState::prepare_resource_archive(candidate_limit, store) selects hot terminal records and prepares their archive objects. It reads the archive store to extend the lookup index, so pass the store you archive into. For the first commit of a run there is no index to read yet, and () is enough for this call.
  2. enqueue_resource_archive stores the objects, verifies them through the batch’s store_and_verify, and queues the commit. The next boundary applies it.
  3. finalize_resource_archive_retention acknowledges the commit after it has settled. It reads how the commit settled from the state-side retention handle, so it also works after a later commit archived the receipt.

Production has the same protocol for its own archive: prepare_production_archive(candidate_limit, store), enqueue_production_archive, and finalize_production_archive_retention. Since 0.16.0 the production archive has the same lookup index under its own namespace, one commit carries up to 16 blobs of 64 records, and the maintenance receipt of an applied commit is archived by the next commit, so the root holds at most one and a run may make any number of commits; archived_production_terminal_record looks a record up. The fiscal extension and the two reference economy plugins archive the same way.

  • Volume. One resource commit carries up to 16,384 records in up to 64 pages, and a run may make any number of commits. A commit applies only to the root it was prepared from, named by a digest of the state revision, the admitted sequence, the number of hot terminal records, and the archive head; prepare it from the whole root (resource_state).
  • Lookup. Every resource operation asks the archive whether its operation key was used before, so that a re-sent operation stays a no-op after its outcome has left the root. Each commit extends a cumulative lookup index, and the check reads the head directory and one authenticated page per index level. In the recorded measurements a miss read 4 objects at about a thousand archived records and 7 at about a million, whatever the number of commits. A missing or damaged archive object is an error that fails the boundary, which settles once the store serves the object; it is never read as “not archived”.
  • Order and capacity. Within one boundary, archive commits and retention acknowledgements (the packets that finalize_resource_archive_retention queues) settle before the resource plugin’s other packets. An archive commit never fails for lack of room. A commit that went stale, because another commit settled first, changes nothing, and its receipt is removed when you finalize it; a repeated acknowledgement settles as a no-op.
  • Keep the lookup pages. A restore, an exact replay, or a fork reads the lookup index of every archive head it passes through, and the engine never deletes a page. Each prepared batch lists superseded_lookup_pages, the pages its index no longer reaches. Delete them only if you give up restoring, replaying, and forking from points before that commit, only after the commit settled as Applied, and only once a durable snapshot or checkpoint taken after it exists. live_resource_archive_lookup_pages and live_production_archive_lookup_pages recompute the pages a live run still reads.

Ordinary settlement never scans cold history. Production restore validation resolves each compacted resource input through the resource archive’s lookup index, one index path and one blob per input, and audits the resource archive once whenever it has a head.

No packet a host application or a holder can send fails a resource boundary. A packet that cannot settle for an expected reason changes nothing, leaves its operation key free, and is named by one canwu.resource.ingress_skipped.v1 event (RESOURCE_INGRESS_SKIPPED_EVENT, read back with ResourceIngressSkipV1::from_event) with its ingress ID, the key it carried, and a reason, while the boundary commits with every other packet:

Reason Meaning What makes it settle
operation_key_taken A different request holds the key, in the root or the archive Nothing: send the request under another key
reserved_operation_key A request that is not a completion operation names a key under resource:completion: Nothing: use a key outside the prefix
failed_settlement_checks The packet does not decode, or fails the checks its ingress makes at settlement (its tracked command, provider binding, certificate, boundary time, current evidence, or a refused credit of a production output batch) A corrected packet, which may reuse the key
archive_backlog The result does not fit the archive-candidate or hot-outcome budget, and an archive commit of every hot candidate would make room The same request, sent again once a commit has applied
reserved_capacity The result does not fit even beside an empty backlog: the room is held by the reservations of admitted leases or by finished participants whose reports wait, or the budget is smaller than the work (the detail says when nothing can ever make room) The same request, once leases complete, are released, or expire, or a larger max_archive_candidates
root_limit The result passes another limit of the root, in practice the share of the state byte cap operations may use (ResourceLimitsV1::operation_state_bytes, the cap less MAINTENANCE_STATE_BYTE_RESERVE) The same request once the root is smaller

Room is measured on the result, so a refused operation needs room for its outcome only, and an applied one for the records it adds and the reservations it makes. A resend whose outcome is already recorded settles nothing and leaves no event. A domain refusal (a stale revision, an unknown target, a lease the budget does not allow) is still the operation’s recorded rejection.

Work another plugin scheduled is never handed back to the host, because that plugin may never send it again. A production output batch, or a completion step a plugin scheduled that can settle later as the same step (Prepare, Abort, Release, and the external participant’s prepare, complete, and release), that does not fit while an archive commit or a lease release can make room is deferred: the resource plugin queues the same work to itself as an internal resource_deferred_packet_v1 packet due one minute later, doubling with each further deferral up to 1,024 minutes, retimes a batch’s credits to that instant, and emits the skip event with deferred_until set. Work waiting on a backlog or on reserved room is retried until the commit or release that ends the shortage; work waiting on the byte cap is skipped after its 1,024-minute retry; work no commit or release can ever fit, and steps bound to their boundary time (an acquisition, a grant, an activation, a consumption, an expiry, a single step), are skipped at once. While a deferral waits the ingress queue is not empty, so the run cannot seal; the host’s archive maintenance ends the wait. An expiry tick skipped for room schedules no successor; the next applied archive commit resumes it, and enqueue_resource_completion_expiry_tick queues one by hand.

A batch skipped for room, for a taken key, or for an output account that changed revision while it waited leaves its execution CompletedPendingOutputSettlement; production refuses the keys and output sets that would make that certain when the execution starts. What still fails a resource boundary is an invalid record or an engine invariant: an archive object that is missing, forged, or unreadable during a key lookup, a forged archive commit or retention acknowledgement, a root that does not validate for a reason other than a limit, maintenance writes past the byte reserve in one boundary, and arithmetic or encoding failure.

Production applies the same rule: a packet that does not decode is refused with canwu.production.ingress_refused.v1 (canwu.production.output_ack_refused.v1 for an acknowledgement), an operation packet settles only when the admission of a tracked production command queued it, a completion step or continuation witness that production’s checks refuse settles nothing, and a stale or duplicate archive commit settles as a no-op. What can still fail a production boundary is a forged archive packet from another plugin, a failed store read, counter overflow, and the root’s 256 MiB byte cap.

  • Content: resource and unit revisions; opening accounts with their custodians, capacities, and place_scope; floor policies; process revisions; sites; and facilities with their incident risk. canwu-economy-reference-content shows a content pack whose ResourceCapabilityRevision records place deposits on a ResourceCapabilityStage from Potential to DeliveredAccepted.
  • Authority: who may create accounts, submit demands and under which requester, and issue access grants. A Pooled or ExactAccounts source policy only selects accounts; spending another custodian’s stock needs an access grant and a Granted policy.
  • Timing and order: when to queue an allocation pass for each requester, and the priority and tie-break key of each demand. The grain loop takes its priorities from a decision ticket.
  • Evidence kinds: the record kinds passed to ResourcePlugin::new, the realization_evidence_kinds of each process revision, and the authority records that grants and outflows cite.
  • Consumers: your plugins authorize consumption intents in a resource_consumption_intents map and react to outcomes.
  • Transport: itineraries and execution through canwu-transport and canwu-movement, whose evidence accepts arrivals.
  • Controllers for degraded-facility tickets, plus money, markets, prices, wages, combat formulas, and the client UI.
Limit Value
ResourceLimitsV1::canonical() 8,192 accounts, demands, and transfers each (hard maximum 262,144; a pooled demand’s allocation pass reads every account, about 30 µs each, which is why the canonical account count stays there: a host that raises it draws on exact accounts or grants); 16,384 definitions and unit revisions; 2,048 allocation candidates per pass and 2,048 dirty demands (hard maximum 16,384 each); 32 consumption batch items per boundary (max_batch_items_per_boundary, hard maximum 2,048) and 256 reports per boundary; 8,192 hot operation outcomes (hard maximum 16,384); 256 hot terminal archive candidates (hard maximum 16,384; the maintenance receipt budget must be at least as large); 256 MiB of state, measured on the live root plus the hot terminal and live entries
MAX_DEMAND_SOURCE_ACCOUNTS 256 accounts in an ExactAccounts or Granted list
MAX_RESOURCE_ACCESS_GRANTS 16,384 grants per resource state, revoked ones included
MAX_RESOURCE_TRANSFER_LINKS 16 entries in a transfer’s link history, at most 14 of them in transit; a further intermediate advance is refused with limit_exceeded; an arrival, a return, or a terminal disposition is always admitted. Each link adds about 286 bytes to an open transfer in the live root (measured once, with 20- to 30-character identities)
MAX_RESOURCE_CONSUMPTION_BATCH_ITEMS 256 items in one consumption batch; six hot terminal records per item that closes its demand, plus two for the batch
Completion leases 16 terminal receipts per lifecycle (MAX_COMPLETION_RECEIPTS_PER_LIFECYCLE); 16 pending acquisitions per authority and 1,024 in total; a grant that is never activated expires after 8 boundaries (PREACTIVATION_LEASE_TTL_BOUNDARIES); an applied single-step lease takes 2 + 16 archive candidate slots and 1 + 16 hot outcome slots, a refused one only the slot of its outcome
Protected floors 4,096 policy revisions in one resource root, authored and content-addressed together (MAX_PROTECTED_FLOOR_POLICY_REVISIONS); 16 override classes of 1 to 160 bytes each in a content-addressed revision
Production output batch 1 to 64 credits under one certificate
ProductionLimitsV1::canonical(), per root, fixed by the scenario and validated against maximum() 16,384 sites, facilities, work orders, executions, and capacity allocations (maximum 65,536 each); 32,768 operation outcomes (maximum 131,072); 16,384 observation heads, dirty entries, and archive-due entries; 8,192 rolled-out heads (max_observation_rollover); 4,096 operations and batch items recorded per boundary (max_operations_per_boundary, maximum 65,536) and 1,024 items per batch (max_batch_items, maximum 4,096, never above the operations bound); 512 incident checks and 64 reports per boundary; 256 MiB of state. Every bound is a root’s own and adds up across roots, except max_observation_records and max_observation_records_per_holder, which count across the world
Process and report shape 64 requirement groups (MAX_REQUIREMENT_GROUPS), 16 alternatives per group (MAX_REQUIREMENT_ALTERNATIVES), 256 facts and blockers per report (MAX_REPORT_FACTS)
Projections 8 source adapters (MAX_TYPED_SOURCE_ADAPTERS), 256 observation facts (MAX_OBSERVATION_FACTS), 32 price factors (MAX_PRICE_FACTORS)

Reaching a limit returns an error, a rejected outcome, or a skipped packet, and no stock changes. Every lease reserves its terminal records before any debit, so accepted work can still finish when the hot archive is full; the next lease, four-step or single-step, is skipped as reserved_capacity until leases release room or max_archive_candidates is raised.

The archive candidate budget, max_archive_candidates, needs the most care. An operation whose result does not fit settles nothing and is named by a skip event with archive_backlog (an archive commit makes room) or reserved_capacity (it does not); it is not a recorded rejection, its key stays free, and the sender sends it again once room returns. With archive_backlog, queue one commit that selects every candidate, in the same boundary if you like, because archive commits settle first; a second commit prepared from the same root goes stale once the first applies. A commit frees at most the budget minus one record, because its own maintenance receipt stays a candidate, so the requests beyond that are skipped again and settle behind the next commit. Nothing is queued for a skipped request, so a long shortage costs events and resends, never a stopped run.

The 16,384 ceiling is headroom for a burst, not a working size. A boundary that settles an operation costs the same whatever the backlog (about 0.2 ms and 8 KB of journal in the recorded measurements, at 0 and at 4,000 hot records), because it writes only the live root and the buckets it touches. The backlog is paid for when it is archived: the boundary that applies a commit rewrites one bucket per archived record, about 25 microseconds and 1.3 KB of journal each (103 ms for 4,054 records, 524 ms for 16,054), preparing a commit reads every hot record, and a restore validates them all. Commit a large backlog in batches, acknowledge each before you prepare the next, and keep in mind that a 64-page commit that goes stale leaves a retention handle of about 13 KB in the live root, so a root near its byte cap has room for about five unacknowledged stale commits of that size.

终端窗口
cargo run -p canwu-economy-reference --example grain_loop
cargo run -p canwu-resource --example resource_lifecycle
cargo run -p canwu-resource --example completion_lease
cargo run -p canwu-production --example process_constraints
cargo test -p canwu-resource --test resource_contract
cargo test -p canwu-production --test g3_contract
cargo test -p canwu-force-supply-reference --test g4_contract
cargo test -p canwu-economy-reference --test grain_harness

grain_loop prints a GrainLoopSummary as JSON; its conservation_closing equals final_stock (2,206) because no transfer is left in escrow. The grain, production, and military supply case walks through it month by month. resource_lifecycle builds a ResourceState without an engine and allocates one demand, completion_lease ends with lease state: Activated, and process_constraints prints the three blockers of a machine process (ToolsMachines, Energy, and Maintenance).

The resource_contract tests cover conservation, protected floors, transfer escrow, and leases. The g3_contract tests cover overlapping capacity, output settled only after the acknowledgement, incidents staged in phase 10 and committed in phase 11, and degraded-facility tickets. requisition_keeps_resource_force_externality_and_ack_as_distinct_steps in g4_contract walks the requisition saga, and snapshot_checkpoint_journal_and_fork_continue_identically in grain_harness checks that a snapshot, a checkpoint journal, and a fork reach the same checkpoint hash.