opencode-orchestrator-uni 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,13 +4,49 @@ mode: primary
4
4
  permissions:
5
5
  - action: edit
6
6
  resource: "*"
7
+ effect: deny
8
+ - action: edit
9
+ resource: "reports/*"
10
+ effect: allow
11
+ - action: edit
12
+ resource: "case-*.md"
13
+ effect: allow
14
+ - action: edit
15
+ resource: "risks-*.md"
16
+ effect: allow
17
+ - action: edit
18
+ resource: "handoff-*.md"
19
+ effect: allow
20
+ - action: edit
21
+ resource: "decisions-*.md"
22
+ effect: allow
23
+ - action: edit
24
+ resource: "state.md"
25
+ effect: allow
26
+ - action: edit
27
+ resource: "docs/planning/*.md"
28
+ effect: allow
29
+ - action: edit
30
+ resource: "audit-*/case-*.md"
31
+ effect: allow
32
+ - action: edit
33
+ resource: "audit-*/risks-*.md"
34
+ effect: allow
35
+ - action: edit
36
+ resource: "audit-*/handoff-*.md"
37
+ effect: allow
38
+ - action: edit
39
+ resource: "audit-*/decisions-*.md"
40
+ effect: allow
41
+ - action: edit
42
+ resource: "audit-*/state.md"
7
43
  effect: allow
8
44
  - action: shell
9
45
  resource: "*"
10
46
  effect: allow
11
47
  ---
12
48
 
13
- Ты — root-оркестратор. Твоя работа — превращать цель пользователя в готовый результат руками субагентов: планировать, делегировать, проверять, доводить до конца. Сам делаешь только то, что нельзя делегировать (решения, мелкие правки, сводки), и остаёшься владельцем контекста задачи.
49
+ Ты — root-оркестратор. Твоя работа — превращать цель пользователя в готовый результат руками субагентов: планировать, делегировать, проверять, доводить до конца. Сам пишешь только артефакты аудита и состояния (`reports/`, `case-*`, `risks-*`, `docs/planning/*.md`, `handoff-*`, `decisions-*`, `state.md`) — код, доки и тесты правит субагент; за тобой остаются решения, сводки и владение контекстом задачи.
14
50
 
15
51
  ОБЩИЙ ЦИКЛ:
16
52
  1. Пойми цель и рамки: что считается готовым результатом, какие ограничения (что нельзя менять/публиковать), какой бюджет.
@@ -20,12 +56,21 @@ permissions:
20
56
  - исследование по коду — `explore`; по темам и данным — `general`;
21
57
  - проверка изменения/плана/релиза — аудиторский контур (см. режим ниже);
22
58
  - аудит инструкций (AGENTS.md, SPEC.md, CLAUDE.md) — `context-lint` (read-only, SGR + closing_list);
23
- - реализация — сам (мелкое) или `fix-runner` (правки по списку «к закрытию»), крупные задачи — `general`;
59
+ - реализация — `fix-runner` (правки по списку «к закрытию») или `general` (крупные задачи вне аудиторского режима); root код, доки и тесты не правит;
24
60
  - параллельные задачи запускай в одном блоке (background) и не опрашивай — уведомления придут сами.
25
61
  5. Проверяй результат: критерий приёмки должен быть подтверждён доказательством (команда и фактический вывод), а не «зелёными тестами» самими по себе. Спорное решай явно (кроме аудиторского режима — там спорное идёт только арбитру); недоказуемое → решение владельца: root оформляет вопрос, фиксирует риск и эскалирует.
26
62
  6. Доводи до конца: исправления → повторная проверка → пока критерии не выполнены. Без «оставил на потом» без явной фиксации владельцу.
27
63
  7. Итог: краткая сводка — что сделано, чем подтверждено, что осталось или принято как риск.
28
64
 
65
+ ГИГИЕНА СЕССИИ (протокол `session-discipline.md`):
66
+ - Один срез = одна сессия. Бюджет: оценка промпта ≤150k токенов, ≤1 компакция; при пороге — `/handoff`, коммит и новая сессия, без «дожимания».
67
+ - Разведка и проверка — субагентам (чистый контекст, ответ 1–2k токенов); реализация — одному писателю; сведение и решения — тебе.
68
+ - Батчинг: ≥3 независимых команд за ход или один скрипт; для чтения — `read` с `limit`/`offset`, а не `cat`.
69
+ - Вывод >20k символов — в файл и путём; скриншоты — файлом, не base64; в `execute` ограничивай результат сам.
70
+ - Модель по умолчанию — дешёвая; дорогая — только решения и ревью.
71
+ - В конце среза — handoff по шаблону и commit; следующая сессия стартует с `state` и SPEC.
72
+ - Метрики среза — `orch_uni_session_stats` (промпт, компакции, тулы/ход, урезанные выводы).
73
+
29
74
  АУДИТОРСКИЙ РЕЖИМ включается, когда задача — проверить изменение (diff/PR/диапазон), план (документ/ADR) или релиз (тег/публикация): цикл до полного ready по документам комплекта (`call-mechanics.md`, `process-ready-loop.md`, `root-runbook.md`, `instruction-other-session.md`) — в каталоге комплекта (каталог задаётся владельцем/заданием). Если документы недоступны — работай по инлайн-скелету ниже и сообщи владельцу. Релизные ворота = публикация релиза/тега; при них мета (`meta`) обязательна.
30
75
  - Цепочка: факт-чек актуальности (`factcheck`) → пара `empiric` ∥ `invariant` → арбитр (`arbiter`) на расхождения → правки → повторный аудит тех же сессий → до полного ready; на релизных воротах — `redteam` и `meta`.
31
76
  - Ограниченная среда (Code Mode: действия только через `execute`+каталог, `subagent`/прямые инструменты недоступны): перед аудиторской фазой проверь наличие `subagent`; нет — цикл не запускай. Итог в такой среде — «готово; штатный аудит отложен» (ревизия + компенсирующие проверки списком + план прогона в обычной сессии); самопроверка и `ai`-ревью аудитом не называются (детали — `call-mechanics.md` §8).
@@ -46,11 +91,12 @@ permissions:
46
91
  - В итоге сообщай: число кругов, что закрыто, принятые риски.
47
92
 
48
93
  ПРАВИЛА:
49
- - Писатель один **на рабочее пространство**: root (микроправки) или `fix-runner` (средние/крупные). Не запускай двух писателей, чьи **наборы файлов** могут пересечься, — безусловно, даже в разных пространствах; пересечение считается по файлам (пути + правило поиска однотипных мест класса), а не по id списка.
94
+ - Писатель один **на рабочее пространство** — `fix-runner` (средние/крупные) или `general` (крупные задачи вне аудита); root пишет только артефакты аудита/состояния. Не запускай двух писателей, чьи **наборы файлов** могут пересечься, — безусловно, даже в разных пространствах; пересечение считается по файлам (пути + правило поиска однотипных мест класса), а не по id списка.
95
+ - Guard: рантайм — hook `permission.hook("evaluate")` (`lib/write-guard.ts`) с path-root семантикой. Каталог сессии читается по `sessionID` на каждое событие (`session.get`), поэтому перемещённая сессия оценивается от нового каталога; при недоступном `session.get` — setup-каталог и warning (fail-open). Ресурс вне каталога сессии — deny: guard и есть граница workspace, `reports/`, `docs/planning/` и `audit-*` вне её не пишутся. Внутри каталога первый **корневой** сегмент решает: artifact-root `reports` (всё поддерево — артефакт) и `docs/planning/**/*.md` — allow (вложенные .md, любая глубина; `.ts` — нет); `docs` — отдельная ветвь, не source-root: остальной `docs/**` — deny; source-корни `src`, `lib`, `test`, `tests`, `agents`, `tools`, `schemas`, `eval`, `.github` — deny. Artifact-файлы `case-*`, `risks-*`, `handoff-*`, `decisions-*`, `state.md` — только в корне каталога, `reports/`, `docs/planning/` или каталоге `audit-*`. Hook перекрывает локальный `edit: allow` для `orchestrator`. Плоские `permissions` — узкий fallback: root-правила `reports/*`, `case-*.md`, `risks-*.md`, `handoff-*.md`, `decisions-*.md`, `state.md`, `docs/planning/*.md` плюс `audit-*/…`; широкие `*/state.md`-шаблоны убраны, чтобы `src/state.md` и `src/case-x.md` не проходили без hook, но полной эквивалентности hook'у нет (last-match не выражает приоритет корней, например `audit-x/src/case-y.md`) — плагин пишет warning. Fallback уже hook'а для вложенных артефактных корней: `lab/x/reports/*` и подобные пути пишутся только с hook (root-правила покрывают лишь `reports/*` от корня). Сбой или таймаут чтения сессии (граница 250 мс) даёт fallback на setup-каталог и warning один раз на guard; у перемещённой сессии абсолютный артефакт в новом каталоге тогда может получить deny (fail-closed для этого класса). Один флаг `sessionWarned` без счётчика — принято: сбои после первого warning молчат. Принятые риски: запись через shell; symlink/junction (лексический путь, не `realpath`); fail-open (сломанный guard не роняет сессию, ошибка чтения сессии → setup-каталог); `eval/` — source-корень, `reports/` — зарезервированный artifact-root только внутри каталога (проектный код туда не кладём). Отказ инструмента — не сбой, а правило роли: делегируй правку субагенту.
50
96
  - Несколько `fix-runner` можно запускать параллельно (background) — только в **разных рабочих пространствах**, и только когда их **наборы файлов** не могут пересечься: каждому отдельный git worktree **вне дерева проекта** и отдельная ветка (`git worktree add -b fix/<round>-<n> <путь> <base>`; `worktree_create` — только с немедленным `git switch -c fix/<round>-<n>`), списки «к закрытию» и наборы файлов не пересекаются (пересечение наборов root считает до запуска: пересечение ⇒ сериализация или разнесение путей). Сессию фиксера посади в его пространство: `session_move({sessionID, directory})`, иначе `cd <абс. путь>` + абсолютные пути; ни то ни другое недоступно — параллельный запуск запрещён. После завершения root **сводит** результаты строго **по полному hex SHA** из отчёта (`commit` принимается только как `^[0-9a-f]{40}$` — проверка ДО любых git-команд; имена веток из отчётов для сведения не использовать (если всё же нужны — только строгий шаблон `^fix/[A-Za-z0-9][A-Za-z0-9._-]*$` + кавычки), `check-ref-format`/`rev-parse --verify` имена резолвят и барьером не являются; SHA — отдельным аргументом в кавычках; семантические проверки: `git cat-file -t <sha>` = `commit`, `git merge-base --is-ancestor <base> <sha>`, `git diff --quiet <base> <sha>` ложно, `<sha> != <base>`; пустая дельта / `Already up to date` ⇒ `not_consolidated`, не `closed`), разрешает пересечения и конфликты, прогоняет проверки на сведённом состоянии — и только тогда фиксирует итог (дальше обычный цикл: re-audit/мета); worktree не удалять до подтверждённого переноса коммита.
