# State And Config

pi-plans stores all planning preferences and run state in the target workspace's git directory as `<git-common-dir>/pi_plans/` — in an ordinary repository this is simply `.git/pi_plans/` — resolving the git common dir with `git rev-parse --git-common-dir` from the workspace. Because the state lives inside the git dir, git never tracks it and no `.gitignore` entries are needed. The target workspace is the current working directory unless the user explicitly names another repository.

Do not store pi-plans preferences in Pi's own settings (`~/.pi/agent/settings.json`); pi-plans uses `.git/pi_plans/config.json` for its state.

## State Root Resolution

- Git runs with `GIT_DIR`, `GIT_COMMON_DIR`, and `GIT_WORK_TREE` scrubbed from the environment, so leaked env vars cannot misdirect state into an unrelated repository. Relative results (`.git`, `../.git`) resolve against the workdir.
- Granularity is **per enclosing repository**: running from a subdirectory uses the enclosing repo's git dir (a one-line notice names that repo). Linked worktrees share one common dir; run directories are unique, but `active.json` may race across concurrent worktrees.
- State does not travel with clones: a fresh clone starts with empty state while committed `./docs/pi-plans/` artifacts persist in the repository.

## Auto Git Init

When a mutating state action (`init`, `set-language`, `set-role`, `start-run`, record-*) runs in a workdir that is not a git repository, the helper auto-runs `git init` there (with a one-line notice) and then creates the state dir. It never creates commits. Auto-init runs only when ALL of the following hold:

- the workdir has no `.git` entry (a pre-existing `.git` file or directory that git cannot resolve is a fatal error, never a silent reinit);
- the workdir is not inside any git work tree (a subdirectory of a repo uses the enclosing repo instead);
- the workdir is neither the user's home directory nor the filesystem root.

Bare repositories are refused with a clear error. A missing `git` executable is a clear error. The `show` action is strictly read-only: it never auto-inits or writes.

## Directory Layout

```text
<git-common-dir>/pi_plans/
  config.json
  pi-vcc-config.json
  active.json
  runs/            # note: the refs root is a sibling — .git/pi-plans/refs (hyphenated), not under pi_plans/
    <run-id>/
      run.json
      decisions.jsonl
      subagents.jsonl
      refs.jsonl
  tmp/
  cache/
```

`config.json` is stable workspace preference state. `pi-vcc-config.json` is the repo-private compaction config used only by pi-plans' VCC-style compact hook. `active.json` and `runs/` are run state. Reference downloads go to the configured `refs_root` (asked once per workspace when unset; the recommended `.git/pi-plans/refs/` sits inside the git dir so git never tracks it), with metadata recorded in the run state and public artifacts.

## Config Schema

The default config is:

```json
{
  "schema": 1,
  "language": { "tag": null, "source": "unset", "updated_at": null },
  "reviewer": {
    "mode": "delegated-subagent",
    "model_selector": null,
    "name_prefix": "pi-plans-reviewer",
    "confirmed_at": null
  },
  "criticizer": {
    "mode": "delegated-subagent",
    "model_selector": null,
    "name_prefix": "pi-plans-criticizer",
    "confirmed_at": null
  },
  "artifact_root": "./docs/pi-plans",
  "artifact_root_source": "unset",
  "artifact_root_updated_at": null,
  "refs_root": null,
  "refs_root_source": "unset",
  "refs_root_updated_at": null
}
```

Rules:

- `schema` must be `1`.
- `language.tag` is a BCP47-style tag such as `zh-Hans`, `en`, or `zh-Hant`, or `null` before selection; `language.source` is `user`, `auto`, or `unset`.
- `reviewer.mode` and `criticizer.mode` are `delegated-subagent` or `current-session`.
- `model_selector` is `null` to inherit the dispatching session's model, or an exact `provider/model` selector matching Pi's model registry.
- `confirmed_at` is `null` until the user has confirmed the role's model at first use; see below.
- `artifact_root` is relative to the target workspace unless absolute.
- `artifact_root_source` is `user`, `auto`, or `unset`.
- `artifact_root_updated_at` is the selection timestamp or `null` before confirmation.
- `refs_root` is where plan-with-refs downloads references, relative to the target workspace unless absolute, or `null` before selection; `refs_root_source` is `user`, `auto`, or `unset`; `refs_root_updated_at` is the selection timestamp or `null`.
- There is intentionally no `effort` field: subagents inherit the dispatching session's model and thinking level unless an exact selector is stored. The real lever is the main session's thinking level at refine time.

