Skip to content

TRA-1: Config + Persistence Foundation - #1

Merged
nonnil merged 3 commits into
mainfrom
symphony/tra-1-config-persistence
May 9, 2026
Merged

TRA-1: Config + Persistence Foundation#1
nonnil merged 3 commits into
mainfrom
symphony/tra-1-config-persistence

Conversation

@nonnil

@nonnil nonnil commented May 9, 2026

Copy link
Copy Markdown
Member

Summary

Implements the TRA-1 config and persistence foundation:

  • Adds typed trauma.config.json loading and validation through src/server/config.
  • Resolves projectPath, storePath, and databasePath to absolute paths relative to the config file.
  • Enforces storePath inside projectPath and databasePath outside the markdown backup scope (storePath) before startup behavior proceeds.
  • Adds the Drizzle SQLite foundation schema, generated migration, and status CHECK constraints.
  • Adds DB initialization that creates the configured DB directory, enables SQLite pragmas, runs bundled migrations, and returns typed repository entrypoints.
  • Updates Playwright webServer startup to use build/start instead of the dev watcher for CI smoke tests.

Config Keys Implemented

  • storePath
  • projectPath
  • databasePath
  • backup.git.enabled
  • backup.git.remote
  • backup.git.branch
  • backup.git.push
  • backup.git.commitMessageTemplate

Schema Tables Added

  • memories
  • tags
  • categories
  • memory_tags
  • memory_categories
  • highlights

Migration Files Created

  • drizzle/0000_conscious_mikhail_rasputin.sql
  • drizzle/meta/0000_snapshot.json
  • updated drizzle/meta/_journal.json

Repository API Names

Future workers should use:

  • loadTraumaConfig() / validateTraumaConfig() from src/server/config
  • initializeDatabase() from src/server/db
  • createRepositories() from src/server/db
  • schema from src/server/db
  • repositories.memories.findById() as the initial repository skeleton

Verification

Initial handoff:

  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run test tests/server/config/config.test.ts tests/server/db/schema.test.ts -> passed, 2 files / 5 tests before implementation.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run db:generate -> passed; regenerated the initial migration after adding CHECK constraints.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run typecheck -> passed.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run test -> passed, 3 files / 7 tests.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run build -> passed.
  • GIT_DIR=.tmp/gitdir GIT_WORK_TREE=. git diff --check and git diff --check --cached -> passed with empty output.
  • Pre-push hook ran typecheck, test, and build successfully.
  • GitHub Actions CI / Verify -> passed, including baseline verification and E2E smoke tests.

Rework commits 4fea9a4 and e8107aa:

  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run test tests/server/config/config.test.ts tests/server/db/schema.test.ts -> failed before implementation for relative configPath with cwd and cwd-coupled migration lookup.
  • Same focused command -> passed, 2 files / 9 tests after implementation.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run test tests/server/db/schema.test.ts -> failed before the lifecycle fix with closeCalls: 0, then passed, 1 file / 5 tests after closing SQLite on initialization failure.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run typecheck -> passed.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run test -> passed, 3 files / 11 tests.
  • MISE_TRUSTED_CONFIG_PATHS="$PWD/mise.toml" mise exec -- bun run build -> passed.
  • GIT_DIR=.tmp/gitdir GIT_WORK_TREE=. git diff --check -> passed with empty output.
  • Pre-push hook ran typecheck, test, and build successfully after each pushed rework commit.

Review Follow-up

  • Added DB-level CHECK constraints for status fields.
  • Made config read errors distinguish missing files from other read failures.
  • Removed the unsupported Node/sqlite-proxy DB fallback; runtime DB initialization is Bun SQLite only.
  • Applied bundled migrations before repositories are returned.
  • Validated databasePath against storePath rather than the whole git worktree.
  • Wired drizzle.config.ts to TRAUMA_DATABASE_PATH or trauma.config.json databasePath.
  • Added regression coverage for relative configPath plus cwd, migrations independent of launch cwd, DB rejection of invalid memory status values, and SQLite handle closure on initialization failure.
  • Replied to all visible unresolved review threads.

Notes

  • Direct writes to the Symphony workspace .git were blocked, so git operations used the in-workspace .tmp/gitdir workaround. .tmp/ was not staged.
  • Untracked package-lock.json existed before implementation and was intentionally left out of scope.

@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

PR introduces a complete persistence foundation: TypeScript configuration system for validating Drizzle ORM paths and backup settings, SQLite schema defining six tables for memories with extraction/backup status tracking, database connection initialization with Bun SQLite runtime selection, repository pattern for type-safe queries, Drizzle migrations, and comprehensive tests validating configuration constraints and database operations.

