Fix Prisma schema detection bugs

Census breaks on first Prisma schema match per root, starving scan-engine's best-match scorer. Three bugs share one disease: census assumes every Prisma project has exactly one `schema.prisma` at a hardcoded path. This breaks dual-candidate roots, multi-file schemas (`prismaSchemaFolder`, GA since Prisma 5.15), and provider detection in split-file layouts.

verdict PASSscore 13 / 13findings 5 (1 risk · 1 debt · 3 obs)duration 28mrejection cycles 0shipped Apr 24, 2026surface cli

Pipeline timeline

Intent to proven code in 28m across Think, Plan, Build, and Verify.

Think
9m
Plan
9m
Build
14m
Verify
5m

Assertion ledger

13 claims, each independently verified. Showing 8 — show all →

IDSaysMatcher
A001When two schema files exist in the same project, the scanner picks the one with more data modelsverifiedok
A002The scanner identifies the correct file path when multiple schema candidates competeverifiedok
A003Dual schema candidates are both considered — the weaker one doesn't block the strongerverifiedok
A004A multi-file Prisma schema without a traditional anchor file is still detectedverifiedok
A005Model counting works across multiple schema files in a directoryverifiedok
A006The database provider is detected even in a multi-file schema layoutverifiedok
A007No false alarm fires when a multi-file schema is successfully detectedverifiedok
A008The database provider is found even when it lives in a supporting file, not the main schemaverifiedok

Findings 5 total

riskpackages/cli/src/engine/scan-engine.tsclosed
First-provider-wins with no conflict detection: `scan-engine.ts:313` and `scan-engine.ts:337` — both directory and sibling handlers use `if (!provider)` to take the first datasource block found. If a `.prisma` directory contains conflicting provider declarations (theoretically invalid Prisma, but possible in a broken project), the scanner silently picks whichever file `readdir` returns first. File system ordering is non-deterministic on some platforms. Acceptable because Prisma itself rejects multiple datasource blocks, but the scanner's behavior would be platform-dependent on malformed input.
obspackages/cli/src/engine/census.tsclosed
Census directory check is non-recursive: `census.ts:219` — `readdirSync(prismaDir)` only checks top-level entries. The second fallback glob `/prisma/*.prisma` in `scan-engine.ts:289` is also one level deep. A `prisma/subdir/models.prisma` layout would be missed by both paths. No known Prisma project uses nested subdirectories within the Prisma folder, but this is a documented gap.
obspackages/cli/tests/engine/scanProject.test.tsclosed
SQL-only edge case tests indirect path: `scanProject.test.ts:210` — The test creates `prisma/migrations/001_init.sql`. Census's `readdirSync('prisma/')` sees `['migrations']` — a directory name, not a `.sql` file. The test passes because no direct `.prisma` files exist in `prisma/`, not because it explicitly filters `.sql` files. A more direct test would place `.sql` files directly in `prisma/` (e.g., `prisma/seed.sql`). The current test still validates the contract (A010, A011) correctly, but tests the directory-name-filter path, not the extension-filter path.
debtpackages/cli/tests/engine/scanProject.test.tsclosed
A009 uses weak matcher when specific value is known: `scanProject.test.ts:199` — `toBeGreaterThan(0)` when the test fixture has exactly 2 models. The contract specifies `greater: 0`, so the test matches the contract. But `toBe(2)` would catch regressions that `toBeGreaterThan(0)` wouldn't (e.g., if sibling counting broke and only returned 1 model).
obsclosed
A002 uses `contains` matcher, allows ambiguous match: Contract A002 says `target: schemas.prisma.path, matcher: contains, value: "schema.prisma"`. Both candidates (`prisma/schema.prisma` and `schema.prisma`) contain `"schema.prisma"`. The test passes regardless of which candidate wins — it can't distinguish them. The modelCount assertion (A001) indirectly proves the right candidate won, but A002 alone doesn't verify path correctness. A more precise value like `"prisma/schema.prisma"` would be unambiguous.

Integrity seal

scopesha256:2ef27097fdc2c...
contractsha256:a57a2d44cb2d1...
plansha256:6f4b7eb636d25...
specsha256:792be65bc3b49...
build-reportsha256:a43dffe54a336...
verify-reportsha256:fe777fda9bb27...
audit cmd$ ana proof audit fix-prisma-schema-detection   → all hashes match