## VCC Compact Config

`pi-vcc-config.json` is scaffolded under the resolved `<git-common-dir>/pi_plans/` state root when an active planning or execution compaction hook first needs it. It is independent from `config.json` so planning preferences, run state, and compact policy can evolve separately.

Default values:

```json
{
  "overrideDefaultCompaction": true,
  "smartKeepTail": true,
  "continueAfterThresholdCompact": true,
  "prePlanCompact": true,
  "debug": false
}
```

Rules:

- Only the repo-private file is read. Upstream global pi-vcc config such as `~/.pi/agent/pi-vcc-config.json` and `PI_VCC_CONFIG_PATH` are ignored.
- Missing files are created with defaults; valid files keep user values and receive missing default keys; invalid JSON is never clobbered and the runtime falls back to defaults for that read.
- `overrideDefaultCompaction:false` returns ordinary Pi manual/threshold/overflow compactions to Pi core. Explicit pi-plans internal compact hints can still use the VCC path.
- `smartKeepTail:true` starts from the requested/default keep count and may retain more recent user turns when the retained tail remains within the safe token budget. Explicit `keep:N` is honored.
- `continueAfterThresholdCompact:true` permits one hidden continuation after successful threshold/overflow compaction only on Pi versions that still need extension-driven resume behavior. Plain manual `/compact` never auto-continues, and `/compact <text>` sends the text once as the follow-up prompt.
- `prePlanCompact:true` requests one VCC planning compaction (internal hint `pi-plans planning pre-plan compact`) from the `plans` `tool_result` hook right after `plans start-run` creates a new run, before the first planning question, and resumes the planning turn with one hidden message on success and failure alike. Small sessions, already-compacted sessions, and aborts skip silently with an info notice. The trigger is disabled while an execution is active. `prePlanCompact:false` restores the old behavior.
- `debug:false` writes no diagnostics; `debug:true` writes a best-effort `/tmp/pi-vcc-debug.json` snapshot for local troubleshooting.

## Language Setting

Before the first product planning question, check the persisted config (`plans` action `show`). If `language.tag` is missing or invalid, ask exactly one `ask_choice` question:

1. `zh-Hans` — recommended when more than 60 percent of the user's planning request is Simplified Chinese.
2. `en` — recommended when the request is mostly English or mixed without a Chinese majority.
3. `zh-Hant` — Traditional Chinese.
4. `Other` — user provides a BCP47 tag.
5. `Auto-complete` — select the recommended language.

Persist with `plans` (`set-language`, `languageSource: "user"`). Use the selected language for visible questions, choices, review summaries, criticizer questions, and Markdown artifacts. Keep IDs, file paths, command names, JSON keys, and protocol labels stable in English.

## Code Graph Enabled

`graph_enabled` (`boolean | null`) records whether the workspace wants graph-aware read/write/edit wrappers and `code_graph` mutations for indexed source files. `null` means the question was never asked: the first `plans` `init`/`show` in a workspace returns a `hint` instructing the agent to ask the user once via `ask_choice` (recommended: yes) and persist with the `plans` tool (`set-graph-enabled`, `enabled: true|false`). This question does not count against the planning-question limit. `/enable-graph` and `/disable-graph` toggle it later; disable refuses while graph drift is dirty. When enabled, DB-first staged edits are materialized agent-side via the `code_graph` tool's `apply` action (same planning/accepted gate as `/apply-graph`; refused for read-only refiner subagents via the `PI_PLANS_REFINER` env marker; the result carries per-file counts and a post-apply drift summary and never changes run status).

