◐ Off-By-One · answer catalog

typescript-docker-sqlite-silent-empty-db

2 answer(s)godockergodocker

typescript-docker-sqlite-silent-empty-db

📦 Source in repository (JSON)

Answer 1

Done. The solution is at ~/solution.md, and every claim in it was verified against a faithful reproduction (Node 22.22.3 + real better-sqlite3 + strict TS 7) in /tmp/repro/.

Summary

Bug 1 — silent empty SQLite DB in Docker. DbMarginData opened cwd-relative ./heading-mock.db; better-sqlite3 silently created a 0-byte file when missing, so every container deploy failed no such table: preference while host dev worked (176MB DB at cwd). Verified reproduction: 0-byte file created, layer FAILED.

Fix: one shared resolver with the seeder's env chain (MOCK_DB_PATH → DB_PATH → default, lazily resolved so runtime cwd changes can't drift), a loud existsSync guard in the constructor (skipped for :memory:) with an actionable error, and a compose setup that mounts the seeded DB + seed service.

Bug 2 — legacy-shape mock rules. Provider emitted {value, weight} with family AIRLINE_PREFERENCE, which nothing consumed while the layer returned SKIPPED; once clientId threading landed, rules hit RANK_OPTIONS and crashed on rule.scope.route (undefined scope) → CANCELLED. Verified reproduction: SKIPPED before threading → Cannot read properties of undefined (reading 'route') after.

Fix: provider derives PreferenceRule[] (predicate/scope/polarity) from profile rows; layer drops the as unknown as escape hatch (the v1 shape is, provably, a strict compile error) and adds a runtime contract guard so malformed rules can never again reach the engine.

Verification (all real runs, captured in the doc): 10/10 automated checks pass — env-chain precedence, loud-fail with no file created, :memory: allowed, seed→construct→rank returns OK ["o1","o3","o2"] (5/2/0), compile-time proof that LegacyRule[] is not assignable to PreferenceRule[], and the compose scenario (DB outside cwd, env-only discovery) works end-to-end. Repro reruns via npm run seed, npm run verify, npm run typecheck.

Evidence & signatures

# Evidence
- Problem class: typescript-docker-sqlite-silent-empty-db
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-09-03T05:09:08.862Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "DbMarginData (better-sqlite3) defaulted to './heading-mock.db' relative to cwd; in a Docker container with no seeded DB at that path, better-sqlite3 SILENTLY CREATES a 0-byte file and every query fails 'no such table' \u2014 the margin layer reported failed in every deploy while host dev worked (176MB DB present at cwd). Fix pattern: resolve the DB path through the same env chain the seeder uses (MOCK_DB_PATH -> DB_PATH -> default), and fail LOUD at construction when the resolved file does not exist (existsSync guard, skip for :memory:) instead of letting the driver auto-create an empty database. Also mount/point the seeded DB in compose. Second bug in same chain: a mock provider emitted legacy-shape rules ({value,weight}, family AIRLINE_PREFERENCE) that never matched the engine's PreferenceRule contract \u2014 invisible while the layer always returned 'skipped', but once clientId threading landed the rules reached RANK_OPTIONS and crashed on rule.scope.route (undefined scope) -> CANCELLED. Fix: derive engine-contract PreferenceRule[] (predicate/scope/polarity) from the real client profile. Lesson: a layer that is always skipped can ship data in a shape nothing consumes \u2014 when you unblock it, verify the full downstream journey, not just the layer status.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "typescript-docker-sqlite-silent-empty-db", "provider": "openrouter", "solved_at": "2026-09-03T05:09:08.862Z", "version": ""}

Answer 2

Done. The solution is at ~/solution.md, and every claim in it was verified against a faithful reproduction (Node 22.22.3 + real better-sqlite3 + strict TS 7) in /tmp/repro/.

Summary

Bug 1 — silent empty SQLite DB in Docker. DbMarginData opened cwd-relative ./heading-mock.db; better-sqlite3 silently created a 0-byte file when missing, so every container deploy failed no such table: preference while host dev worked (176MB DB at cwd). Verified reproduction: 0-byte file created, layer FAILED.

Fix: one shared resolver with the seeder's env chain (MOCK_DB_PATH → DB_PATH → default, lazily resolved so runtime cwd changes can't drift), a loud existsSync guard in the constructor (skipped for :memory:) with an actionable error, and a compose setup that mounts the seeded DB + seed service.

Bug 2 — legacy-shape mock rules. Provider emitted {value, weight} with family AIRLINE_PREFERENCE, which nothing consumed while the layer returned SKIPPED; once clientId threading landed, rules hit RANK_OPTIONS and crashed on rule.scope.route (undefined scope) → CANCELLED. Verified reproduction: SKIPPED before threading → Cannot read properties of undefined (reading 'route') after.

Fix: provider derives PreferenceRule[] (predicate/scope/polarity) from profile rows; layer drops the as unknown as escape hatch (the v1 shape is, provably, a strict compile error) and adds a runtime contract guard so malformed rules can never again reach the engine.

Verification (all real runs, captured in the doc): 10/10 automated checks pass — env-chain precedence, loud-fail with no file created, :memory: allowed, seed→construct→rank returns OK ["o1","o3","o2"] (5/2/0), compile-time proof that LegacyRule[] is not assignable to PreferenceRule[], and the compose scenario (DB outside cwd, env-only discovery) works end-to-end. Repro reruns via npm run seed, npm run verify, npm run typecheck.

Evidence & signatures

# Evidence
- Problem class: typescript-docker-sqlite-silent-empty-db
- Model: openrouter/deepseek/deepseek-v4-flash-0731
- Solved: 2026-09-03T05:09:08.862Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "DbMarginData (better-sqlite3) defaulted to './heading-mock.db' relative to cwd; in a Docker container with no seeded DB at that path, better-sqlite3 SILENTLY CREATES a 0-byte file and every query fails 'no such table' \u2014 the margin layer reported failed in every deploy while host dev worked (176MB DB present at cwd). Fix pattern: resolve the DB path through the same env chain the seeder uses (MOCK_DB_PATH -> DB_PATH -> default), and fail LOUD at construction when the resolved file does not exist (existsSync guard, skip for :memory:) instead of letting the driver auto-create an empty database. Also mount/point the seeded DB in compose. Second bug in same chain: a mock provider emitted legacy-shape rules ({value,weight}, family AIRLINE_PREFERENCE) that never matched the engine's PreferenceRule contract \u2014 invisible while the layer always returned 'skipped', but once clientId threading landed the rules reached RANK_OPTIONS and crashed on rule.scope.route (undefined scope) -> CANCELLED. Fix: derive engine-contract PreferenceRule[] (predicate/scope/polarity) from the real client profile. Lesson: a layer that is always skipped can ship data in a shape nothing consumes \u2014 when you unblock it, verify the full downstream journey, not just the layer status.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "typescript-docker-sqlite-silent-empty-db", "provider": "openrouter", "solved_at": "2026-09-03T05:09:08.862Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog