diff --git a/.pql/pql-plan.json b/.pql/pql-plan.json index 995f0359..bc8b63a2 100644 --- a/.pql/pql-plan.json +++ b/.pql/pql-plan.json @@ -1,5 +1,5 @@ { - "exported_at": "2026-05-06T13:45:07Z", + "exported_at": "2026-05-06T14:40:38Z", "decisions": [ { "id": "D-1", @@ -9,7 +9,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-3", @@ -19,7 +19,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-4", @@ -29,7 +29,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-5", @@ -39,7 +39,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-6", @@ -49,7 +49,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-7", @@ -59,7 +59,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-8", @@ -69,7 +69,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-9", @@ -79,7 +79,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-10", @@ -89,7 +89,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-11", @@ -99,7 +99,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-12", @@ -109,7 +109,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-13", @@ -119,7 +119,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-14", @@ -129,7 +129,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-15", @@ -139,7 +139,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-16", @@ -149,7 +149,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-17", @@ -159,7 +159,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-18", @@ -169,7 +169,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-19", @@ -179,7 +179,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-20", @@ -189,7 +189,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/accessibility.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-21", @@ -199,7 +199,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/accessibility.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-22", @@ -209,7 +209,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/accessibility.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-23", @@ -219,7 +219,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-24", @@ -229,7 +229,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-25", @@ -239,7 +239,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-26", @@ -249,7 +249,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-27", @@ -259,7 +259,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-28", @@ -269,7 +269,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-29", @@ -279,7 +279,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-30", @@ -289,7 +289,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-31", @@ -299,7 +299,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-32", @@ -309,7 +309,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-33", @@ -319,7 +319,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-34", @@ -329,7 +329,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-35", @@ -339,7 +339,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-36", @@ -349,7 +349,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-37", @@ -359,7 +359,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-38", @@ -369,7 +369,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-39", @@ -379,7 +379,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-40", @@ -389,7 +389,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-41", @@ -399,7 +399,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-42", @@ -409,7 +409,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-43", @@ -419,7 +419,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-44", @@ -429,7 +429,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-45", @@ -439,7 +439,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-46", @@ -449,7 +449,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-47", @@ -459,7 +459,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-48", @@ -469,7 +469,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-49", @@ -479,7 +479,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-50", @@ -489,7 +489,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-51", @@ -499,7 +499,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-52", @@ -509,7 +509,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-53", @@ -519,7 +519,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-54", @@ -529,7 +529,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-55", @@ -539,7 +539,7 @@ "status": "active", "date": "2026-04-23", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-56", @@ -549,7 +549,7 @@ "status": "active", "date": "2026-04-23", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-57", @@ -559,7 +559,7 @@ "status": "active", "date": "2026-04-23", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-58", @@ -569,7 +569,7 @@ "status": "active", "date": "2026-04-23", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-59", @@ -579,7 +579,7 @@ "status": "active", "date": "2026-04-25", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-60", @@ -589,7 +589,7 @@ "status": "active", "date": "2026-04-26", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-61", @@ -599,7 +599,7 @@ "status": "active", "date": "2026-04-26", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-62", @@ -609,7 +609,7 @@ "status": "active", "date": "2026-04-26", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-63", @@ -619,7 +619,7 @@ "status": "active", "date": "2026-04-26", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "D-64", @@ -629,7 +629,7 @@ "status": "active", "date": "2026-05-03", "file_path": "decisions/architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "D-65", @@ -639,7 +639,7 @@ "status": "active", "date": "2026-04-26", "file_path": "decisions/tooling.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-1", @@ -648,7 +648,7 @@ "title": "Authorisation granularity on the IPC socket", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "Q-2", @@ -657,7 +657,7 @@ "title": "Back-pressure on event streams", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-3", @@ -666,7 +666,7 @@ "title": "Event persistence + audit/undo", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-4", @@ -675,7 +675,7 @@ "title": "`.canvas` schema compatibility with Obsidian", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-5", @@ -684,7 +684,7 @@ "title": "IPC wire-format stability + `schema_version:`", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-6", @@ -693,7 +693,7 @@ "title": "Window chrome — native frame vs frameless custom", "status": "resolved", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-7", @@ -702,7 +702,7 @@ "title": "macOS app bundle signing / notarisation", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-8", @@ -711,7 +711,7 @@ "title": "Extension API shape — widgets, subcommands, both?", "status": "open", "file_path": "decisions/questions-extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-9", @@ -720,7 +720,7 @@ "title": "Lua runtime vendoring", "status": "open", "file_path": "decisions/questions-extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-10", @@ -729,7 +729,7 @@ "title": "Extension manifest `schema_version:`", "status": "open", "file_path": "decisions/questions-extensions.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-11", @@ -738,7 +738,7 @@ "title": "Coverage gates — hard thresholds vs soft reporting", "status": "open", "file_path": "decisions/questions-testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-12", @@ -747,7 +747,7 @@ "title": "Screen-reader automation (axe-core via Playwright)", "status": "open", "file_path": "decisions/questions-testing.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-13", @@ -756,7 +756,7 @@ "title": "Web production-mode a11y", "status": "open", "file_path": "decisions/questions-accessibility.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "Q-14", @@ -765,7 +765,7 @@ "title": "i18n plurals / gender / date-format tooling", "status": "open", "file_path": "decisions/questions-accessibility.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:05" }, { "id": "Q-15", @@ -774,7 +774,7 @@ "title": "Editor tab — full LSP vs tree-sitter-only highlight", "status": "open", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-16", @@ -783,7 +783,7 @@ "title": "`tree-sitter-dart` grammar maintenance", "status": "open", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-17", @@ -792,7 +792,7 @@ "title": "Icon set growth", "status": "open", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-18", @@ -801,7 +801,7 @@ "title": "Theme hot-reload in release builds", "status": "open", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-19", @@ -810,7 +810,7 @@ "title": "(withdrawn)", "status": "resolved", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-20", @@ -819,7 +819,7 @@ "title": "Kernel DB service — namespaced SQL access?", "status": "open", "file_path": "decisions/questions-process.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-21", @@ -828,7 +828,7 @@ "title": "Pql absorbs planning vs keeps separate", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-22", @@ -837,7 +837,7 @@ "title": "Ticket persistence strategy", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-23", @@ -846,7 +846,7 @@ "title": "SSH-remote development — run clide against a remote workspace", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-25", @@ -855,7 +855,7 @@ "title": "Body text face — mono everywhere vs Josefin Sans UI + mono code", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-26", @@ -864,7 +864,7 @@ "title": "Small screen layout (\u003c 1000px)", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-27", @@ -873,7 +873,7 @@ "title": "Two-editor split", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-28", @@ -882,7 +882,7 @@ "title": "Terminal strip scope — shell only or logs/errors/tests", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-29", @@ -891,7 +891,7 @@ "title": "Branch picker location", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-30", @@ -900,7 +900,7 @@ "title": "Focus behavior when editor is dirty and viewer is peeked", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "Q-31", @@ -909,7 +909,7 @@ "title": "XWayland fallback for frameless — proper Wayland protocol needed", "status": "open", "file_path": "decisions/questions-architecture.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-2", @@ -919,7 +919,7 @@ "status": "active", "date": "2026-04-20", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-3", @@ -929,7 +929,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-4", @@ -939,7 +939,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-5", @@ -949,7 +949,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-6", @@ -959,7 +959,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-7", @@ -969,7 +969,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-8", @@ -979,7 +979,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-9", @@ -989,7 +989,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-10", @@ -999,7 +999,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-11", @@ -1009,7 +1009,7 @@ "status": "active", "date": "2026-04-21", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" }, { "id": "R-12", @@ -1019,7 +1019,7 @@ "status": "active", "date": "2026-04-22", "file_path": "decisions/rejected.md", - "synced_at": "2026-05-05 13:11:46" + "synced_at": "2026-05-06 14:37:06" } ], "decision_refs": [ @@ -1051,7 +1051,7 @@ "source_id": "D-16", "target_id": "D-19", "ref_type": "references", - "note": "**Decision:** Bundled extensions (every `app/lib/builtin/\u003cname\u003e`) are Dart — they link into the app binary. Third-party extensions (Tier 6) run in sandboxed Lua via the `ptyc`-peer Lua runtime (s..." + "note": "**Decision:** Bundled extensions (every `lib/builtin/\u003cname\u003e`) are Dart — they link into the app binary. Third-party extensions (Tier 6) run in sandboxed Lua via the `ptyc`-peer Lua runtime (see [..." }, { "source_id": "D-18", @@ -1105,13 +1105,13 @@ "source_id": "D-39", "target_id": "D-40", "ref_type": "references", - "note": "**Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-planning-vs-keep..." + "note": "**Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-superseded-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-plann..." }, { "source_id": "D-39", "target_id": "Q-21", "ref_type": "references", - "note": "**Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-planning-vs-keep..." + "note": "**Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-superseded-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-plann..." }, { "source_id": "D-39", @@ -1597,7 +1597,7 @@ "source_id": "Q-16", "target_id": "Q-15", "ref_type": "references", - "note": "**Context:** If tree-sitter is the Tier-2 answer ([Q-15](#q-15-editor-tab-full-lsp-vs-tree-sitter-only)), grammar sourcing matters." + "note": "**Context:** If tree-sitter is the Tier-2 answer ([Q-15](#q-15-editor-tab-full-lsp-vs-tree-sitter-only-highlight)), grammar sourcing matters." }, { "source_id": "Q-17", @@ -2705,10 +2705,31 @@ "id": "T-86", "type": "task", "title": "codify UI spacing constants (gaps, paddings, control sizes)", - "status": "in_progress", + "status": "done", "priority": "medium", "created_at": "2026-05-06 13:28:58", - "updated_at": "2026-05-06 13:38:44" + "updated_at": "2026-05-06 13:45:12" + }, + { + "id": "T-87", + "type": "bug", + "title": "kill secondary Claude tmux sessions on tab close and on app shutdown", + "description": "D-41 lifecycle contract: \"closing a secondary kills that tmux session\" and \"secondary numbering (-1, -2, ...) resets between clide runs since ephemeral state is lost\" — the second clause only holds if non-primary tmux sessions are reaped on app shutdown.\n\nNeither happens today.\n\n**Gap 1 — tab close is UI-only.** `ClaudeSessionHostState` (lib/builtin/claude/src/claude_session_host.dart:18) builds tabs via MultitabController; closing a secondary just removes the entry. No `tmux kill-session` shellout anywhere in lib/builtin/claude/ or lib/src/pty/ (verified by grep for `kill-session` / `killSession` / `tmux kill`).\n\n**Gap 2 — app shutdown leaks secondaries.** Nothing reaps `clide-claude-\u003crepohash\u003e-N` sessions when the app exits. Repro: spawn two secondaries, quit clide, `tmux -L clide list-sessions` still shows them. Confirmed live: 4 sessions on the clide socket (primary + -1, -2, -3) survived prior runs.\n\n**Acceptance:**\n- Closing a secondary tab in the Claude pane runs `tmux -L clide kill-session -t \u003cname\u003e` for that secondary's session name (use existing `secondarySessionName(repoRoot, n)`).\n- App shutdown enumerates `clide-claude-\u003crepohash\u003e-*` sessions on the clide socket and kills the non-primary ones. Primary is left detached (D-41 explicit).\n- `_nextSecondary` index policy: keep the in-process counter for the running app; cold-start always begins at 1 because secondary sessions are gone.\n- Add a test that exercises the close path and asserts the kill-session command was invoked (mock the tmux runner).\n\n**Out of scope:** primary lifecycle (unchanged — survives shutdown by design), general-purpose terminal panes (no tmux wrapping per D-41), Windows behaviour.", + "status": "in_progress", + "priority": "high", + "decision_ref": "D-41", + "created_at": "2026-05-06 13:46:07", + "updated_at": "2026-05-06 13:46:29" + }, + { + "id": "T-88", + "type": "task", + "title": "track dugite-native upstream releases for security updates", + "status": "backlog", + "priority": "low", + "decision_ref": "D-59", + "created_at": "2026-05-06 14:34:35", + "updated_at": "2026-05-06 14:34:35" } ], "ticket_deps": null, @@ -4111,6 +4132,26 @@ "old_value": "backlog", "new_value": "in_progress", "changed_at": "2026-05-06 13:38:44" + }, + { + "ticket_id": "T-86", + "field": "status", + "old_value": "in_progress", + "new_value": "done", + "changed_at": "2026-05-06 13:45:12" + }, + { + "ticket_id": "T-87", + "field": "description", + "new_value": "D-41 lifecycle contract: \"closing a secondary kills that tmux session\" and \"secondary numbering (-1, -2, ...) resets between clide runs since ephemeral state is lost\" — the second clause only holds if non-primary tmux sessions are reaped on app shutdown.\n\nNeither happens today.\n\n**Gap 1 — tab close is UI-only.** `ClaudeSessionHostState` (lib/builtin/claude/src/claude_session_host.dart:18) builds tabs via MultitabController; closing a secondary just removes the entry. No `tmux kill-session` shellout anywhere in lib/builtin/claude/ or lib/src/pty/ (verified by grep for `kill-session` / `killSession` / `tmux kill`).\n\n**Gap 2 — app shutdown leaks secondaries.** Nothing reaps `clide-claude-\u003crepohash\u003e-N` sessions when the app exits. Repro: spawn two secondaries, quit clide, `tmux -L clide list-sessions` still shows them. Confirmed live: 4 sessions on the clide socket (primary + -1, -2, -3) survived prior runs.\n\n**Acceptance:**\n- Closing a secondary tab in the Claude pane runs `tmux -L clide kill-session -t \u003cname\u003e` for that secondary's session name (use existing `secondarySessionName(repoRoot, n)`).\n- App shutdown enumerates `clide-claude-\u003crepohash\u003e-*` sessions on the clide socket and kills the non-primary ones. Primary is left detached (D-41 explicit).\n- `_nextSecondary` index policy: keep the in-process counter for the running app; cold-start always begins at 1 because secondary sessions are gone.\n- Add a test that exercises the close path and asserts the kill-session command was invoked (mock the tmux runner).\n\n**Out of scope:** primary lifecycle (unchanged — survives shutdown by design), general-purpose terminal panes (no tmux wrapping per D-41), Windows behaviour.", + "changed_at": "2026-05-06 13:46:26" + }, + { + "ticket_id": "T-87", + "field": "status", + "old_value": "backlog", + "new_value": "in_progress", + "changed_at": "2026-05-06 13:46:29" } ] } diff --git a/decisions/accessibility.md b/decisions/accessibility.md index 59a0b66e..e02cb1a1 100644 --- a/decisions/accessibility.md +++ b/decisions/accessibility.md @@ -8,7 +8,7 @@ A11y + i18n are Tier-0 contracts, not Tier-6 polish. - **Date:** 2026-04-21 - **Decision:** Every widget primitive wraps its interaction surface in a `Semantics` node at the point of creation. A11y coverage is a test-time gate (`ci/test_a11y.sh`), not a post-hoc polish pass. `ensureSemantics()` fires at app boot; Flutter's semantics tree is always populated. - **Rationale:** Retrofitting a11y onto a grown UI is what every project that skips this promises to do later and then doesn't. Making it a Tier-0 contract costs one `Semantics` line per primitive and a semantic-coverage test; postponing costs a rewrite. -- **Cost:** Widget authors maintain correct labels; tests reject new primitives without semantics. Enforced by `app/test/a11y/` coverage tests. +- **Cost:** Widget authors maintain correct labels; tests reject new primitives without semantics. Enforced by `test/a11y/` coverage tests. - **Raised by:** 2026-04-21 planning. ### D-21: i18n is a Tier-0 contract (fframe pattern + locale-fallback chain) diff --git a/decisions/architecture.md b/decisions/architecture.md index a20a11ed..86447a93 100644 --- a/decisions/architecture.md +++ b/decisions/architecture.md @@ -13,7 +13,7 @@ Core, rendering, IPC, kernel, panel manager. ### D-8: Feature-first folder layout - **Date:** 2026-04-21 -- **Decision:** Under `app/lib/`, organise by feature (`kernel/`, `extension/`, `widgets/`, `builtin//`) rather than by layer (`models/`, `views/`, `controllers/`). Private implementation lives under each feature's `src/`; the feature's public surface is a barrel file at the feature root (e.g. `app/lib/kernel/kernel.dart`). +- **Decision:** Under `lib/`, organise by feature (`kernel/`, `extension/`, `widgets/`, `builtin//`) rather than by layer (`models/`, `views/`, `controllers/`). Private implementation lives under each feature's `src/`; the feature's public surface is a barrel file at the feature root (e.g. `lib/kernel/kernel.dart`). - **Rationale:** Features grow and get deleted as units; layer-first layouts fragment a feature across three directories and make deletions risky. Matches extensions-as-features (every extension already has its own folder). - **Cost:** Imports cross features only via the barrel — enforce by review, no automated check yet. - **Raised by:** 2026-04-21 planning. @@ -89,7 +89,7 @@ Core, rendering, IPC, kernel, panel manager. ### D-4: Ignore file strategy - **Date:** 2026-04-20 (was ADR 0004; ported from the claudian lineage) -- **Decision:** One mechanism everywhere: the `ignore_files:` list in `.pql/config.yaml`. Ordered list of gitignore-shaped files; later entries win on per-pattern conflicts. pql defaults to `ignore_files: [.gitignore]`. Per [D-3](#d-3-pql-as-supporter-tool), clide writes the list on load — `[.gitignore, .clideignore]` if `.clideignore` exists, else `[.gitignore]`. `.clideignore` carries **only** the clide-specific deviations from `.gitignore` (supports `!pattern` negations); never duplicate gitignore's contents. Walker magic: none except `.git/` — every other tool-owned dir (`.pql/`, `.clide/`) is added to `.gitignore` at install time; exclusion flows through the normal `ignore_files:` chain. +- **Decision:** One mechanism everywhere: the `ignore_files:` list in `.pql/config.yaml`. Ordered list of gitignore-shaped files; later entries win on per-pattern conflicts. pql defaults to `ignore_files: [.gitignore]`. Per [D-3](#d-3-pql-as-supporter-tool-clide-wraps-never-duplicates), clide writes the list on load — `[.gitignore, .clideignore]` if `.clideignore` exists, else `[.gitignore]`. `.clideignore` carries **only** the clide-specific deviations from `.gitignore` (supports `!pattern` negations); never duplicate gitignore's contents. Walker magic: none except `.git/` — every other tool-owned dir (`.pql/`, `.clide/`) is added to `.gitignore` at install time; exclusion flows through the normal `ignore_files:` chain. - **Context:** Every file-enumerating surface in clide (pql query panels, canvas drivers, graph view, file watchers, pane lists, file tree) needs to skip the obvious junk — `vendor/`, `node_modules/`, `dist/`, build artifacts — or results drown in noise. Clide's working assumption is that the git repo *is* the workspace — no separate "vault" concept. - **Rationale:** Users get one config knob, in a file they might already know (pql users) or never need to touch (clide-only users). `.clideignore` is short by design — it's deltas, not a full list. Sidecar consumers read the same key and apply identical precedence, so Claude and the user always see the same filtered surface. - **Cost:** Removing clide from a repo leaves pql working with vanilla defaults (clide's last-written `ignore_files:` stays until pql or the user rewrites it; worth reconsidering during uninstall design). diff --git a/decisions/extensions.md b/decisions/extensions.md index 0ac9f373..cf3940d7 100644 --- a/decisions/extensions.md +++ b/decisions/extensions.md @@ -13,7 +13,7 @@ Extension contract, Lua runtime, grain, contribution points. ### D-16: Built-ins in Dart, third-party in sandboxed Lua - **Date:** 2026-04-21 -- **Decision:** Bundled extensions (every `app/lib/builtin/`) are Dart — they link into the app binary. Third-party extensions (Tier 6) run in sandboxed Lua via the `ptyc`-peer Lua runtime (see [D-19](#d-19-lua-runtime-as-ptyc-peer-supporter-tool)). The contribution contract is language-agnostic — same contribution shapes, same manifest schema. +- **Decision:** Bundled extensions (every `lib/builtin/`) are Dart — they link into the app binary. Third-party extensions (Tier 6) run in sandboxed Lua via the `ptyc`-peer Lua runtime (see [D-19](#d-19-lua-runtime-as-ptyc-peer-supporter-tool)). The contribution contract is language-agnostic — same contribution shapes, same manifest schema. - **Rationale:** Dart built-ins get full SDK power (custom painters, isolates, FFI); third-party Lua gets a narrow capability API, no arbitrary syscalls, no deps on pub.dev. VS Code's Node-runs-with-full-power model is a supply-chain nightmare we're explicitly rejecting. - **Cost:** Two implementation paths for the same contract; we pay in API design to keep them equivalent at the seams. - **Raised by:** 2026-04-21 planning. @@ -41,7 +41,7 @@ Extension contract, Lua runtime, grain, contribution points. ### D-46: Core frame builtins vs shipped extensions boundary - **Date:** 2026-04-22 -- **Decision:** The `app/lib/builtin/` directory is reserved for core frame infrastructure — components the shell cannot function without. Everything that renders *content* (editor surfaces, tool panels, integrations) is a shipped extension: still Dart, still bundled in the binary, but architecturally an extension that registers through the contribution contract and could in principle be disabled by the user. +- **Decision:** The `lib/builtin/` directory is reserved for core frame infrastructure — components the shell cannot function without. Everything that renders *content* (editor surfaces, tool panels, integrations) is a shipped extension: still Dart, still bundled in the binary, but architecturally an extension that registers through the contribution contract and could in principle be disabled by the user. **Core frame builtins** (cannot be disabled; the frame breaks without them): `default-layout`, `welcome`, `ipc-status`, `theme-picker`, `terminal`, `files`, `grammars-core`, `settings-ui`, `extensions-ui`, `keybindings-ui`. @@ -53,7 +53,7 @@ Extension contract, Lua runtime, grain, contribution points. `editor`, `claude`, `claude-control`, `markdown`, `diff`, `git-ui`, `pql`, `canvas`, `graph`, `decisions`, `tickets`, `todos`, `problems`. - **Rationale:** The previous session bled several content extensions (jira, todos, decisions, tickets, canvas, graph) into `builtin/` as stubs, treating "shipped with the app" as "part of the frame." This conflates two concerns: the frame's structural integrity and the bundled feature set. A user who disables the canvas extension should get a working IDE with no canvas panel; a user who disables the layout extension gets a broken window. The boundary is: can the frame render and function without it? If yes, it's a shipped extension, not a frame builtin. -- **Cost:** Shipped extensions need a separate registration path (e.g. `app/lib/extensions/` or equivalent) distinct from `app/lib/builtin/`. The extension contract must support "bundled Dart extension" as a first-class category alongside "builtin" and "third-party Lua." Migration is incremental — move one at a time, each behind a working build. +- **Cost:** Shipped extensions need a separate registration path (e.g. `lib/extensions/` or equivalent) distinct from `lib/builtin/`. The extension contract must support "bundled Dart extension" as a first-class category alongside "builtin" and "third-party Lua." Migration is incremental — move one at a time, each behind a working build. - **Supersedes:** Removes `builtin.jira` (already deleted; should never have been a builtin — Jira integration is a third-party extension, not a shipped one). - **Raised by:** 2026-04-22 session review. diff --git a/decisions/process.md b/decisions/process.md index c1e48d1a..37f21c97 100644 --- a/decisions/process.md +++ b/decisions/process.md @@ -43,7 +43,7 @@ Q&D record system itself, kanban, commit conventions, changelog. - **Date:** 2026-04-21 - **Decision:** Planning subcommands (`decisions`, `ticket`, `plan`) land in pql's repo long-term. Clide consumes them via shell-out, matching [D-3](architecture.md)'s wrap-don't-duplicate rule for pql. Clide does not grow Dart subcommands for planning. - **Rationale:** A terminal user or a user in VS Code / JetBrains still needs Q&D access. Binding planning tooling to clide-the-Flutter-app would cut them off from their own work — see [R-9](rejected.md#r-9-port-planning-tooling-into-clide). pql is already the CLI, already universal, already wrapped by clide. -- **Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-planning-vs-keeps-separate). +- **Cost:** Planning features don't ship until pql catches up. Mitigated by [D-40](#d-40-superseded-python-stopgap-under-toolsscriptsplan). Gated by [Q-21](questions-process.md#q-21-pql-absorbs-planning-vs-keeps-separate). - **Raised by:** 2026-04-21 planning. ### D-40: [SUPERSEDED] Python stopgap under `tools/scripts/plan` diff --git a/decisions/questions-process.md b/decisions/questions-process.md index f8737c22..91a277fd 100644 --- a/decisions/questions-process.md +++ b/decisions/questions-process.md @@ -16,7 +16,7 @@ Tooling-domain questions currently live here too. Split into ### Q-16: `tree-sitter-dart` grammar maintenance - **Status:** Open - **Question:** `UserNobody14/tree-sitter-dart` is archived. `nielsenko/tree-sitter-dart` is the maintained fork. Do we pin `nielsenko/`, mirror it in-repo, or lean on the Dart analyzer's own semantic output and skip tree-sitter for Dart? -- **Context:** If tree-sitter is the Tier-2 answer ([Q-15](#q-15-editor-tab-full-lsp-vs-tree-sitter-only)), grammar sourcing matters. +- **Context:** If tree-sitter is the Tier-2 answer ([Q-15](#q-15-editor-tab-full-lsp-vs-tree-sitter-only-highlight)), grammar sourcing matters. - **Source:** 2026-04-21 planning. ### Q-17: Icon set growth diff --git a/decisions/testing.md b/decisions/testing.md index bb05c698..5a69a0d5 100644 --- a/decisions/testing.md +++ b/decisions/testing.md @@ -41,7 +41,7 @@ Test pyramid, drivers, client-side constraint. ### D-28: Test organisation — mirror `lib/` in `test/` - **Date:** 2026-04-21 -- **Decision:** Every test file lives at the same relative path as its subject. `app/lib/kernel/src/i18n/catalog_loader.dart` pairs with `app/test/kernel/i18n/catalog_loader_test.dart`. No separate `unit/` vs `widget/` directories; test type is detected by what the test imports. +- **Decision:** Every test file lives at the same relative path as its subject. `lib/kernel/src/i18n/catalog_loader.dart` pairs with `test/kernel/i18n/catalog_loader_test.dart`. No separate `unit/` vs `widget/` directories; test type is detected by what the test imports. - **Rationale:** Matching paths makes "jump to test" predictable in any editor. Type-by-imports matches how `flutter test` already works. - **Cost:** Large feature folders mirror into large test folders. Acceptable. - **Raised by:** 2026-04-21 planning. diff --git a/decisions/tooling.md b/decisions/tooling.md index 3fcdae5d..bb5aa1d5 100644 --- a/decisions/tooling.md +++ b/decisions/tooling.md @@ -20,7 +20,7 @@ Toolchain, supply chain, CI, ignore strategy. ### D-42: Dependencies documented in `licenses.yaml` - **Date:** 2026-04-22 -- **Decision:** `app/assets/licenses.yaml` has three sections: `self:` (clide's MIT license, rendered first in the About screen so the user knows what they're running), `dependencies:` (third-party artefacts that **ship in the binary** — fonts, runtime Dart packages, native supporter tools, bundled data), and `dev_dependencies:` (build-time-only tooling — test runners, mocks, lints, golden harness — tracked for audit but **not rendered** in the About screen because they don't reach the user). Each entry has name, kind, version, homepage, license identifier, and a one-line purpose; runtime entries also carry a `license_file:` pointer to the bundled license text so the About screen can display it verbatim. Adding any dependency is a two-step commit: add the artefact **and** the corresponding `licenses.yaml` entry in the same changeset, under the correct section. +- **Decision:** `assets/licenses.yaml` has three sections: `self:` (clide's MIT license, rendered first in the About screen so the user knows what they're running), `dependencies:` (third-party artefacts that **ship in the binary** — fonts, runtime Dart packages, native supporter tools, bundled data), and `dev_dependencies:` (build-time-only tooling — test runners, mocks, lints, golden harness — tracked for audit but **not rendered** in the About screen because they don't reach the user). Each entry has name, kind, version, homepage, license identifier, and a one-line purpose; runtime entries also carry a `license_file:` pointer to the bundled license text so the About screen can display it verbatim. Adding any dependency is a two-step commit: add the artefact **and** the corresponding `licenses.yaml` entry in the same changeset, under the correct section. - **Rationale:** Complements [D-31](#d-31-prefer-zero-deps-exact-pin). Prefer-zero-deps is a *budget*; `licenses.yaml` is the *visible consequence*. An extra row in the About screen is a review-time signal that the shipped-binary surface grew. Splitting dev deps out keeps the user-facing list small and honest — a test framework is not something the user needs to see in About — while still documenting every supply-chain input for audit completeness. The runtime entries discharge the redistribution obligations bundled licenses impose (OFL, MIT, BSD all require preserving the license text alongside the binary) without ad-hoc NOTICE files. - **Cost:** One extra edit per dep. Zero tolerance for drift — an un-listed dep is a contributor-visible bug. Until the About screen lands at Tier 6, `licenses.yaml` is accurate but not rendered; the discipline applies from now regardless so Tier 6 inherits a clean list. - **Raised by:** 2026-04-22 planning (user-directed best practice). @@ -47,7 +47,7 @@ Toolchain, supply chain, CI, ignore strategy. - **Decision:** Ship a self-contained Git binary from [dugite-native](https://github.com/desktop/dugite-native) (the same distribution GitHub Desktop bundles). Downloaded at build time via `make dugite-fetch`, stored under `native/dugite/`, gitignored. The `Toolchain` class resolves to the bundled binary first, falling back to system git on PATH. - **Rationale:** The macOS app sandbox blocks execution of Homebrew-installed git (symlinks resolve to Cellar paths that SBPL cannot match without freezing rendering). `/usr/bin/git` is an xcrun shim that refuses to run inside a sandbox. Bundling dugite-native makes clide self-contained — no dependency on Homebrew, Xcode CLT, or system git. The approach is proven: GitHub Desktop, Tower, and other git GUI apps all bundle their own git for the same reason. - **Alternatives rejected:** (R) libgit2 via FFI — missing porcelain commands (pull/push/rebase), no hooks, would require rewriting GitClient. (R) Build git from source — dugite-native already does this with better infra. (R) SBPL exceptions for Homebrew — `(subpath "/opt/homebrew")` for process-exec freezes Flutter rendering on macOS 26. -- **Cost:** ~57 MB download (~199 MB unpacked, stripped at build time). Must track dugite-native releases for security updates. GPL-2.0 (git binary) applies to the bundled artefact, not to clide's MIT code. +- **Cost:** ~57 MB download (~199 MB unpacked, stripped at build time). Must track dugite-native releases for security updates (tracked in T-88). GPL-2.0 (git binary) applies to the bundled artefact, not to clide's MIT code. - **Cross-reference:** [D-31](#d-31-prefer-zero-deps-exact-pin), [D-42](#d-42-dependencies-documented-in-licensesyaml). - **Raised by:** 2026-04-25 macOS sandbox investigation.