Aliveness-4: follow-ups, live fixes, on-demand cars & ridesharing (130/131) - #104
Merged
Conversation
…nistic pick Ambulatory walks now prefer the curb in front of a gathering venue (park/beach/ bar/cafe/restaurant/cinema/gym/library/church) instead of drifting to an arbitrary road anchor, and the wander pick runs on a seeded per-(worldSeed, tick, person) stream rather than the global unseeded Phaser.Math.RND. - Field.getLoiterAnchors: the road-anchor subset in front of gathering venues, recomputed lazily behind a dirty flag (marked stale on build/teardown, and by City on business assignment since a Workplace has no business at stamp time). - Person.updateDestination: takes an optional WanderContext (loiter set + seed + tick); a SeededRandom(worldSeed).fork(personId).fork(tick).fork(pick) draw prefers a reachable loiter node (LOITER_BIAS 0.65) else the full roam set. Debug test people keep the legacy unseeded building wander. - Field.update threads the loiter set, worldSeed and tick to ambulatory people. Live movement stays frame-paced (arrival timing best-effort); the choice is deterministic per seed. Tests: loiter bias, fallback, per-person determinism, and the Field recompute filter/adjacency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two of the three V3-remainder pieces the aliveness-4 audit surfaced (a toddler left home alone while both parents commuted; a single parent hauled to jail leaving a child unminded): - Home-alone care. City.unattendedYoungDependentAtHome reads households + live presence: the last available adult AT home with a co-resident child under CARE_AGE_YEARS (10) also home, no other available adult around. A new guardianshipHook anchors caring_for_children (obligation band, below a work shift) so that parent minds the child instead of drifting to a discretionary activity. Live-only resolver (presence is a map concept) → inert off-map. - Dependent fan-out on caregiver loss. resolveRehousing now counts a DETAINED adult as an unavailable guardian, so a jailed sole caregiver's minors fan out to a relative exactly as an orphaning does (the death-only path never fired while the parent was alive). Illness keeps the parent home (not relocated). Deferred: accompaniment (requirement 1) — V3's minAge gate already blocks young children's discretionary located trips, leaving only school (on foot), so a joint-plan escort primitive has almost nothing to bind to; held for later. Tests: the reader's presence/other-adult/detained cases, the hook's four branches, and the resolveRehousing jail fan-out (+ the not-jailed control). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The audit's generic work-lot draw put a beach between the bar and the bakery and stacked duplicate supermarket/school while whole categories went unserved. - beach/cemetery/park now carry placement:"amenity" and are fenced from all three random draw paths (generic draw, re-occupancy, entrepreneurship) via a shared isMenuOnlyBlueprint predicate (civic OR amenity). They are placeable through the construction menu (added to construction.json — their only spawn path, validator-enforced like civic) and stay venue-mapped so visiting_beach/ park resolve when one is deliberately placed. - The first-placement demand-deficit draw folds in an unrepresented-category boost, so the town spreads across categories instead of stacking a second supermarket/school while dining/leisure sit empty. - Validators: economyContent accepts placement 'amenity'; the construction validator requires amenity blueprints be menu-pinned (like civic). Asset byte-unaffected: the offline generator's logical world builds its roster by round-robin over all blueprint keys (not openBusiness/the deficit draw) and generateBusiness ignores placement, so no regen and no perf re-baseline (perf suite green). Only the live map draw changed; determinism per seed holds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A person with homeless == true never has a location:'home' domestic action proposed for free time — it would only request a transition to a home that resolves to nothing and block on no_route tick after tick (the audit's "spending time at home / watching TV at the rubble"). The outdoor repertoire (walks, looking_for_a_home, park/bench rest) takes their weight instead. Scope notes: - The broad location:home data pass was reconsidered and NOT done: the named domestic actions are DISCRETE (they ignore location), and their real domestic wrappers (spending_time_at_home / sleep / resting_at_home_sick) are already location:home and already block for the homeless. The selection hard-gate is the correct, sufficient mechanism; resting/napping location-less actions stay valid outdoors (a homeless person on a bench is coherent). - Asset byte-unaffected: the generator's logical world has elastic housing (no evictions → homeless is never set off-map), so the gate never fires during generation. No regen, no perf re-baseline. - Deferred: a sleeping_rough/shelter action for the survival-sleep gap. Tests: a housed person picks home actions; a homeless one never does but still has an (outdoor) day-shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…28) in CLAUDE.md §4.18 124/125/128/126/123/127 landed on the branch; 123 & 127 turned out asset-byte- unaffected (round-robin logical roster + elastic off-map housing). 129 (persistent household cars) remains deferred to the live-observation pass for its W8 sprite-invariant check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s it replaced Live-play finding: in a freshly-built town, Field.destinations AND roadAnchors came up permanently EMPTY, so V2's ambulatory street roam had no road targets and every walk fell back to building-entrance wander (the audit's persistent entrance-clustering, finding 2.3) — and task 128's loiter nodes never registered. Root cause (confirmed live via add/delete instrumentation — ops were "del K, add K, del K"): a road or building placed on a supertile anchor shares its key "row-col" with the grass footprint it overwrites (grass footprints are anchored on the SAME 3k+1 grid roads snap to). stampFootprint registered the new structure's anchor and THEN ran the overwritten-teardown loop, whose destroyStructure(grass) deletes that shared key — wiping the just-added entry. Roads always snap to the grid, so roadAnchors was always empty. Fix: move the destinations/roadAnchors registration to AFTER the teardown loop. This also explains (and removes) the "ts-jest instanceof quirk" the loiter test worked around — it was this same bug (grass anchors are grid-aligned in tests too), not module duplication; loiterAnchors.test now uses the real build path. Regression: two fieldMutations tests assert a grid-aligned road/building lands in roadAnchors/destinations; revert-dance verified (buggy order → both fail). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A commute car now PARKS on arrival and is re-boarded on the next trip, instead of the per-trip spawn-at-origin / despawn-on-arrival churn (the audit's "magic pop-in/pop-out cars everywhere"). - Person.processTravel Arrived: disembark but KEEP the car linked + controlled (parked) instead of despawning. A controlled+unoccupied car sits still (Field.update skips wander on controlled; drive() gates on occupied) and is not an orphan (still linked → the runWakePass sweep skips it). - City.startCommute: reuse the owner's PARKED car when it's near the body (the common round-trip case, since a trip parks it at the building the next leaves from). A car stranded far away is released (Person.releaseVehicle) and a fresh one spawns at the origin. A mid-drive (occupied) car is never reused — that keeps the W8 148-car-leak despawn via setVehicle. - Field.removePerson despawns the owner's parked car (no orphan on removal). - SaveManager marks a restored linked car controlled so it doesn't wander; parked cars re-derive across save/load (no save-version bump). Live-verified in the observation pass: across two in-game days the vehicle count stayed bounded (1-5, no leak), drivers retained parked cars, and every W8 sprite invariant read zero at every sample. Tests: arrival parks (personTravel), re-board reuse + removal despawn (commute); the re-plan 148-car guard still green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tale until midnight
Two live-play bugs found via in-browser analysis.
Bug — Pause does nothing. GameManager.emit/emitSingle defaulted the payload with
`if (!payload) payload = {}`, which clobbers a FALSY-but-valid payload. The Pause
button emits setTimeScale(0); 0 is falsy → became {} → setTimeScale({}) fell back
to 1×, so time never paused. Fixed the guard to `payload === undefined || null`
(0/false/'' now pass through). Also fixed a latent `await context ? … : …`
precedence bug in emitSingle (it awaited the context, not the callback result).
Bug — City Services shows "No facility" for placed buildings. recomputeServices
ran ONLY on newDay, so a hospital/police/fire/school placed mid-day didn't
register until the next midnight sweep. Now recompute on setupBusiness (placement,
incl. construction-menu civics) and closeBusiness (teardown/bankruptcy).
Tests: emit passes falsy 0/false/'' to handlers (phaser mocked so GameManager
loads headless); a placed hospital registers its healthcare facility immediately.
Both revert-dance verified.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…at 50x) GameManager.advanceTime advanced the clock by the whole frame delta then emitted the time-cadence signals ONCE, comparing only the final timestamp. At high speed a single frame (a hitch at 50x: 100ms cap × 50 = two in-game minutes) jumped the clock past a minute whose commute-departure / transition pump then never fired at that exact minute-of-hour (LiveWorld schedules departures per minute) — so people mid-transition (e.g. leaving a relative's house) stalled, worse the faster you ran (the user's "orphaned people stuck near their relatives' house, more at 50x"). Fix: advance the clock in <=1-in-game-minute steps, emitting the cadence after each step, so no minute/tick/day is ever skipped. At 1x a frame is a fraction of a minute -> a single iteration, no behaviour change (timeConsistency keystone still green). (The "orphaned car sprites" the user also saw are task-129 PARKED cars — empty, still owned, sitting at the curb by design — not orphans: auditSprites reads 0 orphanControlledVehicles / orphanSprites on the live instance.) Regression test: a 3-in-game-minute frame emits timeChanged for minutes [1,2,3], not just [3]; a sub-minute frame emits at most once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…28 DONE, 129 REVERTED Task 130 is the four-pass plan the maintainer asked for: revert 129's persistent cars to on-demand spawn/despawn, then build coordinated ridesharing on the joint-plan machinery. - Pass 1: every shared-ride case (drive kids to school + pickup, police carpool patrol, group/couple/household venue trips, relatives driving the ill to hospital, work carpool, drive-a-friend-home, accompaniment). - Pass 2: audit of existing collective-action coverage — the agenda/linkId joint-plan pattern exists (visiting->hosting), but Vehicle is single-occupant, the commute is solo-per-person, and none of the driving collective actions or guards exist. - Pass 3: the work — revert 129; multi-occupant Vehicle (eject-all on despawn); the shared-ride primitive; new drive_* / ride_* actions+events; the canDrive gate (kids/severely-ill can't drive, far-school ride, 126 accompaniment); amend school/treatment/routines; no loose ends. - Pass 4: gaps — driver election + failure, co-location/board window, return trips, open-map/non-enterable destinations, despawn-with-occupants ordering, mid-ride interruption, one-car-not-N, determinism/bootstrap/asset parity, save/version, perf, the empty-car gate, feed legibility. Housekeeping: renamed 123-128 -> _DONE and 129 -> _REVERTED (reverted by 130); updated the README index + open-work note (incl. the four live-play bug fixes). No code changes — implementation follows the maintainer's review of 130. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cars are on-demand again: spawn as the driver leaves the origin building (the existing ExitingBuilding->WalkingToCar sequence), despawn as they enter the destination (Person.processTravel Arrived). No parked cars accumulating. - Person.Arrived: despawn the car (removeVehicle + unlink) instead of parking it. - City.startCommute: removed the parked-car reuse/release block — spawn fresh. - Removed Person.releaseVehicle (129-only). - Field.removePerson still despawns a removed driver's in-flight car (retagged); SaveManager still marks a restored linked car controlled (anti-wander, retagged). V1's smarts are KEPT: walk-vs-drive by distance, origin truth, no zero-length drive, minors walk. Tests updated to the despawn-on-arrival / one-car contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pawn Vehicle occupancy is no longer a single boolean. It now tracks an occupant list (driver + passengers, SEAT_CAPACITY 4) so a car can carry a shared ride: - board(person, asDriver) / disembark(person); the first boarder is the driver unless asDriver says otherwise. drive() gates on hasDriver() (a car with only passengers can't move). isOccupied()/isAboard()/getOccupants()/getDriver()/ seatsAvailable() accessors. Person is a type-only import (no runtime cycle). - Field.removeVehicle EJECTS every remaining occupant before despawning (Person.ejectFromVehicle): stepped out at the car's position, sprite restored, vehicle link cleared — nobody is ever left invisible-inside a vanished car (the W8 contract generalized from one driver to N riders). - Callers updated: Person EnteringCar/ExitingCar/abortTravel/setVehicle, City's orphan sweep. The W8 audit's occupiedDriverlessVehicles now means isOccupied() && !hasDriver() (a phantom-passenger car). Tests: the occupancy gate uses a real occupant; a shared-car despawn ejects both driver and passenger. Vehicle unit tests use setDebugDriver for the drive gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d window City.startGroupRide spawns exactly ONE car for a co-located group (driver + up to SEAT_CAPACITY-1 passengers) heading to a shared destination, never one car per rider. The driver drives; passengers are carried by boarding the same car: - Person.setVehicle(vehicle, asDriver) + isDriver(): a passenger boards the same car but doesn't route it — only the driver sets the car's destination in EnteringCar; the passenger's travel machine (WalkingToCar -> board -> Driving waits on the shared car's isDestinationReached -> ExitingCar -> walk to entrance) carries them for free off the driver's arrival. - Vehicle board window: setRideExpectations(n, frames) makes the car hold at the curb until all expected riders board (readyToDepart), so it never drives off without a passenger still walking to it; the window counts down so a no-show can't strand the car (it leaves without them). Solo commutes (1,0) depart instantly — unchanged. Triggering (which producer installs a group ride) comes in Phase D/E; this is the mechanism, tested directly: board-window gating, window lapse, and one-car setup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tranded City.canDrive(person): a driver must be an adult, not detained, and not severely ill (health >= MIN_DRIVE_HEALTH 0.35, read from the event context) — so children never drive, a jailed person never drives, and the severely ill are driven by someone else. startCommute now routes a drive-distance trip by driver-eligibility: - walk if within range (anyone, incl. minors); - a driver spawns a solo car (as before); - a NON-driver (child / ill / detained) bound FAR gets a lift — electDriver finds an available co-located adult (co-residents first) and starts a group ride with the non-driver as passenger. This is the "kids can't reach a far school alone" guard and the never-strand-a-non-driver net; - no eligible driver -> walk as a last resort (far, but never stuck). Narrated collective actions install their own rides upstream (Phase D); this is the safety fallback at the transition seam. Tests: canDrive age gate; a far-bound child is driven by a co-located parent (one car, correct roles). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s) remaining The on-demand + ridesharing CORE is done and tested: on-demand cars, multi- occupant vehicles with eject-all, the shared-ride primitive with a board window, and the canDrive gate + driver election (kids/ill driven, never stranded). The narrated collective-action layer (drive_kids_to_school etc. as real actions + producers) and far-school scoring / patrol / outings / return trips remain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lift
A group ride now writes truthful per-person log entries derived from the
destination + riders: a minor bound for a school building narrates
"Drove {kid} to school" / "Got a lift to school"; anyone bound for a
hospital narrates "Drove {relative} to the hospital" / "Was driven to
the hospital"; else a plain "Gave {target} a ride" / "Caught a ride".
Six manual, effect-free texture events (events.json), invoked live-only
from City.narrateRide — bootstrap/the generator never call startGroupRide,
so the off-map RNG stream and the committed asset are untouched.
The narration rides the SAME election path that already forms the ride
(Phase E), so no separate proactive producer is needed for the behavior
to read; Phase F's far-school preference is already the enrollment sweep's
nearest-first scoring (task 058), so a child only rides when near seats
are full. Regenerated the event-classification + sim-relationships docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… as follow-up Records the delivered scope: on-demand cars, multi-occupant Vehicle with eject-all, the shared-ride primitive, canDrive + election, and the narrated reachable flagships (school run, hospital drive, plain lift). Far-school preference is the enrollment sweep's existing nearest-first scoring, and the open-map guard already holds (outside transitions resolve with no car). The speculative proactive-producer catalog (patrol carpool, group/household/couple outings, work carpool) is a proposed follow-up rather than speculative content under §5.6. README task table + narrative updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Captures the deferred R-cases from task 130's Pass 1 as a scoped ticket: R2 police carpool patrol, R3/R9 group/household venue outings, R5 work carpool, R6 couple outing, R7 drive-a-friend-home, and narrated return trips (school pickup, hospital discharge). Grounded in the real foundations — the N-person generalization of the Planner's 2-person mirrored-agenda pattern, City.startGroupRide, JobOrchestrator, and routines.json — with acceptance criteria and the open planning questions left for the task's own exploration pass (per §5.4). Linked from task 130 and the README task table. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hare one car The unifying reactive mechanism for household/co-worker/couple rides (R3/R5/ R6/R9) with no bespoke producer. LiveWorld's departure phase now gathers, when a departure comes due, every co-located companion (same origin building) bound to the SAME destination and hands the group to City.startCommuteGroup, which elects one driver and forms a single group ride (reusing startGroupRide + narrateRide). A near destination is walked by all; a group with no eligible driver falls back per-person (never stranded); a group larger than one car spawns a second car for the overflow. Live-only (bootstrap/the generator never run LiveWorld.pump) — off-map stream + asset untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
narrateRide now reads the ORIGIN as well as the destination: a minor driven
FROM a school back home narrates "Picked up {kid} from school" / "Rode home
from school"; anyone driven FROM a hospital home narrates "Drove {relative}
home from the hospital" / "Was driven home from the hospital"; the outbound
school run / hospital drive and the generic lift are unchanged. Four new
manual, effect-free texture events. Regenerated the event-classification +
sim-relationships docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…l (R3/R9) A weekend household may plan a trip to a placed venue TOGETHER: City schedules each co-resident member old enough to be at a venue on their own (VENUE_INDEPENDENCE_AGE — younger children stay home with a guardian) the same leisure-outing agenda entry (visiting_beach / eating_out / night_at_the_cinema) in the same afternoon window. When those entries come due the members set off from home to the SAME venue at once, and the Commit-1 carpool mechanism folds them into ONE car — the group ride is emergent, no new coordination primitive. Adoption is deterministic per (worldSeed, household, week); a town with none of the venues placed schedules nothing. City-scheduled (handleNewDay), so the off-map generator never runs it — asset-neutral. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the task file to _DONE, update the README table + narrative. The proactive ride layer shipped as one reactive mechanism (opportunistic carpooling) + a thin household-outing co-scheduler that compose to cover R3/R5/R6/R8/R9 + narrated school-pickup/hospital-discharge returns. R2 (cruising police carpool — a new movement model) and bespoke R7 are deferred as distinct features. All live-only, asset-neutral. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mmits git add -A during tasks 130/131 staged the src/history .tbz files that a concurrent asset regeneration was rewriting, baking a PARTIAL, inconsistent asset into the code commits. Reset src/history to main's committed asset (index-only — the working tree's in-progress regen output is untouched) so the branch's net diff is code + docs only. The regenerated asset lands as its own dedicated final commit once the run completes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…letion Loose-ends pass over the car/ride surface. Removed getDriver() and seatsAvailable() — both had zero consumers in src and test (dead since the 130 multi-occupant refactor). Fixed a stale comment referencing a never-built joinRide() (board() is the real entry). Added an end-to-end test that a group-ride PASSENGER (non-driver) rides through to arrival without routing the car and is never stranded — the one production path the ride tests exercised only indirectly. All other new symbols verified consumed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
entriesByLink() had zero callers and was the ONLY reader of the AgendaEntry linkId field — so the whole link tag was written by three producers (the Planner visit, planJointActivity, the 131 household outing) and never read for anything. Removed entriesByLink() (method + AgendaAccess interface), the linkId field, and all three writers. The joint-plan linkage was never carried by the tag anyway — mirrored entries run together because they share the same earliest/latestTick window; the tests now assert that real mechanism instead of the decorative tag. Behavior-neutral (linkId drove no logic). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Shortens the default offline history-asset run to 90 recorded years. Validator only requires recordYears >= 1; no test pins the value. CLAUDE.md updated to match (250 living × 90 years). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…erson
Live-observed bug (a "Visiting Relatives" person standing frozen on the
street): the planner picked a relative/friend to visit from the pool/social
graph without checking they're actually ON THE MAP. A person-located visit
(locationOverride 'person:<id>') to someone NOT materialized resolves to
town-wide {kind:'outside'}, so the transition "arrives" instantly and the
visitor just stands where they are, visiting a ghost — 2 of 3 active
relative-visits in the observed town targeted off-map people.
Added an optional WorldAdapter.isPresent (LiveWorld: is the person on the
field). bestRelativeTarget/bestFriendTarget/the sick-visit finder now skip
non-present targets — returning null (no visit enqueued) when none are
present. Bootstrap/logical/the generator have no isPresent impl and default to
"present" (town-wide abstract, the sanctioned seam), so the off-map RNG stream
and the committed asset are untouched. Revert-danced (4 tests fail without the
filter).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
) - Only ONE Person detail window at a time — opening another person replaces it (PersonSelected now 'replaceType'). The activity-label set follows the open person windows, so only that one person's label shows (debug show-all still overrides). - Opening a dialog whose entity is already open brings that window to the FRONT instead of duplicating; clicking or dragging any window raises it. Each window carries a z (stacking order); interaction bumps it above the rest via onFocus, threaded Hud → detail window → Window (zIndex + onMouseDown + drag-start). - Windows are keyed by a STABLE id instead of a per-render uuid, which used to remount every window on every HUD state change (resetting position/z). nextZ is hoisted to module scope so the open/focus handlers stay stable for the once-on-mount effect (clean exhaustive-deps). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Person / Business / House inspectors open at 800x700; Construction at 400x415. Every dialog is capped at 80% of the viewport in each dimension (min() on open + maxWidth/maxHeight on the Rnd so a resize can't exceed it). - Windows open just below the clock and left-aligned with it (origin 20,70; the clock sits at left 20 / top 20, ~38px tall) instead of "directly behind" it, and their z-index rides above the clock/toasts (base 1001) so a focused window is never hidden behind the HUD chrome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The feed was bottom-left anchored, so it expanded UPWARD. Re-anchored it to the top-right just below the .time-controls speed buttons (top 52px ≈ their ~30px height + a 10px gap) and right-aligned with them; being top-anchored it now grows DOWNWARD when uncollapsed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ngs (#5) A person walking south briefly clipped UNDER the road tiles south of them: Road.calculateDepth() was row*10, so a road at a higher (souther) row out-sorted the person (depth (row+1)*10+1) until they reached it. Roads never overlap, so they need no per-row ordering — Road.DEPTH is now a constant 1, above soil (0) and below every building ((row+1)*10 >= 10) and person. Buildings and people keep the "more south = more on top" rule, so tall buildings still occlude people to their north. Updated the depth-ordering tests (a southern road never out-sorts a northern person/building) and CLAUDE.md §4.5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… gate) The 131 ride methods on City.ts (startGroupRide/startCommuteGroup/canDrive/ electDriver/narrateRide) were tested only in the agents module (commute.test), which the per-module coverage gate discards for execution (City.ts is execution-owned) — leaving execution at 78.4%. Added an execution-harness ride suite in city.test.ts (election, one-car carpool, walk/overflow, school/ hospital/return narration, LiveWorld.isPresent), lifting execution to 82.3%. Also covered the previously-untested planner jail-visit producer, lifting actions/Planner 79.4% → 91%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vehicle.hasDeparted() (has a route AND is cleared to leave — past the board window) marks a car as in-motion. Person's EnteringCar step now abandons the boarding if the car has already departed and it isn't aboard: a rideshare whose board window lapsed, or any moving car, no longer gets a rider leaping into it — they clear the link (without touching the driver's car) and fall back to idle so the Brain re-plans on foot. A driver still boards its freshly-spawned, un-routed car (not yet departed), and normal boarding is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#1+#2) Replaces the one-shot 'person:<id>' → location snapshot (which only worked for building targets, no-op'd for street targets, and used the 'home' key alias so two people each at their own home falsely "co-located") with a first-class { kind:'person' } transition that PURSUES the target: - LiveWorld registers a pursuit and, each pump, re-resolves the target's current location and routes the pursuer toward them — commute-and-enter for a building/ home (their OWN home, never the pursuer's), a new directed outdoor walk (Field.walkPersonTo → Person.walkOutdoorsTo, driven by walk()) for a street cell, re-chasing only when they move — resolving on true co-location. - coLocated(a,b) is PHYSICAL (same concrete building / outdoor cell), added to the WorldAdapter. ActionEngine's materialize, the running standing-gate, and the interaction contract (requiresSameBuilding) all use it, so a visit/interaction runs only when the two are actually together and re-pursues if the target walks off — no more "visiting" from your own couch across town. - ActionEngine hands 'person:<id>' straight to the world as { kind:'person' } instead of pre-resolving; venue/building/home semantics unchanged. - BootstrapWorld/the generator resolve a person target immediately to the target's own location (town-wide abstract — the sanctioned seam), so the asset is untouched; arcScenarios stays green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ral)
Two seam guards so the offline generator is unchanged by the pursuit rework:
- LogicalWorld.requestTransition resolves a { kind:'person' } target to the
target's own current location immediately (as BootstrapWorld does) — exactly
what ActionEngine used to pre-resolve, so the stored location + immediate
arrival are identical.
- The running standing-gate re-checks co-location only for CONCRETE person
targets (building/home), matching the pre-131 gate off-map (homes are building
keys there, never the 'home' alias); an outdoor target stays ungated (reaching
them is materialize's job). Live still uses physical coLocated. The departure
log only fires on a pending transition, which never happens off-map.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r boarding (CLAUDE.md §4.7/§4.14) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LiveWorld is execution-owned but the pursuit was exercised only in the agents module (commute.test), which the per-module gate discards for execution. Adds an execution-harness test driving coLocated + a person pursuit to arrival and the absent-target cancel, lifting execution 80.6% → 82.3%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dow (#3) The 800×700 house window sizes the family-tree SVG big enough that, on a viewport short enough for the 80% cap to shrink the window, the SVG overflowed and intercepted pointer events over the resident links (integration regression: house-resident not clickable). Fixes: - .house-details is now a flex column; the resident list keeps its own space at the top with position/z-index above the family-tree SVG (which fills the rest and clips), so it can never be covered. - Resident rows are content-width, so the click target is the NAME (far left) — a large sibling inspector opened/dragged over the house window no longer covers it. Also better UX. All 25 HUD integration tests pass locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nners The full round-trip test steps the sim with hundreds of per-chunk CDP samples; it finishes in ~30s locally but consistently edged past the 60s default on the CI runner (pre-existing flakiness, unrelated to the pursuit/HUD work). Bumped its timeout to 150s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Windows had a double scrollbar: .window-body scrolled (overflow:auto) AND each detail window wrapped its content in a second overflow:auto container. Also .window-body used height:-webkit-fill-available, which overflowed the flex column and added its own scrollbar. Now .window-body is the SINGLE scroller — flex:1 + min-height:0 sizes it correctly between header and footer — and the redundant inner overflow containers are removed. - Renamed 130-ridesharing-and-on-demand-cars.md → _DONE (+ updated references in README.md and the 129 task file). Co-Authored-By: Claude Opus 4.8 <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.
Summary
The aliveness-4 branch: the round-4 visual-truth arc's deferred follow-ups, a long-standing world bug, three live-play bug fixes, and the on-demand-cars + ridesharing rework (tasks 130 + 131). One PR, code + docs + tests only — the regenerated history asset lands as its own dedicated final commit (a partial mid-regen asset that
git add -Aaccidentally swept into the code commits was reset out; see thechore:commit).Aliveness-4 follow-ups (tasks 123–128)
caring_for_childrenanchor + a jailed-caregiver rehousing fan-out.placement:"amenity"fences beach/cemetery/park to the construction menu + an unrepresented-category draw boost.The stampFootprint world bug (found live)
Field.stampFootprintregistered a structure'sdestinations/roadAnchorskey before tearing down the grass it replaced — and a grid-aligned structure shares that anchor key with the grass, sodestroyStructurewiped it.roadAnchors/destinationscame up permanently empty in built towns (so V2 ambulatory roam had no targets and 128 loiter nodes never registered). Fixed by registering after the teardown; regression-tested + revert-danced.Live bug fixes
GameManager.emit'sif (!payload)clobbered a falsysetTimeScale(0).advanceTimeskipped crossed in-game minutes (the per-minute departure pump missed them); now steps in ≤1-minute increments.Task 130 — on-demand cars + coordinated ridesharing
Reverts task 129's persistent cars back to on-demand spawn/despawn (car appears as the driver leaves the origin, vanishes as they enter the destination), and builds ridesharing:
Vehicle(driver + passenger list) that ejects every occupant on despawn.startGroupRide— one car, a board window, driver routes.canDrivegate + driver election — kids/severely-ill can't drive, kids can't reach a far school alone (a parent drives them), the ill are driven, nobody stranded.Task 131 — proactive ride producers
Delivered more cleanly than the ticket assumed — one reactive mechanism + a thin co-scheduler compose:
Asset / regeneration
Everything here is live-only glue (LiveWorld carpool, City-scheduled outings, live-only narration; new events are manual-only and never rolled off-map), so it's asset-neutral — the in-progress regeneration remains valid. The regenerated asset will be committed on top.
Test plan
npm run typecheck+npm run lintclean.executionarcScenarioslive↔bootstrap keystone + W8 sprite audit stay green.🤖 Generated with Claude Code