@cassiomc1/forgeloop 1.2.4 → 1.5.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/AGENT_COMPATIBILITY.md +4 -0
- package/CLAUDE.md +1 -0
- package/DOCS_INDEX.md +14 -0
- package/EXECUTION_STATE.md +48 -0
- package/LOOP_ENGINEERING.md +55 -6
- package/LOOP_SYSTEM_DESIGN.md +32 -1
- package/PROTOCOL_INTEGRATION.md +71 -0
- package/README.md +86 -0
- package/TERMINOLOGY.md +15 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/THREAT_MODEL.md +22 -1
- package/docs/ARTIFACT_REFERENCE.md +54 -0
- package/docs/CLI_REFERENCE.md +177 -5
- package/docs/CROSS_HARNESS_CONTINUITY.md +34 -0
- package/docs/DOCUMENTATION_GUIDE.md +31 -0
- package/docs/GETTING_STARTED.md +10 -0
- package/docs/MCP.md +126 -0
- package/docs/RECIPES.md +87 -1
- package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
- package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
- package/docs/TROUBLESHOOTING.md +243 -40
- package/docs/UNIVERSAL_INTEGRATION.md +48 -0
- package/package.json +17 -3
- package/schemas/execution.schema.json +11 -1
- package/schemas/task-recovery.schema.json +61 -0
- package/schemas/work-state.schema.json +1 -0
- package/src/cli.js +182 -337
- package/src/commands/audit.js +5 -0
- package/src/commands/doctor.js +22 -0
- package/src/commands/inspect.js +6 -0
- package/src/commands/migrate-protocol.js +18 -0
- package/src/commands/progress.js +6 -2
- package/src/commands/protocol-info.js +16 -0
- package/src/commands/run-check.js +2 -0
- package/src/commands/status.js +17 -0
- package/src/commands/task-create.js +42 -3
- package/src/commands/task-list.js +14 -1
- package/src/commands/task-lock-status.js +29 -0
- 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 +21 -7
- package/src/commands/task-unlock.js +8 -6
- package/src/commands/validate-protocol.js +19 -2
- package/src/core/artifact-registry.js +12 -0
- package/src/core/artifacts.js +17 -4
- package/src/core/audit.js +20 -4
- package/src/core/bundles.js +15 -0
- package/src/core/cli-command-definitions.js +94 -4
- package/src/core/command-executors.js +387 -0
- package/src/core/command-input.js +107 -0
- package/src/core/command-runtime.js +106 -0
- package/src/core/completion-artifacts.js +17 -6
- package/src/core/completion-ownership.js +88 -0
- package/src/core/completion.js +15 -3
- package/src/core/diagnosis.js +15 -11
- package/src/core/error-codes.js +136 -1
- package/src/core/events.js +239 -9
- package/src/core/execution.js +73 -9
- package/src/core/filesystem.js +75 -8
- package/src/core/inspect.js +27 -0
- package/src/core/integration-invocation-policy.js +170 -0
- package/src/core/integration-limits.js +20 -0
- package/src/core/integration-resources.js +127 -0
- package/src/core/next-action-model.js +60 -0
- package/src/core/next-action.js +31 -0
- package/src/core/phase.js +10 -3
- package/src/core/project-root.js +21 -0
- package/src/core/protocol-info.js +54 -0
- package/src/core/protocol-migration.js +59 -0
- package/src/core/reconcile-closure.js +54 -14
- package/src/core/recovery-history.js +116 -0
- package/src/core/resumability.js +8 -6
- package/src/core/schema-validation.js +1 -0
- package/src/core/task-claim-state.js +272 -0
- package/src/core/task-command.js +8 -4
- 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 +248 -18
- package/src/core/task-migration.js +24 -1
- package/src/core/task-paths.js +6 -3
- 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/templates.js +1 -0
- package/src/core/transaction.js +285 -0
- package/src/core/work-state.js +70 -6
- package/src/integration.js +47 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { LIFECYCLE_MILESTONES, validateStateLedgerCoherence } from "./events.js";
|
|
2
|
+
|
|
3
|
+
export const CANONICAL_COMPLETION_EVENT = "COMPLETION_VALIDATED";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Canonical completion ownership proof: the minimal, validator-backed evidence
|
|
7
|
+
* that the lifecycle itself officially reached COMPLETE. This is intentionally
|
|
8
|
+
* NOT a re-run of full publication/receipt/evidence semantics — it only proves
|
|
9
|
+
* that claim ownership may be released because canonical completion exists.
|
|
10
|
+
*
|
|
11
|
+
* Returns `{ valid: true, completionEvent }` or `{ valid: false, errors }`.
|
|
12
|
+
*/
|
|
13
|
+
export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
|
|
14
|
+
const errors = [];
|
|
15
|
+
if (!taskId || typeof taskId !== "string") {
|
|
16
|
+
return { valid: false, errors: [{ code: "E_COMPLETION_OWNERSHIP_UNPROVEN", message: "Completion ownership proof requires a taskId" }] };
|
|
17
|
+
}
|
|
18
|
+
if (!state || state.phase !== "COMPLETE") {
|
|
19
|
+
errors.push({ code: "E_COMPLETION_OWNERSHIP_UNPROVEN", message: "Work-state phase is not COMPLETE" });
|
|
20
|
+
}
|
|
21
|
+
if (!ledger || ledger.valid !== true) {
|
|
22
|
+
errors.push({
|
|
23
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
24
|
+
message: `Task event ledger is invalid; completion cannot be proven${ledger?.errors?.length
|
|
25
|
+
? `: ${ledger.errors.map((error) => error.message).join("; ")}`
|
|
26
|
+
: ""}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let completionEvent = null;
|
|
31
|
+
if (ledger && Array.isArray(ledger.events)) {
|
|
32
|
+
if (ledger.events.some((event) => event.taskId !== taskId)) {
|
|
33
|
+
errors.push({
|
|
34
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
35
|
+
message: "Ledger contains an event belonging to a different task",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const candidates = ledger.events
|
|
39
|
+
.filter((event) => event.event === CANONICAL_COMPLETION_EVENT && event.taskId === taskId);
|
|
40
|
+
if (candidates.length === 0) {
|
|
41
|
+
errors.push({
|
|
42
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
43
|
+
message: `No canonical ${CANONICAL_COMPLETION_EVENT} event exists for this task`,
|
|
44
|
+
});
|
|
45
|
+
} else if (candidates.length > 1) {
|
|
46
|
+
errors.push({
|
|
47
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
48
|
+
message: `Multiple ${CANONICAL_COMPLETION_EVENT} events exist; completion is ambiguous`,
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
completionEvent = candidates[0];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (state && ledger && Array.isArray(ledger.events)) {
|
|
56
|
+
const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
|
|
57
|
+
if (coherenceErrors.length > 0) {
|
|
58
|
+
for (const error of coherenceErrors) {
|
|
59
|
+
errors.push({
|
|
60
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
61
|
+
message: `State/ledger coherence invalid: ${error.message}`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// No contradictory lifecycle activity may follow the canonical completion:
|
|
68
|
+
// any milestone at or after VERIFICATION_RECORDED occurring after the
|
|
69
|
+
// completion event means the lifecycle moved past terminal state.
|
|
70
|
+
if (completionEvent && ledger && Array.isArray(ledger.events)) {
|
|
71
|
+
const completionIndex = ledger.events.indexOf(completionEvent);
|
|
72
|
+
const contradiction = ledger.events.slice(completionIndex + 1).find((event) => {
|
|
73
|
+
const index = LIFECYCLE_MILESTONES.indexOf(event.event);
|
|
74
|
+
return index >= LIFECYCLE_MILESTONES.indexOf("VERIFICATION_RECORDED");
|
|
75
|
+
});
|
|
76
|
+
if (contradiction) {
|
|
77
|
+
errors.push({
|
|
78
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
79
|
+
message: `Lifecycle event ${contradiction.event} follows canonical completion; terminal state contradicted`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (errors.length > 0) {
|
|
85
|
+
return { valid: false, completionEvent: null, errors };
|
|
86
|
+
}
|
|
87
|
+
return { valid: true, completionEvent };
|
|
88
|
+
}
|
package/src/core/completion.js
CHANGED
|
@@ -4,7 +4,7 @@ import { appendProtocolEvent, LIFECYCLE_MILESTONES, validateEventLedger, validat
|
|
|
4
4
|
import { evaluatePreflight } from "./preflight.js";
|
|
5
5
|
import { readContract } from "./contract.js";
|
|
6
6
|
import { readPersistedRoute } from "./route-artifact.js";
|
|
7
|
-
import { readWorkState,
|
|
7
|
+
import { readWorkState, mutateWorkState, classifyLoadedWorkState } from "./work-state.js";
|
|
8
8
|
import { createReceipt, validateReceipt } from "./receipt.js";
|
|
9
9
|
import { completionRelationshipErrors } from "./completion-relationships.js";
|
|
10
10
|
import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
|
|
@@ -517,7 +517,13 @@ export async function runComplete({
|
|
|
517
517
|
} catch {
|
|
518
518
|
// The evaluator already reports a missing or invalid receipt; evidence-only rejection can persist without one.
|
|
519
519
|
}
|
|
520
|
-
|
|
520
|
+
next.revision = (state.revision ?? 0) + 1;
|
|
521
|
+
await mutateWorkState(target, {
|
|
522
|
+
expectedRevision: state.revision ?? 0,
|
|
523
|
+
packageRoot,
|
|
524
|
+
taskId,
|
|
525
|
+
statePath,
|
|
526
|
+
}, () => next);
|
|
521
527
|
let nextReceipt = null;
|
|
522
528
|
if (receipt) {
|
|
523
529
|
nextReceipt = await createReceipt({
|
|
@@ -565,12 +571,18 @@ export async function runComplete({
|
|
|
565
571
|
publicationStatus: result.publicationStatus,
|
|
566
572
|
lastUpdated: new Date().toISOString(),
|
|
567
573
|
};
|
|
574
|
+
next.revision = (state.revision ?? 0) + 1;
|
|
568
575
|
const nextReceipt = await createReceipt({
|
|
569
576
|
...receipt.value,
|
|
570
577
|
stateFingerprint: canonicalFingerprint(next),
|
|
571
578
|
verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
|
|
572
579
|
}, packageRoot, { target, taskId: state.taskId, authorityContext, runtimeContext });
|
|
573
|
-
await
|
|
580
|
+
await mutateWorkState(target, {
|
|
581
|
+
expectedRevision: state.revision ?? 0,
|
|
582
|
+
packageRoot,
|
|
583
|
+
taskId,
|
|
584
|
+
statePath,
|
|
585
|
+
}, () => next);
|
|
574
586
|
await writeJsonArtifact(target, receiptRel, nextReceipt, "execution-receipt", packageRoot);
|
|
575
587
|
}
|
|
576
588
|
const contract = await readContract(target, packageRoot, { taskId, contractPath });
|
package/src/core/diagnosis.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { appendProtocolEvent, validateEventLedger } from "./events.js";
|
|
2
|
-
import { readWorkState,
|
|
2
|
+
import { readWorkState, mutateWorkState } from "./work-state.js";
|
|
3
3
|
import {
|
|
4
4
|
DIAGNOSIS_INFORMATION_GAIN,
|
|
5
5
|
assertDiagnosisDetails,
|
|
@@ -110,16 +110,16 @@ export async function recordDiagnosis({
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
if (existingEvent?.details?.diagnosisFingerprint === requestedFingerprint) {
|
|
113
|
-
const updatedState = {
|
|
114
|
-
|
|
115
|
-
diagnosedHypothesis: existingEvent.details.hypothesis,
|
|
116
|
-
lastUpdated: new Date().toISOString(),
|
|
117
|
-
};
|
|
118
|
-
await writeWorkState(target, updatedState, {
|
|
113
|
+
const updatedState = await mutateWorkState(target, {
|
|
114
|
+
expectedRevision: state.revision ?? 0,
|
|
119
115
|
packageRoot,
|
|
120
116
|
taskId: taskId ?? null,
|
|
121
117
|
statePath,
|
|
122
|
-
})
|
|
118
|
+
}, () => ({
|
|
119
|
+
...state,
|
|
120
|
+
diagnosedHypothesis: existingEvent.details.hypothesis,
|
|
121
|
+
lastUpdated: new Date().toISOString(),
|
|
122
|
+
}));
|
|
123
123
|
return {
|
|
124
124
|
event: existingEvent,
|
|
125
125
|
state: updatedState,
|
|
@@ -155,12 +155,16 @@ export async function recordDiagnosis({
|
|
|
155
155
|
{ taskId: taskId ?? null, eventsPath },
|
|
156
156
|
);
|
|
157
157
|
|
|
158
|
-
const updatedState = {
|
|
158
|
+
const updatedState = await mutateWorkState(target, {
|
|
159
|
+
expectedRevision: state.revision ?? 0,
|
|
160
|
+
packageRoot,
|
|
161
|
+
taskId: taskId ?? null,
|
|
162
|
+
statePath,
|
|
163
|
+
}, () => ({
|
|
159
164
|
...state,
|
|
160
165
|
diagnosedHypothesis: hypothesis.trim(),
|
|
161
166
|
lastUpdated: new Date().toISOString(),
|
|
162
|
-
};
|
|
163
|
-
await writeWorkState(target, updatedState, { packageRoot, taskId: taskId ?? null, statePath });
|
|
167
|
+
}));
|
|
164
168
|
|
|
165
169
|
return {
|
|
166
170
|
event,
|
package/src/core/error-codes.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
export const E_TASK_REQUIRED = "E_TASK_REQUIRED";
|
|
11
11
|
export const E_TASK_NOT_FOUND = "E_TASK_NOT_FOUND";
|
|
12
12
|
export const E_TASK_ALREADY_EXISTS = "E_TASK_ALREADY_EXISTS";
|
|
13
|
+
export const E_TASK_COMPLETE = "E_TASK_COMPLETE";
|
|
13
14
|
export const E_TASK_AMBIGUOUS = "E_TASK_AMBIGUOUS";
|
|
14
15
|
export const E_TASK_SELECTOR_CONFLICT = "E_TASK_SELECTOR_CONFLICT";
|
|
15
16
|
export const E_TASK_DESCRIPTOR_INVALID = "E_TASK_DESCRIPTOR_INVALID";
|
|
@@ -18,6 +19,7 @@ export const E_TASK_CONTEXT_MISMATCH = "E_TASK_CONTEXT_MISMATCH";
|
|
|
18
19
|
|
|
19
20
|
export const E_TASK_LOCKED = "E_TASK_LOCKED";
|
|
20
21
|
export const E_TASK_LOCK_INVALID = "E_TASK_LOCK_INVALID";
|
|
22
|
+
export const E_PROJECT_CLAIMS_LOCK_INCONSISTENT = "E_PROJECT_CLAIMS_LOCK_INCONSISTENT";
|
|
21
23
|
|
|
22
24
|
export const E_TASK_SCOPE_REQUIRED = "E_TASK_SCOPE_REQUIRED";
|
|
23
25
|
export const E_TASK_SCOPE_CONFLICT = "E_TASK_SCOPE_CONFLICT";
|
|
@@ -28,7 +30,19 @@ export const E_TASK_CHANGE_ATTRIBUTION_UNAVAILABLE = "E_TASK_CHANGE_ATTRIBUTION_
|
|
|
28
30
|
|
|
29
31
|
export const E_TASK_LAYOUT_LEGACY = "E_TASK_LAYOUT_LEGACY";
|
|
30
32
|
export const E_TASK_MIGRATION_INVALID = "E_TASK_MIGRATION_INVALID";
|
|
33
|
+
export const E_TASK_RECOVERY_UNSAFE = "E_TASK_RECOVERY_UNSAFE";
|
|
34
|
+
export const E_TASK_RECOVERY_INCONSISTENT = "E_TASK_RECOVERY_INCONSISTENT";
|
|
35
|
+
export const E_LEGACY_RECOVERY_MIGRATION_INVALID = "E_LEGACY_RECOVERY_MIGRATION_INVALID";
|
|
36
|
+
export const E_COMPLETION_OWNERSHIP_UNPROVEN = "E_COMPLETION_OWNERSHIP_UNPROVEN";
|
|
37
|
+
export const E_TASK_CLAIM_OWNERSHIP_INCONSISTENT = "E_TASK_CLAIM_OWNERSHIP_INCONSISTENT";
|
|
38
|
+
export const E_TASK_RECOVERY_AUTHORIZATION_REQUIRED = "E_TASK_RECOVERY_AUTHORIZATION_REQUIRED";
|
|
39
|
+
export const E_TASK_RECOVERY_AUTHORITY_INVALID = "E_TASK_RECOVERY_AUTHORITY_INVALID";
|
|
40
|
+
export const E_TASK_RECOVERED = "E_TASK_RECOVERED";
|
|
41
|
+
export const E_TASK_NOT_RECOVERED = "E_TASK_NOT_RECOVERED";
|
|
42
|
+
export const E_TASK_RECOVERY_OFFICIAL_PATH_AVAILABLE = "E_TASK_RECOVERY_OFFICIAL_PATH_AVAILABLE";
|
|
43
|
+
export const E_TASK_ALREADY_RECOVERED = "E_TASK_ALREADY_RECOVERED";
|
|
31
44
|
export const E_TASK_MIGRATION_IDENTITY_MISMATCH = "E_TASK_MIGRATION_IDENTITY_MISMATCH";
|
|
45
|
+
export const E_PROTOCOL_MIGRATION_TARGET_UNSUPPORTED = "E_PROTOCOL_MIGRATION_TARGET_UNSUPPORTED";
|
|
32
46
|
|
|
33
47
|
export const E_DIAGNOSIS_REQUIRED = "E_DIAGNOSIS_REQUIRED";
|
|
34
48
|
export const E_DIAGNOSIS_INVALID = "E_DIAGNOSIS_INVALID";
|
|
@@ -150,6 +164,13 @@ export const PUBLIC_ERROR_CODES = Object.freeze({
|
|
|
150
164
|
meaning: "Multiple tasks exist in the project but no task selector was provided.",
|
|
151
165
|
safeResolution: "Select a task explicitly using --task <id> or FORGELOOP_TASK=<id>.",
|
|
152
166
|
}),
|
|
167
|
+
E_TASK_COMPLETE: Object.freeze({
|
|
168
|
+
code: "E_TASK_COMPLETE",
|
|
169
|
+
category: "task-resolution",
|
|
170
|
+
classification: "PUBLIC_STABLE",
|
|
171
|
+
meaning: "A validator-backed COMPLETE task is terminal and cannot be mutated.",
|
|
172
|
+
safeResolution: "Create or select a non-terminal task for further work; do not modify terminal task state.",
|
|
173
|
+
}),
|
|
153
174
|
E_TASK_LOCKED: Object.freeze({
|
|
154
175
|
code: "E_TASK_LOCKED",
|
|
155
176
|
category: "concurrency",
|
|
@@ -157,12 +178,96 @@ export const PUBLIC_ERROR_CODES = Object.freeze({
|
|
|
157
178
|
meaning: "Task mutation is currently locked by another concurrent process or run-check.",
|
|
158
179
|
safeResolution: "Wait for the active mutation to complete or inspect the lock with forgeloop task-show.",
|
|
159
180
|
}),
|
|
181
|
+
E_PROJECT_CLAIMS_LOCK_INCONSISTENT: Object.freeze({
|
|
182
|
+
code: "E_PROJECT_CLAIMS_LOCK_INCONSISTENT",
|
|
183
|
+
category: "concurrency",
|
|
184
|
+
classification: "PUBLIC_STABLE",
|
|
185
|
+
meaning: "The project-wide claim reservation lock has unknown, corrupt, or concurrently changed ownership metadata.",
|
|
186
|
+
safeResolution: "Inspect .forgeloop/.claims.lock and retry only after its lease and owner identity can be validated; never force-delete unknown ownership.",
|
|
187
|
+
}),
|
|
160
188
|
E_TASK_SCOPE_CONFLICT: Object.freeze({
|
|
161
189
|
code: "E_TASK_SCOPE_CONFLICT",
|
|
162
190
|
category: "scope",
|
|
163
191
|
classification: "PUBLIC_STABLE",
|
|
164
192
|
meaning: "Task write claims overlap with another non-complete task in the same checkout.",
|
|
165
|
-
safeResolution: "
|
|
193
|
+
safeResolution: "Inspect the conflicting task classification reported in error.conflicts, then reconcile or recover it through its reported official recovery commands before retrying task creation.",
|
|
194
|
+
}),
|
|
195
|
+
E_TASK_RECOVERY_UNSAFE: Object.freeze({
|
|
196
|
+
code: "E_TASK_RECOVERY_UNSAFE",
|
|
197
|
+
category: "recovery",
|
|
198
|
+
classification: "PUBLIC_STABLE",
|
|
199
|
+
meaning: "Claim-release recovery was refused because the conflicting task is active, inconsistent, already complete, or holds a live lease.",
|
|
200
|
+
safeResolution: "Resolve the reported classification first; live leases must expire or be released by their owner before recovery.",
|
|
201
|
+
}),
|
|
202
|
+
E_TASK_RECOVERY_INCONSISTENT: Object.freeze({
|
|
203
|
+
code: "E_TASK_RECOVERY_INCONSISTENT",
|
|
204
|
+
category: "recovery",
|
|
205
|
+
classification: "PUBLIC_STABLE",
|
|
206
|
+
meaning: "Claim-release recovery was refused because the task state, recovery artifact, lock, or event ledger is inconsistent.",
|
|
207
|
+
safeResolution: "Repair the underlying artifact through its dedicated recovery surface; do not force-complete an unreadable task.",
|
|
208
|
+
}),
|
|
209
|
+
E_LEGACY_RECOVERY_MIGRATION_INVALID: Object.freeze({
|
|
210
|
+
code: "E_LEGACY_RECOVERY_MIGRATION_INVALID",
|
|
211
|
+
category: "recovery",
|
|
212
|
+
classification: "PUBLIC_STABLE",
|
|
213
|
+
meaning: "The legacy recovery-event repair was refused because the ledger does not match the exact known legacy defect signature, has incompatible later activity, holds a live lock, or is otherwise ambiguous.",
|
|
214
|
+
safeResolution: "Inspect the structured plan/errors; ambiguous or tampered ledgers stay INCONSISTENT and are never migrated.",
|
|
215
|
+
}),
|
|
216
|
+
E_COMPLETION_OWNERSHIP_UNPROVEN: Object.freeze({
|
|
217
|
+
code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
|
|
218
|
+
category: "recovery",
|
|
219
|
+
classification: "PUBLIC_STABLE",
|
|
220
|
+
meaning: "Work-state claims COMPLETE but the canonical lifecycle/ledger completion proof is missing or invalid, so historical claims stay reserved.",
|
|
221
|
+
safeResolution: "Restore the canonical completion event and a valid ledger, or re-run the official completion pipeline; phase=COMPLETE alone never releases claims.",
|
|
222
|
+
}),
|
|
223
|
+
E_TASK_CLAIM_OWNERSHIP_INCONSISTENT: Object.freeze({
|
|
224
|
+
code: "E_TASK_CLAIM_OWNERSHIP_INCONSISTENT",
|
|
225
|
+
category: "recovery",
|
|
226
|
+
classification: "PUBLIC_STABLE",
|
|
227
|
+
meaning: "ForgeLoop cannot prove whether a task still owns its historical write claims.",
|
|
228
|
+
safeResolution: "Repair and validate the task descriptor, recovery artifact, and complete event ledger before acquiring overlapping claims or mutating the task.",
|
|
229
|
+
}),
|
|
230
|
+
E_TASK_RECOVERY_AUTHORIZATION_REQUIRED: Object.freeze({
|
|
231
|
+
code: "E_TASK_RECOVERY_AUTHORIZATION_REQUIRED",
|
|
232
|
+
category: "recovery",
|
|
233
|
+
classification: "PUBLIC_STABLE",
|
|
234
|
+
meaning: "task-recover requires explicit caller acknowledgement; this is not host-attested authority.",
|
|
235
|
+
safeResolution: "Re-run with --acknowledge-recovery only when evidence shows the task is STALE or ABANDONED; --operator-authorized remains a deprecated alias.",
|
|
236
|
+
}),
|
|
237
|
+
E_TASK_RECOVERY_AUTHORITY_INVALID: Object.freeze({
|
|
238
|
+
code: "E_TASK_RECOVERY_AUTHORITY_INVALID",
|
|
239
|
+
category: "authority",
|
|
240
|
+
classification: "PUBLIC_STABLE",
|
|
241
|
+
meaning: "Recovery authority metadata is invalid or claims host attestation without a host-owned grant reference.",
|
|
242
|
+
safeResolution: "Use caller acknowledgement, or provide a host-attested recovery grant through a trusted host integration.",
|
|
243
|
+
}),
|
|
244
|
+
E_TASK_RECOVERED: Object.freeze({
|
|
245
|
+
code: "E_TASK_RECOVERED",
|
|
246
|
+
category: "recovery",
|
|
247
|
+
classification: "PUBLIC_STABLE",
|
|
248
|
+
meaning: "The task released its write claims through recovery and ordinary mutation is suspended.",
|
|
249
|
+
safeResolution: "Run forgeloop task-resume --task <id> to reacquire the released claims before mutating the task.",
|
|
250
|
+
}),
|
|
251
|
+
E_TASK_NOT_RECOVERED: Object.freeze({
|
|
252
|
+
code: "E_TASK_NOT_RECOVERED",
|
|
253
|
+
category: "recovery",
|
|
254
|
+
classification: "PUBLIC_STABLE",
|
|
255
|
+
meaning: "task-resume was requested for a task without active recovered state.",
|
|
256
|
+
safeResolution: "Inspect the task with forgeloop task-show; task-resume is only valid while recovery.json is active.",
|
|
257
|
+
}),
|
|
258
|
+
E_TASK_RECOVERY_OFFICIAL_PATH_AVAILABLE: Object.freeze({
|
|
259
|
+
code: "E_TASK_RECOVERY_OFFICIAL_PATH_AVAILABLE",
|
|
260
|
+
category: "recovery",
|
|
261
|
+
classification: "PUBLIC_STABLE",
|
|
262
|
+
meaning: "Claim-release recovery was refused because canonical lifecycle reconciliation is available.",
|
|
263
|
+
safeResolution: "Use forgeloop reconcile-closure and the normal verification/completion pipeline instead of task-recover.",
|
|
264
|
+
}),
|
|
265
|
+
E_TASK_ALREADY_RECOVERED: Object.freeze({
|
|
266
|
+
code: "E_TASK_ALREADY_RECOVERED",
|
|
267
|
+
category: "recovery",
|
|
268
|
+
classification: "PUBLIC_STABLE",
|
|
269
|
+
meaning: "The task already has active durable recovered state.",
|
|
270
|
+
safeResolution: "Inspect the existing recovery metadata; use task-resume to reacquire claims or leave the task recovered.",
|
|
166
271
|
}),
|
|
167
272
|
E_TASK_SCOPE_DIRTY: Object.freeze({
|
|
168
273
|
code: "E_TASK_SCOPE_DIRTY",
|
|
@@ -445,6 +550,7 @@ export const ALL_KNOWN_ERROR_CODES = Object.freeze(new Set([
|
|
|
445
550
|
E_TASK_REQUIRED,
|
|
446
551
|
E_TASK_NOT_FOUND,
|
|
447
552
|
E_TASK_ALREADY_EXISTS,
|
|
553
|
+
E_TASK_COMPLETE,
|
|
448
554
|
E_TASK_AMBIGUOUS,
|
|
449
555
|
E_TASK_SELECTOR_CONFLICT,
|
|
450
556
|
E_TASK_DESCRIPTOR_INVALID,
|
|
@@ -452,6 +558,7 @@ export const ALL_KNOWN_ERROR_CODES = Object.freeze(new Set([
|
|
|
452
558
|
E_TASK_CONTEXT_MISMATCH,
|
|
453
559
|
E_TASK_LOCKED,
|
|
454
560
|
E_TASK_LOCK_INVALID,
|
|
561
|
+
E_PROJECT_CLAIMS_LOCK_INCONSISTENT,
|
|
455
562
|
E_TASK_SCOPE_REQUIRED,
|
|
456
563
|
E_TASK_SCOPE_CONFLICT,
|
|
457
564
|
E_TASK_SCOPE_DIRTY,
|
|
@@ -468,7 +575,19 @@ export const ALL_KNOWN_ERROR_CODES = Object.freeze(new Set([
|
|
|
468
575
|
E_TASK_CHANGE_ATTRIBUTION_UNAVAILABLE,
|
|
469
576
|
E_TASK_LAYOUT_LEGACY,
|
|
470
577
|
E_TASK_MIGRATION_INVALID,
|
|
578
|
+
E_TASK_RECOVERY_UNSAFE,
|
|
579
|
+
E_TASK_RECOVERY_INCONSISTENT,
|
|
580
|
+
E_LEGACY_RECOVERY_MIGRATION_INVALID,
|
|
581
|
+
E_COMPLETION_OWNERSHIP_UNPROVEN,
|
|
582
|
+
E_TASK_CLAIM_OWNERSHIP_INCONSISTENT,
|
|
583
|
+
E_TASK_RECOVERY_AUTHORIZATION_REQUIRED,
|
|
584
|
+
E_TASK_RECOVERY_AUTHORITY_INVALID,
|
|
585
|
+
E_TASK_RECOVERED,
|
|
586
|
+
E_TASK_NOT_RECOVERED,
|
|
587
|
+
E_TASK_RECOVERY_OFFICIAL_PATH_AVAILABLE,
|
|
588
|
+
E_TASK_ALREADY_RECOVERED,
|
|
471
589
|
E_TASK_MIGRATION_IDENTITY_MISMATCH,
|
|
590
|
+
E_PROTOCOL_MIGRATION_TARGET_UNSUPPORTED,
|
|
472
591
|
E_CHECK_INERT,
|
|
473
592
|
E_CHECK_MUTATION_NOT_DETECTED,
|
|
474
593
|
E_POLICY_DRIFT,
|
|
@@ -487,3 +606,19 @@ export const ALL_KNOWN_ERROR_CODES = Object.freeze(new Set([
|
|
|
487
606
|
E_POLICY_INITIALIZATION_FAILED,
|
|
488
607
|
E_INIT_KIT_CONFLICT,
|
|
489
608
|
]));
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Complete public registry. Older callers can retain PUBLIC_ERROR_CODES while
|
|
612
|
+
* documentation and compatibility handshakes enumerate every stable code.
|
|
613
|
+
*/
|
|
614
|
+
export const PUBLIC_ERROR_REGISTRY = Object.freeze(
|
|
615
|
+
Object.fromEntries([...ALL_KNOWN_ERROR_CODES].sort().map((code) => [code, Object.freeze(
|
|
616
|
+
PUBLIC_ERROR_CODES[code] ?? {
|
|
617
|
+
code,
|
|
618
|
+
category: "protocol",
|
|
619
|
+
classification: "PUBLIC_STABLE",
|
|
620
|
+
meaning: "A ForgeLoop protocol validation or lifecycle condition was not satisfied.",
|
|
621
|
+
safeResolution: "Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json.",
|
|
622
|
+
},
|
|
623
|
+
)])),
|
|
624
|
+
);
|