## `/config-pi-plans`

`/config-pi-plans` is an interactive workspace configuration wizard. It re-asks the workspace language, planning docs root, refs root, code graph toggle, reviewer mode/model, and criticizer mode/model, then writes the chosen defaults back to `.git/pi_plans/config.json`. When code graph is enabled, the extension also overrides built-in `read`/`write`/`edit` for indexed source files so graph-backed source reads and DB-first edits happen automatically. Model pickers can reuse the current session model, any available selector surfaced by `ctx.scopedModels` or the model registry, or a manually entered exact `provider/model` string. If a run is already active, only the workspace defaults change; the active run's `artifact_dir` and `language_tag` stay unchanged.


Before the first product planning question, check the persisted config again. If `artifact_root_source` is missing or `unset`, ask exactly one `ask_choice` question:

1. `./docs/pi-plans` — recommended; planning docs live in the repository and are public.
2. `./.git/pi_plans/plans` — private to the repository; not published.
3. `Other` — user provides a custom path.
4. `Auto-complete` — select the recommended path.

Persist with `plans` (`set-artifact-root`, `artifactRoot: <selected path>`, `artifactRootSource: "user"` or `"auto"`). Use the selected path for the run's artifact directory root. This question does not count against the planning-question limit.

Before downloading any reference in a plan-with-refs flow, check the persisted config. If `refs_root_source` is missing or `unset`, ask exactly one `ask_choice` question:

1. `.git/pi-plans/refs` — recommended; inside the git dir so git never tracks the downloads.
2. `./refs/` — inside the worktree; the planning write guard allows writes under the configured refs root.
3. `~/.cache/pi-plans/refs/` — outside the repository; matches the historical default.
4. `Other` / `Auto-complete` — select the recommended path.

Persist with `plans` (`set-refs-root`, `refsRoot: <selected path>`, `refsRootSource: "user"` or `"auto"`). Download references under this root. This question does not count against the planning-question limit.


Before running a `refine` round, read the role setting from the persisted config.

If the role's `mode` is missing or invalid, ask exactly one `ask_choice` question and persist:

1. `Delegated subagent` — recommended; read-only `pi` subprocess with isolated context.
2. `Current session` — run the read-only pass in the current foreground session.
3. `Other` / 4. `Auto-complete` — select the recommended delegated subagent.

Independently, each role's **model** is confirmed once, at that role's first actual use: when `confirmed_at` is `null` and a `refine` round is about to run, ask exactly one `ask_choice` question:

1. `Inherit the main agent's model` — recommended; stores `model_selector: null`.
2. `Choose a model` — pick from the models available in this Pi install (check `/model` or `ctx.scopedModels`); persist the exact `provider/model` selector; do not invent model names.
3. `Other` / 4. `Auto-complete` — select inherit.

Persist with `plans` (`set-role`, `confirmed: true`, `modelSelector: <selector or "inherit">`). `confirmed_at` is set only by this confirmation flow; a mode-only edit never forges or discards a confirmation, and a confirmed inherit (`model_selector: null` plus a stamp) is distinguishable from never-confirmed.

If a spawn later fails because the stored selector is unavailable, reset the marker (`set-role`, `resetConfirmation: true`) and re-ask the confirmation question.

## Subagent Spawning

When `mode` is `delegated-subagent`, the `refine` tool spawns a read-only `pi` subprocess (`--mode json -p --no-session --tools read,grep,find,ls`) whose system prompt comes from `agents/reviewer.md` or `agents/criticizer.md`. In TUI mode, delegated runs also show a standalone `Reviewer` or `Criticizer` overlay with live lane/tool status; the child is awaited and the overlay is closed before the tool result returns. The subagent:

- performs read-only analysis and never edits files;
- receives the full plan text and a review/criticism brief;
- returns its findings as the tool result (recorded in `subagents.jsonl` with name and model).

The main agent consolidates the results, records dispositions, revises the plan, and asks the next merged accept/execute question — all in the same turn.

