From 6547482e6d0c5c94227e16aafa79b305520cfc53 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Mon, 27 Jul 2026 00:18:47 +0200 Subject: [PATCH] docs(meta): reconcile CLAUDE.md + CHANGELOG with the extent inversion; file 19 tickets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md's Phase-4 row described the pre-inversion ladder — "every step a server-derived data canvas at its native gridunit spacing" — which the D-255 amendment reversed. Corrected, with rung 0.5 noted as ruled (D-258) but not implemented rather than restated there. The D-243 scale-ladder section is deliberately untouched: scale.rs still holds the old constants, so it is still accurate, and amending it now would make it wrong in the other direction. CHANGELOG gains three player-facing entries for today's shipped work, with the whole-body-map fix carrying an explicit "still open: no rivers or lakes yet" caveat so it does not read as finished. Tickets T-1211..T-1229 filed: the rung-0.5 epic with its cost measurement gating every child, the scale-constant change, Sol's GeneratorScope, the two test-harness false greens, the make-atlas shutdown bug, two data gaps and three cleanups. Golden regeneration is blocked on both the rung-0.5 epic and the scale-constant change so the revalidation is paid once. Review corrections applied to the delegated pass: - The blocker graph was reported but never created — all 8 claimed edges were absent. Added. `pql ticket list --under T-1211 --unblocked` now correctly returns only the measurement, which was the structural point of the epic. - A changelog entry credited T-1206, which is an unrelated open bug about synthetic settlements landing in open water. Re-attributed to the D-255 amendment. - Tickets have no --decision link to D-258/D-255. Not repairable: --decision exists only on `ticket new` and `refine write` rejects it. Filed upstream as pql FR-5 rather than worked around; the descriptions reference the records in prose meanwhile. Pair session with Jeroen, 2026-07-27. Co-Authored-By: Claude --- .pql/changelog/ticket_deps/2026-07.sql | 10 ++++++ .pql/changelog/ticket_idmap/2026-07.sql | 19 ++++++++++ .pql/changelog/tickets/2026-07.sql | 46 +++++++++++++++++++++++++ CHANGELOG.md | 3 ++ CLAUDE.md | 2 +- 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/.pql/changelog/ticket_deps/2026-07.sql b/.pql/changelog/ticket_deps/2026-07.sql index 58abc7f06..00ec4ca37 100644 --- a/.pql/changelog/ticket_deps/2026-07.sql +++ b/.pql/changelog/ticket_deps/2026-07.sql @@ -36,3 +36,13 @@ INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updat INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0TZC9MJV6KZTSRVRYQ327M', '06FT0V0WBYS20XNMEB67A6AT8G', '2026-07-26 21:56:31.307', '2026-07-26 21:56:31.307', NULL, '062b9827a6527aab503aba3c441bf386', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V04XYBWC93Q1QTETMSQE8', '06FT0V0WBYS20XNMEB67A6AT8G', '2026-07-26 21:56:31.327', '2026-07-26 21:56:31.327', NULL, '74dfdbce427031866b673b86c60a19b8', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V0WBYS20XNMEB67A6AT8G', '06FT0V18KE3QPZ8CYRTVD883VR', '2026-07-26 21:56:31.473', '2026-07-26 21:56:31.473', NULL, 'd45e5ceb46fd5629d9f08915cbe1cb70', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0TYBD74TQNVKTJMKA8D9KM', '06FT0TZC9MJV6KZTSRVRYQ327M', '2026-07-26 21:56:27.160', '2026-07-26 22:17:06.842', NULL, '15484358b8b1d1afe14dc26bd9f89022', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0TYBD74TQNVKTJMKA8D9KM', '06FT0V04XYBWC93Q1QTETMSQE8', '2026-07-26 21:56:27.189', '2026-07-26 22:17:06.859', NULL, 'ea3b05fe575cdf02d4beb90886042e43', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0TZC9MJV6KZTSRVRYQ327M', '06FT0V0WBYS20XNMEB67A6AT8G', '2026-07-26 21:56:31.307', '2026-07-26 22:17:12.987', NULL, '812bf25c281d2a72622cd18b6a4901d4', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V04XYBWC93Q1QTETMSQE8', '06FT0V0WBYS20XNMEB67A6AT8G', '2026-07-26 21:56:31.327', '2026-07-26 22:17:13.003', NULL, 'ed905d7a1a4f31f78ca2249776682774', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V0WBYS20XNMEB67A6AT8G', '06FT0V18KE3QPZ8CYRTVD883VR', '2026-07-26 21:56:31.473', '2026-07-26 22:17:13.016', NULL, 'e556a782d5ac357d0cc3d2160ba9aa74', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V04XYBWC93Q1QTETMSQE8', '06FT0V2JB7MEK355XKDSF7E7KC', '2026-07-26 22:17:13.028', '2026-07-26 22:17:13.028', NULL, 'a2f77ad889a6cb696588ef9e36da392f', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V7D4VQ6YHAWGFH472ZAJM', '06FT0V8B51YH76EGJ3A3RTRTHC', '2026-07-26 21:55:31.292', '2026-07-26 22:17:17.620', NULL, 'c7b6cae5972d7d9d02db873db02c1407', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V7D4VQ6YHAWGFH472ZAJM', '06FT0VCC9809R320XQ28RNWXN4', '2026-07-26 21:56:03.787', '2026-07-26 22:17:17.637', NULL, '9f360f6ebe6de1ab57fa033f445d03ae', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0TX2W0BA10PRR7NMJ2362M', '06FT0VERS6AKQHB30Y1RSRW1FM', '2026-07-26 21:56:23.415', '2026-07-26 22:17:17.650', NULL, 'd3311889283355a96b181d15ab94d904', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); +INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FT0V4HQKPYPVQYAKYVFK68C4', '06FT0VERS6AKQHB30Y1RSRW1FM', '2026-07-26 21:56:23.446', '2026-07-26 22:17:17.664', NULL, 'f39feaa76cb9202261b5617e7a9351ec', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash); diff --git a/.pql/changelog/ticket_idmap/2026-07.sql b/.pql/changelog/ticket_idmap/2026-07.sql index 368688ce1..f4051f60b 100644 --- a/.pql/changelog/ticket_idmap/2026-07.sql +++ b/.pql/changelog/ticket_idmap/2026-07.sql @@ -124,3 +124,22 @@ 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 ('06FSWGG5738Z62MBWD4NQ9D2T0', 'T-1208', '2026-07-26 11:49:14.680', '2026-07-26 11:49:14.680', NULL, 'edb9af23869cbdd56223d62060ec1e43', 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 ('06FSWGHKXRFDPRZQC3VF49KSC8', 'T-1209', '2026-07-26 11:49:26.638', '2026-07-26 11:49:26.638', NULL, 'fc68169cd09d2189576d3dd9d94500db', 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 ('06FSWYQA1XKYKEQ114CZKEDAFW', 'T-1210', '2026-07-26 12:51:23.282', '2026-07-26 12:51:23.282', NULL, 'ee984bb4262b08362e4d3908acbfab6b', 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 ('06FT0TX2W0BA10PRR7NMJ2362M', 'T-1211', '2026-07-26 21:53:56.450', '2026-07-26 21:53:56.450', NULL, '63ee275e3e0d6b3342bbca332d111eed', 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 ('06FT0TYBD74TQNVKTJMKA8D9KM', 'T-1212', '2026-07-26 21:54:06.825', '2026-07-26 21:54:06.825', NULL, 'c4170dbf0f88966e08c0c285d2301188', 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 ('06FT0TZC9MJV6KZTSRVRYQ327M', 'T-1213', '2026-07-26 21:54:15.246', '2026-07-26 21:54:15.246', NULL, 'a4d9e76d806627da475edcdb192bdd94', 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 ('06FT0V04XYBWC93Q1QTETMSQE8', 'T-1214', '2026-07-26 21:54:21.551', '2026-07-26 21:54:21.551', NULL, '04ece9d71a6972193aa7a3a4eaef19bf', 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 ('06FT0V0WBYS20XNMEB67A6AT8G', 'T-1215', '2026-07-26 21:54:27.552', '2026-07-26 21:54:27.552', NULL, 'e7325d9eb5234978f133b210aeafa277', 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 ('06FT0V18KE3QPZ8CYRTVD883VR', 'T-1216', '2026-07-26 21:54:30.688', '2026-07-26 21:54:30.688', NULL, '9b341a1a5f019aec7ed8190a253f8c61', 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 ('06FT0V2JB7MEK355XKDSF7E7KC', 'T-1217', '2026-07-26 21:54:41.369', '2026-07-26 21:54:41.369', NULL, '35c10b6528dd41efe5a3b79a99843914', 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 ('06FT0V4HQKPYPVQYAKYVFK68C4', 'T-1218', '2026-07-26 21:54:57.602', '2026-07-26 21:54:57.602', NULL, 'b5acc5bf904cfcbc04470460d4b82f5b', 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 ('06FT0V6GH94XKAVWXRR5AP9FSG', 'T-1219', '2026-07-26 21:55:13.679', '2026-07-26 21:55:13.679', NULL, '6a1dcc32f2c93c3e18415c2e65ede3e1', 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 ('06FT0V7D4VQ6YHAWGFH472ZAJM', 'T-1220', '2026-07-26 21:55:21.003', '2026-07-26 21:55:21.003', NULL, '78b9d8b6f83d6eac0a52de1732f7ccc4', 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 ('06FT0V8B51YH76EGJ3A3RTRTHC', 'T-1221', '2026-07-26 21:55:28.680', '2026-07-26 21:55:28.680', NULL, '50b89e6dab1e07e61c9943908a5cae0c', 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 ('06FT0V9F4MNF4FZ44KJMKTXJ0G', 'T-1222', '2026-07-26 21:55:37.894', '2026-07-26 21:55:37.894', NULL, '84aeefae8d273e2a13a30a04b25fccb9', 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 ('06FT0V9Y08S6XNMB04JJTGQP44', 'T-1223', '2026-07-26 21:55:41.698', '2026-07-26 21:55:41.698', NULL, 'e4dec700ac155cf6d215e61434eb1a08', 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 ('06FT0VAZ0RV0FQZMSTB13QP56M', 'T-1224', '2026-07-26 21:55:50.150', '2026-07-26 21:55:50.150', NULL, 'ec2d2460d530dad49eb96614ad6a2797', 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 ('06FT0VBSVB8BRZXWQKV46N8S20', 'T-1225', '2026-07-26 21:55:57.018', '2026-07-26 21:55:57.018', NULL, '176075de47ec33f5c266a6446a6d8369', 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 ('06FT0VCC9809R320XQ28RNWXN4', 'T-1226', '2026-07-26 21:56:01.739', '2026-07-26 21:56:01.739', NULL, 'c3a25a121040f67906392273406f40bd', 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 ('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); diff --git a/.pql/changelog/tickets/2026-07.sql b/.pql/changelog/tickets/2026-07.sql index c25a8ebbf..af28395f5 100644 --- a/.pql/changelog/tickets/2026-07.sql +++ b/.pql/changelog/tickets/2026-07.sql @@ -5450,3 +5450,49 @@ test flaked once under full-suite parallel load, confirmed unrelated and non-reproducible in isolation / module-scoped / --lib-only runs, and absent on a clean full-suite rerun). No golden fixtures changed.', 'review', 'medium', 'dudley', 'server', NULL, '2026-07-26 11:33:42.064', '2026-07-26 13:23:59.368', NULL, 'd6affd4f4bef642ca148ee93e6a80a84', 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 ('06FSP3GEJYTA966BSSR9354YC4', 'task', '06FBPPMZNNEV052DBYYY3A897C', 'Atlas header readout ghosted in Quarter capture — rung-transition alpha artifact', 'Araminta''s T-1196 secondary observation (first GJ1002b captures, 2026-07-25): the ''ATLAS - CAPARICA / QUARTER'' header readout above the legend rendered faded/ghosted in the Quarter frame (scratchpad t1196/gj1002b_quarter.png), while the District and Region frames from the same session and capture path rendered it fully legible. Suspect a z-order or alpha transition state caught mid-render on rung change (the capture settles on StepCanvasRequest.is_pending() then waits 2 frames — a header fade animation may outlast the data settle). Reproduce with a T-1157-harness jump_to capture at any Quarter rung and inspect header alpha across post-settle frames; if it is a fade animation racing the capture, decide whether the fix is harness-side (settle until animations idle) or UI-side (no fade on rung change). Likely cosmetic; the only frame of the three with a legibility defect.', 'done', 'low', 'stig', 'client', NULL, '2026-07-25 20:53:37.559', '2026-07-26 13:24:15.122', NULL, 'babf8069d36e51be946855ab64334042', 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 ('06FT0TX2W0BA10PRR7NMJ2362M', 'epic', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Rung-0.5 expanded layer — whole-body hydrology + biome-from-orbit base (D-258)', 'D-258: the Atlas cascade gains a single expanded layer (''rung 0.5''), generated once per body from the rung-0 input pair (heightmap.png + reliefmap.png, never displayed), and every zoom tier below it (Region/District/Quarter/Block/Chunk) derives from that layer instead of independently re-deriving from the source files. Hydrology (drainage, course routing, lake fill) resolves ONCE on rung 0.5 and nowhere else -- it is a whole-body computation, not derivable per-window. Global displays rung 0.5 directly (biome-from-orbit, not a photograph). Same-session amendment: lake shorelines run the same shore-morphology code as ocean shorelines (coastline warp applied to both surfaces in the single rung-0.5 pass; shore-morphology gates key on proximity to water, not to ocean; sea-flavored types like TidalFlat/Estuarine-vs-Delta separated by tidal energy, a derived quantity, never by an is-it-the-ocean switch; salinity excluded from morphology entirely). This is a named, principled carve-out from D-227 derive-don''t-store: a whole-body flow solve is not locally computable, so it cannot be re-derived per window at any price -- storage here buys correctness, not convenience. Not yet ticketed prior to this epic (D-258''s own Implementation note). See governance/decisions/architecture.md#d-258.', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:53:56.448', '2026-07-26 21:53:56.448', NULL, 'd7f8fd1b5e2c6fb611f66d9f390f647b', 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 ('06FT0TYBD74TQNVKTJMKA8D9KM', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Measure rung-0.5 cost/size BEFORE implementing (compute + disk, whole-body)', 'MUST run and be reviewed before any other T-1211 child starts -- this measurement could reshape the rung-0.5 design, per the pair session''s explicit sequencing note. Measure, at minimum: (1) per-body derive cost for the expanded layer at a resolution sized so a whole body draws at 2 screen px per gridunit on a large display (the D-258 sizing rule) -- both single-body cold-derive time and the full ~271-body population sum; (2) per-body and total disk footprint if the layer is cached/stored (rung 0.5 is a named D-227 carve-out -- storage is deliberate, but its size must be known, not assumed); (3) whole-body hydrology solve cost on this layer (drainage + course routing + lake fill) at the same resolution, since D-258 requires this to run exactly once per body and nowhere else. Reference point: D-255''s own rung-0 always-keep tier estimate went from ~8.85 MB (measured against a stale ~18K-cell/body figure) to an estimated 226 MB (1080p) / ~900 MB (4K) once the extent inversion made Global viewport-sized -- D-255 amendment item 6 explicitly says ''re-measure against rung 0.5, not against this record.'' This ticket is that re-measurement. Report back to the team before T-1211''s other children are started; if the numbers are structurally bad (e.g. rung 0.5 at the sizing D-258 specifies costs an order of magnitude more than the old rung-0 model), that is grounds to revisit the resolution target with Jeroen before writing generator code. See governance/decisions/architecture.md#d-258 (rationale + Implementation note).', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:54:06.825', '2026-07-26 21:54:06.825', NULL, '75b9060500208310024c7a3b6a773178', 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 ('06FT0TZC9MJV6KZTSRVRYQ327M', 'story', '06FT0TX2W0BA10PRR7NMJ2362M', 'Rung-0.5 expanded-layer generator (deterministic un-summarisation of rung 0)', 'Build the deterministic expansion from rung 0 (heightmap.png 1024x512 16-bit elevation + reliefmap.png 1024x512 colour biome) to rung 0.5, sized per D-258''s 2-px-per-gridunit-at-large-display rule. Same body + same seed must produce the same layer every time (byte-identical, per D-227''s determinism discipline extended by this carve-out). The reliefmap is a PLURALITY, not ground truth: each reliefmap cell already voted-and-discarded the dominant biome across ~38 km of ground, so this generator un-summarises it -- it does not upscale/interpolate it. Three binding invariants from D-258: (1) biome edges are gradients, never lines -- transitions blend so no boundary falls on a rung-0 cell edge (the D-243 climate edge-fuzz rule applied to biome); (2) descending the ladder reveals COMPOSITION not sharpness -- a cell reading ''forest'' globally must be able to contain clearings/marsh/rock/scrub the vote suppressed, emerging deterministically as the ladder descends; (3) CONSERVATIVE invention is the binding acceptance gate -- downsampling rung 0.5 must reproduce the rung-0 summary it came from (a forest cell may gain marsh pockets but must still read as forest from orbit). Blocked on T-1212 (cost/size measurement) landing first. Depends on: nothing else in this epic to start scaffolding, but hydrology (sibling ticket) and this generator are tightly coupled -- coordinate sequencing with whoever picks up hydrology. See governance/decisions/architecture.md#d-258.', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:54:15.245', '2026-07-26 21:54:15.245', NULL, '9f2ab303b04bb6540162e1d14e7cdbf3', 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 ('06FT0V04XYBWC93Q1QTETMSQE8', 'story', '06FT0TX2W0BA10PRR7NMJ2362M', 'Whole-body hydrology resolves on rung 0.5 (drainage, course routing, lake fill)', 'Move hydrology resolution so it runs exactly ONCE per body, on the rung-0.5 layer, and nowhere else. D-258''s structural rationale: drainage/course-routing/lake-fill are whole-body computations (water arrives from upstream and leaves downstream, both off-screen at every rung below Global), so no per-window derivation at Region/District/Quarter/Block/Chunk can produce a coherent water system -- this is why Region currently carries no courses and lakes cannot fill under the old per-rung-independent model. Cross-rung agreement (the coastline zoomed out is the coastline zoomed in; a river crossing a district reaches the sea on the global map) must hold BY CONSTRUCTION once every rung sources from this one solved layer, not by policing two derivation paths against each other. Contradiction to fix in the same pass: Global currently still rides uses_orbital_derive()==true and the orbital path skips the course/water pass entirely (confirmed live in the pair session) -- that is why Global reads flat at any resolution today; this is not a tuning gap, it is a missing pass, and this ticket is what closes it. Blocked on T-1212 (measurement) and coordinates tightly with the expanded-layer generator sibling ticket. See governance/decisions/architecture.md#d-258 and #d-255 (amendment item 3, ''Region carrying no rivers at all was much of why the top of the ladder read flat'').', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:54:21.551', '2026-07-26 21:54:21.551', NULL, '99fe5c570caee2f8e5c7d1ee6328712d', 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 ('06FT0V0WBYS20XNMEB67A6AT8G', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Re-point every Atlas rung to source from rung 0.5, not the raw heightmap/reliefmap', 'step_canvas.rs (and any other rung-derivation entry point) must be re-pointed so Region/District/Quarter/Block/Chunk all derive from the cached rung-0.5 layer rather than resolving heightmap.png/reliefmap.png directly. heightmap.rs (the rung-0 loader) gains the reliefmap as a second input if it does not already load it. This is the ''no deeper rung reads the source files'' invariant from D-258 -- cross-rung visual agreement becomes structural once this lands, rather than enforced by convention. Depends on T-1213 (generator) and T-1214 (hydrology) existing to re-point to. See governance/decisions/architecture.md#d-258 Implementation note (explicitly names step_canvas.rs and heightmap.rs as the consumers to re-point).', 'backlog', 'high', NULL, NULL, 'D-258', '2026-07-26 21:54:27.551', '2026-07-26 21:54:27.551', NULL, 'bef6103908090e515e923e42297856a7', 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 ('06FT0V18KE3QPZ8CYRTVD883VR', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Global rung: biome-from-orbit map-art palette (client)', 'Client map-art function gains a Global-rung palette that reads rung 0.5 as biome viewed from orbit -- simplified colour and texture in the game''s own visual flavour, not a photograph and not a topographic diagram. Hypsometric/elevation shading is a reading aid WITHIN that image, not its subject; biome is the subject. This replaces whatever Global currently draws (still the old orbital-derive hue map per the pair session''s live observation) once T-1215 re-points Global''s data source to rung 0.5. Depends on T-1215. See governance/decisions/architecture.md#d-258 (''Global displays rung 0.5: biomes as viewed from orbit...'').', 'backlog', 'medium', NULL, NULL, 'D-258', '2026-07-26 21:54:30.683', '2026-07-26 21:54:30.683', NULL, '7ad1ba8eced801983c32bdd22a08d557', 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 ('06FT0V2JB7MEK355XKDSF7E7KC', 'task', '06FT0TX2W0BA10PRR7NMJ2362M', 'Lake shores through the shared shore-morphology path (D-258 amendment)', 'Two fixes, both requiring the single rung-0.5 pass (cannot be done by patching lake sampling alone -- a lake is a filled water surface above terrain, two reads that must agree, so displacing one without the other yields water on hillsides or holes in lakes): (1) apply the SAME coastline warp (coast_warp_px / invent_primitives) to lake shores as to ocean shores -- today only ocean coasts get invented bay/cape irregularity, lake shores trace the bare elevation contour and look ''computed'' next to a made-looking sea (district_profile.rs''s lake_from_hydrology_at(ta, px, py) currently reads the UNWARPED position). (2) shore morphology (TidalFlat/DuneStrand/CliffCoast/Estuarine/Delta/Fjord/Wetland -- MorphologyZone, already has every needed vocabulary word, no new terms) must key on proximity to WATER, not to ocean_fraction_q (which is structurally 0 inside a lake basin, making these gates unreachable at lake edges today). Physical discriminators, not an is-it-the-ocean switch: TidalFlat requires actual tidal range (lakes have effectively none, so correctly get no flats); Estuarine vs Delta is a SEDIMENT-BALANCE / tidal-energy outcome, not salinity (a microtidal sea like the Mediterranean gets deltas -- Nile, Rhone, Po -- despite being salt; lakes always resolve to Delta and so does a tideless sea, which an ocean-vs-lake switch would get wrong); dune strands/fjords/cliffs scale with the water body''s size and relief, not its salinity. Salinity is explicitly NOT a morphology input -- parked, derive from below-sea-level connectivity only if and when gameplay needs it. See governance/decisions/architecture.md#d-258 amendment (same date, same session).', 'backlog', 'medium', NULL, NULL, 'D-258', '2026-07-26 21:54:41.369', '2026-07-26 21:54:41.369', NULL, '24ffa637543eaf755c949727e3796dfd', 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 ('06FT0V4HQKPYPVQYAKYVFK68C4', 'story', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Scale-constant change: district 2,048m to 4,096m, region 204,800m to 262,144m (2^18), partial regions legal', 'RULED 2026-07-26 pair session, NOT YET IMPLEMENTED -- scale.rs still holds DISTRICT_M = 2_048 and REGION_M = 204_800 today, so D-243 (governance/decisions/architecture.md#d-243) remains an accurate description of the shipped code and must NOT be amended until this ticket lands. Amend D-243 as part of closing this ticket, not before. + +Changes ruled: +1. REGION_M: 204,800 -> 262,144 (2^18). Was 100 districts (the only non-power-of-2 rung, with a compile-time assert); becomes 64 districts. Whole stair becomes 2^n: subtile 2^-1, tile 2^0, chunk 2^6, block 2^7, quarter 2^9, district 2^12, region 2^18. Region indexing becomes a bit-shift, not a division. +2. DISTRICT_M: 2,048 -> 4,096 (2^12). District becomes 8 quarters (was 4). Rationale is arithmetic: at district 4,096 the region size balancing the top two Atlas zoom steps (post D-255 extent-inversion) is ~300 km, and region at 262,144 lands the steps at 84x/64x; at the old district 2,048 the balance point is ~212 km, landing at 84x/128x -- lopsided. +3. Partial regions become LEGAL. regions_per_equator currently does round(circumference / REGION_M), lying by up to half a region to force an integer fit. Replace with whole regions + a short remainder region. A body under 262 km circumference is one partial region (''minimal 1 region''). +4. Block (128m), Quarter (512m), Chunk (64m) are UNCHANGED -- Jeroen explicitly ruled against doubling them alongside district. Do not touch these three constants. Block: 256m between streets would exceed every real-world city grid (Manhattan ~80m, Barcelona ~113m, Chicago ~100m, Portland ~61m). Quarter: D-220''s density model is calibrated in 0.262 km2 cells; at 1.048 km2 a 695-person hamlet becomes 1/10 of a cell and the smallest settlements stop being resolvable. +5. Province (D-205 irregular watershed/political overlay) is NOT a size and stays OUT of the containment ladder entirely -- do not regularise it to any metre value (32,768m was considered and explicitly declined; Jeroen: ''the hole on the larger end is forgivable''). + +Consequences to carry through: climate re-derivation (region/district are the climate lockdown scale, D-243 §3), golden regeneration, believability re-validation. Should ride WITH the T-1211 (D-258 rung-0.5) epic so this revalidation is paid once rather than twice -- do not schedule independently of that epic''s completion. Amend D-243 (add an amendment section, do not rewrite the base record) once REGION_M/DISTRICT_M are live in scale.rs and the regions_per_equator rounding is replaced. See pair-session scratchlog 2026-07-26, section ''Item 1 -- SUPERSEDED. Full ladder re-architecture ruled'', part A.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-26 21:54:57.596', '2026-07-26 21:54:57.596', NULL, '4218d9825e54eb91d3062dc514e311e4', 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 ('06FT0V6GH94XKAVWXRR5AP9FSG', 'epic', '06FB0TNSRZXCHGS16BFHSSGSV4', 'Sol is out of generator bounds: GeneratorScope + Authored data canvas', 'Sol (system_id ''GJ 0'', 19 bodies incl. Earth 8.5B pop / Mars 1.2B / Luna 350M / Europa 30M -- 10.08B people, ~16% of the Reach''s 60.9B) is currently excluded from the Atlas ONLY BY ACCIDENT: no heightmaps, zero rows in every atlas_* table. Nothing today stops a future import_heightmaps run from ingesting a real Earth/Mars DEM and silently generating a procedural homeworld. This epic makes the exclusion an explicit, encoded architectural boundary instead of an absence. + +Design (agreed in the 2026-07-26 pair session): +- New server/src/atlas/scope.rs: + pub enum GeneratorScope { Procedural, Authored { deepest_rung: StepCanvasRung } } + pub fn generator_scope(system_id: &str) -> GeneratorScope + Sol maps to Authored { deepest_rung: Global }. The DLC gate is a DEPTH value, not a boolean -- a future Sol expansion deepens the ladder by changing one value, no branching code path. +- No new wire status needed. ''Viewable at Global, no deeper'' IS the existing rung-liveness mechanism -- the rungs below Global simply are not in Sol''s ladder, so there is no error to report. A proposed NotSurveyed wire status was considered and dropped as unnecessary. +- Without this gate, Sol bodies currently hit SourceResolveError::NoTerrainReference and report NotFound -- i.e. ''broken'' -- on the most populous body in the setting. This epic''s completion criterion includes verifying that no longer happens. +- Checked in TWO places: step_canvas.rs before resolver.resolve() is called, AND the heightmap importer (import_heightmaps.py) -- so a stray DEM cannot enter the pipeline even if someone points the importer at a real Earth/Mars dataset by mistake. +- Corrected population counts from this session (supersede any earlier count that silently included Sol): in-bounds populated bodies 269, split 231 open-air / 38 enclosed settlements. Phobos (11 km) and Deimos (6 km) are the ONLY sub-region bodies in the whole DB -- with Sol out of generator bounds, is_rung_live_on_body() never returns false for anything else in scope; a test comment currently cites this as evidence and should be corrected to say so explicitly. + +Note: Sol''s art/data-canvas child ticket and the scope-gate child are somewhat independent (gate can land before art exists -- Sol just shows nothing below Global until then), but both belong under this epic. See governance/decisions/architecture.md#d-255 and the pair-session scratchlog 2026-07-26 section ''Sol is out of generator bounds''.', 'backlog', 'high', NULL, NULL, 'D-255', '2026-07-26 21:55:13.674', '2026-07-26 21:55:13.674', NULL, '72c5c5830241ec1f287129659225d8c5', 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 ('06FT0V7D4VQ6YHAWGFH472ZAJM', 'task', '06FT0V6GH94XKAVWXRR5AP9FSG', 'Implement GeneratorScope enum + gate at step_canvas.rs and the heightmap importer', 'Add server/src/atlas/scope.rs per the epic''s design: GeneratorScope::{Procedural, Authored{deepest_rung}} + generator_scope(system_id) -> GeneratorScope, keyed so ''GJ 0'' (Sol, note the space in the system_id) maps to Authored{deepest_rung: Global}. Check this gate in TWO places: (1) step_canvas.rs before resolver.resolve() is invoked -- a request for a rung deeper than the body''s GeneratorScope allows must resolve to ''rung not live on this body'' via the EXISTING rung-liveness mechanism, not a new error/wire status; (2) import_heightmaps.py -- refuse to bake a heightmap for a system_id whose GeneratorScope is Authored, so a stray real-world DEM cannot silently enter the procedural pipeline. Fix the is_rung_live_on_body() test comment that currently cites Phobos/Deimos as the reason the guard exists -- once Sol is out of bounds, Phobos/Deimos are structurally the ONLY sub-region bodies in-scope, so the guard is purely defensive for everything else; make the comment say so. See governance/decisions/architecture.md#d-255.', 'backlog', 'high', NULL, NULL, 'D-255', '2026-07-26 21:55:20.998', '2026-07-26 21:55:20.998', NULL, '493614c361b2d0e0eae8a87610acc26f', 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 ('06FT0V8B51YH76EGJ3A3RTRTHC', 'task', '06FT0V6GH94XKAVWXRR5AP9FSG', 'Sol authored data canvas: import real-geography elevation/biome fields at Global only', 'Sol gets hand-crafted Atlas art, but as a DATA CANVAS, not a finished image (Jeroen''s explicit ruling) -- an artist paints elevation/biome/water fields at 512x256 (matching the procedural rung-0 heightmap/reliefmap resolution), and an importer converts them into the same wire shape every procedural body uses. This rides the existing StepCanvasResponse wire UNTOUCHED: no new client draw path, goes through the SAME map-art function as all 269 procedural bodies. Rationale (accepted): the player is not looking at a photograph of Earth, they are looking at their implant rendering Earth in the same visual language it renders everything else -- handcrafted where it matters (real coastlines), drawn BY the Atlas rather than pasted into it. A finished image would need a new wire shape, a new display path, and would sit outside D-255''s texel-exactness guarantee. + +Scope: Global rung only, per GeneratorScope::Authored{deepest_rung: Global} (sibling ticket). No deeper rung is generated for Sol -- Earth/Mars/etc. will never have a walkable world by construction; this is treated as a deliberate narrative choice (legacy homeworld, out of reach), not an emergent gap, and is worth a one-line note wherever Sol''s scope is documented so a future contributor does not read it as an oversight. + +Depends on: the GeneratorScope gate ticket (need Authored{deepest_rung} to exist before this has somewhere to plug in) and an artist/art-pipeline decision on who paints the source fields (not resolved in the pair session -- flag for scheduling). See governance/decisions/architecture.md#d-255.', 'backlog', 'medium', NULL, NULL, 'D-255', '2026-07-26 21:55:28.680', '2026-07-26 21:55:28.680', NULL, '5cd21b70f09c5434a9c2689e7fccca10', 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.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-26 21:55:37.893', '2026-07-26 21:55:37.893', NULL, 'da88cc4f5dab09a5feaa848e1c6ac48c', 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.', 'backlog', 'high', NULL, NULL, NULL, '2026-07-26 21:55:41.698', '2026-07-26 21:55:41.698', NULL, '19553acafd53ab06b9b5895add797297', 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 ('06FT0VAZ0RV0FQZMSTB13QP56M', 'bug', '06FBPPMZNNEV052DBYYY3A897C', 'make atlas: _stop_spawned_server crashes on null instance, leaves orphaned server process', 'Found during the 2026-07-26 pair session while verifying the D-255 extent-inversion work in the make atlas companion shell (D-254). server_process.gd:87''s _stop_spawned_server crashes on a null instance when called via atlas_standalone.gd:292 -- reproduced during this session''s shutdown and had to be killed by hand (orphaned process left running after the client exited). Separately, but likely related root cause: every cold make atlas launch also logs ''TCP connection failed after 20 retries'' as an ERROR for what is actually an expected attach-probe path (the client probing for an already-running server before deciding to spawn its own) -- this should not log at ERROR severity since failure is the expected/normal outcome on a cold launch. + +Context: make atlas (the D-254 companion shell) was never verified post-D-255 landing before this session -- last touched 2026-07-22, viewer replaced 2026-07-24/25, so this is likely the first real shutdown exercise since the stepped-Atlas rebuild. Fix both: (1) null-check server_process instance before calling stop on it, or guard the call site in atlas_standalone.gd:292; (2) downgrade or restructure the attach-probe''s expected-failure log to not read as an ERROR. Verify with a full cold-launch-then-quit cycle leaving no orphaned server process.', 'backlog', 'medium', NULL, NULL, NULL, '2026-07-26 21:55:50.150', '2026-07-26 21:55:50.150', NULL, '64760c76b0994ca246e334e81a2651d2', 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 ('06FT0VBSVB8BRZXWQKV46N8S20', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', '2 populated asteroid_belt bodies have NULL radius -- no Global rung, no ladder possible', 'Data gap surfaced during the 2026-07-26 pair session (same session as the Sol GeneratorScope work and the settlement-generator enclosed-settlements ruling). Two populated bodies of type asteroid_belt in systems.db have NULL radius. This is not a simple missing-value fix: a belt is not a sphere, so it has no equirectangular surface, no Global rung, and no Atlas ladder AT ALL under the current body-surface model (every rung from Global down assumes a spherical body_radius_km per D-243/D-255). This needs its OWN design treatment -- what does ''the Atlas'' even mean for a population living in a belt -- not a default radius value patched in to make the existing pipeline not crash. Flag for design discussion before implementation; do not just default a radius to silence a null-check. See pair-session scratchlog 2026-07-26, ''Data gaps to ticket''.', 'backlog', 'medium', NULL, NULL, NULL, '2026-07-26 21:55:57.018', '2026-07-26 21:55:57.018', NULL, '519fc6010dd6a8e4fac24790304c7e2a', 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 ('06FT0VCC9809R320XQ28RNWXN4', 'bug', '06FB0TNSRZXCHGS16BFHSSGSV4', '4 populated bodies have NULL atmosphere (all Sol) -- confirm no fallback needed once Sol is gated', 'Data gap surfaced during the 2026-07-26 pair session, in the same conversation as the settlement-generator enclosed-settlements ruling (open-air requires atmosphere == ''standard''; everything else -- including NULL -- defaults to enclosed, fails safe). All 4 populated bodies with NULL atmosphere are Sol bodies (system_id ''GJ 0''). Once T-1219/T-1220 (Sol GeneratorScope gate) lands and Sol is excluded from the procedural generator entirely, these 4 bodies never reach the settlement generator''s atmosphere check at all -- so no NULL-atmosphere fallback should be needed in practice. This ticket''s job is to CONFIRM that once Sol is gated, no in-scope (non-Sol) body ever has NULL atmosphere, and either close as verified-moot or, if a non-Sol NULL case exists/appears later, implement the enclosed-by-default fallback the settlement-generator ruling already specifies. Depends on T-1220 (Sol scope gate) landing so this can be verified against real data. See pair-session scratchlog 2026-07-26, ''Data gaps to ticket'' and ''Settlement generator -- first design ruling''.', 'backlog', 'low', NULL, NULL, NULL, '2026-07-26 21:56:01.738', '2026-07-26 21:56:01.738', NULL, 'e630df62b9c48407da1327607b915911', 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 ('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); diff --git a/CHANGELOG.md b/CHANGELOG.md index c556e81d2..be0dd7110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,15 @@ Format based on [Keep a Changelog](https://keepachangelog.com/). - **The Atlas can be driven by agents** (D-226, T-971) — a named-intent control channel (AtlasAgentInterface) lets automated QA agents navigate the real Atlas UI through the exact same handlers a click calls: open/select/descend, jump straight to any world coordinate at any zoom step, toggle overlays, and read back a summary of what's on screen — turning the hand-scripted verification drives into a first-class, repeatable QA capability. In-process consumers only for now; a shipped reference driver replaces the ad-hoc scripts ### Changed +- **Every Atlas zoom level now shows the ground its name promises** (D-255 amendment) — a zoom step used to fix how much ground one map cell covered, and let the view size fall out of that; at the Region step this meant a viewport-sized map spanned six times around the planet, so Region silently redrew the same picture as the fully-zoomed-out view. That was the actual cause of "Global and Region look the same." Every step now fixes how much ground the *view* covers instead — the shorter edge of your window always shows exactly one cell's worth of that step's scale, and a wider window simply shows more to the sides. Every zoom level looks like a real map now, and resizing the window is free +- **The whole-body map fits the screen, not the body's internal grid** (D-255 amendment) — the fully-zoomed-out view of a planet used to size itself off an internal bookkeeping grid, throwing away most of the detail already available and getting coarser on some worlds as unrelated tuning changed elsewhere. It now fills the window at the sharpest whole-number scale that fits, so the opening view of a body is as detailed as the game can make it - **Rivers read like rivers** (T-1175) — map courses now taper to a point at their upstream source instead of starting at full width (the classic cartographic river grammar), and the stream/tributary/trunk width ladder was retuned so a tributary joining a trunk visibly reads as a join. Part of the map-fluency polish pass benchmarked against the best-in-class world maps - **Rivers and mountain ranges now have names behind the map** (T-1169) — every body's rivers and peaks are assigned names from the curated per-system pools (17,891 names across the Reach) during world generation, queryable over the wire. The labels that will draw them on the Atlas come in a follow-up; the naming layer underneath is live - **Internal: legacy map-window field retired** (T-1159) — the obsolete duplicate zoom-granularity field was removed from the map wire protocol and caches; no player-visible change - **Zoomed-in map levels draw about four times faster** — each map cell now covers a 2×2 block of screen pixels instead of one, cutting the work per view to roughly a quarter with no loss of crispness (cells are still whole pixels, never blurred) ### Fixed +- **The whole-body map no longer opens as two giant colored blocks** — on first opening the Atlas, the whole-body view occasionally measured itself before the window had finished laying out, producing a two-cell map stretched across the whole screen instead of a real picture of the planet. It now waits for a real size before asking for its first map, and a defensive floor keeps a similar mistake from ever showing an unusably coarse map again. (Still open: the whole-body view is now sized correctly but still doesn't show rivers or lakes — that lands with the upcoming whole-body water pass) - **The Atlas tells you when it is fetching a map** — a slow zoom step used to look like a hang: the "deriving terrain" feedback was being painted underneath the map itself, so it was only ever visible when there was no map at all. A diegetic "DOWNLOADING MAP DATA" readout now appears over the held view while new ground is fetched, and stays out of the way entirely for anything that arrives quickly - **The map fills the window, and follows it when it changes** — resizing the game window (or the implant panel inside it) left the map at its old size, marooned in empty space. It now re-fetches at the new size once the resize settles - **Panning no longer stutters or spams the server** — holding an edge-scroll used to jerk the view and fire a fresh map request every frame past a threshold. Panning is now smooth, with a single fetch once you stop; a fast pan toward the edge still fetches immediately rather than running off into blank space diff --git a/CLAUDE.md b/CLAUDE.md index 38478eeb7..a91ded482 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ Development follows a strict cascade. Each phase has a concrete deliverable. **D | 1 | Wiki content complete — all planets, moons, stations, heightmaps, artwork | Implant-ready Godot map of the Reach with click-throughs + wiki/GTTR popups | | 2 | Economics layer — supply/demand, transport, political/social pressure, corporations, supply chains | Economics spreadsheets/graphs with runtime-tweakable simulation | | 3 | Planetary/moon maps & station layouts — cities, rivers, mountains, roads, biomes, rail | Atlas of the Reach (implant app) | -| 4 | World generation (tile/chunk/block) — deterministic multilayer seed→tile cascade; each layer above the tile fill viewed as a **map in the implant Atlas** (no in-world rendering; tile/voxel output is harness-verified, never Atlas-mapped; **chunk (64 m) is the deepest Atlas rung** — D-255, narrowing the D-226 amendment 2026-07-13) | Deterministic walkable-world data + per-layer Atlas maps + asset catalog + the **stepped Atlas ladder** (D-255: six levels — the Global body-surface opener (rung 0, variable extent, always kept) + five fixed rungs Region→District→Quarter→Block→Chunk; every step a server-derived data canvas at its native gridunit spacing, drawn texel-exact by the client map-art function — heightmap PNGs remain invisible derivation input, never displayed) | +| 4 | World generation (tile/chunk/block) — deterministic multilayer seed→tile cascade; each layer above the tile fill viewed as a **map in the implant Atlas** (no in-world rendering; tile/voxel output is harness-verified, never Atlas-mapped; **chunk (64 m) is the deepest Atlas rung** — D-255, narrowing the D-226 amendment 2026-07-13) | Deterministic walkable-world data + per-layer Atlas maps + asset catalog + the **stepped Atlas ladder** (D-255, amended 2026-07-26 — the extent inversion: six levels — the Global body-surface opener (rung 0, viewport-sized, always kept) + five fixed rungs Region→District→Quarter→Block→Chunk; **a rung fixes the canvas EXTENT and spacing falls out of it** — the shorter viewport axis spans exactly one cell of the rung's level, so canvas cost is constant at every rung — drawn texel-exact by the client map-art function at a uniform 2×2 display ratio; heightmap PNGs remain invisible derivation input, never displayed. Rung 0.5, an expanded whole-body layer where hydrology resolves once and every rung below sources from it, is RULED (D-258) but not yet implemented — see D-258) | | 5 | Player control & in-world rendering — character, walls/stairs/doors, lighting, drawn on **generated** tiles (no test map) | Player viewport with final-version assets on the generated world | | 6 | Detail coloring — room-level NPC population, cultural room grammar | Only when the world is walkable |