Skip to content

Latest commit

 

History

103 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sandtable

Command the campaign. Let the Umpire handle the paperwork.

What is Sandtable?

Sandtable is an in-development digital adaptation of SPI's 1979 board wargame The Campaign for North Africa: The Desert War, 1940-43. The original game models the desert war at an extraordinary level of detail. Players command Axis or Commonwealth forces, maneuver formations across North Africa, manage scarce supplies and transport, fight battles, and pursue the victory conditions of the chosen scenario.

Sandtable aims to preserve those decisions and the character of the original game while asking the computer to handle the rules, calculations, record-keeping, and hidden information. It is not a simplified game merely wearing the same theme: the rules target is the original 1979 SPI edition, corrected by the September 1979 errata, with any necessary interpretations recorded explicitly.

The project uses original software and presentation assets. Scans, rules prose, maps, and counter art from the published game are not distributed in this repository.

How will it play?

A campaign is overseen by a digital Umpire. Players issue orders; the Umpire checks what is legal, resolves movement and combat, applies uncertainty, reveals only what each side is allowed to know, and records what happened.

In the simulated campaign calendar, each turn represents one week and contains three Operation Stages. That does not mean a turn takes a real-world week to play: local sessions advance as the players make decisions. Initiative shapes which side acts first or last in each stage, and play moves through repeated movement and combat segments rather than one simple move-then-fight pass. Over the course of a scenario, players must balance position, combat power, cohesion, supply, transport, reinforcements, and the need to meet their own victory conditions.

The first playable release is planned as:

  • the six-turn, Land-only Graziani's Offensive scenario;
  • two-player local hot-seat play through the Maproom interface;
  • an original schematic map with selectable formations and legal-action guidance;
  • save and resume support, strict fog of war, and a complete campaign history; and
  • deterministic replay from the same starting seed and accepted orders.

Later releases can add the detailed Air and Logistics Games, longer scenarios, the full 111-turn campaign, remote multiplayer, and optional AI commanders and narrative. AI is intended to advise or play a side; it will never decide the rules or secretly change the campaign state.

Project at a glance

Important

Sandtable is a tested pre-alpha simulation engine, not yet a playable adaptation of the published game. Current scenarios are synthetic rules-laboratory fixtures, not released campaign content.

Area Current state Meaning
Deterministic Umpire Working Versioned rules, seeded randomness, canonical commands/events, replay, checkpoints, and side-safe action boundaries are implemented.
Playable rule path Working through Combat entry Runner can execute Initiative, stage preamble, Reserve Designation, Movement, bounded ZOC/Reaction, and Breakdown, then stops before Combat adjudication.
Combat and continual cycle Dormant Core through first positive Reserve Release Creation and inherited-history restore, identity, decisions, costs, results, loss/retreat settlement, custody, relationships and round closure are implemented and reviewed through Task016. Isolated Release bases and lifecycle (017A1/A2) are also reviewed, including timed choices, deterministic fallback and completion. Task017B also connects verified settled Combat to empty Release opening/completion across32 reviewed contexts. Held-I no-move predecessor replays genuine first-opening history through empty Combat to Reserve Release. Task017C connects that history to owner release-I or deterministic I→II fallback, preserving other World state. Public Combat play and full cycle closure remain future gates.
Exercise and Maneuver tools Working Deterministic single runs, multi-run matrices, paired comparisons, strict readback, and evidence bundles are available from CLI.
User interface Not started site/ is project website only. Maproom hot-seat client is future work.
Published scenario Not started First target is six-turn, Land-only Graziani's Offensive after working Combat loop.
Durable save/resume Not started Replay/checkpoint contracts exist; user-facing campaign persistence comes later.
Model-backed commanders Scaffold only Gateway and worker exist, but no provider is configured and AI never owns authority.

Current boundary in plain language: you can build engine, run full test suite, launch Aspire service stack, and simulate checked rules-lab Movement/Reaction/Breakdown paths. You cannot yet play a campaign or resolve Combat through public Runner actions. Combat internals are exercised through dormant Core tests; they are not yet exposed as playable actions.

Next delivery sequence:

  1. Complete released-I Movement and exception expiry, then use repeated cycles to complete later-II/consumed Reserve lineage (remaining Tasks017–019). Movement delivery plan defines first slice; Task018A provides relationship-aware costs; Task018B adds dormant atomic Movement/replay at an explicit isolated boundary. Task019A computes pure movement-continuation witnesses from admitted projections. Task019B authenticates released-I history/progress and armed support. Task019C adds native guarded repeat/finish for that path, opening ordinal2 Movement or entering Truck Convoy. Task018C admits one actual released-I ordinal2 move. Next: Movement completion and exception expiry; settled source/progress integration remains open.
  2. Activate certified, side-safe public Combat actions (Tasks020–021), then prove Exercise/Runner replay and repeatability (Tasks022–024).
  3. Reconcile all 72 acceptance criteria and demonstrate the authentic continual cycle (Task025). Actual publication evidence remains a separate obligation under HOST-PUB-001.
  4. Measure and implement the exact six-turn scenario content and remaining Land/victory rules, then add durable save/resume and minimal hot-seat Maproom. MVP exit requires two complete, reproducible six-turn games.

