| Section | Status | Done | Partial | Missing | Skipped |
|---|---|---|---|---|---|
| Infrastructure | Done | 16 | 0 | 0 | 0 |
| Database (Section 9) | Done | 10 | 0 | 0 | 0 |
| Signal Integration (Section 3) | Done | 9 | 0 | 0 | 1 |
| Message Pipeline (Section 2-3) | Done | 7 | 0 | 0 | 0 |
| RBAC (Section 5) | Done | 10 | 0 | 0 | 0 |
| Memory System (Section 4.4) | Done | 7 | 0 | 0 | 0 |
| AI Instructions (Section 6) | Done | 7 | 0 | 0 | 0 |
| Skills System (Section 7) | Done | 5 | 0 | 0 | 0 |
| Self-Modification Protocol (Section 8) | Done | 5 | 0 | 0 | 0 |
| Security (Section 10) | Done | 7 | 0 | 0 | 0 |
| Email Integration (Section 11) | Partial | 0 | 1 | 0 | 3 |
| Calendar Integration (Section 12) | Done | 0 | 0 | 0 | 1 |
| Credential Management (Section 13) | Done | 6 | 0 | 0 | 0 |
| File Handling (Section 14) | Done | 5 | 0 | 0 | 0 |
| Playwright / Browser Automation (Section 15) | Done | 7 | 0 | 0 | 0 |
| Web Deployment — Cloudflare (Section 16) | Done | 10 | 0 | 0 | 0 |
| Usage Monitoring (Section 18) | Done | 7 | 0 | 0 | 0 |
| Error Handling (Section 17) | Done | 10 | 0 | 0 | 0 |
| Interaction Modes & Commands (Section 17) | Done | 5 | 0 | 0 | 0 |
| Resilience & Operations (Section 19) | Done | 9 | 0 | 0 | 0 |
| Testing (Section 20) | Done | 4 | 0 | 0 | 0 |
| .env Completeness (Section 22.4) | Done | 13 | 0 | 0 | 1 |
| Done | Node.js via NVM (v24.14.0) |
| Done | PostgreSQL 16 (Homebrew service) |
| Done | PostgREST (launchd, port 3000) |
| Done | Docker Desktop |
| Done | signal-cli REST API Docker (json-rpc mode, port 8080) |
| Done | Bridge server (launchd, port 3033) |
| Done | Playwright MCP (launchd, port 8931) |
| Done | Wrangler CLI installed |
| Done | Wrangler authenticated (wrangler login) |
| Done | Cloudflare account ID + API token in .env (set via /config Signal command) |
| Done | Git repo (github.com/cliaz/koda) |
| Done | SSH deploy key on GitHub |
| Done | Central env file (scripts/koda-env.sh) for PATH management |
| Done | PostgreSQL + NVM node in ~/.bashrc PATH |
| Done | Sleep disabled (sudo pmset -a sleep 0 disksleep 0) |
| Done | Auto-login enabled |
| Done | All 17 tables created (users, groups, messages, sessions, tasks, memories, attachments, instructions, skills, deployments, credentials, browser_sessions, email_actions, audit_log, error_log, usage_alert_config, system_config) |
| Done | koda_bridge role + grants |
| Done | koda_agent role + grants |
| Done | koda_owner role (created with SELECT grants) |
| Done | RLS enabled on project-scoped tables |
| Done | koda_agent RLS policies (all 8 tables) |
| Done | koda_bridge RLS policies (all tables — full access) |
| Done | PostgREST JWTs generated |
| Done | pg_hba.conf trust auth for local sockets |
| Done | Response caching for identical read-only queries within 60s (in-memory, invalidated on writes) |
| Done | Signal bot registered (+61460036867) |
| Done | Device linked as secondary |
| Done | WebSocket receiver (json-rpc mode) |
| Done | @mention trigger detection in groups |
| Done | Piecemeal batching in DM |
| Done | Read receipts |
| Done | Markdown stripping from Claude responses |
| Done | Chunk numbering [1/N] format |
| Skipped | Message edit handling (out of scope) |
| Done | Unknown UUID silent ignore (logged, no response) |
| Deviation | DM batching uses timer (1500ms) not explicit @mention trigger — simpler, functionally equivalent |
| Done | Inbound message logging to DB |
| Done | Outbound message logging |
| Done | Message chunking at 1500 chars |
| Done | Working... notification before Claude invocation |
| Done | Concurrency control with FIFO queue (queued requests notified) |
| Done | Global rate limit (RATE_LIMIT_GLOBAL) |
| Done | Owner exempt from per-user rate limit |
| Done | UUID-based user lookup |
| Done | Owner/Admin/User roles |
| Done | Permissions table + capability grants |
| Done | hasPermission / hasRole functions |
| Done | Slash command aliases (/adduser, /removeuser, /setrole, /listusers, /grant, /revoke) |
| Done | Full capability enforcement for all 15 grantable capabilities |
| Done | Capability validation on /user grant (rejects invalid capability names) |
| Done | Sender capabilities included in Claude Code system prompt |
| Done | /grants <uuid> command (list grants for a user) |
| Done | /listusers shows roles and current grants per user |
| Deviation | /user add takes phone (generates UUID) — spec says /adduser <uuid> <role> |
| Done | Memory schema (memories table with priority column) |
| Done | /memory list|add|delete|search commands |
| Done | Memories injected into system prompt (global + session, priority-sorted, token budget enforced) |
| Done | Memory token budget enforcement (MEMORY_TOKEN_BUDGET, ~4 chars/token estimate) |
| Done | /memory clear global sub-command |
| Done | /memory list includes global memories (queries group_id match + IS NULL) |
| Done | /memory commands restricted to owner |
| Done | /instructions set (supports inline text or .md/.txt file attachment) |
| Done | Content hash to skip recondensation |
| Done | Condensation via truncation (2000 chars) — replaced Claude subprocess in Round 4 |
| Done | <!-- ALWAYS INCLUDE --> marker support |
| Done | fs.watch on instructions directory |
| Done | /instructions clear command |
| Done | Global vs session scope enforcement (global = owner only, session = set_instructions_session capability) |
| Done | skills/ directory + index.json |
| Done | /skills list (also accepts /skill) |
| Done | /skills show <name> |
| Done | /skills delete <name> (owner only) |
| Done | Skills index path in system prompt |
| Done | Pre/post git checkpoints (self-modify.sh) |
| Done | Auto-rollback on failure (verify health, revert file + commit) |
| Done | One-file-per-change rule enforcement (system prompt instructions) |
| Done | /modify command (owner only, 10 min timeout, auto-approved) |
| Done | Self-modification skill file + index entry |
| Done | AES-256-GCM credential encryption |
| Done | .secrets/credential_key (chmod 600) |
| Done | .gitignore excludes .env, .secrets/, node_modules/ |
| Done | pg_hba.conf trust auth |
| Done | File permissions health check on startup |
| Deviation | --dangerously-skip-permissions always on (required because bridge is non-interactive) |
| Done | Credential heuristic detection in plain messages |
| Done | Message redaction for /cred and /config commands |
| Skipped | Gmail MCP (deferred by user) |
| Partial | SMTP config in .env (vars present, empty values) (Variables present but empty) |
| Skipped | Action item extraction |
| Skipped | /emailsend autoapprove |
| Skipped | Google Calendar MCP (deferred by user) |
| Done | /cred set (encrypt + store) |
| Done | /cred get (decrypt + return) |
| Done | /cred delete |
| Done | /config set|get|list — .env management via Signal (allowlisted keys, sensitive values encrypted at rest) |
| Done | Message redaction from log on credential commands |
| Done | Credential heuristic detection |
| Deviation | Uses /cred set|get|delete instead of spec's /addcredential, /removecredential — no aliases |
| Done | Inbound attachment save to disk |
| Done | Attachment metadata to DB |
| Done | Directory structure (attachments/, instructions/, skills/, logs/, screenshots/) |
| Done | upload_files capability enforcement |
| Done | Outbound file send (detects file paths in Claude output, sends as Signal attachments) |
| Done | @playwright/mcp installed globally |
| Done | Chromium binary installed |
| Done | Launchd plist running (port 8931) |
| Done | SSE protocol client in bridge (callPlaywrightMCPTool) |
| Done | Persistent browser profile for claude.ai (.browser-profile/) |
| Done | Claude.ai login session established |
| Done | Prompt injection mitigation in system prompt (instructs Claude to ignore web page instructions) |
| Done | Wrangler installed |
| Done | Wrangler authenticated |
| Done | Cloudflare account ID in .env |
| Done | Cloudflare API token in .env (encrypted at rest) |
| Done | Auto-deploy watcher script (deploy-watch.sh + fswatch) |
| Done | Deploy-watch launchd plist (com.koda.deploy-watch) |
| Done | koda.systems custom domain + SSL |
| Done | koda.systems dashboard deployed and live |
| Done | Dashboard password protected (SHA-256 gate) |
| Done | install-checklist.md + install-notes.md downloadable from dashboard |
| Done | Playwright-based scraping of claude.ai/settings/usage (headed mode, persistent profile) |
| Done | In-memory cache (sessionPct, weeklyPct, reset times) |
| Done | Activity-aware polling (5 min after invocations, 60 min idle) |
| Done | Threshold crossing detection (once per crossing) |
| Done | /usage command suite (plain, refresh, set target, set thresholds, alerts, status) |
| Done | usage_alert_config DB persistence |
| Done | Scrape failure handling (logged + alerts owner via Signal) |
| Done | error_log table + logError function |
| Done | uncaughtException handler |
| Done | unhandledRejection handler |
| Done | Claude Code exit handling (logged + error message to user + owner notification) |
| Done | Approval timeout (configurable via APPROVAL_TIMEOUT_MINUTES, default 30 min) |
| Done | unhandledRejection notifies Owner via Signal + logs to error_log |
| Done | Signal send retry with backoff (3x, exponential) |
| Done | PostgREST retry with backoff (3x, skips 4xx client errors) |
| Done | Empty Claude response sends fallback message to user |
| Done | Signal send failures propagated and logged (signalSend/signalSendGroup return boolean, sendChunked tracks failures) |
| Done | Normal mode (message -> Claude Code) |
| Done | Auto-approve mode (/autoapprove on|off with autoapprove capability) |
| Deviation | Approval relay is dead code — --dangerously-skip-permissions bypasses all prompts |
| Done | /autoapprove status sub-command |
| Done | /status sub-commands (errors, logs, sessions, users, memories) |
| Deviation | /cred uses consolidated naming, not spec's /addcredential /removecredential |
| Done | VS Code model routing via /model vscode (extension + bridge) |
| Done | Bridge launchd (KeepAlive) |
| Done | PostgREST launchd |
| Done | Playwright MCP launchd |
| Done | Docker restart policy (unless-stopped) |
| Done | logrotate.conf |
| Done | prune-logs.sh |
| Done | Crontab entry for log pruning (daily 3am) |
| Done | Daily log file rotation (bridge-YYYY-MM-DD.log with symlink) |
| Done | Graceful shutdown with drain (30s timeout, rejects new messages during drain) |
| Done | tests/run_tests.js (62 tests passing) |
| Done | Test coverage (encryption, chunking, rate limiting, JWT, PostgREST, Signal, file structure, RBAC, markdown stripping, outbound file detection, memories, sessions, instructions, message pipeline, skills, audit/error logs) |
| Done | Test DB (koda_test) separate from production |
| Done | Test results to JSON file (tests/results.json) |
| Done | Signal vars |
| Done | Processing vars (MAX_CONCURRENT_CLAUDE, BATCH_DELAY_MS, MAX_SIGNAL_CHUNK) |
| Done | PostgREST vars |
| Done | Cloudflare vars (all set) |
| Done | Usage monitoring vars |
| Done | SMTP vars (present, empty) |
| Done | APPROVAL_TIMEOUT_MINUTES |
| Done | RATE_LIMIT_GLOBAL |
| Done | MEMORY_TOKEN_BUDGET |
| Done | AUTO_APPROVE_DEFAULT |
| Skipped | EMAIL_SEND_AUTOAPPROVE (email deferred) |
| Done | TEST_DB_NAME, TEST_SIGNAL_GROUP_ID, TEST_OWNER_UUID |
| Deviation | Some env var names differ from spec (SIGNAL_PHONE vs SIGNAL_BOT_NUMBER, MAX_SIGNAL_CHUNK vs MESSAGE_SPLIT_LENGTH, etc.) — implementation names are clearer |
| Done | Model selection persisted to system_config table (survives restarts) |
| Done | All env vars documented in .env.example (AUTO_APPROVE_DEFAULT, CLAUDE_MODEL, MEMORY_TOKEN_BUDGET, RATE_LIMIT_GLOBAL) |