diff --git a/.pql/changelog/ticket_history/2026-07.sql b/.pql/changelog/ticket_history/2026-07.sql index 8efb1617e..45e330271 100644 --- a/.pql/changelog/ticket_history/2026-07.sql +++ b/.pql/changelog/ticket_history/2026-07.sql @@ -1500,3 +1500,6 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPS6QD5FG9TC2WXTDVA44FPW', 'status', 'review', 'done', NULL, '2026-07-17 06:36:05', '2026-07-17 06:36:05.018', '2026-07-17 06:36:05.018', NULL, '01cf6177ffbc79d6599bfdbe3a9458c8', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPS6RRBF9HHAED5QKTKQCY1G', 'status', 'review', 'done', NULL, '2026-07-17 06:36:05', '2026-07-17 06:36:05.021', '2026-07-17 06:36:05.021', NULL, 'f5c4072188f4f0acc309f37cc2a9a107', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPS6TF9XDZPKZY8DHG1T0E2R', 'status', 'review', 'done', NULL, '2026-07-17 06:36:05', '2026-07-17 06:36:05.022', '2026-07-17 06:36:05.022', NULL, 'ecd510bf9c15187292bce916dfd25fa8', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'status', 'in_progress', 'review', NULL, '2026-07-17 07:16:47', '2026-07-17 07:16:47.103', '2026-07-17 07:16:47.103', NULL, 'd8b73dc6faef3d1a5d3573045c475b62', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'status', 'in_progress', 'review', NULL, '2026-07-17 07:16:47', '2026-07-17 07:16:47.106', '2026-07-17 07:16:47.106', NULL, '3b30b41a05b18914a5664024712e3435', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FPXAWGRH9QK5QKV7584Q92Z8', 'status', 'in_progress', 'review', NULL, '2026-07-17 07:16:47', '2026-07-17 07:16:47.106', '2026-07-17 07:16:47.106', NULL, 'db5fa6e8953f5df39e0c49c81d06be74', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-07.sql b/.pql/changelog/tickets/2026-07.sql index 2f54cf0a1..b330bf1a2 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -2141,3 +2141,10 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat Refinement (2026-07-17, Si review): READY. Upstream verified HEALTHY — glaciation derivation (derive_glaciation_grade_from_climate, district_profile.rs:339-366) is correctly wired and even gates the Fjord family; GJ244Ad moisture clears the gate floor. This is purely a rendering gap: aliveness_probe render_window_panels computes the full DistrictProfile and discards .glaciation_grade (line ~458 reads only morphology_zone). SCOPE RULING (lead): probe-only THIS ticket — per-pixel ice tint blended on the morphology panel keyed on glaciation_grade (+ optionally a 6th dedicated glaciation panel; PANEL_NAMES/bufs are array-driven, cheap). The wire+client half (glaciation: Vec parallel array inside DistrictGridLayer + build_district_grid + protocol.gd + _draw_gen_district — governance-UNCONSTRAINED per the D-226 growth-ceiling reading: the cap governs new top-level Option layers, not per-cell fields inside a shipped layer; elev_q precedent) is DEFERRED to T-1124''s design pass, which owns the which-fields-ship-at-district-resolution question. Freeze contract confirmed: D-239 SS6 — tint is classifier-tuning territory, no MorphologyZone vocab reopen; an ice zone would need its own explicit D-amendment (not pursued).', 'done', 'medium', 'dudley', 'server', NULL, '2026-07-16 20:25:45.551', '2026-07-17 06:36:05.022', NULL, 'f4a9417688f87f17eef4d66b8a8bb266', 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 ('06FPXTD20GS2CSC10RCC53Q6V4', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Spawned-server orphan watchdog — survive companion SIGTERM (parent-death signal or equivalent)', 'T-1132 known gap (Stig, live-verified): a raw SIGTERM to the companion process bypasses Godot''s entire notification system (_notification never fires, _exit_tree never runs — confirmed by isolated repro), so a spawn-mode companion killed that way orphans its owned server process. All WM close paths (X button, Alt+F4, wmctrl -c) are clean and verified. No GDScript fix exists — the mitigation is server-side: when spawned as a companion child (e.g. a --parent-watch flag or detecting the pipe), the server should exit when its parent dies — prctl(PR_SET_PDEATHSIG, SIGTERM) on Linux at minimum, or a portable stdin-EOF watchdog (the companion already holds the child''s stdio pipe via start_with_pipe, so stdin closing on parent death is a reliable cross-platform death signal). Keep it opt-in per spawn args so make game and test-harness spawns are unaffected.', 'backlog', 'low', NULL, 'server', NULL, '2026-07-17 07:10:32.964', '2026-07-17 07:10:32.964', NULL, 'ab6964ffc45341d3f5c0014f1bc0d01a', 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 ('06FPX7HZY1CK3VSTRXP1NP9VYW', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Design pass: D-254 companion-app architecture — connection model, reader protocol, app shell, data browser, save/load seam', 'Produce and record D-254 (id claimed). Participants: Tyre (architecture lead, writes the record) + Oscar (protocol/connection model — his activation). Must pin: (1) CONNECTION MODEL — attach to the running game server vs spawn-own (--test-mode? which world?) incl. discovery (port/SR_ADDR), and the multi-connection reality of the bridge today (recon: almost certainly single-connection; a second reader connection is new protocol work); (2) READER CONNECTION CLASS — handshake variant that never spawns a character and cannot send inputs (read-only enforced SERVER-side, not client courtesy — future market trading would widen this deliberately per-verb, design the enum now); (3) APP SHELL — how make atlas launches the existing implant Atlas UI standalone (second main scene vs feature flag vs export preset; keep the implant aesthetic — the diegetic second-monitor pattern is a product goal); (4) DATA BROWSER — Jeroen wants to scan ALL database data: define the browse surface (entity list/detail screens from implant components: systems, bodies, corps, settlements, markets) and its data path (existing wire requests vs a generic read API vs local systems.db read for static data + wire for dynamic — mind the D-010 information-boundary rules for what a reader may see); (5) SAVE/LOAD SEAM — saves are Phase 5+; design the interaction now (attach = inherits the running world; spawn = pick seed today, pick save later), record the hook; (6) FUTURE TRADING note — what changes when the app gains write verbs (auth? per-verb allowlist on the reader class?). Deliverable: D-254 record written in governance/decisions/architecture.md + a proposed implementation ticket tree under T-1128. Wiring is NOT this ticket.', 'review', 'high', NULL, 'server', NULL, '2026-07-17 05:48:12.656', '2026-07-17 07:16:47.102', NULL, '7fe0942f3717617e7cedc71441c6c1f9', 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 ('06FPXAWGRH9QK5QKV7584Q92Z8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Standalone entry scene + attach-or-spawn boot + make atlas target', 'D-254 SS1/SS3/SS5. client/scenes/atlas_standalone.tscn + atlas_standalone.gd (modeled on visual_capture.gd''s boot shape — NOT a flag on main.tscn, which is saturated with player-only paths): attach-mode = TCP connect SR_PORT-or-9876 with ~500ms timeout; ECONNREFUSED falls through to spawn-mode = own child server via the server_process.gd ownership pattern with --port 0 + LISTENING:{port} parse, REAL systems.db (no --test-mode); sends Reader-role StartupMessage; opens implant/map fullscreen (skips main menu/character creation); window title branding via DisplayServer.window_set_title; intercept atlas close-to-gameplay in atlas_standalone.gd (do not modify atlas_app.gd). Makefile: target = trivial build+launch (the scene owns the attach-or-spawn decision — no Makefile orchestration; and per Oscar''s flag this simplicity does NOT shrink the T-1130 server work). Exportable later as a Godot export preset, no architecture change. Blocked by T-1130 for real attach-mode testing (developable against spawn-mode before that). Blocks the browser-UI ticket (needs a shell to launch from). + +Correction: the Makefile target name in the line above is ''atlas'' (make atlas) — shell backticks ate the word during filing. + +Jeroen''s requirement (2026-07-17, mid-implementation — now D-254 SS3): the standalone shell is a GENERIC IMPLANT HOST, not an Atlas launcher. Every ImplantRegistry app is automatically available in the companion; the manifest (app.tres) gains available_in_companion: bool = true (OPT-OUT — set false only for apps that need a playing character/gameplay context). Shell consults the flag at instantiate_all/app-open time and in any app-switching surface; the in-game implant ignores it. Both current apps (Atlas, Economics) stay true. Jeroen''s suggested name availableInAtlasApp recorded in D-254 with the available_in_companion rename rationale (GDScript snake_case + avoids colliding with apps/atlas).', 'review', 'high', 'stig', 'client', NULL, '2026-07-17 06:02:45.316', '2026-07-17 07:16:47.106', NULL, '775c513c295216c2f2892585d3183516', 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 ('06FPXAS26J3BS7ZJVRFT5JDXP8', 'task', '06FPX7FKG0Y6SYWEDPG4PJQHQ8', 'Reader connection: multi-connection bridge + ConnectionRole gate', 'D-254 SS1/SS2 (record on the atlas-companion-app branch; ref attaches after merge+sync). THE GATING TICKET — everything else in T-1128 needs a server that can hold a second connection without hanging it (today: main.rs single blocking accept at ~line 120; a second client hangs silently forever). Scope: ConnectionRole field on StartupMessage (Player | Reader, serde default Player for back-compat, enum shaped for future TradingReader); TcpListener blocking single-accept -> non-blocking per-tick accept loop; BridgeResource becomes 0-1 Player + 0-N Reader collection (NOT general N-player — D-009 scope stays separate); Reader never enters the character-spawn path and receives NO ObserverSnapshot (structural, not filtered); per-connection response tagging for atlas/starmap/citynames request-response buffers; Player-only whole-server-shutdown-on-disconnect (Reader disconnects must never affect the player session); permitted-message matrix enforcement server-side — violation = log + drop frame, repeated = disconnect the reader; lower per-reader inbound frame cap. Deterministic sim unaffected by reader presence. Oscar''s design, Oscar implements.', 'review', 'high', 'oscar', 'server', NULL, '2026-07-17 06:02:17.012', '2026-07-17 07:16:47.106', NULL, 'ee8ec1772f97e0bbbbf877ad0773945f', 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);