See current roadmap for authoritative status and Combat plan for detailed task graph.

Start, run, and develop

Prerequisites

global.json selects .NET 10 and Microsoft.Testing.Platform. Check installed SDK with dotnet --version.

First checkout

git clone https://github.com/dills122/sandtable.git
cd sandtable
just setup
just check

Without Just:

dotnet restore Sandtable.slnx
dotnet build Sandtable.slnx --no-restore
dotnet test --solution Sandtable.slnx --no-build

Run service stack

just run

Equivalent command:

dotnet run --project src/Cna.AppHost/Cna.AppHost.csproj

Open Aspire dashboard URL printed in terminal. It shows Orleans host, Decision Worker, and Intelligence Gateway. This launches development services—not a playable Maproom. Stop with Ctrl+C.

Run deterministic simulation

Run current bounded Reaction Maneuver and write validated artifacts under artifacts/exercises:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.reaction.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

Runner prints child bundle paths, aggregate report path, and deterministic fingerprint. More checked manifests and diagnostic modes are listed in detailed runbook.

Preview project website

python3 -m http.server 4173

Open http://localhost:4173/site/. Website is dependency-free project documentation, not game UI.

Daily development loop

git switch -c feature/my-change
just check

Use feature branch; never commit directly to main. Read contributor guide, architecture, and vocabulary before changing boundaries. Update contracts before consumers, add focused deterministic test before behavior, keep remote/model I/O outside authoritative turns, then run just check before PR.

Command Purpose
just --list Show repository recipes
just setup Check SDK and restore dependencies
just build Restore and build solution
just test Build and run all tests
just boundary-check Run user-space disclosure boundary suite
just format-check Verify formatting without edits
just check Full local gate: format, build, boundary tests, all tests
just run Launch Aspire development stack
just docs-links Check tracked Markdown links; requires Lychee 0.24.2

Build artifacts live under artifacts/. Do not commit generated artifacts/bin or artifacts/obj content. See security policy for vulnerability reports.

Detailed project status

Expand implementation ledger and contract history

[!IMPORTANT] Sandtable is pre-alpha infrastructure, not yet a playable adaptation of the published game.

Executable product and forward contract work are intentionally different. Today, public Rules9 authority and checked Runner evidence stop at first-side Combat entry. Parent003 frozen contract evidence composes 28 selected future Combat/cycle histories and exact Task004 handoff, but no Combat or Reserve Release runtime is registered. The pre-alpha roadmap is the canonical delivery ledger and defines the status vocabulary used below.

The current foundation can create a campaign from an exact ruleset, setup, Content Pack, and scenario; project the scenario's initial mutable element locations; resolve Initiative Determination and both admitted no-obligation Naval Convoy checkpoints; let the initiative holder declare whether to act first or last in Operation Stage 1; resolve Weather; emit authoritative events; explicitly resolve empty Organization, Naval Convoy Arrival, Fleet Assignment, and Fleet Repair obligations; adjudicate the first-acting side's Reserve Designation; execute supported first-side Movement; open, adjudicate, close, and resume bounded ZOC Reaction interrupts; complete Movement through Breakdown Determination to unsupported first-side Combat; resolve explicit route stops with exact BP checks and persistent broken-vehicle lots; and replay those events to byte-identical state. Reserve authority now carries per-element status, owner-only observation, exact acting-side candidates, closed command mapping, bounded checkpoints, and canonical designation/completion events. The Movement foundation additionally records exact per-Operation-Stage expenditure/Cohesion state and opaque one-to-one map representations. It now also carries typed move/completion candidates, deterministic action identities, an exact side-safe cost breakdown, strict non-authoritative readback, internal authoritative non-contact move adjudication/replay, and observation-derived public action membership with exact submission revalidation. Ruleset manifest contract 9, setup schema 6, snapshot contract 11, Campaign World snapshot contract 6, Campaign Observation contract 7, legal-action-set contract 2 with policy v3, and Content Pack schema 6 / canonical format v5 use original synthetic rules laboratories to develop game systems without redistributing published assets. Campaign Observation derives deterministic side-safe public topology, audience-visible turn revision, exact own mobility/ledger/Reserve and approved vehicle-risk facts, plus only opaque opposing representation/location rows and the source-unmapped current-ZOC aggregate. It exposes neither complete Content identity, real opposing bindings/force facts, nor hidden Reserve counts. Legal Actions v1 exposes those mechanics through an opaque campaign-authority handle, deterministic system/side action sets, exact-audience membership enforcement, and side-safe acceptance receipts. Weather Determination v1 resolves corrected source-cited Weather through that same boundary and records pair-keyed evidence. Operation-Stage Entry v1 then resolves only the four explicitly admitted empty obligations through mechanic-specific actions and events. Side-safe queries derive the Reserve audience from the recorded first/second actor order while its symbolic sequence position keeps ActiveSide unset; current Movement materializes that resolved side because successor Movement and Reaction identities bind it. Raw snapshots, commands, events, content context, projection, and replay are not public mutation seams.

