From 981b4dd19e590cfbf25c3a476d75b055d2eed842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Wed, 25 Mar 2026 23:11:25 -0400 Subject: [PATCH] docs: create roadmap (10 phases) --- CLAUDE.md | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 92dc03fa..a86b9832 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,6 +108,7 @@ Root app + `packages/adapter-{claude,gemini,codex,kimi,opencode}-local/` + `pack Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync. +Use these entry points: - `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks - `/gsd:debug` for investigation and bug fixing - `/gsd:execute-phase` for planned phase work @@ -153,3 +154,306 @@ Builders and operators who want to spin up autonomous AI companies without manag 3. **Real, not theatrical** — Agent activity should feel like watching a real company operate, not a sci-fi simulation. Status over spectacle. 4. **Desktop-native craft** — Keyboard-first, focus rings, platform-aware shortcuts, proper window management. No web-app-in-a-wrapper feel. 5. **Accessible by default** — Reduced motion, color contrast, semantic HTML, focus traps. Not an afterthought. + + +## Project + +**AgentCompany — Match Paperclip Quality Milestone** + +AgentCompany is a cross-platform Electron desktop app that lets users create autonomous AI companies. A user provides a company name, description, goal, and deliverable. A CEO agent self-organizes other AI agents, delegates tasks, and the company runs end-to-end while the human observes through a Slack-style hub. This milestone closes the quality gap with the Paperclip reference implementation — bringing agent skill injection, task execution integrity, budget enforcement, session management, and UI maturity to production quality. + +**Core Value:** Agents produce real, usable output — not garbage. Every run has domain knowledge (skills), validated tasks, quality gates on output, cost controls, and session continuity. + +### Constraints + +- **Runtime**: Electron with strict main/preload/renderer separation — no architectural changes +- **Database**: SQLite (`node:sqlite`) — implement all features within SQLite's capabilities +- **Adapters**: Preserve existing 5-adapter modular npm package structure — extend, don't replace +- **Backward compatibility**: Existing companies/agents/tasks must continue to work after migration +- **Performance**: Skill injection must not add >2s to run startup; IPC events must be <100ms + + + +## Technology Stack + +## Languages +- TypeScript 5.9.3 - All application code (main, preload, renderer, shared, packages) +- JavaScript - Build scripts and some config files +- Python - Asset generation scripts (`scripts/generate-icons.py`) +## Runtime +- Electron 39.8.0 - Cross-platform desktop framework +- Node.js 22+ - Server runtime (main process) +- pnpm 10.24.0 +- Lockfile: `pnpm-lock.yaml` (present) +## Frameworks +- Electron 39.8.0 - Desktop runtime with IPC, main/preload/renderer architecture +- React 19.2.4 - UI framework in renderer process +- TailwindCSS 4.2.1 - Utility-first CSS in renderer +- Zustand 5.0.12 - Lightweight state management (renderer) +- Immer 11.1.4 - Immutable state updates +- Framer Motion 12.36.0 - Animation and motion primitives in React components +- Vite 7.0.0 - Module bundler and dev server +- electron-vite 5.0.0 - Vite config wrapper for Electron main/preload/renderer +- @vitejs/plugin-react 5.0.0 - React JSX support in Vite +- @tailwindcss/vite 4.2.1 - TailwindCSS Vite integration +- Vitest 4.1.0 - Unit/integration test runner (Node environment) +- @playwright/test 1.58.2 - E2E testing framework (`tests/e2e/playwright.config.ts`) +- ESLint 9.38.0 - Code linting with flat config +- @typescript-eslint 8.46.1 - TypeScript linting support +- eslint-plugin-react-hooks 7.0.0 - React hooks rule enforcement +- eslint-plugin-react-refresh 0.4.24 - Fast refresh support +- TypeScript 5.9.3 - Type checking (`pnpm typecheck`) +## Key Dependencies +- zod 4.3.6 - Schema validation at IPC boundaries (main, preload, renderer share contracts) +- electron-log 5.4.3 - Structured logging in main process (no console.log in production) +- electron-updater 6.8.3 - Auto-update via electron-updater (when configured) +- node:sqlite - Built-in SQLite bindings for local database (no external DB driver) +- electron.safeStorage - OS keychain integration for credential encryption +- @phosphor-icons/react 2.1.10 - Icon library (16-18px standard) +- @tanstack/react-virtual 3.13.23 - Virtual scrolling for large lists +- clsx 2.1.1 - Conditional className utility +- tailwind-merge 3.5.0 - TailwindCSS className merging +- @fontsource-variable/inter - System sans-serif for UI +- @fontsource-variable/jetbrains-mono 5.2.8 - Monospace for code/data display +- @fontsource/instrument-serif - Serif font support +- @agentcompany/adapter-claude-local - Claude Code CLI adapter (workspace:*) +- @agentcompany/adapter-codex-local - Codex CLI adapter (workspace:*) +- @agentcompany/adapter-gemini-local - Gemini CLI adapter (workspace:*) +- @agentcompany/adapter-utils - Shared adapter utilities (workspace:*) +## Configuration +- Files: `electron.vite.config.ts`, `tsconfig.json`, `vitest.config.ts`, `tests/e2e/playwright.config.ts` +- Profile directory: `{userData}/profile` (default) or `AGENTCOMPANY_PROFILE_DIR` env var +- Database: `{profileDir}/profile.sqlite` (WAL mode, foreign keys enabled) +- Window state: `{profileDir}/window-state.json` +- Artifacts: `{profileDir}/artifacts/` +- Logs: `{profileDir}/logs/` +- Backups: `{profileDir}/backups/` +- Secrets vault: `{profileDir}/vault.key` (600 permissions) +- `electron.vite.config.ts` - Main/preload/renderer entry points and aliases +- `electron-builder.yml` - Platform-specific build targets (macOS dmg/zip, Windows NSIS/portable, Linux AppImage/deb/rpm) +- `tsconfig.json` - Path aliases: `@shared`, `@main`, `@renderer`, `@preload` +- `eslint.config.mjs` - ESLint 9 flat config +- `pnpm-workspace.yaml` - Monorepo with `packages/*` +## Platform Targets +- Formats: dmg (installer), zip (portable) +- Architectures: x64, arm64 +- Signing: `hardenedRuntime: true`, Gatekeeper assessment disabled +- Code sign config via: `CSC_LINK`, `CSC_KEY_PASSWORD` env vars +- Formats: NSIS (installer), portable exe +- Architectures: x64, arm64 +- Code sign config via: `WIN_CSC_LINK`, `WIN_CSC_KEY_PASSWORD` env vars +- NSIS: oneClick false, perMachine false, allow custom install dir +- Formats: AppImage, deb, rpm, snap +- Architectures: x64, arm64 (AppImage/deb/snap); x64 (rpm) +- Desktop category: Development +- deb dependencies: libgtk-3-0, libnotify4, libnss3, libxss1, libxtst6, xdg-utils, libatspi2.0-0, libuuid1, libsecret-1-0 +- Snap plugs: default, removable-media +## Monorepo Structure +- `packages/adapter-claude-local/` - Claude Code CLI integration +- `packages/adapter-codex-local/` - Codex CLI integration +- `packages/adapter-gemini-local/` - Gemini CLI integration +- `packages/adapter-kimi-local/` - Kimi CLI integration (not bundled in main build) +- `packages/adapter-opencode-local/` - OpenCode CLI integration (not bundled in main build) +- `packages/adapter-utils/` - Shared adapter types and utilities +## Key Scripts +- Main: `out/main/index.js` + `out/main/worker.js` (entry points in electron.vite.config.ts) +- Preload: `out/preload/index.js` (CJS format, chunked) +- Renderer: `out/renderer/` (Vite SPA bundle) +- Desktop installers: `build/release/` (platform-specific per electron-builder.yml) +## Adapter System +- `execute()` - Run code in CLI session +- `sessionCodec` - Serialize/deserialize session state +- `testEnvironment()` - Check CLI availability +- `models` - List available models + + + +## Conventions + +## Naming Patterns +- kebab-case for all source files: `connector-auth-service.ts`, `ipc-core-handlers.ts`, `automation-service.ts` +- React components: PascalCase when exporting components: `RuleEditor.tsx`, `CompanyOnboarding.tsx` +- Test files: kebab-case with `.test.ts` or `.spec.ts` suffix: `database.test.ts`, `autonomous-chain.spec.ts`, `notification-service.test.ts` +- camelCase with descriptive verbs: `saveCompany()`, `handleApprovalResolved()`, `checkoutTask()`, `wakeAgentIfPossible()` +- Event handlers: prefix with `handle` or `on`: `handleSave()`, `handleTaskCreated()`, `onClick()` +- Prefix boolean-returning functions with `is` or `has`: `isAgentPendingApproval()`, `hasCompany()`, `belongsToCompany()` +- camelCase for all variables: `companyId`, `workspaceId`, `agentId`, `tempDir` +- Boolean variables prefixed with `is`, `has`, or `can`: `isEditing`, `hasError`, `canApprove` +- Collections in plural form: `agents`, `tasks`, `workspaces`, `tempDirs` +- PascalCase for all types: `AppDatabase`, `DesktopResult`, `CompanyRecord`, `AgentRecord` +- Database entity types suffixed with `Record`: `CompanyRecord`, `AgentRecord`, `TaskRecord`, `ApprovalRecord`, `RunRecord` +- Zod schemas suffixed with `Schema`: `companyInputSchema`, `connectorInputSchema`, `workspaceInputSchema`, `approvalDecisionSchema` +- Generic types: `DesktopResult` for IPC return types, `RuleFormState` for form state interfaces +- UPPERCASE for true constants defined at module level: `BOARD_READER_ID`, `ACCENT`, `LINE` +- camelCase for derived constants/configs: `IPC_CHANNELS`, `sidebarNavDefs`, `defaultRuleForm` +## Code Style +- 2-space indentation (enforced by ESLint configuration) +- No Prettier config in repository — style guided by ESLint rules +- Line length: no hard limit enforced, but prefer reasonable wrapping +- Semicolons: required (enforced by `@typescript-eslint` defaults) +- ESLint 9 with flat config in `eslint.config.mjs` +- TypeScript ESLint rules enabled in recommended configuration +- `@typescript-eslint/no-explicit-any` set to `off` — `any` is allowed for database interop and bridge code +- `@typescript-eslint/no-unused-vars`: warn with patterns `^_` for unused parameters/variables +- Unused variables can be ignored by prefixing with underscore: `_event`, `_prevStatus` +- React Hooks: `exhaustive-deps` set to warn (not error), allowing intentional deviations +- ESLint covers: `src/**/*.{ts,tsx}`, `tests/**/*.{ts,tsx}`, `scripts/**/*.{ts,mjs}`, `packages/**/*.{ts,tsx}` +- Excluded: `node_modules/`, `out/`, `build/`, `dist/`, `coverage/`, `playwright-report/`, `test-results/` +## Import Organization +- `@shared/*` → `src/shared/` — Zod schemas, types, contract definitions, localization +- `@main/*` → `src/main/` — Database, services, IPC handlers, API server +- `@renderer/*` → `src/renderer/` — React components, hooks, UI utilities +- `@preload/*` → `src/preload/` — Context-isolated bridge APIs +- Use named exports for all utilities, services, handlers, and hooks +- Default exports only for React components (required for route-based splitting patterns) +- Example: `export function saveCompany()` not `export default saveCompany` +- Example: `export default function CompanyOnboarding()` for React components +- Use `import type` exclusively for TypeScript-only imports to enable proper tree-shaking +- Example: `import type { ProfileSnapshot, DesktopEvent } from "@shared/types";` +## Error Handling +- All IPC handlers return `DesktopResult` = `{ ok: true, data: T } | { ok: false, error: { code, message } }` +- Use helper functions `ok(data)` and `fail(code, message)` from `ipc-core-common.ts` +- Example from `ipc-core-admin-handlers.ts`: +- Zod is used for all IPC payloads: `const parsed = companyInputSchema.parse(payload);` +- Zod parsing happens at the IPC boundary (main process side) +- Parse before any business logic, catch validation errors and return `fail()` with descriptive message +- Example: `const parsed = approvalDecisionSchema.parse(payload);` +- All entity operations check company ownership with `ensureCompanyExists()` and `belongsToCompany()` +- Pattern from `ipc-core-operations-handlers.ts`: +- Never throw exceptions across IPC. Always catch and return `fail(code, message)` +- Main ↔ Renderer communication must use `DesktopResult` envelope +- Database errors are caught and wrapped: `catch (error) { return fail("APPROVAL_INVALID", error instanceof Error ? error.message : "...") }` +- Use SCREAMING_SNAKE_CASE for error codes: `INVALID_COMPANY_ID`, `CONNECTOR_COMMAND_LOCKED`, `TASK_NOT_FOUND`, `APPROVAL_NOT_FOUND` +- Error codes are used by renderer to determine UI response (e.g., showing specific error message) +## Logging +- No `console.log()` in production code. Use `electron-log` instead. +- Logger injected as dependency to services: `logger: { info, error }` +- Example from `notification-service.test.ts`: +- Critical events: service initialization, crashes, recoveries +- Errors: validation failures, database errors, connectivity issues +- Debug: trace flow through complex orchestration (in dev mode) +## Comments +- Only for non-obvious logic or business rules +- Prefer clear naming and types over comments +- Example: comments explaining why a decision was made, not what the code does +- Minimal use — TypeScript types are the primary documentation +- Use JSDoc for public APIs and exported functions if return type is complex +- Example for complex types: +## Function Design +- Use object parameters (destructuring) for >3 parameters +- Example: `saveAgent({ companyId, name, role, ... })` +- Avoid parameter lists with many flags +- Explicit return types for all exported functions +- Return early for error conditions: `if (error) return fail("CODE", "message");` +- Consistent use of `DesktopResult` for IPC handlers +- Use `async/await`, not promises chains +- Always handle errors in handlers: `try/catch` around database calls +- Example pattern: +## Module Design +- Named exports for utilities, services, handlers +- Default exports ONLY for React components +- Example good pattern: `export function registerCoreAdminHandlers({ ... })` +- Used in `src/renderer/components/` for organizing imports +- Example: `export { AgentCreator, AgentEditor, AgentRoster } from "./..."` +- Database and major services are class-based (e.g., `AppDatabase`, `ServiceGraph`) +- Other services are functional: `createNotificationService()`, `createEventBatcher()` +- Classes own initialization (constructor) and lifecycle (init/close methods) +## React-Specific Patterns +- All React components are functional with hooks +- No class components +- Example: `export function RuleFormModal({ open, editingRule, companyId, onClose, onSaved }: Props)` +- `useCallback()` for stable function references passed to event handlers +- `useEffect()` for side effects with proper dependency arrays +- `useMemo()` for expensive calculations and string translations +- Example from `RuleEditor.tsx`: +- Zustand store (`useAppStore`) for global UI state (sidebar collapsed, selected entities, locale) +- Local `useState()` for component-specific form state +- No Redux, Context API used only for i18n provider +- Explicit interface or inline type for all props +- Example: `function RuleFormModal({ open, editingRule, companyId, onClose, onSaved }: { open: boolean; editingRule: AutomationRuleRecord | null; ... })` + + + +## Architecture + +## Pattern Overview +- Process boundary enforcement: main (Node.js) → preload (context-isolated) ← renderer (React) +- Service graph: loosely-coupled async services coordinated by bootstrap +- Data flow: SQLite → IPC domain-changed events → Zustand store → React UI +- Agent execution: orchestration service queues runs to worker process (child utility process) +- Automation: event-driven triggers (task created, status changed, approval resolved) dispatch workflows +- IPC pattern: `ipcRenderer.invoke()` → Zod validation → DesktopResult pattern +## Layers +- Purpose: Database operations, service coordination, IPC handler registration, external API communication, worker management +- Location: `src/main/` +- Contains: Database class, service factories, IPC handlers, agent orchestration logic, API server, automation rules +- Depends on: Electron main process APIs, SQLite, node:fs, child processes via worker service +- Used by: Preload process (via ipcMain handlers), worker process (via message passing) +- Purpose: Context-isolated bridge exposing minimal DesktopApi to renderer via contextBridge +- Location: `src/preload/index.ts` +- Contains: ~75 typed IPC channel invocations, subscription to desktop events +- Depends on: Electron contextBridge, ipcRenderer, shared contracts +- Used by: Renderer process (via window.agentCompany global) +- Purpose: Display company state, handle user interactions, visualize agent execution +- Location: `src/renderer/` +- Contains: React components, Zustand store (no Redux/Redux-like), hooks, i18n context, UI utilities +- Depends on: React 19, window.agentCompany preload API, Zustand, Framer Motion +- Used by: User directly through Electron window +- Purpose: Execute agent commands in isolated child process (Electron utilityProcess) +- Location: Built from agent adapters in `packages/adapter-*/` +- Contains: Connector CLI execution, run log streaming, status reporting +- Depends on: `EnqueueRunPayload` message contract, agent connector CLIs (Claude, Codex, Gemini, Kimi, OpenCode) +- Used by: Worker service via message passing +## Data Flow +- Zustand store in `src/renderer/state/store.ts` holds: +- No Redux, no global context beyond locale/translation +- Derived state computed via hooks (e.g., `selectedAgent = agents.find(a => a.id === selectedAgentId)`) +- Snapshot updates trigger full re-computation (no partial updates) +## Key Abstractions +- Purpose: All SQLite operations, snapshot generation, business logic for data consistency +- Examples: `src/main/database.ts` +- Pattern: Class with ~100 public methods, synchronous SQLite.DatabaseSync, no ORM +- Responsibilities: CRUD for all entities, filtering/searching, metrics calculation, backup/restore, secret vault integration +- Purpose: Dependency injection and lifecycle management for 9 coordinated services +- Examples: `src/main/service-graph.ts` +- Pattern: Factory function returning object with service getters, lazy initialization via `init()` +- Services: DesktopShell, ConnectorHealth, Worker, Orchestration, Automation, Onboarding, BrowserAction, Updater, AppLifecycle +- Purpose: Agent task execution, workspace resolution, budget tracking, run queuing +- Examples: `src/main/orchestration-service.ts` +- Pattern: Factory with internal state (heartbeat timer, run cache), public methods for task/agent operations +- Coordinates with: Worker service (enqueue), Automation service (dispatch on status change), Database (persist) +- Purpose: Event-driven trigger evaluation and workflow execution +- Examples: `src/main/automation-service.ts` +- Pattern: Factory managing active execution state, async/await for workflow steps +- Triggers: task.created, task.status_changed, approval.resolved, comment.posted, document.created, run.completed +- Actions: assign_task, notify_agent, send_message, create_approval, trigger_heartbeat, escalate, cross_department_notify, auto_approve +- Purpose: Electron window lifecycle, platform integration, keyboard shortcuts, native menus +- Examples: `src/main/desktop-shell-service.ts` +- Pattern: Factory managing BrowserWindow instance, event listeners for menu/shortcut actions +- Responsibilities: window creation, state persistence, platform-specific behaviors +- Purpose: Child process lifecycle, run execution, log streaming +- Examples: `src/main/worker-service.ts` +- Pattern: Factory managing Electron utilityProcess, retry logic, consecutive wake detection +- Message types: enqueue-run (inbound), run-log/run-status/run-finished (outbound) +- Purpose: Handler registration, payload validation, sender URL verification, error sanitization +- Examples: `src/main/ipc-support.ts` +- Pattern: Factory with `registerHandle()` method for each channel, returns `DesktopResult` +- Security: Verifies senderFrame.url matches trusted renderer origin (dev or app://-) before processing +## Entry Points +- Location: Electron main configuration in `electron.vite.config.ts` or `build/` entry +- Triggers: App launch via `electron .` or packaged app +- Responsibilities: Create main runtime, await bootstrap, setup single-instance lock, handle app lifecycle events +- Location: `src/renderer/main.tsx` (React root) +- Triggers: Main process creates BrowserWindow pointing to renderer dist +- Responsibilities: Mount React App component, initialize Zustand store, hydrate snapshot on first load +- Location: `src/main/ipc-core-handlers.ts` dispatches to specialized handler modules +- Handlers split across: `ipc-core-read-handlers.ts`, `ipc-core-operations-handlers.ts`, `ipc-core-work-graph-handlers.ts`, `ipc-agent-coordination-handlers.ts`, etc. +- Each handler: validates input with Zod, calls DB or service method, returns DesktopResult, publishes domain-changed on write +## Error Handling +- IPC validation: Zod schema parsing at handler entry → `sanitizeHandlerError()` converts ZodError to `IPC_INVALID_PAYLOAD` +- DB errors: Throws logged to electron-log, caught by IPC handler, returned as DesktopResult error +- Worker errors: Run status set to "failed" with errorMessage, logged to run log file +- Automation errors: Try-catch blocks log errors but continue processing other rules +- Async cleanup: `appLifecycleService` waits 3 seconds for worker child process to exit before app.exit(0) +## Cross-Cutting Concerns +