## Mission

The DashClaw doctor, the `dashclaw-platform-intelligence` skill, and other derivative artifacts (MCP tool definitions, downloadable skill bundle) rot silently every time DashClaw changes. Someone adds a table, renames an env var, ships a new route — and the doctor's hardcoded checks, the skill's hardcoded facts, and the MCP tool list all drift until they're wrong.

Fix this by wiring the **livingcode** shape system into the **doctor**, the **skill**, and regenerating anything else derivative — all driven from the actual codebase as source of truth. Regenerate on every commit via a pre-commit hook so the artifacts can never lag behind.

## Starting State (Read First)

Before writing code, read these paths to understand what already exists:

**Livingcode (Python, in-repo at `livingcode/`):**
- `livingcode/types.py` — dataclasses including `ShapeModel`, `RouteInfo`, `EnvVarInfo`, `TableInfo`, `ShapeDiff`
- `livingcode/shape.py` — `build_shape(repo_path)` assembles the model
- `livingcode/collectors/routes.py` — scans `app/api/` for `route.js` files
- `livingcode/collectors/env_vars.py` — greps `process.env.X` references
- `livingcode/collectors/schema.py` — parses `schema/schema.js` for `pgTable()` calls
- `livingcode/query.py` — `python -m livingcode query <topic>` live query CLI
- `livingcode/diff.py` — snapshot save + compute_diff
- `livingcode/emit.py` + `livingcode/emitters/skill.py` — artifact generation
- `livingcode/__main__.py` — CLI wiring for `sense`, `query`, `snapshot`, `diff`, `emit`, etc.
- `livingcode/tests/` — unittest suite (must stay green)

**Doctor (JavaScript, in-repo):**
- `app/lib/doctor/engine.mjs` — `runDoctor()` + `applyFix()` dispatcher
- `app/lib/doctor/checks/{database,config,auth,deployment,sdk,governance}.mjs`
- `app/lib/doctor/fixes/index.mjs` — `FIX_REGISTRY` (migrate, generate-secrets, fix-cors, create-default-policy)
- `app/lib/doctor/format.mjs` — terminal renderer
- `app/api/doctor/route.js` — `GET /api/doctor`
- `app/api/doctor/fix/route.js` — `POST /api/doctor/fix`
- `scripts/doctor.mjs` — `npm run doctor` local mode
- `cli/lib/doctor.js` — `dashclaw doctor` remote CLI mode
- `app/lib/setup/runtime-prerequisites.mjs` — `CORE_SETUP_TABLES` constant
- `app/lib/readiness/configurationCheck.mjs` — required/advisory env var logic

**Existing skill to regenerate:**
- Locate the current `dashclaw-platform-intelligence` skill — likely at `~/.claude/skills/dashclaw-platform-intelligence/SKILL.md`. Read it to understand the frontmatter, structure, and any non-generated content that must be preserved.

**Existing MCP definitions (if any):**
- Check `mcp-server/` or any `mcp/` directory in the DashClaw repo. If MCP tool definitions exist as JSON or TS files, they're in scope for regeneration. If no MCP definitions exist yet, skip MCP work (flagged as stretch goal below).

**Design spec (already approved, mostly implemented):**
- `docs/superpowers/specs/2026-04-12-dashclaw-doctor-design.md` — the doctor spec. A recent deep review identified gaps listed in Phase 5 below.

## Critical Constraint: Vercel Runtime

**Python is NOT available on Vercel.** Next.js serverless functions run on Node.js only. That means:

- `/api/doctor` CANNOT spawn Python at request time
- All livingcode-generated artifacts must be **committed JSON/markdown files** that JavaScript can read directly
- Regeneration happens on developer machines (pre-commit hook) and CI — never in production
- Deployed doctor reads `app/lib/doctor/generated/shape.json` which was committed at build time

This is a feature, not a limitation — deterministic artifacts that ship with each deploy.

## Scope (ordered by dependency)

### Phase 1 — Constants Handoff (foundation)

**Goal:** Livingcode emits a JSON snapshot of the shape. Doctor imports it, replacing hardcoded string literals.

**Tasks:**

1. Add a new emit target `shape-json` in `livingcode/emit.py`:
   - Target output path: `app/lib/doctor/generated/shape.json`
   - Contents: full serialized `ShapeModel` (asdict) — routes, env_vars, tables, timestamp
   - Add `livingcode/emitters/shape_json.py` with `emit_shape_json(shape) -> str` returning pretty-printed JSON
   - Register in `TARGETS` tuple in `emit.py`

2. Create `app/lib/doctor/generated/README.md` that says: "This directory is AUTO-GENERATED by livingcode. Do not edit. Regenerate with `npm run livingcode:refresh`."