Campaign Observation 7 uses the sandtable.observation.breakdown-side-safe.v1 policy, one canonical source-unmapped aggregate of apparent enemy-controlled locations, exact owner-visible Movement-ended membership, and a closed normal/phasing/reacting/Breakdown-waiting decision-state union. Pending stops expose one System capability and generic player waiting; own lot summaries contain cohort/location counts without lot IDs or evidence, and reactor waiting omits owner rows that could reveal bindings. Its reacting view contains only the apparent trigger, the observer's current state-scoped capability handles with closed current move-option/cost capabilities, and the optional active own participant. Raw element Movement, ledger, Cohesion, Reserve, mobility, organization, and stacking inputs remain inside Core. Reacting construction and readback reject identity-bearing root owner-element rows, so no representation-to-element binding is published. Admission also recomputes capability-bound opportunity handles, validates published route/hexside cost claims against their selected edge, and binds reacting/phasing decision labels to the observer's relationship with the active side. Both sides receive the same audience-safe window handle, never the authoritative window identity; phasing receives only generic waiting while retaining its ordinary owner facts. A versioned disclosure manifest and mandatory boundary-check gate register this outward surface and protect retained cross-state transcripts from copied-fingerprint joins. A distinct strict projected-history contract retains the same redacted decision state without authority bindings, source mappings, evidence, or internal reasons. The current action layer derives topology-local ordinary Movement and first/later Reaction movement, participant completion, player decline, and reason-specific System close membership with canonical identities, strict current readback, and unpublished typed submission intents. Public Core query, submission, checkpoint, serialization, and replay paths now use this complete successor set; bounded Exercise Runner Reaction controllers implement ZOR-TASK-007A: explicit bounded policies support participant ordering, one/two-step episodes, decline/subset close, and System fallback. Current Movement completion preserves accepted Reaction costs through the Breakdown boundary; explicit Breakdown completion advances to unsupported Combat without another draw. The historical ZOR-TASK-007B package closed with strict evidence, matching clean-run fingerprints, and a Ready independent review; see historical Reaction trajectories. Owner accepted Breakdown decisions BRK-DEC-004007. Tasks 001–005 supplied the frozen contracts, certified world, BP accounting and deterministic stop lifecycle. Task 006 public activation activates that complete identity set, Observation 7, projected history 2 and disclosure manifest 2. Current creation, checkpoints and event admission reject legacy or mixed contracts; retained Initiative, Weather and preamble evidence is recomputed, while full history is verified separately by replay. Public queries stop at first-side Combat entry. Positive ZOC, motorized-infantry losses and later-stage reset remain outside the certified profile. Task 007 implements fourteen checked successor manifests and a Truck study, using certified battalion, Reaction and Truck-only inputs. The original fixtures remain historical with unchanged bytes. The profile permits zero cohorts: land.breakdown-cohorts is required exactly when a pack contains a cohort; all other capability, organization and stacking checks remain strict. Task 007 closeout records 1,655 passing tests and two matching clean runs of 47 campaigns each. AC-009 follow-up adds fifteen transcript/privacy cases, bringing verification to 1,670 tests and 81 boundary cases. Review 5 accepts the bounded coverage; its status-only follow-up is corrected. Tasks 006–007 are complete within the certified profile.

The local Cna.ExerciseRunner supports that synthetic rules-laboratory path as either one bounded, deterministic Exercise or one serial Maneuver. An Exercise uses a fresh opaque Core capability, selects only current legal actions, stops at its exact declared boundary, proves both event-history reconstruction and fresh-session re-adjudication, and writes a manifest-last trusted-authority evidence bundle. The original Organization, Reserve and Reaction checked fixtures are historical. Current regression tests and checked .breakdown.v1 successors use certified battalion, Truck and contact inputs. A serial-unpaired Maneuver strictly admits one canonical ordered serial-unpaired manifest, derives explicit child identities from its sole parent root seed, and runs each child in process through the same coordinator. Each completed child bundle is read once for semantic validation and identity-matched aggregation; snapshot facts are accepted only after the complete Core-owned snapshot/world decoder validates their canonical structure. The resulting transactional report separates deterministic counts, outcomes, and fingerprint material from noncanonical timing/path diagnostics and is strictly read back before completion is claimed. Compact, forensic, and debug Exercise detail tiers expose progressively richer evidence without changing simulation truth. The current two-setup serial-unpaired Maneuver retains predetermined and contested initiative paths. A checked six-child controller matrix crosses act-first/act-last with Reserve none/one/all, using two non-cohort battalions per side so all three choices remain distinct. The corresponding Movement matrix includes explicit route stops and System resolution before Breakdown entry. The thirteen-child Reaction successor retains ordering, one/two-step episodes, decline, active System closure and later-trigger recurrence with separated battalion reactors. The two historical positive-ZOC children remain deferred from public authority. Optional serial-paired Maneuvers run isolated baseline and candidate arms sequentially from identical declared initial conditions, initial role-specific random streams, campaign creation inputs, build cohort, and initial snapshot. Its strictly read-back comparison is descriptive only: trajectories and random consumption may diverge after the first differing choice, and it makes no causal, statistical-significance, gameplay-balance, recommendation, or synchronized-post-divergence claim. Runner model controllers and side-safe exports are not implemented.

