subagentcoworkers

.com 20 pages
the durable spine

Postgres 16 — the durable spine

The local fleet's canonical store is the subagentjobs-postgres container, image postgres:16-alpine. Role gotcha, stated plainly: the documented DSN (postgresql://subagentjobs:subagentjobs@localhost/subagentjobs) is wrong on this Mac — the container was initialized with POSTGRES_USER=wbr (trust auth) and the subagentjobs role is never created locally (the role-creation block in session-start.sh is gated on the remote container). The connection that actually works, carried in both CLAUDE.md's pinned-tooling section and the 008 migration header itself: psql -h 127.0.0.1 -U wbr -d subagentjobs.

baked 2026-07-10 from docker ps --format '{{.Names}}\t{{.Image}}'subagentjobs-postgres · postgres:16-alpine

The estate — migrations 001→010

001 schema 002 indexes+FTS 003 eviction 004 vendor fs 005 task events 006 ecosystem 007 doc pages 008 fleet ledger 009 dispatch 010 repo index fact_filesystem 22,323 rows, no body dim_file_ast 9,957 rows fact_fleet_deploy 38 rows agent_dispatch_events 2 rows — fills as routed sessions run 006 ecosystem fact_orgs 6 · fact_repositories 24 dim_packages 54 fact_repo_files 914 rows · /indexdb crawler tables — empty locally fact_job_posting 0 (001) · fact_doc_pages 0 (007)

Spine baked 2026-07-10 from ls crates/durable-store/migrations/postgres/; row counts from psql -h 127.0.0.1 -U wbr -d subagentjobs -c "SELECT count(*) FROM <table>" per table. agent_dispatch_events shows its real bake-time count (2 — two routed dispatches had already been drained by the Stop hook), not a rounded-down zero. The dashed box is honest too: the job-board crawler tables exist in the schema but are empty on this local instance — the crawl pipeline fills them in the Cloudflare deployment, not here.

The ledger is the state, not the transcript

Rule 3 of the fleet-expansion skill (plugins/cwc-deploy/skills/fleet-expansion/SKILL.md) names the principle this whole estate runs on: "The ledger is the state, not the transcript. One fact_fleet_deploy row per domain … A crashed run resumes with ledger.sh resume." Agents coordinating a deploy wave read and write rows, not conversation context — the 38 rows in fact_fleet_deploy are the deploy history, replayable by any future session with zero tokens spent reconstructing what happened. agent_dispatch_events (009) applies the same principle to model routing: every dispatch decision the hooks enqueue lands as a durable row, so routing policy can be audited from SQL rather than from transcripts.

Full-text search is a habit here, not a feature

Five migrations ship GIN FTS indexes, each verified in the SQL (USING GIN): 002 over job posting title+company, 004 over vendor-file symbol docs (dim_file_ast.doc_tsv, plus GIN array indexes on symbols/imports/exports), 006 over the ecosystem catalog (fact_repositories.fts, dim_packages.fts), 007 over crawled doc pages, and 010 over the repo file index (fact_repo_files.fts, a stored generated tsvector column). The pattern compounds: by 010, adding a searchable corpus meant copying 007's proven sha-CDC-plus-GIN shape, not designing one.

What lives here vs. redis

Postgres holds everything that must survive a crash or be true across sessions: the vendor file index (004), the ecosystem catalog (006), the deploy ledger (008), dispatch telemetry (009), and the repo content index (010). Redis holds only what these tables can regenerate — read-through caches with TTL safety nets and per-run scratch. The one deliberate exception is the docs-rag index: docs/docs (9,454 md files) is owned by sqlite FTS5, and consolidating it into Postgres is an explicitly deferred decision, written into the 010 migration header — see /indexdb for that composition story.

Companion pages: /indexdb (what 010 indexes and why) and /redis (the fast path in front of this spine).