devcontour-harness 0.1.0-alpha.16 → 0.1.0-alpha.18
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 +18 -2
- package/docs/architecture.md +71 -3
- package/docs/quickstart.md +1 -1
- package/docs/release-notes/0.1.0-alpha.17.md +86 -0
- package/docs/release-notes/0.1.0-alpha.18.md +48 -0
- package/docs/review-probes.md +9 -3
- package/docs/review-protocol.md +9 -0
- package/docs/upgrade.md +16 -15
- package/lib/agents/cli-adapter.js +14 -10
- package/lib/agents/review-probe.d.ts +9 -0
- package/lib/agents/review-probe.js +23 -1
- package/lib/agents/schemas.d.ts +206 -0
- package/lib/agents/schemas.js +37 -1
- package/lib/agents/usage.d.ts +22 -0
- package/lib/agents/usage.js +112 -6
- package/lib/contracts/cli.d.ts +30 -0
- package/lib/contracts/diagnosis.d.ts +3 -1
- package/lib/contracts/diagnosis.js +2 -0
- package/lib/contracts/events.d.ts +12 -0
- package/lib/contracts/execution.d.ts +81 -5
- package/lib/contracts/execution.js +58 -0
- package/lib/contracts/failure.d.ts +17 -3
- package/lib/contracts/failure.js +4 -2
- package/lib/contracts/published.d.ts +39 -1
- package/lib/contracts/repair-policy.d.ts +7 -0
- package/lib/contracts/repair-policy.js +17 -0
- package/lib/contracts/review.d.ts +12 -0
- package/lib/contracts/review.js +19 -0
- package/lib/control/harness.d.ts +2 -2
- package/lib/control/recovery.d.ts +1 -1
- package/lib/engine/engine.d.ts +41 -14
- package/lib/engine/engine.js +92 -8
- package/lib/engine/failure.d.ts +1 -1
- package/lib/engine/failure.js +1 -1
- package/lib/engine/property-recovery.d.ts +1 -0
- package/lib/engine/property-recovery.js +4 -1
- package/lib/engine/repair.d.ts +26 -0
- package/lib/engine/repair.js +83 -1
- package/lib/engine/runner.d.ts +7 -0
- package/lib/engine/runner.js +179 -31
- package/lib/explain/state.js +4 -2
- package/lib/index.d.ts +4 -2
- package/lib/index.js +4 -2
- package/lib/projections/stop.d.ts +1 -1
- package/lib/projections/stop.js +16 -0
- package/lib/storage/document.d.ts +48 -8
- package/lib/verification/already-satisfied.d.ts +44 -0
- package/lib/verification/already-satisfied.js +49 -0
- package/lib/verification/gate-scope.d.ts +36 -0
- package/lib/verification/gate-scope.js +97 -0
- package/lib/verification/gates-at.d.ts +67 -0
- package/lib/verification/gates-at.js +173 -0
- package/lib/verification/gates.d.ts +5 -0
- package/lib/verification/gates.js +6 -1
- package/package.json +1 -1
- package/schemas/execution-document.schema.json +134 -0
- package/schemas/run-event.schema.json +4 -0
- package/schemas/stop-diagnosis.schema.json +2 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Headless-движок исполнения инженерных поручени
|
|
|
5
5
|
интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
|
|
6
6
|
и доказательствами.
|
|
7
7
|
|
|
8
|
-
> Статус: `0.1.0-alpha.
|
|
8
|
+
> Статус: `0.1.0-alpha.18` — предварительный выпуск (alpha). Договоры
|
|
9
9
|
> `v1alpha1` могут измениться между alpha-версиями; закрепляйте точную версию.
|
|
10
10
|
> Файл → ревью плана → кандидат → интеграция → принятие → результат JSON, с
|
|
11
11
|
> ограниченным восстановлением, повторным использованием кандидата, бюджетами,
|
|
@@ -28,7 +28,7 @@ Headless-движок исполнения инженерных поручени
|
|
|
28
28
|
Из npm — точная версия (воспроизводимо) или последняя alpha по тегу:
|
|
29
29
|
|
|
30
30
|
```sh
|
|
31
|
-
npm install --save-exact devcontour-harness@0.1.0-alpha.
|
|
31
|
+
npm install --save-exact devcontour-harness@0.1.0-alpha.18 # точная версия
|
|
32
32
|
# или: npm install --save-exact devcontour-harness@alpha # текущая alpha
|
|
33
33
|
npx harness --version
|
|
34
34
|
```
|
|
@@ -123,6 +123,22 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
|
|
|
123
123
|
`devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
|
|
124
124
|
Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
|
|
125
125
|
|
|
126
|
+
## Выпуск 0.1.0-alpha.18
|
|
127
|
+
|
|
128
|
+
`runGatesAt` исполняет gates с предпосылками профиля: явный `dependsOn` или,
|
|
129
|
+
в профиле без него, более ранние check gates (установка); общий с попыткой
|
|
130
|
+
порядок `orderedScope`; все провалы — сразу; `dependsOn: []` — без
|
|
131
|
+
предпосылок. Подробно — [заметки выпуска](docs/release-notes/0.1.0-alpha.18.md).
|
|
132
|
+
|
|
133
|
+
## Выпуск 0.1.0-alpha.17
|
|
134
|
+
|
|
135
|
+
Меньше слепых повторов: reviewer называет класс входов, два отказа ревью по
|
|
136
|
+
одному свойству и классу — пробел приёмки с остановкой; отказы окружения не
|
|
137
|
+
тратят попытки (свой предел), штатная остановка возвращает работу в очередь,
|
|
138
|
+
`completed: false` повторяется с черновика; пустой результат принимается
|
|
139
|
+
только с механическим основанием (иначе `needs-lead`), `runGatesAt` проверяет
|
|
140
|
+
gates на закреплённом SHA; стоимость вызова учитывается по всему потоку. Подробно — [заметки выпуска](docs/release-notes/0.1.0-alpha.17.md).
|
|
141
|
+
|
|
126
142
|
## Выпуск 0.1.0-alpha.16
|
|
127
143
|
|
|
128
144
|
Отказ по файлу эталонного набора — только по строковому литералу, без ложных
|
package/docs/architecture.md
CHANGED
|
@@ -185,6 +185,49 @@ runtime-граф пакета.
|
|
|
185
185
|
- **Покрытие gates.** `uncoveredGates` засчитывает только evidence попыток под
|
|
186
186
|
текущим основанием политики: тот же ID gate с другой командой — другая
|
|
187
187
|
проверка.
|
|
188
|
+
- **Пауза по классу отказа.** Отказ класса с `blocksQueue`
|
|
189
|
+
(`provider-auth`, `environment`) в той же транзакции ставит паузу
|
|
190
|
+
`runtime` и добавляет класс в `pauseFailures`: новых выдач нет ни одной
|
|
191
|
+
задаче. Отказ окружения до кандидата (`blocked`) попытку не засчитывает.
|
|
192
|
+
Снимает паузу `clearFailure([класс])` — его вызывает восстановление,
|
|
193
|
+
назначившее повтор (у DevContour — цикл board workflow, той же транзакцией,
|
|
194
|
+
что повтор), — или `resume` человека; выдача возвращается, когда в списке не
|
|
195
|
+
осталось классов. Поэтому разовый отказ окружения под циклом восстановления
|
|
196
|
+
останавливает выдачу только до его следующего шага, а без цикла пауза стоит
|
|
197
|
+
до решения человека. Пауза человека этим не снимается.
|
|
198
|
+
- **Gates на SHA вне попытки.** `runGatesAt` исполняет названные gates
|
|
199
|
+
компонента на закреплённом SHA так же, как integration поручения:
|
|
200
|
+
отдельное рабочее дерево, снимки зависимостей, ресурсы, lifecycle и
|
|
201
|
+
prepare, окружение фазы `integration`, песочница и `runGate`. Область —
|
|
202
|
+
названные gates и предпосылки каждого (`standaloneScope`), порядок — тот же
|
|
203
|
+
`orderedScope`, что у попытки. Предпосылки (`gatePrerequisites`): у gate с
|
|
204
|
+
объявленным `dependsOn` (в том числе пустым — режим графа) — замыкание по
|
|
205
|
+
`dependsOn`; у gate без поля `dependsOn` (профиль в режиме порядка массива)
|
|
206
|
+
— все более ранние gates вида `check` с их предпосылками: установка,
|
|
207
|
+
сборка, статические проверки. Более ранние test gates не добавляются;
|
|
208
|
+
каждый gate посещается один раз. Задаче эти предпосылки называть
|
|
209
|
+
по-прежнему приходится явно: её область исполняется как записана. Провал
|
|
210
|
+
gate не останавливает проверку. Пропускается только gate, чья явная
|
|
211
|
+
предпосылка (`dependsOn`) не прошла; провал неявной (раннего check gate)
|
|
212
|
+
тесты не пропускает — названы оба (`failures`). Evidence (`test`,
|
|
213
|
+
`integration`, этот SHA) пишется через запись хоста до следующего gate;
|
|
214
|
+
дерево обязано остаться на SHA без изменений.
|
|
215
|
+
- **Пустой результат.** Исполнитель, не изменивший ни файла, не получает
|
|
216
|
+
отказ сразу: кандидат — сама база (без коммита), gates поручения идут на
|
|
217
|
+
ней, а reviewer обеих фаз получает схему ответа с обязательным
|
|
218
|
+
`alreadySatisfied` (gates области задачи — хотя бы один test gate и все
|
|
219
|
+
проверки договоров поручения — и основание). Подтверждения reviewer мало:
|
|
220
|
+
нужно механическое основание, которое находит controller
|
|
221
|
+
(`alreadySatisfiedVerdict`): договор проверки поручения, все gates которого
|
|
222
|
+
в области задачи и названы, или зависимость поручения, чей принятый
|
|
223
|
+
результат входит в SHA, а собственный кандидат изменил файлы области записи
|
|
224
|
+
поручения. Без основания — `needs-lead` (остановка, решение ведущего): общий
|
|
225
|
+
зелёный тест на базе новой работы не доказывает. Без годного подтверждения,
|
|
226
|
+
при отклонении ревью или красном gate на базе — `empty-result`. С
|
|
227
|
+
основанием поручение принимается без нового коммита; основание пишется в
|
|
228
|
+
evidence ревью (`alreadySatisfied.basis`), и `engine.finish` повторяет
|
|
229
|
+
правило целиком по записанному, сверяя основание по зависимости с её
|
|
230
|
+
принятой попыткой.
|
|
188
231
|
- **Область записи.** Изменения вне `writeScope`, защищённых путей и
|
|
189
232
|
закреплённого контекста отклоняют попытку до интеграции.
|
|
190
233
|
- **Gates.** Тестовый gate принимается по свежему непустому отчёту без
|
|
@@ -217,11 +260,32 @@ runtime-граф пакета.
|
|
|
217
260
|
исполнение, завершено) сохраняется и переживает рестарт; владелец задания
|
|
218
261
|
fenced. Упавшее поручение повторяется только по записанному классу отказа:
|
|
219
262
|
в пределах повторов одинакового отпечатка для класса, попыток поручения
|
|
220
|
-
(`maxAttempts`) и бюджета восстановлений задания (`repairBudget`).
|
|
263
|
+
(`maxAttempts`) и бюджета восстановлений задания (`repairBudget`). Отказ
|
|
264
|
+
окружения или провайдера (и истёкшее владение) попытку поручения не
|
|
265
|
+
засчитывает, даже после кандидата: о работе он ничего не говорит; повторы
|
|
266
|
+
таких отказов ограничены паузой выдачи, пределом одинаковых отпечатков,
|
|
267
|
+
`repairBudget` и своим пределом на поручение (`MAX_NEUTRAL_FAILURES` = 3,
|
|
268
|
+
счётчик `neutralFailures`): на нём `retry` без сброса, выдача и
|
|
269
|
+
восстановление отказывают; сброс человека обнуляет счётчик. Попытка, прерванная штатной остановкой (`Runner.stop`),
|
|
270
|
+
не падает: черновик сохраняется, попытка помечается `interrupted:
|
|
271
|
+
shutdown`, поручение возвращается в очередь (`ready`) без траты попытки и
|
|
272
|
+
без паузы по классу; после снятия паузы остановки она продолжается с
|
|
273
|
+
кандидата или черновика. Интегрированная попытка так не возвращается.
|
|
274
|
+
Решение и
|
|
221
275
|
списание бюджета — одна транзакция с повтором. Отказ провайдера, отмена,
|
|
222
276
|
неизвестный класс и устаревшая база — остановка с причиной. Восстановление
|
|
223
277
|
снимает только свою причину паузы; паузу человека не снимает. Режим
|
|
224
|
-
`operator` автоматических повторов не делает.
|
|
278
|
+
`operator` автоматических повторов не делает. Тексты отказов ревью от
|
|
279
|
+
попытки к попытке разные, поэтому сверх отпечатка считается и названное:
|
|
280
|
+
два отказа ревью подряд по одному необъявленному свойству с тем же классом
|
|
281
|
+
входов (`classified`; класс сравнивается без регистра и лишних пробелов) —
|
|
282
|
+
пробел приёмки (`acceptanceGap`), остановка с `gap` и причиной для ведущего
|
|
283
|
+
вместо третьей слепой попытки. Совпадение только свойства или только класса
|
|
284
|
+
(`unboundProperties`, `inputClasses`) — слабее: общий класс бывает у разных
|
|
285
|
+
дефектов, поэтому нужно три отказа подряд.
|
|
286
|
+
Счёт — как у эпизода свойства: отказ окружения его не меняет, отказ gate,
|
|
287
|
+
другое название или новое основание поручения обрывают серию. Объявленные
|
|
288
|
+
свойства ведёт эпизод разбора.
|
|
225
289
|
- **Переиспользование кандидата.** После отказа окружения или по времени,
|
|
226
290
|
когда кандидат уже был, повтор берёт его вместо новой реализации, только
|
|
227
291
|
если совпадают все основания: постановка, контракты, политика, runtime и
|
|
@@ -398,7 +462,11 @@ runtime-граф пакета.
|
|
|
398
462
|
reviewer — получает последние 20 входов с ожиданием, наблюдением и командой.
|
|
399
463
|
Это диагностика: gate не расширяет и корпусом не становится без
|
|
400
464
|
подтверждения эталоном; обязательный корпус от ограничения реестра не
|
|
401
|
-
зависит.
|
|
465
|
+
зависит. Блокирующая находка со входом называет и нарушенное свойство или
|
|
466
|
+
критерий (`property`), и класс входов (`inputClass`) — генератор или
|
|
467
|
+
семейство, к которому относится пример. Класс записывается в реестр и на
|
|
468
|
+
отклонённую попытку (`inputClasses`); следующий writer получает задачу
|
|
469
|
+
чинить класс, а не экземпляр.
|
|
402
470
|
- **Контекст плана и согласование.** Снимок контекста хранится по digest, а
|
|
403
471
|
действующим становится только вместе с согласованием графа
|
|
404
472
|
(`approval.context`). Пока новый снимок на ревью, после его отклонения,
|
package/docs/quickstart.md
CHANGED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# devcontour-harness 0.1.0-alpha.17
|
|
2
|
+
|
|
3
|
+
Меньше слепых повторов и ручных сбросов. Включает всё из
|
|
4
|
+
[0.1.0-alpha.16](0.1.0-alpha.16.md). Gates, правила находок, изоляция,
|
|
5
|
+
принятие target ref и `maxAttempts` не менялись; формат состояния получил
|
|
6
|
+
необязательные поля (ниже, «Совместимость»).
|
|
7
|
+
|
|
8
|
+
## Что нового
|
|
9
|
+
|
|
10
|
+
- **Класс входов в находке ревью.** Блокирующая находка со входом называет
|
|
11
|
+
нарушенное свойство или критерий (`reproduction.property`) и класс входов
|
|
12
|
+
(`reproduction.inputClass`) — генератор или семейство, а не экземпляр. Поле
|
|
13
|
+
обязательно в схеме ответа runtime; ответ прежней формы без него
|
|
14
|
+
разбирается. Класс пишется в реестр воспроизведений и на отклонённую
|
|
15
|
+
попытку (`inputClasses`, пары `classified`); следующий writer получает
|
|
16
|
+
задачу чинить класс.
|
|
17
|
+
- **Пробел приёмки.** `repairDecision` останавливает поручение с полем `gap`
|
|
18
|
+
и причиной для ведущего, когда два отказа ревью подряд называют одно и то
|
|
19
|
+
же необъявленное свойство с тем же классом входов (класс без регистра и
|
|
20
|
+
лишних пробелов). Совпадение только свойства или только класса требует
|
|
21
|
+
трёх отказов подряд: общий класс бывает у разных дефектов. Отказ
|
|
22
|
+
окружения серию не меняет; отказ gate, другое название и новое основание
|
|
23
|
+
поручения её обрывают. `explain` показывает то же. Объявленные свойства
|
|
24
|
+
по-прежнему ведёт эпизод разбора.
|
|
25
|
+
- **Отказы окружения не тратят попытки.** Отказ окружения или провайдера и
|
|
26
|
+
истёкшее владение не засчитываются в `maxAttempts`, и после кандидата.
|
|
27
|
+
Предел у них свой: `MAX_NEUTRAL_FAILURES` = 3 на поручение (счётчик
|
|
28
|
+
`neutralFailures`); на нём `retry` без сброса, выдача и восстановление
|
|
29
|
+
отказывают, сброс человека обнуляет счётчик.
|
|
30
|
+
- **Штатная остановка не роняет работу.** `Runner.stop` помечает свои
|
|
31
|
+
попытки: прерванная сохраняет черновик, получает `interrupted: shutdown`,
|
|
32
|
+
а поручение возвращается в очередь (`ready`) без траты попытки и без паузы
|
|
33
|
+
по классу отказа; после снятия паузы остановки следующая попытка
|
|
34
|
+
продолжает с кандидата или черновика. Интегрированная попытка так не
|
|
35
|
+
возвращается: это обычный отказ, хост получает `hooks.failed`.
|
|
36
|
+
- **Класс отказа `unfinished`.** `completed: false` исполнителя был
|
|
37
|
+
`unknown` и останавливал работу без повтора, с сохранённым черновиком.
|
|
38
|
+
Теперь это `unfinished`: повтор (предел одинаковых — 2) продолжает с
|
|
39
|
+
черновика (`continuedFrom.kind: unfinished`) в пределах `maxAttempts` и
|
|
40
|
+
`repairBudget`; для эпизода свойства отказ нейтрален.
|
|
41
|
+
- **Пустой результат только с механическим основанием.** Пустая реализация
|
|
42
|
+
больше не отклоняется сразу: кандидат — сама база, gates задачи идут на
|
|
43
|
+
ней, оба ревью отвечают схемой с обязательным `alreadySatisfied` (gates
|
|
44
|
+
области задачи — хотя бы один test gate и все gates договоров проверки
|
|
45
|
+
поручения — и основание). Принятие требует ещё механического основания,
|
|
46
|
+
которое находит controller: договор проверки поручения со всеми gates в
|
|
47
|
+
области задачи, или зависимость, чей принятый результат входит в
|
|
48
|
+
проверяемый SHA и чей кандидат менял файлы области записи поручения.
|
|
49
|
+
Основание пишется в evidence ревью (`alreadySatisfied.basis`). Без него —
|
|
50
|
+
новый класс отказа `needs-lead`: остановка без повтора, решение ведущего.
|
|
51
|
+
`engine.finish` повторяет правило целиком по записанным данным.
|
|
52
|
+
- **`runGatesAt`.** Именованные gates компонента (с предпосылками) на
|
|
53
|
+
закреплённом SHA вне попытки — тем же путём, что интеграция задачи: свой
|
|
54
|
+
worktree, снимки зависимостей, ресурсы, lifecycle и prepare, окружение
|
|
55
|
+
интеграции, песочница проверок; каждый evidence — через callback хоста,
|
|
56
|
+
первый провал останавливает прогон, дерево должно остаться прежним.
|
|
57
|
+
- **Учёт стоимости по всему потоку.** Адаптер CLI разбирает stdout по
|
|
58
|
+
строкам по мере поступления и хранит только поля телеметрии (последний
|
|
59
|
+
`result` claude, `turn.completed` codex, первая названная модель): цена
|
|
60
|
+
длинного вызова больше не теряется за пределом хвоста вывода. Разбор
|
|
61
|
+
ограничен по памяти и не бросает исключений из обработчика вывода.
|
|
62
|
+
- **Отчёт свойств в пробах ревьюера.** Проба gate получает путь отчёта
|
|
63
|
+
свойств в scratch controller (`HARNESS_PROPERTY_REPORT`,
|
|
64
|
+
`DEVCONTOUR_PROPERTY_REPORT` — та же переменная, что у движка) и сохраняет
|
|
65
|
+
копию отчёта как evidence пробы; отчёт-ссылка отклоняется.
|
|
66
|
+
|
|
67
|
+
## Совместимость
|
|
68
|
+
|
|
69
|
+
Новые необязательные поля попытки (`interrupted`, `inputClasses`,
|
|
70
|
+
`classified`), поручения (`neutralFailures`), записи воспроизведения
|
|
71
|
+
(`inputClass`), evidence ревью (`alreadySatisfied`) и классы отказа
|
|
72
|
+
`unfinished` и `needs-lead`; в схемах данных CLI это единственные
|
|
73
|
+
аддитивные изменения перечней. Прежние записи не меняются.
|
|
74
|
+
Строгие схемы прежних версий новых значений не знают: после первой записи
|
|
75
|
+
alpha.17 откат — только через копию. Изменение поведения: тесты хоста,
|
|
76
|
+
ждавшие засчитанную попытку после отказа окружения или `failed` после
|
|
77
|
+
штатной остановки, нужно обновить. Матрица — [upgrade](../upgrade.md).
|
|
78
|
+
|
|
79
|
+
## Проверка выпуска
|
|
80
|
+
|
|
81
|
+
`npm run check` на macOS arm64, Node.js 24.3. Новые тесты на fixture runtime
|
|
82
|
+
и реальных Git-фикстурах (`repair`, `reproductions`, `runner` T14,
|
|
83
|
+
`explain-engine`, `network-recovery`, `reuse`, `usage`, `probe-gates`,
|
|
84
|
+
`already-satisfied`, `gates-at`); каждый новый тест обнаруживает свой мутант
|
|
85
|
+
реализации. Закрепление схем данных CLI (H3-01) сверяет байты H2 без
|
|
86
|
+
аддитивных значений `unfinished` и `needs-lead`. Живые модели не вызывались.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# devcontour-harness 0.1.0-alpha.18
|
|
2
|
+
|
|
3
|
+
Проверка gates на закреплённом SHA исполняет их с теми предпосылками, на
|
|
4
|
+
которые опирается профиль. Включает всё из [0.1.0-alpha.17](0.1.0-alpha.17.md).
|
|
5
|
+
Формат состояния, gates попыток, правила находок, изоляция, принятие target
|
|
6
|
+
ref и `maxAttempts` не менялись.
|
|
7
|
+
|
|
8
|
+
## Что нового
|
|
9
|
+
|
|
10
|
+
- **Предпосылки проверки вне задачи.** `runGatesAt` брал только явные
|
|
11
|
+
`dependsOn`. В профиле, который опирается на порядок массива (установка
|
|
12
|
+
первой, без `dependsOn`), проверка одного test gate шла без установки и
|
|
13
|
+
падала на отсутствующих модулях, хотя задачи проходили: их область называла
|
|
14
|
+
установку явно. Теперь область проверки — названные gates и предпосылки
|
|
15
|
+
каждого (`standaloneScope`), порядок — тот же `orderedScope`, что у попытки
|
|
16
|
+
(прежний внутренний `ordered` runner, без изменения поведения).
|
|
17
|
+
Предпосылки (`gatePrerequisites`): у gate с объявленным `dependsOn` —
|
|
18
|
+
замыкание по `dependsOn`; у gate без поля `dependsOn` — все более ранние
|
|
19
|
+
gates вида `check` со своими предпосылками. Более ранние test gates не
|
|
20
|
+
добавляются. Каждый gate посещается один раз, поэтому `dependsOn` на более
|
|
21
|
+
поздний gate вместе с неявным ребром не зацикливается. Область задачи
|
|
22
|
+
по-прежнему исполняется как записана.
|
|
23
|
+
- **Явный `dependsOn: []` — без предпосылок.** Объявленный пустой
|
|
24
|
+
`dependsOn` — режим графа: неявных предпосылок нет. Порядок массива
|
|
25
|
+
действует только для gate без поля.
|
|
26
|
+
- **Все провалы сразу.** Провал gate больше не останавливает проверку:
|
|
27
|
+
исполняются все названные gates, результат перечисляет каждый непрошедший
|
|
28
|
+
(`failures`; `failure` — первый, как раньше). Пропускается только gate,
|
|
29
|
+
чья явная предпосылка (`dependsOn`, `explicitPrerequisites`) не прошла, — он
|
|
30
|
+
назван с этой причиной. Провал неявной предпосылки (раннего check gate) не
|
|
31
|
+
скрывает исход тестов: исполняются и названы оба. Evidence по-прежнему
|
|
32
|
+
пишется через запись хоста и привязано к SHA.
|
|
33
|
+
|
|
34
|
+
## Совместимость
|
|
35
|
+
|
|
36
|
+
Формат состояния не меняется. `GatesAtResult` получает поле `failures`;
|
|
37
|
+
хост, ждавший остановки на первом провале, получает теперь все исполненные
|
|
38
|
+
gates. Новые экспорты: `orderedScope`, `gatePrerequisites`,
|
|
39
|
+
`explicitPrerequisites`, `standaloneScope`. Матрица — [upgrade](../upgrade.md).
|
|
40
|
+
|
|
41
|
+
## Проверка выпуска
|
|
42
|
+
|
|
43
|
+
Затронутые тесты на реальных Git-фикстурах (`gates-at`: профиль в порядке
|
|
44
|
+
массива с установкой первой и без `dependsOn`, петля через неявное ребро,
|
|
45
|
+
пустой `dependsOn`, цепочки двух и трёх уровней, смешанная цепочка, провал
|
|
46
|
+
неявной предпосылки; `runner` T01, T09); каждый новый тест обнаруживает свой
|
|
47
|
+
мутант реализации. Полный `npm run check` перед публикацией выполняет
|
|
48
|
+
ведущий. Живые модели не вызывались.
|
package/docs/review-probes.md
CHANGED
|
@@ -29,13 +29,19 @@ To run a declared gate, call run_check with exactly {"gate":"domain"}, {"gate":"
|
|
|
29
29
|
3. Если путь отчёта зашит в argv (см. ниже) — не запускает, возвращает ошибку среды с рецептом.
|
|
30
30
|
4. Создаёт каталог evidence `<каталог ревью>/probe-runs/<runId>/` (вне дерева, без перезаписи) и
|
|
31
31
|
уникальный scratch `dc-probe-report-*` во временном каталоге системы.
|
|
32
|
-
5. Выставляет `HARNESS_REPORT_PATH` и `DEVCONTOUR_REPORT_PATH` = `<scratch>/<id>.xml`,
|
|
33
|
-
|
|
32
|
+
5. Выставляет `HARNESS_REPORT_PATH` и `DEVCONTOUR_REPORT_PATH` = `<scratch>/<id>.xml`,
|
|
33
|
+
`DEVCONTOUR_PROPERTY_REPORT` = `<scratch>/<id>.property.json` (то же имя, что у gate движка),
|
|
34
|
+
`CI=1`; у `test` gate ещё `TURBO_FORCE=true` и `GOFLAGS=… -count=1`, как у gate движка.
|
|
35
|
+
Проверка, которая пишет отчёт свойств по пути из переменной, под probe пишет его в scratch, а
|
|
36
|
+
не получает EPERM в дереве только для чтения. `DEVCONTOUR_SEED`, `DEVCONTOUR_PROPERTIES` и
|
|
37
|
+
`DEVCONTOUR_CORPUS` probe не выставляет: генеративные gate он не исполняет.
|
|
34
38
|
6. Запускает команду в песочнице: запись — только в scratch и `TMPDIR`; дерево, `.git`,
|
|
35
39
|
каталог controller и прежние `.reports/*.xml` закрыты на запись.
|
|
36
40
|
7. Читает отчёт: symlink, жёсткая ссылка, путь вне scratch — `unsafe` (не читается); файл старше
|
|
37
41
|
запуска — `stale`; не JUnit или без testcases — `corrupt`; нет файла — `missing`.
|
|
38
|
-
8. Сохраняет замаскированную копию отчёта (`<id>.xml`),
|
|
42
|
+
8. Сохраняет замаскированную копию отчёта (`<id>.xml`), отчёта свойств, если он записан
|
|
43
|
+
(`<id>.property.json`, `gate.propertyReport` = `{path, scope: "probe-only"}`: движок его не
|
|
44
|
+
читает), журнал (`<id>.log`) и `run.json`, затем
|
|
39
45
|
удаляет scratch. Таймаут и отмена (`notifications/cancelled`) останавливают процессы проверки,
|
|
40
46
|
evidence сохраняется тоже.
|
|
41
47
|
|
package/docs/review-protocol.md
CHANGED
|
@@ -121,6 +121,15 @@ DevContour хранит `Evidence` как есть; все новые поля
|
|
|
121
121
|
логического ревью). Непринятый результат содержит и записи невалидных
|
|
122
122
|
вызовов (`review.protocol: invalid` с их причинами).
|
|
123
123
|
|
|
124
|
+
- `alreadySatisfied` — у записи ревью попытки без изменений (кандидат —
|
|
125
|
+
база): gates и основание, которыми reviewer подтвердил, что приёмка уже
|
|
126
|
+
выполнена. Поле ответа reviewer `alreadySatisfied` (`null` — нет
|
|
127
|
+
подтверждения) входит в схему только для такой попытки; обычное ревью
|
|
128
|
+
отвечает прежней схемой. Одобрение без годного подтверждения записывается
|
|
129
|
+
непрошедшим, отказ попытки — `empty-result`; без механического основания
|
|
130
|
+
(`basis`: договор проверки или зависимость, изменившая область записи) —
|
|
131
|
+
`needs-lead`.
|
|
132
|
+
|
|
124
133
|
## 6. Записи alpha.5 (legacy)
|
|
125
134
|
|
|
126
135
|
Запись ревью без `reviewRound` — запись прежней версии. Её находки (у
|
package/docs/upgrade.md
CHANGED
|
@@ -21,21 +21,22 @@
|
|
|
21
21
|
|
|
22
22
|
## Матрица форматов
|
|
23
23
|
|
|
24
|
-
| Состояние, записанное…
|
|
25
|
-
|
|
|
26
|
-
| standalone `stateVersion: 1` версией alpha.1
|
|
27
|
-
| хост, центральная база
|
|
28
|
-
| хост, базы компонентов
|
|
29
|
-
| alpha.2 после штатной остановки controller
|
|
30
|
-
| alpha.2 с живой или брошенной ролью controller
|
|
31
|
-
| alpha.4 с совместной проверкой
|
|
32
|
-
| alpha.6 standalone с записями ревью нового протокола или доставкой `uncertain`
|
|
33
|
-
| alpha.6, хост DevContour (адаптер alpha.4)
|
|
34
|
-
| alpha.7 с новым `inspection` ревью кода
|
|
35
|
-
| alpha.14 с договором проверки `reference` или `gates` обязательства
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
| Состояние, записанное… | Читает 0.1.0-alpha.1 | Читает 0.1.0-alpha.2 | Читает 0.1.0-alpha.6 | Читает 0.1.0-alpha.7 | Читает 0.1.0-alpha.8 | Читает 0.1.0-alpha.9 | Читает 0.1.0-alpha.10 | Читает 0.1.0-alpha.11 | Читает 0.1.0-alpha.12 | Читает 0.1.0-alpha.13 | Читает 0.1.0-alpha.14 | Читает 0.1.0-alpha.15 | Читает 0.1.0-alpha.16 | Читает 0.1.0-alpha.17 | Читает 0.1.0-alpha.18 | Пишет поверх | Примечание |
|
|
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
|
+
| alpha.14 с договором проверки `reference` или `gates` обязательства | не проверялось | не проверялось | не проверялось | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | да | да | да | да | да | — | контракт хранится строгой схемой (`contract-documents`): прежние версии новых полей `gates`, `verification`, `reference` не знают → откат через копию |
|
|
36
|
+
| alpha.17 с `interrupted`, `inputClasses`, `classified`, `neutralFailures`, `alreadySatisfied` в evidence ревью или классом отказа `unfinished`/`needs-lead` | не проверялось | не проверялось | не проверялось | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | **нет** (строгая схема) | да | да | — | попытка (`interrupted`, `inputClasses`, `classified`), поручение (`neutralFailures`), `alreadySatisfied` evidence ревью и классы отказа `unfinished`, `needs-lead` — новые значения строгих схем; прежние версии их отклоняют → откат через копию |
|
|
37
|
+
| неизвестная `stateVersion` | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет, отказ до записи | нет | `STATE_VERSION_UNSUPPORTED`, exit 4 |
|
|
38
|
+
|
|
39
|
+
Столбец alpha.2 относится и к alpha.3; alpha.4 и alpha.5 читают все строки, кроме неизвестной `stateVersion` и строк alpha.6 с новыми полями (standalone — отказ до записи) и строки alpha.7. alpha.8, alpha.9, alpha.10, alpha.11, alpha.12 и alpha.13 формат состояния не меняют и читают всё, что читает alpha.7; alpha.14 и alpha.15 (локальные, не опубликованы) и alpha.16 читают всё, что читает alpha.13, и добавляют необязательные поля договора проверки (строка alpha.14). alpha.17 читает всё, что читает alpha.16, и добавляет необязательные поля попытки, поручения и evidence ревью и классы отказа `unfinished` и `needs-lead` (строка alpha.17); alpha.18 формат состояния не меняет и читает всё, что читает alpha.17. Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
|
|
39
40
|
формата до записи проверяют тесты совместимости пакета
|
|
40
41
|
<!-- repo-only -->(`tests/compatibility.test.ts`)<!-- /repo-only -->.
|
|
41
42
|
|
|
@@ -8,7 +8,7 @@ import { redactor } from "../environment/redaction.js";
|
|
|
8
8
|
import { inspectReview } from "./inspect.js";
|
|
9
9
|
import { probeCallMs, probeServer, probeTool } from "./review-probe.js";
|
|
10
10
|
import { claudeResult, runtimeEvents } from "./runtime-events.js";
|
|
11
|
-
import {
|
|
11
|
+
import { runtimeVersion, unknownUsage, usageStream } from "./usage.js";
|
|
12
12
|
/** Встроенные инструменты исполнителя claude, когда профиль их не называет; shell — в песочнице ОС. */
|
|
13
13
|
export const claudeWriterTools = ['Read', 'Glob', 'Grep', 'Edit', 'Write', 'Bash'];
|
|
14
14
|
const writerTools = (profile) => profile?.claudeTools ?? claudeWriterTools;
|
|
@@ -283,12 +283,17 @@ export function cliAdapter(runtime, options = {}) {
|
|
|
283
283
|
// короткий секрет портит `item.completed`), текст полей маскируется
|
|
284
284
|
// при разборе; сырой поток только в памяти и не сохраняется.
|
|
285
285
|
let raw = '';
|
|
286
|
+
// Телеметрия — по всему потоку, независимо от пределов журнала и
|
|
287
|
+
// хвоста stdout: итоговое событие долгого вызова лежит за ними.
|
|
288
|
+
const telemetry = usageStream(runtime);
|
|
286
289
|
try {
|
|
287
290
|
result = await command(argv, r.cwd, {
|
|
288
291
|
contain: { dirs: review ? [] : r.sandbox.writable },
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
+
onRawStdout: (text) => {
|
|
293
|
+
telemetry.push(text);
|
|
294
|
+
if (review)
|
|
295
|
+
raw = (raw + text).slice(-2_000_000);
|
|
296
|
+
},
|
|
292
297
|
onOutput: (stream, text) => {
|
|
293
298
|
if (logged >= 10_000_000)
|
|
294
299
|
return;
|
|
@@ -312,12 +317,11 @@ export function cliAdapter(runtime, options = {}) {
|
|
|
312
317
|
return unavailable(`${runtime}: runtime не запускается: ${String(error)}`);
|
|
313
318
|
throw error;
|
|
314
319
|
}
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const model = reportedModel(result.stdout);
|
|
320
|
+
const usage = telemetry.usage(result.code === 0 && !result.timedOut && !signal.aborted);
|
|
321
|
+
const model = telemetry.model();
|
|
322
|
+
// Сообщённые runtime счётчики и стоимость — и в диагностике вызова:
|
|
323
|
+
// runtime.log ограничен и итоговой строки может не содержать.
|
|
324
|
+
await writeFile(join(dir, 'runtime.json'), JSON.stringify({ ...result.diagnostics, usage }, null, 2));
|
|
321
325
|
const bookkeeping = ['system', 'result', 'error', 'session.created', 'thread.started'];
|
|
322
326
|
const started = result.timedOut ||
|
|
323
327
|
runtimeEvents(result.stdout).events.some((e) => !bookkeeping.includes(String(e.type)));
|
|
@@ -118,6 +118,15 @@ export type ProbeGateRun = {
|
|
|
118
118
|
/** Имена переменных, которые выставил controller (значения не пишутся). */
|
|
119
119
|
controllerEnv: string[];
|
|
120
120
|
report?: ProbeReport;
|
|
121
|
+
/**
|
|
122
|
+
* Копия отчёта свойств, если проверка записала его по пути
|
|
123
|
+
* `DEVCONTOUR_PROPERTY_REPORT` в scratch. Только evidence probe: движок
|
|
124
|
+
* этот отчёт не читает, и свойства по нему не засчитываются.
|
|
125
|
+
*/
|
|
126
|
+
propertyReport?: {
|
|
127
|
+
path: string;
|
|
128
|
+
scope: 'probe-only';
|
|
129
|
+
};
|
|
121
130
|
/** Каталог evidence запуска в каталоге ревью controller. */
|
|
122
131
|
artifacts?: string;
|
|
123
132
|
/** Временный каталог отчёта; убран после сохранения evidence. */
|
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import { idSchema, relativePathSchema, sha256, } from "../contracts/index.js";
|
|
11
11
|
import { redactor } from "../environment/redaction.js";
|
|
12
|
-
import { completedCheck, freshnessEnv, junitSummary, runCheck, sandboxBlockedHosts, } from "../verification/gates.js";
|
|
12
|
+
import { completedCheck, freshnessEnv, propertyReportEnv, junitSummary, runCheck, sandboxBlockedHosts, } from "../verification/gates.js";
|
|
13
13
|
/**
|
|
14
14
|
* Пробные проверки reviewer под пределами harness.
|
|
15
15
|
*
|
|
@@ -112,6 +112,21 @@ export function fixedReportArgument(argv, cwd, report) {
|
|
|
112
112
|
}
|
|
113
113
|
return undefined;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Копия отчёта свойств из scratch в evidence probe. Как и JUnit: symlink,
|
|
117
|
+
* жёсткая ссылка, файл вне scratch и слишком большой файл не читаются.
|
|
118
|
+
*/
|
|
119
|
+
async function savePropertyReport(path, scratch, runDir, id, redact) {
|
|
120
|
+
const stat = await lstat(path).catch(() => undefined);
|
|
121
|
+
if (!stat?.isFile() || stat.nlink > 1 || stat.size > REPORT_LIMIT)
|
|
122
|
+
return undefined;
|
|
123
|
+
const real = await realpath(path);
|
|
124
|
+
if (!within(dirname(real), scratch))
|
|
125
|
+
return undefined;
|
|
126
|
+
const saved = join(runDir, `${id}.property.json`);
|
|
127
|
+
await writeFile(saved, redact(await readFile(real, 'utf8')), { flag: 'wx' });
|
|
128
|
+
return { path: saved, scope: 'probe-only' };
|
|
129
|
+
}
|
|
115
130
|
/**
|
|
116
131
|
* Отчёт запуска gate в scratch controller. Symlink, жёсткая ссылка и путь вне
|
|
117
132
|
* scratch не читаются: так отчёт подменялся бы чужим файлом — прежним
|
|
@@ -458,12 +473,18 @@ export class ReviewProbes {
|
|
|
458
473
|
}
|
|
459
474
|
meta.scratch = scratch;
|
|
460
475
|
const reportPath = join(scratch, `${gate.id}.xml`);
|
|
476
|
+
// Отчёт свойств — тоже в scratch, под тем же именем переменной, что и у
|
|
477
|
+
// gate движка (`propertyReportEnv`): проверка, пишущая отчёт свойств по
|
|
478
|
+
// прежнему пути в дереве, под probe получала бы EPERM. Seed, селектор и
|
|
479
|
+
// корпус probe не выставляет: генеративные gate он не исполняет.
|
|
480
|
+
const propertyPath = join(scratch, `${gate.id}.property.json`);
|
|
461
481
|
const controllerEnv = {
|
|
462
482
|
CI: '1',
|
|
463
483
|
...freshnessEnv(gate.kind, env),
|
|
464
484
|
...(gate.report
|
|
465
485
|
? { DEVCONTOUR_REPORT_PATH: reportPath, HARNESS_REPORT_PATH: reportPath }
|
|
466
486
|
: {}),
|
|
487
|
+
[propertyReportEnv]: propertyPath,
|
|
467
488
|
};
|
|
468
489
|
meta.controllerEnv = Object.keys(controllerEnv);
|
|
469
490
|
const started = Date.now();
|
|
@@ -493,6 +514,7 @@ export class ReviewProbes {
|
|
|
493
514
|
const read = gate.report
|
|
494
515
|
? await inspectReport(reportPath, scratch, started, redact)
|
|
495
516
|
: undefined;
|
|
517
|
+
meta.propertyReport = await savePropertyReport(propertyPath, scratch, runDir, gate.id, redact);
|
|
496
518
|
if (read) {
|
|
497
519
|
meta.report = read.report;
|
|
498
520
|
if (read.xml !== undefined) {
|