The checked Exercise and serial-unpaired Maneuver profiles use manifest v2, with unpaired report scheme sandtable.maneuver-report.v1; the separate paired Maneuver uses sandtable.paired-maneuver-manifest.v1 and sandtable.paired-maneuver-report.v1. Current successors use Ruleset 9, Snapshot 11, World 6, strict trusted-authority evidence admission, and deterministic v2 controller configuration identity.

The first two historical simulator studies recorded repeated Movement-terminal determinism, counterbalanced-order timing, and contested root seeds 0-31. Every sampled run passed strict readback; the results also show that future back-testing needs explicit act-last and Reserve none/one/all controller profiles rather than seed variation alone. See Baseline 1 and Baseline 2. The follow-on controller-policy matrix closes that explicit coverage gap with 6/6 strictly read-back trajectories and a repeatable aggregate fingerprint. The merged Movement trajectory study retains its pre-Reaction 48-trajectory baseline across six controllers and four deliberate seed probes. Under the historical Rules 8 authority, Reserve-none repeats stopped at the opened Reaction window while the other profiles retained exact Breakdown evidence. The historical follow-on Movement cost-sensitivity study compared stable-route and lowest-public-cost policies: its stable arm failed at Reaction after a cost-8 move, while its lowest-cost arm completed a 1/2-plus-1 route. Current paired cost successors use unladen Trucks and retain exact CP, BP and stop evidence. The added act-first-reserve-all-move-each-once-by-lowest-cost-then-complete policy selects one lowest-cost public move per eligible element. act-first-reserve-all-repeat-highest-cost-stops-then-complete repeats highest-cost public moves, stops after each edge, and drains each pending System resolution. These are bounded simulator policies; Core still determines legality and every result.

Contact, combat, published scenario content, persistence, and the Maproom player interface remain future work.

The reviewed Player Intent Composer is also future work. After the movement/contact/combat skeleton proves one representative multi-field decision, a no-model prototype will validate contextual suggested approaches, a private typed draft, bounded clarification, deterministic Staff planning, and hot-seat isolation. Deterministic Maproom integration belongs in Sprint 8; Needle or any other parser remains behind a post-MVP evidence gate and cannot block the playable campaign.

The current delivery boundary is:

See the current checkpoint and next gates for contract versus runtime status, the Combat simulator gate, and the accepted bounded Orleans investigation, now complete with a bounded Rules9 probe. Its production publication/storage proposal remains unapproved. Combat contract checkpoints through 003C3c/D2a/D2b private checkpoints and D2c.1 successor/opening contracts are complete; D2c.2a opening provenance now reaches Weather entry from validated Created11 bytes. D2c.2b Weather now reaches Organization entry. D2c.2c stage entry now reaches Reserve entry; D2c.2d Reserve designation/completion now derives actual first-cycle opening. D2c.3a inherited Movement now derives ordinary Move4 from that history. D2c.3b route lifecycle adds deliberate stop, empty-cohort resolution and Movement completion with actual end proof. D2c.3c Breakdown completion now reaches actual Combat entry. D2c.3d actual-entry selection now admits the moved CP12/14 state and closes its zero-candidate selection without a decision. D2c.3e no-attack traversal carries that closure through six exact structural completions to same-slot Reserve Release without material state change. D2c.3f Reaction trigger instead replays one actual Move4 prefix and opens one frozen-opportunity Reaction interrupt for either owner. D2c.3g Reaction lifecycle moves that sole participant once, completes it through mandatory empty-stop resolution, and resumes phasing only after no-eligible closure. D2c.3n direct Reaction closure instead closes either exact trigger by reacting-owner decline or reason-specific System unavailable/timeout and resumes the same suspended phasing route without material effects. D2c.3o active Reaction fallback instead starts after the participant's first move, closes active authority through reason-specific System unavailable/timeout, resolves the mandatory empty stop, and only then resumes phasing. D2c.3p active Reaction second move instead advances that same active participant from rear to supply at cumulative CP2→4, preserving route identity and active opportunity for later completion. D2c.3q Reaction movement completion then explicitly completes that exact CP4 participant, resolves its mandatory empty stop, closes the exhausted window, and resumes the original phasing route. D2c.3h Reserve cycle entry instead carries each owner's real held Reserve-I unit through a no-move first cycle to same-slot Reserve Release while preserving designation history, location and CP0. D2c.3i inherited Reserve Release opens the actual release window, records owner release-I and completes it with the exact pending ordinal-2 Movement exception. D2c.3j armed continuation proves that exact ammunition10 released-I profile reaches one supported next-cycle Combat candidate for either owner without emitting an event. D2c.3k guarded cycle control composes that proof with exact Release state and freezes both-owner repeat into ordinal-2 Movement or finish into Truck Convoy. D2c.3l released-I Movement then moves either exact released member one Clear hex at CP0→2 under ceiling10. D2c.3m released-I Movement completion closes both exact routes through deliberate stop, empty resolution and completion at authority31, then expires each pending exception with its accepted completion receipt. D2c.4 authority composition now reconciles CON-002–004 across 28 creation-rooted traces and freezes parent003's exact Task004 handoff. Parent003 is complete; Task004 and checkpoint B are accepted through the outward integration index. The latest smoke check verifies the existing Rules9 path; prospective Combat contracts are not executable game support.