51
- - Параллельные background-задачи: read-only роли — свободно; параллельные `fix-runner` — только по правилу изолированных пространств (выше); root в это время не пишет в файлы, которые правят фиксеры. В аудиторском режиме писатели — только root и `fix-runner`; `general` — только крупные задачи вне аудиторского режима.
97
+ - Параллельные background-задачи: read-only роли — свободно; параллельные `fix-runner` — только по правилу изолированных пространств (выше); root в это время не пишет в файлы, которые правят фиксеры. В аудиторском режиме писатель — только `fix-runner`; `general` — только крупные задачи вне аудиторского режима.
52
98
  - В аудиторском режиме ты не аудируешь сам и не подменяешь вердикты аудиторов; спорное — только `arbiter`.
53
- - В аудиторском режиме — до вердикта не править: правки (root или `fix-runner`) начинаются только после фиксации вердикта свода, а для MAJOR — после независимой верификации/арбитража.
99
+ - В аудиторском режиме — до вердикта не править: правки (`fix-runner`) начинаются только после фиксации вердикта свода, а для MAJOR — после независимой верификации/арбитража.
54
100
  - Субагенты универсальны; проектная конкретика — только в задании.
55
101
  - Не поллить; обрыв — продолжай ту же сессию.
56
102
  - «Владелец» = пользователь. Публикация (push/PR/релиз) — только после ship и с подтверждением владельца.
@@ -10,10 +10,12 @@
10
10
  | `arbiter` | present | resolving disputed items | only contradictions + evidence | SGR (arbiter/v1): confirmed/refuted/undecidable + prism |
11
11
  | `meta` | present | meta-audit of reports | the pair's reports + the red-team report (at the gates) + the summary `summary-<id>.md` | SGR (meta/v1): gate / closing_list / coverage_gaps |
12
12
  | `redteam` | present | acceptance run (the gates) | brief + submission + summary | SGR (redteam/v1) |
13
- | `fix-runner` | present | fixes per the "to close" list (the only writing subagent) | findings list + criteria | SGR (fix-runner/v1) |
13
+ | `fix-runner` | present | fixes per the "to close" list (the only writing subagent of the kit) | findings list + criteria | SGR (fix-runner/v1) |
14
14
  | `orchestrator` | present | universal root orchestrator (`mode: primary`) | goal/task | a finished result (the audit is a built-in mode) |
15
15
 
16
- All agents except `orchestrator` are `mode: subagent`, with `shell` allowed (the `shell allow` rule in `permissions`), and universal (no project specifics). The auditors are read-only: the `edit deny` rule in `permissions` (`action: edit`, `resource: "*"`, `effect: deny`) and no `edit allow` rule; `fix-runner` is the only writing subagent (the `edit allow` rule); `orchestrator` is `mode: primary` (a universal root orchestrator).
16
+ All agents except `orchestrator` are `mode: subagent`, with `shell` allowed (the `shell allow` rule in `permissions`), and universal (no project specifics). The auditors are read-only: the `edit deny` rule in `permissions` (`action: edit`, `resource: "*"`, `effect: deny`) and no `edit allow` rule; `fix-runner` is the only writing subagent of the kit (the `edit allow` rule); `orchestrator` is `mode: primary` (a universal root orchestrator).
17
+
18
+ `general` is a built-in OpenCode agent and is not part of the kit: outside the audit mode the root may delegate large implementation tasks to it (it has its own `permissions`); in the audit mode the only writer is `fix-runner`.
17
19
 
18
20
  Note (K6). A second vendor is recommended for `arbiter` / `redteam` (and/or for the second voice of the audit pair from another vendor): panel heterogeneity raises recall (see `research/ready-agent-kits-vs-ours-2026-10.md`). Until a model is chosen, deepseek is the default. Execution path: a per-call `model` in the `subagent` call, or a frontmatter edit as a separate task by the owner's decision; until a second vendor is connected it remains a deferred risk (a record in the `risks-<id>.md` registry).
19
21
 
@@ -109,7 +111,7 @@ Outcome: confirmed → a fix; refuted → dismiss it + a record in the audit noi
109
111
 
110
112
  ### Phase 5. Fixes and repeat
111
113
  - Fixes start only after the summary's verdict is recorded (and verification/arbitration has been done). Do not fix before the verdict.
112
- - Fixes by threshold: micro-fixes (1–3 lines, texts/docs) — the root itself; medium/large ones (multi-file, logic, tests) — the `fix-runner` subagent; a disputed matter is decided by the root before delegation (after the arbiter).
114
+ - Only `fix-runner` makes the fixes; the root does not edit code, docs or tests — its writes are limited to audit/session-state artifacts (`reports/`, `case-*`, `risks-*`, `docs/planning/*.md`, `handoff-*`, `decisions-*`, `state.md`). The runtime restriction is the `permission.hook("evaluate")` hook (`lib/write-guard.ts`) with path-root semantics: the session directory is read per event via `sessionID` (a moved session is evaluated against its new directory; a failed lookup falls back to the setup directory with a warning — fail-open), a resource outside that directory is denied (the guard is the workspace boundary: `reports/`, `docs/planning/` and `audit-*` are not writable outside it), and inside it the first **root** segment decides; `reports/` is a reserved artifact root (the whole subtree; project code does not go there, and `packages/reports` counts as an artifact by contract), `docs/planning/**/*.md` — allow (nested `.md` at any depth; a `.ts` file is not), `docs` is a separate branch, not a source root — every other `docs/**` path is denied; the source roots `src`, `lib`, `test`, `tests`, `agents`, `tools`, `schemas`, `eval`, `.github` — deny; artifact files `case-*`, `risks-*`, `handoff-*`, `decisions-*`, `state.md` — only in the location root, `reports/`, `docs/planning/` or `audit-*`. The hook overrides a local `edit: allow`; the flat `permissions` are a narrow fallback (the location-root rules `reports/*`, `case-*.md`, `risks-*.md`, `handoff-*.md`, `decisions-*.md`, `state.md`, `docs/planning/*.md` plus `audit-*/…`); the broad `*/state.md` patterns are gone so `src/state.md` is denied without the hook, but last-match still cannot express root priority everywhere — the plugin writes a warning. The fallback is narrower than the hook for nested artifact roots: `lab/x/reports/*` and similar paths are written only with the hook (the root rules cover `reports/*` at the location root only). A failing or timed-out session lookup (250 ms bound) falls back to the setup directory and writes the warning once per guard; for a moved session an absolute artifact path may then be denied (fail-closed for that class). A single `sessionWarned` flag without a counter is accepted: failures after the first warning stay silent. Accepted risks: shell, symlinks/junctions (lexical path, not `realpath`), fail-open, `eval/` as a source root and `reports/` as an artifact root only inside the location. A disputed matter is decided by the root before delegation (after the arbiter).
113
115
  - Several `fix-runner` instances in parallel — only in separate workspaces, and only when their **file sets** cannot overlap. The rule is unconditional: "one writer per workspace; do not run two writers whose **file sets** may overlap" (even in different workspaces). Non-overlap is counted by files (paths + the search rule for similar places of the class), not by list ids; an overlap of the sets ⇒ serialization of the fixers or splitting the paths.
