@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,41 @@
1
+ import { authorizeAction } from "../core/action-authorization.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+
4
+ /**
5
+ * Canonical public authorization surface. This command is only an adapter
6
+ * over the core authorization service: it creates no new trust mechanism and
7
+ * accepts no authority fields in actor-controlled arguments. Trusted host
8
+ * authority arrives exclusively through the out-of-band executor context.
9
+ */
10
+ export async function runActionAuthorize({
11
+ target,
12
+ packageRoot,
13
+ taskId,
14
+ actionId,
15
+ approvalId,
16
+ authorityContext,
17
+ }) {
18
+ return withTaskMutation(
19
+ target,
20
+ { taskId, packageRoot },
21
+ "action-authorize",
22
+ (ctx) => authorizeAction({
23
+ target,
24
+ packageRoot,
25
+ taskId: ctx.taskId,
26
+ actionId,
27
+ approvalId,
28
+ authorityContext,
29
+ }),
30
+ );
31
+ }
32
+
33
+ export function formatActionAuthorizeResult(result) {
34
+ return [
35
+ "FORGELOOP ACTION AUTHORIZED",
36
+ `action: ${result.action.actionId}`,
37
+ `state: ${result.action.state}`,
38
+ `decision: ${result.authorization.capabilityDecision}`,
39
+ "",
40
+ ].join("\n");
41
+ }
@@ -0,0 +1,10 @@
1
+ import { proposeAction } from "../core/actions.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+
4
+ export async function runActionPropose({ target, packageRoot, taskId, input }) {
5
+ return withTaskMutation(target, { taskId, packageRoot }, "action-propose", (ctx) =>
6
+ proposeAction(target, { packageRoot, taskId: ctx.taskId, input: { ...input, provenance: "CALLER_REPORTED" } }));
7
+ }
8
+ export function formatActionProposeResult(result) {
9
+ return `FORGELOOP ACTION PROPOSED\naction: ${result.action.actionId}\nstate: ${result.action.state}\n`;
10
+ }
@@ -0,0 +1,10 @@
1
+ import { reconcileAction } from "../core/action-reconciliation.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+ export async function runActionReconcile(args) {
4
+ const { target, packageRoot, taskId } = args;
5
+ return withTaskMutation(target, { taskId, packageRoot }, "action-reconcile", (ctx) =>
6
+ reconcileAction({ ...args, taskId: ctx.taskId }));
7
+ }
8
+ export function formatActionReconcileResult(result) {
9
+ return `FORGELOOP ACTION RECONCILED\naction: ${result.action.actionId}\noutcome: ${result.outcome}\nstate: ${result.action.state}\n`;
10
+ }
@@ -0,0 +1,47 @@
1
+ import { transitionAction } from "../core/actions.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+ import {
4
+ E_ACTION_AUTHORIZATION_INVALID,
5
+ E_ACTION_INVALID,
6
+ E_ACTION_VERIFICATION_REQUIRED,
7
+ } from "../core/error-codes.js";
8
+
9
+ // Public action-record may only record observations that do not create
10
+ // authority. AUTHORIZED and VERIFIED are owned by canonical core services and
11
+ // are rejected here even if a transport mistakenly exposes this command.
12
+ const CALLER_RECORDABLE_ACTION_STATES = Object.freeze([
13
+ "STARTED",
14
+ "COMMITTED",
15
+ "COMMIT_UNKNOWN",
16
+ "FAILED",
17
+ "CANCELLED",
18
+ ]);
19
+
20
+ export async function runActionRecord({ target, packageRoot, taskId, actionId, state, provenance, evidenceRef }) {
21
+ if (!["CALLER_REPORTED", "EXTERNAL_OBSERVED"].includes(provenance)) {
22
+ const error = new Error("action-record provenance must be CALLER_REPORTED or EXTERNAL_OBSERVED");
23
+ error.code = E_ACTION_INVALID;
24
+ throw error;
25
+ }
26
+ if (state === "AUTHORIZED") {
27
+ const error = new Error("AUTHORIZED is owned by the canonical authorization service; caller surfaces cannot authorize actions");
28
+ error.code = E_ACTION_AUTHORIZATION_INVALID;
29
+ throw error;
30
+ }
31
+ if (state === "VERIFIED") {
32
+ const error = new Error("VERIFIED requires canonical independent postcondition evidence via forgeloop action-verify");
33
+ error.code = E_ACTION_VERIFICATION_REQUIRED;
34
+ throw error;
35
+ }
36
+ if (!CALLER_RECORDABLE_ACTION_STATES.includes(state)) {
37
+ const error = new Error(`action-record state must be one of ${CALLER_RECORDABLE_ACTION_STATES.join(", ")}`);
38
+ error.code = E_ACTION_INVALID;
39
+ throw error;
40
+ }
41
+ return withTaskMutation(target, { taskId, packageRoot }, "action-record", (ctx) =>
42
+ transitionAction(target, { packageRoot, taskId: ctx.taskId, actionId, to: state,
43
+ details: { ...(evidenceRef ? { evidenceRef } : {}), reportedProvenance: provenance } }));
44
+ }
45
+ export function formatActionRecordResult(result) {
46
+ return `FORGELOOP ACTION RECORDED\naction: ${result.actionId}\nstate: ${result.state}\n`;
47
+ }
@@ -0,0 +1,10 @@
1
+ import { readAction } from "../core/actions.js";
2
+ import { resolveTaskContext } from "../core/task-context.js";
3
+
4
+ export async function runActionShow({ target, packageRoot, taskId, actionId }) {
5
+ const ctx = await resolveTaskContext(target, { packageRoot, taskId });
6
+ return readAction(target, { packageRoot, taskId: ctx.taskId, actionId });
7
+ }
8
+ export function formatActionShowResult(result) {
9
+ return `FORGELOOP ACTION\naction: ${result.actionId}\nstate: ${result.state}\ncapability: ${result.capability}\n`;
10
+ }
@@ -0,0 +1,10 @@
1
+ import { verifyAction } from "../core/action-verification.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+
4
+ export async function runActionVerify({ target, packageRoot, taskId, actionId, evidenceRef }) {
5
+ return withTaskMutation(target, { taskId, packageRoot }, "action-verify", (ctx) =>
6
+ verifyAction({ target, packageRoot, taskId: ctx.taskId, actionId, evidenceRef }));
7
+ }
8
+ export function formatActionVerifyResult(result) {
9
+ return `FORGELOOP ACTION VERIFIED\naction: ${result.actionId}\nstate: ${result.state}\nevidence: ${result.lastEvidenceRef}\n`;
10
+ }
@@ -3,10 +3,15 @@ import { withTaskMutation } from "../core/task-command.js";
3
3
 