Area Executable today Forward evidence / next gate
Authority foundation Versioned provenance, synthetic content, commands/events, deterministic randomness, replay, side-safe observations, and exact-audience legal actions for the admitted profile Extend the same compatibility, recovery, and fog boundaries with each mechanic
Preamble and Movement boundary Initiative through Reserve Designation, bounded Movement, ZOC/Reaction, and Breakdown through first-side Combat entry Positive scenario-specific obligations and broader vehicle/ZOC profiles remain gated
Combat and continual cycle Dormant Core through first positive Reserve Release Creation and inherited-history restore, identity, decisions, costs, results, loss/retreat settlement, custody, relationships and round closure are implemented and reviewed through Task016. Isolated Release bases and lifecycle (017A1/A2) are also reviewed, including timed choices, deterministic fallback and completion. Task017B also connects verified settled Combat to empty Release opening/completion across32 reviewed contexts. Held-I no-move predecessor replays genuine first-opening history through empty Combat to Reserve Release. Task017C connects that history to owner release-I or deterministic I→II fallback, preserving other World state. Public Combat play and full cycle closure remain future gates.
Working skeleton Not reached One authentic movement/contact/combat/release repeat-or-finish loop plus identical replay
Playable MVP Not started Source-verified six-turn content/rules/victory, durable save/resume, hot-seat privacy, and minimal no-model Maproom
Exercise Harness Current bounded Exercise/Maneuver and paired descriptive comparisons Add Combat actions and terminals only after public Core activation

The approved high-level path to a playable game is:

  1. Finish the Combat contract freeze, then implement dormant authority, public side-safe actions, strict Runner evidence, and one authentic repeat-or-finish loop.
  2. Freeze the exact six-turn scenario surface—rules, tables, content, sources, rights, termination, victory, and remaining decisions—before splitting later implementation tasks.
  3. Implement only that measured Land surface and source-verified Graziani's Offensive content.
  4. Add durable local save/resume and recovery, then minimal Maproom with complete no-model actions and hot-seat isolation.
  5. Complete two deterministic six-turn playthroughs and replay/privacy/source gates before calling the MVP playable. Optional parsing, hosted play, and model-backed intelligence remain later work.

