Simulation Aliveness 4 — the visual-experience arc (V1–V11) - #103
Merged
Conversation
The round-4 observation session findings (movement tracer + live Chrome play + static sweeps) and the proposed arc, including the maintainer additions M1-M8. This is the plan the branch executes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Commit the movement tracer (TestHarness): per-frame state timelines, breadcrumbs, and anomaly detection (teleport/disembark/renderJump/stuck/ overlap/spriteAudit); activityCensus; startTrace/traceReport/etc. - Definitive orphaned-sprite sweep (MainScene M2): every created person/ vehicle sprite is registered and reconciled per in-game minute against Field's live lists — a sprite exists iff its entity is live. New countOrphanSprites() feeds auditSprites().orphanSprites. - forceEvent routes invoke signals through City (M2): extracted consumeResultSignals from handleTick; new City.forceEventAndConsume so a forced crime files a real incident (the raw invoke dropped every signal). - Teleport classifier: the by-design car-exit sync is 'disembark', so a real 'teleport' means a bug and alerts at zero noise. Person.getSpeed() keeps the threshold honest ahead of V10 per-kind speeds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New games start at 09:00 (M7): NEW_GAME_START_TICK seeds the clock in startNewGameWorld (new-game-only path; loads restore saved time). A town already awake and commuting, not a dead midnight. - Speed ladder 1x/10x/50x (M8): TIME_SCALES = [1, 10, 50]; the HUD toolbar and T-key cycle derive from it automatically. - 50x distortion pass (M8): Person.walk and Vehicle.drive now consume the whole frame's travel budget across as many curb/lane segments as it covers, clamped per axis (no overshoot). The old single-step reached one waypoint and RETURNED, wasting the rest — so at 50x feet fell behind the clock and commutes ate extra in-game hours. One iteration at <=10x, so 1x/4x/8x are byte-identical; only large 50x/hitch deltas iterate. - timeConsistency extended to 10x/50x (walker lands the identical position and clock at every speed); ladder + 09:00 assertions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit found every trip was a car trip spawning at the person's HOME regardless of where they stood: a woman out walking would walk home, board a car parked at her own door, drive zero metres, and get out. Three fixes in City.startCommute: - Origin truth: the car spawns on the road nearest the BODY (adjacent road if at a building, else Field.nearestRoadTile of the pixel position; body fallback for road-less worlds) — never the person's distant home. - Walk vs drive: trips within WALK_COMMUTE_MAX_TILES (12) are walked; the car is reserved for real commutes. - No zero-length drive: origin road == destination road → walk. Plus Person.setAmbulatory clears the stale wander path on an ambulatory action's end, killing the tracer's 'sleepwalk' (the body finishing a stale stroll leg for minutes after the action was over). Tests: walk-short + origin-truth in commute.test.ts; the 148-car W8 test made faithful (re-plan while genuinely far from home). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit found 'outside' was ONE town-wide location: any two pedestrians
anywhere co-located, so an object was lent between people 386px (24 tiles)
apart, and street chats/witnesses/gossip crossed the whole map.
- Cell-scoped outdoor co-location: LogicalLocation's outside variant gains
an optional ; LiveWorld.locationOf tags an outdoor person with the
4-tile map patch they stand in, and peopleAt filters by it. A cell-less
{kind:outside} still means 'anywhere outdoors' (the global 'is a chase
on?' query, and bootstrap/logical worlds — the sanctioned seam, like
venue hours, so the generator stays town-wide and deterministic).
locationKey/parseLocationKey/sameLocation all cell-aware.
- Street roaming: ambulatory 'taking a walk' now wanders the ROAD anchors
and stops mid-block, instead of pathing to a building entrance and
clustering at civic doorways (the maintainer's long-standing sighting).
Field tracks roadAnchors; Field.update routes ambulatory people to them.
Tests: cell co-location (far/near/global) in worldAdaptersGaps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
M5 — per-kind movement speed. Running was purely nominal: a fleeing suspect, a jogger, and an evening stroller all moved at one fixed walk speed. Person now maps its authored ambulatory kind to a real speed (stroll 0.9x, jog 1.6x, run 2.2x), and the police pursuit gets a premium (chase 2.5x) so an officer visibly CLOSES on a fleeing suspect. City derives the kind each minute from the active action (chasing_a_suspect -> chase). M6 — the chase resolves PHYSICALLY, not by a dice roll. A suspect could be "caught" while the officer was across town. Now City.resolveChase checks co-location: an on-duty police officer in the suspect's own street cell (V2's local co-location, closed by the M5 chase premium) is a catch; otherwise the suspect outran them and got away (the impunity path). The flee action's duration is the chase time, so no insta-catch. The off-map generator keeps its abstract roll (LogicalWorld.resolveChase) — no sprites off-map, the sanctioned seam. Tests: locomotion-speed ordering; a distant officer does not catch (evaded, not arrested); the arrest ceremony now catches via co-location. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit found visiting_friends was a free-time continuous action with no
interaction and no target: it ran wherever the body was ("Visiting friends"
floating over a business), with no friend and no host. Same for
visiting_relatives, hosting_gathering, taking_a_walk_together.
- visiting_friends / visiting_relatives are now planner-only (free-time
weight 0) and ALWAYS located to a real person. The planner enqueues a
MIRRORED hosting_a_friend_visit for the host (new action), linked to the
same window — so the friend/relative is genuinely hosting at their home
at the same time, both ending together. A visit with no real target is
simply not made (no unlocated fallback).
- New see_relatives routine drives visiting_relatives to a living kin;
bestFriendTarget/bestRelativeTarget pick the target.
- Anti-regression validator: a targetless, location-less social continuous
action that satisfies the social need must be planner/joint-only
(weight 0) — it can never free-roll again. Invalid fixture added.
Tests: the friend visit enqueues a linked host mirror; the validator
rejects a free-rolling social action; reachability + fire-evacuation tests
updated for the weight shift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit found a 2-year-old taking solo shopping trips, roaming to venues, and job-hunting across town — no guardianship layer at all. - New `minAge` field on actions, enforced once in ActionEngine.startAction: below it, a typed requirementsUnmet (the child stays home, where the household-meal fan-out feeds them). Venue-located actions (shops, cafes, gyms…) get a default independence floor (VENUE_INDEPENDENCE_AGE = 8) even without an explicit value; job_hunting is explicitly 18+. School (a `building` location) is unaffected — children still walk to school. - Validator: minAge is a non-negative number; ACTION_KEYS + fixture. Upkeep folded in: regenerated simulation-relationships.md (V9's hosting_a_friend_visit bumped the action count — the Windows docs:sim env syntax had silently no-op'd); re-baselined the generation op-counts for the intentional V9/V3 behavior shift (fewer visit evals, +context builds from the age check). Tests: a toddler can't visit a venue or do an 18+ errand; an 8-year-old clears the venue floor but not the errand; an adult does both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit watched a man go back to bed IN his burning house (evacuation ended, he re-picked sleep at home) and a freshly-homeless woman keep "resting at home" at the rubble (her running instance survived teardown). - The burning gate (LiveWorld): a located transition INTO a building with an open fire is refused — at request AND in the pump (a building that ignites mid-trip cancels like one that vanished). Nobody walks back into a fire to sleep or work; the evacuation hook owns the exit. City wires isBurning via Game.incidents.openFireAt. - Re-validate running home activities on displacement: when a household is evicted/demolished, each member's home-located instance is interrupted, so a stale "spending_time_at_home" can't keep running at a rubble lot — the woken brain re-plans from reality (shelter-seeking, the street). Tests: a transition into a burning building is refused, a non-burning one is fine. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit found a nurse-only hospital treated NOBODY: the doctor-rounds hook was gated to jobKey 'doctor' alone, so a staffed ward the coverage ledger counted as healthcare healed no one, and a seriously-ill man wandered off to run errands past an open-but-doctorless clinic. - The rounds hook now admits both medical treating roles (doctor AND nurse) via TREATING_JOB_KEYS. treating_patient carries no role requirement, so a nurse runs it at the same mechanism — a staffed hospital is a treating one. was_treated_by_doctor still lands, so the recovery boost applies. Test: a nurse-only ward treats a co-located patient-in-treatment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- M1: activity labels render only over INSPECTED people (a person window open), so the street isn't a wall of overlapping "Taking a walk" text. The HUD emits inspectedPeopleChanged when person windows open/close; the scene gates labels on it. A masterSwitch-gated L key toggles show-all for debugging. - Formation offsets now separate ALL co-located visible people (grouped by position cell, slotted by stable id), not just same-activity groups — a couple's walk or a chase pair with differing activities used to render as one overlapping sprite. The activity alias is gone (co-location handles the chase pair). - Eased offsets: the rendered offset lerps toward its target so a re-slot glides instead of popping 8-10px (the "jitter clips" sighting). Cleaned up on sprite reconciliation. Scene-layer (browser-suite territory); full Jest suite green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Night-floor on taking_a_walk: a strong ×0.05 suppressor for 22:00-06:00, so the town stops taking 3 AM strolls (the audit's midnight-empty-but-not streets). evening_stroll already had a daytime floor; taking_a_walk had a daytime boost but no night floor. - Re-baselined the generation op-counts for the shifted night-time picks. Verified (no change needed): live first-offense sentencing is already fine-only (convictSuspect checks priors BEFORE the fresh got_caught lands) — the aliveness-3 "first offense → jail" sighting is inflated priors carried in from the off-map crime carousel, a generator/asset-regen concern. Deferred (own follow-up, economy/determinism/asset risk): the business draw-coherence guard (beach/cemetery/duplicate categories) — it perturbs the determinism-tested draw and the committed asset, so it belongs with the maintainer's asset regeneration pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The engine narrative for V1-V11 in CLAUDE.md §4.18, and the proposal marked implemented. Validated by the full Jest suite (1947), ESLint, the perf gates, and the Playwright integration smoke test (prod build boots, HUD mounts, determinism hook installs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seven task files for the workstream remainders the arc consciously held back, each self-contained and JIRA-style with concrete code references: - 123 Business draw coherence (V7): placement-aware draw + amenity fencing (no beach/cemetery downtown, no duplicate schools). Determinism/asset. - 124 Evacuation as a scene (V4): rally + conclusion + family-notify wakes. - 125 Deferred venue needs (V5): a closed venue defers to opening instead of dissolving; severity-banded illness gating. - 126 Guardianship depth (V3): accompaniment, home-alone care, dependent fan-outs on caregiver jail/illness/death. - 127 Homeless day-shape + domestic home-locations (V4/Appendix-B.1): the domestic-location data pass + a homeless shelter repertoire. Generator. - 128 Street wander graph + seeded wander (V2): loiter nodes + seeded pick. - 129 Persistent household cars (V1 stretch): park it, don't conjure it. README table + Open-work note updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit's seriously-ill man went grocery shopping past a placed-but-CLOSED hospital: `hasVenue` returns false for both "no hospital" and "hospital off-hours", so the treatment producer proposed nothing and an errand filled the slot. - New `WorldAdapter.hasVenuePlaced` (LiveWorld: a hosting business exists regardless of hours) distinguishes closed from absent. - The treatment hook now proposes `resting_at_home_sick` (need band, urgency-scaled) when the hospital is placed-but-closed — the sick person WAITS at home until it opens (the normal treatment proposal then takes over), instead of dissolving the need into an unrelated trip. A town with no hospital keeps the plain 092 behavior. Test: a placed-but-closed hospital makes the sick rest, not shop. Remaining for task 125 (own commits): the general shopping/dining deferral via the same primitive, and the severity-banded illness data suppression (requirement 2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The second half of task 125: a seriously-ill person (health < 0.35) has their going-out/leisure/social/outdoor free-time weights collapsed to a token in the Brain's selection (both selectActionForNeed and computeFreeTimeAction), so serious illness visibly cancels the day — the audit's mildly-and-not-so-mildly ill people who walked and visited until midnight now recover at home. Home/recovery actions are untouched and dominate. Perf op-counts re-baselined (the Brain gate perturbs the generator stream); the asset regeneration is the maintainer's step. Test: a healthy person goes out; the same person seriously ill goes out strictly less. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 6-hour "Evacuating!" wander the audit saw is already bounded by V4's burning gate (a person can't re-enter the burning building, so evacuation is a one-shot 2-tick action, not a re-entry loop). This adds the missing family-notify channel: - New `homeFire` WakeKind: a fire at an occupied HOME enqueues a reactive Brain wake for its residents (City.runFireHazard), so occupants elsewhere re-plan at the minute instead of the next hourly flip — the mood valence (lost_home_to_fire) already lands; this drops them out of their routine now. Live-only (the generator has its own fire path). - City.hasPendingWakes() test accessor. Test: an occupied home's ignition queues a wake (cityReactiveLoops). Remaining (noted in the task file): a richer rally-to-a-named-safe-spot and a dedicated kin "gather at home" hook — polish on top of the bounded evacuation + the wake. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The round-4 arc: the movement is truthful now (rounds 1–3) — make it sensible, and make place, purpose, and company real on the street. Audit + proposal:
docs/proposals/simulation-aliveness-4-audit.md; engine narrative:CLAUDE.md§4.18. One PR, all phases (V1–V11), sequential commits.What landed
TestHarness(state timelines, breadcrumbs, teleport/disembark/renderJump/stuck/overlap/spriteAudit anomalies); a definitive orphaned-sprite reconciliation sweep inMainScene(a sprite exists iff its entity is live);forceEventnow routes signals through the real City consumers.Person.walk/Vehicle.driveconsume the whole frame's travel budget across segments (feet keep up with the clock, no overshoot; 1×/4×/8× stay byte-identical).visiting_friends/visiting_relativesare planner-located to a real person with a mirroredhosting_a_friend_visit, both ending together; a validator bans free-rolling social actions.minAgegate on actions (venue default + explicit 18+ for job hunting) — no more toddlers taking solo shopping trips.L); formation offsets separate all co-located people, eased so re-slots glide.taking_a_walk(no more 3 AM strolls).Validation
window.__townboxdeterminism hook installs).Maintainer pre-merge steps (the aliveness-2/3 pattern)
Deferred follow-ups are called out per-workstream in the proposal Part 6 and CLAUDE.md §4.18 (the business draw-coherence guard, evacuation rally/conclusion, deferred venue-need planning).
🤖 Generated with Claude Code