Add pg_dump snapshot cache for software-factory Playwright tests#4378
Add pg_dump snapshot cache for software-factory Playwright tests#4378
Conversation
Implements a Tier 2 cache between the existing PG template (Tier 1) and the expensive full rebuild (Tier 3). When the PG template is missing but a committed pg_dump snapshot exists with a matching fingerprint, the template is restored via pg_restore in seconds instead of a ~10 min full rebuild. New files: - db-snapshot.ts: core snapshot logic (fingerprint, dump, restore, check) - lint-snapshot-freshness.ts: lint script to detect stale snapshots - .gitattributes: mark pgdump as binary for git - db-snapshots/.gitkeep: directory placeholder Modified files: - api.ts: integrate snapshot restore/save in both ensureFactoryRealmTemplate and ensureCombinedFactoryRealmTemplate - shared.ts: add dbSnapshotDir export - package.json: add lint:snapshot-freshness script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Lint error now shows clear instructions for developers unfamiliar with the software-factory package: what went wrong, which directories to check, exact command to fix, and a fallback note about PG requirements - Add --update-snapshot flag to cache:prepare that uses the canonical fixture list (no need to remember which dirs to pass) - Include initial snapshot dump (40MB) and fingerprint Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strip data that is rebuilt at clone/startup time before dumping: - boxel_index_working (exact copy of boxel_index, rebuilt on clone) - boxel_index.last_known_good_deps (14MB of fallback deps, rebuilt on index) - modules (cleared on realm startup) - jobs/job_reservations (cleared on clone) Clone to temp DB, strip, VACUUM FULL, then pg_dump for best compression. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12MB compressed pg_dump of the Playwright test template database, generated from the current base realm, source realm, and test fixtures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1694bb788c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR adds a committed PostgreSQL pg_dump snapshot + fingerprinting layer to speed up software-factory Playwright template database preparation, with a new lint rule to ensure the committed snapshot stays in sync with fixtures/source.
Changes:
- Introduces snapshot creation/restore utilities (
pg_dump/pg_restore) and deterministic fingerprinting for fixture/source freshness checks. - Integrates snapshot restore (fast path) and snapshot save (after full rebuild) into the harness template preparation flow.
- Adds
--update-snapshotsupport tocache:prepareand alint:snapshot-freshnessscript that fails when the committed snapshot is stale.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/software-factory/src/harness/shared.ts | Exposes dbSnapshotDir path for snapshot storage. |
| packages/software-factory/src/harness/db-snapshot.ts | Implements fingerprinting + snapshot dump/restore helpers. |
| packages/software-factory/src/harness/api.ts | Adds snapshot restore-before-rebuild and save-after-rebuild hooks for templates. |
| packages/software-factory/src/cli/cache-realm.ts | Adds --update-snapshot to use the canonical fixture set for snapshot regeneration. |
| packages/software-factory/scripts/lint-snapshot-freshness.ts | Adds lint script to validate committed snapshot freshness via fingerprint. |
| packages/software-factory/package.json | Registers lint:snapshot-freshness script. |
| packages/software-factory/db-snapshots/fingerprint.json | Adds committed snapshot fingerprint metadata. |
| packages/software-factory/db-snapshots/.gitkeep | Keeps snapshot directory present in git when empty. |
| packages/software-factory/.gitattributes | Marks template.pgdump as binary to avoid text transforms. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Gate snapshot writes to canonical fixture set only (prevents single-realm builds from overwriting the committed 4-realm snapshot) - Fix pgEnv() to not set PGPASSWORD to empty string (preserves .pgpass) - Gate snapshot restore on !hasTemplateDatabase (avoids CREATE DATABASE failure when DB exists but metadata is missing) - Fix URL consistency in single-realm restore path (derive both templateRealmURL and templateRealmServerURL from snapshot metadata) - Remove unreachable cacheVersion lint check (already covered by fingerprint) - Add isCanonicalFixtureSet() helper Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The global setup sets SOFTWARE_FACTORY_SOURCE_REALM_DIR to test-fixtures/public-software-factory-source, but the fingerprint was computed using the env-dependent default (realm/). This caused a fingerprint mismatch in CI, bypassing the snapshot restore. Fix: use CANONICAL_SOURCE_REALM_DIR (hardcoded to the Playwright test value) instead of the env-dependent sourceRealmDir, ensuring the fingerprint is consistent across lint, local dev, and CI. Also reduces dump size from 11.7MB to 4.1MB since the canonical source realm is smaller. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test-fixtures/public-software-factory-source directory was a 3-file
stub that could diverge from the real realm/. Replace it with a symlink
to realm/ so they can never diverge.
Add SOURCE_REALM_GLOB ('*.gts .realm.json !document.gts') that filters
which files from the source realm get copied for indexing and included
in the snapshot fingerprint. This keeps the test DB small (only card
definitions, not instance data) while ensuring the snapshot stays in
sync with the actual source realm.
Also add realpathSync to copyRealmFixture so symlinks are resolved
before copying, and add fileFilter option to hashRealmFixture.
All 25 Playwright tests pass. cache:prepare restores from snapshot in
4.4s (vs ~10min full build).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
wiki.gts is not needed for the Playwright test suite. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The public-software-factory-source fixture was a duplicate of the source realm (realm/) — the same content was being indexed at two different URL paths. Remove it and let the source realm be indexed once at /software-factory/, using the glob filter to copy only card definitions (darkfactory.gts, test-results.gts) to the temp build dir. Also remove darkfactory.spec.ts which tested card rendering against the adopter fixture — these schema tests are covered by runtime-schema.spec.ts and the bootstrap/tool-executor tests. Results: - 3 fixture realms instead of 4 - Dump size: 4.1 MB (down from 11.7 MB) - cache:prepare from snapshot: 0.8s - 21 Playwright tests pass in 7.0m Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
backspace
left a comment
There was a problem hiding this comment.
I’m approving but I only superficially understand and haven’t exercised locally
Summary
lint:snapshot-freshnesslint rule that fails when the committed snapshot is stale--update-snapshotflag tocache:preparefor easy snapshot regenerationpublic-software-factory-sourcefixture — source realm (realm/) is now used directly with a glob filterdarkfactory.spec.tstestsThe Problem
The
cache:preparestep builds a PostgreSQL template database by spinning up a full realm server, indexing all fixtures (~376 files across base realm, source realm, and test fixture directories), and waiting for indexing to complete. This takes 5-10 minutes.The PG template database doesn't survive machine reboots, PostgreSQL restarts, Docker container rebuilds, or fresh git checkouts. Since test fixtures rarely change, developers frequently sit through a full 10-minute rebuild for content that hasn't changed.
How It Works
Three-tier cache hierarchy
CREATE DATABASE ... TEMPLATEclone. This is the existing behavior, unchanged.pg_restorefrom a committedpg_dumpfile when the PG template is gone but fixtures haven't changed.Snapshot fingerprint
A fingerprint is computed by SHA-256 hashing the full file contents of every file in:
packages/base/(base realm — most likely to trigger regeneration)packages/software-factory/realm/(source realm, filtered throughSOURCE_REALM_GLOB— only.gtscard definitions and.realm.json)packages/software-factory/test-fixtures/(3 fixture directories: darkfactory-adopter, bootstrap-target, test-realm-runner)Plus the
CACHE_VERSIONconstant (bumped on schema changes). The fingerprint is stored indb-snapshots/fingerprint.jsonalongside the dump.Source realm glob filter
The source realm (
realm/) contains 205 files but tests only need the core card definitions. A glob filter controls which files are included:This filter is applied both when computing the fingerprint and when copying the source realm to the temp build directory. Only
darkfactory.gts,test-results.gts, and.realm.jsonmake it through. The wiki briefs, document instances, and other content are not necessary for tests.When the snapshot is created/regenerated
cache:prepareafter any full build (Tier 3). If the build succeeds and the fixtures match the canonical set, the template is dumped and the fingerprint is written.cd packages/software-factory && pnpm cache:prepare --update-snapshotDump size optimization
The dump is optimized to ~4 MB by:
boxel_index_working(rebuilt fromboxel_indexat clone time)last_known_good_deps(14MB of fallback data, rebuilt on next index)modules,jobs,job_reservations(rebuilt at startup)VACUUM FULLbefore dumpingpublic-software-factory-sourcefixture (source realm indexed once, not twice)Lint rule:
lint:snapshot-freshnessA new lint check (auto-included in
pnpm lintvia the existing concurrently glob) that:fingerprint.jsonThe error message explains what went wrong, which directories could have caused the change, and gives the exact command to fix it — designed for developers who don't normally work in the software-factory package.
Removed
public-software-factory-sourceThe old
test-fixtures/public-software-factory-sourcewas a 3-file stub of the source realm that could silently diverge fromrealm/. It was indexed as both the source realm AND a separate fixture (duplicate). Now the source realm (realm/) is used directly, filtered throughSOURCE_REALM_GLOB, and indexed once at/software-factory/.Files
src/harness/db-snapshot.tssrc/harness/api.tssrc/harness/isolated-realm-stack.tssrc/harness/shared.tsdbSnapshotDirexport,fileFilteroption onhashRealmFixturesrc/cli/cache-realm.ts--update-snapshotflagscripts/lint-snapshot-freshness.tsplaywright.global-setup.tstests/fixtures.tstests/darkfactory.spec.tspackage.jsonlint:snapshot-freshnessscript.gitattributestemplate.pgdumpas binarydb-snapshots/template.pgdumpdb-snapshots/fingerprint.jsonTest plan
SOFTWARE_FACTORY_SOURCE_REALM_DIRenv var🤖 Generated with Claude Code