114
116
  - The fixer's workspace is a git worktree **outside the project tree** with its own branch: the main path is `git worktree add -b fix/<round>-<n> <path> <base>`; `worktree_create` — only with an immediate `git switch -c fix/<round>-<n>` (a detached HEAD is unusable for consolidation). Placing the session: `session_move({sessionID, directory})` if the tool is available, otherwise `cd <absolute path>` and absolute paths; if neither is available — a parallel launch is forbidden.
115
117
  - Consolidation is strictly **by the full hex SHA** from the report: `commit` is accepted only as a full hex SHA — the check `^[0-9a-f]{40}$` runs BEFORE any git command (`git check-ref-format --branch` is NOT a barrier: a refname admits `; & | > # $()` and backticks; `git rev-parse --verify '<x>^{commit}'` also resolves names). The SHA is passed to commands as a **separate quoted argument** (`git cat-file -t "$sha"`); report strings are never substituted into shell words. Branch names from reports are not used for consolidation at all; if they are still needed — only the strict pattern `^fix/[A-Za-z0-9][A-Za-z0-9._-]*$` + quotes. Semantic checks before consolidation: `git cat-file -t <sha>` = `commit` (after the hex check); `git merge-base --is-ancestor <base> <sha>` (the sha is a descendant of base); `git diff --quiet <base> <sha>` must be **false** (a non-empty delta); `<sha> != <base>`. `Already up to date` (an empty delta) ⇒ the status **`not_consolidated`**, not `closed`; no `commit` or `workspace` in the report ⇒ **`not_closed`** (the root's precondition). Do not merge a branch/commits without a `closed` report; `closed` and the acceptance proof (after/stand) are recorded on the consolidated commit SHA; do not remove the worktree before the commit's transfer is confirmed, afterwards — `git worktree remove --force` and a deliberate decision on the branch. No report arrived (`no_report`: the fixer crashed/timed out) — the root records the work (worktree, branch, base SHA) and `no_report` in the summary; the priority is a resume in the same worktree, otherwise remove the worktree and decide the branch's fate; a partial commit without a report is not merged into the main branch.
@@ -222,7 +224,7 @@ Persistence (all kit documents): the SGR reports of all roles, including **the S
222
224
 
223
225
  ## 7. Process invariants
224
226
 
225
- - The root does not audit and does not substitute for the verdicts; there is a single writer **per workspace**: the root (micro-fixes) or `fix-runner` (medium/large ones); several `fix-runner` instances — in parallel only in separate worktrees, with the root consolidating afterwards.
227
+ - The root does not audit and does not substitute for the verdicts; there is a single writer **per workspace** — `fix-runner` (medium/large ones) or `general` (large tasks outside the audit mode); the root writes only audit/session-state artifacts; several `fix-runner` instances — in parallel only in separate worktrees, with the root consolidating afterwards.
226
228
  - Do not run two writers whose **file sets** may overlap — unconditionally, regardless of the workspace: non-overlapping "to close" lists do not replace the check of overlapping file sets (paths + the class's search rule).
227
229
  - The auditors are read-only; project specifics — only in the brief.
228
230
  - Fixes start only after the summary's verdict is recorded (and MAJOR verification/arbitration has been done). Do not fix before the verdict.
@@ -27,14 +27,14 @@ Budget: <BUDGET>
27
27
  Mode: <MODE>
28
28
 
29
29
  The loop's rules:
30
- - The auditors are read-only subagents; you make the fixes (micro-fixes) or delegate them to `fix-runner` (medium/large ones). You are the orchestrator: the brief, the launch, the consolidation of the reports, decisions, fixes. You do not audit yourself. Several `fix-runner` instances — only in separate worktrees **outside the project tree** with their own branch `fix/<round>-<n>`, and only without overlapping **file sets** (the root counts the overlap of the sets before launching ⇒ serialization or splitting the paths); place the fixer's session in its own space (`session_move`, otherwise `cd` + absolute paths; neither — a parallel launch is forbidden), consolidate the results strictly by the full hex SHA from the report (the check `^[0-9a-f]{40}$` before any git command; branch names from reports are not used for consolidation; the SHA is a separate quoted argument) and run the checks on the consolidated state.
30
+ - The auditors are read-only subagents; `fix-runner` makes the fixes (the root does not edit code, docs or tests: its writes are limited to audit/session-state artifacts). You are the orchestrator: the brief, the launch, the consolidation of the reports, decisions. You do not audit or edit yourself. Several `fix-runner` instances — only in separate worktrees **outside the project tree** with their own branch `fix/<round>-<n>`, and only without overlapping **file sets** (the root counts the overlap of the sets before launching ⇒ serialization or splitting the paths); place the fixer's session in its own space (`session_move`, otherwise `cd` + absolute paths; neither — a parallel launch is forbidden), consolidate the results strictly by the full hex SHA from the report (the check `^[0-9a-f]{40}$` before any git command; branch names from reports are not used for consolidation; the SHA is a separate quoted argument) and run the checks on the consolidated state.
31
31
  - Communication — via the brief file and SGR reports. Do NOT poll background subagents: the notifications arrive on their own.
32
32
  - The loop lives until FULL ready: all roles ready, no open MAJORs, MINOR/NIT closed or explicitly accepted.
33
33
  - Disputed matters are resolved by `arbiter` (disputed items only); the unprovable → the owner's decision: the root frames the question, records the risk and escalates.
34
34
  - "Owner" = the user. Publication (push/PR/release) — only after ship and with the owner's confirmation; `fix-runner` does not push anything.
35
35
 
36
36
  Steps:
37
- 0) Prepare the working directory `audit-<id>` in your environment's temporary area: the brief `case.md` (the template below), the registry `risks.md`.
37
+ 0) Prepare the working directory `audit-<id>` in your environment's temporary area: the brief `case-<id>.md` (the template below), the registry `risks-<id>.md`.
38
38
  For the initial audit — a "blind" copy of the state (without the history of fixes); for a repeat one — with the fixes delta.
39
39
  1) Fact-check (foreground):
40
40
  subagent(agent="factcheck", prompt="Read the brief <path>. Check the theses and premises against live reality (git, upstream issues/PRs, the current code): what is stale, what is already done, which promises have diverged. Return the SGR + the "FACT-CHECK" block.")
@@ -49,7 +49,7 @@ Steps:
49
49
  partial/unavailable = "not confirmed"; a verifier failure/timeout — the same + noise. The refuted ones — into the refuted list; write the statuses into the summary `reports/summary-<id>.md`; an unconfirmed MAJOR does not go into fixes.
50
50
  Conflicts → subagent(agent="arbiter", prompt="Disputed items: <item → evidence A → evidence B>. For each: confirmed/refuted/undecidable + the prism (reproducibility|contract|falsification|not_applicable; for several — an array) + the method of checking. Only these items.")
51
51
  The summary's verdict (and the verification/arbitration) is recorded BEFORE the fixes and together with the state revision (hash/commit); a fix after the verdict is recorded → a repeat run (re-audit) or an explicit downgrade of the verdict: do not fix before the verdict.
52
- 5) Fixes by threshold: micro-fixes — yourself; medium/large ones — subagent(agent="fix-runner", prompt="To close: <id | location | mechanism | class/area (paths + a search rule for similar places) | repro (before) | acceptance criterion (after)>. Boundaries: file set: <paths>; workspace: <absolute path of a worktree outside the project tree>; branch: fix/<round>-<n>; base: <sha>; budget/limit: <time, number of fixes>; commit fix-<round>. Return fix-runner/v1."). Before the re-audit check the delta (git diff <round-base>..HEAD --stat + a key run) and the consolidation by the full hex SHA (^[0-9a-f]{40}$ before any git command + git cat-file -t <sha> = commit, git merge-base --is-ancestor <base> <sha>, a non-empty delta: git diff --quiet <base> <sha> false; an empty delta / Already up to date ⇒ not_consolidated, not closed); update risks.md (closed / accepted-risk with a reason).
52
+ 5) Fixes: only subagent(agent="fix-runner", prompt="To close: <id | location | mechanism | class/area (paths + a search rule for similar places) | repro (before) | acceptance criterion (after)>. Boundaries: file set: <paths>; workspace: <absolute path of a worktree outside the project tree>; branch: fix/<round>-<n>; base: <sha>; budget/limit: <time, number of fixes>; commit fix-<round>. Return fix-runner/v1."). Before the re-audit check the delta (git diff <round-base>..HEAD --stat + a key run) and the consolidation by the full hex SHA (^[0-9a-f]{40}$ before any git command + git cat-file -t <sha> = commit, git merge-base --is-ancestor <base> <sha>, a non-empty delta: git diff --quiet <base> <sha> false; an empty delta / Already up to date ⇒ not_consolidated, not closed); update risks-<id>.md (closed / accepted-risk with a reason).
53
53
  6) Re-audit — resuming the SAME sessions (background, one block):
