devcontour-harness 0.1.0-alpha.11 → 0.1.0-alpha.13
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 +37 -5
- package/docs/architecture.md +6 -5
- package/docs/connectors.md +26 -0
- package/docs/quickstart.md +1 -1
- package/docs/release-notes/0.1.0-alpha.12.md +66 -0
- package/docs/release-notes/0.1.0-alpha.13.md +91 -0
- package/docs/standalone.md +25 -4
- package/docs/turbo-starter.md +2 -1
- package/docs/upgrade.md +14 -14
- package/examples/turbo-starter/setup.mjs +9 -2
- package/lib/connectors/files.js +2 -0
- package/lib/integrations/connector/commands.d.ts +11 -4
- package/lib/integrations/connector/commands.js +16 -2
- package/lib/integrations/connector/config.d.ts +5 -0
- package/lib/integrations/connector/config.js +18 -1
- package/lib/integrations/connector/index.d.ts +1 -1
- package/lib/integrations/connector/index.js +1 -1
- package/lib/integrations/connector/snapshot.d.ts +22 -1
- package/lib/integrations/connector/snapshot.js +60 -3
- package/lib/integrations/connector/source.d.ts +4 -0
- package/lib/integrations/connector/source.js +1 -0
- package/package.json +1 -1
- package/skills/harness/SKILL.md +17 -15
- package/skills/harness-setup/SKILL.md +478 -0
- package/templates/controller-mode/README.md +5 -0
package/README.md
CHANGED
|
@@ -5,16 +5,17 @@ Headless-движок исполнения инженерных поручени
|
|
|
5
5
|
интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
|
|
6
6
|
и доказательствами.
|
|
7
7
|
|
|
8
|
-
> Статус: `0.1.0-alpha.
|
|
8
|
+
> Статус: `0.1.0-alpha.13` — предварительный выпуск (alpha). Договоры
|
|
9
9
|
> `v1alpha1` могут измениться между alpha-версиями; закрепляйте точную версию.
|
|
10
10
|
> Файл → ревью плана → кандидат → интеграция → принятие → результат JSON, с
|
|
11
11
|
> ограниченным восстановлением, повторным использованием кандидата, бюджетами,
|
|
12
12
|
> потребителями, свойствами и корпусом, совместной проверкой репозиториев и
|
|
13
13
|
> явным режимом одного вендора.
|
|
14
14
|
|
|
15
|
-
Пакет включает встроенные профили стеков (`profiles/`),
|
|
16
|
-
`skills/harness/SKILL.md`
|
|
17
|
-
|
|
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.
|
|
31
|
+
npm install --save-exact devcontour-harness@0.1.0-alpha.13 # точная версия
|
|
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,31 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
|
|
|
117
123
|
`devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
|
|
118
124
|
Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
|
|
119
125
|
|
|
126
|
+
## Выпуск 0.1.0-alpha.13
|
|
127
|
+
|
|
128
|
+
Controller mode для задач Relay и установка без лишних вопросов. Подробно —
|
|
129
|
+
[заметки выпуска](docs/release-notes/0.1.0-alpha.13.md).
|
|
130
|
+
|
|
131
|
+
- `tasks[].context` и `contextDir` коннектора: локальные пакеты контекста
|
|
132
|
+
(оверлей controller-mode) закрепляются в снимке задач Relay.
|
|
133
|
+
- Skill `harness-setup` решает рутинные выборы сам и спрашивает только на
|
|
134
|
+
развилках (вендоры writer/reviewer, недостающие факты Relay, изоляция,
|
|
135
|
+
Docker, конфликты с правилами проекта, коммит, запись в Relay, живые
|
|
136
|
+
вызовы); маршрут для pnpm/Turborepo-монорепо (Turbo Starter): свой
|
|
137
|
+
профиль, gates из команд проекта, controller mode.
|
|
138
|
+
|
|
139
|
+
## Выпуск 0.1.0-alpha.12
|
|
140
|
+
|
|
141
|
+
Skill установки и обновления. Подробно —
|
|
142
|
+
[заметки выпуска](docs/release-notes/0.1.0-alpha.12.md).
|
|
143
|
+
|
|
144
|
+
- `skills/harness-setup/SKILL.md` — агент один раз спрашивает решения первой
|
|
145
|
+
настройки (новый или существующий проект, ветка, каталог установки —
|
|
146
|
+
`.harness-tools/` в проекте или снаружи, источник пакета, writer и reviewer
|
|
147
|
+
с моделями, согласование, источник задач: файлы или Relay), ставит точную
|
|
148
|
+
версию, выполняет `init` и обновляет версию с копией state и проверкой
|
|
149
|
+
совместимости. Движок, договоры и формат состояния не менялись.
|
|
150
|
+
|
|
120
151
|
## Выпуск 0.1.0-alpha.11
|
|
121
152
|
|
|
122
153
|
Профили в форме хоста. Подробно —
|
|
@@ -226,6 +257,7 @@ alpha.5 не читает; прежний DevContour его открывает
|
|
|
226
257
|
## Документация
|
|
227
258
|
|
|
228
259
|
- [Быстрый старт по шагам](docs/quickstart.md)
|
|
260
|
+
- [Установка, первая настройка и обновление версии — skill harness-setup](skills/harness-setup/SKILL.md)
|
|
229
261
|
- [Самостоятельный проект: init, планирование (plan-request, plan-check), explain, capabilities, проекции](docs/standalone.md)
|
|
230
262
|
- [Архитектура и границы доверия](docs/architecture.md)
|
|
231
263
|
- [Договоры: входы, выходы, порты, ошибки, CLI](docs/contracts.md)
|
package/docs/architecture.md
CHANGED
|
@@ -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`.
|
|
78
|
-
|
|
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/`),
|
|
417
|
-
|
|
418
|
-
|
|
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
|
Профиль — вход для сборки политики, а не исполняемый объект: разрешение не
|
package/docs/connectors.md
CHANGED
|
@@ -109,14 +109,40 @@ CLI: 0 — успех, 2 — неверный вход, 3 — отказ/бло
|
|
|
109
109
|
| `projectId` | Ожидаемая идентичность проекта backend; несовпадение блокирует чтение снимка и любые мутации |
|
|
110
110
|
| `actor` | Автор всех записей коннектора |
|
|
111
111
|
| `stateDir` | Каталог состояния коннектора (относительно файла); без него — `--state` |
|
|
112
|
+
| `contextDir` | Каталог локальных пакетов контекста `tasks[].context` (относительно файла); по умолчанию `work/context` |
|
|
112
113
|
| `repository` | ID репозитория политики (и путь для сверки) |
|
|
113
114
|
| `tasks[]` | Задачи: `ref` (постоянный ID или ключ), роль, `writeScope`, `gates`, сопоставление критериев, обязательные документы |
|
|
114
115
|
| `tasks[].criteria[]` | Критерий (`id` или точный `title`) → `gate` и `testId` JUnit; `evidence: "gate"` — достаточно прошедшего gate (явный выбор) |
|
|
116
|
+
| `tasks[].context` | Локальные пакеты контекста поручения — каталоги `<contextDir>/<id>` (например, `controller-mode`); закрепляются в снимке |
|
|
115
117
|
| `completion` | `complete` — закрыть по правилам backend (по умолчанию в `done`); `review-only` — перевести в `review`/`column` и оставить человеку; `historicalComment` — при конфликте постановки записать явно исторический комментарий |
|
|
116
118
|
| `eligibility.columns` | Колонки, из которых задача допускается к запуску (`done`/`cancelled` — никогда) |
|
|
117
119
|
| `limits` | Пределы документов и постановки, число повторных чтений, срок владения доставкой |
|
|
118
120
|
| `history` | Запись истории работы (корневая и дочерние сессии) |
|
|
119
121
|
|
|
122
|
+
### Локальные пакеты контекста
|
|
123
|
+
|
|
124
|
+
`tasks[].context` добавляет к поручению задачи пакеты из `<contextDir>/<id>` —
|
|
125
|
+
те же каталоги, что у файлового графа: файлы вкладываются в запросы writer и
|
|
126
|
+
reviewer, `references/` закрепляется digest без вложения. Так к задачам Relay
|
|
127
|
+
подключается оверлей controller-mode
|
|
128
|
+
(`templates/controller-mode/`): `"context": ["controller-mode"]`.
|
|
129
|
+
|
|
130
|
+
- Пакеты закрепляются в `context.json` снимка; поручение получает
|
|
131
|
+
`context: [<контекст задачи>, ...пакеты]`. `source-snapshot` и manifest
|
|
132
|
+
называют их в `localContext` (ID, digest, число файлов и справок, байты).
|
|
133
|
+
- Digest пакетов и их распределение по задачам (какие у какой, в каком
|
|
134
|
+
порядке) входят в ревизию набора задач: изменённый, новый или перенесённый
|
|
135
|
+
пакет — новый `source-snapshot`, ревью плана и согласование (`run --source`
|
|
136
|
+
иначе отказывает `APPROVAL_STALE`). Semantic digest задачи от пакетов не
|
|
137
|
+
зависит: включение поля или изменение пакета не делает доставку уже
|
|
138
|
+
принятого результата конфликтом.
|
|
139
|
+
- Нет каталога или пакета — `NOT_READY` до чтения backend; пакет через
|
|
140
|
+
symlink — `INVALID_INPUT`. `source-check` называет то и другое в `problems`.
|
|
141
|
+
- Пределы контекста (файл, сумма, не больше 30 источников вместе с
|
|
142
|
+
контекстами задач) проверяются при снимке: превышение — `LIMIT_EXCEEDED`, и
|
|
143
|
+
задача не допускается к запуску.
|
|
144
|
+
- Без поля снимок, ревизия и digest задачи те же, что в прежних версиях.
|
|
145
|
+
|
|
120
146
|
### Идентичность и снимок
|
|
121
147
|
|
|
122
148
|
- Идентичность задачи — `<provider>:<projectId>:<постоянный ID>`; ID поручения
|
package/docs/quickstart.md
CHANGED
|
@@ -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).
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# devcontour-harness 0.1.0-alpha.13
|
|
2
|
+
|
|
3
|
+
Controller mode для задач Relay и маршрут установки, который спрашивает только
|
|
4
|
+
на развилках. Gates, `maxAttempts`, бюджеты попыток и повторов, правила
|
|
5
|
+
находок, изоляция, принятие target ref и формат состояния не менялись.
|
|
6
|
+
|
|
7
|
+
## Что нового
|
|
8
|
+
|
|
9
|
+
- **`tasks[].context` и `contextDir` коннектора.** Задача Relay получает,
|
|
10
|
+
кроме своего контекста, локальные пакеты из `<contextDir>/<id>` (по
|
|
11
|
+
умолчанию `work/context` рядом с `connector.json`) — например, оверлей
|
|
12
|
+
controller-mode из `templates/controller-mode/`. Пакет закрепляется в
|
|
13
|
+
`context.json` снимка теми же правилами, что файловый контекст графа
|
|
14
|
+
(`references/` — справки без вложения). Digest пакетов и их распределение
|
|
15
|
+
по задачам входят в ревизию набора задач: изменённый пакет требует нового `source-snapshot` и
|
|
16
|
+
согласования (`run --source` иначе отказывает `APPROVAL_STALE`). Semantic
|
|
17
|
+
digest задачи от пакетов не зависит: включение поля или изменение пакета не
|
|
18
|
+
делает доставку принятого результата конфликтом. Нет каталога или пакета —
|
|
19
|
+
`NOT_READY` до чтения backend; `source-check` называет его в `problems`
|
|
20
|
+
([connectors](../connectors.md#локальные-пакеты-контекста)).
|
|
21
|
+
- **Пределы контекста при снимке.** `source-snapshot` проверяет пределы
|
|
22
|
+
снимка контекста (размер файла, сумма, не больше 30 источников) и сообщает
|
|
23
|
+
превышение как `LIMIT_EXCEEDED`, а не только отказом при запуске;
|
|
24
|
+
`source-check` проверяет пределы локальных пакетов. Вывод
|
|
25
|
+
`source-snapshot` и manifest называют закреплённые пакеты (`localContext`).
|
|
26
|
+
- **`controllerContext`** (`examples/turbo-starter/setup.mjs`) пересобирает
|
|
27
|
+
пакет с пустого каталога и не берёт в справки `.harness/` и сам каталог
|
|
28
|
+
пакета: повторная сборка внутри репозитория не вкладывает копии пакета и
|
|
29
|
+
не оставляет справки удалённых AGENTS.md. `upstream: null` — сборка для
|
|
30
|
+
обычного репозитория проекта.
|
|
31
|
+
- **Skill `harness-setup`:**
|
|
32
|
+
- таблица «решить или спросить»: рутинные выборы (ветка, каталог
|
|
33
|
+
установки, источник пакета, профиль, gates, согласование, completion)
|
|
34
|
+
агент делает сам и объясняет в отчёте; спрашивает о вендорах
|
|
35
|
+
writer/reviewer, ревью тем же вендором, controller mode (оверлей
|
|
36
|
+
главнее правил продукта), пропуске проверки проекта или CI, недостающих
|
|
37
|
+
фактах Relay, изоляции, Docker, конфликтах с правилами проекта, коммите,
|
|
38
|
+
записи в Relay и живых вызовах;
|
|
39
|
+
- pnpm/Turborepo-монорепо (Turbo Starter): локальный `.harness/profile.json`
|
|
40
|
+
из команд проекта (`pnpm --filter`), test-gates с JUnit без новых
|
|
41
|
+
зависимостей или приёмочные проверки в `.harness/acceptance/`, gate
|
|
42
|
+
детерминизма генерации, защищённые пути, среда с Docker только по
|
|
43
|
+
согласию;
|
|
44
|
+
- controller mode: пакет собирается `controllerContext`, в Git не
|
|
45
|
+
коммитится (`.git/info/exclude`, пересборка в новом клоне) и
|
|
46
|
+
подключается к файловым графам и к задачам Relay (`tasks[].context`);
|
|
47
|
+
- при обновлении — предложить новые возможности выпуска, не применяя их.
|
|
48
|
+
|
|
49
|
+
## Изменения договора
|
|
50
|
+
|
|
51
|
+
Добавления в конфигурацию коннектора: `contextDir` (строка) и
|
|
52
|
+
`tasks[].context` (1–10 ID); в manifest снимка и ответ `source-snapshot` —
|
|
53
|
+
`localContext` (только при пакетах). Без них снимок, ревизия набора, semantic
|
|
54
|
+
digest задачи, digest графа и контекста те же, что в alpha.12. Пакет с именем
|
|
55
|
+
вида ID контекста задачи (`c` + 20 hex) — `INVALID_INPUT`.
|
|
56
|
+
|
|
57
|
+
Изменение отказа: корень источника контекста через symlink (в файловом
|
|
58
|
+
графе и в пакетах коннектора) — `INVALID_INPUT` «Контекст не читается через
|
|
59
|
+
symlink» вместо `NOT_READY` «не найден»; symlink внутри источника и раньше
|
|
60
|
+
давал `INVALID_INPUT`.
|
|
61
|
+
|
|
62
|
+
## Совместимость
|
|
63
|
+
|
|
64
|
+
Формат состояния не менялся: alpha.13 читает всё, что читает alpha.12, а
|
|
65
|
+
alpha.12 — всё, что пишет alpha.13. Конфигурация коннектора с новыми полями
|
|
66
|
+
alpha.12 не принимает (строгая схема). Обновление и откат — по
|
|
67
|
+
[upgrade](../upgrade.md).
|
|
68
|
+
|
|
69
|
+
## Проверка выпуска
|
|
70
|
+
|
|
71
|
+
`npm run check` (lint, typecheck, schemas, build, весь набор тестов, package
|
|
72
|
+
smoke из установленного архива) на macOS arm64, Node.js 24.3. Тесты
|
|
73
|
+
коннектора на подделке backend (`tests/connector-context.test.ts`):
|
|
74
|
+
|
|
75
|
+
- пакет закреплён в снимке и поручении, его байты в контексте попытки
|
|
76
|
+
writer; поручение принято и доставлено;
|
|
77
|
+
- без поля ревизия набора, digest задачи, графа и контекста совпадают со
|
|
78
|
+
значениями alpha.12 (golden-тест, сверен с кодом тега);
|
|
79
|
+
- изменённый пакет — `APPROVAL_STALE` до запуска; перенос пакета между
|
|
80
|
+
задачами и смена порядка — новая ревизия; больше 30 источников —
|
|
81
|
+
`LIMIT_EXCEEDED`; доставка принятого
|
|
82
|
+
результата после изменения пакета и после включения поля — не конфликт;
|
|
83
|
+
- нет каталога — отказ без обращений к backend; нет пакета, свой
|
|
84
|
+
`contextDir`, конфликт имени, symlink-пакет, превышение пределов в снимке
|
|
85
|
+
и `source-check`, `localContext` в manifest.
|
|
86
|
+
|
|
87
|
+
Пересборка `controllerContext` после коммита пакета и удаления AGENTS.md —
|
|
88
|
+
`tests/starter-setup.test.ts`. Тест skill сверяет команды, флаги,
|
|
89
|
+
обязательные аргументы и пути `<pkg>/…` с поставкой. Живые модели и
|
|
90
|
+
настоящие Relay не вызывались; W01 на настоящих Relay для alpha.13 не
|
|
91
|
+
повторялся.
|
package/docs/standalone.md
CHANGED
|
@@ -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
|
|
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/turbo-starter.md
CHANGED
|
@@ -224,7 +224,8 @@ controller. Оверлей `templates/controller-mode/AGENTS.controller.md` v1
|
|
|
224
224
|
читает живые файлы своего worktree;
|
|
225
225
|
- `context/controller-mode/provenance.json` содержит версию и sha256
|
|
226
226
|
оверлея, baseline SHA и blob каждого product AGENTS.md;
|
|
227
|
-
- в каждом поручении указан `"context": ["controller-mode"]
|
|
227
|
+
- в каждом поручении указан `"context": ["controller-mode"]`; для задач
|
|
228
|
+
Relay — в `tasks[].context` коннектора ([connectors](connectors.md#локальные-пакеты-контекста)).
|
|
228
229
|
|
|
229
230
|
Оверлей перечисляет правила, делегированные controller-у: worktree, commit,
|
|
230
231
|
merge, история Relay, `pnpm run setup`, вложенные оркестраторы и `harness`.
|
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.
|
|
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 | Пишет поверх | Примечание |
|
|
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.13 формат состояния не меняют и читают всё, что читает alpha.7. Каждая следующая версия добавляет сюда свой столбец. Отказ неизвестного
|
|
38
38
|
формата до записи проверяют тесты совместимости пакета
|
|
39
39
|
<!-- repo-only -->(`tests/compatibility.test.ts`)<!-- /repo-only -->.
|
|
40
40
|
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
writeFileSync,
|
|
35
35
|
} from 'node:fs';
|
|
36
36
|
import { tmpdir } from 'node:os';
|
|
37
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
37
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
38
38
|
import { fileURLToPath } from 'node:url';
|
|
39
39
|
import { parseArgs } from 'node:util';
|
|
40
40
|
|
|
@@ -257,10 +257,17 @@ export function changeFiles(fixture, patch) {
|
|
|
257
257
|
*/
|
|
258
258
|
export function controllerContext(fixture, dir) {
|
|
259
259
|
const target = join(dir, 'controller-mode');
|
|
260
|
+
// Пересборка начинается с пустого каталога: справки удалённых AGENTS.md не
|
|
261
|
+
// остаются, а закоммиченный пакет и .harness/ не попадают в свои же справки.
|
|
262
|
+
rmSync(target, { recursive: true, force: true });
|
|
260
263
|
mkdirSync(join(target, 'references'), { recursive: true });
|
|
261
264
|
const overlay = readFileSync(join(CONTROLLER_TEMPLATE, 'AGENTS.controller.md'));
|
|
262
265
|
writeFileSync(join(target, 'AGENTS.controller.md'), overlay);
|
|
263
|
-
const
|
|
266
|
+
const inside = relative(fixture.repo, resolve(target));
|
|
267
|
+
const exclude = [':(exclude).harness/**'];
|
|
268
|
+
if (inside && !inside.startsWith('..') && !isAbsolute(inside))
|
|
269
|
+
exclude.push(`:(exclude)${inside.split(sep).join('/')}/**`);
|
|
270
|
+
const product = git(fixture.repo, ['ls-files', '--', 'AGENTS.md', '**/AGENTS.md', ...exclude])
|
|
264
271
|
.split('\n')
|
|
265
272
|
.filter(Boolean);
|
|
266
273
|
const references = product.map((path) => {
|
package/lib/connectors/files.js
CHANGED
|
@@ -71,6 +71,8 @@ export class FileContextSource {
|
|
|
71
71
|
const base = realpathSync(this.dir);
|
|
72
72
|
const sources = [...new Set(ids)].map((id) => {
|
|
73
73
|
const root = resolve(base, id);
|
|
74
|
+
if (root.startsWith(base + sep) && existsSync(root) && lstatSync(root).isSymbolicLink())
|
|
75
|
+
throw new HarnessError('INVALID_INPUT', `Контекст не читается через symlink: ${id}`);
|
|
74
76
|
if (!root.startsWith(base + sep) || !existsSync(root) || !lstatSync(root).isDirectory())
|
|
75
77
|
throw new HarnessError('NOT_READY', `Источник контекста ${id} не найден в ${this.dir}`);
|
|
76
78
|
const pin = (path) => {
|
|
@@ -63,6 +63,7 @@ export declare class Connector {
|
|
|
63
63
|
requiredDocuments: string[];
|
|
64
64
|
repositoryId?: string | undefined;
|
|
65
65
|
gates?: string[] | undefined;
|
|
66
|
+
context?: string[] | undefined;
|
|
66
67
|
}[];
|
|
67
68
|
completion: {
|
|
68
69
|
mode: "complete" | "review-only";
|
|
@@ -85,6 +86,7 @@ export declare class Connector {
|
|
|
85
86
|
client: string;
|
|
86
87
|
};
|
|
87
88
|
stateDir?: string | undefined;
|
|
89
|
+
contextDir?: string | undefined;
|
|
88
90
|
} | {
|
|
89
91
|
provider: "relay-v2-cli";
|
|
90
92
|
relay: Record<string, unknown> & {
|
|
@@ -115,6 +117,7 @@ export declare class Connector {
|
|
|
115
117
|
requiredDocuments: string[];
|
|
116
118
|
repositoryId?: string | undefined;
|
|
117
119
|
gates?: string[] | undefined;
|
|
120
|
+
context?: string[] | undefined;
|
|
118
121
|
}[];
|
|
119
122
|
completion: {
|
|
120
123
|
mode: "complete" | "review-only";
|
|
@@ -137,7 +140,10 @@ export declare class Connector {
|
|
|
137
140
|
client: string;
|
|
138
141
|
};
|
|
139
142
|
stateDir?: string | undefined;
|
|
143
|
+
contextDir?: string | undefined;
|
|
140
144
|
};
|
|
145
|
+
/** Каталог пакетов `tasks[].context`: `contextDir` конфигурации, иначе `work/context` рядом с ней. */
|
|
146
|
+
get contextDir(): string;
|
|
141
147
|
/** Состояние коннектора (создаётся при первой записи). */
|
|
142
148
|
get store(): ConnectorStore;
|
|
143
149
|
/** Только чтение: без создания файла; нет состояния — `undefined`. */
|
|
@@ -186,10 +192,6 @@ export declare class Connector {
|
|
|
186
192
|
* semantic digest: повтор неизменной постановки ничего не меняет.
|
|
187
193
|
*/
|
|
188
194
|
snapshot(outDir?: string): Promise<{
|
|
189
|
-
connector: string;
|
|
190
|
-
graphId: string;
|
|
191
|
-
graphDigest: string;
|
|
192
|
-
contextDigest: string;
|
|
193
195
|
revision: string;
|
|
194
196
|
created: boolean;
|
|
195
197
|
eligible: boolean;
|
|
@@ -209,6 +211,11 @@ export declare class Connector {
|
|
|
209
211
|
manifest: string;
|
|
210
212
|
};
|
|
211
213
|
snapshot: import("./snapshot.ts").BuiltSnapshot;
|
|
214
|
+
localContext?: import("./snapshot.ts").LocalContextManifest[] | undefined;
|
|
215
|
+
connector: string;
|
|
216
|
+
graphId: string;
|
|
217
|
+
graphDigest: string;
|
|
218
|
+
contextDigest: string;
|
|
212
219
|
}>;
|
|
213
220
|
/**
|
|
214
221
|
* Перед запуском — свежее чтение: semantic digest обязан совпасть с
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import { HarnessError, } from "../../contracts/index.js";
|
|
3
|
+
import { API_VERSION, HarnessError, } from "../../contracts/index.js";
|
|
4
4
|
import { redactor } from "../../environment/redaction.js";
|
|
5
5
|
import { connectorIdOf, loadConfig, orderIdOf, resolveFrom, storeFileOf, } from "./config.js";
|
|
6
6
|
import { RelaySink } from "./delivery.js";
|
|
7
7
|
import { HistoryRecorder } from "./history.js";
|
|
8
8
|
import { createTransportFor } from "./registry.js";
|
|
9
|
-
import { assertEligible, checkIdentity, matchCriterion, readStable, semanticDigest, } from "./snapshot.js";
|
|
9
|
+
import { assertEligible, checkIdentity, contextLimitIssues, localContext, matchCriterion, readStable, semanticDigest, } from "./snapshot.js";
|
|
10
10
|
import { RelayWorkSource } from "./source.js";
|
|
11
11
|
import { redactingTransport, settingsSecrets } from "./redact.js";
|
|
12
12
|
import { ConnectorStore } from "./store.js";
|
|
@@ -46,6 +46,10 @@ export class Connector {
|
|
|
46
46
|
get config() {
|
|
47
47
|
return this.loaded.config;
|
|
48
48
|
}
|
|
49
|
+
/** Каталог пакетов `tasks[].context`: `contextDir` конфигурации, иначе `work/context` рядом с ней. */
|
|
50
|
+
get contextDir() {
|
|
51
|
+
return resolveFrom(this.loaded.baseDir, this.config.contextDir ?? 'work/context');
|
|
52
|
+
}
|
|
49
53
|
/** Состояние коннектора (создаётся при первой записи). */
|
|
50
54
|
get store() {
|
|
51
55
|
this.storeInstance ??= new ConnectorStore(this.storeFile, { provider: this.config.provider, projectId: this.config.projectId }, this.options.onStoreWrite ? { onWrite: this.options.onStoreWrite } : {});
|
|
@@ -64,6 +68,7 @@ export class Connector {
|
|
|
64
68
|
config: this.config,
|
|
65
69
|
transport: this.transport,
|
|
66
70
|
store: this.store,
|
|
71
|
+
contextDir: this.contextDir,
|
|
67
72
|
});
|
|
68
73
|
}
|
|
69
74
|
get history() {
|
|
@@ -114,6 +119,14 @@ export class Connector {
|
|
|
114
119
|
return this.checkReport(undefined, false, [message], []);
|
|
115
120
|
}
|
|
116
121
|
const mapping = [];
|
|
122
|
+
try {
|
|
123
|
+
const local = await localContext(config, this.contextDir);
|
|
124
|
+
if (local.length)
|
|
125
|
+
problems.push(...contextLimitIssues({ apiVersion: API_VERSION, sources: local, missing: [] }));
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
problems.push(this.redact(error instanceof Error ? error.message : String(error)));
|
|
129
|
+
}
|
|
117
130
|
try {
|
|
118
131
|
const { reads } = await readStable(this.transport, config.tasks.map((t) => t.ref), config.limits.rereads);
|
|
119
132
|
reads.forEach((read, i) => {
|
|
@@ -202,6 +215,7 @@ export class Connector {
|
|
|
202
215
|
graphId: snapshot.graph.id,
|
|
203
216
|
graphDigest: snapshot.manifest.graphDigest,
|
|
204
217
|
contextDigest: snapshot.manifest.contextDigest,
|
|
218
|
+
...(snapshot.manifest.localContext ? { localContext: snapshot.manifest.localContext } : {}),
|
|
205
219
|
revision: snapshot.revision,
|
|
206
220
|
created,
|
|
207
221
|
eligible: snapshot.problems.length === 0,
|