@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,80 @@
|
|
|
1
|
+
import { buildTaskTrace } from "./trace.js";
|
|
2
|
+
import { buildTaskReflection } from "./reflection.js";
|
|
3
|
+
|
|
4
|
+
export const COMPARABLE_WORK_EVENTS = new Set([
|
|
5
|
+
"EXECUTION_STARTED", "VERIFICATION_RECORDED", "DIAGNOSTIC_CASE_RECORDED",
|
|
6
|
+
"INTERVENTION_RECORDED", "ACTION_STARTED", "ACTION_RECONCILED", "REVIEW_STARTED",
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export async function buildTrajectoryMetrics({ target, packageRoot, taskId }) {
|
|
10
|
+
const trace = await buildTaskTrace({ target, packageRoot, taskId });
|
|
11
|
+
const reflection = await buildTaskReflection({ target, packageRoot, taskId });
|
|
12
|
+
const events = trace.events;
|
|
13
|
+
const verificationCycles = new Set(events.filter((event) => event.type === "VERIFICATION_STARTED")
|
|
14
|
+
.map((event) => event.data?.verificationCycle).filter(Number.isInteger));
|
|
15
|
+
const diagnosticCycles = new Set(trace.diagnostics.cases.map((item) => item.verificationCycle));
|
|
16
|
+
const firstEventAt = events.find((event) => event.timestampQuality === "authoritative")?.timestamp ?? null;
|
|
17
|
+
const lastEventAt = [...events].reverse().find((event) => event.timestampQuality === "authoritative")?.timestamp ?? null;
|
|
18
|
+
const firstMs = firstEventAt ? Date.parse(firstEventAt) : NaN;
|
|
19
|
+
const lastMs = lastEventAt ? Date.parse(lastEventAt) : NaN;
|
|
20
|
+
const interventions = reflection.interventions ?? { count: 0, informative: 0, nonInformative: 0 };
|
|
21
|
+
return {
|
|
22
|
+
schemaVersion: 1,
|
|
23
|
+
taskId,
|
|
24
|
+
completion: {
|
|
25
|
+
validated: Boolean(trace.completion.validatedAt),
|
|
26
|
+
phase: trace.task.phase,
|
|
27
|
+
},
|
|
28
|
+
trajectory: {
|
|
29
|
+
events: events.length,
|
|
30
|
+
verificationCycles: verificationCycles.size,
|
|
31
|
+
diagnosticCycles: diagnosticCycles.size,
|
|
32
|
+
strategyChanges: Math.max(0, (reflection.strategies?.length ?? 0) - 1),
|
|
33
|
+
oscillationDetected: Boolean(reflection.oscillation?.detected),
|
|
34
|
+
noEffectiveInformationGainCycles: reflection.informationGain?.cyclesWithoutEffectiveGain?.length ?? 0,
|
|
35
|
+
interventions: {
|
|
36
|
+
total: interventions.count ?? 0,
|
|
37
|
+
informative: interventions.informative ?? 0,
|
|
38
|
+
nonInformative: interventions.nonInformative ?? 0,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
actions: {
|
|
42
|
+
total: trace.actions?.total ?? 0,
|
|
43
|
+
verified: trace.actions?.verified ?? 0,
|
|
44
|
+
// Canonical trust counts come from the action-readiness projection;
|
|
45
|
+
// raw VERIFIED labels are observability only.
|
|
46
|
+
trustedSatisfied: await (async () => {
|
|
47
|
+
try {
|
|
48
|
+
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
49
|
+
const readiness = await evaluateRequiredActionReadiness({ target, packageRoot, taskId });
|
|
50
|
+
return readiness.satisfied;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
})(),
|
|
55
|
+
unresolvedRequired: await (async () => {
|
|
56
|
+
try {
|
|
57
|
+
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
58
|
+
const readiness = await evaluateRequiredActionReadiness({ target, packageRoot, taskId });
|
|
59
|
+
return readiness.unresolved;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
})(),
|
|
64
|
+
failed: trace.actions?.failed ?? 0,
|
|
65
|
+
ambiguous: trace.actions?.ambiguous ?? 0,
|
|
66
|
+
reconciliations: trace.actions?.reconciliationCount ?? 0,
|
|
67
|
+
},
|
|
68
|
+
executions: {
|
|
69
|
+
observedCommands: trace.executions.length,
|
|
70
|
+
failedCommands: trace.executions.filter((execution) => execution.status === "failed").length,
|
|
71
|
+
},
|
|
72
|
+
timing: {
|
|
73
|
+
firstEventAt,
|
|
74
|
+
lastEventAt,
|
|
75
|
+
wallClockMs: Number.isFinite(firstMs) && Number.isFinite(lastMs) ? Math.max(0, lastMs - firstMs) : null,
|
|
76
|
+
},
|
|
77
|
+
usage: { tokens: null, costUsd: null, source: "UNKNOWN" },
|
|
78
|
+
comparableSteps: events.filter((event) => COMPARABLE_WORK_EVENTS.has(event.type)).length,
|
|
79
|
+
};
|
|
80
|
+
}
|
package/src/core/transaction.js
CHANGED
|
@@ -103,6 +103,13 @@ export async function withTaskTransaction({
|
|
|
103
103
|
recordCommitEvent = false,
|
|
104
104
|
} = {}, callback) {
|
|
105
105
|
if (!target || !taskId) throw new Error("target and taskId are required for a task transaction");
|
|
106
|
+
const activeTransaction = getActiveTaskTransaction();
|
|
107
|
+
if (activeTransaction) {
|
|
108
|
+
if (activeTransaction.taskId !== taskId) {
|
|
109
|
+
throw new Error(`cannot nest task transaction for ${taskId} inside ${activeTransaction.taskId}`);
|
|
110
|
+
}
|
|
111
|
+
return callback(activeTransaction);
|
|
112
|
+
}
|
|
106
113
|
const started = Date.now();
|
|
107
114
|
const runWithLock = async () => withTaskLock(target, lockTaskId, operation, async (lock) => {
|
|
108
115
|
const transactionId = `txn-${randomUUID()}`;
|
|
@@ -115,9 +122,13 @@ export async function withTaskTransaction({
|
|
|
115
122
|
await writeManifest(target, manifestPath, manifest);
|
|
116
123
|
const tx = {
|
|
117
124
|
transactionId,
|
|
125
|
+
taskId,
|
|
118
126
|
lock,
|
|
119
127
|
async readText(relativePath) {
|
|
120
128
|
await assertSafePath(target, relativePath);
|
|
129
|
+
if (manifest.writes.some((entry) => writePath(entry) === relativePath && entry.kind === "DELETE")) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
121
132
|
const staged = ensureWithin(target, `${stageRoot}/${relativePath}`);
|
|
122
133
|
if (await fileExists(staged)) return readFile(staged, "utf8");
|
|
123
134
|
const appendStaged = ensureWithin(target, `${stageRoot}/${relativePath}.append`);
|
|
@@ -128,8 +139,9 @@ export async function withTaskTransaction({
|
|
|
128
139
|
},
|
|
129
140
|
async stageText(relativePath, text) {
|
|
130
141
|
await assertSafePath(target, relativePath);
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
const conflicting = manifest.writes.find((entry) => writePath(entry) === relativePath);
|
|
143
|
+
if (conflicting?.kind === "APPEND" || conflicting?.kind === "DELETE") {
|
|
144
|
+
throw new Error(`cannot replace ${conflicting.kind.toLowerCase()}-staged path: ${relativePath}`);
|
|
133
145
|
}
|
|
134
146
|
const staged = `${stageRoot}/${relativePath}`;
|
|
135
147
|
await assertSafePath(target, staged);
|
|
@@ -162,6 +174,23 @@ export async function withTaskTransaction({
|
|
|
162
174
|
}
|
|
163
175
|
await writeManifest(target, manifestPath, manifest);
|
|
164
176
|
},
|
|
177
|
+
async stageDelete(relativePath) {
|
|
178
|
+
await assertSafePath(target, relativePath);
|
|
179
|
+
if (manifest.writes.some((entry) => writePath(entry) === relativePath)) {
|
|
180
|
+
throw new Error(`cannot delete write-staged path: ${relativePath}`);
|
|
181
|
+
}
|
|
182
|
+
const destination = ensureWithin(target, relativePath);
|
|
183
|
+
if (!(await fileExists(destination))) {
|
|
184
|
+
throw new Error(`transaction delete target is missing: ${relativePath}`);
|
|
185
|
+
}
|
|
186
|
+
manifest.writes.push({
|
|
187
|
+
path: relativePath,
|
|
188
|
+
kind: "DELETE",
|
|
189
|
+
hadPrevious: false,
|
|
190
|
+
published: false,
|
|
191
|
+
});
|
|
192
|
+
await writeManifest(target, manifestPath, manifest);
|
|
193
|
+
},
|
|
165
194
|
};
|
|
166
195
|
try {
|
|
167
196
|
const result = await transactionContext.run(tx, async () => {
|
|
@@ -221,6 +250,11 @@ export async function withTaskTransaction({
|
|
|
221
250
|
entry.backupCreated = true;
|
|
222
251
|
}
|
|
223
252
|
await writeManifest(target, manifestPath, manifest);
|
|
253
|
+
if (entry.kind === "DELETE") {
|
|
254
|
+
entry.published = true;
|
|
255
|
+
await writeManifest(target, manifestPath, manifest);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
224
258
|
await rename(staged, destination);
|
|
225
259
|
entry.published = true;
|
|
226
260
|
await writeManifest(target, manifestPath, manifest);
|
package/src/core/work-state.js
CHANGED
|
@@ -538,13 +538,18 @@ export async function classifyLoadedWorkState({ target, state, contractFile = nu
|
|
|
538
538
|
};
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
-
export async function readAndClassifyWorkState({ target, packageRoot = getPackageRoot(), contractFile = null, maxAgeMs } = {}) {
|
|
541
|
+
export async function readAndClassifyWorkState({ target, packageRoot = getPackageRoot(), contractFile = null, maxAgeMs, taskId = null, stateFile = null, statePath = null } = {}) {
|
|
542
|
+
const effectiveStateRel = stateFile ?? statePath ?? (taskId ? taskArtifactPath(taskId, "state") : WORK_STATE_PATH);
|
|
542
543
|
let state = null;
|
|
543
544
|
try {
|
|
544
|
-
state = await readWorkState(target,
|
|
545
|
+
state = await readWorkState(target, {
|
|
546
|
+
packageRoot,
|
|
547
|
+
...(taskId ? { taskId } : {}),
|
|
548
|
+
...(stateFile || statePath ? { statePath: effectiveStateRel } : {}),
|
|
549
|
+
});
|
|
545
550
|
} catch (error) {
|
|
546
551
|
return {
|
|
547
|
-
path:
|
|
552
|
+
path: effectiveStateRel,
|
|
548
553
|
present: true,
|
|
549
554
|
status: "INVALID",
|
|
550
555
|
reasons: ["STATE_INVALID"],
|
|
@@ -565,7 +570,7 @@ export async function readAndClassifyWorkState({ target, packageRoot = getPackag
|
|
|
565
570
|
if (!state) {
|
|
566
571
|
const repository = await currentRepositoryFingerprint(target);
|
|
567
572
|
return {
|
|
568
|
-
path:
|
|
573
|
+
path: effectiveStateRel,
|
|
569
574
|
present: false,
|
|
570
575
|
status: "ABSENT",
|
|
571
576
|
reasons: ["NO_CHECKPOINT"],
|
|
@@ -589,7 +594,7 @@ export async function readAndClassifyWorkState({ target, packageRoot = getPackag
|
|
|
589
594
|
|
|
590
595
|
const classification = await classifyLoadedWorkState({ target, state, contractFile, maxAgeMs });
|
|
591
596
|
return {
|
|
592
|
-
path:
|
|
597
|
+
path: effectiveStateRel,
|
|
593
598
|
present: true,
|
|
594
599
|
...classification,
|
|
595
600
|
phase: state.phase,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FORGELOOP_INTEGRATION_RUNTIME_VERSION,
|
|
3
|
+
executeForgeLoopCommand,
|
|
4
|
+
} from "./core/command-runtime.js";
|
|
5
|
+
import { validateForgeLoopCommandInput, defaultCommandInputValues } from "./core/command-input.js";
|
|
6
|
+
import { CLI_COMMAND_DEFINITIONS } from "./core/cli-command-definitions.js";
|
|
7
|
+
import {
|
|
8
|
+
INTEGRATION_RISK_CLASSES,
|
|
9
|
+
classifyForgeLoopInvocation,
|
|
10
|
+
getForgeLoopCapabilities,
|
|
11
|
+
} from "./core/integration-invocation-policy.js";
|
|
12
|
+
import { readForgeLoopIntegrationResource, INTEGRATION_RESOURCE_DEFINITIONS } from "./core/integration-resources.js";
|
|
13
|
+
import { resolveForgeLoopProjectRoot } from "./core/project-root.js";
|
|
14
|
+
import { INTEGRATION_LIMITS } from "./core/integration-limits.js";
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Version of the installed @cassiomc1/forgeloop package providing this
|
|
21
|
+
* integration API (closing plan §14-16): lets external adapters report the
|
|
22
|
+
* real core version without hardcoding or deep-importing package internals.
|
|
23
|
+
*/
|
|
24
|
+
export function getForgeLoopPackageVersion() {
|
|
25
|
+
const packageJsonPath = path.join(
|
|
26
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
27
|
+
"..",
|
|
28
|
+
"package.json",
|
|
29
|
+
);
|
|
30
|
+
return JSON.parse(readFileSync(packageJsonPath, "utf8")).version;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
executeForgeLoopCommand,
|
|
35
|
+
validateForgeLoopCommandInput,
|
|
36
|
+
defaultCommandInputValues,
|
|
37
|
+
getForgeLoopCapabilities,
|
|
38
|
+
classifyForgeLoopInvocation,
|
|
39
|
+
readForgeLoopIntegrationResource,
|
|
40
|
+
resolveForgeLoopProjectRoot,
|
|
41
|
+
INTEGRATION_LIMITS,
|
|
42
|
+
INTEGRATION_RISK_CLASSES,
|
|
43
|
+
INTEGRATION_RESOURCE_DEFINITIONS,
|
|
44
|
+
CLI_COMMAND_DEFINITIONS,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const FORGELOOP_INTEGRATION_API_VERSION = FORGELOOP_INTEGRATION_RUNTIME_VERSION;
|