refactor(core): Postgres template databases for sub-second branch reset - #2
Merged
Conversation
Replaces the per-branch container model with one long-lived container per connector that hosts a frozen seed database (IS_TEMPLATE=true) plus N branch databases cloned via CREATE DATABASE ... TEMPLATE seed. Reset goes from "docker rm -f + docker run + waitForReady + psql restore" (5-15s) to "DROP DATABASE WITH (FORCE); CREATE DATABASE FROM TEMPLATE" (~200-800ms on a 10k-row schema), an ~10x improvement. Key changes: - New low-level helpers in branching/docker.ts: createConnectorContainer, waitForConnectorReady, execSqlInDb, loadInitSqlIntoDb, dumpDatabase, restoreDumpToDatabase, listDatabases. - DockerBranchProvider rewritten around container.json (one per connector, in .sow/snapshots/<connector>/container.json). - providerMetaVersion=2 with explicit migration error for v1 branches. - stopBranch is now per-branch connection termination (the container is shared); startBranch verifies the container is running. - manager.resetBranch no longer recreates the container in the docker case. - All identifiers go through quoteIdent and connector/branch names are regex-validated to prevent injection through the connector name. - 7 new unit tests with mocked docker helpers; one BUGSTER_DB_URL-gated integration benchmark asserting reset < 1500ms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 tasks
faculopezscala
added a commit
that referenced
this pull request
Apr 6, 2026
The 5 code PRs (#2-#6) deliver the engineering for the launch. This PR delivers everything user-facing: the README pitch, the package metadata, the docs/ folder, and the CHANGELOG. This PR depends on PRs #2-#6 being merged first because the new README references commands those PRs add (sow sandbox, sow doctor <connector>, the --allow-unsafe flag, the sub-second reset). Land them first, rebase this against main, then merge. README.md Hero rewritten from "Safe test databases from production Postgres" to "Stop letting Claude touch your prod database". Body explains the anxiety-reduction pitch: a coding agent is about to do something database-adjacent and you feel that quiet pang. sow is the safety layer. New "Why sow" section. New "How It Works" diagram showing the template-DB shape (one container per connector, N branch DBs, reset in <1s). New "Cookbook" stub linking to docs/cookbook.md. New "Documentation" section with the docs/ index. packages/cli/package.json Description: "Stop letting Claude touch your prod database. PII-safe local Postgres sandbox for coding agents." Keywords: added ai-agents, coding-agents, claude-code, cursor, sandbox, mcp. packages/core/package.json Description: "sow core engine — analyze, sample, sanitize, and branch Postgres databases for safe coding-agent sandboxes" Keywords: added ai-agents, coding-agents, sandbox. packages/mcp/package.json Description corrected from "15 tools" to "22 tools" (the actual count in packages/mcp/src/index.ts) and repositioned: "sow MCP server — 22 tools for coding agents (Claude Code, Cursor, Codex) to safely manage Postgres sandboxes" Keywords: added claude-code, cursor, codex, coding-agents, sandbox. docs/sandbox.md (new) The sow sandbox flagship command — what it does, the flags, the .env.local backup/revert flow, when not to use it, and what's actually in the sandbox. docs/sanitization.md (new) What sow sanitizes (the PII type table), how JSONB walking works, the fail-closed gate, the --allow-unsafe escape hatch, custom rules via .sow.yml, what sow does NOT do (free-text NER, etc.), and the read-only-on-the-source guarantee. docs/cookbook.md (new) Three end-to-end workflows with concrete prompts: 1. Let Claude refactor your schema without fear 2. Let Cursor generate seed data for a new feature 3. Let your coding agent debug a failing migration Plus the "agent reset loop" pattern diagram, the MCP tool list, and operational tips (one long-running sandbox per project, checkpoints for known-good states, sow doctor as the inspection surface, principle of least privilege on the source DB user). CHANGELOG.md (new) Scaffold with three sections: - [Unreleased] documenting the planned PR #2-#6 features under Added/Changed - [0.1.14] documenting the SQL injection security fix that already shipped (PR #1, merged earlier in the session) - [0.1.13] one-line summary of the initial public release Test/build/lint all clean (89/89 tests, 3/3 packages built, no source code changed in this PR). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lane B (Issue #1): refactor the Docker branch provider so `sow branch reset` runs in well under 1.5s instead of the current 5-15s.
Architecture change
Before: One Docker container per branch. Reset = `docker rm -f` + `docker run` + `pg_isready` poll + `psql -f init.sql`. Total: 5-15s.
After: One long-lived Docker container per connector, hosting:
Reset is now just:
```sql
DROP DATABASE sow_feature_a WITH (FORCE);
CREATE DATABASE sow_feature_a WITH TEMPLATE sow_seed_myconn OWNER sow;
```
That's ~200-800ms on a 10k-row schema — roughly 10x faster than the old container-recycle path. Container boot cost is amortized across every branch for the connector instead of being paid on every reset.
What changed
Tests
Test plan
Migration story
Branches created by older sow versions have a v1 `providerMeta` shape (`{ containerId, containerName, pgVersion }` with no `databaseName`). Any operation on such a branch now throws a clear error instructing the user to delete and recreate the branch. This is safer than auto-migrating because the old per-branch containers would need to be torn down and the user's port assignments may shift.
🤖 Generated with Claude Code