devcontour-harness 0.1.0-alpha.5 → 0.1.0-alpha.6
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 +24 -2
- package/docs/compatibility-versions.md +56 -0
- package/docs/compatibility.md +2 -2
- package/docs/connectors.md +323 -0
- package/docs/contracts.md +33 -5
- package/docs/integrations/hindsight.md +148 -0
- package/docs/quickstart.md +1 -1
- package/docs/release-notes/0.1.0-alpha.6.md +238 -0
- package/docs/review-probes.md +120 -0
- package/docs/review-protocol.md +251 -0
- package/docs/toolchains.md +167 -0
- package/docs/turbo-starter.md +269 -0
- package/docs/upgrade.md +48 -12
- package/examples/README.md +1 -0
- package/examples/toolchains/README.md +32 -0
- package/examples/toolchains/go/bad/label/label.go +9 -0
- package/examples/toolchains/go/good/label/label.go +9 -0
- package/examples/toolchains/go/policy.json +79 -0
- package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/go/repo/_gitignore +1 -0
- package/examples/toolchains/go/repo/go.mod +5 -0
- package/examples/toolchains/go/repo/go.sum +2 -0
- package/examples/toolchains/go/repo/label/label.go +9 -0
- package/examples/toolchains/go/repo/label/label_test.go +27 -0
- package/examples/toolchains/go/repo/label/version.go +9 -0
- package/examples/toolchains/go/task.json +25 -0
- package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/policy.json +126 -0
- package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
- package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-go/task.json +25 -0
- package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/policy.json +121 -0
- package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
- package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
- package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
- package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
- package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-python/task.json +25 -0
- package/examples/toolchains/npm/bad/src/label.js +4 -0
- package/examples/toolchains/npm/good/src/label.js +4 -0
- package/examples/toolchains/npm/policy.json +82 -0
- package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/npm/repo/_gitignore +4 -0
- package/examples/toolchains/npm/repo/package-lock.json +22 -0
- package/examples/toolchains/npm/repo/package.json +13 -0
- package/examples/toolchains/npm/repo/src/label.js +4 -0
- package/examples/toolchains/npm/repo/test/label.test.js +13 -0
- package/examples/toolchains/npm/task.json +25 -0
- package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/policy.json +85 -0
- package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/pnpm/repo/_gitignore +4 -0
- package/examples/toolchains/pnpm/repo/package.json +10 -0
- package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
- package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
- package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/pnpm/task.json +25 -0
- package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/policy.json +80 -0
- package/examples/toolchains/python/repo/.python-version +1 -0
- package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/python/repo/_gitignore +5 -0
- package/examples/toolchains/python/repo/pyproject.toml +19 -0
- package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/repo/tests/test_label.py +13 -0
- package/examples/toolchains/python/repo/uv.lock +108 -0
- package/examples/toolchains/python/task.json +25 -0
- package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/policy.json +110 -0
- package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/turbo/repo/_gitignore +4 -0
- package/examples/toolchains/turbo/repo/package.json +13 -0
- package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
- package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
- package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/turbo/repo/turbo.json +7 -0
- package/examples/toolchains/turbo/task.json +25 -0
- package/examples/turbo-starter/README.md +16 -0
- package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
- package/examples/turbo-starter/changes/issued-at.patch +130 -0
- package/examples/turbo-starter/cleanup.mjs +58 -0
- package/examples/turbo-starter/graph.json +32 -0
- package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
- package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
- package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
- package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
- package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
- package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
- package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
- package/examples/turbo-starter/harness/env/backend.env.template +7 -0
- package/examples/turbo-starter/harness/env/compose.env.template +6 -0
- package/examples/turbo-starter/policy.json +109 -0
- package/examples/turbo-starter/policy.lifecycle.json +114 -0
- package/examples/turbo-starter/setup.mjs +352 -0
- package/lib/agents/cli-adapter.js +2 -0
- package/lib/agents/review-probe.d.ts +179 -7
- package/lib/agents/review-probe.js +648 -55
- package/lib/agents/schemas.d.ts +3 -1
- package/lib/agents/schemas.js +7 -3
- package/lib/cli.js +167 -7
- package/lib/contracts/cli.d.ts +820 -11
- package/lib/contracts/cli.js +243 -4
- package/lib/contracts/errors.d.ts +3 -1
- package/lib/contracts/errors.js +2 -0
- package/lib/contracts/events.d.ts +37 -5
- package/lib/contracts/events.js +9 -0
- package/lib/contracts/execution.d.ts +176 -8
- package/lib/contracts/execution.js +9 -1
- package/lib/contracts/failure.js +5 -0
- package/lib/contracts/index.d.ts +1 -0
- package/lib/contracts/index.js +1 -0
- package/lib/contracts/policy.d.ts +15 -0
- package/lib/contracts/policy.js +10 -0
- package/lib/contracts/ports.d.ts +50 -0
- package/lib/contracts/profiles.d.ts +14 -0
- package/lib/contracts/profiles.js +4 -0
- package/lib/contracts/published.d.ts +230 -7
- package/lib/contracts/result.d.ts +171 -0
- package/lib/contracts/result.js +39 -1
- package/lib/contracts/review.d.ts +231 -0
- package/lib/contracts/review.js +326 -2
- package/lib/contracts/toolchain.d.ts +96 -0
- package/lib/contracts/toolchain.js +89 -0
- package/lib/control/doctor.d.ts +16 -1
- package/lib/control/doctor.js +45 -3
- package/lib/control/harness.d.ts +52 -6
- package/lib/control/harness.js +28 -1
- package/lib/control/recovery.d.ts +3 -1
- package/lib/control/review.js +14 -6
- package/lib/engine/classify.js +1 -0
- package/lib/engine/engine.d.ts +39 -15
- package/lib/engine/prompt.d.ts +29 -1
- package/lib/engine/prompt.js +57 -0
- package/lib/engine/property-recovery.js +2 -0
- package/lib/engine/result.js +52 -0
- package/lib/engine/reuse.js +6 -1
- package/lib/engine/runner.d.ts +25 -0
- package/lib/engine/runner.js +491 -122
- package/lib/environment/lifecycle.js +6 -0
- package/lib/environment/live-calls.js +3 -2
- package/lib/environment/os-tools.d.ts +16 -0
- package/lib/environment/os-tools.js +35 -0
- package/lib/environment/process.js +5 -4
- package/lib/integrations/connector/commands.d.ts +358 -0
- package/lib/integrations/connector/commands.js +428 -0
- package/lib/integrations/connector/config.d.ts +215 -0
- package/lib/integrations/connector/config.js +214 -0
- package/lib/integrations/connector/delivery.d.ts +107 -0
- package/lib/integrations/connector/delivery.js +591 -0
- package/lib/integrations/connector/fake.d.ts +291 -0
- package/lib/integrations/connector/fake.js +786 -0
- package/lib/integrations/connector/history.d.ts +124 -0
- package/lib/integrations/connector/history.js +497 -0
- package/lib/integrations/connector/index.d.ts +16 -0
- package/lib/integrations/connector/index.js +16 -0
- package/lib/integrations/connector/redact.d.ts +19 -0
- package/lib/integrations/connector/redact.js +91 -0
- package/lib/integrations/connector/registry.d.ts +15 -0
- package/lib/integrations/connector/registry.js +43 -0
- package/lib/integrations/connector/snapshot.d.ts +131 -0
- package/lib/integrations/connector/snapshot.js +422 -0
- package/lib/integrations/connector/source.d.ts +37 -0
- package/lib/integrations/connector/source.js +73 -0
- package/lib/integrations/connector/steps.d.ts +71 -0
- package/lib/integrations/connector/steps.js +188 -0
- package/lib/integrations/connector/store.d.ts +140 -0
- package/lib/integrations/connector/store.js +277 -0
- package/lib/integrations/connector/summary.d.ts +38 -0
- package/lib/integrations/connector/summary.js +59 -0
- package/lib/integrations/connector/testing-worker.d.ts +1 -0
- package/lib/integrations/connector/testing-worker.js +81 -0
- package/lib/integrations/connector/testing.d.ts +205 -0
- package/lib/integrations/connector/testing.js +1615 -0
- package/lib/integrations/connector/transport.d.ts +240 -0
- package/lib/integrations/connector/transport.js +28 -0
- package/lib/integrations/memory/context.d.ts +120 -0
- package/lib/integrations/memory/context.js +239 -0
- package/lib/integrations/memory/index.d.ts +8 -0
- package/lib/integrations/memory/index.js +8 -0
- package/lib/integrations/memory/provider.d.ts +79 -0
- package/lib/integrations/memory/provider.js +22 -0
- package/lib/integrations/memory/sink.d.ts +56 -0
- package/lib/integrations/memory/sink.js +116 -0
- package/lib/integrations/memory/testing.d.ts +44 -0
- package/lib/integrations/memory/testing.js +61 -0
- package/lib/integrations/relay-v1/index.d.ts +122 -0
- package/lib/integrations/relay-v1/index.js +860 -0
- package/lib/integrations/relay-v2/index.d.ts +145 -0
- package/lib/integrations/relay-v2/index.js +752 -0
- package/lib/integrations/relay-v2/process.d.ts +32 -0
- package/lib/integrations/relay-v2/process.js +112 -0
- package/lib/integrations/relay-v2/protocol.d.ts +172 -0
- package/lib/integrations/relay-v2/protocol.js +142 -0
- package/lib/profiles/resolve.d.ts +5 -1
- package/lib/profiles/resolve.js +31 -0
- package/lib/storage/document.d.ts +60 -12
- package/lib/toolchains/index.d.ts +9 -0
- package/lib/toolchains/index.js +9 -0
- package/lib/toolchains/inspect.d.ts +105 -0
- package/lib/toolchains/inspect.js +831 -0
- package/lib/toolchains/manifests.d.ts +67 -0
- package/lib/toolchains/manifests.js +238 -0
- package/lib/toolchains/probe.d.ts +28 -0
- package/lib/toolchains/probe.js +54 -0
- package/lib/toolchains/recipes.d.ts +33 -0
- package/lib/toolchains/recipes.js +51 -0
- package/lib/verification/acceptance.d.ts +16 -0
- package/lib/verification/acceptance.js +16 -0
- package/lib/verification/gates.d.ts +16 -0
- package/lib/verification/gates.js +44 -3
- package/package.json +22 -1
- package/profiles/go-gotestsum.json +21 -0
- package/profiles/npm-ci.json +22 -0
- package/profiles/pnpm-frozen.json +24 -0
- package/profiles/python-uv.json +26 -0
- package/schemas/execution-document.schema.json +155 -0
- package/schemas/execution-policy.schema.json +48 -0
- package/schemas/result-envelope.schema.json +213 -1
- package/schemas/run-event.schema.json +93 -0
- package/schemas/run-result.schema.json +207 -0
- package/schemas/stack-profile.schema.json +43 -0
- package/templates/controller-mode/AGENTS.controller.md +58 -0
- package/templates/controller-mode/README.md +44 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Внешняя память (Hindsight): точка подключения
|
|
2
|
+
|
|
3
|
+
Статус: **контракт и проверка поддельным провайдером**. Готового
|
|
4
|
+
Hindsight-коннектора в harness нет: нет HTTP-клиента, SDK, MCP-сервера,
|
|
5
|
+
развёртывания сервера и платных обращений. Harness работает без памяти;
|
|
6
|
+
память подключается явно через opt-in entrypoint
|
|
7
|
+
`devcontour-harness/integrations/memory` (исходники — `src/integrations/memory/`).
|
|
8
|
+
Корень пакета и `devcontour-harness/contracts` этот модуль не импортируют
|
|
9
|
+
<!-- repo-only -->(проверяется `tests/memory-availability.test.ts`, `M02/boundary`)<!-- /repo-only -->. Глобально
|
|
10
|
+
|
|
11
|
+
установленный MCP или Hindsight SDK для пользователей harness не требуется.
|
|
12
|
+
|
|
13
|
+
## Что есть
|
|
14
|
+
|
|
15
|
+
| Часть | API | Назначение |
|
|
16
|
+
| ------------------ | ----------------------------------------------------------- | ------------------------------------------------------------- |
|
|
17
|
+
| Договор провайдера | `MemoryRecallProvider`, `MemoryRetainProvider` | два метода, которые реализует будущий адаптер |
|
|
18
|
+
| Источник контекста | `memoryContextSource(provider, options)` | recall → источник `ContextSnapshot` (`kind: memory`) |
|
|
19
|
+
| Композиция | `composeContext(parts, at)`, `mergeSnapshots(...)` | файлы + память в одном снимке; повтор ID — отказ |
|
|
20
|
+
| Получатель итога | `memoryResultSink(provider, options)` | обычный `ResultSink` с собственным ключом доставки |
|
|
21
|
+
| Тестовый провайдер | `src/integrations/memory/testing.ts` (`FakeMemoryProvider`) | детерминированные ответы, счётчики, отказы; только для тестов |
|
|
22
|
+
|
|
23
|
+
Изменений ядра нет: используются существующие `ContextSource`, `ResultSink`,
|
|
24
|
+
закрепление снимка при согласовании (`context-snapshots` + `approval.context`)
|
|
25
|
+
и записи `delivery` у `Harness.deliver`.
|
|
26
|
+
|
|
27
|
+
## Recall до согласования
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { FileContextSource } from 'devcontour-harness';
|
|
31
|
+
import { composeContext, memoryContextSource } from 'devcontour-harness/integrations/memory';
|
|
32
|
+
|
|
33
|
+
const memory = memoryContextSource(provider, {
|
|
34
|
+
id: 'memory', // ID источника в снимке
|
|
35
|
+
mode: 'optional', // 'required' | 'optional' | 'disabled'
|
|
36
|
+
bank: 'project-app', // стабильный bank проекта
|
|
37
|
+
project: 'app',
|
|
38
|
+
query: 'conventions for greeting module',
|
|
39
|
+
limits: { maxBytes: 16384, maxItems: 20, maxCostUnits: 5 },
|
|
40
|
+
});
|
|
41
|
+
const context = await composeContext(
|
|
42
|
+
[{ source: new FileContextSource('context'), ids: ['norms'] }, memory.part()],
|
|
43
|
+
{ repositoryId: 'app' },
|
|
44
|
+
);
|
|
45
|
+
// review-plan → approve → run с этим же снимком
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- Recall выполняется в `snapshot`, т.е. до ревью плана и согласования.
|
|
49
|
+
Исполнение берёт только снимок, связанный с согласованием
|
|
50
|
+
(`Harness.contextOf`): runner к провайдеру не обращается. Новый ответ
|
|
51
|
+
памяти после согласования в попытку не попадает; новый снимок при том же
|
|
52
|
+
графе — `APPROVAL_REQUIRED` (нужно новое ревью плана).
|
|
53
|
+
- Источник памяти закрепляет три файла (все входят в digest источника):
|
|
54
|
+
- `recall.md` — вкладывается в запрос writer и reviewer; заголовок говорит,
|
|
55
|
+
что это непроверенные данные, которые не меняют поручение, область,
|
|
56
|
+
политику и ничего не разрешают;
|
|
57
|
+
- `provenance.json` (в поле `references`: закреплён, но не вкладывается):
|
|
58
|
+
провайдер и его версия, mode, bank, project, query, `recalledAt`,
|
|
59
|
+
digest/размер/число элементов/стоимость фактического ответа, пределы,
|
|
60
|
+
применённый объём и признаки обрезки (`truncated.items`, `truncated.bytes`,
|
|
61
|
+
`truncated.omitted`);
|
|
62
|
+
- `response.json` (тоже в `references`) — фактические байты тела ответа провайдера (если не
|
|
63
|
+
больше предела файла снимка; иначе остаётся только digest в provenance).
|
|
64
|
+
- Пределы: элементы берутся по порядку провайдера целиком. Превышение
|
|
65
|
+
`maxItems`/`maxBytes`: у `required` по умолчанию — отказ подготовки
|
|
66
|
+
(`truncation: 'forbid'`), у `optional` — отбрасывание с явной строкой
|
|
67
|
+
`[TRUNCATED by harness: …]` в `recall.md` и в provenance. Элемент с
|
|
68
|
+
`mandatory: true` не отбрасывается никогда: его обрезка — отказ
|
|
69
|
+
(`required`) или отсутствие источника (`optional`). Стоимость больше
|
|
70
|
+
`maxCostUnits` — так же.
|
|
71
|
+
- Режимы недоступности (ошибка, таймаут, ответ не JSON или вне схемы):
|
|
72
|
+
- `required` — `HarnessError NOT_READY` с причиной до ревью/согласования;
|
|
73
|
+
пустой «успешный» ответ не подставляется. Если ID памяти указан в
|
|
74
|
+
`order.context`, снимок без неё не пройдёт ни `run`, ни `review-plan`;
|
|
75
|
+
- `optional` — запись в `snapshot.missing` с причиной; снимок
|
|
76
|
+
согласуется и исполняется без памяти;
|
|
77
|
+
- `disabled` — провайдер не вызывается и может не передаваться вовсе;
|
|
78
|
+
снимок совпадает со снимком без памяти.
|
|
79
|
+
- Память — контекст, а не постановка: она не меняет `WorkOrder`, write
|
|
80
|
+
scope, gates, политику и approval digest. Непроверенный дневник writer
|
|
81
|
+
(его `summary`) reviewer не передаётся: reviewer получает diff, evidence
|
|
82
|
+
controller и тот же закреплённый контекст.
|
|
83
|
+
|
|
84
|
+
## Запись подтверждённого итога
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const sink = memoryResultSink(provider, { bank: 'project-app', project: 'app' });
|
|
88
|
+
await harness.deliver(workId, sink); // отдельно от Relay/файлового получателя
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- `sink.id` по умолчанию `memory-<provider.id>`: ключ доставки
|
|
92
|
+
`<sink>:<work>:<run>` отдельный, состояние видно в `result().delivery` и
|
|
93
|
+
событиях `delivery.*` с этим `sink`. Сбой памяти не откатывает Git
|
|
94
|
+
accepted и не меняет receipt других получателей. Повтор
|
|
95
|
+
`Harness.deliver(work, sink)` отправляет только память; writer, reviewer и
|
|
96
|
+
другие получатели не вызываются. Общей распределённой транзакции нет.
|
|
97
|
+
- Сохраняются только `accepted` результаты; остальные исходы получают
|
|
98
|
+
receipt `skipped: …` без обращения к провайдеру.
|
|
99
|
+
- Содержимое — итог controller (исход, SHA, gates, находки), не пересказ writer.
|
|
100
|
+
|
|
101
|
+
### Identity: bank, document_id, operation_id
|
|
102
|
+
|
|
103
|
+
| Идентичность | Как получается | Гарантия |
|
|
104
|
+
| -------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
105
|
+
| bank | стабильный по проекту (`options.bank`), задаётся оператором | память одного проекта не смешивается с другими |
|
|
106
|
+
| `document_id` | `dch-` + digest(provider, bank, project, work, run, digest результата) — `memoryDocumentId` | повтор того же результата — **upsert** того же документа (в Hindsight `update_mode: replace` по умолчанию удаляет прежний документ и его память) |
|
|
107
|
+
| `operation_id` | UUID (форма v5) из digest(ключ доставки, document_id) — `uuidOf` | повтор **асинхронного** retain с тем же `operation_id` возвращает прежнюю операцию и не создаёт новой работы |
|
|
108
|
+
|
|
109
|
+
Это разные гарантии: upsert документа не мешает серверу повторно выполнить
|
|
110
|
+
асинхронную обработку (извлечение фактов, расход LLM), а `operation_id` не
|
|
111
|
+
задаёт идентичность документа. Адаптер должен передавать оба.
|
|
112
|
+
|
|
113
|
+
## Что проверить при создании реального адаптера
|
|
114
|
+
|
|
115
|
+
Сверено с публичной документацией https://hindsight.vectorize.io
|
|
116
|
+
(`/developer/api/retain`, `/developer/api/recall`, на 2026-10-22 указана
|
|
117
|
+
версия документации 1.1); **на конкретном сервере не проверялось**:
|
|
118
|
+
|
|
119
|
+
- retain принимает `content`, `document_id`, `async`, `operation_id` (UUID),
|
|
120
|
+
`context`, `timestamp`, `metadata`, `tags`, `update_mode`; операции
|
|
121
|
+
async — `GET /v1/default/banks/{bank_id}/operations`; ответ async не
|
|
122
|
+
содержит usage;
|
|
123
|
+
- recall принимает `query` (до 500 токенов), `budget`, `max_tokens`, `types`,
|
|
124
|
+
`tags`; ответ `results[]` с `id`, `text`, `document_id`, `scores`; бюджет
|
|
125
|
+
ответа задаётся токенами, а не числом элементов.
|
|
126
|
+
|
|
127
|
+
Обязательные проверки адаптера (не реализованы, статус «to verify»):
|
|
128
|
+
|
|
129
|
+
1. Версия API и capabilities сервера: поддержка `operation_id` для
|
|
130
|
+
идемпотентного async retain и `document_id` upsert в используемой
|
|
131
|
+
версии; без поддержки `operation_id` повтор async retain не считается
|
|
132
|
+
идемпотентным — адаптер должен ждать операцию синхронно или сообщать
|
|
133
|
+
неизвестный исход.
|
|
134
|
+
2. Отображение `MemoryLimits` на `max_tokens`/`budget` и перевод `results[]`
|
|
135
|
+
в `memoryRecallBodySchema` (тело ответа закрепляется как получено).
|
|
136
|
+
3. Учёт: стоимость recall/retain (`costUnits`, `usage` источника и
|
|
137
|
+
получателя) ведётся отдельно от usage coding runtime и бюджетов попыток.
|
|
138
|
+
4. Ключи и адрес сервера — только у выбранного адаптера; без них core,
|
|
139
|
+
файловый сценарий и `mode: 'disabled'` работают.
|
|
140
|
+
|
|
141
|
+
## Проверка
|
|
142
|
+
|
|
143
|
+
<!-- repo-only -->В репозитории harness: `node --import tsx --test tests/memory-*.test.ts`<!-- /repo-only --> — M01 (одинаковые
|
|
144
|
+
|
|
145
|
+
закреплённые байты у writer и reviewer, смена ответа после согласования,
|
|
146
|
+
пределы), M02 (required/optional/disabled с одним недоступным провайдером),
|
|
147
|
+
M03 (независимая доставка памяти и повтор только её). Используется обычный
|
|
148
|
+
путь `review-plan → approve → run` с fixture runtime.
|
package/docs/quickstart.md
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# devcontour-harness 0.1.0-alpha.6
|
|
2
|
+
|
|
3
|
+
Этот выпуск добавляет:
|
|
4
|
+
|
|
5
|
+
- переносимые инструменты целевого проекта: npm, pnpm, Turborepo, Go и
|
|
6
|
+
Python/uv;
|
|
7
|
+
- рецепт Turbo Starter;
|
|
8
|
+
- общий контракт внешнего источника заданий и доставки с двумя opt-in
|
|
9
|
+
транспортами Relay;
|
|
10
|
+
- протокол истории находок ревью кода;
|
|
11
|
+
- нейтральную точку подключения внешней памяти.
|
|
12
|
+
|
|
13
|
+
Ядро — Engine, Runner, файловые источник и получатель, standalone CLI —
|
|
14
|
+
работает как раньше. Интеграции подключаются только явно, через отдельные
|
|
15
|
+
входы пакета. Пакет не публикуется автоматически.
|
|
16
|
+
|
|
17
|
+
## Новые входы пакета
|
|
18
|
+
|
|
19
|
+
| Вход | Назначение |
|
|
20
|
+
| ------------------------------------------- | -------------------------------------------------------------- |
|
|
21
|
+
| `devcontour-harness/toolchains` | рецепты установки, пробы версий, диагностика (`inspectPolicy`) |
|
|
22
|
+
| `devcontour-harness/integrations/connector` | общий контракт источника и доставки, состояние коннектора |
|
|
23
|
+
| `devcontour-harness/integrations/relay-v1` | транспорт Relay v1 (HTTP) |
|
|
24
|
+
| `devcontour-harness/integrations/relay-v2` | транспорт Relay-2 (subprocess CLI) |
|
|
25
|
+
| `devcontour-harness/integrations/memory` | дополнительный `ContextSource` памяти и получатель итога |
|
|
26
|
+
|
|
27
|
+
Корень пакета и `./contracts` эти модули не импортируют. CLI загружает
|
|
28
|
+
коннектор и адаптер транспорта только для явно выбранного provider.
|
|
29
|
+
|
|
30
|
+
## Изменения API и поведения
|
|
31
|
+
|
|
32
|
+
### CLI
|
|
33
|
+
|
|
34
|
+
- `source-check` — проверка подключения, проекта, capabilities и mapping;
|
|
35
|
+
только чтение.
|
|
36
|
+
- `source-snapshot --out <dir> [--policy]` — закреплённый снимок задач:
|
|
37
|
+
`graph.json`, `context.json`, `manifest.json`.
|
|
38
|
+
- `source-status` — локальный и внешний статус доставки.
|
|
39
|
+
- `source-deliver [--work]` — повтор или продолжение доставки с тем же ключом
|
|
40
|
+
операции.
|
|
41
|
+
- `source-reconcile --operation <key> [--decision resend|adopt:<id>|recheck]` —
|
|
42
|
+
явный разбор неизвестного исхода или конфликта.
|
|
43
|
+
- `run --source <connector.json>`: задачи перечитываются, их semantic digest
|
|
44
|
+
сверяется с закреплённым снимком, eligibility проверяется. Всё это —
|
|
45
|
+
до любого вызова runtime.
|
|
46
|
+
- `review-plan --source`: внешние предпосылки допускаются по решению
|
|
47
|
+
источника (`trusts`) и доступности SHA в target ref — те же правила, что у
|
|
48
|
+
`run`. Без `--source` поведение прежнее.
|
|
49
|
+
- `cleanup --state --run` — довести незавершённую очистку попытки.
|
|
50
|
+
- `run` выполняет обязательный preflight toolchain: при любой проблеме
|
|
51
|
+
диагностики попытка не начинается.
|
|
52
|
+
|
|
53
|
+
Прежние команды и их коды выхода не изменились.
|
|
54
|
+
|
|
55
|
+
### Доставка
|
|
56
|
+
|
|
57
|
+
- `DeliveryOutcome` получил необязательный вариант
|
|
58
|
+
`{ status: 'uncertain', operation, reason }`. Прежние получатели
|
|
59
|
+
(`delivered` / `conflict`) компилируются и работают без изменений.
|
|
60
|
+
- `DeliveryState`: значение статуса `uncertain` и необязательное поле
|
|
61
|
+
`operation`.
|
|
62
|
+
- Новое событие журнала `delivery.uncertain`:
|
|
63
|
+
`{ sink, key, revision, attempts, operation, reason }`.
|
|
64
|
+
- Повтор доставки не отправляет мутацию, пока неизвестный исход не разобран
|
|
65
|
+
(`source-reconcile`).
|
|
66
|
+
- Протокол коннектора: намерение сохраняется до внешнего вызова, затем
|
|
67
|
+
read-back по маркеру, затем receipt.
|
|
68
|
+
- Подтверждённый отказ получателя — безопасный повтор.
|
|
69
|
+
- Неизвестный исход — `uncertain`, без автоматического повтора.
|
|
70
|
+
- Перед каждым неподтверждённым шагом сверяется snapshot: старая постановка
|
|
71
|
+
не закрывает новую; чужое изменение между сверкой и CAS даёт конфликт.
|
|
72
|
+
- Состояние коннектора — отдельный SQLite
|
|
73
|
+
`<state>/connectors/<provider>-<hash>.sqlite`. Это не вторая база
|
|
74
|
+
исполнения.
|
|
75
|
+
- Секреты маскируются в ошибках транспорта, проблемах `source-check` и
|
|
76
|
+
причинах шагов.
|
|
77
|
+
|
|
78
|
+
### Протокол ревью кода
|
|
79
|
+
|
|
80
|
+
Подробно — [review-protocol](../review-protocol.md).
|
|
81
|
+
|
|
82
|
+
**История находок.**
|
|
83
|
+
|
|
84
|
+
- Находки ведутся на поручение (область = поручение + репозиторий), а не на
|
|
85
|
+
попытку или SHA.
|
|
86
|
+
- ID находки выдаёт controller; reviewer называет состояние каждой открытой
|
|
87
|
+
находки (`previous`).
|
|
88
|
+
- Записи ревью получили необязательные поля: `EvidenceRecord.reviewRound` и
|
|
89
|
+
`findings[].id/status`. В `RunResult.evidence[]` — `findings[].id/status` и
|
|
90
|
+
`review`.
|
|
91
|
+
- На одном SHA и фазе бывает несколько записей ревью: невалидная, затем
|
|
92
|
+
валидная. Это не отказ задачи.
|
|
93
|
+
|
|
94
|
+
**Ошибка протокола.**
|
|
95
|
+
|
|
96
|
+
- Ошибка формата ответа reviewer отделена от содержательного отказа.
|
|
97
|
+
- Reviewer вызывается повторно ограниченное число раз, writer при этом не
|
|
98
|
+
вызывается. Затем — код `REVIEW_PROTOCOL_INVALID` (класс infrastructure,
|
|
99
|
+
exit 4) и вид отказа попытки `review-protocol`. Автоматического повтора
|
|
100
|
+
попытки нет.
|
|
101
|
+
- Политика: необязательное поле `reviewProtocolRetries` (0–2, по умолчанию 1).
|
|
102
|
+
Значение по умолчанию в политику не подставляется.
|
|
103
|
+
|
|
104
|
+
**Совместимость адаптеров.**
|
|
105
|
+
|
|
106
|
+
- `RuntimeCapabilities.reviewProtocol?: 1` — строгий протокол для адаптера,
|
|
107
|
+
который его объявляет. `cliAdapter` (codex, claude, fixture executables) его
|
|
108
|
+
объявляет.
|
|
109
|
+
- Адаптер хоста без этого поля получает семантику alpha.5: раунд помечается
|
|
110
|
+
`legacy`, переходы не требуются. Поэтому прежние runtime-обёртки хоста
|
|
111
|
+
работают без изменений.
|
|
112
|
+
- `RuntimeRequest.findings` — открытые и закрытые находки для адаптера с
|
|
113
|
+
протоколом.
|
|
114
|
+
|
|
115
|
+
**Проверки reviewer.** `run_check` принимает `{"gate": "<id>"}`: controller
|
|
116
|
+
исполняет объявленный gate (его argv, cwd, окружение, путь отчёта во
|
|
117
|
+
временном каталоге) без записи в исходники. Другой вид — исследовательский
|
|
118
|
+
`{"argv": [...]}`. Подробно — [review-probes](../review-probes.md).
|
|
119
|
+
|
|
120
|
+
### Gates и отчёты
|
|
121
|
+
|
|
122
|
+
- Каждый gate `kind: test` получает `TURBO_FORCE=true` и `-count=1` в конце
|
|
123
|
+
`GOFLAGS`. Дочерние процессы наследуют оба значения, поэтому они действуют
|
|
124
|
+
через любую обёртку, в библиотеке и в CLI.
|
|
125
|
+
- Это только заставляет тесты выполниться заново и не меняет смысл
|
|
126
|
+
результата. Значения записываются в заголовок журнала gate (`fresh=…`).
|
|
127
|
+
- Gates `kind: check` не изменились.
|
|
128
|
+
- JUnit: атрибуты `name`/`classname` декодируются один раз, включая двойное
|
|
129
|
+
экранирование `"` репортером Node.js 24. Testcase с кавычками в имени теперь
|
|
130
|
+
сопоставляется с `testId` критерия.
|
|
131
|
+
|
|
132
|
+
### Инструменты целевого проекта (toolchains)
|
|
133
|
+
|
|
134
|
+
- `repositories[].toolchain`: `tools` — проверенные версии, `packageManagers` —
|
|
135
|
+
явный выбор менеджера по каталогу. Поле необязательное, аддитивное.
|
|
136
|
+
- Новые встроенные профили: `npm-ci`, `pnpm-frozen`, `go-gotestsum`,
|
|
137
|
+
`python-uv`. Прежние профили побайтно те же, их закрепления не изменились.
|
|
138
|
+
- `doctor --policy` добавляет `toolchains[]`: план шагов и gates, инструменты
|
|
139
|
+
и их версии, lockfiles, отчёты, неоднозначности, проблемы с кодом и полем.
|
|
140
|
+
- Диагностика только читает файлы и запускает ограниченные пробы версий
|
|
141
|
+
(`--version`).
|
|
142
|
+
- Путь инструмента разрешается до канонического. Инструмент внутри
|
|
143
|
+
репозитория или каталога состояния не запускается (`TOOL_IN_PROJECT`).
|
|
144
|
+
- Диапазон версии в `package.json` допустим, если lockfile закрепляет точную
|
|
145
|
+
версию.
|
|
146
|
+
- Подробно — [toolchains](../toolchains.md).
|
|
147
|
+
|
|
148
|
+
### Turbo Starter
|
|
149
|
+
|
|
150
|
+
- Рецепт для Turbo Starter на закреплённом commit.
|
|
151
|
+
- В архиве:
|
|
152
|
+
- `examples/turbo-starter/`: политика, граф, приёмочные тесты с JUnit,
|
|
153
|
+
шаблоны env, подготовленные изменения;
|
|
154
|
+
- lifecycle-мост `compose-bridge`: PostgreSQL на попытку и фазу, с порядком
|
|
155
|
+
принятия и очистки;
|
|
156
|
+
- оверлей controller mode в `templates/controller-mode/`.
|
|
157
|
+
- Upstream не патчится.
|
|
158
|
+
- Подробно — [turbo-starter](../turbo-starter.md).
|
|
159
|
+
|
|
160
|
+
### Точка подключения памяти
|
|
161
|
+
|
|
162
|
+
- Договор провайдера: `MemoryRecallProvider` и `MemoryRetainProvider`.
|
|
163
|
+
- `memoryContextSource` — recall как источник `ContextSnapshot`.
|
|
164
|
+
- `composeContext` / `mergeSnapshots` — файлы и память в одном закреплённом
|
|
165
|
+
снимке.
|
|
166
|
+
- `memoryResultSink` — запись подтверждённого итога с собственным ключом.
|
|
167
|
+
- Изменений ядра нет; без памяти harness работает как прежде.
|
|
168
|
+
- Подробно — [hindsight](../integrations/hindsight.md).
|
|
169
|
+
|
|
170
|
+
## Совместимость
|
|
171
|
+
|
|
172
|
+
- **Прежние потребители.**
|
|
173
|
+
- Реализации портов в форме alpha.5 проходят проверку типов против `.d.ts`
|
|
174
|
+
архива: `WorkSource`, `ContextSource`, `ResultSink` без `uncertain`,
|
|
175
|
+
`ExecutionHost` без необязательных портов, `ExecutionStore` из четырёх
|
|
176
|
+
методов.
|
|
177
|
+
- С ними проходит файловый сценарий через CLI, `Harness.run` и
|
|
178
|
+
`Engine`/`Runner`.
|
|
179
|
+
- Регистрация Relay или памяти и новый обязательный метод хоста не нужны.
|
|
180
|
+
- **Основания согласований.**
|
|
181
|
+
- Digest политик, графов, поручений и контекста прежних примеров совпадают
|
|
182
|
+
со значениями alpha.5.
|
|
183
|
+
- Политика без новых полей (`toolchain`, `reviewProtocolRetries`) сохраняет
|
|
184
|
+
`policyDigest`, `executionDigest` и закрепления. Политика с ними получает
|
|
185
|
+
новое основание явно (новое согласование).
|
|
186
|
+
- **Состояние alpha.5.** alpha.6 читает его без преобразования:
|
|
187
|
+
- результат, журнал и записи доставки читаются так же, строки базы не
|
|
188
|
+
меняются;
|
|
189
|
+
- прежние записи ревью — legacy-раунды;
|
|
190
|
+
- receipts и история не переписываются.
|
|
191
|
+
- **Откат.**
|
|
192
|
+
- Standalone-состояние с новыми полями — записи ревью с `reviewRound`,
|
|
193
|
+
доставка `uncertain` — alpha.5 и alpha.4 не читают: отказ
|
|
194
|
+
`STATE_VERSION_UNSUPPORTED` до любой записи.
|
|
195
|
+
- Хост DevContour с адаптером alpha.4 такое состояние открывает без отказа
|
|
196
|
+
и пишет в него роль controller и события. Новые поля при этом
|
|
197
|
+
сохраняются, потери данных нет, но строгой гарантии нет.
|
|
198
|
+
- Откат с alpha.6 в обоих случаях — только восстановлением копии состояния,
|
|
199
|
+
сделанной до обновления. Матрица — [upgrade](../upgrade.md).
|
|
200
|
+
- **Версия движка.** Номер версии — это `engineVersion`. Правила
|
|
201
|
+
alpha.2–alpha.5 действуют и между alpha.5 и alpha.6: `CONTROLLER_BUSY`, а
|
|
202
|
+
`LEASE_STALE` — до истечения срока и recovery.
|
|
203
|
+
- **DevContour.**
|
|
204
|
+
- Прежняя встройка (vendored архив, `vendor-harness.mjs`) работает с новым
|
|
205
|
+
архивом без изменений DevContour: собственные runtime-обёртки DevContour
|
|
206
|
+
не объявляют `reviewProtocol` и сохраняют семантику ревью alpha.5.
|
|
207
|
+
- Переход DevContour на протокол ревью — отдельное изменение его адаптеров.
|
|
208
|
+
- Проверенные версии инструментов —
|
|
209
|
+
[compatibility-versions](../compatibility-versions.md).
|
|
210
|
+
|
|
211
|
+
## Известные ограничения
|
|
212
|
+
|
|
213
|
+
- Ядро наблюдает процессы системными `ps`, `lsof` и `sysctl`. С alpha.6 они
|
|
214
|
+
ищутся по штатным путям ОС (`/bin/ps`, `/usr/sbin/lsof`, `/usr/sbin/sysctl`
|
|
215
|
+
и т. п.), а не через PATH: PATH из одних `node` и `git` достаточен. Только
|
|
216
|
+
если ни одного штатного пути нет (нестандартная ОС), используется PATH. В
|
|
217
|
+
песочнице, запрещающей запуск `ps`, containment невозможен — это отказ
|
|
218
|
+
окружения.
|
|
219
|
+
- **Relay-2 на macOS** требует SQLite не ниже 3.44. Системная библиотека
|
|
220
|
+
старше: путь к новой задаётся в `relay.env` коннектора (`DYLD_LIBRARY_PATH`).
|
|
221
|
+
- Доставка не обещает distributed exactly-once. У получателя нет
|
|
222
|
+
идемпотентности по запросу, Git и Relay не образуют общей транзакции.
|
|
223
|
+
Неоднозначные случаи решает оператор (`source-reconcile`).
|
|
224
|
+
- Гарантия одного владельца доставки действует только для процессов с общим
|
|
225
|
+
каталогом состояния коннектора. Независимые установки с разными каталогами
|
|
226
|
+
состояния вне гарантии и могут доставить одну операцию дважды; это видно в
|
|
227
|
+
`ownership` у `source-check`/`source-status`.
|
|
228
|
+
- Готового Hindsight-адаптера нет: только договор, композиция и проверка
|
|
229
|
+
поддельным провайдером.
|
|
230
|
+
- Встроенные профили `npm-ci`, `pnpm-frozen`, `go-gotestsum` и `python-uv`
|
|
231
|
+
задают путь отчёта жёстко. В проверке reviewer `{"gate": …}` такой gate
|
|
232
|
+
возвращает диагностику окружения с рецептом; gates движка работают как
|
|
233
|
+
обычно.
|
|
234
|
+
- На macOS отмена может не найти отсоединённого не-node потомка, запущенного
|
|
235
|
+
менее чем за 250 мс до отмены (опрос `ps -E`).
|
|
236
|
+
- **Ограничения Turbo Starter upstream.** `pnpm env:check` падает на
|
|
237
|
+
Node 24.3.0. `check-types` admin-панели скачивает пакет через `npx` (нужна
|
|
238
|
+
сеть). Изоляция gates в примере выключена.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Проверки reviewer (`run_check`) и объявленные gates
|
|
2
|
+
|
|
3
|
+
Reviewer кода, которому профиль разрешает запускать команды, получает MCP-инструмент
|
|
4
|
+
`devcontour_probe.run_check`. Его исполняет controller: песочница ОС, исходники только для
|
|
5
|
+
чтения, предел на команду, общий бюджет проверок и резерв времени на заключение.
|
|
6
|
+
|
|
7
|
+
## Два вида вызова
|
|
8
|
+
|
|
9
|
+
| Вызов | Что исполняется | Куда можно писать |
|
|
10
|
+
| ------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------- |
|
|
11
|
+
| `{"argv": ["node", "-e", "…"]}` | исследовательская команда reviewer, без shell, в корне проверяемого дерева | только собственный `TMPDIR` |
|
|
12
|
+
| `{"gate": "domain"}` | объявленный gate из эффективной политики: его `command`, `cwd`, окружение фазы | только scratch отчёта controller и `TMPDIR` |
|
|
13
|
+
|
|
14
|
+
Других полей нет: `env`, `cwd`, `reportPath`, `write`, `scope` и любые неизвестные поля —
|
|
15
|
+
ошибка протокола (`-32602`), ничего не запускается. Можно передать `timeoutMs`, но он только
|
|
16
|
+
уменьшает предел controller. Неизвестный ID и генеративный gate (`property`) — результат
|
|
17
|
+
`rejected`, бюджет не тратится.
|
|
18
|
+
|
|
19
|
+
Запрос reviewer содержит готовый вызов для каждого объявленного gate, например:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
To run a declared gate, call run_check with exactly {"gate":"domain"}, {"gate":"api"} (one gate per call).
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Что делает controller при `{"gate": "<id>"}`
|
|
26
|
+
|
|
27
|
+
1. Берёт gate из спецификации probe (копия gates поручения из эффективной политики).
|
|
28
|
+
2. Проверяет `cwd`: реальный путь внутри проверяемого дерева, иначе `rejected`.
|
|
29
|
+
3. Если путь отчёта зашит в argv (см. ниже) — не запускает, возвращает ошибку среды с рецептом.
|
|
30
|
+
4. Создаёт каталог evidence `<каталог ревью>/probe-runs/<runId>/` (вне дерева, без перезаписи) и
|
|
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 движка.
|
|
34
|
+
6. Запускает команду в песочнице: запись — только в scratch и `TMPDIR`; дерево, `.git`,
|
|
35
|
+
каталог controller и прежние `.reports/*.xml` закрыты на запись.
|
|
36
|
+
7. Читает отчёт: symlink, жёсткая ссылка, путь вне scratch — `unsafe` (не читается); файл старше
|
|
37
|
+
запуска — `stale`; не JUnit или без testcases — `corrupt`; нет файла — `missing`.
|
|
38
|
+
8. Сохраняет замаскированную копию отчёта (`<id>.xml`), журнал (`<id>.log`) и `run.json`, затем
|
|
39
|
+
удаляет scratch. Таймаут и отмена (`notifications/cancelled`) останавливают процессы проверки,
|
|
40
|
+
evidence сохраняется тоже.
|
|
41
|
+
|
|
42
|
+
Результат и запись в `probes.jsonl` содержат `runId`, `status`, фактический `argv`, `gate.id`,
|
|
43
|
+
`gate.phase`, `gate.sha`, `gate.cwd`, `gate.controllerEnv` (только имена), `gate.report`
|
|
44
|
+
(`status`, `path`, `digest` — SHA-256 сохранённых байтов, `tests`, `failures`, `skipped`),
|
|
45
|
+
`gate.scratchRemoved` и `log` — журнал controller. `review.json` переносит эти записи в
|
|
46
|
+
`probes`; `reproduction.run` находки ссылается на `runId`, а через него — на журнал.
|
|
47
|
+
|
|
48
|
+
## Бюджет проверок и авария controller
|
|
49
|
+
|
|
50
|
+
Бюджет проверок общий на логическое ревью (область, фаза, SHA), включая повторы reviewer и
|
|
51
|
+
рестарт controller. Учёт — журнал `probes.jsonl` каждого вызова:
|
|
52
|
+
|
|
53
|
+
1. До запуска команды controller резервирует её предел и синхронно пишет строку
|
|
54
|
+
`{"type":"start","runId","reservedMs","startedAt","pid"}` с fsync. Не удалось записать —
|
|
55
|
+
`error`/`environment`, команда не запускается.
|
|
56
|
+
2. Команда и все её потомки получают `HARNESS_PROBE_RUN=<runId>`.
|
|
57
|
+
3. Строка исхода (обычная запись запуска) разрешает резерв фактической длительностью.
|
|
58
|
+
4. Новый controller перед следующим вызовом reviewer считает расход: завершённые — по
|
|
59
|
+
длительности, начатые без исхода — **весь резерв** (время до гибели не известно точно и не
|
|
60
|
+
больше резерва; засчитывается верхняя граница). Пережившие процессы такого запуска (по метке
|
|
61
|
+
и их группам процессов) останавливаются, в журнал дописывается
|
|
62
|
+
`{"type":"abandoned","runId","chargedMs"}`; повторное восстановление расход не меняет, а
|
|
63
|
+
строка исхода, пришедшая позже, заменяет списание фактом без двойного счёта.
|
|
64
|
+
5. Следующий вызов получает бюджет `budget − spent` и прежний срок логического ревью; остаток
|
|
65
|
+
меньше секунды — вызов без инструмента проверок.
|
|
66
|
+
|
|
67
|
+
Ограничение: на macOS `ps -E` не показывает окружение системных бинарников (например,
|
|
68
|
+
`/bin/sleep`); такой процесс останавливается, если он в группе процессов запуска.
|
|
69
|
+
|
|
70
|
+
## Статусы
|
|
71
|
+
|
|
72
|
+
| `status` | `failure` | Смысл |
|
|
73
|
+
| ------------------------------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `passed` | — | код 0 и (для gate с отчётом) свежий JUnit без failures и skips |
|
|
75
|
+
| `failed` | — | код ≠ 0 или упали testcases: наблюдение о коде |
|
|
76
|
+
| `failed` | `report` | код 0, но отчёт `missing`/`stale`/`corrupt`/`unsafe`: не прохождение и не находка в коде |
|
|
77
|
+
| `error` | `environment` | путь отчёта зашит в argv, отказ записи (EPERM/EACCES) мимо `HARNESS_REPORT_PATH`, отказ scratch, сеть закрыта песочницей, нет песочницы |
|
|
78
|
+
| `timeout`, `cancelled` | — | команда остановлена вместе с потомками |
|
|
79
|
+
| `budget-exhausted`, `rejected` | — | команда не запускалась |
|
|
80
|
+
|
|
81
|
+
Ошибка среды и ошибка отчёта — не находка в коде; находка, сославшаяся на запуск с
|
|
82
|
+
`failure: environment`, не получает `observed`.
|
|
83
|
+
|
|
84
|
+
**Probe — наблюдение reviewer.** Обязательные gates candidate и integration запускает и
|
|
85
|
+
оценивает движок; probe не записывает evidence gate, не ставит технический PASS и не двигает
|
|
86
|
+
target ref.
|
|
87
|
+
|
|
88
|
+
## Рецепт: путь отчёта из переменной controller
|
|
89
|
+
|
|
90
|
+
Gate пригоден для probe, если команда пишет JUnit в `HARNESS_REPORT_PATH` (или
|
|
91
|
+
`DEVCONTOUR_REPORT_PATH`). Gate движка получает ту же переменную с путём `report.path` внутри
|
|
92
|
+
дерева, поэтому рецепт с прежним путём по умолчанию не меняет поведение gate движка.
|
|
93
|
+
|
|
94
|
+
Controller не ищет и не заменяет подстроки в argv. Если элемент argv целиком или после `=`
|
|
95
|
+
(`--junitxml=…`) совпадает с `report.path` относительно `cwd` gate, probe возвращает
|
|
96
|
+
`error`/`environment` с номером аргумента и рецептом, не запуская команду. Если путь зашит в
|
|
97
|
+
скрипт (package script), запуск идёт, запись в дерево получает EPERM, и результат —
|
|
98
|
+
та же ошибка среды с рецептом.
|
|
99
|
+
|
|
100
|
+
| Инструмент | Сейчас в профилях/примерах | Пригодно для probe |
|
|
101
|
+
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
102
|
+
| demo runner (`test/run-tests.mjs`) | читает `DEVCONTOUR_REPORT_PATH`/`HARNESS_REPORT_PATH`, иначе `.reports/<suite>.xml` | да, как есть |
|
|
103
|
+
| npm (`profiles/npm-ci.json`, `examples/toolchains/npm`) | `npm run test` → `--test-reporter-destination=.reports/junit.xml` в package script | `"test": "node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=junit --test-reporter-destination=${HARNESS_REPORT_PATH:-.reports/junit.xml} test/*.test.js"` |
|
|
104
|
+
| pnpm (`profiles/pnpm-frozen.json`, `examples/toolchains/pnpm`, `turbo`) | тот же путь в package script пакета | то же в script пакета; для вложенного пакета — абсолютный `HARNESS_REPORT_PATH` не зависит от каталога |
|
|
105
|
+
| Go (`profiles/go-gotestsum.json`) | `["gotestsum", "--junitfile", ".reports/junit.xml", "--", "-count=1", "./..."]` | `["sh", "-c", "GOCACHE=\"${GOCACHE:-$TMPDIR/go-build}\" exec gotestsum --junitfile \"${HARNESS_REPORT_PATH:-.reports/junit.xml}\" -- -count=1 ./..."]` |
|
|
106
|
+
| Python (`profiles/python-uv.json`) | `["uv", "run", "--no-sync", "pytest", "--junitxml=.reports/junit.xml"]` | `["sh", "-c", "PYTHONDONTWRITEBYTECODE=1 UV_CACHE_DIR=\"${UV_CACHE_DIR:-$TMPDIR/uv}\" exec uv run --no-sync pytest -p no:cacheprovider --junitxml=\"${HARNESS_REPORT_PATH:-.reports/junit.xml}\""]` |
|
|
107
|
+
| mixed-python (`cwd: backend`) | `--junitxml=../.reports/backend.xml` | `--junitxml="${HARNESS_REPORT_PATH:-../.reports/backend.xml}"` через `sh -c` |
|
|
108
|
+
|
|
109
|
+
Рецепты npm, Go и Python проверены на настоящих инструментах под песочницей ОС
|
|
110
|
+
<!-- repo-only -->(`tests/integration/probe-recipes.test.ts`)<!-- /repo-only -->: команда профиля даёт `error`/`environment` (для
|
|
111
|
+
|
|
112
|
+
npm — после EPERM записи из package script), рецепт проходит в probe со свежим JUnit вне
|
|
113
|
+
дерева и в gate движка с отчётом в дереве. Под `isolation.mode: os` cache Go и uv по умолчанию
|
|
114
|
+
лежит вне доступного на запись, поэтому рецепт направляет `GOCACHE`/`UV_CACHE_DIR` в `TMPDIR`
|
|
115
|
+
(если они не заданы), а pytest — без `.pytest_cache` и байткода в дереве. pnpm и turbo
|
|
116
|
+
используют тот же механизм package script, что и npm; отдельным прогоном не проверялись.
|
|
117
|
+
|
|
118
|
+
Встроенные профили не меняются (их байты закреплены digest). Для них probe по ID даёт
|
|
119
|
+
проверяемую диагностику вместо EPERM; gate движка работает как прежде. Новый профиль с рецептом —
|
|
120
|
+
новый файл и новый ID.
|