Skip to content

Halifax reconstruction, time-margin metric, and the operator console - #47

Open
legend5teve wants to merge 45 commits into
denoslab:mainfrom
legend5teve:feat/visualization-module-plots
Open

Halifax reconstruction, time-margin metric, and the operator console#47
legend5teve wants to merge 45 commits into
denoslab:mainfrom
legend5teve:feat/visualization-module-plots

Conversation

@legend5teve

@legend5teve legend5teve commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Two bodies of work. The Halifax reconstruction modules, which turn the domain-expert
exchange into a runnable E0 substrate, and an operator console that wraps the simulator
without modifying it and lets a scenario package be authored by drawing on a map.

Halifax reconstruction

  • M1 alert engine. A timed, area-scoped alert schedule in alerts.json, resolved
    per agent and per instant, so the information regime becomes a property of where a
    household is and what has been issued.
  • C.7 belief-only compliance. An authority-trust channel, with the mandatory order
    rule removed, so an order enters through belief and not as a switch.
  • M2 staggered awareness. A household is dormant until a signal reaches it, and its
    urgency clock starts at that instant rather than at t=0.
  • M3 door-to-door and timeline export. The RCMP sweep channel, and a consolidated
    per-run timeline covering ignition, alerts, awareness, departure, arrival, and
    clearance.
  • Part J metrics. Mobilization delay, order compliance, awareness-source share, area
    clearance, corridor flow, and the non-evacuee fire-reach companion count.
  • M0 clock fix. DECISION_PERIOD_S moved to 240 s against a 0.2 s integration step,
    guarded so a decision round is always a whole number of SUMO steps.

Time margin, the outcome measure that was missing

Every arm reported 182 of 182 arrived and zero fire contact, at the historical timing and
with every alert delayed an hour alike, so nothing measured the cost of being late.

Time margin reports the gap between a household leaving and the fire reaching its home.
Fire arrival is interpolated against the previous sample rather than quantised to the
240 s round, and homes the fire never reaches fall back to the closest the fire came, so
a run has a number either way. Households resolve to cleared, caught_at_home,
never_threatened, or never_departed_safe.

Building-centroid hazard basis

Hazard distance was measured to the spawn edge polyline, so every household on a street
inherited the exposure of its most exposed corner. Configs carrying home_xy building
centroids now resolve per household. Everything else keeps its spawn edge and is
bit-identical to before, and the basis travels in the metric output and the run params so
the two experiment batches can never be pooled.

Two new configs, neither replacing anything. halifax_3town_e0_centroid and
halifax_3town_e0_centroid_exact both carry the same 45 edges and 182 households as
halifax_3town_e0. The exact one is the one to run.

Snapping correctness

Building-to-edge matching measured to the nearest middle vertex, which put a house near
the end of a long street onto a different street. It now measures to the edge polyline
with the same call the hazard model uses.

Households also no longer spawn on a motorway, motorway link, trunk, or trunk link.
Passenger cars are allowed on all of them, so drivability alone was never the right test.
A building whose nearest road is one of those is matched to the nearest community street.

Runtime

Position, angle, edge, and route were each read twice per vehicle per step. They now
arrive on one TraCI subscription call per step. The per-step stdout log is off by
default, and fire polygons are drawn only under a GUI binary.

Measured on an 1800 s run, 1,750,605 TraCI calls to 109,959. A 3600 s run went from 194 s
to 102 s wall clock with a byte-identical metrics summary.

Operator console

Runs the simulation script as it is, through a launcher that attaches a read-only bridge
between TraCI steps. The bridge issues no TraCI command and changes no simulation
variable, and ui/tests/test_run_parity.py proves the trajectory is unchanged. Console
runs land in outputs/ui_runs/ so they cannot sit beside a research campaign. The
backend is standard library only, so nothing is added to the environment the simulator
runs in, and the built interface needs no network at any point.

Authoring. An operator draws a box, the buildings inside become households, a second
selection becomes the area an order covers, and fire origins are placed by click with a
scrubber showing the front at a chosen instant. A fourth tool sets one building's agent
count, since most buildings hold one vehicle while a school or a care home holds many.

Buildings are the unit of selection because that is what an operator and the record both
name, and edges are the unit the simulator inserts on, so the translation happens once
when the package is written. That resolved building-level alert areas without touching
agentevac/.