3. Create `app/lib/doctor/shape.mjs`:
   - ESM module that imports the JSON with `import shape from './generated/shape.json' with { type: 'json' }`
   - Exports named helpers: `getAllTables()`, `getGovernanceTables()`, `getRequiredEnvVars()`, `getOptionalEnvVars()`, `getRoute(path)`, `findRouteByPattern(prefix)`
   - Also exports a `META` object with `{ generatedAt: shape.timestamp, source: 'livingcode' }`

4. Refactor hardcoded literals to use `shape.mjs`:
   - `app/lib/doctor/checks/governance.mjs` — replace `'guard_policies'`, `'action_records'` string literals with lookups via `getGovernanceTables()`
   - `app/lib/doctor/checks/auth.mjs` — replace `'api_keys'` literal with lookup
   - `app/lib/doctor/checks/deployment.mjs` — replace repeated `'NEXTAUTH_URL'` / `'ALLOWED_ORIGIN'` literals with lookups
   - `app/lib/doctor/fixes/fix-cors.mjs` — same
   - `app/lib/doctor/fixes/create-default-policy.mjs` — use `getGovernanceTables()` result for the INSERT target
   - Use `sql.identifier(name)` for parameterized table names to keep queries safe

5. Leave the `'org_default'` hardcoding alone for now (multi-tenant is out of scope).

**Tests:**
- Add `livingcode/tests/test_shape_json_emitter.py` — verify JSON output is valid, contains expected keys
- Add a node test (in doctor's existing test style — check if there's already a test harness; if not, add a basic one) that asserts `shape.mjs` loads and `getGovernanceTables()` returns a non-empty array

### Phase 2 — Generated Check Modules

**Goal:** Adding a new table or env var to DashClaw automatically gets it checked by the doctor.

**Tasks:**

1. New emit target `doctor-checks` in livingcode:
   - Writes `app/lib/doctor/generated/checks-from-shape.mjs`
   - File exports `async function runShapeChecks({ sql, env })` that returns a flat array of check results
   - Emits one check per table (verifies table exists in DB via `SELECT to_regclass`)
   - Emits one check per required env var (verifies `env[name]` is set, suggests the appropriate fix action if missing)
   - Auto-maps fixable env vars to existing fix actions: `NEXTAUTH_SECRET → generate_secret`, `ENCRYPTION_KEY → generate_encryption_key`, `DASHCLAW_API_KEY → generate_api_key`
   - Non-fixable required env vars (like `DATABASE_URL`) get `fix: { type: 'prompt', action: 'prompt_database_url' }` — prompt fix is implemented in Phase 5

2. Wire generated checks into `app/lib/doctor/engine.mjs`:
   - Add a new category `shape` (or extend existing categories by returning shape-derived checks alongside hand-written ones)
   - In `CHECK_RUNNERS`, add `shape: shapeChecks` that imports from `generated/checks-from-shape.mjs`
   - Shape checks must not duplicate hand-written checks — dedupe by `id`. If the hand-written `db_connection` check already exists, skip the generated `schema_action_records` version for the same purpose. Alternative: put generated checks under a distinct `id` prefix like `shape_*` and accept the overlap as belt-and-suspenders.

3. Ensure `scripts/doctor.mjs` (local) and `/api/doctor` (remote) both pick up the new category.

**Tests:**
- `livingcode/tests/test_doctor_checks_emitter.py` — verify generated JS module has correct structure (check IDs, categories, fix refs)
- Run `npm run doctor` locally to verify the new checks appear

### Phase 3 — Drift Guard as a Doctor Check

**Goal:** If livingcode detects the shape has drifted since the last snapshot, the doctor surfaces it as a warning — making derivative-artifact staleness visible.

**Tasks:**

1. New check at `app/lib/doctor/checks/drift.mjs`:
   - Reads `shape.timestamp` from the generated JSON
   - Reads `app/lib/doctor/generated/last-snapshot.json` if present (the last committed snapshot)
   - Computes a minimal diff in JS (or calls out: if diff is complex, just compare timestamps + route/table counts and warn if they differ)
   - Returns a check per category of drift:
     - PASS if shape matches committed snapshot
     - WARN if shape is newer but no material changes
     - FAIL (or WARN) if routes/env/tables have added/removed entries → message names which artifacts are likely stale

2. Add a new fix action `regenerate_artifacts`:
   - `scope: 'local'` (filesystem access needed)
   - Spawns the livingcode refresh script (Phase 4) to regenerate everything + commit
   - Only usable from `npm run doctor`, not from deployed `/api/doctor/fix`

3. Update the `drift` check's fix metadata to point to `regenerate_artifacts` when drift is detected locally; omit the fix metadata for remote calls (where the fix can't run).

**Tests:**
- Unit test the diff logic in isolation (create two sample shape JSONs, verify detection)

### Phase 4 — Skill + Downloads Regeneration

**Goal:** The platform-intelligence skill at BOTH locations regenerates automatically. The website's downloadable skill bundle stays current.

**Tasks:**

1. The existing `livingcode emit skill` target already produces the markdown. Verify it reads current shape data and includes a prominent "auto-generated — do not edit" warning.

2. Create `scripts/livingcode-refresh.mjs` (Node) OR a shell script that:
   - Runs `python -m livingcode emit shape-json --output app/lib/doctor/generated/shape.json`
   - Runs `python -m livingcode emit doctor-checks --output app/lib/doctor/generated/checks-from-shape.mjs`
   - Runs `python -m livingcode emit skill --output <temp>` then copies the output to BOTH:
     - `%USERPROFILE%/.claude/skills/dashclaw-platform-intelligence/SKILL.md` (user-global)
     - `public/downloads/dashclaw-platform-intelligence/SKILL.md` (website download)
   - Creates a zipped bundle `public/downloads/dashclaw-platform-intelligence.zip` containing the skill directory for one-click download
   - Writes `app/lib/doctor/generated/last-snapshot.json` (same as shape.json, used by drift check)
   - Emits clear stdout for each step: `[livingcode] shape.json updated`, etc.

3. Add `package.json` script: `"livingcode:refresh": "node scripts/livingcode-refresh.mjs"`.

4. Add a pre-commit hook that runs the refresh whenever relevant files change:
   - **Trigger condition:** any staged file matches `app/api/**/route.js`, `app/lib/**/*.mjs`, `app/lib/**/*.js`, `schema/schema.js`, or `middleware.js`
   - **Implementation:** the user's global hook at `~/.git-hooks/pre-commit` already chains to the repo-local `.git/hooks/pre-commit`. Install a repo-local hook that:
     1. Detects the trigger condition via `git diff --cached --name-only`
     2. Runs `npm run livingcode:refresh`
     3. Runs `git add app/lib/doctor/generated/ public/downloads/dashclaw-platform-intelligence*`
     4. Proceeds with the rest of the commit
   - Because `.git/hooks/` is not tracked, also provide `scripts/install-hooks.mjs` that copies the hook into place, plus documentation in `README.md` or `CONTRIBUTING.md`.
   - Bypass via `git commit --no-verify` is acceptable for emergency hotfixes.

5. Update `CLAUDE.md` (project-level) with a short section under "Where to Look First":
   > **Generated artifacts:** `app/lib/doctor/generated/`, `public/downloads/dashclaw-platform-intelligence/`, and the global platform-intelligence skill are regenerated by `npm run livingcode:refresh`. This runs automatically on commit when API/lib/schema files change. Never edit these by hand.

**Tests:**
- Run `npm run livingcode:refresh` manually. Verify all four outputs update with fresh timestamps.
- Verify the pre-commit hook fires by making a trivial change to a route and committing — the JSON + skill should appear in the commit.

### Phase 5 — Close Doctor Spec Gaps

The deep review identified these gaps vs the design spec. Close them:

1. **Prompt-based fixes** (from spec: `prompt_database_url`, `prompt_nextauth_url`)
   - Add `app/lib/doctor/fixes/prompt-database-url.mjs` — uses `readline/promises` to ask for a Postgres URL, validates with a quick `sql\`SELECT 1\`` before writing to `.env`
   - Add `app/lib/doctor/fixes/prompt-nextauth-url.mjs` — validates URL format
   - Register both in `FIX_REGISTRY` with `scope: 'local'`
   - `scripts/doctor.mjs` should call these when a check has `fix.type: 'prompt'`
   - API `/api/doctor/fix` continues to reject local-scope fixes — unchanged

