diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 92cec62..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: npm
- directory: /
- schedule:
- interval: weekly
- day: monday
- time: '06:00'
- timezone: Etc/UTC
- open-pull-requests-limit: 5
- # Pin @types/node to the runtime's major (Node 22 LTS). Bumping the types
- # past Node 22 would surface APIs the runtime can't actually call.
- ignore:
- - dependency-name: '@types/node'
- update-types: ['version-update:semver-major']
- groups:
- dev-deps:
- dependency-type: development
- update-types: [minor, patch]
- runtime-deps:
- dependency-type: production
- update-types: [minor, patch]
- commit-message:
- prefix: chore
- include: scope
-
- - package-ecosystem: github-actions
- directory: /
- schedule:
- interval: weekly
- day: monday
- time: '06:00'
- timezone: Etc/UTC
- open-pull-requests-limit: 5
- groups:
- gh-actions:
- patterns: ['*']
- commit-message:
- prefix: ci
- include: scope
diff --git a/.gitignore b/.gitignore
index 68f719d..4286dd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,10 +41,11 @@ data/cron-*.log
.env.*
!.env.example
-# OpenSpec — keep the `openspec/` proposals + specs in version control,
-# but ignore the per-IDE integration files `openspec init` materializes
-# under .claude/.codebuddy/.continue/.junie/.kiro. Those are local tooling
-# for whichever editor each developer uses.
+# Local planning scratch — design proposals live in Linear, not the repo.
+openspec/
+
+# Per-IDE agent integration files — local tooling for whichever editor each
+# developer uses.
.claude/*
# Exception: project-specific Claude Code skills (`pupila-*`) ship with the
# repo so contributors get the agent guidance automatically. Provider-generic
diff --git a/AGENTS.md b/AGENTS.md
index ec5d0d3..93a4687 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -113,8 +113,6 @@ tsconfig.test.json # extends above with rootDir=. so tests/ typecheck without l
.github/
workflows/
check.yml # PR/push: biome + typecheck + tests + build + audit (only remaining workflow)
- check.yml # PR/push: lint + typecheck + test + audit
- dependabot.yml # weekly npm + github-actions PRs
```
> **CodeQL workflow removed.** Code Scanning isn't available on private repos without GitHub Advanced Security. If the repo ever goes public, restore `.github/workflows/codeql.yml` from commit `7397117`.
@@ -299,9 +297,7 @@ One workflow only — the project moved to local-first scheduling.
The previously included `jobs.yml` (daily aggregator cron) and `keepalive.yml` (cron-keepalive) workflows were removed. Daily aggregation now runs locally via `scripts/install-launchd.sh` (macOS) or `scripts/install-cron.sh` (Linux), which install two agents: one for `pnpm run dev`, one for `pnpm run ai-review`. See README "Schedule the daily run" for usage.
-`.github/dependabot.yml` opens weekly grouped PRs for npm + github-actions.
-
-**Pinning.** All third-party actions are referenced by full 40-char commit SHA, not a floating `@v4` / `@v5` tag, with the version in a trailing comment. When updating an action, replace both the SHA and the comment. Dependabot keeps these current via PRs.
+**Pinning.** All third-party actions are referenced by full 40-char commit SHA, not a floating `@v4` / `@v5` tag, with the version in a trailing comment. When updating an action, replace both the SHA and the comment manually.
## Tests
diff --git a/CLAUDE.md b/CLAUDE.md
index 5ab2627..cbaea08 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -72,7 +72,6 @@ pnpm run mcp # MCP server over stdio
| `ui/` | Local-only React dashboard (see `ui/CLAUDE.md`) |
| `scripts/` | Apply worker, installers (launchd/cron/mcp), clean |
| `tests/` | 330 vitest cases, fixtures in `tests/fixtures/` |
-| `openspec/changes/` | OpenSpec proposals (committed) |
| `.claude/skills/` | Project skills (`pupila-*`) ship; provider-generic skills are local-only |
## Orchestrator flow
@@ -165,7 +164,7 @@ Vitest, 330 cases across `tests/` (`*.test.ts` glob). Run via `pnpm test` or `pn
One workflow only — `.github/workflows/check.yml` — every push to `main` and PR. Seven gates: Biome lint, typecheck (3 tsconfigs), Vitest, `tsc` build, Vite UI build, bundle-size budget, `pnpm audit`. Daily aggregation runs **locally** via launchd/cron (see `scripts/install-*.sh`).
-Third-party actions pinned by 40-char SHA with version comment. Dependabot opens weekly grouped PRs.
+Third-party actions pinned by 40-char SHA with version comment. Bump manually when needed.
> **CodeQL workflow removed** — Code Scanning isn't available on private repos without GitHub Advanced Security. Restore from commit `7397117` if the repo goes public.
diff --git a/README.md b/README.md
index 682c0fd..21e8d95 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@


-# PUPILA: A-eye on every job board
+# PUPILA: An A-Eye on every job board
@@ -236,7 +236,7 @@ Idempotent — running on an already-clean state prints `nothing to clean`. Afte
| HTML scraping | Inline regex parsers (no cheerio/jsdom) |
| Schedule | Local launchd (macOS) / cron (Linux), two agents: aggregator + AI review |
| Output | Files in your local checkout (`data/jobs.json`, `data/feed.xml` RSS, `JOBS.md`, `data/archive/.json` on month-start) |
-| Static analysis | Biome + tsc + build on every PR via `check.yml`; Dependabot for npm + GitHub Actions |
+| Static analysis | Biome + tsc + build on every PR via `check.yml` |
## Architecture
@@ -735,9 +735,8 @@ Keyword arrays are joined with `|` and compiled into word-bounded, case-insensit
```
pupila/
├── .github/
-│ ├── workflows/
-│ │ └── check.yml # PR/push: biome + typecheck + tests + build + audit
-│ └── dependabot.yml # weekly npm + github-actions updates
+│ └── workflows/
+│ └── check.yml # PR/push: biome + typecheck + tests + build + audit
├── assets/
│ └── readme/
│ └── dashboard.jpg # README banner artwork
@@ -853,7 +852,7 @@ The previously included `jobs.yml` (daily aggregator cron) and `keepalive.yml` (
> **Note on CodeQL.** A CodeQL workflow was previously included but removed because **Code Scanning isn't available on private repos for personal accounts** without GitHub Advanced Security.
-The remaining workflow pins third-party actions to **commit SHAs** (not floating `@v4` / `@v5` tags) for supply-chain safety. [`Dependabot`](./.github/dependabot.yml) opens weekly PRs to bump those SHAs and the npm deps; the `check.yml` workflow validates each PR before merge.
+The remaining workflow pins third-party actions to **commit SHAs** (not floating `@v4` / `@v5` tags) for supply-chain safety. Bump SHAs and npm deps manually (`pnpm update`, then refresh action SHAs from the upstream tag) when you want to update — `check.yml` gates the change like any other PR.
## Customization
@@ -903,7 +902,6 @@ Defense-in-depth measures, ranked from runtime to build-time:
- **Tests** (`pnpm test`) — Vitest, extensive backend + UI suite covering security-sensitive code (URL safety, regex filters, dedup tiebreaks, applied-status grouping, salary parsing, RSS escaping, custom-ATS HTML/GraphQL parsers, AI Apply core, apply-queue mutators, swipe-skip storage, profile bootstrap) plus every UI hook + key components. Runs on every PR.
- **`pnpm audit --prod --audit-level high`** in [`check.yml`](./.github/workflows/check.yml). Reports known CVEs in production deps.
- **Pinned actions.** The remaining workflow references third-party actions by commit SHA, not floating tags. Defends against tag-hijacking.
-- **Dependabot** ([`dependabot.yml`](./.github/dependabot.yml)) — weekly PRs for npm + GitHub Actions. Each PR is gated by `check.yml`.
- **Minimum permissions.** `check.yml` uses `contents: read` only — no workflow has write access to the repo.
## Known upstream issues (as of 2026-04)
diff --git a/SECURITY.md b/SECURITY.md
index fdfb9ee..d5430b7 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -45,7 +45,7 @@ Examples of in-scope issues:
- Issues that require the attacker to already have local code execution on your machine — at that point they own everything anyway.
- Exposing the UI publicly. The dev server binds to `127.0.0.1:5173` by design; running it on a public interface is a configuration mistake, not a vulnerability.
- Committing `config/applied.json` or `config/candidate-brief.md` to a public fork. Both are gitignored; the user has to explicitly opt in to track them.
-- Dependency CVEs already flagged by `pnpm audit` in CI — those are tracked in the open and Dependabot PRs.
+- Dependency CVEs already flagged by `pnpm audit` in CI — those are tracked in the open.
## Hall of fame
diff --git a/openspec/changes/mcp-server-ci-dependabot/.openspec.yaml b/openspec/changes/mcp-server-ci-dependabot/.openspec.yaml
deleted file mode 100644
index 9f70866..0000000
--- a/openspec/changes/mcp-server-ci-dependabot/.openspec.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-schema: spec-driven
-created: 2026-05-15
diff --git a/openspec/changes/mcp-server-ci-dependabot/README.md b/openspec/changes/mcp-server-ci-dependabot/README.md
deleted file mode 100644
index cfd1899..0000000
--- a/openspec/changes/mcp-server-ci-dependabot/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# mcp-server-ci-dependabot
-
-DEV-84 — CI smoke test + dependabot grouping for MCP deps
diff --git a/openspec/changes/mcp-server-ci-dependabot/proposal.md b/openspec/changes/mcp-server-ci-dependabot/proposal.md
deleted file mode 100644
index 46f333c..0000000
--- a/openspec/changes/mcp-server-ci-dependabot/proposal.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Why
-
-The MCP feature added two runtime deps (`@modelcontextprotocol/sdk@1.29.0`, `zod@4.4.3`) on a fast version cadence. The existing `.github/workflows/check.yml` runs `pnpm typecheck` / `pnpm test` / `pnpm run lint` — but those gates exercise tool runners directly via Vitest, NOT the protocol round-trip. A regression in the SDK or in our stdio bootstrap (e.g., a stray `console.log` from a freshly-imported lib corrupting the JSON-RPC frame) would slip past CI today.
-
-Separately, Dependabot opens weekly grouped npm PRs but has no group for the MCP deps. Without grouping, an SDK bump and a zod bump arrive as two PRs that BOTH need review of the same surface — wasteful.
-
-## What Changes
-
-- Add an `mcp:smoke` script to `package.json` that pipes a single `tools/list` JSON-RPC request to `pnpm run mcp` via stdin and asserts the response contains all 17 expected tool names. Pure black-box check — no Vitest, no SDK Client; verifies the framing + registration end-to-end.
-- Add an `MCP smoke` step to `.github/workflows/check.yml` after `pnpm run ui:build` and before `pnpm run lint:bundle-size`. Runs `pnpm run mcp:smoke` with a hard 30s timeout.
-- Add an `mcp-deps` group to `.github/dependabot.yml` containing `@modelcontextprotocol/sdk` and `zod`. Weekly cadence (matches the existing config). Patch + minor bumps land in one PR.
-
-The smoke test MUST work on a fresh CI checkout — no `data/jobs.json`, no `config/profile.json`, no `config/candidate-brief.md`. Tools that depend on those files already return clean empty/error responses on missing files (verified by existing unit tests), so the smoke check only validates that the SERVER itself boots and registers tools.
-
-## Capabilities
-
-### New Capabilities
-
-- `mcp-ci-smoke`: A CI gate that boots `pnpm run mcp`, sends a single `tools/list` JSON-RPC request, and fails the job if the response is missing any of the 17 registered tools — independent of Vitest's in-process integration suite.
-
-### Modified Capabilities
-
-_None — `dependabot.yml` is configuration, not a capability spec._
-
-## Impact
-
-- **Files touched:** `package.json` (one new script), `.github/workflows/check.yml` (one new step), `.github/dependabot.yml` (one new group entry).
-- **CI runtime:** +5–15s for the smoke step (server boot + one request + teardown).
-- **Affected APIs:** none — read-only check.
-- **Risk:** the smoke script must NOT depend on user data files; broken if it does. Failure modes covered by the smoke check today are already covered by the in-process integration suite (`tests/mcp/integration.test.ts`) — the smoke adds value as a defense against environment-level regressions (Node version mismatch, missing executable bit on tsx, SDK API drift) that an in-process test can't catch.
-- **Dependabot:** existing PRs already in flight will not be re-grouped; the change takes effect for the next refresh cycle.
diff --git a/openspec/changes/mcp-server-ci-dependabot/specs/mcp-ci-smoke/spec.md b/openspec/changes/mcp-server-ci-dependabot/specs/mcp-ci-smoke/spec.md
deleted file mode 100644
index 1f472ae..0000000
--- a/openspec/changes/mcp-server-ci-dependabot/specs/mcp-ci-smoke/spec.md
+++ /dev/null
@@ -1,62 +0,0 @@
-## ADDED Requirements
-
-### Requirement: CI runs an MCP smoke check on every push and PR
-
-The `.github/workflows/check.yml` workflow SHALL execute a `MCP smoke` step on every push to `main` and every pull request. The step invokes `pnpm run mcp:smoke`, which boots `pnpm run mcp`, sends a single `tools/list` JSON-RPC request, and validates the response.
-
-#### Scenario: A push to main runs the smoke check
-
-- **GIVEN** a commit is pushed to `main`
-- **WHEN** the check workflow runs
-- **THEN** the `MCP smoke` step executes after the UI build
-- **AND** the workflow fails IF the step exits non-zero
-
-#### Scenario: The smoke check has a hard timeout
-
-- **GIVEN** the MCP server hangs during startup
-- **WHEN** `pnpm run mcp:smoke` runs
-- **THEN** it MUST exit non-zero within 30 wall-clock seconds, even if the server never responds
-
-### Requirement: Smoke check works without any user data
-
-The smoke script SHALL succeed on a fresh checkout with no `data/jobs.json`, no `config/profile.json`, no `config/candidate-brief.md`. The check validates server boot + tool registration ONLY — not tool execution against real data.
-
-#### Scenario: A fresh CI runner with no data files
-
-- **GIVEN** an `ubuntu-latest` runner with the repo just cloned (no gitignored files materialized)
-- **WHEN** `pnpm run mcp:smoke` runs
-- **THEN** it exits 0
-- **AND** the response is asserted against the canonical 17-tool list
-
-### Requirement: Every registered tool appears in the smoke check expected list
-
-The smoke script SHALL maintain an explicit list of expected tool names. Whenever a new tool is registered in `src/mcp/server.ts`, the smoke script's expected list MUST be updated in the same PR.
-
-#### Scenario: A new tool is added without updating the smoke
-
-- **GIVEN** a PR adds `register()` to `src/mcp/server.ts` but does NOT update `scripts/mcp-smoke.mjs`
-- **WHEN** the smoke runs in CI
-- **THEN** the check passes (the new tool is extra, not missing) — but CI does NOT enforce parity in this direction; reviewer responsibility
-
-#### Scenario: A tool is removed without updating the smoke
-
-- **GIVEN** a PR removes `registerListJobs()` from `src/mcp/server.ts` but leaves it in `scripts/mcp-smoke.mjs` expected list
-- **WHEN** the smoke runs in CI
-- **THEN** the check FAILS with `[mcp-smoke] missing tool: list_jobs`
-
-### Requirement: Dependabot groups MCP runtime deps
-
-The `.github/dependabot.yml` SHALL define an `mcp-deps` group containing `@modelcontextprotocol/sdk` and `zod`. Minor and patch updates for these packages SHALL land as a single grouped PR.
-
-#### Scenario: SDK and zod both have patch releases the same week
-
-- **GIVEN** `@modelcontextprotocol/sdk@1.29.1` and `zod@4.4.4` both publish before the weekly refresh
-- **WHEN** Dependabot opens its next batch of PRs
-- **THEN** exactly one PR exists with the title `Bump the mcp-deps group ...`
-- **AND** it contains both updates
-
-#### Scenario: A major-version bump arrives
-
-- **GIVEN** `@modelcontextprotocol/sdk@2.0.0` publishes
-- **WHEN** Dependabot processes it
-- **THEN** the major bump opens as its OWN ungrouped PR — major versions are explicitly excluded from the group's `update-types` filter
diff --git a/openspec/changes/mcp-server-ci-dependabot/tasks.md b/openspec/changes/mcp-server-ci-dependabot/tasks.md
deleted file mode 100644
index 055bf1d..0000000
--- a/openspec/changes/mcp-server-ci-dependabot/tasks.md
+++ /dev/null
@@ -1,44 +0,0 @@
-## Tasks
-
-### 1. `mcp:smoke` script
-
-- [ ] Add `"mcp:smoke": "node scripts/mcp-smoke.mjs"` to `package.json` scripts.
-- [ ] Create `scripts/mcp-smoke.mjs`. The script spawns `pnpm run mcp`, writes a single line `{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}\n` to its stdin, reads stdout until it sees a JSON-RPC response on a line starting with `{`, parses it, and asserts:
- - [ ] `data.result.tools` is an array of length ≥ 17.
- - [ ] Every name in the expected list appears in the response: `list_jobs`, `get_job_detail`, `get_brief`, `mark_applied`, `update_status`, `clear_applied`, `enqueue_apply`, `cancel_apply`, `skip_job`, `queue_status`, `worker_status`, `run_summary`, `get_ai_review`, `list_ai_reviews`, `trigger_fetch`, `get_fetch_status`, `regenerate_profile`.
-- [ ] Wall-clock timeout: 30 seconds. On timeout, kill the child + exit 1 with a clear `[mcp-smoke] timed out` message.
-- [ ] Exit 0 only when assertions pass; exit non-zero on parse failure, missing tool, or timeout.
-- [ ] Verify locally on a working tree WITHOUT `data/jobs.json` / `config/profile.json` / `config/candidate-brief.md` — the smoke MUST pass on a fresh checkout.
-
-### 2. CI step in `check.yml`
-
-- [ ] After the `Vite — build UI bundle (ui/dist/)` step and before `lint:bundle-size`, add a new step:
- ```yaml
- - name: MCP — smoke test (tools/list over stdio)
- run: pnpm run mcp:smoke
- timeout-minutes: 1
- ```
-- [ ] No matrix changes — runs once on the existing `ubuntu-latest` runner.
-
-### 3. Dependabot grouping
-
-- [ ] Edit `.github/dependabot.yml`. Within the existing `npm` ecosystem block, add a `groups:` entry:
- ```yaml
- groups:
- mcp-deps:
- patterns:
- - "@modelcontextprotocol/sdk"
- - "zod"
- update-types:
- - "minor"
- - "patch"
- ```
-- [ ] Leave existing groups (if any) intact.
-- [ ] Major version bumps stay ungrouped — those need individual review.
-
-### 4. Validation
-
-- [ ] `pnpm run mcp:smoke` exits 0 locally with the current 17 tools.
-- [ ] Locally simulate a regression: temporarily remove one tool from `src/mcp/server.ts`, re-run smoke, confirm exit 1 with a clear "missing tool: " message; revert.
-- [ ] Open the PR; CI's new `MCP smoke` step appears green.
-- [ ] Confirm in Dependabot's next refresh that SDK + zod bumps would land as one grouped PR. (Cannot validate this without waiting for the cycle — document the expected behavior in the PR description.)
diff --git a/openspec/changes/mcp-server-readme-docs/.openspec.yaml b/openspec/changes/mcp-server-readme-docs/.openspec.yaml
deleted file mode 100644
index 9f70866..0000000
--- a/openspec/changes/mcp-server-readme-docs/.openspec.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-schema: spec-driven
-created: 2026-05-15
diff --git a/openspec/changes/mcp-server-readme-docs/README.md b/openspec/changes/mcp-server-readme-docs/README.md
deleted file mode 100644
index 5077119..0000000
--- a/openspec/changes/mcp-server-readme-docs/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# mcp-server-readme-docs
-
-DEV-83 — README + tool reference + troubleshooting docs for the MCP server
diff --git a/openspec/changes/mcp-server-readme-docs/proposal.md b/openspec/changes/mcp-server-readme-docs/proposal.md
deleted file mode 100644
index 3a1fe09..0000000
--- a/openspec/changes/mcp-server-readme-docs/proposal.md
+++ /dev/null
@@ -1,37 +0,0 @@
-## Why
-
-The MCP server now exposes 17 tools spanning every actionable UI surface (read / write / queue / aux / long-running). Today the only documentation is the inline tool `description` field — discoverable from inside an MCP client, but invisible to anyone deciding whether to install the server or troubleshooting a broken setup.
-
-CLAUDE.md is explicit that `README.md` is **hand-maintained** — the pipeline never overwrites it. All MCP-facing docs must be authored as a manual edit, modeled on the existing README walkthrough sections.
-
-This change sequences last in the v1 epic because the tool surface had to stabilize first; the 17 tools are now locked.
-
-## What Changes
-
-- New `## MCP server` section in `README.md` covering:
- - **One-line install** (`bash scripts/install-mcp.sh`) — the canonical entry point from a cloned repo.
- - **Verifying the install** — `claude mcp list` expected output + restart-required note for Claude Desktop and Cursor.
- - **Tool reference** — table of all 17 tools grouped by category (Read / Applied / Queue / Aux / Long-running), one-sentence description per tool, "called by" mapping to the UI control it mirrors.
- - **Troubleshooting** — six failure modes drawn from the build's risk notes: stale PID file, worker-not-running enqueue, no LLM CLI detected, `jobs.json` missing, JSON-RPC framing corruption from a stray `console.log`, install-script prereq failure.
- - **Architecture note** — one-paragraph "MCP server is the fourth direct consumer of `src/lib/*`" pointer to `src/mcp/`.
-- Update `CLAUDE.md` with a new `## MCP server` subsection paralleling the existing Settings / Jobs / Jinder docs, listing the tool surface and pointing at `src/mcp/` for future Claude Code sessions.
-
-This is **doc only** — no code changes, no test changes. README and CLAUDE.md are the only files touched.
-
-## Capabilities
-
-### New Capabilities
-
-- `mcp-server-docs`: User-facing documentation for the MCP server feature — install flow, tool reference, troubleshooting. Lives in the hand-maintained `README.md` and the Claude-Code-facing `CLAUDE.md`.
-
-### Modified Capabilities
-
-_None — this is net-new documentation; no existing spec changes._
-
-## Impact
-
-- **Files touched:** `README.md`, `CLAUDE.md`. No code, no tests, no CI.
-- **Affected APIs:** none.
-- **Affected dependencies:** none.
-- **Risk:** none on the runtime path. Sole risk is doc drift — every future MCP tool change must update the tool-reference table. Add a CLAUDE.md note to that effect to make the obligation explicit.
-- **Audience:** any user installing the MCP server via `scripts/install-mcp.sh` + any future Claude Code session navigating `src/mcp/`.
diff --git a/openspec/changes/mcp-server-readme-docs/specs/mcp-server-docs/spec.md b/openspec/changes/mcp-server-readme-docs/specs/mcp-server-docs/spec.md
deleted file mode 100644
index 15329d3..0000000
--- a/openspec/changes/mcp-server-readme-docs/specs/mcp-server-docs/spec.md
+++ /dev/null
@@ -1,55 +0,0 @@
-## ADDED Requirements
-
-### Requirement: README documents the MCP server install path
-
-The `README.md` SHALL include a top-level `## MCP server` section that, at minimum, presents the one-line install command (`bash scripts/install-mcp.sh`) AND the verification command (`claude mcp list`).
-
-#### Scenario: A new user installs the MCP server from the README
-
-- **GIVEN** a user has cloned the repo
-- **WHEN** they read `README.md` linearly
-- **THEN** they find the `## MCP server` heading within the document
-- **AND** the section contains the literal string `bash scripts/install-mcp.sh`
-- **AND** the section contains the literal string `claude mcp list`
-
-### Requirement: README documents every registered MCP tool
-
-The `README.md` `## MCP server` section SHALL include a tool reference covering every tool registered by `createMcpServer()` in `src/mcp/server.ts`. Each tool documented MUST appear in the live `tools/list` JSON-RPC response.
-
-#### Scenario: A reader counts tools in the README
-
-- **GIVEN** the README's tool-reference table
-- **WHEN** counted against the names returned by `tools/list` from `pnpm run mcp`
-- **THEN** the two sets match exactly (no missing, no extra)
-
-#### Scenario: A new tool is added to the server
-
-- **GIVEN** a new file under `src/mcp/tools/` calling `server.registerTool(...)`
-- **WHEN** the PR adding it is opened
-- **THEN** the PR MUST also update the README tool-reference table — the obligation is documented in `CLAUDE.md`
-
-### Requirement: README documents at least six troubleshooting scenarios
-
-The `## MCP server` section SHALL include a "Troubleshooting" subsection covering at minimum:
-1. MCP client does not list `pupila` after install
-2. `enqueue_apply` returns a worker-not-running warning
-3. `regenerate_profile` returns a precondition error
-4. `list_jobs` returns zero rows on a fresh clone
-5. Garbled output / JSON-RPC framing corruption
-6. `scripts/install-mcp.sh` exits on missing prereq
-
-#### Scenario: A user hits a failure not in the troubleshooting list
-
-- **GIVEN** a documented failure mode missing from the section
-- **WHEN** a user files an issue
-- **THEN** the maintainer adds the failure mode to the README before closing — the section is a living rolodex, not a one-shot doc
-
-### Requirement: CLAUDE.md documents the MCP server architecture
-
-The hand-maintained `CLAUDE.md` SHALL include a `## MCP server` section paralleling the existing Settings / Jobs / Jinder documentation depth, covering the repo layout under `src/mcp/`, the stdio-transport invariant (no `console.log`), and the per-tool single-flight invariants.
-
-#### Scenario: A future Claude Code session reads CLAUDE.md
-
-- **GIVEN** a future agent navigating `src/mcp/` for the first time
-- **WHEN** they read `CLAUDE.md` end-to-end
-- **THEN** they find the architecture rationale, the stdio invariant, and the single-flight locks WITHOUT reading any source code
diff --git a/openspec/changes/mcp-server-readme-docs/tasks.md b/openspec/changes/mcp-server-readme-docs/tasks.md
deleted file mode 100644
index 55161fb..0000000
--- a/openspec/changes/mcp-server-readme-docs/tasks.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## Tasks
-
-### 1. README — MCP server section
-
-- [ ] Open `README.md`. Find the existing "Local UI" / "AI per-job review" sections — model the new `## MCP server` heading on their depth (≤ 80 lines total, code-blocks-and-tables heavy).
-- [ ] Add the one-line install command at the top of the section: `bash scripts/install-mcp.sh` (note: must be run from a cloned repo, not via curl|bash — the script is repo-coupled).
-- [ ] Add the **Verifying the install** subsection with expected `claude mcp list` output showing `pupila` registered, plus a "restart Claude Desktop / Cursor to load" note.
-- [ ] Add the **Tool reference** table grouped by category. Five columns: tool name, category, input fields, returns, UI equivalent. All 17 tools — copy the live `description` fields from `src/mcp/tools/*.ts`.
-- [ ] Add the **Troubleshooting** subsection covering exactly six failure modes:
- - [ ] `claude mcp list` does not show `pupila` after install (Claude Desktop / Cursor not restarted)
- - [ ] `enqueue_apply` returns warning "Apply worker is not running" (start `pnpm run apply-worker` in another terminal)
- - [ ] `regenerate_profile` returns precondition error (run `pnpm run setup-brief --file ~/cv.pdf` first)
- - [ ] `list_jobs` returns `total: 0` on a fresh clone (run `pnpm run dev` once to seed `data/jobs.json`)
- - [ ] User sees garbled output in their MCP client (a tool somewhere logged to stdout — file a bug citing `src/mcp/lib/stdout-guard.ts`)
- - [ ] `scripts/install-mcp.sh` exits 1 with "missing prerequisite" (install node 22 / pnpm / git per the prereq-check failure message; **never** auto-install)
-- [ ] Add the **Architecture** paragraph at the bottom of the section: "The MCP server is the fourth direct consumer of `src/lib/*` (alongside Vite middleware, apply-worker, and `pnpm run ai-review`). Tools live in `src/mcp/tools/`, shared logic in `src/lib/`." Link to `src/mcp/`.
-
-### 2. CLAUDE.md — MCP server subsection
-
-- [ ] Add a new `## MCP server` section after `## AI per-job review`, modeled on the existing `## Jinder (swipe-to-apply)` depth.
-- [ ] Cover: repo layout (`src/mcp/{index,server,paths,errors}.ts`, `src/mcp/{schemas,tools,lib}/`), entry point (`pnpm run mcp` / `tsx src/mcp/index.ts`), stdio-transport invariant (no `console.log` ever — `src/mcp/lib/stdout-guard.ts` is the guardrail), the single-flight invariants for `trigger_fetch` and `regenerate_profile`, and the fact that `enqueue_apply` does NOT replace `pnpm run apply-worker`.
-- [ ] Add the explicit obligation: "Every future MCP tool change MUST update the README tool-reference table — there is no codegen for it."
-- [ ] Update `## Repo layout` to include the new `src/mcp/` tree and `scripts/install-mcp.sh` / `scripts/_merge-mcp-config.mjs`.
-
-### 3. Validation
-
-- [ ] `pnpm typecheck`, `pnpm test`, `pnpm run lint` all unchanged (this is doc-only — no regressions possible).
-- [ ] Hand-review the README rendering on GitHub web (no `pnpm run` for README preview — eyeball it on the PR diff).
-- [ ] Cross-check the tool-reference table against `pnpm run mcp | tools/list` to make sure every registered tool is documented.
diff --git a/src/mcp/CLAUDE.md b/src/mcp/CLAUDE.md
index b2f845f..cf7f2bb 100644
--- a/src/mcp/CLAUDE.md
+++ b/src/mcp/CLAUDE.md
@@ -126,9 +126,8 @@ src/mcp/
## When something changes
-- **README tool-reference table is hand-maintained.** Every tool change MUST update the `## MCP server` section in `README.md`. There is no codegen. See `openspec/changes/mcp-server-readme-docs/`.
+- **README tool-reference table is hand-maintained.** Every tool change MUST update the `## MCP server` section in `README.md`. There is no codegen.
- **Verify locally**: `pnpm run mcp` in one terminal, hit it from a connected client (Claude Code/Desktop/Cursor). Or use the in-process integration test.
-- **CI smoke check** (when DEV-84 lands): `pnpm run mcp:smoke` will spawn the server, send `tools/list`, assert all expected tools present.
## Related