devcontour-harness 0.1.0-alpha.11 → 0.1.0-alpha.12

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,16 +5,17 @@ Headless-движок исполнения инженерных поручени
5
5
  интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
6
6
  и доказательствами.
7
7
 
8
- > Статус: `0.1.0-alpha.11` — предварительный выпуск (alpha). Договоры
8
+ > Статус: `0.1.0-alpha.12` — предварительный выпуск (alpha). Договоры
9
9
  > `v1alpha1` могут измениться между alpha-версиями; закрепляйте точную версию.
10
10
  > Файл → ревью плана → кандидат → интеграция → принятие → результат JSON, с
11
11
  > ограниченным восстановлением, повторным использованием кандидата, бюджетами,
12
12
  > потребителями, свойствами и корпусом, совместной проверкой репозиториев и
13
13
  > явным режимом одного вендора.
14
14
 
15
- Пакет включает встроенные профили стеков (`profiles/`), skill
16
- `skills/harness/SKILL.md` и шаблоны политики, графа и инструкции агентам
17
- (`templates/`).
15
+ Пакет включает встроенные профили стеков (`profiles/`), skills
16
+ `skills/harness/SKILL.md` (работа с поручениями) и
17
+ `skills/harness-setup/SKILL.md` (установка, первая настройка и обновление
18
+ версии), шаблоны политики, графа и инструкции агентам (`templates/`).
18
19
 
19
20
  ## Требования
20
21
 
@@ -27,7 +28,7 @@ Headless-движок исполнения инженерных поручени
27
28
  Из npm — точная версия (воспроизводимо) или последняя alpha по тегу:
28
29
 
29
30
  ```sh
30
- npm install --save-exact devcontour-harness@0.1.0-alpha.11 # точная версия
31
+ npm install --save-exact devcontour-harness@0.1.0-alpha.12 # точная версия
31
32
  # или: npm install --save-exact devcontour-harness@alpha # текущая alpha
32
33
  npx harness --version
33
34
  ```
@@ -35,6 +36,11 @@ npx harness --version
35
36
  Выпуски публикуются под тегом `alpha`; ставьте точную версию или `@alpha`,
36
37
  а не версию по умолчанию без тега.
37
38
 
39
+ Чтобы не менять зависимости и lockfile существующего проекта, пакет ставят в
40
+ отдельный каталог инструментов — снаружи репозитория или в `.harness-tools/`
41
+ внутри него ([standalone](docs/standalone.md#установка), skill
42
+ [harness-setup](skills/harness-setup/SKILL.md)).
43
+
38
44
  Из локального архива `devcontour-harness-<version>.tgz` (поддерживаемый
39
45
  способ без registry):
40
46
 
@@ -117,6 +123,18 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
117
123
  `devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
118
124
  Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
119
125
 
126
+ ## Выпуск 0.1.0-alpha.12
127
+
128
+ Skill установки и обновления. Подробно —
129
+ [заметки выпуска](docs/release-notes/0.1.0-alpha.12.md).
130
+
131
+ - `skills/harness-setup/SKILL.md` — агент один раз спрашивает решения первой
132
+ настройки (новый или существующий проект, ветка, каталог установки —
133
+ `.harness-tools/` в проекте или снаружи, источник пакета, writer и reviewer
134
+ с моделями, согласование, источник задач: файлы или Relay), ставит точную
135
+ версию, выполняет `init` и обновляет версию с копией state и проверкой
136
+ совместимости. Движок, договоры и формат состояния не менялись.
137
+
120
138
  ## Выпуск 0.1.0-alpha.11
121
139
 
122
140
  Профили в форме хоста. Подробно —
@@ -226,6 +244,7 @@ alpha.5 не читает; прежний DevContour его открывает
226
244
  ## Документация
227
245
 
228
246
  - [Быстрый старт по шагам](docs/quickstart.md)
247
+ - [Установка, первая настройка и обновление версии — skill harness-setup](skills/harness-setup/SKILL.md)
229
248
  - [Самостоятельный проект: init, планирование (plan-request, plan-check), explain, capabilities, проекции](docs/standalone.md)
230
249
  - [Архитектура и границы доверия](docs/architecture.md)
231
250
  - [Договоры: входы, выходы, порты, ошибки, CLI](docs/contracts.md)
@@ -74,8 +74,8 @@ RunResult (SHA, evidence, ограничения) -> ResultSink
74
74
  Таблица перечисляет каталоги `src/`. Git-операции — worktrees, snapshots,
75
75
  проверка области, candidate/integration, CAS и recovery — отдельного каталога
76
76
  не образуют: они в `engine` (`runner.ts`, `joint-runner.ts`), `control`
77
- (`base-move.ts`) и `verification`. Поставляемый skill с инструкциями и
78
- шаблонами лежит в корневом каталоге пакета `skills/`; исполняемые гарантии
77
+ (`base-move.ts`) и `verification`. Поставляемые skills лежат в корневом
78
+ каталоге пакета `skills/`, шаблоны — в `templates/`; исполняемые гарантии
79
79
  остаются в движке.
80
80
 
81
81
  Модули `setup`, `planning`, `explain`, `projections`, `catalog` и разрешение
@@ -413,9 +413,10 @@ runtime-граф пакета.
413
413
 
414
414
  ## Профили, skills и шаблоны
415
415
 
416
- Встроенные профили (`profiles/`), skill (`skills/harness/SKILL.md`) и шаблоны
417
- (`templates/`: политика, граф, инструкция агентам репозитория) входят в пакет.
418
- Skill и шаблоны только объясняют использование: движок, runner и проверки их
416
+ Встроенные профили (`profiles/`), skills (`skills/harness/SKILL.md` — работа
417
+ с поручениями, `skills/harness-setup/SKILL.md` — установка, первая настройка
418
+ и обновление версии) и шаблоны (`templates/`: политика, граф, инструкция
419
+ агентам репозитория) входят в пакет. Skills и шаблоны только объясняют использование: движок, runner и проверки их
419
420
  не читают, и удаление каталога skills не отключает ни gates, ни ревью, ни
420
421
  область записи, ни fencing — это проверяет smoke установленного архива.
421
422
  Профиль — вход для сборки политики, а не исполняемый объект: разрешение не
@@ -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.11
16
+ npm install --save-exact devcontour-harness@0.1.0-alpha.12
17
17
  npx harness --version
18
18
  npx harness doctor
19
19
  ```
@@ -0,0 +1,66 @@
1
+ # devcontour-harness 0.1.0-alpha.12
2
+
3
+ Skill установки, первой настройки и обновления версии. Движок, CLI, договоры
4
+ `v1alpha1`, политика, профили и формат состояния не менялись; gates,
5
+ `maxAttempts`, бюджеты попыток и повторов, правила находок, изоляция и
6
+ принятие target ref — прежние.
7
+
8
+ ## Что нового
9
+
10
+ - **`skills/harness-setup/SKILL.md`** — маршрут для агента пользователя:
11
+ - один раунд вопросов: существующий или новый проект, ветка подготовки,
12
+ каталог установки (`.harness-tools/` внутри проекта или каталог
13
+ снаружи), источник пакета (npm `@alpha`, сведённый к точной версии,
14
+ точная версия или архив с sha256), writer и reviewer, модели и effort,
15
+ ревью тем же вендором (только явным «да»), режим согласования плана,
16
+ источник задач (файлы графа, Relay v1, Relay-2), коммит подготовки;
17
+ - новый проект: Git, `package.json` со scripts `check` и `test` (JUnit в
18
+ `.reports/junit.xml`) и lockfile — только с согласия пользователя;
19
+ - установка в проект: версию закрепляют `.harness-tools/package.json` и
20
+ `package-lock.json`, `node_modules/` не коммитится, каталог добавляется в
21
+ `protectedPaths` политики; из архива — каталог целиком в
22
+ `.git/info/exclude`;
23
+ - `init` и правки политики по ответам: `model`, `effort` (только для
24
+ codex), reviewer того же вендора для ревью кода и контракта
25
+ (`same-vendor-allowed`), `approvalMode`; проверка `doctor --policy`
26
+ (схема, runtime, toolchain), `init --plan` без конфликтов и `validate`
27
+ первого графа (перекрёстные проверки политики);
28
+ - `connector.json` для Relay и проверки без записи в Relay
29
+ (`source-check`, `source-snapshot`);
30
+ - обновление: остановка writers, копия state, проверка копии новой
31
+ версией, установка, `doctor --state`, `init --plan` (изменённый профиль,
32
+ перенос репозитория или версия API — решение человека; manifest
33
+ пересоздаётся с прежним state, политика не перегенерируется), `validate`
34
+ графов, `resume`; откат по [upgrade](../upgrade.md);
35
+ - восстановление закреплённой версии в новом клоне — `npm ci --prefix
36
+ .harness-tools`.
37
+ - `skills/harness/SKILL.md` по-прежнему ведёт работу с поручениями; новый
38
+ skill передаёт ему управление после подготовки. В нём уточнены каталог
39
+ установки (снаружи или `.harness-tools/`) и причины `content-differs` на
40
+ `project.json` после обновления или переноса.
41
+
42
+ ## Документация
43
+
44
+ - [standalone](../standalone.md): установка вне проекта или в
45
+ `.harness-tools/`, закрепление версии файлом, обновление.
46
+ - [architecture](../architecture.md): состав поставляемых skills.
47
+ - [upgrade](../upgrade.md): столбец alpha.12 в матрице форматов.
48
+
49
+ ## Совместимость
50
+
51
+ Формат состояния не менялся: alpha.12 читает всё, что читает alpha.11, а
52
+ alpha.11 — всё, что пишет alpha.12. Обновление и откат — по
53
+ [upgrade](../upgrade.md).
54
+
55
+ ## Проверка выпуска
56
+
57
+ `npm run check` (lint, typecheck, schemas, build, весь набор тестов, package
58
+ smoke из установленного архива) на macOS arm64, Node.js 24.3. Тест skill
59
+ сверяет с договором CLI каждую команду маршрута: команду, флаги и
60
+ обязательные аргументы полных вызовов; smoke установленного архива требует
61
+ оба skill. Маршрут установки в `.harness-tools/` пройден вручную на свежем
62
+ npm-репозитории с опубликованной alpha.11: `init --plan`, `init`, правки
63
+ политики, `doctor --policy`, отказ `validate` для `effort` не у codex,
64
+ пересоздание manifest с прежним state. Живые модели не вызывались;
65
+ проверка DevContour для alpha.12 не повторялась (движок не менялся,
66
+ [проверенные версии](../compatibility-versions.md) относятся к alpha.11).
@@ -4,20 +4,41 @@
4
4
  без внешнего хоста: пользователь называет репозиторий, цель и ограничения,
5
5
  остальное агент получает от `harness init`, а план составляет сам через
6
6
  `harness plan-request` и `harness plan-check`. Пошаговый маршрут для агента —
7
- skill [`skills/harness/SKILL.md`](../skills/harness/SKILL.md).
7
+ skill [`skills/harness/SKILL.md`](../skills/harness/SKILL.md); установку,
8
+ первую настройку и обновление версии ведёт skill
9
+ [`skills/harness-setup/SKILL.md`](../skills/harness-setup/SKILL.md).
8
10
 
9
- ## Установка вне проекта
11
+ ## Установка
10
12
 
11
13
  Пакет ставится в отдельный каталог инструментов, а не в зависимости проекта:
12
- `package.json` и lockfile проекта не меняются.
14
+ `package.json` и lockfile проекта не меняются. Каталог — снаружи
15
+ репозитория или внутри него, в `.harness-tools/`.
13
16
 
14
17
  ```sh
15
- npm install --prefix <tooldir> --omit=dev --ignore-scripts ./devcontour-harness-<version>.tgz
18
+ npm install --prefix <tooldir> --save-exact --omit=dev --ignore-scripts devcontour-harness@<version>
19
+ # или из архива: npm install --prefix <tooldir> --omit=dev --ignore-scripts ./devcontour-harness-<version>.tgz
16
20
  H=<tooldir>/node_modules/.bin/harness
17
21
  $H --version
18
22
  $H doctor
19
23
  ```
20
24
 
25
+ Внутри проекта (`<tooldir>` = `<repo>/.harness-tools`) версию закрепляют
26
+ `.harness-tools/package.json` и `package-lock.json`: их коммитят вместе с
27
+ `.harness/`, а `node_modules/` исключает `.harness-tools/.gitignore`. Чтобы
28
+ поручение не могло изменить инструмент, которым его проверяют, каталог
29
+ добавляют в `protectedPaths` политики. Установка из архива записывает в
30
+ `package.json` локальный путь `file:` — такой каталог целиком исключают через
31
+ `.git/info/exclude`. Worktrees попыток создаются из Git, поэтому
32
+ неотслеживаемый каталог инструментов в них не попадает. Состояние исполнения
33
+ в любом случае остаётся вне репозитория.
34
+
35
+ Обновление — смена закреплённой версии в том же каталоге по процедуре
36
+ [upgrade](upgrade.md): остановка writers, копия state, проверка копии новой
37
+ версией, установка, `doctor --state`, `init --plan`, `validate` графов,
38
+ `resume`. Новая версия не перегенерирует политику: если изменился встроенный
39
+ профиль, `init --plan` сообщает `content-differs` на `project.json`, и решение
40
+ принимает человек.
41
+
21
42
  ## `harness init`
22
43
 
23
44
  ```sh
package/docs/upgrade.md CHANGED
@@ -21,20 +21,20 @@
21
21
 
22
22
  ## Матрица форматов
23
23
 
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 | Пишет поверх | Примечание |
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. alpha.8, alpha.9, alpha.10 и alpha.11 формат состояния не меняют и читают всё, что читает alpha.7. Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
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 | Пишет поверх | Примечание |
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. alpha.8, alpha.9, alpha.10, alpha.11 и alpha.12 формат состояния не меняют и читают всё, что читает alpha.7. Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
38
38
  формата до записи проверяют тесты совместимости пакета
39
39
  <!-- repo-only -->(`tests/compatibility.test.ts`)<!-- /repo-only -->.
40
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devcontour-harness",
3
- "version": "0.1.0-alpha.11",
3
+ "version": "0.1.0-alpha.12",
4
4
  "description": "Headless execution harness: work graph, isolated attempts, gates, independent reviews and accepted results.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,8 +39,10 @@ shasum -a 256 <file.tgz>
39
39
  ```
40
40
 
41
41
  Use the exact tarball (or exact registry version) you were given and record
42
- its sha256. Do not install it into the project: the project `package.json`
43
- and lockfile stay untouched.
42
+ its sha256. Do not install it into the project's dependencies: the project
43
+ `package.json` and lockfile stay untouched. `<tooldir>` is outside the
44
+ repository or `<repo>/.harness-tools`; the `harness-setup` skill of the
45
+ package covers both, the version pin and upgrades.
44
46
 
45
47
  ### 2. Check the machine
46
48
 
@@ -65,19 +67,19 @@ Schema of input and response, and the byte limits. Read only.
65
67
  interruption it resumes from the saved manifest decisions and writes only
66
68
  what is missing.
67
69
 
68
- | Conflict or refusal | Meaning | Action |
69
- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
70
- | `symlink` | the path or an ancestor (`conflictPath`) is a symlink | never write through it; the user replaces the link |
71
- | `ancestor-not-directory` | an ancestor of the path is a file | ask the user; never delete their files |
72
- | `not-regular-file` | the destination exists and is not a regular file | ask the user |
73
- | `outside-root` | the destination resolves outside the repository | ask the user |
74
- | `content-differs` on `.harness/project.json` | an explicit `--writer` or `--profile` differs from the saved manifest | keep the saved decision, or the user confirms the change explicitly |
75
- | `content-differs` on `.harness/policy.json` | the saved policy's writer differs from the manifest or explicit `--writer` | keep the saved decision, or the user confirms the change explicitly |
76
- | `INVALID_INPUT` with a path | not a Git root, invalid manifest or policy (also a truncated write), `--state` inside the repository | read the issue path; a broken file is removed by a human, not overwritten |
77
- | `NOT_READY` `no-commits` | the repository has no commits | the user commits first |
78
- | `NOT_READY` `stack-ambiguous` / `stack-unsupported` | both lockfiles, or none | pass `--profile`; when profile gates do not fit, write `.harness/profile.json` and pass `--profile ./.harness/profile.json` |
79
- | `NOT_READY` toolchain problems | missing scripts, wrong tool versions | fix the project or hand it to the user; never weaken the policy |
80
- | `CONTROLLER_BUSY` | the repository is already owned by another controller | stop: a second controller and state are not created |
70
+ | Conflict or refusal | Meaning | Action |
71
+ | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
72
+ | `symlink` | the path or an ancestor (`conflictPath`) is a symlink | never write through it; the user replaces the link |
73
+ | `ancestor-not-directory` | an ancestor of the path is a file | ask the user; never delete their files |
74
+ | `not-regular-file` | the destination exists and is not a regular file | ask the user |
75
+ | `outside-root` | the destination resolves outside the repository | ask the user |
76
+ | `content-differs` on `.harness/project.json` | an explicit `--writer` or `--profile` differs from the saved manifest; after an upgrade or a move, also a changed built-in profile, root or API version | keep the saved decision, or the user confirms the change explicitly |
77
+ | `content-differs` on `.harness/policy.json` | the saved policy's writer differs from the manifest or explicit `--writer` | keep the saved decision, or the user confirms the change explicitly |
78
+ | `INVALID_INPUT` with a path | not a Git root, invalid manifest or policy (also a truncated write), `--state` inside the repository | read the issue path; a broken file is removed by a human, not overwritten |
79
+ | `NOT_READY` `no-commits` | the repository has no commits | the user commits first |
80
+ | `NOT_READY` `stack-ambiguous` / `stack-unsupported` | both lockfiles, or none | pass `--profile`; when profile gates do not fit, write `.harness/profile.json` and pass `--profile ./.harness/profile.json` |
81
+ | `NOT_READY` toolchain problems | missing scripts, wrong tool versions | fix the project or hand it to the user; never weaken the policy |
82
+ | `CONTROLLER_BUSY` | the repository is already owned by another controller | stop: a second controller and state are not created |
81
83
 
82
84
  ### 4. Plan the graph
83
85
 
@@ -0,0 +1,356 @@
1
+ ---
2
+ name: harness-setup
3
+ description: Install, configure or upgrade devcontour-harness for a new or existing npm/pnpm Git repository — ask the setup questions once (project, branch, install location, package source, writer and reviewer models, approval, task source such as files or Relay), install the exact version inside the project (`.harness-tools/`) or outside it, run `harness init`, record the decisions and hand over to the `harness` skill; upgrade an installed version with a state copy and a compatibility check.
4
+ ---
5
+
6
+ # Harness setup
7
+
8
+ This skill installs and upgrades the `devcontour-harness` package for one
9
+ repository and leaves it ready for work. Running work orders (plan, run,
10
+ explain, result) is the job of the `harness` skill shipped inside the package
11
+ (`<tooldir>/node_modules/devcontour-harness/skills/harness/SKILL.md`); read it
12
+ once the package is installed.
13
+
14
+ This skill only explains the route. Gates, review, write scope, budgets and
15
+ the reviewer vendor rule are enforced by the harness itself; nothing here
16
+ replaces or relaxes them.
17
+
18
+ Every `harness` command prints one JSON envelope on stdout (`ok` with `data`,
19
+ or `error`); exit codes: 0 success, 2 invalid input, 3 refusal or block,
20
+ 4 infrastructure. Below `$H` is the installed CLI:
21
+ `H=<tooldir>/node_modules/.bin/harness`.
22
+
23
+ Modes: **install** (first setup), **upgrade** (change the installed version),
24
+ **check** (report what is installed; read only). Pick the mode from the
25
+ request; when the repository already has `.harness/` and a pinned or
26
+ installed `$H`, it is upgrade or check, not install.
27
+
28
+ ## Install
29
+
30
+ ### 0. Ask once
31
+
32
+ Ask everything in one round (a multiple-choice question per item where the
33
+ host supports it); offer the default first. Do not ask what the repository
34
+ already answers: detect the lockfile, `packageManager`, an existing
35
+ `.harness/`, the current branch and which of `claude` / `codex` are installed
36
+ first. Whether a runtime is signed in is not detectable without a model call:
37
+ the user confirms it.
38
+
39
+ | # | Question | Options (default first) | Becomes |
40
+ | --- | ------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
41
+ | 1 | Repository | existing path / new project at a path | step 2 |
42
+ | 2 | Branch for the setup | new branch `harness-setup` / current branch | step 3; never the name `harness` (it clashes with `harness/accepted`) |
43
+ | 3 | Install location | inside the project `.harness-tools/` / outside, in a tool directory | `<tooldir>` of step 4 |
44
+ | 4 | Package source | npm `@alpha` resolved to an exact version / npm exact version / local `.tgz` with its sha256 | step 4 |
45
+ | 5 | Writer runtime | `claude` / `codex` | `init --writer`; the reviewer is the other vendor |
46
+ | 6 | Models and effort | runtime defaults / explicit `model` (and `effort` for codex) for writer and reviewer | step 6 |
47
+ | 7 | Reviewer of the writer's vendor | no (`different-vendor`) / yes for code and contract review, stated explicitly | step 6 |
48
+ | 8 | Plan approval | `agent` (the reviewer approves) / `operator` (a human runs `approve`) | step 6 |
49
+ | 9 | Task source | graph files in `.harness/work/` / Relay v1 (HTTP) / Relay-2 (CLI) | step 7 |
50
+ | 10 | Commit the setup | commit on the setup branch / leave uncommitted | step 8 |
51
+
52
+ For Relay also ask: the v1 endpoint, or the absolute Relay-2 binary, the
53
+ absolute `relay.config.json` / `relay.workspace.json` and the workspace
54
+ project; `projectId`; `actor` (default `harness-controller`); the task refs
55
+ and, per task, its write scope when it is narrower than the whole
56
+ repository; the columns a task may start from; `completion.mode`
57
+ (`review-only` for the first run, or `complete`).
58
+
59
+ The answers are the user's decisions. Do not change them later without
60
+ asking.
61
+
62
+ ### 1. Preconditions
63
+
64
+ Node.js ≥ 24.3, `git`, `npm` (or `pnpm` for a pnpm project) and the runtime
65
+ CLIs the answers name (`claude --version`, `codex --version`). A missing or
66
+ signed-out runtime is the user's to fix; a live run is impossible without it.
67
+
68
+ The package manager version is checked exactly: the built-in profiles pin
69
+ one (`npm-ci` pins npm `11.4.2`), and `package.json#packageManager`
70
+ (`npm@<version>` / `pnpm@<version>`) overrides that pin. Another version is
71
+ `NOT_READY` (`TOOL_VERSION`) at `init`; adding `packageManager` to an existing
72
+ project is the user's decision.
73
+
74
+ ### 2. Repository
75
+
76
+ **Existing.** It must be the top level of a Git repository with at least one
77
+ commit and exactly one lockfile: `package-lock.json` (profile `npm-ci`) or
78
+ `pnpm-lock.yaml` (profile `pnpm-frozen`). Both or none is decided by the user
79
+ (`--profile`, or a local `.harness/profile.json` passed as
80
+ `--profile ./.harness/profile.json`).
81
+
82
+ **New.** Create it only with the user's consent, at the path they gave:
83
+
84
+ ```sh
85
+ git init <repo> && cd <repo>
86
+ npm init -y && npm install # writes package-lock.json
87
+ ```
88
+
89
+ The built-in `npm-ci` profile needs two scripts: `check` and `test`, where
90
+ `test` writes JUnit to `.reports/junit.xml`. A minimal set for Node's test
91
+ runner, with the installed npm version pinned:
92
+
93
+ ```json
94
+ "packageManager": "npm@<npm --version>",
95
+ "scripts": {
96
+ "check": "node --check index.js",
97
+ "test": "mkdir -p .reports && node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=.reports/junit.xml"
98
+ }
99
+ ```
100
+
101
+ Add `.reports/` and `node_modules/` to `.gitignore`, a first source file and
102
+ a passing test, then commit. The first commit must pass its own gates: a
103
+ project failing on its base is a separate first task, not a reason to relax
104
+ a gate.
105
+
106
+ ### 3. Branch
107
+
108
+ `git status` must be clean (ask the user about local changes; never stash or
109
+ discard them yourself). Then `git switch -c <branch>`.
110
+
111
+ `run` creates `refs/heads/harness/accepted` from the `HEAD` of the main
112
+ checkout when that ref does not exist yet. Keep the setup branch checked out,
113
+ with the setup committed (step 8), until the first `run`: otherwise
114
+ `harness/accepted` starts from a commit without it. When `harness/accepted`
115
+ already exists, report its SHA and leave it alone. Accepted work lands in
116
+ `harness/accepted`, not in the setup branch; merging it anywhere is the
117
+ user's decision.
118
+
119
+ ### 4. Install the exact version
120
+
121
+ Resolve `@alpha` to the exact version first and install that:
122
+ `npm view devcontour-harness@alpha version`. For a tarball, compare
123
+ `shasum -a 256 <file.tgz>` with the sha256 the user gave and stop on a
124
+ mismatch.
125
+
126
+ **Inside the project** — `<tooldir>` = `<repo>/.harness-tools`:
127
+
128
+ ```sh
129
+ T=<repo>/.harness-tools
130
+ mkdir -p "$T"
131
+ npm install --prefix "$T" --save-exact --omit=dev --ignore-scripts devcontour-harness@<version>
132
+ printf 'node_modules/\n' > "$T/.gitignore"
133
+ ```
134
+
135
+ The version is pinned by `.harness-tools/package.json` and its
136
+ `package-lock.json`, not by a directory name; both are committed with the
137
+ setup, `node_modules/` is not. The project's own `package.json` and lockfile
138
+ do not change. When the project declares npm `workspaces` or a
139
+ `pnpm-workspace.yaml` whose globs match `.harness-tools`, or lint/type/test
140
+ globs that would read it, tell the user before going on.
141
+
142
+ In a fresh clone, or after `node_modules/` was removed, restore the pinned
143
+ version without changing the pin:
144
+
145
+ ```sh
146
+ npm ci --prefix <repo>/.harness-tools --omit=dev --ignore-scripts
147
+ ```
148
+
149
+ A tarball source records a local `file:` path in `.harness-tools/package.json`
150
+ that does not exist on other machines: install it the same way, but keep the
151
+ whole directory out of Git with `.git/info/exclude` instead of committing it:
152
+
153
+ ```sh
154
+ npm install --prefix "$T" --omit=dev --ignore-scripts <file.tgz>
155
+ grep -qxF '.harness-tools/' <repo>/.git/info/exclude || echo '.harness-tools/' >> <repo>/.git/info/exclude
156
+ ```
157
+
158
+ **Outside the project** — any directory the user names, the same commands
159
+ with `--prefix <tooldir>`; nothing is written into the repository.
160
+
161
+ Then:
162
+
163
+ ```sh
164
+ H=<tooldir>/node_modules/.bin/harness
165
+ $H --version
166
+ $H doctor
167
+ ```
168
+
169
+ Never install the package into the project's own dependencies as part of this
170
+ route, and never put the execution state inside the repository.
171
+
172
+ ### 5. `harness init`
173
+
174
+ ```sh
175
+ $H init --repository <repo> --writer <claude|codex> --plan
176
+ $H init --repository <repo> --writer <claude|codex>
177
+ ```
178
+
179
+ The first call writes nothing: read `data.files`, `data.conflicts`,
180
+ `data.toolchain.problems`, `data.facts` and `data.manifest.paths.state`, and
181
+ resolve every conflict or problem with the table of the `harness` skill
182
+ (step 3) before the second call. `init` is create-only: `.harness/.gitignore`,
183
+ `.harness/README.md`, `.harness/project.json` (local, not under Git) and
184
+ `.harness/policy.json`. The state lives outside the repository
185
+ (`HARNESS_STATE_HOME`, `XDG_STATE_HOME` or `~/.local/state/devcontour-harness`).
186
+
187
+ ### 6. Runtimes and approval
188
+
189
+ Edit `.harness/policy.json` only as the answers require:
190
+
191
+ - models: `model` on `roles.<role>.writer` and on `review.code.reviewer`,
192
+ `review.plan.reviewer`, `review.contract.reviewer`;
193
+ - effort: `effort` (`low`, `medium`, `high`, `xhigh`) only on bindings whose
194
+ `runtime` is `codex`; on any other runtime it is refused;
195
+ - a reviewer of the writer's vendor (an explicit yes only): on
196
+ `review.code` and/or `review.contract`, set `reviewer.runtime` to the
197
+ writer's runtime and add `"vendor": "same-vendor-allowed"`. Leave
198
+ `review.plan` with the other vendor: `init` reads the saved writer from the
199
+ plan reviewer, so changing it makes every later `init --plan` report
200
+ `content-differs` on `policy.json`. When the user wants it anyway, say so
201
+ and let them decide;
202
+ - operator approval: top-level `"approvalMode": "operator"`;
203
+ - with the version pin committed inside the project: add `.harness-tools/` to
204
+ `repositories[0].protectedPaths`, so no work order can change the tool it
205
+ is checked by.
206
+
207
+ Never remove or weaken anything `init` wrote. Keep the writer runtime as
208
+ `init` wrote it: a different writer vendor is a new `init --writer` decision.
209
+
210
+ Check after editing:
211
+
212
+ ```sh
213
+ $H doctor --policy <repo>/.harness/policy.json
214
+ $H init --repository <repo> --plan
215
+ ```
216
+
217
+ `doctor` checks the policy schema, runtimes and toolchain only; `init --plan`
218
+ must show `data.conflicts` = 0. The cross-checks of the policy (effort on a
219
+ non-codex runtime, the reviewer vendor rule, unknown gates) run in
220
+ `$H validate --graph <graph> --policy <repo>/.harness/policy.json`: run it
221
+ for the first graph before any review or run, and fix the policy, not the
222
+ check, when it fails.
223
+
224
+ ### 7. Task source
225
+
226
+ **Graph files.** Nothing more to configure; the `harness` skill plans the
227
+ first graph.
228
+
229
+ **Relay.** Write `.harness/connector.json` from `docs/connectors.md` of the
230
+ installed package ("Конфигурация"):
231
+
232
+ - `"version": 1` and `"provider": "relay-v1-http"` or `"relay-v2-cli"`,
233
+ always explicit — never guessed from a binary named `relay`;
234
+ - `projectId` and `actor` at the top level; `repository.id` is the policy
235
+ repository id (`app` after `init`);
236
+ - adapter settings nested under `relay`: v1 `relay.endpoint`; Relay-2
237
+ absolute `relay.binary` and `relay.config`, `relay.project` for a workspace,
238
+ `relay.env` that may set `RELAY_HOME` (and on macOS `DYLD_LIBRARY_PATH` to
239
+ SQLite ≥ 3.44) but never `RELAY_CONFIG` or `RELAY_ACTOR`;
240
+ - `tasks[]`: `ref`; `role` and `gates` only from `data.facts` of `init`;
241
+ `writeScope` (required) = the role's `writePaths` from the facts, or, when
242
+ the role has none, `["."]` (the whole repository) or the narrower paths the
243
+ user named; `criteria` mapped to a gate and a JUnit `testId`;
244
+ - `completion: { "mode": "review-only" }` (or `"complete"`) and
245
+ `eligibility.columns` from the answers;
246
+ - no `stateDir` (pass `--state <manifest.paths.state>`), no secret values.
247
+
248
+ Checks that do not write to Relay:
249
+
250
+ ```sh
251
+ $H source-check --source <repo>/.harness/connector.json --policy <repo>/.harness/policy.json --state <state>
252
+ $H source-snapshot --source <repo>/.harness/connector.json --policy <repo>/.harness/policy.json --state <state> --out <dir outside the repo>
253
+ ```
254
+
255
+ `source-check` is read only. `source-snapshot` reads Relay but records the
256
+ snapshot in the connector state under `--state` and writes `graph.json`,
257
+ `context.json` and `manifest.json` to `--out`. `run --source`,
258
+ `source-deliver` and `source-reconcile` write to Relay: run them only after
259
+ the user says so.
260
+
261
+ ### 8. Commit
262
+
263
+ With the user's yes: commit on the setup branch `.harness/.gitignore`,
264
+ `.harness/README.md`, `.harness/policy.json`, `.harness/connector.json` (when
265
+ present) and, for an npm source inside the project,
266
+ `.harness-tools/.gitignore`, `.harness-tools/package.json`,
267
+ `.harness-tools/package-lock.json`. Never `project.json`, `node_modules/` or
268
+ the state. Do not push or merge.
269
+
270
+ ### 9. Report and hand over
271
+
272
+ Report: installed version (and the tarball sha256), `doctor`, the decisions
273
+ of the manifest (profile, writer, reviewer, state path), the policy edits,
274
+ the connector check, the commit SHA. Then continue with the `harness` skill:
275
+ brief → `plan-request` → draft → `plan-check` → `validate` → `run`. A live
276
+ `run` or `review-plan` calls paid models: start it when the user asks.
277
+
278
+ ## Upgrade
279
+
280
+ An upgrade changes only the package. Policy, profiles, graphs, accepted
281
+ results and `.harness/project.json` stay as they are unless the user decides
282
+ otherwise in step 7.
283
+
284
+ 1. **Current and target.** `$H --version`; target from the user, or
285
+ `npm view devcontour-harness dist-tags`. Install the target into a scratch
286
+ directory outside the repository (`N=<scratch>/node_modules/.bin/harness`)
287
+ and read its `docs/release-notes/<version>.md` and the format matrix of
288
+ `docs/upgrade.md` for every version in between.
289
+ 2. **Stop the writers.** `$H status --state <state>`; `$H pause --state <state>`;
290
+ wait for active attempts to finish or ask the user to stop them; stop every
291
+ `run` process on this state. A paused queue alone is not enough.
292
+ 3. **Record.** `git rev-parse refs/heads/harness/accepted` and
293
+ `$H result --state <state> --work <id>` for the accepted orders.
294
+ 4. **Copy the state** while nothing writes: `cp -Rp <state> <state>.bak-<old version>`.
295
+ 5. **Check on a second copy** with the target version: `$N doctor --state <copy>`
296
+ and `$N status --state <copy>`. Hash the copy's files before and after;
297
+ they must not change. Stop and report when `doctor` shows
298
+ `data.state.compatible: false` or `status` fails with
299
+ `STATE_VERSION_UNSUPPORTED`.
300
+ 6. **Install** into the same `<tooldir>`:
301
+ `npm install --prefix <tooldir> --save-exact --omit=dev --ignore-scripts devcontour-harness@<new>`
302
+ (or the new `.tgz` after its sha256 check).
303
+ 7. **Verify.**
304
+ - `$H --version`; `$H doctor --state <state> --policy <repo>/.harness/policy.json`.
305
+ - `$H init --repository <repo> --plan` must show `data.conflicts` = 0.
306
+ `content-differs` on `.harness/project.json` means a saved decision no
307
+ longer matches: the built-in profile digest changed (compare
308
+ `$H profile --profile <id>` with the pin in `project.json`), the
309
+ repository moved (`root`, `projectId`), or the API version changed.
310
+ Show the difference to the user. With their yes, move the manifest out
311
+ of the repository, next to the state copy of step 4
312
+ (`mv <repo>/.harness/project.json <state>.bak-<old version>/project.json`),
313
+ and run
314
+ `$H init --repository <repo> --profile <saved profile.ref> --writer <saved runtimes.writer> --state <saved paths.state>`
315
+ with the values of the moved manifest, so the profile choice and the state
316
+ path are kept; never edit the manifest by hand.
317
+ - The policy is not regenerated. List gates or defaults of the new profile
318
+ that the policy lacks and add them only with the user's yes; never weaken
319
+ the existing ones. A changed policy changes the facts of planning, so
320
+ graphs may need plan review or approval again (`explain` names it).
321
+ - `$H validate --graph <graph> --policy <repo>/.harness/policy.json` for
322
+ every `.harness/work/*.json` graph.
323
+ 8. **Resume.** `$H resume --state <state>`, then `run` as before. Attempts that
324
+ were active at the stop are not continued with old tokens; recovery either
325
+ accepts a saved integration SHA or ends the attempt.
326
+ 9. **Commit** the changed `.harness-tools/package.json` and
327
+ `package-lock.json` on a branch, with the user's yes.
328
+
329
+ Rollback: stop the writers again; reinstall the old exact version; when the
330
+ new version already wrote state the old one cannot read, restore the copy of
331
+ step 4 and reconcile `harness/accepted` with the recorded SHAs
332
+ (`docs/upgrade.md`, "Откат"). Never `git reset --hard` the user's repository.
333
+
334
+ ## Check
335
+
336
+ Read only: `$H --version`, `$H doctor`, `$H init --repository <repo> --plan`,
337
+ `$H status --state <paths.state>`, and the pin in
338
+ `.harness-tools/package.json` when present. Report a mismatch between the pin
339
+ and `$H --version`, or a missing `node_modules/` (restore with `npm ci`, step
340
+ 4); do not fix it unasked.
341
+
342
+ ## Don'ts
343
+
344
+ - Do not choose for the user: location, source, runtimes, models, the vendor
345
+ rule, approval mode, task source and commits are their answers.
346
+ - Do not install into the project's dependencies, edit its `package.json` or
347
+ lockfile (except when bootstrapping a new project with consent), or put the
348
+ state inside the repository.
349
+ - Do not weaken gates, budgets, isolation or the reviewer rule, and do not set
350
+ `same-vendor-allowed` without an explicit yes.
351
+ - Do not overwrite `.harness/project.json` or `.harness/policy.json` on a
352
+ `content-differs`; that is a decision to show to the user.
353
+ - Do not write to Relay, call live models, push, tag or publish without being
354
+ asked.
355
+ - Do not upgrade while a controller writes the state, and do not skip the
356
+ state copy.