4
4
  export { advanceWorkState };
5
5
 
6
- export async function runAdvance({ target, packageRoot, to, taskId, task }) {
6
+ export async function runAdvance({ target, packageRoot, to, taskId, task, authorityContext, runtimeContext }) {
7
7
  if (!to) throw new Error("--to is required for advance");
8
8
  return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "advance", async (ctx) => {
9
- return advanceWorkState(target, to, { packageRoot, taskId: ctx?.taskId ?? null });
9
+ return advanceWorkState(target, to, {
10
+ packageRoot,
11
+ taskId: ctx?.taskId ?? null,
12
+ authorityContext,
13
+ runtimeContext,
14
+ });
10
15
  });
11
16
  }
12
17
 
@@ -0,0 +1,64 @@
1
+ import { readAction } from "../core/actions.js";
2
+ import { readWorkState } from "../core/work-state.js";
3
+ import { requestApproval } from "../core/approvals.js";
4
+ import { evaluateActionCapability } from "../core/capability-policy.js";
5
+ import { loadPolicyIdentity } from "../core/policy-engine.js";
6
+ import {
7
+ E_ACTION_APPROVAL_NOT_REQUIRED,
8
+ E_ACTION_AUTHORITY_REQUIRED,
9
+ E_ACTION_CAPABILITY_DENIED,
10
+ } from "../core/error-codes.js";
11
+ import { withTaskMutation } from "../core/task-command.js";
12
+
13
+ function approvalPolicyError(code, message) {
14
+ const error = new Error(message);
15
+ error.code = code;
16
+ return error;
17
+ }
18
+
19
+ export async function runApprovalRequest({ target, packageRoot, taskId, approvalId, actionId, reason }) {
20
+ return withTaskMutation(target, { taskId, packageRoot }, "approval-request", async (ctx) => {
21
+ const action = await readAction(target, { packageRoot, taskId: ctx.taskId, actionId });
22
+ const state = await readWorkState(target, { packageRoot, taskId: ctx.taskId });
23
+ let policyIdentity;
24
+ try {
25
+ policyIdentity = await loadPolicyIdentity(target, packageRoot, ctx.taskId);
26
+ } catch (error) {
27
+ throw approvalPolicyError(
28
+ error.code ?? "E_POLICY_INVALID",
29
+ `Capability policy epoch validation failed before requesting approval: ${error.message}`,
30
+ );
31
+ }
32
+ if (policyIdentity.status !== "VALID") {
33
+ throw approvalPolicyError(
34
+ policyIdentity.code ?? "E_ACTION_POLICY_LOCK_REQUIRED",
35
+ "The current capability policy is not bound to a valid task policy epoch; repair or refresh policy state before requesting approval.",
36
+ );
37
+ }
38
+ const capability = await evaluateActionCapability({ target, packageRoot, action });
39
+ if (capability.decision === "ALLOW") {
40
+ throw approvalPolicyError(
41
+ E_ACTION_APPROVAL_NOT_REQUIRED,
42
+ "The current capability policy allows this action without approval.",
43
+ );
44
+ }
45
+ if (capability.decision === "DENY") {
46
+ throw approvalPolicyError(
47
+ capability.reasonCode ?? E_ACTION_CAPABILITY_DENIED,
48
+ `The current capability policy denies ${action.capability}; approval cannot override a denial.`,
49
+ );
50
+ }
51
+ if (capability.decision === "REQUIRE_AUTHORITY") {
52
+ throw approvalPolicyError(
53
+ capability.reasonCode ?? E_ACTION_AUTHORITY_REQUIRED,
54
+ "The current capability policy requires trusted host authority; approval cannot substitute for it.",
55
+ );
56
+ }
57
+ return requestApproval(target, { packageRoot, taskId: ctx.taskId, input: {
58
+ approvalId, actionId: action.actionId, actionFingerprint: action.actionFingerprint,
59
+ contractFingerprint: state.contractFingerprint, taskRevision: state.revision ?? 0,
60
+ capability: action.capability, reason,
61
+ } });
62
+ });
63
+ }
64
+ export function formatApprovalRequestResult(result) { return `FORGELOOP APPROVAL REQUESTED\napproval: ${result.approval.approvalId}\nstatus: ${result.approval.status}\n`; }
@@ -0,0 +1,10 @@
1
+ import { resolveApproval } from "../core/approvals.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+ export async function runApprovalResolve({ target, packageRoot, taskId, approvalId, decision, authorityKind, hostGrantRef, authorityContext, reason }) {
4
+ return withTaskMutation(target, { taskId, packageRoot }, "approval-resolve", (ctx) => resolveApproval(target, {
5
+ packageRoot, taskId: ctx.taskId, approvalId, decision, authorityKind, hostGrantRef, reason,
6
+ // Trusted host authority arrives out-of-band; it is never actor input.
7
+ authorityContext,
8
+ }));
9
+ }
10
+ export function formatApprovalResolveResult(result) { return `FORGELOOP APPROVAL RESOLVED\napproval: ${result.approvalId}\ndecision: ${result.decision}\n`; }
@@ -14,6 +14,11 @@ export async function runAudit(options = {}) {
14
14
 
15
15
  export function formatAuditResult(result) {
16
16
  const lines = [`FORGELOOP AUDIT: ${result.status}`];
17
+ if (result.recovery) lines.push(`RECOVERY: ${result.recovery.status} (${result.recovery.recoveryId})`);
18
+ if (result.claims) {
19
+ lines.push(`CLAIM STATE: ${result.claims.state}`);
20
+ lines.push(`MUTATION ALLOWED: ${result.claims.mutationAllowed ? "yes" : "no"}`);
21
+ }
17
22
  for (const error of result.errors) {
18
23
  lines.push(`${error.code}: ${error.message}`);
19
24
  if (error.next) lines.push(`NEXT: ${error.next}`);
@@ -1,5 +1,5 @@
1
1
  import {
2
- computePolicyLockData,
2
+ computePersistedPolicyLockData,
3
3
  evaluateTargetPolicy,
4
4
  loadEffectiveRules,
5
5
  writePolicyLock,
@@ -56,7 +56,7 @@ export async function runBaseline({
56
56
  await writeBaseline(target, baseline, packageRoot);
57
57
 
58
58
  const rules = await loadEffectiveRules(target, packageRoot);
59
- const lock = computePolicyLockData(rules, baseline);
59
+ const lock = await computePersistedPolicyLockData(target, packageRoot, rules, baseline);
60
60
  await writePolicyLock(target, lock, packageRoot);
61
61
 
62
62
  return {
@@ -85,7 +85,7 @@ export async function runBaseline({
85
85
  await writeBaseline(target, baseline, packageRoot);
86
86
 
87
87
  const rules = await loadEffectiveRules(target, packageRoot);
88
- const lock = computePolicyLockData(rules, baseline);
88
+ const lock = await computePersistedPolicyLockData(target, packageRoot, rules, baseline);
89
89
  await writePolicyLock(target, lock, packageRoot);
90
90
 
91
91
  return {
@@ -0,0 +1,6 @@
1
+ import { evaluateTrajectory } from "../core/trajectory-evaluation.js";
2
+ import { withResolvedTask } from "../core/task-command.js";
3
+ export async function runEval({ target, packageRoot, taskId, scenarioPath }) {
4
+ return withResolvedTask(target, { taskId, packageRoot }, (ctx) => evaluateTrajectory({ target, packageRoot, taskId: ctx.taskId, scenarioPath }));
5
+ }
6
+ export function formatEvalResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
@@ -0,0 +1,18 @@
1
+ import { buildTaskHistory, formatHistoryResult } from "../core/history.js";
2
+ import { resolveTaskContext, TASK_SELECTION_MODES } from "../core/task-context.js";
3
+
4
+ export { buildTaskHistory, formatHistoryResult };
5
+
6
+ export async function runHistory({ target, packageRoot, taskId, task, filters = {} }) {
7
+ const resolved = await resolveTaskContext(target, {
8
+ packageRoot,
9
+ taskId: taskId ?? task,
10
+ selectionMode: TASK_SELECTION_MODES.READ,
11
+ });
12
+ return buildTaskHistory({
13
+ target,
14
+ packageRoot,
15
+ taskId: resolved?.taskId ?? null,
16
+ filters,
17
+ });
18
+ }
@@ -187,7 +187,7 @@ export async function runInit({
187
187
  const { discoverPolicy } = await import("../core/policy-discovery.js");
188
188
  const {
189
189
  loadEffectiveRules,
190
- computePolicyLockData,
190
+ computePersistedPolicyLockData,
191
191
  writeDiscoveryReport,
192
192
  writePolicyLock,
193
193
  detectPolicyCapability,
@@ -210,7 +210,7 @@ export async function runInit({
210
210
  }
211
211
  baseline = createBaselineFromViolations([]);
212
212
  effectiveRules = await loadEffectiveRules(target, packageRoot);
213
- lock = computePolicyLockData(effectiveRules, baseline);
213
+ lock = await computePersistedPolicyLockData(target, packageRoot, effectiveRules, baseline);
214
214
  } catch (cause) {
215
215
  throw policyInitializationError(cause);
216
216
  }
@@ -11,6 +11,8 @@ export async function inspectTarget(options = {}) {
11
11
  }
12
12
 
13
13
  export function formatInspectResult(report) {
14
+ const task = report.taskInspection;
15
+ if (task) return formatTaskInspection(report, task);
14
16
  const lines = [
15
17
  `Target: ${report.target.path}`,
16
18
  `Manifest: ${report.manifest.status}`,
@@ -19,9 +21,62 @@ export function formatInspectResult(report) {
19
21
  `Authority source: ${report.authority.sourceType ?? "none configured"} / ${report.authority.trusted ? "TRUSTED" : report.authority.trustMode === "NONE" ? "UNATTESTED" : "UNTRUSTED"}`,
20
22
  `Authority trust: ${report.authority.trustMode}`,
21
23
  `State: ${report.state.status}`,
24
+ ...(report.recovery
25
+ ? [`Recovery: ${report.recovery.status} (${report.recovery.recoveryId})`]
26
+ : []),
27
+ ...(report.claims
28
+ ? [`Claim state: ${report.claims.state}`, `Mutation allowed: ${report.claims.mutationAllowed ? "yes" : "no"}`]
29
+ : []),
22
30
  `Adapters: ${report.adapters.detected.length} detected`,
23
31
  `Findings: ${report.findings.length}`,
24
32
  report.ok ? "healthy: ForgeLoop target is ready" : "unhealthy: ForgeLoop target needs attention",
25
33
  ];
26
34
  return `${lines.join("\n")}\n`;
27
35
  }
36
+
37
+ function formatTaskInspection(report, task) {
38
+ const rule = "-".repeat(56);
39
+ const lines = [
40
+ "ForgeLoop Task Inspection",
41
+ rule,
42
+ `Task: ${task.task ?? report.claims?.state ?? "-"}`,
43
+ `Phase: ${task.lifecycle.phase ?? "-"}`,
44
+ `Verification cycle: ${task.lifecycle.verificationCycle ?? "-"}`,
45
+ `Ledger: ${task.integrity.valid ? "VALID" : "INVALID"}`,
46
+ `Snapshot: ${task.snapshot.consistent ? "CONSISTENT" : "INCONSISTENT"}`,
47
+ `Progress: ${task.progress.status}`,
48
+ "",
49
+ "Verification",
50
+ ];
51
+ for (const check of task.verification.checks) {
52
+ lines.push(` ${check.id.padEnd(20)} ${check.currentResult ?? "unknown"}${Number.isInteger(check.attemptCount) ? ` attempts: ${check.attemptCount}` : ""}`);
53
+ }
54
+ if (task.verification.checks.length === 0) lines.push(" (no checks recorded)");
55
+ lines.push(
56
+ "",
57
+ "Diagnostics",
58
+ ` Cases: ${task.diagnostics.diagnosticCaseCount}`,
59
+ ` Legacy diagnoses: ${task.diagnostics.legacyDiagnosisCount}`,
60
+ ` Interventions: ${task.diagnostics.interventionCount}`,
61
+ ` Dispositions: ${task.diagnostics.dispositionCount}`,
62
+ );
63
+ const surfaces = task.failureSurfaces ?? [];
64
+ if (surfaces.length > 0) {
65
+ lines.push("", "Failure surface");
66
+ for (const entry of surfaces) {
67
+ lines.push(` cycle ${entry.verificationCycle}: ${entry.surface.join(", ")}`);
68
+ }
69
+ }
70
+ if ((task.issues ?? []).length > 0) {
71
+ lines.push("", "Signals");
72
+ for (const issue of task.issues) {
73
+ lines.push(` ${issue.code}`);
74
+ }
75
+ }
76
+ lines.push(
77
+ "",
78
+ "Recommended next command",
79
+ ` ${task.next.command}`,
80
+ );
81
+ return `${lines.join("\n")}\n`;
82
+ }
@@ -0,0 +1,7 @@
1
+ import { buildTrajectoryMetrics } from "../core/trajectory-metrics.js";
2
+ import { withResolvedTask } from "../core/task-command.js";
3
+ export async function runMetrics({ target, packageRoot, taskId }) {
4
+ return withResolvedTask(target, { taskId, packageRoot }, (ctx) =>
5
+ buildTrajectoryMetrics({ target, packageRoot, taskId: ctx.taskId }));
6
+ }
7
+ export function formatMetricsResult(result) { return `${JSON.stringify(result, null, 2)}\n`; }
@@ -1,9 +1,15 @@
1
1
  import { getNextAction } from "../core/next-action.js";
2
2
  import { withResolvedTask } from "../core/task-command.js";
3
3
 
4
- export async function runNext({ target, packageRoot, taskId, task }) {
4
+ export async function runNext({ target, packageRoot, taskId, task, authorityContext, runtimeContext }) {
5
5
  return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
6
- return getNextAction({ target, packageRoot, taskId: ctx?.taskId ?? null });
6
+ return getNextAction({
7
+ target,
8
+ packageRoot,
9
+ taskId: ctx?.taskId ?? null,
10
+ authorityContext,
11
+ runtimeContext,
12
+ });
7
13
  });
8
14
  }
9
15
 
@@ -1,6 +1,6 @@
1
1
  import { discoverPolicy } from "../core/policy-discovery.js";
2
2
  import {
3
- computePolicyLockData,
3
+ computePersistedPolicyLockData,
4
4
  loadEffectiveRules,
5
5
  readBaseline,
6
6
  writeDiscoveryReport,
@@ -15,7 +15,7 @@ export async function runPolicyDiscover({ target = process.cwd(), packageRoot, w
15
15
  await writeDiscoveryReport(target, discovery, packageRoot);
16
16
  const baseline = await readBaseline(target, packageRoot);
17
17
  const rules = await loadEffectiveRules(target, packageRoot);
18
- lock = computePolicyLockData(rules, baseline);
18
+ lock = await computePersistedPolicyLockData(target, packageRoot, rules, baseline);
19
19
  await writePolicyLock(target, lock, packageRoot);
20
20
  }
21
21
 
@@ -1,12 +1,16 @@
1
1
  import { evaluateProgress, PROGRESS_STATUS } from "../core/progress.js";
2
2
  import { readEvents } from "../core/events.js";
3
3
  import { readWorkState } from "../core/work-state.js";
4
- import { resolveTaskContext } from "../core/task-context.js";
4
+ import { resolveTaskContext, TASK_SELECTION_MODES } from "../core/task-context.js";
5
5
 
6
6
  export { evaluateProgress };
7
7
 
8
8
  export async function runProgress({ target, packageRoot, taskId, task }) {
9
- const resolved = await resolveTaskContext(target, { packageRoot, explicitTaskId: taskId ?? task });
9
+ const resolved = await resolveTaskContext(target, {
10
+ packageRoot,
11
+ taskId: taskId ?? task,
12
+ selectionMode: TASK_SELECTION_MODES.READ,
13
+ });
10
14
  const activeTaskId = resolved.taskId;
11
15
 
12
16
  const state = await readWorkState(target, { packageRoot, taskId: activeTaskId });
@@ -1,11 +1,13 @@
1
1
  import { recordDiagnosis } from "../core/diagnosis.js";
2
+ import { recordStructuredDiagnosticCase } from "../core/diagnostic-record.js";
2
3
  import { withTaskMutation } from "../core/task-command.js";
3
4
 
4
- export { recordDiagnosis };
5
+ export { recordDiagnosis, recordStructuredDiagnosticCase };
5
6
 
6
7
  export async function runRecordDiagnosis({
7
8
  target,
8
9
  packageRoot,
10
+ file = null,
9
11
  hypothesis,
10
12
  failureClass,
11
13
  evidenceRefs = [],
@@ -15,6 +17,26 @@ export async function runRecordDiagnosis({
15
17
  taskId,
16
18
  task,
17
19
  }) {
20
+ if (file) {
21
+ const legacyFields = [hypothesis, failureClass, settledBy, nextSafeAction].some((value) => value !== undefined && value !== null)
22
+ || (Array.isArray(evidenceRefs) && evidenceRefs.length > 0)
23
+ || Boolean(evidenceRef);
24
+ if (legacyFields) {
25
+ const error = new Error("record-diagnosis accepts either --file (structured case) or legacy diagnosis fields, not both");
26
+ error.code = "E_INPUT_CONFLICT";
27
+ throw error;
28
+ }
29
+ return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "record-diagnosis", async (ctx) => {
30
+ return recordStructuredDiagnosticCase({
31
+ target,
32
+ packageRoot,
33
+ caseFile: file,
34
+ caseInput: null,
35
+ taskId: ctx?.taskId ?? null,
36
+ });
37
+ });
38
+ }
39
+
18
40
  const refs = Array.isArray(evidenceRefs) && evidenceRefs.length > 0
19
41
  ? evidenceRefs
20
42
  : (evidenceRef ? [evidenceRef] : []);
@@ -34,6 +56,20 @@ export async function runRecordDiagnosis({
34
56
  }
35
57
 
36
58
  export function formatRecordDiagnosisResult(result) {
59
+ if (result.diagnosticCase) {
60
+ const d = result.diagnosticCase;
61
+ return [
62
+ `FORGELOOP DIAGNOSTIC CASE RECORDED`,
63
+ `CYCLE: ${d.verificationCycle}`,
64
+ `REVISION: ${d.diagnosticRevision}`,
65
+ `FAILURE CLASS: ${d.failureClass}`,
66
+ `OBSERVATIONS: ${(d.observations ?? []).length}`,
67
+ `CONTRIBUTORS: ${(d.contributors ?? []).length}`,
68
+ `HYPOTHESES: ${(d.hypotheses ?? []).map((hypothesis) => hypothesis.id).join(", ")}`,
69
+ `NEXT SAFE ACTION: ${d.nextSafeAction?.statement ?? ""}`,
70
+ `FINGERPRINT: ${d.diagnosticFingerprint}`,
71
+ ].join("\n") + "\n";
72
+ }
37
73
  const d = result.diagnosis ?? result.event?.details ?? {};
38
74
  return [
39
75
  `FORGELOOP DIAGNOSIS RECORDED`,
@@ -0,0 +1,45 @@
1
+ import { recordHypothesisDisposition } from "../core/diagnostic-record.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+
4
+ export { recordHypothesisDisposition };
5
+
6
+ export async function runRecordHypothesisDisposition({
7
+ target,
8
+ packageRoot,
9
+ hypothesis,
10
+ hypothesisRef = null,
11
+ status,
12
+ evidenceRefs = [],
13
+ evidenceRef = null,
14
+ reason,
15
+ taskId,
16
+ task,
17
+ }) {
18
+ const refs = Array.isArray(evidenceRefs) && evidenceRefs.length > 0
19
+ ? evidenceRefs
20
+ : (evidenceRef ? [evidenceRef] : []);
21
+
22
+ return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "record-hypothesis-disposition", async (ctx) => {
23
+ return recordHypothesisDisposition({
24
+ target,
25
+ packageRoot,
26
+ hypothesisRef: hypothesis ?? hypothesisRef,
27
+ status,
28
+ evidenceRefs: refs,
29
+ reason,
30
+ taskId: ctx?.taskId ?? null,
31
+ });
32
+ });
33
+ }
34
+
35
+ export function formatRecordHypothesisDispositionResult(result) {
36
+ const disposition = result.disposition ?? {};
37
+ return [
38
+ "FORGELOOP HYPOTHESIS DISPOSITION RECORDED",
39
+ `CYCLE: ${disposition.verificationCycle}`,
40
+ `HYPOTHESIS: ${disposition.hypothesisRef}`,
41
+ `STATUS: ${disposition.status}`,
42
+ `EVIDENCE: ${(disposition.evidenceRefs ?? []).join(", ")}`,
43
+ `REASON: ${disposition.reason}`,
44
+ ].join("\n") + "\n";
45
+ }
@@ -0,0 +1,35 @@
1
+ import { recordIntervention } from "../core/diagnostic-record.js";
2
+ import { withTaskMutation } from "../core/task-command.js";
3
+
4
+ export { recordIntervention };
5
+
6
+ export async function runRecordIntervention({
7
+ target,
8
+ packageRoot,
9
+ file = null,
10
+ taskId,
11
+ task,
12
+ }) {
13
+ return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "record-intervention", async (ctx) => {
14
+ return recordIntervention({
15
+ target,
16
+ packageRoot,
17
+ interventionFile: file,
18
+ interventionInput: null,
19
+ taskId: ctx?.taskId ?? null,
20
+ });
21
+ });
22
+ }
23
+
24
+ export function formatRecordInterventionResult(result) {
25
+ const intervention = result.intervention?.intervention ?? {};
26
+ return [
27
+ "FORGELOOP INTERVENTION RECORDED",
28
+ `CYCLE: ${result.intervention?.verificationCycle}`,
29
+ `ID: ${intervention.id}`,
30
+ `KIND: ${intervention.kind}`,
31
+ `STATEMENT: ${intervention.statement}`,
32
+ `HYPOTHESES: ${(intervention.hypothesisRefs ?? []).join(", ")}`,
33
+ result.repeatedSemanticIntervention ? "WARNING: this semantic intervention has been recorded before" : null,
34
+ ].filter((line) => line !== null).join("\n") + "\n";
35
+ }
@@ -0,0 +1,38 @@
1
+ import { buildTaskReflection } from "../core/reflection.js";
2
+ import { resolveTaskContext, TASK_SELECTION_MODES } from "../core/task-context.js";
3
+
4
+ export { buildTaskReflection };
5
+
6
+ export function formatReflectResult(result) {
7
+ const lines = [
8
+ "ForgeLoop Reflection",
9
+ "─".repeat(56),
10
+ "",
11
+ `Task: ${result.taskId ?? "unknown"}`,
12
+ `Phase: ${result.taskPhase ?? "UNKNOWN"}`,
13
+ `Status: ${result.status}`,
14
+ `Cycles: ${result.verificationCycles}`,
15
+ `Integrity: ${result.integrityValid ? "VALID" : "INCONSISTENT"}`,
16
+ "",
17
+ `Hypotheses: ${result.hypotheses.created} created, ${result.hypotheses.open} open, ${result.hypotheses.supported} supported, ${result.hypotheses.weakened} weakened, ${result.hypotheses.falsified} falsified`,
18
+ `Interventions: ${result.interventions.count} (${result.interventions.informative} informative, ${result.interventions.nonInformative} non-informative)`,
19
+ `Oscillation: ${result.oscillation.detected ? "DETECTED" : "none"}`,
20
+ result.signals.length > 0 ? `Signals: ${result.signals.join(", ")}` : "Signals: none",
21
+ "",
22
+ `Recommended protocol action: ${result.recommendedProtocolAction}`,
23
+ ];
24
+ return lines.join("\n") + "\n";
25
+ }
26
+
27
+ export async function runReflect({ target, packageRoot, taskId, task }) {
28
+ const resolved = await resolveTaskContext(target, {
29
+ packageRoot,
30
+ taskId: taskId ?? task,
31
+ selectionMode: TASK_SELECTION_MODES.READ,
32
+ });
33
+ return buildTaskReflection({
34
+ target,
35
+ packageRoot,
36
+ taskId: resolved?.taskId ?? null,
37
+ });
38
+ }
@@ -1,6 +1,14 @@
1
1
  import { evaluateReport } from "../core/report.js";
2
+ import { withResolvedTask } from "../core/task-command.js";
2
3
 
3
- export { evaluateReport as runReport };
4
+ export async function runReport(options = {}) {
5
+ const target = options.target;
6
+ const packageRoot = options.packageRoot;
7
+ const taskId = options.taskId ?? options.task ?? null;
8
+ return withResolvedTask(target, { taskId, packageRoot }, async (ctx) => {
9
+ return evaluateReport({ ...options, taskId: ctx?.taskId ?? null });
10
+ });
11
+ }
4
12
 
5
13
  export function formatReportResult(result) {
6
14
  const lines = ["ForgeLoop Compliance"];