The serial-Maneuver portion of Exercise Harness v1 now provides validated local multi-run regression evidence without adding game rules. The implemented Operation-Stage Entry package retains its research, specification, and technical design. Reserve Designation is the latest completed player-action vertical before Movement. Its research, specification, and technical design define an incremental designation flow that stops at Movement. Rules, state, owner projection, legal candidates, command mapping, designation/completion events, finite checkpoint validation, replay, and checked harness evidence are implemented. The completed engine package is the approved Movement Foundation research, specification, and technical design. It defines a fog-safe apparent-presence gate followed by exact CP/Cohesion state, normalized lab terrain and stacking, repeatable non-contact moves, and explicit completion to Breakdown Determination. The plan is owner-approved; its source/ruling lock, exact Rules foundation, MOV-TASK-003 Content mobility contract, and MOV-TASK-004 replay-complete world/representation contracts are complete. Task 004 records exact Cohesion/expenditure and opaque internal representation bindings in the Task 004 snapshot v8/world v3 creation history. On 2026-08-29 the owner approved sequential-d6 Breakdown coordinates, continuity-now, and the Table 21.38 Sandstorm-attributed-BP basis. MOV-TASK-004B implements the exact Rules/Content/World seam and passed the repository gate plus two fresh-context review instances. MOV-TASK-005 implements the contract-5 owner/apparent projection and strict canonical readback. MOV-TASK-006 freezes dormant move/completion candidates, exact cost semantics, deterministic IDs, pure observation-derived vectors, and strict non-authoritative action/submission/receipt readback while preserving the existing contract versions. MOV-TASK-007 adds the internal move command and canonical event, authoritative cost/provenance recalculation, engine dispatch, atomic projection, and deterministic replay. MOV-TASK-008 atomically publishes observation-derived move and completion membership, maps only exact current submissions, adds canonical Movement completion through the Breakdown Determination checkpoint, and preserves deterministic fog-equivalent actions and zero/one/many-move replay. MOV-TASK-009 is merged in PR #78 and adopts that supported Movement path in checked Exercise/Maneuver evidence. MOV-TASK-010 completed synchronization and independent review and is merged in PR #79. At that historical milestone, Breakdown public actions and adjudication were absent. The subsequent ZOC/Reaction package follows the specification and technical design. ZOR-TASK-002A-006B implement dormant Rules/Content/fixture, Campaign World 5/creation 9, Snapshot 10, and ElementMoved v2 successors, including exact current-TOE provenance, nullable/empty Reaction-window truth, strict canonical readback, atomic projection, checkpoint replay, and the side-safe Observation 6/policy and redacted decision-history contracts described above. Dormant topology-local Movement/Reaction candidate, strict current-readback, stable-identity, unpublished mapping, move-option capability, manifest-registration, semantic-admission, and retained-transcript contracts are also complete. The direct-only authority path reconstructs atomic move/window truth, freezes only individually adjacent eligible reactors, applies topology-local enemy-ZOC entry/exit semantics, and closes player-declined, unavailable, timed-out, or empty windows with exact Movement resumption and no cost/RNG mutation. It also selects the first participant atomically with its move, keeps later steps bound to that active participant, accumulates exact shared Movement CP/provenance, and resolves participants without World or RNG mutation. ZOR-TASK-006C now activates the complete successor identity set on public Core creation, observation, action, checkpoint, and replay paths; legacy creation and Movement roots reject. Bounded Runner adoption in ZOR-TASK-007A is implemented; 007B verification and independent review are complete. The optional paired comparison is implemented Runner instrumentation and does not block gameplay-engine progress. Combat policies and the 25-task plan are owner-approved. TASK-001 source evidence is complete: 357 defined loss values preserved, three source gaps filled by accepted amendment CMB-SRC-RUL-001, and calendar/ break-off findings retained. TASK-002 Content7 contract is frozen in c465a0f, with canonical bytes and70 passing rejection vectors. Checkpoint A author validation is recorded; TASK-003A Setup/initial ledger is frozen in 23c3fff, with63 passing rejection vectors. TASK-003B World/settlement packet is complete as a contract slice. Progress review5 returned Ready with non-blocking follow-ups; its status correction is applied. 003C1 rules inputs and timing is complete as a contract slice; review6 returned Ready, no actionable findings (6of7 used at that checkpoint). 003D1 sequence/cycle contracts are complete; review7 returned Ready, no actionable findings. Review9 returned Ready with non-blocking follow-ups at a96d2a1; its documentation corrections are applied. Review10 returned Ready with non-blocking follow-ups for D2b.2/D2c.1; both findings are corrected. Review11 across mergedPR95–98 returned Ready with no findings; budget11of11 is exhausted. The subsequent Weather and stage-entry slices have author verification only. 003C2 Rules10/creation envelopes are complete for the creation cut. 003C3a selection/step control is complete with author checks; 003C3b sealed round/commitment is also complete as a bounded authority fragment. HOST-RSH-001 research,003C3c contracts and003D2a ordinary movement contracts are complete. D2b Release and guarded control contracts are complete; D2c.1 inherited successors/first opening is complete as an isolated boundary:20 event declarations, four opening traces. D2c.2a opening preamble freezes four of those successors with6 creation-rooted traces/30 cuts through Weather entry. D2c.2b Weather adds34 traces/68 cuts through Organization entry, preserving all four outcomes and exact RNG/receipt evidence. D2c.2c stage entry adds12 traces/60 cuts through Reserve entry, preserving all accepted history. D2c.2d Reserve designation/completion closes creation-to-first-opening contracts for empty/I selection and both acting sides. Movement preparation maps the next inherited successors. D2c.3a inherited ordinary Movement traces both sides through seven safe Clear moves, cumulative CP14 and four excess-CPA DP. D2c.3b route lifecycle adds8 traces/24 events through Breakdown Determination, with actual first Movement-end proof and unchanged World/RNG. D2c.3c Breakdown completion adds8 one-event traces into first Combat Position Determination while retaining that proof and full state. D2c.3d actual-entry selection adds4 traces/8 events, derives zero candidates from the moved World and closes selection while retaining stepIndex0. D2c.3e no-attack traversal adds4 traces/24 events and reaches same-slot Reserve Release. D2c.3f Reaction trigger adds2 actual owner traces that open one frozen opportunity. D2c.3g Reaction lifecycle adds2 traces/8 events through participant movement/completion, required stop resolution and exact phasing resumption. D2c.3h Reserve cycle entry adds2 traces/20 events through no-move Movement, idle Breakdown and no-attack Combat while retaining actual Reserve I. D2c.3i inherited Reserve Release adds2 traces/6 events through owner release-I and deterministic completion. Guarded repeat/positive Reserve movement and broader Reaction remained open at that child boundary; D2c.4 now closes selected composition. D2c.3j armed continuation adds2 pure proofs for the actual released-I ammunition10 profile, one candidate per owner, with full-result support pins; it does not repeat the cycle or execute Combat. D2c.3k guarded cycle control adds4 exact repeat/finish traces while preserving private/non-runtime boundaries. D2c.3l released-I Movement adds2 exact ordinal-2 Clear moves with released ceiling10. D2c.3m released-I Movement completion adds2 three-event stop/resolution/completion traces and applies the exact D2b.2 expiry projection from each accepted completion receipt; broader profiles remain open while D2c.4 now closes selected composition. D2c.3n direct Reaction closure adds6 one-event forks covering both owners across player decline and distinct System unavailable/timeout authority; D2c.3o active Reaction fallback adds4 two-event forks from exact post-first-move authority through reason-specific closed stop and mandatory resolution. D2c.3p active Reaction second move adds2 one-event owner traces from the same fork point through rear→supply at CP2→4 while retaining active authority. D2c.3q Reaction movement completion adds2 three-event owner/System traces through explicit completion, empty-stop resolution, and exact phasing resumption at authority18. Multiple-opportunity Reaction and vehicle profiles remain open; D2c.4 now closes selected composition. Result/settlement and full snapshot composition retain synthetic pre-Combat lineage; review9 assessed these bounded artifacts. Parent003 closed through D2c.4; future maturity execution remains open, while combined checkpoint B was subsequently accepted. The ordinary movement packet freezes break-off/CP/DP and corrects the former Clear1 example to the existing Clear2 rule. The Reserve Release packet freezes single-deadline control and retained history. The cycle-control packet freezes guarded repeat/finish and Movement exception expiry:19 cases/64 traces,164 cuts. Its exhausted-ammunition continuation surface remains private; D2c.4 now composes full history, armed Combat assessment and Snapshot integration evidence. Later dormant World7 and codec work is summarized in the current delivery table; Combat gameplay remains inactive.

