Spec: crate workspace — core, terminal, Bevy, assets #
Type: spec
Status: IMPLEMENTED
Status note: landed 2026-07-10. This page is the **standing** package
architecture: shape, contracts, and the reasons they exist — not a
one-shot migration checklist. Knowledge page architecture.md mirrors
as-built paths; amend this page when package boundaries change.
Stage: Process
Design:
- wiki/vision/simulation-laws.md#justification-and-legibility
- wiki/process/living-spec.md#the-corpus-rule
- wiki/process/living-spec.md#no-dead-code
Depends on:
- wiki/process/agent-scale.md#spec-agent-scale-architecture-many-agents-one-main
- wiki/engineering/architecture.md#architecture
- wiki/process/workflows.md#workflows
Dependency notes #
- agent-scale.md — multi-agent gates and claims
map onto these packages (
--lib≈ core/terminal;--frontend≈ bevy/assets). - architecture.md — knowledge mirror of as-built layout and the sim/frontend contract (non-authoritative where this page conflicts).
- workflows.md — how to run cargo and check.sh against packages.
Why the workspace exists #
The game law was already clear: rules in the lib; frontends are thin views. A single Cargo package with optional Bevy features did not enforce that law. It only suggested it:
- Compile graph — Any
src/touch shared one feature matrix with Bevy. Agents paying for a sim-only fix still rebuilt (or risked rebuilding) the heavy frontend graph. Tieredcheck.shpath heuristics papered over a boundary the crate graph did not draw. - Mental model — One package trained agents to treat “the crate” as the
unit of work. Contending on one
Cargo.tomland one local-package personality made parallel worktrees thrash. - False coupling — Feature flags (
terminal,bevy_ui) are switches, not walls. They do not stop a frontend from re-implementing a rule “just this once,” and they do not prove that core is free of Bevy.
The workspace makes the law structural:
- A core agent can
cargo test -p misaligned-corewithout Bevy in the graph. - A Bevy agent does not rebuild terminal chrome.
- Package edges are the same cut as the sim/frontend contract.
This is a spread-out codebase on purpose — less monolith, more packages with narrow jobs — so many agents can work without contending on one product crate.
As-built shape (binding) #
Cargo.toml — virtual workspace only (members, shared deps, profiles)
crates/
misaligned-core/ — sim library (package name); lib name `misaligned`
misaligned-terminal/ — binary `misaligned` (crossterm + agent protocol)
misaligned-bevy/ — binary `misaligned-bevy` (Bevy 0.18 frontend)
misaligned-assets/ — shared art lib + `misaligned-assets` / `misaligned-effects`
There is no root src/ product tree and no feature-flag substitute for
these packages. Optional features must not re-merge package jobs.
Why four packages (not two, not twenty) #
| Package | Job | Why it is separate |
|---|---|---|
| core | All game rules, save format, sim tests | Must stay free of render/input toolchains so sim work stays cheap and deterministic. |
| terminal | Player/agent text surface + wall clock | First-class frontend; must not pull Bevy; must not own rules. |
| bevy | Material/digital 3D/2.5D view + shot harness | Heavy compile graph; isolated so frontend thrash does not block sim agents. |
| assets | Shared meshes/effects plus focused art harnesses | Reusable render code without loading the full game loop; no sim progression. |
Not one umbrella misaligned-frontends package: that would keep terminal
and Bevy compile-coupled and erase the frontend-tier win.
Not one crate per mechanics module: coordination cost exceeds compile win until a real module boundary demands it (see out of scope).
Naming choices (and why) #
| Choice | Why |
|---|---|
Package misaligned-core, lib name misaligned |
Frontends keep use misaligned::… so imports and mental model stay “the sim,” not a rename churn. Package name states the job (core); lib name states the product. |
Binary names misaligned, misaligned-bevy, misaligned-assets, misaligned-effects |
Product names and each focused art harness stay explicit. |
Directory prefix crates/misaligned-* |
Workspace-local, greppable, matches package names. |
Dependency edges (and why) #
misaligned-terminal ──► misaligned-core
misaligned-bevy ──► misaligned-core
──► misaligned-assets (shared rack / mesh spawn)
misaligned-assets ──► bevy only (no core, no sim progression)
misaligned-core ──► serde, serde_json, dirs
✗ bevy, crossterm
- Bevy → assets exists so rack chassis meshes are not copy-pasted into the
game binary. Assets remain free of
Simso the tester does not boot a run. dirsin core is allowed only for save-path infrastructure on the save format (where the JSON file lives), not for wall-clock or network I/O as game rules. Sim progression still does not read the environment for behavior switches (see env registry: game rules are not env-toggled).- No shared
CARGO_TARGET_DIRacross worktrees for local packages — already caused a false green. Dependency caches (e.g. sccache) may be shared; local package artifacts stay private (agent-scale / seed script).
Package contracts (standing) #
| Package | May contain | Must not contain |
|---|---|---|
misaligned-core |
Sim, map, systems, save format, rule tests |
Bevy, crossterm, wall-clock-driven rules, Instant::now as game time, env reads that change rules |
misaligned-terminal |
UI, input, agent line protocol, wall-clock → tick | Game-rule ifs that belong on Sim |
misaligned-bevy |
View, shot harness, wall-clock → tick | Game-rule forks of Sim behavior |
misaligned-assets |
Mesh/effect render code, asset tester, effects lab | Full-run save/load; sim progression |
Frontends talk to core through command/query methods and read state for render — same contract as architecture.md “The sim/frontend contract,” enforced by package edges.
Gates map onto packages (why tiers work) #
| Change class | Default gate | Why |
|---|---|---|
| docs | ./tools/check.sh --docs |
No product graph. |
| lib / sim | ./tools/check.sh --lib |
Core (+ terminal clippy) + cheap cargo check -p misaligned-bevy so API breaks still surface without full Bevy clippy. |
| frontend | ./tools/check.sh --frontend |
Bevy + assets only. |
| full / CI | ./tools/check.sh --full / Tangled |
Whole workspace; conservative. |
Path classification keys off crates/misaligned-*, not a single src/.
What this is not #
- A claim that four packages are forever enough — only that further splits need a new reason as strong as the ones above.
- Permission to put game rules in a frontend “temporarily.”
- Permission to reintroduce feature flags as the real boundary between jobs.
- A mandate to micro-crate every mechanic.
Rejected alternatives (keep these rejected) #
- Stay one package, smarter features only — features are not walls; agents still share one product crate and one mental model.
- core + one
misaligned-frontendsumbrella — terminal and Bevy stay compile-coupled; loses proportional frontend gates. - Shared
CARGO_TARGET_DIRacross worktrees for speed — produced a false green; forbidden for local package artifacts. - Rename the lib to
misaligned_corein Rust code — huge import churn for no boundary gain; package name already states the job.
Standing acceptance criteria #
These remain true for the life of the layout (not a one-time land checklist):
- The repo is a Cargo workspace with at least
misaligned-core,misaligned-terminal,misaligned-bevy, andmisaligned-assets. misaligned-corehas no dependency onbevyorcrossterm.cargo test -p misaligned-coredoes not build Bevy.- Game rules live only in core; frontends do not re-implement sim rules.
- Binary names remain
misaligned,misaligned-bevy,misaligned-assets,misaligned-effectsunless a deliberate product rename is specced. ./tools/check.shauto-classification and documented tiers key off packages; CI enforces a conservative full workspace gate.- architecture.md stays an accurate as-built mirror; ROADMAP conflict language names packages/hot modules under core.
- Save format ownership stays in core; migrations do not move into frontends.
- Why sections above stay true — if a future change weakens a reason (e.g. Bevy back in core “just for types”), amend this page in the same commit or reject the change.
History (non-authoritative) #
Implemented 2026-07-10 from a READY deferred work order. Pre-split form was a
single package with optional terminal / bevy_ui features under root src/.
Migration detail lives in wiki/log/2026-07-10-crate-workspace.md.