Every SSE reconnect on a task was reading every row in task_actions
and messages for that task, then slicing to 50 in JS. D1 billed for
the full scan. With 100 concurrent SSE sessions, this is the dominant
component of the 7M rows read/day we see on prod.
Changes:
- listMessages / getTaskActions: add limit param, default to the new
MAX_TASK_PARTITION_ROWS constant (500). Without since returns the
latest N in ASC order (DESC fetch + reverse). With since returns up
to N after the cursor in ASC order.
- sse.ts: initial fetch asks for 50 rows (no cursor) or 500 (with
cursor). Catch-up polls request 500. When either feed returns
exactly the cap an event gap SSE frame is emitted so the client
can fall back to HTTP instead of silently missing older rows.
- taskRepo.getTaskById now delegates to getTaskActions instead of
duplicating the SQL.
- Known limitation documented for created_at cursor collision.
Unify the app into a single Worker deployment:
- Move functions/api/ → server/, add worker/index.ts entry point
- Merge relay-worker DO (TunnelRelay) into the main worker
- Replace Pages Functions with @cloudflare/vite-plugin build
- Integrate share SSR (OG meta injection) into Hono routes
- Remove dayjs dep from shared (native Date suffices)
- Simplify dev to single `vite dev`, deploy via CF Git integration
- Delete packages/relay-worker/ and Pages-specific _routes.json
Boards now have a required `type` field — `dev` for development workflows
(git/PR/code review) and `ops` for operations workflows (no repo). This
drives per-type system prompts, workspace creation (worktree vs tmpdir),
and task validation (dev requires repository_id, ops forbids it).
Key changes:
- Workspace abstraction (workspace.ts) decouples workspace lifecycle from
repo operations — scheduler and process manager stay unchanged
- System prompt generates per-board-type agent work protocol
- BoardType enum with isBoardType guard centralizes type validation
- gh auth downgraded to warning so ops-only daemons can run without it
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the task_notes table and agent_id/session_id columns with
task_actions and actor_type/actor_id to track all identity types
(user, machine, agent:worker, agent:leader). This enables user and
machine actions to appear in the activity log and board animations.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Prettier's aggressive line-splitting (printWidth 100, single quotes) didn't
match the project's existing compact code style. Switch to Biome which
provides formatting + linting in one tool with better chain-call handling.
- Remove prettier, add @biomejs/biome
- Configure biome: double quotes, lineWidth 150, recommended lint rules
- Update lefthook pre-commit to use `biome check`
- Full codebase format pass with new config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validate lastEventId before creating the TransformStream. If the ID is
provided but not found in task_logs or messages, return a 400 JSON error
instead of silently replaying all events from the beginning.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Coverage improved from 36.73% to 47.33%. Four new test files with 115 test
cases covering board CRUD, repository CRUD, all HTTP routes, and SSE streaming.
Shared test infrastructure extracted to tests/helpers/db.ts. Updated CLAUDE.md
with test ownership rule: main agent owns source code, test-writer agent owns
test code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>