Skip to content

Connections Registry: Formalized Panel with Logos + Custom Connections #327

Description

@jeonghun-jj-lee

type: spec
date: 2026-08-10
status: draft
priority: p2
platform: amicode
tags: [spec, connections, ui, architecture]
linked_plan: null

Connections Registry: Formalized Panel with Logos + Custom Connections

Important

Decision Surface

Problem: The Connections panel is hardcoded to two entries (Company Compute, Pasqal Cloud), renders only colored dots (no brand identity), and provides no mechanism for users to add their own connections. New services (Slack, GitHub, Linear) need homes, and the system needs to be open to user-defined connections.

Approach: Introduce a ConnectionRegistry pattern. Built-in connections (5 total) ship with inline SVG logos and specific validators. Custom connections are user-created with optimistic storage (no server-side probe). Only configured connections appear in the panel; an "Add" picker surfaces unconfigured built-ins and a custom entry point.

Approaches Considered:

  • (A) Registry pattern (chosen): ConnectionRegistry holds built-in catalog + user entries. Validators are per-entry. ConnectionType loosens to string. Clean extension point, single-object additions for future built-ins.
  • (B) Additive (extend hardcoded): Keep CONNECTION_IDS, add a parallel custom array. Minimal change but two data paths, growing if/else chains.
  • (C) Configuration-driven: All connections in an external config file. Overengineered — built-in validators aren't user-configurable.

Scope: Panel infrastructure only (credential home + status + logos + custom connections). Gate wiring (features checking connection status before acting) is a follow-up per-service.

Assumptions:

  • Slack, GitHub, and Linear each take a single token (no OAuth flows in this pass)
  • Custom connections store optimistically — no validation probe
  • Existing Company Compute and Pasqal credential files are untouched
  • No auto-detection of existing credentials (gh CLI auth, env-var tokens)

Acceptance Criteria

  1. Built-in catalog expanded: 5 connections ship: Harmoniqs Cloud, Pasqal Cloud, Slack, GitHub, Linear — each with an inline SVG brand icon and a specific validator
  2. Logo rendering: Each connection card shows an 18px logo icon (SVG for built-ins, letter-avatar for custom) with a 6px state-dot badge on the bottom-right corner
  3. Only configured connections visible: The panel shows only connections that have been configured (connected or previously attempted). Empty state shows "No connections yet" + Add button
  4. Add picker: An "Add connection" button at the bottom of the list opens a picker. Custom entry at the top, then unconfigured built-ins with their logos. Selecting one opens the appropriate inline form
  5. Custom connections: User provides name + token + optional URL. Stored in ~/.amico/custom-connections.json (0600). Render as "connected" immediately on save (optimistic). "Remove" action deletes entirely
  6. Built-in disconnect hides card: "Disconnect" on a built-in clears the credential and removes the card from the panel. The connection reappears in the Add picker
  7. New validators probe correctly: Slack validates against auth.test, GitHub against /user, Linear against the GraphQL endpoint. Probe failures classify as invalid/unreachable per existing discipline
  8. Backward-compatible wire shape: GET /amicode/connections response keeps its shape. New icon field is optional (older UIs fall back to dots). New /catalog and /add-custom and /remove routes are additive
  9. Custom connections file at 0600: Born atomic with restrictive permissions (ADR 0001 discipline)

Key Decisions

Registry architecture

A ConnectionRegistry replaces the hardcoded CONNECTION_IDS array. It holds:

  • A static built-in catalog (5 entries, each with id, name, icon, validator reference, authShape)
  • A dynamic custom-connections list loaded from ~/.amico/custom-connections.json
  • allConnections() merges both; configuredConnections() filters to those with stored credentials

The ConnectionType union type loosens to string across server and UI. Type guards remain for built-in-specific logic (HP flip on Company Compute connect, keychain for Pasqal).

Data contracts

ConnectionEntry (registry unit):

interface ConnectionEntry {
  id: string
  kind: "built-in" | "custom"
  name: string
  icon: { kind: "svg"; svg: string } | { kind: "letter"; letter: string }
  validator: "company-compute" | "pasqal" | "slack" | "github" | "linear" | "none"
  authShape: "base-url-token" | "token-only" | "pasqal-credentials"
  url?: string
}

Custom connections file (~/.amico/custom-connections.json):

[
  { "id": "custom-<uuid>", "name": "My Lab Server", "url": "https://...", "token": "<encrypted>" }
]

New routes:

Method Route Purpose
GET /amicode/connections/catalog Available (unconfigured) built-in connections for the picker
POST /amicode/connections/add-custom Create a custom connection
POST /amicode/connections/remove Remove a custom connection

Validator specs

Connection Probe Valid Invalid Unreachable
Slack GET https://slack.com/api/auth.test with Bearer token JSON body ok: true JSON body ok: false Network failure / non-JSON
GitHub GET https://api.github.com/user with Bearer token 200 401/403 Other status / network failure
Linear POST https://api.linear.app/graphql with Bearer, body {query:"{ viewer { id } }"} 200 + data present 401/403 Other / network failure

UI component map

Component Location Purpose
ConnectionIcon (new) packages/ui/src/amicode/connection-icon.tsx 18px logo + 6px state badge
ConnectionPicker (new) packages/ui/src/amicode/connection-picker.tsx Add-flow picker list + inline forms
ConnectionCard (modified) packages/ui/src/amicode/connections-tab.tsx Replace dot with ConnectionIcon; add "Remove" action for custom
AmicodeConnectionsTab (modified) same file Add picker trigger; handle empty state

Icon assets

All SVGs are monochrome, currentColor fill, 18px viewBox. Built-ins: Harmoniqs logomark, Pasqal "P" mark, Slack hashtag, GitHub octocat silhouette, Linear angled-L. Custom: circle with background fill and first-letter initial.

Constraints & Invariants

  • Secrets never appear in status responses, error messages, or logs (existing discipline, extended to custom connections)
  • Loopback guard applies to all new mutation routes (add-custom, remove)
  • Custom connections file is born atomic at 0600 (ADR 0001)
  • The existing Company Compute and Pasqal credential files are never moved or reformatted
  • Custom connection IDs are prefixed custom- to avoid collisions with future built-in IDs

Prior Art

Source

Resolved in design session 2026-08-10. CONTEXT.md updated with broadened Connection definition.

Notes

  • Gate wiring (Slack posts checking connection status, GitHub operations checking, Linear checking) is intentionally out of scope — tracked as per-service follow-ups once the panel infrastructure lands
  • OAuth flows for Slack/GitHub/Linear are a future enhancement (this pass is token-paste only)
  • The connectionTitle() function is subsumed by the registry's name field but kept as a backward-compat shim until all call sites migrate

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions