Skip to content

feat: storage schema — import sessions, canonical transactions, merchant tables#93

Merged
kayodebristol merged 2 commits intomainfrom
copilot/design-storage-schema
Mar 25, 2026
Merged

feat: storage schema — import sessions, canonical transactions, merchant tables#93
kayodebristol merged 2 commits intomainfrom
copilot/design-storage-schema

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 25, 2026

Implements the full persistence schema described in the issue: nine database-aligned tables with in-memory stores (PluresDB/SQLite-ready via migration hooks), enforcing the core invariants of immutable raw data and append-only audit trail.

New package: packages/storage

Schema types (schema.ts) — one readonly interface per table, amounts as integer cents throughout:
ImportSessionRecord, RawTransactionRecord, CanonicalTransactionRecord, MerchantRecord, MerchantAliasRecord, AccountRecord, PostingRecord, RecurringSeriesRecord, ReviewDecisionRecord

Store implementations — one class per table:

  • RawTransactionStoreinsert()-only; throws on duplicate id. No update/delete exposed.
  • ReviewDecisionStorerecord()-only; throws on duplicate id. Full history queryable by entity.
  • ImportSessionStore — secondary index on fileHash for idempotent re-import detection.
  • CanonicalTransactionStoreupdate() for reviewer decisions (reviewed, category, merchantId, confidence); raw-tx uniqueness enforced.
  • MerchantAliasStore — supports exact | prefix | contains | regex match types.
  • PostingStore — date-range and canonical-transaction lookups.
  • RecurringSeriesStore — status/merchant/account filters with partial update().

Migration layer

MigrationRunner tracks applied versions and runs them in order, skipping already-applied ones. SCHEMA_MIGRATIONS ships v1 (all 9 tables) as a no-op for in-memory — the apply() hook is the integration point for future DDL:

// Ready for PluresDB/SQLite DDL — just fill in apply():
{ version: 1, description: 'Initial schema: import_sessions, ...', async apply() { /* CREATE TABLE ... */ } }

Factory

createStorageSchema() runs migrations then returns all stores as a typed StorageSchema — one instance per logical DB context.

const schema = await createStorageSchema();
schema.rawTransactions.insert(rawTx);                          // immutable
schema.canonicalTransactions.update(id, { reviewed: true });   // canonical layer
schema.reviewDecisions.record({ ...decision });                // audit trail

Build wiring

packages/storage added to build:packages (after domain) and to all test copy/mkdir steps in root package.json.

Original prompt

This section details on the original issue you should resolve

<issue_title>feat: storage schema — import sessions, canonical transactions, merchant tables</issue_title>
<issue_description>## Summary
Design and implement the storage schema that underpins the entire application. Current storage is likely too shallow.

Schema

-- Import provenance
import_sessions(id, file_hash, filename, account_id, imported_at, total_rows, parsed_rows, error_rows)

-- Raw imported data (never modified)
raw_transactions(id, import_session_id, row_number, date, amount_cents, description, raw_data_json)

-- Resolved/canonical transactions
canonical_transactions(id, raw_transaction_id, date, amount_cents, merchant_id, category, confidence, reviewed)

-- Merchant resolution
merchants(id, canonical_name, category, created_at, updated_at)
merchant_aliases(id, merchant_id, alias_pattern, match_type, created_by)

-- Double-entry ledger
accounts(id, name, type, institution, currency, created_at)
postings(id, date, debit_account_id, credit_account_id, amount_cents, canonical_transaction_id, memo)

-- Recurring detection
recurring_series(id, merchant_id, account_id, interval_days, avg_amount_cents, confidence, status)

-- Review decisions
review_decisions(id, entity_type, entity_id, decision, decided_at, previous_value, new_value)

Principles

  • Raw data is immutable — never modify imported rows
  • All mutations go through canonical layer
  • Every decision is logged in review_decisions
  • Use PluresDB if available, SQLite as fallback

Acceptance

  • Schema implemented with migrations
  • Raw transactions preserved immutably
  • All CRUD operations tested
  • Review decisions create audit trail</issue_description>

Comments on the Issue (you are @copilot in this section)


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

@github-actions github-actions Bot marked this pull request as ready for review March 25, 2026 04:42
Copilot AI review requested due to automatic review settings March 25, 2026 04:42
Copilot AI review requested due to automatic review settings March 25, 2026 04:42
Copilot AI requested review from Copilot and removed request for Copilot March 25, 2026 04:57
Copilot AI changed the title [WIP] Implement storage schema for import sessions and transactions feat: storage schema — import sessions, canonical transactions, merchant tables Mar 25, 2026
Copilot AI requested a review from kayodebristol March 25, 2026 05:00
@kayodebristol kayodebristol merged commit 87c9105 into main Mar 25, 2026
5 of 7 checks passed
@kayodebristol kayodebristol deleted the copilot/design-storage-schema branch March 25, 2026 06: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.

feat: storage schema — import sessions, canonical transactions, merchant tables

2 participants