Test a source change
Run focused checks, protect the retained default database, and finish with product-level acceptance when the change needs it.
Match the proof to the change. A focused unit test is the fast loop. A real database path belongs on the disposable scratch instance. A user-visible change is not finished until it runs through a claimed Server and the actual client surface.
Start narrow
Run the smallest test or package suite that exercises the changed behavior, then widen the proof:
bun test <path-to-focused-test>
bun run --cwd <workspace> test:unit
bun run --cwd <workspace> typecheck
bun run --cwd <workspace> lintFor a repository-wide confidence pass:
bun run test:unit
bun run typecheck
bun run lintThe root scripts use Turbo to run their matching workspace tasks. Full integration coverage is intentionally heavier and serialized:
bun run test:integrationThe Server integration runner executes each test file in its own Bun process to prevent process-global mocks, app instances, and environment changes from leaking across files.
Keep fixture writes away from default
Database-backed tests use the disposable instance ID test-cruft when their
package preload or fixture bootstrap runs. The shared guard refuses fixture
writes to (default) unless someone explicitly bypasses it or the process
identifies itself as CI. CI is treated as ephemeral; do not use a CI flag to
make a developer workstation's retained database look disposable.
Do not normalize the bypass
Do not set ALLOW_DEFAULT_DB_TESTS=1 in a shell profile, CI environment, or
copied test command. It is an escape hatch for a deliberate one-off action,
not a development default.
When adding a live-database test, call the package's fixture bootstrap before
opening a database connection. Do not assume a package-level bunfig.toml
preload will fire when someone runs one test from the repository root.
Recover a contaminated scratch instance
If failures show that test-cruft itself is stale or corrupted, inspect the
failure first. Then, only when its deletion is intended, reset it:
bun run dev:reset-test-cruft --yesThat command deletes and recreates the entire test-cruft instance. It is not
a cleanup command for default, and it is not a first response to a failing
assertion.
Finish at the product boundary
Tests do not replace acceptance when the change crosses Server, Workbench, Electron, authentication, setup, provider routing, or multi-user behavior.
Use one of the paths in Run Nautilo from source. For most isolated product acceptance, clone the claimed default into an absent named instance. Use a genuinely fresh named instance only when blank-server, claim, or first-owner behavior is the thing under test.
Then verify the applicable outcome:
- the intended instance—not a neighboring worktree's instance—is running;
- setup state is
ready; - the known owner can sign in;
- the changed UI or API path works through the real client;
- a model-backed turn succeeds when model execution is in scope; and
- a second profile or client proves identity separation when multi-user state is in scope.
Record the exact instance, command, test path, and observed result in the pull request. “Tests pass” is not enough when the important proof happened in a specific retained environment.
Database changes need a second decision
Before applying a new migration to retained data, use the database migration safety decision tree. A clean Git merge and a green unit suite do not prove that two migration histories can be safely replayed against the same database.