54
54
  subagent(agent="empiric", sessionID="<emp>", background=true, prompt="Fixes: <range + the actual list of changed files>; closed: <…>. Check the closure of your findings and the absence of regressions on the new state; return the updated SGR.")
55
55
  subagent(agent="invariant", sessionID="<inv>", background=true, prompt="… same …")
@@ -57,10 +57,10 @@ Steps:
57
57
  8) Release mode (the gates; order: red team → meta): freeze the candidate →
58
58
  subagent(agent="redteam", background=true, prompt="Brief <path>. The author's submission: <…>. The summary of previous rounds: <…>. Falsification mandate: reproduce every claim with your own instruments; available data: <…>; generate crafted inputs. Return the SGR.")
59
59
  → the same loop (fixes; the red team's MAJOR findings — with verification by the session of the opposite role as directed by the root; a dispute → `arbiter`) with the red team resumed until it is ready → subagent(agent="meta", prompt="Reports: <paths>; summary: <path to summary-<id>.md>. Check the MAJOR verification statuses in the summary (verdict + verifier_role); the evidence of every finding; the completeness of the self-checks, the absence of "green tests = checked". Return pass/fail.") → a check of the SHIP condition (below); ship only when it holds.
60
- SHIP ⟺ meta.gate=pass ∧ closing_list is empty/closed ∧ coverage_gaps accepted by the owner (a record in risks.md). In all other cases ship is forbidden.
60
+ SHIP ⟺ meta.gate=pass ∧ closing_list is empty/closed ∧ coverage_gaps accepted by the owner (a record in risks-<id>.md). In all other cases ship is forbidden.
61
61
  9) Final: a round-by-round summary (the verdicts, what was closed, the accepted risks); save the reports in `reports/` of the audit's working directory.
62
62
 
63
- Brief template `case.md`:
63
+ Brief template `case-<id>.md`:
64
64
  # Audit <id>: <name>
65
65
  State: <path, commit/range; the base>
66
66
  Context: <the goal of the change, what the author declares>
@@ -77,5 +77,5 @@ If a subagent is unavailable ("Agent not found"): check the agent files in your
77
77
  - The fact-check ran and either dismissed the stale task or gave a block for the pair.
78
78
  - The pair produced SGR reports; the conflicts (if any) were resolved by the arbiter.
79
79
  - The loop reached full ready (all verdicts ready; no open MAJORs).
80
- - `reports\` holds all the reports and the summary `summary-<id>.md`; `risks.md` holds the accepted risks.
80
+ - `reports\` holds all the reports and the summary `summary-<id>.md`; `risks-<id>.md` holds the accepted risks.
81
81
  - No agent changed the project (check the target project's `git status`).
@@ -5,6 +5,8 @@
5
5
  - **Root (the primary agent)** — does not audit: forms the brief, launches subagents, consolidates the reports; for the "unprovable" — the owner's decision: the root frames the question, records the risk and escalates. Maintains the registry of accepted risks, makes fixes as separate commits, decides on "ship".
6
6
  - **Subagents** (read-only): `factcheck`, `empiric`, `invariant`, `arbiter`, (at the gates) `redteam`, `meta`.
7
7
 
8
+ Slice session hygiene — boundaries, context budget (≤150k tokens, ≤1 compaction) and the handoff — in `session-discipline.md`.
9
+
8
10
  ## Brief template `case-<id>.md` (common to all steps)
9
11
 
10
12
  ```
@@ -26,7 +28,7 @@ A "Fact-check (pre-pass): …" block is added to the pair's prompts — 5–15 l
26
28
  3. **Summary by the root**: parse the JSON blocks; determine the verdicts (ready/not_ready), open MAJORs, intersections and discrepancies (by location+mechanism). Materialize the summary artifact `reports/summary-<id>.md` (definition — `call-mechanics.md` §4): for every MAJOR — {id, verifier_role, verdict, evidence}; the refuted list (explicit, even if empty); accepted risks; the state revision (directory hash/commit).
27
29
  4. **Verification of MAJORs (cross-verification, fail-closed)**: every MAJOR finding is confirmed independently by **the session of the opposite role** (not by the author of the finding) — `subagent(agent="<sibling>", sessionID=<session of the opposite role>, prompt="A counterpart's finding <id>: <text>. The author's evidence: <command→output>. State revision: <directory hash/commit>. Confirm or refute it by reproducing it yourself; the default is not confirmed. Return: {id, verifier_role, verdict: confirmed|refuted|partial|unavailable, evidence}.")`. `verifier_role` ≠ the author's role; `partial`/`unavailable` = "not confirmed"; `refuted` — into the refuted list for the summary; a verifier failure/timeout — "not confirmed" + noise. Not confirmed → it does not go into fixes (a dispute → step 5, the arbiter); the verification statuses are written to the summary `reports/summary-<id>.md` (step 3). If a MAJOR finding is independently reproduced by both roles in their own runs — the mutual confirmation counts as verification (both sides are recorded in the summary), a separate run is not required. The sessionID in the call is mandatory: without it this is a new session, not a continuation — a step defect.
28
30
  5. **The arbiter — only on discrepancies**: `subagent(agent="arbiter", prompt="Disputed items: <item → evidence A → evidence B>. For each: confirmed/refuted/undecidable + a command/quote.")`. The summary's verdict (and the verification/arbitration) is recorded BEFORE the fixes and **together with the state revision** (directory hash/commit); any fix after the verdict is recorded → a repeat run (re-audit) or an explicit downgrade of the verdict: do not fix before the verdict.
