opencode-orchestrator-uni 0.5.0 → 0.5.2
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.
- package/README.md +244 -508
- package/README.ru.md +247 -519
- package/agents/en/orchestrator.md +42 -5
- package/agents/ru/orchestrator.md +42 -5
- package/docs/en/call-mechanics.md +6 -4
- package/docs/en/instruction-other-session.md +6 -6
- package/docs/en/reference.md +482 -0
- package/docs/en/root-runbook.md +1 -1
- package/docs/en/session-discipline.md +1 -1
- package/docs/ru/call-mechanics.md +6 -4
- package/docs/ru/instruction-other-session.md +6 -6
- package/docs/ru/reference.md +482 -0
- package/docs/ru/root-runbook.md +1 -1
- package/docs/ru/session-discipline.md +1 -1
- package/index.ts +17 -1
- package/lib/write-guard.ts +283 -0
- package/package.json +2 -2
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
Режим: <РЕЖИМ>
|
|
28
28
|
|
|
29
29
|
Правила контура:
|
|
30
|
-
- Аудиторы — read-only субагенты; правки
|
|
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
|
|
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)
|
|
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
|
|
60
|
+
SHIP ⟺ meta.gate=pass ∧ closing_list пуст/закрыт ∧ coverage_gaps приняты владельцем (запись в risks-<id>.md). Во всех прочих случаях ship запрещён.
|
|
61
61
|
9) Финал: сводка по кругам (вердикты, что закрыто, принятые риски); сохрани отчёты в `reports/` рабочего каталога аудита.
|
|
62
62
|
|
|
63
|
-
Шаблон брифа `case
|
|
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
|
|
80
|
+
- В `reports\` лежат все отчёты и свод `summary-<id>.md`, в `risks-<id>.md` — принятые риски.
|
|
81
81
|
- Ни один агент не изменил проект (проверь `git status` целевого проекта).
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
# Справочник
|
|
2
|
+
|
|
3
|
+
Подробный справочник комплекта оркестратора: защита записи, пять инструментов,
|
|
4
|
+
протокол сессии, настройка моделей и локали, правила приоритета.
|
|
5
|
+
|
|
6
|
+
[Назад в README](../../README.ru.md)
|
|
7
|
+
|
|
8
|
+
Комплект регистрируется при setup через `ctx.agent.transform` /
|
|
9
|
+
`ctx.reference.transform`. Reference `audit-kit` указывает на per-locale каталог
|
|
10
|
+
`docs/<locale>/` пакета; OpenCode перечисляет его как дополнительный локальный
|
|
11
|
+
каталог, поэтому агенты читают `<reference path>/call-mechanics.md` напрямую.
|
|
12
|
+
Поставляются `call-mechanics.md`, `process-ready-loop.md`, `root-runbook.md`,
|
|
13
|
+
`instruction-other-session.md`, `context-efficiency.md` и
|
|
14
|
+
`session-discipline.md` — например,
|
|
15
|
+
`<reference path>/process-ready-loop.md`.
|
|
16
|
+
|
|
17
|
+
## Защита записи
|
|
18
|
+
|
|
19
|
+
У самого оркестратора `edit` запрещён на `*`. Плоские `permissions` — лишь
|
|
20
|
+
узкий fallback-allowlist для артефактов аудита/состояния, а реальный guard —
|
|
21
|
+
hook `permission.hook("evaluate")` в `lib/write-guard.ts` с path-root
|
|
22
|
+
семантикой.
|
|
23
|
+
|
|
24
|
+
Аудиторы read-only на уровне инструментов для `edit`/`write`/`patch`
|
|
25
|
+
(`edit: deny`); промпт запрещает остальную запись. Запись через shell (redirect,
|
|
26
|
+
`sed -i`, `tee`) — принятый риск (I-M15), а не песочница.
|
|
27
|
+
|
|
28
|
+
- Каталог сессии читается на каждое событие (`session.get`), поэтому
|
|
29
|
+
перемещённая сессия оценивается от нового каталога. При ошибке чтения
|
|
30
|
+
используется setup-каталог и пишется warning — fail-open.
|
|
31
|
+
- Ресурс вне каталога сессии — deny: guard и есть граница workspace, поэтому
|
|
32
|
+
`reports/`, `docs/planning/` и `audit-*` вне её не пишутся. Внутри каталога
|
|
33
|
+
первый **корневой** сегмент решает.
|
|
34
|
+
- `reports/` — зарезервированный artifact-root: всё поддерево доступно для
|
|
35
|
+
записи (даже `reports/src/x.ts`), поэтому проектный код туда не кладём (путь
|
|
36
|
+
`packages/reports/**` по контракту тоже считается артефактом).
|
|
37
|
+
`docs/planning/**/*.md` разрешён (вложенный Markdown на любой глубине; `.ts` —
|
|
38
|
+
нет). `docs` — отдельная ветвь, не source-root: остальной `docs/**` запрещён.
|
|
39
|
+
- Source-корни (`src`, `lib`, `test`, `tests`, `agents`, `tools`, `schemas`,
|
|
40
|
+
`eval`, `.github`) запрещены даже под артефактным префиксом
|
|
41
|
+
(`src/reports/x.ts`, `src/docs/planning/x.md`).
|
|
42
|
+
- Корневые artifact-файлы (`case-*`, `risks-*`, `handoff-*`, `decisions-*`,
|
|
43
|
+
`state.md`) разрешены только в корне location, под `reports/`,
|
|
44
|
+
`docs/planning/` или в каталоге `audit-*` — `app/state.md` запрещён.
|
|
45
|
+
- Hook выполняется после плоских правил, поэтому локальный `edit: allow` для
|
|
46
|
+
`orchestrator` тоже закрыт. Плоский fallback несёт только этот артефактный
|
|
47
|
+
набор root-правилами (`reports/*`, `case-*.md`, `risks-*.md`, `handoff-*.md`,
|
|
48
|
+
`decisions-*.md`, `state.md`, `docs/planning/*.md`) плюс `audit-*/…`; широкие
|
|
49
|
+
`*/state.md`-шаблоны убраны, поэтому `src/state.md` запрещён и без hook, но
|
|
50
|
+
last-match всё равно не выражает приоритет корней везде
|
|
51
|
+
(`audit-x/src/case-y.md`) — плагин пишет warning.
|
|
52
|
+
- Fallback уже hook'а для вложенных артефактных корней: `lab/x/reports/*` и
|
|
53
|
+
подобные пути пишутся только с hook (root-правила покрывают лишь `reports/*`
|
|
54
|
+
от корня location).
|
|
55
|
+
- Сбой или таймаут чтения сессии (граница 250 мс) даёт fallback на
|
|
56
|
+
setup-каталог и warning один раз на guard; у перемещённой сессии абсолютный
|
|
57
|
+
артефакт в новом каталоге тогда может получить deny (fail-closed для этого
|
|
58
|
+
класса). Один флаг `sessionWarned` без счётчика — принято: сбои после первого
|
|
59
|
+
warning молчат.
|
|
60
|
+
- `shell` остаётся для git и проверок. Запись через shell и symlink/junction
|
|
61
|
+
(хост матчит нормализованный лексический путь, а не `realpath`) — принятые
|
|
62
|
+
риски, как и fail-open (сломанный guard не роняет сессию); ими управляет
|
|
63
|
+
промпт, а не песочница. Отказ инструмента — это правило роли, правка уходит
|
|
64
|
+
субагенту.
|
|
65
|
+
|
|
66
|
+
## Инструменты
|
|
67
|
+
|
|
68
|
+
Плагин регистрирует пять детерминированных **read-only** инструментов в
|
|
69
|
+
пространстве имён `orch_uni`.
|
|
70
|
+
|
|
71
|
+
| Имя | Read-only | Что делает |
|
|
72
|
+
| -------------------------- | --------- | ----------------------------------------------------------------------------- |
|
|
73
|
+
| `orch_uni_validate_report` | да | Достаёт SGR JSON-блок отчёта, разрешает его схему, валидирует |
|
|
74
|
+
| `orch_uni_context_lint` | да | Детерминированный аудит AGENTS.md/SPEC.md: бюджет, vague, ссылки, секции |
|
|
75
|
+
| `orch_uni_snapshot` | да | Детерминированная ревизия дерева каталогов (sha256 на файл + агрегатный хэш) |
|
|
76
|
+
| `orch_uni_sessions` | да | Дочерние сессии сессии: агент, модель, стоимость, токены, время |
|
|
77
|
+
| `orch_uni_session_stats` | да | Счётчики протокола сессии: промпт, компакции, тулы/ход, урезанные выводы |
|
|
78
|
+
|
|
79
|
+
Каждый инструмент отвечает JSON-текстом. Ошибки — **типизированные коды** внутри
|
|
80
|
+
тела (`E_INPUT`, `E_NO_JSON`, …), а не исключения, чтобы агент мог ветвиться по
|
|
81
|
+
коду, не разбирая прозу. Ни один из пяти ничего не пишет в воркспейс, хранилище
|
|
82
|
+
сессий или конфигурацию.
|
|
83
|
+
|
|
84
|
+
### `orch_uni_validate_report`
|
|
85
|
+
|
|
86
|
+
```jsonc
|
|
87
|
+
{ "text": "<текст отчёта>" } // или
|
|
88
|
+
{ "path": "reports/empiric-r1.md", // абсолютный или относительно каталога локации
|
|
89
|
+
"schema": "empiric/v1" } // опционально: ожидаемый id схемы
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Укажите ровно одно из `text` / `path`. Инструмент берёт **последний** fenced-блок ```` ```json ````,
|
|
93
|
+
который парсится и несёт поле `schema`, разрешает схему по этому полю и валидирует документ.
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
{ "valid": true, "schema": "empiric/v1", "errors": [], "warnings": [ … ] }
|
|
97
|
+
{ "valid": false, "code": "E_INVALID", "schema": "empiric/v1",
|
|
98
|
+
"errors": [ { "path": "$.findings[0].severity", "message": "expected one of …" } ],
|
|
99
|
+
"warnings": [] }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Код | Значение |
|
|
103
|
+
| ------------------- | -------------------------------------------------------------------- |
|
|
104
|
+
| `E_INPUT` | Плохой вызов: нет/оба `text`/`path`, неверные типы, нечитаемый файл |
|
|
105
|
+
| `E_NO_JSON` | Fenced-блок ```` ```json ```` не найден |
|
|
106
|
+
| `E_PARSE` | Блок есть, но ни один не парсится как JSON |
|
|
107
|
+
| `E_UNKNOWN_SCHEMA` | Отчёт называет id, которого нет в комплекте (или не называет вовсе) |
|
|
108
|
+
| `E_SCHEMA_MISMATCH` | Отчёт называет схему, отличную от ожидаемой |
|
|
109
|
+
| `E_INVALID` | Документ не удовлетворяет своей схеме (см. `errors`) |
|
|
110
|
+
| `E_SCHEMA_IO` | Схемы комплекта недоступны или битые (см. `directory`, `file`) |
|
|
111
|
+
| `E_ABORTED` | Сессия остановлена во время чтения отчёта |
|
|
112
|
+
|
|
113
|
+
`warnings` несут правила промпта, которые *структурно валидный* отчёт всё ещё
|
|
114
|
+
может нарушить — то, что JSON Schema выразить не может: отсутствующий `rc` у
|
|
115
|
+
claim'а `empiric` с `method` = `run|measure|compare_base`, элементы `self_check`
|
|
116
|
+
вне 1–19, инвариант `invariant`, помеченный `violated` без контрпримера,
|
|
117
|
+
непокрытая обязательная строка `lines_checked`, расхождение `verdict` и `gate` у
|
|
118
|
+
`meta`, инварианты `closing_list`/`verdict` у `redteam`.
|
|
119
|
+
|
|
120
|
+
Схемы лежат в `schemas/*.json` и выведены **строго** из SGR-секций
|
|
121
|
+
`agents/*.md`, которые остаются источником истины: тестовый набор парсит
|
|
122
|
+
SGR-шаблон каждого агента и падает, когда поле промпта или значение enum не
|
|
123
|
+
принимается его схемой. `context-lint-failure/v1` — типизированный конверт ошибки
|
|
124
|
+
`orch_uni_context_lint`: у него нет шаблона агента, и отчётом он не является. Схемы
|
|
125
|
+
разрешают `additionalProperties` — агенты могут
|
|
126
|
+
добавлять свои поля (`verdict_label`, `check`, …).
|
|
127
|
+
|
|
128
|
+
### `orch_uni_context_lint`
|
|
129
|
+
|
|
130
|
+
```jsonc
|
|
131
|
+
{ "path": "AGENTS.md" } // файл или каталог, относительно локации
|
|
132
|
+
{ "path": ".", "max_lines": 150, "max_tokens": 2000 }
|
|
133
|
+
{ "path": ".", "max_words": 2000 } // бюджет слов SPEC; по умолчанию 1500 (цель), потолок 2000
|
|
134
|
+
{ "path": ".", "max_file_bytes": 500000, "max_files": 100, "max_findings": 50, "max_total_findings": 500 }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Детерминированный read-only аудит `AGENTS.md`, `SPEC.md` и `CLAUDE.md`. Возвращает
|
|
138
|
+
`{ schema: "context-lint/v1", ok: true, verdict, closing_list, files, summary, truncated? }`;
|
|
139
|
+
каждая находка несёт `id`, `severity`, `kind`, `evidence`, `detail` и `fix`. Проверки:
|
|
140
|
+
бюджет строк/токенов/слов, различные vague-фразы, дубли строк, битые относительные ссылки,
|
|
141
|
+
плейсхолдеры, даты/версии в первых трёх строках, обязательные секции и колонка Test
|
|
142
|
+
у AC. Ссылки и явные цели ограничены **реальным** путём: symlink/junction, ведущий за
|
|
143
|
+
пределы корня, пропускается, а такая цель отклоняется (`E_OUT_OF_ROOT`). Код-фенсы,
|
|
144
|
+
inline-код и HTML-комментарии маскируются: пример не считается правилом — структурные
|
|
145
|
+
проверки (команды, границы, проверка) читают полную маску, а требования, AC-id и
|
|
146
|
+
non-goals — маску фенсов. Цели ссылок передаются из построчного сканера по одной
|
|
147
|
+
(массива кандидатов нет), поэтому память не растёт с числом ссылок; проверки ограничены
|
|
148
|
+
на файл 50 000 кандидатами, сигнал проверяется на каждой строке и каждые 100 кандидатов,
|
|
149
|
+
а кэши ссылок держат не более 5 000 записей, поэтому abort срабатывает; 2001 ссылка
|
|
150
|
+
проверяется обычным образом, 50 000 валидных ссылок дают pass, а 50 001-й кандидат делает
|
|
151
|
+
список неполным (`truncated: true` плюс MAJOR-маркер `link candidates truncated` в
|
|
152
|
+
`closing_list`), он не может дать pass.
|
|
153
|
+
Отдельного потолка на
|
|
154
|
+
битые ссылки нет: вывод ограничивают общие лимиты находок, а переполнение помечается.
|
|
155
|
+
Строка сканируется любой длины: сканер ссылок и регулярное выражение плейсхолдеров линейны,
|
|
156
|
+
поэтому 4100-символьная строка проверяется как любая другая, а битая ссылка или
|
|
157
|
+
плейсхолдер на ней — настоящая находка, а не маркер. Внутреннего потолка дублей нет. Цели
|
|
158
|
+
UNC/сетевые пути никогда не пробуются. Замер (bun 1.4.2, Windows, свежий процесс):
|
|
159
|
+
50 001 битая ссылка с `max_findings: 2` → ~1,4 с и ~67 МБ пикового RSS; 50 тыс.
|
|
160
|
+
обычных строк → ~0,14 с / ~65 МБ; одна строка 2 МБ со ссылками → ~3,1 с / ~116 МБ
|
|
161
|
+
(~41 МБ без ссылок). Маскирование переиспользует строку как есть, если в ней нет
|
|
162
|
+
backtick и `<pre>`/`<code>`/`<!--`, поэтому не копирует строку посимвольно, а строка
|
|
163
|
+
в 2 МБ остаётся ограниченной 50 000 кандидатами, а не материализует все ссылки.
|
|
164
|
+
Лимиты: ≤ 2 МБ на файл, ≤ 500 файлов за прогон. `max_findings` (на файл) — это N на
|
|
165
|
+
severity: сохраняются до N MAJOR и N MINOR/INFO, излишек заменяется одним маркером
|
|
166
|
+
(MAJOR, если выпал MAJOR, иначе INFO); MAJOR-маркер остаётся в `closing_list`.
|
|
167
|
+
`max_total_findings` (глобально, по умолчанию 1000)
|
|
168
|
+
сохраняет сначала MAJOR; при переполнении MAJOR остаются `max_total` находок плюс один
|
|
169
|
+
MAJOR-маркер `truncated`. Усечённый список помечается `truncated: true`, а не скрывается.
|
|
170
|
+
Маркеры дополнительны к `max_findings` и не расходуют его бюджет: маркер усечённых
|
|
171
|
+
кандидатов-ссылок и маркер усечённых находок дают по маркеру в одном прогоне, поэтому
|
|
172
|
+
`max_findings: 2` на 20 001 битой ссылке даёт 2 сохранённые находки плюс 2 маркера. Маркеры
|
|
173
|
+
указывают на `file:1` в `evidence`.
|
|
174
|
+
Non-goals считаются как элементы верхнего уровня: перенесённый абзац — один элемент,
|
|
175
|
+
inline `;`/`,`/`and`/`и` делят только верхний уровень (не внутри `(…)`, `[…]`, бэктиков
|
|
176
|
+
любой длины или `<code>`), а bullet с отступом 0–4 пробела под лейблом — элемент; фенсы,
|
|
177
|
+
`<pre>` и код с большим отступом — нет.
|
|
178
|
+
Плейсхолдеры — `<...>` (три и более точки) и `TODO`/`TBD`/`FIXME`; `<DoD>` и
|
|
179
|
+
`<name>` — разметка.
|
|
180
|
+
`max_lines` — единственный бюджет корня (200 строк), который применяет инструмент;
|
|
181
|
+
цифры 60/200/100 по уровням в [`docs/`](../) — ориентир для авторов, а не отдельные
|
|
182
|
+
лимиты инструмента. `max_words` по умолчанию равен цели 1500 слов SPEC; передайте
|
|
183
|
+
`max_words: 2000`, чтобы применять документированный потолок. Типизированные ошибки: `E_INPUT`, `E_NOT_FOUND`, `E_NO_FILES`,
|
|
184
|
+
`E_IO`, `E_ABORTED`, `E_LIMIT_FILE`, `E_LIMIT_FILES`, `E_OUT_OF_ROOT`.
|
|
185
|
+
Ошибка — типизированный payload `{schema:"context-lint-failure/v1", ok:false, code, message, path?, bytes?, files?, max?}`,
|
|
186
|
+
который валидируется только по `context-lint-failure/v1`; отчёт —
|
|
187
|
+
`{schema:"context-lint/v1", ok, verdict, closing_list, files, summary, truncated?}`, и каждый файл несёт `lines`,
|
|
188
|
+
`words`, `chars` и `tokens_est` (токены оцениваются как кириллица/3 + прочее/4). Каталог
|
|
189
|
+
сканируется до глубины 3, а `max_files` отказывает рано: обход останавливается на первом файле
|
|
190
|
+
выше лимита. Ничего не пропадает молча: каталог ниже лимита глубины, ссылка или junction вне
|
|
191
|
+
корня workspace и незакрытые fence, `<pre>` или HTML-комментарий дают INFO-находки, поэтому `pass`
|
|
192
|
+
не скрывает непроверенную область. Незакрытый `<code>` — строчная маска: он скрывает
|
|
193
|
+
остаток своей строки и не репортится (INFO дают только fence, `<pre>` и HTML-комментарий,
|
|
194
|
+
которые тянутся через строки). Отмена наблюдается между файлами, каждые 2048 строк
|
|
195
|
+
построчной проверки и в каждом гейте, поэтому худшая латентность — один проход маски по
|
|
196
|
+
файлу под `max_file_bytes` (десятки мс на 2 МБ), а не весь скан. Команды считаются только в коде, таблице задач или секции
|
|
197
|
+
Commands, HTML-комментарий вроде `<!-- npm install -->` командой не является, а `make` требует
|
|
198
|
+
цели, поэтому проза «Make sure …» — не команда. Скан от корня подхватывает и фикстуры вроде
|
|
199
|
+
`eval/`; передайте явный путь, если нужны только канонические файлы.
|
|
200
|
+
|
|
201
|
+
### `orch_uni_snapshot`
|
|
202
|
+
|
|
203
|
+
```jsonc
|
|
204
|
+
{ "path": "lab/stack-test", "exclude": ["tmp"] } // exclude опционален и аддитивен
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
```jsonc
|
|
208
|
+
{ "root": "C:\\…\\lab\\stack-test",
|
|
209
|
+
"files": [ { "path": "a.txt", "sha256": "…", "bytes": 1 } ],
|
|
210
|
+
"aggregate": "…" }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- `aggregate = sha256` по отсортированным строкам `"<relpath>\0<sha256>\n"`
|
|
214
|
+
(разделители `/`, порядок кодпойнтов) — стабилен между прогонами, машинами и
|
|
215
|
+
порядком обхода и меняется, когда меняется путь или содержимое любого файла.
|
|
216
|
+
- По умолчанию (всегда) исключены: `node_modules`, `.git`, `.ae`; `exclude`
|
|
217
|
+
добавляет имена, целые относительные пути или относительные префиксы
|
|
218
|
+
(`lab/tmp`). Симлинки пропускаются.
|
|
219
|
+
- Лимиты: ≤ 5000 файлов, ≤ 50 МиБ на файл — `E_LIMIT_FILES` / `E_LIMIT_BYTES`
|
|
220
|
+
(падает весь снапшот; усечённый снапшот дал бы неверный агрегат). `E_NOT_FOUND`
|
|
221
|
+
для отсутствующего пути или не-каталога, `E_ABORTED`, когда сессия остановлена.
|
|
222
|
+
|
|
223
|
+
### `orch_uni_sessions`
|
|
224
|
+
|
|
225
|
+
```jsonc
|
|
226
|
+
{} // дети вызывающей сессии
|
|
227
|
+
{ "sessionID": "ses_…", "recursive": true } // всё поддерево другой сессии
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```jsonc
|
|
231
|
+
{ "sessionID": "ses_parent", "recursive": false, "source": "session.index",
|
|
232
|
+
"authoritative": false, "indexSize": 1, "count": 1,
|
|
233
|
+
"limitations": [ … ],
|
|
234
|
+
"sessions": [ { "sessionID": "ses_child", "parentID": "ses_parent", "agent": "explore",
|
|
235
|
+
"model": "deepseek/deepseek-flash", "variant": "default", "title": "…",
|
|
236
|
+
"cost": 0.0005, "tokens": { "input": 3491, "output": 2 },
|
|
237
|
+
"created": "2026-10-15T04:01:06.627Z", "updated": "…" } ] }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`source` говорит, какой маршрут дал ответ:
|
|
241
|
+
|
|
242
|
+
- `session.list` — эта сборка предоставляет API листинга сессий; ответ
|
|
243
|
+
**авторитетный** и полный (`recursive` обходит поддерево постранично).
|
|
244
|
+
- `session.index` — фолбэк, используемый OpenCode 2.0.28: у `ctx.session` есть
|
|
245
|
+
`get/create/…`, но **нет `list`**, поэтому детей нельзя перечислить из
|
|
246
|
+
хранилища. Плагин при setup подписывается на события `session.created` /
|
|
247
|
+
`session.deleted` и хранит `child → parent` в собственном хранилище плагина;
|
|
248
|
+
детали по каждому ребёнку читаются из `ctx.session.get()` в момент вызова.
|
|
249
|
+
Ответ **не авторитетный**; оговорки возвращаются в `limitations` (сессии,
|
|
250
|
+
созданные до старта инстанса плагина, не видны; пропуск событий скрывает
|
|
251
|
+
детей, созданных пока плагин был выгружен).
|
|
252
|
+
- `E_NO_API` — нет ни одного маршрута. Фолбэк через сканирование контекста
|
|
253
|
+
(поиск частей инструмента `subagent` в `ctx.session.context`) был оценён и
|
|
254
|
+
отклонён: у летящих детей нет id сессии в их tool-части, а компакция стирает
|
|
255
|
+
историю. Открытый вопрос: листинг сессий со стороны плагина (или фильтр по
|
|
256
|
+
`parentID` в `ctx.session.context`) убрал бы фолбэк целиком.
|
|
257
|
+
|
|
258
|
+
`E_INPUT` при некорректных аргументах, `E_NOT_FOUND`, когда родительской сессии
|
|
259
|
+
нет, `E_ABORTED`, когда сессия остановлена.
|
|
260
|
+
|
|
261
|
+
### `orch_uni_session_stats`
|
|
262
|
+
|
|
263
|
+
```jsonc
|
|
264
|
+
{} // счётчики вызывающей сессии
|
|
265
|
+
{ "sessionID": "ses_…" } // любая сессия на этом сервере, не только дочерняя
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
```jsonc
|
|
269
|
+
{ "ok": true, "sessionID": "ses_…",
|
|
270
|
+
"counters": {
|
|
271
|
+
"requests": 12, "promptTokensLast": 151204, "promptTokensMax": 161002,
|
|
272
|
+
"promptTokensMedian": 148900, "band": 1, "compactions": 1,
|
|
273
|
+
"compactionAttempts": 1, "compactionFailures": 0, "compactionSource": "event",
|
|
274
|
+
"reminders": 2, "toolCalls": 31, "outputsCapped": 1,
|
|
275
|
+
"outputCharsSaved": 18422, "errors": 0,
|
|
276
|
+
"toolCallsByTool": { "shell": 18, "bash": 6, "webfetch": 2, "read": 5 },
|
|
277
|
+
"toolOutputsCappedByTool": { "shell": 1, "bash": 0, "webfetch": 0, "read": 0 }
|
|
278
|
+
},
|
|
279
|
+
"limits": { "promptTokensSoft": 150000, "maxCompactions": 1, "maxToolOutputChars": 20000 },
|
|
280
|
+
"overBudget": true, "compactionLimitReached": true, "toolsPerTurn": 2.58,
|
|
281
|
+
"tracked": true,
|
|
282
|
+
"guard": { "available": true, "hooksRegistered": true, "sessionEnabled": true,
|
|
283
|
+
"toolOutputEnabled": true,
|
|
284
|
+
"toolOutputTools": ["shell", "bash", "webfetch"],
|
|
285
|
+
"compactionSource": "event", "eventLive": true,
|
|
286
|
+
"streamFailures": 0, "unkeyedCalls": 0 } }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`sessionID` может называть **любую сессию на этом сервере**, не только дочернюю
|
|
290
|
+
вызывающего; это осознанно — по отчёту оркестратор контролирует все свои срезы.
|
|
291
|
+
Счётчики принадлежат инстансу плагина, а `tracked` равен `true`, как только сессию
|
|
292
|
+
наблюдало любое событие или хук; событие `session.deleted` её забывает. `band` —
|
|
293
|
+
максимум достигнутого уровня бюджета (`0` ниже мягкого порога);
|
|
294
|
+
`promptTokensMedian` — медиана по окну последних 256 запросов; `toolCalls` и
|
|
295
|
+
per-tool счётчики включают вложенные вызовы Code Mode; `toolCallsByTool` и
|
|
296
|
+
`toolOutputsCappedByTool` разносят вызовы и урезанные выводы по нормализованному
|
|
297
|
+
имени инструмента (namespace снят, регистр нижний); `outputCharsSaved` — число
|
|
298
|
+
символов, не байтов, и верхняя оценка (внедрённая заглушка вычитается).
|
|
299
|
+
`compactions` считает только подтверждённые `session.compaction.ended`;
|
|
300
|
+
`compactionAttempts` — каждый вызов хука `session.compaction`, поэтому на хосте с
|
|
301
|
+
умершим или отсутствующим потоком событий попытки растут, а `compactions` и
|
|
302
|
+
`compactionLimitReached` стоят (неудачная попытка не становится стоп-сигналом).
|
|
303
|
+
Умерший поток событий один раз виден в `guard.streamFailures`; per-session
|
|
304
|
+
`errors` считает только ошибки хуков, поэтому транспортная авария не
|
|
305
|
+
приписывается каждой сессии. Дубликаты событий компакции считаются один раз
|
|
306
|
+
только при наличии `event.id` или durable-позиции; без identity повторная
|
|
307
|
+
доставка может пересчитать событие (fail-open в сторону счёта).
|
|
308
|
+
Сессия, которую не отдаёт хранилище и не наблюдал этот инстанс, — `E_NOT_FOUND`
|
|
309
|
+
(распознаётся по типизированным `_tag`/`name`/`code` — `Session.NotFoundError`,
|
|
310
|
+
`SessionNotFoundError`, `NotFoundError`, `ENOENT` — с узким fallback по сообщению);
|
|
311
|
+
`session.get`, вернувший `undefined` для ненаблюдаемой сессии, — тот же ответ
|
|
312
|
+
«не найдено»; transient-ошибка `session.get` (блокировка, права, `unknown
|
|
313
|
+
database`) — `E_INTERNAL`; известная сессия без запросов — валидный отчёт с
|
|
314
|
+
`tracked: false`. Без `session.get` ненаблюдаемая сессия —
|
|
315
|
+
`E_NO_API`, наблюдаемая — `ok` с `session: null` и записью в `limitations`.
|
|
316
|
+
Коды: `E_INPUT`, `E_NOT_FOUND`, `E_INTERNAL`, `E_NO_API`, `E_UNAVAILABLE`
|
|
317
|
+
(guard не зарегистрирован или его хуки не установились), `E_ABORTED`.
|
|
318
|
+
|
|
319
|
+
## Гигиена сессии
|
|
320
|
+
|
|
321
|
+
Плагин реализует протокол [`session-discipline.md`](./session-discipline.md):
|
|
322
|
+
сессия = один срез, бюджет промпта ≤150k токенов, ≤1 компакция, вывод инструментов
|
|
323
|
+
— в файл, в конце среза — handoff.
|
|
324
|
+
|
|
325
|
+
Runtime-часть — три хука (`session.context`, `session.compaction`,
|
|
326
|
+
`tool.execute.after`) и подписка на события компакции:
|
|
327
|
+
|
|
328
|
+
- `session.context` — оценивает промпт (system + messages) и при переходе на
|
|
329
|
+
новый уровень бюджета добавляет одно короткое напоминание. Напоминание
|
|
330
|
+
инжектится **один раз при переходе уровня**, а не в каждом запросе; внутри
|
|
331
|
+
уровня текст стабилен, поэтому префикс-кэш не рвётся.
|
|
332
|
+
- события компакции — хук `session.compaction` регистрируется **всегда**: он
|
|
333
|
+
считает попытки (`compactionAttempts`) и отдаёт tentative-напоминание ближайшему
|
|
334
|
+
запросу; напоминание не называет номер компакции и не заявляет лимит.
|
|
335
|
+
Подтверждает компакцию только `session.compaction.ended` (дедуп по `event.id`
|
|
336
|
+
или durable-позиции) и выставляет confirmed-note с номером и лимитом;
|
|
337
|
+
`session.compaction.failed` увеличивает `compactionFailures`, снимает
|
|
338
|
+
tentative-note и сохраняет confirmed-note, ещё ждущий доставки. Если поток
|
|
339
|
+
событий завершился или упал, `guard.eventLive` становится `false`,
|
|
340
|
+
`guard.compactionSource` — `"hook"`, а `guard.streamFailures` — `1`
|
|
341
|
+
(per-session `errors` остаётся `0`); хук продолжает считать попытки, но
|
|
342
|
+
подтверждённых компакций и ложного «лимит исчерпан» на этом пути нет.
|
|
343
|
+
- `tool.execute.after` — для `shell`, `bash`, `webfetch` урезает вывод больше
|
|
344
|
+
`maxChars`: полный текст пишется в приватный корень на инстанс плагина
|
|
345
|
+
`os.tmpdir()/opencode-tool-output-<random>/` (POSIX: 0700 на корень и каталог
|
|
346
|
+
сессии, 0600 на файл), модель получает голову и путь. На Windows mode не
|
|
347
|
+
действует (замер: 0666), приватность там держится только на ACL каталога
|
|
348
|
+
`os.tmpdir()` и кодом не проверяется. Корень создаётся `mkdtemp` при setup,
|
|
349
|
+
поэтому заранее созданный каталог или
|
|
350
|
+
junction `opencode-tool-output` не используется; на POSIX корень обязан
|
|
351
|
+
принадлежать текущему uid и не давать доступа group/other, иначе guard
|
|
352
|
+
отключается с warning (fail-open: результат не трогается). Каталог сессии
|
|
353
|
+
(`s-<uuid>`) и файл (`c-<uuid>.txt`, запись `wx`) — случайные, их `realpath`
|
|
354
|
+
проверяется внутри корня. При выгрузке плагина корень удаляется best effort.
|
|
355
|
+
Строка с lone surrogate нормализуется один раз (U+FFFD), поэтому файл, голова и
|
|
356
|
+
счётчики описывают одну и ту же строку. Остаточный риск принят: same-user
|
|
357
|
+
атакующий всё ещё может подменить каталог
|
|
358
|
+
сессии между `realpath` и `writeFile` (в Node нет `openat`). Если `TMPDIR`
|
|
359
|
+
указывает внутрь воркспейса, файлы попадут туда. `read`, `write`, `edit`,
|
|
360
|
+
`execute` и инструменты комплекта не урезаются; kit-имя без namespace
|
|
361
|
+
(`session_stats`) отбрасывается в конфиге с warning.
|
|
362
|
+
|
|
363
|
+
`maxCompactions: 0` отключает лимит компакций. `guard.toolOutputEnabled` равен
|
|
364
|
+
`true` только когда приватный корень создан **и** эффективный список инструментов
|
|
365
|
+
непуст; `guard.toolOutputTools` — этот список, `guard.unkeyedCalls` — вызовы
|
|
366
|
+
хуков без непустого `sessionID` (отбрасываются, а не сливаются в бакет `""`).
|
|
367
|
+
Пустой `tools` выключает кап с warning. Guard fail-open: ошибка хука не
|
|
368
|
+
ломает запрос и не портит результат. Отключается флагами `enabled`:
|
|
369
|
+
|
|
370
|
+
```jsonc
|
|
371
|
+
{
|
|
372
|
+
"plugins": [
|
|
373
|
+
{
|
|
374
|
+
"package": "opencode-orchestrator-uni",
|
|
375
|
+
"options": {
|
|
376
|
+
"sessionGuard": { "enabled": true, "promptTokensSoft": 150000, "maxCompactions": 1 },
|
|
377
|
+
"toolOutputGuard": {
|
|
378
|
+
"enabled": true,
|
|
379
|
+
"maxChars": 20000,
|
|
380
|
+
"headChars": 4000,
|
|
381
|
+
"tools": ["shell", "bash", "webfetch"]
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Метрики среза — `orch_uni_session_stats`; закрытие среза — команда `/handoff`.
|
|
390
|
+
|
|
391
|
+
## Модели
|
|
392
|
+
|
|
393
|
+
Определения агентов не несут `model`; модели настраиваются у записи плагина в
|
|
394
|
+
`opencode.json(c)`:
|
|
395
|
+
|
|
396
|
+
```jsonc
|
|
397
|
+
{
|
|
398
|
+
"$schema": "https://opencode.ai/config.json",
|
|
399
|
+
"plugins": [
|
|
400
|
+
{
|
|
401
|
+
"package": "opencode-orchestrator-uni",
|
|
402
|
+
"options": {
|
|
403
|
+
// Модель по умолчанию для всех агентов комплекта.
|
|
404
|
+
"model": "deepseek/deepseek-flash",
|
|
405
|
+
// Переопределения по агентам (id -> строка или развёрнутая форма).
|
|
406
|
+
"agents": {
|
|
407
|
+
"meta": "deepseek/deepseek-reasoner",
|
|
408
|
+
"fix-runner": { "providerID": "anthropic", "model": "claude-sonnet-4-5", "variant": "high" }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- `model` — модель по умолчанию для всех агентов комплекта; `agents.<id>` —
|
|
417
|
+
переопределение по агенту. Оба принимают `"provider/model[#variant]"` или
|
|
418
|
+
`{ providerID, model, variant }`.
|
|
419
|
+
- Без опций агенты комплекта остаются без модели — субагенты наследуют модель
|
|
420
|
+
сессии.
|
|
421
|
+
- Неверные значения игнорируются с предупреждением (`[orchestrator.uni] …`).
|
|
422
|
+
- Субагенты работают на своей настроенной модели; основной `orchestrator` — на
|
|
423
|
+
модели сессии, поэтому модель, заданная для `orchestrator`, — информационная
|
|
424
|
+
для клиентов (на момент OpenCode 2.0.28).
|
|
425
|
+
- Запись `agents` в конфиге или локальный Markdown-файл агента со своей моделью
|
|
426
|
+
всё равно побеждает — конфигурация применяется после пакетных плагинов.
|
|
427
|
+
|
|
428
|
+
## Локаль
|
|
429
|
+
|
|
430
|
+
Определения агентов и процессные документы поставляются per-locale — в
|
|
431
|
+
`agents/<locale>/` и `docs/<locale>/`; сейчас это `ru` и `en` (английский
|
|
432
|
+
контент — перевод русских оригиналов). Комплект выбирает локаль в таком порядке:
|
|
433
|
+
|
|
434
|
+
1. `options.locale` (`"ru"` или `"en"`; полные теги вроде `"ru-RU"` принимаются);
|
|
435
|
+
2. автодетект: `LC_ALL` / `LC_MESSAGES` / `LANG`, затем Intl-локаль рантайма
|
|
436
|
+
(настройка ОС);
|
|
437
|
+
3. дефолт: `en`.
|
|
438
|
+
|
|
439
|
+
```jsonc
|
|
440
|
+
{
|
|
441
|
+
"$schema": "https://opencode.ai/config.json",
|
|
442
|
+
"plugins": [
|
|
443
|
+
{
|
|
444
|
+
"package": "opencode-orchestrator-uni",
|
|
445
|
+
"options": { "locale": "ru", "model": "deepseek/deepseek-flash" }
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Если для запрошенной локали контент не поставлен, происходит откат к дефолтной
|
|
452
|
+
локали с предупреждением в логе плагина. Обе локали регистрируют одних и тех же
|
|
453
|
+
агентов под одними и теми же id — переключение локали меняет только язык
|
|
454
|
+
промптов, процессных документов и описания reference.
|
|
455
|
+
|
|
456
|
+
## Приоритет
|
|
457
|
+
|
|
458
|
+
**Определения** агентов заполняются только для id, не определённых где-либо ещё:
|
|
459
|
+
агент с тем же id из вашей конфигурации — запись `agents` в конфиге или
|
|
460
|
+
Markdown-файл в `~/.config/opencode/agents/` (либо `.opencode/agents/`) —
|
|
461
|
+
побеждает, и плагин пропускает своё определение. **Настроенная модель** (см.
|
|
462
|
+
[Модели](#модели)) всё равно применяется к агентам комплекта; модель,
|
|
463
|
+
определённая вашей записью агента или файлом, побеждает её, потому что
|
|
464
|
+
конфигурация применяется после пакетных плагинов. То же правило «занято —
|
|
465
|
+
пропускаем» действует для reference `audit-kit`.
|
|
466
|
+
|
|
467
|
+
Если вы раньше устанавливали комплект копированием файлов агентов в свой
|
|
468
|
+
конфиг-каталог, **удалите эти копии** после включения плагина, иначе устаревшие
|
|
469
|
+
копии продолжат перекрывать определения, поставляемые плагином.
|
|
470
|
+
|
|
471
|
+
## Заметки
|
|
472
|
+
|
|
473
|
+
- Одна рантайм-зависимость: [`gray-matter`](https://www.npmjs.com/package/gray-matter)
|
|
474
|
+
для парсинга фронтматтера. OpenCode сам устанавливает зависимости пакетных
|
|
475
|
+
плагинов.
|
|
476
|
+
- Id плагина: `orchestrator.uni`.
|
|
477
|
+
- Пакет загружается напрямую из TypeScript: `exports` указывает на `index.ts`.
|
|
478
|
+
Шага сборки и каталога `dist/` нет (тот же приём, что в `@opencode/plugin-figma`).
|
|
479
|
+
- Поставляются только файлы из `package.json#files`: `index.ts`, `lib/`, `schemas/`,
|
|
480
|
+
`agents/`, `docs/`, `README.md`, `README.ru.md`, `LICENSE`.
|
|
481
|
+
|
|
482
|
+
<p align="right">(<a href="../../README.ru.md">назад в README</a>)</p>
|
package/docs/ru/root-runbook.md
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
3. **Свод root'ом**: распарсить JSON-блоки; вычислить вердикты (ready/not_ready), открытые MAJOR, пересечения и расхождения (по location+механизму). Материализовать свод-артефакт `reports/summary-<id>.md` (определение — `call-mechanics.md` §4): по каждой MAJOR — {id, verifier_role, verdict, evidence}; refuted-список (явный, даже пустой); принятые риски; ревизия состояния (хэш каталога/коммит).
|
|
29
29
|
4. **Верификация MAJOR (перекрёстная, fail-closed)**: каждая MAJOR-находка подтверждается независимо **сессией противоположной роли** (не автором находки) — `subagent(agent="<sibling>", sessionID=<сессия противоположной роли>, prompt="Находка <id> контрагента: <текст>. Evidence автора: <команда→вывод>. Ревизия состояния: <хэш каталога/коммит>. Подтверди или опровергни своим воспроизведением; дефолт — не подтверждена. Верни: {id, verifier_role, verdict: confirmed|refuted|partial|unavailable, evidence}.")`. `verifier_role` ≠ роль автора; `partial`/`unavailable` = «не подтверждена»; `refuted` — в список refuted для сводки; сбой/таймаут верификатора — «не подтверждена» + шум. Не подтверждена → в правки не идёт (спор → шаг 5, арбитр); статусы верификации пишутся в свод `reports/summary-<id>.md` (шаг 3). Если MAJOR-находка независимо воспроизведена обеими ролями в их собственных прогонах — взаимное подтверждение засчитывается как верификация (обе стороны в свод), отдельный прогон не требуется. sessionID при вызове обязателен: без него это новая сессия, а не продолжение — дефект шага.
|
|
30
30
|
5. **Арбитр — только при расхождениях**: `subagent(agent="arbiter", prompt="Спорные пункты: <пункт → evidence A → evidence B>. По каждому: подтверждён/опровергнут/недоказуем + команда/цитата.")`. Вердикт свода (и верификация/арбитраж) фиксируются ДО правок и **вместе с ревизией состояния** (хэш каталога/коммит); любая правка после фиксации вердикта → повторный прогон (re-audit) либо явное понижение вердикта: до вердикта — не править.
|
|
31
|
-
6.
|
|
31
|
+
6. **Правки**: только `fix-runner` — root код, доки и тесты не правит; его запись ограничена артефактами аудита/состояния (вход: список «к закрытию» — id, локация, механизм, **класс/область** (пути + правило поиска однотипных мест), repro-до, критерий-после, severity — плюс границы и **бюджет/лимит (время, число правок)**; выход: fix-runner/v1 с before/after/`proof`/тестами). Отдельные коммиты `fix-<round>`; обновить реестр: `closed` / `accepted-risk` (с причиной). Root проверяет дельту раунда — `git diff <round-base>..HEAD --stat` (не только `git show --stat`) — до re-audit. Стенд раунда (если в комплекте поставляется проверочный стенд — инструмент `tools/kit-verify` каталога комплекта; в раундах по другим проектам — только если стенд явно включён в границы вызова): baseline стенда в промпт фиксера (если вывод передан — фиксер не дублирует прогон); закрытие — повторный прогон стенда; оба вывода — в поле `stand` отчёта. Стенд поставляется в git-чекауте комплекта: в npm-пакете каталог `tools/` не поставляется → `stand: unavailable` и собственные воспроизводимые проверки. Статусы `fix-runner`: `needs_decision` → решение root (при споре — арбитр; если решение за владельцем — эскалация) с фиксацией в реестре рисков; `not_closed` → повторная итерация с тем же id; `budget_exceeded` → расширить бюджет решением владельца либо разбить список и продолжить с того же id (коммиты и worktree сохранить до закрытия или уборки); поле `out_of_scope` (замечено вне списка) → включить в следующий список «к закрытию» или эскалировать. Статус **`no_report`** (отчёта нет: фиксер упал/таймаут) — root фиксирует в своде выполненную работу (worktree, ветка, base SHA) и `no_report`; ветку/коммиты НЕ сливать; приоритет — resume в том же worktree; иначе убрать: `git worktree remove --force` и решить судьбу ветки. Частичный коммит без отчёта в основную ветку не вливается. Несколько `fix-runner` — только в отдельных worktree **вне дерева проекта** (у каждого своя ветка `fix/<round>-<n>`); путь пространства сверяется префиксами путей (`git -C <project> rev-parse --show-toplevel` не должен быть префиксом канонического пути пространства; иначе — `needs_decision`, пространство не создаётся и не используется); перед запуском root считает пересечение **наборов файлов** (пути + правило поиска класса), а не только списков: пересечение ⇒ сериализация или разнесение путей. Посадка сессии в её пространство: `session_move({sessionID, directory})` (если доступен), иначе `cd <абс. путь>` + абсолютные пути; ни то ни другое — параллельный запуск запрещён. В свежем worktree зависимости ставятся до прогонов (`npm ci` / `bun install`). После завершения root сводит результаты строго по **полному hex SHA** из отчёта: `commit` принимается только как `^[0-9a-f]{40}$` — проверка ДО любых git-команд (имена веток из отчётов для сведения не использовать; `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`; merge/cherry-pick по SHA, прогоняет проверки на сведённом состоянии и только тогда фиксирует `closed`; worktree не удалять до подтверждённого переноса коммита, после — `git worktree remove --force` и осознанное решение по ветке; повторный запуск после инцидента — resume в том же worktree либо явное удаление/переименование ветки решением root'а (повторный `git worktree add -b` падает при занятой ветке), `locked` worktree — `git worktree unlock` либо `git worktree remove -f -f`.
|
|
32
32
|
7. **Re-audit = resume те же сессии** (sessionID из шага 2): «Правки: <диапазон + фактический список изменённых файлов>. Проверь закрытие своих находок и отсутствие регрессий; верни обновлённый SGR.» — исторически так и делалось (продолжение сессий GLM/DS). sessionID обязателен: вызов без него (новая сессия вместо продолжения) — дефект шага. Re-audit и `closed` — на **сведённом** состоянии: доказательство приёмки (after/stand) перепривязывается к сведённому commit SHA; `closed` без отчёта фиксера (`no_report`) запрещён; нет ключа `commit` или `workspace` в отчёте фиксера ⇒ `not_closed`.
|
|
33
33
|
8. **Повторяй шаги 3–7, пока не достигнут полный ready** (число кругов не ограничено; все роли ready, MAJOR=0). С 3-го круга нерешённые пункты — арбитр поштучно; повторяющиеся ложные — снять и записать как шум аудита.
|
|
34
34
|
9. **Ворота (релиз; порядок: ред-тим → мета)**: замороженный коммит → `redteam` (мандат фальсификации + корпуса/стенды) → цикл по его находкам (MAJOR — с верификацией сессией противоположной роли) → `meta` (на вход: отчёты + свод `reports/summary-<id>.md`; evidence, полнота, отсутствие «зелёные тесты = проверено»).
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
- Разведка и проверка («найти», «понять», «воспроизвести») — субагентам: у них
|
|
45
45
|
чистый контекст, а в ответ возвращается 1–2k токенов выводов, а не дамп.
|
|
46
|
-
- Реализация — одному
|
|
46
|
+
- Реализация — одному пишущему субагенту на рабочее пространство (root сам код, доки и тесты не правит — только артефакты аудита/состояния): связанные решения должны
|
|
47
47
|
приниматься в одном контексте (иначе параллельные агенты расходятся в стиле и
|
|
48
48
|
контрактах).
|
|
49
49
|
- Решения, сведение и приёмка — root-оркестратору.
|