@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
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { readAction } from "../core/actions.js";
|
|
2
2
|
import { resolveTaskContext } from "../core/task-context.js";
|
|
3
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
3
4
|
|
|
4
5
|
export async function runActionShow({ target, packageRoot, taskId, actionId }) {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
return withProjectReadSnapshot(target, async () => {
|
|
7
|
+
const ctx = await resolveTaskContext(target, { packageRoot, taskId });
|
|
8
|
+
return readAction(target, { packageRoot, taskId: ctx.taskId, actionId });
|
|
9
|
+
});
|
|
7
10
|
}
|
|
8
11
|
export function formatActionShowResult(result) {
|
|
9
12
|
return `FORGELOOP ACTION\naction: ${result.actionId}\nstate: ${result.state}\ncapability: ${result.capability}\n`;
|
package/src/commands/advance.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { assertCanonicalPersistence } from "../storage/operational-context.js";
|
|
1
2
|
import { advanceWorkState } from "../core/phase.js";
|
|
2
3
|
import { withTaskMutation } from "../core/task-command.js";
|
|
3
4
|
|
|
4
5
|
export { advanceWorkState };
|
|
5
6
|
|
|
6
|
-
export async function runAdvance({ target, packageRoot, to, taskId, task, authorityContext, runtimeContext }) {
|
|
7
|
+
export async function runAdvance({ target, packageRoot, to, taskId, task, authorityContext, runtimeContext, persistence = null }) {
|
|
7
8
|
if (!to) throw new Error("--to is required for advance");
|
|
9
|
+
|
|
10
|
+
assertCanonicalPersistence(persistence);
|
|
11
|
+
|
|
8
12
|
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "advance", async (ctx) => {
|
|
9
13
|
return advanceWorkState(target, to, {
|
|
10
14
|
packageRoot,
|
package/src/commands/baseline.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import {
|
|
2
3
|
computePersistedPolicyLockData,
|
|
3
4
|
evaluateTargetPolicy,
|
|
@@ -18,6 +19,17 @@ function baselineError(code, message) {
|
|
|
18
19
|
return err;
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
async function hasActivePolicySnapshot(target, packageRoot) {
|
|
23
|
+
return withProjectReadSnapshot(target, async () => {
|
|
24
|
+
const tasks = await discoverTasks(target, packageRoot);
|
|
25
|
+
for (const task of tasks) {
|
|
26
|
+
if (task.healthy && task.phase && task.phase !== "COMPLETE" && task.phase !== "BLOCKED"
|
|
27
|
+
&& await readTaskPolicySnapshot(target, task.taskId, packageRoot)) return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
export async function runBaseline({
|
|
22
34
|
target = process.cwd(),
|
|
23
35
|
packageRoot,
|
|
@@ -28,17 +40,7 @@ export async function runBaseline({
|
|
|
28
40
|
const policyEval = await evaluateTargetPolicy({ target, packageRoot });
|
|
29
41
|
let baseline = await readBaseline(target, packageRoot);
|
|
30
42
|
|
|
31
|
-
const
|
|
32
|
-
let hasActivePolicyTask = false;
|
|
33
|
-
for (const t of tasks) {
|
|
34
|
-
if (t.healthy && t.phase && t.phase !== "COMPLETE" && t.phase !== "BLOCKED") {
|
|
35
|
-
const snap = await readTaskPolicySnapshot(target, t.taskId, packageRoot);
|
|
36
|
-
if (snap) {
|
|
37
|
-
hasActivePolicyTask = true;
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
43
|
+
const hasActivePolicyTask = await hasActivePolicySnapshot(target, packageRoot);
|
|
42
44
|
|
|
43
45
|
if (record) {
|
|
44
46
|
if (hasActivePolicyTask && !policyResetAuthorized) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import { canonicalFingerprint } from "../core/artifacts.js";
|
|
2
3
|
import { readContract } from "../core/contract.js";
|
|
3
4
|
import {
|
|
@@ -10,7 +11,7 @@ import { currentRepositoryFingerprint } from "../core/repository.js";
|
|
|
10
11
|
import { resolveTaskContext } from "../core/task-context.js";
|
|
11
12
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
12
13
|
import { assertTaskMutationAllowed } from "../core/task-claim-state.js";
|
|
13
|
-
import { withTaskTransaction } from "../core/transaction.js";
|
|
14
|
+
import { getTaskTransaction, withTaskTransaction } from "../core/transaction.js";
|
|
14
15
|
import {
|
|
15
16
|
CHECKPOINT_REVALIDATED_EVENT,
|
|
16
17
|
assertCheckpointRevalidatedDetails,
|
|
@@ -47,6 +48,10 @@ function assertRouteIdentity(state, contract, route) {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
async function inspectEligibility(target, packageRoot, taskId) {
|
|
51
|
+
return withProjectReadSnapshot(target, () => inspectSelectedEligibility(target, packageRoot, taskId));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function inspectSelectedEligibility(target, packageRoot, taskId) {
|
|
50
55
|
const state = await readWorkState(target, { packageRoot, taskId });
|
|
51
56
|
if (!state) throw revalidationError("A persisted work-state checkpoint is required");
|
|
52
57
|
if (state.phase !== "ROUTED") throw revalidationError(`Checkpoint revalidation supports phase ROUTED, found ${state.phase}`);
|
|
@@ -162,13 +167,24 @@ export async function runCheckpointRevalidate({ target, packageRoot, taskId } =
|
|
|
162
167
|
const effectiveTaskId = context.taskId;
|
|
163
168
|
const initial = await inspectEligibility(target, packageRoot, effectiveTaskId);
|
|
164
169
|
if (initial.alreadyFresh) return resultFor(effectiveTaskId, initial.state, initial.repository, null, null, true);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
const nested = Boolean(await getTaskTransaction(target));
|
|
171
|
+
try {
|
|
172
|
+
return await withTaskTransaction({
|
|
173
|
+
target,
|
|
174
|
+
taskId: effectiveTaskId,
|
|
175
|
+
operation: "checkpoint-revalidate",
|
|
176
|
+
packageRoot,
|
|
177
|
+
recordCommitEvent: false,
|
|
178
|
+
}, async (transaction) => revalidateLocked(target, packageRoot, effectiveTaskId, transaction));
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (nested || error?.code !== "E_STATE_REVISION_CONFLICT") throw error;
|
|
181
|
+
// A competing writer may have completed this exact revalidation. Recheck
|
|
182
|
+
// all eligibility and history after our preparation rolled back; never
|
|
183
|
+
// replay mutation or convert an unrelated conflict into success.
|
|
184
|
+
const current = await inspectEligibility(target, packageRoot, effectiveTaskId);
|
|
185
|
+
if (!current.alreadyFresh) throw error;
|
|
186
|
+
return resultFor(effectiveTaskId, current.state, current.repository, null, null, true);
|
|
187
|
+
}
|
|
172
188
|
}
|
|
173
189
|
|
|
174
190
|
export function formatCheckpointRevalidateResult(result) {
|
package/src/commands/complete.js
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { runComplete as evaluateAndComplete } from "../core/completion.js";
|
|
2
2
|
import { withTaskMutation } from "../core/task-command.js";
|
|
3
|
+
import { isRecoverableCompletionEvidenceCode } from "../core/completion-recovery.js";
|
|
3
4
|
|
|
4
5
|
export async function runComplete(options = {}) {
|
|
5
6
|
const target = options.target;
|
|
6
7
|
const packageRoot = options.packageRoot;
|
|
7
8
|
const taskId = options.taskId ?? options.task ?? null;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const rejected = new Error("Completion validation rejected");
|
|
10
|
+
let rejectedResult;
|
|
11
|
+
try {
|
|
12
|
+
return await withTaskMutation(target, { taskId, packageRoot }, "complete", async (ctx) => {
|
|
13
|
+
const result = await evaluateAndComplete({ ...options, taskId: ctx?.taskId ?? null });
|
|
14
|
+
const recordsEvidenceRecovery = options.persist !== false && result.errors.length > 0
|
|
15
|
+
&& result.errors.every(error => isRecoverableCompletionEvidenceCode(error.code));
|
|
16
|
+
if (result.status === "REJECTED" && !recordsEvidenceRecovery) {
|
|
17
|
+
rejectedResult = result;
|
|
18
|
+
throw rejected;
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
});
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// Return the domain rejection only after its transaction has rolled back.
|
|
24
|
+
if (error === rejected) return rejectedResult;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
11
27
|
}
|
|
12
28
|
|
|
13
29
|
export function formatCompleteResult(result) {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from "../core/resumability.js";
|
|
16
16
|
import { taskArtifactPath } from "../core/task-paths.js";
|
|
17
17
|
import { ensureSemanticDecision } from "../core/decision/service.js";
|
|
18
|
+
import { operationalArtifactExists } from "../storage/operational-context.js";
|
|
18
19
|
|
|
19
20
|
function bootstrapInconsistent(message, candidate = false) {
|
|
20
21
|
const error = new Error(message);
|
|
@@ -176,7 +177,8 @@ export async function runContractCreate({ target, packageRoot, taskId, task, con
|
|
|
176
177
|
// fingerprint to prove idempotent equivalence; it never redefines the
|
|
177
178
|
// canonical historical contract.
|
|
178
179
|
const existingContractPath = taskArtifactPath(ctx.taskId, "contract");
|
|
179
|
-
if (
|
|
180
|
+
if (operationalArtifactExists(target, existingContractPath)
|
|
181
|
+
?? await fileExists(ensureWithin(target, existingContractPath)).catch(() => false)) {
|
|
180
182
|
const existingContract = await readContract(target, packageRoot, { taskId: ctx.taskId });
|
|
181
183
|
const existingLedger = await validateEventLedger(target, packageRoot, { taskId: ctx.taskId });
|
|
182
184
|
const validatedContractEvents = existingLedger.events.filter(
|
package/src/commands/doctor.js
CHANGED
|
@@ -4,13 +4,14 @@ import { readTemplateEntries } from "../core/templates.js";
|
|
|
4
4
|
import { createEvidence } from "../core/evidence.js";
|
|
5
5
|
import { LAYOUT_VERSION } from "../core/target-layout.js";
|
|
6
6
|
import { inspectNativeAdapter, validateNativeAdapterTargets } from "../core/native-adapters.js";
|
|
7
|
-
import { findIncompleteTransactions
|
|
7
|
+
import { findIncompleteTransactions } from "../core/transaction.js";
|
|
8
8
|
import { isRepositoryCandidate } from "../repository-index/lifecycle.js";
|
|
9
9
|
import { getRepositoryIndexStatus, sanitizeRepositoryIndexStatus } from "../repository-index/status.js";
|
|
10
10
|
import { searchRepository } from "../repository-index/search.js";
|
|
11
11
|
import { getPersistentTransportStatus } from "../persistent-transport/client.js";
|
|
12
12
|
import { hasTypesafeCredentials } from "../adapters/typesafe/client.js";
|
|
13
13
|
import { PINNED_JEV_MODEL } from "../core/decision/constants.js";
|
|
14
|
+
import { getOperationalStore } from "../storage/operational-context.js";
|
|
14
15
|
|
|
15
16
|
function semanticDecisionPlaneFinding() {
|
|
16
17
|
const credentialState = hasTypesafeCredentials() ? "credentials configured" : "credentials missing";
|
|
@@ -193,20 +194,39 @@ async function inspectRepositoryIndexForDoctor({ target, packageRoot, repository
|
|
|
193
194
|
return result;
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
async function inspectStorageForDoctor(target, findings) {
|
|
198
|
+
let storage = null;
|
|
199
|
+
const store = getOperationalStore(target);
|
|
200
|
+
if (store) {
|
|
201
|
+
const { checkStorageIntegrity, readStorageMeta } = await import("../storage/connection.js");
|
|
202
|
+
const integrity = checkStorageIntegrity(store.db);
|
|
203
|
+
const metadata = readStorageMeta(store.db);
|
|
204
|
+
storage = { backend: "sqlite", storageVersion: metadata.storage_version, schemaVersion: metadata.schema_version, integrity };
|
|
205
|
+
findings.push(finding(
|
|
206
|
+
integrity.ok ? "sqlite-storage-integrity" : "E_STORAGE_INTEGRITY_INVALID",
|
|
207
|
+
integrity.ok ? "info" : "error",
|
|
208
|
+
".forgeloop/state.sqlite",
|
|
209
|
+
integrity.ok ? "SQLite pages, foreign keys, ledger hashes and artifact bindings passed storage integrity checks." : "SQLite storage integrity checks failed; no automatic evidence repair was attempted.",
|
|
210
|
+
integrity.ok ? "Domain chronology and authority still require their existing validators." : "Preserve the database and diagnose the reported storage inconsistencies before writing or migrating.",
|
|
211
|
+
));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return storage ? { storage } : {};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function findDoctorTransactions(target) {
|
|
218
|
+
// SQLite recovery belongs to its native open/transaction boundary. Retained
|
|
219
|
+
// legacy manifests are import evidence, not operational recovery input.
|
|
220
|
+
return getOperationalStore(target) ? [] : findIncompleteTransactions(target);
|
|
221
|
+
}
|
|
222
|
+
|
|
196
223
|
export async function runDoctor({ target, packageRoot, adoptPaths = [], strict = false, fix = false, repositoryIndex, repositoryIndexOptions }) {
|
|
197
224
|
const findings = [];
|
|
198
|
-
|
|
199
|
-
if (fix && incompleteTransactions.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
transaction.status === "ROLLED_BACK" ? "TRANSACTION_RECOVERED" : "E_TRANSACTION_RECOVERY_FAILED",
|
|
204
|
-
transaction.status === "ROLLED_BACK" ? "info" : "error",
|
|
205
|
-
`.forgeloop/.txn/${transaction.transactionId}`,
|
|
206
|
-
`Transaction recovery result: ${transaction.status}.`,
|
|
207
|
-
));
|
|
208
|
-
}
|
|
209
|
-
incompleteTransactions = await findIncompleteTransactions(target);
|
|
225
|
+
const incompleteTransactions = await findDoctorTransactions(target);
|
|
226
|
+
if (fix && incompleteTransactions.length) {
|
|
227
|
+
findings.push(finding("E_STORAGE_OPERATION_UNSUPPORTED", "error", ".forgeloop/.txn",
|
|
228
|
+
"Filesystem transaction repair is retired; retained evidence was preserved.",
|
|
229
|
+
"Reconcile incomplete transactions with the compatible pre-migration release before importing into SQLite."));
|
|
210
230
|
}
|
|
211
231
|
for (const transaction of incompleteTransactions) {
|
|
212
232
|
findings.push(finding(
|
|
@@ -407,11 +427,14 @@ export async function runDoctor({ target, packageRoot, adoptPaths = [], strict =
|
|
|
407
427
|
|
|
408
428
|
findings.push(semanticDecisionPlaneFinding());
|
|
409
429
|
|
|
430
|
+
const storageFields = await inspectStorageForDoctor(target, findings);
|
|
431
|
+
|
|
410
432
|
const ok = findings.every((item) => item.severity !== "error")
|
|
411
433
|
&& (!strict || findings.every((item) => item.severity !== "warning"));
|
|
412
434
|
return {
|
|
413
435
|
ok,
|
|
414
436
|
findings,
|
|
437
|
+
...storageFields,
|
|
415
438
|
repositoryIndex: repositoryIndexResult,
|
|
416
439
|
evidence: [createEvidence({
|
|
417
440
|
kind: "OBSERVED",
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { lstat, readFile as readBytesFromFile } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { lstat } from "node:fs/promises";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { ensureWithin, isPathWithin, realpathWithTransientWindowsRetry } from "../core/filesystem.js";
|
|
4
|
+
import { assertSafePath, ensureWithin } from "../core/filesystem.js";
|
|
6
5
|
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
7
6
|
import { readContract } from "../core/contract.js";
|
|
8
7
|
import { readGuideMetadata, requiredGatesForGuides } from "../core/guide-metadata.js";
|
|
9
8
|
import { optionalConfig } from "../core/preflight-loaders.js";
|
|
10
|
-
import { persistGate } from "../core/gate-artifact.js";
|
|
9
|
+
import { persistGate, readCanonicalGateArtifactBinding, hashGateFile, readGateFileJson } from "../core/gate-artifact.js";
|
|
11
10
|
import { readWorkState } from "../core/work-state.js";
|
|
12
11
|
import { taskArtifactPath, taskGatePath } from "../core/task-paths.js";
|
|
13
12
|
import { withTaskMutation } from "../core/task-command.js";
|
|
@@ -61,30 +60,29 @@ async function hashArtifact(target, artifactPath) {
|
|
|
61
60
|
if (typeof artifactPath !== "string" || !artifactPath || path.isAbsolute(artifactPath)) {
|
|
62
61
|
throw gateError(`Gate artifact must be a project-relative path: ${artifactPath}`);
|
|
63
62
|
}
|
|
64
|
-
let candidate;
|
|
65
63
|
try {
|
|
66
|
-
|
|
64
|
+
ensureWithin(target, artifactPath);
|
|
67
65
|
} catch (cause) {
|
|
68
66
|
throw gateError(`Gate artifact escapes the project: ${artifactPath}`, { cause });
|
|
69
67
|
}
|
|
70
|
-
|
|
68
|
+
const canonical = readCanonicalGateArtifactBinding(target, artifactPath);
|
|
69
|
+
if (canonical) {
|
|
70
|
+
await assertSafePath(target, artifactPath);
|
|
71
|
+
if (canonical.sha256 === null) throw gateError(`Gate artifact is unavailable: ${artifactPath}`);
|
|
72
|
+
if (canonical.byteLength > MAX_GATE_ARTIFACT_BYTES) throw gateError(`Gate artifact exceeds the maximum size of ${MAX_GATE_ARTIFACT_BYTES} bytes: ${artifactPath}`);
|
|
73
|
+
return { path: artifactPath, sha256: canonical.sha256 };
|
|
74
|
+
}
|
|
75
|
+
let info;
|
|
76
|
+
try { info = await lstat(ensureWithin(target, artifactPath)); }
|
|
77
|
+
catch (cause) { throw gateError(`Gate artifact is unavailable: ${artifactPath}`, { cause }); }
|
|
78
|
+
if (!info.isFile() || info.isSymbolicLink()) throw gateError(`Gate artifact must be a regular file: ${artifactPath}`);
|
|
79
|
+
if (info.size > MAX_GATE_ARTIFACT_BYTES) throw gateError(`Gate artifact exceeds the maximum size of ${MAX_GATE_ARTIFACT_BYTES} bytes: ${artifactPath}`);
|
|
71
80
|
try {
|
|
72
|
-
|
|
81
|
+
const binding = await hashGateFile(target, artifactPath, { maxBytes: MAX_GATE_ARTIFACT_BYTES });
|
|
82
|
+
return { path: artifactPath, sha256: binding.sha256 };
|
|
73
83
|
} catch (cause) {
|
|
74
|
-
throw gateError(`Gate artifact is unavailable: ${artifactPath}`, { cause });
|
|
75
|
-
}
|
|
76
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
77
|
-
throw gateError(`Gate artifact must be a regular file: ${artifactPath}`);
|
|
78
|
-
}
|
|
79
|
-
if (stat.size > MAX_GATE_ARTIFACT_BYTES) {
|
|
80
|
-
throw gateError(`Gate artifact exceeds the maximum size of ${MAX_GATE_ARTIFACT_BYTES} bytes: ${artifactPath}`);
|
|
81
|
-
}
|
|
82
|
-
const rootReal = await realpathWithTransientWindowsRetry(target);
|
|
83
|
-
const candidateReal = await realpathWithTransientWindowsRetry(candidate).catch(() => null);
|
|
84
|
-
if (!candidateReal || !isPathWithin(rootReal, candidateReal)) {
|
|
85
|
-
throw gateError(`Gate artifact escapes the project: ${artifactPath}`);
|
|
84
|
+
throw gateError(`Gate artifact is unavailable or unsafe: ${artifactPath}`, { cause });
|
|
86
85
|
}
|
|
87
|
-
return { path: artifactPath, sha256: sha256(await readBytesFromFile(candidateReal)) };
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
async function readEvidenceFile(target, evidencePath) {
|
|
@@ -92,16 +90,7 @@ async function readEvidenceFile(target, evidencePath) {
|
|
|
92
90
|
throw gateError(`Gate evidence must be a project-relative path: ${evidencePath}`);
|
|
93
91
|
}
|
|
94
92
|
try {
|
|
95
|
-
const
|
|
96
|
-
const stat = await lstat(candidate);
|
|
97
|
-
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("not a regular file");
|
|
98
|
-
if (stat.size > MAX_GATE_EVIDENCE_BYTES) throw new Error(`file exceeds ${MAX_GATE_EVIDENCE_BYTES} bytes`);
|
|
99
|
-
const rootReal = await realpathWithTransientWindowsRetry(target);
|
|
100
|
-
const candidateReal = await realpathWithTransientWindowsRetry(candidate);
|
|
101
|
-
if (!isPathWithin(rootReal, candidateReal)) throw new Error("outside project");
|
|
102
|
-
const bytes = await readBytesFromFile(candidateReal);
|
|
103
|
-
if (bytes.length > MAX_GATE_EVIDENCE_BYTES) throw new Error(`file exceeds ${MAX_GATE_EVIDENCE_BYTES} bytes`);
|
|
104
|
-
const value = JSON.parse(bytes.toString("utf8"));
|
|
93
|
+
const value = await readGateFileJson(target, evidencePath, { maxBytes: MAX_GATE_EVIDENCE_BYTES });
|
|
105
94
|
return { path: evidencePath, value };
|
|
106
95
|
} catch (cause) {
|
|
107
96
|
throw gateError(`Gate evidence file is invalid: ${evidencePath}`, { cause });
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { lstat } from "node:fs/promises";
|
|
2
|
-
|
|
3
1
|
import { optionalConfig } from "../core/preflight-loaders.js";
|
|
4
2
|
import { readContract } from "../core/contract.js";
|
|
5
3
|
import { appendProtocolEvent, validateEventLedger, validateStateLedgerCoherence } from "../core/events.js";
|
|
6
|
-
import {
|
|
7
|
-
import { sha256 } from "../core/manifest.js";
|
|
4
|
+
import { assertSafePath, ensureWithin } from "../core/filesystem.js";
|
|
8
5
|
import { requiredGatesForGuides } from "../core/guide-metadata.js";
|
|
9
6
|
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
10
7
|
import { staleReasons } from "../core/next-action-artifacts.js";
|
|
@@ -13,7 +10,7 @@ import { taskGatePath, taskArtifactPath } from "../core/task-paths.js";
|
|
|
13
10
|
import { withTaskMutation } from "../core/task-command.js";
|
|
14
11
|
import { readWorkState } from "../core/work-state.js";
|
|
15
12
|
import { stateIdentityErrors } from "../core/completion-relationships.js";
|
|
16
|
-
import { persistGate, readGateIfPresent, validateGateArtifacts } from "../core/gate-artifact.js";
|
|
13
|
+
import { persistGate, readCanonicalGateArtifactBinding, hashGateFile, readGateIfPresent, validateGateArtifacts } from "../core/gate-artifact.js";
|
|
17
14
|
import {
|
|
18
15
|
GATE_REVALIDATED_EVENT,
|
|
19
16
|
assertGateRevalidatedDetails,
|
|
@@ -31,15 +28,16 @@ function revalidationError(message, code = "E_GATE_REVALIDATION_UNSAFE", artifac
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
async function hashGateArtifact(target, relativePath) {
|
|
34
|
-
let artifactPath;
|
|
35
31
|
try {
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
ensureWithin(target, relativePath);
|
|
33
|
+
const canonical = readCanonicalGateArtifactBinding(target, relativePath);
|
|
34
|
+
if (canonical) {
|
|
35
|
+
await assertSafePath(target, relativePath);
|
|
36
|
+
if (canonical.sha256 === null) throw new Error("artifact is missing from canonical storage");
|
|
37
|
+
return { path: relativePath, sha256: canonical.sha256 };
|
|
38
|
+
}
|
|
39
|
+
const binding = await hashGateFile(target, relativePath);
|
|
40
|
+
return { path: relativePath, sha256: binding.sha256 };
|
|
43
41
|
} catch (cause) {
|
|
44
42
|
throw revalidationError(`Cannot refresh gate artifact ${relativePath}: ${cause.message}`, "E_GATE_REVALIDATION_UNSAFE", [relativePath]);
|
|
45
43
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import { listCanonicalHandoffs } from "../core/handoff.js";
|
|
2
3
|
import {
|
|
3
4
|
readHandoffAcceptanceLedger,
|
|
4
5
|
resolveHandoffAcceptance,
|
|
5
6
|
} from "../core/handoff-acceptance.js";
|
|
6
7
|
|
|
7
|
-
export async function runHandoffList(
|
|
8
|
+
export async function runHandoffList(options = {}) {
|
|
9
|
+
return withProjectReadSnapshot(options.target, () => projectHandoffList(options));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function projectHandoffList({ target, packageRoot, taskId } = {}) {
|
|
8
13
|
const handoffs = await listCanonicalHandoffs(target, { packageRoot, taskId });
|
|
9
14
|
const ledger = await readHandoffAcceptanceLedger(target, packageRoot, { taskId });
|
|
10
15
|
const handoffsWithAcceptance = handoffs.map((handoff) => {
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
1
2
|
import { readCanonicalHandoff } from "../core/handoff.js";
|
|
2
3
|
import {
|
|
3
4
|
readHandoffAcceptanceLedger,
|
|
4
5
|
resolveHandoffAcceptance,
|
|
5
6
|
} from "../core/handoff-acceptance.js";
|
|
6
7
|
|
|
7
|
-
export async function runHandoffShow(
|
|
8
|
+
export async function runHandoffShow(options = {}) {
|
|
9
|
+
return withProjectReadSnapshot(options.target, () => projectHandoffShow(options));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function projectHandoffShow({ target, packageRoot, taskId, handoffId } = {}) {
|
|
8
13
|
const artifact = await readCanonicalHandoff(target, { packageRoot, taskId, handoffId });
|
|
9
14
|
const ledger = await readHandoffAcceptanceLedger(target, packageRoot, { taskId });
|
|
10
15
|
const resolved = resolveHandoffAcceptance({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { migrateProtocol } from "../core/protocol-migration.js";
|
|
2
2
|
|
|
3
|
-
export async function runMigrateProtocol({ target, packageRoot, to, dryRun = false } = {}) {
|
|
4
|
-
return migrateProtocol(target, { to, dryRun, packageRoot });
|
|
3
|
+
export async function runMigrateProtocol({ target, packageRoot, to, dryRun = false, destination, writersQuiesced = false } = {}) {
|
|
4
|
+
return migrateProtocol(target, { to, dryRun, packageRoot, destination, writersQuiesced });
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export function formatMigrateProtocolResult(result) {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { projectModelRoute } from "../core/model-router/router.js";
|
|
2
2
|
import { resolveRequiredSemanticDecision } from "../core/decision/resolver.js";
|
|
3
3
|
import { readCurrentDecisionBindings } from "../core/decision/task-bindings.js";
|
|
4
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
4
5
|
|
|
5
|
-
export async function runModelRoute(
|
|
6
|
+
export async function runModelRoute(options = {}) {
|
|
7
|
+
if (options.target && options.taskId) return withProjectReadSnapshot(options.target, () => projectSnapshotModelRoute(options));
|
|
8
|
+
return projectSnapshotModelRoute(options);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function projectSnapshotModelRoute({ target, packageRoot, taskId, decisionId, workType, surfaces, risks, platforms, behaviorChange, executableChange, generationRequired, architectureChange, ambiguity, semanticRecommendation, contract }) {
|
|
6
12
|
if (target && taskId) {
|
|
7
13
|
const bindings = await readCurrentDecisionBindings(target, packageRoot, taskId);
|
|
14
|
+
// Decision stateFingerprint includes semantic inputs; this reader proves the lifecycle via taskStateFingerprint.
|
|
15
|
+
delete bindings.stateFingerprint;
|
|
8
16
|
const decision = await resolveRequiredSemanticDecision({
|
|
9
17
|
target, packageRoot, taskId, decisionId, decisionKind: "MODEL_ROUTE", currentBindings: bindings,
|
|
10
18
|
});
|
package/src/commands/next.js
CHANGED
|
@@ -3,8 +3,14 @@ import { withResolvedTask } from "../core/task-command.js";
|
|
|
3
3
|
import { readPersistedRoute } from "../core/route-artifact.js";
|
|
4
4
|
import { projectExecutionProfile } from "../core/execution-profile.js";
|
|
5
5
|
import { explainNextAction } from "../core/next-explanation.js";
|
|
6
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
6
7
|
|
|
7
|
-
export async function runNext(
|
|
8
|
+
export async function runNext(options) {
|
|
9
|
+
const read = () => projectNext(options);
|
|
10
|
+
return options.target ? withProjectReadSnapshot(options.target, read) : read();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function projectNext({ target, packageRoot, taskId, task, authorityContext, runtimeContext, compact = false, explain = false }) {
|
|
8
14
|
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
9
15
|
const result = await getNextAction({
|
|
10
16
|
target,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { isOperationalArtifactPath, needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
|
|
4
|
+
import { getOperationalStore, readOperationalText } from "../storage/operational-context.js";
|
|
3
5
|
import { diffPolicies } from "../core/policy-diff.js";
|
|
4
6
|
import {
|
|
5
7
|
loadEffectiveRules,
|
|
@@ -7,6 +9,23 @@ import {
|
|
|
7
9
|
readTaskPolicySnapshot,
|
|
8
10
|
} from "../core/policy-engine.js";
|
|
9
11
|
|
|
12
|
+
function canonicalInputPath(target, file) {
|
|
13
|
+
return path.relative(target, path.resolve(target, file)).replaceAll("\\", "/");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function readPolicyInput(target, file) {
|
|
17
|
+
const relative = canonicalInputPath(target, file);
|
|
18
|
+
const native = readOperationalText(target, relative);
|
|
19
|
+
if (native.selected) {
|
|
20
|
+
if (native.text === null) throw Object.assign(new Error(`Policy input does not exist: ${relative}`), { code: "ENOENT" });
|
|
21
|
+
return JSON.parse(native.text);
|
|
22
|
+
}
|
|
23
|
+
if (getOperationalStore(target) && isOperationalArtifactPath(relative)) {
|
|
24
|
+
throw Object.assign(new Error(`Unsupported canonical policy input: ${relative}`), { code: "E_STORAGE_OPERATION_UNSUPPORTED" });
|
|
25
|
+
}
|
|
26
|
+
return JSON.parse(await readFile(path.resolve(target, file), "utf8"));
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
export async function runPolicyDiff({
|
|
11
30
|
target = process.cwd(),
|
|
12
31
|
packageRoot,
|
|
@@ -14,12 +33,15 @@ export async function runPolicyDiff({
|
|
|
14
33
|
before = null,
|
|
15
34
|
after = null,
|
|
16
35
|
} = {}) {
|
|
36
|
+
if ([before, after].some(file => file && isOperationalArtifactPath(canonicalInputPath(target, file)))
|
|
37
|
+
&& await needsExistingProjectScope(target)) {
|
|
38
|
+
return withExistingProjectScope(target, () => runPolicyDiff({ target, packageRoot, taskId, before, after }), { readOnly: true });
|
|
39
|
+
}
|
|
17
40
|
let beforePolicy;
|
|
18
41
|
let afterPolicy;
|
|
19
42
|
|
|
20
43
|
if (before) {
|
|
21
|
-
|
|
22
|
-
beforePolicy = JSON.parse(rawBefore);
|
|
44
|
+
beforePolicy = await readPolicyInput(target, before);
|
|
23
45
|
} else if (taskId) {
|
|
24
46
|
const snapshot = await readTaskPolicySnapshot(target, taskId, packageRoot);
|
|
25
47
|
beforePolicy = snapshot ? { rules: snapshot.rules, baseline: { entries: [] } } : { rules: [], baseline: { entries: [] } };
|
|
@@ -28,8 +50,7 @@ export async function runPolicyDiff({
|
|
|
28
50
|
}
|
|
29
51
|
|
|
30
52
|
if (after) {
|
|
31
|
-
|
|
32
|
-
afterPolicy = JSON.parse(rawAfter);
|
|
53
|
+
afterPolicy = await readPolicyInput(target, after);
|
|
33
54
|
} else {
|
|
34
55
|
const rules = await loadEffectiveRules(target, packageRoot);
|
|
35
56
|
const baseline = await readBaseline(target, packageRoot);
|
package/src/commands/progress.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { evaluateProgress, PROGRESS_STATUS } from "../core/progress.js";
|
|
2
|
-
import {
|
|
2
|
+
import { withEventLedgerAudit } from "../core/events.js";
|
|
3
3
|
import { readWorkState } from "../core/work-state.js";
|
|
4
4
|
import { resolveTaskContext, TASK_SELECTION_MODES } from "../core/task-context.js";
|
|
5
5
|
|
|
@@ -13,16 +13,22 @@ export async function runProgress({ target, packageRoot, taskId, task }) {
|
|
|
13
13
|
});
|
|
14
14
|
const activeTaskId = resolved.taskId;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
return withEventLedgerAudit(target, packageRoot, { taskId: activeTaskId }, async ledger => {
|
|
17
|
+
if (!ledger.valid) {
|
|
18
|
+
const first = ledger.errors[0] ?? { code: "E_LEDGER_INVALID", message: "Progress requires a valid event ledger" };
|
|
19
|
+
throw Object.assign(new Error(first.message), { code: first.code });
|
|
20
|
+
}
|
|
21
|
+
const state = await readWorkState(target, { packageRoot, taskId: activeTaskId });
|
|
22
|
+
const events = ledger.events;
|
|
23
|
+
|
|
24
|
+
const progress = evaluateProgress({ state, events });
|
|
25
|
+
return {
|
|
26
|
+
taskId: activeTaskId ?? state?.taskId ?? "unknown",
|
|
27
|
+
phase: state?.phase ?? "UNKNOWN",
|
|
28
|
+
verificationCycle: state?.verificationCycle ?? 1,
|
|
29
|
+
...progress,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export function formatProgressResult(result) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertCanonicalPersistence } from "../storage/operational-context.js";
|
|
1
2
|
import { recordDiagnosis } from "../core/diagnosis.js";
|
|
2
3
|
import { recordStructuredDiagnosticCase } from "../core/diagnostic-record.js";
|
|
3
4
|
import { withTaskMutation } from "../core/task-command.js";
|
|
@@ -16,7 +17,9 @@ export async function runRecordDiagnosis({
|
|
|
16
17
|
nextSafeAction,
|
|
17
18
|
taskId,
|
|
18
19
|
task,
|
|
20
|
+
persistence = null,
|
|
19
21
|
}) {
|
|
22
|
+
assertCanonicalPersistence(persistence);
|
|
20
23
|
if (file) {
|
|
21
24
|
const legacyFields = [hypothesis, failureClass, settledBy, nextSafeAction].some((value) => value !== undefined && value !== null)
|
|
22
25
|
|| (Array.isArray(evidenceRefs) && evidenceRefs.length > 0)
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
import { runSemanticPlan as projectPlan } from "../core/semantic-planning/service.js";
|
|
2
2
|
import { resolveRequiredSemanticDecision } from "../core/decision/resolver.js";
|
|
3
3
|
import { readCurrentDecisionBindings } from "../core/decision/task-bindings.js";
|
|
4
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
const PROJECTION_VALUES = Object.freeze({
|
|
7
|
+
failure: Object.freeze({ implementation: "incorrect implementation", expectation: "stale expectation",
|
|
8
|
+
state: "state divergence", dependency: "dependency incompatibility", environment: "network/environment" }),
|
|
9
|
+
diagnosis: Object.freeze({ inspect_state: "inspect state transition", inspect_caller: "inspect caller",
|
|
10
|
+
inspect_dependency: "inspect dependency behavior", narrow_experiment: "run narrower experiment" }),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function projectionRecommendation(kind, decision) {
|
|
14
|
+
const values = PROJECTION_VALUES[kind];
|
|
15
|
+
if (!values) return decision;
|
|
16
|
+
const field = kind === "failure" ? "failureClass" : "priority";
|
|
17
|
+
const value = decision[field];
|
|
18
|
+
if (value === "unknown") return null;
|
|
19
|
+
return { ...decision, [field]: Object.hasOwn(values, value) ? values[value] : value };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function runSemanticPlan(options = {}) {
|
|
23
|
+
if (options.target && options.taskId) return withProjectReadSnapshot(options.target, () => projectSnapshotSemanticPlan(options));
|
|
24
|
+
return projectSnapshotSemanticPlan(options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function projectSnapshotSemanticPlan({ target, packageRoot, taskId, decisionId, kind, input, semanticRecommendation }) {
|
|
6
28
|
if (target && taskId) {
|
|
7
29
|
const decisionKind = { failure: "FAILURE_TRIAGE", diagnosis: "DIAGNOSIS_PRIORITY", review: "REVIEW_PLAN" }[kind];
|
|
8
30
|
const bindings = await readCurrentDecisionBindings(target, packageRoot, taskId);
|
|
31
|
+
// Decision stateFingerprint includes semantic inputs; this reader proves the lifecycle via taskStateFingerprint.
|
|
32
|
+
delete bindings.stateFingerprint;
|
|
9
33
|
const decision = await resolveRequiredSemanticDecision({ target, packageRoot, taskId, decisionId, decisionKind, currentBindings: bindings });
|
|
10
|
-
semanticRecommendation = decision.decision;
|
|
34
|
+
semanticRecommendation = projectionRecommendation(kind, decision.decision);
|
|
11
35
|
}
|
|
12
36
|
return projectPlan({ kind, input, semanticRecommendation });
|
|
13
37
|
}
|