An alert area is a strict subset of the households, enforced in the selection, the view,
and the backend. A building holding no agents cannot be evacuated, and including it would
pull its road into the ordered area and order households that were never selected.

The write path creates a package directory or does nothing. A name already under
configs/ is refused with 409, a failure part way through removes what was written, and
no code path opens an existing package file for writing.

Review notes

  • agentevac/simulation/main.py carries the time-margin wiring, the centroid basis, and
    the TraCI batching together, because they cannot be separated by file.
  • One default changed. The per-step vehicle log is off, restored with VEHICLE_STEP_LOG=1.
  • configs/westwood_uitest is a package authored during browser verification, 175
    buildings and 821 agents. Safe to delete.
  • Map bundles under ui/assets/previews/ are generated and gitignored. Build them with
    python -m ui.tools.build_map_assets, about 20 s per package.

Verification

672 Python tests pass with 4 skipped, 85 frontend tests pass, typecheck clean, production
build succeeds. The parity test is opt-in behind AGENTEVAC_UI_SLOW_TESTS=1 since it
starts SUMO twice.

The authoring path was checked end to end against a live backend, and the whole interface
was walked through in a browser.

🤖 Generated with Claude Code

legend5teve and others added 30 commits March 5, 2026 16:05
 Changes to be committed:
	modified:   agentevac/simulation/main.py
	modified:   agentevac/simulation/spawn_events.py
	modified:   agentevac/utils/replay.py
	modified:   sumo/Repaired.netecfg
	modified:   sumo/Repaired.sumocfg
  Module updated: agentevac/utils/replay.py

  - Fixed RouteReplay._load_schedule(...) so it only reads step and veh_id for replayable events:
      - route_change
      - departure_release
  - Non-replayable events like agent_cognition and metrics_snapshot are now ignored without touching veh_id.

  Cause

  - The loader was accessing rec["veh_id"] before checking the event type.
  - metrics_snapshot records do not have veh_id, so replay loading crashed with KeyError.

  Verification

  1. python3 -m py_compile agentevac/utils/replay.py passed.
  2. Reproduced the failing case with a small local script:

  - one route_change
  - one agent_cognition
  - one metrics_snapshot
  - replay load now succeeds and only indexes the route-change step.
…g, and per-agent heterogeneity

- Add compute_signal_conflict() using Jensen-Shannon divergence in belief_model.py
- Restructure all three LLM prompts (pre-departure, destination, route) to expose
  raw env vs. social disagreement via your_observation/neighbor_assessment/
  information_conflict/combined_belief fields
- Add conflict_assessment field to all Pydantic response models
- Add conflict recording to metrics (record_conflict_sample, compute_average_signal_conflict)
- Implement distance-based noise scaling (proposal Eq. 1): effective sigma scales
  with fire margin / reference distance via DIST_REF_M config
- Add per-agent parameter heterogeneity via sample_profile_params() with truncated
  normal distributions; configurable via *_SPREAD env vars (default 0 = legacy)
