Provider seam: optional deep-link capability (DeepLinker) + provider-owned open metadata (prep for #2) #6

Closed
opened 2026-07-06 22:28:01 +02:00 by dominik.polakovics · 0 comments

This was generated by AI during triage.

Context

Prep work for #2 (Codex provider). Triage of #2 resolved its open attach-story question: OpenAI's remote-connections feature cannot attach a headless CLI session on a Linux server (it requires the Codex desktop App as host, macOS/Windows, QR pairing — see https://developers.openai.com/codex/remote-connections), so a Codex session will have no web Open link. The deep-link machinery must therefore become an optional provider capability instead of a mandatory part of the AgentProvider seam. None of this needs Codex itself — it is a refactor of the existing claude-code-only codebase, and it fixes an existing violation of the provider-isolation rule: core instance code currently imports the claude-code package to compare against its generic-deep-link constant, and the SPA hardcodes the claude.ai fallback URL.

Agent Brief

Category: enhancement
Summary: Make deep-link capture an optional provider capability (DeepLinker), move the open-affordance fallback into provider-owned metadata, and give link-less providers a copyable tmux-attach affordance in the UI.

Current behavior:
CaptureDeepLink is a mandatory method on the AgentProvider interface. On a miss it returns the provider's generic fallback link, and the instance service detects a miss by comparing the result against the claude-code package's exported generic-link constant — a provider-specific import in core. The SPA's open-state logic hardcodes https://claude.ai/code and a claude-specific tooltip as the universal fallback, so every instance row always renders a claude.ai link even if its provider had no such concept.

Desired behavior:
Deep-link capture is an optional capability, following the existing ConnectingReporter pattern: a separate DeepLinker interface that providers may implement. For providers that don't, no capture machinery ever arms (neither at Start nor at startup re-adoption) and deep_link_url stays NULL. The capture contract simplifies: a miss returns an empty string — generic fallbacks are no longer returned through capture, so the write-only-on-hit rule needs no provider-specific constant. Each provider's fallback open link (and its explanatory tooltip text) becomes provider-owned metadata exposed through the providers API; the SPA's open-state logic is driven by that data. An instance row whose provider has no fallback link renders a copyable attach affordance instead — copying tmux attach -t <session_name> (the session name is already in the instances API) with a tooltip explaining the session is driven from a terminal on the lab host. Connecting-pulse and exact-link behavior for claude-code stays exactly as it is today.

Key interfaces:

  • provider.AgentProvider — loses CaptureDeepLink; everything else unchanged.
  • New provider.DeepLinker optional interface — CaptureDeepLink(ctx, sessionName, worktree) (string, error); empty string means miss. The claude-code provider implements it and stops returning its generic link from capture.
  • The instance service's capture arming and the reconcile re-adoption hook — arm only for providers implementing DeepLinker; the claude-code import in core goes away.
  • The providers API response — each provider entry gains fallback-open metadata (URL + human title; empty/absent for providers without a web surface). Additive change to the pinned shape.
  • The SPA open-state helper and its tests — the hardcoded generic link/title constants are replaced by providers-API data; a new state variant renders the copyable attach command.
  • The provider test fake — must cover both a DeepLinker and a non-DeepLinker provider.

Acceptance criteria:

  • A registered provider that does not implement DeepLinker runs manual and AFK sessions end to end with no capture goroutine armed at Start or at startup re-adoption, and its runs keep deep_link_url NULL.
  • Core packages (instance, reconcile, httpapi, store) neither import a concrete provider package nor compare against provider-specific strings — grep-clean for claudecode/claude.ai outside the provider implementations and their tests.
  • The SPA contains no hardcoded provider URL or provider-specific tooltip; the claude-code fallback link and title arrive via the providers API and render exactly as before (miss → generic session-picker link, capture hit → exact link, poll in flight → connecting pulse).
  • An instance row for a provider without fallback metadata shows the copyable tmux attach -t <session> affordance with an explanatory tooltip.
  • A captured empty string is never persisted, and a previously captured real link is never overwritten — the existing write-only-on-hit contract holds under the new shape, covered by tests.
  • The seam change is recorded as a new ADR (optional capability interfaces on the provider seam), consistent with the existing ADR series.

Out of scope:

  • The Codex provider implementation itself (#2 — blocked on this issue).
  • The embedded remote interface (terminal/chat in lab's UI) — separate roadmap item; this issue must not preclude it but builds none of it.
  • Any change to the spawn, auth, catalog, or workspace-seeding parts of the seam.
  • New category labels or tracker conventions.
> *This was generated by AI during triage.* ## Context Prep work for #2 (Codex provider). Triage of #2 resolved its open attach-story question: OpenAI's remote-connections feature cannot attach a headless CLI session on a Linux server (it requires the Codex desktop App as host, macOS/Windows, QR pairing — see https://developers.openai.com/codex/remote-connections), so a Codex session will have **no web Open link**. The deep-link machinery must therefore become an optional provider capability instead of a mandatory part of the `AgentProvider` seam. None of this needs Codex itself — it is a refactor of the existing claude-code-only codebase, and it fixes an existing violation of the provider-isolation rule: core instance code currently imports the claude-code package to compare against its generic-deep-link constant, and the SPA hardcodes the `claude.ai` fallback URL. ## Agent Brief **Category:** enhancement **Summary:** Make deep-link capture an optional provider capability (`DeepLinker`), move the open-affordance fallback into provider-owned metadata, and give link-less providers a copyable tmux-attach affordance in the UI. **Current behavior:** `CaptureDeepLink` is a mandatory method on the `AgentProvider` interface. On a miss it returns the provider's generic fallback link, and the instance service detects a miss by comparing the result against the claude-code package's exported generic-link constant — a provider-specific import in core. The SPA's open-state logic hardcodes `https://claude.ai/code` and a claude-specific tooltip as the universal fallback, so every instance row always renders a claude.ai link even if its provider had no such concept. **Desired behavior:** Deep-link capture is an optional capability, following the existing `ConnectingReporter` pattern: a separate `DeepLinker` interface that providers may implement. For providers that don't, no capture machinery ever arms (neither at Start nor at startup re-adoption) and `deep_link_url` stays NULL. The capture contract simplifies: a miss returns an empty string — generic fallbacks are no longer returned through capture, so the write-only-on-hit rule needs no provider-specific constant. Each provider's fallback open link (and its explanatory tooltip text) becomes provider-owned metadata exposed through the providers API; the SPA's open-state logic is driven by that data. An instance row whose provider has no fallback link renders a copyable attach affordance instead — copying `tmux attach -t <session_name>` (the session name is already in the instances API) with a tooltip explaining the session is driven from a terminal on the lab host. Connecting-pulse and exact-link behavior for claude-code stays exactly as it is today. **Key interfaces:** - `provider.AgentProvider` — loses `CaptureDeepLink`; everything else unchanged. - New `provider.DeepLinker` optional interface — `CaptureDeepLink(ctx, sessionName, worktree) (string, error)`; empty string means miss. The claude-code provider implements it and stops returning its generic link from capture. - The instance service's capture arming and the reconcile re-adoption hook — arm only for providers implementing `DeepLinker`; the claude-code import in core goes away. - The providers API response — each provider entry gains fallback-open metadata (URL + human title; empty/absent for providers without a web surface). Additive change to the pinned shape. - The SPA open-state helper and its tests — the hardcoded generic link/title constants are replaced by providers-API data; a new state variant renders the copyable attach command. - The provider test fake — must cover both a `DeepLinker` and a non-`DeepLinker` provider. **Acceptance criteria:** - [ ] A registered provider that does not implement `DeepLinker` runs manual and AFK sessions end to end with no capture goroutine armed at Start or at startup re-adoption, and its runs keep `deep_link_url` NULL. - [ ] Core packages (instance, reconcile, httpapi, store) neither import a concrete provider package nor compare against provider-specific strings — grep-clean for `claudecode`/`claude.ai` outside the provider implementations and their tests. - [ ] The SPA contains no hardcoded provider URL or provider-specific tooltip; the claude-code fallback link and title arrive via the providers API and render exactly as before (miss → generic session-picker link, capture hit → exact link, poll in flight → connecting pulse). - [ ] An instance row for a provider without fallback metadata shows the copyable `tmux attach -t <session>` affordance with an explanatory tooltip. - [ ] A captured empty string is never persisted, and a previously captured real link is never overwritten — the existing write-only-on-hit contract holds under the new shape, covered by tests. - [ ] The seam change is recorded as a new ADR (optional capability interfaces on the provider seam), consistent with the existing ADR series. **Out of scope:** - The Codex provider implementation itself (#2 — blocked on this issue). - The embedded remote interface (terminal/chat in lab's UI) — separate roadmap item; this issue must not preclude it but builds none of it. - Any change to the spawn, auth, catalog, or workspace-seeding parts of the seam. - New category labels or tracker conventions.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/coding-lab#6
No description provided.