# Logging Patterns & Debug Environment Reference

> Pick the section matching `stack.framework` from `baldart.config.yml`. The
> first section (Next.js) is the most common; sections for other frameworks
> follow the same intent: maximize signal during a debug session, with a way
> to turn it off cleanly when done.

## Next.js 16 Built-In Logging (`stack.framework: nextjs`)

Add to `next.config.ts` during debug sessions:

```typescript
logging: {
  fetches: { fullUrl: true },            // logs all server-side fetch() with cache HIT/MISS
  serverFunctions: true,                  // logs Server Actions with name + args + duration
  browserToTerminal: true,                // forwards ALL client console.* to terminal with [browser] prefix
  incomingRequests: {
    ignore: [/\/api\/v1\/ping/, /\/_next/], // filter noise
  },
},
```

`browserToTerminal` (v16.2+) is the most powerful: it streams client-side `console.*` to the dev server terminal with file:line:col source location. No code changes needed.

## Remix Built-In Logging (`stack.framework: remix`)

- Enable `loader` / `action` timing via `DEBUG=remix:*` env.
- For request logging, instrument `entry.server.ts` with a `handleRequest`
  wrapper that logs method/path/duration.
- Browser console forwarding: not built-in; use a small client-side logger
  posting to `/__debug` route handler during the session.

## SvelteKit (`stack.framework: sveltekit`)

- Enable verbose `vite` logging via `vite --debug` for build-time issues.
- `handle` hook in `hooks.server.ts` is the natural choke point: log
  method/path/duration there during a session.

## Astro / Nuxt / Other

- Use the framework's request hook (Astro middleware, Nuxt `defineEventHandler`
  wrapper) to log structured request/response data.
- Stick to plain `console.log` with a unique prefix that's easy to grep and
  remove (e.g. `[BUG-1234]`).

## Generic fallback (any framework)

When the framework has no built-in equivalent, add `pino` or `winston` to the
project and emit JSON logs at every interesting choke point (request entry,
DB call, external API call). Remove on session close.

## Environment Variables for Debug Sessions

Set in `.env.local` during investigation, remove after:

```bash
# Examples — pick the ones matching this project's stack:
DEBUG_MODULES=<module>.<verb>            # server-side module filter (project-specific)
DEBUG_DB=true                            # generic DB op tracing — name varies by stack:
                                         #   firestore  → DEBUG_FIRESTORE=true
                                         #   supabase   → SUPABASE_DEBUG=true
                                         #   postgres   → DEBUG=knex:query  (or your ORM's flag)
                                         #   mongo      → DEBUG=mongoose:* (if using mongoose)
DEBUG_NETWORK=true                       # request/response logging
DEBUG_PROXY=true                         # proxy/middleware logging
NEXT_PUBLIC_DEBUG_COMPONENTS=<module>    # Next.js only — client component logs (rebuild required)
NEXT_OTEL_VERBOSE=1                      # Next.js only — verbose OpenTelemetry spans
NEXT_TURBOPACK_TRACING=1                            # Turbopack bundler trace → .next/dev/trace-turbopack
```

## Debug Log Convention

ALL temporary log lines MUST end with `// DEBUG:` comment marker.

Cleanup command:
```bash
grep -rn '// DEBUG:' src/ --include='*.ts' --include='*.tsx'
```

Remove every line found. Run grep again. Zero results = clean.

## Datastore-Specific Debugging

> The patterns below are illustrated for **Firestore** — apply them only when
> `stack.database: firestore`. For other stores, use the equivalent primitive
> (SQL `SERIALIZABLE` retry/`SAVEPOINT` for Postgres/Supabase, Mongo
> `withTransaction`, etc.). The *principle* (surface silent retries, log the
> attempt counter, beware emulator-only debug hooks) is datastore-agnostic.

### Transaction contention (Firestore example)
Firestore retries transactions silently up to 5 times. Only explicit attempt counting surfaces this:
```typescript
let attempt = 0; // DEBUG:
await firestore.runTransaction(async (tx) => {
  attempt++; // DEBUG:
  console.log(`[DEBUG:tx] attempt=${attempt} path=${ref.path}`); // DEBUG:
});
```

### Snapshot ambiguity
`snapshot.empty === true` is AMBIGUOUS — could be security rules denial OR genuinely no documents. Check:
- `snapshot.metadata.fromCache` — `true` = offline/cached, `false` = network response
- `snapshot.metadata.hasPendingWrites` — pending optimistic writes