- Fix stale subjective_information reference in scenarios.py
- Add experiment stage scripts (stages 0-5) for RQ1/RQ2/RQ3 sweeps
- Add comprehensive tests for all new features (291 tests passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… recording

- Add observation-based exposure function for no_notice scenario that uses
  agent belief state and route length instead of route-specific fire data
- Enable expected_utility in all three scenarios (no_notice, alert_guided,
  advice_guided) with scenario-aware LLM policy text
- Update menu filtering to retain travel time and utility for no_notice agents
- Fix NET_FILE default from .rou.xml (route file) to .net.xml (network file),
  which caused EDGE_SHAPE to be empty and all exposure scores to be zero
- Fix exposure recording to fire only on decision rounds instead of every
  simulation step, preventing dilution of the exposure average
- Add Repaired.net.xml to repo; update SUMO configs to use local net file

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e parameters

Use SIM_END_TIME_S (default 1200s) to control simulation duration instead of
relying on getMinExpectedNumber(), which terminated early when no agents had
departed yet. Remove dummy t_0 vehicle from route file. Add --sim-end-time
CLI flag and SIM_END_TIME_S env var. Update fire source growth rates and
timing for more aggressive spread scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add input-hash caching (Plan C) across all 3 LLM call sites to skip
  redundant API calls when agent inputs haven't changed between rounds
- Add parallel LLM dispatch (Plan A) for process_pending_departures using
  ThreadPoolExecutor — two-phase collect-then-process pattern fires all
  non-cached predeparture LLM calls concurrently (up to MAX_CONCURRENT_LLM)
- Add 4 new fields to AgentRuntimeState for cache state tracking
- Add RQ1–RQ4 experiment runner scripts for automated parameter sweeps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stop the simulation loop as soon as every spawned vehicle has departed
and arrived at its destination, instead of running until SIM_END_TIME_S.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure LLM decision prompts with explicit priority levels
(safety > official guidance > risk assessment), add EOC guidance_source
to operator briefings, and fix early termination to check actual
arrivals via metrics.arrived_count() instead of active vehicle count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dule-plots

# Conflicts:
#	agentevac/agents/agent_state.py
#	agentevac/analysis/metrics.py
#	agentevac/simulation/main.py
#	sumo/Repaired.netecfg
#	sumo/Repaired.sumocfg
…etwork updates

- Record per-step edge traces via getRoadID() for faithful replay of
  actual routes taken (not just planned routes)
- Add departure destination choice: agents pick a destination via LLM
  before spawning, so vehicles head the right direction from step zero
  (parallel LLM dispatch via ThreadPoolExecutor)
- Fix early termination to check arrived_count instead of empty vehicle
  list (prevents premature exit when SUMO defers vehicle insertion)
- Replay mode reads to_edge from departure records for correct initial
  routing
- Update SUMO network with new shelter edges (E#S0, E#S1, E#S2) and
  refreshed spawn_events
- Refine scenario prompt suffixes and DecisionModel schema
  (situation_summary field, expanded reason descriptions)
- Update forecast_layer and corresponding tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dule-plots

# Conflicts:
#	agentevac/simulation/main.py
#	sumo/Repaired.net.xml
#	sumo/Repaired.netecfg
#	sumo/Repaired.sumocfg
…selection

- Record departure destination choice in metrics via record_decision_snapshot
  so destination_choice_share counts all agents (not just those processed by
  process_vehicles)
- Replace fixed-offset vehicle selection with round-robin so all agents get
  mid-route LLM re-evaluation over successive decision ticks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The experiment runner lacked a --sumo-seed argument, so RQ scripts
passed the seed via env var prefix which broke under dash (/bin/sh).
Add --sumo-seed to experiments.py and rewrite all four RQ scripts to
use POSIX-compatible for-loops with the new flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Record each agent's sampled psychological parameters (theta_trust,
theta_r, theta_u, gamma, lambda_e, lambda_t) and write them to an
agent_profiles JSON file alongside the metrics file. Enables post-hoc
verification of population heterogeneity distributions in RQ4 runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…stances

The old thresholds (danger ≤100m, risky ≤300m, buffered ≤700m) were
calibrated for a city-block mental model. In the actual simulation,
the minimum observed margin is 619m, so every agent always classified
the fire as "safe" and never perceived risk. Scale thresholds to
danger ≤1200m, risky ≤2500m, buffered ≤5000m so agents meaningfully
perceive fire hazard. Also scale RISK_DECAY_M from 80 to 960 to
keep the exponential risk curve proportional.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
En-route agents in no_notice mode can now see fire on the first few
edges ahead of their current position (VISUAL_LOOKAHEAD_EDGES, default 3).
This adds a penalty to the current destination's exposure score, making
agents more likely to switch shelters when fire blocks their route.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ling

_observation_based_exposure() now prefers travel_time_s_fastest_path
(minutes * 0.3) over len_edges (count * 0.15) to better reflect actual
exposure duration. Falls back to edge count when travel time is unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…y prompts

Adds an explicit instruction to all three LLM policy locations (pre-departure,
en-route destination, en-route route) requiring agents to only reference
information explicitly present in the prompt data. Prevents GPT-4o-mini from
fabricating neighbor behaviors, evacuation patterns, or shelter choices that
cascade through the messaging system.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agents in no_notice mode now only perceive fires within
FIRE_PERCEPTION_RANGE_M (default 1200m) of their position:

- Perception horizon: if no fire is in range, env_signal margins are
  None → observed_state="unknown" (genuine uncertainty instead of false
  "safe").  When fire is in range, margins are computed from visible
  fires only.

- Route-level fire data: all reachable menu items gain
  proximity_blocked_edges and proximity_min_margin_m from visible fires,
  enabling the utility function to differentiate destinations by hazard.

- Exposure scoring: _observation_based_exposure() adds a proximity
  penalty (blocked * 8.0 + margin_penalty) matching _expected_exposure
  weights, so routes through visible fire are strongly deprioritised.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd compact JSON encoder

Functional updates:
- Extract AgentMessagingBus and OutboxMessage from main.py into
  agentevac/agents/messaging.py with spatial filtering support
  (comm_radius_m parameter for distance-based broadcast delivery)
- Add filter_history_for_scenario() in scenarios.py to sanitize
  agent self-history records before embedding in LLM prompts
  (strips forecast, advisory, fire metrics per information regime)
- Add _CompactLeafEncoder in run_parameters.py for readable
  parameter log output (leaf dicts rendered on single lines)
- Add agentevac/analysis/analyze_run.py run analysis utility
- Add tests for messaging spatial filtering (test_messaging.py)
  and history filtering (test_scenarios.py)
- Add Research_Proposal_0317.pptx to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
legend5teve and others added 15 commits March 30, 2026 01:14
…rust enforcement, and spatial messaging

Functional updates in agentevac/simulation/main.py:

DEPARTURE PROMPT:
- Replace BINDING CONSTRAINT with DECISION RULE prompt — 4 ordered
  rules (p_danger > theta_r, heuristic_departure_signal, official
  order, else wait) with stop-at-first-match semantics
- Add heuristic_departure_signal field to predeparture prompt,
  exposing the pre-computed departure model output (covers theta_r,
  urgency decay via theta_u/gamma, low-confidence precaution, and
  neighbor departure pressure)

THETA_TRUST LLM ENFORCEMENT (predeparture + routing prompts):
- When theta_trust=0.0: strip inbox entirely, add BINDING CONSTRAINT
  instructing LLM to IGNORE all neighbor/social data
- When theta_trust>0.0: inject calibrated percentage guidance
  (e.g., "rely 70% on own observation, 30% on neighbor messages")
- Apply _consider_pol, _belief_weigh_pol, trust_policy to all 3
  routing prompt locations (destination, route, and predeparture)

SCENARIO-AWARE HISTORY:
- Filter agent_self_history through filter_history_for_scenario()
  before embedding in routing prompts (prevents data leakage from
  historical records)

SPATIAL MESSAGING:
- Add COMM_RADIUS_M env var for distance-based broadcast filtering
- Precompute SPAWN_EDGE_MIDPOINT for pre-departure agent positions
- Pass agent positions to messaging.begin_round() for spatial delivery
- Include comm_radius_m in messaging config sections of all prompts

CONFIGURATION:
- Remove inline AgentMessagingBus/OutboxMessage (now imported)
- Widen lambda_e bounds to (0.0, 100.0) and lambda_t to (0.0, 100.0)
- Add fire source F0_8 at (16348, 6801) with r0=400
- Export fire_sources and fire_events to run parameter log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Functional updates:
- Disable veh3 and veh4 spawn groups (commented out) to reduce
  toy-model agent count for focused testing
- Comment out large-scale spawn groups (veh9–veh41) — reserved
  for future 100+ agent experiments
- Update SUMO network (Repaired.net.xml): add junction J20 and
  associated internal edges for improved routing near south zone
- Update Repaired.netecfg and Repaired.sumocfg timestamps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move hardcoded fire sources, destinations, routes, and spawn events from
Python source into JSON config files under configs/<map>/.  Add --map CLI
flag (default: lytton) so switching networks requires no code changes.

Add compact spawn format: {"edge": "...", "count": N} auto-generates
agent IDs from edge names, staggers positions within edge bounds, and
cycles colors.  Position overflow is validated against actual SUMO edge
lengths after network load.

Also includes risk_density normalization (edge-count invariance) with
5 new tests, and enables veh3/veh4/veh9 spawn groups in spawn_events.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflicts by keeping local (branch) versions which include
config externalization, compact spawn format, and risk_density
normalization changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d logging

Add configs/halifax/ with network paths, 4 shelters, 3 fire sources,
and 38 spawn groups.  Include Halifax SUMO config files (net.xml and
buildings.xml excluded via .gitignore due to size).

Add scripts/generate_spawns_from_buildings.py: offline tool that reads
building polygons, finds nearest drivable edges via KD-tree, and writes
spawns.json in compact or detailed format.  Supports --mode per-building
and per-edge with configurable --count, plus SUMO XY --bbox filtering.

Record SUMO_SEED in run_params JSON for reproducibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Track input/output/total tokens and LLM call count across all four
OpenAI call sites using a thread-safe accumulator. Results are included
in run_metrics JSON output. Also adds total_agents count and --map flag
to experiment runner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Splits the previously-uniform information channel into two streams:

- Environmental signals (personal observation) are now always real-time;
  the noise model still applies but `apply_signal_delay` is no longer used
  on the env path. This better reflects that drivers see what's around them
  immediately, regardless of broadcast lag.
- Institutional channels (forecast, annotated route/destination menus,
  advisory labels, expected-utility scores) are subject to `INFO_DELAY_S`
  via a new `apply_institutional_delay` resolver and a new
  `institutional_history` ring on each `AgentRuntimeState`. When history
  is shorter than the delay, the agent receives a `no_notice`-grade view
  on the institutional channel for that round, modeling the real-world lag
  before the first official report arrives. In `no_notice` mode the
  institutional channel is already invisible, so the delay is a no-op.

Wired through all four LLM decision sites in main.py (predeparture
routing, departure-destination, in-transit destination, in-transit route).

Also expands `_run_parameter_payload` with control_mode, decision_period_s,
openai_model, max_concurrent_llm, net_file, sumo_cfg, and new agent_memory,
forecast, and overlays sections so sweep artifacts capture the full
runtime configuration. Bumps `MAX_CONCURRENT_LLM` default 20 -> 50.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass --params-log-path to each subprocess so every sweep run writes its
own run_params_<case_id>.json alongside metrics_<case_id>.json. Without
this, downstream plot scripts had no way to recover the scenario / sigma
/ delay / trust metadata for completed runs and fell back to "unknown".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
plot_experiment_comparison.py:
- Filter out *_profiles.json siblings from --metrics-glob so the per-agent
  cognition dump no longer pollutes load_cases with all-zero rows.
- Add a filename-parser fallback in _param_metadata that recovers
  scenario / sigma / delay / trust from the metrics filename when no
  run_params companion is present (older runs).
- Promote the scenario palette to module-level SCENARIO_COLORS /
  SCENARIO_ORDER constants so other plot scripts can reuse them.

plot_run_metrics.py:
- Add a --metrics-glob flag and a new plot_kpi_multirun() that draws a
  2x2 KPI grid (departure variance, route entropy, hazard exposure,
  avg travel time) with one bar per run, sorted contiguously by scenario,
  colored by scenario, with a shared legend. The single-run dashboard
  path is unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Building-grouped spawn manifest for the Halifax map, keyed by edge id
with per-edge agent counts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the timed-alert reconstruction of the 28 May 2023 Upper
Tantallon wildfire evacuation, decomposed into the build-plan modules.

- M0, the two-clock fix, DECISION_PERIOD_S=240 with a SIM_STEP_LENGTH_S
  constant, and a per-source fire radius cap via max_r_m.
- M1, alert_schedule.py resolves a timed, area-scoped alert schedule so
  the information regime becomes per-agent and per-time. config_loader
  loads alerts.json and ALERT_TIME_OFFSET_S drives the E1 timing arm.
- C.7, belief-only compliance. An evacuate order blends into belief
  through theta_auth times a channel factor rather than a mandatory
  prompt rule, and rule_based_policy.py mirrors the LLM path.
- M2, staggered awareness. An agent stays dormant until a signal
  reaches it via alert, door knock, perception, or peer, so the urgency
  clock starts at awareness.
- M3, door-to-door as a live driver, a door-knock awareness trigger and
  an in-person belief channel through DOOR_CHANNEL_FACTOR, plus the
  per-run consolidated timeline export in run_timeline.py.
- Part J metrics, mobilization delay, order compliance by channel and
  area, area clearance, awareness-source share, corridor flow, and the
  non-evacuee fire-reach companion count.
- E0 config under configs/halifax_3town_e0 and the E0/E1/E4 experiment
  driver scripts/run_experiments.py.

473 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the outcome measure the existing metrics were missing, lets a household be
measured at its own building instead of its street, and removes the per-step
TraCI traffic that dominated every run.

Time margin reports the gap between a household leaving and the fire reaching
its home, so a run has a consequence number even where nobody is caught. Fire
arrival is interpolated against the previous sample rather than quantised to the
240 s round, and homes the fire never reaches fall back to the closest approach.
Households fall into cleared, caught_at_home, never_threatened, or
never_departed_safe.

The home a household is measured at follows a geometry basis. Configs carrying
home_xy building centroids resolve per household, everything else keeps its
spawn edge and is bit-identical to before. The basis travels in the metric
output and the run params so the two experiment batches can never be pooled.

Per-step vehicle state moves onto TraCI subscriptions. Position, angle, edge,
and route were each read twice per vehicle per step, seven round trips in total,
now served by one call. The per-step stdout log is off by default, since it
reached 44 MB for a one-hour run. Fire polygons are drawn only under a GUI
binary and only when the radius has actually moved.

Measured on an 1800 s run: 1,750,605 TraCI calls to 109,959, and a 3600 s run
from 194 s to 102 s wall clock with a byte-identical metrics summary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The spawn generator snapped a building to whichever edge had the nearest middle
vertex, which put a house near the end of a long street onto a different street
whose midpoint happened to be closer. Snapping now measures to the edge polyline
with the same polygonOffsetAndDistanceToPoint call the hazard model uses, so an
authored package and the simulation agree on where a household sits. Pass
--snap midpoint to reproduce a config authored before this.

Households no longer spawn on a motorway, motorway link, trunk, or trunk link.
Passenger cars are allowed on all of them, so drivability alone was never the
right test, and a driveway does not meet a limited-access highway. A building
whose nearest road is one of those is matched to the nearest community street,
and left unsnapped when there is none in range. --allow-highway-spawn restores
the old behaviour.

--attach-centroids-to holds an existing spawn set fixed and only attaches
building geometry, so a centroid config is the same household population as the
edge config it derives from and the two batches stay comparable. Assignment runs
in two passes over a global claimed set, so one building never becomes two
households.

Two new configs, neither replacing anything. halifax_3town_e0_centroid was built
with the midpoint snap, halifax_3town_e0_centroid_exact with the polyline snap.
Both carry the same 45 edges and 182 households as halifax_3town_e0, with 182
distinct buildings and no edge fallbacks. The exact one is the one to run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The console wraps the simulator without modifying it, running the simulation
script as it is through a launcher that attaches a read-only bridge between
TraCI steps. Three views cover configuring a run, watching it, and reviewing it.

Adds authoring. An operator draws a box on the map, the buildings inside become
households, a second selection becomes the area an order covers, and fire
origins are placed by click with a scrubber showing the front at a chosen
instant. A fourth tool sets one building's agent count, since most buildings are
houses holding one vehicle while a school or a care home holds many.

Buildings are the unit of selection because that is what an operator and the
record both name, and edges are the unit the simulator inserts on, so the
translation happens once when the package is written. That resolved the
building-level alert areas without touching agentevac/, since alerts.json keeps
the edge format the alert engine already reads.

An alert area is a strict subset of the households, enforced in the selection,
the view, and the backend. A building holding no agents cannot be evacuated, and
including it would pull its road into the ordered area and order households that
were never selected.

The write path creates a package directory or does nothing. A name already under
configs/ is refused with 409, a failure part way through removes what was
written, and no code path opens an existing package file for writing.

Map bundles gained a building layer carrying each building's centroid,
footprint, simulation coordinates, and the road a household there would spawn
onto. Simulation coordinates needed a longitude and latitude to UTM transform,
which sumolib cannot do without pyproj, so the series implementation in
ui/bridge/projection.py now runs both directions. It round-trips within 10 mm
across three UTM zones and agrees with sumolib plus pyproj to 64 mm on 182 real
buildings.

Also adds a Quit control, which ends a run in flight so it exports through its
own finally block, then stops the console and releases its port.

configs/westwood_uitest is the package authored during browser verification, 175
buildings and 821 agents. Safe to delete.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Uncommitted changes that predate the console work and travel with it. The
halifax_3town_e0 shelter list moves from the placeholder Shelter_A to D onto the
three real destinations the runs already use, run_experiments gains sweep flags,
and paper/ is ignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@legend5teve legend5teve changed the title Halifax reconstruction modules M1-M3, C.7, and Part J metrics Halifax reconstruction, time-margin metric, and the operator console Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant