docs(governance): D-104 — explicit supporter-binary path overrides (T-495)

clide resolves external supporter binaries (claude, d2, future tools)
via an explicit user-scope tools: map (name → absolute path), consulted
first. First run auto-detects each tool once and writes the discovered
absolute paths into the map — a one-time populate, pinned thereafter, not
a per-launch heuristic. Escapes the login-shell-probe brittleness (T-439
misses brew when shellenv lives only in ~/.bashrc) while staying zero-
config for standard installs. User-scope only (D-93); generalizes D-58's
CLIDE_PQL_BIN override. Filed T-495; gates the d2 template (T-494).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 22:18:53 +02:00
co-authored by Claude Opus 4.8
parent d036e6b9b0
commit 99cebc6d11
5 changed files with 49 additions and 0 deletions
+1
View File
@@ -54,3 +54,4 @@ 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 ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2AD3HPR3HXSVASVZEX8PK0', '2026-06-28 15:30:00.399', '2026-06-28 15:30:00.399', NULL, 'e0961be9550747530f530a7fd300583f', 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 ('06FB2ERREMEEF26KKHGNZBWW64', '06FGX26B0NAC9WRJMVB0QE6CV8', '2026-06-28 15:30:03.803', '2026-06-28 15:30:03.803', NULL, 'fa47a4a4aff706215efa3a449681d18f', 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 ('06FB2G2KHKT5CJYR0TK1WQGMD0', '06FB2EV29HSK6EJ5VF50R87VC4', '2026-06-28 15:30:10.567', '2026-06-28 15:30:10.567', NULL, '52cd02f3a176cd078db479ef850cbcf4', 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 ('06FGYPJ6FTEPP4JK3D7D01ZSMM', '06FGX26B0NAC9WRJMVB0QE6CV8', '2026-06-28 17:50:13.767', '2026-06-28 17:50:13.767', NULL, '3e0ac1d814ac13a12b68714c7320075f', 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);
+22
View File
@@ -8065,3 +8065,25 @@ SCHEMA DRAFTED (2026-06-28): docs/design/drawing-card-schema.md — declarative
MODEL CLARIFICATION (user): we build clide''s OWN native Flutter CustomPaint interpreting a declarative scene-graph document. We do NOT port the HTML Canvas 2D API ''HTML canvas'' in D-91 is only the mental model (a general drawing surface, chosen to reject Obsidian''s .canvas schema), not an API to implement. The declarative model is closer to SVG/a retained scene-graph than to canvas''s imperative 2D context.
RESCOPED (D-103, 2026-06-28): the primitive layer is now SVG and the SVG renderer (T-320) is the engine so T-318 is NO LONGER a primitive renderer. T-318 = the document envelope ({template? | svg/svgPath}, card metadata), the template DISPATCH, and the clide FLUTTER OVERLAY: per-object label/description caption widgets + lightbox affordance, anchored to SVG elements via data-label / data-description / data-lightbox. Now blocked by T-320 (engine first). Schema: docs/design/drawing-card-schema.md.', NULL, '2026-06-28 15:30:33', '2026-06-28 15:30:33.771', '2026-06-28 15:30:33.771', NULL, '6298b402fc49547c7bdba1f3d5626bed', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGX26B0NAC9WRJMVB0QE6CV8', 'description', 'A conversation-pane drawing card (peer of the SVG card T-320) that renders a D2 diagram. SEPARATE card type from SVG, but the ONLY difference is a compile step in front: D2 source → compile to SVG → render through the SAME SVG widget (T-320). Blocked on T-320 for that reason.
CLI/parity (D-6): driven via the drawing-card dispatch (T-318) ''clide draw --file pipeline.d2'' (type inferred from .d2), or template:"d2" with inline source in the drawing-card JSON. Honest userError on a d2 compile failure (bad source), mirroring image/svg validation.
UX: the rendered diagram LEADS; the d2 source folds into a collapsed ''view d2 source'' disclosure on the card (ClideCollapserCard pattern, T-305) so the code doesn''t dominate. Display-only per D-78.
Open: D2 compiler dependency d2 is Go (there is a d2-diagram skill). Decide whether to shell out to a d2 binary (supporter-tool pattern, peer of pql) or vendor it; flag before building. Wireframe: docs/design/wireframes/conversation/d2-render-card.png.', 'A conversation-pane drawing card (peer of the SVG card T-320) that renders a D2 diagram. SEPARATE card type from SVG, but the ONLY difference is a compile step in front: D2 source compile to SVG render through the SAME SVG widget (T-320). Blocked on T-320 for that reason.
CLI/parity (D-6): driven via the drawing-card dispatch (T-318) ''clide draw --file pipeline.d2'' (type inferred from .d2), or template:"d2" with inline source in the drawing-card JSON. Honest userError on a d2 compile failure (bad source), mirroring image/svg validation.
UX: the rendered diagram LEADS; the d2 source folds into a collapsed ''view d2 source'' disclosure on the card (ClideCollapserCard pattern, T-305) so the code doesn''t dominate. Display-only per D-78.
Open: D2 compiler dependency d2 is Go (there is a d2-diagram skill). Decide whether to shell out to a d2 binary (supporter-tool pattern, peer of pql) or vendor it; flag before building. Wireframe: docs/design/wireframes/conversation/d2-render-card.png.
PATH / tool-resolution dependency (2026-06-28): the d2 compile step shells out to the ''d2'' binary, so it relies on clide resolving d2 on PATH. clide already derives a login-shell PATH for every spawned tool T-439 (consolidated the Linux PATH fix T-347), both DONE so this is very likely already covered. ACCEPTANCE: verify clide resolves ''d2'' via that login-shell PATH on a Homebrew/linuxbrew install (d2 lives in /home/linuxbrew/.linuxbrew/bin). WATCH: a ''$SHELL -lc'' login probe can miss brew if ''brew shellenv'' is only in ~/.bashrc (login shells source .bash_profile/.profile, not .bashrc); if so, that''s a real follow-up on T-439''s probe surfaced from here. Honest userError if d2 is unresolved, hinting how to install/locate it. (The ''command -v d2'' miss seen during design was Claude Code''s own lean sandbox PATH, not clide.)', NULL, '2026-06-28 16:18:22', '2026-06-28 16:18:22.531', '2026-06-28 16:18:22.531', NULL, 'dc5a34f7af2980ed5caac6be224670c4', 2) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FGYPJ6FTEPP4JK3D7D01ZSMM', 'description', NULL, 'Implement D-104. clide resolves external supporter binaries (claude, d2, future supporter tools) via an explicit user-scope ''tools:'' map (name → absolute path), consulted FIRST.
Resolution order: (1) explicit settings path use it; honest error + fallback-with-warning if missing/non-exec. (2) bundled/pinned (pql D-58, git D-59). (3) login-shell PATH (T-439). (4) bare PATH.
First run: auto-detect each tool ONCE probe the login-shell PATH + well-known dirs (~/.local/bin, /usr/local/bin, /opt/homebrew, /home/linuxbrew/.linuxbrew/bin) and WRITE the discovered absolute paths into the map (pinned, not re-probed each launch). Provide a re-detect action.
Storage: user-scope SettingsStore keyed by machine (D-93) never committed. Surface in the Config tab (T-414) + flag unresolved tools in the Problems panel (echoes D-58''s soft floor). Generalizes D-58''s CLIDE_PQL_BIN override to all supporter tools. First consumer: the d2 template (T-494).', NULL, '2026-06-28 17:50:13', '2026-06-28 17:50:13.728', '2026-06-28 17:50:13.728', NULL, 'db0f73d19a558e556733e8b42ef7081a', 2) ON CONFLICT(hash) DO NOTHING;
+17
View File
@@ -9927,3 +9927,20 @@ SCHEMA DRAFTED (2026-06-28): docs/design/drawing-card-schema.md — declarative
MODEL CLARIFICATION (user): we build clide''s OWN native Flutter CustomPaint interpreting a declarative scene-graph document. We do NOT port the HTML Canvas 2D API ''HTML canvas'' in D-91 is only the mental model (a general drawing surface, chosen to reject Obsidian''s .canvas schema), not an API to implement. The declarative model is closer to SVG/a retained scene-graph than to canvas''s imperative 2D context.
RESCOPED (D-103, 2026-06-28): the primitive layer is now SVG and the SVG renderer (T-320) is the engine so T-318 is NO LONGER a primitive renderer. T-318 = the document envelope ({template? | svg/svgPath}, card metadata), the template DISPATCH, and the clide FLUTTER OVERLAY: per-object label/description caption widgets + lightbox affordance, anchored to SVG elements via data-label / data-description / data-lightbox. Now blocked by T-320 (engine first). Schema: docs/design/drawing-card-schema.md.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 11:11:40', '2026-06-28 15:30:33.771', NULL, '98e798ae1f9749ff4d6895b5ad15cbd9', 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 ('06FGX26B0NAC9WRJMVB0QE6CV8', 'task', '06FB2EDCBYRBDSV9V1PJ1KE3CM', 'D2 diagram card — compile d2 source to SVG, reuse the SVG render widget', 'A conversation-pane drawing card (peer of the SVG card T-320) that renders a D2 diagram. SEPARATE card type from SVG, but the ONLY difference is a compile step in front: D2 source → compile to SVG → render through the SAME SVG widget (T-320). Blocked on T-320 for that reason.
CLI/parity (D-6): driven via the drawing-card dispatch (T-318) ''clide draw --file pipeline.d2'' (type inferred from .d2), or template:"d2" with inline source in the drawing-card JSON. Honest userError on a d2 compile failure (bad source), mirroring image/svg validation.
UX: the rendered diagram LEADS; the d2 source folds into a collapsed ''view d2 source'' disclosure on the card (ClideCollapserCard pattern, T-305) so the code doesn''t dominate. Display-only per D-78.
Open: D2 compiler dependency d2 is Go (there is a d2-diagram skill). Decide whether to shell out to a d2 binary (supporter-tool pattern, peer of pql) or vendor it; flag before building. Wireframe: docs/design/wireframes/conversation/d2-render-card.png.
PATH / tool-resolution dependency (2026-06-28): the d2 compile step shells out to the ''d2'' binary, so it relies on clide resolving d2 on PATH. clide already derives a login-shell PATH for every spawned tool T-439 (consolidated the Linux PATH fix T-347), both DONE so this is very likely already covered. ACCEPTANCE: verify clide resolves ''d2'' via that login-shell PATH on a Homebrew/linuxbrew install (d2 lives in /home/linuxbrew/.linuxbrew/bin). WATCH: a ''$SHELL -lc'' login probe can miss brew if ''brew shellenv'' is only in ~/.bashrc (login shells source .bash_profile/.profile, not .bashrc); if so, that''s a real follow-up on T-439''s probe surfaced from here. Honest userError if d2 is unresolved, hinting how to install/locate it. (The ''command -v d2'' miss seen during design was Claude Code''s own lean sandbox PATH, not clide.)', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-28 14:01:13.733', '2026-06-28 16:18:22.531', NULL, 'db85be505664f8ca1354594bebe73063', 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 ('06FGYPJ6FTEPP4JK3D7D01ZSMM', 'task', NULL, 'Supporter-binary path resolution: explicit user-scope overrides + first-run auto-detect', NULL, 'backlog', 'medium', NULL, NULL, 'D-104', '2026-06-28 17:50:02.366', '2026-06-28 17:50:02.366', NULL, '8643aef22705d5565006a90eead1db18', 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 ('06FGYPJ6FTEPP4JK3D7D01ZSMM', 'task', NULL, 'Supporter-binary path resolution: explicit user-scope overrides + first-run auto-detect', 'Implement D-104. clide resolves external supporter binaries (claude, d2, future supporter tools) via an explicit user-scope ''tools:'' map (name → absolute path), consulted FIRST.
Resolution order: (1) explicit settings path use it; honest error + fallback-with-warning if missing/non-exec. (2) bundled/pinned (pql D-58, git D-59). (3) login-shell PATH (T-439). (4) bare PATH.
First run: auto-detect each tool ONCE probe the login-shell PATH + well-known dirs (~/.local/bin, /usr/local/bin, /opt/homebrew, /home/linuxbrew/.linuxbrew/bin) and WRITE the discovered absolute paths into the map (pinned, not re-probed each launch). Provide a re-detect action.
Storage: user-scope SettingsStore keyed by machine (D-93) never committed. Surface in the Config tab (T-414) + flag unresolved tools in the Problems panel (echoes D-58''s soft floor). Generalizes D-58''s CLIDE_PQL_BIN override to all supporter tools. First consumer: the d2 template (T-494).', 'backlog', 'medium', NULL, NULL, 'D-104', '2026-06-28 17:50:02.366', '2026-06-28 17:50:13.728', NULL, '53f89d410f3d83567393d0baa2ae8c4f', 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);