# Changelog

All notable changes to `agent-bober` will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.19.0] — 2026-07-26

### Fixed

- **Config bootstrap in plugin / standalone installs**: `bober.plan` and `bober.run` bootstrapped `bober.config.json` by telling the model to reference `src/config/schema.ts` and `src/config/defaults.ts` — source files that are **absent** when the plugin (or a bare `.claude/` copy) runs inside a user's project, so the config was frequently never written and every downstream step failed with a missing-config error. Both skills now embed a self-contained default config inline, make the write step **non-optional** (create-then-adjust rather than ask-and-wait), and no longer depend on the agent-bober source tree.
- **`run` prerequisites check on plugin installs**: `bober.run` step 1b ran `bash scripts/check-prereqs.sh` and stopped the run if it failed — but that script is not present in a plugin/standalone project, so the run dead-ended. The step now skips gracefully when the script is absent and only enforces it when present.

### Changed

- **Release catch-up to `main`**: first published release since `0.18.0` to include the work merged via [#55](https://github.com/BOBER3r/agent-bober/pull/55) — the cross-domain knowledge platform, medical pipeline, SEO agent/skill suite, stack-aware security auditor, and fleet orchestrator. The npm package and the plugin manifest (`.claude-plugin/plugin.json`) both track `main` at `0.19.0`.

## [0.18.0] — 2026-06-23

### Fixed

- **Standalone `plan` → `sprint` flow**: `plan` now materializes sprint contracts to `.bober/contracts/` after planning. Previously only the `run` pipeline did this, so a standalone `plan` followed by `sprint` failed with "No sprint contracts found." `plan answer` likewise materializes contracts when it resolves a spec to `ready`, and the next-step hints now correctly point to `npx agent-bober sprint`.

### Added

- **Shared `materializeContracts` helper** (`src/orchestrator/contract-materialization.ts`) used by both `plan` and the `run` pipeline as the single source of truth for spec → contract materialization. It prefers valid embedded `spec.sprints[]` (e.g. planner-authored contracts), falling back to feature-derived generation when they are absent or invalid. Contract ids are now deterministic and zero-padded (`sprint-<specId>-NN`) so `listContracts` ordering matches execution order at ten or more sprints.
- **`run` honors embedded `spec.sprints[]`**: when the planner emits valid embedded contracts, `run` uses them and skips the per-feature precision LLM calls.

### Changed

- **`sprint` scopes to the active spec**: contract selection is now filtered to the latest spec's `specId`, so stale contracts from other specs are not executed. `sprint` also refuses a `needs-clarification` spec (printing the open questions and the correct `plan answer` command) and gives a clearer empty-contracts message pointing at `plan` or `run`.

## [0.17.1] — 2026-06-13

### Added

- **`agent-bober update` command**: refreshes a project's installed Claude Code slash commands (`.claude/commands/`) and agent definitions (`.claude/agents/`) from the currently-installed package — the end-user upgrade path after `npm i -g agent-bober@latest`. It respects the project's recorded `mode`/`preset` so the installed command set matches what `init` chose, and it is **non-destructive**: `bober.config.json`, `.bober/` state, and `.gitignore` are never touched. Errors out (exit 1) if no `bober.config.json` is present.

### Fixed

- **Plugin manifest version drift**: `.claude-plugin/plugin.json` was pinned at `0.15.0` and the marketplace/README advertised "10 subagents"; both now track the real release (`0.17.x`, 11 subagents) so `/plugin update bober` advertises the correct version.

## [0.17.0] — 2026-06-13

### Added

- **Per-sprint documenter** ([#41](https://github.com/BOBER3r/agent-bober/pull/41)): a new `documenter` agent spawned after a sprint's evaluator returns PASS — it writes a concise record of what the sprint built and finds & updates the existing docs (README, ADRs, CLAUDE.md, module docs) while the change is fresh, instead of batching all docs into a final sprint. Documentation only (never touches application code or tests) and **advisory** — a documenter failure or timeout never downgrades the already-passed sprint. On by default; configure via the `documenter` config section (`enabled`, `model`, `maxTurns`, `timeoutMs`).
- **`simplicity` lens** ([#39](https://github.com/BOBER3r/agent-bober/pull/39)): a complexity-only (YAGNI) lens added to both the evaluator (`evaluator.panel`) and architect (`architect.panel`) lens panels. It surfaces reinvented standard-library code, dependencies doing what a native platform feature already does, single-implementation abstractions, dead flexibility, and logic that could be materially shorter — and is explicitly forbidden from ever flagging a test, a validation at a trust boundary, error handling, security, or accessibility as deletable. Mirrored in `skills/shared/{lens-panel,arch-lens-panel}.md` with the existing drift/parity gates.
- **`bober:` ceiling-comment convention** ([#39](https://github.com/BOBER3r/agent-bober/pull/39)): the generator marks a deliberate simplification that has a known ceiling with a `bober:` comment naming the ceiling **and** the upgrade path (e.g. `// bober: global lock, per-account locks if throughput matters`). The code-reviewer treats a marked shortcut as intent and an unmarked shortcut with an obvious ceiling as a finding; the evaluator treats a marked simplification as not-a-smell (scoped strictly to code-quality, never to the test/verification discipline).

### Fixed

- **Stale plugin `.claude/` copies**: regenerated the `bober-planner` agent + `bober-plan` command and the `bober-documenter` agent copies that had drifted from their canonical `agents/` / `skills/` sources (the planner's bounded-lessons-index step and the new documenter agent were missing from the plugin surface). Run `npm run update-all` to keep these in sync.
- **Untracked plugin agent/command copies now committed**: the `bober-diagnoser`, `bober-deployer`, and `bober-postmortemer` incident agents and the `bober-graph` / `bober-impact` / `bober-onboard` commands existed on disk but were never tracked, so they did not ship on the plugin surface for everyone. They are now committed (canonical sources were already tracked); all six are provider-agnostic and honour the configured provider (Anthropic / DeepSeek / OpenAI-compatible).

## [0.16.0] — 2026-06-04

### Added

- **Multi-provider support — DeepSeek** ([#21](https://github.com/BOBER3r/agent-bober/pull/21), [#24](https://github.com/BOBER3r/agent-bober/pull/24)): DeepSeek is now a first-class provider via the built-in `openai-compat` adapter pointed at `https://api.deepseek.com`. Shorthands `deepseek` / `deepseek-v4-pro` / `deepseek-v4-flash` auto-set the endpoint; set `DEEPSEEK_API_KEY`. Supports **all** roles including tool-calling (curator, generator, evaluator, code-reviewer). See [`docs/providers.md`](docs/providers.md).
- **Multi-provider support — claude-code (subscription)** ([#21](https://github.com/BOBER3r/agent-bober/pull/21), [#24](https://github.com/BOBER3r/agent-bober/pull/24)): a no-API-key `ClaudeCodeAdapter` that shells out to the `claude` CLI on your Claude subscription (`binary` / `timeoutMs` overrides). Planner and researcher roles only — tool-using roles fall back to another configured provider (role-aware fallback).
- **Evaluator lens panel** ([#25](https://github.com/BOBER3r/agent-bober/pull/25), [#26](https://github.com/BOBER3r/agent-bober/pull/26)): opt-in `evaluator.panel` runs the evaluation across multiple independent lenses (`correctness`, `security`, `regression`, `quality`) with bounded fan-out and a reconcile step, emitting per-lens verdict telemetry. Off by default — byte-identical behavior when disabled.
- **Architect lens panel** ([#27](https://github.com/BOBER3r/agent-bober/pull/27)): opt-in `architect.panel` gates the architecture approach-selection and review checkpoints into bounded per-lens fan-out (`scalability`, `security`, `cost`, `operability`, `maintainability`, `reversibility`) with a fail-closed reconcile. Off by default.
- **Native lens-panel surface**: an optional `lensVerdicts` field on the evaluator result schema plus lens-aware evaluator/architect agent modes and a parity/drift gate, so the Claude Code plugin surface mirrors the TypeScript panel behavior. Canonical references at `skills/shared/lens-panel.md` and `skills/shared/arch-lens-panel.md`.
- **Config-selectable orchestration engine**: `pipeline.engine` (`'ts'` | `'skill'` | `'workflow'`, default `'ts'`) selects the pipeline orchestration engine behind an engine-selection seam, with an eligibility probe that downgrades `workflow` → `ts` when ineligible or in `careful` mode. No behavior change on the default `ts` path.
- **Graph telemetry + `update-all`** ([#19](https://github.com/BOBER3r/agent-bober/pull/19), [#20](https://github.com/BOBER3r/agent-bober/pull/20)): tokensave code-graph preflight telemetry written to `.bober/history.jsonl`, and an `update-all` sync flow (`npm run update-all`) that keeps the CLI, skills, agents, and plugin marketplace in sync.
- **Preset-aware slash-command installation** ([#11](https://github.com/BOBER3r/agent-bober/pull/11), [#12](https://github.com/BOBER3r/agent-bober/pull/12)) *(shipped in 0.12.0, documented here)*: `bober init` now installs only the universal commands plus the commands relevant to the chosen preset, instead of every command.

### Fixed

- **Plugin PostToolUse hooks schema** ([#22](https://github.com/BOBER3r/agent-bober/pull/22), [#23](https://github.com/BOBER3r/agent-bober/pull/23)): PostToolUse hooks are now wrapped in the required `hooks[]` array so the Claude Code plugin loads them correctly.

## [0.15.0] — 2026-05-29

### Added

- **Claude Opus 4.8 support** ([#17](https://github.com/BOBER3r/agent-bober/pull/17)): the `opus` shorthand now resolves to `claude-opus-4-8` (1M context, adaptive thinking); added an `opus-4-7` shorthand to pin the previous model.
- **`@anthropic-ai/sdk` upgraded `0.39.0` → `0.100.1`** to expose the Opus 4.8 request fields, with zero adapter behavior change.
- **Anthropic prompt caching** (pattern borrowed from nousresearch/hermes-agent): ephemeral `cache_control` breakpoints on the system prompt + recent messages (system-and-last-3, capped at 4 per request), behind `providerConfig.promptCaching` — default **on** for Anthropic, no-op for other providers.
- **`effort` control**: optional `ChatParams.effort` (`low` | `medium` | `high` | `xhigh` | `max`) forwarded as top-level `output_config.effort`; omitted when unset so the API default (`high` on Opus 4.8) applies. Non-Anthropic adapters ignore it.
- **Mid-conversation system blocks**: a `SystemUpdateMessage` message variant renders to an Anthropic `mid_conv_system` content block with optional ephemeral `cache_control`; OpenAI and Google adapters handle it best-effort without error.
- **`bober_list_pending_approvals`**: List all pending careful-flow checkpoints awaiting human
  approval. Accepts optional `{ projectPath?: string }` (must be absolute when supplied; defaults
  to cwd). Returns `[{ checkpointId, ageMs, prompt }]` — identical shape to `bober list-approvals
  --json`. Backed by the new shared `listPendingApprovals(projectRoot)` helper in
  `src/state/approval-state.ts`.
- **`bober_approve_checkpoint`**: Approve a pending checkpoint over MCP by writing
  `.bober/approvals/<id>.approved.json` with the same payload shape as `bober approve`
  (`{ approvedAt, approverId, editDelta? }`). Accepts `{ checkpointId, projectPath?, editDelta? }`.
  Guards with `pendingExists` before writing. Returns `{ approvedAt, checkpointId }`.
- **`bober_reject_checkpoint`**: Reject a pending checkpoint over MCP by writing
  `.bober/approvals/<id>.rejected.json` with the same payload shape as `bober reject`
  (`{ rejectedAt, rejecterId, feedback }`). Accepts `{ checkpointId, projectPath?, feedback }`
  (feedback required and non-empty). Guards with `pendingExists` before writing.
  Returns `{ rejectedAt, checkpointId }`.
- **`bober_list_projects`**: Enumerate bober projects under one or more search roots.
  Accepts `{ searchRoots: string[] }`. Walks each root one level deep; returns
  `[{ projectPath, name, mode?, hasActiveRuns, lastRunAt? }]` for every directory
  containing `bober.config.json`. Unreadable roots are skipped with a stderr warning.
  READ-ONLY — does not instantiate RunManager; reads `.bober/runs/*/state.json` directly.
- **`bober_list_specs`**: List PlanSpecs in a project. Accepts `{ projectPath }`.
  Reads `.bober/specs/*.json` with loose parsing (invalid files silently skipped).
  Returns `[{ specId, title, status, sprintCount, completedAt? }]`.
- **`bober_get_project_state`**: Aggregate per-project state counts for the cockpit sidebar.
  Accepts `{ projectPath }`. Returns `{ configExists, activeRunCount, lastRunAt?,
  openIncidentCount, pendingApprovalCount, specCount, mode? }`. READ-ONLY — does not
  instantiate RunManager.
- All six new tools accept an optional `projectPath` (required for discovery tools; optional
  for approval tools). When supplied, `projectPath` must be absolute — a relative path returns
  a soft-error JSON `{ error: "projectPath must be absolute" }` rather than throwing.
- **`listPendingApprovals(projectRoot)`** helper extracted from
  `src/cli/commands/list-approvals.ts` into `src/state/approval-state.ts`. Both the CLI and
  the MCP tool `bober_list_pending_approvals` share this helper. Exported from
  `src/state/index.ts` as `listPendingApprovals` / `PendingApprovalRow`.
- **`readRunStatesFromDisk(projectRoot)`** helper added to `src/state/run-state.ts` as a
  named alias for `listRunStateFiles`. Exported from `src/state/index.ts`. Cockpit discovery
  tools use it to enumerate run states for arbitrary project roots without touching the
  RunManager singleton.
- MCP tool count: **23 → 29**.

- **`bober_run_in_worktree`**: Start a pipeline inside an isolated git worktree on a new branch.
  Input: `{ task: string, allowDirty?: boolean, keepOnSuccess?: boolean }`. Returns
  `{ runId, branch, worktreePath, status: 'running' }` immediately (fire-and-forget like `bober_run`).
  Multiple worktree runs can execute concurrently on the same project. Use `bober_get_run_status`
  to track progress.
- **`bober worktree run <task>`** CLI subcommand mirroring the MCP tool. Flags:
  `--allow-dirty` (skip uncommitted-changes guard), `--keep-on-success` (retain worktree after success).
  Prints `{ runId, branch, worktreePath, projectRoot }` JSON to stdout.
- **`runInWorktree(task, projectRoot, config, opts)`** (`src/orchestrator/worktree.ts`): the shared helper
  the CLI and MCP tool both use. Creates a git worktree under `<pipeline.worktreeRoot>/<runId>` on a
  branch derived from `generator.branchPattern`, runs the pipeline inside it, and on success removes
  the worktree per `pipeline.cleanupWorktreeOnSuccess`. On failure (or if `--keep-on-success`/`keepOnSuccess`)
  the worktree is retained for debugging and its path is printed to stderr.
- **`pipeline.worktreeRoot`** config field: directory (relative to projectRoot) under which
  worktrees are created. Default `.bober/worktrees`.
- **`pipeline.cleanupWorktreeOnSuccess`** config field: when true (default), remove the worktree
  via `git worktree remove` after a successful run. On failure the worktree is always retained.
- **`RunState.worktreePath`** and **`RunState.branch`** optional fields. Populated by `runInWorktree`
  before the pipeline starts; surfaced in `bober_get_run_status` output.
- **`RunManager.startRun(task, projectRoot, config, pipelineFn?, opts?)`** signature extended with
  optional `opts: { runId?, worktreePath?, branch? }`. Existing 3- and 4-arg callers are unchanged.
- **`git.ts`** helpers: `addWorktree`, `removeWorktree`, `isClean` shelling out to git CLI (no new deps).

### Follow-ups (documented, NOT implemented this sprint)

- Garbage collection of orphaned worktrees from prior failed runs (`bober worktree prune`).
- Worktree-aware bober_status (the cockpit uses bober_get_run_status by runId instead).
- Cross-worktree merge automation.

- **`bober_subscribe_events`**: Subscribe to runId-scoped live events. Input: `{ runId: string, since?: string }`. Returns `{ subscriptionId, status: 'subscribed', startedAt }`. The server begins emitting `bober/events` notifications for every line appended to `.bober/history.jsonl` or `.bober/telemetry/<date>.jsonl` whose `runId` matches the subscription. The optional `since` parameter triggers a one-time backfill of pre-existing events with `timestamp > since`.
- **`bober_unsubscribe_events`**: Unsubscribe from a runId-scoped event stream. Input: `{ subscriptionId: string }`. Releases file-watch handles when no other subscription is watching the same files. Returns `{ subscriptionId, status: 'unsubscribed' }` or a soft-error `{ error: 'Subscription not found: <id>' }`.
- **`EventStreamManager`** (`src/mcp/event-stream.ts`): In-process class that tails `.bober/history.jsonl` and `.bober/telemetry/<date>.jsonl` using `fs.watch`. One file-watch handle is shared across all subscriptions watching the same file (reference-counted). Date roll-over is detected via a polling interval (5 s, `unref()`'d). Lines without an extractable `runId` (top-level or `details.runId`) are silently skipped. Per-subscription bounded queue (default 1000) drops the oldest events on overflow; a single `bober/events.dropped` notification with `{ subscriptionId, dropped: N }` is emitted once per overflow window. All diagnostic output is routed to `process.stderr` (stdout is reserved for the MCP JSON-RPC transport).
- **`pipeline.eventQueueBound`** config field: per-subscription bounded queue limit. Default `1000`, minimum `1`. Readable by the server from `bober.config.json` and passed to `EventStreamManager` at startup.
- **`bober_list_active_runs`**: Lists all runs tracked by the RunManager. Accepts optional `{ status?: 'running'|'completed'|'failed'|'aborted' }` filter. Returns a JSON array of RunState objects. Omit the filter to get all runs regardless of status.
- **`bober_get_run_status`**: Fetches the full RunState for a specific run by `runId`. Input: `{ runId: string }` (required). Returns the complete RunState JSON or `{ error: 'Run not found: <runId>' }` when the runId is unknown.
- **`bober_abort_run`**: Aborts a currently running pipeline run. Input: `{ runId: string, reason?: string }`. Flips `status` to `'aborted'`, persists `abortedAt` and `abortReason` to `state.json`, and returns `{ runId, status: 'aborted', abortedAt }`. Returns `{ error: 'Run not found: <runId>' }` for unknown runs, `{ error: 'Run is not active' }` for non-running runs. Note: this sprint flips state only; forceful in-flight subprocess termination (SIGTERM propagation) is deferred to a future hardening sprint.
- **`RunState.status`** type union widened to include `'aborted'`; new optional fields `abortedAt?: string` and `abortReason?: string` added.

## [0.14.0] — 2026-05-25

Bober Vision — agent-bober becomes a four-mode software engineering teammate
instead of a single-mode autopilot. The pipeline you already know is Mode 1;
the other three modes share its scaffolding (planner, evaluator, audit log)
but pause at different boundaries and operate at different blast radii.

The headline change: it is now safe to delegate production-touching work.
Careful-flow gates every meaningful boundary; Diagnose has a native vocabulary
for incidents; Postmortem auto-synthesizes from the audit trail.

See [VISION.md](./VISION.md) for the full design rationale and example flows.

### Added

- **Mode 1 — Autopilot** (unchanged): the existing generator-evaluator loop.
  Use for spikes and greenfield. `bober run`.
- **Mode 2 — Careful-flow**: checkpoint-gated execution. Every
  research/plan/sprint boundary surfaces a diff and waits for approval via
  CLI prompt, disk marker (`.bober/approvals/*.pending.json`), or GitHub PR.
  Per-run audit log at `.bober/audits/<runId>.jsonl`. New CLI:
  `bober approve <checkpointId>`, `bober reject <checkpointId>`,
  `bober list-approvals`, `bober audit-show <runId>`.
- **Mode 3 — Diagnose**: production-incident response. Generic observability
  MCP plugin slots (any MCP server matching the schema can supply metrics
  and logs). Structured incident timeline at `.bober/incidents/<id>/`.
  Change-management gates around destructive actions. Playbook library
  searchable by symptom. New CLI: `bober incident [start|status|end|list|abort]`,
  `bober rollback <incidentId>`, `bober playbook [list|show|search]`.
- **Mode 4 — Postmortem**: auto-synthesized from incident artifacts when an
  incident transitions to `resolved`. Every claim has an inline citation
  back to timeline/changelog/observations. Required sections enforced
  (TL;DR, Impact, Timeline, Root Cause 5-Whys, Contributing Factors, What
  Went Well, What Went Wrong, Action Items). New CLI:
  `bober postmortem [generate|show] <incidentId>`.
- **Behavioral discipline foundation** (verbatim port of obra/superpowers):
  Iron Laws, Red Flags, Rationalization-Prevention tables, SessionStart
  bootstrap, anti-pattern catalog, AGENTS.md contract. Surfaces as 9 new
  universal skills: `bober.using-bober`, `bober.verify`, `bober.debug`,
  `bober.code-review`, `bober.incident`, `bober.diagnose`, `bober.deploy`,
  `bober.runbook`, `bober.postmortem`. Installed by `agent-bober init` and
  copied to `.claude/commands/`.
- **4 new agent definitions** in `agents/`: `bober-code-reviewer`,
  `bober-diagnoser`, `bober-deployer`, `bober-postmortemer`.
- **Opt-in local-only telemetry** (default OFF). When enabled, writes mode-0600
  JSONL events to `.bober/telemetry/<date>.jsonl`. ESLint rule
  (`no-restricted-imports` + `no-restricted-globals`) scoped to
  `src/telemetry/**` blocks all network primitives at lint time. Privacy
  invariant: only IDs, counts, durations, and enums in payloads; never
  user-content strings. New CLI: `bober telemetry [status|purge|export]`.
- **Config schema migration**: `bober config migrate` rewrites an existing
  `bober.config.json` to explicitly include all new vision-era fields with
  default values. Back-compat parsing handles missing fields automatically —
  the migrate command is informative, not required.
- **Mode + checkpoint config**: `pipeline.mode` (`autopilot` | `careful`,
  default `autopilot`), `pipeline.checkpointMechanism` (`noop` | `disk` |
  `cli` | `github-pr`, default `noop`). All optional; absence means autopilot.
- **End-to-end correctness gate**: `tests/e2e/four-modes.test.ts` (11 tests)
  exercises all four modes on a fixture project. Uses the real
  `DiskCheckpointMechanism` (not a mock), spawns the real
  `ExternalMcpServer` subprocess for the MCP protocol boundary, runs the
  real incident lifecycle including `verifyResolution`, and validates the
  auto-generated postmortem against the required-sections + citation rules.

### Changed

- `src/cli/commands/init.ts`: `UNIVERSAL_COMMANDS` extended with the 9 new
  vision skills. `agentFiles` extended with the 4 new agent definitions.
  All vision-era surfaces now ship with every brownfield/preset init.
- `VISION.md`, `README.md`, `AGENTS.md` updated to document the four modes,
  the careful-flow mechanisms, the incident lifecycle, the telemetry
  guarantee, and the slash-command set.

### Tests

563 → **1115 tests passing** across 82 test files (4 pre-existing skipped).
New test coverage includes: incident timeline + state machine, resolution
verification, rollback (full + per-step gates), postmortem section/citation
assertions, playbook search, careful-flow integration, observability MCP
spawn, deployer change classification + ChangeEntry recording, config
schema back-compat, telemetry writer (mode-0600, default-off, concurrency,
privacy), CLI subcommands for config/telemetry.

### Backward compatibility

- Existing `bober.config.json` files (pre-vision) parse cleanly with the
  extended schema — all new fields are optional and default to current
  autopilot behavior. No migration required.
- Existing CLI surface (`bober plan`, `bober sprint`, `bober eval`,
  `bober run`, `bober graph`, etc.) is unchanged.
- Telemetry defaults OFF. No network egress under any condition; enforced
  statically by ESLint and verified at runtime.

## [0.13.0] — 2026-05-24

Graph (tokensave) integration — user-facing CLI commands and slash-command skills for code-graph workflows.

### Added

- **`agent-bober graph [init|sync|status]`** — manage the code graph index.
  - `init`: runs `tokensave init`, writes `.bober/graph/manifest.json`. Exits 2 with platform-aware install hint when tokensave is missing.
  - `sync [--force]`: re-indexes changed files (full re-index with `--force`). Updates manifest.
  - `status [--json]`: prints `{ready, indexedFileCount, tokensaveVersion, lastSyncedHeadSha, stale}`. Human-readable or JSON.
- **`agent-bober onboard`** — generate 5 onboarding markdown files in `.bober/onboarding/` using the code graph (architecture overview, hotspots, knowledge gaps, communities, README). Prints a summary table on completion.
- **`agent-bober impact <symbol|file>`** — analyse the impact radius of a symbol or file. Writes `.bober/graph/impact/<slug>.md` with sections `# Impact: <target>`, `## Affected symbols`, `## Tests covering this symbol`.
- **3 new universal skills** (installed in all presets and brownfield):
  - `skills/bober.graph/SKILL.md` (`/bober-graph`) — code graph management
  - `skills/bober.onboard/SKILL.md` (`/bober-onboard`) — onboarding doc generation
  - `skills/bober.impact/SKILL.md` (`/bober-impact`) — impact analysis (with `argument-hint: <symbol|file>`)
- **`scripts/e2e-graph-smoke.sh`** — end-to-end smoke test script (gates on tokensave binary availability).
- Architecture document: [`.bober/architecture/arch-20260524-port-code-review-graph-architecture.md`](.bober/architecture/arch-20260524-port-code-review-graph-architecture.md)

### Changed

- All 3 graph commands respect `graph.enabled=false` — exit 1 with message: *"Graph integration is disabled. Enable via `graph.enabled: true` in bober.config.json."*
- `src/cli/commands/init.ts` skill map: `bober.graph`, `bober.onboard`, `bober.impact` added to `UNIVERSAL_COMMANDS` — included in every preset and brownfield init.

### KPI gate result

60% combined reduction (synthetic-fixture baseline; real-pipeline measurement via `node scripts/run-kpi-gate.mjs`).

### Tests

549 → **563 tests passing** (added 14 tests: slug derivation, command success paths, disabled-graph paths, skill bundle frontmatter, init.ts skill inclusion).

## [0.12.0] — 2026-04-17

Tuned for Claude Opus 4.7 — the model now follows instructions literally and
no longer fills in blanks left by vague specs. This release pushes precision
discipline through the contract schemas, the planner, the generator, and the
evaluator so the harness stops doing bad work silently.

### Added

- **Structural ambiguity-score clarification gate.** Plans that are too vague
  to safely decompose are no longer fabricated into broken sprints. The
  planner now emits `status: "needs-clarification"` with structured
  `clarificationQuestions`, and the pipeline blocks until the user answers.
- **`bober plan answer <specId> [<questionId> "<answer>"]` CLI command.**
  Resolves clarification questions one-shot or via interactive walkthrough
  with `prompts`. Auto-promotes the spec to `status: "ready"` when the last
  open question is answered.
- **`PlanSpec` precision fields:** `status` (lifecycle enum), `mode`,
  `ambiguityScore` (0-10), `clarificationQuestions`, `resolvedClarifications`,
  `assumptions`, `outOfScope`. New helpers in `src/contracts/spec.ts`:
  `hasOpenClarifications`, `getOpenClarifications`, `isPipelineReady`,
  `resolveClarification`.
- **`SprintContract` precision fields:** `nonGoals`, `stopConditions`,
  `definitionOfDone`, `assumptions`, `outOfScope`, `ambiguityScore`. New
  helpers: `findPrecisionIssues`, `isContractPrecise`. `saveContract` rejects
  contracts containing banned vague phrases (`"works correctly"`,
  `"looks good"`, etc.).
- **Generator preflight (Step 0)** — refuses to start work on contracts with
  placeholder or missing precision fields, returning `status: "blocked"`
  immediately rather than burning tokens on a doomed implementation.
- **Evaluator nonGoals/outOfScope adherence check (Step 5.5)** — converts each
  contract `nonGoal` into a concrete `git diff` check; one violation fails
  the whole sprint regardless of success-criteria results.
- **`PlannerResult` discriminated union** — `runPlanner` returns
  `{ kind: "ready", spec } | { kind: "needs-clarification", spec }`. Callers
  must narrow on `kind`.
- **Migration script** at `scripts/migrate-specs.mjs` — converts legacy
  PlanSpec JSON files (`projectType` → `mode`, `id` → `featureId`,
  priority enum, etc.) to the new schema. Idempotent.
- **`scripts/sync-skills.mjs`** — splits inlined `.claude/commands/*.md`
  back into canonical `skills/*/SKILL.md` + `references/*.md` so the shipped
  npm package always matches the local install.
- New `CHANGELOG.md`.

### Changed

- **PlanSpec field renames:** `id` → `specId`, `projectType` → `mode`,
  `nonFunctional` → `nonFunctionalRequirements`. Feature shape: `id` →
  `featureId`, priority enum `must|should|could` → `must-have|should-have|nice-to-have`,
  `estimatedSprints` → `estimatedComplexity` (low/medium/high).
- **SprintContract field renames:** `id` → `contractId`, `feature` → `title`,
  `expectedChanges` → `estimatedFiles`. Criterion shape: `id` → `criterionId`,
  added required `required: boolean`, removed runtime-only `passed`.
  `verificationMethod` is now a strict enum
  (`manual|typecheck|lint|unit-test|playwright|api-check|build|agent-evaluation`)
  — free-form values are rejected.
- **Bumped Claude model defaults** to current valid IDs:
  `sonnet → claude-sonnet-4-6`, `haiku → claude-haiku-4-5`. `opus` was
  already correct at `claude-opus-4-7`.
- **Pipeline (`runPipeline`)** branches on `PlannerResult.kind`. When
  clarification is needed it logs the open questions, appends a
  `planning-needs-clarification` history event, and returns
  `{ success: false, needsClarification: true }` without spawning sprints.
- **Skills (`bober.plan`, `bober.run`, `bober.sprint`)** updated with
  spec-status triage, clarification surfacing, and the planner-result branch.
- **Agent prompts (`bober-planner`, `bober-generator`, `bober-evaluator`)**
  rewritten for the new schemas and the precision/clarification gates. Planner
  now emits Format A (ready) or Format B (needs-clarification) JSON summary.

### Migration notes

Existing on-disk PlanSpec files are migrated automatically by
`scripts/migrate-specs.mjs` (run once after upgrading; idempotent on
re-runs). Existing SprintContract files keep their richer on-disk shape but
must satisfy the new precision-field minimums when re-saved — re-running the
planner against an existing spec is the recommended path. Direct API
consumers of `runPlanner` must update to handle the new
`PlannerResult` discriminated return type.

### Fixed

- `loadSpec` / `listSpecs` no longer silently drop on-disk specs that the
  Zod schema didn't recognize. The schema now matches reality plus the new
  fields, and `saveContract` enforces the precision gate at the boundary.
- Removed a duplicate `<!-- Reference: contract-schema.md -->` block that had
  crept into `skills/bober.sprint/SKILL.md` from a previous re-init.

### Tests

225 → **251 tests passing** (added 22 spec-schema tests + 4 plan-answer CLI
tests; zero regressions in existing suites).
