# Okstra — Folder structure and feature summary

> A living map based on the current repository. For quick usage see [`README.md`](../README.md), for the internal execution contract see [`docs/architecture.md`](architecture.md), for the storage model see [`docs/architecture/storage-model.md`](architecture/storage-model.md), and for detailed CLI options see [`docs/cli.md`](cli.md).

---

## Table of contents

- [1. Project identity](#1-project-identity)
- [2. Top-level structure](#2-top-level-structure)
- [3. Build / install boundary](#3-build--install-boundary)
- [4. Folder responsibilities](#4-folder-responsibilities)
- [5. Key runtime modules](#5-key-runtime-modules)
- [6. Post-install layout](#6-post-install-layout)
- [7. Core workflows](#7-core-workflows)
- [8. Doc maintenance checklist](#8-doc-maintenance-checklist)
- [Appendix A. Report / row ID glossary](#appendix-a-report--row-id-glossary)

---

## 1. Project identity

`okstra` is a host-aware, multi-provider cross-verification runtime distributed as the npm package `okstra`. It is not a one-shot reviewer; it runs one neutral lifecycle core through Claude Code, Codex, or an explicit external adapter around a stable task key.

Current baseline:

- package version: see `package.json`
- Node CLI entrypoint: `bin/okstra` (Node.js 22+)
- Python orchestration authority: `scripts/okstra_ctl/run.py::prepare_task_bundle`
- lifecycle: `requirements-discovery → error-analysis → implementation-option-selection → implementation-planning → implementation → final-verification → release-handoff`
- installed skills: 13
- provider workers: `claude`, `codex`, `antigravity`, `grok`, `kimi`; functional report writer: `report-writer`
- final report SSOT: `schemas/final-report-v2.0.schema.json` + `*.data.json`

Design principles:

1. **Single prepare authority**: the slash skill, the Bash CLI, and the Node preview all converge on `prepare_task_bundle()`.
2. **Stable task identity**: `<project-id>/<task-group>/<task-id>` runs through phase, run, worktree, and report.
3. **Artifact-home rule**: the okstra-owned project artifact root is the single `<PROJECT_ROOT>/.okstra/**`.
4. **Template/schema/validator lockstep**: `templates/`, `schemas/`, `validators/`, and the worker/report-writer contract enforce the same report shape.

---

## 2. Top-level structure

```text
okstra/
├── bin/okstra                       Node CLI router
├── src/                             TypeScript Node command sources (`*.mts`)
├── dist/                            compiled `.mjs` CLI artifacts; generated
├── scripts/                         Python + Bash runtime sources
│   ├── okstra_ctl/                  orchestration core
│   ├── okstra_project/              project root / project.json resolver
│   ├── okstra_token_usage/          token usage + cost accounting
│   ├── okstra_vendor/               vendored Jinja2 / MarkupSafe (final-report rendering)
│   ├── lib/okstra/                  Bash helpers for okstra.sh
│   └── lib/okstra-ctl/              Bash control-center subcommands
├── skills/                          Claude Code skills (13); `_fragments/` holds shared marker blocks
├── .agents/skills/                  Codex repo-local maintainer skills
├── .claude/skills/                  Claude Code project-only mirror-sync surface
├── .codex/hooks.json                Codex project lifecycle hooks
├── agents/                          native Claude execution-adapter definitions
├── prompts/                         launch/profile contracts, duty catalog, wizard prompt JSON
├── schemas/                         JSON schema for final-report data.json
├── templates/                       report, setup, PR, project-doc templates/assets
├── validators/                      run / brief / schedule / view validators
├── tools/build.mjs                  source → runtime sync
├── tools/sync-skill-fragments.mjs   expand shared fragment blocks into skills/*/SKILL.md
├── tools/korean-sources/            maintainer-only Korean mirror drift report
├── config/korean-sources.json       configured English Markdown source roots
├── runtime/                         generated install payload; do not edit directly
├── tests/                           pytest unit suite
├── tests-e2e/                       Bash end-to-end scenarios
├── docs/                            architecture, CLI, plans/specs
├── package.json                     npm metadata and published file list
├── README.md                        public user manual
├── CHANGES.md / CHANGELOG.md        human-authored and generated changelogs
└── RELEASING.md                     release process
```

`runtime/` is build output. Source edits go to `scripts/`, `skills/`, `agents/`, `prompts/`, `schemas/`, `templates/`, `validators/`; then `npm run build` syncs them into `runtime/`.

---

## 3. Build / install boundary

### 3.1 npm package

`package.json` publishes:

- `bin/`
- `src/`
- `runtime/`
- `docs/architecture.md`
- `docs/architecture/`
- `docs/cli.md`
- `docs/container.md`
- `docs/performance-improvement-plan-v2.md`
- selected contributor, follow-up, AI, and task-process documentation paths
- `README.md`

So `runtime/` is not the only npm-published content. It is the install payload copied into `~/.okstra`.

### 3.2 Build sync

`tools/build.mjs` rebuilds `runtime/` from:

| Source | Runtime destination |
|---|---|
| `scripts/okstra_project` | `runtime/python/okstra_project` |
| `scripts/okstra_ctl` | `runtime/python/okstra_ctl` |
| `scripts/okstra_token_usage` | `runtime/python/okstra_token_usage` |
| `scripts/okstra_vendor` | `runtime/python/okstra_vendor` |
| `scripts/lib` | `runtime/bin/lib` |
| selected `scripts/okstra*.sh` / `*.py` entrypoints | `runtime/bin/` |
| `agents`, `prompts`, `schemas`, `templates`, `validators`, `skills` | same relative tree under `runtime/` |

`npm run build` and `prepack` both run this sync. Never hand-edit `runtime/`.

The public manuals under `docs/` are selected directly by `package.json` for npm publication; npm-published source documentation is not copied into `runtime/docs/`. The install payload receives the operating contracts it needs through the explicit `prompts/`, `templates/`, and `schemas/` sync rows above.

Runtime/install asset changes follow this checklist:

1. Add or edit the source asset under `scripts/`, `skills/`, `agents/`, `prompts/`, `schemas`, `templates`, or `validators`.
2. Add a `tools/build.mjs` sync mapping when the asset must appear under `runtime/` (for example, a new executable such as `okstra-claude-exec.sh`).
3. Run `npm run build`.
4. Verify `tests-js/runtime-build-sync.test.mjs`.
5. Verify `tests-js/install-runtime.test.mjs`.
6. Reinstall or repack the package before testing global `okstra install`.

### 3.3 Install output

`okstra install` copies from the installed npm package `runtime/` tree, not from the source checkout. `okstra -v` only proves the CLI version; the same semver can still point at stale package contents when the global package was installed before the latest build.

`okstra install` copies or symlinks:

- `runtime/python/*` → `~/.okstra/lib/python/`
- `runtime/bin/*` → `~/.okstra/bin/`
- `runtime/templates/*` → `~/.okstra/templates/`
- `runtime/skills/<name>` (the fourteen user-facing skills only) → `~/.agents/skills` always, plus `~/.claude/skills` when `~/.claude` exists
- `runtime/prompts/*` → `~/.okstra/prompts/` (lead contracts under `prompts/lead/`, coding-preflight pack under `prompts/coding-preflight/`)
- the native Claude execution adapters in `runtime/agents/workers/` → `~/.claude/agents/` when `~/.claude` exists; retired provider transport-agent files are removed only when the prior install manifest owned them
- install manifests → `~/.okstra/installed-skills.json` (target-aware), `~/.okstra/installed-agents.json`
- version stamp → `~/.okstra/version`

`--link <repo>` mode is for development and symlinks installed files back to repo sources.

`src/lib/host-registry-client.mts` is the single Node boundary for host catalog, ID/alias resolution, and readiness probes. It delegates host policy to `okstra_ctl.entrypoints.hosts`; the Claude Code adapter checks project workspace trust, while other adapters own their own checks. `okstra install` defaults to `--runtime auto`, records the request and any successful resolution in `installed-runtimes.json` schemaVersion 2, and still copies the shared runtime payload from the installed package `runtime/` tree even when host detection is unavailable. Skill targets always include the default Agent-compatible `~/.agents/skills` target, with `~/.claude/skills` also populated when `~/.claude` exists. Dynamic capabilities are probed by the selected adapter rather than frozen into the install manifest.

---

## 4. Folder responsibilities

### 4.1 `bin/` and `src/` — Node CLI

`bin/okstra` is a thin dynamic-import router. Every command module exports `run(args) -> Promise<number>` or a named install/uninstall runner.

`src/` is layered: the dispatch table (`src/cli-registry.mts`) stays at the root, shared infrastructure with no command export lives under `src/lib/`, and command modules are grouped by domain under `src/commands/{lifecycle,execute,inspect,report,memory,pr}/`. `npm run build:ts` compiles these sources to `dist/**/*.mjs`; `bin/okstra` executes the compiled output. `scripts/okstra-central.sh` remains the Bash central-state helper.

| Command | Module | Role |
|---|---|---|
| `paths` | `src/commands/lifecycle/paths.mts` | Resolve package/runtime/home paths (library at `src/lib/paths.mts`) |
| `install`, `ensure-installed` | `src/commands/lifecycle/install.mts` | Install or refresh runtime, skills, agents, templates |
| `uninstall` | `src/commands/lifecycle/uninstall.mts` | Remove managed runtime/skills/agents, optionally purge data |
| `doctor` | `src/commands/lifecycle/doctor.mts` | Diagnose runtime and Python imports |
| `setup` | `src/commands/lifecycle/setup.mts` | Create/update `<PROJECT_ROOT>/.okstra/project.json` |
| `check-project` | `src/commands/lifecycle/check-project.mts` | Verify project registration |
| `preflight` | `src/commands/lifecycle/preflight.mts` | One-call skill preflight: ensure-installed + check-project + host-specific runtime readiness (single JSON) |
| `config` | `src/commands/lifecycle/config.mts` | Read/write project/global settings such as PR template path |
| `migrate` | `src/commands/lifecycle/migrate.mts` | One-shot legacy `.project-docs/okstra` → `.okstra` migration helper |
| `git-reconcile` | `src/commands/execute/git-reconcile.mts` | Reconcile stale stage SHAs after external git history changes |
| `handoff` | `src/commands/execute/handoff.mts` | Stage-group release-handoff eligibility / assemble / record helpers |
| `integrate-stages` | `src/commands/execute/integrate-stages.mts` | Merge verified stages into the task worktree and clean stage worktrees |
| `task-list`, `task-show` | `src/commands/inspect/task-list.mts`, `src/commands/inspect/task-show.mts` | Task/run introspection for skills; `task-show` consumes the Python task read-side snapshot |
| `resolve-task-key` | `src/commands/inspect/resolve-task-key.mts` | Resolve a bare task-id to candidate task-keys from the project catalog |
| `set-work-status` | `src/commands/inspect/set-work-status.mts` | Set a task's user-managed `workStatus` in task-manifest.json (Python: `okstra_ctl.set_work_status`) |
| `time-report`, `log-report`, `error-report`, `error-zip` | `src/commands/inspect/*.mts` | Read-side task runtime, wrapper log, and error aggregation helpers |
| `run-audit` | `src/commands/inspect/run-audit.mts` | Anomaly detection — checks run artifacts against progress invariants and reports invariant violations, read-only (Python: `okstra_ctl.run_audit`) |
| `worker-liveness` | `src/commands/inspect/worker-liveness.mts` | Report whether pending workers are still alive, so the lead's poll ends a stalled wait early instead of paying the deadline (Python: `okstra_ctl.worker_liveness`) |
| `worker-audit-check` | `src/commands/execute/worker-audit-check.mts` | Apply the Phase 7 worker audit-sidecar rules while the worker session is still alive, so it can fix its own citations (Python: `okstra_ctl.worker_audit_check`, rules in `okstra_ctl.worker_audit_ledger`) |
| `context-cost` | `src/commands/inspect/context-cost.mts` | Estimate task bundle file/read context cost |
| `worktree-lookup` | `src/commands/execute/worktree-lookup.mts` | Look up a task-key's registered worktree |
| `worktree-status` | `src/commands/execute/worktree-status.mts` | Clean-worktree check over source paths only, excluding okstra's provisioned entries and nested stage worktrees (Python: `okstra_ctl.worktree.dirty_entries_excluding_okstra`) |
| `plan-validate` | `src/commands/execute/plan-validate.mts` | Check approved-plan approval marker |
| `render-bundle` | `src/commands/execute/render-bundle.mts` | Preview `prepare_task_bundle(render_only=True)` |
| `profile` | `src/commands/inspect/profile-show.mts` | Print a phase profile with `{{INCLUDE:}}` expanded and its lazy-read sidecars appended transitively, so one grep answers whether a task-type covers a rule — a top-level grep alone returns false negatives (Python: `okstra_ctl.profile_show`). Read-only, unlike `render-bundle` |
| `run` | `src/commands/execute/run.mts` | Host-aware execution front door (`auto` → Claude/Codex/Antigravity/external path selection) |
| `codex-run`, `codex-dispatch` | `src/commands/execute/codex-*.mts` | Codex lead dry-run bundle preparation; `codex-dispatch` is the compatibility alias for provider-neutral worker dispatch |
| `agent-prompt`, `worker-dispatch` | `src/commands/execute/{agent-prompt,worker-dispatch}.mts` | Materialize/verify invocation prompts, record host-native specification/result links, and launch verified CLI assignments through the provider-neutral dispatcher |
| `team` | `src/commands/execute/team.mts` | External lead tmux-pane worker dispatch / await / teardown |
| `convergence` | `src/commands/execute/convergence.mts` | Internal admin CLI for the deterministic Phase 5.5 convergence engine (`seed`/`plan-round`/`apply-round`/`apply-critic-gaps`/`finalize`/`validate`/`example`; Python: `okstra_ctl.convergence`) |
| `plan-items` | `src/commands/execute/plan-items.mts` | Internal admin CLI for deterministic plan-body item extraction and exact-match validation (`extract`/`validate`; Python: `okstra_ctl.plan_items_cli`) |
| `agent-activity` | `src/commands/report/agent-activity.mts` | Thin Node shim for `okstra_ctl.agent_activity`; `append` records one run-bound activity and `project` writes the validated event projection into final-report data |
| `report-finalize` | `src/commands/report/finalize.mts` | Run the whole Phase 7 post-report sequence in contractual order (Python: `okstra_ctl.report_finalize`) — the single reference point shared with the Codex lead adapter |
| `render-views` | `src/commands/report/render-views.mts` | Render schema v2 data with its task-specific human template, or use the quick-report compatibility view |
| `render-final-report`, `inject-report-index` | `src/commands/report/*.mts` | Render the full reading copy Markdown from data.json on demand; v1 index injection remains compatibility-only |
| `wizard` | `src/commands/execute/wizard.mts` | Drive the `okstra-run` interactive state machine, including the final outcome envelope |
| `token-usage` | `src/commands/execute/token-usage.mts` | Wrap installed Python token usage CLI |
| `spawn-followups`, `error-log` | `src/commands/execute/*.mts` | Follow-up task bundle creation and run error-log append helpers |
| `memory` | `src/commands/memory/memory.mts` | Store/find global conversation memory under `~/.okstra/memory-book` |
| `pr` | `src/commands/pr/pr.mts` | `okstra pr <template\|branches\|gen>` — PR body template store under `~/.okstra/template/pr/` (bundled fallback `src/commands/pr/default.md`), base-branch recommendation, and a fixed-text generation bundle for the okstra-pr-gen skill. `--json` preserves the template + `<base>..HEAD` commits + `<base>...HEAD` diffstat machine contract. Git-only; no project registration required |
| `recap` | `src/commands/inspect/recap.mts` | `okstra recap <assemble\|record\|note>` Node wrapper backing the okstra-inspect `recap` facet — `assemble` is a read-only phase-transition summary, `record` appends one line to `recap/recap-log.jsonl`, and `note` writes an agent-authored note under `notes/` and prints the `--clarification-response` argument for a follow-up run |
| `stage-map` | `src/commands/inspect/stage-map.mts` | `okstra stage-map <task-key>` — exposes a task's implementation-planning Stage Map as JSON (`stages[].{stage_number,title,depends_on,step_count}` + consumer-state-based `doneStages[]`). If there is no Stage Map, `stages: []`. The read-side basis from which `okstra-schedule-gen` derives stage units and dependency closure |
| `design-prep` | `src/commands/inspect/design-prep.mts` | `okstra design-prep <list\|show\|write>` thin shim into `scripts/okstra_ctl/design_prep.py` — queries (`list`/`show`) the design items that implementation-planning pre-authored with AI, and records the user-confirmed responses as an append-only sidecar under `design-prep-inputs/` (`write`, `--confirmed` required). It never modifies the report snapshot |
| `rollup` | `src/commands/inspect/rollup.mts` | Read-only roll-up; `--text` is the fixed model projection and machine mode remains JSON |
| `usage-report` | `src/commands/inspect/usage-report.mts` | Read-only usage snapshot; `--text` is the fixed model projection and machine mode remains JSON |
| `container` | `src/commands/inspect/container.mts` | Container lifecycle shim; `--text` is the command-specific model projection and machine mode remains JSON |
| `code-review` | `src/commands/inspect/code-review.mts` | `okstra code-review target` thin shim into `scripts/okstra_ctl/code_review_target.py` — resolves what one implementation stage's or one branch's review reads (worktree, branch, base/head commits) and where its result file goes, for the okstra-code-review skill. Read-only; creates no directory or file |
| `manager` | `src/commands/manager.mts` | Cross-project manager; fixed text is the default and `--json` selects machine output |

`src/lib/python-helper.mts` centralizes Node → Python execution so command modules do not duplicate subprocess wiring.

`src/lib/helper-scripts.mts` is the SSOT list of the Python helper scripts that `okstra <cmd>` subcommands front through `runInstalledScript()`. `okstra preflight` asserts every one of them resolves under `~/.okstra/bin` using the same resolver the dispatch path uses, so a stale install fails at the cheap environment gate instead of at the blocking lead step that calls it mid-run. A contract test keeps the list in step with the `scriptName:` literals in `src/commands/**`.

### 4.2 `scripts/` — Runtime source

Top-level scripts:

| File | Role |
|---|---|
| `okstra.sh` | Bash CLI wrapper around `prepare_task_bundle`, optionally launches `claude` |
| `okstra-ctl.sh` | Bash control center for list/show/open/rerun/reconcile/project commands |
| `okstra-central.sh` | Central run index writer / reconciler entrypoint |
| `okstra-{claude,codex,antigravity,grok,kimi}-exec.sh` | Worker CLI entrypoints — four lines each, `exec`ing `okstra-provider-exec.py` with the provider id. They hold no provider logic; adding a flag to one of these instead of to the provider adapter is exactly the drift this shape exists to prevent |
| `okstra-provider-exec.py` | The one worker entrypoint: parses the shared positional contract plus `--presentation`, resolves the provider's `ExecutionStrategy` from the registry, refuses a missing CLI before any artifact is written, then hands the run to `okstra_ctl.worker_runner` |
| `okstra-wrapper-status.py` | Standalone writer for one worker status sidecar. No longer on the dispatch path — `worker_runner.py` writes the same document in-process |
| `okstra-token-usage.py` | Token usage CLI entrypoint |
| `okstra-render-final-report.py` | Render version-selected final-report Markdown from data.json |
| `okstra-render-report-views.py` | Render schema v2 task-specific HTML directly from data.json, or a legacy view from quick Markdown |
| `okstra-error-log.py` | Normalize worker/lead error sidecars |
| `okstra-spawn-followups.py` | Follow-up spawning helper |
| _(removed)_ | `okstra-trace-cleanup.sh` closed harness-owned worker panes with a tmux title scan. It could only work in a tmux-hosted session, and current runs are cmux, so it never closed anything; `okstra team reclaim` / `okstra team teardown` replace it by closing the panes okstra itself opened and recorded (ADR-0012) |

### 4.3 `scripts/okstra_ctl/` — Python orchestration core

Important modules:

| Module | Role |
|---|---|
| `run.py` | `prepare_task_bundle()` single authority and CLI parser; for final-verification it adapts CLI stage input into `FinalVerificationTargetRequest`, maps the acquired target into render context, and owns `verification-target.md` snapshot/digest materialization before manifests and prompts are rendered |
| `agent_activity.py` | Records activity rows against run-manifest identity, imports validated command evidence from worker audit sidecars, and deterministically projects the current run's `lead-events-*.jsonl` activity rows into `agentActivity[]`. Manifests without `activityContractVersion: 1` are left unchanged. |
| `exact_coverage.py` | Shared pure calculator for requirement coverage and scope precision in option selection and selected-direction planning |
| `implementation_options.py` | Option-selection criteria, weighting, candidate fingerprint convergence, ranking, and semantic validation |
| `implementation_direction.py` | Selected report/response validation, direction snapshot materialization, and selected-direction reference validation |
| `implementation_stage.py` | `implementation` single-stage run orchestration — read the Stage Lifecycle Snapshot → pick an available Stage Map entry → provision an isolated stage worktree → publish the selected stage as run context (extracted from `run.py`) |
| `stage_targets.py` | Stage readiness/verification policy SSOT — from the Stage Lifecycle Snapshot (`consumers.jsonl` ledger + carry sidecar backfill + active registry reservation) it decides which stage is runnable, which commit it branches from, and what final-verification checks. `acquire_final_verification_target()` acquires the ledger, registry, worktree, Git, and optional whole-task integration facts behind one task-key mutex and returns a typed target without render-context coupling. `order_stage_closure` topologically sorts (Kahn) the dependency closure of the wizard's multi-selected stage set to produce the unattended `chain-stages` chaining order |
| `stage_fix_carry.py` | fix-run carry derivation for a re-run on an `implementation` stage whose latest final-report data.json carries verifier `FAIL` verdicts — collects the previous report path, previous run HEAD, failed verifiers, carried blocking findings, and a routing recommendation, which `run.py` renders into the analysis profile through the `{{FIX_RUN_CONTEXT}}` token. A first run, or a re-run after `PASS`, yields no carry and renders the token empty |
| `stage_reconcile.py` | best-effort git reconciliation shared by the stage prepare flow (delegates to `git_reconcile.auto_reconcile`; advisory — failures are only reported to stderr, the dependency gate stays authoritative) |
| `stage_ledger.py` | assembles the Stage Ledger handed to plan authoring — "what is already built" from the carry sidecar's plan, "which stage numbers are used" from the latest plan (ADR-0015 append-only, judged on the latest plan's `max`); it only joins `stage_targets` (status/lifecycle) and `stage_map` (source-of-stage) and serialises, owning no verdict. Carries `sourcePlan`/`latestPlan` and surfaces `planDivergence`; when the ledger cannot be read it emits the reason in plain text under the same heading instead of omitting the block |
| `design_surfaces.py` | deterministic detection of an `implementation-planning` stage's design surface — matches the stage's file-path tokens/suffixes/patterns and action wording via `SurfaceRule` to derive which design input the stage needs among domain contract, DB/table schema, external interface, transaction/consistency, transformation mapping, lifecycle, rollout/observability, and manual user test, plus its evidence (`TriggerEvidence`). An unmappable structure raises `DesignSurfaceError` |
| `design_prep.py` | fingerprint / materialize / resolve backend for design-preparation requests (CLI: `okstra design-prep <list\|show\|write>`) — computes an assessment fingerprint from the approved planning snapshot's `ASSESSMENT_FIELDS`, idempotently writes an Okstra-owned request under `design-prep-requests/`, and resolves the highest-revision append-only user response under `design-prep-inputs/` whose fingerprint matches as the effective response. Keeps the three authorities (report snapshot / Okstra request / user input) separate and never modifies the report or existing revisions. Sidecar I/O is protected by a directory-fd anchor + flock |
| `incremental_scope.py` | incremental re-verification decision for an `implementation-planning` clarification re-run (deterministic pure function) — reads the dependency graph from the previous run data.json's `implementationPlanning.stageMap` and returns `mode="incremental"` only when the base-ref SHA is unchanged and the affected stages' `downstream_stage_closure` is at most half of all stages; an unlinked `C-NNN` is `mode="unresolved"` (needs `--impacted`), not full. CLI: `okstra incremental-scope` |
| `incremental_carry.py` | carry merge for an incremental re-run — verifies unchanged carried stage rows and merges their previous `P-Step-*` / `P-Prep-*` verdicts, plus unchanged `P-Val-*` / `P-Req-*` / `P-Rb-*` rows whose extract hash still matches, into the convergence-owned v3 plan state with a `carriedForwardFromSeq` tag. Rewrites `dispatchQueue` and the sibling `plan-items-*.json` so the next prompt does not re-score a carried checklist row. The historical v2 data.json form remains readable. Ownership, scope, or schema drift exits non-zero with `CarryError`. CLI: `okstra incremental-carry` |
| `build_tools.py` | allowlist SSOT for deciding whether a plan's command cell invokes the project build toolchain (`npm`/`pytest`/`cargo`/`gradle`/… behind transparent leaders like `sudo`/`env`). The planning worktree has no dependencies installed, so `validators/validate-run.py` uses this to warn (advisory) when a toolchain stage declares no install precondition. Intentionally an allowlist, not a denylist, so unknown tokens go undetected rather than firing on `grep`/`sed` in every plan |
| `stage_citations.py` | shared grammar SSOT for reading the Stage Map stage numbers a prose cell cites (`Stages 1, 2, and 3`, ranges, etc.). One definition serves two readers that must not drift — the coverage check in `validators/validate-run.py` proving every stage traces to a requirement, and `incremental_scope.py`'s back-trace resolving which stages an answered clarification touches |
| `self_mock_signals.py` | self-mock signal SSOT — language-keyed regexes (`SIGNALS`), the `EXT_TO_LANG` extension map, and the waiver-matching mechanics both gates share — `selfmock_path_key` (the one path-normalization), `waiver_entry_key` (the `(file, line, <discriminator>)` triple, with the hand-typed line coerced to `int`) and `partition_waived_entries` (the split into still-failing vs waived). Gate A passes the discriminator `signal`, gate B `mutant`; one definition means the two cannot disagree about whether a waiver matches a finding. The signals are each ported from a `prompts/coding-preflight/languages/<lang>.md` "Self-mock signals to refuse" bullet with the source `doc_keyword` retained so a drift guard fails when doc and module diverge. Patterns stay deliberately narrow (only the "stub the subject's own method, then assert the stub" shape and reaching into the subject's privates; subject identity is never inferred beyond the literal `sut` token). The static detector `validators/detect_self_mock.py`, the drift guard and `mutation_probe.py` MUST import from here; four documented shapes needing subject identity no regex has are left to the mutation gate (`mutation_probe.py`) |
| `mutation_probe.py` | gate B of the self-mock gate — the tool-agnostic mutation probe. `ADAPTERS` maps an `EXT_TO_LANG` language key to an adapter (`ts_js` → Stryker, `python` → Cosmic Ray, `rust` → cargo-mutants, `java`/`kotlin` → PIT, which reports `unsupported` because its SCM scoping is a Maven-only goal and the report↔path mapping is unverified). The Python adapter activates only when the worktree has both an installed `cosmic-ray` executable and `cosmic-ray.toml`; Okstra never installs it. It verifies that `module-path` covers every changed Python target and that no target is excluded, snapshots the configured Python source bytes and modes, restores them after every run, and filters the JSONL dump to diff-added lines. A completed session with no applicable operator on a changed line is `nothing-to-verify`; malformed configuration, command/report failures, incomplete trials, and failed source restoration are blocking `integrity-inspection` results. `run_probe` owns everything that must not differ between tools: production-source selection, the refusal to run on an empty target set, the requirement that the diff name EVERY changed source, the adapter result-shape check and the user-acknowledged waiver application; adapters only parse. `evaluate` counts a mutant only when it covers a line the diff added or modified, and records the pre-cap `survivedTotal` so a trimmed report cannot be fully waived to PASS. Anything that stops a real inspection — no adapter, tool not installed, unreadable report, unknown outcome word, no conclusive trial, a diff that misses a changed source — answers `unsupported(<reason>)`, never `PASS`. `classify_reason` is the 3-class SSOT (capability-gap / nothing-to-verify / integrity-inspection, unknown → integrity) read by BOTH the cross-language merge here and the blocking decision in `validators/validate-run.py` |
| `run_context.py` | Per-task mutex, run context and run-input persistence; `consumers_mutex` helper for atomic `consumers.jsonl` writes |
| `path_hints.py` | Compact path-hint persistence + legacy context hydration — stores `run-context` / `active-run-context` in the schemaVersion `2.0` `identity` + `pathHints` compact schema, and hydrates the legacy flat path keys (`RUN_MANIFEST_RELATIVE_PATH`, `TEAM_STATE_PATH`, etc.) in memory the moment the host-side reader reads them |
| `consumers.py` | Append-only `consumers.jsonl` writer + reader — records which `implementation` runs consumed which `implementation-planning` stage |
| `implementation_outcome.py` | Artifact-derived reconstruction of the implementation phase outcome — reads `runs/implementation/carry/stage-<N>.json` + `consumers.jsonl` + the approved Stage Map to derive `phaseOutcome.implementation`, and when every stage has pass-grade carry evidence it raises `workflow.nextRecommendedPhase.status` to `ready` (keeping the `contract-violated` audit information). It never picks the phase — `phase` is left as the last stage's report routing settled it, and the status is raised only while `phase` is non-empty |
| `paths.py` | Path/sequence computation for task/run artifacts (including recap directory/log paths) |
| `recap.py` | deterministic backend for the okstra-inspect `recap` facet — `assemble` builds the cross-run phase transitions from the timeline, `record` append-only writes a summary/Q&A to `<task-root>/recap/recap-log.jsonl` (other task artifacts unchanged) |
| `render.py` | task manifest, run manifest, timeline, task index, discovery, team-state, prompt/template render |
| `workflow.py` | Phase sequence (`PHASE_SEQUENCE`), per-phase allowed outputs, forbidden actions. It does not decide the next phase — that is `next_phase.py` |
| `next_phase.py` | `workflow.nextRecommendedPhase` SSOT — the pointer's shape (`make` / `is_pointer` over `{phase, status, rationale}`, `status` ∈ `ready`/`pending`/`blocked`/`terminal`), the promotion of a legacy string pointer (`promote`), the projection of one report's routing field into a pointer (`project`), and the `ready`-only read the shell and wizard autofill share (`autofill_task_type`). There is no static phase table and no sequence walk: the next phase comes from what the report authored, and nothing else may compute one |
| `workers.py`, `models.py` | Worker roster; `models.py` is the model catalog SSOT (`ModelSpec` per alias + `ROLE_DEFAULTS`) — add-a-model single reference point from which picker options, codex pricing, and role defaults all derive |
| `worktree.py`, `worktree_registry.py` | One worktree per task-key, branch registry, sync dirs/files/snapshots |
| `project_meta.py`, `resolver.py`, `path_resolve.py` | Project/task/run resolution |
| `clarification_items.py` | Unified §5 clarification table parser and approval blockers |
| `md_table.py` | Markdown pipe-table escape/split SSOT — the `mdcell` filter (`escape_pipes`) and the `\|`-aware `split_pipe_row`; shared by the renderer, HTML view, and validators |
| `qa_commands.py` | QA command deny-list validation for plans |
| `conformance.py` | validates task-level Tier 3 manifests, parses `QA-RESULT`, detects diff capability surfaces, and reduces results to PASS/ADVISORY/BLOCKING; DB/HTTP/external non-PASS is user-owned advisory while local IO and contract defects remain blocking, enforced by `scripts/okstra_ctl/conformance.py::decide_conformance_gate` and `validators/validate-run.py::_validate_conformance`. Also the single definition of the plan's `Conformance tests:` declaration format (`parse_conformance_tests`, `malformed_conformance_stages`), read both at the approval boundary (`run.py::_validate_approved_plan`) and at the end of an implementation run (`validators/validate-run.py`) so the two cannot disagree |
| `pr_template.py` | PR body template resolution for release-handoff |
| `report_views.py`, `render_final_report.py`, `final_report_schema.py` | Final-report render layer: from the assembled `data.json` (schema v2 or v3) it independently produces the full reading copy Markdown and human HTML; both schema versions render the reading copy from `final-report-v2.template.md` (schema v3 reuses the v2 template for read/render compatibility) |
| `report_inputs.py` | Report contract 3.0 role-input path + single-owner registry — resolves each owning role's input artifact from the manifest (`narrative` → report-writer, `approval-decisions` → lead, `agent-activity` → activity ledger, `execution-status` → team-state, `convergence`) |
| `report_assembly.py` | Validates the role-owned report inputs and publishes the contract 3.0 record once (assembled in a temp file, then atomically promoted); a bad input aborts with owner / artifact path / field path / reason and preserves the existing `data.json` |
| `report_projections.py` | Pure projections turning role-owned execution inputs (agent activity, execution status, convergence, design, token usage) into canonical final-report fragments |
| `report_narrative.py` | Lossless read/write contract for the report-writer-owned narrative Markdown (`report-narrative-<task-type>-<seq>.md`) — the only artifact the report-writer authors under contract 3.0 |
| `report_synthesis_packet.py` | Builds the frozen, read-only synthesis input packet handed to the report-writer — collects each source's label / owner / path / content digest into `report-writer-synthesis-packet-<task-type>-<seq>.{json,md}`, materialized at dispatch by `initial_prompt_materialization.py` so the writer synthesizes from a byte-stable snapshot. `report_assembly.py` calls `verify_report_synthesis_packet_sources` at publish time so a drifted source aborts the record; schema is `schemas/report-synthesis-packet-v1.0.schema.json` |
| `approval_decisions.py` | Lead-owned approval-decision input ledger — `disposition` (`select`/`accept-risk`/`request-revision`/`reject`), reach, scope effect, and classification invariants (`correctness-critical`/`noncritical-dissent`/`user-decision`) |
| `design_snapshot.py` | Builds the design-surface-detector-owned snapshot from the report narrative — reproducible design surfaces plus conservative `PREP-NNN` preparation items (delegates surface detection to `design_surfaces.py`) |
| `report_markdown.py` | Schema-ordered Markdown serialisation of a data.json subtree for the full reading copy — headings, tables for uniform row sets, prose for narrative fields; field order read from the schema, not from the mapping |
| `final_report_paths.py`, `report_view_artifacts.py` | Path-helper SSOT for the final-report markdown/data.json pair and the generated view artifacts (HTML view, user-responses directory) |
| `wizard.py` | `okstra-run` prompt state machine; user-facing Korean strings live in `prompts/wizard/prompts.ko.json` |
| `wizard_stage_intent.py` | stage-related intent projection of the `okstra-run` wizard output — normalizes whole-task (`__whole_task__`) vs single/multi stage selection into render-args (`resolve_wizard_stage_intent`) |
| `index.py`, `jsonl.py`, `reconcile.py`, `listing.py`, `batch.py`, `backfill.py` | `~/.okstra` run index and history operations |
| `run_index_row.py` | single reference point for creating / slimming / hydrating a `~/.okstra` run-index row — runId SSOT, preserves projectId raw |
| `error_report.py`, `error_log_core.py`, `error_zip.py` | backend for the okstra-inspect errors/error-zip facets — `error_log_core` is the read-only core that globs/parses/aggregates `errors-*.jsonl`, `error_report` renders the errors facet, and `error_zip` collects cross-project run directories, allowlist-anonymizes, aggregates clusters, and produces a zip |
| `error_log_write.py` | the single writer for `errors-*.jsonl`, shared by the `okstra error-log` CLI and by `dispatch_core`, which records a wrapper's non-zero exit as a `cli-failure` in-process. Owns the agent/role/error-type allow-lists (agents derived from the provider registry) and the cause-evidence gate |
| `run_audit.py` | backend for the okstra-inspect run-audit facet — reads run-manifest / final-report / team-state artifacts and reports invariant violations (read-only, never the lead's self-report) |
| `worker_heartbeat.py`, `worker_liveness.py` | `worker_heartbeat` is the single definition of the `- PROGRESS:` heartbeat line shape and its 5-minute (+60s grace) cadence budget, shared by the Phase 7 audit (`validators/validate_session_conformance.py`) and the live probe; `worker_liveness` backs `okstra worker-liveness`, resolving each pending worker from its team-state row (`livenessMode` picks the artifact, `startedAt` anchors the grace) and reporting `stalled` (heartbeat past the budget, or none yet for this dispatch past the grace) or `did-not-launch` (no wrapper `.log`/`.status.json` past the launch grace) |
| `log_report.py`, `time_report.py` | read-side backend for the okstra-inspect logs/time facets (`okstra log-report` pairs each wrapper transcript `.log` with its sibling prompt `.md` and reports both byte counts without changing legacy transcript-size fields; `okstra time-report` is per-task time aggregation) |
| `rollup.py` | read-side backend for the okstra-rollup skill — fans the catalog out per task-group (or the whole project) and deterministically aggregates each task's run count, elapsed time (raw ms), error count, and latest report path, plus group-level totals/status, category, and phase distribution. Reuses the `time_report`/`error_log_core` functions and delegates report-body synthesis to the skill |
| `usage_report.py` | Read-only okstra-usage backend — scans the whole current project's recent run timelines, defaults to 30 days, and returns task-type coverage, raw/billable tokens, known USD cost, CPU-sum and wall-clock milliseconds, unavailable reason counts, and unmatched pricing models |
| `json_registry.py` | shared flock + atomic JSON persistence for small okstra registries (`registry_lock`/`load_registry_json`/`save_registry_json`) — shared by `container_registry` and `worktree_registry` |
| `stage_integrate.py` | whole-task stage integration (merge) + worktree teardown core (`integrate_stages`) — shared by whole-task final-verification entry, `okstra integrate-stages`, and container up |
| `resolve_task_key.py` | shared skill helper resolving a bare task-id → `task-catalog.json` candidate entries (`okstra resolve-task-key`) |
| `code_review_paths.py` | filesystem-layout SSOT for code-review result files — `stage_review_dir` / `branch_review_dir` plus `next_stage_review` / `next_branch_review`, which read the existing files to derive the next round's name (`stage-<NN>.md`, then `-r2`, `-r3`, …) or the next same-day sequence (`<YYYY-MM-DD>-<NN>.md`), so skill markdown never re-derives a literal review path |
| `code_review_target.py` | `okstra code-review target` backend — argument validation and JSON shaping only. Stage mode delegates whole to `okstra_project.state.code_review_target_snapshot`; branch mode is resolved here, defaulting the diff base to the merge-base with the default branch (`refs/remotes/origin/HEAD`, else `main`/`master`). Read-only: it never creates the review directory |
| `session.py`, `tmux.py`, `seeding.py`, `locks.py`, `invocation.py`, `sequence.py`, `ids.py`, `material.py` | Supporting lifecycle helpers |
| `pane_reclaim.py` | resolves which runs of the current project still hold a non-terminal dispatch, so the `SessionStart(compact)` hook can re-inject the pane-cleanup obligation for them. The signal is the newest `team-state` per run directory, not the central run index — an in-session run never appears there (ADR-0011). Imports the status split from the `dispatch_state.NON_TERMINAL_WORKER_STATUSES` SSOT |
| `improvement_lenses.py` | lens enum SSOT + cap constants for the improvement-discovery phase (DEFAULT 8, ABSOLUTE 12, MIN/MAX PRIORITY 1/4, SOURCE_WORKERS) |
| `improvement_assignment.py` | improvement-discovery primary-pass lens assignment — round-robins the resolved `requiredWorkerRoles` order over the resolved priority lenses (`assign_primary_lenses`) and validates the resulting map (`validate_primary_lens_assignments`). Only the primary pass rotates; every analyser still confirms the full lens set afterwards |
| `container.py` | the `okstra container` convergence entrypoint of the okstra-container-build public skill — `provision_container_group` + `up`/`status`/`logs`/`stop-watcher`/`down` dispatch, env-override synthesis, compose argv assembly, and per-container watcher startup |
| `container_registry.py` | flock-guarded auxiliary index — tracks per-container-group tmux session/pane and watcher findings |
| `plan_run_root.py` | shared helper deriving `approved_plan_path` → `plan_run_root` and back-tracing the task-key |
| `manager_cli.py` | `okstra manager` Python entrypoint — purpose-specific fixed text by default, machine JSON with `--json` |
| `manager_paths.py` | Manager state path SSOT under `~/.okstra/managers/<manager-id>/`; slug fallback uses `u-<sha1-prefix>` when a safe segment would be empty |
| `manager_store.py` | Manager-owned state mutation — project membership, task planning, assignment, directives, event append |
| `manager_sync.py` | One-way child project `.okstra` snapshot reader; corrupt child state becomes row-level `error` so other children continue |
| `manager_launch.py` | Child launch packet and manager child context renderer; records `prepared` launch metadata/events without changing project-local task state |
| `agent_invocation.py` | Deep invocation-contract module — composes model assignment, common/functional duty, and task instructions; publishes immutable prompt/metadata pairs; verifies five digests; owns standalone result/completion envelopes |
| `agent_prompt_cli.py` | CLI boundary for run-backed and standalone materialization/verification plus host-native dispatch and result-link records |
| `dispatch_state.py` | Provider-neutral `WorkerJob`, invocation metadata validation, immutable host-native dispatch/result-link recording, and shared team-state mutation helpers |
| `dispatch_core.py` | Backend-neutral worker dispatch core — verifies invocation metadata immediately before worker execution, then records and collects code-owned process/pane attempts shared by every lead runtime |
| `worker_dispatch.py` | Provider-neutral deterministic dispatcher for every `runner=cli-wrapper` assignment; it never composes or rewrites a prompt |
| `cmux.py` | cmux-pane worker backend — mirrors the tmux backend's contract (a worker that gets a pane frees the lead process; anything that stops a pane opening degrades quietly to the blocking wrapper, recording a surface UUID rather than a tmux pane id). Detects a usable cmux session before selecting the backend (CLI resolves + ping answers PONG + the lead's workspace is resolvable), derives placement from the workspace geometry each dispatch, relays lead/worker events to the cmux sidebar, and records the run's terminal backend in the manifest so both phases of a run land on one backend. A sandbox that hides cmux (`PermissionError` on the socket) stops dispatch with the remedy instead of degrading into the same broken fallback; a quit app (`FileNotFoundError`) still degrades |
| `codex_dispatch.py` | Compatibility adapter delegating `okstra codex-dispatch` to the provider-neutral `worker_dispatch` path |
| `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run from worker-owned profile sections; report/lead procedure stays outside the packet |
| `analysis_inputs.py` | shared input boundary for `project-analysis`, `feature-analysis`, and `change-impact-analysis` — validates evidence-report identity and review status, enforces the type-to-type relation allowlist, computes `exact`/`stale` freshness, and resolves free-text or `PF-NNN` feature targets for both wizard and prepare paths |
| `user_response.py` | parses clarification/approval responses and the analysis-review sidecar; `parse_analysis_review` validates accepted, revision-requested, and rejected decisions plus their affected IDs and reason; `format_show_view` prints why-asked, linked plan items, and cited artifacts for the in-session picker |
| `context_cost.py` | read-side context-cost estimator for a prepared okstra task bundle (the `okstra context-cost` backend) |
| `schema_excerpt.py` | generates a task-type-scoped excerpt of the final-report schema — a schema reduction to inject into the worker/lead prompt |
| `work_categories.py` | requirements-discovery work-category (domain) **SSOT** (`is_valid_category`) — the work-category allowlist is defined only here |
| `model_discovery.py` | pre-dispatch model-identity normalization for CLI workers — roster-gated label correction + a per-role reasoning-effort policy (deterministic, no per-run improvisation) for CLIs (agy) that bake effort into the model name |
| `domain/`, `application/`, `ports/` | Host-neutral values and errors, wizard/run use cases, and the interaction/session/dispatch/accounting port contracts |
| `registry/host_registry.py`, `registry/provider_registry.py` | Discover bundled adapters plus explicit user installs under `~/.okstra/adapters/{hosts,providers}/<id>/`; project-local adapter code is outside the discovery roots |
| `adapters/hosts/`, `adapters/providers/` | Six bundled host strategies and the independent provider catalogs; host manifests select a native provider without merging the two axes |
| `lead_events.py` | Structured JSONL events emitted by artifact-accounted lead runtimes. Its locked append path assigns monotonic `A-NNN` identifiers to activity events in the same canonical event file. |
| `team_reconcile.py` | stale team-member reconciliation at run-end teardown |
| `worker_prompt_headers.py` | shared rendering of phase-aware worker prompt anchors (`worker_prompt_headers`): coding-preflight only for implementation and compact target identity for final-verification |
| `worker_prompt_body.py` | provider-neutral initial analysis body/input renderer shared by Codex and external/team dispatch paths |
| `report_language.py` | report-writer `**Report Language:**` resolution (`resolve_report_language`) — precedence project config → global config → task-brief inference, shared by both dispatchers so the stamped language does not depend on which dispatch path ran |
| `worker_prompt_policy.py` | `PromptPlan` generating SSOT — resolves functional audience, equality group, packet-only input, coding-preflight eligibility, required headers, and size limits without consulting provider/model identity |
| `worker_prompt_contract.py` | deterministic cross-task initial-prompt validator and normalized cross-worker equality SSOT, reused by dispatch adapters and the Phase 7 persisted-artifact validator |
| `initial_prompt_materialization.py` | sole owner of roster-derived initial prompt rendering, request compatibility checks, contract validation, and immutable create-if-absent publication; exposes `materialize_initial_prompts(InitialPromptMaterializationRequest) -> tuple[Path, ...]` |
| `convergence_engine.py` | pure `ConvergenceEngine` reducer — seeds Round 0 working state, plans roster-aware rounds, applies structured outcomes and one critic-gap batch, finalizes schema v1.3, and validates replayable state without dispatch or filesystem ownership |
| `convergence_store.py`, `convergence_migration.py` | atomic JSON persistence plus legacy/new-engine seed decisions; valid terminal finals are reused, while invalid state requires byte-preserving archival before restart |
| `convergence.py` | `okstra convergence` internal CLI orchestration for `seed`, `plan-round`, `apply-round`, `apply-critic-gaps`, `finalize`, `validate`, and `example`; it composes the reducer, store, and migration policy without duplicating their decisions |
| `plan_items.py`, `plan_items_cli.py` | deterministic extraction of the report-writer narrative `P-*` plan-item queue plus the `okstra plan-items extract` / `validate` / `seed` / `collect-verdicts` / `apply-verdicts` / `derivations` adapter; v2 data.json remains a read input |
| `claim_reproduction.py` | reproduces a plan-body single-vote `fact` claim before it can block on one vote — runs the declared probe (`path-exists` / `path-absent` / `literal-present` / `literal-absent` / `citations-differ`) inside the resolved project root and returns `reproduced` / `not-reproduced` / `not-runnable`, which `plan-items apply-verdicts --run-manifest` writes into `reproductionResult` (always overwriting the worker-sent value so a verifier cannot score its own claim). A `judgement` claim, or a `fact` that does not reproduce, takes the quorum route |
| `plan_derivations.py` | the supersession sweep `_common-contract.md` requires an author to do by hand — extracts the symbols, paths, and ids an answered clarification names and reports every plan string that mentions one. Advisory: it locates candidates and never judges which are now false |
| `scope_provenance.py` | single source of truth for the scope-provenance grammar every phase-emitted requirement must declare, shared by `validators/validate-run.py` and `validators/validate_fanout.py` so the planning report and fan-out packets cannot drift |
| `worker_artifact_paths.py` | canonical worker artifact path derivation (e.g. `audit_sidecar_rel` inserts `-audit-` after the first `-worker-` token), so dispatch and validation agree on non-canonical-path rejection |
| `report_finalize.py` | Phase 7 post-report sequence **SSOT** — runs `check-source` → `token-usage` → `render-views` → `spawn-followups` → `validate-run` in that load-bearing order. A non-zero exit still runs every later check through `validate-run` and names the earliest failure; `teardown-stages` is skipped when any earlier step failed. Both lead paths converge here: the Codex adapter calls it in-process (`codex_dispatch`), a Claude-led run reaches it through `okstra report-finalize`. Neither reimplements the sequence |
| `wrapper_status.py` | worker wrapper status sidecar reader — the host-side reader of the sidecar `worker_runner.py` writes. `is_terminal` is the one question it answers for the dispatch record and the pane reclaim: does `stage` read `exited` |
| `worker_runner.py` | runs one worker CLI and records what happened — shared by every provider entrypoint. Owns the `selectors` pump over the child's streams, the stream-arrival idle watchdog (`killpg` on breach), the run-wide progress cap on the log copy, and the status sidecar's whole life. A run that dies after launch still closes its sidecar, so `worker_liveness` never reads a dead worker as running |
| `session_transcript.py` | worker session transcript — one line per event (time, speaker, body) with a run-wide progress-line cap (`LOG_LINE_CAP`, elision notice) so a single-file dispatch's tool echo cannot dominate the project's `.okstra/` bytes; the fixed shape lets a later lead write share the same file |
| `domain/worker_presentation.py` | provider-output presentation strategy — decides whether to merge stderr into stdout and who receives each stream's lines, so "do not interpret" is a first-class option and the screen does not silently blank when a provider changes its output format |
| `worker_request.py` | assembles the `WorkerExecRequest` every strategy then takes on trust: resolved paths, the write scope in the order the CLIs are told it (project root → stage tree → the tree's git-common-dir), the verifier's toolchain grants, and the role's idle budget |
| `domain/worker_exec.py` | the provider axis' vocabulary — `WorkerExecRequest`, `ExecCommand`, `ExecutionPolicy`, the `ExecutionStrategy` protocol, and `PolicySupport`, by which a provider that *cannot* express the policy must say so rather than silently run without it |
| `domain/worker_stream.py` | the normalised event vocabulary (`Text` / `ToolCall` / `ToolResult` / `Denial` / `Result`) plus its three pure projections: `format_live` (one readable row per event, for the pane), `format_log` (the same plus bodies, for the archive), `final_text` (the closing message alone). Also `content_block_events`, the normaliser for the wire shape keyed on `type` with `message.content` blocks, which three providers share. No files, no clock |
| `domain/worker_role.py` | per-role execution budgets — the 1500s/600s idle pair lives here once instead of being re-declared in each wrapper |
| `task_target.py` | shared helper resolving `task-key → (task_root, project_root)` (`resolve_task_root`) |
| `contract_graph.py`, `contract_graph_cli.py` | runtime-contract graph loader + cross-reference/dependency-closure validator and its `okstra contract-check --root <dir> (--profile\|--operation)` CLI boundary. Loads the agent contract schemas (`common`/`role`/`duty`/`profile`/`operation`), validates known role capabilities, and reports the dependency closure with per-file `path`/`schemaVersion`/`sha256`; an invalid contract raises `ContractGraphError` |
| `json_boundary.py` | strict JSON persistence boundaries for okstra-owned artifacts — a sealed `ExternalJsonSource` (validated producer + path) is the only way owned JSON is read, and `JsonBoundaryError` names artifact / reason / path when a write cannot satisfy its contract; the SSOT that keeps the model out of internal JSON key/path authorship |
| `fixed_text.py` | shared scalar-line format for the model-facing fixed-text projections — `scalar` neutralises complex values and control characters, `line` renders one static-labelled Markdown list row, and `value_lines` losslessly flattens a JSON-shaped value into fixed name/order/value rows |
| `model_io_cli.py` | renders purpose-scoped fixed Markdown input from okstra-owned JSON for the model boundary — resolves the current run/project through the run manifest (`validated_run_authority`, `canonical_run_state_artifact`) and emits only each command's allow-listed fields in fixed order instead of expanding arbitrary nested objects |

> `i18n.py` (the final-report i18n dictionary loader + Jinja2 lookup) is an intentionally undocumented internal helper — it is a render helper that users and contributors do not need to know about in the canonical docs, so it is excluded from the module map.

### 4.4 `scripts/okstra_project/`

Project resolver and read-only state helpers:

- `resolver.py`: locate project root and upsert `project.json`
- `state.py`: read project metadata, task catalog, task manifest, and curated task read-side snapshots
  - `code_review_target_snapshot()`: resolve one implementation stage's review target — worktree path (empty once the stage worktree is torn down), branch, `base_ref` from the stage's registry row, head commit, result path, and round — so callers never read the registry or the run-root layout themselves

### 4.5 `scripts/okstra_token_usage/`

Token/cost accounting:

- provider adapters: `claude.py`, `codex.py`, `antigravity.py`, `grok.py` (`grok.py` reads the Grok Build session docs under `~/.grok/sessions/<percent-encoded-cwd>/`, taking cumulative tokens from the last `params.update.usage.modelUsage` snapshot in `updates.jsonl`)
- aggregation: `collect.py`, `blocks.py`, `jsonl_io.py`, `paths.py`
- incremental scan cache: `cursor.py` (`$OKSTRA_HOME/cache/token-usage/` byte cursor + usage event extracts; bypass with `--no-cache`)
- pricing: `pricing.py`
- report substitution: `report.py`
- CLI: `cli.py` via `scripts/okstra-token-usage.py` and `okstra token-usage`

### 4.6 `prompts/`

| Path | Role |
|---|---|
| `launch.template.md` | Lead prompt template rendered for each run |
| `duties/common.md`, `duties/<audience>.md` | Canonical common and functional duty contracts composed into every Okstra-owned LLM invocation; `direction-selection-worker` owns direction comparison/validation while `planning-worker` realizes the selected direction; provider/model identity does not select the duty |
| `profiles/_common-contract.md` | Shared phase contract |
| `profiles/<task-type>.md` | Phase profiles (single language — runtime always loads from `profiles/`, never a translated mirror) |
| `implementation-option-selection.md` | Read-only lifecycle profile for candidate comparison or preselected-direction validation before detailed planning |
| `project-analysis.md`, `feature-analysis.md`, `change-impact-analysis.md` | Read-only sidetrack profiles for project mapping, one-feature behavior tracing, and proposed-change impact mapping |
| `wizard/prompts.ko.json` | Korean wizard prompt single source of truth |

### 4.7 `templates/`

| Path | Role |
|---|---|
| `templates/reports/final-report-v2.template.md` | Full reading copy Markdown spine |
| `templates/reports/final-report-v2.template.md` | Schema v2 full reading copy Markdown spine |
| `templates/reports/md/tasks/*.template.md`, `md/macros/sections.md` | Eleven dedicated task bodies for the full reading copy Markdown, sibling of `html/tasks/`; shared section macro |
| `templates/reports/html/base.template.html`, `html/tasks/*.template.html` | Shared HTML shell plus eleven dedicated task templates for human reports; task bodies are not shared |
| `templates/reports/report.css`, `report.js` | Inline assets for self-contained HTML report views |
| `templates/reports/*.template.md` | Inputs, schedule, user-response, settings templates |
| `project-analysis-input.template.md`, `feature-analysis-input.template.md`, `change-impact-analysis-input.template.md` | Brief input templates for the three analysis sidetracks |
| `user-response.template.md`, `report.js` | Analysis Review sidecar block and the browser control that exports accept/revision/reject without changing the source report |
| `templates/project-docs/task-index.template.md` | Project task index template |
| `templates/worker-prompt-preamble.md` | Initial analysis audience procedure and output contract |
| `templates/implementation-worker-preamble.md` | Shared implementation executor/verifier procedure, including coding-preflight and worktree rules |
| `templates/report-writer-prompt-preamble.md` | Report-writer input and authoring procedure without analysis or implementation instructions |
| `templates/worker-error-contract.md` | Audience-neutral error-path, sidecar schema, and write protocol shared by every initial worker |

### 4.8 `schemas/`

`schemas/final-report-v3.0.schema.json` is the current final-report data.json contract. The report-writer worker writes only the narrative Markdown. Report assembly combines it with the role-owned machine inputs and atomically publishes `final-report-<task-type>-<seq>.data.json`; independent renderers produce the full reading copy Markdown and task-specific human HTML. `schemas/final-report-v2.0.schema.json` is the historical read contract.

The deterministic convergence inputs are `schemas/convergence-groups-v1.0.schema.json`, `schemas/convergence-round-results-v1.0.schema.json`, and `schemas/convergence-critic-results-v1.0.schema.json`. `tools/build.mjs` syncs the entire source `schemas/` directory to `runtime/schemas/`; these JSON Schema files are runtime contracts, not Markdown publication-inventory entries.

Optional (v1.0 backward-compatible) top-level keys:

- `readerSummary` — the summary block a human reads first. When present, all five fields (`decision`, `humanActionRequired`, `blockingItems`, `safeToSkip`, `recommendedCommand`) are required. Existing data.json without it still renders as-is (the HTML dashboard falls back to `verdictCard`).
- `implementationPlanning.incrementalDecision` — the incremental re-verification decision (`mode`, `reverifyStages`, `carryStages`, `reason`). When `mode == "incremental"`, the renderer emits the `### 0.1 Incremental Re-Verification Scope` audit block and `validators/validate-run.py` blocks its absence.

### 4.9 `validators/`

| File | Role |
|---|---|
| `validate-run.py` | Run/final-report validation: schema v2 record + structured data rules |
| `validate-brief.py`, `validate-brief.sh` | Brief frontmatter/body contract validation |
| `validate-report-views.py` | HTML view validation (form-control placement / no external URLs / stale source digest / Response ID parity) |
| `validate_analysis_report.py` | Cross-field validation for the three read-only analysis reports: frozen target/evidence snapshots, current-code evidence, review-source identity, and exact affected-ID resolution coverage on revision reruns |
| `validate-schedule.py` | Schedule section/order/code validation |
| `validate-implementation-plan-stages.py` | enforces the Stage Map structure — checks the S1–S8 rules (`## 5.5 Stage Map` + `## 5.5.<i> Stage <i>` sections, ≤ 8 steps per stage, etc.) |
| `validate_improvement_report.py` | enforces the 11-item contract of the improvement-discovery final-report. Automatically invoked by `validate-run.py` when `task_type == "improvement-discovery"` |
| `detect_self_mock.py` | self-mock detector — runs BOTH gates and writes the run's sidecar. Gate A (static) scans the changed TEST files for SUT-stub signals (patterns imported from the SSOT `scripts/okstra_ctl/self_mock_signals.py`, never redefined here), matching each file as one whole-file string so multi-line signals are caught. Python strings and comments are token-masked without changing line positions before those regexes run, so examples in docstrings and comments do not become findings while executable `patch.object(self, ...)` and `sut._private` accesses remain detectable. Writes a `qa/self-mock[-stage-<N>].json` sidecar and prints `QA-RESULT: PASS|FAIL` as its last line (exit 0 = no hits, exit 1 = at least one hit). The sidecar records `scannedFiles`/`skippedFiles` so the gate can prove every changed test file was actually scanned (a run that skips them cannot pass on empty input). An optional `--waivers <path>` moves hits matching `(file,line,signal)` from `staticDetect.hits` to `staticDetect.waived` (each carrying the user's `reason`/`acknowledgedBy`) and records the file as `waiverSource`. Gate B (mutation) runs in the same call: `--changed-file` takes the stage's WHOLE changed set (each adapter selects its own production sources out of it), `--diff` and `--worktree` scope it, and `scripts/okstra_ctl/mutation_probe.py` writes the result into the sidecar's `mutation` block; the received set is recorded as `changedFiles` so the gate can prove gate B was not handed an empty input. `overall` and the exit code follow BOTH gates — a mutation FAIL with a clean static scan still exits 1. The same `--waivers` file feeds both (gate A reads its `signal` entries, gate B its `mutant` ones). Its verdict feeds the fail-closed `_validate_selfmock` gate in `validate-run.py` (implementation / final-verification): a diff that touches test files with no readable PASS sidecar blocks the run; a `waived` entry missing `reason`/`acknowledgedBy`, or a `waiverSource` that is not the task's own `qa/self-mock-waivers.json`, also blocks |
| `validate-workflow.sh` | End-to-end fixture workflow validation |
| `lib/*.sh` | Shared shell validator helpers and fixtures |

### 4.10 `skills/`

14 user-facing skills (the only skills `okstra install` copies). The list SSOT is `USER_SKILL_NAMES` in `src/lib/skill-catalog.mts`; the Claude plugin manifest and the installer both derive from it.

Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflight, python bootstrap note) is kept canonical in `skills/_fragments/*.md` and expanded in place inside each `SKILL.md` between `<!-- BEGIN FRAGMENT: <name> -->` / `<!-- END FRAGMENT: <name> -->` markers by `tools/sync-skill-fragments.mjs` (`--check` fails on drift). Sources stay fully expanded, so `runtime/` and installed copies remain self-contained; the guards are `tests-js/skill-fragments.test.mjs` and `tests/contract/test_prompt_fragment_ownership.py`.

| Skill | User-invocable | Role |
|---|---:|---|
| `okstra-brief-gen` | yes | Produce task brief from ticket/doc/link/conversation |
| `okstra-run` | yes | Start/resume an okstra task in the current registered host session |
| `okstra-memory` | yes | Store/search/archive global conversation memory under `~/.okstra/memory-book` |
| `okstra-chat` | yes | Global rooms under `~/.okstra/chat` so host sessions from different providers can send and read addressed messages |
| `okstra-inspect` | yes | Unified read-side — sub-commands `status` (lifecycle + workStatus), `history` (past runs / re-run / resume), `report` (find final-report), `time` (elapsed-time breakdown), `logs` (wrapper log inventory + cleanup), `cost` (task bundle context/read cost), `errors` (error-log aggregation), `error-zip` (anonymized cross-project error bundle), `run-audit` (progress-invariant audit over run artifacts), `recap` (cross-run phase recap). `SKILL.md` is a thin core (preflight + dispatch table + shared rules) and each sub-command body lives in `skills/okstra-inspect/facets/<sub-command>.md`, lazily read only after dispatch resolves; the 1:1 match between dispatch rows and facet files is enforced by `tests/contract/test_okstra_inspect_facets.py` |
| `okstra-rollup` | yes | Cross-task roll-up — aggregate runs/time/errors across a task-group (or whole project) and synthesize a digest from the report files |
| `okstra-usage` | yes | Read-only project usage snapshot — aggregate recent run coverage, tokens, known cost, CPU, and wall-clock time by task type (default: 30 days) |
| `okstra-schedule-gen` | yes | Generate task-group schedule |
| `okstra-container-build` | yes | Non-linear deploy tool — deploy a verified task's code as a docker compose group and watch each container (sub-commands `up` / `status` / `logs` / `stop-watcher` / `down`) |
| `okstra-pr-gen` | yes | Register PR body templates under `~/.okstra/template/pr/` and generate a PR description from a branch diff (drives `okstra pr template` / `branches` / `gen`). **Global skill** — needs a Git repo, not `<PROJECT_ROOT>/.okstra/project.json` |
| `okstra-manager` | yes | Coordinate cross-project okstra tasks through manager-owned plans, assignments, sync snapshots, status, and child launch context packets |
| `okstra-setup` | yes | Install/check runtime and register project |
| `okstra-user-response` | yes | Submit answers to a run's open clarification items through the installed response helper without hand-editing artifacts. The skill reads cited context from `show-view` before asking, uses the host native selection UI, and each option names the outcome of picking it |
| `okstra-code-review` | yes | Census-based code review of a diff — one okstra `implementation` stage, or any branch — against this project's coding-preflight rules. The result file goes under the task bundle (`code-reviews/stage-<NN>.md`) in stage mode and under `.project-docs/code-reviews/<branch>/` in branch mode; `okstra code-review target` resolves both |

> The former internal skills — `context-loader`, `team-contract`, `convergence`, `report-writer`, `coding-preflight` — and the lead contract are no longer skills. They ship as runtime resources under `prompts/lead/*` and `prompts/coding-preflight/*` (installed to `~/.okstra/prompts/`); see §4.11.

### 4.11 `agents/`

| File | Role |
|---|---|
| `agents/workers/claude-worker.md` | Claude analyzer/verifier/executor spec |
| `agents/workers/report-writer-worker.md` | data.json SSOT author and audit sidecar writer |
| `agents/workers/translator-worker.md` | Claude-native final-report translation execution adapter |

These files are native Claude execution adapters, not provider-neutral LLM transport wrappers. Non-native providers execute through the deterministic `worker-dispatch` process boundary. The neutral lead lifecycle contract lives at `prompts/lead/okstra-lead-contract.md`. Executable host strategies and their relay contracts live together under `scripts/okstra_ctl/adapters/hosts/<host-id>/`; `prompts/lead/adapters/cmux.md` remains the environment-selected cmux worker-backend contract. Lead resources are installed under `~/.okstra/prompts/lead/`, while executable host adapters are installed under `~/.okstra/lib/python/okstra_ctl/adapters/hosts/`. They are runtime resources, not agent skills.

### 4.12 `tests/` and `tests-e2e/`

- `tests/`: pytest modules organized by production boundary. `domain/wizard/` owns wizard state and answer behavior, `application/` owns use-case and render orchestration tests, and `adapters/{hosts,host_contract,providers,dispatch,accounting}/` owns external strategy contracts. Existing `run/`, `contract/`, `report/`, `inspect/`, `worktree/`, and `handoff/` folders retain their narrower responsibilities. The shared path SSOT is `tests/_paths.py` (`REPO_ROOT`/`TESTS_DIR`/`FIXTURES`), and the repo-root `pytest.ini` adds `tests/` to the import path. Fixtures live in `tests/fixtures/`.
- `tests-e2e/`: `scenario-<id>-<name>.sh` shell scenarios (record-start/reconcile, rerun, task lock, agent install, report view, etc.).
- Each behavior branch has one owning test at the lowest practical layer.
- An end-to-end scenario must cross the public CLI or installed-runtime boundary.
- Tests replace operating-system resources and wall-clock waits with controlled doubles.
- A higher-layer test keeps only the minimum positive flow needed to detect wiring failures.

### 4.13 `tools/korean-sources/`

The maintainer-only Korean review mirrors. The tool reports which mirrors have
drifted and checks a translation's structure; a maintainer session does the
translating and the writing. Nothing here observes edits or runs on its own.

| File | Role |
|---|---|
| `cli.mjs` | The three commands: `status`, `validate --source`, `mark --source` |
| `baseline.mjs` | Drift report and the source/mirror hash pair recorded at the last sync |
| `config.mjs` | `config/korean-sources.json` validation and source↔mirror path mapping |
| `markdown.mjs` | Protected-Markdown structure comparison behind `validate` |
| `path-safety.mjs` | Symlink-refusing repository reads and atomic writes |

Source and mirror hold the same document in two languages, so they never hash
alike and drift cannot be read from the two files alone. `mark` records the pair
of hashes in `.project-docs/ko-sources/.sync-baseline.json`, which is the only
state the tool keeps. Maintainers follow `tools/korean-sources/workflow.md`; the
paired local skills only delegate to it and are not published user skills.

---

## 5. Key runtime modules

### 5.1 Prepare flow

`prepare_task_bundle()` coordinates:

1. Resolve project root and verify/upsert `project.json`.
2. Resolve profile, required workers, role counts, `ModelPool` assignments, and the implementer provider. `lead`/`executor` remain input aliases only.
3. Resolve task identity segments, work category, and the run sequence input needed for path allocation.
4. Provision or reuse the task-key worktree, or the selected implementation stage worktree for stage-isolated runs.
5. For an analysis sidetrack, resolve the immutable source commit from the provisioned worktree's `HEAD`, then resolve evidence reports, freshness, and the feature target through `analysis_inputs.py`.
6. For a new `implementation-planning` run, validate `--selected-direction` and materialize `instruction-set/selected-direction.json`; a same-task planning rerun validates its prior planning report instead.
7. Compute task/run paths and persist run context under `runs/<task-type>/manifests/`.
8. Materialize `instruction-set/` files and lead prompt snapshot.
9. Persist run inputs, team state, task manifest, task index, run manifest, timeline, discovery pointers.
10. Record the run in `~/.okstra/{active,recent}.jsonl` and project index.

### 5.2 Worktree model

Non-`implementation` phases share one task-key worktree:

```text
~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/
```

Branch name (namespace by work_category — `feature`/`improvement` → `feature/`, `bugfix` → `fix/`, `refactor` → `refactor/`, `ops` → `ops/`, unset → `task/`; work_category resolves as explicit flag → `task-manifest.json` classification → `feature`, so `task/` is reached only when nothing was ever recorded):

```text
<work-category-namespace>/<task-id-segment>
```

That task-key worktree is reused for `requirements-discovery` through `implementation-planning`, and by whole-task verification / handoff flows after implementation stages have been integrated. `worktreeSyncDirs`, `worktreeSyncFiles`, and `worktreeSnapshotFiles` provide filesystem continuity only; they do not expand okstra's context/write boundary.

`implementation` is stage-isolated: one run owns one `stage-<N>` worktree and branch, with stage-key reservation in `worktree_registry.py`.

```text
~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/stage-<N>/
<work-category-namespace>/<task-id-segment>-s<N>
```

Single-stage `final-verification --stage <N>` reuses the matching implementation stage worktree read-only, while run artifacts are isolated under `runs/final-verification/stage-<N>/`. Whole-task final-verification keeps the flat `runs/final-verification/` shape.

### 5.3 Final report model

Current report pipeline:

1. Analysis workers write worker result files and the separate audit sidecars named by `Audit sidecar path`.
2. The lead writes semantic groups; the convergence engine persists working state, per-round plans/results, an optional critic transition, and then a validated `state/convergence-<task-type>-<seq>.json` terminal state: schema v1.3 when newly finalized, or an unchanged historical final schema v1.0, v1.1, or v1.2 returned by `reuse-final`.
3. Report-writer worker writes `worker-results/report-writer-narrative-<task-type>-<seq>.md`, including `humanSummary` and one task-type deliverable; Phase 7 later assembles the schema v3 report record.
4. For implementation-planning, `okstra plan-items extract` creates the complete `P-*` queue, `validate` proves it still matches data.json, and the analyser instances run the separate plan-body verification round.
5. Token usage substitution fills usage/cost cells in the report record. The full reading copy is rendered on demand with `okstra render-final-report` from `templates/reports/final-report-v2.template.md`.
6. `scripts/okstra-render-report-views.py` independently selects one of eleven dedicated task templates and emits human-facing HTML directly from the same data.json; run validation checks the record and the human HTML. A quick Markdown input retains its legacy conditional path.

For the three analysis sidetracks, the HTML view also exports an immutable-source `## ANALYSIS REVIEW` sidecar. A revision rerun carries that sidecar, reanalyzes the whole confirmed scope, and records one `analysisReviewResolution` row for every affected ID before `validate_analysis_report.py` accepts the result.

Both Markdown and HTML are derived, not authoring sources. The schema is the contract.

### 5.4 Role execution and model defaults

- `scripts/okstra_ctl/domain/role.py` — canonical roles and duty mapping.
- `scripts/okstra_ctl/model_pool.py` — unified catalog lookup.
- `scripts/okstra_ctl/model_cli.py` — `okstra model list` and atomic `modelDefaults` writes. No inference call.
- `scripts/okstra_ctl/pane_title.py` — pane titles from stored `executionLabel`.
- `scripts/okstra_ctl/doctor.py` — `model_pool_diagnostics()` for `okstra doctor --json`.

---

## 6. Post-install layout

```text
~/.okstra/
├── version
├── lib/python/{okstra_ctl,okstra_project,okstra_token_usage,okstra_vendor,lib/}
├── bin/{okstra.sh,okstra-claude-exec.sh,okstra-codex-exec.sh,okstra-antigravity-exec.sh,...}
├── templates/
├── installed-skills.json
├── installed-agents.json
├── active.jsonl
├── recent.jsonl
├── projects/<project-id>/{meta.json,index.jsonl}
├── managers/<manager-id>/{manager.json,projects.json,task-groups/...}
├── worktrees/{registry.json,registry.lock,<project>/<group>/<task>/}
├── archive/
└── .locks/

~/.claude/skills/okstra-*/SKILL.md
~/.claude/agents/{claude,codex,antigravity,report-writer}-worker.md

<PROJECT_ROOT>/.okstra/
├── project.json
├── CLAUDE.md
├── glossary.md
├── decisions/<NNNN>-<slug>.md
├── discovery/{task-catalog.json,latest-task.json}
└── tasks/<task-group>/<task-id>/
    ├── task-manifest.json
    ├── task-index.md
    ├── history/timeline.json
    └── runs/<task-type>/
        ├── instruction-set/
        ├── manifests/
        ├── state/
        ├── prompts/
        ├── reports/
        ├── status/
        ├── sessions/
        ├── worker-results/
        ├── user-responses/
        ├── design-prep-requests/     # implementation-planning only: Okstra-owned AI draft request (deterministic, idempotent)
        ├── design-prep-inputs/       # implementation-planning only: user/wizard confirmation responses (append-only revision)
        ├── carry/                    # implementation only: stage-<N>.json evidence sidecar
        ├── logs/
        └── (consumers.jsonl)         # implementation-planning only: impl-run backlink accumulation file
```

Project-local `<PROJECT_ROOT>/.claude/settings.local.json` is provisioned as a symlink so spawned agents can load okstra worker-wrapper permissions.

---

## 7. Core workflows

### 7.1 First setup

1. `okstra install`
2. `okstra doctor --runtime claude-code`
3. inside target repo: `okstra setup --project-id <id>` or `/okstra-setup`

### 7.2 Brief creation

`/okstra-brief-gen` turns source material into a validated task brief. It preserves source material verbatim, labels okstra augmentation, and records reporter confirmations.

### 7.3 Task run

`/okstra-run` or `~/.okstra/bin/okstra.sh` collects task inputs and calls `prepare_task_bundle()`. The lead then works from the rendered instruction-set.

### 7.4 Phase flow

| Phase | Purpose | Typical next step |
|---|---|---|
| `requirements-discovery` | Classify and route work | `error-analysis` or `implementation-option-selection` |
| `error-analysis` | Reproduce and explain failure | `implementation-option-selection` |
| `implementation-option-selection` | Compare or validate directions; display at most three exact-coverage candidates | `implementation-planning` after direction confirmation |
| `implementation-planning` | Realize one selected direction as an approval-ready Stage Map and exact-coverage plan | `implementation` after separate plan approval, or `implementation-option-selection` if invalidated |
| `implementation` | Executor changes code, verifiers check independently | `final-verification` |
| `final-verification` | Read-only acceptance verification | `release-handoff` if accepted |
| `release-handoff` | User-selected commit/PR handoff | done or follow-up |

The independent analysis sidetracks do not appear in this phase sequence. `project-analysis` maps the current project, `feature-analysis` traces one existing feature, and `change-impact-analysis` maps a proposed change's impact. Each leaves the next-phase pointer `pending` with no phase, and remains read-only, including no test or build execution.

### 7.5 Report and follow-up

Final report artifacts live under `runs/<task-type>/reports/`. Human responses from the HTML view are saved as `runs/<task-type>/user-responses/user-response-<task-type>-<seq>.md` and can be carried into the next run.

---

## 8. Doc maintenance checklist

When changing code, keep these docs in sync:

- New Node command: update `bin/okstra` usage, `README.md`, this file, and `docs/cli.md`.
- New runtime source copied to users: update `tools/build.mjs`, install/uninstall manifests if applicable, and this file.
- New skill/agent: update `README.md`, this file, install/uninstall fallback lists, and `CHANGES.md`.
- New report field/section: update schema, template, report-writer worker, validator tests, this file's report model if user-visible.
- New phase/profile behavior: update `prompts/profiles/*`, `docs/architecture.md`, `docs/cli.md`, and `README.md` if user-facing.

Edit English canonical Markdown sources directly; nothing asks you to touch the Korean mirror in the same change. A maintainer session reconciles the mirrors on its own schedule with `$sync-korean-sources` or `/sync-korean-sources`, which begins by reading `node tools/korean-sources/cli.mjs status`. `.project-docs/ko-sources/**` is maintainer-local only: it is neither published nor committed.

---

## Appendix A. Report / row ID glossary

| Prefix / token | Meaning |
|---|---|
| `P-NNN` | Problem / verification target summary |
| `C-NNN` | Consensus or clarification item, depending on section context |
| `D-NNN` | Difference between workers |
| `E-NNN` | Primary evidence |
| `S-NNN` | Secondary evidence or alternate interpretation |
| `R-NNN` | Missing information / risk |
| `RR-NNN` | Residual risk |
| `IO-NNN` | Ranked or audited implementation direction in option selection |
| `P-Dir-1` | Selected direction realization item used by plan-body verification |
| `P-Opt-*` | Plan option item used by plan-body verification |
| `P-Step-*` | Plan execution step item |
| `P-Dep-*` | Plan dependency / migration item |
| `P-Val-*` | Plan validation checklist item |
| `P-Rb-*` | Plan rollback item |
| `FU-NNN` | Follow-up task |
| `worker:item` | Source item pointer preserved from worker result into final report |
| `Verdict Token` | `accepted`, `conditional-accept`, `blocked`, `not-applicable` |
| `Direction` | `continue-investigation`, `begin-option-selection`, `begin-implementation`, `approve`, `reject`, `hold` |

Clarifications now live in the unified `## 1. Clarification Items` table. Deprecated `5.1` / `5.2` split sections are no longer part of the schema.

---

*Updated: 2026-08-24 · Source of truth checked against `package.json`, `bin/okstra`, `src/cli-registry.mts`, `src/lib/skill-catalog.mts`, `tools/build.mjs`, `scripts/`, `skills/`, `agents/`, `templates/`, `schemas/`, `validators/`, and tests.*
