Files
settled-reach/.pql/changelog/tickets/2026-05.sql
T
jpmschweitzerandClaude Opus 4.8 5adf2b8bd7 migrate ticket changelog to record_id identity (D-26)
Rewrites the committed .pql/changelog/ from the old T-NNN-keyed format to
the record_id identity model (pql 1.10.0, D-26): tickets keyed by a ULID
record_id, the friendly T-NNN in ticket_idmap, references and hashes
recomputed. Produced by the one-off cmd/migrate-ids in the pql repo.

Other clones: install pql >= 1.10.0, then rm .pql/pql.db && pql plan
rebuild to regenerate the local DB from this changelog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 09:29:34 +02:00

492 lines
183 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXN360AQN3973AHS8M', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_dialogue_sprint20: 5 failures — AudioManager.get_active_dip or other API drift', '5 failing test cases in test_dialogue_sprint20.gd. The suite tests D-020 signal decoupling for dialogue_box. Likely failure: test_show_dialogue_does_not_call_audio_manager_directly compares AudioManager.get_active_dip() before/after show_dialogue — if AudioManager.get_active_dip() behavior changed or is not returning clean state, this would fail. Investigate by running the suite in isolation and reading specific assertion failures from the log. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:11:54', '2026-05-02 08:11:54', NULL, '4dae7120006d6a193a64f8cf9a302c6c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXMVYQJV2QDHH8H7P8', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_input_roundtrip: 3 failures — server binary missing or LocalBridge API change', '3 failing test cases in test_input_roundtrip.gd. All tests check _connect_to_server() which skips if server binary not found — but 3 still fail, suggesting either: (1) server binary is present but something in LocalBridge.send_message, poll, or Protocol.encode_player_inputs changed, or (2) test environment returns false from _connect_to_server but then asserts fail. Investigate by running with --filter test_input_roundtrip and checking the log for exact assertion sites. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:11:59', '2026-05-02 08:11:59', NULL, '43bb1bdbd99567c04b87659d92fe01ea', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY3TXD5HDKHFVGDG18', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_implant_app_lifecycle: 1 failure — ImplantApp or HudGroups state API', '1 failing test case in test_implant_app_lifecycle.gd. Suite tests ImplantApp._internal_app_changed, nav stack, preserves_state, and HudGroups state. after_test resets HudGroups._active_app, _active_mode, _groups — if these private fields were renamed or removed, after_test would fail and bleed state. Or a specific on_open/on_close hook fires differently. Run --filter test_implant_app_lifecycle and check specific assertion. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:12:32', '2026-05-02 08:12:32', NULL, '051b9e0b60e23d92ed295ee5e6fbf2f1', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWK87PCWGZ6GWT2TBM', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_game_state_sprint20: 1 failure — SnapshotHandler or stationary_ticks path', '1 failing test case in test_game_state_sprint20.gd. before_each resets SnapshotHandler._prev_player_position. If this static var was renamed or moved, the before_each hook would error. Alternatively a stationary_ticks path changed in SnapshotHandler. Verify SnapshotHandler still has static var _prev_player_position at that exact name. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:12:38', '2026-05-02 08:12:38', NULL, '8842e278bdab2bdfb8a5a39315a70f73', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYF23MZ86S1XF1542G', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_fog_shader: 1 failure — stale VIS_PERIPHERAL or peripheral sector assertion', '1 failing test case in test_fog_shader.gd. The most likely failure is test_fog_state_forward_vs_peripheral: it asserts Forward > Peripheral visibility values, but Sprint 22 (#569) removed peripheral sector. Now all visible tiles write VIS_FORWARD regardless. The test checks the texture is non-null (not the actual byte values), so it should pass... unless the failure is in test_fog_state_exploration_persists which has a weak assertion. Run --filter test_fog_shader to identify the specific failure. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:12:45', '2026-05-02 08:12:45', NULL, '307954ea49e0c00d28b8df75bf36eb8a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZYDBRV5T9QVQAXGV0', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_e2e_connection: 1 failure — server binary path or TCP connection state', '1 failing test case in test_e2e_connection.gd. test_send_input_receive_snapshot skips if server binary not found. If it fails, either: (1) server binary exists but connection fails (port collision, timing), or (2) LocalBridge.reset() method was removed (the test calls _bridge.reset() but this may not exist on LocalBridge). Verify LocalBridge has a reset() method. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:12:51', '2026-05-02 08:12:51', NULL, 'b4dbcd91698ef145736699282ec6bacf', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY9FV0S9RE42WY7SHW', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_audio_sprint13: 1 failure — AudioManager.clear_dip or API drift', '1 failing test case in test_audio_sprint13.gd. before_test calls AudioManager.clear_dip(), stop_all_loops(), set_volume() for all buses. If any of these methods were renamed or a bus constant changed, setup would fail and cascade. Or a specific dip timing assertion is stale. Run --filter test_audio_sprint13 and read the specific failure. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:12:56', '2026-05-02 08:12:56', NULL, '46d2fa20895bf94f89ab0bd4f35fce3c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXCE9D9JYBZWVXMG68', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_ai_dialogue_sprint26: 1 failure — HardwareDetector autoload missing or settings_dialog API', '1 failing test case in test_ai_dialogue_sprint26.gd. The HardwareDetector tests skip if autoload not found. The GameState field tests (ai_enhanced_dialogue_enabled) look correct. The failing test is likely in the settings_dialog section: SETTINGS_DIALOG_SCENE preloaded at class level — if settings_dialog.tscn doesn''t exist or a method was renamed, class-level preload would fail. Check res://ui/settings_dialog.tscn exists and the relevant methods. Sprint 38 triage #871.', 'backlog', 'low', NULL, 'client', NULL, '2026-05-02 08:13:02', '2026-05-02 08:13:02', NULL, 'ae1744a3e74b50f789af254219b2bacd', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW2J2WY3FV4K2C3YRM', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_dialogue_sprint20 — 5 cases fail (signal decoupling suite)', '5 tests in test_dialogue_sprint20.gd fail since sprint-36. Tests verify D-020 dialogue_box.gd signal decoupling (#558). Code analysis shows signals are correctly implemented but failures persist. Requires actual Godot test run to identify root cause. Opened 2026-05-02 during #871 triage. Tests: test_show_dialogue_emits_dialogue_state_changed_true, test_show_dialogue_does_not_mutate_game_state_directly, test_hide_dialogue_emits_dialogue_state_changed_false, test_show_dialogue_emits_audio_dip_requested_dialogue, test_show_dialogue_does_not_call_audio_manager_directly.', 'cancelled', 'medium', NULL, 'client', NULL, '2026-05-02 08:20:14', '2026-05-02 08:20:14', NULL, '5f474109b5a672e8eb82b8d6b9988e14', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYMWW54FKG4TMV9ZWM', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_input_roundtrip — 3 live-server cases fail', '3 tests in test_input_roundtrip.gd fail. Tests require the Rust server binary at server/target/debug/settled-reach-server. When binary is present, round-trip input tests (movement, interact, move_then_interact) fail — likely a protocol mismatch or timing issue. Opened 2026-05-02 during #871 triage. Skipped gracefully when binary absent.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:19', '2026-05-02 08:20:19', NULL, '65519ef0ba9bc9932f1d6fa59735e95a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYHKX5YTGVJYVPFR9M', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_implant_app_lifecycle — 1 case fails (unknown)', '1 test in test_implant_app_lifecycle.gd fails since sprint-36. Suite tests ImplantApp hook ordering, preserves_state, register_screen. Cannot identify the specific failing test from code analysis alone — requires actual Godot test run. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:22', '2026-05-02 08:20:22', NULL, '1371232c3fb1aa78881adbe258be1b18', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZNA6DFJ4MCGC9Z8GR', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_game_state_sprint20 — 1 case fails (stationary_ticks/zone_id)', '1 test in test_game_state_sprint20.gd fails. Suite tests D-020 server-authoritative stationary_ticks and zone_id. Logic appears correct from code analysis against snapshot_handler.gd — requires actual Godot test run to identify which of the 9 test cases fails. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:28', '2026-05-02 08:20:28', NULL, 'cab9c7755545f2155108fe8f8d0e6ed3', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW2VWRZ69X3SVAW6SG', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_fog_shader — 1 case fails (FogState boundary/timing)', '1 test in test_fog_shader.gd fails. FogState is registered as autoload so all tests run (not skipped). Cannot identify the specific failing test from code analysis. Candidates: test_boundary_wall_fog_vis_forward (uses update_from_state on live FogState), performance timing tests. Requires actual Godot test run. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:32', '2026-05-02 08:20:32', NULL, '4a4e83ea32e43879c0b343857b96b3f0', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYZF0J8H0X0HQJPWXR', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_e2e_connection — 1 case fails (live server required)', '1 test in test_e2e_connection.gd (test_send_input_receive_snapshot) fails when server binary is present. Requires server/target/debug/settled-reach-server. Skips gracefully when binary absent. Likely protocol or connection timing issue. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:37', '2026-05-02 08:20:37', NULL, '75bd889c7e88afadf64bd91a971334e5', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYYP8VGT5WQK2YY7YR', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_audio_sprint13 — 1 case fails (AudioManager/zone)', '1 test in test_audio_sprint13.gd fails. Suite tests D-073 zone crossfade, D-068 volume persistence, D-069 dip timing. All code paths verified correct against AudioManager implementation. Requires actual Godot test run to identify which specific test case fails. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:42', '2026-05-02 08:20:42', NULL, '57e24f494f816b6d59dd6b59677dc90b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYW63JGGSJ5W47V9TC', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_ai_dialogue_sprint26 — 1 case fails (HardwareDetector/settings)', '1 test in test_ai_dialogue_sprint26.gd fails. Suite has 67 tests — HardwareDetector-dependent tests skip gracefully when autoload absent, but HardwareDetector IS registered as an autoload so most run. Cannot identify specific failing case without actual test run. Opened 2026-05-02 during #871 triage.', 'cancelled', 'low', NULL, 'client', NULL, '2026-05-02 08:20:46', '2026-05-02 08:20:46', NULL, '8bec33d28c85029aabf8dcb77963ad9c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXETWQ0T9YEDYZEX9W', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_examine_display_sprint18: 2 failures — ExamineDisplay scene or API mismatch', '2 failing test cases in test_examine_display_sprint18.gd. Scene path res://ui/examine_display.tscn exists. Failures are likely in the ExamineDisplay-specific tests (not the GameState snapshot tests which look straightforward). Check whether ExamineDisplay API changed: _show(result), _dismiss(), _is_showing(), auto_dismiss timer wiring. Sprint 38 triage #871.', 'done', 'low', NULL, 'client', NULL, '2026-05-02 08:12:04', '2026-05-02 08:22:58', NULL, '95408ce7d67c9e917e0e7f2cc7dd64fe', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ5D364SW1QJ7RZJ04', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_client_p1: 2 failures — FogEntities or EntityRenderer API change', '2 failing test cases in test_client_p1.gd. Suite tests fog byte encoding (#3-#6) and entity lifecycle (#7-#9). The fog tests look correct against current FogState API. Entity tests may be failing if EntityRenderer or FogEntities constructor/script changed. Run --filter test_client_p1 and check specific failed assertions. Sprint 38 triage #871.', 'done', 'low', NULL, 'client', NULL, '2026-05-02 08:12:09', '2026-05-02 08:22:58', NULL, 'c2efef4d18140a264b216082b05fe13d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ1JZHT0YSRW8ZDC6R', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_monologue_display: 1 failure — MonologueDisplay private API mismatch', '1 failing test case in test_monologue_display.gd. Suite reads private state (_visible, _queue, _next_fade_in_msec) from monologue_display.tscn instance. If any of these internal var names changed or the scene path moved, the test fails. Check client/ui/monologue_display.gd for current private var names. Sprint 38 triage #871.', 'done', 'low', NULL, 'client', NULL, '2026-05-02 08:12:26', '2026-05-02 08:22:59', NULL, '2a8d1fd6844777e65c5ec7fbd2937892', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYCD16X79D3VNXAYBR', 'bug', '06FB0TNSRXJDJRGQQ073VZRA4W', 'test_time_display_sprint17: 1 failure — test_hud_time_row_updates_after_process', '1 failing test case in test_time_display_sprint17.gd. Likely test_hud_time_row_updates_after_process: calls instance._process(0.016) then asserts hud.get_time_text() returns ''12:00 . Afternoon . D1''. If main.gd _process no longer calls HUD update_from_state or if HUD.get_time_text format changed, this would fail. Check main.gd _process for HUD update wiring. Sprint 38 triage #871.', 'done', 'low', NULL, 'client', NULL, '2026-05-02 08:12:20', '2026-05-02 08:22:59', NULL, 'd84157aa089ee960b82fa123deb2a0ee', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYWGGD3W648EJEJMC0', 'story', NULL, 'Add decisions-orphan-tickets CLI — surface tickets with invalid decision_ref', 'Follow-up from PR #136 review (T8): tickets.decision_ref is free-text without a foreign key. A typo''d D-ID, or a decision that gets renumbered/deleted, will silently leave tickets pointing at nothing — they won''t show up under the referenced decision in ''make decisions-coverage'' or ''tooling/db/decision show <D-NNN>''.
Deliverable: a new CLI subcommand (e.g. ''tooling/db/decision orphan-tickets'') that lists tickets whose decision_ref is set but does not match any row in the decisions table. Optionally add a ''make decisions-orphan-tickets'' Makefile target.
Schema unchanged — pure SELECT. Avoids the fragility of a two-way foreign key across markdown-sourced decisions and DB-sourced tickets.', 'done', 'low', NULL, 'ci', NULL, '2026-04-22 06:22:41', '2026-05-02 16:37:06', NULL, '320c8b375993d15035df6236fd1c0201', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRX0B8MD2GRZ8KV1APC', 'task', NULL, 'Switch meta.schema_version to monotonic semver for savegame migration lineage', 'Follow-up from PR #136 review (T9): the current meta.schema_version field stores a SHA-1 of server/data/systems-schema.sql at generation time. Two SHAs cannot be ordered — you cannot tell from the hash alone which schema version is newer.
When the savegame system is built (Phase 5+) and needs migration lineage (forward-migrating an old save to a current systems.db), SHA hashing is the wrong primitive. Switch to an explicit monotonic version number (semver or integer) bumped manually whenever the schema changes in a backwards-incompatible way. The SHA can still be stored alongside for tamper detection.
This is a Phase 5+ item — do NOT change now. Ticket exists to capture the design debt so it isn''t rediscovered during savegame work.
Blocked by: Phase 5 (world generation + walkable world) reaching savegame design.', 'done', 'low', NULL, 'ci', NULL, '2026-04-22 06:22:51', '2026-05-02 16:37:06', NULL, '8cbd20a953a743909d6f00ad7ffddd1b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ2W0H4Z2CDQSJT5M0', 'task', NULL, 'Copy-team review: wiki/corporations authored by server team in #860', '**Follow-up to sprint 37 server #860.** Server-team agent Dudley authored/modified 21 wiki/corporations/*.md files to resolve the economy-db coverage gate. Per CLAUDE.md team scope rules, wiki/ is copy-team territory — this ticket asks copy team to review the cross-team writes for voice, style, lore consistency, and factual accuracy.
**Files to review:**
*Modified (tag additions for economy coverage):*
- baektu-mining-alliance.md, bergkraft-antriebswerke.md, bifrost-marmor.md
- gate-corporation.md, hanyang-precision.md, jeju-lattice.md
- nordmark-skog.md, norrland-woodcraft.md, salud-alliance.md
- shetland-wool.md, societe-chimique.md, stalownia-kowalski.md
- svanevann-waters.md, thrds.md, tongyeong-drive.md
*New (~36-line corp pages):*
- arbour-aggregates.md
- earth-standard-group.md
- rush-mining.md
- scapa-flow-industries.md
- sede-chemical-works.md
- threshold-fuel-syndicate.md
**Scope of review:**
- Voice/style consistency with existing wiki corp pages
- Lore consistency (system references, corridor geography, D-record citations)
- Tag values match existing commodity vocabulary
- Frontmatter schema compliance
**Rationale for accepting cross-team write:** Lead decision 2026-04-21 — sprint 37 is a maintenance sweep, Dudley''s writes were pragmatically needed to unblock the coverage gate, and the content is plausibly written. Quality review happens here, not at merge time.
**If issues are found:** File fixes as direct copy-team tickets; do not revert the server PR.
**Context:** server PR sprint-37/server, ticket #860 (original), wiki/authoring/tier1-module-authoring.md (schema reference).', 'done', 'medium', NULL, 'copy', NULL, '2026-04-21 15:34:55', '2026-05-02 16:46:06', NULL, '8fa992266fc07171052577a15ddcc2e2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXTXW5V42XZ8HAF990', 'task', NULL, 'Implement CityGenerationContext struct (D-200)', 'Rust struct populated from systems.db at game startup. Fields: city_id, political_archetype, prosperity_baseline, surrounding_biome, road_entry_directions, footprint_radius_km, founding_orientation, world_tier. See D-200, workshop-outcomes.md.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:18', '2026-05-02 20:51:30', NULL, 'c4c3d35deb4096386377dd1a4f45d6fe', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYTHKJCGTFPRP3G1P0', 'story', NULL, 'Free camera viewer — decouple camera from player, WASD pan + scroll zoom, implant/atlas/map access', 'Bare-minimum debug viewer to inspect generated tile output without a player character. This is throwaway/iterative — the rendering system will evolve with the generation pipeline. Do not over-engineer.
Deliverables:
1. Decouple Camera2D from GameState.player_position — add a debug toggle key (e.g. F4)
2. WASD / arrow key pan in free camera mode (no server input sent)
3. Scroll wheel zoom
4. Implant UI accessible in free camera mode — atlas (implant/map/atlas) and star chart (implant/map) openable as normal
Explicitly NOT in scope:
- Visual polish
- Fog or LOS adjustments
- Any rendering changes — use whatever TileRenderer produces today
- Save/restore of camera position
Key files: client/scripts/main.gd (camera lerp in _process), client/scripts/autoloads/hud_groups.gd', 'done', 'high', NULL, 'client', NULL, '2026-04-24 10:56:54', '2026-05-03 09:23:43', NULL, '1046988861533625612c115427a5264d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRX8QPW2MH203K203HW', 'task', NULL, 'Strip archetype-driven client code (follow-up to #878 server)', '**Follow-up to server #878.** Per lead direction 2026-04-21: the CharacterArchetype trace is Phase 6 filler, not production. Server #878 strips the full server-side trace. This ticket mirrors it on the client.
**Keep (production, per lead):**
- Character creation UI flow
- Insert screens / diegetic neural overlays
**Strip (uncleaned filler):**
- `client/scripts/autoloads/game_state.gd` — `character_archetype: String` field (line 100), `lattice_profile` derivation from archetype (line 47)
- `client/scripts/autoloads/session_manager.gd` — `save_character_archetype()`, `_read_archetype_file()`, character.txt save/load (lines 58, 172-192)
- `client/scripts/protocol/protocol.gd` — character_archetype field on StartupMessage encoding
- `client/scripts/autoloads/sim_bridge.gd` — archetype wire-up
- `client/tests/test_signal_sprint24.gd` — audit; delete or narrow if archetype-dependent
- Any monologue color-palette code keyed off archetype (search for lattice_augmented / lattice_baseline)
**Blocked by:** server #878 (removes `character_archetype` field from StartupMessage — protocol break rides #874''s PROTOCOL_VERSION wave).
**Verification:** client launches, character creation UI loads, insert screens render, game session starts without archetype state in the codebase. Grep `character_archetype`, `lattice_profile`, `smuggler`, `detective` in client/ — only character-creation UI references remain.
**Context:** see `docs/architecture/sprint-37-878-audit.md` (Tyre''s full audit of the archetype trace) and the revised #878 scope in the server task description.', 'done', 'medium', NULL, 'client', NULL, '2026-04-21 15:05:27', '2026-05-03 09:23:44', NULL, 'ef6fdd4adeafefe5ef47c21df9f3f696', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZB98P4FKEAGW9PFDW', 'task', NULL, 'Revive fog state behavioral tests (exploration persistence, grow-only bounds)', 'test_fog_sprint22.gd was deleted in Sprint 37 (#870 parse-error cleanup). It contained unique behavioral tests for FogState: EXP_EXPLORED persistence after leaving LOS, grow-only bounds invariant, texture-resize copy, BoundaryWall handling (#585). test_fog_shader.gd covers shader file content only — the FogState data management tests are not covered elsewhere. Rewrite against current FogState API when time allows. Spec: D-059, D-066, #569.', 'done', 'medium', NULL, 'client', NULL, '2026-04-21 15:01:10', '2026-05-03 09:23:45', NULL, '43e3063f53df8539a1040f60360dea82', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ9251FR8EKRRKAXNG', 'bug', NULL, 'dialogue_box confrontation_monologue signal does not fire in headless test mode', 'test_d063_confrontation_signal_fires_on_confrontation_option in test_dialogue_sprint18.gd: signal_fired remains false after _on_option_pressed(0) on a confrontation option. _start_confrontation_beat calls create_tween() before emitting confrontation_monologue; likely the tween creation or tween_property call on ''panel'' aborts execution before the emit in headless mode. Investigate whether panel is null or tween creation fails headless. Test spec: D-063.', 'done', 'medium', NULL, 'client', NULL, '2026-04-20 16:09:48', '2026-05-03 09:23:45', NULL, '80ed1e96e16f42b2348bb0733ba6d1e6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXJDJRGQQ073VZRA4W', 'bug', NULL, 'Pre-existing test failures across 13 suites (sprint-36/37 triage umbrella)', 'Umbrella for pre-existing client test failures that pre-date sprint 36 work and are unrelated to ongoing sprint features. Each needs investigation: stale assertion, real regression from a prior sprint, or flaky. Recommend splitting into per-suite subtasks once a sprint has bandwidth.
## Current state (2026-04-22, after sprint-37 PR #135 review-fix pass)
**23 failing test cases across 13 suites, 60 assertion-level failures** (each test case can report multiple failed assertions):
| Suite | Failing test cases |
|---|---|
| test_dialogue_sprint20.gd | 5 |
| test_rendering.gd | 3 |
| test_input_roundtrip.gd | 3 |
| test_examine_display_sprint18.gd | 2 |
| test_client_p1.gd | 2 |
| test_time_display_sprint17.gd | 1 |
| test_monologue_display.gd | 1 |
| test_implant_app_lifecycle.gd | 1 |
| test_game_state_sprint20.gd | 1 |
| test_fog_shader.gd | 1 |
| test_e2e_connection.gd | 1 |
| test_audio_sprint13.gd | 1 |
| test_ai_dialogue_sprint26.gd | 1 |
**Delta vs sprint-36 baseline:** Sprint-36 triage recorded 22-33 failing test cases (two counts given in the original ticket — title said 22, enumeration summed to 33). Current 23 is consistent with those numbers within drift. Some suites improved:
- test_bug_report_ring_buffer (was 2, now 0) — resolved by sprint-37 #869 fix
- test_game_state (was 1, now 0) — resolved, root cause unidentified; no longer in output
- test_rendering (was 5, now 3)
- test_input_roundtrip (was 6, now 3)
- test_examine_display_sprint18 (was 3, now 2)
- test_client_p1 (was 3, now 2)
- test_e2e_connection (was 2, now 1)
No suite regressed upward. No new suites surfaced failures. The 60 assertion-level count (vs the 22-33 test-case count) is a different counting mode reported by the gdUnit4 JSON summary — the same 23 test cases can raise multiple assertion failures each.
**Umbrella scope also covers** the coverage gaps from sprint-37 #870 deletions that are not tracked by named revive tickets: test_entanglement_sprint22 (no live client behavior — Entanglement is a server-side seed concept per #178), test_sprint30 (no unique live coverage), test_session_manager_sprint19 (SessionManager API contracts — ID format, uniqueness, resume_game — none directly asserted in the new merge-path tests; tracked here rather than filing a dedicated ticket).
Failure log archived to /tmp/sr-test-full.log on dev branch sprint-36/client (timestamp 2026-04-21).', 'done', 'medium', NULL, 'client', NULL, '2026-04-21 07:29:34', '2026-05-03 09:23:46', NULL, 'af2408340ccb59c9ba20c2d1dc2f2278', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ9Q7NHVDRT6ARMCWW', 'task', NULL, 'D-210 SubBiomeVariant implementation', 'D-210 defines SubBiomeVariant for fine-grained biome classification within SettingType::Wilderness districts. Currently deferred — SettingType::Wilderness carries a plain String biome field. Implement SubBiomeVariant enum and wire it into the atlas skeleton generator when the biome taxonomy is finalized.', 'backlog', 'low', NULL, 'server', NULL, '2026-05-03 10:30:14', '2026-05-03 10:30:14', NULL, 'fa0c9f8a80c893c3db9fe374a93acd30', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXCY04WSBRF2346M78', 'task', NULL, 'Populate atlas_city_names corp HQ entries', 'Python: cross-reference corporations.headquarters_system/body to populate corp_id on atlas_city_names rows.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:38:53', '2026-05-03 14:00:25', NULL, '05203d41cf586c7c6f7f36fb2b4c4f44', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXK4THJN2B3PWA98KM', 'task', NULL, 'Add body_radius_km column to bodies table (D-204)', 'Physical radius for area_count formula. planet_class fallback for NULL values. See D-204.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:38:38', '2026-05-03 14:00:25', NULL, '0927249186f9082ecbab9976b319e05e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXN72YEPAD3AWQ7CY0', 'task', NULL, 'Pre-compute province boundaries from watershed analysis (D-205)', 'Python: extract watershed drainage basin boundaries from heightmaps, store as polylines in atlas_province_boundaries. ~2d effort.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:38:46', '2026-05-03 14:00:25', NULL, '1604e5db3bbe7d46dbd46018d49b018b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW4K5KPS81BAMN1F38', 'task', NULL, 'Add atlas_body_heightmaps table to systems-schema.sql (D-202)', 'Float32 LE BLOB storage for heightmaps. ~512KB per body. See D-202 for schema.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:38:24', '2026-05-03 14:00:25', NULL, '1fd76a3a3ff9fd16ecc1a0c1785d9ab4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYVH6DGX06M2T5ZVFG', 'task', NULL, 'Implement D8 drainage routing algorithm (D-208)', 'D8 priority-flood from heightmap. River threshold flow_accumulation > 200. Output: RiverNetwork (river_cells, confluences, mouths), drainage basins. ~50ms/body target. See D-208. ~3d effort.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:30', '2026-05-03 14:00:25', NULL, '233e1733906cfe2f9c5e4ca471c2f642', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYPSX7DHHYQ2GFKX28', 'task', NULL, 'Add atlas_province_boundaries table to systems-schema.sql (D-205)', 'Watershed boundary polylines per body. See D-205.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:38:34', '2026-05-03 14:00:25', NULL, '351f01e5fc236ab0b6ce228633337a68', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWB9G78ZWF6R4D51W0', 'task', NULL, 'Implement heightmap BLOB loader in Rust', 'Load float32 LE heightmap from atlas_body_heightmaps via SQLite. bytemuck reinterpret. ~0.5d.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:22', '2026-05-03 14:00:25', NULL, '3602e701e76198ba669ce3498efaefef', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYVE81M6P7JG20BZ0G', 'bug', NULL, 'Fix WorldTier enum values in generator.rs (D-218)', 'Replace Peripheral/Connected/Core with Epicenter/Regional/Backwater/Passage/Waypoint per D-218. Blocks all generation Rust code.', 'done', 'critical', NULL, 'server', NULL, '2026-05-01 10:38:20', '2026-05-03 14:00:25', NULL, '3bf97ea70aecf780a6c76d513d7aba45', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXYDC7HA2Y9VT5FQD4', 'task', NULL, 'Derive body_radius_km from planet_class fallback', 'Python: populate body_radius_km column. Use explicit values where available, planet_class lookup otherwise. 0.5d effort.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:38:57', '2026-05-03 14:00:25', NULL, '481649564d57b93e6c145e795388123e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW41Q268P7XSKE8A2C', 'task', NULL, 'Implement BodyWorldState resource and LRU cache (D-203)', 'Bevy Resource holding Layer 1-2 output per body. LRU cache policy, 50 bodies, ~5MB. See D-203.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:26', '2026-05-03 14:00:25', NULL, '630d276e3e09a84ff5bd29ab5a26b742', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWC5E7J4B2XRTZZHK0', 'task', NULL, 'Add atlas_city_names table to systems-schema.sql (D-207)', 'Name reservation table replacing authored city positions in markers.json. Columns: body_id, name, economic_role, population, corp_id (nullable), reserved (bool). See D-207.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:38:28', '2026-05-03 14:00:25', NULL, '71742106e8bab4e73fa2291a0b693f6f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWVCBQ6XQNJSDT9KTG', 'task', NULL, 'Implement attractor-matching pipeline (D-211, D-195)', 'Five-phase pipeline: score matrix build, Tier A greedy, Hungarian Tier B+C, synthetic overflow, name fulfillment. Two-tier mismatch flagging (0.35 warning, 0.15 error). See D-211, D-195. ~4d effort.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:35', '2026-05-03 14:00:25', NULL, '94da85249f362c19a55bbc9f70870114', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYHDC2XE89TEFCRZCG', 'task', NULL, 'Generator polish follow-ups to #853: suffix monotony auto-fix + explicit cultural-history prompting', '**Follow-up to #853 (Sprint 37).** Dudley''s #853 patches resolved 5 of 7 subtasks cleanly. Two bits remain as partial/deferred:
**§3 — Suffix monotony (partial):** `gemma_naming.py` now *detects* when >40%% of a body''s mountain names share a trailing word and emits `suffix_monotony_warning` in the counts dict, but does not auto-regenerate. Rationale (per code comment): the batch pipeline has no voice access, so auto-fix would require a targeted re-run. This ticket: either add a targeted re-run pass when the warning fires, or write a wrapper script that catches the warning and re-queries only affected bodies.
**§5 — Cultural crossmix (partial):** The existing corridor_substyles refill mechanism (next-adjacent register) provides some cross-cultural blending, but the ticket asked for explicit `corridor + cultural-history` context threaded into the naming prompt. This ticket: extend `build_batch_prompt` to accept and include a cultural-history blurb (drawn from the body''s corridor_substyles description or a new per-corridor history registry) so the LLM sees both the primary register and the secondary influences.
**Priority:** low — naming generation is functional; these are quality polish that would help after a full atlas regen surfaces visible patterns.
**Context:** see Sprint 37 #853 original spec and Paula''s analysis notes in `docs/design/atlas-generator-refinement-notes.md`. Paula''s collision script should be re-run after these land to verify measurable impact.', 'done', 'low', NULL, 'server', NULL, '2026-04-22 06:20:44', '2026-05-03 14:00:25', NULL, '95b70490cb946eb20b63b7e8d22fab5c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRX0TPK51K258877QAM', 'bug', NULL, 'Multiple baseline tests panic with Bevy Resource-does-not-exist on main', 'Discovered: 2026-04-21 during sprint 37 server work verification. Confirmed present on clean main (commit f0465e40), NOT a sprint regression.
**6 failing tests share the same Bevy runtime panic pattern:**
- t3_pause_mid_corridor_discards_movement_and_resumes (cross_room_transitions.rs)
- player_moves_north_through_full_pipeline
- proof_room_tick_10_matches_golden
- entity_collision_blocks_movement
- movement_validated_within_app
- world_boots_and_ticks
**Symptom (same across all 6):**
thread panicked: Encountered an error in system ''Enable the debug feature to see the name'': Parameter ''Enable the debug feature to see the name'' failed validation: Resource does not exist
**Likely root cause:** A Resource was removed from the app init somewhere without updating the test harness (or the test harness was never updated when a system was added). The Bevy debug feature is OFF so system names are hidden.
**Investigation steps:**
1. Enable Bevy debug feature (feature = [''debug''] on bevy_ecs dep) OR set BEVY_BACKTRACE=full — get the actual panicking system name and missing Resource.
2. Most likely one Resource missing in test harness setup fixes all 6 (they share symptom).
3. Fix: either insert the resource in the test fixture, or wrap the system param in Option<T>/If<T>.
**Why not blocking sprint 37 PRs:** these predate sprint work. Sprint 37 work actively removes 7 archetype-dependent failing tests (archetype_monologue.rs + v01_integration_playthrough.rs) — net improvement to test health. The 6 baseline failures remain, unrelated.
**Assigned:** Hoshe (QA ownership)', 'done', 'medium', NULL, 'server', NULL, '2026-04-21 17:02:53', '2026-05-03 14:00:25', NULL, 'a83190a3650bde732237e95adc429b6c', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWDVZVJ7R9EAVJJC9G', 'task', NULL, 'Populate atlas_city_names from wiki content', 'Python: read wiki settlement data, populate atlas_city_names rows. Named cities with economic_role and population. Corp HQ cross-reference from corporations table.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:38:50', '2026-05-03 14:00:25', NULL, 'acccb911d6ef327eb850fcc09e176c24', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYD4BWGGKHXSQ1M998', 'task', NULL, 'Implement SettlementClass and settlement type enums (D-196)', 'SettlementClass: NameLocked/PopulationBudget/EconomicTriggered/OrganicGrowth. Active/ghost thresholds per class. See D-196.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:10', '2026-05-03 14:00:25', NULL, 'caa598dc1da327977ac07e4cab686954', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYZGFVVQZWJDT1VCYG', 'task', NULL, 'Expand check-systems-db-stamp GENERATOR_SOURCES to cover naming helpers', '**Follow-up to PR #137 / Tyre review of #847 stamp infrastructure.**
`tooling/check-systems-db-stamp` only tracks `generate_atlas` source files. The atlas generation pipeline now has two naming helpers that feed into the final `systems.db` content but are not covered by the stamp:
- `tooling/planet-gen/gemma_naming.py` — batch-names bodies/features via Gemma LLM (#833 / #853).
- `tooling/planet-gen/naming_core.py` — shared algorithms (Levenshtein, distinctiveness, batch prompts) used by `gemma_naming.py`.
If either of these changes without re-running the generator and re-stamping, `make check-systems-db-stamp` will pass while `systems.db` drifts from its source SHAs. This is the exact failure mode `check-systems-db-stamp` exists to prevent.
**Scope:**
1. Extend `GENERATOR_SOURCES["generate_atlas"]` in `tooling/check-systems-db-stamp` to include:
- `tooling/planet-gen/gemma_naming.py`
- `tooling/planet-gen/naming_core.py`
2. If there is a parallel `IMPORT_ECONOMICS_SOURCES`-style constant in the atlas pipeline (mirror the `import_economics` pattern), mirror the update there too. Comment at line 35-37 of the stamp script notes the pattern.
3. Decide whether either naming helper warrants its own `generator_name` entry (if naming is ever run independently of the full atlas generation) — default is fold them into `generate_atlas` unless the economy-db precedent applies.
4. Re-stamp via the normal atlas regen path so the SHA captures the new file set, then verify `make check-systems-db-stamp` still says OK.
**Non-goal:** Do not modify `systems.db` content; this is a stamp-infrastructure expansion only.
**Cross-reference:** PR #137, #847 (atlas determinism smoke test, merged with the original narrow source list), `tooling/check-systems-db-stamp` line 38–48.
**Rationale:** The stamp was added specifically to catch the drift-without-regen class of bug. A narrow source list lets that bug through for naming changes, which are now a meaningful and frequent source of atlas content edits.', 'done', 'medium', NULL, 'server', NULL, '2026-04-22 07:58:45', '2026-05-03 14:00:25', NULL, 'cfe4bac21568e462067f4859473428e6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXRJH5BH1XZYN4DZE0', 'story', NULL, 'District skeleton generator — Phase 1 implementation', 'Implement actual Phase 1 generation logic for DistrictSkeleton. The data model in server/src/simulation/generator.rs is complete but all generation is stubs.
Input: a city entry from markers.json (name, lat/lon, population_tier, primary_function, planet_class, economy node reference)
Output: a filled DistrictSkeleton with real values — block grid assignments, SettingType, ComplexityTier, WorldTier, ZonePalette, DistrictLayoutMode (Grid vs Organic), reservations list, zone_palette.
RESCOPED per planet-down-cascade workshop: Stages 1-2 only (classification + block grid). Stages 3-5 deferred. Depends on WorldTier fix and city decomposition.
Scope for this ticket (Phase 1 skeleton only — no chunk-level tiles):
- Derive SettingType from primary_function + planet_class
- Derive ComplexityTier from population_tier + system WorldTier
- Assign DistrictLayoutMode (grid vs organic) from settlement_pattern
- Populate 4x4 block grid with ZoningType assignments from primary_function
- Generate multi-block reservations for high-population cities (parks, transit terminals)
- No chunk fill, no tile data, no NPC placement
Blocked by: #897 (generation cascade planning — D-records define the contract this implements), #900 (WorldTier enum fix)
Key files:
- server/src/simulation/generator.rs — DistrictSkeleton and all related types
- tooling/planet-gen/generate_atlas.py — city placement input (markers.json)
- server/data/systems.db — economy node cross-reference', 'done', 'high', NULL, 'server', NULL, '2026-04-24 10:57:11', '2026-05-03 14:00:25', NULL, 'd59d8b0e2c34053b4503e36a2da7b326', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYS4PFPZ8N2SW2KV1M', 'task', NULL, 'Implement DistrictType, PoliticalArchetype, FoundingOrientation enums (D-213, D-214, D-215)', 'All generation-pipeline enums for district classification. See D-213, D-214, D-215.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:14', '2026-05-03 14:00:25', NULL, 'd5f11b5f3156501ff97b52f4ee9f86f0', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY2912ZVY8PKFG58YM', 'task', NULL, 'Normalize economic_role values in bodies table', 'Migration: normalize inconsistent values (agriculture->agricultural, resource_extraction->extraction, coordination->service_mixed). Add validation to import_economics.py. See D-194 data quality blocker #3.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:02', '2026-05-03 14:00:25', NULL, 'de4ab761ef2067d54274d3b9713cc79b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZT1JNFQAK8XTPSSMC', 'task', NULL, 'Implement WorldTier, ComplexityTier, SettingType enums in Rust (D-218, D-201)', 'Replace String type aliases with real enums. WorldTier: Epicenter/Regional/Backwater/Passage/Waypoint. Depends on WorldTier bug fix.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:06', '2026-05-03 14:00:25', NULL, 'e0687a4130d32df6523f714fe18c00e9', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXNYREKVCGGKEFV01M', 'task', NULL, 'Add atlas_feature_names table to systems-schema.sql', 'Geographic feature name reservations (rivers, mountains, passes). Columns: body_id, name, feature_type, priority.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:38:31', '2026-05-03 14:00:25', NULL, 'ece3df838a0e8f21853c584c8e6122a4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWNSD8GZKVG1RCBS4W', 'task', NULL, 'Import heightmap BLOBs into atlas_body_heightmaps', 'Python pipeline step: read heightmap PNGs from planet-gen output, convert to float32 LE BLOBs, insert into atlas_body_heightmaps. Part of generate_regional.py or standalone importer.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:38:42', '2026-05-03 14:00:25', NULL, 'f4e7a579e278165ea1462bac9070f515', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXJNA7QBYRTW11Z7CW', 'task', NULL, 'Implement three-component district mix (D-194)', 'Population tier guarantees + 10x9 multiplier table + political archetype modifiers + founding age character. Self-contained per city. See D-194. ~3d effort.', 'done', 'high', NULL, 'server', NULL, '2026-05-01 10:39:39', '2026-05-03 14:00:26', NULL, '037a473f0ea8fd02b9dd9b108810e99b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXRNJQH5CCWPJRV30M', 'task', NULL, 'Implement SystemNameIndex with Aho-Corasick text scanning (D-206)', 'Aho-Corasick automaton over all body/system names from systems.db. Scans news ticker, NPC dialogue, documents to trigger background pre-generation before player travels. Part of the background generation infrastructure (Tier 5, after #924). ~1d effort.', 'done', 'low', NULL, 'server', 'D-206', '2026-05-02 07:46:35', '2026-05-03 14:00:26', NULL, '242cfd92daffcdf55a26f8551b6ce9a4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXXSC85WGGC94C452M', 'task', NULL, 'Implement GeographicAttractor, AttractorType, CompatibilityMatrix types (D-211, D-195)', 'Rust type definitions for the attractor-matching pipeline. GeographicAttractor struct, AttractorType enum (7 variants), CompatibilityMatrix (10x7 scoring table from D-195). Prerequisite types for #919 (attractor-matching pipeline). ~0.5d effort, Tier 3.', 'done', 'medium', NULL, 'server', 'D-211', '2026-05-02 07:46:25', '2026-05-03 14:00:26', NULL, '7ae4df25dde1285b84c04e8c9ecc3905', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY6Q1B8ADW41ZMYARM', 'task', NULL, 'Implement background generation queue (D-206)', 'Rayon thread pool, priority queue (Immediate/High/Medium/Low), Aho-Corasick SystemNameIndex for event-driven pre-generation. See D-206.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:53', '2026-05-03 14:00:26', NULL, '8c885d77c9d06accb79650eaa2a65e7d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY8YKG6MYGRRRQYHH4', 'task', NULL, 'Implement TerritorialStatus derivation (D-212)', 'Priority-ordered classification per Province. 6 values. placed_at_generation flag. See D-212.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:42', '2026-05-03 14:00:26', NULL, 'c5d217c64e26a07f38cfd6aa8c0ff731', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWJP1EK9KN6F0YCWRC', 'task', NULL, 'Implement BlockIrregularity from founding_age (D-216)', 'Age-based block grid irregularity. Archetype-specific step values. Minimum 0.05. See D-216.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:45', '2026-05-03 14:00:26', NULL, 'd22e070684eb925aa31844509ce4fa80', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXWNCFHAQF2WQRFTAG', 'task', NULL, 'Implement tile condition thresholds (D-217)', 'Prosperity-driven tile condition: Intact>0.63, Worn 0.43-0.63, Cracked 0.23-0.43, Broken<0.23. Threshold-crossing cache invalidation. See D-217.', 'done', 'medium', NULL, 'server', NULL, '2026-05-01 10:39:49', '2026-05-03 14:00:26', NULL, 'd424c82637bd901199ef49122f0a6929', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZJPKKA5H11G5YNJ4R', 'task', NULL, 'Render province boundaries on planetary map (D-205)', 'Load atlas_province_boundaries polylines from systems.db and render on the Godot planetary map. Fill provinces with TerritorialStatus-derived colors. Depends on #907 (province boundary pre-computation in Python). Tier 5, ~2d effort.', 'done', 'low', NULL, 'client', 'D-205', '2026-05-02 07:46:40', '2026-05-03 14:12:45', NULL, 'e0ee45d67cbc093c818e220b3e0cfc87', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW2ZSPJ3D9NEZ6JBW8', 'story', NULL, 'Atlas data delivery — replace client-side file reads with server API calls', 'The client currently reads markers.json and star_map_data.json directly from the filesystem. This bypasses the server layer entirely — the client should request all game data through IPC calls to the Rust backend.
Implementation can be simple: the server exposes a query/proxy endpoint that reads the JSON file or runs a DB query and returns the result. The client asks, the server responds. No complex protocol needed — just the boundary enforcement that the client never reads files or DB directly.
Affects: atlas app (reach, system, planet, regional screens), economics monitor. The server-side handler can start as a thin proxy (read file, return contents) and evolve into perception-filtered queries (D-012) later.', 'backlog', 'medium', NULL, 'client', NULL, '2026-05-03 14:14:23', '2026-05-03 14:14:23', NULL, 'bb3c96c0f068f53bbb22995b5f465bf4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXBP82ANSVAFMACCHW', 'epic', '06FB0TNSRZE4CSS8YR0FCEXMK8', 'Phase 1: Wiki content complete — all planets, moons, stations, heightmaps, artwork', 'Deliverable: Implant-ready Godot map of the Reach with click-throughs + wiki/GTTR popups for all systems/planets/moons/stations. Includes: body catalog (epic #738), galaxy map navigator UI, system orbital schematic, heightmap pipeline, planetary shader screenshots, GTTR per-body entries. Schema + tooling already built (atlas CLI, bodies/stations tables, sync-wiki).', 'done', 'critical', NULL, NULL, NULL, '2026-03-25 13:29:07', '2026-05-22 16:56:36', NULL, '939e09e5fe0f2570fdd79db4daa7d4b6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY23YPP1Q0TYJ816HW', 'epic', '06FB0TNSRZE4CSS8YR0FCEXMK8', 'Phase 2: Economics layer — supply/demand, transport, corporations, supply chains', 'Deliverable: Economics spreadsheets/graphs showing simulation running in fast-forward with runtime-tweakable values. X4-style production chains adapted to 300-system scale. Hundreds of corporations with supply chains. Reusable as in-game economic news implant components. Depends on Phase 1 (wiki content provides the economic profiles the simulation reads).', 'done', 'high', NULL, NULL, NULL, '2026-03-25 13:29:24', '2026-05-22 17:11:16', NULL, '027eb6fc96d03ac80f60816d4e558cb2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ8Z6NN74YKCCRCPMR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Generation validation pass + Phase 5 dependency gate', 'Validation pass: generated content reads correctly (settlements legible, walkable, named). Establishes the dependency gate that locks Phase 5 (player control, epic #749) behind Phase 4 completion per the cascade rule (D-166 amended 2026-05-22).', 'backlog', 'medium', NULL, 'server', 'D-166', '2026-05-22 17:42:34', '2026-05-22 17:42:34', NULL, '7c1e4644e95572686e56d371698fe0e6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZKXHAYMT7Q6WW3XDC', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 5 — Chunk→Tile fill (floors, walls, doors, objects)', 'THE primary gap (generator.rs is stubbed). Generate walkable tiles from the block/chunk skeleton: floors, walls, doors, placed objects, using tile-condition thresholds 0.63/0.43/0.23 (D-217). Output is the walkable world data.
Refinement (round 2, 2026-05-25): Layer-5 fill is the D-230 on-demand derive phase (<5ms shell derivation Void/Wall/FloorSlab/Roof from the cached tags) plus the BuildingExteriorTag visual grammar and material vocabulary (D-235: WallMaterial/RoofForm/FacadeRhythm/StreetSurface, template-filtered (era = maintenance/wear, not a material gate)) plus interstitial and operations-surface fill driven by BulkClass coverage (D-233). Blocked by 957, 976, 977.
Refinement (2026-05-26): READY behind 1006/957/976/977/1005. shell_derive = rectangle-containment + z-range over cached tags (~0.5ms/chunk). FillChunk pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Wire chunk_streaming.rs to enqueue FillChunk on load-radius entry (no GenerationQueue interaction today). Interstitial token derived from setback_tier (D-235) x position; bulk operations-surface tagged built-infra, not open space. Child 988 = exterior grammar, now 3-step template-filtered (era is wear, not a material gate). Files: gen_queue.rs FillChunk arm, workers/stubs.rs, plugin.rs SkeletonGenerated handler.', 'backlog', 'high', NULL, 'server', 'D-217', '2026-05-22 17:42:34', '2026-05-22 17:42:34', NULL, '8636bae8d7812ee36cd47485270f837e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXDCJS4P9Z5JQ1DAAM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Asset pipeline + catalog (walls, doors, props; rural & station variants)', 'Parallel asset pipeline producing the walls/doors/props/lamp-posts/etc. the tile fill places, with rural and station variants, tracked in a production-status catalog. Runs alongside generation.', 'backlog', 'medium', NULL, 'visual', 'D-166', '2026-05-22 17:42:34', '2026-05-22 17:42:34', NULL, 'c8a3f14cb0c76f54acc321de853f0df3', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWFFQWH38616CABT04', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Markers → names-only flavored pool + remove 6 hand-authored template machinery (D-223)', 'Implement D-223. (1) Reduce ALL markers.json to a flavored name pool — names only (river/mountain/settlement names), no positions or topographic geometry. River/mountain positions derive from heightmap + D8 drainage (D-208); settlement positions from the economic sim + placement (D-211). (2) Remove the 6 hand-authored template machinery: delete hand-authored marker positioning for Lendel/Edict/Vuurkloof/Røros/Cairnside/Estrade, drop the reserved=1 pinning, and remove all ''template'' special-casing in the atlas pipeline (generate_atlas.py, gemma_naming.py, test_batch_naming.py, apply_name_fixes.py) and the atlas_city_names schema. The 6 BODIES remain real places — keep their wiki/lore/names/economic profiles; only the authored machinery is removed. After this there is no template category. (3) Update atlas pipeline + schema, regen-db. (4) Leave historical archives (sprints, workshops, audits, CHANGELOG, atlas proposals) untouched. Foundation/gating item for the rest of Phase 4.
## Rework plan (verified 2026-05-22 via pipeline map)
Break + geometry-re-add risk is ENTIRELY in generate_atlas.py; import_economics.py is names-only-safe (reads names/DB only; populates atlas_city_names from cities name/kind/population).
1. generate_atlas.py: RETIRE geometry production. process_body() must stop calling place_cities/generate_infrastructure/place_gate_terminal and stop writing geometry to markers (else it re-adds stripped geometry). load_markers(): drop grid validation. sync_markers_to_db(): stop reading center/path/grid/area_fraction.
2. Atlas geometry tables — empty/drop via MIGRATION_SQL: atlas_cities(center), atlas_roads/rivers/railroads(point_count), atlas_pois/oceans(center), atlas_mountain_ranges(geometry), atlas_body_grids(w/h). SURVIVE: atlas_city_names, atlas_feature_names, atlas_province_boundaries, atlas_city_positions, atlas_body_heightmaps.
3. Meta-stamp: update tooling/check-systems-db-stamp GENERATOR_SOURCES + Makefile regen-db if generate_atlas is reduced/retired.
4. reserved/template machinery: apply_name_fixes.py template patches, gemma_naming.py template-name preservation, import_economics reserved=1 corp-HQ insertion — review per D-223 (name-protection reserved may stay; position pinning already gone via strip).
5. Sol: tooling/planet-gen/sol_markers/*.json + sol_import.py — migrate to names-only (separate, not in regen-db critical path).
6. Verify: make regen-db green; atlas geometry tables empty (reveals the gap for the server cascade).', 'done', 'high', NULL, 'server', 'D-223', '2026-05-22 17:37:25', '2026-05-22 21:30:01', NULL, 'd8e039a56df41233cc797a4b1e796fa9', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZZ5XKET93HPHKQHKW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Phase-4 test hardening — deferred review gaps (#953/#963 review)', 'Follow-ups from the Hoshe/Tyre pre-bake review (the BLOCKER/MAJOR items were fixed inline; these are the deferred MINOR/coverage items):
TESTS:
- features.rs: full per-type attractor reachability fixtures — craft heightmaps that guarantee LakeShore (enclosed depression), PassEntrance (saddle), PlainCenter (flat), RiverCrossing (confluence). Currently only RiverMouth/CoastalAccess/ValleyFloor are exercised on the slope/sine fixtures.
- heightmap.rs: 8-bit grayscale decode path; sea_level passthrough assertion; downsample identity/upsample early-return.
- drainage.rs: basin area_pct bit-for-bit determinism (only river_cells + basin count checked today).
- layer1.rs: attach_feature_names mountain branch with a non-empty mountain pool + ≥1 Alpine attractor.
- features.rs: thin_by_spacing behavior (spacing collisions, equirectangular column wrap).
- import_economics.py: automated idempotency test — two runs on a scratch DB yield identical atlas_city_names count (not doubled), and 0 rows for system ''GJ 0'' (Sol exemption). Wire alongside check-systems-db / pre-push.
- Wire test_sim_determinism.py into pre-push or a make target.
MINOR CODE NITS:
- drainage.rs ~480-488: comment the intentional isolated-basin fallback divergence from the old merge-to-basin-0 behavior (Tyre N1).
- planet_simulation.py ~787-788: oasis_water binary_dilation iterations are fixed-pixel — scale by GRID_W/512 for biome-display consistency at 1024 (cosmetic, Atlas only, not cascade; Tyre N2).', 'backlog', 'medium', NULL, 'server', NULL, '2026-05-23 06:09:45', '2026-05-23 06:09:45', NULL, 'e29f5ce66f23aaf1b6439e74789cde9b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZHWCPTQ6EZ2Y1SXA0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 0 — canonical 16-bit heightmap.png (2048×1024) + rename relief→reliefmap (D-202)', 'Replace the DB heightmap-BLOB model (D-202) with a per-body file-based canonical elevation, and fix the misleading naming.
(1) RENAME the existing color hypsometric ''heightmap.png'' → ''reliefmap.png'' (display only, keep 1024×512) across all ~2398 bodies. It is a relief render, not a heightmap.
(2) BAKE a new canonical ''heightmap.png'' = 16-bit GRAYSCALE elevation at 2048×1024, from planet_simulation at higher native resolution (value-noise is resolution-independent; rescale gaussian sigma so mountains keep physical size). Single source of truth for elevation — deterministic, viewable, ~0.5-1.5MB/body PNG-compressed. 2048×1024 chosen to bound install size (sub-pixel detail is synthesized procedurally by lower cascade layers anyway).
(3) Rust heightmap.rs: load the 16-bit PNG from the body''s terrain_reference path (add ''png'' crate dep), normalize to f32 [0,1]. Layer 1 (continental drainage/basins/mountain-ranges) DOWNSAMPLES to ~512×256 (keeps the ~45ms benchmark); lower layers (region/block/tile) sample full-res for local detail. Decouples cost from resolution.
(4) DROP atlas_body_heightmaps table + the DB-BLOB write path in import_heightmaps.py (the PNG is now the store).
(5) Update terrain_reference convention, populate_terrain_reference.py, the Atlas client heightmap loader, and AMEND D-202.
The asset bake + 2398-file rename (1,2) run where numpy/scipy are available; the Rust loader + decision (3,5) land independently in ''rust country''. Foundation for #953 (Layer-1 elevation input) and all lower cascade layers.', 'done', 'high', NULL, 'server', 'D-202', '2026-05-22 22:29:30', '2026-05-23 06:44:16', NULL, '15f2490083b86d97cc7679ca9e7a53c4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY2Y8HR0SWX9BSW7MW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rework or replace pre-push clerk review — non-exhaustive + token-burn', 'Clerk was DISABLED 2026-05-23 in .config/hooks/pre-push (SR_RUN_CLERK=1 to force-run) because it was net-negative in practice during the #963 push:
PROBLEMS:
1. Non-exhaustive / non-deterministic: a single run reports ~the first concrete contradiction it finds and stops. Across one push, three DISTINCT real D-202 inconsistencies surfaced only on the 1st, 2nd, and 3rd re-push (D-200/D-201 contradiction, stale 2048×1024 docstring, sea_level self-contradiction) — each run missing the others. So an APPROVED verdict cannot be trusted as ''clean''.
2. Token burn: re-reviews the entire origin/main..HEAD range on every push attempt; no per-commit verdict cache, so a one-line fix + re-push re-runs ~20 agent reviews.
REWORK DIRECTIONS (from the session discussion):
- Exhaustive enumeration: prompt must list ALL contradictions in a pass, not stop at the first; bias toward completeness.
- Holistic D-record check: review the cumulative decisions/ diff for internal + cross-record consistency (consistency is a global property the per-commit model approximates badly — e.g. line 928 vs 930 within D-202).
- Verdict cache: gitignored SHA->verdict log; reuse cached verdict only when both the commit AND the decisions/ state are unchanged (invalidate on any decisions/ change in range, since that can flip an earlier commit''s verdict — exactly what happened with the 2048 docstring once D-202 changed).
- Move the skip mechanism into the cache (mark a SHA as skip) and REMOVE the ''Clerk-Skip:'' commit-message trailer (no magic values in commit messages).
- Consider: is per-commit the right granularity at all, vs one holistic range review?
Until reworked, decision-record consistency is the author''s responsibility (manual audit).', 'backlog', 'medium', NULL, 'meta', NULL, '2026-05-23 07:49:02', '2026-05-23 07:49:02', NULL, 'f4e203a52313264d1e9988dcefd698f9', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWVJCSHJ28MP9PSW18', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rust dependency maintenance — rand advisory + freshness + serde_yaml', 'From a security/freshness review (2026-05-23, cargo audit + cargo update --dry-run):
SECURITY: No CVEs/vulnerabilities (cargo audit clean across 214 deps; cargo deny advisories ok). One advisory:
- rand 0.9.2 RUSTSEC-2026-0097 — ''unsound with a custom logger using rand::rng()''. WARNING-level (unsound, not an exploitable vuln), already allow-listed in deny.toml. Fix available: rand 0.9.4 (semver-compatible). Bump rand → 0.9.4 to clear it (rand 0.10.1 is a major, separate).
FRESHNESS: ~70 semver-compatible updates available (all routine patch/minor): bevy_ecs/app/tasks 0.18.0→0.18.1, clap 4.5→4.6, rayon 1.11→1.12, pathfinding 4.14→4.15, uuid 1.20→1.23, serde_json, tracing-subscriber, zerocopy, etc. A (lockfile-only) applies them; do as its own commit + cargo test.
MAINTENANCE: serde_yaml 0.9 is deprecated/archived upstream (no advisory, but unmaintained). Plan a migration (serde_norway / serde_yml) eventually — it parses body-def frontmatter + agent files.
Do as a focused dep-maintenance pass (not mixed into feature work): (1) rand 0.9.4 bump [security], (2) compatible-update sweep [freshness], (3) serde_yaml migration [maintenance, larger]. Re-run cargo audit + cargo test after each.', 'done', 'low', NULL, 'server', NULL, '2026-05-23 08:34:30', '2026-05-23 09:03:54', NULL, '38f83f416cdda3e15c8575bcd7c69e35', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWWKYN8PJC178XBRSG', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'clippy-1.93 test-code debt + pre-push --all-targets gap', 'Surfaced 2026-05-23 when a warm server/target/ made the pre-push hook actually run clippy (it skips on cold worktrees). Two parts:
1. HOOK GAP: .config/hooks/pre-push runs ''cargo clippy -- -D warnings'' WITHOUT --all-targets, so it only lints lib+bins (non-test) — test and example targets are never clippy-checked. That''s why test-code clippy debt accumulated unflagged. Decide: add --all-targets to the hook (after cleaning the debt below), or leave tests unchecked.
2. TEST-CODE clippy-1.93 debt (NOT auto-fixable; needs judgment), all in cfg(test) modules / tests/ — does NOT block the current hook:
- disallowed_types HashSet/HashMap → BTreeSet/BTreeMap: tests/shadowcast_bench.rs (×8), tests/news_ticker.rs (HashSet + HashMap), src/npc/mood.rs (cfg(test)), src/simulation/sound.rs (check if cfg(test) or lib — verify SoundEventKind: Ord before swap).
- field_reassign_with_default: src/npc/disclosure.rs, src/simulation/monologue.rs (×2), src/simulation/save_io.rs (test setup) → struct-init.
- assertions_on_constants: src/simulation/listening.rs, tests/cross_room_transitions.rs (intentional invariant asserts → const block or scoped #[allow] with comment).
- approx_constant: src/settings/types.rs Float(3.14) — DELIBERATE non-pi value → scoped #[allow], do NOT replace with PI.
- drop_non_drop: src/npc/vision.rs drop(Mut<WalkabilityMap>) — verify if it''s an intentional early borrow-release (#[allow] + comment) or removable.
- unnecessary_get_then_check: tests/information_boundaries.rs (×3) → contains_key.
- cloned_ref_to_slice_refs: tests/triangle_validation.rs → std::slice::from_ref.
- doc_overindented_list_items: tests/news_ticker.rs.
- unused_must_use: src/simulation/input.rs (cfg(test)) → let _ = .
Do as one focused clippy-1.93 sweep + cargo test. Machine-applicable ones already landed in the prior commit.', 'done', 'low', NULL, 'server', NULL, '2026-05-23 08:37:58', '2026-05-23 09:03:54', NULL, 'ce9ef3b6529697da28989e898b0da129', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWN22SX82ZTBDWSPY4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Layer 1 — empty-world topography (heightmap → drainage → feature tags → sub-biome)', 'Audit + wire the existing atlas modules into a reproducible Layer-1 pipeline: heightmap BLOB load (D-202) → D8 priority-flood drainage (D-208) → 7 geographic feature tags (D-209) → sub-biome variants + terrain_modification_cost (D-210). Attach river names from the markers name pool (D-223) to the largest computed rivers; mountains take names from the pool (positions given by the fixed heightmap).', 'done', 'high', NULL, 'server', 'D-208', '2026-05-22 17:42:34', '2026-05-23 09:08:20', NULL, '471e12a02690a414f06c0e83ffb7fbbe', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZ947KMQDDRX9RSH2M', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Deterministic seed→output harness + golden-seed regression test', 'End-to-end determinism guarantee: same seed → byte-identical generation output across the whole cascade. Golden-seed regression test that runs the pipeline and diffs against a stored fixture. The load-bearing property — every layer must be reproducible (D-010 principle 4). rng.rs exists; wire it through and lock it with a test.
---
Refinement (2026-05-23, /whats-next):
- SCOPE: build an extensible per-layer harness interface (run_cascade(seed, layer_range) -> snapshot) as the primary deliverable; Layer 0->1 golden test is the first instantiation. Layers #954-959 plug in as they land.
- FIXTURE: two golden files under server/tests/golden/ following the UPDATE_GOLDEN=1 pattern -- SHA-256 of heightmap.png bytes (Layer 0) + msgpack of Layer1Output (Layer 1). NOT systems.db (explicitly non-byte-deterministic per asset-pipeline rule / D-202).
- SEEDCHAIN IN SCOPE: implement a real SeedChain type (master world seed -> per-body -> per-district derivation) and thread it through atlas callers (skeleton_gen.rs and generator.rs currently pass a plain seed:u64 with manual wrapping_add). Harness verifies cascade(world_seed=42) derives all sub-seeds deterministically. Replaces the doc-comment-only SeedChain references.
- COUPLING: Q-098 (Layer-1 output persistence vs recompute) is tied to #952; resolve or reference during implementation.
- REUSE: AtlasRng (server/src/atlas/rng.rs), SimRng (server/src/simulation/rng.rs), test_sim_determinism.py (Layer-0 Python guard), drainage.rs inline determinism tests, golden suite (make golden-diff/golden-update).', 'done', 'high', NULL, 'server', 'D-200', '2026-05-22 17:42:34', '2026-05-23 10:26:23', NULL, 'f25f41423fdae8d069b210067267852a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW31YK22RGJ5WQB8Z0', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Activate AnalyzeBody cascade execution + BodyWorldStateCache populate (D-225)', 'Tyre''s #960 design long pole (D-225). gen_queue.rs::run_work_item''s AnalyzeBody is a documented stub (deferred from #142): it returns BodyAnalyzed without running the cascade or populating BodyWorldStateCache. Activate it: on the Rayon background tier, resolve the body heightmap, run run_cascade(Topography) (~45ms), build BodyWorldState from Layer1Output, and deliver the computed state so the main thread inserts it into the D-203 LRU. GenCompletion::BodyAnalyzed must carry the computed BodyWorldState (or a results side-channel), not just body_id (Tyre R3). MUST run off the tick thread (D-203 — no blocking CPU on main). Unblocks the atlas layer-stream proxy (D-225).
---
Scope (Tyre, 2026-05-23): #968 owns the full connect-the-queue-to-the-app loop — GenerationQueue (#924) + BodyWorldStateCache (#917) are built but referenced NOWHERE in the running app (inert tier), so this is one closed loop, not separable. (1) un-stub run_work_item AnalyzeBody to run run_cascade(Topography) and map CascadeSnapshot -> BodyWorldState (new constructor); (2) GenCompletion::BodyAnalyzed carries the computed BodyWorldState, not just body_id (was Tyre Risk 3); (3) register GenerationQueue + BodyWorldStateCache as resources (a GenerationPlugin or fold into an atlas plugin) + a Bevy drain system (PreInput per D-206) draining completions -> cache.insert(state); (4) acceptance: submit AnalyzeBody with a test heightmap path -> drain -> cache populated with non-empty Layer-1 data. BOUNDARY: AnalyzeBody carries resolved inputs { body_id, heightmap_path: PathBuf, sea_level: f32, body_seed: SeedChain } — the enqueuer resolves (mod-first BodySourceResolver + SeedDomain::Body derivation are #969/policy); run_work_item is pure compute (inputs -> BodyWorldState), no FS/DB access. #969 proxy is the production submitter. Do NOT split the drain wiring into a middle ticket.', 'done', 'high', NULL, 'server', 'D-225', '2026-05-23 12:23:55', '2026-05-24 08:50:20', NULL, '6036a0872de37019d2bf47bcf3002d4a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYEJ0G4F0SBHG379YR', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Auto-pause sim on implant-fullscreen (inspection substrate)', 'D-226 layer 1 (substrate). Auto-pause the sim when the client enters a fullscreen implant app (the Atlas). Freeze the world-advancing tick phases (Movement/Simulation/Economy/Storyteller/Knowledge/TickAdvance) via run-conditions keyed to a pause command, but KEEP PreInput (gen-drain), Input (receive resume), Snapshot, PostSnapshot (bridge) alive so the UI keeps fetching data while frozen. Reuse the existing TickRate::Paused + the input.rs paused-allowlist seam; trigger from HudGroups::gameplay_occluded (D-170) -> a pause/resume command over the bridge. For static geography pause is a compute-saver; for dynamic state (economics, later) it is essential. Confirm exactly what TickRate::Paused currently gates (whole loop vs sim-advance) and gate at the phase level.', 'backlog', 'high', NULL, 'server', 'D-226', '2026-05-24 09:22:33', '2026-05-24 09:22:33', NULL, '40c7512deeed7c48e257258cfa1460d7', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWTP633SM8XB302MBM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas agent control channel — observe/act named-intent navigation (D-226)', 'D-226 layer 4 (apex). A client-side AtlasAgentInterface exposing a JSON control channel so an automated agent (terminal/curl, headless Godot) can navigate the REAL Atlas UI: observe -> JSON of (a) current data state shown and (b) a walkable UI affordance tree (toggle controls: id/label/state/locked); act -> named SEMANTIC intents (select_system/open_system/select_body/open_regional/select_city/set_overlay/reset_view/back/close_atlas) backed by the same handlers a click calls (the map uses _gui_input, NOT pixel-clicks), plus get_layer_data_summary (attractor/river/basin counts as JSON without rendering). Single AtlasAgentInterface autoload/static class for a stable contract; generic Control-tree walk so economics/saves plug in later. Turns human-eyeball review into an agent QA sweep. Depends on #960 (the viewer) + the auto-pause substrate. Screenshot capture is a separate ticket (kept off the critical path).', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-05-24 09:22:38', '2026-05-24 09:22:38', NULL, 'bbc8b47323d40fefaf53c54b57755160', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZHH3XBGFRZYJ393FG', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Agent interactive screenshot capture — reuse run-visual primitive (D-226)', 'D-226 layer 5 (capture). A capture_layer_screenshot affordance on the agent channel: set the requested overlays active, clear hover state, render the canvas, return a PNG path. REUSE the existing capture primitive (tests/run-visual + the frame-delayed save_png pattern proven by character_creation.gd standalone capture) — do NOT build a new headless rasterizer. Complements the existing scenario/flow visual-golden regression (that batch/regression role is unchanged); this adds INTERACTIVE agent-driven capture. Isolated from the agent channel (#agent-channel) so the headless-render wrinkle can''t gate the navigation apex. Depends on the agent control channel.', 'backlog', 'medium', NULL, 'client', 'D-226', '2026-05-24 09:22:41', '2026-05-24 09:22:41', NULL, 'b2e500aca5a3a5a4de91859b07b27c52', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXQH2YH9MPZV004C18', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Atlas layer-stream proxy + bridge protocol (D-225)', 'D-225 layer-stream proxy: server-side delivery of per-body Layer1Output to the Godot client, mod-first, compute-on-demand, no bake. NEW: BodySourceResolver (server/src/atlas/source_resolver.rs) resolves body_id -> heightmap.png across mod dirs + base install (terrain_reference from systems.db, read-only); v1 wires base root only but the resolver + search-order exist and are tested with a synthetic mod root. layer_proxy.rs: cache hit serializes; miss enqueues Immediate AnalyzeBody + replies Pending, pushes response on completion. BRIDGE: additive message tag on the EXISTING IPC stream (no second socket, no envelope-everywhere rewrite) — AtlasLayerRequest{body_id, up_to_layer} / AtlasLayerResponse{status, layer1} as new MessagePack message types disambiguated structurally in v1; single framed response (not chunked); raster NOT streamed (client loads reliefmap.png from disk); whole Layer1Output per response (client composites additive overlays). Depends on #968 (AnalyzeBody activation). See D-225.
---
Part of the D-226 inspection harness (data-path, layer 2): the proxy is what the Atlas viewer (#960) and the agent channel (#971) read. See D-226 for the full stack: live-pause substrate (#970) -> proxy (this) -> viewer (#960) -> agent channel (#971) -> capture (#972).', 'done', 'high', NULL, 'server', 'D-225', '2026-05-23 12:24:02', '2026-05-24 16:07:21', NULL, 'cc94ed7b89238dd90e51fdf1c9fcecf1', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYEM4VSX74MS4NKQ1C', 'bug', NULL, 'Client test suite — pre-existing failures under bare run-godot', 'Surfaced 2026-05-24 running the full client suite (tests/run-godot, no server) during #960. NOT caused by #960 — verified: test_input_roundtrip fails identically at the pre-change baseline, and the rest are unrelated subsystems. Two categories to triage:
CATEGORY 1 — server-dependent (expected-fail under bare run-godot): test_e2e_connection (test_send_input_receive_snapshot), test_input_roundtrip (movement/interact/move_then_interact). They error with ''Nil snapshot'' / ''.tick on Nil'' because no sim server is spawned. They need the run-ipc-* harness or SR_LIVE=1. FIX OPTIONS: gate/skip these under bare run-godot (mark as requiring a server) OR have run-godot spawn the server for the e2e tier, so tests/run-godot
{"suite":"godot","total":2522,"passed":2482,"failed":40,"duration_ms":96836,"log":"/tmp/sr-run-godot.1737738.log"} is green without false negatives.
CATEGORY 2 — genuine logic failures in subsystems unrelated to current work: test_audio_sprint13 (test_d067_chime_duration_spec_is_300_to_400ms, line 547 ''Expecting true but is false''), test_fog_sprint22 (exploration_data_preserved_across_resize), test_fog_shader (visibility_texture_update_performance), test_dialogue_sprint18/20 (signal/audio-dip), test_monologue_display (queue_never_exceeds_max_depth), test_implant_app_lifecycle (on_insert_deactivated_closes_insert_app), test_ai_dialogue_sprint26 (settings_dialog warning label). Triage each: fix or remove/xfail with a reason.
Goal: a green (or explicitly-gated) bare `make test-client` so client regressions are detectable. Until then, run targeted suites (run-godot --filter <stem>).', 'backlog', 'medium', NULL, 'client', NULL, '2026-05-24 16:49:54', '2026-05-24 16:49:54', NULL, 'a49cafffca41636bc19123c1de5e1513', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWMZBFV1V6F79VW8S4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Architecture-flavor pipeline — wiki to generator cultural grammar (D-232)', 'Architecture-domain completion of D-167. (a) Gemma architecture-flavor methodology doc: stage-1 register selection + stage-2 structured output {tag, allow:[ObjectTag], block:[ObjectTag], weight, trauma_decay_rate}, analogous to the D-223 naming methodology. (b) trait_templates catalog + atlas_body_trait_bias bake (see round-3 note) (GTTR+index.md). (c) allow/block weighted filter applied at chunk fill over the D-142 zone-type ObjectTag pool (dominant wins categorical; weighted-avg continuous). (d) ObjectTag vocabulary authoring (Miri+Araminta co-maintenance). Three-layer resolution body x district x era-band (era-band depth = Q-106). NOTE: source-authoring location rides on Q-107 (content consolidation); the table+filter are buildable independently. Formally retires D-104/D-105/D-101-axisA/D-107. Flavor stays purely wiki-authored; economics (D-233) is a parallel channel composing only at the filter.
Round 3 (2026-05-25): D-232 rewritten to the TRAIT-TEMPLATE model (not per-body Gemma arrays). Build: (1) trait_templates table = shared holistic catalog (~35 floor / 40-45 target); two-tier eligibility = integer-bps hard gates (bulk_class/prosperity/ubiquity) + weight mods (role/faction/age/corridor/morphology); corridor (geographic_sector) is a WEIGHT, never a gate. (2) atlas_body_trait_bias (sparse, ~30-40 hero bodies: pin/boost/suppress); RETIRE atlas_body_culture + atlas_body_culture_era. (3) Three-phase draw: body vocabulary K-draw (K=5/3/1/0 by complexity_tier, SeedChain D-224, coverage-aware, pin counts toward K) -> district-dominant by zone_affinity (one template/district, applied whole) -> within-template seed picks. (4) Deviation/swerve system + (5) visual fallback hierarchy = separate tasks. CI guardrails (Nigel): eligible pool >=5 post-gate, no template >60pct weight, bias per-body, catalog grows with corpus. Result trait_selection: Vec<String> on skeleton, re-derivable. Channel-separate from D-233 (what vs how). Era = maintenance/wear (D-217), NOT a tech ladder. Source location -> Q-107.
Scope note (2026-05-26): the catalog CONTENT authoring is split out to story #1005 (blocking). 977 is now the MECHANISM only — tables, three-phase draw, district-dominant, deviation system, fallback hierarchy. #1005 (the ~35-template content) blocks the draw/fill/visual tasks.
Refinement (2026-05-26): READY behind 1006/1005. Mechanism only. trait_templates + atlas_body_trait_bias DDL into import_economics.py MIGRATION_SQL + systems-schema.sql, integer-bps weights throughout. draw_body_vocabulary() inside the GenerateSkeleton Rayon task: hard-gate filter -> weight (mods x bias) -> SeedChain weighted draw of K (5/3/1/0 by ComplexityTier) -> trait_selection on skeleton; pin counts toward K; coverage-aware (>=1 template per present district type, else sparsity escape-hatch). pick_district_dominant() at the FillChunk head (zone_affinity-weighted). CI guardrails in importer validation (pool>=5, weight<=60pct, bias per-body). Swerve (1003) + fallback (1004) are child tasks. SeedChain complete, no change. Files: import_economics.py, generator.rs.
--- From PR #148 review (Tyre): the trait_templates.geographic_sector column is a pool-narrowing hint for baseline/heritage sub-pools and a SOFT weight (never a gate); it composes with weight_mods.geographic_sector. The draw must join both, not treat the column as a gate. (D-232 corridor = two-part pool.)', 'backlog', 'high', NULL, 'server', 'D-232', '2026-05-25 19:08:55', '2026-05-25 19:08:55', NULL, 'a0be06c9179573432230fa6f462f02e7', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWABYYB11QMJDTXE2G', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Guarantee-audit engine over step-3 tags (D-097)', 'Implement the guarantee-audit engine per the D-097 amendment. Runs after step-3 fill completes for a district; on a failed mandatory check for the district world_tier, fill regenerates with adjusted params. run_guarantee_audit(skeleton, building_tags, street_graph, seed) -> GuaranteeAuditResult. Tier-1 (all inhabited): social_hub_present, informal_zone_present, encounter_corridor. Tier-2 (full-complexity): traffic_chokepoint, institutional_space, insider_space, economic_node, breach_only_zone. Tier-3 A-1..A-4 assassin-lens: elevated_vantage (above_ground>=3 + LOS to chokepoint), egress_multiplicity (>=2 unique street connections), temporal_opacity (>=1 TemporalWindow door), non_institutional_route (Public->insider path avoiding institutional). All pure graph/geometry over building_tags + street_graph -- NO interior generation. Depends on 957 (tag schema) + the street-geometry story (D-234).
Refinement (2026-05-26): READY behind 1006/976. GuaranteeAuditResult struct replaces the String alias (14 bools). Signature run_guarantee_audit(skeleton, building_tags, street_graph, seed). Mandatory-check gate keys on ComplexityTier (T1 all-inhabited; T2/T3 Full) -- D-097 wording fixed. Each check is a graph/geometry predicate over tags + the MST arterial graph: chokepoint = articulation point; encounter_corridor = Public-node BFS crossing the district; A-1 LOS = 2D footprint occlusion; A-2 = unique perimeter street edges >= 2; A-4 = path avoiding institutional buildings. social_hub eligibility = a social_hub_eligible flag added to the zone-type RONs (D-142). Regenerate-on-fail: MAX_RETRIES const, bump the missing-guarantee zone weight, increment a SeedChain sub-key. Files: generator.rs, the audit module.', 'backlog', 'medium', NULL, 'server', 'D-097', '2026-05-25 19:08:59', '2026-05-25 19:08:59', NULL, '0ba4e7f6ed3dd2b5a5032f04afbfc95d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYE43XRXRWS6GZK4PW', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'DoorSpec/InteriorDescriptor + condition overlay (D-231, D-198)', '(a) DoorSpec/InteriorDescriptor structs on the building tag (D-231): the step3->step4 boundary descriptor + the COMPLETE Phase-6 interior seed (zone_type_id, entry_class, floor_extent, era, flavor_ref, prosperity, layout_mode). Door-count derivation (Main always; Service/Emergency/Hidden by condition) + initial-state derivation (Public->Open, Commercial->TemporalWindow, Restricted->Locked, BreachOnly->Sealed) + mutator semantics (initial state frozen-amber, runtime changes are tile mutators). The interior FILL is Phase 6 (D-166) -- OUT OF SCOPE; only the descriptor lands here. (b) Condition overlay (D-198 two-pass amendment): BuildingConditionState{New|Maintained|Worn|Derelict|Abandoned}, OccupancyState{Full|Partial|Vacant}, VegetationEncroachment -- a SEPARATE Bevy system triggered by EconEvent (D-180), painting OVER the frozen structural fill, feeding D-100 DamageOverlay. Hard wall: overlay cannot change a BuildingTag; fill_chunk holds no PressureState reference.
Refinement (2026-05-26): READY behind 1006. Phase-4 scope = DoorSpec/InteriorDescriptor structs (defined in 1006) + derivation here; interior FILL is Phase-6, OUT of scope. Door-count: Main always; Service if logistical zone; Emergency if >=2 floors; Hidden seeded by zone (research 60/admin 20/res 5). Initial state: Public -> Open or Closed (zone decides: market Open, civic Closed); Commercial -> TemporalWindow; Restricted -> Locked + credential; BreachOnly -> Sealed. Mutations via ChunkMutations/TileOverride (exist). Condition overlay = a SEPARATE Bevy system on EconEvent (D-180), painting BuildingConditionState/OccupancyState/VegetationEncroachment (sub-biome whitelist: TropicalWet, TemperateForest, Wetland, Boreal, Savanna) over the frozen fill; refresh on EconEvent + daily. Condition is its OWN overlay layer; it does NOT feed D-100 physical-damage types (no D-100 change). Hard wall: condition system imports nothing from econ_sim::model and holds no PressureState. Files: generator.rs, economy_plugin.rs, econ-sim/src/events.rs.', 'backlog', 'medium', NULL, 'server', 'D-231', '2026-05-25 19:09:04', '2026-05-25 19:09:04', NULL, 'c1d1dd65cd217f7d6559eaac978bb2cb', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRX3R6G3Y7QR4FAJPNM', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Wiki to Atlas content-set consolidation (Q-107, blocked)', 'Consolidate all wiki content (pages + heightmaps + markers + architecture-flavor) into one first-class, baked Atlas/gamedata content set with a unified bake pipeline -- instead of per-feature file conventions. Resolves where authored per-body content lives (the D-232 flavor source). BLOCKED on Q-107 (open question -- the target tree structure must be decided first). The generator-facing trait_templates + atlas_body_trait_bias tables (D-232) is invariant to this, so it does NOT block the fill seam; sequence this AFTER the fill-seam work. Jeroen: ''the wiki needs to become part of the atlas content set ... not yet another hack.''', 'backlog', 'low', NULL, 'server', 'Q-107', '2026-05-25 19:09:10', '2026-05-25 19:09:10', NULL, '4d26cd6fe611953dee1419ed91f68db0', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZQKSQQQ1T5Q696AAM', 'task', '06FB0TNSRWN78KNTHMER9HN7QR', 'BulkClass + ProductionUbiquity enums + D-184 bulk_class backfill + dominant-commodity lookup', 'Define BulkClass{BulkSolid|BulkLiquid|PrecisionDense|Perishable|NonPhysical} + ProductionUbiquity{Ubiquitous|Common|Specialist|MonopolySource}; back-fill D-184 bulk_class values; build-time lookup of the settlement dominant commodity from the systems.db catalog. Seed-independent.
Round-3 batch refinement (2026-05-26): also define the MorphologyZone enum here (closed set: Fjord, Canyon, Delta, AlluvialPlain, Island, MountainPass, Coastal, Lake, Sea, OpenOcean, MeanderReach, Other) -- it had no home and 954/976/1005 all need it. bulk_class is the 8->5 PROJECTION (size variants bulk/standard/compact/oversized -> BulkSolid; precision -> PrecisionDense; the 8 stay on commodities). Note: the bare enum/struct type DEFS now live in the foundational types task; 982 owns the projection + dominant-commodity DERIVATION logic (derived from highest-output location-bound production chain, D-178, not an authored role->commodity table).', 'in_progress', 'medium', NULL, 'server', 'D-233', '2026-05-25 19:11:05', '2026-05-25 19:11:05', NULL, '87bb3ddec0d73895d9b3d8d91cd69720', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYAE6MPE7720360WM4', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'FillChunk shell derivation <5ms (Void/Wall/FloorSlab/Roof from cached tags)', 'On-demand derive phase: read cached DistrictWorldState; per tag, shell_derive(seed, footprint, extent, z) -> Void|Wall|FloorSlab|Roof per voxel. Pre-condition: only after SkeletonGenerated; re-enqueue High if block_tags absent. Budget ~0.4-0.6ms/chunk.', 'backlog', 'high', NULL, 'server', 'D-230', '2026-05-25 19:11:17', '2026-05-25 19:11:17', NULL, '9b655956f25c1fe8db565bc46365621e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZWSSHC2S5KPKAZ5QR', 'task', '06FB0TNSRZKXHAYMT7Q6WW3XDC', 'BuildingExteriorTag visual grammar + material vocabulary (template-filtered)', '(architecture_flavor x zone_type x era x density) -> BuildingExteriorTag{wall_material, roof_form, setback_tier, facade_rhythm, color_range}. Vocabulary: WallMaterial/RoofForm/FacadeRhythm/StreetSurface (extends D-228 FloorMaterial). 4-step derivation: flavor filter -> zone bias -> era restrict -> density setback -> interstitial type. Color flavor-bounded/era-adjusted/seed-selected.', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-05-25 19:11:19', '2026-05-25 19:11:19', NULL, 'aec76699fda238b1eb3f25cba09d5dad', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRX65YFYFR5RNCM9MCG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Gemma architecture-flavor methodology doc (stage1 register + stage2 structured)', 'Methodology doc analogous to the D-223 naming one: stage-1 register selection from wiki prose, stage-2 structured output {tag, allow, block, weight, trauma_decay_rate}. Defines the Gemma prompt + review at GTTR/wiki authoring.
Round 3 reframe: Gemma is a BOUNDED corpus-distillation pass (one read of all wiki -> propose catalog templates -> human-gated), NOT per-body open-vocab generation. Plus core catalog hand-authoring (Miri = cultural meaning + eligibility; Araminta = matching visual_bundle).
Round-3 refinement (2026-05-26): reframed as a FOLLOW-ON extension, not a blocker of the core catalog (1005). Runs AFTER the core hand-authored set exists, to propose additional templates (corpus-distillation, human-gated) toward the 40-45 target. Needs an architecture-flavor Gemma methodology doc (analogous to the naming one) defining the extraction prompt + structured output schema + human-review gate.', 'backlog', 'high', NULL, 'server', 'D-232', '2026-05-25 19:11:41', '2026-05-25 19:11:41', NULL, '935730c5e5b666270ff9fe4fb6b66f9d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWYHR72SC80F5T0TBW', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'allow/block weighted filter at chunk fill over zone-type ObjectTag pool', 'Apply the flavor profile as a filter over the D-142 zone-type ObjectTag baseline pool: block removed, allow weighted up by weight; dominant wins categorical, weighted-avg continuous. Three-layer: body x district x era-band (depth Q-106).
Round 3: this becomes the THREE-PHASE DRAW (body K-draw [K=5/3/1/0 by complexity_tier, SeedChain, coverage-aware] -> district-dominant by zone_affinity -> within-template seed picks) + CI guardrails, per D-232 — not a flat per-body allow/block filter.', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:45', '2026-05-25 19:11:45', NULL, '1cd82e145b9ba727f0e925ab6d8098f1', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZCG08B0P6PRNQV8KG', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'ObjectTag vocabulary authoring (Miri + Araminta co-maintenance)', 'Author the ObjectTag + exterior-material vocabulary the allow/block lists and D-235 grammar reference. Carried over from the retired generator-arch D-READY-9 requirement. Content task, Miri + Araminta.', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 19:11:47', '2026-05-25 19:11:47', NULL, '391f6f2d578a8849fcd99e8a3b4d331b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRYRD6NSYM5WEMTP76C', 'task', '06FB0TNSRWABYYB11QMJDTXE2G', 'Guarantee-audit T1/T2 checks over building_tags + street_graph', 'Tier-1 (social_hub/informal_zone/encounter_corridor) + Tier-2 (chokepoint/institutional/insider/economic_node/breach_only) checks as graph/geometry queries over the step-3 tags + street graph. Populate GuaranteeAuditResult.', 'backlog', 'medium', NULL, 'server', 'D-097', '2026-05-25 19:11:49', '2026-05-25 19:11:49', NULL, '7a004fc18d209e17daf4f756d670c703', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW3PYHX4XH6T073SFC', 'task', '06FB0TNSRWABYYB11QMJDTXE2G', 'A-1..A-4 assassin-lens graph queries + regenerate-on-fail', 'A-1 elevated vantage (above_ground>=3 + LOS to chokepoint), A-2 egress multiplicity, A-3 temporal opacity (TemporalWindow door), A-4 non-institutional route. On mandatory-check fail for world_tier, regenerate fill with adjusted params.', 'backlog', 'medium', NULL, 'server', 'D-097', '2026-05-25 19:11:51', '2026-05-25 19:11:51', NULL, '74f34b6823765ba8c9fab36cdf7af55a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZEP916TCXCDYNSVS0', 'task', '06FB0TNSRYE43XRXRWS6GZK4PW', 'DoorSpec/InteriorDescriptor structs + door-count/initial-state derivation', 'Structs on the building tag: the step3->step4 boundary + complete Phase-6 interior seed. Door-count (Main always; Service/Emergency/Hidden by condition), initial-state (Public/Commercial/Restricted/BreachOnly), mutator semantics. Interior FILL is Phase 6 -- out of scope; only the descriptor lands.', 'backlog', 'medium', NULL, 'server', 'D-231', '2026-05-25 19:11:53', '2026-05-25 19:11:53', NULL, 'ce38a6668094af5647318ef1df1867f4', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZNMBYYABFVTD36XEC', 'task', '06FB0TNSRYBG3M83M5JB4WD4NC', 'Backfill economic_tier (~97% NULL) + normalize economic_role 5 spellings', 'economic_tier NULL on ~97% of systems (gates D-199 context); economic_role has 5 variant spellings -- normalize to one canonical set. Source-canonical + regen-db.
Scope narrow (2026-05-26, /whats-next activation): economic_tier deferred to follow-up ticket #1007 (no derivation formula, not D-199-required, does not gate the cascade). This ticket now scopes to: role normalization (ALREADY in MIGRATION_SQL at import_economics.py:354-360 + validator at 859-880) -> run make regen-db, run make check-systems-db, verify with SELECT DISTINCT economic_role from bodies. Essentially a regen-and-verify task once #1000 founding_age is also in place.', 'done', 'high', NULL, 'server', NULL, '2026-05-25 19:11:58', '2026-05-25 19:11:58', NULL, '5babf9f3659347e38ef2d1dc406a2aa6', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWRCFA6P4HYWDN1XA0', 'task', '06FB0TNSRYBG3M83M5JB4WD4NC', 'Backfill behavioral_archetype', 'behavioral_archetype mostly NULL across bodies. Source-canonical + regen-db. Lower urgency than founding_age/economic_tier but needed before NPC-adjacent generation.', 'backlog', 'medium', NULL, 'server', NULL, '2026-05-25 19:11:59', '2026-05-25 19:11:59', NULL, '698384aa3d522f3f5d0b89fa0dfbe254', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWGDT3AJJE5QD0E3FR', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Deviation/swerve system — closed-default + rare per-building wildcard', 'Closed vocabulary by default; a rare per-building wildcard draws a COHERENT whole foreign template (cultural-only — function still passes economic hard gates; never an axis-scramble). Two OPPOSED active drivers: foreign-import (another corridor) scaled UP by cosmopolitanism/centrality/transit/Epicenter tier; heritage-callback (the body''s own corridor heritage sub-pool) scaled UP by remoteness/isolation/conservatism. Passive past-vogue holdover from age (D-217 wear). Sparsity escape-hatch = the SAME mechanism triggered by necessity when the closed vocabulary cannot serve a district. Per D-232.', 'backlog', 'medium', NULL, 'server', 'D-232', '2026-05-25 20:36:39', '2026-05-25 20:36:39', NULL, '73394310a19c07ce43327e7de3b2c857', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZNNMZPXSEZ7HN778R', 'task', '06FB0TNSRWMZBFV1V6F79VW8S4', 'Visual-asset fallback hierarchy (specific->generic tokens, patch-upgradable)', 'Every specific texture/material token declares a generic parent it degrades to: temple_wall_wood -> generic wood_wall placeholder until the specific asset ships, then it upgrades IN PLACE. The logical token a building uses is fixed + deterministic at generation (frozen world); only render fidelity is incremental. This is where patched-in themes/textures live -> NO catalog versioning needed for art (versioning only re-enters if a NEW logical template is added post-launch). Per D-232 + D-235. Cross-refs ticket 988 (exterior visual grammar).', 'backlog', 'medium', NULL, 'server', 'D-235', '2026-05-25 20:36:42', '2026-05-25 20:36:42', NULL, '3d1c1768ec730de7795b334ec1920399', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRZGAEWEXJEYRAWX4K4', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Foundation — define all D-229/D-230/D-231/D-233 shared Rust types + struct plumbing (single PR)', 'Consolidates the type-definition layer every downstream fill-seam ticket waits on (the most-repeated refinement gap). One PR that compiles with stubs/defaults, then the logic tickets fill behavior. Defines: BuildingPropertyTag, FloorExtent + FloorHeightProfile (with floor_at_voxel_z / voxel_range_for_floor, resolving Q-104), BuildingEntryClass (Public|Commercial|Restricted|BreachOnly), ConstructionEra, ZoneTypeId (Box<str>), the enums MorphologyZone / BulkClass(5) / ProductionUbiquity, DoorSpec + InteriorDescriptor structs, DistrictWorldState. Struct plumbing: add fields to CityGenerationContext (morphology_zone, trait_selection, dominant_bulk_class, dominant_production_ubiquity), add block_tags-bearing DistrictWorldState to BodyWorldState.districts, and FIX GenCompletion::SkeletonGenerated to carry DistrictWorldState (+ body_id) not just city_id. AccessTier collision: the existing 5-variant spatial AccessTier in generator.rs is RENAMED ZoneAccessTier (FloorZone/VerticalCorridorSpec); BuildingEntryClass is the new per-building-exterior type. Logic (projection/derivation/draw/fill) stays in 982/983/984/985/998 etc., which build ON these types.', 'done', 'high', NULL, 'server', 'D-229', '2026-05-26 18:13:15', '2026-05-28 21:07:48', NULL, '35e2a4e8ff6fa989cd9f9ddcf9bcb98b', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXR8C67AF3RRDXD43G', 'task', '06FB0TNSRYBG3M83M5JB4WD4NC', 'Backfill founding_age_years for 273 inhabited bodies (source-canonical)', 'founding_age_years NULL on all 273 inhabited bodies -- gates ConstructionEra (D-229) + block irregularity (D-216). Edit the TOML/JSON sources + regen-db; never direct-write systems.db.
Refinement (2026-05-26): EVENTS-FIRST derivation, not pure wave-table (avoids double bookkeeping with the existing per-system historical_events.age_years authored data, e.g. Van Maanen 180yr). MIGRATION_SQL block in import_economics.py: bodies.founding_age_years = COALESCE((SELECT age_years FROM historical_events WHERE system_id = bodies.system_id AND event_type IN (founding,first_settlement,colonization) ORDER BY sort_order LIMIT 1), wave_fallback(settlement_wave)). Canonical wave fallback (founding-edge per lore): wave_1=600, wave_2=500, wave_3=300, wave_4=100, wave_5=40. EXCLUDE Sol bodies entirely: settlement_wave=origin -> NULL (Sol is out of player scope, Atlas highest-level only, deeper Sol is future DLC). Source-canonical + regen-db.
---
Refinement (2026-05-30, /whats-next — resolves Si GAPS): EVENTS-FIRST branch uses the REAL authored founding event, not the fictional vocabulary. The named types (founding/first_settlement/colonization) match ZERO rows in historical_events; the actual founding event is event_type=\"colonial_charter\" (9 systems, age_years 480-600, aligns with wave band). civilizational_origin (12900yr = cosmic Earth origin) MUST be excluded. MIGRATION_SQL: bodies.founding_age_years = COALESCE((SELECT age_years FROM historical_events WHERE system_id=bodies.system_id AND event_type=\"colonial_charter\" LIMIT 1), CASE sh.settlement_wave WHEN \"wave_1\" THEN 600 WHEN \"wave_2\" THEN 500 WHEN \"wave_3\" THEN 300 WHEN \"wave_4\" THEN 100 WHEN \"wave_5\" THEN 40 END) -- joined to system_history sh. sort_order NOTE: sort_order=0=MOST RECENT, so do NOT ORDER BY sort_order to pick founding; filter by event_type instead. EXCLUDE settlement_wave IN (\"origin\",\"unsettled\") -> stays NULL (origin=Sol out of player scope per D-236; unsettled=24 systems, literally not settled). Source-canonical: add the UPDATE to MIGRATION_SQL in tooling/economy-db/import_economics.py, then make regen-db; never direct-write systems.db.', 'done', 'high', NULL, 'server', NULL, '2026-05-25 19:11:56', '2026-05-28 21:07:49', NULL, '560431f062525b4aa54f32b24c07f023', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXHDQ0VGR9YHTXWNRW', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Deferred: economic_tier derivation formula (not D-199-required; post-cascade)', 'Split out of #1001 during /whats-next refinement: economic_tier has no derivation formula in any D-record and is NOT one of D-199''s 6 required generation-context fields, so it does not gate the Phase-4 cascade. Defer until either (a) a derivation formula is decided (likely a population-bracketed map: <100K=1, 100K-1M=2, 1M-10M=3, >10M=4, or similar — needs design) OR (b) economic_tier earns a use case in the rolling sim. Live state: ~97pct NULL in system_economy. Not blocking #1001''s role-normalization work.', 'backlog', 'low', NULL, 'server', NULL, '2026-05-28 21:07:54', '2026-05-28 21:07:54', NULL, '1bd3e30f5bcb7fb536cce01ce8e77448', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWW7NX8R77SZ7QHTZG', 'epic', '06FB0TNSRZXCHGS16BFHSSGSV4', 'System economic specialization — authored identity layer (D-237)', 'Authored per-system identity layer: three fields (economic_specialization, cultural_specialization, dominant_faction) sourced from wiki/economics/system_specialization.toml, imported via import_economics.py, consumed by city_context_reader.rs. Authored lore wins; deterministic-varied heuristic fallback for unnamed systems. Outcome: Layer 2 generation reads GTTR-consistent system identity rather than a role+planet_class guess.
Decisions: D-237 (new — authored specialization layer schema + vocabulary + fallback algorithm); D-233 re-amended (authored field is primary driver for BulkClass x ProductionUbiquity; heuristic is fallback); D-199 amended (economic_specialization + cultural_specialization added to read-set); D-232 noted (cultural_specialization drives template pool selection); D-214 noted (authored dominant_faction is primary PoliticalArchetype input).
Workshop: docs/workshops/system-economic-specialization/ (all rounds + outcomes).', 'backlog', 'high', NULL, 'server', 'D-237', '2026-05-31 16:12:18', '2026-05-31 16:12:18', NULL, '24ad9884fbd51fb3ac70220a46342fdf', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWT2P54ZBNMZB4N9F4', 'task', '06FB0TNSRWW7NX8R77SZ7QHTZG', 'city_context_reader.rs: authored-primary + deterministic-varied fallback for all 3 specialization fields', 'Wire all three authored specialization fields into CityGenerationContext, with authored value as primary and deterministic-varied heuristic as fallback.
economic_specialization:
Non-null -> look up in specialization_vocabulary -> read bulk_class_projected + production_ubiquity_projected -> populate CityGenerationContext.dominant_bulk_class / dominant_production_ubiquity.
Null -> deterministic-varied fallback (integer basis points only, D-010):
candidate_weights: Map<specialization_id, u64>
for primary role candidates: += 8_000 bps
for secondary role candidates: += 3_000 bps
for each corp HQ in corp_presence: += 5_000 bps (map corp specialization -> vocab id)
for every vocabulary id: += 400 bps // noise term; ~5% off-primary draw across 200 unnamed systems
seed = SeedChain::root(world_seed).derive(SeedDomain::Layer1System, fnv1a_64(system_id)).seed()
weighted draw sorted alphabetically by spec_id (deterministic order)
ROLE_SPEC_MAP table: see burnelli-sheldon-round3.md §(d).
This replaces the #982 dominant-commodity derivation as PRIMARY; #982 output becomes fallback path.
cultural_specialization:
Non-null -> pass to D-232 template-pool selection (boost matching templates, dampen corridor default for divergent-heritage systems).
Null -> corridor assignment already in D-232 algorithm; no action needed.
dominant_faction:
Non-null (authored in system_factions) -> direct input to D-214 PoliticalArchetype (authored wins).
Null -> Paula''s hop/currency fallback: hop 0-5 -> concord_assembly; west_reach hop 6+ mark_primary -> compact; west_reach hop 5-6 mixed -> compact_sympathetic; hop 7+ other corridors -> independent; Tier-1 monopolist corp HQ -> syndic_dominant (flagged). Full table: paula-round3.md §2.
D-172/D-174 wiring: authored dominant_faction is the primary input; existing validation unchanged.', 'backlog', 'high', NULL, 'server', 'D-237', '2026-05-31 16:13:29', '2026-05-31 16:13:29', NULL, '92c6bd12e99a15b04789708c4f9b8990', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRWRPJSYVRAG7GJBXAR', 'task', '06FB0TNSRWW7NX8R77SZ7QHTZG', 'settlement_pattern → subsurface/domed/enclosed morphology branch in D-233/D-237 fill', 'The economic-built-world fill model (D-233, re-amended by D-237) is a surface model (roofed-coverage fraction, operations-surface remainder, concentrate-vs-scatter on open ground). bodies.settlement_pattern carries non-surface morphologies (underground_concentrated e.g. Vuurkloof/GJ35c, cave, domed, underground_complex) that nothing in the chain currently reads — not in D-199 read-set, not in city_context_reader, not branched in skeleton_gen. Engine primitives exist (D-110 signed base_z, UndergroundComplex reservation, D-106 vertical scale) but no morphology switch connects settlement_pattern to the built-world layer, so enclosed-habitat bodies would generate as surface towns, contradicting lore. SCOPE: (1) add settlement_pattern to the D-199 read-set + city_context_reader; (2) add a surface-vs-enclosed branch to the D-233/D-237 fill (enclosed coverage/vocabulary reinterpretation); (3) amend D-233/D-237 records accordingly. BLOCKED ON Q-108 debate (subterranean/domed/sealed-habitat morphology design) — do not implement before that resolves. Surfaced via Vuurkloof (GJ 35) in the system-economic-specialization workshop.', 'backlog', 'medium', NULL, 'server', 'Q-108', '2026-05-31 17:07:55', '2026-05-31 17:07:55', NULL, '7a352d069d5dbc3dfb939caccb11765f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRXAXB5DEYTVABX211G', 'task', '06FB0TNSRWW7NX8R77SZ7QHTZG', 'Write D-237 + re-amend D-233, amend D-199 / D-232 / D-214', 'Write the new D-237 record to decisions/architecture.md using Burnelli''s draft from docs/workshops/system-economic-specialization/burnelli-sheldon-round3.md §(f). D-237 covers: 3-field schema, 27-value economic vocabulary, unified cultural_specialization vocabulary (12 activity + 9+ heritage values), deterministic-varied fallback algorithm, Groombridge landmark mechanism note.
Also apply these amendments:
- D-233 re-amendment: authored economic_specialization is now the primary driver for (dominant_bulk_class, dominant_production_ubiquity) in CityGenerationContext; heuristic fallback for NULL.
- D-199 amendment: add economic_specialization and cultural_specialization to the minimum read set (dominant_faction was already field 4).
- D-232 note: cultural_specialization (system_economy) is a template-pool selection signal — non-null directs which cultural tradition pool D-232 draws from; null = corridor assignment stands.
- D-214 note: dominant_faction authored values (8-value vocabulary) are the primary PoliticalArchetype input; hop/currency heuristic is fallback.
- D-237 note on Groombridge landmark mechanism: singular MonopolySource-class institution within a Specialist district → encode district as Specialist in vocabulary + D-222 multi-block reservation + D-232 atlas_body_trait_bias hero pin; not a composite vocabulary value.
Source material: burnelli-sheldon-round3.md §(f) has complete draft text. Slot in D-237 (already claimed). No new D-records needed beyond D-237.', 'done', 'high', NULL, 'server', 'D-237', '2026-05-31 16:12:34', '2026-05-31 17:33:45', NULL, '01132691e298c894ae015831d15e4f1a', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRW54ARBAGAMDACG9CR', 'task', '06FB0TNSRWW7NX8R77SZ7QHTZG', 'Schema: economic_specialization + cultural_specialization columns + specialization_vocabulary table', 'Add two new columns to system_economy and create the specialization_vocabulary table.
systems-schema.sql changes:
ALTER TABLE system_economy ADD COLUMN economic_specialization TEXT;
ALTER TABLE system_economy ADD COLUMN cultural_specialization TEXT;
-- cultural_specialization avoids collision with system_culture.cultural_register (NPC-voice, wiki_sync.py pipeline, untouched)
CREATE TABLE IF NOT EXISTS specialization_vocabulary (
specialization_id TEXT PRIMARY KEY,
commodity_id TEXT NOT NULL REFERENCES commodities(commodity_id),
production_ubiquity_override TEXT, -- NULL = use catalog default
bulk_class_projected TEXT NOT NULL, -- pre-computed BulkClass for D-233
production_ubiquity_projected TEXT NOT NULL, -- pre-computed ProductionUbiquity
description TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_spec_vocab_commodity ON specialization_vocabulary(commodity_id);
import_economics.py changes:
Add both ALTER TABLE statements to COLUMN_MIGRATIONS list (idempotent).
Add CREATE TABLE IF NOT EXISTS specialization_vocabulary to MIGRATION_SQL block.
IMPORTANT: system_factions.dominant_faction ALREADY EXISTS as a column (confirmed in schema). Zero schema change for dominant_faction — the import step only upserts authored values. Do not add a new column.
Cultural vocabulary validation is a string-list check in CI code — no separate DB table needed for cultural_specialization values.
---
Refinement (Si, 2026-05-31): READY. Confirmed: system_economy at systems-schema.sql L94; dominant_faction exists L108 (populate-only); commodities PK L254 (FK valid); COLUMN_MIGRATIONS + MIGRATION_SQL idempotent patterns present in import_economics.py. Add the 2 cols INSIDE the system_economy CREATE TABLE block (not ALTER) for fresh builds, AND as COLUMN_MIGRATIONS ALTER entries for existing DBs; add specialization_vocabulary CREATE TABLE to MIGRATION_SQL. check-systems-db-stamp GENERATOR_SOURCES untouched here (belongs to #1013).', 'done', 'high', NULL, 'server', 'D-237', '2026-05-31 16:12:44', '2026-05-31 18:51:38', NULL, '4e1b63c53f89cca0c3b4827a834f2c69', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNSRY7PQPSX5C4HNCNVYG', 'task', '06FB0TNSRWW7NX8R77SZ7QHTZG', 'Source TOMLs: specialization_vocabulary.toml (27 values) + system_specialization.toml (hero-system seeds)', 'Create two new source files consumed by import_economics.py.
wiki/economics/specialization_vocabulary.toml — 27 economic values:
Burnelli''s 26-value list from burnelli-sheldon-round3.md §(b) (agricultural x5, energy x2, extraction x5, manufacturing x6, high-tech x1, services x7) plus Miri''s addition: research_station (commission_certification, concentrated -> NonPhysical x Specialist, for Keid).
Merge resolutions applied: marine_farming absorbed into terroir_organics; military_production + military_station -> military_industrial; specialist_manufacturing -> precision_tech (identical D-233 output); alloy_hub absorbed into general_industrial.
Each entry: specialization_id, commodity_id, production_ubiquity_override (null = catalog default), bulk_class_projected, production_ubiquity_projected, description.
wiki/economics/system_specialization.toml — ~28 hero-system entries (seed for full content pass):
Format:
[GJ-144] # Ran
economic_specialization = ''breadbasket''
cultural_specialization = ''terroir_heritage''
dominant_faction = ''concord_assembly''
Use merged vocabulary labels from specialization_vocabulary.toml (not Miri''s pre-merge Round 3 labels).
Hero systems: Ran, Cygni B, Sindri, Nova Roma, Renaissance, Groombridge, Prometheus, ACB, Tessera, Sirius, Parallax, Gateway, Wolf 359, Vuurkloof, Keid, Bastion, Arbour, Lu Ban, Calloway Reach, Confluent/VGV, Braemar, Nyrheim, Ostmark, Dagat, Altmark, Dai Lo, Zenzele + east-reach lattice anchor (GJ TBD).
CONFLICT to resolve before committing Vuurkloof: Miri says dominant_faction=independent (GTTR engineering community), Paula says concord_assembly (south_reach hop-6 default). Verify against docs/workshops/system-economic-specialization/workshop-outcomes.md.
cultural_specialization accepts both activity-type values (scholarly, agrarian, industrial_heritage, etc.) and heritage-type values (scottish, vietnamese, afrikaans_cape, zulu, chinese, italian_northern, tagalog, norse_compact, french_provencal, etc.). Heritage takes precedence when both apply. Full combined vocabulary list in miri-round3.md §2.
---
Refinement (Si + lead, 2026-05-31): GAP1 RESOLVED — TOML stanza keys use SPACE format [GJ 35], not hyphen [GJ-35]; every wiki/economics TOML + the star_systems PK use spaces (the workshop §1 example was illustrative/wrong). GAP3 RESOLVED — keep lattice_material_source in specialization_vocabulary.toml with NO system stanza yet (east-reach anchor GJ pin is a content-pass item, documented-intentional). GAP2 VUURKLOOF (GJ 35) — author dominant_faction = independent as a PROVISIONAL PLACEHOLDER; flagged OPEN for human decision: harshness/guild-self-governance leans independent vs Assembly wanting a strategic fusion-fuel source leans concord_assembly. Filed D-237 workshop-outcomes §7 currently says concord_assembly — left as-is pending the call; live TOML will carry independent + a # PROVISIONAL comment. Do not treat Vuurkloof faction as final.', 'done', 'high', NULL, 'server', 'D-237', '2026-05-31 16:13:02', '2026-05-31 18:51:38', NULL, 'fa5f97a82ff8dd593cd1f0b5dffd8e90', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);