### Query Explain (firebase-admin)
```typescript
// PLAN mode: no reads, just execution plan
const plan = await query.explain({ analyze: false });
// ANALYZE mode: runs query, returns stats
const analysis = await query.explain({ analyze: true });
// RED FLAG: readOperations >> resultsReturned = missing composite index
```

### Security Rules debugging
`debug()` in Firestore rules works ONLY in the emulator, no-op in production.
Use `@firebase/rules-unit-testing` (already in devDeps) for rule testing.

## Playwright MCP Debug Patterns

### Inject fetch interceptor (zero code changes)
```javascript
// browser_evaluate:
const origFetch = window.fetch;
window.fetch = async (...args) => {
  const url = typeof args[0] === 'string' ? args[0] : args[0]?.url;
  const method = args[1]?.method || 'GET';
  console.log(`[DEBUG:fetch] ${method} ${url}`);
  const start = Date.now();
  try {
    const res = await origFetch(...args);
    console.log(`[DEBUG:fetch] ${res.status} ${url} ${Date.now()-start}ms`);
    if (!res.ok) {
      const clone = res.clone();
      console.error(`[DEBUG:fetch] body:`, await clone.text().catch(() => ''));
    }
    return res;
  } catch(e) {
    console.error(`[DEBUG:fetch] FAIL ${url}`, e.message);
    throw e;
  }
};
```

### Inject SWR mutation tracker
```javascript
// browser_evaluate:
const origMutate = window.__SWR_MUTATE__;
if (origMutate) {
  window.__SWR_MUTATE__ = (...args) => {
    console.log('[DEBUG:swr] mutate', args[0]);
    return origMutate(...args);
  };
}
```

### HAR recording (for complex network analysis)
In Playwright test context:
```typescript
const context = await browser.newContext({ recordHar: { path: 'debug.har', mode: 'full' } });
```

## CDP (Chrome DevTools Protocol) via Playwright

For deep debugging beyond MCP tools:
```typescript
const session = await context.newCDPSession(page);
await session.send('Runtime.enable');
session.on('Runtime.consoleAPICalled', (event) => { /* raw console events */ });
session.on('Runtime.exceptionThrown', (event) => { /* uncaught exceptions */ });
await session.send('Network.enable');
session.on('Network.requestWillBeSent', (event) => { /* all network + headers */ });
```

## Git Bisect Workflow

```bash
# Find the commit that introduced a regression
git bisect start HEAD <last-known-good-tag> --
git bisect run sh -c "npx tsc --noEmit 2>/dev/null || exit 125; <test-command>"
git bisect reset

# Tips:
# - exit 125 = skip (untestable commit, e.g. npm install fails)
# - --first-parent avoids false positives from broken merge branches
# - Save session: git bisect log > session.log / replay: git bisect replay session.log
# - Skip if deps fail: npm ci --silent 2>/dev/null || exit 125
```

## instrumentation.ts Hooks

Next.js `instrumentation.ts` provides:
- `onRequestError(error, request, context)` — fires on server errors
- `context.routeType`: `'render' | 'route' | 'action' | 'proxy'` — distinguishes RSC vs handler vs Server Action
- `error.digest` — stable error ID for Sentry correlation

## Project Codebase Debug Tools

The base skill is generic; project-specific debug entry points are listed in `.baldart/overlays/bug.md`. Typical entries:

| Tool | Location | Purpose |
|------|----------|---------|
| Sandbox / write-interceptor | (project-specific) | Intercept writes during walkthrough/testing |
| Sentry test endpoint | (project-specific) | Validate Sentry pipeline |
| Env summary helper | (project-specific) | Safe env snapshot |
| Auth bypass env var | (project-specific) | Emergency auth bypass |
| Cache debug provider | (project-specific) | Cache/fetch logging (SWR / React Query / Apollo) |
| Error boundaries | (project-specific) | Client error capture + Sentry |
| Auth error map | (project-specific) | Auth-provider errors → typed codes |

## Zustand State Debugging

```typescript
// In browser console:
store.getState()  // point-in-time snapshot
store.subscribe((state) => console.log('[DEBUG:zustand]', state))  // live changes
// Zustand devtools (if enabled): visible in Redux DevTools extension
```
