devcontour-harness 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +183 -2
- package/docs/architecture.md +473 -0
- package/docs/compatibility-versions.md +56 -0
- package/docs/compatibility.md +192 -0
- package/docs/connectors.md +323 -0
- package/docs/contracts.md +603 -0
- package/docs/integrations/hindsight.md +148 -0
- package/docs/quickstart.md +182 -0
- 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 +202 -0
- package/examples/README.md +27 -0
- package/examples/external-connector/README.md +19 -0
- package/examples/external-connector/connector.mjs +55 -0
- package/examples/files-only/README.md +28 -0
- package/examples/files-only/context/norms/rules.md +4 -0
- package/examples/files-only/graph.json +23 -0
- package/examples/files-only/policy.json +30 -0
- package/examples/one-vendor/README.md +19 -0
- package/examples/one-vendor/policy.json +27 -0
- package/examples/single-task/README.md +18 -0
- package/examples/single-task/graph.json +27 -0
- package/examples/single-task/policy-same-vendor.json +55 -0
- package/examples/single-task/policy.json +52 -0
- package/examples/single-task/src/.gitkeep +0 -0
- package/examples/single-task/test/run.js +19 -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/examples/two-vendors/policy.json +30 -0
- package/lib/agents/cli-adapter.d.ts +48 -0
- package/lib/agents/cli-adapter.js +386 -0
- package/lib/agents/inspect.d.ts +6 -0
- package/lib/agents/inspect.js +46 -0
- package/lib/agents/probe-server.d.ts +2 -0
- package/lib/agents/probe-server.js +13 -0
- package/lib/agents/review-probe.d.ts +295 -0
- package/lib/agents/review-probe.js +872 -0
- package/lib/agents/runtime-events.d.ts +11 -0
- package/lib/agents/runtime-events.js +21 -0
- package/lib/agents/schemas.d.ts +217 -0
- package/lib/agents/schemas.js +124 -0
- package/lib/agents/usage.d.ts +8 -0
- package/lib/agents/usage.js +83 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +491 -0
- package/lib/connectors/files.d.ts +108 -0
- package/lib/connectors/files.js +133 -0
- package/lib/contracts/checks.d.ts +307 -0
- package/lib/contracts/checks.js +181 -0
- package/lib/contracts/cli.d.ts +3895 -0
- package/lib/contracts/cli.js +664 -0
- package/lib/contracts/context.d.ts +135 -0
- package/lib/contracts/context.js +129 -0
- package/lib/contracts/digest.d.ts +9 -0
- package/lib/contracts/digest.js +22 -0
- package/lib/contracts/errors.d.ts +81 -0
- package/lib/contracts/errors.js +70 -0
- package/lib/contracts/events.d.ts +827 -0
- package/lib/contracts/events.js +258 -0
- package/lib/contracts/execution.d.ts +3216 -0
- package/lib/contracts/execution.js +872 -0
- package/lib/contracts/failure.d.ts +67 -0
- package/lib/contracts/failure.js +127 -0
- package/lib/contracts/index.d.ts +18 -0
- package/lib/contracts/index.js +18 -0
- package/lib/contracts/policy.d.ts +1080 -0
- package/lib/contracts/policy.js +544 -0
- package/lib/contracts/ports.d.ts +417 -0
- package/lib/contracts/ports.js +1 -0
- package/lib/contracts/primitives.d.ts +63 -0
- package/lib/contracts/primitives.js +73 -0
- package/lib/contracts/profiles.d.ts +336 -0
- package/lib/contracts/profiles.js +109 -0
- package/lib/contracts/published.d.ts +3237 -0
- package/lib/contracts/published.js +34 -0
- package/lib/contracts/result.d.ts +1280 -0
- package/lib/contracts/result.js +311 -0
- package/lib/contracts/review.d.ts +441 -0
- package/lib/contracts/review.js +477 -0
- package/lib/contracts/toolchain.d.ts +96 -0
- package/lib/contracts/toolchain.js +89 -0
- package/lib/contracts/version.d.ts +17 -0
- package/lib/contracts/version.js +17 -0
- package/lib/contracts/work.d.ts +173 -0
- package/lib/contracts/work.js +178 -0
- package/lib/control/base-move.d.ts +44 -0
- package/lib/control/base-move.js +90 -0
- package/lib/control/doctor.d.ts +50 -0
- package/lib/control/doctor.js +113 -0
- package/lib/control/harness.d.ts +1125 -0
- package/lib/control/harness.js +741 -0
- package/lib/control/provenance.d.ts +17 -0
- package/lib/control/provenance.js +25 -0
- package/lib/control/recovery.d.ts +237 -0
- package/lib/control/recovery.js +236 -0
- package/lib/control/review.d.ts +62 -0
- package/lib/control/review.js +178 -0
- package/lib/control/workflow.d.ts +247 -0
- package/lib/control/workflow.js +259 -0
- package/lib/engine/budget.d.ts +345 -0
- package/lib/engine/budget.js +278 -0
- package/lib/engine/classify.d.ts +8 -0
- package/lib/engine/classify.js +32 -0
- package/lib/engine/engine.d.ts +1527 -0
- package/lib/engine/engine.js +2259 -0
- package/lib/engine/failure.d.ts +1 -0
- package/lib/engine/failure.js +3 -0
- package/lib/engine/joint-runner.d.ts +257 -0
- package/lib/engine/joint-runner.js +254 -0
- package/lib/engine/measured.d.ts +15 -0
- package/lib/engine/measured.js +82 -0
- package/lib/engine/prompt.d.ts +66 -0
- package/lib/engine/prompt.js +131 -0
- package/lib/engine/property-recovery.d.ts +50 -0
- package/lib/engine/property-recovery.js +171 -0
- package/lib/engine/repair.d.ts +33 -0
- package/lib/engine/repair.js +82 -0
- package/lib/engine/result.d.ts +9 -0
- package/lib/engine/result.js +218 -0
- package/lib/engine/reuse.d.ts +80 -0
- package/lib/engine/reuse.js +102 -0
- package/lib/engine/runner.d.ts +360 -0
- package/lib/engine/runner.js +1836 -0
- package/lib/engine/standalone.d.ts +6 -0
- package/lib/engine/standalone.js +48 -0
- package/lib/engine/usage.d.ts +87 -0
- package/lib/engine/usage.js +165 -0
- package/lib/environment/isolation.d.ts +110 -0
- package/lib/environment/isolation.js +238 -0
- package/lib/environment/lifecycle.d.ts +40 -0
- package/lib/environment/lifecycle.js +197 -0
- package/lib/environment/live-calls.d.ts +14 -0
- package/lib/environment/live-calls.js +73 -0
- package/lib/environment/os-tools.d.ts +16 -0
- package/lib/environment/os-tools.js +35 -0
- package/lib/environment/process.d.ts +32 -0
- package/lib/environment/process.js +369 -0
- package/lib/environment/redaction.d.ts +22 -0
- package/lib/environment/redaction.js +102 -0
- package/lib/environment/resources.d.ts +31 -0
- package/lib/environment/resources.js +120 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.js +67 -0
- 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/package.d.ts +2 -0
- package/lib/package.js +3 -0
- package/lib/profiles/resolve.d.ts +190 -0
- package/lib/profiles/resolve.js +273 -0
- package/lib/storage/document.d.ts +1234 -0
- package/lib/storage/document.js +80 -0
- package/lib/storage/sqlite.d.ts +35 -0
- package/lib/storage/sqlite.js +260 -0
- package/lib/testing/index.d.ts +5 -0
- package/lib/testing/index.js +5 -0
- package/lib/testing/store-contract.d.ts +8 -0
- package/lib/testing/store-contract.js +167 -0
- 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/validate.d.ts +36 -0
- package/lib/validate.js +175 -0
- package/lib/verification/acceptance.d.ts +284 -0
- package/lib/verification/acceptance.js +670 -0
- package/lib/verification/checks.d.ts +72 -0
- package/lib/verification/checks.js +190 -0
- package/lib/verification/confirm.d.ts +125 -0
- package/lib/verification/confirm.js +411 -0
- package/lib/verification/corpus.d.ts +1303 -0
- package/lib/verification/corpus.js +744 -0
- package/lib/verification/dependencies.d.ts +66 -0
- package/lib/verification/dependencies.js +144 -0
- package/lib/verification/gates.d.ts +160 -0
- package/lib/verification/gates.js +600 -0
- package/lib/verification/joint.d.ts +63 -0
- package/lib/verification/joint.js +121 -0
- package/lib/verification/proof.d.ts +54 -0
- package/lib/verification/proof.js +96 -0
- package/lib/verification/property.d.ts +86 -0
- package/lib/verification/property.js +214 -0
- package/lib/verification/requirements.d.ts +25 -0
- package/lib/verification/requirements.js +69 -0
- package/lib/verification/self-check.d.ts +29 -0
- package/lib/verification/self-check.js +99 -0
- package/lib/verification/spike.d.ts +79 -0
- package/lib/verification/spike.js +124 -0
- package/package.json +100 -4
- package/profiles/expo-mobile.json +15 -0
- package/profiles/go-api.json +15 -0
- package/profiles/go-gotestsum.json +21 -0
- package/profiles/mobile-maestro.json +29 -0
- package/profiles/nest-api.json +26 -0
- package/profiles/next-product.json +72 -0
- package/profiles/node-checks.json +77 -0
- package/profiles/npm-ci.json +22 -0
- package/profiles/pnpm-frozen.json +24 -0
- package/profiles/python-api.json +32 -0
- package/profiles/python-uv.json +26 -0
- package/profiles/react-native.json +50 -0
- package/profiles/react-vite-admin.json +71 -0
- package/schemas/context-snapshot.schema.json +231 -0
- package/schemas/contract-document.schema.json +385 -0
- package/schemas/execution-document.schema.json +3695 -0
- package/schemas/execution-policy.schema.json +2196 -0
- package/schemas/joint-verification.schema.json +617 -0
- package/schemas/result-envelope.schema.json +1652 -0
- package/schemas/review-record.schema.json +177 -0
- package/schemas/run-event.schema.json +2458 -0
- package/schemas/run-result.schema.json +1570 -0
- package/schemas/stack-profile.schema.json +535 -0
- package/schemas/work-graph.schema.json +381 -0
- package/schemas/work-order.schema.json +256 -0
- package/skills/harness/SKILL.md +64 -0
- package/templates/AGENTS.md +13 -0
- package/templates/controller-mode/AGENTS.controller.md +58 -0
- package/templates/controller-mode/README.md +44 -0
- package/templates/graph.json +22 -0
- package/templates/policy.json +28 -0
|
@@ -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.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Протокол ревью кода: находки, переходы и ошибки протокола
|
|
2
|
+
|
|
3
|
+
Документ задаёт правила истории находок независимого ревью кода (candidate и
|
|
4
|
+
integration) и отделяет ошибку формата ответа reviewer от содержательного
|
|
5
|
+
отказа. Правила записаны до реализации и являются договором: тесты
|
|
6
|
+
`tests/review-*.test.ts` проверяют именно их.
|
|
7
|
+
|
|
8
|
+
## 1. Область истории
|
|
9
|
+
|
|
10
|
+
История находок ведётся **на поручение**, а не на попытку, фазу или SHA:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
scope = digestOf({ kind: 'code-review', repositoryId, workId })
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Новая попытка writer, переход candidate → integration и рестарт процесса не
|
|
17
|
+
начинают историю заново: история каждый раз заново собирается из evidence
|
|
18
|
+
всех попыток поручения (`ExecutionView.attempts(workId)`) в порядке записи.
|
|
19
|
+
- Отдельного хранилища нет: состояние находок — это записи ревью в
|
|
20
|
+
`EvidenceRecord` попыток (см. §5). Нового обязательного метода
|
|
21
|
+
`ExecutionStore`, второй базы или нового пространства локальных записей нет.
|
|
22
|
+
- SHA не является идентичностью истории: после исправления SHA меняется, а
|
|
23
|
+
находка — нет.
|
|
24
|
+
- Находка другой области (другое поручение или репозиторий) в этой истории
|
|
25
|
+
неизвестна: ссылка на неё — ошибка протокола `unknown`.
|
|
26
|
+
|
|
27
|
+
## 2. Идентификатор находки
|
|
28
|
+
|
|
29
|
+
ID выдаёт controller в момент записи валидного раунда; reviewer его не
|
|
30
|
+
придумывает и ссылается только на выданный. Формат прежний — SHA-256 hex
|
|
31
|
+
(`^[0-9a-f]{64}$`), без ослабления до произвольной строки.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
id = digestOf({ v: 1, scope, runId, phase, sha, round, index, severity, message })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`round` — номер валидного раунда области, `index` — позиция находки в ответе.
|
|
38
|
+
Повторно найденный дефект с тем же текстом в новом раунде получает **новый**
|
|
39
|
+
ID: дедупликации по тексту нет, поэтому повторно найденный на integration
|
|
40
|
+
дефект не теряется.
|
|
41
|
+
|
|
42
|
+
Находка хранит: severity, сообщение, путь/строку, статус (`open`, `closed`,
|
|
43
|
+
`withdrawn`), origin (runId, фаза, SHA, раунд, согласованная постановка
|
|
44
|
+
`spec` на момент записи) и ряд переходов (раунд, runId, фаза, SHA, from → to,
|
|
45
|
+
основание).
|
|
46
|
+
|
|
47
|
+
## 3. Переходы
|
|
48
|
+
|
|
49
|
+
Для каждой прежней **открытой** находки валидный ответ называет ровно одну
|
|
50
|
+
запись в `previous` с основанием (`basis`, не короче трёх непробельных
|
|
51
|
+
символов):
|
|
52
|
+
|
|
53
|
+
| Из | В | Смысл |
|
|
54
|
+
| -------------------- | ----------- | -------------------------------------------------------------------------------------- |
|
|
55
|
+
| `open` | `closed` | исправление подтверждено на проверяемом SHA |
|
|
56
|
+
| `open` | `withdrawn` | находка ошибочна или потеряла предмет; основание объясняет почему |
|
|
57
|
+
| `open` | `open` | дефект остаётся |
|
|
58
|
+
| `closed`/`withdrawn` | `open` | явное повторное открытие (reopen): дефект снова есть, например в интеграционном дереве |
|
|
59
|
+
|
|
60
|
+
Закрытые и снятые находки передаются reviewer как история и не требуют
|
|
61
|
+
повторного закрытия. Указать для них `closed`/`withdrawn` повторно —
|
|
62
|
+
ошибка `not-open`.
|
|
63
|
+
|
|
64
|
+
Ответ невалиден (ошибка протокола), если:
|
|
65
|
+
|
|
66
|
+
| reason | path | условие |
|
|
67
|
+
| ------------------------ | ---------------------------------- | ----------------------------------------------------------------------------- |
|
|
68
|
+
| `schema` | путь Zod, например `previous.0.id` | ответ вне схемы: не SHA-256 ID, пустое основание, неизвестный статус, поля |
|
|
69
|
+
| `unknown` | `previous.N.id` | ID нет в истории области (в том числе ID чужого поручения/репозитория) |
|
|
70
|
+
| `duplicate` | `previous.N.id` | ID назван второй раз (в том числе с другим статусом) |
|
|
71
|
+
| `not-open` | `previous.N.status` | закрытой/снятой находке назван не `open` |
|
|
72
|
+
| `missing` | `previous` | прежняя открытая находка не названа |
|
|
73
|
+
| `approved-with-blocking` | `approved` | `approved: true` при новой blocking-находке или blocking, оставшейся открытой |
|
|
74
|
+
|
|
75
|
+
`note`, оставленная открытой явным переходом, не мешает `approved: true`.
|
|
76
|
+
|
|
77
|
+
**Атомарность.** Ответ проверяется целиком до любой записи. Невалидный ответ
|
|
78
|
+
не закрывает ни одной находки, не создаёт новых и не даёт PASS: он
|
|
79
|
+
сохраняется только как диагностика (сырой ответ — в своём `review.json`,
|
|
80
|
+
причины — в evidence). Валидный раунд записывается одной транзакцией
|
|
81
|
+
`engine.evidence` под fencing-токеном попытки: попытка, потерявшая владение,
|
|
82
|
+
переходов не сохраняет.
|
|
83
|
+
|
|
84
|
+
## 4. Фазы, повторное открытие и новая редакция
|
|
85
|
+
|
|
86
|
+
- Закрытие на candidate не означает одобрения integration: integration —
|
|
87
|
+
отдельный раунд той же области. Reviewer integration получает открытые
|
|
88
|
+
находки и историю закрытых; найденный там снова дефект блокирует приёмку
|
|
89
|
+
либо явным reopen прежнего ID, либо новой находкой с новым ID.
|
|
90
|
+
- Приёмка по-прежнему требует последней записи ревью каждой фазы на точном SHA
|
|
91
|
+
с PASS (`engine.finish`, `findLast`).
|
|
92
|
+
- **Новая редакция постановки/контрактов** (новое согласование того же
|
|
93
|
+
поручения) не начинает историю заново и не удаляет её. Открытые находки
|
|
94
|
+
переносятся в новую редакцию открытыми; в их origin видна редакция, под
|
|
95
|
+
которой они записаны (`spec`). Reviewer новой редакции обязан назвать их
|
|
96
|
+
состояние, как любые прежние открытые (например, `withdrawn` с основанием
|
|
97
|
+
«требование удалено редакцией …»). Закрытые остаются историей. Одобрение
|
|
98
|
+
прежней редакции новую не подтверждает: новая редакция исполняется новой
|
|
99
|
+
попыткой, и приёмка учитывает только evidence этой попытки.
|
|
100
|
+
- Формулы host digests (`orderDigest`, `policyDigest`, `scopeDigest`,
|
|
101
|
+
основание реализации и эпизода) не меняются: находки в них не входят.
|
|
102
|
+
|
|
103
|
+
## 5. Хранение: аддитивные поля EvidenceRecord
|
|
104
|
+
|
|
105
|
+
DevContour хранит `Evidence` как есть; все новые поля необязательны, старые
|
|
106
|
+
записи читаются без них.
|
|
107
|
+
|
|
108
|
+
- `findings[].id`, `findings[].status` — ID и статус находки, записанной этим
|
|
109
|
+
раундом.
|
|
110
|
+
- `reviewRound` — сведения раунда ревью кода:
|
|
111
|
+
`version: 1`, `scope`, `round`, `call` (номер вызова reviewer в логическом
|
|
112
|
+
ревью), `calls` (предел вызовов), `protocol: valid | invalid`, `expected`
|
|
113
|
+
(ID открытых находок, выданных reviewer), `issues` (path, reason, message —
|
|
114
|
+
у невалидного), `transitions` (id, from, to, basis — у валидного), `spec`.
|
|
115
|
+
|
|
116
|
+
Результат (`RunResult`) получает аддитивно: `findings[].id/status` и у ссылок
|
|
117
|
+
на evidence ревью — `review` (round, call, protocol, issues, transitions).
|
|
118
|
+
Принятый результат несёт только решающие записи: невалидные вызовы в него не
|
|
119
|
+
входят отдельными ссылками, их причины видны у валидного вердикта повтора
|
|
120
|
+
(`review.call > 1`, `review.issues` — причины предыдущих вызовов того же
|
|
121
|
+
логического ревью). Непринятый результат содержит и записи невалидных
|
|
122
|
+
вызовов (`review.protocol: invalid` с их причинами).
|
|
123
|
+
|
|
124
|
+
## 6. Записи alpha.5 (legacy)
|
|
125
|
+
|
|
126
|
+
Запись ревью без `reviewRound` — запись прежней версии. Её находки (у
|
|
127
|
+
отклонившего ревью) получают детерминированный ID из неизменяемого origin:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
id = digestOf({ v: 'legacy-1', scope, evidence: e.id, runId, phase, sha, index, severity, message })
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
После рестарта ID тот же. Находки открыты, пока reviewer явно не назовёт их
|
|
134
|
+
состояние — это явное повторное ревью вместо пустой истории. Принятые
|
|
135
|
+
попытки и их результаты не переписываются: новые поля у них не появляются, и
|
|
136
|
+
исход не меняется задним числом.
|
|
137
|
+
|
|
138
|
+
## 7. Ошибка протокола и ограниченный повтор reviewer
|
|
139
|
+
|
|
140
|
+
| Исход | Поведение |
|
|
141
|
+
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
142
|
+
| Валидный `approved: false` | `REVIEW_REJECTED`, обычный путь исправления writer |
|
|
143
|
+
| Нарушение схемы/переходов/противоречивый approved | повтор reviewer; после исчерпания — `REVIEW_PROTOCOL_INVALID` (класс infrastructure, exit 4) |
|
|
144
|
+
| Reviewer изменил дерево/HEAD, утрачено владение, отмена, смена согласования | соответствующий отказ без протокольного повтора |
|
|
145
|
+
| Runtime недоступен, авторизация, timeout | прежняя инфраструктурная причина, без протокольного повтора |
|
|
146
|
+
|
|
147
|
+
- Политика: необязательное поле `reviewProtocolRetries` (0–2). Нет поля —
|
|
148
|
+
один дополнительный вызов (не более двух вызовов на логическое ревью);
|
|
149
|
+
`0` отключает повтор. Отсутствующее поле не подставляется в политику, её
|
|
150
|
+
digest и pins не меняются.
|
|
151
|
+
- Логическое ревью — (область, фаза, SHA). Повтор идёт на том же SHA, с тем же
|
|
152
|
+
diff, контекстом, назначением reviewer и набором открытых находок; к запросу
|
|
153
|
+
добавляется только структурированная диагностика (path, reason, message) и
|
|
154
|
+
требование вернуть полный валидный вердикт. ID не подменяются «похожими»,
|
|
155
|
+
`previous` не выбрасывается, `approved` не восстанавливается догадкой.
|
|
156
|
+
- Writer не вызывается, номер попытки не растёт. Каждый вызов проходит
|
|
157
|
+
`measuredInvoke` (бюджет до вызова, учёт usage/стоимости, неизвестная
|
|
158
|
+
стоимость по `budgets.onUnknown`). Повтор не получает нового предела
|
|
159
|
+
времени фазы и нового бюджета probe: общий срок и остаток бюджета probe
|
|
160
|
+
переходят от первого вызова.
|
|
161
|
+
- Каждый вызов пишет свои артефакты: первый — `<phase>/review/`, следующие —
|
|
162
|
+
`<phase>/review/call-<n>/` (`review.json`, журнал runtime, probe). Каждый
|
|
163
|
+
невалидный вызов записывается своим evidence (`passed: false`,
|
|
164
|
+
`reviewRound.protocol: invalid`, без находок); итоговое evidence ссылается
|
|
165
|
+
на байты валидного вердикта.
|
|
166
|
+
- Рестарт не обнуляет счётчик. Вызов засчитывается при **начале**: запись
|
|
167
|
+
учёта вызова (`usage`, `CallRecord` со `stage: <фаза>-review`, `runId`)
|
|
168
|
+
создаётся транзакцией `measuredInvoke` до внешнего invoke, поэтому вызов,
|
|
169
|
+
прерванный падением controller, засчитан так же, как завершённый (после
|
|
170
|
+
рестарта он помечается `abandoned`). Начатые вызовы того же (область, фаза,
|
|
171
|
+
SHA) из попыток, прерванных до вердикта (истёкших или с отказом окружения),
|
|
172
|
+
засчитываются продолжающей попытке (кандидат переиспользуется); для записей
|
|
173
|
+
без учёта вызовов — по невалидным evidence. Исчерпанный счётчик — остановка
|
|
174
|
+
`REVIEW_PROTOCOL_INVALID` без вызова. Номер вызова — номер фактически
|
|
175
|
+
начатого вызова. Логическое ревью закрывают валидный вердикт или иное
|
|
176
|
+
завершение попытки (в том числе остановка `REVIEW_PROTOCOL_INVALID`).
|
|
177
|
+
- Срок и бюджет проверок после рестарта не выдаются заново: срок логического
|
|
178
|
+
ревью — `runTimeoutMs` от начала его первого вызова (по записи учёта);
|
|
179
|
+
истёкший срок — явная остановка `REVIEW_PROTOCOL_INVALID` (класс `review-protocol`, без автоматического повтора) без вызова, а каждому вызову достаётся
|
|
180
|
+
только остаток срока. Расход бюджета проверок — по журналам `probes.jsonl`
|
|
181
|
+
прежних вызовов этого логического ревью. Резерв каждой проверки (её предел)
|
|
182
|
+
записывается в журнал строкой `start` с fsync **до** запуска команды; без
|
|
183
|
+
этой записи проверка не запускается. Завершённая проверка списывает
|
|
184
|
+
фактическое время, начатая без строки исхода (controller или сервер проверок
|
|
185
|
+
погиб посреди команды) — весь резерв, никогда не меньше. Новый controller
|
|
186
|
+
останавливает пережившие процессы такой проверки (метка `HARNESS_PROBE_RUN`
|
|
187
|
+
и их группы) и дописывает строку `abandoned`; остаток меньше секунды — вызов
|
|
188
|
+
без инструмента проверок (docs/review-probes.md). Integration SHA после рестарта
|
|
189
|
+
создаётся заново (новое слияние), поэтому это новое логическое ревью; его
|
|
190
|
+
повторы ограничены правилами повтора попытки.
|
|
191
|
+
- Класс отказа попытки `review-protocol`: автоматического повтора нет
|
|
192
|
+
(`repairPolicy` — stop), для эпизода восстановления по свойству он
|
|
193
|
+
нейтрален (не увеличивает и не обнуляет счёт), код остановки результата —
|
|
194
|
+
`REVIEW_PROTOCOL_INVALID`. Кандидат такой попытки переиспользуется
|
|
195
|
+
повтором, назначенным человеком, без нового вызова writer.
|
|
196
|
+
|
|
197
|
+
## 8. План и контракт
|
|
198
|
+
|
|
199
|
+
`technicalReview` (ревью плана и контракта) применяет то же правило
|
|
200
|
+
атомарности: невалидный ответ (вне схемы или с нарушением `reviewIssues`)
|
|
201
|
+
не применяет переходы и не добавляет находок; раунд записывается
|
|
202
|
+
неодобренным с причинами. Повторно найденная закрытая находка с тем же ID
|
|
203
|
+
открывается снова, а не отбрасывается как дубликат. Формат `ReviewRecord`
|
|
204
|
+
не меняется.
|
|
205
|
+
|
|
206
|
+
## 9. Адаптеры без протокола переходов (совместимость хостов)
|
|
207
|
+
|
|
208
|
+
Строгие правила §3 действуют, когда адаптер reviewer объявляет
|
|
209
|
+
`RuntimeCapabilities.reviewProtocol: 1`. Это необязательное поле; нового
|
|
210
|
+
обязательного метода нет. `cliAdapter` (codex, claude, в том числе fixture
|
|
211
|
+
executables `HARNESS_*_BIN`) поле объявляет, поэтому пилот, live и demo
|
|
212
|
+
работают строго. Runner читает поле асинхронно до вызова reviewer, вне
|
|
213
|
+
транзакций хранилища, и кэширует его на runtime. Отказ `capabilities()`
|
|
214
|
+
трактуется как адаптер прежней версии.
|
|
215
|
+
|
|
216
|
+
Адаптер хоста, который поля не объявляет (например, собственные
|
|
217
|
+
runtime-обёртки DevContour), получает семантику переходов alpha.5:
|
|
218
|
+
|
|
219
|
+
- в запросе нет раздела выданных ID, и в ответе переходы не требуются:
|
|
220
|
+
ответ проверяется без прежних открытых находок, как прежний
|
|
221
|
+
`reviewIssues(result, [])`. Ссылка на прежний ID — `unknown`, а
|
|
222
|
+
`approved: true` с новой blocking-находкой — `approved-with-blocking`;
|
|
223
|
+
- находки записываются в историю открытыми с выданными ID, раунд помечен
|
|
224
|
+
`reviewRound.mode: 'legacy'`. Закрытия не выдумываются: одобрение не
|
|
225
|
+
закрывает прежние находки, они остаются в истории открытыми. Одобрение
|
|
226
|
+
при таких находках — поведение alpha.5 для этого адаптера: прежний адаптер
|
|
227
|
+
не умеет называть их состояние;
|
|
228
|
+
- разделение H2 сохраняется: ответ вне схемы или противоречивый `approved`
|
|
229
|
+
даёт ошибку протокола с ограниченным повтором reviewer и
|
|
230
|
+
`REVIEW_PROTOCOL_INVALID`, а не содержательный отказ;
|
|
231
|
+
- writer такого поручения не получает раздела «открытых» находок: их
|
|
232
|
+
некому закрыть.
|
|
233
|
+
|
|
234
|
+
Если позже ревью ведёт адаптер с протоколом, открытые находки legacy-раундов
|
|
235
|
+
требуют явного перехода, как записи alpha.5 (§6).
|
|
236
|
+
|
|
237
|
+
### Что реализует собственный адаптер с протоколом
|
|
238
|
+
|
|
239
|
+
1. `capabilities()` возвращает `reviewProtocol: 1`.
|
|
240
|
+
2. Для `task: 'review-code'` запрос несёт `RuntimeRequest.findings`
|
|
241
|
+
(`{scope, open[], closed[]}`; у находки — `id`, `severity`, `message`, `path`,
|
|
242
|
+
`line`, `status`, `origin {runId, phase, sha, round}`, `basis?`). Это те же
|
|
243
|
+
данные, что раздел запроса `Open review findings issued by the controller`,
|
|
244
|
+
поэтому адаптеру без модели не нужно разбирать текст.
|
|
245
|
+
3. Ответ (`RuntimeResponse.output`, схема `reviewResultSchema` / wire
|
|
246
|
+
`reviewOutputSchema`) содержит в `previous` ровно одну запись
|
|
247
|
+
`{id, status: 'open' | 'closed' | 'withdrawn', basis}` на каждую находку
|
|
248
|
+
`open`. `id` — выданный (SHA-256 hex), `basis` — не меньше трёх непробельных
|
|
249
|
+
символов. Для `closed` допустимо только `{id, status: 'open', basis}`
|
|
250
|
+
(повторное открытие). `approved: true` невозможно при blocking-находке,
|
|
251
|
+
оставленной открытой, или при новой blocking-находке.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Toolchains целевого проекта
|
|
2
|
+
|
|
3
|
+
Harness — Node.js-инструмент. Исполняемому проекту не нужны package.json, npm или
|
|
4
|
+
JavaScript: gates и шаги подготовки — произвольные argv, каталог gate — `cwd`, окружение —
|
|
5
|
+
`environment`. npm и pnpm — менеджеры пакетов, Turborepo — планировщик задач, Go и
|
|
6
|
+
Python/uv — отдельные toolchain. Единого «типа проекта» нет: репозиторий описывается
|
|
7
|
+
композицией шагов.
|
|
8
|
+
|
|
9
|
+
| Что | Где в политике |
|
|
10
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
11
|
+
| Установка зависимостей (locked) | `repositories[].prepare[]` — исполняется в каждом worktree попытки до writer, отдельно для candidate и integration |
|
|
12
|
+
| Команды задач и проверки | `repositories[].gates[]` (`check` / `test` с JUnit) |
|
|
13
|
+
| Каталог | `gates[].cwd`; у шага `prepare` — собственный флаг инструмента (`pnpm --dir`, `npm --prefix`, `uv --directory`, `go -C`) |
|
|
14
|
+
| Окружение | `environment` политики/репозитория (`values`, `inherit`, `secrets`) |
|
|
15
|
+
| Проверенные версии инструментов и явный выбор менеджера пакетов | `repositories[].toolchain` (`tools`, `packageManagers`), необязательно |
|
|
16
|
+
|
|
17
|
+
`toolchain` — необязательное аддитивное поле: политики и профили без него сохраняют прежний
|
|
18
|
+
digest. `tools` — `{ "gotestsum": "1.12.3" }` (названные компоненты версии совпадают) или
|
|
19
|
+
`{ "go": ">=1.24" }` (не ниже). `packageManagers` — `{ ".": "pnpm", "frontend": "pnpm" }`:
|
|
20
|
+
явный выбор снимает неоднозначность, когда в каталоге лежат lockfiles нескольких менеджеров.
|
|
21
|
+
|
|
22
|
+
## Рецепты
|
|
23
|
+
|
|
24
|
+
Проверенный набор (macOS arm64, Darwin 24.6): Node 24.3.0, npm 11.4.2, pnpm 10.12.3,
|
|
25
|
+
Turbo 2.11.10 (devDependency fixture), Go 1.24.5, gotestsum v1.12.3, uv 0.11.28,
|
|
26
|
+
CPython 3.12.9 (выбран uv по `.python-version`), pytest 8.4.1, ruff 0.12.4.
|
|
27
|
+
|
|
28
|
+
| Вариант | prepare | gates | Встроенный профиль |
|
|
29
|
+
| ------------------------------ | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
30
|
+
| Node + npm | `npm ci` | `npm run check`, `npm run test` (JUnit `.reports/junit.xml`) | `npm-ci@0.1.0` |
|
|
31
|
+
| Node + pnpm | `pnpm install --frozen-lockfile` | `pnpm run check`, `pnpm run test` | `pnpm-frozen@0.1.0` |
|
|
32
|
+
| pnpm + Turborepo | `pnpm install --frozen-lockfile` | `pnpm exec turbo run check --filter=<pkg>`, `pnpm exec turbo run test --filter=<pkg> --force` | — (фильтры проекта: политика, см. `examples/toolchains/turbo`) |
|
|
33
|
+
| Go | `go mod download` | `go vet ./...`, `gotestsum --junitfile .reports/junit.xml -- -count=1 ./...` | `go-gotestsum@0.1.0` |
|
|
34
|
+
| Python | `uv sync --locked` | `uv run --no-sync ruff check .`, `uv run --no-sync pytest --junitxml=.reports/junit.xml` | `python-uv@0.1.0` |
|
|
35
|
+
| pnpm frontend + Go backend | `pnpm --dir frontend install --frozen-lockfile`, `go -C backend mod download` | gates с `cwd: frontend` / `cwd: backend` | — (`examples/toolchains/mixed-go`) |
|
|
36
|
+
| pnpm frontend + Python backend | `pnpm --dir frontend install --frozen-lockfile`, `uv sync --locked --directory backend` | gates с `cwd: frontend` / `cwd: backend` | — (`examples/toolchains/mixed-python`) |
|
|
37
|
+
|
|
38
|
+
Правила рецептов:
|
|
39
|
+
|
|
40
|
+
- Только locked-установка: `npm ci`, `pnpm install --frozen-lockfile`, `uv sync --locked`,
|
|
41
|
+
`go mod download`. Несовместимый lockfile — отказ шага с диагностикой менеджера, lockfile
|
|
42
|
+
не переписывается. `npm install`, `pnpm install` без `--frozen-lockfile`, `uv sync` без
|
|
43
|
+
`--locked`, `uv run` без `--no-sync`, `go get`, `go mod tidy` — проблема preflight.
|
|
44
|
+
- Точные версии, без `@latest`, `npx`, `pnpm dlx` без версии, `uvx` без версии, `--if-present`.
|
|
45
|
+
Отсутствующий test task не заменяется build/lint.
|
|
46
|
+
- Отчёт gate лежит относительно корня worktree, а не `cwd`: gate `cwd: backend` пишет JUnit в
|
|
47
|
+
`../.reports/backend.xml`, gate объявляет `.reports/backend.xml`. Harness удаляет файл
|
|
48
|
+
отчёта до запуска и передаёт путь в `HARNESS_REPORT_PATH`.
|
|
49
|
+
- Один monorepo — одна запись Git repository с разными `cwd`. `consumers` — только для
|
|
50
|
+
действительно отдельных репозиториев.
|
|
51
|
+
- Профили языков (`go-gotestsum`, `python-uv`, прежние `go-api`, `python-api`) не наследуют
|
|
52
|
+
npm-подготовку. Слои профиля с разными версиями одного инструмента или разными менеджерами
|
|
53
|
+
одного каталога — отказ разрешения (`Конфликт частей профиля: toolchain.…`).
|
|
54
|
+
- Композиция из кода: `composeRepository(own, components, tools)` и `installRecipes` из
|
|
55
|
+
opt-in entrypoint `devcontour-harness/toolchains`.
|
|
56
|
+
|
|
57
|
+
## Установка harness для Go/Python-проекта
|
|
58
|
+
|
|
59
|
+
Target-проекту package.json не нужен. Harness ставится в отдельное окружение инструмента.
|
|
60
|
+
|
|
61
|
+
npm:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
mkdir -p ~/.local/share/harness-tool && cd ~/.local/share/harness-tool
|
|
65
|
+
npm init -y >/dev/null
|
|
66
|
+
npm install --save-exact devcontour-harness@<версия>
|
|
67
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
68
|
+
harness doctor --policy /path/to/project-harness/policy.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
pnpm:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
mkdir -p ~/.local/share/harness-tool && cd ~/.local/share/harness-tool
|
|
75
|
+
pnpm init >/dev/null
|
|
76
|
+
pnpm add --save-exact devcontour-harness@<версия>
|
|
77
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Политика и граф могут лежать вне target-репозитория: `repositories[].path` относителен
|
|
81
|
+
каталогу политики (`examples/toolchains/go/policy.json` → `"path": "repo"`).
|
|
82
|
+
|
|
83
|
+
## Cache и изменяемые каталоги
|
|
84
|
+
|
|
85
|
+
Каждая попытка исполняется в своём worktree (`<state>/repos/<repo>/worktrees/<attempt>` и
|
|
86
|
+
`…-integration`), и `prepare` устанавливает зависимости заново в нём. Изменяемые артефакты
|
|
87
|
+
одной попытки другой не видны.
|
|
88
|
+
|
|
89
|
+
| Допустимый общий cache (content-addressed, вне worktree) | Изменяемое, только внутри worktree попытки |
|
|
90
|
+
| ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
|
91
|
+
| npm cache (`~/.npm`), pnpm store, Go module cache (`GOMODCACHE`), Go build cache (`GOCACHE`), uv cache | `node_modules/`, `.venv/`, `.reports/`, `.turbo/`, выходы сборки |
|
|
92
|
+
|
|
93
|
+
Cache сборки допустим, но не заменяет тест:
|
|
94
|
+
|
|
95
|
+
- Во время исполнения каждый gate `kind: test` получает `TURBO_FORCE=true` и `GOFLAGS`,
|
|
96
|
+
оканчивающийся на `-count=1` (после значения политики). Окружение наследуют все потомки, так
|
|
97
|
+
что защита действует при любой обёртке (package script, `pnpm --filter … run`, task runner),
|
|
98
|
+
в библиотечном `Harness.run`/`Runner` и в CLI одинаково. Заголовок лога gate записывает
|
|
99
|
+
применённые значения (`fresh={...}`); digest лога входит в evidence. `check` gates cache
|
|
100
|
+
используют как обычно.
|
|
101
|
+
- Turbo: preflight дополнительно требует явной свежести в самой команде — `--force`,
|
|
102
|
+
`--cache=` без чтения или `TURBO_FORCE=true` — и находит turbo внутри package scripts
|
|
103
|
+
(напрямую, через `pnpm run`/`npm run`, `pnpm --filter … run`, `pnpm exec`, `npx`). Без неё —
|
|
104
|
+
`CACHE_REPLAY`. Script turbo-проекта, который не разбирается статически (`$(…)`, обратные
|
|
105
|
+
кавычки, конвейер, `sh -c`, `eval`, `xargs`), — `SCRIPT_UNRESOLVED`: угадывать нельзя.
|
|
106
|
+
Проверено T03: при входах задачи без `src` неверная реализация вне harness даёт «cache hit,
|
|
107
|
+
replaying» и зелёный отчёт; тот же script через gate — «cache bypass, force executing» и
|
|
108
|
+
отказ, в том числе через общий `TURBO_CACHE_DIR` и `Harness.run` без CLI.
|
|
109
|
+
- Go: команда без `-count=1` в test gate — предупреждение (`warnings`): прежний `go-api`
|
|
110
|
+
продолжает проходить preflight, а свежесть обеспечивает `GOFLAGS` во время gate.
|
|
111
|
+
|
|
112
|
+
Isolation `os` пишет только в worktree и временный каталог проверки: для Go/uv/pnpm в
|
|
113
|
+
песочнице cache-каталоги (`GOCACHE`, `UV_CACHE_DIR`, store) нужно указать доступными или
|
|
114
|
+
использовать `isolation.mode: none`, как в примерах. Установка (`prepare`) идёт вне песочницы.
|
|
115
|
+
|
|
116
|
+
## Диагностика: doctor и preflight
|
|
117
|
+
|
|
118
|
+
`harness doctor --policy policy.json` (или `--state`) добавляет `toolchains[]` — по
|
|
119
|
+
репозиторию политики:
|
|
120
|
+
|
|
121
|
+
- `plan` — каждый шаг и gate: фаза, `cwd`, argv, путь отчёта, поле конфигурации;
|
|
122
|
+
- `tools` — внешние инструменты из PATH (найден, версия по пробе, требования и их источник:
|
|
123
|
+
`toolchain.tools`, `package.json#packageManager`, `go.mod#go`) и инструменты среды проекта
|
|
124
|
+
(`turbo` из `package.json`: диапазон допустим, если lockfile закрепляет точную версию,
|
|
125
|
+
иначе `UNPINNED`; `ruff`/`pytest` из `uv.lock`) — последние не запускаются;
|
|
126
|
+
- `lockfiles` — каталоги node-проектов, их lockfiles и выбранный менеджер;
|
|
127
|
+
- `reports`, `ambiguities` (с `resolvedBy`), `problems`, `warnings`, `configSource`.
|
|
128
|
+
|
|
129
|
+
Каждая проблема — код, поле (`repositories.0.gates.1.command`) и путь файла проекта:
|
|
130
|
+
`TOOL_MISSING`, `TOOL_VERSION`, `TOOL_VERSION_UNKNOWN`, `TOOL_NOT_LOCKED`, `MANIFEST_MISSING`,
|
|
131
|
+
`LOCKFILE_MISSING`, `LOCKFILE_AMBIGUOUS`, `SCRIPT_UNKNOWN`, `TASK_UNKNOWN`, `FILTER_UNKNOWN`,
|
|
132
|
+
`INSTALL_UNLOCKED`, `UNPINNED`, `IF_PRESENT`, `CACHE_REPLAY`, `REPORT_MISMATCH`,
|
|
133
|
+
`COMPOSITION_CONFLICT`, `CWD_MISSING`, `PROJECT_FILE_MISSING`, `REPOSITORY_MISSING`,
|
|
134
|
+
`TOOL_IN_PROJECT`, `SCRIPT_UNRESOLVED`.
|
|
135
|
+
|
|
136
|
+
Диагностика только читает файлы (package.json, pnpm-workspace.yaml, turbo.json, go.mod,
|
|
137
|
+
pyproject.toml, uv.lock) и запускает пробы версий внешних инструментов: `<tool> --version`
|
|
138
|
+
(`go version`; для Go-бинарника без метки версии — `go version -m <файл>`), в нейтральном
|
|
139
|
+
временном каталоге, по одной на инструмент, с пределом 10 с и одним повтором с пределом 20 с, если проба не
|
|
140
|
+
уложилась во время (нагрузка). Перед пробой путь инструмента
|
|
141
|
+
разрешается до канонического (все symlink файла и каталога PATH): файл внутри любого
|
|
142
|
+
репозитория политики (включая его `node_modules/.bin`, `.venv`) или каталога состояния с
|
|
143
|
+
worktrees не запускается — проблема `TOOL_IN_PROJECT`. Ни установки, ни генерации, ни новых
|
|
144
|
+
файлов.
|
|
145
|
+
|
|
146
|
+
`doctor` по-прежнему информационный (exit 0, проблемы в `problems`). С политикой он требует
|
|
147
|
+
только runtime, назначенные политикой (`reason: "не назначен политикой"` у остальных).
|
|
148
|
+
`harness run` выполняет ту же диагностику как обязательный preflight: при любой проблеме —
|
|
149
|
+
exit 3, `NOT_READY` с issues `policy.<поле>` до записи `binding.json`, открытия состояния и
|
|
150
|
+
вызова runtime.
|
|
151
|
+
|
|
152
|
+
## Пример и приёмка
|
|
153
|
+
|
|
154
|
+
`examples/toolchains/<variant>/` — `repo/` (target-проект; `_gitignore` становится
|
|
155
|
+
`.gitignore`), `policy.json`, `task.json`, `good/` и `bad/` (реализации для fixture writer).
|
|
156
|
+
<!-- repo-only -->
|
|
157
|
+
|
|
158
|
+
В репозитории harness (не входит в пакет). Сквозной прогон всех семи вариантов через обычный путь harness:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
node --import tsx scripts/acceptance/t01.mjs # все варианты
|
|
162
|
+
node --import tsx scripts/acceptance/t01.mjs go python # выборочно
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
<!-- /repo-only -->
|
|
166
|
+
|
|
167
|
+
Нужны npm, pnpm, go + gotestsum, uv в PATH; отсутствующий инструмент — `BLOCKED`.
|