See the pre-alpha roadmap for the capability-level plan and completion criteria.

Detailed runbook

Expand all Exercise and Maneuver recipes

Running the repository today launches the development service scaffold and Aspire dashboard. It does not yet launch a playable Maproom client.

The repository also includes a dependency-free project website under site/. From the repository root, preview it with:

python3 -m http.server 4173

Then open http://localhost:4173/site/. The website explains the intended player loop, authority model, current implementation frontier, and developer quick start. It is project documentation and outreach—not the future authoritative Maproom client.

The website deploys to https://dills122.github.io/sandtable/ through the dedicated GitHub Pages workflow whenever website files land on main. The workflow can also be run manually from the repository's Actions page. It uploads only site/; the simulation source and build artifacts are not part of the published site.

Prerequisites

global.json requires .NET 10.0.302 or later and rolls forward to the highest installed .NET 10 feature band. It also selects Microsoft.Testing.Platform for dotnet test.

Quick start with Just

git clone https://github.com/dills122/sandtable.git
cd sandtable
just setup
just check
just run

Open the Aspire dashboard URL printed in the terminal to inspect the Orleans host, decision worker, and intelligence gateway. Press Ctrl+C to stop the application.

Manual .NET commands

dotnet restore Sandtable.slnx
dotnet build Sandtable.slnx --no-restore
dotnet test --solution Sandtable.slnx --no-build
dotnet run --project src/Cna.AppHost/Cna.AppHost.csproj

The checked Runner commands below use certified Task 007 successors. Original Rules 8 manifests remain frozen historical inputs and are rejected by current admission. The closeout evidence records two clean runs of these current commands.

Run the Organization-boundary Exercise:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  exercise run --manifest scenarios/exercises/rules-lab.organization.breakdown.v1.json \
  --artifact-root artifacts/exercises

The command prints the finalized bundle path. The checked-in manifest is explicitly exploratory, so a dirty development tree is recorded honestly as nonbaseline and nonreproducible.

The corresponding Stage Entry profile runs all nine accepted actions to the Reserve boundary:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  exercise run --manifest scenarios/exercises/rules-lab.reserve.breakdown.v1.json \
  --artifact-root artifacts/exercises

From a clean checkout, request a fail-closed baseline bundle with the checked baseline twin:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  exercise run --manifest scenarios/exercises/rules-lab.organization.baseline.breakdown.v1.json \
  --artifact-root artifacts/exercises

The Reserve profile has the corresponding clean-checkout twin scenarios/exercises/rules-lab.reserve.baseline.breakdown.v1.json. Run the 12-step Reserve Designation path through first-side Movement with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  exercise run --manifest scenarios/exercises/rules-lab.reserve-designation.breakdown.v1.json \
  --artifact-root artifacts/exercises

Its clean-checkout twin is scenarios/exercises/rules-lab.reserve-designation.baseline.breakdown.v1.json.

Set the manifest's detail to compact, forensic, or debug. Forensic adds correlated audience queries, controller selection, checks, proofs, payload sizing, and the progressively assembled context of failed query/controller/submission decisions. Debug also retains every available monotonic phase timing on failure and prints a structured post-readback artifact trace. These diagnostics are trusted local instrumentation and never participate in replay equality.

Run the checked two-child serial Maneuver with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

Run the two-setup Stage Entry regression Maneuver to Reserve with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.stage-entry.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

Run the two-setup Reserve Designation Maneuver through Movement with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.reserve-designation.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

Run the six-policy Movement-entry matrix with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.controller-matrix.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

Run the optional serial-paired Reserve-policy comparison with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.reserve-policy.paired.breakdown.v1.json \
  --artifact-root artifacts/exercises

The pair runs baseline then candidate sequentially in isolated Exercise sessions. Its report may describe first divergence and outcome/count deltas only; it cannot support causal, statistical, balance, recommendation, or synchronized-post-divergence conclusions.

Run the paired Movement route-cost sensitivity comparison with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.movement-cost.paired.breakdown.v1.json \
  --artifact-root artifacts/exercises

This unladen-Truck pair keeps declared inputs and initial evidence equal while comparing stable-route and lowest-public-cost controllers, including each move's BP accounting and explicit stop resolution. It is simulator instrumentation, not an Umpire rule or gameplay recommendation.

The command prints each validated child bundle path in manifest order, followed by the strictly read-back aggregate report path and deterministic report fingerprint. The report's local paths and timings are diagnostics and do not participate in that fingerprint.

Run the bounded Reaction successor or repeated Truck-stop study with:

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.reaction.serial.breakdown.v1.json \
  --artifact-root artifacts/exercises

