@cassiomc1/forgeloop 1.3.0 → 1.6.0
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/.github/copilot-instructions.md +1 -0
- package/AGENTS.md +1 -0
- package/CLAUDE.md +1 -0
- package/DOCS_INDEX.md +20 -8
- package/EXECUTION_STATE.md +60 -0
- package/LOOP_ENGINEERING.md +135 -5
- package/LOOP_SYSTEM_DESIGN.md +54 -1
- package/PROTOCOL_INTEGRATION.md +87 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +69 -9
- package/TERMINOLOGY.md +15 -0
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/THREAT_MODEL.md +59 -1
- package/docs/ARTIFACT_REFERENCE.md +183 -0
- package/docs/CLI_REFERENCE.md +391 -6
- package/docs/CROSS_HARNESS_CONTINUITY.md +23 -0
- package/docs/DIAGNOSTIC_MODEL.md +181 -0
- package/docs/DOCUMENTATION_GUIDE.md +36 -13
- package/docs/EXECUTION_TRACE.md +76 -0
- package/docs/GETTING_STARTED.md +1 -0
- package/docs/MCP.md +159 -0
- package/docs/RECIPES.md +149 -0
- package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
- package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
- package/docs/TROUBLESHOOTING.md +217 -3
- package/docs/UNIVERSAL_INTEGRATION.md +48 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +13797 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +37 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5002 -0
- package/docs/diagrams/README.md +55 -0
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +122 -0
- package/docs/diagrams/manifest.json +42 -0
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +20 -0
- package/package.json +21 -8
- package/schemas/action.schema.json +100 -0
- package/schemas/approval.schema.json +51 -0
- package/schemas/capability-policy.schema.json +41 -0
- package/schemas/diagnostic-case.schema.json +85 -0
- package/schemas/execution-receipt.schema.json +16 -0
- package/schemas/hypothesis-disposition.schema.json +16 -0
- package/schemas/intervention.schema.json +27 -0
- package/schemas/policy-lock.schema.json +1 -0
- package/schemas/policy-snapshot.schema.json +2 -0
- package/schemas/task-recovery.schema.json +61 -0
- package/schemas/trajectory-evaluation.schema.json +64 -0
- package/schemas/trajectory-scenario.schema.json +42 -0
- package/src/cli.js +267 -347
- package/src/commands/action-authorize.js +41 -0
- package/src/commands/action-propose.js +10 -0
- package/src/commands/action-reconcile.js +10 -0
- package/src/commands/action-record.js +47 -0
- package/src/commands/action-show.js +10 -0
- package/src/commands/action-verify.js +10 -0
- package/src/commands/advance.js +7 -2
- package/src/commands/approval-request.js +64 -0
- package/src/commands/approval-resolve.js +10 -0
- package/src/commands/audit.js +5 -0
- package/src/commands/baseline.js +3 -3
- package/src/commands/eval.js +6 -0
- package/src/commands/history.js +18 -0
- package/src/commands/init.js +2 -2
- package/src/commands/inspect.js +55 -0
- package/src/commands/metrics.js +7 -0
- package/src/commands/next.js +8 -2
- package/src/commands/policy-discover.js +2 -2
- package/src/commands/progress.js +6 -2
- package/src/commands/record-diagnosis.js +37 -1
- package/src/commands/record-hypothesis-disposition.js +45 -0
- package/src/commands/record-intervention.js +35 -0
- package/src/commands/reflect.js +38 -0
- package/src/commands/report.js +9 -1
- package/src/commands/run-action.js +18 -0
- package/src/commands/status.js +17 -0
- package/src/commands/task-create.js +39 -1
- package/src/commands/task-list.js +14 -1
- package/src/commands/task-lock-status.js +2 -2
- package/src/commands/task-recover.js +202 -0
- package/src/commands/task-repair-legacy-recovery.js +417 -0
- package/src/commands/task-resume.js +172 -0
- package/src/commands/task-scope.js +23 -4
- package/src/commands/task-show.js +18 -4
- package/src/commands/trace.js +34 -0
- package/src/commands/validate-protocol.js +40 -15
- package/src/core/action-authorization.js +106 -0
- package/src/core/action-constants.js +86 -0
- package/src/core/action-execution.js +105 -0
- package/src/core/action-ledger-projection.js +302 -0
- package/src/core/action-model.js +581 -0
- package/src/core/action-readiness.js +141 -0
- package/src/core/action-reconciliation-policy.js +49 -0
- package/src/core/action-reconciliation.js +66 -0
- package/src/core/action-verification.js +111 -0
- package/src/core/actions.js +462 -0
- package/src/core/approvals.js +405 -0
- package/src/core/artifact-registry.js +60 -0
- package/src/core/audit.js +45 -4
- package/src/core/bundles.js +30 -0
- package/src/core/capability-policy.js +226 -0
- package/src/core/cli-command-definitions.js +260 -5
- package/src/core/command-executors.js +543 -0
- package/src/core/command-input.js +107 -0
- package/src/core/command-runtime.js +117 -0
- package/src/core/completion-artifacts.js +39 -15
- package/src/core/completion-ownership.js +88 -0
- package/src/core/completion-recovery-rebind.js +194 -0
- package/src/core/completion.js +70 -0
- package/src/core/continuity-reconciliation.js +24 -5
- package/src/core/diagnostic-model.js +396 -0
- package/src/core/diagnostic-projection.js +51 -0
- package/src/core/diagnostic-record.js +360 -0
- package/src/core/error-codes.js +461 -1
- package/src/core/events.js +171 -2
- package/src/core/execution-prerequisites.js +4 -1
- package/src/core/execution.js +26 -188
- package/src/core/failure-signature.js +70 -0
- package/src/core/failure-surface.js +57 -0
- package/src/core/filesystem.js +55 -6
- package/src/core/history.js +110 -0
- package/src/core/hypothesis-projection.js +85 -0
- package/src/core/information-gain-projection.js +283 -0
- package/src/core/information-gain.js +138 -0
- package/src/core/inspect.js +132 -7
- package/src/core/integration-invocation-policy.js +217 -0
- package/src/core/integration-limits.js +20 -0
- package/src/core/integration-resources.js +178 -0
- package/src/core/next-action-model.js +94 -0
- package/src/core/next-action.js +490 -3
- package/src/core/phase.js +42 -22
- package/src/core/policy-engine.js +113 -6
- package/src/core/preflight-consistency.js +31 -5
- package/src/core/preflight.js +19 -2
- package/src/core/prepared-execution.js +227 -0
- package/src/core/progress.js +41 -4
- package/src/core/project-root.js +21 -0
- package/src/core/protocol-info.js +61 -0
- package/src/core/protocol.js +14 -0
- package/src/core/receipt.js +1 -0
- package/src/core/reconcile-closure.js +35 -10
- package/src/core/recovery-history.js +116 -0
- package/src/core/reflection.js +305 -0
- package/src/core/resumability.js +57 -3
- package/src/core/schema-validation.js +9 -0
- package/src/core/strategy-analysis.js +97 -0
- package/src/core/task-claim-state.js +272 -0
- package/src/core/task-command.js +5 -1
- package/src/core/task-conflict-inspection.js +321 -0
- package/src/core/task-context.js +32 -29
- package/src/core/task-discovery.js +14 -1
- package/src/core/task-lock.js +216 -22
- package/src/core/task-paths.js +31 -2
- package/src/core/task-recovery-migration.js +192 -0
- package/src/core/task-recovery.js +205 -0
- package/src/core/task-scope.js +33 -1
- package/src/core/task-snapshot.js +53 -0
- package/src/core/templates.js +9 -0
- package/src/core/trace.js +548 -0
- package/src/core/trajectory-evaluation.js +71 -0
- package/src/core/trajectory-metrics.js +80 -0
- package/src/core/transaction.js +36 -2
- package/src/core/work-state.js +10 -5
- package/src/integration.js +47 -0
- package/docs/assets/forgeloop-flow.svg +0 -1
- package/docs/forgeloop-flow.mmd +0 -51
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { PROTOCOL_VERSION } from "./protocol.js";
|
|
2
|
+
import { LEGACY_RECOVERY_MIGRATION_EVENT } from "./task-recovery-migration.js";
|
|
3
|
+
import { readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
|
|
4
|
+
import {
|
|
5
|
+
E_TASK_RECOVERY_AUTHORITY_INVALID,
|
|
6
|
+
} from "./error-codes.js";
|
|
7
|
+
import { taskArtifactPath } from "./task-paths.js";
|
|
8
|
+
import { getActiveTaskTransaction } from "./transaction.js";
|
|
9
|
+
import { classifyRecoveryHistory } from "./recovery-history.js";
|
|
10
|
+
|
|
11
|
+
export const TASK_RECOVERY_SCHEMA_VERSION = 1;
|
|
12
|
+
export const TASK_RECOVERY_EVENT_TYPES = Object.freeze(new Set([
|
|
13
|
+
"TASK_RECOVERY_RECORDED",
|
|
14
|
+
"OPERATOR_RECOVERY_RECORDED",
|
|
15
|
+
LEGACY_RECOVERY_MIGRATION_EVENT,
|
|
16
|
+
]));
|
|
17
|
+
|
|
18
|
+
export function createTaskRecovery({
|
|
19
|
+
taskId,
|
|
20
|
+
recoveredAt,
|
|
21
|
+
recoveryId,
|
|
22
|
+
recoveryEventSeq,
|
|
23
|
+
classificationAtRecovery,
|
|
24
|
+
reasonCodes,
|
|
25
|
+
releasedClaims,
|
|
26
|
+
previousPhase,
|
|
27
|
+
previousRevision,
|
|
28
|
+
repositoryFingerprint,
|
|
29
|
+
authority,
|
|
30
|
+
}) {
|
|
31
|
+
const recovery = {
|
|
32
|
+
schemaVersion: TASK_RECOVERY_SCHEMA_VERSION,
|
|
33
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
34
|
+
taskId,
|
|
35
|
+
status: "RECOVERED",
|
|
36
|
+
recoveredAt,
|
|
37
|
+
recoveryId,
|
|
38
|
+
recoveryEventSeq,
|
|
39
|
+
classificationAtRecovery,
|
|
40
|
+
reasonCodes: [...reasonCodes],
|
|
41
|
+
releasedClaims: [...releasedClaims],
|
|
42
|
+
previousPhase,
|
|
43
|
+
previousRevision,
|
|
44
|
+
repositoryFingerprint: { ...repositoryFingerprint },
|
|
45
|
+
authority: { ...authority },
|
|
46
|
+
};
|
|
47
|
+
assertTaskRecoveryAuthority(recovery);
|
|
48
|
+
return recovery;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function assertTaskRecoveryAuthority(recovery) {
|
|
52
|
+
const authority = recovery?.authority;
|
|
53
|
+
const validCaller = authority?.kind === "CALLER_ACKNOWLEDGED" && authority.grantRef === undefined;
|
|
54
|
+
const validHost = authority?.kind === "HOST_ATTESTED"
|
|
55
|
+
&& typeof authority.grantRef === "string"
|
|
56
|
+
&& authority.grantRef.trim() !== "";
|
|
57
|
+
if (validCaller || validHost) return recovery;
|
|
58
|
+
const error = new Error(
|
|
59
|
+
"Recovery authority must be caller acknowledgement or host attestation with a trusted grant reference",
|
|
60
|
+
);
|
|
61
|
+
error.code = E_TASK_RECOVERY_AUTHORITY_INVALID;
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function readTaskRecovery(target, { taskId, packageRoot } = {}) {
|
|
66
|
+
try {
|
|
67
|
+
const artifact = await readJsonArtifact(target, taskArtifactPath(taskId, "recovery"), "task-recovery", packageRoot);
|
|
68
|
+
assertTaskRecoveryAuthority(artifact.value);
|
|
69
|
+
return artifact;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error.code === "ARTIFACT_MISSING") return null;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function writeTaskRecovery(target, recovery, packageRoot) {
|
|
77
|
+
assertTaskRecoveryAuthority(recovery);
|
|
78
|
+
return writeJsonArtifact(
|
|
79
|
+
target,
|
|
80
|
+
taskArtifactPath(recovery.taskId, "recovery"),
|
|
81
|
+
recovery,
|
|
82
|
+
"task-recovery",
|
|
83
|
+
packageRoot,
|
|
84
|
+
{ taskId: recovery.taskId, operation: "write-task-recovery" },
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function clearTaskRecovery(target, { taskId } = {}) {
|
|
89
|
+
const transaction = getActiveTaskTransaction();
|
|
90
|
+
if (!transaction) {
|
|
91
|
+
throw new Error("clearTaskRecovery requires an active task transaction");
|
|
92
|
+
}
|
|
93
|
+
await transaction.stageDelete(taskArtifactPath(taskId, "recovery"));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function isTaskRecovered(recovery) {
|
|
97
|
+
return recovery?.status === "RECOVERED";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function assertTaskNotRecovered(target, { taskId, packageRoot } = {}) {
|
|
101
|
+
const { assertTaskMutationAllowed } = await import("./task-claim-state.js");
|
|
102
|
+
return assertTaskMutationAllowed(target, { taskId, packageRoot });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function effectiveTaskClaims({
|
|
106
|
+
validatedClaimState = null,
|
|
107
|
+
historicalWriteClaims = null,
|
|
108
|
+
writeClaims = [],
|
|
109
|
+
} = {}) {
|
|
110
|
+
const historical = historicalWriteClaims ?? writeClaims;
|
|
111
|
+
// Only VALIDATED canonical ownership may release claims. A bare
|
|
112
|
+
// `phase === "COMPLETE"` never proves completion ownership.
|
|
113
|
+
const validatedRelease = validatedClaimState?.valid === true
|
|
114
|
+
&& ["RELEASED_BY_COMPLETION", "RELEASED_BY_RECOVERY"].includes(validatedClaimState.claimState);
|
|
115
|
+
return validatedRelease ? [] : [...historical];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function taskClaimProjection({
|
|
119
|
+
validatedClaimState = null,
|
|
120
|
+
historicalWriteClaims: suppliedHistoricalClaims = null,
|
|
121
|
+
writeClaims = [],
|
|
122
|
+
} = {}) {
|
|
123
|
+
const historicalWriteClaims = [...(suppliedHistoricalClaims ?? writeClaims)];
|
|
124
|
+
const effectiveWriteClaims = effectiveTaskClaims({
|
|
125
|
+
validatedClaimState,
|
|
126
|
+
historicalWriteClaims,
|
|
127
|
+
});
|
|
128
|
+
const claimState = validatedClaimState?.valid === true
|
|
129
|
+
? validatedClaimState.claimState
|
|
130
|
+
: "ACTIVE";
|
|
131
|
+
return {
|
|
132
|
+
writeClaims: effectiveWriteClaims,
|
|
133
|
+
historicalWriteClaims,
|
|
134
|
+
effectiveWriteClaims,
|
|
135
|
+
claimState,
|
|
136
|
+
mutationAllowed: claimState === "ACTIVE",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function sameList(left, right) {
|
|
141
|
+
return Array.isArray(left)
|
|
142
|
+
&& Array.isArray(right)
|
|
143
|
+
&& left.length === right.length
|
|
144
|
+
&& left.every((value, index) => value === right[index]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function recoveryConsistencyError(message) {
|
|
148
|
+
return { code: "E_TASK_RECOVERY_INCONSISTENT", message };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function validateTaskRecoveryConsistency({
|
|
152
|
+
taskId,
|
|
153
|
+
recovery = null,
|
|
154
|
+
events = [],
|
|
155
|
+
historicalWriteClaims = null,
|
|
156
|
+
recoveryHistory = null,
|
|
157
|
+
} = {}) {
|
|
158
|
+
const history = recoveryHistory ?? classifyRecoveryHistory(events);
|
|
159
|
+
const errors = [...history.errors];
|
|
160
|
+
|
|
161
|
+
if (!recovery) {
|
|
162
|
+
if (history.valid && history.activeRecovery) {
|
|
163
|
+
errors.push(recoveryConsistencyError(
|
|
164
|
+
`Recovery event ${history.activeRecovery.recoveryId} has neither an active recovery artifact nor a resume event`,
|
|
165
|
+
));
|
|
166
|
+
}
|
|
167
|
+
return errors;
|
|
168
|
+
}
|
|
169
|
+
if (!history.activeRecovery || history.activeRecovery.recoveryId !== recovery.recoveryId) {
|
|
170
|
+
errors.push(recoveryConsistencyError(
|
|
171
|
+
`Recovery artifact ${recovery.recoveryId ?? "unknown"} does not match the active recovery history`,
|
|
172
|
+
));
|
|
173
|
+
}
|
|
174
|
+
if (recovery.taskId !== taskId) {
|
|
175
|
+
errors.push(recoveryConsistencyError(`Recovery artifact belongs to ${recovery.taskId}, not ${taskId}`));
|
|
176
|
+
}
|
|
177
|
+
const event = events.find((candidate) => candidate.seq === recovery.recoveryEventSeq);
|
|
178
|
+
if (!event || !TASK_RECOVERY_EVENT_TYPES.has(event.event)) {
|
|
179
|
+
errors.push(recoveryConsistencyError(
|
|
180
|
+
`Recovery artifact references missing recovery event seq ${recovery.recoveryEventSeq}`,
|
|
181
|
+
));
|
|
182
|
+
return errors;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const comparisons = [
|
|
186
|
+
[event.taskId === recovery.taskId, "taskId"],
|
|
187
|
+
[event.at === recovery.recoveredAt, "recoveredAt"],
|
|
188
|
+
[event.details?.recoveryId === recovery.recoveryId, "recoveryId"],
|
|
189
|
+
[event.details?.classification === recovery.classificationAtRecovery, "classificationAtRecovery"],
|
|
190
|
+
[sameList(event.details?.reasonCodes, recovery.reasonCodes), "reasonCodes"],
|
|
191
|
+
[sameList(event.details?.releasedClaims, recovery.releasedClaims), "releasedClaims"],
|
|
192
|
+
[event.details?.previousPhase === recovery.previousPhase, "previousPhase"],
|
|
193
|
+
[event.details?.previousRevision === recovery.previousRevision, "previousRevision"],
|
|
194
|
+
[event.details?.currentBranch === recovery.repositoryFingerprint?.branch, "repositoryFingerprint.branch"],
|
|
195
|
+
[event.details?.currentHead === recovery.repositoryFingerprint?.head, "repositoryFingerprint.head"],
|
|
196
|
+
[event.details?.authorityKind === recovery.authority?.kind, "authority.kind"],
|
|
197
|
+
];
|
|
198
|
+
for (const [matches, field] of comparisons) {
|
|
199
|
+
if (!matches) errors.push(recoveryConsistencyError(`Recovery artifact and ledger disagree on ${field}`));
|
|
200
|
+
}
|
|
201
|
+
if (historicalWriteClaims && !sameList(historicalWriteClaims, recovery.releasedClaims)) {
|
|
202
|
+
errors.push(recoveryConsistencyError("Recovery releasedClaims do not match the task descriptor's historical claims"));
|
|
203
|
+
}
|
|
204
|
+
return errors;
|
|
205
|
+
}
|
package/src/core/task-scope.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import {
|
|
3
3
|
E_TASK_CHANGE_OUTSIDE_SCOPE,
|
|
4
|
+
E_TASK_CLAIM_OWNERSHIP_INCONSISTENT,
|
|
4
5
|
E_TASK_DESCRIPTOR_INVALID,
|
|
5
6
|
E_TASK_SCOPE_CONFLICT,
|
|
6
7
|
E_TASK_SCOPE_DIRTY,
|
|
@@ -93,7 +94,9 @@ export function checkScopeConflicts(newClaims, existingTasks = [], currentTaskId
|
|
|
93
94
|
// Only non-COMPLETE tasks hold active write claims
|
|
94
95
|
if (task.phase === "COMPLETE") continue;
|
|
95
96
|
|
|
96
|
-
const taskClaims = normalizeWriteClaims(task.
|
|
97
|
+
const taskClaims = normalizeWriteClaims(task.claimState === undefined
|
|
98
|
+
? (task.writeClaims ?? task.descriptor?.writeClaims ?? [])
|
|
99
|
+
: (task.effectiveWriteClaims ?? []));
|
|
97
100
|
for (const newClaim of normalizedNew) {
|
|
98
101
|
for (const existingClaim of taskClaims) {
|
|
99
102
|
if (claimsOverlap(newClaim, existingClaim)) {
|
|
@@ -112,6 +115,35 @@ export function checkScopeConflicts(newClaims, existingTasks = [], currentTaskId
|
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
export function assertNoScopeConflicts(newClaims, existingTasks = [], currentTaskId = null) {
|
|
118
|
+
const normalizedNew = normalizeWriteClaims(newClaims);
|
|
119
|
+
if (normalizedNew.length > 0) {
|
|
120
|
+
const ownershipBlockers = existingTasks.filter((task) => {
|
|
121
|
+
if (task.taskId === currentTaskId) return false;
|
|
122
|
+
if (task.healthy === false) return true;
|
|
123
|
+
if (task.claimState !== "INCONSISTENT" && task.ownershipValid !== false) return false;
|
|
124
|
+
const retainedClaims = normalizeWriteClaims(
|
|
125
|
+
task.effectiveWriteClaims ?? task.writeClaims ?? task.descriptor?.writeClaims ?? [],
|
|
126
|
+
);
|
|
127
|
+
return retainedClaims.length === 0
|
|
128
|
+
|| normalizedNew.some((claim) => retainedClaims.some((retained) => claimsOverlap(claim, retained)));
|
|
129
|
+
});
|
|
130
|
+
if (ownershipBlockers.length > 0) {
|
|
131
|
+
const error = new Error(
|
|
132
|
+
`Cannot acquire write claims while task ownership is inconsistent: ${ownershipBlockers
|
|
133
|
+
.map((task) => task.taskId ?? task.taskKey)
|
|
134
|
+
.join(", ")}`,
|
|
135
|
+
);
|
|
136
|
+
error.code = E_TASK_CLAIM_OWNERSHIP_INCONSISTENT;
|
|
137
|
+
error.tasks = ownershipBlockers.map((task) => ({
|
|
138
|
+
taskId: task.taskId ?? null,
|
|
139
|
+
taskKey: task.taskKey ?? null,
|
|
140
|
+
claimState: task.claimState ?? "INCONSISTENT",
|
|
141
|
+
reasonCodes: task.reasonCodes ?? [task.error?.code].filter(Boolean),
|
|
142
|
+
}));
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
115
147
|
const conflicts = checkScopeConflicts(newClaims, existingTasks, currentTaskId);
|
|
116
148
|
if (conflicts.length > 0) {
|
|
117
149
|
const first = conflicts[0];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readEvents, validateEventLedger } from "./events.js";
|
|
2
|
+
import { readWorkState } from "./work-state.js";
|
|
3
|
+
import { canonicalFingerprint } from "./artifacts.js";
|
|
4
|
+
|
|
5
|
+
function ledgerTail(events) {
|
|
6
|
+
const last = events.at(-1) ?? null;
|
|
7
|
+
return {
|
|
8
|
+
sequence: last?.seq ?? 0,
|
|
9
|
+
hash: last?.hash ?? null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function buildTaskSnapshot({
|
|
14
|
+
target,
|
|
15
|
+
packageRoot,
|
|
16
|
+
taskId = null,
|
|
17
|
+
eventsPath = null,
|
|
18
|
+
stateFile = null,
|
|
19
|
+
} = {}) {
|
|
20
|
+
const options = { packageRoot, taskId, ...(eventsPath ? { eventsPath } : {}) };
|
|
21
|
+
const state = await readWorkState(target, { ...options, ...(stateFile ? { statePath: stateFile } : {}) });
|
|
22
|
+
const events = await readEvents(target, packageRoot, options);
|
|
23
|
+
const before = {
|
|
24
|
+
stateRevision: state?.revision ?? null,
|
|
25
|
+
...ledgerTail(events),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const validation = await validateEventLedger(target, packageRoot, options);
|
|
29
|
+
|
|
30
|
+
const rereadState = await readWorkState(target, { ...options, ...(stateFile ? { statePath: stateFile } : {}) });
|
|
31
|
+
const rereadEvents = await readEvents(target, packageRoot, options);
|
|
32
|
+
const after = {
|
|
33
|
+
stateRevision: rereadState?.revision ?? null,
|
|
34
|
+
...ledgerTail(rereadEvents),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const consistent = JSON.stringify(before) === JSON.stringify(after);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
consistent,
|
|
41
|
+
taskId: taskId ?? state?.taskId ?? rereadState?.taskId ?? null,
|
|
42
|
+
anchors: before,
|
|
43
|
+
capturedAt: new Date().toISOString(),
|
|
44
|
+
integrity: {
|
|
45
|
+
valid: validation.valid,
|
|
46
|
+
errors: validation.errors,
|
|
47
|
+
eventCount: validation.events.length,
|
|
48
|
+
fingerprint: canonicalFingerprint(validation.events.map(({ seq, event, at }) => ({ seq, event, at }))),
|
|
49
|
+
},
|
|
50
|
+
state: rereadState,
|
|
51
|
+
events: validation.events,
|
|
52
|
+
};
|
|
53
|
+
}
|
package/src/core/templates.js
CHANGED
|
@@ -66,6 +66,15 @@ export const TEMPLATE_PATHS = [
|
|
|
66
66
|
"schemas/task-bundle.schema.json",
|
|
67
67
|
"schemas/authority.schema.json",
|
|
68
68
|
"schemas/task-descriptor.schema.json",
|
|
69
|
+
"schemas/task-recovery.schema.json",
|
|
70
|
+
"schemas/diagnostic-case.schema.json",
|
|
71
|
+
"schemas/intervention.schema.json",
|
|
72
|
+
"schemas/hypothesis-disposition.schema.json",
|
|
73
|
+
"schemas/action.schema.json",
|
|
74
|
+
"schemas/approval.schema.json",
|
|
75
|
+
"schemas/capability-policy.schema.json",
|
|
76
|
+
"schemas/trajectory-evaluation.schema.json",
|
|
77
|
+
"schemas/trajectory-scenario.schema.json",
|
|
69
78
|
];
|
|
70
79
|
|
|
71
80
|
export function getPackageRoot() {
|