@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
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { ARTIFACT_PATHS } from "./artifacts.js";
|
|
2
2
|
import { reconcileContinuity } from "./continuity-reconciliation.js";
|
|
3
|
-
import { NEXT_ACTIONS, result } from "./next-action-model.js";
|
|
3
|
+
import { directCommandSpec, NEXT_ACTIONS, result } from "./next-action-model.js";
|
|
4
|
+
import { taskArtifactPath } from "./task-paths.js";
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const SINGLETON_REQUIRED_ARTIFACTS = Object.freeze([ARTIFACT_PATHS.state, ARTIFACT_PATHS.continuity]);
|
|
6
7
|
|
|
7
|
-
function reason(code, message, artifacts =
|
|
8
|
+
function reason(code, message, artifacts = SINGLETON_REQUIRED_ARTIFACTS) {
|
|
8
9
|
return { code, message, artifacts };
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
function continuityArtifacts(taskId) {
|
|
13
|
+
if (!taskId) {
|
|
14
|
+
return {
|
|
15
|
+
continuity: ARTIFACT_PATHS.continuity,
|
|
16
|
+
required: SINGLETON_REQUIRED_ARTIFACTS,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
continuity: taskArtifactPath(taskId, "continuity"),
|
|
21
|
+
required: [taskArtifactPath(taskId, "state"), taskArtifactPath(taskId, "continuity")],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
export function nextActionForContinuity({ context, continuity } = {}) {
|
|
12
26
|
if (!continuity || ["ABSENT", "NOT_APPLICABLE"].includes(continuity.classification)) return null;
|
|
27
|
+
const taskId = context?.taskId ?? null;
|
|
28
|
+
const artifacts = continuityArtifacts(taskId);
|
|
13
29
|
|
|
14
30
|
if (continuity.classification === "FRESH") {
|
|
15
31
|
const remaining = continuity.continuity?.remainingWork ?? [];
|
|
@@ -20,22 +36,27 @@ export function nextActionForContinuity({ context, continuity } = {}) {
|
|
|
20
36
|
reasons: [reason(
|
|
21
37
|
"CONTINUITY_REMAINING_WORK",
|
|
22
38
|
`Execution continuity records ${remaining.length} remaining implementation item${remaining.length === 1 ? "" : "s"}.`,
|
|
23
|
-
[
|
|
39
|
+
[artifacts.continuity],
|
|
24
40
|
)],
|
|
25
|
-
requiredArtifacts:
|
|
41
|
+
requiredArtifacts: artifacts.required,
|
|
26
42
|
});
|
|
27
43
|
}
|
|
28
44
|
|
|
29
45
|
if (continuity.classification === "RECONCILIATION_REQUIRED") {
|
|
46
|
+
const command = taskId
|
|
47
|
+
? `forgeloop reconcile-continuity --task ${taskId}`
|
|
48
|
+
: "forgeloop reconcile-continuity";
|
|
30
49
|
return result({
|
|
31
50
|
...context,
|
|
32
51
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
33
|
-
commands: [
|
|
52
|
+
commands: [command],
|
|
53
|
+
...(taskId ? { commandSpecs: [directCommandSpec("reconcile-continuity", taskId)] } : {}),
|
|
34
54
|
reasons: [reason(
|
|
35
55
|
"E_CONTINUITY_RECONCILIATION_REQUIRED",
|
|
36
56
|
"Execution continuity no longer matches canonical state or the current checkout; reconcile it before advancing verification.",
|
|
57
|
+
artifacts.required,
|
|
37
58
|
)],
|
|
38
|
-
requiredArtifacts:
|
|
59
|
+
requiredArtifacts: artifacts.required,
|
|
39
60
|
});
|
|
40
61
|
}
|
|
41
62
|
|
|
@@ -49,8 +70,9 @@ export function nextActionForContinuity({ context, continuity } = {}) {
|
|
|
49
70
|
reasons: codes.map((code, index) => reason(
|
|
50
71
|
code,
|
|
51
72
|
continuity.reasons?.[index] ?? `Execution continuity is ${continuity.classification.toLowerCase()}.`,
|
|
73
|
+
artifacts.required,
|
|
52
74
|
)),
|
|
53
|
-
requiredArtifacts:
|
|
75
|
+
requiredArtifacts: artifacts.required,
|
|
54
76
|
});
|
|
55
77
|
}
|
|
56
78
|
|
|
@@ -60,6 +82,6 @@ export function nextActionForContinuity({ context, continuity } = {}) {
|
|
|
60
82
|
export async function evaluateContinuityNextAction({ target, packageRoot, context } = {}) {
|
|
61
83
|
return nextActionForContinuity({
|
|
62
84
|
context,
|
|
63
|
-
continuity: await reconcileContinuity({ target, packageRoot }),
|
|
85
|
+
continuity: await reconcileContinuity({ target, packageRoot, taskId: context?.taskId ?? null }),
|
|
64
86
|
});
|
|
65
87
|
}
|
|
@@ -6,6 +6,7 @@ export const NEXT_ACTIONS = Object.freeze({
|
|
|
6
6
|
CREATE_CONTRACT: "CREATE_CONTRACT",
|
|
7
7
|
ROUTE: "ROUTE",
|
|
8
8
|
SATISFY_GATES: "SATISFY_GATES",
|
|
9
|
+
REVALIDATE_GATES: "REVALIDATE_GATES",
|
|
9
10
|
RUN_PREFLIGHT: "RUN_PREFLIGHT",
|
|
10
11
|
PLAN: "PLAN",
|
|
11
12
|
START_EXECUTION: "START_EXECUTION",
|
|
@@ -47,6 +48,9 @@ export const NEXT_ACTIONS = Object.freeze({
|
|
|
47
48
|
RECOVER_TASK: "RECOVER_TASK",
|
|
48
49
|
RESUME_RECOVERED_TASK: "RESUME_RECOVERED_TASK",
|
|
49
50
|
RESOLVE_RECOVERY_INCONSISTENCY: "RESOLVE_RECOVERY_INCONSISTENCY",
|
|
51
|
+
REPAIR_CONTRACT_BOOTSTRAP: "REPAIR_CONTRACT_BOOTSTRAP",
|
|
52
|
+
MIGRATE_CONTRACT_BOOTSTRAP_REPAIR: "MIGRATE_CONTRACT_BOOTSTRAP_REPAIR",
|
|
53
|
+
REVALIDATE_CHECKPOINT: "REVALIDATE_CHECKPOINT",
|
|
50
54
|
NONE: "NONE",
|
|
51
55
|
});
|
|
52
56
|
|
|
@@ -60,6 +64,33 @@ export function directCommandSpec(commandId, taskId, requiredInputs = []) {
|
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
export function routeCommandSpec(taskId) {
|
|
68
|
+
return {
|
|
69
|
+
commandId: "route",
|
|
70
|
+
executable: "forgeloop",
|
|
71
|
+
subcommand: "route",
|
|
72
|
+
argv: ["route", `--task=${taskId}`, "--json"],
|
|
73
|
+
requiredInputs: [
|
|
74
|
+
{ name: "workType", option: "--work=<type>" },
|
|
75
|
+
{ name: "surface", option: "--surface=<value>", repeatable: true, optional: true },
|
|
76
|
+
{ name: "risk", option: "--risk=<value>", repeatable: true, optional: true },
|
|
77
|
+
{ name: "platform", option: "--platform=<value>", repeatable: true, optional: true },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function gateRevalidationCommandSpec(taskId, gate) {
|
|
83
|
+
return {
|
|
84
|
+
commandId: "gate-revalidate",
|
|
85
|
+
executable: "forgeloop",
|
|
86
|
+
subcommand: "gate-revalidate",
|
|
87
|
+
argv: ["gate-revalidate", `--task=${taskId}`, `--gate=${gate}`, "--acknowledge-stale", "--json"],
|
|
88
|
+
requiredInputs: [
|
|
89
|
+
{ name: "acknowledgeStale", option: "--acknowledge-stale" },
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
63
94
|
export function recoveryGuidanceForClassification(classification, taskId) {
|
|
64
95
|
if (classification === "RECOVERABLE") {
|
|
65
96
|
return {
|
|
@@ -167,8 +198,35 @@ export function result({
|
|
|
167
198
|
};
|
|
168
199
|
}
|
|
169
200
|
|
|
201
|
+
export function contractBootstrapRepairGuidance(taskId, reason = null) {
|
|
202
|
+
const command = `forgeloop task-repair-contract-bootstrap --task ${taskId} --acknowledge-repair --json`;
|
|
203
|
+
return {
|
|
204
|
+
nextAction: NEXT_ACTIONS.REPAIR_CONTRACT_BOOTSTRAP,
|
|
205
|
+
commands: [command],
|
|
206
|
+
commandSpecs: [directCommandSpec("task-repair-contract-bootstrap", taskId, [
|
|
207
|
+
{ name: "acknowledgeRepair", option: "--acknowledge-repair", description: "Explicit caller acknowledgement of the exact repair signature." },
|
|
208
|
+
])],
|
|
209
|
+
reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_AVAILABLE", message: "The exact duplicate contract bootstrap defect is repairable through the official append-only command." },
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function contractBootstrapRepairMigrationGuidance(taskId, reason = null) {
|
|
214
|
+
const command = `forgeloop task-migrate-contract-bootstrap-repair --task ${taskId} --acknowledge-migration --json`;
|
|
215
|
+
return {
|
|
216
|
+
nextAction: NEXT_ACTIONS.MIGRATE_CONTRACT_BOOTSTRAP_REPAIR,
|
|
217
|
+
commands: [command],
|
|
218
|
+
commandSpecs: [directCommandSpec("task-migrate-contract-bootstrap-repair", taskId, [
|
|
219
|
+
{ name: "acknowledgeMigration", option: "--acknowledge-migration", description: "Fresh explicit caller acknowledgement of the exact legacy marker migration." },
|
|
220
|
+
])],
|
|
221
|
+
reason: reason ?? { code: "E_CONTRACT_BOOTSTRAP_REPAIR_MIGRATION_AVAILABLE", message: "The exact legacy contract bootstrap repair marker can be migrated through the official append-only command." },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
170
225
|
export function commandFor(action) {
|
|
171
226
|
return {
|
|
227
|
+
[NEXT_ACTIONS.ROUTE]: "forgeloop route --task <id> --work <type> --json",
|
|
228
|
+
[NEXT_ACTIONS.RESOLVE_STALE_ROUTE]: "forgeloop route --task <id> --work <type> --json",
|
|
229
|
+
[NEXT_ACTIONS.REVALIDATE_GATES]: "forgeloop gate-revalidate --task <id> --gate <name> --acknowledge-stale --json",
|
|
172
230
|
[NEXT_ACTIONS.PLAN]: "forgeloop advance --to PLANNED",
|
|
173
231
|
[NEXT_ACTIONS.RUN_PREFLIGHT]: "forgeloop preflight --json",
|
|
174
232
|
[NEXT_ACTIONS.START_EXECUTION]: "forgeloop advance --to EXECUTING",
|
|
@@ -5,7 +5,7 @@ import { resolvePlannedPhase } from "./next-action-planned-phase.js";
|
|
|
5
5
|
import { resolvePendingActionGuidance } from "./next-action-pending-actions.js";
|
|
6
6
|
import { preExecutionRefreshGuidance } from "./next-action-refresh.js";
|
|
7
7
|
import { ARTIFACT_PATHS, readJsonArtifact } from "./artifacts.js";
|
|
8
|
-
import { taskArtifactPath } from "./task-paths.js";
|
|
8
|
+
import { taskArtifactPath, taskGatePath } from "./task-paths.js";
|
|
9
9
|
import { completionIdentityErrors, evaluateCompletion } from "./completion.js";
|
|
10
10
|
import { readContract } from "./contract.js";
|
|
11
11
|
import { evaluatePreflight, validatePersistedPreflight } from "./preflight.js";
|
|
@@ -16,10 +16,16 @@ import { classifyLoadedWorkState } from "./work-state.js";
|
|
|
16
16
|
|
|
17
17
|
import { evaluateStartExecutionPrerequisites } from "./execution-prerequisites.js";
|
|
18
18
|
|
|
19
|
-
import { NEXT_ACTIONS, commandFor, decision, recoveryGuidanceForClassification, result, uniqueSorted } from "./next-action-model.js";
|
|
19
|
+
import { NEXT_ACTIONS, commandFor, decision, gateRevalidationCommandSpec, recoveryGuidanceForClassification, result, routeCommandSpec, uniqueSorted } from "./next-action-model.js";
|
|
20
20
|
import { artifactError, freshnessReasons, loadArtifact, staleReasons } from "./next-action-artifacts.js";
|
|
21
21
|
|
|
22
22
|
import { inspectTaskConflictState } from "./task-conflict-inspection.js";
|
|
23
|
+
import { validateEventLedger } from "./events.js";
|
|
24
|
+
import {
|
|
25
|
+
isContractBootstrapRepairCandidate,
|
|
26
|
+
isLegacyContractBootstrapRepairMigrationCandidate,
|
|
27
|
+
} from "./contract-bootstrap-recovery.js";
|
|
28
|
+
import { contractBootstrapRepairGuidance, contractBootstrapRepairMigrationGuidance } from "./next-action-model.js";
|
|
23
29
|
|
|
24
30
|
import { evaluateContinuityNextAction } from "./next-action-continuity.js";
|
|
25
31
|
|
|
@@ -32,10 +38,85 @@ export const PHASES_REQUIRING_EXECUTION_CHRONOLOGY = new Set([
|
|
|
32
38
|
"COMPLETE",
|
|
33
39
|
]);
|
|
34
40
|
|
|
41
|
+
const PHASES_ALLOWING_GATE_REVALIDATION = new Set([
|
|
42
|
+
"EXECUTING", "VERIFYING", "DIAGNOSING", "CORRECTING", "REVIEWING",
|
|
43
|
+
]);
|
|
44
|
+
|
|
35
45
|
export function phaseRequiresExecutionChronology(phase) {
|
|
36
46
|
return PHASES_REQUIRING_EXECUTION_CHRONOLOGY.has(phase);
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
async function contractBootstrapRepairGuidanceForExplicitTask({ target, packageRoot, explicitTaskId, context, eventsRel, stateRel }) {
|
|
50
|
+
const ledger = await validateEventLedger(target, packageRoot, { taskId: explicitTaskId });
|
|
51
|
+
const candidate = isContractBootstrapRepairCandidate(ledger.events, ledger.errors, explicitTaskId);
|
|
52
|
+
if (candidate) {
|
|
53
|
+
const guidance = contractBootstrapRepairGuidance(explicitTaskId);
|
|
54
|
+
return result({
|
|
55
|
+
...context,
|
|
56
|
+
nextAction: guidance.nextAction,
|
|
57
|
+
commands: guidance.commands,
|
|
58
|
+
commandSpecs: guidance.commandSpecs,
|
|
59
|
+
reasons: [artifactError(guidance.reason.code, guidance.reason.message, [eventsRel, taskArtifactPath(explicitTaskId, "state")])],
|
|
60
|
+
requiredArtifacts: [eventsRel, taskArtifactPath(explicitTaskId, "contract")],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const tolerantLedger = await validateEventLedger(target, packageRoot, {
|
|
64
|
+
taskId: explicitTaskId,
|
|
65
|
+
allowUnmigratedLegacyContractBootstrapRepairMarkers: true,
|
|
66
|
+
});
|
|
67
|
+
if (!tolerantLedger.valid || !isLegacyContractBootstrapRepairMigrationCandidate(tolerantLedger.events, explicitTaskId)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const guidance = contractBootstrapRepairMigrationGuidance(explicitTaskId);
|
|
71
|
+
return result({
|
|
72
|
+
...context,
|
|
73
|
+
nextAction: guidance.nextAction,
|
|
74
|
+
commands: guidance.commands,
|
|
75
|
+
commandSpecs: guidance.commandSpecs,
|
|
76
|
+
reasons: [artifactError(guidance.reason.code, guidance.reason.message, [eventsRel, stateRel])],
|
|
77
|
+
requiredArtifacts: [eventsRel, taskArtifactPath(explicitTaskId, "contract"), stateRel],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function explicitTaskConflictGuidance({ target, packageRoot, explicitTaskId, context, eventsRel, stateRel, state }) {
|
|
82
|
+
try {
|
|
83
|
+
const migrationGuidance = await contractBootstrapRepairGuidanceForExplicitTask({
|
|
84
|
+
target, packageRoot, explicitTaskId, context, eventsRel, stateRel,
|
|
85
|
+
});
|
|
86
|
+
if (migrationGuidance) return migrationGuidance;
|
|
87
|
+
} catch {
|
|
88
|
+
// The regular conflict inspection below remains the fail-closed path.
|
|
89
|
+
}
|
|
90
|
+
let inspection;
|
|
91
|
+
try {
|
|
92
|
+
inspection = await inspectTaskConflictState(target, {
|
|
93
|
+
taskId: explicitTaskId,
|
|
94
|
+
packageRoot,
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
inspection = {
|
|
98
|
+
classification: "INCONSISTENT",
|
|
99
|
+
reasonCodes: [error.code ?? "E_TASK_RECOVERY_INCONSISTENT"],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (["ACTIVE", "COMPLETE"].includes(inspection.classification)) return null;
|
|
103
|
+
if (PHASES_ALLOWING_GATE_REVALIDATION.has(state?.phase)
|
|
104
|
+
&& inspection.reasonCodes?.includes("E_GATE_STALE")) return null;
|
|
105
|
+
const guidance = recoveryGuidanceForClassification(inspection.classification, explicitTaskId);
|
|
106
|
+
return result({
|
|
107
|
+
...context,
|
|
108
|
+
nextAction: guidance.nextAction,
|
|
109
|
+
commands: guidance.commands,
|
|
110
|
+
commandSpecs: guidance.commandSpecs,
|
|
111
|
+
reasons: inspection.reasonCodes.map((code) => artifactError(
|
|
112
|
+
code,
|
|
113
|
+
`Task conflict state is ${inspection.classification}; follow the structured recovery guidance.`,
|
|
114
|
+
[stateRel, eventsRel, taskArtifactPath(explicitTaskId, "recovery")],
|
|
115
|
+
)),
|
|
116
|
+
requiredArtifacts: [stateRel, eventsRel],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
39
120
|
export async function resolveNextActionPhase({
|
|
40
121
|
target,
|
|
41
122
|
packageRoot,
|
|
@@ -55,33 +136,10 @@ export async function resolveNextActionPhase({
|
|
|
55
136
|
} = {}) {
|
|
56
137
|
const { policyRecoveryAction } = helpers;
|
|
57
138
|
if (explicitTaskId) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
packageRoot,
|
|
63
|
-
});
|
|
64
|
-
} catch (error) {
|
|
65
|
-
inspection = {
|
|
66
|
-
classification: "INCONSISTENT",
|
|
67
|
-
reasonCodes: [error.code ?? "E_TASK_RECOVERY_INCONSISTENT"],
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
if (!["ACTIVE", "COMPLETE"].includes(inspection.classification)) {
|
|
71
|
-
const guidance = recoveryGuidanceForClassification(inspection.classification, explicitTaskId);
|
|
72
|
-
return result({
|
|
73
|
-
...context,
|
|
74
|
-
nextAction: guidance.nextAction,
|
|
75
|
-
commands: guidance.commands,
|
|
76
|
-
commandSpecs: guidance.commandSpecs,
|
|
77
|
-
reasons: inspection.reasonCodes.map((code) => artifactError(
|
|
78
|
-
code,
|
|
79
|
-
`Task conflict state is ${inspection.classification}; follow the structured recovery guidance.`,
|
|
80
|
-
[stateRel, eventsRel, taskArtifactPath(explicitTaskId, "recovery")],
|
|
81
|
-
)),
|
|
82
|
-
requiredArtifacts: [stateRel, eventsRel],
|
|
83
|
-
});
|
|
84
|
-
}
|
|
139
|
+
const taskConflictGuidance = await explicitTaskConflictGuidance({
|
|
140
|
+
target, packageRoot, explicitTaskId, context, eventsRel, stateRel, state,
|
|
141
|
+
});
|
|
142
|
+
if (taskConflictGuidance) return taskConflictGuidance;
|
|
85
143
|
}
|
|
86
144
|
const actionGuidance = await resolvePendingActionGuidance({ target, packageRoot, state, context, eventsRel, authorityContext, runtimeContext, helpers });
|
|
87
145
|
if (actionGuidance) return actionGuidance;
|
|
@@ -137,11 +195,11 @@ export async function resolveNextActionPhase({
|
|
|
137
195
|
contractFile: contractRel,
|
|
138
196
|
});
|
|
139
197
|
if (freshness.status === "REVALIDATION_REQUIRED") {
|
|
140
|
-
const refreshGuidance = await preExecutionRefreshGuidance({ target, packageRoot, state, contract });
|
|
198
|
+
const refreshGuidance = await preExecutionRefreshGuidance({ target, packageRoot, state, contract, freshness });
|
|
141
199
|
return result({
|
|
142
200
|
...context,
|
|
143
201
|
...refreshGuidance,
|
|
144
|
-
nextAction:
|
|
202
|
+
nextAction: refreshGuidance.nextAction,
|
|
145
203
|
reasons: freshnessReasons(state, freshness),
|
|
146
204
|
requiredArtifacts: uniqueSorted([
|
|
147
205
|
stateRel,
|
|
@@ -151,13 +209,15 @@ export async function resolveNextActionPhase({
|
|
|
151
209
|
});
|
|
152
210
|
}
|
|
153
211
|
if (state.phase === "CONTRACT_READY") {
|
|
154
|
-
return
|
|
155
|
-
context,
|
|
156
|
-
NEXT_ACTIONS.ROUTE,
|
|
157
|
-
artifactError("PHASE_CONTRACT_READY", "Persist deterministic routing for the validated contract"),
|
|
158
|
-
|
|
159
|
-
[
|
|
160
|
-
|
|
212
|
+
return result({
|
|
213
|
+
...context,
|
|
214
|
+
nextAction: NEXT_ACTIONS.ROUTE,
|
|
215
|
+
reasons: [artifactError("PHASE_CONTRACT_READY", "Persist deterministic routing for the validated contract")],
|
|
216
|
+
commands: [`forgeloop route --task ${explicitTaskId} --work <type> --json`],
|
|
217
|
+
commandSpecs: [routeCommandSpec(explicitTaskId)],
|
|
218
|
+
requiredArtifacts: contractArtifacts,
|
|
219
|
+
missingArtifacts: [routeRel],
|
|
220
|
+
});
|
|
161
221
|
}
|
|
162
222
|
const routeResult = await loadArtifact(
|
|
163
223
|
() => readPersistedRoute(target, packageRoot, { taskId: explicitTaskId }),
|
|
@@ -187,6 +247,8 @@ export async function resolveNextActionPhase({
|
|
|
187
247
|
...context,
|
|
188
248
|
nextAction: NEXT_ACTIONS.RESOLVE_STALE_ROUTE,
|
|
189
249
|
reasons: stale,
|
|
250
|
+
commands: [`forgeloop route --task ${explicitTaskId} --work <type> --json`],
|
|
251
|
+
commandSpecs: [routeCommandSpec(explicitTaskId)],
|
|
190
252
|
requiredArtifacts,
|
|
191
253
|
});
|
|
192
254
|
}
|
|
@@ -214,6 +276,20 @@ export async function resolveNextActionPhase({
|
|
|
214
276
|
],
|
|
215
277
|
});
|
|
216
278
|
}
|
|
279
|
+
const staleGateErrors = PHASES_ALLOWING_GATE_REVALIDATION.has(state.phase)
|
|
280
|
+
? (executionPrerequisites.preflight?.errors ?? []).filter((error) => error.code === "E_GATE_STALE")
|
|
281
|
+
: [];
|
|
282
|
+
if (staleGateErrors.length > 0) {
|
|
283
|
+
const gates = [...new Set(staleGateErrors.map((error) => error.gate).filter(Boolean))].sort();
|
|
284
|
+
return result({
|
|
285
|
+
...context,
|
|
286
|
+
nextAction: NEXT_ACTIONS.REVALIDATE_GATES,
|
|
287
|
+
reasons: staleGateErrors,
|
|
288
|
+
commands: gates.map((gate) => `forgeloop gate-revalidate --task ${explicitTaskId} --gate ${gate} --acknowledge-stale --json`),
|
|
289
|
+
commandSpecs: gates.map((gate) => gateRevalidationCommandSpec(explicitTaskId, gate)),
|
|
290
|
+
requiredArtifacts: executionPrerequisites.requiredArtifacts,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
217
293
|
if (executionPrerequisites.errors.length > 0) {
|
|
218
294
|
return result({
|
|
219
295
|
...context,
|
|
@@ -235,19 +311,31 @@ export async function resolveNextActionPhase({
|
|
|
235
311
|
const preflightArtifacts = [...requiredArtifacts, preflightRel];
|
|
236
312
|
const persistedPreflightErrors = phaseNeedsChronology
|
|
237
313
|
? []
|
|
238
|
-
: validatePersistedPreflight(preflightArtifact.value?.value, preflight);
|
|
314
|
+
: validatePersistedPreflight(preflightArtifact.value?.value, preflight, preflightArtifact.error);
|
|
239
315
|
|
|
240
316
|
if (["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase) && missingGates.length > 0) {
|
|
317
|
+
const missingGatePaths = missingGates.map((gate) => taskGatePath(state.taskId, gate));
|
|
318
|
+
const gateRecordSpec = {
|
|
319
|
+
commandId: "gate-record",
|
|
320
|
+
executable: "forgeloop",
|
|
321
|
+
subcommand: "gate-record",
|
|
322
|
+
argv: ["gate-record", `--task=${state.taskId}`, `--gate=${missingGates[0]}`, "--status=satisfied", "--json"],
|
|
323
|
+
requiredInputs: [
|
|
324
|
+
{ name: "artifact", option: "--artifact=<project-relative-path>", repeatable: true },
|
|
325
|
+
{ name: "decision", option: "--decision=<text>", repeatable: true },
|
|
326
|
+
],
|
|
327
|
+
};
|
|
241
328
|
return result({
|
|
242
329
|
...context,
|
|
243
330
|
nextAction: NEXT_ACTIONS.SATISFY_GATES,
|
|
244
|
-
reasons: missingGates.map((gate) => artifactError(
|
|
331
|
+
reasons: missingGates.map((gate, index) => artifactError(
|
|
245
332
|
"E_GATE_UNVERIFIED",
|
|
246
333
|
`Required gate is missing or unverified: ${gate}`,
|
|
247
|
-
[
|
|
334
|
+
[missingGatePaths[index]],
|
|
248
335
|
)),
|
|
249
|
-
requiredArtifacts: [...preflightArtifacts, ...
|
|
250
|
-
missingArtifacts:
|
|
336
|
+
requiredArtifacts: [...preflightArtifacts, ...missingGatePaths],
|
|
337
|
+
missingArtifacts: missingGatePaths,
|
|
338
|
+
commandSpecs: [gateRecordSpec],
|
|
251
339
|
});
|
|
252
340
|
}
|
|
253
341
|
if (state.phase === "ROUTED") {
|
|
@@ -1,19 +1,53 @@
|
|
|
1
|
-
import { validateEventLedger } from "./events.js";
|
|
1
|
+
import { validateEventLedger, validateStateLedgerCoherence } from "./events.js";
|
|
2
2
|
import { readPersistedRoute } from "./route-artifact.js";
|
|
3
|
-
import { directCommandSpec } from "./next-action-model.js";
|
|
3
|
+
import { directCommandSpec, NEXT_ACTIONS } from "./next-action-model.js";
|
|
4
|
+
import { resolveTaskClaimState } from "./task-claim-state.js";
|
|
5
|
+
import { sameCanonicalGuideList } from "./contract-bootstrap-recovery.js";
|
|
6
|
+
import { CHECKPOINT_REVALIDATION_PHASES } from "./checkpoint-revalidation.js";
|
|
4
7
|
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Select a safe pre-execution refresh mutation. Repository-only drift keeps
|
|
10
|
+
* the checkpoint and its route identity; the legacy clear-state path remains
|
|
11
|
+
* limited to a documented blocked-preflight recovery.
|
|
12
|
+
*/
|
|
13
|
+
export async function preExecutionRefreshGuidance({ target, packageRoot, state, contract, freshness }) {
|
|
14
|
+
if (!["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase)) return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
|
|
8
15
|
const ledger = await validateEventLedger(target, packageRoot, { taskId: state.taskId });
|
|
9
|
-
if (!ledger.valid || ledger.events.some((entry) => entry.event === "EXECUTION_STARTED"))
|
|
16
|
+
if (!ledger.valid || ledger.events.some((entry) => entry.event === "EXECUTION_STARTED")) {
|
|
17
|
+
return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
|
|
18
|
+
}
|
|
19
|
+
if (CHECKPOINT_REVALIDATION_PHASES.includes(state.phase)
|
|
20
|
+
&& freshness?.status === "REVALIDATION_REQUIRED"
|
|
21
|
+
&& [...new Set(freshness.reasons ?? [])].sort().join(",") === "REPOSITORY_CHANGED"
|
|
22
|
+
&& state.contractFingerprint === contract.fingerprint) {
|
|
23
|
+
try {
|
|
24
|
+
const route = await readPersistedRoute(target, packageRoot, { taskId: state.taskId });
|
|
25
|
+
const ownership = await resolveTaskClaimState(target, { taskId: state.taskId, packageRoot });
|
|
26
|
+
const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
|
|
27
|
+
const repositoryAvailable = typeof freshness.repository?.head === "string" && freshness.repository.head.length > 0;
|
|
28
|
+
const routeIdentityValid = route.value?.contractFingerprint === contract.fingerprint
|
|
29
|
+
&& state.routeFingerprint === route.fingerprint
|
|
30
|
+
&& sameCanonicalGuideList(state.selectedGuides, route.value.guides);
|
|
31
|
+
if (ownership.ownershipValid && ownership.mutationAllowed && repositoryAvailable
|
|
32
|
+
&& coherenceErrors.length === 0 && routeIdentityValid) {
|
|
33
|
+
return {
|
|
34
|
+
nextAction: NEXT_ACTIONS.REVALIDATE_CHECKPOINT,
|
|
35
|
+
commands: [`forgeloop checkpoint-revalidate --task ${state.taskId} --json`],
|
|
36
|
+
commandSpecs: [directCommandSpec("checkpoint-revalidate", state.taskId)],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// Unsupported or ambiguous identity stays on the fail-closed blocker.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
10
43
|
const lastPreflight = ledger.events.filter((entry) => ["PREFLIGHT_READY", "PREFLIGHT_BLOCKED"].includes(entry.event)).at(-1);
|
|
11
|
-
if (lastPreflight?.event !== "PREFLIGHT_BLOCKED") return {};
|
|
44
|
+
if (lastPreflight?.event !== "PREFLIGHT_BLOCKED") return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
|
|
12
45
|
let route;
|
|
13
46
|
try { route = await readPersistedRoute(target, packageRoot, { taskId: state.taskId }); }
|
|
14
|
-
catch { return {}; }
|
|
15
|
-
if (route.value.contractFingerprint !== contract.fingerprint) return {};
|
|
47
|
+
catch { return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER }; }
|
|
48
|
+
if (route.value.contractFingerprint !== contract.fingerprint) return { nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER };
|
|
16
49
|
return {
|
|
50
|
+
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
17
51
|
commands: [`forgeloop clear-state --task ${state.taskId}`, `forgeloop preflight --task ${state.taskId}`],
|
|
18
52
|
commandSpecs: [directCommandSpec("clear-state", state.taskId), directCommandSpec("preflight", state.taskId)],
|
|
19
53
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readJsonArtifact } from "./artifacts.js";
|
|
2
|
-
import { evaluateCompletion } from "./completion.js";
|
|
2
|
+
import { evaluateCompletion, receiptReadFailure } from "./completion.js";
|
|
3
3
|
import { validateReceipt } from "./receipt.js";
|
|
4
4
|
import { completionRelationshipErrors } from "./completion-relationships.js";
|
|
5
|
-
import { evaluateRequiredEvidence, classifyRequirement } from "./evidence-readiness.js";
|
|
5
|
+
import { evaluateRequiredEvidence, classifyRequirement, terminalRequirementsForContract } from "./evidence-readiness.js";
|
|
6
6
|
import { validateEventLedger, validateCompletionRecoveryAuthorization } from "./events.js";
|
|
7
7
|
import { NEXT_ACTIONS, commandFor, decision, recordTerminalResultCommandSpec, result } from "./next-action-model.js";
|
|
8
8
|
import { artifactError, checkListReasons, loadArtifact, requirementsAndCoverage } from "./next-action-artifacts.js";
|
|
@@ -82,7 +82,7 @@ export async function resolveReviewingPhase({ state, context, requiredArtifacts,
|
|
|
82
82
|
return decision(
|
|
83
83
|
context,
|
|
84
84
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
85
|
-
|
|
85
|
+
receiptReadFailure(receipt.error, receiptRel),
|
|
86
86
|
[...requiredArtifacts, receiptRel],
|
|
87
87
|
);
|
|
88
88
|
}
|
|
@@ -153,9 +153,14 @@ export async function resolveReviewingPhase({ state, context, requiredArtifacts,
|
|
|
153
153
|
err.code === "E_PUBLICATION_REQUIREMENT_PENDING" || err.code === "E_PRODUCTION_REQUIREMENT_PENDING"
|
|
154
154
|
));
|
|
155
155
|
if (terminalPendingErrors.length > 0 && terminalPendingErrors.length === completion.errors.length) {
|
|
156
|
+
const canonicalTerminalRequirements = terminalRequirementsForContract(contract.value, {
|
|
157
|
+
additionalEvidence: preflight.policy?.requiredEvidence ?? [],
|
|
158
|
+
});
|
|
156
159
|
const terminalPendingReqs = terminalPendingErrors.map((err) => {
|
|
157
160
|
const reqId = err.requirementId;
|
|
158
|
-
const matchingReq =
|
|
161
|
+
const matchingReq = canonicalTerminalRequirements.find((r) => r.id === reqId)
|
|
162
|
+
?? canonicalTerminalRequirements.find((r) => r.text === reqId)
|
|
163
|
+
?? evidence.requirements.find((r) => r.id === reqId)
|
|
159
164
|
?? evidence.requirements.find((r) => r.text === reqId)
|
|
160
165
|
?? classifyRequirement(reqId ?? err.message);
|
|
161
166
|
return matchingReq;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { receiptReadFailure } from "./completion.js";
|
|
1
2
|
import { readJsonArtifact } from "./artifacts.js";
|
|
2
3
|
import { taskArtifactPath, taskStructuralQualityDirectory } from "./task-paths.js";
|
|
3
4
|
import { validateReceipt } from "./receipt.js";
|
|
@@ -100,7 +101,7 @@ export async function resolveVerifyingPhase({ target, packageRoot, explicitTaskI
|
|
|
100
101
|
return decision(
|
|
101
102
|
context,
|
|
102
103
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
103
|
-
|
|
104
|
+
receiptReadFailure(receipt.error, receiptRel),
|
|
104
105
|
[...requiredArtifacts, receiptRel],
|
|
105
106
|
);
|
|
106
107
|
}
|
package/src/core/next-action.js
CHANGED
|
@@ -15,6 +15,9 @@ import { resolveNextActionPhase } from "./next-action-phases.js";
|
|
|
15
15
|
import { criterionForDecision } from "./settlement-model.js";
|
|
16
16
|
import { validateApprovalForAction } from "./approvals.js";
|
|
17
17
|
import { evaluateActionCapability } from "./capability-policy.js";
|
|
18
|
+
import { resolveTaskContext, TASK_SELECTION_MODES } from "./task-context.js";
|
|
19
|
+
import { getOperationalStore } from "../storage/operational-context.js";
|
|
20
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
18
21
|
|
|
19
22
|
export { NEXT_ACTIONS } from "./next-action-model.js";
|
|
20
23
|
|
|
@@ -287,13 +290,41 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
287
290
|
});
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
293
|
+
const events = await readEvents(target, packageRoot, { taskId: explicitTaskId });
|
|
294
|
+
if (events.some((event) => event.event === "DISCOVERY_STARTED")) {
|
|
295
|
+
return result({
|
|
296
|
+
taskId: explicitTaskId ?? "unknown",
|
|
297
|
+
currentPhase: "DISCOVERING",
|
|
298
|
+
nextAction: NEXT_ACTIONS.CREATE_CONTRACT,
|
|
299
|
+
reasons: [artifactError("PHASE_DISCOVERING", "Create and validate the task contract", [contractRel])],
|
|
300
|
+
commands: [`forgeloop contract-create --task ${explicitTaskId} --preset <documentation|bug|feature|release> --json`],
|
|
301
|
+
commandSpecs: [{
|
|
302
|
+
commandId: "contract-create",
|
|
303
|
+
executable: "forgeloop",
|
|
304
|
+
subcommand: "contract-create",
|
|
305
|
+
argv: ["contract-create", `--task=${explicitTaskId}`, "--json"],
|
|
306
|
+
requiredInputs: [{ name: "preset", option: "--preset=<documentation|bug|feature|release>" }],
|
|
307
|
+
}],
|
|
308
|
+
requiredArtifacts: [contractRel],
|
|
309
|
+
missingArtifacts: [contractRel, stateRel],
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return result({
|
|
313
|
+
taskId: explicitTaskId ?? "unknown",
|
|
314
|
+
currentPhase: "RECEIVED",
|
|
315
|
+
nextAction: NEXT_ACTIONS.DISCOVER,
|
|
316
|
+
reasons: [artifactError("WORK_STATE_ABSENT", "No work-state checkpoint is present", [stateRel])],
|
|
317
|
+
commands: [`forgeloop discover --task ${explicitTaskId} --json`],
|
|
318
|
+
commandSpecs: [{
|
|
319
|
+
commandId: "discover",
|
|
320
|
+
executable: "forgeloop",
|
|
321
|
+
subcommand: "discover",
|
|
322
|
+
argv: ["discover", `--task=${explicitTaskId}`, "--json"],
|
|
323
|
+
requiredInputs: [],
|
|
324
|
+
}],
|
|
325
|
+
requiredArtifacts: [stateRel],
|
|
326
|
+
missingArtifacts: [stateRel],
|
|
327
|
+
});
|
|
297
328
|
}
|
|
298
329
|
|
|
299
330
|
return resolveNextActionPhase({
|
|
@@ -327,9 +358,25 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
327
358
|
}
|
|
328
359
|
|
|
329
360
|
export async function getNextAction(targetOrOptions = {}, packageRootOption) {
|
|
330
|
-
const
|
|
361
|
+
const target = typeof targetOrOptions === "string" ? targetOrOptions : targetOrOptions?.target;
|
|
362
|
+
const read = () => getSelectedNextAction(targetOrOptions, packageRootOption);
|
|
363
|
+
return target ? withProjectReadSnapshot(target, read) : read();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function getSelectedNextAction(targetOrOptions, packageRootOption) {
|
|
367
|
+
let normalized = typeof targetOrOptions === "string"
|
|
368
|
+
? { target: targetOrOptions, packageRoot: packageRootOption }
|
|
369
|
+
: { ...targetOrOptions };
|
|
370
|
+
if (getOperationalStore(normalized.target)) {
|
|
371
|
+
const context = await resolveTaskContext(normalized.target, {
|
|
372
|
+
taskId: normalized.taskId,
|
|
373
|
+
packageRoot: normalized.packageRoot,
|
|
374
|
+
selectionMode: TASK_SELECTION_MODES.READ,
|
|
375
|
+
});
|
|
376
|
+
if (context) normalized = { ...normalized, taskId: context.taskId };
|
|
377
|
+
}
|
|
378
|
+
const res = await computeNextAction(normalized);
|
|
331
379
|
if (res && res.reasons && res.reasons.some((r) => r.code === "E_CONTRACT_UNRESOLVED_DECISION" || r.code === "E_UNRESOLVED_DECISION")) {
|
|
332
|
-
const normalized = typeof targetOrOptions === "string" ? { target: targetOrOptions, packageRoot: packageRootOption } : targetOrOptions;
|
|
333
380
|
const { target, packageRoot, taskId } = normalized ?? {};
|
|
334
381
|
try {
|
|
335
382
|
const explicitTaskId = taskId ?? null;
|