This project is built entirely using Claude Code (CC). Each phase below includes which Claude Code model to use. The general rule:
- Sonnet → most tasks. Fast, cheap, great at scaffolding, CRUD, tests, templates, and straightforward logic. Use this 90% of the time.
- Opus → complex architectural decisions, tricky prompt engineering, subtle bug diagnosis, and any task where you need the AI to hold a lot of context and reason carefully about trade-offs. Use sparingly on the hard stuff.
Tip: Start every CC session by pointing it at CLAUDE.md so it understands the project context. Run:
claudefrom the project root and it will pick up CLAUDE.md automatically.
Goal: Project scaffolds, Linear SDK connected, can read and display workspace data. Timeline: ~1 day
-
1.1 — Project scaffold
- Model: Sonnet
- Init Bun project, install deps (
@linear/sdk,@anthropic-ai/sdk,hono,zod) - Create tsconfig.json (strict mode), bunfig.toml, .env.example, .gitignore
- Create the full directory structure (empty files are fine)
- Prompt: "Initialize a Bun TypeScript project with these deps: @linear/sdk, @anthropic-ai/sdk, hono, zod. Create the directory structure from CLAUDE.md. Strict TS. Add .env.example with the vars listed in CLAUDE.md."
-
1.2 — Config module
- Model: Sonnet
src/config.ts— load and validate env vars with Zod- Should export a typed
configobject, fail fast on missing vars - Prompt: "Create src/config.ts. Use Zod to validate env vars: LINEAR_API_KEY (string), ANTHROPIC_API_KEY (string), PORT (number, default 3000), DATABASE_PATH (string, default ./data/brain.db). Export a typed config object. Throw on invalid config at startup."
-
1.3 — Linear client + reader
- Model: Sonnet
src/linear/client.ts— create and export the Linear SDK client instancesrc/linear/reader.ts— functions to read: teams, cycles, issues (with filters), users, labels, project states- Handle pagination properly
- Prompt: "Create src/linear/client.ts that exports a LinearClient instance using the API key from config. Then create src/linear/reader.ts with functions: getTeams(), getCurrentCycle(teamId), getIssues(filter), getIssue(id), getUsers(), getLabels(teamId). All must handle cursor-based pagination. Return typed results. Add logging with [linear-reader] prefix."
-
1.4 — Basic Hono server + health check
- Model: Sonnet
src/server/app.ts— Hono app with a/healthroutesrc/index.ts— starts the server- Prompt: "Create src/server/app.ts with a Hono app. Add a GET /health route that returns { status: 'ok', timestamp }. Create src/index.ts that imports the app and starts it on the configured port using Bun.serve. Log startup message."
-
1.5 — Verify Linear connection
- Model: Sonnet
- Add a
/debug/linearroute that calls reader functions and shows raw data - This is a temp route for development, can be removed later
- Prompt: "Add a GET /debug/linear route to the Hono app that calls getTeams() and getCurrentCycle() from reader.ts and returns the results as JSON. This is for dev verification."
bun run src/index.tsstarts the server/healthreturns OK/debug/linearreturns real data from your Linear workspace
Goal: Working approval queue with a web UI to review, approve, and reject proposals. Timeline: ~1-2 days
-
2.1 — SQLite database setup
- Model: Sonnet
src/queue/db.ts— initialize SQLite DB, create tables (proposals, audit_log)- Auto-create the data directory if it doesn't exist
- Run migrations on startup
- Prompt: "Create src/queue/db.ts using bun:sqlite. On init, create the data directory if needed, open the DB at DATABASE_PATH, and create the two tables from ARCHITECTURE.md (proposals, audit_log) if they don't exist. Use WAL mode. Export the db instance."
-
2.2 — Proposal CRUD
- Model: Sonnet
src/queue/proposals.ts— createProposal(), getProposal(), listProposals(filter), approveProposal(id), rejectProposal(id, feedback), expireStaleProposals()- All functions log to audit_log
- Prompt: "Create src/queue/proposals.ts with functions for proposal CRUD. createProposal takes type, summary, reasoning, payload and inserts with status 'pending'. approveProposal/rejectProposal update status and log to audit_log. listProposals accepts optional status filter. Use Zod to validate proposal types. Generate ULIDs for IDs (implement a simple ULID generator or use Date.now + random)."
-
2.3 — Executor
- Model: Sonnet (but review the safety logic carefully yourself)
src/queue/executor.ts— takes a proposal ID, verifies it's approved, calls writer.ts, logs resultsrc/linear/writer.ts— thin wrapper around Linear SDK mutations, ONLY called by executor- Prompt: "Create src/queue/executor.ts and src/linear/writer.ts. The executor function takes a proposal ID, loads it from DB, throws if status is not 'approved', then calls the appropriate writer function based on proposal.type. writer.ts exports functions: createIssue(payload), updateIssue(id, payload), addComment(issueId, body). Each calls the Linear SDK. Executor logs success/failure to audit_log and updates proposal status to 'executed'. Add very clear comments that writer.ts must ONLY be imported by executor.ts."
-
2.4 — Dashboard HTML templates
- Model: Sonnet
src/server/views/templates.ts— functions that return HTML strings- Layout, proposal list, proposal detail, audit log views
- Keep it simple: system fonts, minimal CSS, functional
- Prompt: "Create src/server/views/templates.ts with functions: layout(title, body) wrapping content in a full HTML page with minimal CSS (system fonts, max-width 800px, basic table styles, status badges). proposalList(proposals) renders a table with columns: time, type, summary, status, actions. proposalDetail(proposal) shows full reasoning, JSON payload, and approve/reject forms."
-
2.5 — Dashboard routes + proposal API
- Model: Sonnet
src/server/routes/dashboard.ts— GET / (proposal list), GET /proposal/:id (detail)src/server/routes/api.ts— POST /api/proposals (create, used by CC), POST /api/proposals/:id/approve, POST /api/proposals/:id/reject, GET /api/proposals (list as JSON)- Wire approve/reject to the executor
- Prompt: "Create dashboard routes in Hono. GET / shows pending proposals using proposalList template. GET /proposals/:id shows detail with approve/reject forms. POST /api/proposals creates a new proposal (JSON body: type, summary, reasoning, payload) — this is how CC submits proposals. POST /api/proposals/:id/approve calls approveProposal then executor then redirects. POST /api/proposals/:id/reject accepts feedback, calls rejectProposal, redirects. GET /api/proposals returns proposals as JSON (for CC to query). Wire routes into the main app."
-
2.6 — Tests for queue safety
- Model: Sonnet
- Test that executor rejects unapproved proposals
- Test proposal state transitions
- Test that approval + execution flow works end to end (mocked Linear client)
- Prompt: "Write tests using bun:test for the proposal queue. Test: creating a proposal sets status to pending. Approving changes status to approved. Rejecting changes status to rejected. Executor throws on pending/rejected proposals. Executor succeeds on approved proposals (mock writer.ts). Test the full flow: create → approve → execute."
- You can manually insert a test proposal and see it on the dashboard
- Approve/reject buttons work and update status
- Executor runs approved proposals (test with a mock or a real comment on a test ticket)
- Tests pass:
bun test
Superseded. All analysis is done interactively via Claude Code (CC). No Anthropic API, no automated analyzers, no scheduler, no prompt templates. The src/ai/, src/scheduler/, and prompts/ directories have been deleted. @anthropic-ai/sdk has been uninstalled.
Superseded. CC generates proposals interactively and submits them via POST /api/proposals. The executor and writer (built in Phase 2) handle execution of approved proposals.
Goal: Receive real-time changes from Linear and auto-expire conflicting proposals. Timeline: ~1 day
-
3.1 — Linear webhook handler
- Model: Sonnet
src/server/routes/webhooks.ts— receive and validate Linear webhooks- Validate the webhook signature. Parse payload with Zod. Handle: Issue created, Issue updated, Comment created. Log all events.
-
3.2 — Conflict detection for pending proposals
- Model: Opus
- When a webhook reports an issue update, check for pending proposals on that issue. Auto-expire proposals that conflict with the manual change.
- Webhooks flow in from Linear and are logged
- Pending proposals auto-expire when they'd conflict with manual changes
Goal: Make it reliable for daily use. Timeline: ongoing
-
4.1 — Error recovery & resilience
- Model: Sonnet
- Graceful handling of Linear API downtime and SQLite issues
- Automatic retry with backoff for transient failures
-
4.2 — Dashboard improvements
- Model: Sonnet
- Filtering, search, pagination on proposal list
- Bulk approve/reject
- Quick stats (proposals this week, approval rate, etc.)
-
4.3 — Notification system
- Model: Sonnet
- Optional Slack/email notifications when proposals need review
- Daily digest summary
| Task Type | Model | Why |
|---|---|---|
| Scaffolding / boilerplate | Sonnet | Fast, handles structure well |
| CRUD / database operations | Sonnet | Straightforward logic |
| API integration | Sonnet | Well-documented SDK, Sonnet handles it fine |
| HTML templates / dashboard | Sonnet | Template generation is Sonnet's sweet spot |
| Tests | Sonnet | Test logic is usually straightforward |
| Conflict detection / edge cases | Opus | Subtle state machine logic |
| Architectural review / cleanup | Opus | Needs to hold a lot of context |
| Bug diagnosis | Opus | When Sonnet can't figure it out |
Default to Sonnet. Switch to Opus when you're stuck or when the task requires holding a lot of context and reasoning about trade-offs.