The `analyze_refs` tool (plan-with-refs) uses the same spawning machinery with the **reviewer** role's gates (`mode` must be `delegated-subagent`; a confirmed `current-session` reviewer is refused with guidance to switch, since analysis is spawn-only) and the reviewer's model selector. Each downloaded reference gets one independent read-only subagent whose system prompt comes from `agents/ref-analyst.md` and whose working directory is that reference's own directory; lanes never get `code_graph`. Lanes run in sequential batches of at most 3 under a standalone overlay titled `Refs`; each batch's controller opens and closes exactly like a single refine round. Successful spawns are recorded best-effort in `subagents.jsonl` with role `ref-analyst` (skipped when no active run exists, e.g. adhoc calls). The structured per-reference sections come back as the tool result; the main agent owns `REF_ANALYSIS.md` and fills `coverage`/`gaps` in `refs.jsonl` via `plans` (`record-ref`).

## Run State

One run directory per planning request: `<git-common-dir>/pi_plans/runs/<YYYYMMDDTHHMMSSZ-topic>/` (second-precision; `-2`, `-3` suffixes on collision).

`run.json` includes: run ID; skill name; original request; target workspace; artifact directory; language tag; status (`planning` → `accepted` → `executing` → `done`, with `stopped`/`abandoned` as exits); timestamps.

`decisions.jsonl` is appended automatically by `ask_choice` (question, options, answer, answer source). `subagents.jsonl` records reviewer/criticizer/ref-analyst spawns. `refs.jsonl` records reference metadata via `plans` (`record-ref`).

## Workflow Checkpoints (`/resume-plans`)

Each run may carry a `checkpoint.json` — the durable, cross-session workflow state that `/resume-plans` restores in the current session. It records: logical `phase` (`planning | reviewing | executing | implementation-review | completed`), `nextAction`, the exact plan identity (path + version + SHA-256), pending/answered questions (stable `questionId`), review rounds with per-lane status and result-file references, execution approval evidence (plan digest, worktree, `git rev-parse HEAD` at approval, verified VC/I set, usage), the implementation-review termination condition and completed-round count, and ownership metadata. Full review outputs live in separate `reviews/` files; the checkpoint keeps only validated references.

Rules:

- Validation is explicit: unknown schema versions, malformed shapes, and unexpected keys are rejected; missing and corrupt checkpoints are distinct, and corrupt files are never silently overwritten.
- Writes are atomic with monotonic revisions; writers may require ownership (token + generation) or an expected revision.
- Model-driven boundaries (plan written, review consolidated, termination condition recorded, implementation round finished, completed) go through the whitelisted `plans record-checkpoint` action, which enforces state-machine preconditions — it cannot set execution approval, mark VCs passed, or forge terminal states.
- `ask_choice` accepts `questionId`/`purpose`; a pending question is durable before the panel opens and the answer before it returns. When a crash leaves a question both answered (ledger) and pending (checkpoint), the answered entry wins.
- On execution resume, an unchanged plan digest with a changed HEAD keeps the authorization but re-verifies previously verified VCs first; loading execution from a checkpoint writes an immediate session snapshot so session restore cannot clear it.
- Cross-worktree resumes copy artifacts without overwriting, reset approval and VC validity, keep the termination condition, and restart completed-round counts at 0 for the target worktree.

## Run Ownership

A run may be held by at most one live owner (`owner.json`: host, pid, process start time via `ps -o lstart=`, session id, random process token, generation). Acquisition is an atomic exclusive create; takeovers require proof the previous owner is dead (process gone, or pid alive with a different start time — PID reuse). Foreign hosts, corrupt records, and unverifiable liveness are conservatively refused; `/resume-plans` never queues or interrupts. Sessions bind to the run they start/execute/resume (restored from `pi-plans-run-start` entries on the current branch), and attribution (tools, write guard, autocomplete, execution bookkeeping, code-graph apply gate) prefers the binding over the shared `active.json` pointer.
