devcontour-harness 0.1.0-alpha.6 → 0.1.0-alpha.7

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 CHANGED
@@ -5,7 +5,7 @@ Headless-движок исполнения инженерных поручени
5
5
  интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
6
6
  и доказательствами.
7
7
 
8
- > Статус: `0.1.0-alpha.6` — предварительный выпуск (alpha). Договоры
8
+ > Статус: `0.1.0-alpha.7` — предварительный выпуск (alpha). Договоры
9
9
  > `v1alpha1` могут измениться между alpha-версиями; закрепляйте точную версию.
10
10
  > Файл → ревью плана → кандидат → интеграция → принятие → результат JSON, с
11
11
  > ограниченным восстановлением, повторным использованием кандидата, бюджетами,
@@ -27,7 +27,7 @@ Headless-движок исполнения инженерных поручени
27
27
  Из npm — точная версия (воспроизводимо) или последняя alpha по тегу:
28
28
 
29
29
  ```sh
30
- npm install --save-exact devcontour-harness@0.1.0-alpha.6 # точная версия
30
+ npm install --save-exact devcontour-harness@0.1.0-alpha.7 # точная версия
31
31
  # или: npm install --save-exact devcontour-harness@alpha # текущая alpha
32
32
  npx harness --version
33
33
  ```
@@ -117,6 +117,24 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
117
117
  `devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
118
118
  Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
119
119
 
120
+ ## Выпуск 0.1.0-alpha.7
121
+
122
+ Исправление учёта команд reviewer в `inspection` ревью кода. Подробно —
123
+ [заметки выпуска](docs/release-notes/0.1.0-alpha.7.md).
124
+
125
+ - `inspection` собирается controller из событий runtime и журнала проверок
126
+ `run_check` одного вызова reviewer: запуски probe теперь засчитываются, а
127
+ команда с ненулевым кодом — выполнение, а не `diff-only`
128
+ ([review-probes](docs/review-probes.md#итог-inspection-что-reviewer-действительно-выполнил)).
129
+ - Старт probe подтверждается на границе исполнения; отказ до старта, отменённый
130
+ до запуска вызов и резерв бюджета выполнением не считаются. Уже отменённый
131
+ вызов больше не запускает команду.
132
+ - Gates, бюджеты, правила находок и принятия не менялись: `inspection` по-прежнему
133
+ не влияет на решение.
134
+
135
+ Записи alpha.6 читаются без пересчёта; состояние с новыми записями `inspection`
136
+ alpha.6 не читает — откат только через копию ([upgrade](docs/upgrade.md)).
137
+
120
138
  ## Выпуск 0.1.0-alpha.6
121
139
 
122
140
  Ядро работает как прежде; всё новое подключается явно. Подробно —
@@ -13,7 +13,7 @@
13
13
  ```sh
14
14
  mkdir harness-demo && cd harness-demo
15
15
  npm init -y >/dev/null
16
- npm install --save-exact devcontour-harness@0.1.0-alpha.6
16
+ npm install --save-exact devcontour-harness@0.1.0-alpha.7
17
17
  npx harness --version
18
18
  npx harness doctor
