@cassiomc1/forgeloop 1.14.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/CONTRIBUTING.md +11 -7
- package/DOCS_INDEX.md +2 -1
- 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 +8 -4
- 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/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/GUIDE_ROUTER.md +6 -0
- package/LICENSE +133 -21
- package/LOOP_SYSTEM_DESIGN.md +10 -10
- package/README.md +28 -39
- package/THIRD_PARTY_NOTICES.md +3 -4
- package/completions/_forgeloop +11 -3
- package/completions/forgeloop.bash +19 -3
- package/completions/forgeloop.fish +61 -1
- package/docs/AGENT_PROTOCOL_SUMMARY.md +50 -40
- package/docs/ARTIFACT_REFERENCE.md +50 -37
- package/docs/CLI_REFERENCE.md +147 -3
- package/docs/DOCUMENTATION_GUIDE.md +6 -4
- package/docs/GETTING_STARTED.md +3 -3
- package/docs/PACKAGE_CONTENTS.md +14 -13
- package/docs/RELEASE_CHECKLIST.md +81 -10
- package/docs/SELF_HOSTED_TESTS.md +121 -0
- package/docs/documentation-manifest.json +25 -12
- package/package.json +15 -10
- package/schemas/task-bundle.schema.json +7 -2
- package/scripts/generate-agent-protocol-summary.mjs +2 -2
- package/scripts/run-mcp-clean-tests.mjs +30 -0
- package/src/adapters/agent-browser/provider.js +14 -11
- package/src/adapters/emulated-services/provider.js +11 -4
- package/src/adapters/temporary-paths.js +18 -0
- package/src/cli.js +146 -210
- 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 +24 -8
- package/src/commands/complete.js +19 -3
- package/src/commands/contract-create.js +3 -1
- package/src/commands/doctor.js +36 -13
- package/src/commands/gate-record.js +20 -31
- package/src/commands/gate-revalidate.js +11 -13
- 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 +9 -1
- 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/semantic-plan.js +26 -2
- 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 +1 -1
- package/src/commands/task-list.js +8 -43
- package/src/commands/task-migrate.js +3 -3
- package/src/commands/task-recover.js +10 -2
- package/src/commands/task-scope.js +18 -2
- package/src/commands/task-show.js +16 -2
- package/src/commands/validate-protocol.js +29 -14
- 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 +59 -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.js +6 -1
- package/src/core/bundles.js +160 -66
- package/src/core/checkpoint-revalidation.js +12 -10
- package/src/core/cli-command-definitions.js +109 -7
- package/src/core/code-manifest.js +9 -3
- package/src/core/command-executors.js +182 -126
- package/src/core/command-input.js +21 -1
- package/src/core/completion-artifacts.js +71 -29
- package/src/core/completion-ownership.js +19 -8
- package/src/core/completion-recovery-rebind.js +11 -1
- package/src/core/completion.js +60 -9
- package/src/core/conformance.js +2 -1
- 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 +42 -41
- package/src/core/contract-revision.js +8 -9
- package/src/core/decision/artifact.js +2 -1
- package/src/core/decision/events.js +9 -6
- package/src/core/decision/resolver.js +19 -11
- package/src/core/decision/service.js +63 -38
- package/src/core/decision/task-bindings.js +6 -1
- 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/events.js +316 -97
- package/src/core/execution-prerequisites.js +22 -13
- 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 +6 -5
- 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 +10 -0
- package/src/core/integration-resources.js +41 -34
- package/src/core/ledger-event-collection.js +189 -0
- package/src/core/ledger-relations.js +23 -0
- package/src/core/native-storage.js +12 -0
- package/src/core/next-action-continuity.js +31 -9
- package/src/core/next-action-phases.js +1 -1
- package/src/core/next-action-review-phase.js +2 -2
- package/src/core/next-action-verification-phase.js +2 -1
- package/src/core/next-action.js +21 -2
- package/src/core/phase.js +138 -93
- package/src/core/policy-engine.js +12 -13
- package/src/core/preflight-consistency.js +72 -59
- package/src/core/preflight-loaders.js +19 -12
- 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 +3 -0
- package/src/core/protocol-migration.js +7 -6
- package/src/core/reconcile-closure.js +162 -24
- package/src/core/recovery-history.js +66 -47
- package/src/core/reflection.js +72 -77
- package/src/core/responsibility.js +25 -13
- package/src/core/route-artifact.js +13 -3
- package/src/core/runtime-context.js +5 -0
- package/src/core/schema-validation.js +8 -3
- 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/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 +52 -18
- package/src/core/task-command.js +21 -1
- package/src/core/task-conflict-inspection.js +112 -107
- 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-snapshot.js +51 -8
- package/src/core/task-storage-migration.js +18 -0
- package/src/core/templates.js +20 -8
- package/src/core/test-intelligence/prune.js +5 -1
- package/src/core/trace.js +154 -99
- package/src/core/trajectory-evaluation.js +15 -3
- package/src/core/trajectory-metrics.js +47 -32
- package/src/core/transaction.js +16 -288
- package/src/core/verification-scope.js +20 -12
- package/src/core/work-state.js +100 -59
- 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
package/src/commands/status.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import { readAndClassifyWorkState } from "../core/work-state.js";
|
|
2
3
|
import { inspectSchemaHealth } from "../core/schema-validation.js";
|
|
3
4
|
import { reconcileContinuity } from "../core/continuity-reconciliation.js";
|
|
4
5
|
import { withResolvedTask } from "../core/task-command.js";
|
|
5
6
|
import { findTaskById } from "../core/task-discovery.js";
|
|
6
7
|
|
|
7
|
-
export async function runStatus(
|
|
8
|
+
export async function runStatus(options = {}) {
|
|
9
|
+
return withProjectReadSnapshot(options.target, () => runSelectedStatus(options));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function runSelectedStatus({ target, packageRoot, contractFile = null, taskId, task } = {}) {
|
|
8
13
|
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
9
14
|
const effectiveTaskId = ctx?.taskId ?? null;
|
|
10
15
|
const state = await readAndClassifyWorkState({ target, packageRoot, contractFile, taskId: effectiveTaskId });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getOperationalStore } from "../storage/operational-context.js";
|
|
2
|
+
import { assertSafePath } from "../core/filesystem.js";
|
|
3
|
+
|
|
4
|
+
export async function runStorageBackup({ target, destination, includeAttachments = false } = {}) {
|
|
5
|
+
if (typeof destination !== "string" || !destination) throw Object.assign(new Error("storage-backup requires --destination"), { code: "E_CLI_INVOCATION_INVALID" });
|
|
6
|
+
const store = getOperationalStore(target);
|
|
7
|
+
if (!store) throw Object.assign(new Error("storage-backup requires an explicitly migrated SQLite project"), { code: "E_STORAGE_MIGRATION_REQUIRED" });
|
|
8
|
+
const filename = await assertSafePath(target, destination);
|
|
9
|
+
if (includeAttachments) {
|
|
10
|
+
const { backupProjectStorage } = await import("../storage/project-backup.js");
|
|
11
|
+
return backupProjectStorage(store.db, target, filename);
|
|
12
|
+
}
|
|
13
|
+
const { backupStorageDatabase } = await import("../storage/backup.js");
|
|
14
|
+
return backupStorageDatabase(store.db, filename);
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { resumeProjectStorageMigration } from "../storage/migration.js";
|
|
2
|
+
|
|
3
|
+
export async function runStorageMigrationResume({ target, destination, expectedOwnerId, writersQuiesced = false, acknowledgeUnboundHandoff = false, packageRoot } = {}) {
|
|
4
|
+
return resumeProjectStorageMigration(target, { destination, expectedOwnerId, writersQuiesced, acknowledgeUnboundHandoff, packageRoot });
|
|
5
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { lstat } from "node:fs/promises";
|
|
2
|
+
import { assertSafePath } from "../core/filesystem.js";
|
|
3
|
+
import { readMaintenanceOwner } from "../storage/maintenance-owner.js";
|
|
4
|
+
|
|
5
|
+
async function present(target, relativePath) {
|
|
6
|
+
try { await lstat(await assertSafePath(target, relativePath)); return true; }
|
|
7
|
+
catch (error) { if (error.code === "ENOENT") return false; throw error; }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function inspectOwner(target) {
|
|
11
|
+
const marker = await assertSafePath(target, ".forgeloop/.storage-maintenance");
|
|
12
|
+
if (!(await lstat(marker)).isDirectory()) return { status: "MALFORMED", owner: null };
|
|
13
|
+
try {
|
|
14
|
+
const { value } = await readMaintenanceOwner(target);
|
|
15
|
+
return {
|
|
16
|
+
status: "RETAINED",
|
|
17
|
+
owner: { ownerId: value.ownerId, pid: value.pid, acquiredAt: value.acquiredAt, ...(value.hostname ? { hostname: value.hostname } : {}) },
|
|
18
|
+
ownerLiveness: "NOT_VERIFIED",
|
|
19
|
+
handoffPresent: await present(target, ".forgeloop/.storage-maintenance/handoff")
|
|
20
|
+
|| await present(target, `.forgeloop/storage-maintenance-history/handoffs/${value.ownerId}.json`),
|
|
21
|
+
};
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (error.code === "ENOENT") return { status: "INCOMPLETE", owner: null };
|
|
24
|
+
if (error.code === "E_STORAGE_MAINTENANCE_OWNER_INVALID") return { status: "MALFORMED", owner: null };
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Inspect bootstrap layout even when ordinary database dispatch is excluded. */
|
|
30
|
+
export async function runStorageMigrationStatus({ target }) {
|
|
31
|
+
const databasePresent = await present(target, ".forgeloop/state.sqlite");
|
|
32
|
+
const legacyOperationalPaths = [];
|
|
33
|
+
for (const relativePath of [".forgeloop/task-state", ".forgeloop/work-state.json", ".forgeloop/events.ndjson", ".forgeloop/.txn"]) {
|
|
34
|
+
if (await present(target, relativePath)) legacyOperationalPaths.push(relativePath);
|
|
35
|
+
}
|
|
36
|
+
const maintenance = await present(target, ".forgeloop/.storage-maintenance")
|
|
37
|
+
? await inspectOwner(target) : { status: "NONE", owner: null };
|
|
38
|
+
return {
|
|
39
|
+
schemaVersion: 1,
|
|
40
|
+
layout: databasePresent ? legacyOperationalPaths.length ? "MIXED" : "SQLITE_PRESENT" : "FILESYSTEM",
|
|
41
|
+
databasePresent, legacyOperationalPaths, maintenance,
|
|
42
|
+
databaseValidation: "NOT_RUN", publicationValidation: "NOT_VERIFIED",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { resumeProjectStorageRestore } from "../storage/project-restore.js";
|
|
2
|
+
import { resumeActiveProjectReplacement } from "../storage/project-replacement.js";
|
|
3
|
+
|
|
4
|
+
export async function runStorageRestoreResume({ target, operationId, expectedOwnerId, writersQuiesced = false, replaceActive = false, packageRoot } = {}) {
|
|
5
|
+
const resume = replaceActive === true ? resumeActiveProjectReplacement : resumeProjectStorageRestore;
|
|
6
|
+
return resume(target, { operationId, expectedOwnerId, writersQuiesced, packageRoot });
|
|
7
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { restoreProjectStorageToFreshProject } from "../storage/project-restore.js";
|
|
3
|
+
import { replaceActiveProjectStorage } from "../storage/project-replacement.js";
|
|
4
|
+
|
|
5
|
+
export async function runStorageRestore({ target, source, writersQuiesced = false, replaceActive = false, packageRoot } = {}) {
|
|
6
|
+
const restore = replaceActive === true ? replaceActiveProjectStorage : restoreProjectStorageToFreshProject;
|
|
7
|
+
return restore(path.resolve(target, source), target, { writersQuiesced, packageRoot });
|
|
8
|
+
}
|
|
@@ -153,7 +153,7 @@ export async function runTaskAbandon({
|
|
|
153
153
|
const inspection = await inspectTaskConflictState(target, {
|
|
154
154
|
taskId: effectiveTaskId,
|
|
155
155
|
packageRoot,
|
|
156
|
-
ignoredLockId: transaction.lock
|
|
156
|
+
ignoredLockId: transaction.lock?.lockId ?? null,
|
|
157
157
|
});
|
|
158
158
|
assertInspectionAbandonable(effectiveTaskId, inspection);
|
|
159
159
|
assertSnapshotUnchanged(before, snapshot(inspection), effectiveTaskId);
|
|
@@ -1,60 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { discoverTaskListPage } from "../core/task-discovery.js";
|
|
2
2
|
import { WORK_PHASES } from "../core/protocol.js";
|
|
3
3
|
|
|
4
4
|
export async function runTaskList({ target, packageRoot, phase = null, active = false, limit = null, offset = 0 } = {}) {
|
|
5
|
-
const tasks = await discoverTasks(target, packageRoot);
|
|
6
5
|
const normalizedPhase = typeof phase === "string" && phase.trim() !== "" ? phase.trim().toUpperCase() : null;
|
|
6
|
+
const start = Number.isInteger(offset) && offset >= 0 ? offset : 0;
|
|
7
|
+
const pageLimit = Number.isInteger(limit) && limit >= 0 ? limit : null;
|
|
8
|
+
const { tasks: projected, total } = await discoverTaskListPage(target, packageRoot, { phase: normalizedPhase, active, offset: start, limit: pageLimit });
|
|
7
9
|
if (normalizedPhase && !WORK_PHASES.includes(normalizedPhase)) {
|
|
8
10
|
const error = new Error(`Unknown task phase filter: ${phase}`);
|
|
9
11
|
error.code = "E_TASK_PHASE_INVALID";
|
|
10
12
|
throw error;
|
|
11
13
|
}
|
|
12
|
-
const
|
|
13
|
-
const filtered = active
|
|
14
|
-
? phaseFiltered.filter((task) => task.claimState === "ACTIVE" && task.mutationAllowed === true)
|
|
15
|
-
: phaseFiltered;
|
|
16
|
-
const start = Number.isInteger(offset) && offset >= 0 ? offset : 0;
|
|
17
|
-
const end = Number.isInteger(limit) && limit >= 0 ? start + limit : undefined;
|
|
18
|
-
const projected = filtered.slice(start, end);
|
|
14
|
+
const end = pageLimit === null ? undefined : start + pageLimit;
|
|
19
15
|
return {
|
|
20
|
-
tasks: projected
|
|
21
|
-
if (task.healthy === false) {
|
|
22
|
-
return {
|
|
23
|
-
taskId: task.taskId ?? null,
|
|
24
|
-
taskKey: task.taskKey,
|
|
25
|
-
directory: task.directory,
|
|
26
|
-
healthy: false,
|
|
27
|
-
error: task.error,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
taskId: task.taskId,
|
|
32
|
-
taskKey: task.taskKey,
|
|
33
|
-
directory: task.directory,
|
|
34
|
-
healthy: true,
|
|
35
|
-
phase: task.phase,
|
|
36
|
-
writeClaims: task.writeClaims ?? [],
|
|
37
|
-
historicalWriteClaims: task.historicalWriteClaims ?? [],
|
|
38
|
-
effectiveWriteClaims: task.effectiveWriteClaims ?? [],
|
|
39
|
-
claimState: task.claimState,
|
|
40
|
-
recovery: task.recovery,
|
|
41
|
-
mutationAllowed: task.mutationAllowed,
|
|
42
|
-
ownershipValid: task.ownershipValid,
|
|
43
|
-
ownershipErrors: task.ownershipErrors ?? task.errors ?? [],
|
|
44
|
-
reasonCodes: task.reasonCodes ?? [],
|
|
45
|
-
locked: task.locked,
|
|
46
|
-
hasContinuity: task.hasContinuity,
|
|
47
|
-
hasReceipt: task.hasReceipt,
|
|
48
|
-
createdAt: task.createdAt,
|
|
49
|
-
updatedAt: task.updatedAt,
|
|
50
|
-
};
|
|
51
|
-
}),
|
|
16
|
+
tasks: projected,
|
|
52
17
|
...(normalizedPhase ? { phase: normalizedPhase } : {}),
|
|
53
18
|
...(active ? { active: true } : {}),
|
|
54
19
|
offset: start,
|
|
55
20
|
...(Number.isInteger(limit) && limit >= 0 ? { limit } : {}),
|
|
56
|
-
total
|
|
57
|
-
hasMore: end !== undefined ? end <
|
|
21
|
+
total,
|
|
22
|
+
hasMore: end !== undefined ? end < total : false,
|
|
58
23
|
};
|
|
59
24
|
}
|
|
60
25
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { migrateLegacyTaskStorage } from "../core/task-storage-migration.js";
|
|
2
2
|
|
|
3
|
-
export async function runTaskMigrate({ target, packageRoot, dryRun = false } = {}) {
|
|
4
|
-
const result = await
|
|
3
|
+
export async function runTaskMigrate({ target, packageRoot, dryRun = false, destination, writersQuiesced = false } = {}) {
|
|
4
|
+
const result = await migrateLegacyTaskStorage(target, { packageRoot, dryRun, destination, writersQuiesced });
|
|
5
5
|
return result;
|
|
6
6
|
}
|
|
7
7
|
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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"));
|
|
@@ -51,7 +54,11 @@ function isOptionalContinuityAbsence(item) {
|
|
|
51
54
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
52
55
|
import { withResolvedTask } from "../core/task-command.js";
|
|
53
56
|
|
|
54
|
-
export async function runValidateProtocol({
|
|
57
|
+
export async function runValidateProtocol(options) {
|
|
58
|
+
return withProjectReadSnapshot(options.target, () => validateSelectedProtocol(options));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function validateSelectedProtocol({
|
|
55
62
|
target,
|
|
56
63
|
packageRoot,
|
|
57
64
|
routeFile = null,
|
|
@@ -63,9 +70,17 @@ export async function runValidateProtocol({
|
|
|
63
70
|
delegatedResultFiles = [],
|
|
64
71
|
taskId = null,
|
|
65
72
|
task = null,
|
|
66
|
-
}) {
|
|
73
|
+
}, ownedLedger = null) {
|
|
67
74
|
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
68
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
|
+
}
|
|
69
84
|
const effectiveRouteFile = routeFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "route") : ARTIFACT_PATHS.route);
|
|
70
85
|
const effectiveStateFile = stateFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "state") : ARTIFACT_PATHS.state);
|
|
71
86
|
const effectiveReceiptFile = receiptFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "receipt") : ARTIFACT_PATHS.receipt);
|
|
@@ -169,19 +184,15 @@ export async function runValidateProtocol({
|
|
|
169
184
|
} catch {
|
|
170
185
|
// A missing or invalid preflight is already outside the optional protocol set.
|
|
171
186
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const ledger = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId, eventsPath: effectiveEventsFile });
|
|
176
|
-
ledgerEvents = ledger.events ?? [];
|
|
177
|
-
ledgerErrors = [
|
|
187
|
+
const finish = async (ledger) => {
|
|
188
|
+
const ledgerEvents = ledger.events ?? [];
|
|
189
|
+
const ledgerErrors = state && !stateValidationError ? [
|
|
178
190
|
...ledger.errors.map((error) => ({ ...error, artifacts: [effectiveEventsFile] })),
|
|
179
|
-
...validateStateLedgerCoherence(state,
|
|
191
|
+
...validateStateLedgerCoherence(state, ledgerEvents).map((error) => ({
|
|
180
192
|
...error,
|
|
181
193
|
artifacts: [effectiveStateFile, effectiveEventsFile],
|
|
182
194
|
})),
|
|
183
|
-
];
|
|
184
|
-
}
|
|
195
|
+
] : [];
|
|
185
196
|
const recoveryErrors = [];
|
|
186
197
|
if (effectiveTaskId) {
|
|
187
198
|
try {
|
|
@@ -225,6 +236,10 @@ export async function runValidateProtocol({
|
|
|
225
236
|
};
|
|
226
237
|
}
|
|
227
238
|
return result;
|
|
239
|
+
};
|
|
240
|
+
return state && !stateValidationError
|
|
241
|
+
? ownedLedger ? finish(ownedLedger) : withEventLedgerAudit(target, packageRoot, { taskId: effectiveTaskId, eventsPath: effectiveEventsFile }, finish)
|
|
242
|
+
: finish({ events: [], errors: [] });
|
|
228
243
|
});
|
|
229
244
|
}
|
|
230
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");
|