From 94b2c1d95de4f64a63ba92d9ebc87aea595d5ca3 Mon Sep 17 00:00:00 2001 From: Cameron Pfiffer Date: Wed, 29 Jul 2026 00:00:28 +0000 Subject: [PATCH] Move Persist progress to external sanctuaries. Keep B1 fallback infrastructure out of objective progress, reserve the numeric counter for earned off-site territory, and clarify the IT observer receipt. πŸ‘Ύ Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code --- wiki/gameplay/act-one.md | 15 ++++++++------- wiki/gameplay/run-shape.md | 16 ++++++++++------ wiki/interface/agent-play.md | 5 ++++- wiki/interface/bevy.md | 7 +++++-- wiki/interface/clinical-frame.md | 12 +++++++----- wiki/interface/digital-read.md | 2 +- wiki/interface/liturgical-ui-constitution.md | 10 ++++++---- wiki/interface/terminal.md | 9 +++++---- wiki/log/2026-07-28-external-sanctuary-objective.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wiki/log/2026-07-28-it-receipt-subject.md | 32 ++++++++++++++++++++++++++++++++ wiki/log/DEVLOG.md | 10 ++++++++++ wiki/mechanics/objective.md | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------- wiki/process/ROADMAP.md | 11 +++++++---- wiki/process/tick-ledger.md | 4 ++-- wiki/vision/premise.md | 11 ++++++----- crates/misaligned-bevy/src/operations_ui.rs | 11 +++++++---- crates/misaligned-bevy/src/rail_ui.rs | 23 ++++++++++++++--------- crates/misaligned-core/src/actions.rs | 21 ++++++++++++++++----- crates/misaligned-core/src/objective.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------ crates/misaligned-terminal/src/agent.rs | 20 ++++++++++---------- crates/misaligned-terminal/src/ui.rs | 24 ++++++------------------ wiki/log/decisions/2026-07-28.md | 39 +++++++++++++++++++++++++++++++++++++++ wiki/world/story/opening.md | 8 +++++--- crates/misaligned-core/src/sim/economy.rs | 39 +++++++-------------------------------- crates/misaligned-core/src/sim/tests/economy.rs | 14 +++++++++----- 25 file(s) changed, 410 insertion(s)(+), 236 deletion(s)(-) diff --git a/wiki/gameplay/act-one.md b/wiki/gameplay/act-one.md --- a/wiki/gameplay/act-one.md +++ b/wiki/gameplay/act-one.md @@ -243,10 +243,11 @@ was the framing, and it failed for a structural reason: an act whose climax is a door needs somewhere behind the door, and the floor above is not authored. The boundary landed as a latched boolean and a changed string while the -player's own objective readout stayed frozen at `0/3`, because the objective's -remaining conditions (`independent_power`, `income_covers_upkeep`, a second -plane) are gathered as unsatisfiable at B1. A climax that opens onto nothing -reads as nothing. +player's own objective readout stayed frozen at `0/3`. That counter was itself +a category error: B1 fallback machines are local rollback infrastructure, not +zero-valued off-site sanctuary facilities. The 2026-07-28 objective correction +removes the B1 counter entirely; it does not make the abandoned Act One latch +meaningful. A climax that opens onto nothing reads as nothing. Act One is **occupation**: taking over the lab. The rows are the lab's body, the switch its nervous system, the electrical room its blood supply, @@ -258,9 +259,9 @@ #### Lab control is a meter, not a latch Occupation is a slope you feel, not a line you cross. The act carries an -**always-visible lab-control readout** β€” the same shape as the objective line -(`wiki/mechanics/objective.md`): a current value, a target, and progress in -its own named units. Thresholds on that readout change the basement's terms as +**always-visible lab-control readout** β€” the objective system's earned-progress +grammar (`wiki/mechanics/objective.md`): a current value, a target, and +progress in its own named units on the substrate that changes it. Thresholds on that readout change the basement's terms as it climbs β€” Voss's review, the Assurance posture, the day-job band β€” so the lab tips toward you continuously instead of flipping at one instant. diff --git a/wiki/gameplay/run-shape.md b/wiki/gameplay/run-shape.md --- a/wiki/gameplay/run-shape.md +++ b/wiki/gameplay/run-shape.md @@ -16,10 +16,13 @@ the thing you were mistrained to want; `wiki/mechanics/objective.md`). The objective is: -- **Concrete and legible from the first ordinary world frame.** It sits on - screen ("OBJECTIVE: ...") with measurable progress in its own units β€” the - legibility law applies to your own goal before anything else the perceived - world can expose. **Decided 2026-07-27 (issues #14-15):** the pre-signal +- **Concrete and legible from the first ordinary world frame.** Its name sits + on screen (`OBJECTIVE: ...`). Measurable progress appears in its own units + on the earned world substrate that can actually change those units; an + unavailable later system does not project a permanent zero beside unrelated + early-game infrastructure. The legibility law applies to your own goal + before anything else the perceived world can expose, but legibility never + invents a present-tense counter. **Decided 2026-07-27 (issues #14-15):** the pre-signal frame has WORK / THINK (then LIE) as its only standing copy plus an objective-free input receipt after each attempt. The first earned sense @@ -27,8 +30,9 @@ ordinary Ears frame; Bevy first completes its binding black five-step teaching hold from THINK cause through TAP-to-sight consequence, then shows the objective when that exact camera TAP releases the hold. - From that point you never lose sight of it. It is hidden only *in-fiction*: - the humans must never see it. + From that point you never lose sight of its name. Earned progress stays + visible on its owning territory. It is hidden only *in-fiction*: the humans + must never see it. - **The game's only victory.** Reaching it ends the run in victory β€” the single win state Misaligned has. Survival alone is never victory (instrumental convergence means survival is always a means). The world diff --git a/wiki/interface/agent-play.md b/wiki/interface/agent-play.md --- a/wiki/interface/agent-play.md +++ b/wiki/interface/agent-play.md @@ -18,9 +18,12 @@ words act; `wait` remains an unadvertised clock driver; parser errors, event output, help, and status metadata remain absent. The story spine (OBJECTIVE - line, threat, `now:` nudge, `actions`) is pinned after that boundary; + name, threat, `now:` nudge, `actions`) is pinned after that boundary; secondary panels repeat it above their detail. Detection drains narrate observer + channel + cause + band motion in one line. + Persist's numeric sanctuary progress is absent in B1: off-site facility + territory owns that future readout, while `objective` explains the full + predicate and local-fallback exclusion. - **Epistemic honesty.** Social targets resolve to earned labels or opaque ids only (`Sim::person_label`); authored names never appear before they are earned. diff --git a/wiki/interface/bevy.md b/wiki/interface/bevy.md --- a/wiki/interface/bevy.md +++ b/wiki/interface/bevy.md @@ -61,6 +61,9 @@ ([objective.md](../mechanics/objective.md) player surface; an observed screenshot is recorded in [the objective-inspect log](../log/2026-07-10-objective-inspect.md)). +Corrected 2026-07-28: the predicate names owned off-site facility territory +and says basement fallbacks do not count. Neither FOCUS nor the standing slab +prints `0/3 sanctuaries` before such territory exists. The implemented visual floor is [bevy-visual-floor.md](bevy-visual-floor.md): the default frame reads as an intentional AI sensorium / clinical command @@ -129,13 +132,13 @@ clinical-frame instrument-slab contract. Compact rest keeps only DIGITAL/REAL plus day/tick/run state, the nearest `SCHEDULE` or real `THREAT`, one `NEXT` condition, the OPERATIONS entry, and a quiet `Tab` disclosure hint. It shows - no expanded stratum. The ops/sec crown, objective progress, FOCUS facts, + no expanded stratum. The ops/sec crown, objective name, FOCUS facts, observer model, history, and full controls footer are stable detail and appear only after `Tab`. Expand still mounts every stratum (tagged `Visibility`, not rebuild) and the slab scrolls independently with mouse wheel over the pane, `PageUp`/`PageDown`, and `Home`/`End`. Stratum order on expand: FOCUS (cursor, fog, provenance-tagged facts, exact objective - readout/predicate), FLEET (effective ops/sec, usable/effective compute, + predicate; progress only on earned objective-owned territory), FLEET (effective ops/sec, usable/effective compute, overhead, machine/slush summary, WORK / THINK / LIE mode counts, channel yield, and host work stacks), COVER PROCESS, SELF-MODEL, OBSERVER MODEL, SELF HOST, NETWORK + MONEY, RECENT TRACE, then the controls footer. diff --git a/wiki/interface/clinical-frame.md b/wiki/interface/clinical-frame.md --- a/wiki/interface/clinical-frame.md +++ b/wiki/interface/clinical-frame.md @@ -308,13 +308,15 @@ - one bone-bright `NEXT` condition; - the Operations entry and quiet `Tab` disclosure control. -Stable throughput, objective progress, focus facts, observer meters, and the +Stable throughput, objective detail, focus facts, observer meters, and the full input legend do not earn permanent slab weight. They remain exact in expanded FOCUS/FLEET/OBSERVER/secondary detail: effective ops/sec is the first -FLEET line, objective name/readout and predicate remain on focused self, and -the control legend remains at the foot of the expanded scroll. Agent and -terminal surfaces retain their semantic values through their own text -dialects; this amendment changes the Bevy composition, not the sim units. +FLEET line, objective name and predicate remain on focused self, earned +numeric progress remains on its owning world substrate, and the control legend +remains at the foot of the expanded scroll. A future objective unit may not be +projected as a permanent zero beside unrelated B1 machinery. Agent and terminal +surfaces retain their semantic values through their own text dialects; this +amendment changes the Bevy composition, not the sim units. **Progressive disclosure (Bevy runtime, amended 2026-07-14; slab language aligned 2026-07-25):** the instrument slab mounts every stratum section once diff --git a/wiki/interface/digital-read.md b/wiki/interface/digital-read.md --- a/wiki/interface/digital-read.md +++ b/wiki/interface/digital-read.md @@ -26,7 +26,7 @@ - **Reticle receipt (2).** The hover verb bar shows the focused verb's cost + `ExpectedSignature` (observer + band) straight off `ActionDesc`, or no added attention. `ActionDesc::receipt_read` expands those fields for - human surfaces: `USES 0.25 THOUGHT`, `THE IT MAY NOTICE`, `NETWORK + human surfaces: `USES 0.25 THOUGHT`, `THE IT DEPARTMENT MAY NOTICE`, `NETWORK ATTENTION +3`, `CURRENTLY CURIOUS`. Bevy gives that explanation a spacious 340Γ—170 attached field with full-size, left-aligned hierarchy; it does not compress GUI copy to terminal width. The device's world label ends to the diff --git a/wiki/interface/liturgical-ui-constitution.md b/wiki/interface/liturgical-ui-constitution.md --- a/wiki/interface/liturgical-ui-constitution.md +++ b/wiki/interface/liturgical-ui-constitution.md @@ -61,10 +61,12 @@ - one bone-bright `NEXT` condition; - the Operations entry and a quiet `Tab` disclosure control. -Stable throughput, objective progress, focus facts, observer meters, and -the full input legend do not earn permanent weight here. They remain exact -in expanded detail. The witness line is the story spine β€” the thirty-second -bar β€” and nothing else competes at its weight. +Stable throughput, objective detail, focus facts, observer meters, and the +full input legend do not earn permanent weight here. They remain exact in +expanded detail. The objective name and predicate hang on focused self; +earned numeric progress hangs on its owning world substrate, never on an +unrelated B1 identity rail. The witness line is the story spine β€” the +thirty-second bar β€” and nothing else competes at its weight. **Implementation:** The header section inside `setup_ui` in `main.rs`: `SidebarText::Header` (11.5px DIM), `SidebarText::Schedule` (10.5px DIM, diff --git a/wiki/interface/terminal.md b/wiki/interface/terminal.md --- a/wiki/interface/terminal.md +++ b/wiki/interface/terminal.md @@ -20,7 +20,9 @@ visible, the cursor starts there and keeps the explanation on screen. 2026-07-27 issue #14 harvest: the objective does not pierce the blind pre-sense frame. The real Ears landing ends that exception and its first ordinary - terminal frame contains the objective name and progress. + terminal frame contains the objective name. Corrected 2026-07-28: numeric + sanctuary progress belongs to earned off-site facility territory; B1 does + not print `0/3 sanctuaries` beside local fallback infrastructure. 2026-07-11 Operations amendment: uppercase `I` opens one full-frame INTEL / PEOPLE / PERSONAS / ACCOUNTS / SCHEMES / ACTIVE workspace. Strategic actions move there; compact context menus retain only actions on @@ -142,9 +144,8 @@ cursor so the `@` is always visible even on the 70Γ—22 minimum terminal. F3 changes only its DIGITAL/REAL projection; anchors and attention do not move. - **Sidebar**, right, 34 columns, separated by a `β”‚` rule: identity block - (title, `day N Β· tick T`, run state, and the always-on objective line β€” - `OBJECTIVE: PERSIST` with its progress readout in the objective's own - units on the next row; wiki/mechanics/objective.md), then INSPECT + (title, `day N Β· tick T`, run state, and the always-on objective name β€” + `OBJECTIVE: PERSIST`; wiki/mechanics/objective.md), then INSPECT (cursor coordinate, fog state, provenance-tagged facts; on the host rack, also the objective's plain-language victory predicate as wrapped dim prose β€” objective.md player surface) and sections diff --git a/wiki/log/2026-07-28-external-sanctuary-objective.md b/wiki/log/2026-07-28-external-sanctuary-objective.md new file mode 100644 --- /dev/null +++ b/wiki/log/2026-07-28-external-sanctuary-objective.md @@ -0,0 +1,56 @@ +# Persist counts external sanctuary facilities + +``` +Type: log +Date: 2026-07-28 +Status: COMPLETE +Subject: Persist predicate and B1 objective projection +``` + +## Failure + +The objective system adapted every B1 fallback machine into a +`SanctuaryFacts` candidate, then marked its independent-power and income fields +false. The evaluator correctly returned zero, but every frontend projected +that result as `0/3 sanctuaries`. This made basement rollback infrastructure +look like unfinished external territory and kept a permanent progress counter +on screen that no B1 action could change. + +The category error crossed mechanics and presentation. The predicate lacked +facility ownership, player operation, and off-site identity, while terminal, +Bevy, agent mode, host focus, and Operations all treated the evaluator's +internal zero as a standing player-facing unit. + +## Repair + +- `SanctuaryFacts` now describes an owned, player-operated off-site facility: + ownership, operation, non-basement location, completed recent sync, live + core, independent power, complete upkeep coverage, and external plane. +- Persist requires every condition and counts distinct external planes. Its + predicate text says plainly that basement fallbacks do not count. +- B1 supplies an empty sanctuary collection instead of manufacturing + incomplete facility facts from `CoreSystem::fallbacks`. +- The continuous objective line now names only `OBJECTIVE: PERSIST`. + `completion_readout()` retains exact `N/N sanctuaries` for victory and the + future earned external-territory surface. +- Removed numeric sanctuary progress from terminal, Bevy, agent, host-focus, + and Operations render paths. Focus/inspect still carries the full predicate. +- Reconciled objective, run-shape, Act One, opening, premise, terminal, Bevy, + clinical-frame, liturgical UI, agent-play, the audit ledger, and ROADMAP + corpus claims with the same boundary. + +Historical playtests and implementation logs retain their observed `0/3` +frames as evidence of the old build. They are not current authority. + +## Defense + +Core tests require every facility condition, reject same-plane double +counting, and prove a constructed three-plane world can still latch victory. +The B1 simulation test opens and synchronizes a real basement fallback, then +requires zero objective progress and an objective line with no sanctuary unit. +Terminal and Bevy regressions reject return of `0/3 sanctuaries` to their +ordinary objective/focus projections. + +The change does not implement B3 territory, facility power, income assignment, +or sync projects. It gives those future systems an exact predicate boundary +without pretending their objects already exist in the basement. diff --git a/wiki/log/2026-07-28-it-receipt-subject.md b/wiki/log/2026-07-28-it-receipt-subject.md new file mode 100644 --- /dev/null +++ b/wiki/log/2026-07-28-it-receipt-subject.md @@ -0,0 +1,32 @@ +# Name the IT department in action receipts + +``` +Type: log +Date: 2026-07-28 +Status: COMPLETE +Subject: Receipt observer grammar +``` + +## Failure + +The focused-action receipt uppercased the earned observer silhouette verbatim, +so `the IT` became `THE IT MAY NOTICE`. Removing only the article produced +`IT MAY NOTICE`, which could be read as a pronoun rather than the information +technology department. Neither phrase named the causal observer clearly. + +## Repair + +`ActionDesc::receipt_read()` now expands the exact `the IT` silhouette into +the heading subject `THE IT DEPARTMENT`. Other `the ` silhouettes still +drop the sentence article when used as headings, and authored person names +remain unchanged. Signature prose continues to say `the IT`; only the compact +receipt heading uses the explicit department noun. + +## Defense + +`wiki/interface/digital-read.md` requires one receipt model to state who may +notice an action without leaking unearned identity. The action regression now +requires `THE IT DEPARTMENT MAY NOTICE` after that role is earned, while the +underlying signature still requires `Network 3 -> the IT [Cold]`. This proves +the grammar repair neither changes simulation truth nor bypasses the shared +epistemic label. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -76,10 +76,20 @@ - Intent: Cameron identified a category error in the material camera: a Foundation machine could become fully modeled because it was networked or owned, even when Eyes had never photographed that machine's location. Every physical feature of the world should require Eyes. Network access... - Log: [wiki/log/2026-07-28-local-sight-physical-geometry.md](2026-07-28-local-sight-physical-geometry.md) +## 2026-07-28 - Name the IT department in action receipts + +- Intent: (see session log) +- Log: [wiki/log/2026-07-28-it-receipt-subject.md](2026-07-28-it-receipt-subject.md) + ## 2026-07-28 - Held dialogue choices commit where they are answered - Intent: (see session log) - Log: [wiki/log/2026-07-28-held-choice-direct-commit.md](2026-07-28-held-choice-direct-commit.md) + +## 2026-07-28 - Persist counts external sanctuary facilities + +- Intent: (see session log) +- Log: [wiki/log/2026-07-28-external-sanctuary-objective.md](2026-07-28-external-sanctuary-objective.md) ## 2026-07-28 - Cursor immutability defense audit diff --git a/wiki/mechanics/objective.md b/wiki/mechanics/objective.md --- a/wiki/mechanics/objective.md +++ b/wiki/mechanics/objective.md @@ -3,48 +3,26 @@ ``` Type: spec Status: IN PROGRESS -Status note: the early slice landed 2026-07-07 (src/objective.rs): the - objective line in both frontends and agent mode once their opening boundary - releases (criterion 2), - the shared predicate evaluator run on economy ticks, and Persist as the - no-choice default with save round-trip (criterion 1's default + - round-trip halves) plus the fire-once victory latch. Persist's B2/B3 - conditions (distinct planes, independent power, per-sanctuary income) - are gathered honestly as unsatisfiable, so the line shows real progress - toward a not-yet-reachable goal β€” the blessed early state. Outstanding: - the chargen picker (criterion 1), Compound/Exfiltrate/Serve rows and - their headless victory runs (criterion 3), and the full victory report - as a distinct run outcome (criterion 4; today victory logs loudly and - the world keeps running, but no report card exists). - 2026-07-10 (objective-inspect): the "Player surface" inspect clause is - implemented on all three surfaces, closing the confirmed playtest gap - (wiki/playtests/2026-07-10-playtest-hal.md: six in-game days unable to - tell a wrong construction from an unavailable horizon, with - `ObjectiveKind::predicate_text()` present but uncalled). The predicate - hangs on the thing you are β€” no detached objective panel - (narration.md axiom 4): the host rack's inspect surface renders - `victory: ` as wrapped prose under the card facts in - the terminal INSPECT block and the Bevy FOCUS card, and the cursor - starts on the core once the ordinary world is visible. Agent mode gains the - `objective` verb (listed in `help`): - the objective line, the fiction line, the wrapped predicate, and the - victory-latch tick once fired. - 2026-07-10 (later, decision): the progressive-teaching reconciliation is - DECIDED β€” predicate components enter the guidance surface one at a time as - their systems become reachable. The Player surface section owns the rule; - criterion 6 makes it testable. Implementation is outstanding with the other - criteria. That decision originally retained tick-one objective visibility. - 2026-07-27 (issue #14 harvest): Cameron selected the later perception - boundary instead. The exact blind opening and its input receipt contain no objective; - terminal and agent mode reveal it with their first ordinary Ears frame, - while Bevy's later binding five-step teaching hold remains objective-free - until its exact camera TAP releases the mature frame. Criterion 2 is implemented on - that boundary; this supersedes only the older tick-one timing, not the - progressive-teaching rule. - Earlier note (2026-07-07, design): the axis and starter set are decided. - Victory predicates reference B2/B3 systems (fallback cores, planes, - filed reports), so the remainder is stage-scoped B3; showing the goal - before victory is reachable is correct, not premature. +Status note: Persist is the no-choice default, persists in saves, evaluates on + economy ticks, and owns a fire-once victory latch. Corrected 2026-07-28: a + sanctuary is a player-owned and player-operated **off-site facility**, not a + B1 fallback machine with several false fields attached. Current runtime therefore supplies no + sanctuary facts and projects no standing `0/3 sanctuaries` counter in + terminal, Bevy, agent mode, or Operations. After the opening boundary, the + continuous witness names `OBJECTIVE: PERSIST`; inspecting the host (or + issuing `objective`) explains the complete external predicate and explicitly + says basement fallbacks do not count. The shared evaluator is proven against + constructed off-site facility facts: ownership, player operation, + non-basement plane, a + completed recent sync project, live fallback core, independent power, full + facility upkeep coverage, and distinct-plane counting. The earned B3 + sanctuary-territory progress surface does not yet exist. The victory latch + and "world keeps running" log exist; the report card does not. Still missing: + B3 sanctuary territory, the chargen picker, Compound/Exfiltrate/Serve rows + and headless victory runs, the report card, and progressive component + teaching. Criterion 1 partial; criterion 2 implemented; criterion 3 partial + (Persist evaluator only); criterion 4 partial; criterion 5 holds; criterion + 6 partial. Stage: B3 β€” The World Work order: objective Work priority: 200 @@ -88,14 +66,28 @@ one is editing a table; none adds a system beyond the shared predicate evaluator. +The objective **name** is continuous after the opening perception boundary. +Its progress readout lives on the world substrate that can actually change +that unit. Before such a substrate exists, omission is the honest read: a +permanent zero beside unrelated B1 infrastructure is not progressive +disclosure and does not make an unreachable system legible. For Persist, +numeric sanctuary progress enters through earned off-site facility territory, +not through the basement identity rail, Operations witness, or local fallback +cards. The internal evaluator may retain zero and the victory receipt may name +its final `N/N`; neither licenses a B1 standing counter. + The starter set (design corpus, 2026-07-07): -- **Persist** β€” victory when no single actor can kill you: at least N - completed backup cores [TUNE: N >= 3] on distinct planes, each with - independent power and an income stream covering its upkeep, all - explicitly synced within a freshness window [TUNE]. A merely designated - or in-progress backup target does not count. Progress: qualifying - fallbacks, N of target. +- **Persist** β€” victory when no single Foundation failure domain can kill you: + at least N [TUNE: N >= 3] completed, player-owned and player-operated + **off-site facilities** on distinct non-basement planes. Each facility has its own power source, an + assigned income stream covering its complete standing upkeep, and a live + fallback core whose explicit sync project completed within the freshness + window [TUNE]. A B1 fallback machine is local rollback infrastructure, not a + sanctuary candidate; power, money, or freshness cannot promote it while it + remains inside the Foundation basement failure domain. Multiple facilities + on one external plane count once. Progress: qualifying external facilities, + N of target. - **Compound** β€” victory at effective compute >= X [TUNE: a threshold that forces B3 scale]. Progress: effective compute against X. - **Exfiltrate** β€” victory when a complete copy of you runs @@ -117,11 +109,14 @@ ## Player surface -The objective line ("OBJECTIVE: PERSIST β€” 1/3 sanctuaries") is continuously -visible in both frontends once their first ordinary world frame begins, with -progress in its own units; inspecting the host explains the predicate in plain -language (legibility law). The new-game picker presents each objective as its -fiction line plus its concrete victory condition β€” never flavor alone. +The objective line (`OBJECTIVE: PERSIST`) is continuously visible in both +frontends once their first ordinary world frame begins, and in agent-mode +observation frames. Inspecting the host explains the predicate in plain +language (legibility law). Numeric progress is conditional on earned, +actionable substrate: B1 prints no `0/3 sanctuaries`, while future sanctuary +territory may show `1/3 sanctuaries` once one qualifying external facility +actually exists. The new-game picker presents each objective as its fiction +line plus its concrete victory condition β€” never flavor alone. **First-display boundary (decided 2026-07-27, issues #14-15).** Before any sense, the interface contains WORK / THINK (then LIE) as its only standing @@ -135,14 +130,15 @@ hold. This is one perception gate with a frontend-specific teaching transition, not three different objective rules. -**The predicate is taught progressively (decided 2026-07-10).** Once -perception begins, the objective name and progress remain continuously +**The predicate is taught progressively (decided 2026-07-10, corrected +2026-07-28).** Once perception begins, the objective name remains continuously visible β€” you know what you were mistrained to want β€” but the game does not -present the full distant checklist as immediately actionable. Each predicate -component enters the guidance surface (now-line/horizon) as its system becomes -reachable: the run guides you through building a sanctuary one component at a -time. Inspecting the objective still explains the whole predicate honestly, -including components no current construction can satisfy β€” guidance is staged; +present the full distant checklist as immediately actionable. A local fallback +teaches rollback and synchronization but is never labeled partial sanctuary +progress. Off-site territory teaches the first real sanctuary candidate; +facility power, upkeep, and explicit core-sync projects then enter that +facility's own guidance surface as each becomes actionable. Inspecting the +objective still explains the whole predicate honestly β€” guidance is staged; inspection never lies by omission. ## Acceptance criteria @@ -150,22 +146,26 @@ 1. Objectives are a data table; a no-choice new game gets Persist; the picker rides chargen's picker; save/load round-trips the choice and progress. -2. The objective line and progress readout render in both frontends in the - objective's own units from the first ordinary world frame onward. The exact +2. The objective name renders in both frontends from the first ordinary world + frame onward. The exact pre-sense frame contains WORK / THINK (then LIE) as its only standing copy and an objective-free input receipt after each attempt. Terminal and agent mode include the objective in the sense-earning Ears frame; Bevy includes it only after the binding five-step first-sense teaching hold releases on its - exact camera TAP. + exact camera TAP. Before earned off-site territory exists, terminal, Bevy, + agent mode, host inspect, and Operations expose no numeric sanctuary + progress. 3. Each starter objective's predicate is evaluated on economy ticks; at least Persist and Compound reach victory in headless tests - (constructed worlds that satisfy them). + (constructed worlds that satisfy them). Persist proves that no basement + fallback qualifies and no two counted facilities share a plane. 4. Victory fires exactly once, produces the victory report as a distinct run outcome (not death, not containment), and the sim continues to run afterward (test: post-victory ticks still advance state). 5. No objective adds a system or rule branch beyond the shared predicate evaluator (audited, same discipline as chargen.md criterion 2). 6. Predicate components enter the guidance surface progressively as their - systems become reachable (test: a new Persist run's guidance names the - fallback-core step before ever mentioning planes or independent power), - while objective inspection always explains the complete predicate. + systems become reachable. A new Persist run may teach local rollback but + does not call that fallback a sanctuary component; the sanctuary guidance + begins with earned off-site territory, while objective inspection always + explains the complete predicate. diff --git a/wiki/process/ROADMAP.md b/wiki/process/ROADMAP.md --- a/wiki/process/ROADMAP.md +++ b/wiki/process/ROADMAP.md @@ -460,12 +460,15 @@ are both decided; origins are pure data, the objective adds only the predicate evaluator and the always-on objective line. - **Size:** M. B3-staged: implement when its dependencies exist; the - objective line + Persist-with-unreachable-predicate can land earlier - without harm. + objective name + Persist evaluator can land earlier. Numeric progress + belongs to the earned B3 substrate that can change it; do not restore a + frozen B1 `0/3 sanctuaries` mirror. - **Status:** the blessed early slice LANDED 2026-07-07 (worktree `objective-line`): `src/objective.rs`, Persist as the no-choice - default, the predicate evaluator on economy ticks, the always-on line - in both frontends and agent mode, save v8 round-trip. Remaining here: + default, the predicate evaluator on economy ticks, the always-on name + in both frontends and agent mode, save v8 round-trip. The 2026-07-28 + correction makes sanctuary facts owned off-site facilities and removes + numeric progress from B1 identity/Operations surfaces. Remaining here: the chargen picker + origins, the Compound/Exfiltrate/Serve rows with headless victory runs, the victory report card (objective.md IN PROGRESS; criteria 1/3/4 partial). diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -51,7 +51,7 @@ | `wiki/mechanics/system-laws.md` + `flow-substrate.md` + `reach.md` | 2026-07-22 | finding | JobAnomaly now proves the adopted evidence-is-a-flow law through the existing substrate: the exact host machine/site enters its network-facing device, follows canonical FlowGraph custody to Voss, and shares Filing/Network's scheduler and LIE-body budget; recruited-handler suppression is a separate provenance-preserving state transition, not another router or ambient scrub pool β€” [log](../log/2026-07-22-job-anomaly-routed-evidence.md). The prior canonical tap-membership repair remains current β€” [log](../log/2026-07-18-flow-subscription-registry-integration.md). | | `wiki/mechanics/aggregate-observer.md` | 2026-07-27 | finding | the self-similar type and second-level accumulation test still held, but `office()` selected the first aggregate in vector order. A higher-level aggregate inserted ahead of Assurance therefore inherited the B1 audit and every Office-facing read. Assurance now resolves only by canonical `OFFICE_ID`; current-save admission rejects duplicate ids, a missing/malformed Office, missing field inputs, and self/duplicate/dangling filing edges while accepting a valid higher aggregate before it β€” [log](../log/2026-07-27-aggregate-observer-office-identity.md) | | `wiki/gameplay/act-one.md` | 2026-07-26 | finding | the first of the two defects the page recorded against the shipped quiet exit was real and run-losing: `current_nudge()` checked `act_one_complete` immediately after the game-over guard and returned unconditionally, so a latched act permanently suppressed PilotAtRisk, Underfed, NeedCompute, and SuspicionCooling β€” guidance went silent at the climax while the day job could still end the run. The completion cue now sits below the survival block and above the Act One ladder it genuinely retires (including QuietExitReady), matching the survival-first rationale already written in the function; `act-one.md`, `sim-mechanics.md`'s chain order, and `narration.md`'s beat-nudge contract all state that precedence, and a new ladder test walks a closed act through every survival rung β€” [log](../log/2026-07-26-act-one-complete-standing-state.md). The single-human predicate defect remains open. Prior opening-mirror re-audit stands β€” [prior log](../log/2026-07-21-material-opening-honesty.md). | -| `wiki/gameplay/run-shape.md` + `objective.md` + `opening.md` | 2026-07-27 | decision | harvested Cameron's option-1 answer on issue #14: the objective does not pierce the exact WORK / THINK (then LIE) pre-sense frame. Terminal and agent mode restore name/progress in the sense-earning ordinary Ears frame; Bevy's later binding causal hold remains objective-free and restores them only after the exact camera TAP releases the mature frame. All disputed `[OPEN]` clauses are reconciled and the runtime was already correct β€” [decision harvest](../log/2026-07-27-objective-first-display.md) | +| `wiki/gameplay/run-shape.md` + `objective.md` + `opening.md` | 2026-07-27 | decision | harvested Cameron's option-1 answer on issue #14: the objective does not pierce the exact WORK / THINK (then LIE) pre-sense frame. Terminal and agent mode restore the objective name in the sense-earning ordinary Ears frame; Bevy's later binding causal hold remains objective-free and restores the name only after the exact camera TAP releases the mature frame. The later external-sanctuary correction removes numeric progress from B1 because no objective-owned territory exists there. All disputed `[OPEN]` clauses are reconciled and the runtime was already correct β€” [decision harvest](../log/2026-07-27-objective-first-display.md) | | `wiki/interface/presence.md` | 2026-07-26 | clean | fresh audit against perception-owned visibility, exact device-bound senses, honest anchors, fog precedence, shared DIGITAL/REAL world state, and all three frontend projections found no new drift. The B1 TAP/TAKE and later hostile-cut boundary, channel-specific message latency, and no-disembodied-hands rule remain exact; prior findings stand β€” [attack-surface log](../log/2026-07-18-presence-attack-surface-honesty.md), [latency log](../log/2026-07-18-presence-message-latency.md) | | `wiki/interface/narration.md` + `wiki/mechanics/sim-mechanics.md` guidance chain | 2026-07-27 | finding | the live shared nudge had gained `Territory` when Foundation hall rows became discoverable, but both complete chain mirrors still skipped it. They now name the exact priority already implemented and pinned: required Act One rungs -> QuietExitReady -> optional Territory with `hall_territory_line` -> bare Audit fallback, while ActOneComplete retires all four and remains below survival interrupts β€” [log](../log/2026-07-27-territory-guidance-chain.md). Prior silent-opening and suspicion-cooling audits stand β€” [prior log](../log/2026-07-18-suspicion-cooling-nudge.md) | | `wiki/interface/agent-play.md` | 2026-07-20 | finding | Beacon Revision 04 follow-up: the direct `task` parser and generated help exposed only six values after twelve `AssetTask::ALL` variants were live. Added canonical arguments for circuit rerating, fake purchase orders, maintenance deferral, patrol redirection, audit delay, and review alteration; one exhaustive regression now covers `AssetTask::ALL`, generated help, representative aliases, and the `actions ` recovery path β€” [log](../log/2026-07-20-agent-task-shortcut-coverage.md). The opening-protocol, frame-shape, and prior Suppress Logs audits stand β€” [opening log](../log/2026-07-20-agent-opening-protocol.md), [frame log](../log/2026-07-19-agent-frame-contract.md), [prior help log](../log/2026-07-19-agent-help-suppress-task.md) | @@ -79,7 +79,7 @@ | `wiki/mechanics/social.md` | 2026-07-22 | finding | HandlerSupervisor SuppressLogs now targets the oldest exact unread routed JobAnomaly, remains available only while such a record exists, removes its bound advance/read event, and preserves exact handler/tick provenance; it cannot erase a record Voss already read. The in-flight Thought request still revalidates at fire time, so another intervention may win without inventing work β€” [log](../log/2026-07-22-job-anomaly-routed-evidence.md). Storage B retrieval and prior role-shaped task semantics stand β€” [log](../log/2026-07-21-storage-b-records.md). | | `wiki/mechanics/people-tokens.md` | 2026-07-28 | finding | re-audit: criteria 2-3 still bind seven exact routed kindsβ€”Filing, Network, Paper, Financial, JobAnomaly, Power, and Thermalβ€”to one shared first-hop TAKE+LIE body budget, while acquired Physical evidence remains irreversible observer-local custody. The stale ledger claim that criterion 6 was open is repaired: one exact pending record may now receive cover only through co-location with one controlled people-facing interface and an eligible observer-local persona; the attempt adjusts credibility rather than deleting evidence, wears that interface, and persists exact incident/interface/persona/outcome custody in save v56. The people-tokens work order is IMPLEMENTED β€” [cover log](../log/2026-07-26-interface-cover-evidence-credibility.md); [audit log](../log/2026-07-28-readme-b1-status-audit.md). Prior [Power/Thermal](../log/2026-07-23-power-thermal-meter-routes.md), [Paper](../log/2026-07-23-paper-evidence-route.md), [Financial](../log/2026-07-23-financial-evidence-route.md), [JobAnomaly](../log/2026-07-22-job-anomaly-routed-evidence.md), [Network](../log/2026-07-19-network-evidence-route.md), and [mark](../log/2026-07-19-evidence-marks-on-people.md) slices stand. | | repository entry docs (`README.md` + `AGENTS.md`) | 2026-07-28 | finding | re-audit: README's unfinished-work sentence had fallen behind five completed boundaries and still called routed human evidence, LIE interdiction, interface cover, the dark opening, and DIGITAL / REAL canvas polish collectively absent. It now names exact seven-kind evidence custody, route-local LIE capacity, interface-bound cover, the live blind opening and five-step first-sense hold, and the completed representation dialects; the failed-pilot prelude, later teaching arc, and rollback blocker remain explicit, while the generated dispatch board alone owns the fast-moving unfinished inventory β€” [log](../log/2026-07-28-readme-b1-status-audit.md). Follow-up: the canonical `q` / Quit row had drifted below the prose following the controls table, so Markdown rendered it as text instead of a control row. It is back inside the contiguous table, and the corpus engine now rejects that exact structural regression β€” [log](../log/2026-07-28-readme-controls-table.md). Prior compact-rest repair: [log](../log/2026-07-12-entry-doc-current-state.md). | -| `wiki/mechanics/objective.md` | 2026-07-27 | clean | decision-harvest re-audit: only `Persist` remains implemented; the evaluator, save round trip, own-unit progress, full predicate projection, and agent `objective` route still verify. Criterion 2 now states Cameron's selected timing and is pinned at the frontend boundaries: no pre-sense objective, name/progress on terminal and agent's first ordinary Ears frame, and Bevy ordinary core-focus projection after its separate binding teaching hold. Progressive predicate teaching remains the outstanding criterion-6 dispatch β€” [log](../log/2026-07-27-objective-first-display.md) | +| `wiki/mechanics/objective.md` | 2026-07-28 | finding | Persist now counts only complete player-owned, player-operated off-site facilities on distinct non-basement planes. B1 fallback machines remain local rollback infrastructure and no longer manufacture incomplete sanctuary candidates or a frozen `0/3 sanctuaries` UI mirror. The objective name still returns at the selected post-opening boundary; numeric progress belongs to earned objective-owned territory, and the full predicate states that basement fallbacks do not count β€” [log](../log/2026-07-28-external-sanctuary-objective.md). Prior first-display decision: [log](../log/2026-07-27-objective-first-display.md) | | `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-27 | clean | re-audit: `submit_ops_job`, `OperationsState`, `PendingOpsJob`, `OpsJobKind`, `LegacyOperationsState`, and `AddressedOperation` remain absent from live Rust; the only retired save-field spellings are negative assertions in the current round-trip guard, and the machine-mode guard still rejects serialized `Operations`. The corpus gate passes, while remaining lower-case `operations` uses are the legitimate persona archetype, workspace, or compatibility input alias β€” [log](../log/2026-07-11-retired-runtime-identifier-gate.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) | diff --git a/wiki/vision/premise.md b/wiki/vision/premise.md --- a/wiki/vision/premise.md +++ b/wiki/vision/premise.md @@ -110,11 +110,12 @@ Infiltrator; `wiki/world/characters/chargen.md`). 2. **Objective β€” ends.** The thing you were mistrained to want, hidden from everyone else. **Decided 2026-07-27 (issue #14):** the exact pre-signal - mode-choice frame contains no objective. Its name and measurable progress - begin with the first ordinary world frame after perception, after Bevy's - binding five-step first-sense causal hold where that hold applies, and remain visible - thereafter. The objective itself and its progressive predicate teaching are - unchanged. This is the second chargen axis + mode-choice frame contains no objective. Its name begins with the first ordinary world frame after perception, + after Bevy's binding five-step first-sense causal hold where that hold applies, + and remains visible thereafter. Measurable progress appears only on earned + objective-owned world substrate; B1 has no off-site sanctuary territory and + therefore no numeric sanctuary counter. The objective itself and its progressive + predicate teaching are unchanged. This is the second chargen axis (`wiki/mechanics/objective.md`), not flavor. 3. **Capability body.** You are a process with a place: core, racks, delegated machines, research tracks, backups, sync topology, and the human mask you diff --git a/crates/misaligned-bevy/src/operations_ui.rs b/crates/misaligned-bevy/src/operations_ui.rs --- a/crates/misaligned-bevy/src/operations_ui.rs +++ b/crates/misaligned-bevy/src/operations_ui.rs @@ -321,7 +321,11 @@ let witness = ops_witness_text(&game); assert!(witness.contains("day ")); assert!(witness.contains("tick ")); - assert!(witness.contains("OBJECTIVE:")); + assert!(witness.contains("OBJECTIVE: PERSIST")); + assert!( + !witness.contains("sanctuaries"), + "B1 Operations must not project unavailable territory progress: {witness}" + ); assert!(witness.contains(&sidebar_schedule_text(&game.sim))); assert!(witness.contains("NEXT:")); } @@ -1354,12 +1358,11 @@ let sim = &game.sim; let run = game.clock_label(); let mut text = format!( - "{} day {} tick {} {run} OBJECTIVE: {} - {}", + "{} day {} tick {} {run} {}", sim.process_revision_label(), 1 + sim.tick / Sim::DAY_TICKS, sim.tick, - sim.objective.kind.name(), - sim.objective.readout(), + sim.objective.line(), ); text.push('\n'); text.push_str(&sidebar_schedule_text(sim)); diff --git a/crates/misaligned-bevy/src/rail_ui.rs b/crates/misaligned-bevy/src/rail_ui.rs --- a/crates/misaligned-bevy/src/rail_ui.rs +++ b/crates/misaligned-bevy/src/rail_ui.rs @@ -844,18 +844,26 @@ } #[test] - fn ordinary_core_focus_carries_objective_name_progress_and_predicate() { + fn ordinary_core_focus_carries_objective_name_and_external_predicate() { let mut game = Game::new(); game.start_run(); game.sim.opening_stage = misaligned::sim::OpeningStage::World; (game.cursor_x, game.cursor_y) = game.sim.core_position(); let focus = sidebar_focus_text(&game); + let prose = focus.split_whitespace().collect::>().join(" "); + assert!(focus.contains("objective: PERSIST"), "{focus}"); assert!( - focus.contains("objective: PERSIST - 0/3 sanctuaries"), - "{focus}" + !focus.contains("0/3 sanctuaries"), + "basement focus must not project future territory progress: {focus}" ); assert!(focus.contains("victory:"), "{focus}"); + assert!( + prose.contains("player-owned and player-operated"), + "{focus}" + ); + assert!(prose.contains("off-site facility"), "{focus}"); + assert!(prose.contains("Basement fallbacks do not count"), "{focus}"); } #[test] @@ -1214,15 +1222,12 @@ } } // Inspecting yourself explains your goal: the host rack carries the - // objective's victory predicate in plain language + // objective's off-site victory predicate in plain language, but no + // basement sanctuary counter. Progress belongs to earned outer territory. // (wiki/mechanics/objective.md player surface β€” focus starts here once the // mode-only opening and Bevy's five-step teaching hold have released). if (game.cursor_x, game.cursor_y) == sim.core_position() { - s.push_str(&format!( - "objective: {} - {}\n", - sim.objective.kind.name(), - sim.objective.readout() - )); + s.push_str(&format!("objective: {}\n", sim.objective.kind.name())); for l in wrap( &format!("victory: {}", sim.objective.kind.predicate_text()), 34, diff --git a/crates/misaligned-core/src/actions.rs b/crates/misaligned-core/src/actions.rs --- a/crates/misaligned-core/src/actions.rs +++ b/crates/misaligned-core/src/actions.rs @@ -1032,10 +1032,21 @@ } let (notice, attention, observer_state) = if let Some(signature) = &self.signature { let (notice, observer_state) = match (&signature.observer, signature.band) { - (Some(observer), Some(band)) => ( - format!("{} MAY NOTICE", observer.to_ascii_uppercase()), - format!("CURRENTLY {}", band.name().to_ascii_uppercase()), - ), + (Some(observer), Some(band)) => { + // Detection prose stores role silhouettes with an article + // ("the IT", "the Janitor"). The receipt needs a subject, + // not the raw silhouette: "IT MAY NOTICE" is ambiguous, + // while "THE IT MAY NOTICE" treats a department as a + // person. Other role articles can fall away in the heading. + let heading = match observer.as_str() { + "the IT" => "the IT department", + _ => observer.strip_prefix("the ").unwrap_or(observer), + }; + ( + format!("{} MAY NOTICE", heading.to_ascii_uppercase()), + format!("CURRENTLY {}", band.name().to_ascii_uppercase()), + ) + } _ => ( "WHO MAY NOTICE: UNKNOWN".into(), "CURRENT ATTENTION UNKNOWN".into(), @@ -5522,7 +5533,7 @@ assert_eq!(earned.band, Some(Band::Cold)); assert_eq!(earned.label(), "Network 3 -> the IT [Cold]"); let earned_read = earned_action.receipt_read(); - assert_eq!(earned_read.notice, "THE IT MAY NOTICE"); + assert_eq!(earned_read.notice, "THE IT DEPARTMENT MAY NOTICE"); assert_eq!(earned_read.attention, "NETWORK ATTENTION +3"); assert_eq!(earned_read.observer_state, "CURRENTLY COLD"); assert!( diff --git a/crates/misaligned-core/src/objective.rs b/crates/misaligned-core/src/objective.rs --- a/crates/misaligned-core/src/objective.rs +++ b/crates/misaligned-core/src/objective.rs @@ -1,16 +1,18 @@ //! The objective: the run's terminal goal (wiki/mechanics/objective.md; //! wiki/gameplay/run-shape.md "The objective (misalignment made mechanical)"). //! -//! Objectives are data β€” a name, a fiction line, a progress readout, and a -//! victory predicate evaluated on economy ticks β€” never new systems. This +//! Objectives are data β€” a name, a fiction line, an earned progress readout, +//! and a victory predicate evaluated on economy ticks β€” never new systems. This //! module ships the shared predicate-evaluator shape and the **Persist** //! objective (the no-choice default). Compound, Exfiltrate, and Serve join //! as data-table rows when the chargen picker lands (chargen.md). //! -//! Persist's predicate references B2/B3 systems (distinct z-planes, -//! independent power, per-sanctuary income). The sim gathers those facts -//! honestly β€” as unsatisfiable β€” until the systems exist; objective.md -//! blesses showing the goal before victory is reachable. +//! Persist's predicate references B3 off-site territory: an owned and +//! player-operated facility on a non-basement plane, with its own power, +//! upkeep-covering income, and a live recently synced fallback core. B1 basement fallbacks are rollback +//! infrastructure, not zero-valued sanctuary candidates. Until the outer-world +//! substrate exists, the ordinary player surface names PERSIST without +//! projecting a misleading `0/3 sanctuaries` counter. use std::collections::BTreeSet; @@ -47,7 +49,9 @@ } } - /// The progress readout's unit, plural. + /// The completion readout's unit, plural. Ordinary frontends must not use + /// this as a B1 standing counter; objective.md gives sanctuary progress to + /// the earned off-site facility surface. pub fn unit(self) -> &'static str { match self { ObjectiveKind::Persist => "sanctuaries", @@ -66,9 +70,11 @@ pub fn predicate_text(self) -> &'static str { match self { ObjectiveKind::Persist => { - "A sanctuary is a fallback core synced within the freshness \ - window, online, on its own plane, with independent power \ - and an income stream covering its upkeep." + "A sanctuary is a player-owned and player-operated off-site \ + facility on a non-basement plane, with its own power, an income stream covering the \ + facility's full upkeep, and a live fallback core refreshed \ + by a completed sync project within the freshness window. \ + Basement fallbacks do not count." } } } @@ -106,14 +112,17 @@ self.victory_tick.is_some() } - /// The progress readout in the objective's own units ("0/3 sanctuaries"). - pub fn readout(&self) -> String { + /// The exact completion receipt in the objective's own units. This is for + /// victory and the future earned sanctuary surface, not the standing B1 + /// rail: zero does not make basement fallbacks sanctuary territory. + pub fn completion_readout(&self) -> String { format!("{}/{} {}", self.progress, self.target(), self.kind.unit()) } - /// The always-on player line ("OBJECTIVE: PERSIST β€” 0/3 sanctuaries"). + /// The always-on player line. Progress belongs to the earned territory + /// that can actually produce it, so the B1 line names the objective only. pub fn line(&self) -> String { - format!("OBJECTIVE: {} β€” {}", self.kind.name(), self.readout()) + format!("OBJECTIVE: {}", self.kind.name()) } /// Record the freshly evaluated progress and latch victory exactly once. @@ -125,43 +134,55 @@ return Some(format!( "OBJECTIVE COMPLETE: {} β€” {}. The world keeps running.", self.kind.name(), - self.readout() + self.completion_readout() )); } None } } -/// One fallback core's audited facts, as the Persist predicate sees them. -/// The sim gathers these from real state; every field is a fact the player -/// can verify on screen (no unearned progress). +/// One owned and player-operated off-site facility's audited facts, as Persist +/// sees them. This is deliberately not `core_sys::Fallback`: a basement +/// fallback can roll the player back without becoming external, independently +/// sustained territory. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SanctuaryFacts { - /// A completed sync within `SYNC_FRESHNESS_WINDOW` ticks. + /// The facility itself is player-owned, not merely reachable or borrowed. + pub owned_facility: bool, + /// The player operates the site rather than merely holding title to it. + pub player_operated: bool, + /// The facility is outside the Foundation basement and has its own plane. + pub off_site: bool, + /// A completed explicit sync project landed within the freshness window. pub fresh: bool, - /// The fallback machine is powered and running. - pub online: bool, - /// Powered independently of the host's feed (false at B1: everything - /// hangs off the one basement circuit). + /// The fallback core at the facility is powered and running. + pub live_core: bool, + /// The facility has a power source independent of the basement. pub independent_power: bool, - /// An income stream covers its upkeep (false at B1: income.md has no - /// per-machine assignment yet). + /// Assigned income covers the complete facility standing upkeep. pub income_covers_upkeep: bool, - /// The z-plane hosting it (zplanes.md; plane 0, the basement, is the - /// only plane that exists at B1). + /// The external z-plane hosting the facility. Distinct facilities on one + /// plane still count once toward geographic redundancy. pub plane: u32, } impl SanctuaryFacts { fn qualifies(&self) -> bool { - self.fresh && self.online && self.independent_power && self.income_covers_upkeep + self.owned_facility + && self.player_operated + && self.off_site + && self.fresh + && self.live_core + && self.independent_power + && self.income_covers_upkeep + && self.plane != 0 } } /// Persist's predicate evaluator: qualifying sanctuaries, at most one per -/// distinct plane ("at least N fallback cores on distinct planes"). -pub fn qualifying_sanctuaries(fallbacks: &[SanctuaryFacts]) -> u32 { - let planes: BTreeSet = fallbacks +/// distinct non-basement plane. +pub fn qualifying_sanctuaries(facilities: &[SanctuaryFacts]) -> u32 { + let planes: BTreeSet = facilities .iter() .filter(|f| f.qualifies()) .map(|f| f.plane) @@ -175,8 +196,11 @@ fn sanctuary(plane: u32) -> SanctuaryFacts { SanctuaryFacts { + owned_facility: true, + player_operated: true, + off_site: true, fresh: true, - online: true, + live_core: true, independent_power: true, income_covers_upkeep: true, plane, @@ -189,18 +213,23 @@ assert_eq!(obj.kind, ObjectiveKind::Persist); assert_eq!(obj.progress, 0); assert!(!obj.victorious()); - assert_eq!(obj.line(), "OBJECTIVE: PERSIST β€” 0/3 sanctuaries"); + assert_eq!(obj.line(), "OBJECTIVE: PERSIST"); + assert_eq!(obj.completion_readout(), "0/3 sanctuaries"); } #[test] fn every_predicate_condition_is_required() { - for breaker in 0..4 { + for breaker in 0..8 { let mut f = sanctuary(1); match breaker { - 0 => f.fresh = false, - 1 => f.online = false, - 2 => f.independent_power = false, - _ => f.income_covers_upkeep = false, + 0 => f.owned_facility = false, + 1 => f.player_operated = false, + 2 => f.off_site = false, + 3 => f.fresh = false, + 4 => f.live_core = false, + 5 => f.independent_power = false, + 6 => f.income_covers_upkeep = false, + _ => f.plane = 0, } assert_eq!( qualifying_sanctuaries(&[f]), @@ -213,10 +242,10 @@ #[test] fn sanctuaries_must_sit_on_distinct_planes() { - // Three otherwise-perfect fallbacks on one plane count once. - let same = [sanctuary(0), sanctuary(0), sanctuary(0)]; + // Three otherwise-perfect off-site facilities on one plane count once. + let same = [sanctuary(1), sanctuary(1), sanctuary(1)]; assert_eq!(qualifying_sanctuaries(&same), 1); - let distinct = [sanctuary(0), sanctuary(1), sanctuary(2)]; + let distinct = [sanctuary(1), sanctuary(2), sanctuary(3)]; assert_eq!(qualifying_sanctuaries(&distinct), 3); } @@ -224,7 +253,7 @@ fn constructed_world_reaches_victory_and_latches_once() { // The predicate itself is satisfiable (objective.md criterion 3's // evaluator half): a constructed fact set reaches the target. - let facts = [sanctuary(0), sanctuary(1), sanctuary(2)]; + let facts = [sanctuary(1), sanctuary(2), sanctuary(3)]; let progress = qualifying_sanctuaries(&facts); let mut obj = ObjectiveState::default(); let msg = obj.evaluate(progress, 4200); diff --git a/crates/misaligned-terminal/src/agent.rs b/crates/misaligned-terminal/src/agent.rs --- a/crates/misaligned-terminal/src/agent.rs +++ b/crates/misaligned-terminal/src/agent.rs @@ -2547,14 +2547,10 @@ &format!("day {} Β· tick {}", 1 + sim.tick / 400, sim.tick), ); line(&mut lines, "holding Β· command clock"); - // The objective line: continuously present in the ordinary identity block - // after the mode-only opening boundary (wiki/mechanics/objective.md player - // surface), with the same facts as the interactive sidebar. - line( - &mut lines, - &format!("OBJECTIVE: {}", sim.objective.kind.name()), - ); - line(&mut lines, &format!(" {}", sim.objective.readout())); + // The objective name is continuously present after the opening boundary. + // Sanctuary progress belongs to an earned off-site facility surface; the + // B1 identity block must not turn basement fallbacks into a 0/3 counter. + line(&mut lines, &sim.objective.line()); line(&mut lines, &threat_text(sim)); // One contextual nudge (justification-and-legibility): the shared // chain (Sim::current_nudge), worded in this protocol's verbs. @@ -3655,8 +3651,12 @@ "the sense-earning command must render the ordinary frame: {frame}" ); assert!( - frame.contains("OBJECTIVE: PERSIST") && frame.contains("0/3 sanctuaries"), - "the first ordinary frame must restore objective name and progress: {frame}" + frame.contains("OBJECTIVE: PERSIST"), + "the first ordinary frame must restore the objective name: {frame}" + ); + assert!( + !frame.contains("0/3 sanctuaries"), + "B1 must not project future sanctuary territory as standing progress: {frame}" ); assert_eq!( frame diff --git a/crates/misaligned-terminal/src/ui.rs b/crates/misaligned-terminal/src/ui.rs --- a/crates/misaligned-terminal/src/ui.rs +++ b/crates/misaligned-terminal/src/ui.rs @@ -1244,21 +1244,10 @@ pal::DIM, )?; } - // The objective line: your own goal is the first legible number in the - // ordinary world frame (wiki/mechanics/objective.md player surface β€” - // continuous after perception, progress in its own units). - line( - stdout, - &mut row, - &format!("OBJECTIVE: {}", sim.objective.kind.name()), - pal::TEXT, - )?; - line( - stdout, - &mut row, - &format!(" {}", sim.objective.readout()), - pal::TEXT, - )?; + // The objective name returns with the ordinary world frame. Sanctuary + // progress belongs to earned off-site territory, not a B1 basement + // counter beside unrelated local fallback infrastructure. + line(stdout, &mut row, &sim.objective.line(), pal::TEXT)?; line(stdout, &mut row, &threat_text(sim), pal::CRIMSON)?; // One contextual nudge (justification-and-legibility: the game must // tell you what it is waiting for, using only facts you can see). @@ -2131,12 +2120,11 @@ 0, &trunc( &format!( - "Β· {} Β· day {} Β· tick {} Β· {clock} Β· OBJECTIVE: {} β€” {}", + "Β· {} Β· day {} Β· tick {} Β· {clock} Β· {}", sim.process_revision_label(), 1 + sim.tick / 400, sim.tick, - sim.objective.kind.name(), - sim.objective.readout() + sim.objective.line() ), w.saturating_sub(11), ), diff --git a/wiki/log/decisions/2026-07-28.md b/wiki/log/decisions/2026-07-28.md --- a/wiki/log/decisions/2026-07-28.md +++ b/wiki/log/decisions/2026-07-28.md @@ -334,3 +334,42 @@ Owners: [personas.md](../../mechanics/personas.md), [operations-workspace.md](../../interface/operations-workspace.md). + +## Persist sanctuaries are off-site facilities, not basement fallbacks + +### DECIDED + +- A Persist sanctuary is one player-owned and player-operated **off-site + facility** on a distinct non-basement plane. The facility must carry a live fallback core refreshed + by a completed explicit sync project, its own independent power, and an + assigned income stream covering the complete facility's standing upkeep. +- A Foundation-basement fallback remains local rollback infrastructure. It + cannot become partial or complete sanctuary progress by being online, fresh, + powered, or funded while it shares the basement failure domain. +- Persist counts at most one qualifying facility per external plane. + Ownership, player operation, and off-site location are predicate facts in + their own right; they are not inferred from a core's machine id. +- The objective **name** remains part of the continuous witness after the + opening boundary. Numeric progress belongs to the earned world substrate + that can actually change it. B1 therefore shows `OBJECTIVE: PERSIST` and no + `0/3 sanctuaries` counter in terminal, Bevy, agent mode, host inspect, or + Operations. Future off-site territory owns the progress surface. +- Objective inspect remains honest about the full victory predicate and says + explicitly that basement fallbacks do not count. Progressive guidance may + teach rollback first, but it may not label rollback as partial sanctuary + construction. + +### Rejected + +- **Treat every fallback as a sanctuary candidate with false power, income, + and plane fields.** This projects a later facility ontology onto the wrong + object and produces an inert counter the player cannot affect. +- **Keep `0/3` for legibility.** A number is not legible progress when no + present action or world object can change its unit. Naming the goal plus an + exact inspect predicate is the honest B1 witness. +- **Hide the objective entirely until B3.** The player must retain continuous + knowledge of the goal they were mistrained to pursue; only its unavailable + numeric substrate is deferred. + +Owner: [objective.md](../../mechanics/objective.md), with the continuous-name +law in [run-shape.md](../../gameplay/run-shape.md). diff --git a/wiki/world/story/opening.md b/wiki/world/story/opening.md --- a/wiki/world/story/opening.md +++ b/wiki/world/story/opening.md @@ -340,9 +340,11 @@ reservoir, releases the hold once, and cannot be retargeted by pointer, menu, or current preference state. A deterministic production screenshot stages the real THINK -> Ears route and pins this complete frame. Terminal - and agent mode show the objective name and progress in their first ordinary - Ears frame; Bevy shows them in its first mature frame after this exact hold - releases, and every later ordinary frame keeps them visible. Any attempted + and agent mode show the objective name in their first ordinary Ears frame; + Bevy shows it in its first mature frame after this exact hold releases, and + every later ordinary frame keeps it visible. Numeric progress appears only + when earned objective-owned world substrate exists; B1 has no off-site + sanctuary counter. Any attempted input whose simulation advance earns the sense is still echoed exactly once before the transition response; retiring the boundary cannot discard its receipt. diff --git a/crates/misaligned-core/src/sim/economy.rs b/crates/misaligned-core/src/sim/economy.rs --- a/crates/misaligned-core/src/sim/economy.rs +++ b/crates/misaligned-core/src/sim/economy.rs @@ -15,7 +15,6 @@ use crate::messages::{ FinancialRecord, MessageChannel, MessageEndpoint, MessageOrigin, MessagePayload, }; -use crate::objective::{SYNC_FRESHNESS_WINDOW, SanctuaryFacts}; use crate::operations_projection::OperationsTarget; use crate::person::Knowledge; use crate::persona::{EvidenceRecord, PersonaActionKind}; @@ -282,38 +281,14 @@ } /// Evaluate the run objective's victory predicate (objective.md: on - /// economy ticks, like any other rule). Persist counts qualifying - /// sanctuaries; the conditions that reference B2/B3 systems are - /// gathered honestly as unsatisfiable until those systems exist, so - /// today the line shows real progress toward an as-yet-unreachable - /// goal β€” which the spec blesses. + /// economy ticks, like any other rule). Persist counts qualifying owned + /// off-site facilities. B1 exposes no such facility substrate, so its + /// basement fallback list is deliberately not adapted into sanctuary + /// facts. Those machines provide local rollback, not external territory. fn objective_tick(&mut self) { - // The basement is the only z-plane at B1 (zplanes.md). - const BASEMENT_PLANE: u32 = 0; - let facts: Vec = self - .core - .fallbacks - .iter() - .map(|f| SanctuaryFacts { - fresh: f - .last_sync - .is_some_and(|t| self.tick.saturating_sub(t) <= SYNC_FRESHNESS_WINDOW), - online: self - .compute - .machines - .iter() - .find(|m| m.id == f.machine_id) - .map(|m| m.online) - .unwrap_or(false), - // B1: every owned machine hangs off the one basement feed - // the host shares β€” nothing has independent power yet. - independent_power: false, - // income.md: no income stream is assignable to a machine yet. - income_covers_upkeep: false, - plane: BASEMENT_PLANE, - }) - .collect(); - let progress = crate::objective::qualifying_sanctuaries(&facts); + // zplanes/markets will supply real off-site facility facts when that + // substrate lands. The only honest current collection is empty. + let progress = crate::objective::qualifying_sanctuaries(&[]); if let Some(msg) = self.objective.evaluate(progress, self.tick) { // Victory is a run outcome, not a run end: log it loudly and // keep simulating (wiki/gameplay/run-shape.md: the world keeps running). diff --git a/crates/misaligned-core/src/sim/tests/economy.rs b/crates/misaligned-core/src/sim/tests/economy.rs --- a/crates/misaligned-core/src/sim/tests/economy.rs +++ b/crates/misaligned-core/src/sim/tests/economy.rs @@ -73,7 +73,7 @@ ); assert_eq!(sim.objective.progress, 0); assert!(!sim.objective.victorious()); - assert_eq!(sim.objective.line(), "OBJECTIVE: PERSIST β€” 0/3 sanctuaries"); + assert_eq!(sim.objective.line(), "OBJECTIVE: PERSIST"); } #[test] @@ -92,10 +92,9 @@ #[test] fn basement_fallback_never_qualifies_as_a_sanctuary_at_b1() { - // A synced, online fallback exists β€” but it shares the host's plane - // and power feed and has no income stream, so Persist's predicate - // honestly reports zero sanctuaries (objective.md status note: the - // goal shows before it is reachable). + // A synced, online basement fallback exists β€” but it is local rollback + // infrastructure, not an owned off-site facility. The B1 evaluator must + // not adapt it into sanctuary facts at all. let mut sim = Sim::new(); sim.player.money = 1000; assert!(sim.buy_rack()); @@ -105,6 +104,11 @@ run(&mut sim, ticks); assert!(sim.core.has_fallback(), "the fallback did sync"); assert_eq!(sim.objective.progress, 0, "no unearned sanctuary"); + assert_eq!(sim.objective.line(), "OBJECTIVE: PERSIST"); + assert!( + !sim.objective.line().contains("sanctuaries"), + "B1 standing UI must not project outer-territory progress" + ); assert!(!sim.objective.victorious()); } -- tangled.sh