devcontour-harness 0.1.0-alpha.16 → 0.1.0-alpha.19

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.
Files changed (85) hide show
  1. package/README.md +28 -2
  2. package/docs/architecture.md +119 -3
  3. package/docs/contracts.md +3 -0
  4. package/docs/quickstart.md +1 -1
  5. package/docs/release-notes/0.1.0-alpha.17.md +86 -0
  6. package/docs/release-notes/0.1.0-alpha.18.md +48 -0
  7. package/docs/release-notes/0.1.0-alpha.19.md +96 -0
  8. package/docs/review-probes.md +9 -3
  9. package/docs/review-protocol.md +9 -0
  10. package/docs/upgrade.md +17 -15
  11. package/lib/agents/cli-adapter.js +14 -10
  12. package/lib/agents/review-probe.d.ts +9 -0
  13. package/lib/agents/review-probe.js +23 -1
  14. package/lib/agents/schemas.d.ts +206 -0
  15. package/lib/agents/schemas.js +37 -1
  16. package/lib/agents/usage.d.ts +22 -0
  17. package/lib/agents/usage.js +112 -6
  18. package/lib/cli.js +7 -1
  19. package/lib/contracts/cli.d.ts +60 -2
  20. package/lib/contracts/diagnosis.d.ts +3 -1
  21. package/lib/contracts/diagnosis.js +2 -0
  22. package/lib/contracts/events.d.ts +33 -0
  23. package/lib/contracts/events.js +7 -0
  24. package/lib/contracts/execution.d.ts +105 -8
  25. package/lib/contracts/execution.js +71 -1
  26. package/lib/contracts/failure.d.ts +17 -3
  27. package/lib/contracts/failure.js +4 -2
  28. package/lib/contracts/planning.js +2 -2
  29. package/lib/contracts/policy.d.ts +1 -1
  30. package/lib/contracts/ports.d.ts +6 -0
  31. package/lib/contracts/primitives.d.ts +13 -0
  32. package/lib/contracts/primitives.js +15 -0
  33. package/lib/contracts/profiles.js +2 -2
  34. package/lib/contracts/published.d.ts +62 -3
  35. package/lib/contracts/repair-policy.d.ts +7 -0
  36. package/lib/contracts/repair-policy.js +17 -0
  37. package/lib/contracts/review.d.ts +14 -2
  38. package/lib/contracts/review.js +20 -1
  39. package/lib/contracts/work.js +3 -3
  40. package/lib/control/base-move.js +2 -2
  41. package/lib/control/harness.d.ts +3 -3
  42. package/lib/control/harness.js +7 -2
  43. package/lib/control/recovery.d.ts +1 -1
  44. package/lib/control/workflow.js +6 -0
  45. package/lib/engine/budget.d.ts +1 -1
  46. package/lib/engine/engine.d.ts +53 -14
  47. package/lib/engine/engine.js +132 -9
  48. package/lib/engine/failure.d.ts +1 -1
  49. package/lib/engine/failure.js +1 -1
  50. package/lib/engine/property-recovery.d.ts +1 -0
  51. package/lib/engine/property-recovery.js +4 -1
  52. package/lib/engine/repair.d.ts +26 -0
  53. package/lib/engine/repair.js +83 -1
  54. package/lib/engine/runner.d.ts +18 -1
  55. package/lib/engine/runner.js +261 -42
  56. package/lib/environment/git-trust.d.ts +36 -0
  57. package/lib/environment/git-trust.js +174 -0
  58. package/lib/environment/isolation.d.ts +103 -1
  59. package/lib/environment/isolation.js +340 -5
  60. package/lib/environment/process.d.ts +5 -0
  61. package/lib/environment/process.js +20 -1
  62. package/lib/explain/state.js +4 -2
  63. package/lib/index.d.ts +6 -2
  64. package/lib/index.js +8 -2
  65. package/lib/integrations/connector/config.js +2 -2
  66. package/lib/projections/stop.d.ts +1 -1
  67. package/lib/projections/stop.js +16 -0
  68. package/lib/storage/document.d.ts +58 -10
  69. package/lib/verification/already-satisfied.d.ts +44 -0
  70. package/lib/verification/already-satisfied.js +49 -0
  71. package/lib/verification/confirm.js +2 -2
  72. package/lib/verification/gate-scope.d.ts +36 -0
  73. package/lib/verification/gate-scope.js +97 -0
  74. package/lib/verification/gates-at.d.ts +67 -0
  75. package/lib/verification/gates-at.js +173 -0
  76. package/lib/verification/gates.d.ts +17 -1
  77. package/lib/verification/gates.js +87 -9
  78. package/lib/verification/requirements.js +6 -1
  79. package/package.json +1 -1
  80. package/schemas/execution-document.schema.json +160 -4
  81. package/schemas/plan-draft.schema.json +2 -2
  82. package/schemas/run-event.schema.json +26 -0
  83. package/schemas/stop-diagnosis.schema.json +2 -0
  84. package/schemas/work-graph.schema.json +2 -2
  85. package/schemas/work-order.schema.json +2 -2
package/README.md CHANGED
@@ -5,7 +5,7 @@ Headless-движок исполнения инженерных поручени
5
5
  интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
6
6
  и доказательствами.
7
7
 
8
- > Статус: `0.1.0-alpha.16` — предварительный выпуск (alpha). Договоры
8
+ > Статус: `0.1.0-alpha.19` — предварительный выпуск (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.16 # точная версия
31
+ npm install --save-exact devcontour-harness@0.1.0-alpha.19 # точная версия
32
32
  # или: npm install --save-exact devcontour-harness@alpha # текущая alpha
33
33
  npx harness --version
34
34
  ```
@@ -123,6 +123,32 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
123
123
  `devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
124
124
  Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
125
125
 
126
+ ## Выпуск 0.1.0-alpha.19
127
+
128
+ Провал gate интеграции после зелёного кандидата повторяется один раз
129
+ (`recheck`; контрпример свойства или корпуса — нет); области gates до
130
+ `GATE_SCOPE_LIMIT`; дешевле такты очереди. Песочница: каталоги IDE внутри
131
+ `node_modules` открыты, запреты `.git` явные, git хоста сверяет checkout
132
+ (`hostGitArgs`) и снимает исполняемые настройки, защищённые пути сверяются
133
+ после gate, класс `environment` — только по журналу ядра. Подробно и с
134
+ известными пробелами — [заметки выпуска](docs/release-notes/0.1.0-alpha.19.md).
135
+
136
+ ## Выпуск 0.1.0-alpha.18
137
+
138
+ `runGatesAt` исполняет gates с предпосылками профиля: явный `dependsOn` или,
139
+ в профиле без него, более ранние check gates (установка); общий с попыткой
140
+ порядок `orderedScope`; все провалы — сразу; `dependsOn: []` — без
141
+ предпосылок. Подробно — [заметки выпуска](docs/release-notes/0.1.0-alpha.18.md).
142
+
143
+ ## Выпуск 0.1.0-alpha.17
144
+
145
+ Меньше слепых повторов: reviewer называет класс входов, два отказа ревью по
146
+ одному свойству и классу — пробел приёмки с остановкой; отказы окружения не
147
+ тратят попытки (свой предел), штатная остановка возвращает работу в очередь,
148
+ `completed: false` повторяется с черновика; пустой результат принимается
149
+ только с механическим основанием (иначе `needs-lead`), `runGatesAt` проверяет
150
+ gates на закреплённом SHA; стоимость вызова учитывается по всему потоку. Подробно — [заметки выпуска](docs/release-notes/0.1.0-alpha.17.md).
151
+
126
152
  ## Выпуск 0.1.0-alpha.16
127
153
 
128
154
  Отказ по файлу эталонного набора — только по строковому литералу, без ложных
@@ -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`). Пока новый снимок на ревью, после его отклонения,
@@ -478,6 +546,16 @@ executable. Live smoke — отдельный opt-in с явно заданны
478
546
  прежнего владельца, истёкший lease и второй controller ничего не меняют.
479
547
  - **Точные SHA.** PASS gate и ревью относится к конкретному candidate или
480
548
  integration SHA; доказательство другой фазы или другого SHA не засчитывается.
549
+ - **Повтор провала интеграции.** Если собственный gate поручения прошёл на
550
+ кандидате, но упал на integration, controller один раз на кандидата повторяет
551
+ его на том же integration SHA до того, как засчитать отказ `gate`. Обе записи
552
+ остаются; у повтора `recheck: {of, outcome}` — ссылка на первую запись и
553
+ исход: `flaky` (повтор прошёл, первый провал вызван средой, например бюджетом
554
+ времени под нагрузкой) или `confirmed` (отказ засчитывается как прежде). Лог
555
+ повтора — в `integration/recheck/`; замер — стадия `integration-recheck:<gate>`.
556
+ Провал, в отчёте которого названо проваленное свойство или случай корпуса,
557
+ не повторяется: это найденный контрпример, и удачный второй запуск
558
+ недетерминированной проверки его не отменяет.
481
559
  - **Атомарная приёмка.** Продвижение target ref — compare-and-swap внутри
482
560
  транзакции приёмки. Чужой коммит в target ref не теряется: попытка начнётся
483
561
  от новой базы. Падение между update-ref и записью принятия восстанавливается
@@ -487,6 +565,44 @@ executable. Live smoke — отдельный opt-in с явно заданны
487
565
  - **Изоляция.** Режим `os` реально ограничивает процессы writer, reviewer и
488
566
  gates; без механизма песочницы команда не запускается — тихого перехода к
489
567
  запуску без неё нет. Зависшие процессы останавливаются вместе с потомками.
568
+ Обязательные запреты записи sandbox-runtime (конфигурация shell и git,
569
+ `.git/hooks`, `.mcp.json`, `.claude/commands|agents`, каталоги IDE)
570
+ действуют на весь checkout проверки, а не только на её рабочий каталог.
571
+ Исключение одно: каталоги IDE (`.idea`, `.vscode`) внутри `node_modules`
572
+ открыты — пакеты их публикуют, а IDE дерево зависимостей как конфигурацию
573
+ проекта не открывает. На Linux srt пропускает `node_modules` сам; на macOS
574
+ srt запускается из каталога проверки, а запреты задаются явно: каталоги IDE
575
+ закрыты в корне и в каждом подкаталоге существующих каталогов до глубины 3
576
+ (кроме `node_modules`), прочие имена — на любой глубине. Новый каталог
577
+ глубже существующего уровня этим не закрыт — граница подхода (srt на Linux
578
+ закрывает только уже существующие пути). Файл `.git` рабочего дерева,
579
+ перенаправления обычного `.git` (`commondir`, `gitdir`, `config.worktree`) и
580
+ вложенные `.git` на любой глубине тоже закрыты (на Linux — только
581
+ существующий файл `.git`). Дерево больше предела перечисления — отказ
582
+ проверки классом `environment`, а не молча урезанные запреты.
583
+ - **Сверка защищённых путей.** Запреты песочницы проверяют только путь
584
+ операции: каталог, собранный в `node_modules` и перенесённый в дерево
585
+ проекта, ими не закрыт. До и после каждого gate и вокруг работы исполнителя
586
+ сравнивается снимок защищённых имён вне `node_modules` (каталоги IDE,
587
+ конфигурация shell и git, `.claude`, `.git`); новое или изменённое — класс
588
+ `scope-violation` с путями в сообщении.
589
+ - **Классификация отказов песочницы.** Отказ — класс `environment` только
590
+ тогда, когда журнал ядра macOS подтвердил отказ обязательного запрета внутри
591
+ дерева проверки: команда начинается с уникального токена, и записи srt этой
592
+ проверки отличаются от чужих. Текст вывода свидетельством не служит, запись
593
+ вне дерева — ошибка кода (`gate`). На Linux такого свидетельства нет, и
594
+ отказ остаётся `gate`.
595
+ - **Git хоста над деревом проверки.** Каждый вызов git хоста сначала сверяет,
596
+ куда git придёт: файл `.git` дерева, созданного `worktree add`, не изменён;
597
+ gitdir и общий каталог лежат вне дерева и согласованы; `commondir`/`gitdir`
598
+ в обычном `.git` и вложенный репозиторий внутри известного дерева — отказ
599
+ (`scope-violation` в gate). Поверх — `-c core.fsmonitor=false -c
600
+ core.hooksPath=/dev/null -c protocol.ext.allow=never`: hooks репозитория при
601
+ коммитах harness не исполняются.
602
+ - **Чего ещё нет.** Регрессионных тестов в песочнице для запретов `.git`, для
603
+ сверки после переноса каталога и для отказа по пределу перечисления пока
604
+ нет: эти пути покрыты только тестами SBX01–SBX05 и модульными тестами
605
+ `git-trust`.
490
606
  - **Секреты.** Значения секретов маскируются во всём сохраняемом: логах,
491
607
  артефактах ревью, evidence, событиях и результате.
492
608
  - **Бюджеты.** Проверяются до вызова модели в той же транзакции, что и запись
package/docs/contracts.md CHANGED
@@ -615,6 +615,9 @@ manifestDigest, impact?, evidence[], error?}`. Смена любого SHA де
615
615
  `moveBase`/`fencedRefMove`, `runGate`/`runCheck` (и разбор отчёта
616
616
  `junitSummary`, `prepareReportPath`, `sandboxBlockedHosts`), `unprovenReason`,
617
617
  `ReviewProbes`, маскирование (`redactor`, `redactDeep`, `redactError`),
618
+ git хоста над checkout с чужим кодом (`hostGitArgs`, `assertTrustedCheckout`,
619
+ `UntrustedCheckoutError`, `gitHardening`, учёт деревьев `rememberWorktree`/
620
+ `forgetWorktree`/`worktreeKey`/`worktreeAddPath`, `resetGitTrust` для тестов),
618
621
  корпус (`CorpusStore`, подтверждение случая `submitCase` с `ConfirmContext`),
619
622
  ограниченный разбор по свойству (`analyzeRecovery`, `applyCases`,
620
623
  `recoveryBrief`; назначение хоста — модель и профиль ведущего — передаётся
@@ -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.16
16
+ npm install --save-exact devcontour-harness@0.1.0-alpha.19
17
17
  npx harness --version
18
18
  npx harness doctor
19
19
  ```
@@ -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
+ ведущий. Живые модели не вызывались.
@@ -0,0 +1,96 @@
1
+ # devcontour-harness 0.1.0-alpha.19
2
+
3
+ Меньше трения в цикле исполнения и жёстче граница песочницы: провал
4
+ интеграции, вызванный средой, повторяется один раз, установка зависимостей
5
+ не падает на каталогах IDE внутри `node_modules`, а git хоста не исполняет
6
+ конфигурацию, которую записал чужой код. Включает всё из
7
+ [0.1.0-alpha.18](0.1.0-alpha.18.md). `maxAttempts`, правила находок и
8
+ принятие target ref не менялись.
9
+
10
+ ## Что нового
11
+
12
+ ### Цикл исполнения
13
+
14
+ - **Ограниченный повтор провала интеграции.** Собственный gate поручения,
15
+ прошедший на кандидате и упавший на integration, повторяется один раз на
16
+ кандидата на том же integration SHA, прежде чем засчитать отказ `gate`.
17
+ Обе записи остаются; у повтора `recheck: {of, outcome}` — ссылка на первую
18
+ запись и исход `flaky` (повтор прошёл) или `confirmed` (провал
19
+ засчитывается как прежде). Лог повтора — в `integration/recheck/`, замер —
20
+ стадия `integration-recheck:<gate>`. Удачный повтор принимает кандидата без
21
+ новой реализации. Кандидат, переиспользованный следующей попыткой, второго
22
+ повтора не получает; движок отклоняет второй повтор в попытке и исход,
23
+ не согласованный с записью.
24
+ - **Контрпример не повторяется.** Провал, в отчёте которого названо
25
+ проваленное свойство или случай корпуса, не повторяется: удачный второй
26
+ запуск недетерминированной проверки не отменяет найденного входа.
27
+ - **Шире области gates.** Область gates задачи, поручения, потребителя и
28
+ основания ревью вмещает до `GATE_SCOPE_LIMIT` (100, предел gates профиля)
29
+ вместо 20; превышение — понятный отказ с величиной и исправлением.
30
+ Константа экспортируется.
31
+ - **Дешевле такты.** `expire()` без истёкших попыток не открывает транзакцию
32
+ записи; такт очереди читает активные попытки через необязательный быстрый
33
+ путь `ExecutionView.activeAttempts`. Завершённое задание workflow сообщает
34
+ израсходованные попытки стадий, а не сброшенный счётчик.
35
+
36
+ ### Песочница
37
+
38
+ - **Каталоги IDE внутри `node_modules` открыты.** Пакеты публикуют `.idea/`,
39
+ и установка в чистом дереве падала с EPERM при любом коде. На macOS srt
40
+ запускается из служебного каталога проверки, а обязательные запреты
41
+ задаются явно на весь checkout: конфигурация shell, git, MCP и Claude,
42
+ `.git/hooks`, `.git/config` — на любой глубине; `.idea`/`.vscode` — в
43
+ корне и в каждом существующем подкаталоге до глубины 3, кроме
44
+ `node_modules`. Linux не меняется (srt пропускает `node_modules` сам).
45
+ - **Явные запреты `.git`.** На macOS закрыты файл `.git` рабочего дерева,
46
+ перенаправления обычного `.git` (`commondir`, `gitdir`, `config.worktree`)
47
+ и вложенные `.git` на любой глубине; на Linux — существующий файл `.git`.
48
+ Дерево больше предела перечисления — отказ класса `environment`, а не
49
+ молча урезанные запреты.
50
+ - **Git хоста над деревом проверки (`git-trust`).** Каждый вызов git хоста
51
+ сначала сверяет, куда git придёт: файл `.git` дерева, созданного
52
+ `worktree add`, не изменён; gitdir и общий каталог лежат вне дерева и
53
+ согласованы. Перенаправленный `gitdir`/`commondir` в обычном `.git` и
54
+ вложенный репозиторий внутри известного дерева — отказ
55
+ (`UntrustedCheckoutError`, в gate — `scope-violation`). Поверх —
56
+ `-c core.fsmonitor=false -c core.hooksPath=/dev/null -c protocol.ext.allow=never`:
57
+ hooks репозитория при коммитах harness не исполняются. Экспорт для хоста:
58
+ `hostGitArgs`, `assertTrustedCheckout`, `UntrustedCheckoutError`,
59
+ `gitHardening`, `rememberWorktree`, `forgetWorktree`, `worktreeKey`,
60
+ `worktreeAddPath`, `resetGitTrust` (для тестов).
61
+ - **Сверка защищённых путей после gate.** Запреты песочницы видят только
62
+ путь операции, и каталог, собранный в `node_modules` и перенесённый в
63
+ дерево проекта, ими не закрыт. Вокруг каждого gate и работы исполнителя
64
+ сравнивается снимок защищённых имён вне `node_modules`; новое или
65
+ изменённое — `scope-violation` с путями в сообщении.
66
+ - **Класс `environment` — только по журналу ядра.** Отказ песочницы
67
+ классифицируется как сбой среды, только когда журнал ядра macOS подтвердил
68
+ отказ обязательного запрета внутри дерева проверки (команда проверки
69
+ помечена уникальным токеном). Текст вывода (EPERM) свидетельством не
70
+ служит; запись вне дерева и любой отказ на Linux остаются `gate`.
71
+
72
+ ## Известные пробелы
73
+
74
+ - Регрессионные тесты в настоящей песочнице для запретов `.git`, для сверки
75
+ защищённых путей после переноса каталога и для отказа по пределу
76
+ перечисления не написаны (решение владельца проекта). Эти пути покрыты
77
+ тестами SBX01–SBX05 и модульными тестами `git-trust`.
78
+ - Запреты на Linux и запуск/остановка наблюдения за журналом ядра тестами не
79
+ покрыты.
80
+ - Поэтому два мутанта этих путей (D1, D2) выживают.
81
+
82
+ ## Совместимость
83
+
84
+ Формат состояния: новое необязательное поле `recheck` у evidence gate и в
85
+ событии `attempt.gate`, области gates длиннее 20. Прежние версии
86
+ отклоняют такое состояние строгой схемой → откат через копию. Хост, у
87
+ которого хуки gate или writer меняют защищённые имена, получит
88
+ `scope-violation`. Матрица — [upgrade](../upgrade.md).
89
+
90
+ ## Проверка выпуска
91
+
92
+ Затронутые тесты (`runner`, `engine`, `repair`, `planning-schema`,
93
+ `cli-h3-contract`, `probe-gates`, `review-inspection-runner`, `git-trust`,
94
+ `sandbox-install`, `boundaries`), lint, typecheck и `schemas:check`. Полный
95
+ `npm run check` перед публикацией выполняет ведущий. Живые модели не
96
+ вызывались.
@@ -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`, `CI=1`;
33
- у `test` gate ещё `TURBO_FORCE=true` и `GOFLAGS=… -count=1`, как у gate движка.
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`), журнал (`<id>.log`) и `run.json`, затем
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
 
@@ -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` — запись прежней версии. Её находки (у