2. **Schema drift detection** (column-level)
   - Extend `app/lib/doctor/checks/database.mjs` (or add `schema-drift.mjs`) to compare actual DB columns vs expected columns from schema
   - Data source: parse `schema/schema.js` column definitions into the livingcode schema collector (Phase 1's `TableInfo` may need a `columns` field). If the column parsing is too much work for this round, document it as a follow-up and stick to table-level checks.

3. **SDK version check**
   - `app/lib/doctor/checks/sdk.mjs` currently only checks reachability. Add a version check that hits `/api/setup/status` (or equivalent) to get the server's expected minimum SDK version, compare vs the CLI's version (from `cli/package.json`)
   - Warn (don't fail) if the CLI is older than the minimum

4. **Build health + port availability** (local-only)
   - In `scripts/doctor.mjs`, add a check that `npm run build` succeeds (or defer via `--skip-build`)
   - Add a port check using `net.createServer().listen(port)` to see if the configured port is free

### Phase 6 — MCP Tool Definitions (stretch / conditional)

**Only if MCP definitions currently exist in the repo.** If you find a directory like `mcp-server/` or `mcp/` with tool definition JSON/TS files, regenerate them:

1. Add emit target `mcp-tools` in livingcode
2. Generate tool definitions from the shape model — one tool per non-archived POST/PATCH route, with inputSchema derived from `dynamic_params` and route parameters
3. Write to wherever the existing MCP tool file lives

If no MCP directory exists, skip this phase entirely and add a note to the commit message: "MCP regeneration deferred — no existing MCP definitions found."

## Implementation Order

Execute strictly in this order. Do NOT start Phase 2 until Phase 1 is committed and green.

1. Phase 1 — Constants handoff
2. Phase 4 — Refresh script + pre-commit hook (do this BEFORE Phase 2/3 so you can test generation end-to-end)
3. Phase 2 — Generated check modules
4. Phase 3 — Drift guard
5. Phase 5 — Close spec gaps
6. Phase 6 — MCP (if applicable)

## Verification Checklist

Before declaring done, verify all of these:

- [ ] `python -m pytest livingcode/tests/ -v` passes (all tests green)
- [ ] `npm run livingcode:refresh` produces fresh files in `app/lib/doctor/generated/`, `public/downloads/dashclaw-platform-intelligence/`, and `~/.claude/skills/dashclaw-platform-intelligence/SKILL.md`
- [ ] `npm run doctor` runs successfully and shows shape-derived checks alongside hand-written ones
- [ ] `curl http://localhost:3000/api/doctor | jq '.checks | length'` returns a larger number than before (generated checks included)
- [ ] Manually touch `app/api/health/route.js` (add/remove a comment, restore) and commit — the pre-commit hook regenerates the JSON and includes it in the commit
- [ ] `npm run lint` and `npm run openapi:check` still pass
- [ ] The regenerated skill at both locations opens and renders as valid markdown
- [ ] `/api/doctor` on a deployed instance works without Python (JSON-only path)
- [ ] `git status` is clean after running refresh (idempotent — running twice with no source changes is a no-op)

## Guardrails (What NOT to Do)

- **Don't touch `livingcode/` internals beyond adding the two new emit targets** (`shape-json`, `doctor-checks`, and if needed a columns extension to the schema collector). The existing collectors, types, query, diff, and skill emitter all work — don't refactor them.
- **Don't delete or rename any existing doctor check or fix** — the goal is to add shape-derived checks alongside, not replace the working hand-written ones.
- **Don't commit secrets.** The generated JSON contains env var NAMES only, never values.
- **Don't bypass pre-commit with `--no-verify`** except for emergency hotfixes — the whole point is the hook.
- **Don't add a runtime Python call from Node.** All generation happens pre-commit. Production deploys read committed files.
- **Don't introduce new npm dependencies** unless strictly necessary. The refresh script should be a single Node file using only stdlib.
- **Don't skip tests.** Every new emitter gets a Python test. The doctor's new shape.mjs gets a Node test (match whatever harness exists — likely none currently, so a tiny `node --test` file works).
- **Preserve the GitHub account rule:** `ucsandman`, SSH format `git@github.com:ucsandman/<repo>.git`.

## Decisions Already Made (don't re-litigate)

- Scope: Phases 1–5 are in. Phase 6 (MCP) is conditional on existing definitions.
- Regeneration: pre-commit hook + manual `npm run livingcode:refresh`.
- Production: JSON-only (no Python at runtime). Vercel reads committed files.
- Skill outputs to: `~/.claude/skills/dashclaw-platform-intelligence/SKILL.md` and `public/downloads/dashclaw-platform-intelligence/`.
- Keep `'org_default'` hardcoded — multi-tenant is out of scope.
- If schema collector column parsing proves too complex, table-level drift detection is acceptable for this round; flag as follow-up.

## Questions to Ask Before Starting

Only ask if genuinely ambiguous. You have full read access to both livingcode and doctor — exhaust that before asking.

Acceptable questions:
- Existing MCP directory location (if you can't find one, just skip Phase 6)
- Whether an existing test harness exists for doctor Node code
- Conflicts between pre-commit hook design and an existing hook

Don't ask:
- Anything covered in "Decisions Already Made"
- Cosmetic choices (check ID naming, file formatting)
- Whether to proceed — just proceed

## Commit Strategy

- One commit per phase, with the phase number in the subject line
- Example: `feat(doctor): Phase 1 — shape-json emitter and constants handoff`
- Commit the generated JSON/markdown files as part of each phase commit
- Co-author line: `Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>`
- Push to `main` (per user preference: no PRs)

---

**Go.**
