9.0 KiB
Approach
- Read existing files before writing. Don't re-read unless changed.
- Thorough in reasoning, concise in output.
- Skip files over 100KB unless required.
- No sycophantic openers or closing fluff.
- No emojis or em-dashes.
- Do not guess APIs, versions, flags, commit SHAs, or package names. Verify by reading code or docs before asserting.
MCP Tools: code-review-graph
IMPORTANT: This project has a knowledge graph. ALWAYS use the code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore the codebase. The graph is faster, cheaper (fewer tokens), and gives you structural context (callers, dependents, test coverage) that file scanning cannot.
When to use graph tools FIRST
- Exploring code:
semantic_search_nodesorquery_graphinstead of Grep - Understanding impact:
get_impact_radiusinstead of manually tracing imports - Code review:
detect_changes+get_review_contextinstead of reading entire files - Finding relationships:
query_graphwith callers_of/callees_of/imports_of/tests_for - Architecture questions:
get_architecture_overview+list_communities
Fall back to Grep/Glob/Read only when the graph doesn't cover what you need.
Key Tools
| Tool | Use when |
|---|---|
detect_changes |
Reviewing code changes — gives risk-scored analysis |
get_review_context |
Need source snippets for review — token-efficient |
get_impact_radius |
Understanding blast radius of a change |
get_affected_flows |
Finding which execution paths are impacted |
query_graph |
Tracing callers, callees, imports, tests, dependencies |
semantic_search_nodes |
Finding functions/classes by name or keyword |
get_architecture_overview |
Understanding high-level codebase structure |
refactor_tool |
Planning renames, finding dead code |
Workflow
- The graph auto-updates on file changes (via hooks).
- Use
detect_changesfor code review. - Use
get_affected_flowsto understand impact. - Use
query_graphpattern="tests_for" to check coverage.
Local environment / running tests
.envholds production credentials (o2switch host, real DB name/password). Never load it for local runs or tests..env.localholds the local dev DB credentials (DB_HOST=127.0.0.1,DB_USER=convert_user,DB_NAME=file_converter,DB_PASSWORD=change_me). This is what local testing should use.src/config.jsusesdotenv/config, which only loads.envand does not override variables already present inprocess.env. There's no vitest/dotenv wiring that picks up.env.localautomatically.- To run tests locally without touching prod config, pass the
.env.localvalues as inline env vars so they take precedence beforedotenv/configruns, e.g.:DB_HOST=127.0.0.1 DB_USER=convert_user DB_PASSWORD=change_me DB_NAME=file_converter STORAGE_DIR=./storage PORT=3000 npx vitest run - A local MariaDB is expected to already be running on
127.0.0.1:3306with thefile_converterDB andconvert_usercredentials seeded. - Known pre-existing failures unrelated to any fix:
test/cleanup.test.js("deletes an expired pending job...") andtest/jobs/jobRepository.test.js("finds expired jobs and allows deleting them") — both fail onmainindependent of other changes (looks like a clock/timezone mismatch aroundexpiresAtcomparisons, not yet root-caused). Don't assume a change caused these; verify againstmainfirst if they show up again.
Deployment (o2switch)
- o2switch is shared hosting: no compiler toolchain, no root access. Any dependency with a native/binary component must ship as a precompiled binary — it cannot be built from source on the server.
sharp(libvips) andpuppeteer(bundled Chromium) are the current binary dependencies inpackage.json. Install them so npm fetches the prebuilt binary for the target platform/arch rather than triggering a source build.- Before adding any new dependency with native bindings, confirm it publishes prebuilt binaries for o2switch's platform/arch — otherwise it will fail to install or run there.
Database schema & migrations
-
prisma/schema.prismais the source of truth for theconversion_jobsschema;prisma/migrations/is its version history.db/schema.sqlno longer exists. -
Prisma CLI commands (
prisma migrate dev,prisma migrate deploy,prisma generate) needDATABASE_URLin their own process environment, separate from the app. Compute it from the sameDB_HOST/DB_USER/DB_PASSWORD/DB_NAMEvalues used for local tests, vianode scripts/printDatabaseUrl.js— never writeDATABASE_URLinto.envor.env.local. -
prisma migrate devdoes NOT work against the local dev DB — verified by actually running it (DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate dev --name test_shadow_db_probe). It needs to create/drop a temporary shadow database to detect schema drift, and the localconvert_userdoes not haveCREATE DATABASE/DROP DATABASEprivileges, so it fails immediately with:Error: P3014 Prisma Migrate could not create the shadow database. Please make sure the database user has permission to create databases. Original error: Error code: P1010 User was denied access on the database `prisma_migrate_shadow_db_...`No migration folder is created when it fails this way (it errors before writing anything), so nothing needs cleaning up afterward. Do not use
migrate devin this project untilconvert_user's privileges change. -
To create a new migration locally after editing
prisma/schema.prisma, use the shadow-database-free fallback instead. Note this is not the same pattern Task 3 used for the0_initbaseline — Task 3 used--from-empty(diffing against a blank schema), which is a different mode that also happens not to need a shadow database, but is not applicable here since the local DB is not empty. The verified fallback for an already-baselined DB is--from-schema-datasource(introspects the live DB's actual current structure — a real DB connection, but not a shadow database) diffed against--to-schema-datamodel(reads the target state straight from a schema file, no DB connection at all):DB_HOST=127.0.0.1 DB_USER=convert_user DB_PASSWORD=change_me DB_NAME=file_converter STORAGE_DIR=./storage \ DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate diff \ --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma --script > /tmp/migration.sqlThis was verified twice against the local dev DB: with
prisma/schema.prismaunchanged it printed-- This is an empty migration.(no shadow database requested, no error); with a throwaway copy of the schema (an extranotes String? @db.Textfield onConversionJob, passed as--to-schema-datamodel <path-to-throwaway-copy>) it printed a correctALTER TABLE conversion_jobs ADD COLUMN notes TEXT NULL;— again with no shadow database involved. The earlier documented fallback here (--from-migrations prisma/migrations) was wrong:--from-migrationsinternally requires--shadow-database-urltoo, so it fails for the same P3014 reasonmigrate devdoes; it was never actually tested before being written down.Write the generated SQL to a path under the OS temp directory (e.g.
/tmp/migration.sql), never to a file in the repo (an untrackedmigration.sqlin the repo root is easy to accidentally commit). Review it, then manually create the nextprisma/migrations/<timestamp>_<description>/migration.sqlfolder with that content, and mark it applied without executing it (since you'll apply it for real viamigrate deployor by hand):DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate resolve --applied <timestamp>_<description>Delete the temp SQL file once it's been copied into the migration folder.
-
To apply pending migrations in production: with the real
DB_*values loaded from.env, runDATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate deploybefore starting the app. -
ONE-TIME step, required before the very first
migrate deployagainst any environment whoseconversion_jobstable predates Prisma (this includes o2switch production, which still has the table created by hand from the now-deleteddb/schema.sql, but no_prisma_migrationstracking table): do not runmigrate deployfirst. Instead, baseline that environment exactly like Task 3 did locally:DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate resolve --applied 0_initThis tells Prisma that
0_initis already applied (the table already exists) without trying toCREATE TABLEit again. Run this once, ever, per environment — after that,migrate deployis the correct command for all subsequent deployments to that environment. Runningmigrate deployfirst (without this baseline step) against such an environment will fail with a MySQL "table already exists" error and leave Prisma's migration history in a failed state requiring manual recovery.