diff --git a/DESIGN-REVIEW.md b/DESIGN-REVIEW.md new file mode 100644 index 0000000..25fa0ae --- /dev/null +++ b/DESIGN-REVIEW.md @@ -0,0 +1,337 @@ +# Triple-C Design & Product Review + +**Date:** 2026-08-09 · **Version reviewed:** 0.3.0 · **Reviewer:** Fable 5 + +Scope: `app/src/` (App, layout, projects, settings, terminal, ui, store, index.css), +README/CLAUDE.md/TODO.md, the four repo screenshots, and `triple-c-app-logov2.png`. + +--- + +## Summary verdict + +The bones are good. The floating-panel layout reads clean, the GitHub-dark palette is +inoffensive, and terminal-as-centerpiece is correct for this product. + +The two real problems are structural, and they are the same problem seen from two sides: +**the project — the app's actual unit of work — has no room to live.** Everything about a +project (backend auth, mounts, git identity, env vars, ports, Claude settings, file +manager) is stuffed into a ~280px sidebar card (`ProjectCard.tsx`, 1,257 lines) that +sprays out seven modals to compensate. + +`screenshot_for_fix/project_config_run_off.png` is not a bug to patch. It is the +architecture reporting that the config does not fit where it lives. Fixing that one thing +also solves the modal pile, the density problems, *and* creates the surface where newer +Claude Code concepts belong. + +--- + +## Part A — Visual & interaction design + +### A1. Tokens: coherent but thin, with one real contrast failure + +`index.css` is GitHub Primer dark, verbatim (`#0d1117 / #161b22 / #21262d / #30363d / +#8b949e / #58a6ff`). Defensible — familiar, calm, terminal-adjacent — but the token layer +stops at 11 variables. Roles the code is already faking ad hoc: + +- **No elevation/overlay token.** Modals reuse `--bg-secondary`, so a modal over the + sidebar is the same color as the sidebar. Add `--bg-overlay: #1c2128` and + `--shadow-overlay`. +- **No muted-accent tokens.** The code hand-rolls `bg-yellow-500/20 text-yellow-400`, + `bg-blue-500/20 text-blue-400`, `--warning/15`, `--error/10`. Add `--accent-muted`, + `--warning-muted`, `--error-muted`, `--success-muted`. Those raw Tailwind palette colors + are the only two places the token system leaks. +- **Radius drift:** `rounded` (4px), `rounded-lg` (8px), plus hardcoded 3px/6px in help + styles. Pick two: 6px controls, 8px panels. + +**Contrast bug (concrete):** white text on `--accent #58a6ff` is ~**2.5:1** — fails WCAG +AA. That is the primary button ("Add Project"), the "Update" pill, and more. Primer solves +this with two accents: keep `#58a6ff` as the *foreground/link* accent and add +`--accent-emphasis: #1f6feb` for filled buttons (white on `#1f6feb` ≈ 4.7:1). + +Same story for `bg-[var(--success)] text-white` ON toggles — `#3fb950` + white ≈ **2.1:1**, +the worst offender in the app. + +What passes: `--text-secondary #8b949e` on `#161b22` ≈ 5.8:1, fine even at 12px. +`--warning #d29922` ≈ 7:1. But `disabled:opacity-50` on secondary text drops to ~2.4:1 — +and since the entire config form is disabled while the container runs, **the most common +state of the form is illegible.** Use a dedicated `--text-disabled: #6e7681` instead of +opacity. + +### A2. Type and density: everything is 12px + +Roughly 90% of the UI is `text-xs`. Hierarchy is carried almost entirely by weight plus a +single `text-lg` modal title. Forms feel cramped rather than dense — density is +information per pixel, not small type. + +Proposed scale with roles: **11px** uppercase section labels (already used, keep) · +**12px** secondary/meta · **13px** default UI/body/form values · **14px** panel headers · +**16px** view titles. + +Path strings in mono are a nice identity touch — extend mono to all machine values (model +IDs, ports, digests), which the Bedrock/Ollama forms currently render in the UI face. + +The outer chrome spends generously while content starves: `App.tsx` wraps everything in +`p-6 gap-4`, then the config form gets ~180px-wide inputs for AWS secret keys. Keep the +floating-island look; `p-3 gap-3` buys content ~24px horizontally and the terminal two +more rows. + +### A3. The project card is three components wearing one div + +`ProjectCard` is simultaneously a list row, a command strip, and the entire settings form. + +- **Selection and disclosure are conflated.** Clicking a row both selects it and expands an + accordion in place, shoving the other projects down. The 06-28 screenshot shows 18 + projects — this jank is daily. +- **Actions are unstyled text links.** `ActionButton` renders `text-xs px-2 py-0.5` colored + text with no border or background, so Start/Stop/Terminal/Shell/Files/Backup/Config/Remove + read as a wrapping line of links. Worse, **Remove (destructive, red) wraps directly next + to Config** with a ~20px hit target. +- **Double-click-to-rename** is undiscoverable and keyboard/touch-inaccessible. +- **27 hover-only `` markers in ProjectCard alone.** When a form needs 27 tooltips, + the form is the problem. + +### A4. Modals: eight is a pattern smell, and none are real dialogs + +Hanging off ProjectCard: EnvVars, PortMappings, ClaudeInstructions, ClaudeCodeSettings, +ContainerProgress, FileManager, ConfirmRemove — plus AddProject, three reused from +SettingsPanel, and Update/ImageUpdate/Help from TopBar. + +Each reimplements the overlay div, Escape handler, and click-outside logic by hand. **None +has `role="dialog"`, `aria-modal`, a focus trap, or focus restore** — zero hits for +`role=`, `aria-modal`, or `tabIndex` across `components/`. + +The pattern is wrong not because modals are bad, but because these are not modal *tasks*. +Env vars, ports, instructions, and Claude settings are all "edit part of the project +config" — a detail view's job. + +- Legitimately modal: **ConfirmRemove**, **AddProject**. +- **FileManager** wants to be a main-area tab, not a 42rem popup. +- **ContainerProgressModal actively hurts:** starting a container blocks the entire app + behind an overlay for an operation designed to be routine. Replace with inline row state + plus an error toast. +- Whatever survives should be one shared `` primitive with focus trap + ARIA. + +### A5. Keyboard and focus: currently unsupported + +For a tool whose centerpiece is a keyboard-driven terminal, the chrome is mouse-only. + +- Inputs use `focus:outline-none` with only a low-contrast border swap; **buttons have no + focus style at all** — tabbing through the sidebar is invisible. +- One-line fix: add `--focus-ring: #58a6ff` and + `:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }` +- No shortcuts for constant actions: `Ctrl+T` new terminal, `Ctrl+Tab`/`Ctrl+1..9` switch, + `Ctrl+W` close, `Ctrl+P` project switcher. The only shortcut in the app is the STT mic. +- Hit targets below 24px: tab close "×" (~14px), Tooltip "?" (14px), Browse "...". The + status bar is `h-6` yet hosts two interactive controls. + +### A6. Status communication + +Three disconnected dot systems (TopBar Docker/Image, per-project status, StatusBar counts), +all 8px and color-only. + +- **Stopped (gray) and error (red) differ only by hue**, and Docker-unavailable renders the + same gray as Docker-still-being-checked (`dockerAvailable === null` and `false` both fall + through). An outage should be loud; unknown should pulse. +- Color-only encoding fails colorblind users. Add shape or text — `● Running`, `○ Stopped`, + `⚠ Error`. The words are already in the model. +- Raw `String(e)` errors dumped into a 12px card line; bollard errors are long. Errors need + a home: toast plus expandable detail. +- The TopBar tab strip is visually disconnected from the terminal it controls. Move tabs + onto the terminal panel's top edge so the active tab connects to its content. + +### A7. Empty and first-run states + +`WelcomeScreen` is three lines of gray text with no affordance — "Add a project from the +sidebar" *describes* a button instead of *being* one. This is also where brand could exist: +the orange sun-gear logo appears nowhere in the UI and shares no DNA with the blue-on- +graphite chrome. + +Make it an onboarding checklist reusing state already tracked: +✓ Docker detected → ✓ Image pulled → **[ Add your first project ]** → open terminal. +The same pattern fixes the "image missing" case, today just a gray dot in the corner. + +### A8. Dark-only: keep it + +Right call. Terminal-first developer tool, xterm content is dark, audience expects it. The +tokens make a light theme cheap later. Don't spend on it now — but keep discipline that no +color bypasses the token layer. + +### A9. Iconography + +Mixed: hand-inlined Feather-style SVGs in the sidebar rail, text glyphs elsewhere ("×", +"?", "...", "+", "✓", "✕"). Adopt `lucide-react` — same stroke style already being +imitated, tree-shakeable — and replace the text glyphs. It also supplies the per-concept +icons Part B needs. + +--- + +## Part B — Information architecture & product concepts + +### B1. The diagnosis + +Current IA: `Projects | MCP | Settings` in a sidebar, terminal in main, project detail +crammed into the list. + +Deleting the MCP tab was correct — but **the lesson matters more than the freed slot. +MCP died as a Triple-C feature because Claude Code absorbed it.** Hooks, skills, agents, +plugins, output styles, and statusline are all the same species: files under `.claude/` +that Claude Code manages natively with its own TUIs (`/agents`, `/hooks`, `/plugins`). If +Triple-C builds form editors for them, it loses the same race again and becomes exactly +what it should fear — a settings-file editor with a GUI skin. + +What Claude Code *cannot* do is what Triple-C uniquely owns: **the container boundary and +what persists behind it.** The config volume, the workspace mounts, the lifecycle, the +scheduler already shipping in every image, and the fleet view across many projects. + +> **Principle: Triple-C shows state and launches things. Claude Code edits its own config.** + +Sessions, checkpoints, background tasks, scheduled tasks, capability inventory → surface +them, read from the volume, launch into the terminal. Hook/skill/agent *editing* → +deep-link into the terminal, don't rebuild. + +### B2. Proposed IA: three nouns + +**Project** (a sandboxed workspace) · **Session** (a resumable conversation) · +**Library** (reusable capabilities pushed into projects). Everything is one of these, or +Settings. + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ TopBar: ⌂ api-server │ ▣ api-server ✕ │ ▣ api (bash) ✕ │ ● ● ? │ +├─────────────┬──────────────────────────────────────────────────────┤ +│ ◤ Projects │ MAIN AREA — a tab strip of two tab kinds: │ +│ ● api-serv │ ⌂ project-home tabs ▣ terminal tabs │ +│ ○ blog │ │ +│ ● data-pipe│ ⌂ api-server ● Running · 2h 14m │ +│ … │ ┌─────────┬──────────┬────────────┬────────┐ │ +│ ◧ Library │ │Overview │ Sessions │ Automation │ Config │ │ +│ ⚙ Settings │ └─────────┴──────────┴────────────┴────────┘ │ +├─────────────┴──────────────────────────────────────────────────────┤ +│ StatusBar: 18 projects · 8 running · 4 terminals 🎤 ↓Jump │ +└────────────────────────────────────────────────────────────────────┘ +``` + +- **Sidebar** becomes a pure list plus nav rail. Rows carry name, path, status dot, and on + hover a play/stop and terminal button. Clicking opens (or focuses) that project's + **Project Home** tab. The freed MCP slot becomes **Library**. +- **Main area** hosts two tab kinds: terminals (as today) and project-home tabs, like VS + Code's Settings tab. The terminal stays the centerpiece; Project Home is one keystroke + away rather than a layer on top. +- **All seven config modals dissolve** into the Config tab, full-width, grouped: + *Workspace* (folders/mounts), *Model* (backend + auth), *Access* (git/SSH/env/ports), + *Runtime* (docker access, sandbox, permission mode, Mission Control). Room for visible + helper text kills most of the 27 tooltips. Save-on-blur stays but gains a visible + "Saved ✓ / Failed" indicator — today failures go only to `console.error`, which is + silent data loss. + +#### Project Home — Overview tab + +``` + api-server ● Running · started 2h ago + [ Stop ] [ Open Claude Terminal ] [ Shell ] [ Files ] [⋯ menu] + + Permission mode ( Plan ) ( Default ) ( Accept Edits ) (▮ Bypass ▮) + Sandbox ON — bubblewrap isolation Backend Anthropic + + CAPABILITIES (read from container volume) + ◆ Skills 7 ◆ Agents 3 ◆ Hooks 2 ◆ Plugins 1 ◆ Commands 5 + └ click any → drawer listing names/descriptions, + [Manage in terminal] → opens claude with /agents etc. + + RECENT SESSIONS SCHEDULED TASKS + "Refactor OAuth flow" 2h ago [Resume] nightly-review 0 3 * * * + "Fix flaky CI test" 1d ago [Resume] [2 notifications] +``` + +### B3. The four concepts worth building + +**1. Sessions & Resume — the flagship.** The stop/start container model creates a problem +plain Claude Code doesn't have: stop a container, come back Tuesday, and "which +conversation was I in?" is buried in the volume. Read session metadata via `docker exec` +(the exec and tar plumbing already exists), list sessions with summary and age, and make +**[Resume]** open a terminal running `claude --resume `. Closing a terminal tab today +silently abandons a session; it should say "Session saved — resume from Project Home." +This turns the biggest architectural quirk into the best feature. + +Do **not** build a checkpoint browser. Mention rewind (`Esc Esc`) in Help and stop there. + +**2. Library — the MCP tab's successor.** The pattern was already invented three times: +global MCP servers with per-project checkboxes, global Claude instructions, and Mission +Control's bundled skill install. Generalize it once: a Library of **skills, agents, and +slash commands** defined globally with per-project enable, synced into the container's +`.claude` volume by the entrypoint. Across many projects, "write a skill once, enable it in +twelve sandboxes" is genuinely differentiated. Keep the editor minimal — name plus markdown +textarea, or "import from folder." Not a structured form per frontmatter field. + +**3. Permission mode as the hero control.** The whole pitch is "sandbox so you can safely +go fast," yet that pitch is expressed as a scary boolean buried in a config accordion. +Replace it with Claude Code's real vocabulary — a segmented control (**Plan / Default / +Accept Edits / Bypass**) on Overview, echoed as a badge on terminal tabs, with sandbox +state beside it. When sandbox is ON, Bypass loses its red paint ("contained by sandbox"); +when sandbox is OFF *and* Bypass is on, that is when caution color earns its place. This +reframes the product's core value in the product's own UI. + +**4. Automation tab.** `triple-c-scheduler` ships in every container with +add/list/logs/notifications — and its only UI is a CLAUDE.md paragraph telling Claude to +run it. Wrap it: task list (name, cron, last run, enabled), toggle/run-now/view-log, and a +notification badge on the project row. "Your nightly agent left you a note" is a reason to +open the app in the morning. Fleet-of-scheduled-agents management across projects is +something the Claude Code TUI does not offer. + +**Explicitly skip:** status line builder, output-styles editor, hook *editors* (surface the +count, deep-link to the terminal), checkpoint browser, marketplace browser. Each is niche, +natively handled, or a settings-editor trap. + +### B4. Coherence test + +Every screen answers exactly one question: + +| Screen | Question | +|---|---| +| Sidebar | What projects exist and are they up? | +| Project Home | What can this sandbox do, and where did I leave off? | +| Terminal | Do the work. | +| Library | What capabilities do I reuse? | +| Settings | How does the host behave? | + +Anything that doesn't answer one of those doesn't get a nav slot. + +--- + +## Priorities + +### Tier 1 — high impact, cheap + +1. `:focus-visible` ring and stop stripping outlines (one CSS rule + token). Add + `Ctrl+T` / `Ctrl+W` / `Ctrl+1..9` / `Ctrl+Tab`. +2. Contrast: `--accent-emphasis: #1f6feb` for filled buttons; kill white-on-`#3fb950`; + `--text-disabled` instead of `opacity-50`. +3. Real buttons for project actions; Remove into an overflow menu; primary action filled. +4. Inline start/stop progress and an error toast; delete `ContainerProgressModal`. +5. Status dots get labels or shapes; Docker-down turns red; null state pulses. +6. Welcome screen becomes an onboarding checklist with a real button, plus the logo. +7. One shared `` with focus trap and ARIA for the modals that remain. +8. Permission-mode segmented control replacing the boolean. +9. `lucide-react` icons; move the tab strip onto the terminal panel. + +### Tier 2 — high impact, expensive + +1. **Project Home tabbed view** — the structural fix that dissolves the modal pile and the + 1,257-line ProjectCard. The forms already exist; this is mostly moving and splitting. +2. **Sessions tab** with `claude --resume`. +3. **Library** — generalize global→per-project sync to skills/agents/commands. +4. **Automation tab** wrapping `triple-c-scheduler`, with notification badges. + +### Tier 3 — skip + +- Light theme (dark-only is right; tokens keep the door open). +- Editors for hooks, statusline, output styles; checkpoint browser; marketplace browser. +- Any new global sidebar tab beyond Library. +- Rebuilding MCP management in any form. Let the deletion be a lesson, not a vacancy. + +--- + +**One sentence:** promote the project from a sidebar card to a first-class workspace view, +use the volume you already own to surface sessions/capabilities/automation instead of +building config editors, and spend a focused week on focus rings, contrast, and button +affordances — the visual layer needs sanding, not redesign. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..70a1f47 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,190 @@ +# Triple-C Roadmap — Claude Code Feature Parity + +**Date:** 2026-08-09 · **Baseline:** v0.3.0 · **Claude Code reference:** 2.1.226 + +Companion to [DESIGN-REVIEW.md](DESIGN-REVIEW.md), which covers visual design and +information architecture. This document covers *which Claude Code capabilities Triple-C +should surface, and why.* + +--- + +## Guiding principle + +> **Triple-C shows state and launches things. Claude Code edits its own config.** + +Triple-C's built-in MCP server management was removed in this cycle because Claude Code +absorbed the capability natively (`claude mcp add/list/remove`, `.mcp.json`, `/mcp`). +Hooks, skills, agents, plugins, output styles, and statusline are the same species: files +under `.claude/` with first-class Claude Code TUIs. Building GUI form editors for them +means losing the same race again. + +What Claude Code cannot do is what Triple-C uniquely owns: **the container boundary and +what persists behind it** — the config volume, workspace mounts, lifecycle, the bundled +scheduler, and the fleet view across many projects. + +--- + +## Current coverage (v0.3.0) + +Triple-C sets exactly five `settings.json` keys, plus a sandbox block: + +| Key | Surfaced as | +|---|---| +| `tui` | TUI Mode select (`fullscreen`) | +| `effort` | Effort Level select (`low`/`medium`/`high`) | +| `autoScrollEnabled` | Auto-Scroll Disabled toggle | +| `focusMode` | Focus Mode toggle | +| `showThinkingSummaries` | Thinking Summaries toggle | +| `sandbox.*` | Sandbox toggle (`enabled`, `enableWeakerNestedSandbox`, `allowUnsandboxedCommands`) | + +Plus four env feature flags — `CLAUDE_CODE_NO_FLICKER`, `CLAUDE_CODE_ENABLE_AWAY_SUMMARY`, +`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`, `ENABLE_PROMPT_CACHING_1H` — and arbitrary user-set +`CLAUDE_CODE_*` vars via the Env Vars modal. + +Also covered: per-project auth backends (Anthropic OAuth, Bedrock incl. SSO refresh, +Ollama, OpenAI-compatible), user-level `CLAUDE.md` composition, `claude update` on every +container start, terminal ergonomics (OAuth URL detection, OSC 52 clipboard, image paste, +file drag-drop, STT), the web terminal, and workspace backup. + +--- + +## Gap analysis + +### Committed for this cycle + +| # | Gap | Today | Plan | +|---|---|---|---| +| 1 | **Permission modes** | one boolean → `--dangerously-skip-permissions` | Four-state control (Plan / Default / Accept Edits / Bypass) → `--permission-mode`. Verified choices on 2.1.226: `acceptEdits`, `auto`, `bypassPermissions`, `manual`, `dontAsk`, `plan`. | +| 2 | **Session resume** | none | List sessions from the config volume; `[Resume]` opens a terminal on `claude --resume `. | +| 3 | **Capability inventory** | none | Read-only counts + names for skills / agents / hooks / plugins / commands / native MCP servers. Deep-link to the terminal to manage. | +| 4 | **Automation** | `triple-c-scheduler` ships in every container with *zero* UI | Task list, cron editor, run-now, logs, notification badges. | +| 5 | **Container auth handoff** | manual code paste | See "Authentication handoff" below — design decision pending. | + +### Deliberately skipped + +Status line builder · output-styles editor · hook *editors* · checkpoint/rewind browser · +plugin marketplace browser. Each is niche, natively handled by Claude Code's own TUI, or a +settings-editor trap. Surface counts and deep-link instead. + +### Not yet scheduled + +- Granular `permissions.allow` / `ask` / `deny` rules and `additionalDirectories` +- Sandbox detail settings (`filesystem.allowRead/allowWrite`, `allowedDomains`, + `excludedCommands`) — currently documented for hand-editing via `SANDBOX_INSTRUCTIONS` +- Project-level `.claude/settings.json` vs user-level settings hierarchy +- A model picker. **Note:** the only model strings in the app today are stale placeholders + (`anthropic.claude-sonnet-4-20250514-v1:0` in `AwsSettings.tsx` and `ProjectCard.tsx`, + `qwen3.5:27b`, `gpt-4o / gemini-pro / etc.`). These are free-text placeholders, not + dropdowns, but they should be refreshed to current model identifiers regardless. +- The container's settings.json merge is **shallow** (`jq -s '.[0] * .[1]'`), so a + user-authored nested block such as `sandbox.filesystem.allowWrite` is replaced wholesale + on every container start. Worth deepening to `*` recursive merge. + +--- + +## Authentication handoff + +**Goal:** stop making users hand-copy an auth code into every container. + +**Constraint discovered during research:** `claude login`'s callback server uses an +**ephemeral port** and its redirect URI is **not configurable** for the main login flow +(`--callback-port` and `oauth.callbackPort` apply to *MCP server* OAuth only). So a design +that pre-assigns each container a fixed callback port and routes to it cannot work as +stated — there is no fixed port to route. + +There is also a known container gotcha: on Linux, Node resolves `localhost` to IPv6 first, +so the callback server may bind `[::1]:PORT` only and be unreachable over IPv4 +([anthropics/claude-code#44844](https://github.com/anthropics/claude-code/issues/44844)). + +Two viable options: + +### Option A — long-lived token injection (simple) + +`claude setup-token` (verified present on 2.1.226: *"Set up a long-lived authentication +token (requires Claude subscription)"*) returns a ~1-year OAuth token. Triple-C runs it +once on the host, stores the token in the OS keychain via the existing `secure.rs`, and +injects `CLAUDE_CODE_OAUTH_TOKEN` into every container that uses the Anthropic backend. + +- No routing, no ports, no proxy. +- One auth event covers every project. +- Cost: small. Reuses existing keychain and env-injection plumbing. +- Limits: token is subscription-scoped and expires annually; per the docs a `setup-token` + token cannot drive Remote Control sessions or claude.ai connector fetches. + +### Option B — the Auth Bridge (general loopback-callback bridge) + +Option A only solves Claude Code. The same problem affects every CLI that authenticates by +starting a temporary loopback listener and opening a browser at a URL that redirects back +to it — Concourse `fly login` (random loopback port serving `/auth/callback`), +`aws sso login`, and many others. Inside a container the host browser cannot reach that +listener, so login stalls. + +Because the ports are ephemeral and unconfigurable, nothing can be pre-assigned. The bridge +**discovers** listeners instead: + +1. While enabled for a running project, poll the container for loopback TCP listeners by + reading `/proc/net/tcp` and `/proc/net/tcp6` over `docker exec` — no dependency on + `ss`/`netstat`/`lsof`, which aren't guaranteed in the image. +2. For each newly-appeared loopback listener, bind **the same port on the host's + `127.0.0.1`** (never `0.0.0.0` — that would expose container internals to the LAN). +3. Proxy each accepted connection into the container over the Docker API via + `socat - TCP:127.0.0.1:` (socat already ships in the image), reusing the existing + attached-exec streaming in `docker/exec.rs`. Going through the Docker API rather than a + container IP keeps this working on Docker Desktop, where container IPs are not routable + from the host. +4. Fall back to `TCP6:[::1]:` when the listener appeared only on IPv6 — on Linux, + Node resolves `localhost` to IPv6 first, so `claude login` frequently binds `::1` only + ([anthropics/claude-code#44844](https://github.com/anthropics/claude-code/issues/44844)). +5. Tear down when the listener vanishes, the container stops, the bridge is disabled, or + the app exits. Ports already covered by the project's explicit port mappings are skipped; + host-side conflicts are reported rather than silently swallowed. + +Opt-in per project (`auth_bridge_enabled`, default off), since it makes container-internal +loopback services reachable from the host. + +**Plan:** ship **A** for Claude Code specifically — it removes the pain for the common case +at a fraction of the cost — and **B** as the general mechanism covering every other CLI. +They compose: A means most users never trigger a browser login at all; B catches AWS SSO, +Concourse, and anything else that needs a real callback. + +--- + +## Sequencing + +**Phase 0 — done.** Remove MCP (frontend, backend, entrypoint, docs) with a self-healing +migration for containers created against the old per-project Docker network. + +**Phase 1 — foundations.** Permission modes end-to-end (including the scheduler bug fix +below). Read-only introspection backend: sessions, capabilities, scheduler. + +**Phase 2 — Tier-1 polish.** Focus rings, contrast fixes, real buttons, inline start/stop +progress, status labels, onboarding welcome screen, shared accessible ``. + +**Phase 3 — Project Home.** Move project config out of the sidebar card into a tabbed +main-area view (Overview / Sessions / Automation / Config), dissolving the modal pile and +splitting the 1,257-line `ProjectCard`. + +**Phase 4 — authentication handoff.** Option A, then evaluate B. + +**Phase 5 — Library.** Global skills/agents/commands with per-project enable, synced into +the config volume by the entrypoint. Generalizes the pattern the MCP tab was reaching for. + +--- + +## Bugs found during this review + +1. **Scheduled tasks ignore the project's permission setting.** + `container/triple-c-task-runner:69` runs + `claude -p "$PROMPT" --dangerously-skip-permissions` unconditionally, regardless of the + project's Full Permissions toggle. Being fixed as part of Phase 1. + +2. **Docs claim Reset preserves credentials; it does not.** + `rebuild_project_container` calls `remove_project_volumes`, which deletes both + `triple-c-home-{id}` (holding `~/.claude.json`) and `triple-c-claude-config-{id}` + (holding `~/.claude`). README.md, HOW-TO-USE.md, and CLAUDE.md all still state that + OAuth tokens survive a Reset. Pre-existing; not yet corrected. + +3. **Stale model placeholders** — see "Not yet scheduled" above. + +4. **Silent save failures.** Project config saves on blur; failures go only to + `console.error`. No user-visible indication. Addressed in Phase 3.