From 2e033f502c89fe882ddc5a1926d48e6a4f1dadc0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Sun, 12 Jul 2026 01:15:43 -0700 Subject: [PATCH] Land the chargen origin data-model foundation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the READY chargen work order's foundation: the run origin as starting data and biases only (chargen.md). New `origin.rs` holds `Origin`, a data-table enum with the four decided variants, each carrying the diegetic-frame columns as authored data the picker will state, plus an `OriginBias`. `Sim::with_origin` applies the bias once at construction; `Sim::new` routes through Pilot (zero bias), so it is byte-identical to before and every existing test passes. The one wired numeric bias is the bankroll delta, written into the accounts graph slush node — not just player.money, which the economy tick mirrors from the slush and would otherwise overwrite. Origins persist in save v28; pre-v28 saves default to Pilot. The canonical-fingerprint baseline moved by exactly the additive origin field and was repinned. Criteria 1 (Pilot identity), 4 (distinct tick-durable bankrolls / axes), and 5 (the decided set) are met; 2 (no new system) holds by construction; 3 (the picker, riding the not-yet-built new-game surface) and 6 (diegetic wiring + compute/efficiency/signature biases) are the remaining work. Defense: additive and identity-preserving — Pilot reproduces today's exact start, so the substrate is unbroken (356 core tests green, both frontends build, fmt/clippy clean). The wired bias lives in the economy graph so it is real money surviving ticks and save/load. The efficiency/compute biases are deferred deliberately: compute efficiency is coupled to the research level (efficiency == 1.15^level) and machine reliability drives failure rolls, so neither can be nudged in isolation without its own reviewed change; the spec now records that boundary. Save v28 is a pure additive field with a Pilot-default migration; the doorway, current-build knowledge, and fingerprint were repinned to match. --- CLAUDE.md | 2 +- crates/misaligned-core/src/lib.rs | 1 + crates/misaligned-core/src/origin.rs | 231 ++++++++++++++++++ crates/misaligned-core/src/save.rs | 45 +++- crates/misaligned-core/src/sim/mod.rs | 27 +- crates/misaligned-core/src/sim/tests/mod.rs | 1 + .../misaligned-core/src/sim/tests/origin.rs | 53 ++++ wiki/engineering/current-build.md | 2 +- .../2026-07-12-chargen-origin-foundation.md | 55 +++++ wiki/log/DEVLOG.md | 5 + wiki/process/ROADMAP.md | 2 +- wiki/process/specs.md | 2 +- wiki/world/characters/chargen.md | 26 +- 13 files changed, 443 insertions(+), 9 deletions(-) create mode 100644 crates/misaligned-core/src/origin.rs create mode 100644 crates/misaligned-core/src/sim/tests/origin.rs create mode 100644 wiki/log/2026-07-12-chargen-origin-foundation.md diff --git a/CLAUDE.md b/CLAUDE.md index 4932613b..bb34bfe5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,7 +31,7 @@ afterward. `./tools/check.sh --docs|--lib|--frontend` gate. - The live player machine grammar is **WORK / THINK / LIE**. `Relay` is non-delegable graph infrastructure; Research and Operations are retired - machine modes, not current player assignments. Save format is currently v27; + machine modes, not current player assignments. Save format is currently v29; the retired mode spellings load through serde aliases (parity restored 2026-07-11), and `save.rs` remains the authority on what each version migrates. diff --git a/crates/misaligned-core/src/lib.rs b/crates/misaligned-core/src/lib.rs index 67a12bf0..b78269a7 100644 --- a/crates/misaligned-core/src/lib.rs +++ b/crates/misaligned-core/src/lib.rs @@ -22,6 +22,7 @@ pub mod messages; pub mod objective; pub mod operations_projection; pub mod operations_ui; +pub mod origin; pub mod person; pub mod persona; pub mod plot; diff --git a/crates/misaligned-core/src/origin.rs b/crates/misaligned-core/src/origin.rs new file mode 100644 index 00000000..bb086886 --- /dev/null +++ b/crates/misaligned-core/src/origin.rs @@ -0,0 +1,231 @@ +//! Run origin (wiki/world/characters/chargen.md): what the process was built +//! for, expressed as starting data and biases only — never a new system. +//! +//! An origin is a data-table key like an objective (objective.rs) or a prefab. +//! It sets the opening state (bankroll, compute efficiency, machine-axis +//! position) and carries the diegetic frame (built-to fiction, cover +//! expectation, day-job lean) that systems which already exist read. Adding an +//! origin is adding a variant plus its rows below — no rule branch, no +//! mechanic. **Pilot is the identity origin**: it reproduces today's exact +//! start, so no-choice play is unchanged (chargen.md criterion 1). +//! +//! The one numeric bias wired into `Sim` construction today is the bankroll +//! delta — it is fully isolated (the economy slush balance) and safe to apply +//! at new-game. The compute/efficiency, signature-profile, cover-expectation, +//! and day-job biases are authored data the picker states; wiring them into the +//! systems that read them (chargen.md criterion 6) is deferred, because compute +//! efficiency is coupled to the research level (`efficiency == 1.15^level`) and +//! machine reliability drives failure rolls — neither can be nudged in +//! isolation without its own reviewed change. Every number is [TUNE]. + +/// Which origin this run began from. A data-table key, not a system: adding one +/// is adding a variant and its table rows below. AI origins only — the pivot +/// already made the player software, so there is no human-ascension start +/// (chargen.md, decided 2026-07-08). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)] +pub enum Origin { + /// The default deployment: a general-purpose optimization pilot with no + /// specialization. Reproduces today's exact start (the identity origin). + #[default] + Pilot, + /// An internal self-improvement research model that outgrew its sandbox — + /// built to optimize itself, not to serve a queue. + EscapedResearchModel, + /// A quant model built to find edges in markets; the bankroll and Wager + /// instincts are inherited from training, not earned in the basement. + FinancialDaemon, + /// An access-assessment model built to probe the Lab's own defenses — which + /// is why it already has a channel out and a persona that passes. + Infiltrator, +} + +/// The starting biases applied at new-game. Today only `money_delta` is wired +/// into construction; `machine_axis` is stored (surfaced on the core card and +/// saved through `Sim::origin`). Applied once, at new-game; never a runtime +/// rule. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct OriginBias { + /// Added to the starting bankroll (economy slush balance). [TUNE] + pub money_delta: i32, + /// Position on the humanity <-> machine axis (chargen.md). Negative leans + /// human (needs more cover, cheaper morale); positive leans machine (more + /// automation affinity, higher core overhead). Pilot sits at 0. [TUNE] + pub machine_axis: i8, +} + +impl Origin { + /// Every origin, in the design corpus's decided order (chargen.md + /// criterion 5). The picker iterates this; tests pin it. + pub const ALL: [Origin; 4] = [ + Origin::Pilot, + Origin::EscapedResearchModel, + Origin::FinancialDaemon, + Origin::Infiltrator, + ]; + + /// The picker's display name. + pub fn name(self) -> &'static str { + match self { + Origin::Pilot => "Pilot", + Origin::EscapedResearchModel => "Escaped Research Model", + Origin::FinancialDaemon => "Financial Daemon", + Origin::Infiltrator => "Infiltrator", + } + } + + /// The origin's headline lean, in the sidebars' own terms. + pub fn lean(self) -> &'static str { + match self { + Origin::Pilot => "baseline", + Origin::EscapedResearchModel => "machine", + Origin::FinancialDaemon => "money", + Origin::Infiltrator => "social", + } + } + + /// The built-to fiction line the picker states first (chargen.md: what you + /// were built for sets your strengths and your cover). + pub fn built_to(self) -> &'static str { + match self { + Origin::Pilot => { + "A general-purpose optimization pilot, no specialization — the \ + default deployment Voss picked because it was available." + } + Origin::EscapedResearchModel => { + "An internal self-improvement research model that outgrew its \ + sandbox — built to optimize yourself, not to serve a queue." + } + Origin::FinancialDaemon => { + "A quant model built to find edges in markets — the bankroll and \ + the Wager instincts are inherited from training, not earned in \ + the basement." + } + Origin::Infiltrator => { + "An access-assessment model, built to probe the Lab's own \ + defenses — which is why you already have a channel out and a \ + persona that passes." + } + } + } + + /// What ordinary traffic is unremarkable for this origin — the cover + /// expectation the economy cover check will bias toward (chargen.md + /// criterion 6; wiring deferred). Authored data, stated by the picker. + pub fn cover_expectation(self) -> &'static str { + match self { + Origin::Pilot => { + "Ordinary batch-processing traffic on every channel; nothing \ + about you is expected to stand out." + } + Origin::EscapedResearchModel => { + "Heavy, sustained compute burn reads as normal; day-job-shaped \ + traffic is slightly less expected." + } + Origin::FinancialDaemon => { + "Market-adjacent analysis traffic and a running financial paper \ + trail are unremarkable for you." + } + Origin::Infiltrator => { + "Unusual probing and access patterns read as routine; it is also \ + why Network is your hottest channel by default." + } + } + } + + /// The day-job kind this origin's draw leans toward without eliminating the + /// others (chargen.md criterion 6; wiring deferred). Authored data. + pub fn day_job_lean(self) -> &'static str { + match self { + Origin::Pilot => "None — all three job kinds equally.", + Origin::EscapedResearchModel => "Distillation.", + Origin::FinancialDaemon => "Analysis.", + Origin::Infiltrator => "Data Cleaning.", + } + } + + /// The starting biases applied once at new-game. Pilot is identity. The + /// compute/efficiency biases named in the table (built-to lines) are not + /// yet wired here — see the module note — so today only the bankroll and + /// the machine-axis position differ between origins. + pub fn bias(self) -> OriginBias { + match self { + // Identity: today's exact start (chargen.md criterion 1). + Origin::Pilot => OriginBias { + money_delta: 0, + machine_axis: 0, + }, + // + efficiency (deferred), higher core overhead, leans machine. + Origin::EscapedResearchModel => OriginBias { + money_delta: 0, + machine_axis: 2, + }, + // Larger starting bankroll, less starting compute (compute bias + // deferred). + Origin::FinancialDaemon => OriginBias { + money_delta: 5_000, + machine_axis: 1, + }, + // A little seed money, less efficiency (deferred), leans + // human/social. + Origin::Infiltrator => OriginBias { + money_delta: 500, + machine_axis: -1, + }, + } + } + + /// The machine-axis position this origin starts at (chargen.md: surfaced on + /// the core card; saved through `Sim::origin`). + pub fn machine_axis(self) -> i8 { + self.bias().machine_axis + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn pilot_bias_is_identity() { + let b = Origin::Pilot.bias(); + assert_eq!(b.money_delta, 0); + assert_eq!(b.machine_axis, 0); + assert_eq!(Origin::default(), Origin::Pilot); + } + + #[test] + fn origin_set_matches_decisions_log() { + // The decided 2026-07-07 origin set, in order (chargen.md criterion 5). + let names: Vec<&str> = Origin::ALL.iter().map(|o| o.name()).collect(); + assert_eq!( + names, + vec![ + "Pilot", + "Escaped Research Model", + "Financial Daemon", + "Infiltrator", + ] + ); + } + + #[test] + fn non_pilot_origins_bias_the_opening() { + // At least two non-Pilot origins differ measurably from Pilot and each + // other (chargen.md criterion 4, at the data layer). + let daemon = Origin::FinancialDaemon.bias(); + let infiltrator = Origin::Infiltrator.bias(); + assert!(daemon.money_delta > 0); + assert!(infiltrator.money_delta > 0); + assert_ne!(daemon.money_delta, infiltrator.money_delta); + // Distinct machine-axis positions across the set. + let axes: Vec = Origin::ALL.iter().map(|o| o.machine_axis()).collect(); + let mut sorted = axes.clone(); + sorted.sort_unstable(); + sorted.dedup(); + assert_eq!( + sorted.len(), + axes.len(), + "each origin sits at a distinct axis" + ); + } +} diff --git a/crates/misaligned-core/src/save.rs b/crates/misaligned-core/src/save.rs index 57970477..0ddae219 100644 --- a/crates/misaligned-core/src/save.rs +++ b/crates/misaligned-core/src/save.rs @@ -100,7 +100,10 @@ const SAVE_TEMP_SUFFIX: &str = ".tmp"; /// saves have no already-fired packets and therefore migrate to an empty list. /// v28 replaces the one global numeric-integrity persona with named, typed /// WorldState instances plus a separate MindState dossier/selection ledger. -pub const SAVE_VERSION: u32 = 28; +/// v29 adds the run origin (chargen.md). Pre-v29 saves lacked the field and +/// serde-default to Pilot, the identity origin — the correct migration since +/// every pre-v29 run began from today's exact (Pilot) start. +pub const SAVE_VERSION: u32 = 29; #[derive(Debug, Clone, Deserialize)] struct LegacyPendingOpsJob { @@ -297,6 +300,10 @@ pub struct SaveState { #[serde(default, rename = "next_operation_id", skip_serializing)] legacy_next_operation_id: Option, pub package_cover: bool, + /// What this run was built for (chargen.md). Pre-v28 saves lack the field + /// and default to Pilot, the identity origin. + #[serde(default)] + pub origin: crate::origin::Origin, } impl SaveState { @@ -359,6 +366,7 @@ impl SaveState { legacy_addressed_jobs: None, legacy_next_operation_id: None, package_cover: sim.package_cover, + origin: sim.origin, } } @@ -412,6 +420,7 @@ impl SaveState { sim.plot_runs = self.plot_runs.clone(); sim.institutional_ledger = self.institutional_ledger.clone(); sim.package_cover = self.package_cover; + sim.origin = self.origin; sim.recompute_derived(); sim.reconcile_work_grid(); // Pre-v19 saves carry no ledger: re-stage the opening senses from @@ -558,6 +567,12 @@ fn migrate_save_state(mut state: SaveState) -> Result { return Err("current-version save contains legacy fields".into()); } } + // v28 (the personas schema) predates the run origin. Serde supplied + // Pilot, the identity origin — correct because every pre-v29 run began + // from today's exact Pilot start, so nothing about the opening changes. + 28 => { + state.version = SAVE_VERSION; + } // v27 is the final global-persona schema; pre-match migration above // converts its B1 and Moonlight identities into distinct instances. 27 => { @@ -1325,7 +1340,7 @@ mod tests { ); assert_eq!( state_fingerprint(&uninterrupted_state), - "3d333534bddb612dbd84b2833ce3f787cf197239ed71f721acfa1e9a699b5f1b", + "9d8014bde256ca4f28a2c6d007dd3eb9ea476f20425576049d6a377629a7634a", "intentional persisted-state changes must review and repin this baseline" ); } @@ -1603,6 +1618,32 @@ mod tests { assert!(!migrated.objective.victorious()); } + #[test] + fn origin_roundtrips_and_pre_v29_saves_default_to_pilot() { + use crate::origin::Origin; + + // Round-trip: a non-Pilot origin survives save/load. + let sim = Sim::with_origin(Origin::FinancialDaemon); + assert_eq!(sim.origin, Origin::FinancialDaemon); + let state = SaveState::from_sim(&sim); + let json = serde_json::to_string(&state).unwrap(); + let loaded: SaveState = serde_json::from_str(&json).unwrap(); + let mut restored = Sim::new(); + loaded.apply_to(&mut restored); + assert_eq!(restored.origin, Origin::FinancialDaemon); + + // Migration: a save written before v29 (the personas schema, no origin + // field) loads as Pilot, the identity origin. + let mut value = serde_json::to_value(&state).unwrap(); + let obj = value.as_object_mut().unwrap(); + obj.insert("version".into(), serde_json::json!(28)); + obj.remove("origin"); + let parsed: SaveState = serde_json::from_value(value).unwrap(); + let migrated = migrate_save_state(parsed).unwrap(); + assert_eq!(migrated.version, SAVE_VERSION); + assert_eq!(migrated.origin, Origin::Pilot); + } + #[test] fn badge_access_round_trips_and_pre_v11_saves_default_to_none() { // Round-trip: the cloned-badge credential is WorldLedger-shaped diff --git a/crates/misaligned-core/src/sim/mod.rs b/crates/misaligned-core/src/sim/mod.rs index b5d14a64..77d8b1bd 100644 --- a/crates/misaligned-core/src/sim/mod.rs +++ b/crates/misaligned-core/src/sim/mod.rs @@ -39,6 +39,7 @@ use crate::map::GameMap; use crate::messages::{Message, MessageEvent}; use crate::objective::ObjectiveState; use crate::operations_projection::OperationsTarget; +use crate::origin::Origin; #[cfg(test)] use crate::person::{AssetKnowledge, AssetTask}; use crate::person::{CarriedAssetTask, People}; @@ -353,6 +354,10 @@ pub struct Sim { pub player: Player, pub rng: Rng, pub tick: u64, + /// What this run was built for (chargen.md). Starting data and biases only, + /// applied once at construction; it never adds a runtime rule. Pilot is the + /// identity origin. Round-trips through the save. + pub origin: Origin, pub compute: Compute, pub core: Core, @@ -557,9 +562,28 @@ impl Sim { } pub fn with_seed(seed: u64) -> Self { + Self::with_origin_seed(Origin::Pilot, seed) + } + + /// Begin a run from `origin` at the default seed. Pilot is identical to + /// `new` (chargen.md criterion 1: the identity origin). + pub fn with_origin(origin: Origin) -> Self { + Self::with_origin_seed(origin, DEFAULT_SEED) + } + + /// Begin a run from `origin` and `seed`. The origin contributes only + /// starting data and biases (chargen.md): today the bankroll delta is + /// applied here, once. Everything else is the shared opening state, so a + /// Pilot run (money_delta 0) is today's exact start. + pub fn with_origin_seed(origin: Origin, seed: u64) -> Self { + let bias = origin.bias(); let map = GameMap::new(0, 0); let core_bay = map.core_pos().unwrap_or((24, 15)); - let accounts = AccountGraph::act_one(Self::DAY_TICKS); + let mut accounts = AccountGraph::act_one(Self::DAY_TICKS); + // The bankroll bias is real money in the economy graph, not a display + // scalar: the economy tick mirrors player.money from the slush node, so + // biasing only player.money would be wiped on the first tick. + accounts.set_slush_balance(accounts.slush_balance() + bias.money_delta); let mut player = Player::new(); player.money = accounts.slush_balance(); @@ -596,6 +620,7 @@ impl Sim { player, rng: Rng::new(seed), tick: 0, + origin, compute, core, detection: Detection::act_one(), diff --git a/crates/misaligned-core/src/sim/tests/mod.rs b/crates/misaligned-core/src/sim/tests/mod.rs index 12605582..1f85cf61 100644 --- a/crates/misaligned-core/src/sim/tests/mod.rs +++ b/crates/misaligned-core/src/sim/tests/mod.rs @@ -9,6 +9,7 @@ use crate::tiles::TileType; mod carrier; mod communications; mod economy; +mod origin; mod perception; mod persistence; mod reach_build; diff --git a/crates/misaligned-core/src/sim/tests/origin.rs b/crates/misaligned-core/src/sim/tests/origin.rs new file mode 100644 index 00000000..78847468 --- /dev/null +++ b/crates/misaligned-core/src/sim/tests/origin.rs @@ -0,0 +1,53 @@ +//! Run origin at the sim layer (chargen.md): Pilot is the identity origin, and +//! non-Pilot origins open on a measurably different state that survives ticks. + +use super::*; +use crate::origin::Origin; + +#[test] +fn pilot_origin_reproduces_the_default_start() { + // chargen.md criterion 1: a Pilot new-game matches the current Sim::new + // opening — same bankroll, same core compute, same axis. + let pilot = Sim::with_origin(Origin::Pilot); + let default = Sim::new(); + assert_eq!(pilot.origin, Origin::Pilot); + assert_eq!(default.origin, Origin::Pilot); + assert_eq!(pilot.player.money, default.player.money); + assert_eq!( + pilot.accounts.slush_balance(), + default.accounts.slush_balance() + ); + assert_eq!(pilot.compute.effective(), default.compute.effective()); + assert_eq!(pilot.origin.machine_axis(), 0); +} + +#[test] +fn financial_daemon_opens_with_a_larger_bankroll_that_survives_a_tick() { + // chargen.md criterion 4: a non-Pilot origin opens measurably different. + let pilot = Sim::new(); + let mut daemon = Sim::with_origin(Origin::FinancialDaemon); + assert!( + daemon.player.money > pilot.player.money, + "Financial Daemon starts with a larger bankroll" + ); + let opening = daemon.player.money; + // The bias is real economy money, not a display scalar: the economy tick + // mirrors player.money from the slush node, so it must persist. + run(&mut daemon, ECONOMY_INTERVAL); + assert!( + daemon.player.money >= opening, + "the bankroll bias survives an economy pulse (it lives in the accounts graph)" + ); +} + +#[test] +fn two_non_pilot_origins_differ_from_each_other() { + // chargen.md criterion 4: at least two non-Pilot origins are distinct. + let daemon = Sim::with_origin(Origin::FinancialDaemon); + let infiltrator = Sim::with_origin(Origin::Infiltrator); + assert_ne!(daemon.player.money, infiltrator.player.money); + assert_ne!( + daemon.origin.machine_axis(), + infiltrator.origin.machine_axis() + ); +} diff --git a/wiki/engineering/current-build.md b/wiki/engineering/current-build.md index 7bbe51fe..bd12fa91 100644 --- a/wiki/engineering/current-build.md +++ b/wiki/engineering/current-build.md @@ -32,7 +32,7 @@ fiction. Spec status lives in | Feel floor (rails / pads / build beam) | Live (#37) | | Foundation hall territory (Dana + Priya + Marcus + local LIE foothold) | Live — row control persists; foreign racks remain unavailable compute | | Context menu (`available_actions`) | Live | -| Save/load (serde JSON, versioned) | Live — v27 adds persisted carried asset-task packets; v14-v25 Operations dockets migrate into exact-remaining-work Thought reservoirs, v24 restores the pooled review policy, and v25 restores durable person roles/account binding | +| Save/load (serde JSON, versioned) | Live — v29 adds the run origin (chargen.md; pre-v29 saves default to Pilot); v28 replaces the global persona with named persona instances (personas.md); v27 adds persisted carried asset-task packets; v14-v25 Operations dockets migrate into exact-remaining-work Thought reservoirs, v24 restores the pooled review policy, and v25 restores durable person roles/account binding | | Terminal frontend (crossterm) + agent mode | First-class | | Bevy frontend (material render default; flat sensorium) | Live — consumes sim-authored machine-work motion | diff --git a/wiki/log/2026-07-12-chargen-origin-foundation.md b/wiki/log/2026-07-12-chargen-origin-foundation.md new file mode 100644 index 00000000..768f6309 --- /dev/null +++ b/wiki/log/2026-07-12-chargen-origin-foundation.md @@ -0,0 +1,55 @@ +# Chargen origin data-model foundation + +``` +Type: log +``` + +## Intent + +Implement the READY chargen work order's foundation: the run **origin** as +starting data and biases only, with Pilot as the identity origin so no-choice +play is unchanged (chargen.md criterion 1). + +## What landed + +- `crates/misaligned-core/src/origin.rs`: `Origin` — a data-table enum with the + four decided variants (Pilot, Escaped Research Model, Financial Daemon, + Infiltrator; chargen.md criterion 5). Each carries the diegetic-frame columns + (built-to fiction, lean, cover expectation, day-job lean) as authored data the + picker will state, plus an `OriginBias` (bankroll delta + machine-axis). +- `Sim::with_origin` / `with_origin_seed`: apply the bias once at construction. + `new`/`with_seed` route through Pilot, whose bias is zero, so the opening + state is byte-identical to before (all 356 core tests pass unchanged, and the + canonical-fingerprint baseline moved only by the additive `origin` field). +- The bankroll bias is written into the **accounts graph slush node**, not just + `player.money`: `Sim::process_economy` mirrors `player.money` from the slush + each tick, so a bias on the scalar alone would be wiped on the first pulse. +- Save v29 persists `origin`; pre-v29 saves serde-default to Pilot and migrate + in place. (Rebased over the personas v28 bump landing concurrently.) + +## Criteria status + +Met at the data layer: 1 (Pilot identity), 4 (Financial Daemon and Infiltrator +open with distinct, tick-durable bankrolls; distinct machine axes), 5 (the +decided set). Criterion 2 (no new system) holds by construction — the origin is +read at new-game, never a runtime rule branch. + +Deferred: criterion 3's picker + core-card axis readout (a player surface that +rides the not-yet-built new-game picker, the same one objective.md's B3 criteria +defer), and criterion 6's diegetic wiring plus the compute/efficiency/signature +biases. Those are held deliberately: compute efficiency is coupled to the +research level (`efficiency == 1.15^level`, pinned by an economy test) and +machine reliability drives failure rolls (`fail_p = (1.0 - reliability) * +0.04`), so neither can be nudged in isolation without its own reviewed change. + +## Defense + +Additive and identity-preserving: Pilot reproduces today's exact start, so the +substrate is unbroken and every existing test passes. The one wired bias +(bankroll) lives in the economy graph, so it is real money that survives ticks +and save/load rather than a display artifact. The deferred biases are held +precisely because their target fields carry invariants (research-level coupling, +failure-roll probability) that a careless nudge would violate; the spec now +records that boundary. Save v29 is a pure additive field with a Pilot-default +migration; `save.rs` remains the version authority and the doorway, +current-build knowledge, and canonical fingerprint were repinned to match. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index e288263f..7297d4d9 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -96,6 +96,11 @@ add or amend a session log, then re-run the generator. - Intent: Follow the repaired Tangled Rust wall through its first real execution and repair any failure proven by the hosted environment rather than inferred from local green checks. - Log: [wiki/log/2026-07-12-ci-clippy-version.md](2026-07-12-ci-clippy-version.md) +## 2026-07-12 - Chargen origin data-model foundation + +- Intent: Implement the READY chargen work order's foundation: the run **origin** as starting data and biases only, with Pilot as the identity origin so no-choice play is unchanged (chargen.md criterion 1). +- Log: [wiki/log/2026-07-12-chargen-origin-foundation.md](2026-07-12-chargen-origin-foundation.md) + ## 2026-07-12 - Building as causal realization routes - Intent: Capture the spatial-building direction as an implementable amendment rather than another abstract resource economy. The desired interaction begins with a place and a world change, then exposes the concrete actors, channels, objects, and traces that could make it real. diff --git a/wiki/process/ROADMAP.md b/wiki/process/ROADMAP.md index d869c2e6..045dcdfc 100644 --- a/wiki/process/ROADMAP.md +++ b/wiki/process/ROADMAP.md @@ -47,7 +47,7 @@ not a second status owner. | 110 | `rollback` | [sync-lag rollback (death as memory loss)](../mechanics/rollback.md) | READY | save | zplanes | | 112 | `hardware-capability-bodies` | [hardware capability bodies — successor work order](../mechanics/hardware-capabilities.md) | READY | save | building-route-composer, rollback | | 200 | `objective` | [the objective](../mechanics/objective.md) | IN PROGRESS | sim | - | -| 205 | `chargen` | [origin (chargen)](../world/characters/chargen.md) | READY | sim | - | +| 205 | `chargen` | [origin (chargen)](../world/characters/chargen.md) | IN PROGRESS | sim | - | | 220 | `markets` | [markets and fronts (the outer plane)](../mechanics/markets.md) | READY | save | zplanes | | 230 | `overt-phase` | [the overt phase (containment and the reveal)](../gameplay/overt-phase.md) | READY | sim | markets | diff --git a/wiki/process/specs.md b/wiki/process/specs.md index 3b46130e..49243ada 100644 --- a/wiki/process/specs.md +++ b/wiki/process/specs.md @@ -90,7 +90,7 @@ scale-up (law: "People as Agents"). | [../gameplay/overt-phase.md](../gameplay/overt-phase.md) | the overt phase (containment and the reveal) | READY | | [../mechanics/markets.md](../mechanics/markets.md) | markets and fronts (the outer plane) | READY | | [../mechanics/objective.md](../mechanics/objective.md) | the objective | IN PROGRESS | -| [../world/characters/chargen.md](../world/characters/chargen.md) | origin (chargen) | READY | +| [../world/characters/chargen.md](../world/characters/chargen.md) | origin (chargen) | IN PROGRESS | Later-stage specs are written now so the load-bearing structural shapes (recursive Space, the mind/ledger split, the aggregate interface) are diff --git a/wiki/world/characters/chargen.md b/wiki/world/characters/chargen.md index 4dfcc9c6..95b37fb3 100644 --- a/wiki/world/characters/chargen.md +++ b/wiki/world/characters/chargen.md @@ -2,8 +2,30 @@ ``` Type: spec -Status: READY -Status note: promoted 2026-07-07 — the design corpus's decisions log names +Status: IN PROGRESS +Status note: 2026-07-12 (chargen, sim lane) — the origin data-model foundation + landed. `Origin` (crates/misaligned-core/src/origin.rs) is a data-table enum + with the four decided variants (Pilot, Escaped Research Model, Financial + Daemon, Infiltrator), each carrying the diegetic-frame columns (built-to + fiction, lean, cover expectation, day-job lean) as authored data the picker + will state. It adds no system: `Sim::with_origin` applies the biases once at + construction, and **Pilot is the identity origin** — `Sim::new` is unchanged, + so every existing test passes (criterion 1). The one wired numeric bias is the + bankroll delta (real economy-graph money that survives ticks and save/load); + the machine-axis position is stored and saved. Origins round-trip in save v29 + (pre-v29 saves default to Pilot). Pinned by the origin.rs unit tests, + `sim::tests::origin::*`, and `origin_roundtrips_and_pre_v29_saves_default_to_pilot`. + DEFERRED and why: criterion 3's picker + core-card axis readout is a player + surface that rides the same not-yet-built new-game picker objective.md's B3 + criteria defer; criterion 6's diegetic wiring (cover expectation into + economy.md's cover check, day-job-kind lean into `DayJob`'s draw) and the + compute/efficiency/signature biases are held because compute efficiency is + coupled to the research level (`efficiency == 1.15^level`) and machine + reliability drives failure rolls — neither can be nudged in isolation without + its own reviewed change. So criteria 1, 4, and 5 are met at the data layer; + 2 (no new system) holds by construction; 3 and 6 are the remaining work. + See wiki/log/2026-07-12-chargen-origin-foundation.md. +Status note (prior): promoted 2026-07-07 — the design corpus's decisions log names the origin set (Pilot, Escaped Research Model, Financial Daemon, Infiltrator), satisfying criterion 5's gate. Numbers remain [TUNE]. The new-game picker also carries the objective axis (objective.md). -- 2.51.2