A video game where you play as a misaligned AI, deceiving and building power. An experiment in spec-driven development.
misaligned wiki engineering crate-workspace.md
9.3 kB

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:

  1. 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. Tiered check.sh path heuristics papered over a boundary the crate graph did not draw.
  2. Mental model — One package trained agents to treat “the crate” as the unit of work. Contending on one Cargo.toml and one local-package personality made parallel worktrees thrash.
  3. 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-core without 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 Sim so the tester does not boot a run.
  • dirs in 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_DIR across 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-frontends umbrella — terminal and Bevy stay compile-coupled; loses proportional frontend gates.
  • Shared CARGO_TARGET_DIR across worktrees for speed — produced a false green; forbidden for local package artifacts.
  • Rename the lib to misaligned_core in 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):

  1. The repo is a Cargo workspace with at least misaligned-core, misaligned-terminal, misaligned-bevy, and misaligned-assets.
  2. misaligned-core has no dependency on bevy or crossterm. cargo test -p misaligned-core does not build Bevy.
  3. Game rules live only in core; frontends do not re-implement sim rules.
  4. Binary names remain misaligned, misaligned-bevy, misaligned-assets, misaligned-effects unless a deliberate product rename is specced.
  5. ./tools/check.sh auto-classification and documented tiers key off packages; CI enforces a conservative full workspace gate.
  6. architecture.md stays an accurate as-built mirror; ROADMAP conflict language names packages/hot modules under core.
  7. Save format ownership stays in core; migrations do not move into frontends.
  8. 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.