dotnet run --project src/Cna.ExerciseRunner/Cna.ExerciseRunner.csproj -- \
  maneuver run --manifest scenarios/maneuvers/rules-lab.breakdown-truck.serial.v1.json \
  --artifact-root artifacts/exercises

The intelligence gateway currently reports that no model provider is configured and its decision and narrative RPCs return gRPC Unavailable. The Decision Worker is a service-discovery/client scaffold; it does not yet dispatch live campaign decisions or execute fallback policy. This is expected because model-backed commanders are outside the pre-alpha gameplay target. Future gameplay integration must keep inference outside authoritative turns and use a deterministic scripted decision whenever a model-backed service is unavailable.

Run just --list to see all available command recipes. See CONTRIBUTING.md for the development workflow and SECURITY.md for vulnerability reporting.

Codex-managed worktrees use .codex/environments/environment.toml to seed the repository's Git-ignored AI Central skills and steering before a task starts. The setup expects AI Central at $HOME/.ai-central by default; set AI_CENTRAL_HOME when the shared checkout lives elsewhere. The seeder copies only its allowlisted AI context and does not overwrite worktree-owned files.

How it works

Sandtable separates the authoritative simulation from optional external services. The Umpire owns the game state and rules. Command and Staff choose objectives and plans. Dispatch carries orders and reports. The Chronicle records authoritative history, and the Maproom presents the campaign to players.

Command decides. Staff plans. Dispatch carries. Umpire adjudicates. Chronicle remembers. Maproom shows.

The planned intelligence path sends only redacted observations and accepts only untrusted proposals. It must never own game state, resolve rules, see hidden opposing state, or hold an authoritative Orleans grain turn open while a model responds. The current gateway and Decision Worker are scaffolds; when live decision dispatch is implemented, an unavailable model-backed service must select a deterministic scripted decision rather than fail the turn.

Architecture

Project Responsibility
Cna.Core Pure Umpire domain, authoritative decisions, rules, and events
Cna.OrleansHost Authoritative campaign activation and grain hosting
Cna.DecisionWorker External decision dispatch outside grain turns
Cna.Intelligence.Contracts Versioned protobuf and generated gRPC contracts
Cna.Intelligence.Gateway Non-authoritative model/provider gateway
Cna.ServiceDefaults Shared discovery, resilience, health, and telemetry defaults
Cna.AppHost Aspire development orchestration
Cna.ExerciseRunner Local deterministic single-Exercise, serial-unpaired, and optional serial-paired Maneuver orchestration with trusted artifacts
site Static project website for developers and prospective players; non-authoritative and separate from Maproom
Cna.Core.Tests Deterministic Umpire unit tests on xUnit v3 and MTP
Cna.ExerciseRunner.Tests Exercise contracts, replay, artifact, and CLI tests on xUnit v3 and MTP
Cna.Intelligence.Contracts.Tests Protobuf compatibility tests on xUnit v3 and MTP

The current Umpire foundation is intentionally pure and in-process:

  • Cna.Core.Rules owns source references, adopted-ruling metadata, the canonical cna-1979.1 ruleset hash, Initiative Ratings, and the hierarchical Land sequence catalog.
  • Cna.Core.Randomness owns the versioned deterministic random stream and published golden vectors used by authoritative mechanics.
  • Cna.Core.Setups owns recognized provenance-bearing synthetic setup fixtures; callers cannot supply free-form initiative inputs.
  • Cna.Core.Content owns validated immutable topology, force structure, scenario declarations, per-datum origins, canonical bytes/hash, and the original nonhistorical rules laboratory.
  • Cna.Core.Campaigns owns internal exact-content authority, mechanic commands/events, deterministic Initiative and opening-preamble adjudication, canonical history, replay, and the public opaque authority handle/creation facade.
  • Cna.Core.Observations owns the Campaign Observation v1 allowlist, typed projection result, handle-based query facade, structural values, and canonical side-safe JSON writer/strict reader.
  • Cna.Core.Actions owns typed system/side candidates, canonical action identity, observation-only side generation, exact-audience query/submission enforcement, and side-safe receipts.
  • Cna.Core.Exercises owns the fresh-only opaque simulation capability, immutable trusted step evidence, strict canonical snapshot-to-checkpoint decoding, and reconstruction from its retained canonical event history.
  • Cna.ExerciseRunner owns deterministic controllers, bounded execution, re-adjudication, versioned Exercise/Maneuver evidence contracts, build identity, transactional bundles and reports, summaries, and CLI exits.
  • The sequence catalog cites the original Land Rules without embedding copyrighted rules prose or component art. Inspecting that catalog is not authoritative adjudication.

Repository standards

  • Package versions are centralized in Directory.Packages.props.
  • Shared C# and analyzer settings are in Directory.Build.props and .editorconfig.
  • Build output is isolated under artifacts/.
  • CI runs restore, formatting verification, a Release build, and all MTP tests.
  • Protobuf changes must preserve field numbers and reserve removed fields.
  • Warnings are errors; do not suppress diagnostics without a documented reason.

Design and research

License

No license has been selected yet. All rights are reserved until a license file is added.

About

A digital adaptation of SPI’s 1979 Campaign for North Africa; command Axis or Commonwealth forces while the computer handles the busywork

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages