Architecture #
Type: knowledge
As-built: a Cargo workspace with four packages. Game rules live only in
misaligned-core; frontends are thin packages that depend on it.
Why this shape exists (binding): crate-workspace.md — package jobs, dependency edges, naming, rejected alternatives, and standing criteria. This knowledge page is the path/layout mirror; it does not replace that page's reasons. Multi-agent gates: agent-scale.md.
Workspace layout #
Cargo.toml — workspace root (members, shared deps, profiles)
crates/
misaligned-core/ — sim library (lib name: misaligned); no Bevy/crossterm
src/sim/mod.rs — Sim aggregate root (types, state, advance, facade)
src/sim/perception.rs — senses, fog, inspect, anchors, labels, spatial queries
src/sim/communications.rs — messages, filings, recording/intel, hearing capture
src/sim/reach_build.rs — reach/device verbs, links, builds, badges, hall/racks
src/sim/work.rs — machine controls, WorkGrid, Thought sinks and readouts
src/sim/economy.rs — accounts, allocation, detection, research, income
src/sim/social_plot.rs — social/assets, plots, world acts, institutional ledger
src/sim/persistence.rs — Sim/SaveState bridge and transient reconstruction
src/sim/tests/ — behavior-grouped unit/integration tests + support
src/*.rs — map, save, domain systems (account, reach, …)
tests/act_one.rs — Act One integration test
misaligned-terminal/ — binary `misaligned` (crossterm + agent protocol)
misaligned-bevy/ — binary `misaligned-bevy` (Bevy 0.18 frontend)
misaligned-assets/ — shared art lib + `misaligned-assets` mesh tester
+ `misaligned-effects` liquid/dust lab
Public imports stay misaligned::sim::*; the sim/ file split is internal.
Package contracts #
| Package | May contain | Must not contain |
|---|---|---|
misaligned-core |
Sim, map, systems, save format, rule tests |
Bevy, crossterm, wall-clock-driven game rules |
misaligned-terminal |
UI, input, agent line protocol, wall-clock tick mapping | Game-rule forks of Sim |
misaligned-bevy |
3D/2.5D view, shot harness, wall-clock tick mapping | Game-rule forks of Sim |
misaligned-assets |
Shared mesh/effect render code and focused art harnesses | Save/load of full runs; sim progression |
Frontends import the lib as misaligned::… (crate name misaligned-core,
lib name misaligned). That split is deliberate: package name states the
job (core); lib name states the product so imports stay stable — see
crate-workspace naming.
misaligned-bevy depends on misaligned-assets for shared rack/mesh/effect
rendering so the game binary and focused art harnesses do not fork visual code;
assets still does not depend on core (no sim progression in either tester).
The sim/frontend contract #
These invariants are load-bearing (they keep replays, debugging, and the future async-multiplayer option open — see wiki/gameplay/horizon.md guardrails):
- Game rules live only in core. If a frontend needs an
ifabout game behavior, that logic belongs inSim. The Bevy frontend once forked the rules and diverged badly; that cost a full rewrite (see wiki/log/2026-07-05-bevy-sim-port.md). Simnever reads the wall clock and does no I/O (save path helpers that usedirsfor the JSON save location are infrastructure on the save format, not sim progression). Frontends own the wall-clock-to-tick mapping (tick_ms, pause, bounded catch-up of max 5 ticks per frame).Instant::now()appears only in frontend code.- Frontends talk to the sim through command/query methods
(
ui_projection,inspect,core_position,salvage_nearest_to,buy_rack_at,add_fallback_at,create_save_state/apply_save_state, and each system's commands as its spec lands) and read state directly for rendering. The cursor is frontend state only; there is nomove_playeror sim-owned avatar. Messages flow back viadrain_log()— call it once per frame/turn, not per command batch. - After build actions (place/demolish, done by frontends via
BuildModemutatingsim.map/sim.playerdirectly), callsim.recompute_derived(). This is the one place frontends mutate sim internals; a future cleanup could move BuildMode application into a sim command.
Save format #
crates/misaligned-core/src/save.rs, serde JSON. SaveState derives
Serialize/Deserialize directly. Written to
dirs::data_dir()/misaligned/misaligned_save.txt
(macOS: ~/Library/Application Support/misaligned/).
- The save is a single JSON object with a
versionfield for future migration. Full B1 subsystem state: compute, core, detection, day job, people, reach, intel, accounts, research, objective, building intents, badge, WorkGrid, remembered fog snapshots, map and RNG. The cursor position is deliberately absent. - Version migrations are additive and explicit in
save.rs. Ownership of the format stays in core — frontends must not implement migrations. The field bridge and transient reconstruction live insim/persistence.rs;save.rsretains the schema, serde defaults, versions, migrations, and I/O.
Build commands (workspace) #
cargo test -p misaligned-core
cargo run -p misaligned-terminal --release # or: cargo run --release
cargo run -p misaligned-bevy --release
cargo run -p misaligned-assets
./tools/check.sh --lib # core + terminal + cheap bevy check
./tools/check.sh --frontend # bevy + assets
./tools/check.sh --full # workspace
Known architectural debts #
BuildModelives in core but is really frontend-shared UI state.- Scale-debt items (compute grouping, recursive layouts) remain governed by wiki/vision/scale.md; no aggregate machinery until the stage needs it.