CLAUDE.md · diff

git:20260722.41bcd96 to git:20260730.4e25f7d

1 added, 1 removed. Audit A to A.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What is Kōan
Kōan is an autonomous background agent that uses idle Claude API quota to work on local projects. It runs as a continuous loop, pulling missions from a shared queue (an authoritative SQLite mission store; `missions.md` is a generated read-only export — see `specs/004-mission-store/`), executing them via Claude Code CLI, and communicating progress via Telegram. Philosophy: "The agent proposes. The human decides." — no unsupervised code modifications.
## On-demand guidance (nested CLAUDE.md)
This root file is intentionally small. Detailed, scope-specific guidance lives in nested `CLAUDE.md` files that Claude Code auto-loads when you work in that subtree:
- **`koan/CLAUDE.md`** — Python rules: test suite, Python 3.11+ compatibility, ruff linting, temp-file & prompt-extraction conventions. Loads for all package code.
- **`koan/app/CLAUDE.md`** — Architecture overview, the full per-module reference (`Key modules`), and the `instance/` runtime-state layout. Loads when editing `koan/app/`.
- **`koan/skills/CLAUDE.md`** — Skills system, authoring conventions, and the "adding a new core skill" checklist. Loads when editing `koan/skills/`.
This content previously lived inline here (≈40k chars); it was split out to keep the always-loaded context small. All prior content is preserved verbatim in the nested files.
## Specs discipline (mandatory)
`specs/` is the **single source of truth for design** — *why* a component exists, the
contract it upholds, and what breaks if you change it. Specs drive the application; docs
explain how to use it (see `specs/README.md` for the specs-vs-docs split). This discipline
is **not optional**:
1. **Before implementing** any feature or refactor, READ the relevant spec first — via
`/brain ask` rather than blind grep, see "Documentation first" below:
- Component change → `specs/components/<group>.md` (core, agent-loop, bridge,
providers, git-github, issue-tracking, skills, web).
- Skill change → `specs/skills/<skill-name>.md`.
The spec tells you the invariants you must not silently break. Do not skip this because
a change "looks small" — small changes break contracts too.
2. **A durable-contract change is an ARCHITECTURAL change** (`specs/components/**`,
`specs/skills/**`). Change the spec **contract-first** — write the *intended* design,
then make the code conform. NEVER edit a durable spec afterward to match code you
already wrote; that turns the source of truth into a mirror of the implementation and
defeats the discipline. Such changes should be **rare**, and the PR MUST **declare**
them — check the "Architectural change" box in the PR body so the new architecture is
reviewed before approval. CI enforces this: `scripts/spec_change_guard.py` fails an
undeclared durable-contract change. See `docs/design/spec-changes-are-architectural.md`.
3. **No spec yet?** If you touch a component or skill that has no spec, WRITE one using
`specs/components/` conventions or `specs/skills/SKILL_SPEC_TEMPLATE.md` — and declare
it (a new contract is an architectural decision). Phase 1 ships specs for the
highest-impact pieces; the rest are added on-demand as they are touched.
4. **`specs/<NNN-slug>/` (speckit) is a different, ephemeral population** — the spec-first
*proposal* artifact, not the durable contracts this discipline governs. Change them
freely in-branch. See `specs/README.md`'s "components/, skills/ vs.
`<NNN-feature-slug>/`" section. When a speckit feature ships, the durable artifact is
the **updated `specs/components/<group>.md`** — landed contract-first and declared per
step 2, not retroactively bent to match the code you wrote.
5. **NEVER commit `.specify/feature.json`** — it is speckit's local current-feature
pointer (repointed automatically by `/speckit.*` commands), not a deliverable. It is
trivially picked up as an unrelated diff that trips the "no scope creep" review gate.
Before staging, confirm your PR does not touch it: `git diff --name-only main.. | grep
-q '.specify/feature.json' && git checkout main -- .specify/feature.json`. Only include
it when the PR's explicit purpose is to change speckit's active-feature pointer.
Specs and `docs/` coexist — most non-trivial changes update `docs/`. Durable specs are
different: anchor clean refactoring by changing the contract **deliberately and first**,
then making the code match — never the reverse.
## Documentation first
`docs/` and `specs/` are each independent OKF v0.1 knowledge bundles (see
`docs/SPEC.md` for the normative, bundle-agnostic spec; `docs/SCHEMA.md` and
`specs/SCHEMA.md` for the per-bundle conventions built on top of it), also jointly
indexed via the `wiki/` directory (`wiki/index.md`, `wiki/docs`, `wiki/specs-components`,
`wiki/specs-skills` symlinks — see `wiki/SCHEMA.md`). The **`/brain` skill**
(`.claude/skills/brain/`) is the entrypoint for consulting and extending both bundles —
it is fully self-sufficient (acquisition, index-first navigation, and OKF-conformance
glue), with no dependency on the `llm-wiki` plugin's `/wiki:*` commands (see
`wiki/SCHEMA.md` for why). Treat this as a hard rule on every feature, refactor, or
architecture decision: **read it first (Consult), update it last (Capture)**.
- **Consult — before you plan, research, or refactor.** Run `/brain ask "<question>"`
(or `/brain search`, an alias). It reads `wiki/index.md` first (never greps `docs/`/
`specs/` blindly), picks candidate pages from their one-line summaries, opens only
those (+ backlinks), and cites them by path. If the index doesn't clearly point to a
page, that's the signal coverage is missing — say so plainly rather than guessing.
State explicitly what you found (naming the docs) — or that nothing relevant existed
— before proposing changes. Never rely on source-code inspection alone when a
relevant doc exists. **In Plan Mode**, the plan file's Context section must state
what `docs/`/`specs/` said (or that nothing relevant was found) before the
recommended approach.
`/brain lint` (wrapping the broadened `scripts/wiki_check.py`) is a periodic health
check, not part of the per-task loop.
- **Source of truth order.** (1) current source code, (2) `docs/`/`specs/`, (3) inline
code comments, (4) assumptions. If docs conflict with the code, trust the code and
propose a docs update in the same change — never silently pick a side.
- **Capture — write/update after you implement.** After changing user behavior,
configuration, daemon flow, provider behavior, shared state, safety boundaries, or an
important implementation decision, create or update the relevant page under
`docs/architecture/`, `docs/users/`, `docs/providers/`, `docs/messaging/`,
`docs/operations/`, `docs/design/`, `docs/security/`, or `docs/setup/` (see
`docs/SCHEMA.md` for frontmatter/tagging conventions), and the matching
`specs/components/<group>.md` if the change touches a component's contract (see
"Specs discipline" above). Then run **`/brain sync`** to close the loop: it bumps
`updated:` frontmatter (and adds `description:` on new/touched pages only — existing
pages are not bulk-backfilled), regenerates any stale `index.md` via
`scripts/okf_backfill.py indexes`, and refreshes the page's `wiki/index.md` entry.
For core skill changes, also update `docs/users/user-manual.md` and
`docs/users/skills.md`.
- **Acquire — capture external material.** When a web article, an existing document,
or a stray idea worth preserving comes up mid-task (not tied to a feature you just
built), run **`/brain ingest <path|url|"idea sentence">`** — it snapshots the source
into the git-ignored `raw/` directory and compiles a durable, cited
`docs/reference/*.md` page. This is separate from feature docs: it's for knowledge
that doesn't originate from code you wrote.
- **Wiki bookkeeping is exempt from the "no unsupervised modification" principle
below** — frontmatter fields and `wiki/index.md`/`docs/index.md`/`specs/index.md`/
per-folder `index.md` entries are committed directly as part
of the same change/PR, no separate review step for that part specifically. This does
not extend to actual spec/contract, doc-body, or code changes. A CI job
(`.github/workflows/wiki-sync.yml`) backstops anything a session missed by pushing a
same-branch fix commit — never to `main`, never a separate PR.
- **`.claude/skills/` vs. `koan/skills/core/`.** `brain` (like the `speckit-*` skills)
is a Claude-Code-native project skill under `.claude/skills/`, invoked directly in a
session — it is not a `koan/skills/core/` runtime skill dispatched via Telegram/
GitHub/Jira, has no attachment to `app/skill_evals.py`'s eval harness, and is out of
scope for `koan/skills/CLAUDE.md`'s "adding a new core skill" checklist. Never
conflate the two.
## Commands
```bash
make setup # Create venv, install dependencies
make start # Start full stack (auto-detects provider: awake+run or ollama+awake+run)
make stop # Stop all running processes (run + awake + ollama)
make status # Show running process status
make logs # Watch live output from all processes + agent progress
make run # Start main agent loop (foreground)
make awake # Start Telegram bridge (foreground)
make ollama # Start full Ollama stack (ollama serve + awake + run)
make dashboard # Start Flask web dashboard (port 5001)
make lint # Run ruff linter (must pass before committing)
make test # Run full test suite (pytest + coverage summary)
make coverage # Run tests with detailed coverage report (HTML in htmlcov/)
make say m="..." # Send test message as if from Telegram
make missions [state=...] # List the mission queue straight from the store (break-glass; bridge-independent)
make mission-rm sel=i1 # Remove/abort a mission by selector (i<N>/p<N>/keyword) when the bridge is down
make rename-project old=X new=Y [apply=1] # Rename a project everywhere (dry-run by default)
make clean # Remove venv
```
Run a single test file:
```bash
KOAN_ROOT=/tmp/test-koan .venv/bin/pytest koan/tests/test_missions.py -v
```
## Architecture
Two parallel processes run independently: an **`awake.py`** Telegram bridge (polls Telegram, classifies chat vs. mission, flushes the outbox) and a **`run.py`** agent loop (pure-Python main loop that invokes the Claude CLI per mission and runs the lifecycle state machine). They communicate through shared files in `instance/` via atomic writes (`utils.atomic_write()`); exclusive process instances are enforced by `pid_manager.py` (PID file + `fcntl.flock()`).
The full two-process detail, the complete per-module reference (`Key modules`), and the `instance/` runtime-state file layout live in **`koan/app/CLAUDE.md`** (auto-loaded when editing `koan/app/`).
## Conventions
- Claude always creates **`<prefix>/*` branches** (default `koan/`, configurable via `branch_prefix` in `config.yaml`), never commits to main
- Project config via `projects.yaml` at KOAN_ROOT (primary), with `KOAN_PROJECTS` env var as fallback. Supports per-project overrides for `cli_provider`, `models`, `tools`, and `git_auto_merge`.
- Environment config via `.env` file and `KOAN_*` variables for secrets and system settings. **CLI provider** is configured via `KOAN_CLI_PROVIDER` env var (primary), with fallback to `CLI_PROVIDER` for backward compatibility. The centralized `get_cli_provider_env()` helper in `utils.py` handles this resolution.
- Multi-project support: up to 50 projects, each with isolated memory under `memory/projects/{name}/`
- **`KOAN.md` convention** — An optional project-root `KOAN.md` (same format as `CLAUDE.md`) is injected into the autonomous agent's system prompt but is **not** loaded by interactive Claude Code sessions. Use it for koan-only, per-project guidance. Precedence: mission instruction > `KOAN.md` > `CLAUDE.md`/defaults. See `docs/users/koan-md.md`.
- `system-prompt.md` defines the Claude agent's identity, priorities, and autonomous mode rules
- **System prompts must be generic** — Never reference specific instance details like owner names in system prompts. Use generic terms like "your human" instead of personal names. Prompts are in English; instance-specific personality and language preferences come from `soul.md`.
- **Never leak private skill/agent/project names** — The public repo must contain zero references to private identifiers from any operator's `instance/` tree. This applies to **source code, comments, docstrings, test fixtures, public docs, example configs, AND commit messages** (which `git log` exposes forever).
- **Forbidden in public artifacts**: private slash-command names (the operator's internal `/<team>-prefix>_<verb>` form), private agent or third-party tool names invoked by handlers, private bot display names (the operator's Telegram/Jira/GitHub bot handle), private JIRA project key prefixes (the all-caps fragment in keys like `<PREFIX>-12345`), private project name strings that identify the operator's customer, and concrete case numbers.
- **Generic placeholders** to use in tests, examples, and docs: skill `my_fix` / alias `myfix` / scope `my_team`, agent `my-custom-workflow`, bot `@koan-bot` or `@testbot`, JIRA keys `PROJ-NNN` / `FOO-NNN`, project `my-toolkit`.
- **Mechanism, not enumeration** — When core code needs to recognise a specific custom skill (e.g. for result forwarding), drive the behaviour off SKILL.md frontmatter flags in the `instance/skills/<scope>/<name>/` tree, not off a hardcoded list of names in `koan/app/`. See `koan/app/skills.py::collect_forward_result_markers` for the pattern: opt-in via `forward_result: true` + optional `title_markers:`, resolved dynamically from the registry at runtime.
- **Pre-commit check** — maintain a private file (gitignored or outside the repo) at `instance/.leak-patterns` listing your operator's private identifiers, one regex alternation per line, then run before staging:
```bash
patterns="$(paste -sd '|' instance/.leak-patterns)"
git diff main.. | grep '^+' | egrep -i "$patterns"
```
Must return empty. The `^+` filter restricts to lines being added on the current branch, so pre-existing leaks on `main` don't false-positive. Keeping the pattern list outside the public repo prevents this convention bullet from itself becoming a leak.
- **If you find a pre-existing leak on `main`** while working in adjacent code, scrub it in the same branch — don't leave it as someone else's problem.
- **User manual maintenance** — When adding, removing, or modifying a core skill, update `docs/users/user-manual.md` and `docs/users/skills.md` accordingly: add the skill to the appropriate tier section and the quick-reference appendix. The manual and skills reference must stay in sync with `koan/skills/core/`. (Skill authoring details and the full new-skill checklist live in `koan/skills/CLAUDE.md`.)
- **Documentation maintenance** — When adding or modifying a feature, update the corresponding section in `README.md` and/or the relevant docs file. Use the nested docs layout in `docs/README.md`: user behavior in `docs/users/`, daemon design in `docs/architecture/`, providers in `docs/providers/`, messaging and tracker integrations in `docs/messaging/`, operations in `docs/operations/`, durable decisions in `docs/design/`, threat models and audit docs in `docs/security/`, and deployment guides in `docs/setup/`. If no documentation file exists for the feature, create one in the matching directory. Public-facing documentation and implementation references must stay in sync with the codebase — undocumented features are invisible to users.
- **OpenAPI spec maintenance** — The REST API (`koan/app/api/`) has a generated OpenAPI document at `koan/openapi.yaml`. Whenever you **add, remove, or modify a REST API endpoint** (a route, its methods, its path params, or its auth), regenerate the document and commit it **in the same change**: `make openapi` then `git add koan/openapi.yaml`. It is derived from the live Flask route table — never hand-edit it. CI (`.github/workflows/openapi.yml`) runs `make openapi-check` only when API-defining files change and fails on drift with the exact fix command. See `docs/operations/rest-api.md`.
> Python-specific conventions (temp files, linting, tests, prompt extraction) and skill-authoring conventions (help groups, naming, the new-skill checklist) live in `koan/CLAUDE.md` and `koan/skills/CLAUDE.md` respectively — see "On-demand guidance" above.
<!-- SPECKIT START -->
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan:
- specs/010-review-consistency-triage/plan.md
+ specs/011-config-mission-hooks/plan.md
<!-- SPECKIT END -->