@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,12 +1,17 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { setImmediate as nextTurn } from "node:timers/promises";
|
|
2
3
|
import { ensureWithin, fileExists } from "./filesystem.js";
|
|
3
4
|
import { getPackageRoot } from "./templates.js";
|
|
4
5
|
import { TASK_STATE_ROOT, TASK_ARTIFACT_FILES, taskArtifactPath } from "./task-paths.js";
|
|
5
6
|
import { readTaskDescriptor } from "./task-descriptor.js";
|
|
6
|
-
import { readJsonArtifact } from "./artifacts.js";
|
|
7
|
+
import { readJsonArtifact, readPortableJsonArtifact } from "./artifacts.js";
|
|
7
8
|
import { readLockInfo } from "./task-lock.js";
|
|
8
9
|
import { taskStorageKey } from "./task-identity.js";
|
|
10
|
+
import { withEventLedgerAudit } from "./events.js";
|
|
9
11
|
import { resolveTaskClaimState } from "./task-claim-state.js";
|
|
12
|
+
import { getOperationalStore, operationalArtifactExists } from "../storage/operational-context.js";
|
|
13
|
+
import { withNativeReadScope } from "./native-storage.js";
|
|
14
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Explicitly recognized legacy-incidental artifacts that may legitimately
|
|
@@ -55,124 +60,313 @@ async function classifyDescriptorlessTaskDirectory(target, taskKey) {
|
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
async function readDiscoveryState(target, taskId, descriptor, packageRoot) {
|
|
64
|
+
// Check work-state if available
|
|
65
|
+
let state = null;
|
|
66
|
+
let phase = null;
|
|
67
|
+
let lastUpdated = descriptor.updatedAt ?? descriptor.createdAt;
|
|
68
|
+
try {
|
|
69
|
+
const reader = getOperationalStore(target) ? readJsonArtifact : readPortableJsonArtifact;
|
|
70
|
+
const stateArtifact = await reader(
|
|
71
|
+
target,
|
|
72
|
+
taskArtifactPath(taskId, "state"),
|
|
73
|
+
"work-state",
|
|
74
|
+
packageRoot,
|
|
75
|
+
);
|
|
76
|
+
state = stateArtifact.value;
|
|
77
|
+
phase = state.phase ?? null;
|
|
78
|
+
if (state.lastUpdated) {
|
|
79
|
+
lastUpdated = state.lastUpdated;
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// State might not exist yet
|
|
83
|
+
}
|
|
84
|
+
return { state, phase, lastUpdated };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function discoveryArtifactExists(target, taskId, kind, filename) {
|
|
88
|
+
return operationalArtifactExists(target, taskArtifactPath(taskId, kind)) ?? await fileExists(filename);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function discoverTasks(target, packageRoot = getPackageRoot(), { taskKeys = null } = {}) {
|
|
92
|
+
return discoverTasksInScope(target, packageRoot, taskKeys, DISCOVERY_PROJECTIONS.FULL);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Internal integration projection. Discovery still validates every task and
|
|
97
|
+
* records every snapshot observation, but releases the full task graph after
|
|
98
|
+
* retaining only the public project/tasks fields and its canonical sort key.
|
|
99
|
+
*/
|
|
100
|
+
export async function discoverTaskSummaries(target, packageRoot = getPackageRoot(), { taskKeys = null } = {}) {
|
|
101
|
+
return discoverTasksInScope(target, packageRoot, taskKeys, DISCOVERY_PROJECTIONS.SUMMARY);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Internal task-list projection. It runs the same per-task validation and
|
|
106
|
+
* snapshot/CAS observation as full discovery while retaining only the fields
|
|
107
|
+
* consumed by the task-list command.
|
|
108
|
+
*/
|
|
109
|
+
export async function discoverTaskListEntries(target, packageRoot = getPackageRoot(), { taskKeys = null } = {}) {
|
|
110
|
+
return discoverTasksInScope(target, packageRoot, taskKeys, DISCOVERY_PROJECTIONS.TASK_LIST);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Retain a finite page only after every task has passed normal discovery. */
|
|
114
|
+
export async function discoverTaskListPage(target, packageRoot = getPackageRoot(), { phase, active, offset, limit }) {
|
|
115
|
+
const retained = [];
|
|
116
|
+
let total = 0;
|
|
117
|
+
const capacity = limit === null ? Infinity : limit === 0 ? 0 : offset + limit;
|
|
118
|
+
const collect = (task, fallbackSortKey) => {
|
|
119
|
+
if ((phase && task.phase !== phase) || (active && (task.claimState !== "ACTIVE" || task.mutationAllowed !== true))) return;
|
|
120
|
+
total += 1;
|
|
121
|
+
if (capacity === 0) return;
|
|
122
|
+
const entry = { sortKey: task.taskId ?? task.taskKey ?? fallbackSortKey, value: task };
|
|
123
|
+
if (capacity === Infinity) { retained.push(entry); return; }
|
|
124
|
+
let low = 0, high = retained.length;
|
|
125
|
+
while (low < high) {
|
|
126
|
+
const middle = Math.floor((low + high) / 2);
|
|
127
|
+
if (retained[middle].sortKey.localeCompare(entry.sortKey) <= 0) low = middle + 1;
|
|
128
|
+
else high = middle;
|
|
129
|
+
}
|
|
130
|
+
if (low >= capacity) return;
|
|
131
|
+
retained.splice(low, 0, entry);
|
|
132
|
+
if (retained.length > capacity) retained.pop();
|
|
133
|
+
};
|
|
134
|
+
await withProjectReadSnapshot(target,
|
|
135
|
+
() => discoverSelectedTasks(target, packageRoot, null, DISCOVERY_PROJECTIONS.TASK_LIST, collect));
|
|
136
|
+
if (capacity === Infinity) retained.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
137
|
+
return { tasks: retained.slice(offset).map(entry => entry.value), total };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const DISCOVERY_PROJECTIONS = Object.freeze({
|
|
141
|
+
FULL: "full",
|
|
142
|
+
SUMMARY: "summary",
|
|
143
|
+
TASK_LIST: "task-list",
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
async function discoverTasksInScope(target, packageRoot, taskKeys, projection) {
|
|
147
|
+
return withProjectReadSnapshot(target,
|
|
148
|
+
() => discoverSelectedTasks(target, packageRoot, taskKeys, projection));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function projectTaskList(task) {
|
|
152
|
+
if (task.healthy === false) {
|
|
153
|
+
return {
|
|
154
|
+
taskId: task.taskId ?? null,
|
|
155
|
+
taskKey: task.taskKey,
|
|
156
|
+
directory: task.directory,
|
|
157
|
+
healthy: false,
|
|
158
|
+
error: task.error,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
taskId: task.taskId,
|
|
163
|
+
taskKey: task.taskKey,
|
|
164
|
+
directory: task.directory,
|
|
165
|
+
healthy: true,
|
|
166
|
+
phase: task.phase,
|
|
167
|
+
writeClaims: task.writeClaims ?? [],
|
|
168
|
+
historicalWriteClaims: task.historicalWriteClaims ?? [],
|
|
169
|
+
effectiveWriteClaims: task.effectiveWriteClaims ?? [],
|
|
170
|
+
claimState: task.claimState,
|
|
171
|
+
recovery: task.recovery,
|
|
172
|
+
mutationAllowed: task.mutationAllowed,
|
|
173
|
+
ownershipValid: task.ownershipValid,
|
|
174
|
+
ownershipErrors: task.ownershipErrors ?? task.errors ?? [],
|
|
175
|
+
reasonCodes: task.reasonCodes ?? [],
|
|
176
|
+
locked: task.locked,
|
|
177
|
+
hasContinuity: task.hasContinuity,
|
|
178
|
+
hasReceipt: task.hasReceipt,
|
|
179
|
+
createdAt: task.createdAt,
|
|
180
|
+
updatedAt: task.updatedAt,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function projectTask(task, projection) {
|
|
185
|
+
if (projection === DISCOVERY_PROJECTIONS.FULL) return task;
|
|
186
|
+
if (projection === DISCOVERY_PROJECTIONS.SUMMARY) return projectTaskSummary(task);
|
|
187
|
+
return projectTaskList(task);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function discoverTaskEntry(target, packageRoot, entry, projection) {
|
|
191
|
+
const descriptorArtifact = await readTaskDescriptor(target, entry.name, packageRoot);
|
|
192
|
+
const descriptor = descriptorArtifact.value;
|
|
193
|
+
const taskId = descriptor.taskId;
|
|
194
|
+
|
|
195
|
+
// P1-1: Verify descriptor taskKey matches the actual directory name
|
|
196
|
+
if (descriptor.taskKey !== entry.name) {
|
|
197
|
+
return projectTask({
|
|
198
|
+
taskId: descriptor.taskId ?? null,
|
|
199
|
+
taskKey: entry.name,
|
|
200
|
+
directory: `${TASK_STATE_ROOT}/${entry.name}`,
|
|
201
|
+
healthy: false,
|
|
202
|
+
error: {
|
|
203
|
+
code: "E_TASK_KEY_MISMATCH",
|
|
204
|
+
message: `Task directory key "${entry.name}" does not match descriptor taskKey "${descriptor.taskKey}"`,
|
|
205
|
+
},
|
|
206
|
+
}, projection);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const { state, phase, lastUpdated } = await readDiscoveryState(target, taskId, descriptor, packageRoot);
|
|
210
|
+
|
|
211
|
+
// Check lock status
|
|
212
|
+
const lockInfo = await readLockInfo(target, taskId);
|
|
213
|
+
|
|
214
|
+
// Check continuity & receipt presence
|
|
215
|
+
const continuityPath = ensureWithin(target, taskArtifactPath(taskId, "continuity"));
|
|
216
|
+
const hasContinuity = await discoveryArtifactExists(target, taskId, "continuity", continuityPath);
|
|
217
|
+
|
|
218
|
+
const receiptPath = ensureWithin(target, taskArtifactPath(taskId, "receipt"));
|
|
219
|
+
const hasReceipt = await discoveryArtifactExists(target, taskId, "receipt", receiptPath);
|
|
220
|
+
|
|
221
|
+
const claimProjection = await resolveTaskClaimState(target, {
|
|
222
|
+
taskId,
|
|
223
|
+
packageRoot,
|
|
224
|
+
descriptor,
|
|
225
|
+
state,
|
|
226
|
+
});
|
|
227
|
+
const recovery = claimProjection.recovery;
|
|
228
|
+
const directory = `${TASK_STATE_ROOT}/${descriptor.taskKey}`;
|
|
229
|
+
const locked = lockInfo !== null;
|
|
230
|
+
|
|
231
|
+
if (projection === DISCOVERY_PROJECTIONS.SUMMARY) {
|
|
232
|
+
return {
|
|
233
|
+
taskId,
|
|
234
|
+
healthy: true,
|
|
235
|
+
phase,
|
|
236
|
+
mutationAllowed: claimProjection.mutationAllowed !== false,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (projection === DISCOVERY_PROJECTIONS.TASK_LIST) {
|
|
241
|
+
return projectTaskList({
|
|
242
|
+
taskId,
|
|
243
|
+
taskKey: descriptor.taskKey,
|
|
244
|
+
directory,
|
|
245
|
+
healthy: true,
|
|
246
|
+
phase,
|
|
247
|
+
writeClaims: claimProjection.writeClaims,
|
|
248
|
+
historicalWriteClaims: claimProjection.historicalWriteClaims,
|
|
249
|
+
effectiveWriteClaims: claimProjection.effectiveWriteClaims,
|
|
250
|
+
claimState: claimProjection.claimState,
|
|
251
|
+
recovery,
|
|
252
|
+
mutationAllowed: claimProjection.mutationAllowed,
|
|
253
|
+
ownershipValid: claimProjection.valid,
|
|
254
|
+
ownershipErrors: claimProjection.ownershipErrors,
|
|
255
|
+
reasonCodes: claimProjection.reasonCodes,
|
|
256
|
+
locked,
|
|
257
|
+
hasContinuity,
|
|
258
|
+
hasReceipt,
|
|
259
|
+
createdAt: descriptor.createdAt,
|
|
260
|
+
updatedAt: descriptor.updatedAt,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
taskId,
|
|
266
|
+
taskKey: descriptor.taskKey,
|
|
267
|
+
healthy: true,
|
|
268
|
+
phase,
|
|
269
|
+
locked,
|
|
270
|
+
lockInfo,
|
|
271
|
+
...claimProjection,
|
|
272
|
+
ownershipValid: claimProjection.valid,
|
|
273
|
+
recovery,
|
|
274
|
+
operatorRecoveredAt: recovery?.recoveredAt ?? null,
|
|
275
|
+
createdAt: descriptor.createdAt,
|
|
276
|
+
updatedAt: descriptor.updatedAt,
|
|
277
|
+
lastUpdated,
|
|
278
|
+
hasContinuity,
|
|
279
|
+
hasReceipt,
|
|
280
|
+
...(claimProjection.errors.length > 0 ? { errors: claimProjection.errors } : {}),
|
|
281
|
+
descriptor,
|
|
282
|
+
directory,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function projectTaskSummary(task) {
|
|
287
|
+
return {
|
|
288
|
+
taskId: task.taskId,
|
|
289
|
+
healthy: task.healthy !== false,
|
|
290
|
+
phase: task.phase ?? null,
|
|
291
|
+
mutationAllowed: task.mutationAllowed !== false,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function compareDiscoveredTasks(a, b) {
|
|
296
|
+
return (a.taskId ?? a.taskKey).localeCompare(b.taskId ?? b.taskKey);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function appendDiscoveredTask(tasks, task, projection, fallbackSortKey = null, collect = null) {
|
|
300
|
+
if (collect) { collect(task, fallbackSortKey); return; }
|
|
301
|
+
if (projection === DISCOVERY_PROJECTIONS.FULL) {
|
|
302
|
+
tasks.push(task);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
tasks.push({ sortKey: task.taskId ?? task.taskKey ?? fallbackSortKey, value: task });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function finishDiscoveredTasks(tasks, projection) {
|
|
309
|
+
if (projection !== DISCOVERY_PROJECTIONS.FULL) {
|
|
310
|
+
return tasks.sort((a, b) => a.sortKey.localeCompare(b.sortKey)).map(({ value }) => value);
|
|
311
|
+
}
|
|
312
|
+
return tasks.sort(compareDiscoveredTasks);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function discoverSelectedTasks(target, packageRoot, taskKeys, projection = DISCOVERY_PROJECTIONS.FULL, collect) {
|
|
316
|
+
const store = getOperationalStore(target);
|
|
59
317
|
const rootPath = ensureWithin(target, TASK_STATE_ROOT);
|
|
60
|
-
if (!(await fileExists(rootPath))) {
|
|
318
|
+
if (!store && !(await fileExists(rootPath))) {
|
|
61
319
|
return [];
|
|
62
320
|
}
|
|
63
321
|
|
|
64
322
|
let entries = [];
|
|
65
323
|
try {
|
|
66
|
-
entries =
|
|
324
|
+
entries = store
|
|
325
|
+
? (taskKeys ?? store.listTaskKeys()).map(name => ({ name, isDirectory: () => true, isSymbolicLink: () => false }))
|
|
326
|
+
: await readdir(rootPath, { withFileTypes: true });
|
|
67
327
|
} catch {
|
|
68
328
|
return [];
|
|
69
329
|
}
|
|
70
330
|
|
|
71
331
|
const tasks = [];
|
|
332
|
+
let scanned = 0;
|
|
72
333
|
for (const entry of entries) {
|
|
334
|
+
// Native reads resolve synchronously. Let other requests run between
|
|
335
|
+
// bounded groups without releasing the owned immutable snapshot.
|
|
336
|
+
if (store && !store.transaction && !store.db.isTransaction && scanned > 0 && scanned % 16 === 0) await nextTurn();
|
|
337
|
+
scanned += 1;
|
|
73
338
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
74
339
|
if (!/^[a-f0-9]{64}$/.test(entry.name)) continue;
|
|
75
340
|
|
|
76
341
|
try {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
tasks.push({
|
|
84
|
-
taskId: descriptor.taskId ?? null,
|
|
85
|
-
taskKey: entry.name,
|
|
86
|
-
directory: `${TASK_STATE_ROOT}/${entry.name}`,
|
|
87
|
-
healthy: false,
|
|
88
|
-
error: {
|
|
89
|
-
code: "E_TASK_KEY_MISMATCH",
|
|
90
|
-
message: `Task directory key "${entry.name}" does not match descriptor taskKey "${descriptor.taskKey}"`,
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Check work-state if available
|
|
97
|
-
let state = null;
|
|
98
|
-
let phase = null;
|
|
99
|
-
let lastUpdated = descriptor.updatedAt ?? descriptor.createdAt;
|
|
100
|
-
try {
|
|
101
|
-
const stateArtifact = await readJsonArtifact(
|
|
102
|
-
target,
|
|
103
|
-
taskArtifactPath(taskId, "state"),
|
|
104
|
-
"work-state",
|
|
105
|
-
packageRoot,
|
|
106
|
-
);
|
|
107
|
-
state = stateArtifact.value;
|
|
108
|
-
phase = state.phase ?? null;
|
|
109
|
-
if (state.lastUpdated) {
|
|
110
|
-
lastUpdated = state.lastUpdated;
|
|
111
|
-
}
|
|
112
|
-
} catch {
|
|
113
|
-
// State might not exist yet
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Check lock status
|
|
117
|
-
const lockInfo = await readLockInfo(target, taskId);
|
|
118
|
-
|
|
119
|
-
// Check continuity & receipt presence
|
|
120
|
-
const continuityPath = ensureWithin(target, taskArtifactPath(taskId, "continuity"));
|
|
121
|
-
const hasContinuity = await fileExists(continuityPath);
|
|
122
|
-
|
|
123
|
-
const receiptPath = ensureWithin(target, taskArtifactPath(taskId, "receipt"));
|
|
124
|
-
const hasReceipt = await fileExists(receiptPath);
|
|
125
|
-
|
|
126
|
-
const claimProjection = await resolveTaskClaimState(target, {
|
|
127
|
-
taskId,
|
|
128
|
-
packageRoot,
|
|
129
|
-
descriptor,
|
|
130
|
-
state,
|
|
131
|
-
});
|
|
132
|
-
const recovery = claimProjection.recovery;
|
|
133
|
-
|
|
134
|
-
tasks.push({
|
|
135
|
-
taskId,
|
|
136
|
-
taskKey: descriptor.taskKey,
|
|
137
|
-
healthy: true,
|
|
138
|
-
phase,
|
|
139
|
-
locked: lockInfo !== null,
|
|
140
|
-
lockInfo,
|
|
141
|
-
...claimProjection,
|
|
142
|
-
ownershipValid: claimProjection.valid,
|
|
143
|
-
recovery,
|
|
144
|
-
operatorRecoveredAt: recovery?.recoveredAt ?? null,
|
|
145
|
-
createdAt: descriptor.createdAt,
|
|
146
|
-
updatedAt: descriptor.updatedAt,
|
|
147
|
-
lastUpdated,
|
|
148
|
-
hasContinuity,
|
|
149
|
-
hasReceipt,
|
|
150
|
-
...(claimProjection.errors.length > 0 ? { errors: claimProjection.errors } : {}),
|
|
151
|
-
descriptor,
|
|
152
|
-
directory: `${TASK_STATE_ROOT}/${descriptor.taskKey}`,
|
|
153
|
-
});
|
|
342
|
+
const readEntry = () => discoverTaskEntry(target, packageRoot, entry, projection);
|
|
343
|
+
const taskId = store?.taskId({ taskKey: entry.name });
|
|
344
|
+
const task = taskId
|
|
345
|
+
? await withEventLedgerAudit(target, packageRoot, { taskId }, readEntry)
|
|
346
|
+
: await readEntry();
|
|
347
|
+
appendDiscoveredTask(tasks, task, projection, entry.name, collect);
|
|
154
348
|
} catch (err) {
|
|
155
349
|
// A directory without a task.json descriptor is not automatically a
|
|
156
350
|
// task namespace: classify by contents so explicitly recognized legacy
|
|
157
351
|
// spillover (policy-snapshot.json) stays compatible while any modern
|
|
158
352
|
// task artifact without a descriptor fails closed instead of silently
|
|
159
353
|
// reopening the legacy singleton fallback.
|
|
160
|
-
if (err.code === "E_TASK_NOT_FOUND" || err.code === "ARTIFACT_MISSING") {
|
|
354
|
+
if (!store && (err.code === "E_TASK_NOT_FOUND" || err.code === "ARTIFACT_MISSING")) {
|
|
161
355
|
const classification = await classifyDescriptorlessTaskDirectory(target, entry.name);
|
|
162
356
|
if (classification.kind === "LEGACY_INCIDENTAL") {
|
|
163
357
|
continue;
|
|
164
358
|
}
|
|
165
|
-
tasks
|
|
359
|
+
appendDiscoveredTask(tasks, projectTask({
|
|
166
360
|
taskId: null,
|
|
167
361
|
taskKey: entry.name,
|
|
168
362
|
directory: `${TASK_STATE_ROOT}/${entry.name}`,
|
|
169
363
|
healthy: false,
|
|
170
364
|
error: classification.error,
|
|
171
|
-
});
|
|
365
|
+
}, projection), projection, entry.name, collect);
|
|
172
366
|
continue;
|
|
173
367
|
}
|
|
174
368
|
// P1-2: Surface corrupt task namespaces instead of silently hiding them
|
|
175
|
-
tasks
|
|
369
|
+
appendDiscoveredTask(tasks, projectTask({
|
|
176
370
|
taskId: null,
|
|
177
371
|
taskKey: entry.name,
|
|
178
372
|
directory: `${TASK_STATE_ROOT}/${entry.name}`,
|
|
@@ -181,15 +375,19 @@ export async function discoverTasks(target, packageRoot = getPackageRoot()) {
|
|
|
181
375
|
code: err.code ?? "E_TASK_DESCRIPTOR_INVALID",
|
|
182
376
|
message: err.message ?? String(err),
|
|
183
377
|
},
|
|
184
|
-
});
|
|
378
|
+
}, projection), projection, entry.name, collect);
|
|
185
379
|
}
|
|
186
380
|
}
|
|
187
381
|
|
|
188
|
-
return tasks
|
|
382
|
+
return finishDiscoveredTasks(tasks, projection);
|
|
189
383
|
}
|
|
190
384
|
|
|
191
385
|
export async function findTaskById(target, taskId, packageRoot = getPackageRoot()) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
386
|
+
return withNativeReadScope(target, async () => {
|
|
387
|
+
const taskKey = taskStorageKey(taskId);
|
|
388
|
+
const store = getOperationalStore(target);
|
|
389
|
+
if (store && !store.taskRow(taskKey)) return null;
|
|
390
|
+
const tasks = await discoverTasks(target, packageRoot, store ? { taskKeys: [taskKey] } : {});
|
|
391
|
+
return tasks.find((t) => t.healthy !== false && (t.taskId === taskId || t.taskKey === taskKey)) ?? null;
|
|
392
|
+
});
|
|
195
393
|
}
|