@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.
Files changed (163) hide show
  1. package/.github/copilot-instructions.md +1 -0
  2. package/AGENTS.md +1 -0
  3. package/CLAUDE.md +1 -0
  4. package/DOCS_INDEX.md +20 -8
  5. package/EXECUTION_STATE.md +60 -0
  6. package/LOOP_ENGINEERING.md +135 -5
  7. package/LOOP_SYSTEM_DESIGN.md +54 -1
  8. package/PROTOCOL_INTEGRATION.md +87 -0
  9. package/QUALITY_SCORECARD.md +2 -0
  10. package/README.md +69 -9
  11. package/TERMINOLOGY.md +15 -0
  12. package/THIRD_PARTY_NOTICES.md +30 -0
  13. package/THREAT_MODEL.md +59 -1
  14. package/docs/ARTIFACT_REFERENCE.md +183 -0
  15. package/docs/CLI_REFERENCE.md +391 -6
  16. package/docs/CROSS_HARNESS_CONTINUITY.md +23 -0
  17. package/docs/DIAGNOSTIC_MODEL.md +181 -0
  18. package/docs/DOCUMENTATION_GUIDE.md +36 -13
  19. package/docs/EXECUTION_TRACE.md +76 -0
  20. package/docs/GETTING_STARTED.md +1 -0
  21. package/docs/MCP.md +159 -0
  22. package/docs/RECIPES.md +149 -0
  23. package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
  24. package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
  25. package/docs/TROUBLESHOOTING.md +217 -3
  26. package/docs/UNIVERSAL_INTEGRATION.md +48 -0
  27. package/docs/assets/diagrams/forgeloop-engineering-flow.html +13797 -0
  28. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +37 -0
  29. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5002 -0
  30. package/docs/diagrams/README.md +55 -0
  31. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +122 -0
  32. package/docs/diagrams/manifest.json +42 -0
  33. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +20 -0
  34. package/package.json +21 -8
  35. package/schemas/action.schema.json +100 -0
  36. package/schemas/approval.schema.json +51 -0
  37. package/schemas/capability-policy.schema.json +41 -0
  38. package/schemas/diagnostic-case.schema.json +85 -0
  39. package/schemas/execution-receipt.schema.json +16 -0
  40. package/schemas/hypothesis-disposition.schema.json +16 -0
  41. package/schemas/intervention.schema.json +27 -0
  42. package/schemas/policy-lock.schema.json +1 -0
  43. package/schemas/policy-snapshot.schema.json +2 -0
  44. package/schemas/task-recovery.schema.json +61 -0
  45. package/schemas/trajectory-evaluation.schema.json +64 -0
  46. package/schemas/trajectory-scenario.schema.json +42 -0
  47. package/src/cli.js +267 -347
  48. package/src/commands/action-authorize.js +41 -0
  49. package/src/commands/action-propose.js +10 -0
  50. package/src/commands/action-reconcile.js +10 -0
  51. package/src/commands/action-record.js +47 -0
  52. package/src/commands/action-show.js +10 -0
  53. package/src/commands/action-verify.js +10 -0
  54. package/src/commands/advance.js +7 -2
  55. package/src/commands/approval-request.js +64 -0
  56. package/src/commands/approval-resolve.js +10 -0
  57. package/src/commands/audit.js +5 -0
  58. package/src/commands/baseline.js +3 -3
  59. package/src/commands/eval.js +6 -0
  60. package/src/commands/history.js +18 -0
  61. package/src/commands/init.js +2 -2
  62. package/src/commands/inspect.js +55 -0
  63. package/src/commands/metrics.js +7 -0
  64. package/src/commands/next.js +8 -2
  65. package/src/commands/policy-discover.js +2 -2
  66. package/src/commands/progress.js +6 -2
  67. package/src/commands/record-diagnosis.js +37 -1
  68. package/src/commands/record-hypothesis-disposition.js +45 -0
  69. package/src/commands/record-intervention.js +35 -0
  70. package/src/commands/reflect.js +38 -0
  71. package/src/commands/report.js +9 -1
  72. package/src/commands/run-action.js +18 -0
  73. package/src/commands/status.js +17 -0
  74. package/src/commands/task-create.js +39 -1
  75. package/src/commands/task-list.js +14 -1
  76. package/src/commands/task-lock-status.js +2 -2
  77. package/src/commands/task-recover.js +202 -0
  78. package/src/commands/task-repair-legacy-recovery.js +417 -0
  79. package/src/commands/task-resume.js +172 -0
  80. package/src/commands/task-scope.js +23 -4
  81. package/src/commands/task-show.js +18 -4
  82. package/src/commands/trace.js +34 -0
  83. package/src/commands/validate-protocol.js +40 -15
  84. package/src/core/action-authorization.js +106 -0
  85. package/src/core/action-constants.js +86 -0
  86. package/src/core/action-execution.js +105 -0
  87. package/src/core/action-ledger-projection.js +302 -0
  88. package/src/core/action-model.js +581 -0
  89. package/src/core/action-readiness.js +141 -0
  90. package/src/core/action-reconciliation-policy.js +49 -0
  91. package/src/core/action-reconciliation.js +66 -0
  92. package/src/core/action-verification.js +111 -0
  93. package/src/core/actions.js +462 -0
  94. package/src/core/approvals.js +405 -0
  95. package/src/core/artifact-registry.js +60 -0
  96. package/src/core/audit.js +45 -4
  97. package/src/core/bundles.js +30 -0
  98. package/src/core/capability-policy.js +226 -0
  99. package/src/core/cli-command-definitions.js +260 -5
  100. package/src/core/command-executors.js +543 -0
  101. package/src/core/command-input.js +107 -0
  102. package/src/core/command-runtime.js +117 -0
  103. package/src/core/completion-artifacts.js +39 -15
  104. package/src/core/completion-ownership.js +88 -0
  105. package/src/core/completion-recovery-rebind.js +194 -0
  106. package/src/core/completion.js +70 -0
  107. package/src/core/continuity-reconciliation.js +24 -5
  108. package/src/core/diagnostic-model.js +396 -0
  109. package/src/core/diagnostic-projection.js +51 -0
  110. package/src/core/diagnostic-record.js +360 -0
  111. package/src/core/error-codes.js +461 -1
  112. package/src/core/events.js +171 -2
  113. package/src/core/execution-prerequisites.js +4 -1
  114. package/src/core/execution.js +26 -188
  115. package/src/core/failure-signature.js +70 -0
  116. package/src/core/failure-surface.js +57 -0
  117. package/src/core/filesystem.js +55 -6
  118. package/src/core/history.js +110 -0
  119. package/src/core/hypothesis-projection.js +85 -0
  120. package/src/core/information-gain-projection.js +283 -0
  121. package/src/core/information-gain.js +138 -0
  122. package/src/core/inspect.js +132 -7
  123. package/src/core/integration-invocation-policy.js +217 -0
  124. package/src/core/integration-limits.js +20 -0
  125. package/src/core/integration-resources.js +178 -0
  126. package/src/core/next-action-model.js +94 -0
  127. package/src/core/next-action.js +490 -3
  128. package/src/core/phase.js +42 -22
  129. package/src/core/policy-engine.js +113 -6
  130. package/src/core/preflight-consistency.js +31 -5
  131. package/src/core/preflight.js +19 -2
  132. package/src/core/prepared-execution.js +227 -0
  133. package/src/core/progress.js +41 -4
  134. package/src/core/project-root.js +21 -0
  135. package/src/core/protocol-info.js +61 -0
  136. package/src/core/protocol.js +14 -0
  137. package/src/core/receipt.js +1 -0
  138. package/src/core/reconcile-closure.js +35 -10
  139. package/src/core/recovery-history.js +116 -0
  140. package/src/core/reflection.js +305 -0
  141. package/src/core/resumability.js +57 -3
  142. package/src/core/schema-validation.js +9 -0
  143. package/src/core/strategy-analysis.js +97 -0
  144. package/src/core/task-claim-state.js +272 -0
  145. package/src/core/task-command.js +5 -1
  146. package/src/core/task-conflict-inspection.js +321 -0
  147. package/src/core/task-context.js +32 -29
  148. package/src/core/task-discovery.js +14 -1
  149. package/src/core/task-lock.js +216 -22
  150. package/src/core/task-paths.js +31 -2
  151. package/src/core/task-recovery-migration.js +192 -0
  152. package/src/core/task-recovery.js +205 -0
  153. package/src/core/task-scope.js +33 -1
  154. package/src/core/task-snapshot.js +53 -0
  155. package/src/core/templates.js +9 -0
  156. package/src/core/trace.js +548 -0
  157. package/src/core/trajectory-evaluation.js +71 -0
  158. package/src/core/trajectory-metrics.js +80 -0
  159. package/src/core/transaction.js +36 -2
  160. package/src/core/work-state.js +10 -5
  161. package/src/integration.js +47 -0
  162. package/docs/assets/forgeloop-flow.svg +0 -1
  163. 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
+ }
@@ -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
- if (manifest.writes.some((entry) => writePath(entry) === relativePath && entry.kind === "APPEND")) {
132
- throw new Error(`cannot replace append-staged path: ${relativePath}`);
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);
@@ -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, packageRoot);
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: WORK_STATE_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: WORK_STATE_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: WORK_STATE_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;