Repository navigation
[v3.8.50] refactor(db): add combo repository boundary - #8757
diegosouzapw merged 3 commits into
Conversation
⏸️ Ejetada do merge-train v3.8.49 — triagem de cherrypick pendenteEsta PR foi ejetada do merge-train local (30→24 PRs) porque sua pegada no diff excede o orçamento seguro de validação em lote (>700 linhas ou nova fronteira arquitetural). Não é rejeição — é adiamento com motivo rastreável. Motivo da ejeção
Como destravar (sugestão do operador)O operador pediu que toda PR ejetada seja revisada para possível cherrypick. Providências sugeridas por categoria:
Próximos passos
|
|
Thanks for the explicit triage and for keeping the PR traceable. I agree that this boundary should not enter through the batch merge train without the one-to-one architecture review. I audited the current exact head specifically against Hard Rule #2:
I added the exact dependency map and review anchors to the PR description so the one-to-one review does not need to infer the boundary from the nine-file diff. For cherrypick triage, I do not currently see a smaller independently buildable subset: the contract, composition binding, SQLite adapters, compatibility facades, and conformance tests form one behavior-preserving vertical slice. I am happy to split or reshape it if the preferred cut line differs after review. One sequencing question to avoid invalidating the frozen-tree evidence twice: should I rebase this Draft onto the current v3.8.50 release target now, or keep this exact head stable until the Hard Rule #2 boundary review is complete and then rebase once against the confirmed target? |
|
Re-homed to |
Merge the current release/v3.8.50 base without changing the validated repository slice.
|
The branch is now refreshed onto the current Fresh validation on the resulting tree:
This is ready for the one-to-one Hard Rule #2 architecture review requested above. To make the review decision concrete, could you confirm one of these directions?
I will not expand the upstream scope into an external database backend until this boundary decision is made. |
…flict resolution) Resolved conflict in src/lib/db/combos.ts: kept HEAD (repository-based getCombos) over base's inline SQLite implementation, which the PR refactors behind the ComboRepository interface.
|
Merge conflict resolved (2026-08-06) Conflict in
Resolution: kept HEAD (repository-based approach), since the PR's entire purpose is the combo repository boundary refactor. Note: This PR inherits base-red issue #9298 (migration collision at versions 135 and 136). All 6 PR test files load correctly but cannot pass due to the base issue. Typecheck running. |
5f47118
into
cheaper-inference:release/v3.8.50
…nce#8075) Add a dialect-agnostic DatabaseBackend abstraction that decouples domain modules from the storage engine. SQLite remains the default (zero-config); PostgreSQL and MySQL are opt-in for cluster/centralized deployments via DATABASE_DRIVER env var. Scope: PR4 equivalent (backend operational skeleton) per the staged plan in issue cheaper-inference#8075. Domain repository contracts (PR3) already exist upstream from cheaper-inference#8757. Settings/cleanup/API route migrations are deferred to later slices. Core: - DatabaseBackend interface with async query/execute/transaction/healthCheck - SqliteBackend wrapping existing better-sqlite3 (zero behavioral change) - PostgresBackend with pg connection pooling and SQL translation - MysqlBackend with mysql2 connection pooling and SQL translation - SqlDialect compiler: translates SQLite-flavored SQL (positional ? params, INSERT OR REPLACE, PRAGMA table_info, AUTOINCREMENT, BLOB) to Postgres/MySQL - Schema registry mapping table PKs for dialect-aware upsert translation - Lazy singleton factory (DATABASE_DRIVER=sqlite|postgres|mysql, default sqlite) - Config via DATABASE_URL or individual DATABASE_HOST/PORT/NAME/USER/PASSWORD - Automatic fallback to SQLite if external backend connection fails PR4 additions: - Classified persistence errors (PersistenceError with retryable/non-retryable codes) - Migration lock contract (process-local, seam for future distributed lock) Migration: - Dialect-aware migration runner for external backends (reads same .sql files) - Offline SQLite → PostgreSQL/MySQL export tool (exportSqliteToDialect) - Migrations tracked in _omniroute_migrations table Tests (90 passing): - SqliteBackend: query, queryOne, execute, transactions, rollback, health - SqlDialect: placeholder conversion, INSERT OR REPLACE/IGNORE translation, PRAGMA table_info, DDL type mapping (AUTOINCREMENT→SERIAL, BLOB→BYTEA) - PersistenceError: classification of 14 error patterns (transient, constraint, auth, migration) - Backend conformance: timestamps, JSON round-trip, affected rows, NULL handling, transaction atomicity, backend identity - Combo repository conformance: CRUD, pagination, ordering, uniqueness, reorder, delete isolation, case-insensitive lookup Env vars documented in .env.example. pg + mysql2 added as optional deps.
Validated in local merge-train (tomni-proxmox-113)
Summary
getCombosCount()API through an explicit compatibility binding in the composition root; all other repository operations remain async internally.Related Issues
Validation
Run only the focused loop for what you changed — the full unit suite, Vitest, the
60% coverage gate, and the production build all run in CI on this PR (#8329):
npm run lint— the changed files pass ESLint and Prettier; the repository-level static gates listed below also passAdditional local gates passed:
npm run typecheck:coreprettier --checknpm run check:cyclesnpm run check:db-rulesgit diff --check upstream/release/v3.8.50...HEADTests Added Or Updated
tests/helpers/persistence/comboRepositoryConformance.ts.tests/unit/db/repositories/sqliteComboRepositories.test.ts.tests/unit/combo-cache-invalidation.test.tsto characterize cache invalidation, backups, affected-row behavior, reorder behavior, and malformed-row behavior across the facade/repository boundary.Coverage Notes
Reviewer Notes
release/v3.8.50and requested one-to-one review under Hard Rule build(deps): bump actions/github-script from 7 to 8 #2. The branch has now been refreshed without force-push; no external-backend work will be stacked upstream until this boundary is accepted or reshaped.release/v3.8.50@7163081f5ed2a2104e85c31bfd1588033d43c580; refreshed head:abe7ea7026516f4751ff9338534b4b140ec7877c; validated tree:e3688ca5451aa2f2afbe73630c74cc8c29c4ee29.active_comboruntime state remains outside this bounded slice. The facade retains its existing direct SQLite write for that separate state; combo definition and model-mapping consumers no longer import concrete repository adapters.prepare()occurrences; this PR establishes the boundary rather than claiming repository-wide coupling reduction.Hard Rule #2 /
localDb.tsreview mapThis PR keeps
src/lib/localDb.tsas a re-export-only compatibility layer:src/lib/localDb.tsis byte-for-byte unchanged from the pinned base to this PR head (the Git blob is70a9dee74f26d8b1160afa86faedbb8047ac9701at both the refreshed base and head).Its existing re-exports for
db/combosanddb/modelComboMappingsremain unchanged; no repository construction, SQL, or business logic is added there.Dependency direction is explicit:
The portable contracts live in
src/domain/persistence/comboRepositories.tsand contain no SQLite dependency.Concrete SQLite adapters are imported only by
routingConfigRepositories.ts; the facades and application consumers do not import them directly.Raw SQL for the selected combo/mapping slice lives in the SQLite adapters. The pre-existing
setActiveCombo()write remains indb/combos.tsbecause active runtime selection is explicitly outside this bounded slice; no part of it is moved intolocalDb.ts.npm run check:db-rulespasses on refreshed headabe7ea7026516f4751ff9338534b4b140ec7877c:OK (110 db modules, 73 re-exported, 37 intentionally internal; Rule #2).The nine-file change is one bounded vertical slice: contract, SQLite implementation, composition binding, compatibility facades, and executable conformance evidence. I do not see a smaller independently buildable cherry-pick subset that would preserve behavior and retain the tests. If the preferred review shape is different, I can split or reshape it after the one-to-one boundary review rather than guessing the cut line.