diff --git a/docs/project-overview-pdr.md b/docs/project-overview-pdr.md index a239fae2..b8a7dafa 100644 --- a/docs/project-overview-pdr.md +++ b/docs/project-overview-pdr.md @@ -1,374 +1,254 @@ -# CCS Product Development Requirements (PDR) - -Last Updated: 2026-05-07 +# CCS Product Development Requirements ## Product Overview -**Product Name**: CCS (Claude Codex Switch) +**Product name:** CCS (Claude Codex Switch) -**Tagline**: The multi-provider profile and runtime manager for Claude Code and compatible CLIs +**Purpose:** Provide one profile and runtime-management surface for Claude Code, +Codex CLI, Factory Droid, CLIProxy-backed OAuth providers, and compatible API +profiles. -**Description**: Multi-provider CLI/runtime manager enabling seamless switching between multiple Claude accounts, OAuth/API providers, and alternate targets such as Claude Code, Factory Droid, and Codex CLI. Includes a React-based dashboard for configuration management, plus support for local and remote CLIProxyAPI instances, hybrid quota management, and official Claude channel runtime setup for Telegram, Discord, and iMessage. +CCS includes: -**Current Version**: v7.34.x+ (First-class ImageAnalysis MCP tooling, WebSearch MCP, performance improvements) +- a TypeScript CLI and local server; +- a React dashboard for configuration, account, health, and usage workflows; +- isolated account contexts and per-profile settings; +- local or remote CLIProxy routing; +- optional managed tools such as WebSearch and image analysis; and +- an integrated Docker image containing CCS, CLIProxy, and the dashboard. ---- +Release versions and completed-release inventories belong in +[`CHANGELOG.md`](../CHANGELOG.md), not this evergreen requirements document. -## Problem Statement +## Problem -Developers using Claude Code face these challenges: +Developers need to switch between accounts, providers, and compatible CLIs +without repeatedly editing credentials or allowing one session's configuration +to leak into another. They also need a visible, reversible way to manage local +proxy state and diagnose provider readiness. -1. **Single Account Limitation**: Cannot run multiple Claude subscriptions simultaneously -2. **Provider Lock-in**: Stuck with Anthropic's API, cannot use alternatives -3. **No Concurrent Sessions**: Cannot work on different projects with different accounts -4. **Complex Configuration**: Manual env var and config file management -5. **No Usage Analytics**: Lack visibility into token usage and costs across providers +## Product Principles ---- +1. **CLI first:** Core configuration and launch behavior remains scriptable. +2. **Explicit state:** Users can inspect which profile, provider, target, and + proxy mode CCS selected. +3. **Isolation:** Account sessions use separate configuration roots where the + target supports them. +4. **Compatibility:** CCS adapts provider credentials to a target without + redefining the provider or target protocol. +5. **Reversibility:** Persistent writes are explicit and recoverable. +6. **Local ownership:** Credentials and profile state remain on infrastructure + selected by the user. -## Solution +## Users -CCS provides: - -1. **Multi-Account Claude**: Isolated instances via `CLAUDE_CONFIG_DIR` -2. **OAuth Providers**: Zero-config Gemini, Codex, xAI/Grok, Antigravity, Kiro, and other active OAuth integrations, with deprecated Copilot compatibility for existing setups -3. **AI Providers**: Dedicated CLIProxy dashboard for Gemini, Codex, Claude, Vertex, and OpenAI-compatible API-key families -4. **API Profiles**: GLM, Kimi, OpenRouter, any Anthropic-compatible API -5. **Visual Dashboard**: React SPA for configuration management -6. **Automatic WebSearch**: First-class local WebSearch tool with deterministic provider chain for third-party providers -7. **Automatic Image Analysis**: First-class local ImageAnalysis tool with direct provider routing for third-party profiles -8. **Usage Analytics**: Token tracking, cost analysis, model breakdown -9. **Official Claude Channels**: Runtime auto-enable plus dashboard token/config flow for Telegram, Discord, and macOS-only iMessage -10. **Routing Strategy Guidance**: First-class `round-robin` vs `fill-first` controls in CLI and dashboard, with explicit opt-in changes and no account-based guessing - ---- - -## Target Users - -| User Type | Use Case | Primary Features | -|-----------|----------|------------------| -| Individual Developer | Work/personal separation | Multi-account Claude | -| Agency/Contractor | Client account isolation | Profile switching | -| Cost-conscious Dev | GLM for bulk operations | API profiles, analytics | -| Enterprise | Custom LLM integration | OpenAI-compatible endpoints | -| Power User | Multiple providers | OpenRouter 300+ models | - ---- +| User | Primary need | +| --- | --- | +| Individual developer | Separate accounts, projects, and provider profiles | +| Consultant or agency | Isolate client contexts | +| API consumer | Reuse Anthropic-compatible and OpenAI-compatible providers | +| Power user | Manage OAuth accounts, routing, health, and quota state | +| Team operator | Run a shared remote CLIProxy or integrated Docker service | ## Functional Requirements -### FR-001: Profile Switching -- Switch between profiles with `ccs ` command -- Support default profile when no argument provided -- Pass through all Claude CLI arguments +### FR-001: Profile resolution and launch -### FR-002: Multi-Account Claude -- Create isolated Claude instances -- Maintain separate sessions, todolists, logs per account -- Share commands, skills, agents across accounts +- Launch the default profile with `ccs`. +- Launch named settings, account, and CLIProxy profiles. +- Pass target-specific arguments without losing CCS-owned routing constraints. +- Resolve provider and target aliases through canonical registries. -### FR-003: OAuth Provider Integration -- Support Gemini, Codex, xAI/Grok, Antigravity, Kiro, and deprecated Copilot compatibility OAuth flows -- Browser-based authentication with provider-specific Authorization Code, Device Code, or polling flows -- Token caching and refresh +### FR-002: Account isolation -### FR-004: API Profile Management -- Configure custom API endpoints -- Support Anthropic-compatible APIs -- Model mapping and configuration -- OpenRouter integration with 300+ models +- Maintain an account registry. +- Use isolated `CLAUDE_CONFIG_DIR` roots for Claude account profiles. +- Keep shared resources and instance-owned state distinguishable. +- Prevent one account's provider overrides from leaking into another launch. -### FR-004A: CLIProxy AI Provider Management -- Configure CLIProxy-managed Gemini, Codex, Claude, Vertex, and OpenAI-compatible API-key entries -- Keep provider authoring separate from CCS API Profile creation -- Support local config editing and remote CLIProxy management parity where available +### FR-003: Provider integration -### FR-005: Dashboard UI -- Visual profile management -- Real-time health monitoring -- Usage analytics with cost tracking -- Modular page architecture (settings, analytics, auth-monitor) +- Support API-key profiles and OAuth-backed CLIProxy providers. +- Support local and remote CLIProxy operation. +- Keep original and Plus CLIProxy backends explicit; do not silently substitute + one when a requested provider requires the other. +- Treat provider capability metadata as code-owned, not prose-owned. See + [`src/cliproxy/provider-capabilities.ts`](../src/cliproxy/provider-capabilities.ts) + and + [`src/cliproxy/types/provider-types.ts`](../src/cliproxy/types/provider-types.ts). -### FR-006: Health Diagnostics -- Verify Claude CLI installation -- Check config file integrity -- Validate symlinks and permissions +### FR-004: Target adapters -### FR-007: WebSearch Fallback -- Expose a CCS-managed local WebSearch tool for third-party profiles that cannot reach Anthropic's native tool -- Suppress native `WebSearch` on third-party launches and steer Claude toward the CCS-owned path when it is available -- Support Exa, Tavily, Brave, and DuckDuckGo real search backends -- Keep Gemini CLI, OpenCode, and Grok as optional legacy fallback -- Graceful fallback chain +- Support Claude Code, Factory Droid, and Codex CLI through target adapters. +- Deliver credentials in the form owned by each target: + environment variables for Claude launches, managed custom-model state for + Droid, and transient configuration overrides for CCS-routed Codex launches. +- Preserve user-owned target configuration outside the explicitly managed + fields. -### FR-007A: First-Class Image Analysis -- Expose a CCS-managed local `ImageAnalysis` MCP tool for third-party profiles that need provider-backed vision -- Resolve the provider route before launch and send requests directly to `/api/provider//v1/messages` -- Use editable prompt templates for `default`, `screenshot`, and `document` analysis modes -- Suppress the old CCS-managed `Read` hook during healthy MCP launches so it cannot compete with the primary path -- Keep the old `Read` hook as compatibility fallback only when MCP provisioning fails but provider-backed analysis is still viable -- Auto-heal stale CCS-managed image hooks and missing isolated MCP sync through launch-time cleanup, dashboard provisioning, and `ccs doctor --fix` -- Fall back to native `Read` without failing the whole launch when managed runtime, auth, or proxy readiness is unavailable +### FR-005: Configuration management -### FR-008: Remote CLIProxy Support -- Connect to remote CLIProxyAPI instances -- CLI flags for proxy configuration (--proxy-host, --proxy-port, etc.) -- Environment variable configuration (CCS_PROXY_HOST, etc.) -- Fallback to local proxy when remote unreachable -- Protocol-based default ports (443 for HTTPS, 8317 for HTTP) -- Dashboard UI for remote server configuration and testing +- Store CCS configuration under the directory resolved by + [`getCcsDir()`](../src/utils/config-manager.ts). +- Store API profile launch settings in `.settings.json` files under + that directory. +- Require all environment values written to settings files to be strings. +- Keep shared Claude settings unchanged during normal profile launches. +- Allow the explicit `ccs persist` workflow to merge a profile into + `~/.claude/settings.json`; back up the existing file and write atomically. +- Reject unsafe settings-file targets such as symlinks. -### FR-009: Quota Management (v7.14) -- Pause/resume individual accounts via `ccs cliproxy pause/resume ` -- Check quota status via `ccs cliproxy status [account]` -- Inspect the current proxy-wide routing strategy via `ccs cliproxy routing` -- Explicitly switch `round-robin` vs `fill-first` from CLI or dashboard -- Keep `round-robin` as the default until the user explicitly changes it -- Never infer routing strategy from account count, tier mix, or paused/default account state -- Auto-failover when account exhausted -- Tier detection: free/pro/ultra/unknown -- Distinguish entitlement failures from temporary capacity exhaustion -- Pre-flight quota checks before session start -- Dashboard UI with pause/resume toggles, tier badges, and quota-detail guidance +### FR-006: Dashboard and diagnostics -### FR-010: Docker Deployment -- Multi-stage Dockerfile with bun 1.2.21 and node:20-bookworm-slim -- Docker Compose setup with resource limits and healthcheck -- Persistent volumes for config, credentials, and CLI tools -- Pre-installed CLIs: claude, gemini, grok, opencode, ccs -- Ports: 3000 (Dashboard), 8317 (CLIProxy) -- Entrypoint with privilege dropping and usage help -- Environment variable configuration support +- Provide local APIs and a React UI for supported configuration workflows. +- Surface health, authentication, provider, routing, and usage state without + exposing credentials. +- Keep dashboard changes aligned with the same configuration contracts used by + the CLI. -### FR-011: Third-Party Tool Integration -- Export shell-evaluable env vars via `ccs env` command -- Support OpenAI, Anthropic, raw output formats -- Auto-detect shell (bash/zsh, fish, PowerShell) from $SHELL -- Security: single-quoted output, key sanitization, shell-specific escaping -- Cross-platform compatibility (macOS, Linux, Windows) +### FR-007: Managed tools -### FR-012: Official Claude Channels -- Support Telegram, Discord, and iMessage selection via `ccs config channels` and the dashboard -- Auto-inject `--channels` only for native Claude `default` and `account` sessions -- Store Telegram/Discord bot tokens in Claude's own `~/.claude/channels//.env` state or the official `*_STATE_DIR` override path when one is configured -- Treat iMessage as macOS-only, tokenless, and dependent on Claude-side install plus OS permissions -- Require Bun, Claude Code v2.1.80+, and verified `claude.ai` auth before runtime auto-enable -- Keep `--dangerously-skip-permissions` optional and never add it when the user already made an explicit permission choice -- Surface platform/auth/version/setup blockers clearly in both CLI and dashboard flows -- Preserve dashboard token drafts when save/refresh fails, and let already-selected unsupported iMessage entries be turned off without allowing re-enable on unsupported platforms +- Provide WebSearch and image-analysis integration for profiles that need + CCS-managed alternatives. +- Prefer explicit provider routes. +- Fail closed when enabled third-party WebSearch cannot prepare its constrained + MCP replacement. +- Allow image analysis to use compatible native behavior when its managed route + is unavailable. ---- +### FR-008: Remote proxy + +- Resolve remote proxy settings from supported CLI flags, environment + variables, and CCS configuration. +- Verify reachability before use. +- Fall back to a local proxy only when fallback is enabled. +- Fail instead of falling back when remote-only mode is selected. + +### FR-009: Quota and account-pool management + +- Display supported quota and account health data. +- Let users pause and resume accounts. +- Keep routing-strategy changes explicit. +- Temporarily remove exhausted accounts from rotation only under the + CCS-managed cooldown contract and restore only CCS-created pauses. + +### FR-010: Docker deployment + +- Publish an integrated multi-architecture image containing CCS, CLIProxy, and + the dashboard. +- Persist CCS state and service logs in declared volumes. +- Expose dashboard and CLIProxy service ports. +- Health-check both services. +- Do not bundle target AI CLIs into the integrated image; consumers that need + them run sibling containers or install them separately. + +### FR-011: Shell and editor integration + +- Export shell-safe environment values through `ccs env`. +- Support the documented shell output formats. +- Keep persistent shared settings behind `ccs persist`. +- Keep editor-specific writes scoped to the selected editor and user layer. ## Non-Functional Requirements -### NFR-001: Performance -- CLI startup < 100ms -- Dashboard load < 2s -- Minimal memory footprint +### NFR-001: Security + +- Do not print or log credentials. +- Bind local proxy services to loopback unless the user explicitly selects a + deployment that exposes them. +- Validate file types and use safe replacement semantics for managed sensitive + configuration. +- Keep remote transport and authentication choices explicit. ### NFR-002: Reliability -- Idempotent operations -- Graceful error handling -- Automatic recovery where possible -### NFR-003: Security -- Local-only proxy binding (127.0.0.1) -- No credential exposure in logs -- Secure token storage +- Make setup and repair operations idempotent where practical. +- Preserve existing user state when merging managed configuration. +- Report recovery actions in actionable error messages. +- Clean up child processes and temporary runtime state on exit. -### NFR-004: Cross-Platform -- Support Linux, macOS, Windows -- Bash 3.2+, PowerShell 5.1+, Node.js 14+ -- Identical behavior across platforms +### NFR-003: Portability -### NFR-005: Maintainability -- Files < 200 lines (with documented exceptions) -- Domain-based organization -- Barrel exports for clean imports -- 90%+ test coverage +- Support macOS, Linux, and Windows for the host CLI where target dependencies + allow. +- Support Node.js 18 or newer, as declared by + [`package.json`](../package.json). +- Support Bun 1.0 or newer for development and supported runtime workflows. +- Keep terminal output ASCII-only and respect `NO_COLOR` and TTY detection. ---- +### NFR-004: Maintainability -## Technical Requirements +- Keep provider metadata centralized. +- Use target adapters instead of target checks scattered through dispatch code. +- Validate CLI, server, and dashboard contracts with focused tests. +- Keep generated or volatile inventories out of evergreen architecture prose. -### TR-001: Runtime Dependencies -- Node.js 14+ or Bun 1.0+ -- Claude Code CLI installed -- Internet access for OAuth/API calls +## Runtime and Deployment Requirements -### TR-002: Optional Dependencies -- CLIProxyAPI binary (auto-managed) -- Exa/Tavily/Brave API keys for higher-quality WebSearch -- Gemini CLI for legacy WebSearch fallback -- Bun plus Claude Code v2.1.80+ with `claude.ai` auth for Official Channels auto-enable - -### TR-003: Configuration -- YAML-based config (`~/.ccs/config.yaml`) -- JSON settings per profile -- Environment variable overrides -- Official channel bot tokens stored in Claude-managed `~/.claude/channels//.env` - ---- +| Surface | Requirement | +| --- | --- | +| Host npm install | Node.js 18+ | +| Development and repository gates | Bun 1.0+ and Node.js 18+ | +| Claude launches | Claude Code installed and authenticated as required by the selected profile | +| Droid launches | Factory Droid installed | +| Codex launches | Codex CLI installed | +| Local OAuth proxy | CCS-managed CLIProxy binary | +| Integrated Docker | Docker or compatible container runtime; target CLIs are not bundled | ## Architecture Constraints -### AC-001: CLI-First Design -- All features accessible via CLI -- Dashboard is convenience layer, not required -- Scriptable and automatable +### AC-001: Profile state and shared settings are separate -### AC-002: Non-Invasive -- Never modify `~/.claude/settings.json` -- Use environment variables for configuration -- Reversible changes only +Normal launches consume CCS-owned profile files and environment state. +`~/.claude/settings.json` is written only through an explicit persistence or +approved settings-management workflow. -### AC-003: Proxy Pattern -- Use local proxy for provider routing -- Claude CLI communicates with localhost -- Proxy handles upstream API calls +### AC-002: Provider and target are independent axes ---- +A provider supplies credentials and routing. A target adapter determines how a +compatible CLI receives them. Unsupported combinations must fail clearly. -## Success Metrics +### AC-003: Proxy trust boundary is visible -| Metric | Target | Current | -|--------|--------|---------| -| Startup time | < 100ms | Achieved | -| Dashboard load | < 2s | Achieved | -| Error rate | < 1% | Achieved | -| Test coverage | > 90% | 90% (1440 tests, 6 skipped) | -| File size compliance | 100% < 200 lines | 95% | +Local CLIProxy, remote CLIProxy, and direct API profiles have different +transport and credential boundaries. CCS must not present them as equivalent or +silently cross those boundaries. ---- +### AC-004: Source owns volatile capability data -## Release Criteria +Provider IDs, aliases, OAuth flow types, callback ports, refresh ownership, +backend restrictions, and quota support must be read from the provider +registries and tests. Documentation describes how to find them rather than +copying a second mutable table. -### v1.0 Release (Complete) -- [x] Multi-account Claude support -- [x] OAuth provider integration (Gemini, Codex, AGY) -- [x] API profile management -- [x] Dashboard UI -- [x] Health diagnostics -- [x] WebSearch fallback -- [x] Cross-platform support +## Acceptance Criteria -### v7.0 Release (Complete) -- [x] OpenRouter integration with 300+ models -- [x] Interactive model picker -- [x] Dynamic model discovery -- [x] Tier mapping (opus/sonnet/haiku) -- [x] Settings page modularization (20 files) -- [x] Analytics page modularization (8 files) -- [x] Auth monitor modularization (8 files) -- [x] Comprehensive test infrastructure (539 CLI + 99 UI tests) - -### v7.1 Release (Complete) -- [x] Remote CLIProxy routing support -- [x] CLI flags for remote proxy (--proxy-host, --proxy-port, etc.) -- [x] Environment variables for proxy config (CCS_PROXY_*) -- [x] Dashboard remote proxy configuration UI -- [x] Connection testing with latency display -- [x] Fallback to local when remote unreachable -- [x] Protocol-based default ports (HTTPS:443, HTTP:8317) - -### v7.2 Release (Complete) -- [x] Kiro (AWS) OAuth provider support via CLIProxyAPIPlus -- [x] GitHub Copilot (ghcp) OAuth provider via Device Code flow (deprecated compatibility) -- [x] Authorization Code flow for Kiro (port 9876) -- [x] Device Code flow for ghcp (no local port needed) - -### v7.14 Release (Complete) -- [x] Hybrid quota management with auto-failover -- [x] `ccs cliproxy pause/resume/status` commands -- [x] API tier detection (free/pro/ultra/unknown) -- [x] Dashboard pause/resume toggles and tier badges -- [x] Pre-flight quota checks before session start - -### v7.23 Release (Complete) -- [x] Docker deployment support (PR #345) -- [x] Multi-stage Dockerfile with bun 1.2.21 -- [x] Docker Compose with resource limits and healthcheck -- [x] Persistent volumes for config and credentials -- [x] Pre-installed AI CLI tools (claude, gemini, grok, opencode) -- [x] Entrypoint with privilege dropping - -### v7.34 Release (Complete) -- [x] First-class `ImageAnalysis` MCP tool for third-party launches -- [x] Direct provider-scoped routing for image analysis requests -- [x] Prompt template selection for default / screenshot / document flows -- [x] Hook fallback retained only for compatibility -- [x] Non-fatal native `Read` fallback when managed runtime is unavailable -- [x] `ccs config image-analysis` CLI command -- [x] Doctor integration for hook validation -- [x] 791-line E2E test suite for image analysis -- [x] Performance: Replace busy-wait with Atomics.wait in config lock -- [x] Network error handling with noRetryPatterns -- [x] Quota 429 rate limit handling improvements -- [x] WebSocket maxPayload limit (DoS prevention) - -### v7.39 Release (Complete) -- [x] `ccs env` command for third-party tool integration (OpenCode, Cursor, Continue) -- [x] Multi-format output: openai, anthropic, raw -- [x] Multi-shell support: bash/zsh, fish, PowerShell (auto-detected) -- [x] CLIProxy profile support (gemini, codex, agy, qwen) -- [x] Settings profile support (glm, kimi, custom API) -- [x] Security: single-quoted output, key sanitization, shell-specific escaping -- [x] Shell completion updated (bash, zsh, fish, PowerShell) -- [x] 34 unit tests for env command - -### v8.0 Release (Planned - Q1 2026) -- [ ] Multiple CLIProxyAPI instances (load balancing, failover) -- [ ] Native git worktree support -- [ ] Critical bug fixes (#158, #155, #124) - -### v9.0 Release (Future - Q2 2026) -- [ ] Team collaboration features -- [ ] Cloud sync for profiles -- [ ] Plugin system -- [ ] CLI extension framework - ---- - -## Dependencies - -### External Services -- Anthropic Claude API -- Google Gemini API -- GitHub Codex API -- GitHub Copilot (ghcp - deprecated Device Code OAuth compatibility) -- AWS Kiro (Authorization Code OAuth) -- Z.AI GLM API -- OpenRouter API -- Moonshot Kimi API -- DeepSeek API -- Alibaba Qwen API -- Minimax API -- Azure Foundry API - -### Third-Party Libraries -- Express.js (web server) -- React (dashboard) -- Vite (build tool) -- shadcn/ui (UI components) -- CLIProxyAPI (proxy binary) -- Vitest (testing) - ---- +- A user can create or select a supported profile and launch it on a compatible + target without manual credential-file editing. +- Concurrent account profiles do not share target session state accidentally. +- Local and remote proxy failures follow the configured fallback policy. +- Persistent settings writes preserve unrelated keys and create a recovery + path. +- Dashboard operations produce configuration compatible with CLI operations. +- Release automation publishes only from the documented branches and lanes. +- Documentation links resolve and architecture claims are traceable to source. ## Risks and Mitigations -| Risk | Probability | Impact | Mitigation | -|------|-------------|--------|------------| -| Claude CLI API changes | Medium | High | Version pinning, compatibility layer | -| Provider API deprecation | Low | High | Fallback chain, multiple providers | -| OAuth token expiry | Medium | Medium | Auto-refresh, clear error messages | -| Binary compatibility | Low | Medium | Multi-platform builds, fallback | - ---- +| Risk | Mitigation | +| --- | --- | +| Provider auth contracts change | Central capability registry plus provider-specific tests | +| Target CLI configuration changes | Adapter boundary and compatibility checks | +| Credential leakage | Local storage, redaction, safe file handling, no secret logging | +| Remote proxy outage | Reachability checks and explicit fallback policy | +| Configuration corruption | Validation, backup, locking, and atomic replacement where supported | +| Documentation drift | Link volatile details to code; keep this document version-neutral | ## Related Documentation -- [Codebase Summary](./codebase-summary.md) - Technical structure -- [Code Standards](./code-standards.md) - Development conventions -- [System Architecture](./system-architecture/index.md) - Architecture diagrams -- [Project Roadmap](./project-roadmap.md) - Development phases and GitHub issues +- [Codebase Summary](./codebase-summary.md) +- [Code Standards](./code-standards.md) +- [System Architecture](./system-architecture/index.md) +- [Provider Flows](./system-architecture/provider-flows.md) +- [Release Process](./release-process.md) +- [Project Roadmap](./project-roadmap.md) diff --git a/docs/release-process.md b/docs/release-process.md index bdbd7ba2..f83a5e58 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -1,106 +1,117 @@ # CCS Release Process -CCS uses a decoupled release model: every merge to `main` immediately publishes -a stable npm `@latest` release and an immutable Docker `:` tag. Docker -mutable tags (`:latest`, `:`, `:`) require a separate manual -promote step after an operator-verified soak window. This decouples the npm -ecosystem from the Docker stability gate. +CCS has separate development, stable npm, and Docker promotion lanes. A branch +push starts the relevant workflow. Eligible `dev` pushes publish the next custom +development prerelease after their gates pass; `main` publishes only when +semantic-release finds release-worthy commits. -## Phase 1 — Automatic stable release (on every merge to `main`) +## Release lanes -1. A PR is merged into `main` with a conventional commit (`feat:`, `fix:`, etc.). -2. `release.yml` triggers semantic-release, which reads `.releaserc.cjs`. -3. Because `main` is a stable channel, semantic-release cuts a GitHub release - tagged `vX.Y.Z` and publishes the npm package to the `@latest` dist-tag - immediately. No rc channel, no soak delay on npm. -4. `docker-release.yml` triggers on the `release: published` event and: - - Validates the tag as stable semver (`vX.Y.Z`). - - Builds the integrated image for `linux/amd64` and `linux/arm64`. - - Pushes **only the immutable** `ghcr.io/kaitranntt/ccs:X.Y.Z` tag. - - Signs the image with cosign (keyless OIDC). - - Runs smoke tests (`smoke-test` job). - - Mutable tags (`:latest`, `:`, `:`) are **not** added at - this stage — `promote-mutable-tags` only runs on explicit - `workflow_dispatch` with `promote_to_latest=true`. +| Source | Workflow | Result | +| --- | --- | --- | +| Push to `dev` | [`dev-release.yml`](../.github/workflows/dev-release.yml) | Custom development prerelease and npm `@dev` publication | +| Push to `main` | [`release.yml`](../.github/workflows/release.yml) | Semantic-release stable version, npm `@latest`, tag, and GitHub release when commits require a release | +| Published stable or `rc` GitHub release | [`docker-release.yml`](../.github/workflows/docker-release.yml) | Immutable integrated Docker version tag, signature, and smoke test | +| Manual stable promotion | [`promote-release.yml`](../.github/workflows/promote-release.yml) | Docker `:latest`, major, and minor aliases | +| Stable GitHub release | [`sync-dev-after-release.yml`](../.github/workflows/sync-dev-after-release.yml) | Merge released `main` state back into `dev` | -## Phase 2 — Manual promotion to Docker mutable tags (rc.1 soak window) +## Development prereleases -After the immutable `:` Docker image has soaked (typically 24 h with no -reported issues), the operator promotes mutable tags: +`Dev Release` runs on pushes to `dev` and can also be dispatched manually. +After build and validation gates, it calls +[`scripts/dev-release.sh`](../scripts/dev-release.sh). That script owns the +`-dev.` version sequence and npm `@dev` publication. It is +intentionally separate from the production semantic-release configuration. -1. Verify the immutable image is healthy: +Generated `chore(release): ...` pushes are skipped by the workflow guard to +prevent release recursion. - ```bash - docker pull ghcr.io/kaitranntt/ccs:X.Y.Z - docker run --rm -p 3000:3000 -p 8317:8317 ghcr.io/kaitranntt/ccs:X.Y.Z - # check http://localhost:3000 and http://localhost:8317 - ``` +## Stable npm and GitHub releases -2. Optionally verify the cosign signature: +`Release` runs on `main`. It builds the CLI and dashboard, runs the fast, slow, +and end-to-end gates, then invokes semantic-release with +[`.releaserc.cjs`](../.releaserc.cjs). - ```bash - cosign verify \ - --certificate-identity-regexp "https://github.com/kaitranntt/ccs/.github/workflows/docker-release.yml" \ - --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - ghcr.io/kaitranntt/ccs:X.Y.Z - ``` +Semantic-release analyzes commits since the previous stable release: -3. Run the `promote-release` workflow via GitHub Actions UI or CLI: +- `feat` produces at least a minor release; +- `fix`, `hotfix`, `refactor`, and `style` produce patch releases under the + repository rules; +- breaking-change notation produces the appropriate major release; and +- commits without a matching release rule may produce no release. - ```bash - gh workflow run promote-release.yml \ - --field tag=vX.Y.Z - ``` +When a release is required, the lane updates `CHANGELOG.md` and `package.json`, +publishes npm `@latest`, creates the stable Git tag and GitHub release, and +pushes the generated release commit to `main`. Do not bump versions or create +release tags manually. - This dispatches `docker-release.yml` with `promote_to_latest=true`, which - triggers the `promote-mutable-tags` job to add `:latest`, `:`, and - `:` via `docker buildx imagetools create`. +## Docker publication and promotion - Alternatively, dispatch `docker-release.yml` directly: +The supported integrated image is `ghcr.io/kaitranntt/ccs`. - ```bash - gh workflow run "Publish Docker Image" \ - --field tag=vX.Y.Z \ - --field promote_to_latest=true - ``` +On a published stable `vX.Y.Z` or release-candidate `vX.Y.Z-rc.N` GitHub +release, `Publish Docker Image`: -## Why npm and Docker have different soak windows +1. validates the release tag; +2. checks out that tag; +3. builds the integrated image for `linux/amd64` and `linux/arm64`; +4. publishes only the matching immutable version tag; +5. signs the image digest with keyless cosign; and +6. smoke-tests the published image. -- **npm `@latest`**: Published immediately on every `main` merge. npm users who - pin a version are unaffected; users who run `npm install -g @kaitranntt/ccs` - get the latest immediately. Rollback is `npm install -g @kaitranntt/ccs@X.Y.Z`. -- **Docker `:latest`**: Promoted only after operator confirmation. Users who - pull `:latest` or run `docker pull` without a pinned tag are shielded from - a bad image. The immutable `:` tag is always available for pinned usage - from the moment of release. - -## Verifying the promotion +Mutable aliases are a separate operator decision. After verifying the immutable +image and allowing the desired soak period, dispatch `promote-release.yml`: ```bash -# Confirm :latest points to the promoted digest -docker buildx imagetools inspect ghcr.io/kaitranntt/ccs:latest +gh workflow run promote-release.yml --field tag=vX.Y.Z +``` -# Confirm npm @latest updated (happens automatically at Phase 1) +The promotion workflow verifies that the stable GitHub release and immutable +image exist, then dispatches `docker-release.yml` with +`promote_to_latest=true`. The promotion job creates `:latest`, `:X`, and +`:X.Y` aliases from the immutable image digest. + +The deprecated `ccs-dashboard` image has its own sunset compatibility job. +Do not use its tag behavior as the contract for the supported integrated image. + +## Post-release development sync + +A published, non-prerelease `vX.Y.Z` release targeting `main` triggers +`Sync Dev After Main Release`. The workflow merges `main` into `dev`, resolves +known generated version-file conflicts in favor of the released `main` state, +and pushes `dev`. That push intentionally triggers the normal `Push CI` and +development-release lanes. + +## Verification + +```bash +# npm channels npm view @kaitranntt/ccs dist-tags + +# immutable integrated image +docker buildx imagetools inspect ghcr.io/kaitranntt/ccs:X.Y.Z + +# mutable alias after promotion +docker buildx imagetools inspect ghcr.io/kaitranntt/ccs:latest ``` -## Rollback +Verify the GitHub Actions run and tag point to the expected commit before +announcing a release. -If a promoted release is found to be bad: +## Recovery -```bash -# Repoint :latest to the previous known-good immutable tag -docker buildx imagetools create \ - --tag ghcr.io/kaitranntt/ccs:latest \ - ghcr.io/kaitranntt/ccs:PREVIOUS.VERSION +- **Bad npm release:** publish a corrected patch. Do not unpublish a version + used by downstream consumers. +- **Bad immutable Docker image:** leave the immutable tag unchanged and publish + a corrected version. +- **Bad mutable Docker promotion:** promote a known-good immutable digest back + to the mutable aliases through the controlled workflow. +- **Failed `dev` sync:** repair the merge against current `main` and `dev`; + never overwrite branch history. -# For npm, publish a fix as a new patch release (do not unpublish) -# Unpublishing npm packages causes downstream breakage for pinned consumers. -``` +## Branch and tag summary -## Branch / tag taxonomy - -| Branch | Semantic-release channel | npm dist-tag | Docker tag (on release event) | Docker mutable (on promote) | -|--------|--------------------------|--------------|-------------------------------|------------------------------| -| `main` | stable | `@latest` | `:` (immutable, immediate) | `:latest`, `:`, `:` (after soak) | -| `dev` | `dev` prerelease | `@dev` | not published | not published | +| Branch | Package channel | npm dist-tag | Integrated Docker | +| --- | --- | --- | --- | +| `dev` | Development prerelease | `@dev` | None | +| `main` | Stable semantic release | `@latest` | Immutable tag on GitHub release; mutable aliases after manual promotion |