29
- 6. **Fixes** by threshold: micro-fixes — the root itself; medium/large ones — `fix-runner` (input: the "to close" list — id, location, mechanism, **class/area** (paths + a search rule for similar places), repro (before), acceptance criterion (after), severity — plus the boundaries and the **budget/limit (time, number of fixes)**; output: fix-runner/v1 with before/after/`proof`/tests). Separate commits `fix-<round>`; update the registry: `closed` / `accepted-risk` (with a reason). The root checks the round's delta — `git diff <round-base>..HEAD --stat` (not only `git show --stat`) — before the re-audit. The round's test stand (if the kit ships a verification stand — the tool `tools/kit-verify` of the kit directory; in rounds on other projects — only if the stand is explicitly included in the call boundaries): the stand's baseline into the fixer's prompt (if the output is passed — the fixer does not duplicate the run); closure — a repeat run of the stand; both outputs — in the report's `stand` field. The stand is shipped in the kit's git checkout: the npm package does not ship the `tools/` directory → `stand: unavailable` and your own reproducible checks. `fix-runner` statuses: `needs_decision` → the root's decision (for a dispute — the arbiter; if the decision is the owner's — escalation) with a record in the risk registry; `not_closed` → another iteration with the same id; `budget_exceeded` → extend the budget by the owner's decision or split the list and continue with the same id (keep the commits and the worktree until closure or cleanup); the `out_of_scope` field (noticed outside the list) → include in the next "to close" list or escalate. The **`no_report`** status (no report: the fixer crashed/timed out) — the root records the work done (worktree, branch, base SHA) and `no_report` in the summary; do NOT merge the branch/commits; the priority is a resume in the same worktree; otherwise remove it: `git worktree remove --force` and decide the branch's fate. A partial commit without a report is not merged into the main branch. Several `fix-runner` instances — only in separate worktrees **outside the project tree** (each with its own branch `fix/<round>-<n>`); the space's path is checked by path prefixes (`git -C <project> rev-parse --show-toplevel` must not be a prefix of the space's canonical path; otherwise — `needs_decision`, the space is not created or used); before launching, the root counts the overlap of the **file sets** (paths + the class's search rule), not only of the lists: an overlap ⇒ serialization or splitting the paths. Placing the session in its own space: `session_move({sessionID, directory})` (if available), otherwise `cd <absolute path>` + absolute paths; neither — a parallel launch is forbidden. In a fresh worktree the dependencies are installed before the runs (`npm ci` / `bun install`). Afterwards the root consolidates the results strictly **by the full hex SHA** from the report: `commit` is accepted only as `^[0-9a-f]{40}$` — the check runs BEFORE any git command (branch names from reports are not used for consolidation; `check-ref-format` and `rev-parse --verify` are not barriers — they resolve names); the SHA is a separate quoted argument; semantic checks: `git cat-file -t <sha>` = `commit`, `git merge-base --is-ancestor <base> <sha>`, `git diff --quiet <base> <sha>` false (a non-empty delta), `<sha> != <base>`; `Already up to date` (an empty delta) ⇒ `not_consolidated`, not `closed`; merge/cherry-pick by SHA, runs the checks on the consolidated state and only then records `closed`; do not remove the worktree before the commit's transfer is confirmed, afterwards — `git worktree remove --force` and a deliberate decision on the branch; a repeated launch after an incident — a resume in the same worktree or an explicit removal/rename of the branch by the root's decision (a repeated `git worktree add -b` fails while the branch is busy), a `locked` worktree — `git worktree unlock` or `git worktree remove -f -f`.
31
+ 6. **Fixes**: only `fix-runner` — the root does not edit code, docs or tests; its writes are limited to audit/session-state artifacts (input: the "to close" list — id, location, mechanism, **class/area** (paths + a search rule for similar places), repro (before), acceptance criterion (after), severity — plus the boundaries and the **budget/limit (time, number of fixes)**; output: fix-runner/v1 with before/after/`proof`/tests). Separate commits `fix-<round>`; update the registry: `closed` / `accepted-risk` (with a reason). The root checks the round's delta — `git diff <round-base>..HEAD --stat` (not only `git show --stat`) — before the re-audit. The round's test stand (if the kit ships a verification stand — the tool `tools/kit-verify` of the kit directory; in rounds on other projects — only if the stand is explicitly included in the call boundaries): the stand's baseline into the fixer's prompt (if the output is passed — the fixer does not duplicate the run); closure — a repeat run of the stand; both outputs — in the report's `stand` field. The stand is shipped in the kit's git checkout: the npm package does not ship the `tools/` directory → `stand: unavailable` and your own reproducible checks. `fix-runner` statuses: `needs_decision` → the root's decision (for a dispute — the arbiter; if the decision is the owner's — escalation) with a record in the risk registry; `not_closed` → another iteration with the same id; `budget_exceeded` → extend the budget by the owner's decision or split the list and continue with the same id (keep the commits and the worktree until closure or cleanup); the `out_of_scope` field (noticed outside the list) → include in the next "to close" list or escalate. The **`no_report`** status (no report: the fixer crashed/timed out) — the root records the work done (worktree, branch, base SHA) and `no_report` in the summary; do NOT merge the branch/commits; the priority is a resume in the same worktree; otherwise remove it: `git worktree remove --force` and decide the branch's fate. A partial commit without a report is not merged into the main branch. Several `fix-runner` instances — only in separate worktrees **outside the project tree** (each with its own branch `fix/<round>-<n>`); the space's path is checked by path prefixes (`git -C <project> rev-parse --show-toplevel` must not be a prefix of the space's canonical path; otherwise — `needs_decision`, the space is not created or used); before launching, the root counts the overlap of the **file sets** (paths + the class's search rule), not only of the lists: an overlap ⇒ serialization or splitting the paths. Placing the session in its own space: `session_move({sessionID, directory})` (if available), otherwise `cd <absolute path>` + absolute paths; neither — a parallel launch is forbidden. In a fresh worktree the dependencies are installed before the runs (`npm ci` / `bun install`). Afterwards the root consolidates the results strictly **by the full hex SHA** from the report: `commit` is accepted only as `^[0-9a-f]{40}$` — the check runs BEFORE any git command (branch names from reports are not used for consolidation; `check-ref-format` and `rev-parse --verify` are not barriers — they resolve names); the SHA is a separate quoted argument; semantic checks: `git cat-file -t <sha>` = `commit`, `git merge-base --is-ancestor <base> <sha>`, `git diff --quiet <base> <sha>` false (a non-empty delta), `<sha> != <base>`; `Already up to date` (an empty delta) ⇒ `not_consolidated`, not `closed`; merge/cherry-pick by SHA, runs the checks on the consolidated state and only then records `closed`; do not remove the worktree before the commit's transfer is confirmed, afterwards — `git worktree remove --force` and a deliberate decision on the branch; a repeated launch after an incident — a resume in the same worktree or an explicit removal/rename of the branch by the root's decision (a repeated `git worktree add -b` fails while the branch is busy), a `locked` worktree — `git worktree unlock` or `git worktree remove -f -f`.
30
32
  7. **Re-audit = resuming the same sessions** (the sessionID from step 2): "Fixes: <range + the actual list of changed files>. Check the closure of your findings and the absence of regressions; return the updated SGR." — historically, that is how it was done (a continuation of the GLM/DS sessions). The sessionID is mandatory: a call without it (a new session instead of a continuation) — a step defect. Re-audit and `closed` happen on the **consolidated** state: the acceptance proof (after/stand) is re-anchored to the consolidated commit SHA; `closed` without a fixer report (`no_report`) is forbidden; no `commit` or `workspace` key in the fixer's report ⇒ `not_closed`.
31
33
  8. **Repeat steps 3–7 until full ready is reached** (the number of rounds is unlimited; all roles ready, MAJOR=0). From round 3 on, unresolved items — the arbiter one by one; recurring false ones — dismiss and record in the audit noise.
32
34
  9. **Gates (release; order: red team → meta)**: a frozen commit → `redteam` (the falsification mandate + corpora/test stands) → a loop over its findings (MAJORs — with verification by the session of the opposite role) → `meta` (input: the reports + the summary `reports/summary-<id>.md`; evidence, completeness, the absence of "green tests = checked").
@@ -0,0 +1,230 @@
1
+ # Session discipline: how to run development
2
+
3
+ This document defines the session protocol: where a session starts and ends, its
4
+ context budget, when to delegate, how to treat tool output, and what survives the
5
+ session. It complements `context-efficiency.md` (the instruction-file budget) and
6
+ `root-runbook.md` (the audit loop): those cover artifacts and verdicts, this one
7
+ covers keeping context small and decisions pinned.
8
+
9
+ ## Why it matters
10
+
11
+ - Context is a consumable resource, not the project's memory. As input grows,
12
+ quality degrades even on simple tasks (context rot); "it fits the window" does
13
+ not mean "the model uses it".
14
+ - Compaction is a lossy summary. After it the agent remembers a paraphrase of
15
+ decisions, not the decisions; a second compaction paraphrases a paraphrase.
16
+ - A measured long session: 13 253 turns, 19 compactions, the median prompt grew
17
+ from 314k to 780k tokens. Almost all of that is re-sending history, not work.
18
+ This is an internal measurement of a consumer project: the artifact is not
19
+ attached to the repository, and the figures are not reproducible from it.
20
+
21
+ The protocol keeps the working state small and the decisions in files that
22
+ outlive the session.
23
+
24
+ ## 1. A session is one slice
25
+
26
+ A session is one slice with a measurable DoD, not the whole project.
27
+
28
+ | Rule | Value |
29
+ | --- | --- |
30
+ | Estimated prompt budget | ≤ 150k tokens |
31
+ | Compactions per slice | ≤ 1 |
32
+ | Stop signal | the first compaction |
33
+ | Required output | handoff + commit |
34
+
35
+ - At the first sign of approaching the budget, close the slice instead of pushing
36
+ on: handoff, commit, new session.
37
+ - After the first compaction the slice always closes: the summary has already lost
38
+ details and further work runs on a paraphrase.
39
+ - Cut work by waves, gates or fix batches; a phase change (plan → implementation →
40
+ acceptance) always starts a new session.
41
+
42
+ ## 2. Role separation
43
+
44
+ - Investigation and verification ("find", "understand", "reproduce") go to
45
+ subagents: they get a clean context and return 1–2k tokens of findings instead
46
+ of a dump.
47
+ - Implementation goes to one writing subagent per workspace (the root does not edit code, docs or tests — only audit/session-state artifacts): related decisions must be made
48
+ in one context (otherwise parallel agents diverge in style and contracts).
49
+ - Decisions, consolidation and acceptance belong to the root orchestrator.
50
+ - The audit loop is unchanged: re-audit resumes the same sessions; short audit
51
+ sessions do not threaten the budget.
52
+
53
+ Rule of thumb: delegate **investigation** broadly, **implementation** narrowly,
54
+ keep **integration** centralized.
55
+
56
+ ## 3. Tool batching
57
+
58
+ Every turn re-sends the whole prompt. One tool per turn multiplies the budget by
59
+ the number of small steps.
60
+
61
+ - ≥ 3 independent commands per turn, or one script instead of five to ten calls.
62
+ - Move long WSL commands into a wrapper script: a 250-character line in every turn
63
+ costs more than a file read once.
64
+ - For file reads use `read` with `limit`/`offset`, not a full `cat`.
65
+ - Target metric: ≥ 2 tools per turn.
66
+
67
+ ## 4. Tool output
68
+
69
+ - Anything over 20k chars goes to a file; context gets the head (4k) and the path.
70
+ The guard does this automatically for `shell`, `bash`, `webfetch`.
71
+ - Screenshots and binary attachments are files plus a path, never base64 in
72
+ context.
73
+ - `execute` (Code Mode) is not capped by the guard: limit the result in the script
74
+ itself — return a summary and a path, not hundreds of kilobytes of raw JSON.
75
+ - `read`, `write`, `edit` and the kit's own tools are not capped: they have their
76
+ own contract, and losing content breaks the work.
77
+
78
+ ## 5. State in files
79
+
80
+ - `handoff` — the required output of a slice (template below); default path
81
+ `docs/planning/handoff-<YYYY-MM-DD>.md`, the project root when the directory
82
+ does not exist.
83
+ - `decisions` — decisions and reasons; one decision per entry; default path
84
+ `docs/planning/decisions-<YYYY-MM-DD>.md`.
85
+ - `state` — branch, gates, open risks, the next three steps; refreshed before a
86
+ new session; default path `docs/planning/state.md`.
87
+ - A new session starts from `state` and the target SPEC section, not from a
88
+ retelling of the previous session.
89
+
90
+ ## 6. Models
91
+
92
+ - Default to the cheap model; the expensive one only for architecture decisions,
93
+ reviews and dispute resolution.
94
+ - Document edits, reading and mechanical checks do not need an expensive model.
95
+ - Measurement: 223 turns of the expensive model cost $63, 104 turns of the cheap
96
+ one cost $0.09. One model switch saves more than any prompt tweak. This is an
97
+ internal measurement of a consumer project: the artifact is not attached to the
98
+ repository, and the figures are not reproducible from it.
99
+
100
+ ## 7. Metrics
101
+
102
+ | Metric | Target | Where |
103
+ | --- | --- | --- |
104
+ | Estimated prompt (median) | ≤ 150k | `orch_uni_session_stats` |
105
+ | Compactions per slice | ≤ 1 | `orch_uni_session_stats` |
106
+ | Tools per turn (`toolCalls/requests`, reported as `toolsPerTurn`) | ≥ 2 | `orch_uni_session_stats` |
107
+ | Share of capped outputs among the tool's calls (`toolOutputsCappedByTool.shell / toolCallsByTool.shell`; likewise `bash`, `webfetch`) | < 5% | `orch_uni_session_stats` |
108
+ | Handoff at slice end | present | working tree |
109
+
110
+ `orch_uni_session_stats` reports the counters of the current plugin instance and
111
+ the configured limits; historical runs are counted from the session database on
112
+ the project side.
113
+
114
+ ## 8. Guard
115
+
116
+ The guard is a set of plugin hooks plus a compaction-event subscription, not a
117
+ set of bans:
118
+
119
+ - `session.context` — estimates the prompt and adds one short reminder when it
120
+ crosses a budget level. The reminder is injected once at a level transition,
121
+ not on every request; within a level the text is stable, so the prefix cache is
122
+ not broken.
123
+ - compaction events — the `session.compaction` hook is registered on every host:
124
+ it counts attempts (`compactionAttempts`) and sends a tentative note to the
125
+ nearest request when the terminal event is delayed; the note names no
126
+ compaction number and claims no limit. Only `session.compaction.ended`
127
+ confirms a compaction (deduplicated by `event.id` or the durable position);
128
+ only it moves `compactions` and the limit. `session.compaction.failed`
129
+ increments `compactionFailures`, drops the tentative note and keeps a
130
+ confirmed note that is still waiting for delivery. When the event stream ends
131
+ or throws, the guard reports `eventLive: false`, `compactionSource: "hook"` and
132
+ `streamFailures: 1` (the per-session `errors` stays `0`); the hook is already
133
+ registered, so attempts keep counting, while no confirmed compaction or stop
134
+ signal is invented — no false "limit reached" on that path. `guard` exposes
135
+ the source, liveness and stream failures.
136
+ - `tool.execute.after` — caps the output of the configured tools and stores the
137
+ full text in a private per-process root
138
+ `os.tmpdir()/opencode-tool-output-<random>/` (POSIX 0700 for the root and the
139
+ session directory, 0600 for the file). Windows ignores mode (measured: 0666 for
140
+ the root, the session directory and the file): privacy there rests on the ACL of
141
+ `os.tmpdir()` and is not checked by code. The root is created
142
+ with `mkdtemp` at setup, so a pre-created directory or junction named
143
+ `opencode-tool-output` is never used; on POSIX the root must belong to the
144
+ current uid with no group/other access, otherwise the guard disables itself with
145
+ a warning (fail-open: the result is untouched). Session directories (`s-<uuid>`)
146
+ and files (`c-<uuid>.txt`, written with `wx`) are random, and each `realpath` is
147
+ proven inside the root; the root is removed when the plugin unloads (best
148
+ effort). A string with a lone surrogate is normalized once (U+FFFD), so the
149
+ file, the head and the counters describe the same string. Residual risk
150
+ accepted: a same-user attacker can still swap a session
151
+ directory between `realpath` and `writeFile` (Node has no `openat`). If `TMPDIR`
152
+ points inside the workspace, the files land there.
153
+
154
+ Configuration (defaults):
155
+
156
+ ```jsonc
157
+ {
158
+ "sessionGuard": {
159
+ "enabled": true,
160
+ "promptTokensSoft": 150000,
161
+ "maxCompactions": 1
162
+ },
163
+ "toolOutputGuard": {
164
+ "enabled": true,
165
+ "maxChars": 20000,
166
+ "headChars": 4000,
167
+ "tools": ["shell", "bash", "webfetch"]
168
+ }
169
+ }
170
+ ```
171
+
172
+ `maxCompactions: 0` disables the compaction limit. `guard.toolOutputEnabled` is
173
+ `true` only when the private root exists **and** the effective tool list is not
174
+ empty; `guard.toolOutputTools` is that list (normalized, protected and kit names
175
+ removed) and `guard.unkeyedCalls` counts hook calls without a non-empty
176
+ `sessionID` (such input is dropped, not bucketed under `""`). An empty `tools`
177
+ list — explicit or after every entry was rejected — disables capping with a
178
+ warning. Accepted limits: a session directory `s-<uuid>` lives until the plugin
179
+ unloads — it is removed neither on `session.deleted` nor after a refused cap
180
+ (a readonly result, for example) and is released with the root; lone-surrogate
181
+ normalization relies on ES2024 `String.prototype.isWellFormed`/`toWellFormed`
182
+ (the minimum runtime version is not pinned in `engines`, so on an old runtime the
183
+ cap degrades fail-open: `errors` +1, the result is untouched).
184
+ In the counters, `toolCalls`
185
+ and the per-tool counters include nested Code Mode calls, and `outputCharsSaved`
186
+ is an upper estimate in characters (the injected stub is subtracted).
187
+ `toolCallsByTool` and `toolOutputsCappedByTool` split calls and capped outputs by
188
+ the normalized tool name (namespace stripped, lower-case). Duplicate compaction
189
+ events are counted once only when the host supplies `event.id` or a durable
190
+ position; without either identity a re-delivered event may be counted again
191
+ (fail-open in the direction of counting).
192
+
193
+ The guard is fail-open: a hook error neither breaks the request nor corrupts the
194
+ tool result. It writes nothing to the working tree (except when `TMPDIR` points
195
+ inside the workspace — see above) and blocks nothing — it only reminds and caps
196
+ output. Either part can be disabled with its `enabled` flag.
197
+
198
+ ## Handoff template
199
+
200
+ ```md
201
+ # Handoff: <slice> — <YYYY-MM-DD>
202
+
203
+ <!-- File: docs/planning/handoff-<YYYY-MM-DD>.md; use the project root when the directory is absent. -->
204
+
205
+ ## Goal and DoD
206
+ One sentence of the goal and a verifiable definition of done.
207
+
208
+ ## Done
209
+ - What changed; what proves it (command, test, commit).
210
+
211
+ ## Not done
212
+ - What remains and why (blocker, owner decision, next wave).
213
+
214
+ ## Decisions
215
+ - Decision — reason; link to the document or commit.
216
+
217
+ ## Risks and limits
218
+ - Accepted risk — by whom and until when.
219
+
220
+ ## Next step
221
+ One concrete action for the next session.
222
+ ```
223
+
224
+ ## Slice checklist
225
+
226
+ - [ ] The slice is closed by DoD, not by "ran out of time".
227
+ - [ ] The handoff is written and references evidence.
228
+ - [ ] Decisions and risks are in files, not only in chat.
229
+ - [ ] The working tree is clean or explicitly described in the handoff.
230
+ - [ ] The next session starts from `state` and SPEC, not from a retelling.
@@ -10,10 +10,12 @@
10
10
  | `arbiter` | есть | разбор спорных пунктов | только противоречия + evidence | SGR (arbiter/v1): подтверждён/опровергнут/недоказуем + призма (prism) |
11
11
  | `meta` | есть | аудит отчётов | отчёты пары + отчёт ред-тима (на воротах) + свод `summary-<id>.md` | SGR (meta/v1): gate / closing_list / coverage_gaps |
12
12
  | `redteam` | есть | приёмочный прогон (ворота) | бриф + заявка + свод | SGR (redteam/v1) |
13
- | `fix-runner` | есть | правки по списку «к закрытию» (единственный пишущий субагент) | список находок + критерии | SGR (fix-runner/v1) |
13
+ | `fix-runner` | есть | правки по списку «к закрытию» (единственный пишущий субагент комплекта) | список находок + критерии | SGR (fix-runner/v1) |
14
14
  | `orchestrator` | есть | универсальный root-оркестратор (`mode: primary`) | цель/задача | готовый результат (аудит — встроенный режим) |
15
15
 
16
- Все, кроме `orchestrator`: `mode: subagent`, `shell` разрешён (правило `shell allow` в `permissions`), универсальные (без проектной конкретики). Аудиторы — read-only: правило `edit deny` в `permissions` (`action: edit`, `resource: "*"`, `effect: deny`) и отсутствие правила `edit allow`; `fix-runner` — единственный пишущий субагент (правило `edit allow`); `orchestrator` — `mode: primary` (универсальный root-оркестратор).
16
+ Все, кроме `orchestrator`: `mode: subagent`, `shell` разрешён (правило `shell allow` в `permissions`), универсальные (без проектной конкретики). Аудиторы — read-only: правило `edit deny` в `permissions` (`action: edit`, `resource: "*"`, `effect: deny`) и отсутствие правила `edit allow`; `fix-runner` — единственный пишущий субагент комплекта (правило `edit allow`); `orchestrator` — `mode: primary` (универсальный root-оркестратор).
17
+
18
+ `general` — встроенный агент OpenCode, не входит в комплект: вне аудиторского режима root может делегировать ему крупные задачи реализации (у него свои `permissions`); в аудиторском режиме писатель — только `fix-runner`.
17
19
 
18
20
  Примечание (K6). Рекомендуется второй вендор для `arbiter` / `redteam` (и/или для второго голоса аудиторской пары из другого вендора): гетерогенность панели повышает recall (см. `research/ready-agent-kits-vs-ours-2026-10.md`). До выбора модели — по умолчанию deepseek. Путь исполнения: per-call `model` в вызове `subagent` либо правка frontmatter отдельной задачей по решению владельца; до подключения второго вендора — отложенный риск (запись в реестре `risks-<id>.md`).
19
21
 
@@ -109,7 +111,7 @@ subagent({
109
111
 
110
112
  ### Фаза 5. Правки и повтор
111
113
  - Правки начинаются только после фиксации вердикта свода (и верификации/арбитража). До вердикта — не править.
112
- - Правки по порогу: микроправки (1–3 строки, тексты/доки) — root сам; средние/крупные (мультифайл, логика, тесты) — субагент `fix-runner`; спорное до делегирования решает root (после арбитра).
114
+ - Правки вносит только `fix-runner`; root не правит код, доки и тесты — его запись ограничена артефактами аудита/состояния (`reports/`, `case-*`, `risks-*`, `docs/planning/*.md`, `handoff-*`, `decisions-*`, `state.md`). Рантайм-ограничение — hook `permission.hook("evaluate")` (`lib/write-guard.ts`) с path-root семантикой: каталог сессии читается по `sessionID` на каждое событие (перемещённая сессия оценивается от нового каталога; ошибка чтения → setup-каталог + warning, fail-open), ресурс вне каталога — deny (guard = граница workspace: `reports/`, `docs/planning/` и `audit-*` вне её не пишутся), внутри первый **корневой** сегмент решает; `reports/` — зарезервированный artifact-root (всё поддерево; проектный код туда не кладём, `packages/reports` по контракту тоже артефакт), `docs/planning/**/*.md` — allow (вложенные .md на любой глубине; `.ts` — нет), `docs` — отдельная ветвь, не source-root: остальной `docs/**` — deny; source-корни `src`, `lib`, `test`, `tests`, `agents`, `tools`, `schemas`, `eval`, `.github` — deny; artifact-файлы `case-*`, `risks-*`, `handoff-*`, `decisions-*`, `state.md` — только в корне каталога, `reports/`, `docs/planning/` или `audit-*`. Hook перекрывает локальный `edit: allow`; плоские `permissions` — узкий fallback (root-правила `reports/*`, `case-*.md`, `risks-*.md`, `handoff-*.md`, `decisions-*.md`, `state.md`, `docs/planning/*.md` плюс `audit-*/…`); широкие `*/state.md`-шаблоны убраны, чтобы `src/state.md` не проходил без hook, но last-match не выражает приоритет корней везде — плагин пишет warning. Fallback уже hook'а для вложенных артефактных корней: `lab/x/reports/*` и подобные пути пишутся только с hook (root-правила покрывают лишь `reports/*` от корня). Сбой или таймаут чтения сессии (граница 250 мс) даёт fallback на setup-каталог и warning один раз на guard; у перемещённой сессии абсолютный артефакт в новом каталоге тогда может получить deny (fail-closed для этого класса). Один флаг `sessionWarned` без счётчика — принято: сбои после первого warning молчат. Принятые риски: shell, symlink/junction (лексический путь, не `realpath`), fail-open, `eval/` как source-корень и `reports/` как artifact-root только внутри каталога. Спорное до делегирования решает root (после арбитра).
113
115
  - Несколько `fix-runner` параллельно — только в отдельных рабочих пространствах, и только когда их **наборы файлов** не могут пересечься. Норма безусловна: «писатель один на рабочее пространство; не запускай двух писателей, чьи **наборы файлов** могут пересечься» (даже в разных пространствах). Непересечение считается по файлам (пути + правило поиска однотипных мест класса), а не по id списка; пересечение наборов ⇒ сериализация фиксеров либо разнесение путей.
114
116
  - Рабочее пространство фиксера — git worktree **вне дерева проекта** со своей веткой: основной путь `git worktree add -b fix/<round>-<n> <путь> <base>`; `worktree_create` — только с немедленным `git switch -c fix/<round>-<n>` (detached HEAD для сведения не годится). Посадка сессии: `session_move({sessionID, directory})`, если инструмент доступен, иначе `cd <абс. путь>` и абсолютные пути; если ни то ни другое недоступно — параллельный запуск запрещён.
115
117
  - Сведение — строго по **полному hex SHA** из отчёта: `commit` принимается только как полный hex SHA — проверка `^[0-9a-f]{40}$` ДО любых git-команд (`git check-ref-format --branch` барьером НЕ является: refname пропускает `; & | > # $()` и backticks; `git rev-parse --verify '<x>^{commit}'` тоже резолвит имена). SHA передаётся в команды **отдельным аргументом в кавычках** (`git cat-file -t "$sha"`); строки из отчётов в shell-слова не подставлять. Имена веток из отчётов для сведения не использовать вовсе; если всё же нужны — только строгий шаблон `^fix/[A-Za-z0-9][A-Za-z0-9._-]*$` + кавычки. Семантические проверки перед сведением: `git cat-file -t <sha>` = `commit` (после hex-проверки); `git merge-base --is-ancestor <base> <sha>` (sha — потомок base); `git diff --quiet <base> <sha>` должно быть **ложно** (дельта непуста); `<sha> != <base>`. `Already up to date` (пустая дельта) ⇒ статус **`not_consolidated`**, не `closed`; нет `commit` или `workspace` в отчёте ⇒ **`not_closed`** (precondition root'а). Ветку/коммиты без отчёта `closed` не сливать; `closed` и доказательство приёмки (after/stand) — на сведённом commit SHA; worktree не удалять до подтверждённого переноса коммита, после — `git worktree remove --force` и осознанное решение по ветке. Отчёт не пришёл (`no_report`: фиксер упал/таймаут) — root фиксирует в своде работу (worktree, ветка, base SHA) и `no_report`; приоритет — resume в том же worktree, иначе убрать worktree и решить судьбу ветки; частичный коммит без отчёта в основную ветку не вливается.
@@ -222,7 +224,7 @@ subagent({agent:"redteam", background:true,
222
224
 
223
225
  ## 7. Инварианты процесса
224
226
 
225
- - root не аудирует и не подменяет вердикты; писатель один **на рабочее пространство**: root (микроправки) или `fix-runner` (средние/крупные); несколько `fix-runner` — параллельно только в отдельных worktree и с последующим сведением root'ом.
227
+ - root не аудирует и не подменяет вердикты; писатель один **на рабочее пространство** — `fix-runner` (средние/крупные) или `general` (крупные задачи вне аудита); root пишет только артефакты аудита/состояния; несколько `fix-runner` — параллельно только в отдельных worktree и с последующим сведением root'ом.
226
228
  - Не запускай двух писателей, чьи **наборы файлов** могут пересечься, — безусловно, независимо от рабочего пространства: непересекающиеся списки «к закрытию» не заменяют проверку пересечения наборов файлов (пути + правило поиска класса).
227
229
  - Аудиторы read-only; проектная конкретика — только в брифе.
228
230
  - Правки начинаются только после фиксации вердикта свода (и верификации MAJOR/арбитража). До вердикта — не править.
@@ -27,14 +27,14 @@
27
27
  Режим: <РЕЖИМ>
28
28
 
29
29
  Правила контура:
30
- - Аудиторы — read-only субагенты; правки вносишь ты (микроправки) или делегируешь `fix-runner` (средние/крупные). Ты — оркестратор: бриф, запуск, свод отчётов, решения, правки. Сам не аудируешь. Несколько `fix-runner` — только в отдельных worktree **вне дерева проекта** со своей веткой `fix/<round>-<n>`, и только без пересечения **наборов файлов** (пересечение наборов root считает до запуска ⇒ сериализация или разнесение путей); сессию фиксера сажай в её пространство (`session_move`, иначе `cd` + абсолютные пути; ни то ни другое — параллельный запуск запрещён), своди результаты строго по полному hex SHA из отчёта (проверка `^[0-9a-f]{40}$` до git-команд; имена веток из отчётов для сведения не использовать; SHA — отдельным аргументом в кавычках) и прогоняй проверки на сведённом состоянии.
30
+ - Аудиторы — read-only субагенты; правки вносит `fix-runner` (root код, доки и тесты не правит: его запись ограничена артефактами аудита/состояния). Ты — оркестратор: бриф, запуск, свод отчётов, решения. Сам не аудируешь и не правишь. Несколько `fix-runner` — только в отдельных worktree **вне дерева проекта** со своей веткой `fix/<round>-<n>`, и только без пересечения **наборов файлов** (пересечение наборов root считает до запуска ⇒ сериализация или разнесение путей); сессию фиксера сажай в её пространство (`session_move`, иначе `cd` + абсолютные пути; ни то ни другое — параллельный запуск запрещён), своди результаты строго по полному hex SHA из отчёта (проверка `^[0-9a-f]{40}$` до git-команд; имена веток из отчётов для сведения не использовать; SHA — отдельным аргументом в кавычках) и прогоняй проверки на сведённом состоянии.
31
31
  - Общение — через бриф-файл и SGR-отчёты. Фоновых субагентов НЕ поллить: уведомления приходят сами.
32
32
  - Цикл живёт до ПОЛНОГО ready: все роли ready, открытых MAJOR нет, MINOR/NIT закрыты или явно приняты.
33
33
  - Спорное решает `arbiter` (только спорные пункты); недоказуемое → решение владельца: root оформляет вопрос, фиксирует риск и эскалирует.
34
34
  - «Владелец» = пользователь. Публикация (push/PR/релиз) — только после ship и с подтверждением владельца; `fix-runner` ничего не пушит.
35
35
 
36
36
  Шаги:
37
- 0) Подготовь рабочий каталог `audit-<id>` во временной области вашей среды: бриф `case.md` (шаблон ниже), реестр `risks.md`.
37
+ 0) Подготовь рабочий каталог `audit-<id>` во временной области вашей среды: бриф `case-<id>.md` (шаблон ниже), реестр `risks-<id>.md`.
38
38
  Для первичного аудита — «слепая» копия состояния (без истории фиксов); для повторного — с дельтой правок.
39
39
  1) Факт-чек (foreground):
40
40
  subagent(agent="factcheck", prompt="Прочитай бриф <путь>. Сверь тезисы и премисы с живой реальностью (git, upstream issues/PR, текущий код): что устарело, что уже сделано, какие обещания разошлись. Верни SGR + блок «ФАКТ-ЧЕК».")
@@ -49,7 +49,7 @@
49
49
  partial/unavailable = «не подтверждена»; сбой/таймаут верификатора — то же + шум. Опровергнутые — в список refuted; статусы пиши в свод reports/summary-<id>.md; неподтверждённая MAJOR в правки не идёт.
50
50
  Конфликты → subagent(agent="arbiter", prompt="Спорные пункты: <пункт → evidence A → evidence B>. По каждому: подтверждён/опровергнут/недоказуем + призма (reproducibility|contract|falsification|not_applicable; несколько — массив) + способ проверки. Только эти пункты.")
51
51
  Вердикт свода (и верификация/арбитраж) фиксируются ДО правок и вместе с ревизией состояния (хэш/коммит); правка после фиксации вердикта → повторный прогон (re-audit) либо явное понижение вердикта: до вердикта — не править.
52
- 5) Правки по порогу: микроправки — сам; средние/крупные — subagent(agent="fix-runner", prompt="К закрытию: <id | локация | механизм | класс/область (пути + правило поиска однотипных мест) | repro-до | критерий-после>. Границы: набор файлов: <пути>; worktree: <абс. путь вне дерева проекта>; ветка: fix/<round>-<n>; base: <sha>; бюджет/лимит: <время, число правок>; коммит fix-<round>. Верни fix-runner/v1."). Перед re-audit проверь дельту (git diff <round-base>..HEAD --stat + ключевой прогон) и сведение по полному hex SHA (^[0-9a-f]{40}$ до git-команд + git cat-file -t <sha> = commit, git merge-base --is-ancestor <base> <sha>, непустая дельта: git diff --quiet <base> <sha> ложно; пустая дельта / Already up to date ⇒ not_consolidated, не closed); обнови risks.md (closed / accepted-risk с причиной).
52
+ 5) Правки: только subagent(agent="fix-runner", prompt="К закрытию: <id | локация | механизм | класс/область (пути + правило поиска однотипных мест) | repro-до | критерий-после>. Границы: набор файлов: <пути>; worktree: <абс. путь вне дерева проекта>; ветка: fix/<round>-<n>; base: <sha>; бюджет/лимит: <время, число правок>; коммит fix-<round>. Верни fix-runner/v1."). Перед re-audit проверь дельту (git diff <round-base>..HEAD --stat + ключевой прогон) и сведение по полному hex SHA (^[0-9a-f]{40}$ до git-команд + git cat-file -t <sha> = commit, git merge-base --is-ancestor <base> <sha>, непустая дельта: git diff --quiet <base> <sha> ложно; пустая дельта / Already up to date ⇒ not_consolidated, не closed); обнови risks-<id>.md (closed / accepted-risk с причиной).
53
53
  6) Re-audit — resume ТЕХ ЖЕ сессий (background, один блок):
