@cassiomc1/forgeloop 1.13.0 → 2.0.0-rc.2
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/AGENT_COMPATIBILITY.md +8 -0
- package/CONTRIBUTING.md +11 -7
- package/DOCS_INDEX.md +37 -4
- package/ENG/c-development-eng.md +2 -2
- package/ENG/cpp-development-eng.md +2 -2
- package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
- package/ENG/flutter-development-eng.md +4 -2
- package/ENG/go-development-eng.md +2 -2
- package/ENG/java-development-eng.md +3 -3
- package/ENG/nodejs-backend-development-eng.md +10 -6
- package/ENG/php-development-eng.md +3 -3
- package/ENG/python-development-eng.md +205 -0
- package/ENG/rust-development-eng.md +42 -1
- package/ENG/sec-code-eng.md +7 -7
- package/ENG/sql-development-eng.md +4 -4
- package/ENG/swift-development-eng.md +5 -4
- package/ENG/test-code-eng.md +1 -1
- package/ENG/typescript-development-eng.md +4 -4
- package/EXECUTION_STATE.md +12 -0
- package/GUIDE_ROUTER.md +6 -0
- package/LICENSE +133 -21
- package/LOOP_ENGINEERING.md +28 -2
- package/LOOP_SYSTEM_DESIGN.md +10 -10
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/README.md +63 -59
- package/TERMINOLOGY.md +2 -0
- package/THIRD_PARTY_NOTICES.md +3 -4
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +29 -3
- package/completions/forgeloop.bash +55 -3
- package/completions/forgeloop.fish +183 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +74 -39
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +171 -35
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +512 -8
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +34 -11
- package/docs/GETTING_STARTED.md +3 -3
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +46 -18
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +9 -0
- package/docs/RELEASE_CHECKLIST.md +116 -12
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SELF_HOSTED_TESTS.md +121 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +198 -1
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +774 -16
- package/docs/protocol-requirements.json +24 -0
- package/package.json +43 -11
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/task-bundle.schema.json +7 -2
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +6 -6
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +6 -3
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/scripts/run-mcp-clean-tests.mjs +30 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +316 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +289 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/temporary-paths.js +18 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +218 -174
- package/src/commands/action-show.js +5 -2
- package/src/commands/advance.js +5 -1
- package/src/commands/baseline.js +13 -11
- package/src/commands/checkpoint-revalidate.js +192 -0
- package/src/commands/complete.js +19 -3
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +266 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +51 -13
- package/src/commands/gate-record.js +194 -0
- package/src/commands/gate-revalidate.js +135 -0
- package/src/commands/handoff-list.js +6 -1
- package/src/commands/handoff-show.js +6 -1
- package/src/commands/migrate-protocol.js +2 -2
- package/src/commands/model-route.js +40 -0
- package/src/commands/next.js +7 -1
- package/src/commands/policy-diff.js +25 -4
- package/src/commands/progress.js +17 -11
- package/src/commands/record-diagnosis.js +3 -0
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +41 -0
- package/src/commands/status.js +6 -1
- package/src/commands/storage-backup.js +15 -0
- package/src/commands/storage-migrate.js +5 -0
- package/src/commands/storage-migration-resume.js +5 -0
- package/src/commands/storage-migration-status.js +44 -0
- package/src/commands/storage-restore-resume.js +7 -0
- package/src/commands/storage-restore.js +8 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-list.js +8 -43
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-migrate.js +3 -3
- package/src/commands/task-recover.js +10 -2
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/task-scope.js +18 -2
- package/src/commands/task-show.js +16 -2
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +39 -15
- package/src/commands/validate-receipt.js +8 -1
- package/src/config/guides.json +4 -0
- package/src/core/action-ledger-projection.js +10 -3
- package/src/core/actions.js +38 -75
- package/src/core/activation.js +11 -3
- package/src/core/approvals.js +17 -51
- package/src/core/artifact-registry.js +83 -2
- package/src/core/artifacts.js +81 -27
- package/src/core/attachment-paths.js +45 -0
- package/src/core/attestation-coverage.js +6 -1
- package/src/core/attestation-verifier.js +21 -2
- package/src/core/attestation.js +17 -6
- package/src/core/audit-ux.js +514 -0
- package/src/core/audit.js +6 -1
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/bundles.js +160 -66
- package/src/core/checkpoint-revalidation.js +321 -0
- package/src/core/cli-command-definitions.js +350 -7
- package/src/core/code-manifest.js +9 -3
- package/src/core/command-executors.js +274 -108
- package/src/core/command-input.js +136 -44
- package/src/core/completion-artifacts.js +85 -34
- package/src/core/completion-ownership.js +19 -8
- package/src/core/completion-recovery-rebind.js +11 -1
- package/src/core/completion.js +64 -15
- package/src/core/config.js +3 -0
- package/src/core/conformance.js +2 -1
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/continuity-lint.js +13 -2
- package/src/core/continuity-reconciliation.js +16 -3
- package/src/core/continuity.js +16 -8
- package/src/core/contract-bootstrap-recovery.js +656 -0
- package/src/core/contract-revision.js +209 -0
- package/src/core/decision/artifact.js +70 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +104 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +138 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +181 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +35 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/diagnosis-model.js +11 -0
- package/src/core/diagnosis.js +10 -1
- package/src/core/diagnostic-projection.js +29 -12
- package/src/core/diagnostic-record.js +28 -3
- package/src/core/efficiency.js +6 -1
- package/src/core/error-codes.js +278 -0
- package/src/core/events.js +535 -147
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +36 -13
- package/src/core/execution-profile.js +63 -38
- package/src/core/execution.js +26 -40
- package/src/core/filesystem.js +11 -0
- package/src/core/gate-artifact.js +105 -19
- package/src/core/gate-provenance.js +125 -0
- package/src/core/handoff.js +26 -14
- package/src/core/history.js +39 -35
- package/src/core/hypothesis-projection.js +32 -0
- package/src/core/information-gain-projection.js +104 -100
- package/src/core/inspect.js +12 -3
- package/src/core/integration-invocation-policy.js +37 -4
- package/src/core/integration-resources.js +119 -87
- package/src/core/ledger-event-collection.js +189 -0
- package/src/core/ledger-relations.js +23 -0
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/native-storage.js +12 -0
- package/src/core/next-action-continuity.js +31 -9
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +131 -43
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +9 -4
- package/src/core/next-action-verification-phase.js +2 -1
- package/src/core/next-action.js +56 -9
- package/src/core/phase.js +266 -103
- package/src/core/policy-engine.js +12 -13
- package/src/core/preflight-consistency.js +89 -62
- package/src/core/preflight-loaders.js +48 -9
- package/src/core/preflight-model.js +6 -1
- package/src/core/preflight.js +29 -12
- package/src/core/progress.js +21 -10
- package/src/core/protocol-info.js +68 -0
- package/src/core/protocol-migration.js +7 -6
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +281 -67
- package/src/core/recovery-history.js +67 -47
- package/src/core/reflection.js +72 -77
- package/src/core/responsibility.js +25 -13
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +28 -4
- package/src/core/router.js +67 -1
- package/src/core/runtime-context.js +123 -61
- package/src/core/schema-validation.js +11 -3
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/settlement.js +12 -1
- package/src/core/signing/bundle-file.js +53 -0
- package/src/core/signing/bundle-reference.js +15 -0
- package/src/core/signing/sigstore.js +60 -33
- package/src/core/signing/statement-file.js +27 -0
- package/src/core/sources.js +37 -0
- package/src/core/storage-runtime-registry.js +41 -0
- package/src/core/structural-quality/artifacts.js +20 -14
- package/src/core/structural-quality/service.js +81 -56
- package/src/core/target-layout.js +1 -1
- package/src/core/task-claim-state.js +249 -15
- package/src/core/task-command.js +21 -1
- package/src/core/task-conflict-inspection.js +133 -102
- package/src/core/task-descriptor.js +29 -22
- package/src/core/task-discovery.js +288 -90
- package/src/core/task-lock.js +66 -306
- package/src/core/task-migration-validation.js +5 -5
- package/src/core/task-migration.js +17 -287
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/task-snapshot.js +51 -8
- package/src/core/task-storage-migration.js +18 -0
- package/src/core/templates.js +23 -8
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +94 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +164 -105
- package/src/core/trajectory-evaluation.js +15 -3
- package/src/core/trajectory-metrics.js +47 -32
- package/src/core/transaction.js +16 -287
- package/src/core/verification-scope.js +20 -12
- package/src/core/work-state.js +100 -59
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
- package/src/repository-index/binary-manager.js +7 -5
- package/src/storage/artifact-bytes.js +22 -0
- package/src/storage/attachment-binding.js +11 -0
- package/src/storage/attachment-files.js +82 -0
- package/src/storage/attachment-references.js +36 -0
- package/src/storage/backup.js +53 -0
- package/src/storage/capabilities.js +31 -0
- package/src/storage/connection-owner.js +119 -0
- package/src/storage/connection.js +376 -0
- package/src/storage/existing-project-scope.js +45 -0
- package/src/storage/export-destination.js +44 -0
- package/src/storage/exporter.js +284 -0
- package/src/storage/file-durability.js +46 -0
- package/src/storage/importer.js +461 -0
- package/src/storage/index.js +74 -0
- package/src/storage/leases.js +63 -0
- package/src/storage/ledger-event-snapshot.js +150 -0
- package/src/storage/ledger-relations.js +220 -0
- package/src/storage/legacy-maintenance-boundary.js +18 -0
- package/src/storage/legacy-signature.js +115 -0
- package/src/storage/maintenance-handoff.js +129 -0
- package/src/storage/maintenance-owner.js +42 -0
- package/src/storage/maintenance.js +310 -0
- package/src/storage/metadata-json.js +35 -0
- package/src/storage/migration-activate.js +59 -0
- package/src/storage/migration-archive.js +57 -0
- package/src/storage/migration-attachment-root.js +61 -0
- package/src/storage/migration-candidate.js +398 -0
- package/src/storage/migration-publication.js +143 -0
- package/src/storage/migration-rollback-admission.js +54 -0
- package/src/storage/migration-rollback-preparation.js +132 -0
- package/src/storage/migration-rollback-publication.js +183 -0
- package/src/storage/migration-rollback-release.js +66 -0
- package/src/storage/migration-rollback-source-stage.js +103 -0
- package/src/storage/migration-rollback.js +43 -0
- package/src/storage/migration-source-partition.js +56 -0
- package/src/storage/migration-source.js +255 -0
- package/src/storage/migration-terminal.js +39 -0
- package/src/storage/migration-validation.js +75 -0
- package/src/storage/migration.js +43 -0
- package/src/storage/operational-attachments.js +49 -0
- package/src/storage/operational-context.js +50 -0
- package/src/storage/project-backup.js +98 -0
- package/src/storage/project-bootstrap.js +24 -0
- package/src/storage/project-boundary.js +79 -0
- package/src/storage/project-read-snapshot.js +18 -0
- package/src/storage/project-replacement.js +137 -0
- package/src/storage/project-restore.js +203 -0
- package/src/storage/record-definitions.js +45 -0
- package/src/storage/repository.js +507 -0
- package/src/storage/restore-journal.js +62 -0
- package/src/storage/restore-layout.js +30 -0
- package/src/storage/restore-preparation.js +84 -0
- package/src/storage/restore-replacement-archive.js +162 -0
- package/src/storage/restore-replacement-owner.js +31 -0
- package/src/storage/restore-replacement-preparation.js +128 -0
- package/src/storage/restore-replacement.js +142 -0
- package/src/storage/restore-terminal.js +32 -0
- package/src/storage/runtime.js +31 -0
- package/src/storage/schema.js +214 -0
- package/src/storage/singleton-import.js +84 -0
- package/src/storage/snapshot-continuity.js +16 -0
- package/src/storage/snapshot.js +36 -0
- package/src/storage/storage-marker.js +79 -0
- package/src/storage/task-guards.js +107 -0
- package/src/storage/topology.js +42 -0
- package/src/storage/transaction.js +179 -0
- package/src/storage/unit-of-work.js +663 -0
- package/src/storage/windows-process-incarnation.js +206 -0
- package/.forgeloop/forgeloop.gitignore +0 -4
- package/LICENSE-DOCS.md +0 -13
- package/src/core/transaction-maintenance.js +0 -70
|
@@ -19,10 +19,17 @@ function validatePolicyInput(command, options) {
|
|
|
19
19
|
if (command !== "policy" && options.policy) throw inputError(`Policy name is not valid for ${command}`);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function
|
|
22
|
+
function validateContractBootstrapRepairMigrationInput(command, options) {
|
|
23
|
+
if (command !== "task-migrate-contract-bootstrap-repair") return;
|
|
24
|
+
if (!options.taskId) throw inputError("task-migrate-contract-bootstrap-repair requires --task");
|
|
25
|
+
if (!options.acknowledgeMigration) throw inputError("task-migrate-contract-bootstrap-repair requires --acknowledge-migration");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function validateTaskCreationInput(command, options, help = false) {
|
|
29
|
+
if (help) return;
|
|
23
30
|
if (command === "bundle" && !options.taskId) throw inputError("bundle requires --task");
|
|
24
31
|
if (command === "task-create" && !options.taskId) throw inputError("task-create requires --task");
|
|
25
|
-
if (
|
|
32
|
+
if (!["task-create", "contract-create", "contract-revise"].includes(command) && (options.preset || options.preview)) {
|
|
26
33
|
throw inputError(`preset/preview options are not valid for ${command}`);
|
|
27
34
|
}
|
|
28
35
|
if (command === "task-create" && options.preview && !options.preset && !options.contractFile) {
|
|
@@ -31,6 +38,15 @@ function validateTaskCreationInput(command, options) {
|
|
|
31
38
|
if (command === "task-create" && options.preset && options.contractFile) {
|
|
32
39
|
throw inputError("task-create accepts either --preset or --contract-file, not both");
|
|
33
40
|
}
|
|
41
|
+
if (command === "contract-create" && !options.taskId) throw inputError("contract-create requires --task");
|
|
42
|
+
if (command === "contract-create" && !options.preset && !options.contractFile) throw inputError("contract-create requires --preset or --contract-file");
|
|
43
|
+
if (command === "contract-create" && options.preset && options.contractFile) throw inputError("contract-create accepts either --preset or --contract-file, not both");
|
|
44
|
+
if (command === "contract-revise" && !options.taskId) throw inputError("contract-revise requires --task");
|
|
45
|
+
if (command === "contract-revise" && !options.preset && !options.contractFile) throw inputError("contract-revise requires --preset or --contract-file");
|
|
46
|
+
if (command === "contract-revise" && options.preset && options.contractFile) throw inputError("contract-revise accepts either --preset or --contract-file, not both");
|
|
47
|
+
if (command === "task-repair-contract-bootstrap" && !options.taskId) throw inputError("task-repair-contract-bootstrap requires --task");
|
|
48
|
+
if (command === "task-repair-contract-bootstrap" && !options.acknowledgeRepair) throw inputError("task-repair-contract-bootstrap requires --acknowledge-repair");
|
|
49
|
+
validateContractBootstrapRepairMigrationInput(command, options);
|
|
34
50
|
const hasTaskListFilter = Boolean(
|
|
35
51
|
options.phase
|
|
36
52
|
|| options.active
|
|
@@ -42,6 +58,15 @@ function validateTaskCreationInput(command, options) {
|
|
|
42
58
|
}
|
|
43
59
|
}
|
|
44
60
|
|
|
61
|
+
function validateGateRecordInput(command, options, help) {
|
|
62
|
+
if (command !== "gate-record" || help) return;
|
|
63
|
+
if (!options.taskId) throw inputError("gate-record requires --task");
|
|
64
|
+
if (!options.gate) throw inputError("gate-record requires --gate");
|
|
65
|
+
if (!["satisfied", "unverified", "blocked"].includes(options.gateStatus)) {
|
|
66
|
+
throw inputError("gate-record --status must be satisfied, unverified, or blocked");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
45
70
|
function validateExecutionProfileInput(command, options) {
|
|
46
71
|
if (options.executionProfile !== null && options.executionProfile !== undefined) {
|
|
47
72
|
if (command !== "route") throw inputError(`executionProfile is not valid for ${command}`);
|
|
@@ -91,54 +116,91 @@ function validateOutputInput(command, options) {
|
|
|
91
116
|
if (command !== "next" && options.explain === true) throw inputError(`explain output is not valid for ${command}`);
|
|
92
117
|
}
|
|
93
118
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
const TASK_SELECTOR_COMMANDS = new Set([
|
|
120
|
+
"context-plan",
|
|
121
|
+
"model-route",
|
|
122
|
+
"semantic-plan",
|
|
123
|
+
"test-utility",
|
|
124
|
+
"checkpoint-revalidate",
|
|
125
|
+
"workspace-bind",
|
|
126
|
+
"workspace-status",
|
|
127
|
+
"handoff-create",
|
|
128
|
+
"handoff-list",
|
|
129
|
+
"handoff-show",
|
|
130
|
+
"handoff-accept",
|
|
131
|
+
"responsibility-set",
|
|
132
|
+
"responsibility-status",
|
|
133
|
+
"verify-scope",
|
|
134
|
+
"attestation-create",
|
|
135
|
+
"attestation-status",
|
|
136
|
+
"attestation-verify",
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
function validateRequiredTaskSelector(command, options, help) {
|
|
140
|
+
if (TASK_SELECTOR_COMMANDS.has(command) && !help && !options.taskId) {
|
|
141
|
+
throw inputError(`${command} requires --task`);
|
|
101
142
|
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validateHandoffShowInput(command, options, help) {
|
|
146
|
+
if (command === "handoff-show" && !help && !options.handoffId) throw inputError("handoff-show requires --id");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function validateHandoffAcceptInput(command, options, help) {
|
|
150
|
+
if (command !== "handoff-accept" || help) return;
|
|
151
|
+
if (!options.handoffId) throw inputError("handoff-accept requires --handoff");
|
|
152
|
+
if (!options.consumerId) throw inputError("handoff-accept requires --consumer-id");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateResponsibilityInput(command, options, help) {
|
|
102
156
|
if (command === "responsibility-set" && !help && !options.responsibilityLabel) {
|
|
103
157
|
throw inputError("responsibility-set requires --label");
|
|
104
158
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function validateVerificationScopeInput(command, options, help) {
|
|
162
|
+
if (command !== "verify-scope" || help) return;
|
|
163
|
+
const mode = String(options.verificationScopeMode ?? "AUTO").toUpperCase();
|
|
164
|
+
if (!["AUTO", "CHANGED", "CLAIMED", "FULL"].includes(mode)) throw inputError("verify-scope --mode must be AUTO, CHANGED, CLAIMED, or FULL");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function validateAttestationRangeInput(command, options, help) {
|
|
168
|
+
if (command !== "attestation-verify-range" || help) return;
|
|
169
|
+
if (!options.baseRevision) throw inputError("attestation-verify-range requires --base");
|
|
170
|
+
if (!options.headRevision) throw inputError("attestation-verify-range requires --head");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function validateTaskSelectorsInput(command, options, help) {
|
|
174
|
+
validateRequiredTaskSelector(command, options, help);
|
|
175
|
+
validateHandoffShowInput(command, options, help);
|
|
176
|
+
validateHandoffAcceptInput(command, options, help);
|
|
177
|
+
validateResponsibilityInput(command, options, help);
|
|
178
|
+
validateVerificationScopeInput(command, options, help);
|
|
179
|
+
validateAttestationRangeInput(command, options, help);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function validateRecordCheckInput(options) {
|
|
183
|
+
if (!options.checkId) throw inputError("record-check requires --id");
|
|
184
|
+
if (!options.checkRequirement) throw inputError("record-check requires --requirement");
|
|
185
|
+
if (!options.checkStatus) throw inputError("record-check requires --status");
|
|
186
|
+
if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
|
|
187
|
+
if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function validateCommandCheckInput(options, command) {
|
|
191
|
+
if (!options.checkId) throw inputError(`${command} requires --id`);
|
|
192
|
+
if (!options.checkRequirement) throw inputError(`${command} requires --requirement`);
|
|
193
|
+
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
194
|
+
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
195
|
+
throw inputError(`${command} accepts only --id, --requirement, --details,${command === "run-check" ? " --timeout-ms, --scope-ref," : ""} and -- <argv>`);
|
|
112
196
|
}
|
|
197
|
+
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError(`${command} requires -- followed by an exact command argv`);
|
|
113
198
|
}
|
|
114
199
|
|
|
115
200
|
function validateCheckInput(command, options, help) {
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!options.checkStatus) throw inputError("record-check requires --status");
|
|
120
|
-
if (!options.checkEvidenceKind) throw inputError("record-check requires --evidence-kind");
|
|
121
|
-
if (!options.checkCommand && !options.checkResult) throw inputError("record-check requires --command or --result");
|
|
122
|
-
}
|
|
123
|
-
if (command === "run-check" && !help) {
|
|
124
|
-
if (!options.checkId) throw inputError("run-check requires --id");
|
|
125
|
-
if (!options.checkRequirement) throw inputError("run-check requires --requirement");
|
|
126
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
127
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
128
|
-
throw inputError("run-check accepts only --id, --requirement, --details, --timeout-ms, --scope-ref, and -- <argv>");
|
|
129
|
-
}
|
|
130
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("run-check requires -- followed by an exact command argv");
|
|
131
|
-
}
|
|
132
|
-
if (command === "reconcile-closure" && !help) {
|
|
133
|
-
if (!options.taskId) throw inputError("reconcile-closure requires --task");
|
|
134
|
-
if (!options.checkId) throw inputError("reconcile-closure requires --id");
|
|
135
|
-
if (!options.checkRequirement) throw inputError("reconcile-closure requires --requirement");
|
|
136
|
-
if (options.checkKind || options.checkStatus || options.checkEvidenceKind || options.checkCommand
|
|
137
|
-
|| options.checkResult || options.checkExitCode !== null || options.checkExecutionRef || options.checkProvenance) {
|
|
138
|
-
throw inputError("reconcile-closure accepts only --id, --requirement, --details, and -- <argv>");
|
|
139
|
-
}
|
|
140
|
-
if (!Array.isArray(options.commandArgv) || options.commandArgv.length === 0) throw inputError("reconcile-closure requires -- followed by an exact command argv");
|
|
141
|
-
}
|
|
201
|
+
if (help) return;
|
|
202
|
+
if (command === "record-check") validateRecordCheckInput(options);
|
|
203
|
+
if (command === "run-check" || command === "reconcile-closure") validateCommandCheckInput(options, command);
|
|
142
204
|
}
|
|
143
205
|
|
|
144
206
|
/**
|
|
@@ -172,6 +234,8 @@ export function defaultCommandInputValues() {
|
|
|
172
234
|
assetPath: null,
|
|
173
235
|
binaryPath: null,
|
|
174
236
|
force: false,
|
|
237
|
+
acknowledgeRepair: false,
|
|
238
|
+
acknowledgeMigration: false,
|
|
175
239
|
workType: null,
|
|
176
240
|
surfaces: [],
|
|
177
241
|
risks: [],
|
|
@@ -193,6 +257,13 @@ export function defaultCommandInputValues() {
|
|
|
193
257
|
file: null,
|
|
194
258
|
contractFile: null,
|
|
195
259
|
preset: null,
|
|
260
|
+
gate: null,
|
|
261
|
+
gateStatus: null,
|
|
262
|
+
gateArtifacts: [],
|
|
263
|
+
gateDecisions: [],
|
|
264
|
+
gateUnknowns: [],
|
|
265
|
+
gateAssumptions: [],
|
|
266
|
+
gateEvidenceFile: null,
|
|
196
267
|
preview: false,
|
|
197
268
|
phase: null,
|
|
198
269
|
active: false,
|
|
@@ -253,6 +324,25 @@ export function defaultCommandInputValues() {
|
|
|
253
324
|
};
|
|
254
325
|
}
|
|
255
326
|
|
|
327
|
+
function validateStorageRestoreInput(command, options, help) {
|
|
328
|
+
if (command === "storage-restore-resume" && !help && (options.writersQuiesced !== true || typeof options.operationId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.operationId) || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-restore-resume requires exact --operation and --expected-owner UUIDs and explicit --writers-quiesced");
|
|
329
|
+
if (["storage-restore", "storage-restore-resume"].includes(command) && options.replaceActive !== undefined && typeof options.replaceActive !== "boolean") throw inputError("Active replacement selection must be a boolean");
|
|
330
|
+
if (command === "storage-restore" && !help && (typeof options.source !== "string" || !options.source || options.writersQuiesced !== true)) throw inputError("storage-restore requires --source and explicit --writers-quiesced");
|
|
331
|
+
if (command === "storage-backup" && !help && (typeof options.destination !== "string" || !options.destination)) throw inputError("storage-backup requires --destination");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function validateStorageMigrationInput(command, options, help) {
|
|
335
|
+
if (command === "storage-rollback-resume" && !help && !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId ?? "")) throw inputError("storage-rollback-resume requires exact --expected-owner UUID");
|
|
336
|
+
if (["storage-rollback", "storage-rollback-resume"].includes(command) && !help && (typeof options.destination !== "string" || !options.destination || typeof options.legacyRoot !== "string" || !options.legacyRoot || options.writersQuiesced !== true || options.nativeWritesExcluded !== true)) throw inputError("storage-rollback requires --destination, --legacy-root, explicit --writers-quiesced and --native-writes-excluded");
|
|
337
|
+
if (command === "storage-migrate" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true)) throw inputError("storage-migrate requires --destination and explicit --writers-quiesced");
|
|
338
|
+
if (command === "storage-migration-resume" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-migration-resume requires --destination, exact --expected-owner UUID and explicit --writers-quiesced");
|
|
339
|
+
validateUnboundHandoffAcknowledgement(command, options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function validateUnboundHandoffAcknowledgement(command, options) {
|
|
343
|
+
if (command === "storage-migration-resume" && options.acknowledgeUnboundHandoff !== undefined && typeof options.acknowledgeUnboundHandoff !== "boolean") throw inputError("--reconcile-unbound-handoff must be a boolean acknowledgement");
|
|
344
|
+
}
|
|
345
|
+
|
|
256
346
|
/**
|
|
257
347
|
* Canonical semantic validation shared by the CLI argv parser and
|
|
258
348
|
* programmatic integrations. JSON-Schema style checks cannot express these
|
|
@@ -261,9 +351,11 @@ export function defaultCommandInputValues() {
|
|
|
261
351
|
export function validateForgeLoopCommandInput({ command, input, help = false } = {}) {
|
|
262
352
|
const options = input ?? {};
|
|
263
353
|
if (!command) return;
|
|
264
|
-
|
|
354
|
+
validateStorageRestoreInput(command, options, help);
|
|
355
|
+
validateStorageMigrationInput(command, options, help);
|
|
265
356
|
validatePolicyInput(command, options);
|
|
266
|
-
validateTaskCreationInput(command, options);
|
|
357
|
+
validateTaskCreationInput(command, options, help);
|
|
358
|
+
validateGateRecordInput(command, options, help);
|
|
267
359
|
validateSearchCommandInput(command, options, help);
|
|
268
360
|
validateProfileAndUsageInput(command, options, help);
|
|
269
361
|
validateOutputInput(command, options);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
|
|
2
3
|
import {
|
|
3
4
|
ARTIFACT_PATHS,
|
|
4
5
|
canonicalFingerprint,
|
|
@@ -18,7 +19,7 @@ import { readPersistedRoute } from "./route-artifact.js";
|
|
|
18
19
|
import { createReceipt, validateReceipt } from "./receipt.js";
|
|
19
20
|
import { readWorkState, mutateWorkState } from "./work-state.js";
|
|
20
21
|
import { assertExecutionPrerequisites, hasExecutionStarted } from "./execution-prerequisites.js";
|
|
21
|
-
import { normalizeRequirements } from "./evidence-readiness.js";
|
|
22
|
+
import { normalizeRequirements, terminalRequirementsForContract } from "./evidence-readiness.js";
|
|
22
23
|
import { classifyCommandResolution, validateVerificationAuthority } from "./verification-capability.js";
|
|
23
24
|
import { readExecutionArtifact, validateExecutionBinding } from "./execution.js";
|
|
24
25
|
import { taskArtifactPath, taskExecutionPath } from "./task-paths.js";
|
|
@@ -80,10 +81,18 @@ function requiredString(value, label) {
|
|
|
80
81
|
return value;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
async function readBoundReceiptArtifact(target, receiptRel, packageRoot) {
|
|
85
|
+
try { return await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot); }
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error.code === "E_STORAGE_PAYLOAD_MISMATCH" && error.taskIdentityMismatch) throw artifactError("E_RECEIPT_TASK_MISMATCH", "Execution receipt does not belong to the current contract task", [receiptRel]);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
async function readCurrentReceipt(target, packageRoot, options = {}) {
|
|
84
93
|
const receiptRel = options.receiptPath ?? (options.taskId ? taskArtifactPath(options.taskId, "receipt") : ARTIFACT_PATHS.receipt);
|
|
85
94
|
try {
|
|
86
|
-
return await
|
|
95
|
+
return await readBoundReceiptArtifact(target, receiptRel, packageRoot);
|
|
87
96
|
} catch (error) {
|
|
88
97
|
if (error.code === "ARTIFACT_MISSING") {
|
|
89
98
|
throw artifactError(
|
|
@@ -245,7 +254,21 @@ export async function requiredEvidenceForTarget({
|
|
|
245
254
|
])].sort();
|
|
246
255
|
}
|
|
247
256
|
|
|
248
|
-
|
|
257
|
+
async function resolveCompletionTaskOptions(options) {
|
|
258
|
+
if (options.taskId) return options;
|
|
259
|
+
const state = await readWorkState(options.target, { packageRoot: options.packageRoot, statePath: options.statePath });
|
|
260
|
+
return { ...options, taskId: state?.taskId };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function prepareCompletion(options = {}) {
|
|
264
|
+
options = await resolveCompletionTaskOptions(options);
|
|
265
|
+
if (!options.taskId) return prepareSelectedCompletion(options);
|
|
266
|
+
return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
|
|
267
|
+
taskId: options.taskId, operation: "prepare-completion", recordCommitEvent: true },
|
|
268
|
+
() => prepareSelectedCompletion(options));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function prepareSelectedCompletion({
|
|
249
272
|
target,
|
|
250
273
|
packageRoot,
|
|
251
274
|
authorityContext,
|
|
@@ -255,6 +278,7 @@ export async function prepareCompletion({
|
|
|
255
278
|
routePath = null,
|
|
256
279
|
statePath = null,
|
|
257
280
|
receiptPath = null,
|
|
281
|
+
eventsPath,
|
|
258
282
|
}) {
|
|
259
283
|
const contract = await readContract(target, packageRoot, { taskId, contractPath });
|
|
260
284
|
const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
|
|
@@ -266,12 +290,12 @@ export async function prepareCompletion({
|
|
|
266
290
|
throw artifactError("E_STATE_MISSING", "Work state is required before preparing completion", [stateRel]);
|
|
267
291
|
}
|
|
268
292
|
if (hasExecutionStarted(state.phase)) {
|
|
269
|
-
await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
|
|
293
|
+
await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
|
|
270
294
|
}
|
|
271
295
|
|
|
272
296
|
let existing = null;
|
|
273
297
|
try {
|
|
274
|
-
existing = await
|
|
298
|
+
existing = await readBoundReceiptArtifact(target, receiptRel, packageRoot);
|
|
275
299
|
await validateReceipt(existing.value, packageRoot, {
|
|
276
300
|
target,
|
|
277
301
|
taskId: contract.value.taskId,
|
|
@@ -463,7 +487,7 @@ export async function assertRecordCheckPrerequisites({
|
|
|
463
487
|
[stateRel],
|
|
464
488
|
);
|
|
465
489
|
}
|
|
466
|
-
await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
|
|
490
|
+
await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
|
|
467
491
|
|
|
468
492
|
const contract = await readContract(target, packageRoot, { taskId, contractPath });
|
|
469
493
|
const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
|
|
@@ -478,10 +502,15 @@ export async function assertRecordCheckPrerequisites({
|
|
|
478
502
|
const requested = normalizeRequirements(requiredEvidence).find((item) => (
|
|
479
503
|
item.id === requirement || item.text === requirement
|
|
480
504
|
));
|
|
481
|
-
|
|
505
|
+
const terminalRequested = terminalRequirementsForContract(contract.value, {
|
|
506
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
507
|
+
}).find((item) => (
|
|
508
|
+
item.id === requirement || item.text === requirement
|
|
509
|
+
));
|
|
510
|
+
if ((requested?.terminalOwned || terminalRequested) && status === "passed" && evidenceKind === "OBSERVED") {
|
|
482
511
|
throw artifactError(
|
|
483
512
|
"E_FUTURE_LIFECYCLE_EVIDENCE",
|
|
484
|
-
`Terminal-owned requirement cannot be recorded before its authoritative result: ${requested.text}`,
|
|
513
|
+
`Terminal-owned requirement cannot be recorded before its authoritative result: ${(requested ?? terminalRequested).text}`,
|
|
485
514
|
[stateRel, eventsRel],
|
|
486
515
|
);
|
|
487
516
|
}
|
|
@@ -516,29 +545,15 @@ export async function assertRecordCheckPrerequisites({
|
|
|
516
545
|
};
|
|
517
546
|
}
|
|
518
547
|
|
|
519
|
-
export async function recordCheck({
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
result,
|
|
529
|
-
exitCode,
|
|
530
|
-
details,
|
|
531
|
-
executionRef,
|
|
532
|
-
provenance,
|
|
533
|
-
authorityContext,
|
|
534
|
-
runtimeContext,
|
|
535
|
-
taskId = null,
|
|
536
|
-
contractPath = null,
|
|
537
|
-
routePath = null,
|
|
538
|
-
statePath = null,
|
|
539
|
-
receiptPath = null,
|
|
540
|
-
eventsPath = null,
|
|
541
|
-
}) {
|
|
548
|
+
export async function recordCheck(options) {
|
|
549
|
+
validateRecordCheckInput(options);
|
|
550
|
+
options = await resolveCompletionTaskOptions(options);
|
|
551
|
+
if (!options.taskId) return recordValidatedCheck(options);
|
|
552
|
+
return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
|
|
553
|
+
taskId: options.taskId, operation: "record-check", recordCommitEvent: true }, () => recordValidatedCheck(options));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function validateRecordCheckInput({ id, kind = "command", requirement, status, evidenceKind, command, result, details, executionRef, provenance }) {
|
|
542
557
|
requiredString(id, "check id");
|
|
543
558
|
requiredString(kind, "check kind");
|
|
544
559
|
requiredString(requirement, "check requirement");
|
|
@@ -563,7 +578,31 @@ export async function recordCheck({
|
|
|
563
578
|
&& (typeof result !== "string" || result.trim() === "")) {
|
|
564
579
|
throw artifactError("E_CHECK_INVALID", "record-check requires --command or --result");
|
|
565
580
|
}
|
|
581
|
+
}
|
|
566
582
|
|
|
583
|
+
async function recordValidatedCheck({
|
|
584
|
+
target,
|
|
585
|
+
packageRoot,
|
|
586
|
+
id,
|
|
587
|
+
kind = "command",
|
|
588
|
+
requirement,
|
|
589
|
+
status,
|
|
590
|
+
evidenceKind,
|
|
591
|
+
command,
|
|
592
|
+
result,
|
|
593
|
+
exitCode,
|
|
594
|
+
details,
|
|
595
|
+
executionRef,
|
|
596
|
+
provenance,
|
|
597
|
+
authorityContext,
|
|
598
|
+
runtimeContext,
|
|
599
|
+
taskId = null,
|
|
600
|
+
contractPath = null,
|
|
601
|
+
routePath = null,
|
|
602
|
+
statePath = null,
|
|
603
|
+
receiptPath = null,
|
|
604
|
+
eventsPath = null,
|
|
605
|
+
}) {
|
|
567
606
|
const context = await assertRecordCheckPrerequisites({
|
|
568
607
|
target,
|
|
569
608
|
packageRoot,
|
|
@@ -775,7 +814,15 @@ export async function recordCheck({
|
|
|
775
814
|
};
|
|
776
815
|
}
|
|
777
816
|
|
|
778
|
-
export async function recordTerminalResult({
|
|
817
|
+
export async function recordTerminalResult(options = {}) {
|
|
818
|
+
if (!options.target) return recordTerminalResultInTransaction(options);
|
|
819
|
+
if (await getTaskTransaction(options.target)) return recordTerminalResultInTransaction(options);
|
|
820
|
+
const state = await readWorkState(options.target, { packageRoot: options.packageRoot, taskId: options.taskId, statePath: options.statePath });
|
|
821
|
+
if (!state) return recordTerminalResultInTransaction(options);
|
|
822
|
+
return withTaskTransaction({ target: options.target, taskId: state.taskId, packageRoot: options.packageRoot, operation: "record-terminal-result" }, () => recordTerminalResultInTransaction({ ...options, taskId: options.taskId ?? state.taskId }));
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
async function recordTerminalResultInTransaction({
|
|
779
826
|
target,
|
|
780
827
|
packageRoot,
|
|
781
828
|
requirement,
|
|
@@ -827,8 +874,12 @@ export async function recordTerminalResult({
|
|
|
827
874
|
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
828
875
|
});
|
|
829
876
|
|
|
830
|
-
const
|
|
831
|
-
|
|
877
|
+
const terminalRequirements = terminalRequirementsForContract(contract.value, {
|
|
878
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
879
|
+
});
|
|
880
|
+
const requested = terminalRequirements.find((item) => (
|
|
881
|
+
item.id === requirement || item.text === requirement
|
|
882
|
+
)) ?? normalizeRequirements(requiredEvidence).find((item) => (
|
|
832
883
|
item.id === requirement || item.text === requirement
|
|
833
884
|
));
|
|
834
885
|
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import { isLedgerEventCollection } from "./ledger-event-collection.js";
|
|
1
2
|
import { LIFECYCLE_MILESTONES, validateStateLedgerCoherence } from "./events.js";
|
|
2
3
|
|
|
3
4
|
export const CANONICAL_COMPLETION_EVENT = "COMPLETION_VALIDATED";
|
|
4
5
|
|
|
6
|
+
function completionCandidates(events, taskId) {
|
|
7
|
+
let count = 0;
|
|
8
|
+
let candidate = null;
|
|
9
|
+
for (const event of events) {
|
|
10
|
+
if (event.event !== CANONICAL_COMPLETION_EVENT || event.taskId !== taskId) continue;
|
|
11
|
+
count += 1;
|
|
12
|
+
candidate = event;
|
|
13
|
+
}
|
|
14
|
+
return { count, candidate };
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
/**
|
|
6
18
|
* Canonical completion ownership proof: the minimal, validator-backed evidence
|
|
7
19
|
* that the lifecycle itself officially reached COMPLETE. This is intentionally
|
|
@@ -28,31 +40,30 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
|
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
let completionEvent = null;
|
|
31
|
-
if (ledger &&
|
|
43
|
+
if (ledger && isLedgerEventCollection(ledger.events)) {
|
|
32
44
|
if (ledger.events.some((event) => event.taskId !== taskId)) {
|
|
33
45
|
errors.push({
|
|
34
46
|
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
35
47
|
message: "Ledger contains an event belonging to a different task",
|
|
36
48
|
});
|
|
37
49
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
if (candidates.length === 0) {
|
|
50
|
+
const { count, candidate } = completionCandidates(ledger.events, taskId);
|
|
51
|
+
if (count === 0) {
|
|
41
52
|
errors.push({
|
|
42
53
|
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
43
54
|
message: `No canonical ${CANONICAL_COMPLETION_EVENT} event exists for this task`,
|
|
44
55
|
});
|
|
45
|
-
} else if (
|
|
56
|
+
} else if (count > 1) {
|
|
46
57
|
errors.push({
|
|
47
58
|
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
48
59
|
message: `Multiple ${CANONICAL_COMPLETION_EVENT} events exist; completion is ambiguous`,
|
|
49
60
|
});
|
|
50
61
|
} else {
|
|
51
|
-
completionEvent =
|
|
62
|
+
completionEvent = candidate;
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
if (state && ledger &&
|
|
66
|
+
if (state && ledger && isLedgerEventCollection(ledger.events)) {
|
|
56
67
|
const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
|
|
57
68
|
if (coherenceErrors.length > 0) {
|
|
58
69
|
for (const error of coherenceErrors) {
|
|
@@ -67,7 +78,7 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
|
|
|
67
78
|
// No contradictory lifecycle activity may follow the canonical completion:
|
|
68
79
|
// any milestone at or after VERIFICATION_RECORDED occurring after the
|
|
69
80
|
// completion event means the lifecycle moved past terminal state.
|
|
70
|
-
if (completionEvent && ledger &&
|
|
81
|
+
if (completionEvent && ledger && isLedgerEventCollection(ledger.events)) {
|
|
71
82
|
const completionIndex = ledger.events.indexOf(completionEvent);
|
|
72
83
|
const contradiction = ledger.events.slice(completionIndex + 1).find((event) => {
|
|
73
84
|
const index = LIFECYCLE_MILESTONES.indexOf(event.event);
|
|
@@ -3,6 +3,7 @@ import { appendProtocolEvent, validateCompletionRecoveryAuthorization, validateE
|
|
|
3
3
|
import { createReceipt } from "./receipt.js";
|
|
4
4
|
import { taskArtifactPath } from "./task-paths.js";
|
|
5
5
|
import { mutateWorkState, readWorkState } from "./work-state.js";
|
|
6
|
+
import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
|
|
6
7
|
|
|
7
8
|
function resolveArtifactPath(key, taskId, override) {
|
|
8
9
|
if (override) return override;
|
|
@@ -65,7 +66,16 @@ function findMatchingRejectionEvent(events, attempt, cycle) {
|
|
|
65
66
|
* rejection carrying the current fingerprints is appended,
|
|
66
67
|
* - the execution receipt is re-bound to the current checkpoint when present.
|
|
67
68
|
*/
|
|
68
|
-
export async function rebindCompletionRejectionSnapshot({
|
|
69
|
+
export async function rebindCompletionRejectionSnapshot(options = {}) {
|
|
70
|
+
if (await getTaskTransaction(options.target)) return rebindRejectionInTransaction(options);
|
|
71
|
+
const state = await readWorkState(options.target, { packageRoot: options.packageRoot,
|
|
72
|
+
taskId: options.taskId, statePath: resolveArtifactPath("state", options.taskId, options.statePath) });
|
|
73
|
+
if (!state) return { rebound: false };
|
|
74
|
+
return withTaskTransaction({ target: options.target, taskId: options.taskId ?? state.taskId,
|
|
75
|
+
packageRoot: options.packageRoot, operation: "rebind-completion-rejection" }, () => rebindRejectionInTransaction(options));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function rebindRejectionInTransaction({
|
|
69
79
|
target,
|
|
70
80
|
packageRoot,
|
|
71
81
|
taskId = null,
|