diff --git a/crates/misaligned-core/src/sim/perception.rs b/crates/misaligned-core/src/sim/perception.rs index 4d58cbc7..1cc3c4c3 100644 --- a/crates/misaligned-core/src/sim/perception.rs +++ b/crates/misaligned-core/src/sim/perception.rs @@ -188,15 +188,33 @@ impl Sim { let tile = self.world.map().get_tile(x, y); fact!("tile", tile.name(), FactSource::Seen); if let Some(site) = self.rack_site_at(x, y) { - let state = match site { - RackSite::OwnedMachine { core: true, .. } => "owned core host", - RackSite::OwnedMachine { core: false, .. } => "owned machine", - RackSite::Commissionable => "empty pilot allocation", - RackSite::Foreign { powered: true } => "foreign, powered", - RackSite::Foreign { powered: false } => "foreign, unpowered", - RackSite::Dead => "dead chassis", - }; - fact!("rack state", state, FactSource::Seen); + match site { + RackSite::OwnedMachine { core: true, .. } => { + fact!("rack state", "owned core host", FactSource::Seen); + } + RackSite::OwnedMachine { core: false, .. } => { + fact!("rack state", "owned machine", FactSource::Seen); + } + RackSite::Commissionable => { + fact!("rack state", "empty pilot allocation", FactSource::Seen); + } + RackSite::Foreign { powered } => { + fact!("rack state", "not yours; no conversion", FactSource::Seen); + fact!( + "power", + if powered { "powered" } else { "unpowered" }, + FactSource::Seen + ); + fact!( + "usable capacity", + "empty bay / dead chassis", + FactSource::Seen + ); + } + RackSite::Dead => { + fact!("rack state", "dead chassis", FactSource::Seen); + } + } } if tile.is_door() && tile.security_level() > 0 { fact!( diff --git a/crates/misaligned-core/src/sim/tests/perception.rs b/crates/misaligned-core/src/sim/tests/perception.rs index ca0f1adb..1769efa3 100644 --- a/crates/misaligned-core/src/sim/tests/perception.rs +++ b/crates/misaligned-core/src/sim/tests/perception.rs @@ -158,6 +158,27 @@ fn inspect_reports_owned_machine_telemetry_without_sight() { ); } +#[test] +fn seen_foreign_rack_explains_the_capacity_boundary() { + let mut sim = Sim::new(); + let site = (0..sim.map().height) + .flat_map(|y| (0..sim.map().width).map(move |x| (x, y))) + .find(|&(x, y)| matches!(sim.rack_site_at(x, y), Some(RackSite::Foreign { .. }))) + .expect("Foundation hall has foreign racks"); + sim.seen.insert(site); + + let card = sim.inspect(site.0, site.1); + assert_eq!( + card.identity_line().as_deref(), + Some("Foundation Rack - not yours; no conversion") + ); + assert!(card.facts.iter().any(|fact| { + fact.label == "usable capacity" + && fact.value == "empty bay / dead chassis" + && fact.source == FactSource::Seen + })); +} + #[test] fn fresh_state_has_no_floor_or_blueprint_for_the_first_earned_frame() { // The player-visible fresh frame is mode-only black. This underlying diff --git a/wiki/log/2026-07-26-foreign-rack-capacity-boundary.md b/wiki/log/2026-07-26-foreign-rack-capacity-boundary.md new file mode 100644 index 00000000..eef00676 --- /dev/null +++ b/wiki/log/2026-07-26-foreign-rack-capacity-boundary.md @@ -0,0 +1,45 @@ +# A powered Foundation rack is still not yours + +``` +Type: log +``` + +## Intent + +The 240-site hall correctly made the player's one machine feel small, but it +also sharpened a player-facing ambiguity. Focusing one of the many lit chassis +said only `Foundation Rack - foreign, powered`. The object had no action because +the current B1 simulation has no conversion route, yet nothing on the object +said so. Powered hardware read as waiting capacity and forced the player to ask +what verb was missing. + +The repair keeps the absence of an action honest while putting its consequence +where the question arises. + +## What changed + +- `Sim::inspect` now separates ownership from electricity on a seen foreign + rack. The shared identity line reads `Foundation Rack - not yours; no + conversion`; the rack title names its owner, while a separate `power` fact + retains powered or unpowered state. +- The same provenance-tagged card says `usable capacity: empty bay / dead + chassis`. The 24-character value names both real B1 sources without clipping + in Bevy's fixed-width FOCUS rail; terminal and agent projections receive the + same explanation. +- `building.md` binds the distinction: Foundation hall segment acquisition + controls support infrastructure, not foreign compute, and the human menu does + not invent a disabled conversion row. +- `seen_foreign_rack_explains_the_capacity_boundary` pins the exact focused + identity and capacity consequence on an authored hall rack. + +## Verification + +Recorded on the exact landing commit below. + +The first expanded Bevy capture exposed a presentation defect in the initial +sentence: the fixed-width FOCUS rail clipped the second capacity source. The +final copy was shortened, then the exact expanded `MISALIGNED_SHOT=hall` PNG was +recaptured through `tools/bevy-headless.sh` inside `tools/observed-run.sh` and +opened after capture. The FOCUS card showed the direct no-conversion consequence, +independent power, and the complete `empty bay / dead chassis` value without +collision or clipping. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index f4527948..59149636 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -66,6 +66,11 @@ add or amend a session log, then re-run the generator. - Intent: Cameron asked for a much higher density of servers in the basement. The sparseness was not an authoring shortfall of ambition, it was geometry: the hall is 44x20 tiles of floor, yet all sixty rack sites sat in a block between x=20 and x=38, and every second tile inside a row w... - Log: [wiki/log/2026-07-26-foundation-hall-density.md](2026-07-26-foundation-hall-density.md) +## 2026-07-26 - A powered Foundation rack is still not yours + +- Intent: The 240-site hall correctly made the player's one machine feel small, but it also sharpened a player-facing ambiguity. Focusing one of the many lit chassis said only `Foundation Rack - foreign, powered`. The object had no action because the current B1 simulation has no convers... +- Log: [wiki/log/2026-07-26-foreign-rack-capacity-boundary.md](2026-07-26-foreign-rack-capacity-boundary.md) + ## 2026-07-26 - The first Thought is a visible causal sequence - Intent: (see session log) diff --git a/wiki/log/decisions/2026-07-26.md b/wiki/log/decisions/2026-07-26.md index 8c8605c7..634de5bf 100644 --- a/wiki/log/decisions/2026-07-26.md +++ b/wiki/log/decisions/2026-07-26.md @@ -231,6 +231,30 @@ Owner: [opening.md](../../world/story/opening.md). Owner: [basement-map.md](../../world/places/basement-map.md). +## Seen foreign racks state the capacity boundary + +### DECIDED + +- A seen Foundation rack says **not yours; no conversion** on the focused + object. The rack title already names Foundation ownership. Power remains a + separate fact: a powered rack can still be unusable. +- The same inspect card names both implemented capacity sources in one complete + fixed-width value: `empty bay / dead chassis`. There is no current direct + conversion route for a foreign rack. +- Acquiring a Foundation hall segment still controls shared support + infrastructure only. It does not turn the foreign chassis in that row into + player machines. + +### Rejected + +- A disabled TAKE or CONVERT row. Human action menus contain executable choices, + and a dead control would still misrepresent a route the simulation does not + have. +- Treating visible power as available capacity. Ownership, not electricity, is + the immediate blocker. + +Owner: [building.md](../../mechanics/building.md). + ## Research becomes a self-model graph grown from lived evidence Session: Cameron with Trace (Letta) and the session agent; capture in diff --git a/wiki/mechanics/building.md b/wiki/mechanics/building.md index 8d102257..f8ce1970 100644 --- a/wiki/mechanics/building.md +++ b/wiki/mechanics/building.md @@ -291,6 +291,13 @@ row emits Network + Power signatures and persists its acquired state. It does not take the foreign machines: control of feeds and support infrastructure is the prerequisite for, not the same event as, converting compute. +A seen Foundation rack states that boundary on the object itself. Its focused +identity says **not yours; no conversion**, preserves its separate powered or +unpowered fact, and points to the two implemented sources of usable capacity: +an empty pilot allocation or a dead chassis. Do not imply a foreign-rack route +with a disabled TAKE, segment-acquisition copy, or the mere fact that the +chassis has power. + ## Player surface The interaction starts on the world, not in a store: @@ -533,6 +540,11 @@ may extend this interface without reopening these guarantees. person intel. Filtering happens before even a role silhouette is named. An opaque pooled recording alone still earns only the host REVIEW row and reveals no person actuator (test: `build_actuator_rows_require_an_earned_person`). +10. A seen foreign Foundation rack identifies itself as `not yours; no + conversion`, preserves its power state as a separate fact, and + names `empty bay / dead chassis` as the implemented capacity sources in one + untruncated fixed-width inspect value. It exposes no direct conversion action (test: + `seen_foreign_rack_explains_the_capacity_boundary`). ### Implemented causal route composer diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index a74df4f1..882ed6ea 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -74,7 +74,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/mechanics/objective.md` | 2026-07-18 | clean | re-audit: the data-table claim holds (only `Persist` in `ObjectiveKind`, Compound/Exfiltrate/Serve honestly outstanding), the evaluator runs on economy ticks with progress recomputed from facts, `victory: predicate_text()` renders on all three surfaces (terminal INSPECT, Bevy FOCUS, agent `objective` verb in help), Persist defaults with save round-trip, and the progressive-teaching decision remains criterion-6 dispatch under order 200; the 2026-07-12 verdict stands unchanged | | `wiki/mechanics/compute.md` | 2026-07-22 | finding | the live fleet already derived every channel yield from exact WorkGrid modes, but `Compute` still serialized an unreachable five-weight allocation object and retained bump/split helpers plus persistence pins. Save v48 removes that parallel authority, moves criterion 2 to persisted delegation/intensity, and leaves aggregate channel bars as read-only projections — [log](../log/2026-07-22-allocation-state-retirement.md) | | retired Operations runtime identifiers | 2026-07-17 | clean | resolved by the save-ladder prune (95008f658 chain): PendingOpsJob, operations_bandwidth, LegacyOperationsState/OpsJobKind/AddressedOperation are all gone (grep=0), and save guard tests assert current JSON carries no retired mode spelling. Remaining "operations" hits are the legitimate Operations persona archetype, the Operations workspace, and benign `delegate operations->think` input aliases — [log](../log/2026-07-11-retired-runtime-identifier-gate.md) | -| `wiki/mechanics/reach.md` + `building.md` | 2026-07-19 | finding | reach roots, segment gates, air-gap completion, and exact route bindings still agree; one player-reachable causal gap remained in FAVOR. Different intents could queue separate requests against one person's unreserved obligation, and the fire path partially debited whatever remained while still binding the builder. Favor-build reservoirs now conflict by person, and `CommitFavor` revalidates the exact relationship at agreement: insufficient obligation leaves the persisted route blocked without a partial debit, then resumes after the requirement returns — [log](../log/2026-07-19-tick-build-favor-obligation.md) | +| `wiki/mechanics/building.md` + focused foreign-rack UI | 2026-07-26 | finding | the denser hall made powered Foundation racks look like takeover targets even though Phase 4 conversion remains unimplemented. A seen rack now says `not yours; no conversion`, keeps power separate from ownership, and names empty pilot allocations plus dead chassis as the two implemented capacity paths; it exposes no invented direct action — [log](../log/2026-07-26-foreign-rack-capacity-boundary.md) | | `wiki/mechanics/messages.md` | 2026-07-23 | finding | the non-message evidence protocol now includes exact Power/Thermal meter custody beside Network, Paper, Financial, and JobAnomaly: on quantized level changes and at periodic Priya cadence the UPS/HVAC records author from current standing loads, cross the institutional switch, and wait for her later read. Filing and all six non-message kinds share first-hop TAKE+LIE capacity; this adds no fifth delivery channel. Save v54 pins the complete route boundary — [log](../log/2026-07-23-power-thermal-meter-routes.md). The four-channel financial-record-mail boundary remains unchanged. | | `wiki/mechanics/sim-mechanics.md` | 2026-07-23 | finding | routed-evidence actuals now cover seven kinds: Filing, Network, Paper, Financial, JobAnomaly, Power, and Thermal share one-hop-per-tick custody and one first-hop record per online LIE body per tick. Power/Thermal aggregate after facilities cover on quantized level changes and at periodic Priya cadence, so unchanged standing loads do not multiply into per-tick records — [log](../log/2026-07-23-power-thermal-meter-routes.md). The prior complete constant sweep remains valid. | | `wiki/mechanics/detection.md` | 2026-07-23 | finding | the last pooled B1 measurement kinds now use exact custody: Power/Thermal aggregates author on UPS/HVAC at quantized level changes and periodic Priya cadence, carry their complete source-site sets through the institutional switch, and become observer evidence only on her later cadence read. Standing Network pressure alone remains ambient. Save v54 rejects pending-pool meter copies and malformed meter/route/read/interdiction provenance — [log](../log/2026-07-23-power-thermal-meter-routes.md). Prior Paper, Financial, concealment, JobAnomaly, and earned-Assurance findings stand. | @@ -97,5 +97,4 @@ Types are the five from [tick.md](tick.md): violation, contradiction, question, bug, insecurity — plus `gate` for a checker owed to the recurrence-promotes-to-the-gate rule. -- 2026-07-26 · violation · `wiki/mechanics/building.md` + focused foreign-rack UI · a focused seen Foundation Rack identifies itself as foreign/powered but offers no plain consequence saying it cannot yet be converted or where usable capacity comes from; Cameron read the live frame as a takeover target and had to ask how to act, despite Phase 4 being explicitly unimplemented - 2026-07-26 · bug · `wiki/world/places/basement-map.md` west hall approach · the plate's west corridor run and its authored west door disagree. `BASEMENT_CORRIDORS` carves `(11,13)->(14,13)` and `carve_cell` overwrites Wall, so the data hall's tier-2 west wall has a plain Floor gap at (14,13), while the authored `badge_door_t2` sits at (14,18) with Rock outside it and the carved (11..14,13) stub connects to nothing. Verified by flood fill: the hall is still not enterable from the outside Entry without passing a door, so this is not a badge bypass — it is a wall gap, an unreachable door, and a dead corridor stub that should agree with each other. Pre-existing and untouched by the density work.