19
19
  ```
@@ -0,0 +1,71 @@
1
+ # devcontour-harness 0.1.0-alpha.7
2
+
3
+ Исправление учёта фактически выполненных команд reviewer в `inspection` ревью
4
+ кода. Gates, `maxAttempts`, бюджеты попыток и повторов, правила находок, защиты
5
+ worktree и принятие target ref не менялись: `inspection` по-прежнему описывает
6
+ evidence и не влияет на решение.
7
+
8
+ ## Что исправлено
9
+
10
+ - **Проверки reviewer не учитывались.** На пилоте alpha.6 reviewer запускал
11
+ проверки через `run_check` (в том числе объявленные gates), а `inspection`
12
+ оставался `diff-only` с пустым списком команд: учитывались только shell-события
13
+ Codex. Теперь итог собирает controller из событий runtime и журнала
14
+ `probes.jsonl` одного вызова reviewer; самоотчёт модели по-прежнему ничего не
15
+ подтверждает.
16
+ - **Ненулевой код выхода превращал выполнение в `diff-only`.** Режим зависел от
17
+ наличия команды с кодом 0. Теперь `mode: commands` — любая подтверждённая
18
+ выполненная команда; провал теста — исход, а не отсутствие выполнения.
19
+ - **Уже отменённый вызов проверки запускал команду.** Слушатель отмены на уже
20
+ отменённом сигнале не срабатывал, и команда шла до таймаута. Теперь такой
21
+ вызов — `cancelled` без запуска.
22
+ - **Код и вывод терялись при сбое после исхода** (уборка временных каталогов,
23
+ сохранение evidence gate): исход сохраняется как `error`/`environment` с
24
+ кодом и выводом.
25
+ - **Короткий секрет портил разбор событий Codex** и служебные значения
26
+ записи: события разбираются из немаскированного потока только в памяти,
27
+ маскируются поля текста до обрезки; сохраняемые журналы маскированы, как и
28
+ раньше.
29
+
30
+ ## Граница старта
31
+
32
+ Запуск probe засчитывается по признаку `started`, полученному на границе
33
+ исполнения `runCheck`: без песочницы — порождение процесса, в песочнице — метка,
34
+ которую обёртка создаёт перед `exec`. Отказ до старта, исчерпанный бюджет,
35
+ неизвестный gate, отменённый до запуска вызов, резерв `start` и `abandoned`
36
+ выполнением не считаются; они перечислены в `inspection.excluded`.
37
+
38
+ ## Изменения договора
39
+
40
+ Только добавления и nullable — подробно в
41
+ [review-probes](../review-probes.md#итог-inspection-что-reviewer-действительно-выполнил):
42
+
43
+ - `inspection.source`: новые значения `controller-probes`,
44
+ `runtime-and-controller`;
45
+ - `inspection.commands[].exitCode` может быть `null`; новые необязательные
46
+ `origins`, `status`, `failure`, `gate`, `note`;
47
+ - новые необязательные `inspection.context`, `total`, `excluded`,
48
+ `excludedTotal`, `invalid`;
49
+ - строки исхода `probes.jsonl` и `review.json.probes`: `started`, `scope`,
50
+ `exitCode`, `note`;
51
+ - `runCheck` возвращает `started` и принимает `onStart`; `command()` —
52
+ `onSpawn`, `onRawStdout`.
53
+
54
+ Полный `inspection` в `run-result` не добавлен; публичные схемы, кроме
55
+ `execution-document`, не менялись.
56
+
57
+ ## Совместимость
58
+
59
+ alpha.7 читает состояние alpha.6 без преобразования и без пересчёта прежних
60
+ `inspection`, digest и решений. Направление одностороннее: строгая схема alpha.6
61
+ не принимает новые значения `source`, `exitCode: null` и новые поля, поэтому
62
+ откат с alpha.7 — только восстановлением копии состояния, сделанной до
63
+ обновления ([upgrade](../upgrade.md)). Потребителю, разбирающему `inspection`,
64
+ нужно принять новые значения enum и nullable `exitCode`; признак выполнения —
65
+ `mode`.
66
+
67
+ ## Проверка выпуска
68
+
69
+ `npm run check` (lint, typecheck, schemas, build, весь набор тестов, package
70
+ smoke) на macOS arm64, Node.js 24.3.0. Матрица приёмки IN01–IN20; независимая
71
+ приёмка другой моделью воспроизвела IN01–IN19 собственными прогонами.
@@ -85,6 +85,85 @@ To run a declared gate, call run_check with exactly {"gate":"domain"}, {"gate":"
85
85
  оценивает движок; probe не записывает evidence gate, не ставит технический PASS и не двигает
86
86
  target ref.
87
87
 
88
+ ## Итог `inspection`: что reviewer действительно выполнил
89
+
90
+ `review.json.inspection` и `EvidenceRecord.inspection` ревью кода — один и тот же объект. Его
91
+ собирает controller для **одного вызова reviewer** из двух наблюдений: событий runtime этого
92
+ вызова (Codex `item.completed`/`command_execution`) и журнала проверок `probes.jsonl` этого
93
+ вызова. Самоотчёт модели (`execution.commands`, `reproduction.executed`, runId в тексте) ничего
94
+ не подтверждает.
95
+
96
+ - `mode: commands` — подтверждена хотя бы одна выполненная команда; `reason: null`. Ненулевой
97
+ код, таймаут или отмена после старта и ошибка среды после старта выполнение не отменяют.
98
+ - `mode: diff-only` — подтверждённых выполнений нет; `commands: []`, `reason` объясняет.
99
+ - `commands` — не PASS: тесты могли упасть, gates и вердикт оценивает движок. Принятие по-прежнему
100
+ зависит только от обязательных gates и валидного вердикта; `commands` не лечит невалидный ответ,
101
+ изменённое дерево или проваленный gate, а неуспешный probe сам не создаёт находку.
102
+
103
+ ### Граница старта
104
+
105
+ Запуск probe засчитывается по признаку `started` из `runCheck`: без песочницы — процесс порождён
106
+ (событие `spawn`), в песочнице — обёртка создала метку старта перед `exec` (запуск самой обёртки
107
+ не считается). Признак отмечается на границе и переживает исключение после неё.
108
+
109
+ | Событие | В `commands` |
110
+ | ----------------------------------------------------------------------------------------- | ------------ |
111
+ | Команда завершилась с любым кодом | да |
112
+ | `timeout`/`cancelled` после старта (код `null`, частичный вывод) | да |
113
+ | `error`/`environment` после старта (сеть закрыта песочницей, EPERM, сбой записи evidence) | да |
114
+ | `failed`/`report`: код 0, JUnit отсутствует или испорчен | да |
115
+ | Неизвестный gate, бюджет исчерпан, нет executable, отказ песочницы или scratch до старта | нет |
116
+ | Вызов отменён до запуска (`cancelled`, команда не исполнялась) | нет |
117
+ | Только резерв `start`, `abandoned`, битая строка, объект без формы исхода | нет |
118
+
119
+ Незасчитанные записи перечислены в `inspection.excluded` с причиной; полная диагностика — в
120
+ `review.json.probes` и журнале.
121
+
122
+ ### Форма
123
+
124
+ | Смысл | JSON path | Тип и значения |
125
+ | ------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
126
+ | Источник итога | `source` | `codex-events`, `unavailable` (прежние), `controller-probes`, `runtime-and-controller` |
127
+ | Вызов reviewer | `context` | `{attempt, phase: candidate\|integration, sha, call}`; `attempt` — ID попытки, не runId probe |
128
+ | Все подтверждённые операции | `total` | целое ≥ 0 |
129
+ | Происхождение команды | `commands[].origins[]` | `{type: "runtime", runtime, item: string\|null}` или `{type: "controller", runId}` |
130
+ | Код выхода | `commands[].exitCode` | целое или `null` (неизвестен: таймаут, отмена, старая проекция без кода) |
131
+ | Исход | `commands[].status` | `completed`/`failed` (runtime); `passed`/`failed`/`timeout`/`cancelled`/`error` (probe) |
132
+ | Неуспех не в коде | `commands[].failure` | `environment`, `report` |
133
+ | Gate | `commands[].gate` | ID объявленного gate |
134
+ | Пояснение controller | `commands[].note` | строка ≤ 2000 |
135
+ | Не засчитано | `excluded[]` | `{runId, status, reason}`, `reason`: `not-started`, `legacy-unconfirmed`, `foreign-scope`, `unknown-status`, `unfinished`, `abandoned`; не более 100, полное число — `excludedTotal` |
136
+ | Строки журнала без формы исхода | `invalid` | целое ≥ 0 |
137
+
138
+ Пределы: не более 100 команд; `command` ≤ 12000 символов; `output` ≤ 4000 (stdout и stderr через
139
+ перевод строки, при нехватке места — хвосты обоих примерно поровну); `note` ≤ 2000. Потоки probe
140
+ в журнале по-прежнему ограничены хвостом 8000 с маркером `…`. Порядок: команды runtime в порядке
141
+ потока, затем запуски probe в порядке журнала; при превышении сохраняются последние 100, а
142
+ `mode` вычисляется по всему набору. Пользовательский текст маскируется до обрезки; перечисления и
143
+ идентификаторы controller не маскируются.
144
+
145
+ ### Идентичность и область
146
+
147
+ Операция одна только при надёжной идентичности: один runId probe; один item ID в потоке runtime;
148
+ явная структурированная связь наблюдения runtime с runId probe (origin `controller` у команды
149
+ runtime) — тогда у операции оба происхождения. Одинаковый argv с разными runId — две операции;
150
+ совпадение текста, вывода или замаскированных аргументов не склеивает.
151
+
152
+ Каждый исход в журнале несёт `scope: {attempt, phase, sha, call}`. Запись с явно чужими
153
+ попыткой, фазой, SHA или вызовом (или gate с чужими `phase`/`sha`) не засчитывается
154
+ (`excluded`, `foreign-scope`). `call-2` не получает команд `call-1`, integration — команд
155
+ candidate; общий бюджет проверок логического ревью по-прежнему считается по журналам всех вызовов.
156
+
157
+ ### Старые записи
158
+
159
+ Прежние `inspection` (без новых полей, в том числе противоречивые `diff-only` с командой) и
160
+ evidence без `inspection` читаются как есть; чтение не пересчитывает итог, digest и решение.
161
+ Для отдельной агрегации старых данных: завершённые события runtime засчитываются как раньше;
162
+ записи probe без `started` со статусом `passed`/`failed` — выполнение (код и вывод, если их нет,
163
+ остаются неизвестными: `exitCode: null`, пустой `output`); `error`/`timeout`/`cancelled` без
164
+ подтверждения старта не засчитываются (`legacy-unconfirmed`) — это ограничение старых данных.
165
+ Явный `started: false` главнее правила старых записей.
166
+
88
167
  ## Рецепт: путь отчёта из переменной controller
89
168
 
90
169
  Gate пригоден для probe, если команда пишет JUnit в `HARNESS_REPORT_PATH` (или
package/docs/upgrade.md CHANGED
@@ -7,13 +7,13 @@
7
7
 
8
8
  ## Версии
9
9
 
10
- | Что | Правило |
11
- | --------------- | --------------------------------------------------------------------------- |
12
- | Пакет | `0.1.0-alpha.N`; выпущенная версия неизменяема (тот же номер — те же байты) |
13
- | Публичный API | `devcontour-harness/v1alpha1` |
14
- | Политика | `policyVersion: 1` |
15
- | Состояние | `stateVersion: 1` |
16
- | Отчёты и корпус | `version: 1` (отчёт свойств, договор проверки, корпус, receipt среды) |
10
+ | Что | Правило |
11
+ | --------------- | --------------------------------------------------------------------------- || --------------------
12
+ | Пакет | `0.1.0-alpha.N`; выпущенная версия неизменяема (тот же номер — те же байты) |
13
+ | Публичный API | `devcontour-harness/v1alpha1` |
14
+ | Политика | `policyVersion: 1` |
15
+ | Состояние | `stateVersion: 1` |
16
+ | Отчёты и корпус | `version: 1` (отчёт свойств, договор проверки, корпус, receipt среды) |
17
17
 
18
18
  Для `0.x`: patch — совместимые исправления; minor — отдельно описанные
19
19
  изменения договора, включая несовместимые. Неизвестная версия входа, политики
@@ -21,19 +21,20 @@
21
21
 
22
22
  ## Матрица форматов
23
23
 
24
- | Состояние, записанное… | Читает 0.1.0-alpha.1 | Читает 0.1.0-alpha.2 | Читает 0.1.0-alpha.6 | Пишет поверх | Примечание |
25
- | ------------------------------------------------------------------------------ | ------------------------ | ------------------------ | -------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
26
- | standalone `stateVersion: 1` версией alpha.1 | да | да | да | да | одна база SQLite: документ, события, локальные записи |
27
- | хост, центральная база | да (через адаптер) | да (через адаптер) | да (через адаптер) | да | данные не переносятся; новые поля необязательны |
28
- | хост, базы компонентов | да (через адаптер) | да (через адаптер) | да (через адаптер) | да | нужна копия всех баз при остановленных writers |
29
- | alpha.2 после штатной остановки controller | да | да | да | да | роль controller снята — нового поля в состоянии нет |
30
- | alpha.2 с живой или брошенной ролью controller | **нет**, отказ до записи | да | да | — | роль хранит `engineVersion`; строгая схема alpha.1 его не знает → откат через копию |
31
- | alpha.4 с совместной проверкой | **нет**, отказ до записи | **нет**, отказ до записи | да | — | проверка хранит `engineVersion`; прежние версии его не знают → откат через копию |
32
- | alpha.6 standalone с записями ревью нового протокола или доставкой `uncertain` | **нет**, отказ до записи | **нет**, отказ до записи | да | — | `reviewRound`, `findings[].id/status`, статус доставки `uncertain`; alpha.4/alpha.5 отказывают `STATE_VERSION_UNSUPPORTED` до записи → откат через копию |
33
- | alpha.6, хост DevContour (адаптер alpha.4) | — | — | да (через адаптер) | да | адаптер DevContour alpha.4 открывает такое состояние без отказа и пишет роль controller и события; новые поля сохраняются → для отката всё равно копия |
34
- | неизвестная `stateVersion` | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет | `STATE_VERSION_UNSUPPORTED`, exit 4 |
35
-
36
- Столбец alpha.2 относится и к alpha.3; alpha.4 и alpha.5 читают все строки, кроме неизвестной `stateVersion` и строк alpha.6 с новыми полями (standalone — отказ до записи). Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
24
+ | Состояние, записанное… | Читает 0.1.0-alpha.1 | Читает 0.1.0-alpha.2 | Читает 0.1.0-alpha.6 | Читает 0.1.0-alpha.7 | Пишет поверх | Примечание |
25
+ | ------------------------------------------------------------------------------ | ------------------------ | ------------------------ | ----------------------- | -------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
26
+ | standalone `stateVersion: 1` версией alpha.1 | да | да | да | да | да | одна база SQLite: документ, события, локальные записи |
27
+ | хост, центральная база | да (через адаптер) | да (через адаптер) | да (через адаптер) | да (через адаптер) | да | данные не переносятся; новые поля необязательны |
28
+ | хост, базы компонентов | да (через адаптер) | да (через адаптер) | да (через адаптер) | да (через адаптер) | да | нужна копия всех баз при остановленных writers |
29
+ | alpha.2 после штатной остановки controller | да | да | да | да | да | роль controller снята — нового поля в состоянии нет |
30
+ | alpha.2 с живой или брошенной ролью controller | **нет**, отказ до записи | да | да | да | — | роль хранит `engineVersion`; строгая схема alpha.1 его не знает → откат через копию |
31
+ | alpha.4 с совместной проверкой | **нет**, отказ до записи | **нет**, отказ до записи | да | да | — | проверка хранит `engineVersion`; прежние версии его не знают → откат через копию |
32
+ | alpha.6 standalone с записями ревью нового протокола или доставкой `uncertain` | **нет**, отказ до записи | **нет**, отказ до записи | да | да | — | `reviewRound`, `findings[].id/status`, статус доставки `uncertain`; alpha.4/alpha.5 отказывают `STATE_VERSION_UNSUPPORTED` до записи → откат через копию |
33
+ | alpha.6, хост DevContour (адаптер alpha.4) | — | — | да (через адаптер) | да (через адаптер) | да | адаптер DevContour alpha.4 открывает такое состояние без отказа и пишет роль controller и события; новые поля сохраняются → для отката всё равно копия |
34
+ | alpha.7 с новым `inspection` ревью кода | не проверялось | не проверялось | **нет** (строгая схема) | да | — | новые значения `source`, `exitCode: null`, `origins`/`context`/`excluded`; строгая схема alpha.6 их не принимает, поведение прежних версий при открытии не проверялось → откат через копию |
35
+ | неизвестная `stateVersion` | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет | `STATE_VERSION_UNSUPPORTED`, exit 4 |
36
+
37
+ Столбец alpha.2 относится и к alpha.3; alpha.4 и alpha.5 читают все строки, кроме неизвестной `stateVersion` и строк alpha.6 с новыми полями (standalone — отказ до записи) и строки alpha.7. Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
37
38
  формата до записи проверяют тесты совместимости пакета
38
39
  <!-- repo-only -->(`tests/compatibility.test.ts`)<!-- /repo-only -->.
39
40
 
@@ -135,6 +136,22 @@ recovery.
135
136
  (новое согласование). Встроенные профили alpha.5 побайтно прежние; новые
136
137
  профили — новые id (`npm-ci`, `pnpm-frozen`, `go-gotestsum`, `python-uv`).
137
138
 
139
+ ### alpha.7: учёт выполненных команд reviewer
140
+
141
+ Формат `stateVersion: 1` прежний. `inspection` в `review.json` и evidence ревью кода получает
142
+ новые значения и необязательные поля ([review-probes](review-probes.md#итог-inspection-что-reviewer-действительно-выполнил)):
143
+ `source` — также `controller-probes` и `runtime-and-controller`; `commands[].exitCode` может
144
+ быть `null`; новые `commands[].origins/status/failure/gate/note`, `context`, `total`,
145
+ `excluded`, `excludedTotal`, `invalid`. Строки исхода в `probes.jsonl` несут `started` и
146
+ `scope`; `review.json.probes` — `exitCode`, `started`, `scope`, `note`.
147
+
148
+ Направление совместимости одностороннее: alpha.7 читает записи alpha.6 и ранее без
149
+ пересчёта; строгая схема alpha.6 не принимает новый `source`, `exitCode: null` и новые поля,
150
+ поэтому состояние с такими записями alpha.6 не читает. Потребителю, разбирающему
151
+ `inspection`, нужно принять новые значения `source`, nullable `exitCode` и необязательные
152
+ поля; признак выполнения — `mode`, а не наличие кода 0. Откат — как для alpha.6: только
153
+ восстановлением копии состояния, сделанной до обновления.
154
+
138
155
  ## Перед обновлением
139
156
 
140
157
  1. **Остановить записывающих.** Запретить новые выдачи (`harness pause`),
@@ -176,11 +193,11 @@ recovery.
176
193
 
177
194
  ## Откат
178
195
 
179
- | Ситуация | Действие |
180
- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
- | Изменения исходников до выкатки | Вернуться к проверенной паре коммитов/версий в отдельном checkout или revert-коммитами; страховочную ветку не переписывать |
182
- | Только пакет, формат данных совместим | Остановить writers → вернуть прежнюю версию (архив или точную версию npm), зависимость и lock → `harness doctor` → возобновить |
183
- | Новая версия уже изменила формат | Остановить writers → восстановить согласованную копию вместе с совместимой версией пакета → сверить Git refs и артефакты (ниже); простого понижения версии мало |
196
+ | Ситуация | Действие |
197
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- || --------------------
198
+ | Изменения исходников до выкатки | Вернуться к проверенной паре коммитов/версий в отдельном checkout или revert-коммитами; страховочную ветку не переписывать |
199
+ | Только пакет, формат данных совместим | Остановить writers → вернуть прежнюю версию (архив или точную версию npm), зависимость и lock → `harness doctor` → возобновить |
200
+ | Новая версия уже изменила формат | Остановить writers → восстановить согласованную копию вместе с совместимой версией пакета → сверить Git refs и артефакты (ниже); простого понижения версии мало |
184
201
 
185
202
  ### Сверка Git после восстановления базы
186
203
 
@@ -279,9 +279,16 @@ export function cliAdapter(runtime, options = {}) {
279
279
  diagnostics: { exitCode: null, reason: redact(reason).slice(0, 500), log: logPath },
280
280
  });
281
281
  let result;
282
+ // События runtime для inspection разбираются до маскирования (иначе
283
+ // короткий секрет портит `item.completed`), текст полей маскируется
284
+ // при разборе; сырой поток только в памяти и не сохраняется.
285
+ let raw = '';
282
286
  try {
283
287
  result = await command(argv, r.cwd, {
284
288
  contain: { dirs: review ? [] : r.sandbox.writable },
289
+ ...(review
290
+ ? { onRawStdout: (text) => void (raw = (raw + text).slice(-2_000_000)) }
291
+ : {}),
285
292
  onOutput: (stream, text) => {
286
293
  if (logged >= 10_000_000)
287
294
  return;
@@ -333,7 +340,7 @@ export function cliAdapter(runtime, options = {}) {
333
340
  model,
334
341
  runtimeVersion: version,
335
342
  usage,
336
- ...(review ? { inspection: inspectReview(runtime, result.stdout) } : {}),
343
+ ...(review ? { inspection: inspectReview(runtime, raw, redact) } : {}),
337
344
  };
338
345
  const diagnostics = (why) => ({
339
346
  exitCode: result.code,
@@ -1,6 +1,60 @@
1
1
  import type { EvidenceRecord } from '../contracts/index.ts';
2
+ import { type ProbeJournal } from './review-probe.ts';
2
3
  type ReviewInspection = NonNullable<EvidenceRecord['inspection']>;
4
+ export type InspectionContext = NonNullable<ReviewInspection['context']>;
5
+ /** Пределы итога: число команд и длины сохраняемых строк (docs/review-probes.md). */
6
+ export declare const inspectionLimits: {
7
+ readonly commands: 100;
8
+ readonly command: 12000;
9
+ readonly output: 4000;
10
+ readonly note: 2000;
11
+ readonly excluded: 100;
12
+ };
3
13
  export declare const unobservedReview: () => ReviewInspection;
4
- /** Что reviewer действительно выполнил — по событиям runtime, а не по словам модели. */
5
- export declare function inspectReview(runtime: string, output: string): ReviewInspection;
14
+ /**
15
+ * Что reviewer действительно выполнил — по событиям runtime, а не по словам
16
+ * модели. Завершённая команда — выполнение при любом коде выхода: ненулевой
17
+ * код — исход проверки, а не признак того, что её не было. Повтор события
18
+ * того же item ID — одна команда.
19
+ */
20
+ export declare function inspectReview(runtime: string, output: string, redact?: (text: string) => string): ReviewInspection;
21
+ /**
22
+ * Итог `inspection` одного вызова reviewer: события runtime этого вызова и
23
+ * журнал проверок controller этого вызова. Самоотчёт модели (`execution`,
24
+ * `reproduction.executed`, runId в тексте) сюда не входит.
25
+ *
26
+ * Операция одна, если совпадает надёжная идентичность: runId запуска
27
+ * controller, item ID runtime внутри его потока или явная структурированная
28
+ * связь наблюдения runtime с runId controller (origin `controller` у команды
29
+ * runtime). Совпадение текста, вывода или замаскированных аргументов
30
+ * операции не склеивает.
31
+ *
32
+ * Порядок: команды runtime в порядке потока, затем запуски controller в
33
+ * порядке журнала; при превышении предела сохраняются последние
34
+ * {@link inspectionLimits.commands}, `total` — число всех подтверждённых.
35
+ * Mode — по всему набору. Пользовательский текст маскируется до обрезки;
36
+ * перечисления и идентификаторы controller не маскируются.
37
+ */
38
+ export declare function reviewInspection(input: {
39
+ /** Наблюдение runtime (ответ адаптера); нет — runtime его не дал. */
40
+ runtime?: ReviewInspection;
41
+ /** Журнал проверок controller этого вызова или сохранённая проекция `review.json.probes`. */
42
+ journal?: ProbeJournal;
43
+ context: InspectionContext;
44
+ redact?: (text: string) => string;
45
+ }): ReviewInspection;
46
+ /**
47
+ * Маскирование сохранённого inspection: только свободный текст (`command`,
48
+ * `output`, `note`, `reason`) и с обрезкой до пределов после маски — замена
49
+ * секрета может удлинить строку. Перечисления, origins, контекст и
50
+ * идентификаторы controller не маскируются — короткий секрет, совпавший с
51
+ * их частью, не делает запись невалидной.
52
+ */
53
+ export declare function redactInspection(inspection: ReviewInspection, redact: (text: string) => string): ReviewInspection;
54
+ /**
55
+ * Сохранённая проекция `review.json.probes` (alpha.6 и ранее: без кода, вывода и
56
+ * признака старта) как журнал для {@link reviewInspection}. Пересчёт старого
57
+ * состояния не выполняется: это вход для отдельной агрегации старых данных.
58
+ */
59
+ export declare function journalFromProjection(probes: unknown): ProbeJournal;
6
60
  export {};