Changes

Foundation Infrastructure

Layer / File(s) Summary
Configuration Types & Errors
src/server/config/types.ts, src/server/config/errors.ts
Defines TraumaConfig, ResolvedTraumaConfig, ConfigValidationResult (discriminated union), and TraumaConfigError custom error class.
Config Loading & Path Validation
src/server/config/load.ts
Implements loadTraumaConfig to resolve and parse config files, validateTraumaConfig with field/type checking, and helpers for path resolution, nested field reading, and path containment validation.
Config Module Export
src/server/config/index.ts
Barrel re-exports error class, loader/validator functions, and all config types.
Database Schema Definitions
src/server/db/schema.ts
Defines ExtractionStatus and BackupStatus unions, timestamps helper, six SQLite tables with composite keys, cascading foreign keys, check constraints on status values, and Drizzle relations mappings.
Repository Interfaces & Factory
src/server/db/repositories.ts
Exports TraumaDatabase type, MemoryRepository with findById query, TraumaRepositories container, and createRepositories factory wiring Drizzle queries.
Database Connection Initialization
src/server/db/connection.ts
Implements initializeDatabase to create SQLite file, enable foreign keys/WAL, load Bun SQLite constructor with error handling, build Drizzle instance, optionally run migrations, and return connection wrapper.
Database Module Export
src/server/db/index.ts
Barrel re-exports initializeDatabase, createRepositories, database types, schema tables, and bundled schema object.
SQL Migration & Metadata
drizzle/0000_conscious_mikhail_rasputin.sql, drizzle/meta/0000_snapshot.json, drizzle/meta/_journal.json
Creates six tables with indexes and cascading deletes; records migration in Drizzle snapshot and journal.
Configuration Resolution & Integration
drizzle.config.ts, playwright.config.ts
Updates Drizzle config to resolve database path from env var or trauma.config.json; updates Playwright to run production build before tests.
Configuration Tests
tests/server/config/config.test.ts
Validates loadTraumaConfig: valid config with path resolution, relative configPath handling, invalid JSON errors, and path containment constraints.
Database & Schema Tests
tests/server/db/schema.test.ts
Validates schema exports, database initialization with migrations from different working directories, check constraint validation, and Bun script execution helpers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A schema blooms in SQLite's glade,
With memories stored and backups made,
Config validated, paths align,
Repositories query in perfect line,
Tests ensure our foundation's sound,
The database dream is homeward bound!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'TRA-1: Config + Persistence Foundation' directly summarizes the PR's main objective: implementing configuration loading/validation and database persistence setup.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch symphony/tra-1-config-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nonnil
nonnil force-pushed the symphony/tra-1-config-persistence branch from 34f14b0 to bd689a2 Compare May 9, 2026 06:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
tests/server/config/config.test.ts (1)

56-69: ⚡ Quick win

Add a regression test for databasePath containment rule

This suite should also assert rejection when databasePath is inside projectPath, since that’s a core invariant enforced by validateTraumaConfig.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/server/config/config.test.ts` around lines 56 - 69, Add a regression
test mirroring the existing "rejects storePath outside projectPath" case but for
databasePath: use createTempRoot() and writeConfig(...) to set projectPath:
"./data" and databasePath: "./data/.trauma/trauma.sqlite" (or any path that
resolves inside projectPath), then call loadTraumaConfig({ configPath }) and
assert it throws (e.g. expect(() => loadTraumaConfig({ configPath
})).toThrow(...)). Place the test alongside the existing one and reference
loadTraumaConfig, validateTraumaConfig, writeConfig, and createTempRoot so the
containment rule for databasePath is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@drizzle/0000_rapid_maria_hill.sql`:
- Around line 31-34: The migration currently defines extraction_status and
backup_status as unconstrained text columns; update the create table statement
in this migration to add DB-level CHECK constraints (or an ENUM type) that
restrict extraction_status and backup_status to the allowed set of values used
by the code (e.g., 'pending','running','failed','complete' or whatever the repo
expects). Modify the column definitions for `extraction_status` and
`backup_status` to include the CHECK (...) clauses (or create/use a named ENUM
and use that type) so invalid values cannot be persisted, and ensure any
INSERTs/defaults in this migration conform to those allowed values.

In `@src/server/config/load.ts`:
- Around line 24-27: The catch block currently throws TraumaConfigError with a
fixed "Missing trauma config" title for any I/O failure; change it to produce a
more accurate top-level message (e.g., "Failed to load trauma config") or branch
on error properties (errno/code) to distinguish ENOENT vs permission/other
errors, and include configPath plus formatUnknownError(error) in the details;
update the throw of TraumaConfigError at the catch in load (referencing
TraumaConfigError, configPath, and formatUnknownError) so the main message
reflects the actual failure type while keeping the detailed array entry with
formatted error information.

