@cleocode/core 2026.9.8 → 2026.9.10
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/dist/audit/reconstruct.d.ts +13 -43
- package/dist/audit/reconstruct.d.ts.map +1 -1
- package/dist/audit/reconstruct.js +295 -290
- package/dist/audit/reconstruct.js.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +8 -2
- package/dist/bootstrap.js.map +1 -1
- package/dist/compliance/protocol-rules.d.ts.map +1 -1
- package/dist/compliance/protocol-rules.js +3 -1
- package/dist/compliance/protocol-rules.js.map +1 -1
- package/dist/dispatch/mutate-projection.d.ts.map +1 -1
- package/dist/dispatch/mutate-projection.js +12 -0
- package/dist/dispatch/mutate-projection.js.map +1 -1
- package/dist/dispatch/mvi-projection.d.ts +29 -6
- package/dist/dispatch/mvi-projection.d.ts.map +1 -1
- package/dist/dispatch/mvi-projection.js +109 -139
- package/dist/dispatch/mvi-projection.js.map +1 -1
- package/dist/docs/docs-read-model.d.ts +3 -2
- package/dist/docs/docs-read-model.d.ts.map +1 -1
- package/dist/docs/docs-read-model.js +4 -3
- package/dist/docs/docs-read-model.js.map +1 -1
- package/dist/docs/export-document.js +5129 -2297
- package/dist/docs/export-document.js.map +3 -3
- package/dist/docs/projection.d.ts +67 -0
- package/dist/docs/projection.d.ts.map +1 -0
- package/dist/docs/projection.js +370 -0
- package/dist/docs/projection.js.map +1 -0
- package/dist/doctor/acceptance-drift.d.ts +6 -2
- package/dist/doctor/acceptance-drift.d.ts.map +1 -1
- package/dist/doctor/acceptance-drift.js +43 -5
- package/dist/doctor/acceptance-drift.js.map +1 -1
- package/dist/doctor/knowledge-summary.d.ts +41 -0
- package/dist/doctor/knowledge-summary.d.ts.map +1 -0
- package/dist/doctor/knowledge-summary.js +81 -0
- package/dist/doctor/knowledge-summary.js.map +1 -0
- package/dist/doctor/knowledge.d.ts +192 -0
- package/dist/doctor/knowledge.d.ts.map +1 -0
- package/dist/doctor/knowledge.js +1864 -0
- package/dist/doctor/knowledge.js.map +1 -0
- package/dist/git/work-tree.d.ts +169 -0
- package/dist/git/work-tree.d.ts.map +1 -0
- package/dist/git/work-tree.js +341 -0
- package/dist/git/work-tree.js.map +1 -0
- package/dist/hooks/handlers/task-hooks.d.ts +8 -5
- package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/task-hooks.js +15 -60
- package/dist/hooks/handlers/task-hooks.js.map +1 -1
- package/dist/identity/cleo-identity.d.ts +13 -0
- package/dist/identity/cleo-identity.d.ts.map +1 -1
- package/dist/identity/cleo-identity.js +20 -6
- package/dist/identity/cleo-identity.js.map +1 -1
- package/dist/injection.d.ts.map +1 -1
- package/dist/injection.js +29 -13
- package/dist/injection.js.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -0
- package/dist/internal.js.map +1 -1
- package/dist/lifecycle/verification/satisfies-validator.d.ts.map +1 -1
- package/dist/lifecycle/verification/satisfies-validator.js +124 -120
- package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
- package/dist/llm/plugin-facade.js +4047 -1726
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/memory/brain-backfill.d.ts +27 -12
- package/dist/memory/brain-backfill.d.ts.map +1 -1
- package/dist/memory/brain-backfill.js +325 -134
- package/dist/memory/brain-backfill.js.map +1 -1
- package/dist/memory/brain-doctor.d.ts +21 -0
- package/dist/memory/brain-doctor.d.ts.map +1 -1
- package/dist/memory/brain-doctor.js +111 -53
- package/dist/memory/brain-doctor.js.map +1 -1
- package/dist/memory/brain-links.d.ts.map +1 -1
- package/dist/memory/brain-links.js +31 -4
- package/dist/memory/brain-links.js.map +1 -1
- package/dist/memory/brain-reasoning.d.ts.map +1 -1
- package/dist/memory/brain-reasoning.js +28 -2
- package/dist/memory/brain-reasoning.js.map +1 -1
- package/dist/memory/brain-reconciler.d.ts +14 -19
- package/dist/memory/brain-reconciler.d.ts.map +1 -1
- package/dist/memory/brain-reconciler.js +70 -97
- package/dist/memory/brain-reconciler.js.map +1 -1
- package/dist/memory/brain-search.d.ts +2 -0
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/brain-search.js +59 -29
- package/dist/memory/brain-search.js.map +1 -1
- package/dist/memory/brain-similarity.d.ts.map +1 -1
- package/dist/memory/brain-similarity.js +5 -4
- package/dist/memory/brain-similarity.js.map +1 -1
- package/dist/memory/brain-stub-prune.d.ts +31 -9
- package/dist/memory/brain-stub-prune.d.ts.map +1 -1
- package/dist/memory/brain-stub-prune.js +65 -18
- package/dist/memory/brain-stub-prune.js.map +1 -1
- package/dist/memory/brain-writer-handlers.d.ts +10 -1
- package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
- package/dist/memory/brain-writer-handlers.js +23 -3
- package/dist/memory/brain-writer-handlers.js.map +1 -1
- package/dist/memory/brain-writer-thread.d.ts +11 -1
- package/dist/memory/brain-writer-thread.d.ts.map +1 -1
- package/dist/memory/brain-writer-thread.js +87 -20
- package/dist/memory/brain-writer-thread.js.map +1 -1
- package/dist/memory/brain-writer-worker.js +8 -1
- package/dist/memory/brain-writer-worker.js.map +1 -1
- package/dist/memory/decision-cross-link.d.ts +39 -0
- package/dist/memory/decision-cross-link.d.ts.map +1 -1
- package/dist/memory/decision-cross-link.js +316 -0
- package/dist/memory/decision-cross-link.js.map +1 -1
- package/dist/memory/decisions.d.ts +4 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/memory/decisions.js +7 -24
- package/dist/memory/decisions.js.map +1 -1
- package/dist/memory/dispatch-trace.d.ts +12 -5
- package/dist/memory/dispatch-trace.d.ts.map +1 -1
- package/dist/memory/dispatch-trace.js +17 -7
- package/dist/memory/dispatch-trace.js.map +1 -1
- package/dist/memory/eligibility.d.ts +51 -0
- package/dist/memory/eligibility.d.ts.map +1 -0
- package/dist/memory/eligibility.js +77 -0
- package/dist/memory/eligibility.js.map +1 -0
- package/dist/memory/engine-compat.d.ts +2 -0
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/engine-compat.js +10 -0
- package/dist/memory/engine-compat.js.map +1 -1
- package/dist/memory/extraction-gate.d.ts +21 -7
- package/dist/memory/extraction-gate.d.ts.map +1 -1
- package/dist/memory/extraction-gate.js +178 -216
- package/dist/memory/extraction-gate.js.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +38 -5
- package/dist/memory/graph-auto-populate.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.js +182 -60
- package/dist/memory/graph-auto-populate.js.map +1 -1
- package/dist/memory/graph-memory-bridge.d.ts +14 -1
- package/dist/memory/graph-memory-bridge.d.ts.map +1 -1
- package/dist/memory/graph-memory-bridge.js +66 -28
- package/dist/memory/graph-memory-bridge.js.map +1 -1
- package/dist/memory/index.d.ts +1 -19
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +1 -50
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/learnings.d.ts.map +1 -1
- package/dist/memory/learnings.js +2 -23
- package/dist/memory/learnings.js.map +1 -1
- package/dist/memory/manifest-filter.d.ts +21 -0
- package/dist/memory/manifest-filter.d.ts.map +1 -0
- package/dist/memory/manifest-filter.js +51 -0
- package/dist/memory/manifest-filter.js.map +1 -0
- package/dist/memory/manifest-ingestion.d.ts +28 -30
- package/dist/memory/manifest-ingestion.d.ts.map +1 -1
- package/dist/memory/manifest-ingestion.js +104 -235
- package/dist/memory/manifest-ingestion.js.map +1 -1
- package/dist/memory/observer-reflector.d.ts +2 -0
- package/dist/memory/observer-reflector.d.ts.map +1 -1
- package/dist/memory/observer-reflector.js +6 -49
- package/dist/memory/observer-reflector.js.map +1 -1
- package/dist/memory/patterns.d.ts +10 -0
- package/dist/memory/patterns.d.ts.map +1 -1
- package/dist/memory/patterns.js +25 -33
- package/dist/memory/patterns.js.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.d.ts +72 -9
- package/dist/memory/pipeline-manifest-sqlite.d.ts.map +1 -1
- package/dist/memory/pipeline-manifest-sqlite.js +508 -412
- package/dist/memory/pipeline-manifest-sqlite.js.map +1 -1
- package/dist/memory/retrieval/build-retrieval-bundle.d.ts.map +1 -1
- package/dist/memory/retrieval/build-retrieval-bundle.js +16 -15
- package/dist/memory/retrieval/build-retrieval-bundle.js.map +1 -1
- package/dist/memory/retrieval/fetch.d.ts +7 -0
- package/dist/memory/retrieval/fetch.d.ts.map +1 -1
- package/dist/memory/retrieval/fetch.js +14 -11
- package/dist/memory/retrieval/fetch.js.map +1 -1
- package/dist/memory/retrieval/observe.d.ts +7 -1
- package/dist/memory/retrieval/observe.d.ts.map +1 -1
- package/dist/memory/retrieval/observe.js +58 -4
- package/dist/memory/retrieval/observe.js.map +1 -1
- package/dist/memory/retrieval/search.d.ts +14 -1
- package/dist/memory/retrieval/search.d.ts.map +1 -1
- package/dist/memory/retrieval/search.js +133 -69
- package/dist/memory/retrieval/search.js.map +1 -1
- package/dist/memory/retrieval/telemetry.d.ts +31 -0
- package/dist/memory/retrieval/telemetry.d.ts.map +1 -0
- package/dist/memory/retrieval/telemetry.js +53 -0
- package/dist/memory/retrieval/telemetry.js.map +1 -0
- package/dist/nexus/analyze-orchestrator.d.ts +41 -9
- package/dist/nexus/analyze-orchestrator.d.ts.map +1 -1
- package/dist/nexus/analyze-orchestrator.js +257 -58
- package/dist/nexus/analyze-orchestrator.js.map +1 -1
- package/dist/nexus/api-contracts.d.ts +2 -6
- package/dist/nexus/api-contracts.d.ts.map +1 -1
- package/dist/nexus/api-contracts.js +11 -2
- package/dist/nexus/api-contracts.js.map +1 -1
- package/dist/nexus/context.d.ts.map +1 -1
- package/dist/nexus/context.js +14 -37
- package/dist/nexus/context.js.map +1 -1
- package/dist/nexus/diff.d.ts.map +1 -1
- package/dist/nexus/diff.js +2 -1
- package/dist/nexus/diff.js.map +1 -1
- package/dist/nexus/identity.d.ts +29 -3
- package/dist/nexus/identity.d.ts.map +1 -1
- package/dist/nexus/identity.js +63 -12
- package/dist/nexus/identity.js.map +1 -1
- package/dist/nexus/impact.d.ts +21 -19
- package/dist/nexus/impact.d.ts.map +1 -1
- package/dist/nexus/impact.js +96 -192
- package/dist/nexus/impact.js.map +1 -1
- package/dist/nexus/knowledge.d.ts +80 -0
- package/dist/nexus/knowledge.d.ts.map +1 -0
- package/dist/nexus/knowledge.js +786 -0
- package/dist/nexus/knowledge.js.map +1 -0
- package/dist/nexus/living-brain.d.ts +8 -5
- package/dist/nexus/living-brain.d.ts.map +1 -1
- package/dist/nexus/living-brain.js +252 -188
- package/dist/nexus/living-brain.js.map +1 -1
- package/dist/nexus/registry.d.ts +11 -2
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +144 -128
- package/dist/nexus/registry.js.map +1 -1
- package/dist/nexus/source-roots.d.ts +16 -0
- package/dist/nexus/source-roots.d.ts.map +1 -0
- package/dist/nexus/source-roots.js +174 -0
- package/dist/nexus/source-roots.js.map +1 -0
- package/dist/nexus/task-evidence.d.ts +22 -0
- package/dist/nexus/task-evidence.d.ts.map +1 -0
- package/dist/nexus/task-evidence.js +210 -0
- package/dist/nexus/task-evidence.js.map +1 -0
- package/dist/nexus/tasks-bridge.d.ts +14 -7
- package/dist/nexus/tasks-bridge.d.ts.map +1 -1
- package/dist/nexus/tasks-bridge.js +81 -40
- package/dist/nexus/tasks-bridge.js.map +1 -1
- package/dist/orchestrate/lifecycle-ops.d.ts.map +1 -1
- package/dist/orchestrate/lifecycle-ops.js +170 -120
- package/dist/orchestrate/lifecycle-ops.js.map +1 -1
- package/dist/orchestrate/query-ops.d.ts +6 -7
- package/dist/orchestrate/query-ops.d.ts.map +1 -1
- package/dist/orchestrate/query-ops.js +509 -532
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestration/bootstrap.d.ts +15 -1
- package/dist/orchestration/bootstrap.d.ts.map +1 -1
- package/dist/orchestration/bootstrap.js +21 -10
- package/dist/orchestration/bootstrap.js.map +1 -1
- package/dist/orchestration/index.d.ts +35 -3
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js +64 -35
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/lead-rollup.d.ts +24 -1
- package/dist/orchestration/lead-rollup.d.ts.map +1 -1
- package/dist/orchestration/lead-rollup.js +31 -11
- package/dist/orchestration/lead-rollup.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +25 -45
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/orchestration/unblock.d.ts +15 -1
- package/dist/orchestration/unblock.d.ts.map +1 -1
- package/dist/orchestration/unblock.js +23 -12
- package/dist/orchestration/unblock.js.map +1 -1
- package/dist/orchestration/validate-spawn.d.ts.map +1 -1
- package/dist/orchestration/validate-spawn.js +3 -3
- package/dist/orchestration/validate-spawn.js.map +1 -1
- package/dist/orchestration/waves.d.ts +29 -13
- package/dist/orchestration/waves.d.ts.map +1 -1
- package/dist/orchestration/waves.js +43 -55
- package/dist/orchestration/waves.js.map +1 -1
- package/dist/paths.d.ts +17 -163
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +145 -496
- package/dist/paths.js.map +1 -1
- package/dist/project-info.d.ts +2 -11
- package/dist/project-info.d.ts.map +1 -1
- package/dist/project-info.js +3 -31
- package/dist/project-info.js.map +1 -1
- package/dist/project-scope.d.ts +239 -0
- package/dist/project-scope.d.ts.map +1 -0
- package/dist/project-scope.js +473 -0
- package/dist/project-scope.js.map +1 -0
- package/dist/release/pr-evidence.d.ts +23 -4
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +65 -9
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/render/nexus/graph/index.d.ts.map +1 -1
- package/dist/render/nexus/graph/index.js +4 -2
- package/dist/render/nexus/graph/index.js.map +1 -1
- package/dist/render/orchestration/audit-reconstruct.d.ts +11 -0
- package/dist/render/orchestration/audit-reconstruct.d.ts.map +1 -1
- package/dist/render/orchestration/audit-reconstruct.js +21 -6
- package/dist/render/orchestration/audit-reconstruct.js.map +1 -1
- package/dist/render/tasks/show.js +2 -1
- package/dist/render/tasks/show.js.map +1 -1
- package/dist/resources/spawn-wrapper.d.ts +82 -14
- package/dist/resources/spawn-wrapper.d.ts.map +1 -1
- package/dist/resources/spawn-wrapper.js +585 -27
- package/dist/resources/spawn-wrapper.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +4055 -1734
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sequence/index.d.ts.map +1 -1
- package/dist/sequence/index.js +76 -68
- package/dist/sequence/index.js.map +1 -1
- package/dist/session/canon-lint.d.ts +24 -3
- package/dist/session/canon-lint.d.ts.map +1 -1
- package/dist/session/canon-lint.js +72 -31
- package/dist/session/canon-lint.js.map +1 -1
- package/dist/session/engine-ops.d.ts +1 -4
- package/dist/session/engine-ops.d.ts.map +1 -1
- package/dist/session/engine-ops.js +42 -6
- package/dist/session/engine-ops.js.map +1 -1
- package/dist/sessions/briefing.d.ts +11 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +76 -8
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/handoff.d.ts +3 -1
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/handoff.js +2 -0
- package/dist/sessions/handoff.js.map +1 -1
- package/dist/shutdown-deadline.d.ts +24 -33
- package/dist/shutdown-deadline.d.ts.map +1 -1
- package/dist/shutdown-deadline.js +71 -14
- package/dist/shutdown-deadline.js.map +1 -1
- package/dist/shutdown.d.ts +21 -10
- package/dist/shutdown.d.ts.map +1 -1
- package/dist/shutdown.js +89 -42
- package/dist/shutdown.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/orchestrator/validator.d.ts +87 -20
- package/dist/skills/orchestrator/validator.d.ts.map +1 -1
- package/dist/skills/orchestrator/validator.js +330 -241
- package/dist/skills/orchestrator/validator.js.map +1 -1
- package/dist/store/agent-resolver.d.ts.map +1 -1
- package/dist/store/agent-resolver.js +16 -9
- package/dist/store/agent-resolver.js.map +1 -1
- package/dist/store/attachment-store.d.ts +4 -1
- package/dist/store/attachment-store.d.ts.map +1 -1
- package/dist/store/attachment-store.js +20 -3
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/store/background-jobs.d.ts +426 -105
- package/dist/store/background-jobs.d.ts.map +1 -1
- package/dist/store/background-jobs.js +1208 -252
- package/dist/store/background-jobs.js.map +1 -1
- package/dist/store/background-ops.d.ts +145 -8
- package/dist/store/background-ops.d.ts.map +1 -1
- package/dist/store/background-ops.js +459 -15
- package/dist/store/background-ops.js.map +1 -1
- package/dist/store/backup-inspect.d.ts +48 -4
- package/dist/store/backup-inspect.d.ts.map +1 -1
- package/dist/store/backup-inspect.js +384 -4
- package/dist/store/backup-inspect.js.map +1 -1
- package/dist/store/converters.d.ts.map +1 -1
- package/dist/store/converters.js +19 -2
- package/dist/store/converters.js.map +1 -1
- package/dist/store/data-accessor.d.ts +9 -1
- package/dist/store/data-accessor.d.ts.map +1 -1
- package/dist/store/data-accessor.js +22 -5
- package/dist/store/data-accessor.js.map +1 -1
- package/dist/store/data-safety-central.d.ts.map +1 -1
- package/dist/store/data-safety-central.js +272 -232
- package/dist/store/data-safety-central.js.map +1 -1
- package/dist/store/db-helpers.d.ts +19 -3
- package/dist/store/db-helpers.d.ts.map +1 -1
- package/dist/store/db-helpers.js +42 -58
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts +63 -4
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +166 -19
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/exodus/archive.d.ts +21 -7
- package/dist/store/exodus/archive.d.ts.map +1 -1
- package/dist/store/exodus/archive.js +60 -18
- package/dist/store/exodus/archive.js.map +1 -1
- package/dist/store/exodus/migrate.d.ts.map +1 -1
- package/dist/store/exodus/migrate.js +27 -23
- package/dist/store/exodus/migrate.js.map +1 -1
- package/dist/store/exodus/on-open.d.ts +16 -27
- package/dist/store/exodus/on-open.d.ts.map +1 -1
- package/dist/store/exodus/on-open.js +121 -169
- package/dist/store/exodus/on-open.js.map +1 -1
- package/dist/store/exodus/recovery.d.ts +90 -0
- package/dist/store/exodus/recovery.d.ts.map +1 -0
- package/dist/store/exodus/recovery.js +434 -0
- package/dist/store/exodus/recovery.js.map +1 -0
- package/dist/store/exodus/types.d.ts +15 -0
- package/dist/store/exodus/types.d.ts.map +1 -1
- package/dist/store/memory-accessor.d.ts +52 -3
- package/dist/store/memory-accessor.d.ts.map +1 -1
- package/dist/store/memory-accessor.js +122 -7
- package/dist/store/memory-accessor.js.map +1 -1
- package/dist/store/nexus-sqlite.d.ts +24 -6
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.js +39 -20
- package/dist/store/nexus-sqlite.js.map +1 -1
- package/dist/store/ports/domain-binding.d.ts.map +1 -1
- package/dist/store/ports/domain-binding.js +27 -5
- package/dist/store/ports/domain-binding.js.map +1 -1
- package/dist/store/schema/background-jobs.d.ts +181 -2
- package/dist/store/schema/background-jobs.d.ts.map +1 -1
- package/dist/store/schema/background-jobs.js +27 -3
- package/dist/store/schema/background-jobs.js.map +1 -1
- package/dist/store/schema/cleo-global/skills.d.ts +2 -2
- package/dist/store/schema/cleo-project/lifecycle.d.ts +2 -2
- package/dist/store/schema/cleo-project/runtime.d.ts +1 -1
- package/dist/store/schema/cleo-project/tasks-core.d.ts +1 -1
- package/dist/store/schema/lifecycle.d.ts +2 -2
- package/dist/store/schema/skills-schema.d.ts +2 -2
- package/dist/store/schema/tasks.d.ts +1 -1
- package/dist/store/session-store.d.ts +3 -1
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +252 -207
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts +39 -4
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +689 -527
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/tasks-sqlite.d.ts +3 -1
- package/dist/store/tasks-sqlite.d.ts.map +1 -1
- package/dist/store/tasks-sqlite.js +309 -279
- package/dist/store/tasks-sqlite.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +8 -8
- package/dist/store/writer-lease.d.ts +80 -8
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +503 -134
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/system/audit.d.ts +2 -1
- package/dist/system/audit.d.ts.map +1 -1
- package/dist/system/audit.js +118 -71
- package/dist/system/audit.js.map +1 -1
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/backup.js +5 -5
- package/dist/system/backup.js.map +1 -1
- package/dist/system/inject-generate.js +1 -1
- package/dist/task-work/index.d.ts +3 -0
- package/dist/task-work/index.d.ts.map +1 -1
- package/dist/task-work/index.js +34 -37
- package/dist/task-work/index.js.map +1 -1
- package/dist/tasks/ac-coverage-gate.d.ts.map +1 -1
- package/dist/tasks/ac-coverage-gate.js +4 -0
- package/dist/tasks/ac-coverage-gate.js.map +1 -1
- package/dist/tasks/ac-immutability.d.ts +7 -4
- package/dist/tasks/ac-immutability.d.ts.map +1 -1
- package/dist/tasks/ac-immutability.js +19 -17
- package/dist/tasks/ac-immutability.js.map +1 -1
- package/dist/tasks/ac-table.d.ts +5 -5
- package/dist/tasks/ac-table.d.ts.map +1 -1
- package/dist/tasks/ac-table.js +26 -12
- package/dist/tasks/ac-table.js.map +1 -1
- package/dist/tasks/acceptance-input.d.ts +52 -0
- package/dist/tasks/acceptance-input.d.ts.map +1 -0
- package/dist/tasks/acceptance-input.js +259 -0
- package/dist/tasks/acceptance-input.js.map +1 -0
- package/dist/tasks/add.d.ts +67 -29
- package/dist/tasks/add.d.ts.map +1 -1
- package/dist/tasks/add.js +255 -97
- package/dist/tasks/add.js.map +1 -1
- package/dist/tasks/complete.d.ts +16 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +152 -55
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/decompose.d.ts +103 -0
- package/dist/tasks/decompose.d.ts.map +1 -0
- package/dist/tasks/decompose.js +224 -0
- package/dist/tasks/decompose.js.map +1 -0
- package/dist/tasks/delete.d.ts +5 -8
- package/dist/tasks/delete.d.ts.map +1 -1
- package/dist/tasks/delete.js +4 -3
- package/dist/tasks/delete.js.map +1 -1
- package/dist/tasks/dependency-check.d.ts +59 -0
- package/dist/tasks/dependency-check.d.ts.map +1 -1
- package/dist/tasks/dependency-check.js +73 -0
- package/dist/tasks/dependency-check.js.map +1 -1
- package/dist/tasks/deps-ready.d.ts +13 -1
- package/dist/tasks/deps-ready.d.ts.map +1 -1
- package/dist/tasks/deps-ready.js +16 -5
- package/dist/tasks/deps-ready.js.map +1 -1
- package/dist/tasks/engine-converters.d.ts +7 -0
- package/dist/tasks/engine-converters.d.ts.map +1 -1
- package/dist/tasks/engine-converters.js +8 -1
- package/dist/tasks/engine-converters.js.map +1 -1
- package/dist/tasks/evidence.d.ts +88 -27
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +344 -43
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/find.d.ts +16 -12
- package/dist/tasks/find.d.ts.map +1 -1
- package/dist/tasks/find.js +107 -93
- package/dist/tasks/find.js.map +1 -1
- package/dist/tasks/gate-preview.d.ts +73 -0
- package/dist/tasks/gate-preview.d.ts.map +1 -0
- package/dist/tasks/gate-preview.js +119 -0
- package/dist/tasks/gate-preview.js.map +1 -0
- package/dist/tasks/gate-runner.d.ts +88 -14
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +719 -250
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/hierarchy-policy.d.ts +8 -0
- package/dist/tasks/hierarchy-policy.d.ts.map +1 -1
- package/dist/tasks/hierarchy-policy.js +9 -2
- package/dist/tasks/hierarchy-policy.js.map +1 -1
- package/dist/tasks/hierarchy.d.ts +67 -1
- package/dist/tasks/hierarchy.d.ts.map +1 -1
- package/dist/tasks/hierarchy.js +81 -1
- package/dist/tasks/hierarchy.js.map +1 -1
- package/dist/tasks/index.d.ts +3 -0
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +3 -0
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/infer-add-params.d.ts +1 -20
- package/dist/tasks/infer-add-params.d.ts.map +1 -1
- package/dist/tasks/infer-add-params.js +2 -203
- package/dist/tasks/infer-add-params.js.map +1 -1
- package/dist/tasks/list.d.ts +7 -2
- package/dist/tasks/list.d.ts.map +1 -1
- package/dist/tasks/list.js +24 -36
- package/dist/tasks/list.js.map +1 -1
- package/dist/tasks/ops.d.ts +6 -61
- package/dist/tasks/ops.d.ts.map +1 -1
- package/dist/tasks/ops.js +5 -53
- package/dist/tasks/ops.js.map +1 -1
- package/dist/tasks/plan.d.ts +15 -1
- package/dist/tasks/plan.d.ts.map +1 -1
- package/dist/tasks/plan.js +23 -25
- package/dist/tasks/plan.js.map +1 -1
- package/dist/tasks/population.d.ts +46 -0
- package/dist/tasks/population.d.ts.map +1 -0
- package/dist/tasks/population.js +88 -0
- package/dist/tasks/population.js.map +1 -0
- package/dist/tasks/reconcile-scope.d.ts +157 -0
- package/dist/tasks/reconcile-scope.d.ts.map +1 -0
- package/dist/tasks/reconcile-scope.js +242 -0
- package/dist/tasks/reconcile-scope.js.map +1 -0
- package/dist/tasks/req.d.ts +51 -12
- package/dist/tasks/req.d.ts.map +1 -1
- package/dist/tasks/req.js +125 -69
- package/dist/tasks/req.js.map +1 -1
- package/dist/tasks/session-scope.d.ts +6 -31
- package/dist/tasks/session-scope.d.ts.map +1 -1
- package/dist/tasks/session-scope.js +3 -17
- package/dist/tasks/session-scope.js.map +1 -1
- package/dist/tasks/severity-attestation.d.ts +39 -10
- package/dist/tasks/severity-attestation.d.ts.map +1 -1
- package/dist/tasks/severity-attestation.js +75 -36
- package/dist/tasks/severity-attestation.js.map +1 -1
- package/dist/tasks/task-next.d.ts +5 -0
- package/dist/tasks/task-next.d.ts.map +1 -1
- package/dist/tasks/task-next.js +10 -9
- package/dist/tasks/task-next.js.map +1 -1
- package/dist/tasks/task-reparent.d.ts.map +1 -1
- package/dist/tasks/task-reparent.js +3 -13
- package/dist/tasks/task-reparent.js.map +1 -1
- package/dist/tasks/update.d.ts +20 -43
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +64 -49
- package/dist/tasks/update.js.map +1 -1
- package/dist/validation/engine-ops.d.ts +17 -1
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +149 -11
- package/dist/validation/engine-ops.js.map +1 -1
- package/dist/workgraph/reparent-preflight.d.ts +1 -1
- package/dist/workgraph/reparent-preflight.d.ts.map +1 -1
- package/dist/workgraph/reparent-preflight.js +6 -1
- package/dist/workgraph/reparent-preflight.js.map +1 -1
- package/dist/workgraph/scaffold-apply.d.ts.map +1 -1
- package/dist/workgraph/scaffold-apply.js +81 -76
- package/dist/workgraph/scaffold-apply.js.map +1 -1
- package/dist/workgraph/structure-validation.d.ts +1 -1
- package/dist/workgraph/structure-validation.d.ts.map +1 -1
- package/dist/workgraph/structure-validation.js +6 -1
- package/dist/workgraph/structure-validation.js.map +1 -1
- package/migrations/drizzle-tasks/20260919180000_t12263-job-ownership-fences/migration.sql +34 -0
- package/migrations/drizzle-tasks/20260919184500_t12265-durable-pending-proposals/migration.sql +4 -0
- package/package.json +18 -13
- package/schemas/config.schema.json +2 -2
- package/schemas/project-context.schema.json +12 -0
- package/templates/CLEO-INJECTION.md +114 -101
package/dist/tasks/evidence.js
CHANGED
|
@@ -26,6 +26,7 @@ import { readFile, stat } from 'node:fs/promises';
|
|
|
26
26
|
import { dirname, isAbsolute, join, resolve as resolvePath } from 'node:path';
|
|
27
27
|
import { EvidenceParseError, ExitCode, GATE_EVIDENCE_REQUIREMENTS, parseEvidenceString, validateEvidenceForGate, } from '@cleocode/contracts';
|
|
28
28
|
import { CleoError } from '../errors.js';
|
|
29
|
+
import { describeUnusableEvidenceGitRoot, E_EVIDENCE_GIT_ROOT, findNestedGitWorkTrees, findWorkTreeContainingCommit, gitToplevel, isGitWorkTree, resolveDeclaredEvidenceGitRoot, } from '../git/work-tree.js';
|
|
29
30
|
import { pushWarning } from '../output.js';
|
|
30
31
|
import { getEffectiveHead } from '../worktree/effective-head.js';
|
|
31
32
|
import { DISABLE_ENV, describeMemoryLimit } from './heavy-tool-limit.js';
|
|
@@ -132,26 +133,69 @@ export function parseEvidence(raw) {
|
|
|
132
133
|
throw err;
|
|
133
134
|
}
|
|
134
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate a single parsed atom against the filesystem / git / tools.
|
|
138
|
+
*
|
|
139
|
+
* @param parsed - Parsed atom from {@link parseEvidence}
|
|
140
|
+
* @param projectRoot - Absolute path to project root (for resolving files, git)
|
|
141
|
+
* @param taskId - Optional CLEO task ID. When provided, enables branch-scope
|
|
142
|
+
* check (T9178), content-intersect check (T9245), worktree-aware HEAD
|
|
143
|
+
* resolution (T-WT-3), and git-show file fallback for branch-only files
|
|
144
|
+
* (T11959).
|
|
145
|
+
* @param siblingCommitSha - Optional sha of a sibling `commit:` atom from the
|
|
146
|
+
* same evidence string. When provided, `files:` paths are resolved against
|
|
147
|
+
* that commit's tree FIRST (T12107 / gh#1195) before falling back to the
|
|
148
|
+
* worktree and git refs — covers a merged commit whose files are not yet
|
|
149
|
+
* present in the local checkout.
|
|
150
|
+
* @param context - Current task, gate and acceptance criteria used to validate scoped proof.
|
|
151
|
+
* @returns Validation outcome with canonicalised form on success
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const result = await validateAtom(parsedAtom, projectRoot, task.id, undefined, context);
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @task T832
|
|
158
|
+
* @task T11959
|
|
159
|
+
* @task T12107
|
|
160
|
+
* @adr ADR-051 §3
|
|
161
|
+
*/
|
|
135
162
|
export async function validateAtom(parsed, projectRoot,
|
|
136
163
|
/** T9178: When provided, validates commit is on task/<taskId> branch. */
|
|
137
164
|
taskId,
|
|
138
165
|
/** T12107: sha of a sibling `commit:` atom in the same evidence string. */
|
|
139
|
-
siblingCommitSha) {
|
|
166
|
+
siblingCommitSha, context) {
|
|
167
|
+
if (parsed.kind === 'pr' && (!Number.isInteger(parsed.prNumber) || parsed.prNumber <= 0)) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
codeName: 'E_EVIDENCE_INVALID',
|
|
171
|
+
reason: 'PR number must be a positive integer.',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
140
174
|
// gh#1365: resolve the tree under test ONCE, here, and pass it down as a
|
|
141
175
|
// required parameter. Resolving inside each validator would centralise the
|
|
142
176
|
// logic but still let two of them resolve independently and drift — which is
|
|
143
177
|
// exactly how gh#1419 happened (a revalidation key derived from one root
|
|
144
178
|
// while the validation answered about another). A required parameter makes
|
|
145
179
|
// that divergence unrepresentable rather than merely discouraged.
|
|
180
|
+
// T12308: hand the resolver the SHA this evidence is about so a CLEO root
|
|
181
|
+
// that parents several repositories resolves itself. `files:` atoms carry no
|
|
182
|
+
// SHA of their own, so the sibling `commit:`/`pr:` anchor supplies it — the
|
|
183
|
+
// same anchor `validateFiles` already uses to read bytes at that commit.
|
|
184
|
+
const executionRootHints = {
|
|
185
|
+
...(parsed.kind === 'commit' ? { commitSha: parsed.sha } : {}),
|
|
186
|
+
...(parsed.kind === 'files' && (context?.artifactCommitSha ?? siblingCommitSha) !== undefined
|
|
187
|
+
? { commitSha: (context?.artifactCommitSha ?? siblingCommitSha) }
|
|
188
|
+
: {}),
|
|
189
|
+
};
|
|
146
190
|
const roots = {
|
|
147
191
|
storeRoot: projectRoot,
|
|
148
|
-
executionRoot: resolveEvidenceExecutionRoot(projectRoot),
|
|
192
|
+
executionRoot: resolveEvidenceExecutionRoot(projectRoot, undefined, executionRootHints),
|
|
149
193
|
};
|
|
150
194
|
switch (parsed.kind) {
|
|
151
195
|
case 'commit':
|
|
152
196
|
return validateCommit(parsed.sha, roots, taskId);
|
|
153
197
|
case 'files':
|
|
154
|
-
return validateFiles(parsed.paths, roots, taskId, siblingCommitSha);
|
|
198
|
+
return validateFiles(parsed.paths, roots, taskId, context?.artifactCommitSha ?? siblingCommitSha, Boolean(context?.artifactCommitSha));
|
|
155
199
|
case 'test-run':
|
|
156
200
|
return validateTestRun(parsed.path, roots);
|
|
157
201
|
case 'tool':
|
|
@@ -173,7 +217,7 @@ siblingCommitSha) {
|
|
|
173
217
|
// db, which is CLEO's own record, not the repository's.
|
|
174
218
|
return validateDecision(parsed.decisionId, roots);
|
|
175
219
|
case 'pr':
|
|
176
|
-
return validatePrAtom(parsed.prNumber, roots);
|
|
220
|
+
return validatePrAtom(parsed.prNumber, roots, context);
|
|
177
221
|
case 'satisfies': {
|
|
178
222
|
// ADR-079-r2: 5-check validator pipeline shipped by T10507.
|
|
179
223
|
// Delegates to the dedicated validator module to keep the dispatch
|
|
@@ -309,15 +353,17 @@ function resolveIntegrationBranches(env, projectContext) {
|
|
|
309
353
|
*
|
|
310
354
|
* Checking the remote-tracking ref preserves verification after a merged local
|
|
311
355
|
* integration branch has been cleaned up.
|
|
356
|
+
*
|
|
357
|
+
* @param executionRoot - The repo the evidence is about, never the store root.
|
|
312
358
|
*/
|
|
313
|
-
async function findReachableIntegrationBranch(sha, branches,
|
|
359
|
+
async function findReachableIntegrationBranch(sha, branches, executionRoot) {
|
|
314
360
|
for (const branch of branches) {
|
|
315
361
|
const refs = [`refs/heads/${branch}`, `refs/remotes/origin/${branch}`];
|
|
316
362
|
for (const ref of refs) {
|
|
317
|
-
const exists = await runCommand('git', ['rev-parse', '--verify', ref],
|
|
363
|
+
const exists = await runCommand('git', ['rev-parse', '--verify', ref], executionRoot);
|
|
318
364
|
if (exists.exitCode !== 0)
|
|
319
365
|
continue;
|
|
320
|
-
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, ref],
|
|
366
|
+
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, ref], executionRoot);
|
|
321
367
|
if (reachable.exitCode === 0)
|
|
322
368
|
return branch;
|
|
323
369
|
}
|
|
@@ -333,6 +379,17 @@ async function validateCommit(sha, roots, taskId) {
|
|
|
333
379
|
codeName: 'E_EVIDENCE_INVALID',
|
|
334
380
|
};
|
|
335
381
|
}
|
|
382
|
+
// gh#1462: without a work tree every git call below fails, and the first one
|
|
383
|
+
// (`cat-file`) reported it as "Commit not found in repository" — which says
|
|
384
|
+
// the commit is absent, not that git never ran in a repository at all. Same
|
|
385
|
+
// distinction `pr:` needs, so it fails the same way.
|
|
386
|
+
if (!isGitWorkTree(executionRoot)) {
|
|
387
|
+
return {
|
|
388
|
+
ok: false,
|
|
389
|
+
reason: describeUnusableEvidenceGitRoot(projectRoot, executionRoot),
|
|
390
|
+
codeName: E_EVIDENCE_GIT_ROOT,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
336
393
|
const exists = await runCommand('git', ['cat-file', '-e', `${sha}^{commit}`], executionRoot);
|
|
337
394
|
if (exists.exitCode !== 0) {
|
|
338
395
|
return {
|
|
@@ -352,7 +409,7 @@ async function validateCommit(sha, roots, taskId) {
|
|
|
352
409
|
const { loadProjectContext } = await import('../agents/variable-substitution.js');
|
|
353
410
|
const projectCtx = loadProjectContext(projectRoot);
|
|
354
411
|
const integrationBranches = resolveIntegrationBranches(process.env, projectCtx.loaded ? projectCtx.context : null);
|
|
355
|
-
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, effectiveHead],
|
|
412
|
+
const reachable = await runCommand('git', ['merge-base', '--is-ancestor', sha, effectiveHead], executionRoot);
|
|
356
413
|
if (reachable.exitCode !== 0) {
|
|
357
414
|
// DHQ-083 companion (a): when the commit is not reachable from the task
|
|
358
415
|
// branch HEAD, also accept commits reachable from the configured
|
|
@@ -398,7 +455,7 @@ async function validateCommit(sha, roots, taskId) {
|
|
|
398
455
|
const branchRef = `task/${taskId}`;
|
|
399
456
|
const branchExists = await runCommand('git', ['rev-parse', '--verify', branchRef], executionRoot);
|
|
400
457
|
if (branchExists.exitCode === 0) {
|
|
401
|
-
const onBranch = await runCommand('git', ['merge-base', '--is-ancestor', sha, branchRef],
|
|
458
|
+
const onBranch = await runCommand('git', ['merge-base', '--is-ancestor', sha, branchRef], executionRoot);
|
|
402
459
|
if (onBranch.exitCode !== 0) {
|
|
403
460
|
// T11959 / DHQ-083 companion (a): also accept commits reachable from
|
|
404
461
|
// the configured integration branches. This covers:
|
|
@@ -406,7 +463,7 @@ async function validateCommit(sha, roots, taskId) {
|
|
|
406
463
|
// - post-merge task branches that have been cleaned up (the SHA is now
|
|
407
464
|
// only reachable from main, not from `task/<id>` which no longer exists)
|
|
408
465
|
// gh#1116 / T12028: reuses the integrationBranches resolved above.
|
|
409
|
-
const reachableIntegrationBranch = await findReachableIntegrationBranch(sha, integrationBranches,
|
|
466
|
+
const reachableIntegrationBranch = await findReachableIntegrationBranch(sha, integrationBranches, executionRoot);
|
|
410
467
|
if (reachableIntegrationBranch === null) {
|
|
411
468
|
const branchList = integrationBranches.join(', ');
|
|
412
469
|
return {
|
|
@@ -418,14 +475,16 @@ async function validateCommit(sha, roots, taskId) {
|
|
|
418
475
|
}
|
|
419
476
|
}
|
|
420
477
|
// T9245: content-intersect check — diff MUST touch at least one AC file.
|
|
421
|
-
|
|
478
|
+
// gh#1462: the task row comes from the store, the diff from the repo under
|
|
479
|
+
// test — both roots, like every other mixed read on this switch.
|
|
480
|
+
const intersectResult = await checkCommitContentIntersect(sha, taskId, roots);
|
|
422
481
|
if (!intersectResult.ok) {
|
|
423
482
|
return intersectResult;
|
|
424
483
|
}
|
|
425
484
|
}
|
|
426
|
-
const short = await runCommand('git', ['rev-parse', '--short', sha],
|
|
485
|
+
const short = await runCommand('git', ['rev-parse', '--short', sha], executionRoot);
|
|
427
486
|
const shortSha = short.stdout.trim() || sha.slice(0, 7);
|
|
428
|
-
const full = await runCommand('git', ['rev-parse', sha],
|
|
487
|
+
const full = await runCommand('git', ['rev-parse', sha], executionRoot);
|
|
429
488
|
const fullSha = full.stdout.trim() || sha;
|
|
430
489
|
return { ok: true, atom: { kind: 'commit', sha: fullSha, shortSha } };
|
|
431
490
|
}
|
|
@@ -850,27 +909,26 @@ export function resolveCanonicalProjectRoot(projectRoot) {
|
|
|
850
909
|
* Fix (T-WT-2): This function calls `resolveCanonicalProjectRoot(projectRoot)`
|
|
851
910
|
* to obtain the main repo root before opening `tasks.db`. The canonical main
|
|
852
911
|
* DB is authoritative for all task metadata reads during gate verification.
|
|
853
|
-
* Git operations (`gitShowFiles`)
|
|
854
|
-
*
|
|
912
|
+
* Git operations (`gitShowFiles`) use `executionRoot` — the repo the evidence is
|
|
913
|
+
* about, which gh#1462 showed is not necessarily the store root.
|
|
855
914
|
*
|
|
856
915
|
* @param sha - Commit SHA whose diff is inspected.
|
|
857
916
|
* @param taskId - CLEO task ID used to load AC file declarations.
|
|
858
|
-
* @param
|
|
859
|
-
* DB reads use `resolveCanonicalProjectRoot(projectRoot)` internally.
|
|
917
|
+
* @param roots - Store root for the task row, execution root for the diff.
|
|
860
918
|
*
|
|
861
919
|
* @internal
|
|
862
920
|
* @task T9245
|
|
863
921
|
* @task T-WT-2
|
|
922
|
+
* @task gh#1462
|
|
864
923
|
* @adr ADR-051-worktree-extension
|
|
865
924
|
*/
|
|
866
|
-
async function checkCommitContentIntersect(sha, taskId,
|
|
925
|
+
async function checkCommitContentIntersect(sha, taskId, roots) {
|
|
926
|
+
const { storeRoot, executionRoot } = roots;
|
|
867
927
|
// BUG-C FIX (T-WT-2 / E-WORKTREE-IVTR): always read task metadata from the
|
|
868
|
-
// canonical (main) repository DB. When
|
|
928
|
+
// canonical (main) repository DB. When the store root is a worktree path the
|
|
869
929
|
// worktree's tasks.db is a stale spawn-time snapshot; the main DB is the
|
|
870
|
-
// authoritative source for task.files and task.acceptance.
|
|
871
|
-
|
|
872
|
-
// object store across worktrees, so any dir in the shared tree works.
|
|
873
|
-
const canonicalRoot = resolveCanonicalProjectRoot(projectRoot);
|
|
930
|
+
// authoritative source for task.files and task.acceptance.
|
|
931
|
+
const canonicalRoot = resolveCanonicalProjectRoot(storeRoot);
|
|
874
932
|
// Best-effort load — DB unavailability MUST NOT block verify.
|
|
875
933
|
let task = null;
|
|
876
934
|
try {
|
|
@@ -906,7 +964,7 @@ async function checkCommitContentIntersect(sha, taskId, projectRoot) {
|
|
|
906
964
|
* is still reported; it just cannot fail the gate.
|
|
907
965
|
*/
|
|
908
966
|
const enforcing = provenance === 'declared';
|
|
909
|
-
const diffFiles = await gitShowFiles(sha,
|
|
967
|
+
const diffFiles = await gitShowFiles(sha, executionRoot);
|
|
910
968
|
const acPreview = `${acFiles.slice(0, 5).join(', ')}${acFiles.length > 5 ? '…' : ''}`;
|
|
911
969
|
if (diffFiles.length === 0) {
|
|
912
970
|
const reason = `Commit ${sha.slice(0, 7)} touches no files — cannot satisfy implemented gate ` +
|
|
@@ -1026,7 +1084,7 @@ async function gitShowFileContentAtCommit(relPath, sha, projectRoot) {
|
|
|
1026
1084
|
* @task T11959
|
|
1027
1085
|
* @task T12107
|
|
1028
1086
|
*/
|
|
1029
|
-
async function validateFiles(paths, roots, taskId, commitSha) {
|
|
1087
|
+
async function validateFiles(paths, roots, taskId, commitSha, requireCommitArtifact = false) {
|
|
1030
1088
|
const { storeRoot: projectRoot, executionRoot } = roots;
|
|
1031
1089
|
if (paths.length === 0) {
|
|
1032
1090
|
return {
|
|
@@ -1051,6 +1109,13 @@ async function validateFiles(paths, roots, taskId, commitSha) {
|
|
|
1051
1109
|
if (commitSha) {
|
|
1052
1110
|
content = await gitShowFileContentAtCommit(p, commitSha, executionRoot);
|
|
1053
1111
|
}
|
|
1112
|
+
if (requireCommitArtifact && content === null) {
|
|
1113
|
+
return {
|
|
1114
|
+
ok: false,
|
|
1115
|
+
codeName: 'E_EVIDENCE_INSUFFICIENT',
|
|
1116
|
+
reason: `Cannot inspect ${p} at PR merge ${commitSha}; fetch that commit before recording evidence. Current checkout bytes cannot substitute.`,
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1054
1119
|
if (content === null && existsSync(abs)) {
|
|
1055
1120
|
// Happy path — file exists on disk.
|
|
1056
1121
|
const st = await stat(abs);
|
|
@@ -1109,11 +1174,38 @@ async function validateFiles(paths, roots, taskId, commitSha) {
|
|
|
1109
1174
|
* pointing at a tmpdir, a non-git cwd) falls back to `projectRoot`, so
|
|
1110
1175
|
* single-checkout behaviour is bit-for-bit unchanged.
|
|
1111
1176
|
*
|
|
1177
|
+
* gh#1462 added the other direction: when `projectRoot` is a PARENT of the
|
|
1178
|
+
* checkout, neither the caller's cwd (which is that parent) nor `projectRoot`
|
|
1179
|
+
* is a git work tree, and `gh`/`git` run there fail with "fatal: not a git
|
|
1180
|
+
* repository". The checkout is a direct child in that layout, so it is used
|
|
1181
|
+
* when exactly one child is a work tree.
|
|
1182
|
+
*
|
|
1183
|
+
* T12308 closed the two holes that layout still had. A root parenting SEVERAL
|
|
1184
|
+
* repositories — the reported case had thirteen — had no resolution at all,
|
|
1185
|
+
* and the remediation the failure printed (`GIT_DIR`/`GIT_WORK_TREE`) could not
|
|
1186
|
+
* work, because the guard asks whether the CURRENT DIRECTORY is in a work tree
|
|
1187
|
+
* and the CLEO root never is. So the order is now, most to least specific:
|
|
1188
|
+
*
|
|
1189
|
+
* 1. an operator declaration (`CLEO_EVIDENCE_GIT_ROOT`, `GIT_WORK_TREE`, or
|
|
1190
|
+
* `evidence.gitRoot` in `.cleo/project-context.json`) — honoured by
|
|
1191
|
+
* RUNNING there, which is what the old advice never did;
|
|
1192
|
+
* 2. the caller's own checkout, when it belongs to this project;
|
|
1193
|
+
* 3. `projectRoot` itself;
|
|
1194
|
+
* 4. the one child checkout, or — when the evidence names a commit — the one
|
|
1195
|
+
* child checkout that actually CONTAINS it, which resolves a multi-repo
|
|
1196
|
+
* root with no configuration at all.
|
|
1197
|
+
*
|
|
1198
|
+
* Anything still ambiguous keeps `projectRoot`, and the atom reports the
|
|
1199
|
+
* candidates by name rather than attesting against a guessed repository.
|
|
1200
|
+
*
|
|
1112
1201
|
* @param projectRoot - Absolute CLEO store root.
|
|
1113
1202
|
* @param cwd - Directory the CLI was invoked from. Defaults to `process.cwd()`.
|
|
1203
|
+
* @param hints - What the evidence itself reveals about which repo it is about.
|
|
1114
1204
|
* @returns Absolute path of the tree to execute in.
|
|
1115
1205
|
*
|
|
1116
1206
|
* @task T12112 (gh#1220, gh#1226, gh#1230)
|
|
1207
|
+
* @task gh#1462
|
|
1208
|
+
* @task T12308
|
|
1117
1209
|
*/
|
|
1118
1210
|
export function resolveEvidenceExecutionRoot(projectRoot,
|
|
1119
1211
|
// This function's entire purpose is to recover the tree the CLI was INVOKED
|
|
@@ -1124,20 +1216,27 @@ export function resolveEvidenceExecutionRoot(projectRoot,
|
|
|
1124
1216
|
// while every test stayed green. The bare cwd is the only correct source,
|
|
1125
1217
|
// and it only LOCATES a candidate; the same-project check below decides
|
|
1126
1218
|
// whether it is honoured.
|
|
1127
|
-
cwd = process.cwd())
|
|
1128
|
-
|
|
1219
|
+
cwd = process.cwd(), // CWD-OK: the caller's invocation dir is the subject, not a stand-in for the project root (gh#1220)
|
|
1220
|
+
hints = {}) {
|
|
1221
|
+
// T12308: an explicit declaration outranks every inference. Returned even
|
|
1222
|
+
// when it is NOT a work tree, so the guard can reject it BY NAME — silently
|
|
1223
|
+
// falling back would measure a different checkout than the operator named
|
|
1224
|
+
// and attest the result as if it were theirs.
|
|
1225
|
+
const declared = resolveDeclaredEvidenceGitRoot(projectRoot);
|
|
1226
|
+
if (declared !== null)
|
|
1227
|
+
return gitToplevel(declared.path) ?? declared.path;
|
|
1228
|
+
let toplevel = null;
|
|
1129
1229
|
try {
|
|
1130
|
-
toplevel =
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1230
|
+
toplevel =
|
|
1231
|
+
execFileSync('git', ['rev-parse', '--show-toplevel'], {
|
|
1232
|
+
cwd,
|
|
1233
|
+
encoding: 'utf-8',
|
|
1234
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1235
|
+
}).trim() || null;
|
|
1135
1236
|
}
|
|
1136
1237
|
catch {
|
|
1137
|
-
|
|
1238
|
+
toplevel = null; // not a git checkout — try the store root and its children
|
|
1138
1239
|
}
|
|
1139
|
-
if (!toplevel)
|
|
1140
|
-
return projectRoot;
|
|
1141
1240
|
const sameProject = (a, b) => {
|
|
1142
1241
|
const norm = (x) => {
|
|
1143
1242
|
try {
|
|
@@ -1150,8 +1249,27 @@ cwd = process.cwd()) {
|
|
|
1150
1249
|
return norm(a) === norm(b);
|
|
1151
1250
|
};
|
|
1152
1251
|
// Only redirect when the caller's tree is a worktree OF this project.
|
|
1153
|
-
if (sameProject(resolveCanonicalProjectRoot(toplevel), projectRoot))
|
|
1252
|
+
if (toplevel !== null && sameProject(resolveCanonicalProjectRoot(toplevel), projectRoot)) {
|
|
1154
1253
|
return toplevel;
|
|
1254
|
+
}
|
|
1255
|
+
// The ordinary single-project layout: the CLEO root IS the checkout.
|
|
1256
|
+
if (isGitWorkTree(projectRoot))
|
|
1257
|
+
return projectRoot;
|
|
1258
|
+
// gh#1462: the CLEO root is a PARENT of the checkout. `gh` and `git` run
|
|
1259
|
+
// from `projectRoot` fail with "fatal: not a git repository", which reads as
|
|
1260
|
+
// a broken tool or an unsatisfiable atom. Use the one direct child that is a
|
|
1261
|
+
// work tree; ambiguity is reported, not guessed.
|
|
1262
|
+
const candidates = findNestedGitWorkTrees(projectRoot);
|
|
1263
|
+
if (candidates.length === 1)
|
|
1264
|
+
return candidates[0];
|
|
1265
|
+
// T12308: several children. The atom itself can still identify one — a SHA
|
|
1266
|
+
// exists in exactly one of these repositories in every layout that is not a
|
|
1267
|
+
// fork of itself. Zero or several matches stay unresolved.
|
|
1268
|
+
if (candidates.length > 1 && hints.commitSha !== undefined) {
|
|
1269
|
+
const byCommit = findWorkTreeContainingCommit(candidates, hints.commitSha);
|
|
1270
|
+
if (byCommit !== null)
|
|
1271
|
+
return byCommit;
|
|
1272
|
+
}
|
|
1155
1273
|
return projectRoot;
|
|
1156
1274
|
}
|
|
1157
1275
|
async function validateTestRun(path, roots) {
|
|
@@ -1703,7 +1821,14 @@ async function validateDecision(decisionId, roots) {
|
|
|
1703
1821
|
*
|
|
1704
1822
|
* @task T9764
|
|
1705
1823
|
*/
|
|
1706
|
-
async function validatePrAtom(prNumber, roots) {
|
|
1824
|
+
async function validatePrAtom(prNumber, roots, context) {
|
|
1825
|
+
if (!context) {
|
|
1826
|
+
return {
|
|
1827
|
+
ok: false,
|
|
1828
|
+
codeName: 'E_EVIDENCE_INSUFFICIENT',
|
|
1829
|
+
reason: 'PR evidence requires current task, gate, and acceptance-criterion context.',
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1707
1832
|
// Dynamic import keeps the verification module free of a static dependency
|
|
1708
1833
|
// on the release subtree, mirroring the pattern used by validateDecision.
|
|
1709
1834
|
const { resolvePrEvidenceAtom } = await import('../release/pr-evidence.js');
|
|
@@ -1727,6 +1852,33 @@ async function validatePrAtom(prNumber, roots) {
|
|
|
1727
1852
|
if (!result.ok) {
|
|
1728
1853
|
return { ok: false, reason: result.reason, codeName: result.codeName };
|
|
1729
1854
|
}
|
|
1855
|
+
if (result.changedFileCount !== result.changedPaths.length || result.changedPaths.length === 0) {
|
|
1856
|
+
return {
|
|
1857
|
+
ok: false,
|
|
1858
|
+
codeName: 'E_EVIDENCE_INSUFFICIENT',
|
|
1859
|
+
reason: `PR #${prNumber} changed-file coverage is incomplete or empty (${result.changedPaths.length}/${result.changedFileCount}); inspect the full diff before recording evidence.`,
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
const declaredFiles = context.task.files ?? [];
|
|
1863
|
+
const taskMention = new RegExp(`(^|[^A-Za-z0-9])${context.task.id}([^A-Za-z0-9]|$)`);
|
|
1864
|
+
const explicitlyLinked = taskMention.test(`${result.title}\n${result.body}\n${result.headRefName}`);
|
|
1865
|
+
const scopeIntersects = declaredFiles.length > 0 && diffIntersectsAc(result.changedPaths, declaredFiles);
|
|
1866
|
+
if ((!explicitlyLinked && !scopeIntersects) || (declaredFiles.length > 0 && !scopeIntersects)) {
|
|
1867
|
+
return {
|
|
1868
|
+
ok: false,
|
|
1869
|
+
codeName: 'E_EVIDENCE_CONTENT_MISMATCH',
|
|
1870
|
+
reason: `PR #${prNumber} does not establish a relationship to task ${context.task.id}. Changed artifacts: ${result.changedPaths.join(', ')}. Declare task files or cite the exact task in the PR; declared scope must intersect the diff.`,
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
if (context.gates.includes('implemented') &&
|
|
1874
|
+
classifyEvidenceTask(context) === 'code' &&
|
|
1875
|
+
result.changedPaths.every(isDocumentArtifact)) {
|
|
1876
|
+
return {
|
|
1877
|
+
ok: false,
|
|
1878
|
+
codeName: 'E_EVIDENCE_CONTENT_MISMATCH',
|
|
1879
|
+
reason: `PR #${prNumber} changes only documentation and cannot implement code task ${context.task.id}. Changed artifacts: ${result.changedPaths.join(', ')}.`,
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1730
1882
|
return {
|
|
1731
1883
|
ok: true,
|
|
1732
1884
|
atom: {
|
|
@@ -1736,9 +1888,79 @@ async function validatePrAtom(prNumber, roots) {
|
|
|
1736
1888
|
mergedAt: result.mergedAt,
|
|
1737
1889
|
successCount: result.successCount,
|
|
1738
1890
|
totalChecks: result.totalChecks,
|
|
1891
|
+
changedPaths: result.changedPaths,
|
|
1892
|
+
taskId: context.task.id,
|
|
1739
1893
|
},
|
|
1740
1894
|
};
|
|
1741
1895
|
}
|
|
1896
|
+
function isDocumentArtifact(path) {
|
|
1897
|
+
return (/\.(md|mdx|rst|adoc|txt)$/i.test(path) ||
|
|
1898
|
+
/(?:^|\/)(README|LICENSE|CHANGELOG)(?:\.[^/]*)?$/i.test(path));
|
|
1899
|
+
}
|
|
1900
|
+
function classifyEvidenceTask(context) {
|
|
1901
|
+
if (context.task.kind === 'research' || context.task.kind === 'spike')
|
|
1902
|
+
return 'research';
|
|
1903
|
+
if (context.task.files?.length && context.task.files.every(isDocumentArtifact))
|
|
1904
|
+
return 'documentation';
|
|
1905
|
+
if (context.task.kind === 'bug')
|
|
1906
|
+
return 'code';
|
|
1907
|
+
if (context.task.labels?.some((label) => label === 'docs' || label === 'documentation'))
|
|
1908
|
+
return 'documentation';
|
|
1909
|
+
return 'code';
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Require explicit criterion linkage to inspected artifacts and real gate results.
|
|
1913
|
+
* @param context - Current task classification, requested gates and criterion records.
|
|
1914
|
+
* @param gate - Individual gate whose evidence is being assessed.
|
|
1915
|
+
* @param atoms - Independently validated artifacts, results and criterion references.
|
|
1916
|
+
* @returns A refusal reason when proof is insufficient, otherwise null.
|
|
1917
|
+
* @remarks PR state and CI provenance alone cannot establish every acceptance gate.
|
|
1918
|
+
* @example
|
|
1919
|
+
* ```ts
|
|
1920
|
+
* const refusal = checkTaskEvidenceContext(context, "implemented", validatedAtoms);
|
|
1921
|
+
* ```
|
|
1922
|
+
*/
|
|
1923
|
+
export function checkTaskEvidenceContext(context, gate, atoms) {
|
|
1924
|
+
const filePaths = atoms.flatMap((atom) => atom.kind === 'files' ? atom.files.map((file) => file.path) : []);
|
|
1925
|
+
const prAtoms = atoms.filter((atom) => atom.kind === 'pr');
|
|
1926
|
+
if (prAtoms.length > 1)
|
|
1927
|
+
return 'Record one PR and its merge-pinned artifacts per verification attempt; multiple merge identities are ambiguous.';
|
|
1928
|
+
for (const atom of prAtoms) {
|
|
1929
|
+
if (atom.taskId !== context.task.id || !atom.changedPaths?.length)
|
|
1930
|
+
return 'PR provenance lacks verified task scope; re-verify with current task context.';
|
|
1931
|
+
if (gate === 'implemented' && !filePaths.some((path) => atom.changedPaths?.includes(path)))
|
|
1932
|
+
return `PR #${atom.prNumber} requires files evidence for an artifact actually changed by that PR.`;
|
|
1933
|
+
if (gate === 'implemented' &&
|
|
1934
|
+
classifyEvidenceTask(context) === 'code' &&
|
|
1935
|
+
atom.changedPaths.every(isDocumentArtifact))
|
|
1936
|
+
return 'Documentation-only PR evidence cannot implement the current code task.';
|
|
1937
|
+
}
|
|
1938
|
+
if (!['implemented', 'testsPassed', 'qaPassed'].includes(gate))
|
|
1939
|
+
return null;
|
|
1940
|
+
if (classifyEvidenceTask(context) === 'code' &&
|
|
1941
|
+
(gate === 'testsPassed' || gate === 'qaPassed') &&
|
|
1942
|
+
!atoms.some((atom) => atom.kind === 'test-run' || (atom.kind === 'tool' && !atom.notApplicable))) {
|
|
1943
|
+
return `Code task ${context.task.id} requires an actual verification result for ${gate}; absence of a toolchain is not a passing result.`;
|
|
1944
|
+
}
|
|
1945
|
+
const linked = atoms.filter((atom) => atom.kind === 'satisfies' && atom.targetTaskId === context.task.id);
|
|
1946
|
+
if (context.criteria.length > 0 && linked.length === 0)
|
|
1947
|
+
return `Task ${context.task.id} requires explicit criterion linkage: add satisfies:${context.task.id}#AC<n> alongside artifacts and verification results.`;
|
|
1948
|
+
for (const atom of linked) {
|
|
1949
|
+
if (!context.criteria.some((criterion) => criterion.id === atom.resolvedAcUuid))
|
|
1950
|
+
return `Criterion ${atom.resolvedAcUuid ?? atom.targetAcAlias} is not in the current task criteria.`;
|
|
1951
|
+
}
|
|
1952
|
+
if (linked.length > 0 &&
|
|
1953
|
+
gate === 'implemented' &&
|
|
1954
|
+
filePaths.length === 0 &&
|
|
1955
|
+
!atoms.some((atom) => atom.kind === 'decision'))
|
|
1956
|
+
return 'Criterion implementation evidence requires inspected files or a sourced research decision.';
|
|
1957
|
+
if (classifyEvidenceTask(context) === 'code' &&
|
|
1958
|
+
gate === 'implemented' &&
|
|
1959
|
+
atoms.some((atom) => atom.kind === 'decision') &&
|
|
1960
|
+
!atoms.some((atom) => atom.kind === 'commit' || atom.kind === 'pr'))
|
|
1961
|
+
return 'A research decision alone cannot implement a code-fix task.';
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1742
1964
|
/**
|
|
1743
1965
|
* Check that the provided evidence atoms satisfy the callsite-coverage
|
|
1744
1966
|
* requirement for tasks carrying the `callsite-coverage` label.
|
|
@@ -1881,11 +2103,13 @@ export function checkGateEvidenceMinimumDetailed(gate, atoms) {
|
|
|
1881
2103
|
* @param capturedBy - Agent identifier
|
|
1882
2104
|
* @param override - True when CLEO_OWNER_OVERRIDE is set
|
|
1883
2105
|
* @param overrideReason - Reason supplied with the override
|
|
2106
|
+
* @param context - Task and criterion context used to capture durable proof bindings.
|
|
2107
|
+
* @param gate - Gate associated with the captured result and artifact references.
|
|
1884
2108
|
* @returns Canonical GateEvidence ready to persist
|
|
1885
2109
|
*
|
|
1886
2110
|
* @task T832
|
|
1887
2111
|
*/
|
|
1888
|
-
export function composeGateEvidence(atoms, capturedBy, override, overrideReason) {
|
|
2112
|
+
export function composeGateEvidence(atoms, capturedBy, override, overrideReason, context, gate) {
|
|
1889
2113
|
const result = {
|
|
1890
2114
|
atoms,
|
|
1891
2115
|
capturedAt: new Date().toISOString(),
|
|
@@ -1896,6 +2120,34 @@ export function composeGateEvidence(atoms, capturedBy, override, overrideReason)
|
|
|
1896
2120
|
if (overrideReason)
|
|
1897
2121
|
result.overrideReason = overrideReason;
|
|
1898
2122
|
}
|
|
2123
|
+
if (context && gate) {
|
|
2124
|
+
const linkedIds = new Set(atoms.flatMap((atom) => atom.kind === 'satisfies' && atom.targetTaskId === context.task.id && atom.resolvedAcUuid
|
|
2125
|
+
? [atom.resolvedAcUuid]
|
|
2126
|
+
: []));
|
|
2127
|
+
const artifactPaths = atoms.flatMap((atom) => atom.kind === 'files' ? atom.files.map((file) => file.path) : []);
|
|
2128
|
+
const resultAtomIndices = atoms.flatMap((atom, index) => {
|
|
2129
|
+
if (gate === 'implemented')
|
|
2130
|
+
return atom.kind === 'commit' || atom.kind === 'pr' || atom.kind === 'decision'
|
|
2131
|
+
? [index]
|
|
2132
|
+
: [];
|
|
2133
|
+
return atom.kind === 'tool' || atom.kind === 'test-run' ? [index] : [];
|
|
2134
|
+
});
|
|
2135
|
+
result.scope = {
|
|
2136
|
+
taskId: context.task.id,
|
|
2137
|
+
gate,
|
|
2138
|
+
classification: classifyEvidenceTask(context),
|
|
2139
|
+
criteria: context.criteria
|
|
2140
|
+
.filter((criterion) => linkedIds.has(criterion.id))
|
|
2141
|
+
.map((criterion) => ({
|
|
2142
|
+
criterionId: criterion.id,
|
|
2143
|
+
criterionHash: createHash('sha256').update(criterion.text).digest('hex'),
|
|
2144
|
+
artifactPaths: artifactPaths.length
|
|
2145
|
+
? artifactPaths
|
|
2146
|
+
: (context.task.verification?.evidence?.implemented?.scope?.criteria.find((link) => link.criterionId === criterion.id)?.artifactPaths ?? []),
|
|
2147
|
+
resultAtomIndices,
|
|
2148
|
+
})),
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
1899
2151
|
return result;
|
|
1900
2152
|
}
|
|
1901
2153
|
/**
|
|
@@ -1964,6 +2216,42 @@ export function isHardAtom(atom) {
|
|
|
1964
2216
|
* @adr ADR-051 §5 / §8 (Decision 8)
|
|
1965
2217
|
*/
|
|
1966
2218
|
export async function revalidateEvidence(evidence, projectRoot, gate, taskId, options) {
|
|
2219
|
+
if (taskId && gate && !evidence.override) {
|
|
2220
|
+
const { getTaskAccessor } = await import('../store/data-accessor.js');
|
|
2221
|
+
const accessor = await getTaskAccessor(resolveCanonicalProjectRoot(projectRoot));
|
|
2222
|
+
const task = await accessor.loadSingleTask(taskId);
|
|
2223
|
+
if (!task)
|
|
2224
|
+
throw new CleoError(ExitCode.NOT_FOUND, `Evidence task ${taskId} is missing`);
|
|
2225
|
+
const context = {
|
|
2226
|
+
task,
|
|
2227
|
+
gates: [gate],
|
|
2228
|
+
criteria: await accessor.getAcRows(taskId),
|
|
2229
|
+
};
|
|
2230
|
+
let reason = checkTaskEvidenceContext(context, gate, evidence.atoms);
|
|
2231
|
+
if (!reason &&
|
|
2232
|
+
!(classifyEvidenceTask(context) !== 'code' &&
|
|
2233
|
+
isDecisionOnlyImplementation(task.verification?.evidence?.implemented) &&
|
|
2234
|
+
DECISION_ONLY_INAPPLICABLE_GATES.includes(gate))) {
|
|
2235
|
+
reason = checkGateEvidenceMinimum(gate, evidence.atoms);
|
|
2236
|
+
}
|
|
2237
|
+
if (!reason && evidence.scope) {
|
|
2238
|
+
if (evidence.scope.taskId !== taskId || evidence.scope.gate !== gate)
|
|
2239
|
+
reason = 'Evidence scope does not match the current task and gate.';
|
|
2240
|
+
for (const link of evidence.scope.criteria) {
|
|
2241
|
+
const criterion = context.criteria.find((row) => row.id === link.criterionId);
|
|
2242
|
+
if (!criterion ||
|
|
2243
|
+
createHash('sha256').update(criterion.text).digest('hex') !== link.criterionHash)
|
|
2244
|
+
reason = `Criterion ${link.criterionId} changed after verification; record fresh evidence.`;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (reason)
|
|
2248
|
+
return {
|
|
2249
|
+
stillValid: false,
|
|
2250
|
+
failedAtoms: [
|
|
2251
|
+
{ atom: evidence.atoms[0] ?? { kind: 'note', note: 'missing evidence' }, reason },
|
|
2252
|
+
],
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
1967
2255
|
// T9245: critical-gate override rejection.
|
|
1968
2256
|
// When the gate is `implemented` or `testsPassed`, evidence that has no
|
|
1969
2257
|
// non-override hard atom is rejected outright. We define "override-only"
|
|
@@ -2004,7 +2292,11 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
|
|
|
2004
2292
|
const startedAt = Date.now();
|
|
2005
2293
|
const cached = await revalidateCommitAtom(atom.sha, {
|
|
2006
2294
|
storeRoot: projectRoot,
|
|
2007
|
-
|
|
2295
|
+
// T12308: same hint as validation used, so a multi-repo root cannot
|
|
2296
|
+
// verify green and then re-validate against a different sibling.
|
|
2297
|
+
executionRoot: resolveEvidenceExecutionRoot(projectRoot, undefined, {
|
|
2298
|
+
commitSha: atom.sha,
|
|
2299
|
+
}),
|
|
2008
2300
|
});
|
|
2009
2301
|
if (!cached.check.ok)
|
|
2010
2302
|
failed.push({ atom, reason: cached.check.reason });
|
|
@@ -2022,12 +2314,21 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
|
|
|
2022
2314
|
// checked FIRST (the file may still be absent from the local checkout
|
|
2023
2315
|
// at complete time when local main was never fast-forwarded).
|
|
2024
2316
|
const siblingCommit = evidence.atoms.find((a) => a.kind === 'commit');
|
|
2025
|
-
const
|
|
2317
|
+
const siblingPr = evidence.atoms.find((atom) => atom.kind === 'pr');
|
|
2318
|
+
const siblingCommitSha = siblingPr?.mergeCommitSha ?? siblingCommit?.sha;
|
|
2319
|
+
const executionRoot = resolveEvidenceExecutionRoot(projectRoot, undefined, siblingCommitSha === undefined ? {} : { commitSha: siblingCommitSha });
|
|
2026
2320
|
for (const f of atom.files) {
|
|
2027
|
-
const abs = isAbsolute(f.path) ? f.path : resolvePath(
|
|
2321
|
+
const abs = isAbsolute(f.path) ? f.path : resolvePath(executionRoot, f.path);
|
|
2028
2322
|
let content = null;
|
|
2029
2323
|
if (siblingCommitSha) {
|
|
2030
|
-
content = await gitShowFileContentAtCommit(f.path, siblingCommitSha,
|
|
2324
|
+
content = await gitShowFileContentAtCommit(f.path, siblingCommitSha, executionRoot);
|
|
2325
|
+
}
|
|
2326
|
+
if (siblingPr && content === null) {
|
|
2327
|
+
failed.push({
|
|
2328
|
+
atom,
|
|
2329
|
+
reason: `Cannot inspect ${f.path} at recorded PR merge ${siblingCommitSha}; current checkout bytes cannot substitute.`,
|
|
2330
|
+
});
|
|
2331
|
+
continue;
|
|
2031
2332
|
}
|
|
2032
2333
|
if (!content && existsSync(abs)) {
|
|
2033
2334
|
content = await readFile(abs);
|
|
@@ -2036,7 +2337,7 @@ export async function revalidateEvidence(evidence, projectRoot, gate, taskId, op
|
|
|
2036
2337
|
// T11959: git-show fallback — file may have been on task branch at
|
|
2037
2338
|
// verify time and is now on main after merge, or it may still only
|
|
2038
2339
|
// exist on the branch. Use the same resolution path as validateFiles.
|
|
2039
|
-
content = await gitShowFileContent(f.path, taskId,
|
|
2340
|
+
content = await gitShowFileContent(f.path, taskId, executionRoot);
|
|
2040
2341
|
}
|
|
2041
2342
|
if (!content) {
|
|
2042
2343
|
failed.push({ atom, reason: `File removed since verify: ${f.path}` });
|