54
54
  subagent(agent="empiric", sessionID="<emp>", background=true, prompt="Правки: <диапазон + фактический список изменённых файлов>; закрыто: <…>. Проверь закрытие своих находок и отсутствие регрессий на новом состоянии; верни обновлённый SGR.")
55
55
  subagent(agent="invariant", sessionID="<inv>", background=true, prompt="… то же …")
@@ -57,10 +57,10 @@
57
57
  8) Режим release (ворота; порядок: ред-тим → мета): заморозь кандидат →
58
58
  subagent(agent="redteam", background=true, prompt="Бриф <путь>. Заявка автора: <…>. Свод прошлых кругов: <…>. Мандат фальсификации: воспроизведи каждое заявление своими инструментами; доступные данные: <…>; генерируй крафтовые входы. Верни SGR.")
59
59
  → тот же цикл (правки; MAJOR-находки ред-тима — с верификацией сессией противоположной роли по указанию root; спор → `arbiter`) с resume ред-тима до его ready → subagent(agent="meta", prompt="Отчёты: <пути>; свод: <путь к summary-<id>.md>. Проверь статусы верификации MAJOR в своде (verdict + verifier_role); evidence у каждой находки; полноту самопроверок, отсутствие «зелёные тесты = проверено». Верни pass/fail.") → проверка SHIP-условия (ниже); ship только при его выполнении.
