|
| 1 | +# CLAUDE.md — operating manual |
| 2 | + |
| 3 | +Guidance for agents working in this repo. The full design lives on the **`design-docs`** |
| 4 | +branch (GOALS, PARITY, ARCHITECTURE, ROADMAP, ADRs 0001–0008). This file grows one slice |
| 5 | +at a time (see ROADMAP "Definition of done — every slice"). |
| 6 | + |
| 7 | +## The one rule that matters most: the determinism invariant |
| 8 | + |
| 9 | +The control plane makes **no LLM calls**. All LLM calls happen **inside task containers**. |
| 10 | + |
| 11 | +- LLM-free packages: `core`, `taskservice`, `sessionservice`, `terminal`, `workflows`. |
| 12 | +- The **only** LLM-bearing package is `container/` (the agent runs there). |
| 13 | + |
| 14 | +If you add a package that orchestrates or renders, keep it LLM-free. |
| 15 | + |
| 16 | +## Module map (current) |
| 17 | + |
| 18 | +``` |
| 19 | +src/panopticon/ |
| 20 | + core/ # domain models, state classes, the Workflow interface (the state |
| 21 | + # machine: resolution, queries, start_task/apply_transition), |
| 22 | + # store & artifact interfaces — pure, no I/O |
| 23 | + workflows/ # built-in Workflow subclasses (Spike seed for now) |
| 24 | + taskservice/ # control plane: TaskService, FastAPI REST API, the SQLAlchemy store |
| 25 | + # adapter (in-memory or on-disk SQLite), filesystem artifact store, MCP |
| 26 | + sessionservice/ # the runner (stub for now; real Docker+tmux runner later) |
| 27 | + container/ # in-container client + entrypoint protocol — the ONLY LLM-bearing pkg |
| 28 | +``` |
| 29 | + |
| 30 | +## Conventions |
| 31 | + |
| 32 | +- **The state machine is deterministic and clock-free.** Timestamps are passed in by the |
| 33 | + caller (the task service stamps them); the workflow never reads the clock. Keep it that way. |
| 34 | +- **Identity vs. slug.** A task's identity is its internal `id` (generated by the task |
| 35 | + service). The `slug` is a human label, nullable, **set in the container** via a hook |
| 36 | + (ARCHITECTURE.md §8.3) — not chosen host-side. |
| 37 | +- **All task-state mutations go through the task service**, which enforces transitions via |
| 38 | + the workflow before persisting (the store is the single writer; ADR 0006). |
| 39 | +- **Interfaces vs. adapters.** Interfaces (ABCs) live in `core`; adapters live in the owning |
| 40 | + package. New backends implement an interface; they don't change callers. |
| 41 | + |
| 42 | +## Dev commands |
| 43 | + |
| 44 | +```sh |
| 45 | +uv sync # create the venv, install deps |
| 46 | +uv run pytest # run the test suite |
| 47 | +uv run mypy -p panopticon # type-check (strict) |
| 48 | +``` |
| 49 | + |
| 50 | +CI (`.github/workflows/ci.yml`) runs `uv sync`, `mypy`, and `pytest` on every PR. |
| 51 | + |
| 52 | +## Tests worth knowing |
| 53 | + |
| 54 | +- `tests/test_workflow.py` — the **golden harness**: every legal/illegal transition, turn |
| 55 | + derivation, responsibility gating, and workflow validation. Extend it when you touch the |
| 56 | + state machine. |
| 57 | +- `tests/test_store.py` — store **contract tests run against in-memory and on-disk SQLite**, |
| 58 | + proving the interface is backend-agnostic (and that rows/domain models stay in sync). |
| 59 | +- `tests/test_skeleton.py` — the end-to-end walking skeleton (create → register → slug → |
| 60 | + transition → history) over the REST API, no Docker. |
| 61 | + |
| 62 | +## Glossary |
| 63 | + |
| 64 | +- **Task** — a unit of work; identity is `id`, label is `slug`. |
| 65 | +- **Repo** — a repository tasks operate on (owns secret references, later slices). |
| 66 | +- **Workflow** — a `Workflow` subclass whose **states are nested `State` classes** |
| 67 | + (declarative). It declares `initial`; states are discovered and their transitions |
| 68 | + (class refs or label strings) resolved + validated when the workflow is instantiated. |
| 69 | + The lifecycle is code, not hardcoded control flow. |
| 70 | +- **State** — a class (`State` non-terminal, inherits a `Dropped` transition; or |
| 71 | + `TerminalState`). Carries a `label` (persisted in `Task.state`, shown on the dashboard), |
| 72 | + `turn_on_enter`, `advanced_by`, `responsibilities`, and `transitions`. Built-ins: |
| 73 | + `Complete`, `Dropped`. |
| 74 | +- **Actor** — a party, `user` or `agent`. A state declares `turn_on_enter` (who holds the |
| 75 | + turn on entry; seeds `Task.turn`) and `advanced_by` (who transitions out — the default is |
| 76 | + `USER`). The two are orthogonal. |
| 77 | +- **Responsibility / Status** — an agent obligation for a state. Entering a state seeds its |
| 78 | + responsibilities onto that entry's history record, all `PENDING` (a promise); the agent |
| 79 | + fulfils each one at a time (`MET`, or `FAILED` with a comment) — mutating that entry — and a |
| 80 | + later advance is gated on all being resolved. Agent-only. |
| 81 | +- **Registration / liveness** — a container's standing claim that it is working on a task. |
| 82 | +- **Task service** — the deterministic control plane (sole DB authority). |
| 83 | +- **Session service / runner** — spawns task containers (stubbed for now). |
| 84 | +- **Terminal controller** — the user-facing CLI/dashboard (Slice 3). |
| 85 | +- **Artifact** — a file-backed per-task document (plan, notes), reachable via REST/FS/MCP. |
0 commit comments