Repository navigation
fix(extension): hub-client file clicks die silently — the open-file handler's no-posture branch needs a named outcome + the runtime wiring of linkResolution needs a bridge-level test #1845
Description
Important
Problem — Live-reported during the v0.3.8-alpha exercise (Aaron, on the macbook/erlich hub-client posture): clicking files in session output (the edit/write tool parts' rows, and some review rows) does nothing — no open, no toast, no console error. Three sibling bugs were found and fixed along the way (#1839 the layout gate, #1841 the diffCanRender gate, #1843 the webview string-frame stream bug + the whole-row click target — all merged, all live on the dist). But the core symptom persists in the last tested state: a verified-rendered <button data-slot="message-part-title-filename" data-clickable> (devtools-probed in the correct iframe context, so the part HAS a path) still produces nothing on click.
The chain, statically verified end-to-end: iframe onClick → openFileInEditor (posts {source:"amicode", kind:"open-file", url:"file://…"} to the parent) → the top-document relay (origin-pinned to the app origin, open-file in the allowlist) → vscode.postMessage → chat_bridge's open-file handler → resolveChatFileLink → Preview. Every link exists in source. The failure is runtime.
Prime suspect — the handler's own documented silent branch (chat_bridge.ts, the if (!linkResolution) path): "without the context the pre-#1826 local-stat gate stands" — if (!fs.existsSync(fsPath)) return true — a hub path on the client's disk fails the stat and returns with NO named outcome. On the hub-client posture that's exactly the observed behavior: total silence. That would mean io.linkResolution is undefined in the live panel's io despite the activation wiring (extension.ts:1209 registers the callback; chat_panel.ts:252 reads it at io construction) — the #1826 slice's unit tests covered the handler logic but never the runtime wiring of the context into the panel io.
Approach — Two fixes: (1) the silent branch becomes a NAMED OUTCOME — the handler must never die silently (the same discipline as the block system's named outcomes: a toast naming "no posture context" or "not found locally (pre-posture gate)"); (2) root-cause the runtime wiring: verify ChatPanel.linkResolutionContextCallback?.() is non-undefined in a real panel's io at click time, and add a BRIDGE-LEVEL test (the panel io construction with the callback registered), not just unit tests over the resolver. If the context is genuinely absent at runtime, find where the registration's activation ordering loses it (stale panel instance? per-message io rebuild? the deck variant?).
Scope — in: chat_bridge's silent branch, the io wiring + its bridge-level test. out: the app-side fixes (already landed: #1839/#1841/#1843 + the row-click target).
Assumptions — Last tested state: extension v0.3.8-alpha.3, dist bundle index-D2RIBKJy.js, amicode.hubServerUrl armed (the P4 telemetry assert passes), posture should be hub-client.
The debug kit (discriminators, ready to run)
- Top-context relay observer (webview devtools, context
top, then click a row):
window.addEventListener("message", (e) => { if (e.data?.source === "amicode") console.log("RELAY-SAW", e.data.kind, e.origin) })RELAY-SAW open-file→ the post lands; the break is extension-side (the silent branch / wiring) — the prime suspect confirmed.- nothing → the iframe post never fires or the origin pin rejects it; next probe goes inside the iframe context (note: cross-origin property writes from the iframe are blocked — wrap from the top context).
- Iframe-context button probe (context = the
127.0.0.1:4097frame):
const btn = document.querySelectorAll("[data-slot='message-part-title-filename']")[0]; let fired = 0; btn.addEventListener("click", () => { fired++; console.log("BTN CLICK", fired) }); btn.click()
— distinguishes handler-fires-but-no-ops from overlay-intercepted physical clicks. NOTE: the devtools default context istop(vscode-file://vscode-app); the app is the cross-origin iframe — context selection matters (a probe fromtopthrew SecurityError on this box). - Post-reload bundle check: the last tested state predates the
D2RIBKJydist (the row-click target + stream fix); confirm the panel runs it before re-testing.
Prior Art
- feat(app): posture-aware link resolution — vault-local, hub-Preview, remote explicit #1826 (the design), feat(app): posture-aware link resolution — vault-local, hub-Preview, remote explicit #1836 (the implementation whose wiring is now suspect), fix(session-ui): file links silently inert when the new-layout setting is off — un-gate markInlineCode + markFileLinks #1839/fix(session-ui): session-output file rows are dead when their live summary carries no line counts #1841/fix(app): the webview event stream dies on every frame — string SSE data hitting the wire discrimination as a TypeError #1843 (the sibling fixes, all merged), the campaign ledger session-20261021-rd-system-v2 §2 (the full investigation trail).
Source
Found by Aaron live-exercising the v0.3.8-alpha build. Filed at his call to park the live debug and move on — the trail above is the complete state.
Activity
aarontrowbridge commentedon Oct 25, 2026
Fixed in v0.3.9 (#1871).
fix(app): chat file links linkify after a shell remount — the outgoin…
aarontrowbridge commentedon Oct 25, 2026
Root cause found with a headless repro (panel framed like the webview, an inline hub path, click, record the parent envelope). Opening a session by
/<dir>/session/<id>redirects to/server/<key>/session/<id>and remounts the server-scoped shell. The outgoingAmicodeFileRefBridge'sonCleanupran after the incoming one registered and calledregisterFileRefResolver(undefined), wiping the live resolver. Result: zeroresolve-filerequests, pills never linkified, authored links kept dead relative hrefs, clicks posted nothing. The direct/server/URL worked, which is why this looked intermittent.Fix on lane/0.3.10 (#1890): the unregister handle clears only its own registration, and a reactive version re-linkifies already-rendered messages. Verified both routes: linkified, resolved, open-file posted; the extension resolves hub-client clicks to preview-file, and the Preview tab reads the hub file. The v0.3.9 silent-branch toasts were real but not the cause.
aarontrowbridge commentedon Oct 27, 2026
Shipped in v0.3.10 (https://github.com/harmoniqs/amicode/releases/tag/v0.3.10).