chore(meta): close the two harness tickets, file the gaps they exposed

T-1222 and T-1223 shipped today but were still sitting in backlog. Closed,
with what actually landed recorded on each — including that T-1223's title
premise was wrong: godot-cold-parse does not miss client/tests specifically,
it only ever sees the startup path, so the fix was a new tool rather than a
widened filter.

Three gaps opened after the reconciliation pass and had no ticket:

T-1230 — re-enable test_character_visual_sprint28 and fix the per-test
compositor rebuild that made it 37% of the client suite. Skipping it bought
39s; the skip must not become permanent, and the ticket says so with the
deadline (Phase 5 player rendering) and the better fix to prefer.

T-1231 — the enclosed-settlement and Sol GeneratorScope rulings still exist
only in a scratchlog under /tmp with no D-record behind them. Qatux flagged
this and correctly refused to invent the governance itself.

T-1232 — scene_helper.gd turns out to have no importers at all, which is why
its five-month parse breakage cost nothing. Delete or adopt: user's call.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-27 01:14:03 +02:00
co-authored by Claude
parent 791600dd24
commit 885a10af09
3 changed files with 52 additions and 0 deletions
+21
View File
@@ -3211,3 +3211,24 @@ non-reproducible in isolation / module-scoped / --lib-only runs, and absent on
a clean full-suite rerun). No golden fixtures changed.', NULL, '2026-07-26 13:23:55', '2026-07-26 13:23:55.540', '2026-07-26 13:23:55.540', NULL, '8af7979612b90a17bce6b8bafca84fb4', 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 ('06FSWCYA60VHFK44J09WW78FY0', 'status', 'in_progress', 'review', NULL, '2026-07-26 13:23:59', '2026-07-26 13:23:59.369', '2026-07-26 13:23:59.369', NULL, 'ed0a9656e3b0ada543a2f58e17eda9f5', 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 ('06FSP3GEJYTA966BSSR9354YC4', 'status', 'review', 'done', NULL, '2026-07-26 13:24:15', '2026-07-26 13:24:15.122', '2026-07-26 13:24:15.122', NULL, '37112750b5d1aeec9384cb9a1f5544ae', 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 ('06FT0V9F4MNF4FZ44KJMKTXJ0G', 'description', 'Bit the team during the 2026-07-26 pair session and should be fixed BEFORE the larger D-258/scale-constant work lands, since that work will touch a lot of test files and this bug actively masks failures during exactly that kind of change. tests/run-godot currently reports ''Tests passed'' when a gdUnit4 suite fails to parse entirely (e.g. a syntax error in a test file) -- the harness sees a run with total: 0 tests and treats zero-failures-because-zero-tests as green, rather than treating ''a suite that should have contributed tests contributed none'' as a hard failure. Fix: total: 0 for a suite that was expected to run must be a FAIL, not a silent pass-through. Needs a regression test: intentionally break a test file''s parse and assert the harness reports failure, not success.', 'Bit the team during the 2026-07-26 pair session and should be fixed BEFORE the larger D-258/scale-constant work lands, since that work will touch a lot of test files and this bug actively masks failures during exactly that kind of change. tests/run-godot currently reports ''Tests passed'' when a gdUnit4 suite fails to parse entirely (e.g. a syntax error in a test file) -- the harness sees a run with total: 0 tests and treats zero-failures-because-zero-tests as green, rather than treating ''a suite that should have contributed tests contributed none'' as a hard failure. Fix: total: 0 for a suite that was expected to run must be a FAIL, not a silent pass-through. Needs a regression test: intentionally break a test file''s parse and assert the harness reports failure, not success.
FIXED 2026-07-27 (commits b2ef73256, 263a98f3e). Three bugs, not one. (1) load_error/no_tests guards added: a suite that fails to LOAD, or a run that executes zero tests, are now harness failures with a harness_error JSON field and exit 2 -- the exit code cannot inherit gdUnit4''s, which returns 0 in both states. Verified by injecting each failure, including the case that actually bit: one broken file among many, where total stays large and failed stays zero. (2) DOUBLE COUNT: the parser matched gdUnit4''s per-suite ''Statistics:'' lines AND the ''Overall Summary:'' line that already sums them, so every total was exactly 2x -- the real suite is 1830 tests, not 3660. (3) ANSI escapes sit between summary fields, so patterns on the raw log fell through to a weaker fallback that could not see skips and reported a fully-skipped suite as 26 FAILED. All parsing now runs on a de-ANSI''d copy; skipped is parsed, surfaced, and excluded from passed.', NULL, '2026-07-26 23:12:10', '2026-07-26 23:12:10.402', '2026-07-26 23:12:10.402', NULL, '8e0f23f4bb55fbe300530ed5a6b6b062', 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 ('06FT0V9Y08S6XNMB04JJTGQP44', 'description', 'Companion bug to the tests/run-godot false-green (sibling ticket) -- both bit the team in the same 2026-07-26 pair session, and together they mean a broken test file can pass BOTH the cold-parse gate and the test run with no signal anywhere. tooling/godot-cold-parse currently scans production/source directories but does not include client/tests/ in its parse-check sweep, so a syntax error introduced in a test file is invisible to the cold-parse gate (which would have caught it) AND to the test run (which reports total: 0 as green per the sibling ticket). Fix: extend godot-cold-parse''s scan set to include client/tests/. Do this ticket and its sibling FIRST, ahead of the D-258/scale-constant work (T-1211/T-1218), since that work touches a large volume of test files and needs both gates trustworthy going in.', 'Companion bug to the tests/run-godot false-green (sibling ticket) -- both bit the team in the same 2026-07-26 pair session, and together they mean a broken test file can pass BOTH the cold-parse gate and the test run with no signal anywhere. tooling/godot-cold-parse currently scans production/source directories but does not include client/tests/ in its parse-check sweep, so a syntax error introduced in a test file is invisible to the cold-parse gate (which would have caught it) AND to the test run (which reports total: 0 as green per the sibling ticket). Fix: extend godot-cold-parse''s scan set to include client/tests/. Do this ticket and its sibling FIRST, ahead of the D-258/scale-constant work (T-1211/T-1218), since that work touches a large volume of test files and needs both gates trustworthy going in.
FIXED 2026-07-27 (commits a005e4840, bf1976613) -- but the premise in this title is TOO NARROW. godot-cold-parse does not miss client/tests/ specifically; it only ever sees scripts on the STARTUP path (autoloads + main scene chain). Verified by breaking a non-startup UI script (step_canvas_legend.gd) and a test file in turn: it reported ''clean'', exit 0, for both. It also filters ''Cannot infer the type'', which had hidden a genuinely unparseable file for five months. Fix shipped as a NEW tool rather than a widened filter: tooling/godot-parse-sweep + client/tools/parse_sweep.gd open all 226 project scripts and fail on any that do not parse. Enforced in the pre-push hook AHEAD of the test suite, so a parse failure costs ~4s instead of ~135s (the hook never ran cold-parse at all, so nothing previously enforced this). The two-part split is forced: no Godot API reports GDScript parse failure reliably -- CACHE_MODE_IGNORE segfaults the engine on a broken script, GDScript.new()+reload() false-positived 150 of 226 healthy scripts, and plain load() returns non-null for a broken one. The engine''s stderr is the only honest signal. First run found client/tests/util/scene_helper.gd unparseable since 2026-02-25.', NULL, '2026-07-26 23:12:22', '2026-07-26 23:12:22.665', '2026-07-26 23:12:22.665', NULL, '19ea43b7aa92622eb2f334c1a56e72cf', 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 ('06FT0V9F4MNF4FZ44KJMKTXJ0G', 'status', 'backlog', 'done', NULL, '2026-07-26 23:12:26', '2026-07-26 23:12:26.266', '2026-07-26 23:12:26.266', NULL, '7f56e47d4c4b49a457c29853fc678013', 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 ('06FT0V9Y08S6XNMB04JJTGQP44', 'status', 'backlog', 'done', NULL, '2026-07-26 23:12:26', '2026-07-26 23:12:26.269', '2026-07-26 23:12:26.269', NULL, 'dffd6923b5e046dec68a4a3ef06a8629', 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 ('06FT1CZSETZPGX9F5747TDBA7W', 'description', 'GAP FLAGGED BY QATUX during the 2026-07-26/27 reconciliation, and it is real: two substantive design rulings from the pair session exist ONLY in a scratchlog under /tmp, with no governance record backing them. D-258 covers rung-0.5; neither of these is in it. (A) ENCLOSED SETTLEMENTS. Non-breathable worlds get enclosed settlements -- point locations on the Atlas, interiors deferred to a separate generator, handled like embedded stations. Rationale: D-220''s density model is physically inapplicable to a pressure vessel, not merely mistuned (it measures people per km2 of footprint driven by utility cost per metre of street; run airless it yields 1,500 ppl/km2 of open-air streets in vacuum). The handoff mechanism already exists: D-166''s door boundary contract -- a dome''s airlock IS that door. Branch on an explicit property, NOT the atmosphere string (leaves room for enclosed settlements on breathable worlds: hostile-biome research posts, undersea, arcologies). Jeroen ruled folds into enclosed; by the same logic and must too, making the rule a single condition -- open-air requires atmosphere == ''standard'', everything else enclosed. NULL atmosphere defaults to enclosed (fails safe). Split: 231 open / 38 enclosed across 269 in-bounds populated bodies. Atmosphere keeps its full range for the SURFACE-EXPEDITION layer (Jeroen: ''surface expeditions will play differently'') -- the binary collapse applies only to settlement layout. (B) SOL GENERATOR SCOPE. See T-1219..T-1221 for the implementation; the DESIGN has no record: the GeneratorScope enum shape (Procedural | Authored { deepest_rung }), the DLC gate being a DEPTH rather than a boolean, no new wire status because ''viewable at Global, no deeper'' IS the existing rung-liveness path, and the ruling that Sol art is an authored DATA CANVAS rather than a finished image (rides the existing wire, no new client draw path, same map-art function as all 269 procedural bodies). Claim ids with {"id":"D-259","domain":"architecture","title":"..."} before writing.', 'GAP FLAGGED BY QATUX during the 2026-07-26/27 reconciliation, and it is real: two substantive design rulings from the pair session exist ONLY in a scratchlog under /tmp, with no governance record backing them. D-258 covers rung-0.5; neither of these is in it.
(A) ENCLOSED SETTLEMENTS. Non-breathable worlds get enclosed settlements -- point locations on the Atlas, interiors deferred to a separate generator, handled like embedded stations. Rationale: D-220''s density model is physically inapplicable to a pressure vessel, not merely mistuned (it measures people per km2 of footprint driven by utility cost per metre of street; run airless it yields 1,500 ppl/km2 of open-air streets in vacuum). The handoff mechanism already exists -- D-166''s door boundary contract, where a dome''s airlock IS that door. Branch on an explicit ''enclosed'' PROPERTY, not on the atmosphere string, which leaves room for enclosed settlements on breathable worlds (hostile-biome research posts, undersea, arcologies) with no second code path.
Jeroen ruled that ''thin'' folds into enclosed. By the same logic ''thick'' (Venus-like) and ''reducing'' (toxic) must too -- neither is open-air -- which collapses the rule to a single condition: open-air requires atmosphere == standard, everything else is enclosed. NULL atmosphere defaults to enclosed, which fails safe (an enclosed settlement on a breathable world is merely conservative; an open one in vacuum is absurd). Split: 231 open / 38 enclosed across 269 in-bounds populated bodies. NOTE the thick/reducing extension is the lead''s inference from Jeroen''s thin ruling, not his words -- confirm before writing.
Atmosphere keeps its full expressive range for the SURFACE-EXPEDITION layer (Jeroen: ''surface expeditions will play differently'' -- thin gives suit + dust storms + weather; none gives hard vacuum, no weather, extreme swings, radiation). The binary collapse applies ONLY to settlement layout. Record that, so the collapse is not later mistaken for atmosphere being unimportant.
(B) SOL GENERATOR SCOPE. T-1219..T-1221 carry the implementation; the DESIGN has no record. Needs capturing: the GeneratorScope enum shape (Procedural | Authored with a deepest_rung), the DLC gate being a DEPTH rather than a boolean (a Sol expansion deepens the ladder by changing one value), the absence of any new wire status because ''viewable at Global, no deeper'' IS the existing rung-liveness path rather than an error to report, and the ruling that Sol art is an authored DATA CANVAS rather than a finished image -- it rides the existing wire untouched, needs no new client draw path, and goes through the same map-art function as all 269 procedural bodies. The framing Jeroen accepted: you are not looking at a photograph of Earth, you are looking at your implant rendering Earth in the same visual language it renders everything else.
Also record that Sol bodies carry dangling terrain_reference rows pointing at heightmaps deliberately never baked (import_heightmaps.py explicitly excludes Sol), so a real Earth or Mars DEM dropped at that exact path would silently start generating a procedural homeworld with no gate to stop it.
Claim ids with ''pql decisions claim D architecture ...'' before writing.', NULL, '2026-07-26 23:13:40', '2026-07-26 23:13:40.114', '2026-07-26 23:13:40.114', NULL, '7c7563a0fa9899b39ec05877ab33b0cb', 2) ON CONFLICT(hash) DO NOTHING;
+3
View File
@@ -143,3 +143,6 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0VDA5Q7JZFXG7XYWFR6M3C', 'T-1227', '2026-07-26 21:56:09.390', '2026-07-26 21:56:09.390', NULL, 'f2917021939a93d921bbbc34111fba71', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0VDJPQWHQZ73WRB7KM56QW', 'T-1228', '2026-07-26 21:56:11.573', '2026-07-26 21:56:11.573', NULL, 'c380e75611d0835950ecbed7ca75f862', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0VERS6AKQHB30Y1RSRW1FM', 'T-1229', '2026-07-26 21:56:21.321', '2026-07-26 21:56:21.321', NULL, 'bad1b79b90c5e25a8bace4e4dfc383d8', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT1CXPZ8HH73P67G6R98NV6M', 'T-1230', '2026-07-26 23:12:40.186', '2026-07-26 23:12:40.186', NULL, 'e915b5b01c1970246c33217b337b6d7e', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT1CZSETZPGX9F5747TDBA7W', 'T-1231', '2026-07-26 23:12:57.211', '2026-07-26 23:12:57.211', NULL, '54cbe170611367610549c31882930676', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT1D64MS28Z2BGV4Z771JRQG', 'T-1232', '2026-07-26 23:13:49.222', '2026-07-26 23:13:49.222', NULL, 'c5a8956d41c58cd2a29decf840ebdc77', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
+28
View File
@@ -5496,3 +5496,31 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat
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 ('06FT0VDA5Q7JZFXG7XYWFR6M3C', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'step_canvas_viewer.gd is 1,133 lines against the 1,000-line gdlint cap', 'Pre-existing lint-cap violation, made worse by the 2026-07-26 pair session''s fetch-overlay wiring work (still net additive to a file already over cap). Split step_canvas_viewer.gd so it comes back under the 1,000-line gdlint limit -- likely candidate: extract the fetch-overlay wiring / cache-store plumbing into a separate component now that step_canvas_fetch_overlay.gd exists as a dedicated node, reducing what step_canvas_viewer.gd itself needs to own directly. Verify gdlint passes clean after the split and that the viewer test suite (92/92 as of this session) stays green.', 'backlog', 'low', NULL, NULL, NULL, '2026-07-26 21:56:09.390', '2026-07-26 21:56:09.390', NULL, '268d5f4b86aa39d08661b88b50fee029', 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 ('06FT0VDJPQWHQZ73WRB7KM56QW', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Remove unreferenced DISPLAY_RATIO_SHALLOW constant', 'DISPLAY_RATIO_SHALLOW (step_canvas_transport.gd) is unreferenced since the display-ratio band went uniform at 2x2 across every rung (D-255 2026-07-26 amendment, item 4 -- superseding the old ''1x1 ideal to >=5x5 acceptable'' banded model). Delete the dead constant and confirm no remaining references (grep the client tree, not just the transport file, in case a test or a comment still cites it).', 'backlog', 'low', NULL, NULL, NULL, '2026-07-26 21:56:11.573', '2026-07-26 21:56:11.573', NULL, '3f03f56deae9c61866af0da2759c7d30', 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 ('06FT0VERS6AKQHB30Y1RSRW1FM', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Regenerate Atlas visual-capture goldens (defer until rung-0.5 + scale-constant work lands)', 'DEFERRED BY DESIGN -- do not start this until BOTH T-1211 (D-258 rung-0.5 epic) and T-1218 (scale-constant change) have landed, so goldens are regenerated once rather than twice. Two independent reasons goldens are already known-stale from the 2026-07-26 pair session alone, before rung-0.5/scale-constants even land: (1) the extent inversion + uniform 2x2 display ratio changed every rung''s canvas size and sampling, so atlas_*_District / atlas_*_Global visual-capture goldens (tests/visual.json via _setup_atlas_golden_shot) no longer match; (2) existing disk-cache entries hold 1x1-sized canvases from before the 2x2 gridunit change -- these age out naturally via the cache key (a different extent is a different cache key, per D-227''s version-tag/eviction discipline), so NO manual purge is needed there, but the goldens themselves still need hand regeneration since they are golden-image fixtures, not cache entries. Run make fixtures-gauntlet + the tests/run-visual golden regeneration flow once rung-0.5 and the scale constants are both live, then do a full eyeball pass, not just a pixel-diff, since both the data source (rung 0.5) and the metre constants (district/region) will have changed underneath the images in the same window. See governance/decisions/architecture.md#d-258 and the pair-session scratchlog 2026-07-26 backfile checklist.', 'backlog', 'medium', NULL, NULL, 'D-258', '2026-07-26 21:56:21.321', '2026-07-26 21:56:21.321', NULL, '61b17c35456103cc1663077c9710f96b', 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 ('06FT0V9F4MNF4FZ44KJMKTXJ0G', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'tests/run-godot reports false PASS when a suite fails to parse (total: 0)', 'Bit the team during the 2026-07-26 pair session and should be fixed BEFORE the larger D-258/scale-constant work lands, since that work will touch a lot of test files and this bug actively masks failures during exactly that kind of change. tests/run-godot currently reports ''Tests passed'' when a gdUnit4 suite fails to parse entirely (e.g. a syntax error in a test file) -- the harness sees a run with total: 0 tests and treats zero-failures-because-zero-tests as green, rather than treating ''a suite that should have contributed tests contributed none'' as a hard failure. Fix: total: 0 for a suite that was expected to run must be a FAIL, not a silent pass-through. Needs a regression test: intentionally break a test file''s parse and assert the harness reports failure, not success.
FIXED 2026-07-27 (commits b2ef73256, 263a98f3e). Three bugs, not one. (1) load_error/no_tests guards added: a suite that fails to LOAD, or a run that executes zero tests, are now harness failures with a harness_error JSON field and exit 2 -- the exit code cannot inherit gdUnit4''s, which returns 0 in both states. Verified by injecting each failure, including the case that actually bit: one broken file among many, where total stays large and failed stays zero. (2) DOUBLE COUNT: the parser matched gdUnit4''s per-suite ''Statistics:'' lines AND the ''Overall Summary:'' line that already sums them, so every total was exactly 2x -- the real suite is 1830 tests, not 3660. (3) ANSI escapes sit between summary fields, so patterns on the raw log fell through to a weaker fallback that could not see skips and reported a fully-skipped suite as 26 FAILED. All parsing now runs on a de-ANSI''d copy; skipped is parsed, surfaced, and excluded from passed.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-26 21:55:37.893', '2026-07-26 23:12:10.402', NULL, '2d81d19695b6b5f32442af48101585ca', 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 ('06FT0V9Y08S6XNMB04JJTGQP44', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'tooling/godot-cold-parse does not cover client/tests/, so a broken test file reads clean twice over', 'Companion bug to the tests/run-godot false-green (sibling ticket) -- both bit the team in the same 2026-07-26 pair session, and together they mean a broken test file can pass BOTH the cold-parse gate and the test run with no signal anywhere. tooling/godot-cold-parse currently scans production/source directories but does not include client/tests/ in its parse-check sweep, so a syntax error introduced in a test file is invisible to the cold-parse gate (which would have caught it) AND to the test run (which reports total: 0 as green per the sibling ticket). Fix: extend godot-cold-parse''s scan set to include client/tests/. Do this ticket and its sibling FIRST, ahead of the D-258/scale-constant work (T-1211/T-1218), since that work touches a large volume of test files and needs both gates trustworthy going in.
FIXED 2026-07-27 (commits a005e4840, bf1976613) -- but the premise in this title is TOO NARROW. godot-cold-parse does not miss client/tests/ specifically; it only ever sees scripts on the STARTUP path (autoloads + main scene chain). Verified by breaking a non-startup UI script (step_canvas_legend.gd) and a test file in turn: it reported ''clean'', exit 0, for both. It also filters ''Cannot infer the type'', which had hidden a genuinely unparseable file for five months. Fix shipped as a NEW tool rather than a widened filter: tooling/godot-parse-sweep + client/tools/parse_sweep.gd open all 226 project scripts and fail on any that do not parse. Enforced in the pre-push hook AHEAD of the test suite, so a parse failure costs ~4s instead of ~135s (the hook never ran cold-parse at all, so nothing previously enforced this). The two-part split is forced: no Godot API reports GDScript parse failure reliably -- CACHE_MODE_IGNORE segfaults the engine on a broken script, GDScript.new()+reload() false-positived 150 of 226 healthy scripts, and plain load() returns non-null for a broken one. The engine''s stderr is the only honest signal. First run found client/tests/util/scene_helper.gd unparseable since 2026-02-25.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-26 21:55:41.698', '2026-07-26 23:12:22.665', NULL, '8f2ac9a25583f0301b89a375c394b50d', 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 ('06FT0V9F4MNF4FZ44KJMKTXJ0G', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'tests/run-godot reports false PASS when a suite fails to parse (total: 0)', 'Bit the team during the 2026-07-26 pair session and should be fixed BEFORE the larger D-258/scale-constant work lands, since that work will touch a lot of test files and this bug actively masks failures during exactly that kind of change. tests/run-godot currently reports ''Tests passed'' when a gdUnit4 suite fails to parse entirely (e.g. a syntax error in a test file) -- the harness sees a run with total: 0 tests and treats zero-failures-because-zero-tests as green, rather than treating ''a suite that should have contributed tests contributed none'' as a hard failure. Fix: total: 0 for a suite that was expected to run must be a FAIL, not a silent pass-through. Needs a regression test: intentionally break a test file''s parse and assert the harness reports failure, not success.
FIXED 2026-07-27 (commits b2ef73256, 263a98f3e). Three bugs, not one. (1) load_error/no_tests guards added: a suite that fails to LOAD, or a run that executes zero tests, are now harness failures with a harness_error JSON field and exit 2 -- the exit code cannot inherit gdUnit4''s, which returns 0 in both states. Verified by injecting each failure, including the case that actually bit: one broken file among many, where total stays large and failed stays zero. (2) DOUBLE COUNT: the parser matched gdUnit4''s per-suite ''Statistics:'' lines AND the ''Overall Summary:'' line that already sums them, so every total was exactly 2x -- the real suite is 1830 tests, not 3660. (3) ANSI escapes sit between summary fields, so patterns on the raw log fell through to a weaker fallback that could not see skips and reported a fully-skipped suite as 26 FAILED. All parsing now runs on a de-ANSI''d copy; skipped is parsed, surfaced, and excluded from passed.', 'done', 'high', NULL, NULL, NULL, '2026-07-26 21:55:37.893', '2026-07-26 23:12:26.261', NULL, 'e0076aba35093efb329ac20bea78997e', 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 ('06FT0V9Y08S6XNMB04JJTGQP44', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'tooling/godot-cold-parse does not cover client/tests/, so a broken test file reads clean twice over', 'Companion bug to the tests/run-godot false-green (sibling ticket) -- both bit the team in the same 2026-07-26 pair session, and together they mean a broken test file can pass BOTH the cold-parse gate and the test run with no signal anywhere. tooling/godot-cold-parse currently scans production/source directories but does not include client/tests/ in its parse-check sweep, so a syntax error introduced in a test file is invisible to the cold-parse gate (which would have caught it) AND to the test run (which reports total: 0 as green per the sibling ticket). Fix: extend godot-cold-parse''s scan set to include client/tests/. Do this ticket and its sibling FIRST, ahead of the D-258/scale-constant work (T-1211/T-1218), since that work touches a large volume of test files and needs both gates trustworthy going in.
FIXED 2026-07-27 (commits a005e4840, bf1976613) -- but the premise in this title is TOO NARROW. godot-cold-parse does not miss client/tests/ specifically; it only ever sees scripts on the STARTUP path (autoloads + main scene chain). Verified by breaking a non-startup UI script (step_canvas_legend.gd) and a test file in turn: it reported ''clean'', exit 0, for both. It also filters ''Cannot infer the type'', which had hidden a genuinely unparseable file for five months. Fix shipped as a NEW tool rather than a widened filter: tooling/godot-parse-sweep + client/tools/parse_sweep.gd open all 226 project scripts and fail on any that do not parse. Enforced in the pre-push hook AHEAD of the test suite, so a parse failure costs ~4s instead of ~135s (the hook never ran cold-parse at all, so nothing previously enforced this). The two-part split is forced: no Godot API reports GDScript parse failure reliably -- CACHE_MODE_IGNORE segfaults the engine on a broken script, GDScript.new()+reload() false-positived 150 of 226 healthy scripts, and plain load() returns non-null for a broken one. The engine''s stderr is the only honest signal. First run found client/tests/util/scene_helper.gd unparseable since 2026-02-25.', 'done', 'high', NULL, NULL, NULL, '2026-07-26 21:55:41.698', '2026-07-26 23:12:26.269', NULL, '7b2d5b71e7c49d5f02a77fde39dd7a5f', 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 ('06FT1CXPZ8HH73P67G6R98NV6M', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Re-enable test_character_visual_sprint28 and fix its per-test compositor rebuild', 'SKIPPED 2026-07-27 (commit 791600dd2) via gdUnit4 __is_skipped, on Jeroen''s call, because 3D character representation is not under active work and this one suite was 37% of the whole client run. Client suite went 135s -> 96s from this file alone. Measured across all 86 suites: this suite 50.0s / 26 tests (~1.9s each); all suites 127.1s / 1830 tests; the other 78 suites ~33s / 1804 tests. The cost is asset loading PER TEST -- every test instantiates a fresh CharacterVisual Node3D and loads the skeleton .glb plus body/skin-tone assets. Two things to do here, in order: (1) fix the cause -- build the compositor once per suite (before()) or cache the loaded resources, instead of rebuilding per test; (2) delete the _init() in the suite to re-enable. Nothing else was changed: every test is intact and was passing when skipped. MUST be re-enabled no later than Phase 5 player rendering, when this code is live again. Prefer fixing the setup over leaving it skipped for cost -- a permanently skipped suite is dead coverage that will rot silently. Note run-godot now surfaces skipped as its own JSON field and excludes it from passed, so the 26 stay visible in every run.', 'backlog', 'medium', NULL, 'client', NULL, '2026-07-26 23:12:40.186', '2026-07-26 23:12:40.186', NULL, '2a4d9262945c68441387e624b5fc920c', 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 ('06FT1CZSETZPGX9F5747TDBA7W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Record the Sol GeneratorScope + enclosed-settlement rulings as D-records', 'GAP FLAGGED BY QATUX during the 2026-07-26/27 reconciliation, and it is real: two substantive design rulings from the pair session exist ONLY in a scratchlog under /tmp, with no governance record backing them. D-258 covers rung-0.5; neither of these is in it. (A) ENCLOSED SETTLEMENTS. Non-breathable worlds get enclosed settlements -- point locations on the Atlas, interiors deferred to a separate generator, handled like embedded stations. Rationale: D-220''s density model is physically inapplicable to a pressure vessel, not merely mistuned (it measures people per km2 of footprint driven by utility cost per metre of street; run airless it yields 1,500 ppl/km2 of open-air streets in vacuum). The handoff mechanism already exists: D-166''s door boundary contract -- a dome''s airlock IS that door. Branch on an explicit property, NOT the atmosphere string (leaves room for enclosed settlements on breathable worlds: hostile-biome research posts, undersea, arcologies). Jeroen ruled folds into enclosed; by the same logic and must too, making the rule a single condition -- open-air requires atmosphere == ''standard'', everything else enclosed. NULL atmosphere defaults to enclosed (fails safe). Split: 231 open / 38 enclosed across 269 in-bounds populated bodies. Atmosphere keeps its full range for the SURFACE-EXPEDITION layer (Jeroen: ''surface expeditions will play differently'') -- the binary collapse applies only to settlement layout. (B) SOL GENERATOR SCOPE. See T-1219..T-1221 for the implementation; the DESIGN has no record: the GeneratorScope enum shape (Procedural | Authored { deepest_rung }), the DLC gate being a DEPTH rather than a boolean, no new wire status because ''viewable at Global, no deeper'' IS the existing rung-liveness path, and the ruling that Sol art is an authored DATA CANVAS rather than a finished image (rides the existing wire, no new client draw path, same map-art function as all 269 procedural bodies). Claim ids with {"id":"D-259","domain":"architecture","title":"..."} before writing.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-26 23:12:57.206', '2026-07-26 23:12:57.206', NULL, '218dafced176867270e66a8962e4d62f', 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 ('06FT1CZSETZPGX9F5747TDBA7W', 'task', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Record the Sol GeneratorScope + enclosed-settlement rulings as D-records', 'GAP FLAGGED BY QATUX during the 2026-07-26/27 reconciliation, and it is real: two substantive design rulings from the pair session exist ONLY in a scratchlog under /tmp, with no governance record backing them. D-258 covers rung-0.5; neither of these is in it.
(A) ENCLOSED SETTLEMENTS. Non-breathable worlds get enclosed settlements -- point locations on the Atlas, interiors deferred to a separate generator, handled like embedded stations. Rationale: D-220''s density model is physically inapplicable to a pressure vessel, not merely mistuned (it measures people per km2 of footprint driven by utility cost per metre of street; run airless it yields 1,500 ppl/km2 of open-air streets in vacuum). The handoff mechanism already exists -- D-166''s door boundary contract, where a dome''s airlock IS that door. Branch on an explicit ''enclosed'' PROPERTY, not on the atmosphere string, which leaves room for enclosed settlements on breathable worlds (hostile-biome research posts, undersea, arcologies) with no second code path.
Jeroen ruled that ''thin'' folds into enclosed. By the same logic ''thick'' (Venus-like) and ''reducing'' (toxic) must too -- neither is open-air -- which collapses the rule to a single condition: open-air requires atmosphere == standard, everything else is enclosed. NULL atmosphere defaults to enclosed, which fails safe (an enclosed settlement on a breathable world is merely conservative; an open one in vacuum is absurd). Split: 231 open / 38 enclosed across 269 in-bounds populated bodies. NOTE the thick/reducing extension is the lead''s inference from Jeroen''s thin ruling, not his words -- confirm before writing.
Atmosphere keeps its full expressive range for the SURFACE-EXPEDITION layer (Jeroen: ''surface expeditions will play differently'' -- thin gives suit + dust storms + weather; none gives hard vacuum, no weather, extreme swings, radiation). The binary collapse applies ONLY to settlement layout. Record that, so the collapse is not later mistaken for atmosphere being unimportant.
(B) SOL GENERATOR SCOPE. T-1219..T-1221 carry the implementation; the DESIGN has no record. Needs capturing: the GeneratorScope enum shape (Procedural | Authored with a deepest_rung), the DLC gate being a DEPTH rather than a boolean (a Sol expansion deepens the ladder by changing one value), the absence of any new wire status because ''viewable at Global, no deeper'' IS the existing rung-liveness path rather than an error to report, and the ruling that Sol art is an authored DATA CANVAS rather than a finished image -- it rides the existing wire untouched, needs no new client draw path, and goes through the same map-art function as all 269 procedural bodies. The framing Jeroen accepted: you are not looking at a photograph of Earth, you are looking at your implant rendering Earth in the same visual language it renders everything else.
Also record that Sol bodies carry dangling terrain_reference rows pointing at heightmaps deliberately never baked (import_heightmaps.py explicitly excludes Sol), so a real Earth or Mars DEM dropped at that exact path would silently start generating a procedural homeworld with no gate to stop it.
Claim ids with ''pql decisions claim D architecture ...'' before writing.', 'backlog', 'high', NULL, 'server', NULL, '2026-07-26 23:12:57.206', '2026-07-26 23:13:40.114', NULL, '53a08ddb97ea73677337c7a50dd1cc05', 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 ('06FT1D64MS28Z2BGV4Z771JRQG', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'client/tests/util/scene_helper.gd has no importers -- dead test utility', 'Found by the new parse sweep on 2026-07-27. This helper had not PARSED since 2026-02-25 -- five months -- because a lambda used ''func(a := null, ...)'', and := cannot infer a type from null. Fixed in commit a005e4840 with explicit '': Variant'' params. But the reason it went unnoticed for five months is that NOTHING IMPORTS IT: a grep across client/tests/ returns zero consumers. So the blast radius of the breakage was zero, and the file is dead code. Decide: delete it, or adopt it (it wraps signal-emission monitoring for scene tests, which several suites currently hand-roll). Not deleted unilaterally -- deleting code is the user''s call. Low priority; it is inert either way now that it parses.', 'backlog', 'low', NULL, 'client', NULL, '2026-07-26 23:13:49.222', '2026-07-26 23:13:49.222', NULL, '336371298ea7b847684b5e093ef1b299', 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);