In `@src/server/db/connection.ts`:
- Around line 29-45: initializeDatabase currently opens the SQLite file and
returns repositories without applying the bundled Drizzle schema/migrations,
causing "no such table" errors on a fresh DB; modify initializeDatabase (before
calling createRepositories) to apply the shipped schema or Drizzle
migrations—e.g., load and execute the packaged SQL schema or invoke the Drizzle
migration runner (or a migrate/applyMigrations helper) against the sqlite
instance/createDrizzleDatabase(db) so all tables (like memories) are created
before repositories are constructed and returned.
- Around line 49-60: The loadDatabaseConstructor function currently
unconditionally falls back to require("node:sqlite"), which throws
ERR_UNKNOWN_BUILTIN_MODULE on Node < v22.5.0; update loadDatabaseConstructor to
detect Node version (via process.versions.node or a semver check) before trying
the "node:sqlite" builtin and, for older Node versions, import a userland SQLite
module (e.g., require("better-sqlite3") or require("sqlite3") with a compatible
API) or throw a clear error; ensure you reference the same return type
(SQLiteDatabase) and the require("bun:sqlite") path remains first, only
attempting the builtin "node:sqlite" when Node >= 22.5.0.
- Around line 63-87: The fallback branch in createDrizzleDatabase incorrectly
casts the sqlite-proxy driver to BunSQLiteDatabase; remove the "as unknown as
BunSQLiteDatabase" cast and instead let the require("drizzle-orm/sqlite-proxy")
return its actual type (SqliteRemoteDatabase) so the function's return type
reflects the async proxy driver; update the function signature or adjust callers
that rely on Bun-specific behavior (references: createDrizzleDatabase, drizzle
from "drizzle-orm/sqlite-proxy", BunSQLiteDatabase, SqliteRemoteDatabase,
schema, SQLiteDatabase) to accept the proxy's proper type rather than forcing a
Bun driver cast.

In `@src/server/db/schema.ts`:
- Around line 30-35: The TypeScript-only unions on extractionStatus and
backupStatus (fields extraction_status and backup_status) do not enforce allowed
values at the SQLite level; add persistent CHECK constraints on those columns so
the DB rejects invalid strings (e.g., a CHECK that column IN (...allowed literal
values from ExtractionStatus/BackupStatus)); implement this via your schema
builder's check/constraint API for the extractionStatus and backupStatus columns
and add a migration that either converts/cleans or rejects existing invalid rows
before applying the constraint.

---

Nitpick comments:
In `@tests/server/config/config.test.ts`:
- Around line 56-69: Add a regression test mirroring the existing "rejects
storePath outside projectPath" case but for databasePath: use createTempRoot()
and writeConfig(...) to set projectPath: "./data" and databasePath:
"./data/.trauma/trauma.sqlite" (or any path that resolves inside projectPath),
then call loadTraumaConfig({ configPath }) and assert it throws (e.g. expect(()
=> loadTraumaConfig({ configPath })).toThrow(...)). Place the test alongside the
existing one and reference loadTraumaConfig, validateTraumaConfig, writeConfig,
and createTempRoot so the containment rule for databasePath is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a34e51f-04a7-48bf-993b-6586a94b5a83

📥 Commits

Reviewing files that changed from the base of the PR and between cecd7ab and 34f14b0.

📒 Files selected for processing (13)
  • drizzle/0000_rapid_maria_hill.sql
  • drizzle/meta/0000_snapshot.json
  • drizzle/meta/_journal.json
  • src/server/config/errors.ts
  • src/server/config/index.ts
  • src/server/config/load.ts
  • src/server/config/types.ts
  • src/server/db/connection.ts
  • src/server/db/index.ts
  • src/server/db/repositories.ts
  • src/server/db/schema.ts
  • tests/server/config/config.test.ts
  • tests/server/db/schema.test.ts

Comment thread drizzle/0000_conscious_mikhail_rasputin.sql
Comment thread src/server/config/load.ts
Comment thread src/server/db/connection.ts Outdated
Comment thread src/server/db/connection.ts
Comment thread src/server/db/connection.ts Outdated
Comment thread src/server/db/schema.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 34f14b0cb5

ℹ️ 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".

