mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 14:59:47 +00:00
e60b60244ff3170ee66102a4edb59bef36254d99
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e493d5f44b |
v0.32.8 fix: multi-source bug class extermination — embed, extract, takes, patterns, integrity, migrate-engine (#860)
* fix: thread source_id through embed --stale to fix silent discard of non-default source embeddings
listStaleChunks correctly finds chunks across all sources, but
embedOneSlug called getChunks(slug) and upsertChunks(slug, merged)
without passing sourceId. Both default to source_id='default', so
for non-default sources (e.g. media-corpus):
1. getChunks returns empty (wrong source)
2. merged array has no existing chunks to merge into
3. upsertChunks writes nothing (or errors silently)
4. Embeddings generated by the API are silently discarded
Fix:
- Add source_id to StaleChunkRow type
- Add p.source_id to listStaleChunks SQL in both postgres + pglite engines
- Extract sourceId from stale row in embed command
- Pass { sourceId } to getChunks and upsertChunks
- Group stale chunks by composite key (source_id::slug) instead of bare slug
to handle same-slug pages across multiple sources
Verified: 97 chunks embedded across 35 pages in first run after fix.
Previously 0 non-default-source chunks were embedded across 3 full runs.
* fix: comprehensive multi-source threading for embed, listPages, and migrate-engine
Multi-source brains (e.g. with a 'media-corpus' source alongside
'default') have a pervasive bug: operations that iterate pages across
all sources then call engine methods (getChunks, upsertChunks,
getChunksWithEmbeddings) without passing sourceId. These methods all
default to source_id='default', silently operating on the wrong page
(or no page at all) for non-default sources.
Changes:
1. Page type + rowToPage: add optional source_id field so downstream
callers can read the source from page objects returned by listPages.
2. PageFilters: add sourceId filter so listPages can scope to a single
source (used by embed --source and future extract --source).
3. listPages (postgres + pglite): wire the sourceId filter into SQL.
4. embed command — three paths fixed:
a. embedPage (single-slug): accepts sourceId, threads to getPage +
getChunks + upsertChunks.
b. embedAll (--all): reads page.source_id from listPages results,
threads to getChunks + upsertChunks per page.
c. embedAllStale (--stale): reads source_id from StaleChunkRow,
groups by composite key (source_id::slug) instead of bare slug,
threads to getChunks + upsertChunks per key.
5. embed CLI: add --source <id> flag, threaded through all paths.
6. migrate-engine: thread page.source_id through
getChunksWithEmbeddings + upsertChunks so engine migrations don't
lose non-default-source chunks.
7. getChunksWithEmbeddings (postgres + pglite + BrainEngine interface):
accept optional { sourceId } to scope the chunk lookup.
8. StaleChunkRow type: add source_id field.
9. listStaleChunks SQL (postgres + pglite): add p.source_id to SELECT.
Verified: embed --stale correctly embeds 97 chunks across 35 pages
(previously 0 non-default-source chunks across 3 full runs).
embed --source media-corpus --dry-run correctly scopes to that source.
* v0.32.4 fix: multi-source threading for embed, listPages, and migrate-engine
Bump VERSION + package.json + CHANGELOG for the comprehensive multi-source
fix. Embed now threads source_id through every page → chunk handoff so
non-default sources stop silently dropping out (~22k chunks recovered on
the brain that surfaced this).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: complete slugs→keys rename in embedAllStale
The composite-key rename in the prior commit missed 4 references in the
worker loop and trailing console.log, so the file failed typecheck
(`Cannot find name 'slugs'`). The author's "Verified compiling + running"
claim was false at the time of the PR.
Also drop the dead `const bySlug = byKey` alias — unused after rename.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: add check-source-id-projection.sh + fix getPage/putPage projections
Two SELECT projections fed `rowToPage` without including `source_id`:
- postgres-engine.ts:562 (getPage), :609 (putPage RETURNING)
- pglite-engine.ts:505 (getPage), :548 (putPage RETURNING)
After the type-tightening in the next commit makes `Page.source_id`
required, those projections would silently produce `Page` rows with
source_id=undefined while TypeScript claims `: string`. Codex's plan
review (F2) caught this; this commit closes it.
The new `scripts/check-source-id-projection.sh` greps for the rowToPage
feeder shape (`SELECT id, slug, type, title, ...`) and fails the build
if any projection lacks `source_id`. Wired into `bun run verify`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(engine): Page.source_id required + listAllPageRefs + validateSourceId
Three coordinated changes that unlock the Phase 3 bug-site fixes:
1. `Page.source_id` is now required (was optional, v0.31.12). The DB column
is `NOT NULL DEFAULT 'default'` so every row has it; the type now matches.
`rowToPage` always emits it (falls back to 'default' if a stale projection
somehow misses the column, but `scripts/check-source-id-projection.sh` is
the primary guard).
2. `BrainEngine.listAllPageRefs()` returns `Array<{slug, source_id}>` ordered
by `(source_id, slug)`. Cheap cross-source enumeration for hot loops in
extract-takes / extract / integrity that previously used
`getAllSlugs() → getPage(slug)` (N+1 query AND silently defaulted to
'default'). PGLite + Postgres parity.
3. `validateSourceId(id)` in utils. Allows `[a-z0-9_-]+` only. Used by the
per-source disk-layout fix coming in Phase 3 before any
`join(brainDir, source_id, ...)` call so source_id can't traverse out
of brainDir.
Deferred to v0.33 follow-up:
- D2 strict tightening of BrainEngine slug-method signatures (the compile-
time guard for "future getPage calls must pass sourceId")
- F3 OperationContext.sourceId required at MCP boundary
- F4 LinkBatchInput / TimelineBatchInput required source_id fields
- D6 forEachPage / listPagesAfter helpers (use listPages directly for now)
Those are nice-to-have guardrails for future regressions. Current commit's
correctness via D7 + listAllPageRefs is what blocks the Phase 3 bug-site
fixes from working multi-source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: thread source_id through cycle phases, extract, integrity, migrate-engine
Five bug sites that previously called slug-only engine methods inside a
loop over pages, silently defaulting to source_id='default' for every
non-default-source page. Now all five use listAllPageRefs to enumerate
(slug, source_id) pairs and thread sourceId through to engine.getPage,
getTags, addLink, addTimelineEntry, getRawData, getVersions, etc.
Site-by-site:
- src/core/cycle/extract-takes.ts: listAllPageRefs replaces N+1
getAllSlugs+getPage. Takes for non-default-source pages now extract.
- src/core/cycle/patterns.ts + synthesize.ts: reverseWriteSlugs renamed
to reverseWriteRefs with Array<{slug, source_id}> contract. Disk
layout (F6): non-default sources land at brainDir/.sources/<id>/<slug>.md
so same-slug-different-source pages don't collide. Default-source
pages stay at brainDir/<slug>.md so single-source brains see no
change. source_id validated against [a-z0-9_-]+ at write time to
prevent path traversal.
- src/commands/extract.ts: extractLinksFromDB + extractTimelineFromDB
use listAllPageRefs. Cross-source link resolution rule (F10): origin's
source wins, fall back to default, else skip (don't silently push a
wrong-source edge). addLinksBatch / addTimelineEntriesBatch now fill
from_source_id / to_source_id / origin_source_id / source_id so
multi-source JOINs target the correct page row.
- src/commands/integrity.ts: same listAllPageRefs pattern in both the
primary scan loop and the auto-repair loop.
- src/commands/migrate-engine.ts: end-to-end source_id threading
(page + tags + timeline + raw + versions + links). Resume manifest
keyed on `${source_id}::${slug}` so multi-source resumes don't
collide on same-slug rows (pre-fix entries treated as default for
back-compat).
test/cycle-synthesize-slug-collection.test.ts updated for the new
collectChildPutPageSlugs return shape (Array<{slug, source_id}>
instead of string[]).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(e2e): multi-source bug class regression + CHANGELOG + e2e-test-map wire-up
test/e2e/multi-source-bug-class.test.ts — 7-case PGLite regression suite
pinning every bug site fixed in this PR:
- listAllPageRefs ordering by (source_id, slug) [F11]
- getPage with sourceId picks the right (source, slug) row [F2]
- extract-takes processes both alice pages independently
- listPages filters correctly with PageFilters.sourceId
- addLinksBatch with from/to_source_id targets the right rows [F4]
- validateSourceId rejects path traversal [F6]
- reverse-write disk layout uses .sources/<id>/<slug>.md [F6]
No DATABASE_URL needed (PGLite in-memory + canonical R3+R4 pattern).
Wire into scripts/e2e-test-map.ts so changes to any of the 6 touched
source files automatically trigger this test.
CHANGELOG expanded from the embed-only narrative to cover the full
bug-class extermination — extract, takes, patterns, integrity,
migrate-engine, plus the per-source disk layout, the CI gate, and
the new listAllPageRefs primitive. Voice: lead with what users can
DO that they couldn't before; real numbers from the production brain
that surfaced it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(integrity): batch path scans (source_id, slug) pairs too
The batch-load fast path in scanIntegrity used `SELECT DISTINCT ON (slug)`,
which silently collapsed multi-source duplicate slugs into a single scan —
the same bug class this PR fixes. test/e2e/integrity-batch.test.ts had a
case pinning the broken behavior ("scan once, not once-per-source") that
asserted batchResult.pagesScanned===1 for two real (source, slug) rows.
Switching the projection from `DISTINCT ON (slug)` to a plain `SELECT ...
ORDER BY source_id, slug` makes batch + sequential paths report the same
count (2) and matches the v0.32.4 listAllPageRefs walk.
Test renamed + assertion flipped to lock in the correct multi-source-aware
behavior: both paths now report 2, not 1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: sync CLAUDE.md + llms bundles for v0.32.4
CLAUDE.md annotations updated on the 4 files that materially changed in
this PR's bug-class extermination:
- src/core/engine.ts — new listAllPageRefs() method
- src/core/utils.ts — new validateSourceId() helper + Page.source_id
required field plumbing
- src/commands/integrity.ts — batch projection switched from DISTINCT ON
(slug) to ORDER BY (source_id, slug) so multi-source scans aren't
collapsed
- scripts/check-source-id-projection.sh (NEW entry) — CI guard against
SELECT projections that drop source_id
Plus a new test inventory entry for test/e2e/multi-source-bug-class.test.ts
in the E2E section.
llms-full.txt regenerated per CLAUDE.md's iron rule. llms.txt is unchanged
(just an index).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump version slot v0.32.4 → v0.32.8
VERSION + package.json + CHANGELOG header only. Annotation
sweep across src/tests/scripts and the CLAUDE.md + llms bundle
regen land in the two follow-up commits so each step bisects
independently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: retag v0.32.4 → v0.32.8 across src/scripts/tests
Inline "introduced in" annotations follow the version slot bump
in the prior commit. No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: retag CLAUDE.md v0.32.4 → v0.32.8 + regen llms-full.txt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Merge remote-tracking branch 'origin/master' into fix/multi-source-threading
---------
Co-authored-by: Wintermute <wintermute@garrytan.com>
Co-authored-by: Garry Tan <garrytan@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
bd2fe8a1fa |
v0.32.5 feat: gbrain-context OpenClaw context engine — deterministic temporal/spatial injection (#880)
* feat: gbrain-context OpenClaw context engine — deterministic temporal/spatial injection Adds a context engine plugin that runs on every assemble() call to inject structured live context into the system prompt: - Garry's current local time (computed from heartbeat-state.json timezone) - Current location (city + timezone from heartbeat or flight data) - Home time when traveling (e.g. 'Mon 7:58 AM PT') - Active travel status - Quiet hours detection - Airport→timezone mapping for 30+ airports This kills the 'time warp' bug class where compacted sessions lose track of time/location. The engine delegates compaction to the legacy runtime and only owns systemPromptAddition injection. Zero LLM calls, <5ms. Files: - src/core/context-engine.ts — engine implementation (SDK-free, testable) - src/openclaw-context-engine.ts — plugin entry point (requires SDK) - test/context-engine.test.ts — 9 tests, all passing Enable: plugins.slots.contextEngine = 'gbrain-context' * feat: add activity injection — calendar events + open tasks in context block Reads memory/calendar-cache.json and ops/tasks.md to inject: - **Right now:** current meeting (with attendees) from calendar - **Coming up:** next 3 events within 4-hour window - **Open tasks:** unchecked items from Today section - Stale calendar warning when cache is >6 hours old Skips all-day events and generic markers (Home, OOO, Out of Office). Caps upcoming events at 3 and tasks at 5 to keep prompt lean. 15 tests passing (was 9). * v0.32.5 feat: gbrain-context OpenClaw context engine — deterministic temporal/spatial injection Ships PR #873 by @garrytan-agents (two underlying commits preserved): - |
||
|
|
9e2093fc9b |
v0.26.6 feat(schema): PGLite ↔ Postgres parity gate (closes #588) (#590)
* v0.26.3 feat(schema): PGLite ↔ Postgres parity gate + access_tokens.id type fix (#588) Drift gate (test/e2e/schema-drift.test.ts) spins up fresh PGLite + Postgres, runs each engine's initSchema(), snapshots information_schema.columns, and diffs the four-tuple (data_type, udt_name, is_nullable, column_default) per column. 17 unit cases for the pure diff function (test/helpers/schema-diff.ts + schema-diff.test.ts) including a D3 negative test that reproduces the v0.26.1 oauth_clients.token_ttl regression. 6 E2E cases including 4 sentinels for oauth_clients, mcp_request_log, access_tokens, eval_candidates. The gate caught one real drift on its first run: access_tokens.id was UUID on Postgres (schema.sql:328, migration v4) and TEXT on PGLite (pglite-schema.ts). Reconciled to UUID DEFAULT gen_random_uuid() on both sides. CI wiring in scripts/e2e-test-map.ts triggers schema-drift on changes to schema.sql, pglite-schema.ts, or migrate.ts. The 2-table allowlist (files, file_migration_ledger) is narrow by design — every other Postgres table must reach PGLite via PGLITE_SCHEMA_SQL or a migration's sqlFor.pglite branch. Bookkeeping: master HEAD's VERSION was 0.26.0 even though the prior commit shipped as v0.26.1 (the bump never landed). Moving to 0.26.3 per the same bookkeeping discontinuity. Codex flagged a versioning hardening follow-up (scripts/check-version-sync.sh pre-push guard) for v0.26.4. Also fixes two pre-existing CI failures master shipped through: - check-privacy.sh: src/core/mounts-cache.ts had two banned name references ("Wintermute"). Replaced with "your OpenClaw" per CLAUDE.md:550. - check-no-legacy-getconnection.sh: src/commands/integrity.ts:355 was a new legacy db.getConnection() caller. Added to the script's allowlist with a PR 1 cleanup note (matches the existing 8 grandfathered entries). Out of scope (filed for v0.26.4): manual ALTER TABLE on production Postgres that never made it into source files (the actual v0.26.1 trigger; needs a gbrain doctor --schema-audit mechanism); index parity; versioning hardening guard. Plan + codex review pivot: original plan compared raw schema.sql vs raw pglite-schema.ts; codex showed they're intentionally divergent today (PGLite reaches its end-state via PGLITE_SCHEMA_SQL + migrations). Pivoted to end-state comparison, which catches real drift without false positives. Closes #588. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump v0.26.3 → v0.26.4 Per user instruction. No code or test changes — VERSION + package.json + CHANGELOG header/body + CLAUDE.md key-files entry. Regenerated llms-full.txt. "NOT in this release" deferral targets bumped from v0.26.4 → v0.26.5 (those items are still deferred; they're now deferred from v0.26.4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump v0.26.4 → v0.26.6 Per user instruction. Bookkeeping-only — VERSION + package.json + CHANGELOG header/body + CLAUDE.md key-files entry. Regenerated llms-full.txt. "NOT in this release" deferral targets bumped from v0.26.5 → v0.26.7 (those items remain deferred; now from v0.26.6 instead of v0.26.4). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
90e22c22e2 |
v0.23.1 feat: local CI gate + 4-tier wall-time optimization (~13x faster) (#528)
* feat: diff-aware E2E test selector Adds scripts/select-e2e.ts: reads git diff vs origin/master, classifies the change set (EMPTY/DOC_ONLY/SRC), and emits the relevant E2E test files on stdout. Fail-closed by design: any unmapped src/ change runs all E2E. - scripts/e2e-test-map.ts: hand-tuned path-glob -> test files map - scripts/select-e2e.ts: pure-function selector with three explicit cases - scripts/run-e2e.sh: accepts optional file list from argv + --dry-run-list - test/select-e2e.test.ts: 24 cases including 3 codex regression guards (skills/, untracked files, unmapped src/) * feat: local CI gate via docker compose Adds bun run ci:local — runs every check GH Actions runs (gitleaks + unit + 29 E2E files) inside a Docker container that bind-mounts the repo. Pure bind-mount + named volumes (gbrain-ci-node-modules, gbrain-ci-bun-cache, gbrain-ci-pg-data) for fast warm restarts. - docker-compose.ci.yml: pgvector/pgvector:pg16 + oven/bun:1 - scripts/ci-local.sh: orchestrator with --diff, --no-pull, --clean - gitleaks runs on host (scoped to working dir + branch commits) - DATABASE_URL unset for unit phase (matches GH Actions split) - git installed in container at startup (oven/bun:1 omits it) - Postgres host port via GBRAIN_CI_PG_PORT env (default 5434) Stronger than PR CI: runs all 29 E2E files vs CI's 2-file Tier 1. * chore: bump version and changelog (v0.23.1) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: document local CI gate for v0.23.1 CLAUDE.md gains key-files entries for docker-compose.ci.yml, scripts/ci-local.sh, scripts/select-e2e.ts + e2e-test-map.ts, and the scripts/run-e2e.sh argv tweak. Pre-ship requirements section now lists the Docker-based local gate as Path A alongside the manual lifecycle. CONTRIBUTING.md tests section adds the bun run ci:local / ci:local:diff / ci:select-e2e block with prerequisites (Docker engine + gitleaks) and the GBRAIN_CI_PG_PORT override. AGENTS.md "Before shipping" promotes ci:local as the easiest path and keeps the manual lifecycle as a fallback. README.md Contributing section points to ci:local for the full gate. CHANGELOG.md untouched — v0.23.1 entry already finalized. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat: SHARD=N/M env support in scripts/run-e2e.sh Filters the E2E file list to every M-th file starting at index N (1-indexed). Sequential execution within a shard preserves the TRUNCATE CASCADE no-race property documented at the top of the file. Empty-shard handling under `set -u` uses ${arr[@]:-} fallback. Standalone change; not yet wired up in ci-local.sh. * feat: 4-way parallel E2E shards in ci:local Replaces the single postgres service with 4 (postgres-1..4) on host ports 5434-5437. scripts/ci-local.sh fans 4 workers via xargs -P4 inside the runner container; each pinned to its own DATABASE_URL via SHARD=N/4. Wall-time on a 16-core host: ~6 min sequential -> ~1.5-2 min sharded. Total full-gate wall-time goes from ~25 min to ~3-5 min warm. Also handles git-worktree (Conductor) layouts: when /app/.git is a file instead of a directory, parse the gitdir + commondir and bind-mount the shared host gitdir at its absolute path. Without this, in-container `git ls-files` (used by scripts/check-trailing-newline.sh and friends) exits 128 with "not a git repository". Also runs `git config --global --add safe.directory '*'` inside the container so the root-uid container can read host-uid gitdir without "dubious ownership" rejection. CHANGELOG entry updated to cover the speedup. - docker-compose.ci.yml: 4 pgvector services + per-shard named volumes - scripts/ci-local.sh: parallel xargs orchestration + worktree mount fix - CHANGELOG.md v0.23.1: 4-way sharded wall-time, 36 E2E files, --no-shard flag * chore: regenerate llms-full.txt for v0.23.1 doc updates Required by test/build-llms.test.ts case 4 — committed llms-full.txt must match `bun run build:llms` output. The CHANGELOG + CLAUDE.md updates in this branch shifted bytes; regen catches up. * feat: scripts/run-unit-shard.sh + slow-test convention Tier 1 + Tier 4 plumbing: - scripts/run-unit-shard.sh: SHARD=N/M filter for unit files (excludes test/e2e/*). Excludes *.slow.test.ts (Tier 4 convention) so the fast shard fan-out skips known-slow files; CI's `bun run test` still includes them via default discovery. - scripts/run-slow-tests.sh: companion that runs ONLY *.slow.test.ts. Wired as `bun run test:slow`. - scripts/profile-tests.sh: portable awk parser that extracts the top-N slowest tests from any captured `bun test` output. Wired as `bun run test:profile`. Use it to pick demotion candidates. * feat: PGLite snapshot fixture for ~4.5x faster cold init (Tier 3) scripts/build-pglite-snapshot.ts boots a fresh PGLite, runs the full initSchema() (forward bootstrap + 30 migrations), and dumps the post-init state to test/fixtures/pglite-snapshot.tar plus a SHA-256 schema hash sidecar (.version). Both gitignored — built on demand via `bun run build:pglite-snapshot`. PGLiteEngine.connect() reads GBRAIN_PGLITE_SNAPSHOT env: validates the sidecar hash against the in-process MIGRATIONS hash, loads via PGLite's loadDataDir blob, sets _snapshotLoaded so initSchema() short-circuits. Measured per-file cold init drops from 828ms → 181ms. Bootstrap-correctness tests (bootstrap.test.ts, schema-bootstrap-coverage.test.ts) explicitly delete the env at file top so they keep exercising the cold path they verify. * feat: --classify-only + heartbeat tolerance fix (Tiers 2 + flake fix) - scripts/select-e2e.ts: --classify-only flag emits EMPTY|DOC_ONLY|SRC. Used by ci-local.sh's --diff fast-path to skip the heavy gate when only docs changed. - test/progress.test.ts: startHeartbeat tolerance widened to 1-20 over 200ms (was 2-6 over 85ms). Under 4-way parallel shard load on a contended host, setTimeout's effective quantum balloons and the tight bound flakes. The test still verifies "fires multiple times, stops cleanly" — exact count was never load-bearing. * feat: 4-way unit + E2E sharding in ci-local.sh + CHANGELOG (Tiers 1-4) ci-local.sh ties the four tiers together: - Tier 2: pre-flight diff classification on host. DOC_ONLY exits in ~5s (gitleaks only, no postgres, no container). - Tier 1: guards + typecheck run ONCE before fan-out. xargs -P4 then spawns 4 shards inside the runner container, each running unit phase (env -u DATABASE_URL bash run-unit-shard.sh) followed by E2E phase (DATABASE_URL=postgres-N bash run-e2e.sh) — both sharded N/4. Per-shard logs in /tmp/shard-logs/shard-N.log; printed in shard order at the end. - Tier 3: snapshot fixture built once at runner startup if missing, GBRAIN_PGLITE_SNAPSHOT exported so all shards inherit. - Tier 4: run-unit-shard.sh excludes *.slow.test.ts; run-slow-tests.sh + test:slow npm script handle the demoted set. - --no-shard preserves the legacy single-process flow for debug. package.json: build:pglite-snapshot, test:slow, test:profile scripts. Measured wall-time on 16-core host: 100s warm (down from ~22 min cold single-process). 4 shards × ~640-1024 unit tests each, plus 9 E2E files each. PGLite snapshot saves 4.5× per cold init (828ms → 181ms). CHANGELOG.md updated with measured numbers + four-tier breakdown. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |