@cassiomc1/forgeloop 0.1.0 → 0.1.2

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 (100) hide show
  1. package/.cursor/rules/project-loop.mdc +7 -1
  2. package/.github/copilot-instructions.md +6 -0
  3. package/AGENTS.md +9 -1
  4. package/AGENT_COMPATIBILITY.md +14 -1
  5. package/CLAUDE.md +7 -1
  6. package/ENG/accessibility-eng.md +5 -0
  7. package/ENG/clean-code-eng.md +1 -0
  8. package/ENG/design-code-eng.md +5 -0
  9. package/ENG/games-code-design-web-eng.md +6 -0
  10. package/ENG/perf-code-eng.md +3 -0
  11. package/ENG/premium-sites-studio-eng.md +9 -0
  12. package/ENG/sec-code-eng.md +5 -0
  13. package/ENG/test-code-eng.md +3 -0
  14. package/GUIDE_ROUTER.md +16 -0
  15. package/LOOP_ENGINEERING.md +139 -4
  16. package/ORCHESTRATOR_INTEGRATION.md +15 -0
  17. package/QUALITY_SCORECARD.md +4 -0
  18. package/README.md +43 -1
  19. package/THREAT_MODEL.md +6 -0
  20. package/conformance/README.md +42 -0
  21. package/conformance/backend-auth/EXPECTED_ROUTE.json +7 -0
  22. package/conformance/backend-auth/REQUEST.md +4 -0
  23. package/conformance/backend-auth/REQUIRED_EVIDENCE.json +3 -0
  24. package/conformance/backend-auth/REQUIRED_GATES.json +3 -0
  25. package/conformance/blind-premium-website/EXPECTED_ROUTE.json +7 -0
  26. package/conformance/blind-premium-website/REQUEST.md +6 -0
  27. package/conformance/blind-premium-website/REQUIRED_EVIDENCE.json +14 -0
  28. package/conformance/blind-premium-website/REQUIRED_GATES.json +3 -0
  29. package/conformance/complete-website/EXPECTED_ROUTE.json +7 -0
  30. package/conformance/complete-website/REQUEST.md +6 -0
  31. package/conformance/complete-website/REQUIRED_EVIDENCE.json +3 -0
  32. package/conformance/complete-website/REQUIRED_GATES.json +3 -0
  33. package/conformance/docs-only/EXPECTED_ROUTE.json +7 -0
  34. package/conformance/docs-only/REQUEST.md +4 -0
  35. package/conformance/docs-only/REQUIRED_EVIDENCE.json +3 -0
  36. package/conformance/docs-only/REQUIRED_GATES.json +3 -0
  37. package/conformance/runs/2026-08-11-codex-first-live.md +98 -0
  38. package/conformance/runs/2026-08-11-codex-second-live.md +87 -0
  39. package/conformance/simple-bug/EXPECTED_ROUTE.json +7 -0
  40. package/conformance/simple-bug/REQUEST.md +4 -0
  41. package/conformance/simple-bug/REQUIRED_EVIDENCE.json +3 -0
  42. package/conformance/simple-bug/REQUIRED_GATES.json +3 -0
  43. package/package.json +5 -4
  44. package/schemas/activation.schema.json +15 -0
  45. package/schemas/check.schema.json +23 -0
  46. package/schemas/config.schema.json +16 -0
  47. package/schemas/current-contract.schema.json +50 -0
  48. package/schemas/event.schema.json +20 -0
  49. package/schemas/evidence-coverage.schema.json +17 -0
  50. package/schemas/execution-receipt.schema.json +10 -0
  51. package/schemas/gate.schema.json +32 -0
  52. package/schemas/policy.schema.json +19 -0
  53. package/schemas/preflight.schema.json +23 -0
  54. package/schemas/routing-result.schema.json +1 -0
  55. package/schemas/source-registry.schema.json +29 -0
  56. package/schemas/task-bundle.schema.json +14 -0
  57. package/schemas/work-state.schema.json +7 -1
  58. package/src/cli.js +230 -9
  59. package/src/commands/activate.js +11 -0
  60. package/src/commands/advance.js +12 -0
  61. package/src/commands/audit.js +12 -0
  62. package/src/commands/bundle.js +10 -0
  63. package/src/commands/complete.js +18 -0
  64. package/src/commands/policy.js +19 -0
  65. package/src/commands/preflight.js +12 -0
  66. package/src/commands/prepare-completion.js +18 -0
  67. package/src/commands/record-check.js +20 -0
  68. package/src/commands/report.js +10 -0
  69. package/src/commands/route.js +14 -2
  70. package/src/commands/validate-receipt.js +7 -6
  71. package/src/core/activation.js +16 -0
  72. package/src/core/artifacts.js +132 -0
  73. package/src/core/audit.js +88 -0
  74. package/src/core/bundles.js +120 -0
  75. package/src/core/checks.js +115 -0
  76. package/src/core/completion-artifacts.js +281 -0
  77. package/src/core/completion.js +295 -0
  78. package/src/core/config.js +35 -0
  79. package/src/core/conformance.js +7 -0
  80. package/src/core/contract.js +141 -0
  81. package/src/core/coverage.js +89 -0
  82. package/src/core/events.js +124 -0
  83. package/src/core/gate-artifact.js +54 -0
  84. package/src/core/gates.js +55 -0
  85. package/src/core/guide-metadata.js +62 -0
  86. package/src/core/phase.js +88 -0
  87. package/src/core/policies.js +69 -0
  88. package/src/core/preflight.js +246 -0
  89. package/src/core/profile.js +48 -0
  90. package/src/core/protocol.js +34 -0
  91. package/src/core/receipt.js +40 -1
  92. package/src/core/report.js +49 -0
  93. package/src/core/repository.js +18 -0
  94. package/src/core/route-artifact.js +33 -0
  95. package/src/core/router.js +16 -8
  96. package/src/core/schema-validation.js +11 -0
  97. package/src/core/sources.js +86 -0
  98. package/src/core/templates.js +22 -4
  99. package/src/core/work-state.js +41 -13
  100. /package/.forgeloop/{.gitignore → forgeloop.gitignore} +0 -0
package/src/cli.js CHANGED
@@ -14,11 +14,22 @@ import { formatRouteResult, runRoute } from "./commands/route.js";
14
14
  import { runValidateReceipt } from "./commands/validate-receipt.js";
15
15
  import { formatValidateProtocolResult, runValidateProtocol } from "./commands/validate-protocol.js";
16
16
  import { runUpdate } from "./commands/update.js";
17
+ import { formatActivateResult, runActivate } from "./commands/activate.js";
18
+ import { formatAdvanceResult, runAdvance } from "./commands/advance.js";
19
+ import { formatPreflightResult, runPreflight } from "./commands/preflight.js";
20
+ import { formatCompleteResult, runComplete } from "./commands/complete.js";
21
+ import { formatAuditResult, runAudit } from "./commands/audit.js";
22
+ import { formatReportResult, runReport } from "./commands/report.js";
23
+ import { formatPolicyResult, runPolicy } from "./commands/policy.js";
24
+ import { formatBundleResult, runBundle } from "./commands/bundle.js";
25
+ import { formatPrepareCompletionResult, runPrepareCompletion } from "./commands/prepare-completion.js";
26
+ import { formatRecordCheckResult, runRecordCheck } from "./commands/record-check.js";
17
27
  import { resolveTarget } from "./core/filesystem.js";
18
28
  import { getPackageRoot } from "./core/templates.js";
29
+ import { ARTIFACT_PATHS } from "./core/artifacts.js";
19
30
 
20
31
  function usage(command = null) {
21
- const commands = "init|doctor|update|route|inspect|status|validate-state|clear-state|validate-receipt|validate-protocol";
32
+ const commands = "init|doctor|update|activate|route|preflight|advance|prepare-completion|record-check|complete|audit|report|policy|bundle|inspect|status|validate-state|clear-state|validate-receipt|validate-protocol";
22
33
  const options = [" --path <directory> target project directory (default: current directory)"];
23
34
  if (!command || command === "init" || command === "update") {
24
35
  options.push(" --dry-run show planned writes without changing files");
@@ -37,9 +48,21 @@ function usage(command = null) {
37
48
  options.push(" --executable-change declare executable/configuration change");
38
49
  options.push(" --json emit route result as JSON");
39
50
  }
40
- if (!command || ["inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(command)) {
51
+ if (!command || command === "advance") {
52
+ options.push(" --to <phase> destination workflow phase");
53
+ }
54
+ if (!command || ["activate", "advance", "prepare-completion", "record-check", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(command)) {
41
55
  options.push(" --json emit structured output as JSON");
42
56
  }
57
+ if (!command || ["preflight", "complete", "audit", "report"].includes(command)) {
58
+ options.push(" --strict require strict protocol compliance");
59
+ }
60
+ if (!command || command === "policy") {
61
+ options.push(" <name> policy pack name");
62
+ }
63
+ if (!command || command === "bundle") {
64
+ options.push(" --task <id> task ID to export as a portable bundle");
65
+ }
43
66
  if (!command || ["status", "inspect", "validate-protocol"].includes(command)) {
44
67
  options.push(" --contract-file <path> current JSON contract used for freshness comparison");
45
68
  }
@@ -53,6 +76,17 @@ function usage(command = null) {
53
76
  if (!command || command === "validate-receipt") {
54
77
  options.push(" --file <path> receipt file relative to target");
55
78
  }
79
+ if (!command || command === "record-check") {
80
+ options.push(" --id <id> stable check identifier");
81
+ options.push(" --kind <kind> check kind (default: command)");
82
+ options.push(" --requirement <id> completion requirement covered by the check");
83
+ options.push(" --status <status> passed, failed, blocked, or not-run");
84
+ options.push(" --evidence-kind <kind> OBSERVED, INFERRED, NOT_VERIFIED, or BLOCKED");
85
+ options.push(" --command <text> command already run by the agent (recorded only)");
86
+ options.push(" --result <text> observed result supplied by the agent");
87
+ options.push(" --exit-code <number> observed process exit code");
88
+ options.push(" --details <json> additional structured check details");
89
+ }
56
90
  options.push(" --version show the installed package version");
57
91
  options.push(" --help show this help");
58
92
 
@@ -72,6 +106,7 @@ export function parseArgs(argv) {
72
106
  platforms: [],
73
107
  behaviorChange: false,
74
108
  executableChange: false,
109
+ to: null,
75
110
  file: null,
76
111
  contractFile: null,
77
112
  routeFile: null,
@@ -79,6 +114,17 @@ export function parseArgs(argv) {
79
114
  receiptFile: null,
80
115
  taskBriefFiles: [],
81
116
  delegatedResultFiles: [],
117
+ checkId: null,
118
+ checkKind: null,
119
+ checkRequirement: null,
120
+ checkStatus: null,
121
+ checkEvidenceKind: null,
122
+ checkCommand: null,
123
+ checkResult: null,
124
+ checkExitCode: null,
125
+ checkDetails: null,
126
+ policy: null,
127
+ task: null,
82
128
  help: false,
83
129
  version: false,
84
130
  };
@@ -86,7 +132,7 @@ export function parseArgs(argv) {
86
132
 
87
133
  for (let index = 0; index < argv.length; index += 1) {
88
134
  const argument = argv[index];
89
- if (["init", "doctor", "update", "route", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(argument)) {
135
+ if (["init", "doctor", "update", "activate", "route", "preflight", "advance", "prepare-completion", "record-check", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(argument)) {
90
136
  if (command) throw new Error(`Multiple commands are not supported: ${argument}`);
91
137
  command = argument;
92
138
  } else if (argument === "--help" || argument === "-h") {
@@ -126,6 +172,16 @@ export function parseArgs(argv) {
126
172
  options.behaviorChange = true;
127
173
  } else if (argument === "--executable-change") {
128
174
  options.executableChange = true;
175
+ } else if (argument === "--to") {
176
+ const phase = argv[index + 1];
177
+ if (!phase || phase.startsWith("-")) throw new Error("--to requires a phase");
178
+ options.to = phase;
179
+ index += 1;
180
+ } else if (argument === "--task") {
181
+ const task = argv[index + 1];
182
+ if (!task || task.startsWith("-")) throw new Error("--task requires an ID");
183
+ options.task = task;
184
+ index += 1;
129
185
  } else if (argument === "--file") {
130
186
  const file = argv[index + 1];
131
187
  if (!file || file.startsWith("-")) throw new Error("--file requires a path");
@@ -153,6 +209,59 @@ export function parseArgs(argv) {
153
209
  if (!file || file.startsWith("-")) throw new Error("--delegated-result-file requires a path");
154
210
  options.delegatedResultFiles.push(file);
155
211
  index += 1;
212
+ } else if (argument === "--id") {
213
+ const id = argv[index + 1];
214
+ if (!id || id.startsWith("-")) throw new Error("--id requires a check ID");
215
+ options.checkId = id;
216
+ index += 1;
217
+ } else if (argument === "--kind") {
218
+ const kind = argv[index + 1];
219
+ if (!kind || kind.startsWith("-")) throw new Error("--kind requires a check kind");
220
+ options.checkKind = kind;
221
+ index += 1;
222
+ } else if (argument === "--requirement") {
223
+ const requirement = argv[index + 1];
224
+ if (!requirement || requirement.startsWith("-")) throw new Error("--requirement requires an evidence target");
225
+ options.checkRequirement = requirement;
226
+ index += 1;
227
+ } else if (argument === "--status") {
228
+ const status = argv[index + 1];
229
+ if (!status || status.startsWith("-")) throw new Error("--status requires a check status");
230
+ options.checkStatus = status;
231
+ index += 1;
232
+ } else if (argument === "--evidence-kind") {
233
+ const evidenceKind = argv[index + 1];
234
+ if (!evidenceKind || evidenceKind.startsWith("-")) throw new Error("--evidence-kind requires an evidence kind");
235
+ options.checkEvidenceKind = evidenceKind;
236
+ index += 1;
237
+ } else if (argument === "--command") {
238
+ const commandText = argv[index + 1];
239
+ if (!commandText || commandText.startsWith("-")) throw new Error("--command requires recorded text");
240
+ options.checkCommand = commandText;
241
+ index += 1;
242
+ } else if (argument === "--result") {
243
+ const resultText = argv[index + 1];
244
+ if (!resultText || resultText.startsWith("-")) throw new Error("--result requires recorded text");
245
+ options.checkResult = resultText;
246
+ index += 1;
247
+ } else if (argument === "--exit-code") {
248
+ const exitCode = argv[index + 1];
249
+ if (!exitCode || exitCode.startsWith("-")) throw new Error("--exit-code requires a non-negative integer");
250
+ if (!/^\d+$/.test(exitCode)) throw new Error("--exit-code requires a non-negative integer");
251
+ options.checkExitCode = Number(exitCode);
252
+ index += 1;
253
+ } else if (argument === "--details") {
254
+ const details = argv[index + 1];
255
+ if (!details || details.startsWith("-")) throw new Error("--details requires a JSON object");
256
+ try {
257
+ options.checkDetails = JSON.parse(details);
258
+ } catch {
259
+ throw new Error("--details must be valid JSON");
260
+ }
261
+ if (!options.checkDetails || typeof options.checkDetails !== "object" || Array.isArray(options.checkDetails)) {
262
+ throw new Error("--details must be a JSON object");
263
+ }
264
+ index += 1;
156
265
  } else if (argument === "--path") {
157
266
  options.path = argv[index + 1];
158
267
  if (!options.path || options.path.startsWith("-")) throw new Error("--path requires a directory");
@@ -162,6 +271,8 @@ export function parseArgs(argv) {
162
271
  if (!options.path || options.path.startsWith("-")) throw new Error("--path requires a directory");
163
272
  } else if (argument === "--version" || argument === "-v") {
164
273
  options.version = true;
274
+ } else if (!argument.startsWith("-") && command === "policy" && !options.policy) {
275
+ options.policy = argument;
165
276
  } else if (!argument.startsWith("-") && !command) {
166
277
  command = argument;
167
278
  } else {
@@ -171,7 +282,7 @@ export function parseArgs(argv) {
171
282
 
172
283
  if (!command) return { command: null, options };
173
284
 
174
- const jsonCommands = ["doctor", "route", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
285
+ const jsonCommands = ["doctor", "route", "activate", "advance", "prepare-completion", "record-check", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
175
286
  if (!jsonCommands.includes(command) && options.json) {
176
287
  throw new Error(`Option --json is not valid for ${command}`);
177
288
  }
@@ -181,7 +292,7 @@ export function parseArgs(argv) {
181
292
  ].includes(command) && options.dryRun) {
182
293
  throw new Error(`Option --dry-run is not valid for ${command}`);
183
294
  }
184
- if (command !== "doctor" && options.strict) {
295
+ if (!["doctor", "preflight", "complete", "audit", "report"].includes(command) && options.strict) {
185
296
  throw new Error(`Option --strict is not valid for ${command}`);
186
297
  }
187
298
  if (command !== "doctor" && options.adopt.length > 0) {
@@ -190,6 +301,21 @@ export function parseArgs(argv) {
190
301
  if (command !== "route" && (options.work || options.surfaces.length || options.risks.length || options.platforms.length || options.behaviorChange || options.executableChange)) {
191
302
  throw new Error(`Route options are not valid for ${command}`);
192
303
  }
304
+ if (command !== "advance" && options.to) {
305
+ throw new Error(`Option --to is not valid for ${command}`);
306
+ }
307
+ if (command === "policy" && !options.policy) {
308
+ throw new Error("policy requires a name");
309
+ }
310
+ if (command !== "policy" && options.policy) {
311
+ throw new Error(`Policy name is not valid for ${command}`);
312
+ }
313
+ if (command === "bundle" && !options.task) {
314
+ throw new Error("bundle requires --task");
315
+ }
316
+ if (command !== "bundle" && options.task) {
317
+ throw new Error(`--task is not valid for ${command}`);
318
+ }
193
319
  if (command !== "validate-receipt" && options.file) {
194
320
  throw new Error(`Option --file is not valid for ${command}`);
195
321
  }
@@ -199,6 +325,27 @@ export function parseArgs(argv) {
199
325
  if (command !== "validate-protocol" && (options.routeFile || options.stateFile || options.receiptFile || options.taskBriefFiles.length > 0 || options.delegatedResultFiles.length > 0)) {
200
326
  throw new Error(`Protocol artifact options are not valid for ${command}`);
201
327
  }
328
+ const checkOptions = [
329
+ options.checkId,
330
+ options.checkKind,
331
+ options.checkRequirement,
332
+ options.checkStatus,
333
+ options.checkEvidenceKind,
334
+ options.checkCommand,
335
+ options.checkResult,
336
+ options.checkExitCode,
337
+ options.checkDetails,
338
+ ];
339
+ if (command !== "record-check" && checkOptions.some((value) => value !== null)) {
340
+ throw new Error(`Check recording options are not valid for ${command}`);
341
+ }
342
+ if (command === "record-check" && !options.help) {
343
+ if (!options.checkId) throw new Error("record-check requires --id");
344
+ if (!options.checkRequirement) throw new Error("record-check requires --requirement");
345
+ if (!options.checkStatus) throw new Error("record-check requires --status");
346
+ if (!options.checkEvidenceKind) throw new Error("record-check requires --evidence-kind");
347
+ if (!options.checkCommand && !options.checkResult) throw new Error("record-check requires --command or --result");
348
+ }
202
349
  return { command, options };
203
350
  }
204
351
 
@@ -257,7 +404,9 @@ export async function main(argv = process.argv.slice(2)) {
257
404
  }
258
405
 
259
406
  if (command === "route") {
260
- const result = runRoute({
407
+ const result = await runRoute({
408
+ target,
409
+ packageRoot,
261
410
  workType: options.work,
262
411
  surfaces: options.surfaces,
263
412
  risks: options.risks,
@@ -269,6 +418,78 @@ export async function main(argv = process.argv.slice(2)) {
269
418
  return 0;
270
419
  }
271
420
 
421
+ if (command === "activate") {
422
+ const result = await runActivate({ target, packageRoot });
423
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatActivateResult(result));
424
+ return 0;
425
+ }
426
+
427
+ if (command === "preflight") {
428
+ const result = await runPreflight({ target, packageRoot, strict: options.strict });
429
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPreflightResult(result));
430
+ return result.status === "READY" ? 0 : 1;
431
+ }
432
+
433
+ if (command === "advance") {
434
+ const result = await runAdvance({ target, packageRoot, to: options.to });
435
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatAdvanceResult(result));
436
+ return 0;
437
+ }
438
+
439
+ if (command === "prepare-completion") {
440
+ const result = await runPrepareCompletion({ target, packageRoot });
441
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPrepareCompletionResult(result));
442
+ return 0;
443
+ }
444
+
445
+ if (command === "record-check") {
446
+ const result = await runRecordCheck({
447
+ target,
448
+ packageRoot,
449
+ id: options.checkId,
450
+ kind: options.checkKind ?? "command",
451
+ requirement: options.checkRequirement,
452
+ status: options.checkStatus,
453
+ evidenceKind: options.checkEvidenceKind,
454
+ command: options.checkCommand,
455
+ result: options.checkResult,
456
+ exitCode: options.checkExitCode,
457
+ details: options.checkDetails ?? undefined,
458
+ });
459
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordCheckResult(result));
460
+ return 0;
461
+ }
462
+
463
+ if (command === "complete") {
464
+ const result = await runComplete({ target, packageRoot, strict: options.strict });
465
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatCompleteResult(result));
466
+ return result.status === "VALID" ? 0 : 1;
467
+ }
468
+
469
+ if (command === "audit") {
470
+ const result = await runAudit({ target, packageRoot, strict: options.strict });
471
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatAuditResult(result));
472
+ return result.status === "VALID" ? 0 : 1;
473
+ }
474
+
475
+ if (command === "report") {
476
+ const result = await runReport({ target, packageRoot, strict: options.strict });
477
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatReportResult(result));
478
+ return result.verdict === "VALID" ? 0 : 1;
479
+ }
480
+
481
+ if (command === "policy") {
482
+ const result = await runPolicy({ target, packageRoot, name: options.policy });
483
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPolicyResult(result));
484
+ return 0;
485
+ }
486
+
487
+ if (command === "bundle") {
488
+ const result = await runBundle({ target, packageRoot, taskId: options.task });
489
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatBundleResult(result));
490
+ return 0;
491
+ }
492
+
272
493
  if (command === "inspect") {
273
494
  const result = await inspectTarget({ target, packageRoot, contractFile: options.contractFile });
274
495
  console.log(options.json ? JSON.stringify(result, null, 2) : formatInspectResult(result));
@@ -285,9 +506,9 @@ export async function main(argv = process.argv.slice(2)) {
285
506
  const result = await runValidateProtocol({
286
507
  target,
287
508
  packageRoot,
288
- routeFile: options.routeFile,
289
- stateFile: options.stateFile,
290
- receiptFile: options.receiptFile,
509
+ stateFile: options.stateFile ?? ARTIFACT_PATHS.state,
510
+ receiptFile: options.receiptFile ?? ARTIFACT_PATHS.receipt,
511
+ routeFile: options.routeFile ?? ARTIFACT_PATHS.route,
291
512
  contractFile: options.contractFile,
292
513
  taskBriefFiles: options.taskBriefFiles,
293
514
  delegatedResultFiles: options.delegatedResultFiles,
@@ -0,0 +1,11 @@
1
+ import { activateSession } from "../core/activation.js";
2
+
3
+ export { activateSession };
4
+
5
+ export async function runActivate({ target, packageRoot }) {
6
+ return activateSession(target, packageRoot);
7
+ }
8
+
9
+ export function formatActivateResult(result) {
10
+ return `activated: ${result.activationMarker}\nsession: ${result.sessionId}\n`;
11
+ }
@@ -0,0 +1,12 @@
1
+ import { advanceWorkState } from "../core/phase.js";
2
+
3
+ export { advanceWorkState };
4
+
5
+ export async function runAdvance({ target, packageRoot, to }) {
6
+ if (!to) throw new Error("--to is required for advance");
7
+ return advanceWorkState(target, to, { packageRoot });
8
+ }
9
+
10
+ export function formatAdvanceResult(result) {
11
+ return `phase: ${result.phase}\nprevious: ${result.previousPhase ?? "none"}\n`;
12
+ }
@@ -0,0 +1,12 @@
1
+ import { evaluateAudit } from "../core/audit.js";
2
+
3
+ export { evaluateAudit as runAudit };
4
+
5
+ export function formatAuditResult(result) {
6
+ const lines = [`FORGELOOP AUDIT: ${result.status}`];
7
+ for (const error of result.errors) {
8
+ lines.push(`${error.code}: ${error.message}`);
9
+ if (error.next) lines.push(`NEXT: ${error.next}`);
10
+ }
11
+ return `${lines.join("\n")}\n`;
12
+ }
@@ -0,0 +1,10 @@
1
+ import { exportTaskBundle } from "../core/bundles.js";
2
+
3
+ export async function runBundle({ target, packageRoot, taskId }) {
4
+ if (!taskId) throw new Error("--task is required for bundle");
5
+ return exportTaskBundle(target, taskId, packageRoot);
6
+ }
7
+
8
+ export function formatBundleResult(result) {
9
+ return `bundle: ${result.taskId}\npath: ${result.path}\nartifacts: ${result.artifacts.join(", ")}\n`;
10
+ }
@@ -0,0 +1,18 @@
1
+ import { runComplete as evaluateAndComplete } from "../core/completion.js";
2
+
3
+ export { evaluateAndComplete as runComplete };
4
+
5
+ export function formatCompleteResult(result) {
6
+ const lines = [`FORGELOOP COMPLETE: ${result.status}`];
7
+ if (result.status === "VALID") {
8
+ lines.push(`TASK: ${result.taskStatus}`);
9
+ lines.push(`VERIFICATION: ${result.verificationStatus}`);
10
+ lines.push(`PUBLICATION: ${result.publicationStatus}`);
11
+ lines.push(`PRODUCTION_READINESS: ${result.productionReadiness}`);
12
+ }
13
+ for (const error of result.errors) {
14
+ lines.push(`${error.code}: ${error.message}`);
15
+ if (error.next) lines.push(`NEXT: ${error.next}`);
16
+ }
17
+ return `${lines.join("\n")}\n`;
18
+ }
@@ -0,0 +1,19 @@
1
+ import { createConfig, writeConfig } from "../core/config.js";
2
+ import { getPolicy, validatePolicy } from "../core/policies.js";
3
+
4
+ export async function runPolicy({ target, packageRoot, name }) {
5
+ const policy = getPolicy(name);
6
+ await validatePolicy(policy, packageRoot);
7
+ const config = createConfig({
8
+ complianceMode: policy.complianceMode,
9
+ policy: policy.name,
10
+ requiredGates: policy.requiredGates,
11
+ requiredEvidence: policy.requiredEvidence,
12
+ });
13
+ await writeConfig(target, config, packageRoot);
14
+ return { policy, config };
15
+ }
16
+
17
+ export function formatPolicyResult(result) {
18
+ return `policy: ${result.policy.name}\nmode: ${result.policy.complianceMode}\n`;
19
+ }
@@ -0,0 +1,12 @@
1
+ import { runPreflight as evaluateAndPersistPreflight } from "../core/preflight.js";
2
+
3
+ export { evaluateAndPersistPreflight as runPreflight };
4
+
5
+ export function formatPreflightResult(result) {
6
+ const lines = [`FORGELOOP PREFLIGHT: ${result.status}`];
7
+ for (const error of result.errors) {
8
+ lines.push(`${error.code}: ${error.message}`);
9
+ if (error.next) lines.push(`NEXT: ${error.next}`);
10
+ }
11
+ return `${lines.join("\n")}\n`;
12
+ }
@@ -0,0 +1,18 @@
1
+ import { prepareCompletion as prepareCompletionArtifacts } from "../core/completion-artifacts.js";
2
+
3
+ export { prepareCompletionArtifacts as prepareCompletion };
4
+
5
+ export async function runPrepareCompletion({ target, packageRoot }) {
6
+ return prepareCompletionArtifacts({ target, packageRoot });
7
+ }
8
+
9
+ export function formatPrepareCompletionResult(result) {
10
+ return [
11
+ "FORGELOOP COMPLETION PREPARED",
12
+ `receipt: ${result.path}`,
13
+ `task: ${result.receipt.taskId}`,
14
+ `required evidence: ${result.requiredEvidence.length}`,
15
+ `changed paths: ${result.changedPaths.length}`,
16
+ "",
17
+ ].join("\n");
18
+ }
@@ -0,0 +1,20 @@
1
+ import { recordCheck as recordCheckArtifact } from "../core/completion-artifacts.js";
2
+
3
+ export { recordCheckArtifact as recordCheck };
4
+
5
+ export async function runRecordCheck(input) {
6
+ return recordCheckArtifact(input);
7
+ }
8
+
9
+ export function formatRecordCheckResult(result) {
10
+ return [
11
+ "FORGELOOP CHECK RECORDED",
12
+ `id: ${result.check.id}`,
13
+ `requirement: ${result.check.requirement}`,
14
+ `status: ${result.check.status}`,
15
+ `evidence: ${result.evidence.kind}`,
16
+ `coverage: ${result.coverage.find((item) => item.requirement === result.check.requirement)?.status ?? "NOT_VERIFIED"}`,
17
+ `receipt: ${result.path}`,
18
+ "",
19
+ ].join("\n");
20
+ }
@@ -0,0 +1,10 @@
1
+ import { evaluateReport } from "../core/report.js";
2
+
3
+ export { evaluateReport as runReport };
4
+
5
+ export function formatReportResult(result) {
6
+ const lines = ["ForgeLoop Compliance"];
7
+ for (const item of result.sections) lines.push(`${item.label.padEnd(20)} ${item.status}`);
8
+ lines.push(`\nVERDICT: ${result.verdict}`);
9
+ return `${lines.join("\n")}\n`;
10
+ }
@@ -1,7 +1,9 @@
1
1
  import { evaluateRoute } from "../core/router.js";
2
+ import { persistRoute } from "../core/route-artifact.js";
3
+ import { readContract } from "../core/contract.js";
2
4
 
3
- export function runRoute({ workType, surfaces, risks, platforms, behaviorChange, executableChange }) {
4
- return evaluateRoute({
5
+ export async function runRoute({ target, packageRoot, workType, surfaces, risks, platforms, behaviorChange, executableChange }) {
6
+ const route = evaluateRoute({
5
7
  workType,
6
8
  surfaces,
7
9
  risks,
@@ -9,6 +11,16 @@ export function runRoute({ workType, surfaces, risks, platforms, behaviorChange,
9
11
  behaviorChange,
10
12
  executableChange,
11
13
  });
14
+ if (target && packageRoot) {
15
+ let contractFingerprint;
16
+ try {
17
+ contractFingerprint = (await readContract(target, packageRoot)).fingerprint;
18
+ } catch {
19
+ contractFingerprint = undefined;
20
+ }
21
+ await persistRoute(target, route, packageRoot, { contractFingerprint });
22
+ }
23
+ return route;
12
24
  }
13
25
 
14
26
  export function formatRouteResult(result) {
@@ -1,19 +1,20 @@
1
1
  import { assertSafePath, ensureWithin, readBytes } from "../core/filesystem.js";
2
2
  import { validateReceipt } from "../core/receipt.js";
3
3
  import { assertJsonBytes, assertJsonLimits } from "../core/json-safety.js";
4
+ import { ARTIFACT_PATHS } from "../core/artifacts.js";
4
5
 
5
6
  export async function runValidateReceipt({ target, packageRoot, file }) {
6
- if (!file) throw new Error("--file is required for validate-receipt");
7
- await assertSafePath(target, file);
8
- const receiptPath = ensureWithin(target, file);
7
+ const relativeFile = file ?? ARTIFACT_PATHS.receipt;
8
+ await assertSafePath(target, relativeFile);
9
+ const receiptPath = ensureWithin(target, relativeFile);
9
10
  let receipt;
10
11
  try {
11
12
  const bytes = await readBytes(receiptPath);
12
- assertJsonBytes(bytes, file);
13
+ assertJsonBytes(bytes, relativeFile);
13
14
  receipt = JSON.parse(bytes.toString("utf8"));
14
- assertJsonLimits(receipt, file);
15
+ assertJsonLimits(receipt, relativeFile);
15
16
  } catch (error) {
16
- throw new Error(`Unable to parse receipt ${file}: ${error.message}`);
17
+ throw new Error(`Unable to parse receipt ${relativeFile}: ${error.message}`);
17
18
  }
18
19
  return validateReceipt(receipt, packageRoot);
19
20
  }
@@ -0,0 +1,16 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ import { ARTIFACT_PATHS, writeJsonArtifact } from "./artifacts.js";
4
+ import { PROTOCOL_VERSION } from "./protocol.js";
5
+
6
+ export async function activateSession(target, packageRoot, options = {}) {
7
+ const value = {
8
+ schemaVersion: 1,
9
+ protocolVersion: PROTOCOL_VERSION,
10
+ sessionId: randomUUID(),
11
+ activationMarker: `forgeloop-${randomUUID()}`,
12
+ createdAt: options.createdAt ?? new Date().toISOString(),
13
+ };
14
+ const written = await writeJsonArtifact(target, ARTIFACT_PATHS.session, value, "activation", packageRoot, options);
15
+ return written.value;
16
+ }