feat(doctor): detect static-with-DB drift + dark (undeclared) databases - #183
Merged
Conversation
Adds failing tests for two new doctor checks: 1. checkStaticDbDrift (offline/local): fires when kind=static + any DB field declared on an already-registered AppRecord (mirrors P1 predicate as detection for existing drift, not a new registration gate). 2. parseDarkDbOutput (on-VM SSH): fires when pg_database/pg_roles shows an app DB/role not declared by the AppRecord (dbBackend absent/none). Fail-safe on probe error → unknown. Both checks integrate into auditVm/runFleetDoctor and the fleet failingVms counter. Tests cover: fires / does-not-fire / fail-safe / single-SSH invariant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds two doctor checks that surface existing managed-DB drift across the fleet: 1. static-db-drift (offline, record-based) — src/doctor/static-db-drift.ts Detects AppRecords where kind=static but DB fields (migrateCmd, dbBackend, previewDbBackend, databaseUrlEnv, envDbVars) are set. These migrations NEVER run in the static serve path. Same predicate as P1's validateStaticNoDb but as DETECTION for already-registered apps. Emits status=fail per offending app, status=pass when fleet is clean. 2. dark-db (on-VM SSH probe, appDbCheckTemplates) — checks.ts + doctor.ts Extends the EXISTING single-SSH auditVm batch with a sudo -u postgres psql probe that lists non-system databases and roles on the VM. parseDarkDbOutput fires when dbBackend is absent/none yet a real app DB or role exists (dark, hand-installed Postgres outside the managed dblab flow). Fail-safe on probe error → unknown so the sweep continues. System DBs (postgres/template0/ template1) and system roles (pg_* prefix + postgres) are excluded. Integration: - static-db-drift: injected as LOCAL-ONLY check in runFleetDoctor per-VM LOCAL checks block (same pattern as dblab-not-oversized). Participates in failingVms counter and JSON output. - dark-db: added to appDbCheckTemplates (checks.ts); wired via existing evaluateDoctorCheck dispatch in doctor.ts (passes app to parser for dbBackend/databaseUrlEnv awareness). Static apps are NOT skipped for dark-db — detecting a dark DB on a static app is exactly the gamechangers scenario. All 33 new tests GREEN; zero regressions vs base (47→46 pre-existing failures). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parseDarkDbOutput declared databaseUrlEnv as a parameter but never read it, triggering TS6133 in CI. Now incorporated into the recommendation message when set, giving the operator a hint about which env var to configure when folding the dark DB into the managed flow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collaborator
Author
samorev verdict: PASSGate status
Reconciliation (build-agent local 46-fail vs CI 0-fail)Confirmed LOCAL-ENVIRONMENT artifact, NOT a regression. CI runs the full suite with a Findings (all confirmations, no blockers)
Suggested next steps (non-blocking)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Check 1:
static-db-drift(offline, record-based) —src/doctor/static-db-drift.tsDetects
AppRecords wherekind=staticbut DB fields (migrateCmd,dbBackend,previewDbBackend,databaseUrlEnv,envDbVars) are set. These migrations NEVER run in the static serve path. This is the DETECTION mirror of P1'svalidateStaticNoDbregistration gate — surfaces already-registered apps (pre-P1 drift). Onefailresult per offending app;passwhen the fleet is clean.Check 2:
dark-db(on-VM SSH, folds into existing single-SSH batch) —checks.ts+parseDarkDbOutputindoctor.tsExtends
appDbCheckTemplateswith asudo -u postgres psqlprobe listing non-system databases and roles on the VM. Fires whendbBackendis absent/"none"yet real app databases or roles exist (hand-installed Postgres outside the managed dblab flow). Fail-safe on probe error →unknown. System DBs (postgres/template0/template1) and system roles (pg_*prefix +postgres) are excluded at SQL query time and defensively in the parser.Single-SSH invariant preserved:
dark-dbis folded into the existingbuildAuditScript/parseAuditOutputbatch inauditVm. No separate SSH round-trip added.Static apps NOT skipped for
dark-db: detecting a hand-installed Postgres on akind=staticVM is precisely the gamechangers scenario.STATIC_APP_SKIP_IDSdeliberately excludesdark-db.static-db-driftinrunFleetDoctor: injected in the LOCAL-ONLY checks block per-VM (same pattern asdblab-not-oversized). Always emits a result (pass or fail) so it appears in JSON output and participates infailingVmscounter.Root-cause analysis
P1 (#181) blocks NEW registrations with the
kind=static+ DB-fields combo. But existing apps (e.g. gamechangers) that were registered before P1 are already drifted: they appear askind=staticwithmigrateCmd/dbBackendset, and their migrations have never run in eitherenv-createor prod-deploy (both static paths have no migrate phase). P2 (#182) ensures new onboarding defaults tonode+DB. These two checks close the detection gap for the pre-existing drift.Test plan
test/doctor-drift-checks.test.ts(33 tests, all GREEN):unknownon probe errorauditVmandrunFleetDoctor: correctdark-dbresult in per-VM checks,failingVmscounter correct, single-SSH invariant (1 call)static-db-driftper-VM with correct pass/failNo regressions: 1209 pass vs 1176 pass on base; 46 fail vs 47 fail on base (pre-existing failures identical, our changes actually reduce count by 1).
🤖 Generated with Claude Code