Comment thread src/server/db/connection.ts Outdated
Comment thread src/server/config/load.ts Outdated
Comment thread src/server/db/connection.ts Outdated
Comment thread src/server/config/types.ts
@nonnil
nonnil force-pushed the symphony/tra-1-config-persistence branch from bd689a2 to c2bfac7 Compare May 9, 2026 06:29
@nonnil
nonnil force-pushed the symphony/tra-1-config-persistence branch from c2bfac7 to 2e3336d Compare May 9, 2026 06:33
@nonnil

nonnil commented May 9, 2026

Copy link
Copy Markdown
Member Author

review @claude @copilot

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the TRA-1 foundation for config loading/validation and SQLite persistence, establishing the initial Drizzle schema + migration and wiring up database initialization and CI E2E startup behavior.

Changes:

  • Added typed trauma.config.json loading + validation with path resolution and safety constraints (storePath within projectPath, databasePath outside storePath).
  • Introduced Drizzle SQLite foundation schema, initial migration artifacts, and a minimal repository layer with DB initialization/migrations.
  • Updated Playwright webServer command to build + start (instead of dev) for CI smoke tests.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/server/db/schema.test.ts Adds unit tests asserting schema exports and a smoke check that DB initialization + migrations create the expected tables.
tests/server/config/config.test.ts Adds unit tests for config loading, JSON parse errors, and path constraint validation.
src/server/db/schema.ts Defines the initial Drizzle SQLite schema (tables, indexes, relations) and status CHECK constraints.
src/server/db/repositories.ts Introduces a typed createRepositories entrypoint with an initial memories.findById method.
src/server/db/index.ts Provides DB module exports and a schema table registry for consumers/tests.
src/server/db/connection.ts Implements Bun SQLite-backed DB initialization, pragmas, migrations, and repository wiring.
src/server/config/types.ts Adds types for the trauma config shape and resolved runtime config.
src/server/config/load.ts Implements config file discovery/loading, JSON parsing, validation, and path resolution.
src/server/config/index.ts Public barrel exports for config load/validate/types/errors.
src/server/config/errors.ts Adds a dedicated TraumaConfigError carrying structured issues.
playwright.config.ts Switches Playwright webServer to build && start for CI stability.
drizzle/meta/0000_snapshot.json Records Drizzle schema snapshot metadata for the initial migration.
drizzle/meta/_journal.json Records Drizzle migration journal entries.
drizzle/0000_conscious_mikhail_rasputin.sql Adds the generated initial SQL migration for the foundation schema.
drizzle.config.ts Resolves DB path for Drizzle Kit via env var or trauma.config.json (fallback default).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/server/config/load.ts Outdated
Comment thread src/server/db/connection.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server/db/connection.ts`:
- Around line 46-61: The DB handle `sqlite` is opened before several operations
that can throw (`sqlite.exec`, `createDrizzleDatabase`, `applyMigrations`,
`createRepositories`), so if any fail the file descriptor is leaked; wrap the
initialization sequence in a try/catch (declare `sqlite` in an outer scope), and
in the catch ensure you call `sqlite.close()` if `sqlite` is truthy before
rethrowing the error; apply this change around `loadDatabaseConstructor`, `new
Database(...)`, `sqlite.exec(...)`, `createDrizzleDatabase`, `applyMigrations`,
and `createRepositories`, returning or throwing only after the handle is closed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1d59d46-1535-47ce-b6d8-d4ce88db4c74

📥 Commits

Reviewing files that changed from the base of the PR and between 34f14b0 and 4fea9a4.

📒 Files selected for processing (15)
  • drizzle.config.ts
  • drizzle/0000_conscious_mikhail_rasputin.sql
  • drizzle/meta/0000_snapshot.json
  • drizzle/meta/_journal.json
  • playwright.config.ts
  • src/server/config/errors.ts
  • src/server/config/index.ts
  • src/server/config/load.ts
  • src/server/config/types.ts
  • src/server/db/connection.ts
  • src/server/db/index.ts
  • src/server/db/repositories.ts
  • src/server/db/schema.ts
  • tests/server/config/config.test.ts
  • tests/server/db/schema.test.ts
✅ Files skipped from review due to trivial changes (5)
  • playwright.config.ts
  • src/server/config/index.ts
  • tests/server/config/config.test.ts
  • src/server/config/types.ts
  • drizzle/meta/0000_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/server/config/errors.ts
  • drizzle/meta/_journal.json
  • src/server/db/index.ts
  • src/server/db/repositories.ts
  • src/server/db/schema.ts

Comment thread src/server/db/connection.ts Outdated

@nonnil nonnil left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@nonnil
nonnil merged commit 2bde0bf into main May 9, 2026
2 checks passed
@nonnil
nonnil deleted the symphony/tra-1-config-persistence branch May 9, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants