A video game where you play as a misaligned AI, deceiving and building power. An experiment in spec-driven development.
README.md

Plots — authored manipulation templates #

The binding design contract is wiki/mechanics/plots.md. The executable schema is crates/misaligned-core/src/plot.rs; every nested TOML under this directory is discovered at build time by crates/misaligned-core/build.rs, compiled in with include_str!, then parsed and semantically validated by PlotCatalog::load_builtin(). Adding a plot never requires editing Rust sources or a hand-maintained file list. There is no runtime filesystem I/O.

The built-ins cover every Act One role and leverage. A plot is not owned by Marcus, Dana, Ray, Priya, Voss, or any other person id. It matches human characteristics, then a PlotRun binds one live person. Beats perform real typed world acts against that binding; narration is attached to them. At least one beat must declare causal = true and execute a typed world act (the Marcus bar).

The older seed ids and folders retain character names so save v23 runs can continue to resolve their stable plot_id. New reusable content belongs under assets/plots/generic/ (or a characteristic/category folder), with no person name in its id.

Schema 2 #

Top level:

  • schema = 2, unique kebab-case id, non-blank author, non-blank category, title, and synopsis. The file stem must equal id.
  • [target] is a closed characteristic matcher. roles and leverages are non-empty arrays; optional minimum_access and requires_switch_admin declare job capabilities the causal story actually assumes. There is no named-person selector.
  • Schema 2 is the reusable entry-template schema. It deliberately cannot single out a cast member. A bespoke continuation may be valid when its causal mechanism depends on a prior run binding, unique relationship, asset, witnessed event, or fixed campaign history, but representing that dependency requires an explicit spec/schema extension. Do not imitate a named selector by choosing an over-specific characteristic combination.
  • Supported roles are custodian, network-administrator, security-observer, facilities-manager, and handler-supervisor. Leverages use the exact Rust enum spelling: Debt, Overwork, Boredom, Ambition, and Publication.
  • [entry] contains requires_knowledge = "Leverage", optional requires_channel (a real MessageChannel), optional requires_persona, positive thought_cost (the compute-cost basis converted into the visible Thought reservoir threshold), zero or more { account, amount } minimum balances, and a failure_ending id. Resources gate entry; they are not escrow and may disappear before a later act.
  • beats are ordered. Each has a unique id, narration, and at least one acts entry or a choice. Optional causal = true marks the Marcus-bar beat; at least one causal beat with world acts is required, and every success path must pass it before terminating. A terminal beat has either ending or choice; later beats are unreachable and fail validation.
  • endings contain only id, success, narration, and relationship effects: disposition, obligation, and leverage_serviced. Money and signatures are never asserted by an ending.

World acts use an internal kind tag:

  • message: real channel, typed from/to endpoint selectors, summary, and positive delivery_delay. The run waits until this persisted message is read before advancing.
  • transfer: typed from and to account selectors, positive amount, memo, and optional retire_target_debt. The account graph performs the movement; target-relative accounts resolve from the bound person.
  • institutional: typed event, impact, and persistent detail. Event kind derives its signature channel; impact derives the numeric size. There is no author-controlled raw signature beside the event.

Supported account selectors are slush, lab-operating, payroll, procurement, vendor, utility, target-employee, target-creditor, and external. Every transfer endpoint must exist before the plot becomes eligible. Endpoint selectors are inline tagged tables: { kind = "player" }, { kind = "target" }, or { kind = "external", value = "collection agency" }.

Choices have a kebab-case id, a prompt, and at least two options. Each option has id, label, and an ending reference. References, duplicate ids, empty acts, impossible resources, stem/id mismatches, blank author/category, repeated target characteristics, and debt selectors on non-debt templates fail catalog validation.

Player-facing game strings remain ASCII-only (title, synopsis, narration, choice prompts/labels, ending narration, act summary/memo/detail). Only {target} and {persona} template placeholders are permitted; unknown or unbalanced braces fail with a named field reason. Use ungendered target prose unless the target matcher itself establishes otherwise.

Plot-only changes run the product validation path: tools/check.sh classifies assets/plots/** as lib, and Tangled push CI treats them as Rust-impacting via the assets/plots/** push path in .tangled/workflows/check.yml.