@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
|
@@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { resolveTaskContext } from "../core/task-context.js";
|
|
4
4
|
import { inspectTaskConflictState } from "../core/task-conflict-inspection.js";
|
|
5
5
|
import { appendProtocolEvent } from "../core/events.js";
|
|
6
|
-
import { withTaskTransaction } from "../core/transaction.js";
|
|
6
|
+
import { getTaskTransaction, withTaskTransaction } from "../core/transaction.js";
|
|
7
7
|
import { currentRepositoryFingerprint } from "../core/repository.js";
|
|
8
8
|
import {
|
|
9
9
|
readLockInfo,
|
|
@@ -86,6 +86,7 @@ export async function runTaskRecover({
|
|
|
86
86
|
acknowledgeRecovery = false,
|
|
87
87
|
operatorAuthorized = false,
|
|
88
88
|
} = {}) {
|
|
89
|
+
const nested = Boolean(await getTaskTransaction(target));
|
|
89
90
|
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
|
|
90
91
|
const effectiveTaskId = context.taskId;
|
|
91
92
|
|
|
@@ -130,7 +131,7 @@ export async function runTaskRecover({
|
|
|
130
131
|
const inspection = await inspectTaskConflictState(target, {
|
|
131
132
|
taskId: effectiveTaskId,
|
|
132
133
|
packageRoot,
|
|
133
|
-
ignoredLockId: transaction.lock
|
|
134
|
+
ignoredLockId: transaction.lock?.lockId ?? null,
|
|
134
135
|
});
|
|
135
136
|
assertRecoveryAllowed(effectiveTaskId, inspection);
|
|
136
137
|
if (inspection.evidence.workStateRevision !== inspectionBeforeLock.evidence.workStateRevision
|
|
@@ -194,6 +195,13 @@ export async function runTaskRecover({
|
|
|
194
195
|
message: `Task ${effectiveTaskId} recovered by caller acknowledgement; write claims released without completion claim`,
|
|
195
196
|
};
|
|
196
197
|
});
|
|
198
|
+
}).catch(async error => {
|
|
199
|
+
if (error.code === "E_STATE_REVISION_CONFLICT" && !nested) {
|
|
200
|
+
// The failed transaction has rolled back. Report a competing validated
|
|
201
|
+
// recovery through the existing domain error; never replay claim release.
|
|
202
|
+
await assertConsistentRecoverableOwnership(target, effectiveTaskId, packageRoot);
|
|
203
|
+
}
|
|
204
|
+
throw error;
|
|
197
205
|
});
|
|
198
206
|
}
|
|
199
207
|
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { canonicalFingerprint } from "../core/artifacts.js";
|
|
2
|
+
import { readContract } from "../core/contract.js";
|
|
3
|
+
import { appendProtocolEvent, validateEventLedger } from "../core/events.js";
|
|
4
|
+
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
5
|
+
import { currentRepositoryFingerprint } from "../core/repository.js";
|
|
6
|
+
import { resolveTaskContext } from "../core/task-context.js";
|
|
7
|
+
import { taskArtifactPath } from "../core/task-paths.js";
|
|
8
|
+
import { withProjectClaimsLock, readLockInfo, classifyLockStaleness, releaseStaleTaskLockIfUnchanged } from "../core/task-lock.js";
|
|
9
|
+
import { withTaskTransaction } from "../core/transaction.js";
|
|
10
|
+
import { createWorkState, mutateWorkState, readWorkState } from "../core/work-state.js";
|
|
11
|
+
import { resolveTaskClaimState } from "../core/task-claim-state.js";
|
|
12
|
+
import {
|
|
13
|
+
CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
14
|
+
CONTRACT_BOOTSTRAP_REPAIR_DEFECT,
|
|
15
|
+
CONTRACT_BOOTSTRAP_REPAIR_EVENT,
|
|
16
|
+
CONTRACT_BOOTSTRAP_REPAIR_VERSION,
|
|
17
|
+
assertContractBootstrapRepairDetails,
|
|
18
|
+
contractBootstrapRepairId,
|
|
19
|
+
isContractBootstrapRepairCandidate,
|
|
20
|
+
resolveEffectiveContractBootstrapRepairAnchor,
|
|
21
|
+
} from "../core/contract-bootstrap-recovery.js";
|
|
22
|
+
import {
|
|
23
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_AUTHORIZATION_REQUIRED,
|
|
24
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_INVALID,
|
|
25
|
+
E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE,
|
|
26
|
+
E_TASK_LOCKED,
|
|
27
|
+
} from "../core/error-codes.js";
|
|
28
|
+
|
|
29
|
+
function repairError(code, message, details = {}) {
|
|
30
|
+
const error = new Error(message);
|
|
31
|
+
error.code = code;
|
|
32
|
+
Object.assign(error, details);
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function readRouteIfProven(target, packageRoot, taskId, events, contractFingerprint) {
|
|
37
|
+
const routePath = taskArtifactPath(taskId, "route");
|
|
38
|
+
const hasRouteValidated = events.some((event) => event.event === "ROUTE_VALIDATED");
|
|
39
|
+
if (!hasRouteValidated) return null;
|
|
40
|
+
try {
|
|
41
|
+
const route = await readPersistedRoute(target, packageRoot, { taskId });
|
|
42
|
+
if (route.value.contractFingerprint !== contractFingerprint) {
|
|
43
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Persisted route does not bind to the canonical contract fingerprint");
|
|
44
|
+
}
|
|
45
|
+
return route;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code === "ARTIFACT_MISSING") {
|
|
48
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "ROUTE_VALIDATED history requires a persisted route artifact", { artifacts: [routePath] });
|
|
49
|
+
}
|
|
50
|
+
if (error.code === E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE) throw error;
|
|
51
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Persisted route cannot be proven safe: ${error.message}`, { artifacts: [routePath] });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stateForRepair(state, { taskId, contractFingerprint, route, repository }) {
|
|
56
|
+
const routed = Boolean(route);
|
|
57
|
+
const phase = routed ? "ROUTED" : "CONTRACT_READY";
|
|
58
|
+
const steps = routed
|
|
59
|
+
? { completedSteps: ["contract", "route"], pendingSteps: ["planning", "implementation", "verification"] }
|
|
60
|
+
: { completedSteps: ["contract"], pendingSteps: ["route", "planning", "implementation", "verification"] };
|
|
61
|
+
const base = state ? {
|
|
62
|
+
...state,
|
|
63
|
+
taskId,
|
|
64
|
+
contractFingerprint,
|
|
65
|
+
...(routed ? { routeFingerprint: route.fingerprint } : { routeFingerprint: undefined }),
|
|
66
|
+
repositoryFingerprint: repository,
|
|
67
|
+
phase,
|
|
68
|
+
selectedGuides: routed ? [...route.value.guides] : [],
|
|
69
|
+
completedSteps: steps.completedSteps,
|
|
70
|
+
pendingSteps: steps.pendingSteps,
|
|
71
|
+
revision: (state.revision ?? 0) + 1,
|
|
72
|
+
lastUpdated: new Date().toISOString(),
|
|
73
|
+
...(state.phase !== phase ? { previousPhase: state.phase } : {}),
|
|
74
|
+
} : createWorkState({
|
|
75
|
+
taskId,
|
|
76
|
+
contractFingerprint,
|
|
77
|
+
...(routed ? { routeFingerprint: route.fingerprint } : {}),
|
|
78
|
+
repositoryFingerprint: repository,
|
|
79
|
+
phase,
|
|
80
|
+
selectedGuides: routed ? [...route.value.guides] : [],
|
|
81
|
+
requiredGates: [],
|
|
82
|
+
satisfiedGates: [],
|
|
83
|
+
completedSteps: steps.completedSteps,
|
|
84
|
+
pendingSteps: steps.pendingSteps,
|
|
85
|
+
requiredArtifacts: [],
|
|
86
|
+
checks: [],
|
|
87
|
+
failures: [],
|
|
88
|
+
blockers: [],
|
|
89
|
+
verificationEvidence: [],
|
|
90
|
+
revision: 0,
|
|
91
|
+
});
|
|
92
|
+
if (base.routeFingerprint === undefined) delete base.routeFingerprint;
|
|
93
|
+
return createWorkState(base);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assertStateCompatible(state, taskId, contractFingerprint, route) {
|
|
97
|
+
if (!state) return;
|
|
98
|
+
if (state.taskId !== taskId || state.contractFingerprint !== contractFingerprint) {
|
|
99
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state is bound to a different task or contract");
|
|
100
|
+
}
|
|
101
|
+
if (!["CONTRACT_READY", "ROUTED"].includes(state.phase)) {
|
|
102
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Existing work-state phase ${state.phase} is beyond the repair boundary`);
|
|
103
|
+
}
|
|
104
|
+
if (route && state.routeFingerprint !== undefined && state.routeFingerprint !== route.fingerprint) {
|
|
105
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state route fingerprint conflicts with the proven route");
|
|
106
|
+
}
|
|
107
|
+
if (!route && state.routeFingerprint !== undefined) {
|
|
108
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state claims a route that cannot be proven from the artifacts");
|
|
109
|
+
}
|
|
110
|
+
if (state.phase === "CONTRACT_READY") {
|
|
111
|
+
if (state.routeFingerprint !== undefined) {
|
|
112
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not claim a route");
|
|
113
|
+
}
|
|
114
|
+
if (Array.isArray(state.selectedGuides) && state.selectedGuides.length > 0) {
|
|
115
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have selected guides");
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(state.requiredGates) && state.requiredGates.length > 0) {
|
|
118
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have required gates");
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(state.satisfiedGates) && state.satisfiedGates.length > 0) {
|
|
121
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "CONTRACT_READY work-state must not have satisfied gates");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function markerResult(taskId, marker, state, alreadyRepaired) {
|
|
127
|
+
return {
|
|
128
|
+
taskId,
|
|
129
|
+
repaired: alreadyRepaired ? false : true,
|
|
130
|
+
alreadyRepaired,
|
|
131
|
+
repairId: marker.details.repairId,
|
|
132
|
+
phase: state.phase,
|
|
133
|
+
reconstructedPhase: marker.details.reconstructedPhase,
|
|
134
|
+
markerEventSeq: marker.seq,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function verifyExistingRepair(target, packageRoot, taskId, ledger) {
|
|
139
|
+
const anchor = resolveEffectiveContractBootstrapRepairAnchor(ledger.events);
|
|
140
|
+
if (!anchor || !ledger.valid) {
|
|
141
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Existing contract bootstrap repair marker is invalid or the repaired ledger is no longer strict");
|
|
142
|
+
}
|
|
143
|
+
const contract = await readContract(target, packageRoot, { taskId });
|
|
144
|
+
if (contract.fingerprint !== anchor.details.contractFingerprint) {
|
|
145
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Contract fingerprint no longer matches the repair marker");
|
|
146
|
+
}
|
|
147
|
+
const state = await readWorkState(target, { packageRoot, taskId });
|
|
148
|
+
const ownership = await resolveTaskClaimState(target, { taskId, packageRoot });
|
|
149
|
+
if (!ownership.ownershipValid) {
|
|
150
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Current task state no longer satisfies the repaired-task invariants", {
|
|
151
|
+
errors: ownership.errors,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return markerResult(taskId, { ...anchor.sourceMarker, details: anchor.details }, state, true);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function runTaskRepairContractBootstrap({ target, packageRoot, taskId, acknowledgeRepair } = {}) {
|
|
158
|
+
if (!acknowledgeRepair) {
|
|
159
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_AUTHORIZATION_REQUIRED, "task-repair-contract-bootstrap requires fresh explicit --acknowledge-repair");
|
|
160
|
+
}
|
|
161
|
+
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
|
|
162
|
+
const effectiveTaskId = context.taskId;
|
|
163
|
+
const initial = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
164
|
+
if (initial.events.some((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT)) {
|
|
165
|
+
return verifyExistingRepair(target, packageRoot, effectiveTaskId, initial);
|
|
166
|
+
}
|
|
167
|
+
const candidate = isContractBootstrapRepairCandidate(initial.events, initial.errors, effectiveTaskId);
|
|
168
|
+
if (!candidate) {
|
|
169
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Ledger does not match the exact contract bootstrap repair signature", { errors: initial.errors });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return withProjectClaimsLock(target, "task-repair-contract-bootstrap", async () => {
|
|
173
|
+
const lock = await readLockInfo(target, effectiveTaskId);
|
|
174
|
+
const status = classifyLockStaleness(lock);
|
|
175
|
+
if (status.status === "LIVE") throw repairError(E_TASK_LOCKED, `Task ${effectiveTaskId} has a live mutation lock`);
|
|
176
|
+
if (status.status === "UNKNOWN" || status.status === "CORRUPT") {
|
|
177
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, `Task ${effectiveTaskId} lock state is ${status.status}`);
|
|
178
|
+
}
|
|
179
|
+
if (status.status === "STALE") {
|
|
180
|
+
const released = await releaseStaleTaskLockIfUnchanged(target, effectiveTaskId, lock);
|
|
181
|
+
if (!released.released && released.reason !== "LOCK_MISSING") {
|
|
182
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE,
|
|
183
|
+
`Task ${effectiveTaskId} stale lock could not be safely released: ${released.reason}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return withTaskTransaction({
|
|
187
|
+
target,
|
|
188
|
+
taskId: effectiveTaskId,
|
|
189
|
+
operation: "task-repair-contract-bootstrap",
|
|
190
|
+
packageRoot,
|
|
191
|
+
recordCommitEvent: true,
|
|
192
|
+
}, async () => {
|
|
193
|
+
const locked = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId });
|
|
194
|
+
if (locked.events.some((event) => event.event === CONTRACT_BOOTSTRAP_REPAIR_EVENT)) {
|
|
195
|
+
return verifyExistingRepair(target, packageRoot, effectiveTaskId, locked);
|
|
196
|
+
}
|
|
197
|
+
const lockedCandidate = isContractBootstrapRepairCandidate(locked.events, locked.errors, effectiveTaskId);
|
|
198
|
+
if (!lockedCandidate
|
|
199
|
+
|| lockedCandidate.duplicateContractEvent.hash !== candidate.duplicateContractEvent.hash
|
|
200
|
+
|| lockedCandidate.duplicateContractCommit.hash !== candidate.duplicateContractCommit.hash) {
|
|
201
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_INVALID, "Ledger changed between inspection and repair lock");
|
|
202
|
+
}
|
|
203
|
+
const contract = await readContract(target, packageRoot, { taskId: effectiveTaskId });
|
|
204
|
+
if (contract.fingerprint !== lockedCandidate.contractFingerprint) {
|
|
205
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Current contract does not match the canonical historical contract fingerprint");
|
|
206
|
+
}
|
|
207
|
+
const route = await readRouteIfProven(target, packageRoot, effectiveTaskId, locked.events, contract.fingerprint);
|
|
208
|
+
const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
|
|
209
|
+
assertStateCompatible(state, effectiveTaskId, contract.fingerprint, route);
|
|
210
|
+
if (!state) {
|
|
211
|
+
throw repairError(E_CONTRACT_BOOTSTRAP_REPAIR_UNSAFE, "Existing work-state is required to repair the contract bootstrap defect");
|
|
212
|
+
}
|
|
213
|
+
const repository = await currentRepositoryFingerprint(target);
|
|
214
|
+
const reconstructed = await mutateWorkState(target, {
|
|
215
|
+
expectedRevision: state.revision ?? 0,
|
|
216
|
+
packageRoot,
|
|
217
|
+
taskId: effectiveTaskId,
|
|
218
|
+
}, (current) => {
|
|
219
|
+
assertStateCompatible(current, effectiveTaskId, contract.fingerprint, route);
|
|
220
|
+
return stateForRepair(current, {
|
|
221
|
+
taskId: effectiveTaskId,
|
|
222
|
+
contractFingerprint: contract.fingerprint,
|
|
223
|
+
route,
|
|
224
|
+
repository,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
const repairedAt = new Date().toISOString();
|
|
228
|
+
const details = {
|
|
229
|
+
repairVersion: CONTRACT_BOOTSTRAP_REPAIR_VERSION,
|
|
230
|
+
taskId: effectiveTaskId,
|
|
231
|
+
defect: CONTRACT_BOOTSTRAP_REPAIR_DEFECT,
|
|
232
|
+
canonicalContractEventSeq: lockedCandidate.canonicalContractEvent.seq,
|
|
233
|
+
canonicalContractEventHash: lockedCandidate.canonicalContractEvent.hash,
|
|
234
|
+
duplicateContractEventSeq: lockedCandidate.duplicateContractEvent.seq,
|
|
235
|
+
duplicateContractEventHash: lockedCandidate.duplicateContractEvent.hash,
|
|
236
|
+
contractCreateCommitSeq: lockedCandidate.duplicateContractCommit.seq,
|
|
237
|
+
contractCreateCommitHash: lockedCandidate.duplicateContractCommit.hash,
|
|
238
|
+
contractCreateTransactionId: lockedCandidate.duplicateContractCommit.details.transactionId,
|
|
239
|
+
contractFingerprint: contract.fingerprint,
|
|
240
|
+
reconstructedPhase: reconstructed.phase,
|
|
241
|
+
routeFingerprint: route?.fingerprint ?? null,
|
|
242
|
+
previousStateFingerprint: canonicalFingerprint(state),
|
|
243
|
+
reconstructedStateFingerprint: canonicalFingerprint(reconstructed),
|
|
244
|
+
reconstructedStateRevision: reconstructed.revision,
|
|
245
|
+
repairedAt,
|
|
246
|
+
authorityKind: CONTRACT_BOOTSTRAP_REPAIR_AUTHORITY,
|
|
247
|
+
};
|
|
248
|
+
details.repairId = contractBootstrapRepairId(details);
|
|
249
|
+
assertContractBootstrapRepairDetails(details);
|
|
250
|
+
const marker = await appendProtocolEvent(target, {
|
|
251
|
+
taskId: effectiveTaskId,
|
|
252
|
+
event: CONTRACT_BOOTSTRAP_REPAIR_EVENT,
|
|
253
|
+
at: repairedAt,
|
|
254
|
+
details,
|
|
255
|
+
}, packageRoot, { taskId: effectiveTaskId });
|
|
256
|
+
return markerResult(effectiveTaskId, marker, reconstructed, false);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function formatTaskRepairContractBootstrapResult(result) {
|
|
262
|
+
return `taskId: ${result.taskId}\nphase: ${result.phase}\nrepairId: ${result.repairId}\n${result.alreadyRepaired ? "alreadyRepaired: true" : "repaired: true"}\n`;
|
|
263
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { withEventLedgerAudit } from "../core/events.js";
|
|
2
|
+
import { getOperationalStore } from "../storage/operational-context.js";
|
|
3
|
+
import { needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
|
|
1
4
|
import { resolveTaskContext } from "../core/task-context.js";
|
|
2
5
|
import { readTaskDescriptor, writeTaskDescriptor } from "../core/task-descriptor.js";
|
|
3
6
|
import { normalizeWriteClaims, assertScopeClean, assertScopeNotFrozen } from "../core/task-scope.js";
|
|
@@ -18,8 +21,20 @@ function taskError(code, message, artifacts = []) {
|
|
|
18
21
|
return error;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
export async function runTaskScope(
|
|
24
|
+
export async function runTaskScope(options = {}) {
|
|
25
|
+
const { target, packageRoot, taskId, claims } = options;
|
|
26
|
+
const readOnly = claims === undefined || claims === null;
|
|
27
|
+
if (readOnly && await needsExistingProjectScope(target)) {
|
|
28
|
+
return withExistingProjectScope(target, () => runTaskScope(options), { readOnly: true });
|
|
29
|
+
}
|
|
22
30
|
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
|
|
31
|
+
if (readOnly && getOperationalStore(target)) {
|
|
32
|
+
return withEventLedgerAudit(target, packageRoot, { taskId: context.taskId }, () => runResolvedTaskScope(options, context));
|
|
33
|
+
}
|
|
34
|
+
return runResolvedTaskScope(options, context);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function runResolvedTaskScope({ target, packageRoot, claims }, context) {
|
|
23
38
|
const effectiveTaskId = context.taskId;
|
|
24
39
|
|
|
25
40
|
let descriptorArtifact;
|
|
@@ -38,6 +53,7 @@ export async function runTaskScope({ target, packageRoot, taskId, claims } = {})
|
|
|
38
53
|
return withProjectClaimsLock(target, async () => {
|
|
39
54
|
return withTaskLock(target, effectiveTaskId, "task-scope", async () => {
|
|
40
55
|
await assertTaskNotRecovered(target, { taskId: effectiveTaskId, packageRoot });
|
|
56
|
+
const lockedDescriptor = (await readTaskDescriptor(target, effectiveTaskId, packageRoot)).value;
|
|
41
57
|
const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
|
|
42
58
|
if (state?.phase) {
|
|
43
59
|
assertScopeNotFrozen(state.phase);
|
|
@@ -51,7 +67,7 @@ export async function runTaskScope({ target, packageRoot, taskId, claims } = {})
|
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
const updatedDescriptor = {
|
|
54
|
-
...
|
|
70
|
+
...lockedDescriptor,
|
|
55
71
|
writeClaims: normalizedClaims,
|
|
56
72
|
updatedAt: new Date().toISOString(),
|
|
57
73
|
};
|
|
@@ -9,6 +9,9 @@ import { E_TASK_NOT_FOUND } from "../core/error-codes.js";
|
|
|
9
9
|
import { resolveTaskClaimState } from "../core/task-claim-state.js";
|
|
10
10
|
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
11
11
|
import { projectExecutionProfile } from "../core/execution-profile.js";
|
|
12
|
+
import { withEventLedgerAudit } from "../core/events.js";
|
|
13
|
+
import { needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
|
|
14
|
+
import { getOperationalStore, operationalArtifactExists } from "../storage/operational-context.js";
|
|
12
15
|
|
|
13
16
|
function taskError(code, message, artifacts = []) {
|
|
14
17
|
const error = new Error(message);
|
|
@@ -17,8 +20,19 @@ function taskError(code, message, artifacts = []) {
|
|
|
17
20
|
return error;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
export async function runTaskShow(
|
|
23
|
+
export async function runTaskShow(options = {}) {
|
|
24
|
+
const { target, packageRoot, taskId } = options;
|
|
25
|
+
if (await needsExistingProjectScope(target)) {
|
|
26
|
+
return withExistingProjectScope(target, () => runTaskShow(options), { readOnly: true });
|
|
27
|
+
}
|
|
21
28
|
const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true, selectionMode: TASK_SELECTION_MODES.READ });
|
|
29
|
+
if (getOperationalStore(target)) {
|
|
30
|
+
return withEventLedgerAudit(target, packageRoot, { taskId: context.taskId }, () => projectTaskShow(options, context));
|
|
31
|
+
}
|
|
32
|
+
return projectTaskShow(options, context);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function projectTaskShow({ target, packageRoot, compact = false }, context) {
|
|
22
36
|
const effectiveTaskId = context.taskId;
|
|
23
37
|
|
|
24
38
|
let descriptor = null;
|
|
@@ -54,7 +68,7 @@ export async function runTaskShow({ target, packageRoot, taskId, compact = false
|
|
|
54
68
|
const rel = taskArtifactPath(effectiveTaskId, name);
|
|
55
69
|
artifacts[name] = {
|
|
56
70
|
path: rel,
|
|
57
|
-
exists: await fileExists(ensureWithin(target, rel)),
|
|
71
|
+
exists: operationalArtifactExists(target, rel) ?? await fileExists(ensureWithin(target, rel)),
|
|
58
72
|
};
|
|
59
73
|
}
|
|
60
74
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runTestInventory as inventory } from "../core/test-intelligence/service.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestInventory({ target = process.cwd() } = {}) { return inventory({ projectRoot: target }); }
|
|
4
|
+
export function formatTestInventoryResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { buildPrunePlan } from "../core/test-intelligence/prune.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestPrunePlan({ target, packageRoot, taskId } = {}) { return buildPrunePlan({ target, packageRoot, taskId }); }
|
|
4
|
+
export function formatTestPrunePlanResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runPruneProbe as probe } from "../core/test-intelligence/prune.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestPruneProbe({ target, packageRoot, taskId, testId } = {}) { return probe({ target, packageRoot, taskId, testId }); }
|
|
4
|
+
export function formatTestPruneProbeResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { runTestUtility as analyze } from "../core/test-intelligence/service.js";
|
|
2
|
+
|
|
3
|
+
export async function runTestUtility({ target, packageRoot, taskId, semanticStatus } = {}) { return analyze({ target, packageRoot, taskId, semanticStatus }); }
|
|
4
|
+
export function formatTestUtilityResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
|
|
5
|
+
|
|
@@ -8,17 +8,20 @@ import { validateReceipt } from "../core/receipt.js";
|
|
|
8
8
|
import { validateTaskBrief, validateDelegatedResult } from "../core/delegation.js";
|
|
9
9
|
import { ARTIFACT_PATHS, readJsonArtifact } from "../core/artifacts.js";
|
|
10
10
|
import { evaluatePreflight, validateReadyProtocolConsistency } from "../core/preflight.js";
|
|
11
|
-
import {
|
|
11
|
+
import { withEventLedgerAudit, validateStateLedgerCoherence } from "../core/events.js";
|
|
12
12
|
import { validateChecksExecutionProvenance } from "../core/completion-artifacts.js";
|
|
13
13
|
import { assertContinuitySemantics } from "../core/continuity.js";
|
|
14
14
|
import { currentChangedPaths, currentRepositoryFingerprint } from "../core/repository.js";
|
|
15
15
|
import { resolveTaskClaimState } from "../core/task-claim-state.js";
|
|
16
|
+
import { getOperationalStore, readOperationalText } from "../storage/operational-context.js";
|
|
17
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
16
18
|
|
|
17
19
|
async function readArtifact(target, relativePath, label) {
|
|
18
20
|
if (!relativePath) return null;
|
|
19
21
|
await assertSafePath(target, relativePath);
|
|
20
22
|
const artifactPath = ensureWithin(target, relativePath);
|
|
21
|
-
|
|
23
|
+
const operational = readOperationalText(target, relativePath);
|
|
24
|
+
if (operational.selected ? operational.text === null : !(await fileExists(artifactPath))) {
|
|
22
25
|
return {
|
|
23
26
|
error: {
|
|
24
27
|
code: "ARTIFACT_MISSING",
|
|
@@ -27,7 +30,7 @@ async function readArtifact(target, relativePath, label) {
|
|
|
27
30
|
},
|
|
28
31
|
};
|
|
29
32
|
}
|
|
30
|
-
const bytes = await readBytes(artifactPath);
|
|
33
|
+
const bytes = operational.selected ? Buffer.from(operational.text, "utf8") : await readBytes(artifactPath);
|
|
31
34
|
try {
|
|
32
35
|
assertJsonBytes(bytes, label);
|
|
33
36
|
const value = JSON.parse(bytes.toString("utf8"));
|
|
@@ -44,10 +47,18 @@ async function readArtifact(target, relativePath, label) {
|
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
function isOptionalContinuityAbsence(item) {
|
|
51
|
+
return item.label === "continuity" && item.error?.code === "ARTIFACT_MISSING";
|
|
52
|
+
}
|
|
53
|
+
|
|
47
54
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
48
55
|
import { withResolvedTask } from "../core/task-command.js";
|
|
49
56
|
|
|
50
|
-
export async function runValidateProtocol({
|
|
57
|
+
export async function runValidateProtocol(options) {
|
|
58
|
+
return withProjectReadSnapshot(options.target, () => validateSelectedProtocol(options));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function validateSelectedProtocol({
|
|
51
62
|
target,
|
|
52
63
|
packageRoot,
|
|
53
64
|
routeFile = null,
|
|
@@ -59,9 +70,17 @@ export async function runValidateProtocol({
|
|
|
59
70
|
delegatedResultFiles = [],
|
|
60
71
|
taskId = null,
|
|
61
72
|
task = null,
|
|
62
|
-
}) {
|
|
73
|
+
}, ownedLedger = null) {
|
|
63
74
|
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
64
75
|
const effectiveTaskId = ctx?.taskId ?? null;
|
|
76
|
+
if (effectiveTaskId && getOperationalStore(target) && !ownedLedger) {
|
|
77
|
+
// Keep the complete final proof local to this immutable projection.
|
|
78
|
+
// Nested preflight audits retain their own rules and share its source.
|
|
79
|
+
return withEventLedgerAudit(target, packageRoot, { taskId: effectiveTaskId }, ledger => validateSelectedProtocol({
|
|
80
|
+
target, packageRoot, routeFile, stateFile, receiptFile, contractFile, continuityFile,
|
|
81
|
+
taskBriefFiles, delegatedResultFiles, taskId: effectiveTaskId,
|
|
82
|
+
}, ledger));
|
|
83
|
+
}
|
|
65
84
|
const effectiveRouteFile = routeFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "route") : ARTIFACT_PATHS.route);
|
|
66
85
|
const effectiveStateFile = stateFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "state") : ARTIFACT_PATHS.state);
|
|
67
86
|
const effectiveReceiptFile = receiptFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "receipt") : ARTIFACT_PATHS.receipt);
|
|
@@ -84,7 +103,12 @@ export async function runValidateProtocol({
|
|
|
84
103
|
loaded.push({ label, relativePath, ...artifact });
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
|
|
106
|
+
// Continuity is an optional operational-context artifact. Its absence is
|
|
107
|
+
// represented by evaluateContinuityConformance as NOT_APPLICABLE; only a
|
|
108
|
+
// present but malformed continuity artifact is a validation failure.
|
|
109
|
+
const readErrors = loaded
|
|
110
|
+
.filter((item) => item.error && !isOptionalContinuityAbsence(item))
|
|
111
|
+
.map((item) => item.error);
|
|
88
112
|
const route = loaded.find((item) => item.label === "route")?.value ?? null;
|
|
89
113
|
const state = loaded.find((item) => item.label === "state")?.value ?? null;
|
|
90
114
|
const receipt = loaded.find((item) => item.label === "receipt")?.value ?? null;
|
|
@@ -160,19 +184,15 @@ export async function runValidateProtocol({
|
|
|
160
184
|
} catch {
|
|
161
185
|
// A missing or invalid preflight is already outside the optional protocol set.
|
|
162
186
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const ledger = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId, eventsPath: effectiveEventsFile });
|
|
167
|
-
ledgerEvents = ledger.events ?? [];
|
|
168
|
-
ledgerErrors = [
|
|
187
|
+
const finish = async (ledger) => {
|
|
188
|
+
const ledgerEvents = ledger.events ?? [];
|
|
189
|
+
const ledgerErrors = state && !stateValidationError ? [
|
|
169
190
|
...ledger.errors.map((error) => ({ ...error, artifacts: [effectiveEventsFile] })),
|
|
170
|
-
...validateStateLedgerCoherence(state,
|
|
191
|
+
...validateStateLedgerCoherence(state, ledgerEvents).map((error) => ({
|
|
171
192
|
...error,
|
|
172
193
|
artifacts: [effectiveStateFile, effectiveEventsFile],
|
|
173
194
|
})),
|
|
174
|
-
];
|
|
175
|
-
}
|
|
195
|
+
] : [];
|
|
176
196
|
const recoveryErrors = [];
|
|
177
197
|
if (effectiveTaskId) {
|
|
178
198
|
try {
|
|
@@ -216,6 +236,10 @@ export async function runValidateProtocol({
|
|
|
216
236
|
};
|
|
217
237
|
}
|
|
218
238
|
return result;
|
|
239
|
+
};
|
|
240
|
+
return state && !stateValidationError
|
|
241
|
+
? ownedLedger ? finish(ownedLedger) : withEventLedgerAudit(target, packageRoot, { taskId: effectiveTaskId, eventsPath: effectiveEventsFile }, finish)
|
|
242
|
+
: finish({ events: [], errors: [] });
|
|
219
243
|
});
|
|
220
244
|
}
|
|
221
245
|
|
|
@@ -4,13 +4,17 @@ import { assertJsonBytes, assertJsonLimits } from "../core/json-safety.js";
|
|
|
4
4
|
import { ARTIFACT_PATHS } from "../core/artifacts.js";
|
|
5
5
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
6
6
|
import { withResolvedTask } from "../core/task-command.js";
|
|
7
|
+
import { readOperationalText } from "../storage/operational-context.js";
|
|
8
|
+
import { isOperationalArtifactPath, needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
|
|
7
9
|
|
|
8
10
|
async function validateReceiptFile(target, packageRoot, relativeFile) {
|
|
9
11
|
await assertSafePath(target, relativeFile);
|
|
10
12
|
const receiptPath = ensureWithin(target, relativeFile);
|
|
11
13
|
let receipt;
|
|
12
14
|
try {
|
|
13
|
-
const
|
|
15
|
+
const operational = readOperationalText(target, relativeFile);
|
|
16
|
+
if (operational.selected && operational.text === null) throw new Error("Receipt is missing from canonical storage");
|
|
17
|
+
const bytes = operational.selected ? Buffer.from(operational.text, "utf8") : await readBytes(receiptPath);
|
|
14
18
|
assertJsonBytes(bytes, relativeFile);
|
|
15
19
|
receipt = JSON.parse(bytes.toString("utf8"));
|
|
16
20
|
assertJsonLimits(receipt, relativeFile);
|
|
@@ -43,6 +47,9 @@ export async function runValidateReceipt({
|
|
|
43
47
|
file = null,
|
|
44
48
|
taskId = null,
|
|
45
49
|
} = {}) {
|
|
50
|
+
if ((!file || isOperationalArtifactPath(file)) && await needsExistingProjectScope(target)) {
|
|
51
|
+
return withExistingProjectScope(target, () => runValidateReceipt({ target, packageRoot, file, taskId }), { readOnly: true });
|
|
52
|
+
}
|
|
46
53
|
if (file) {
|
|
47
54
|
return validateReceiptFile(target, packageRoot, file);
|
|
48
55
|
}
|
package/src/config/guides.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { iterateEvents } from "./events.js";
|
|
2
2
|
import { assertActionTransition, assertActionAuthorizationDetails } from "./action-model.js";
|
|
3
3
|
import { E_ACTION_EVIDENCE_INVALID } from "./error-codes.js";
|
|
4
4
|
|
|
@@ -27,6 +27,14 @@ function hasCanonicalVerificationEvidence(details) {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
async function readActionChronology(target, packageRoot, taskId, actionId) {
|
|
31
|
+
const chronology = [];
|
|
32
|
+
for await (const event of iterateEvents(target, packageRoot, { taskId })) {
|
|
33
|
+
if (event.details?.actionId === actionId) chronology.push(event);
|
|
34
|
+
}
|
|
35
|
+
return chronology;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
function issue(code, message) {
|
|
31
39
|
return { code, message };
|
|
32
40
|
}
|
|
@@ -49,8 +57,7 @@ export async function projectActionLedger({
|
|
|
49
57
|
actionId,
|
|
50
58
|
artifact = null,
|
|
51
59
|
}) {
|
|
52
|
-
const
|
|
53
|
-
const chronology = events.filter((event) => event.details?.actionId === actionId);
|
|
60
|
+
const chronology = await readActionChronology(target, packageRoot, taskId, actionId);
|
|
54
61
|
const errors = [];
|
|
55
62
|
|
|
56
63
|
const proposed = chronology.find((event) => event.event === "ACTION_PROPOSED");
|