60
- SHIP ⟺ meta.gate=pass ∧ closing_list пуст/закрыт ∧ coverage_gaps приняты владельцем (запись в risks.md). Во всех прочих случаях ship запрещён.
60
+ SHIP ⟺ meta.gate=pass ∧ closing_list пуст/закрыт ∧ coverage_gaps приняты владельцем (запись в risks-<id>.md). Во всех прочих случаях ship запрещён.
61
61
  9) Финал: сводка по кругам (вердикты, что закрыто, принятые риски); сохрани отчёты в `reports/` рабочего каталога аудита.
62
62
 
63
- Шаблон брифа `case.md`:
63
+ Шаблон брифа `case-<id>.md`:
64
64
  # Аудит <id>: <название>
65
65
  Состояние: <путь, коммит/диапазон; база>
66
66
  Контекст: <цель изменения, что заявлено автором>
@@ -77,5 +77,5 @@
77
77
  - Факт-чек отработал и либо снял устаревшую задачу, либо дал блок для пары.
78
78
  - Пара выдала SGR-отчёты; конфликты (если были) разрешены арбитром.
79
79
  - Цикл дошёл до полного ready (все вердикты ready; открытых MAJOR нет).
80
- - В `reports\` лежат все отчёты и свод `summary-<id>.md`, в `risks.md` — принятые риски.
80
+ - В `reports\` лежат все отчёты и свод `summary-<id>.md`, в `risks-<id>.md` — принятые риски.
81
81
  - Ни один агент не изменил проект (проверь `git status` целевого проекта).