@cassiomc1/forgeloop 0.1.1 → 0.1.3

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 (103) hide show
  1. package/.cursor/rules/project-loop.mdc +12 -1
  2. package/.github/copilot-instructions.md +11 -0
  3. package/AGENTS.md +13 -1
  4. package/AGENT_COMPATIBILITY.md +14 -1
  5. package/CLAUDE.md +11 -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 +163 -4
  16. package/ORCHESTRATOR_INTEGRATION.md +15 -0
  17. package/QUALITY_SCORECARD.md +16 -0
  18. package/README.md +64 -1
  19. package/THREAT_MODEL.md +6 -0
  20. package/conformance/README.md +47 -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 +4 -3
  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 +249 -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/next.js +29 -0
  65. package/src/commands/policy.js +19 -0
  66. package/src/commands/preflight.js +12 -0
  67. package/src/commands/prepare-completion.js +18 -0
  68. package/src/commands/record-check.js +20 -0
  69. package/src/commands/report.js +10 -0
  70. package/src/commands/route.js +14 -2
  71. package/src/commands/validate-receipt.js +7 -6
  72. package/src/core/activation.js +16 -0
  73. package/src/core/artifacts.js +132 -0
  74. package/src/core/audit.js +88 -0
  75. package/src/core/bundles.js +120 -0
  76. package/src/core/checks.js +115 -0
  77. package/src/core/completion-artifacts.js +321 -0
  78. package/src/core/completion-relationships.js +141 -0
  79. package/src/core/completion.js +266 -0
  80. package/src/core/config.js +35 -0
  81. package/src/core/conformance.js +7 -0
  82. package/src/core/contract.js +141 -0
  83. package/src/core/coverage.js +89 -0
  84. package/src/core/events.js +158 -0
  85. package/src/core/execution-prerequisites.js +200 -0
  86. package/src/core/gate-artifact.js +54 -0
  87. package/src/core/gates.js +55 -0
  88. package/src/core/guide-metadata.js +62 -0
  89. package/src/core/next-action.js +709 -0
  90. package/src/core/phase.js +212 -0
  91. package/src/core/policies.js +69 -0
  92. package/src/core/preflight.js +409 -0
  93. package/src/core/profile.js +48 -0
  94. package/src/core/protocol.js +34 -0
  95. package/src/core/receipt.js +40 -1
  96. package/src/core/report.js +49 -0
  97. package/src/core/repository.js +18 -0
  98. package/src/core/route-artifact.js +33 -0
  99. package/src/core/router.js +16 -8
  100. package/src/core/schema-validation.js +11 -0
  101. package/src/core/sources.js +86 -0
  102. package/src/core/templates.js +11 -0
  103. package/src/core/work-state.js +41 -13
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/preflight.schema.json",
4
+ "title": "ForgeLoop preflight result",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "status", "profile", "contract", "routing", "requiredGates", "satisfiedGates", "errors"],
7
+ "properties": {
8
+ "schemaVersion": { "const": 1 },
9
+ "protocolVersion": { "const": 1 },
10
+ "taskId": { "type": "string", "minLength": 1 },
11
+ "status": { "enum": ["READY", "BLOCKED"] },
12
+ "profile": { "type": "object" },
13
+ "contract": { "type": "object" },
14
+ "routing": { "type": "object" },
15
+ "requiredGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
16
+ "satisfiedGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
17
+ "errors": { "type": "array", "items": { "type": "object" } },
18
+ "fingerprints": { "type": "object" },
19
+ "sources": { "type": "object" },
20
+ "policy": { "type": "object" }
21
+ },
22
+ "additionalProperties": false
23
+ }
@@ -7,6 +7,7 @@
7
7
  "properties": {
8
8
  "schemaVersion": { "const": 1 },
9
9
  "protocolVersion": { "const": 1 },
10
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
10
11
  "input": { "type": "object" },
11
12
  "primary": { "oneOf": [{ "type": "string" }, { "type": "null" }] },
12
13
  "guides": { "type": "array", "items": { "type": "string" } },
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/source-registry.schema.json",
4
+ "title": "ForgeLoop source registry",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "sources"],
7
+ "properties": {
8
+ "schemaVersion": { "const": 1 },
9
+ "protocolVersion": { "const": 1 },
10
+ "sources": {
11
+ "type": "object",
12
+ "additionalProperties": {
13
+ "type": "object",
14
+ "required": ["kind", "summary"],
15
+ "properties": {
16
+ "kind": { "enum": ["user-request", "repository-fact", "observation", "command", "agent-decision", "inference", "unknown"] },
17
+ "summary": { "type": "string", "minLength": 1 },
18
+ "path": { "type": "string", "minLength": 1 },
19
+ "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
20
+ "command": { "type": "string", "minLength": 1 },
21
+ "result": { "type": "string", "minLength": 1 },
22
+ "status": { "type": "string", "minLength": 1 }
23
+ },
24
+ "additionalProperties": false
25
+ }
26
+ }
27
+ },
28
+ "additionalProperties": false
29
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/task-bundle.schema.json",
4
+ "title": "ForgeLoop portable task bundle manifest",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "artifacts"],
7
+ "properties": {
8
+ "schemaVersion": { "const": 1 },
9
+ "protocolVersion": { "const": 1 },
10
+ "taskId": { "type": "string", "minLength": 1 },
11
+ "artifacts": { "type": "array", "items": { "type": "string", "minLength": 1 } }
12
+ },
13
+ "additionalProperties": false
14
+ }
@@ -9,6 +9,7 @@
9
9
  "protocolVersion": { "const": 1 },
10
10
  "taskId": { "type": "string", "minLength": 1 },
11
11
  "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
12
+ "routeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
12
13
  "repositoryFingerprint": {
13
14
  "type": "object",
14
15
  "required": ["branch", "head"],
@@ -20,6 +21,9 @@
20
21
  },
21
22
  "phase": { "type": "string", "minLength": 1 },
22
23
  "selectedGuides": { "type": "array", "items": { "type": "string" } },
24
+ "requiredGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
25
+ "satisfiedGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
26
+ "complianceMode": { "enum": ["advisory", "standard", "strict"] },
23
27
  "completedSteps": { "type": "array", "items": { "type": "string" } },
24
28
  "pendingSteps": { "type": "array", "items": { "type": "string" } },
25
29
  "requiredArtifacts": {
@@ -40,7 +44,9 @@
40
44
  "lastUpdated": { "type": "string", "minLength": 1 },
41
45
  "previousPhase": { "type": "string" },
42
46
  "diagnosedHypothesis": { "type": "string", "minLength": 1 },
43
- "verificationEvidence": { "type": "array", "items": { "type": "object" } }
47
+ "verificationEvidence": { "type": "array", "items": { "type": "object" } },
48
+ "evidenceCoverage": { "type": "array", "items": { "type": "object" } },
49
+ "publicationStatus": { "enum": ["not-published", "local-only", "committed", "pushed", "published", "deployed"] }
44
50
  },
45
51
  "additionalProperties": false
46
52
  }
package/src/cli.js CHANGED
@@ -14,11 +14,23 @@ 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";
27
+ import { formatNextActionResult, runNext } from "./commands/next.js";
17
28
  import { resolveTarget } from "./core/filesystem.js";
18
29
  import { getPackageRoot } from "./core/templates.js";
30
+ import { ARTIFACT_PATHS } from "./core/artifacts.js";
19
31
 
20
32
  function usage(command = null) {
21
- const commands = "init|doctor|update|route|inspect|status|validate-state|clear-state|validate-receipt|validate-protocol";
33
+ const commands = "init|doctor|update|activate|route|preflight|advance|next|prepare-completion|record-check|complete|audit|report|policy|bundle|inspect|status|validate-state|clear-state|validate-receipt|validate-protocol";
22
34
  const options = [" --path <directory> target project directory (default: current directory)"];
23
35
  if (!command || command === "init" || command === "update") {
24
36
  options.push(" --dry-run show planned writes without changing files");
@@ -37,9 +49,21 @@ function usage(command = null) {
37
49
  options.push(" --executable-change declare executable/configuration change");
38
50
  options.push(" --json emit route result as JSON");
39
51
  }
40
- if (!command || ["inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(command)) {
52
+ if (!command || command === "advance") {
53
+ options.push(" --to <phase> destination workflow phase");
54
+ }
55
+ if (!command || ["activate", "advance", "next", "prepare-completion", "record-check", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(command)) {
41
56
  options.push(" --json emit structured output as JSON");
42
57
  }
58
+ if (!command || ["preflight", "complete", "audit", "report"].includes(command)) {
59
+ options.push(" --strict require strict protocol compliance");
60
+ }
61
+ if (!command || command === "policy") {
62
+ options.push(" <name> policy pack name");
63
+ }
64
+ if (!command || command === "bundle") {
65
+ options.push(" --task <id> task ID to export as a portable bundle");
66
+ }
43
67
  if (!command || ["status", "inspect", "validate-protocol"].includes(command)) {
44
68
  options.push(" --contract-file <path> current JSON contract used for freshness comparison");
45
69
  }
@@ -53,6 +77,17 @@ function usage(command = null) {
53
77
  if (!command || command === "validate-receipt") {
54
78
  options.push(" --file <path> receipt file relative to target");
55
79
  }
80
+ if (!command || command === "record-check") {
81
+ options.push(" --id <id> stable check identifier");
82
+ options.push(" --kind <kind> check kind (default: command)");
83
+ options.push(" --requirement <id> completion requirement covered by the check");
84
+ options.push(" --status <status> passed, failed, blocked, or not-run");
85
+ options.push(" --evidence-kind <kind> OBSERVED, INFERRED, NOT_VERIFIED, or BLOCKED");
86
+ options.push(" --command <text> command already run by the agent (recorded only)");
87
+ options.push(" --result <text> observed result supplied by the agent");
88
+ options.push(" --exit-code <number> observed process exit code");
89
+ options.push(" --details <json> additional structured check details");
90
+ }
56
91
  options.push(" --version show the installed package version");
57
92
  options.push(" --help show this help");
58
93
 
@@ -72,6 +107,7 @@ export function parseArgs(argv) {
72
107
  platforms: [],
73
108
  behaviorChange: false,
74
109
  executableChange: false,
110
+ to: null,
75
111
  file: null,
76
112
  contractFile: null,
77
113
  routeFile: null,
@@ -79,6 +115,17 @@ export function parseArgs(argv) {
79
115
  receiptFile: null,
80
116
  taskBriefFiles: [],
81
117
  delegatedResultFiles: [],
118
+ checkId: null,
119
+ checkKind: null,
120
+ checkRequirement: null,
121
+ checkStatus: null,
122
+ checkEvidenceKind: null,
123
+ checkCommand: null,
124
+ checkResult: null,
125
+ checkExitCode: null,
126
+ checkDetails: null,
127
+ policy: null,
128
+ task: null,
82
129
  help: false,
83
130
  version: false,
84
131
  };
@@ -86,7 +133,7 @@ export function parseArgs(argv) {
86
133
 
87
134
  for (let index = 0; index < argv.length; index += 1) {
88
135
  const argument = argv[index];
89
- if (["init", "doctor", "update", "route", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(argument)) {
136
+ if (["init", "doctor", "update", "activate", "route", "preflight", "advance", "next", "prepare-completion", "record-check", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"].includes(argument)) {
90
137
  if (command) throw new Error(`Multiple commands are not supported: ${argument}`);
91
138
  command = argument;
92
139
  } else if (argument === "--help" || argument === "-h") {
@@ -126,6 +173,16 @@ export function parseArgs(argv) {
126
173
  options.behaviorChange = true;
127
174
  } else if (argument === "--executable-change") {
128
175
  options.executableChange = true;
176
+ } else if (argument === "--to") {
177
+ const phase = argv[index + 1];
178
+ if (!phase || phase.startsWith("-")) throw new Error("--to requires a phase");
179
+ options.to = phase;
180
+ index += 1;
181
+ } else if (argument === "--task") {
182
+ const task = argv[index + 1];
183
+ if (!task || task.startsWith("-")) throw new Error("--task requires an ID");
184
+ options.task = task;
185
+ index += 1;
129
186
  } else if (argument === "--file") {
130
187
  const file = argv[index + 1];
131
188
  if (!file || file.startsWith("-")) throw new Error("--file requires a path");
@@ -153,6 +210,71 @@ export function parseArgs(argv) {
153
210
  if (!file || file.startsWith("-")) throw new Error("--delegated-result-file requires a path");
154
211
  options.delegatedResultFiles.push(file);
155
212
  index += 1;
213
+ } else if (argument === "--id") {
214
+ const id = argv[index + 1];
215
+ if (!id || id.startsWith("-")) throw new Error("--id requires a check ID");
216
+ options.checkId = id;
217
+ index += 1;
218
+ } else if (argument.startsWith("--id=")) {
219
+ const id = argument.slice("--id=".length);
220
+ if (!id || id.startsWith("-")) throw new Error("--id requires a check ID");
221
+ options.checkId = id;
222
+ } else if (argument === "--kind") {
223
+ const kind = argv[index + 1];
224
+ if (!kind || kind.startsWith("-")) throw new Error("--kind requires a check kind");
225
+ options.checkKind = kind;
226
+ index += 1;
227
+ } else if (argument === "--requirement") {
228
+ const requirement = argv[index + 1];
229
+ if (!requirement || requirement.startsWith("-")) throw new Error("--requirement requires an evidence target");
230
+ options.checkRequirement = requirement;
231
+ index += 1;
232
+ } else if (argument.startsWith("--requirement=")) {
233
+ const requirement = argument.slice("--requirement=".length);
234
+ if (!requirement) throw new Error("--requirement requires an evidence target");
235
+ options.checkRequirement = requirement;
236
+ } else if (argument === "--status") {
237
+ const status = argv[index + 1];
238
+ if (!status || status.startsWith("-")) throw new Error("--status requires a check status");
239
+ options.checkStatus = status;
240
+ index += 1;
241
+ } else if (argument === "--evidence-kind") {
242
+ const evidenceKind = argv[index + 1];
243
+ if (!evidenceKind || evidenceKind.startsWith("-")) throw new Error("--evidence-kind requires an evidence kind");
244
+ options.checkEvidenceKind = evidenceKind;
245
+ index += 1;
246
+ } else if (argument === "--command") {
247
+ const commandText = argv[index + 1];
248
+ if (!commandText || commandText.startsWith("-")) throw new Error("--command requires recorded text");
249
+ options.checkCommand = commandText;
250
+ index += 1;
251
+ } else if (argument === "--result") {
252
+ const resultText = argv[index + 1];
253
+ if (!resultText || resultText.startsWith("-")) throw new Error("--result requires recorded text");
254
+ options.checkResult = resultText;
255
+ index += 1;
256
+ } else if (argument.startsWith("--result=")) {
257
+ const resultText = argument.slice("--result=".length);
258
+ if (!resultText) throw new Error("--result requires recorded text");
259
+ options.checkResult = resultText;
260
+ } else if (argument === "--exit-code") {
261
+ const exitCode = argv[index + 1];
262
+ if (!exitCode || exitCode.startsWith("-")) throw new Error("--exit-code requires a non-negative integer");
263
+ if (!/^\d+$/.test(exitCode)) throw new Error("--exit-code requires a non-negative integer");
264
+ options.checkExitCode = Number(exitCode);
265
+ index += 1;
266
+ } else if (argument === "--details") {
267
+ const details = argv[index + 1];
268
+ if (!details || details.startsWith("-")) throw new Error("--details requires a JSON object");
269
+ try {
270
+ options.checkDetails = JSON.parse(details);
271
+ } catch {
272
+ throw new Error("--details must be valid JSON");
273
+ }
274
+ if (!options.checkDetails || typeof options.checkDetails !== "object" || Array.isArray(options.checkDetails)) {
275
+ throw new Error("--details must be a JSON object");
276
+ }
277
+ index += 1;
156
278
  } else if (argument === "--path") {
157
279
  options.path = argv[index + 1];
158
280
  if (!options.path || options.path.startsWith("-")) throw new Error("--path requires a directory");
@@ -162,6 +284,8 @@ export function parseArgs(argv) {
162
284
  if (!options.path || options.path.startsWith("-")) throw new Error("--path requires a directory");
163
285
  } else if (argument === "--version" || argument === "-v") {
164
286
  options.version = true;
287
+ } else if (!argument.startsWith("-") && command === "policy" && !options.policy) {
288
+ options.policy = argument;
165
289
  } else if (!argument.startsWith("-") && !command) {
166
290
  command = argument;
167
291
  } else {
@@ -171,7 +295,7 @@ export function parseArgs(argv) {
171
295
 
172
296
  if (!command) return { command: null, options };
173
297
 
174
- const jsonCommands = ["doctor", "route", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
298
+ const jsonCommands = ["doctor", "route", "activate", "advance", "next", "prepare-completion", "record-check", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
175
299
  if (!jsonCommands.includes(command) && options.json) {
176
300
  throw new Error(`Option --json is not valid for ${command}`);
177
301
  }
@@ -181,7 +305,7 @@ export function parseArgs(argv) {
181
305
  ].includes(command) && options.dryRun) {
182
306
  throw new Error(`Option --dry-run is not valid for ${command}`);
183
307
  }
184
- if (command !== "doctor" && options.strict) {
308
+ if (!["doctor", "preflight", "complete", "audit", "report"].includes(command) && options.strict) {
185
309
  throw new Error(`Option --strict is not valid for ${command}`);
186
310
  }
187
311
  if (command !== "doctor" && options.adopt.length > 0) {
@@ -190,6 +314,21 @@ export function parseArgs(argv) {
190
314
  if (command !== "route" && (options.work || options.surfaces.length || options.risks.length || options.platforms.length || options.behaviorChange || options.executableChange)) {
191
315
  throw new Error(`Route options are not valid for ${command}`);
192
316
  }
317
+ if (command !== "advance" && options.to) {
318
+ throw new Error(`Option --to is not valid for ${command}`);
319
+ }
320
+ if (command === "policy" && !options.policy) {
321
+ throw new Error("policy requires a name");
322
+ }
323
+ if (command !== "policy" && options.policy) {
324
+ throw new Error(`Policy name is not valid for ${command}`);
325
+ }
326
+ if (command === "bundle" && !options.task) {
327
+ throw new Error("bundle requires --task");
328
+ }
329
+ if (command !== "bundle" && options.task) {
330
+ throw new Error(`--task is not valid for ${command}`);
331
+ }
193
332
  if (command !== "validate-receipt" && options.file) {
194
333
  throw new Error(`Option --file is not valid for ${command}`);
195
334
  }
@@ -199,6 +338,27 @@ export function parseArgs(argv) {
199
338
  if (command !== "validate-protocol" && (options.routeFile || options.stateFile || options.receiptFile || options.taskBriefFiles.length > 0 || options.delegatedResultFiles.length > 0)) {
200
339
  throw new Error(`Protocol artifact options are not valid for ${command}`);
201
340
  }
341
+ const checkOptions = [
342
+ options.checkId,
343
+ options.checkKind,
344
+ options.checkRequirement,
345
+ options.checkStatus,
346
+ options.checkEvidenceKind,
347
+ options.checkCommand,
348
+ options.checkResult,
349
+ options.checkExitCode,
350
+ options.checkDetails,
351
+ ];
352
+ if (command !== "record-check" && checkOptions.some((value) => value !== null)) {
353
+ throw new Error(`Check recording options are not valid for ${command}`);
354
+ }
355
+ if (command === "record-check" && !options.help) {
356
+ if (!options.checkId) throw new Error("record-check requires --id");
357
+ if (!options.checkRequirement) throw new Error("record-check requires --requirement");
358
+ if (!options.checkStatus) throw new Error("record-check requires --status");
359
+ if (!options.checkEvidenceKind) throw new Error("record-check requires --evidence-kind");
360
+ if (!options.checkCommand && !options.checkResult) throw new Error("record-check requires --command or --result");
361
+ }
202
362
  return { command, options };
203
363
  }
204
364
 
@@ -257,7 +417,9 @@ export async function main(argv = process.argv.slice(2)) {
257
417
  }
258
418
 
259
419
  if (command === "route") {
260
- const result = runRoute({
420
+ const result = await runRoute({
421
+ target,
422
+ packageRoot,
261
423
  workType: options.work,
262
424
  surfaces: options.surfaces,
263
425
  risks: options.risks,
@@ -269,6 +431,84 @@ export async function main(argv = process.argv.slice(2)) {
269
431
  return 0;
270
432
  }
271
433
 
434
+ if (command === "activate") {
435
+ const result = await runActivate({ target, packageRoot });
436
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatActivateResult(result));
437
+ return 0;
438
+ }
439
+
440
+ if (command === "preflight") {
441
+ const result = await runPreflight({ target, packageRoot, strict: options.strict });
442
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPreflightResult(result));
443
+ return result.status === "READY" ? 0 : 1;
444
+ }
445
+
446
+ if (command === "advance") {
447
+ const result = await runAdvance({ target, packageRoot, to: options.to });
448
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatAdvanceResult(result));
449
+ return 0;
450
+ }
451
+
452
+ if (command === "next") {
453
+ const result = await runNext({ target, packageRoot });
454
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatNextActionResult(result));
455
+ return 0;
456
+ }
457
+
458
+ if (command === "prepare-completion") {
459
+ const result = await runPrepareCompletion({ target, packageRoot });
460
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPrepareCompletionResult(result));
461
+ return 0;
462
+ }
463
+
464
+ if (command === "record-check") {
465
+ const result = await runRecordCheck({
466
+ target,
467
+ packageRoot,
468
+ id: options.checkId,
469
+ kind: options.checkKind ?? "command",
470
+ requirement: options.checkRequirement,
471
+ status: options.checkStatus,
472
+ evidenceKind: options.checkEvidenceKind,
473
+ command: options.checkCommand ?? undefined,
474
+ result: options.checkResult ?? undefined,
475
+ exitCode: options.checkExitCode,
476
+ details: options.checkDetails ?? undefined,
477
+ });
478
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordCheckResult(result));
479
+ return 0;
480
+ }
481
+
482
+ if (command === "complete") {
483
+ const result = await runComplete({ target, packageRoot, strict: options.strict });
484
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatCompleteResult(result));
485
+ return result.status === "VALID" ? 0 : 1;
486
+ }
487
+
488
+ if (command === "audit") {
489
+ const result = await runAudit({ target, packageRoot, strict: options.strict });
490
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatAuditResult(result));
491
+ return result.status === "VALID" ? 0 : 1;
492
+ }
493
+
494
+ if (command === "report") {
495
+ const result = await runReport({ target, packageRoot, strict: options.strict });
496
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatReportResult(result));
497
+ return result.verdict === "VALID" ? 0 : 1;
498
+ }
499
+
500
+ if (command === "policy") {
501
+ const result = await runPolicy({ target, packageRoot, name: options.policy });
502
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatPolicyResult(result));
503
+ return 0;
504
+ }
505
+
506
+ if (command === "bundle") {
507
+ const result = await runBundle({ target, packageRoot, taskId: options.task });
508
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatBundleResult(result));
509
+ return 0;
510
+ }
511
+
272
512
  if (command === "inspect") {
273
513
  const result = await inspectTarget({ target, packageRoot, contractFile: options.contractFile });
274
514
  console.log(options.json ? JSON.stringify(result, null, 2) : formatInspectResult(result));
@@ -285,9 +525,9 @@ export async function main(argv = process.argv.slice(2)) {
285
525
  const result = await runValidateProtocol({
286
526
  target,
287
527
  packageRoot,
288
- routeFile: options.routeFile,
289
- stateFile: options.stateFile,
290
- receiptFile: options.receiptFile,
528
+ stateFile: options.stateFile ?? ARTIFACT_PATHS.state,
529
+ receiptFile: options.receiptFile ?? ARTIFACT_PATHS.receipt,
530
+ routeFile: options.routeFile ?? ARTIFACT_PATHS.route,
291
531
  contractFile: options.contractFile,
292
532
  taskBriefFiles: options.taskBriefFiles,
293
533
  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,29 @@
1
+ import { getNextAction } from "../core/next-action.js";
2
+
3
+ export async function runNext({ target, packageRoot }) {
4
+ return getNextAction({ target, packageRoot });
5
+ }
6
+
7
+ export function formatNextActionResult(result) {
8
+ const lines = [
9
+ `FORGELOOP NEXT: ${result.nextAction}`,
10
+ `PHASE: ${result.currentPhase}`,
11
+ ];
12
+ if (result.reasons.length > 0) {
13
+ lines.push("REASONS:");
14
+ lines.push(...result.reasons.map((reason) => `- ${reason.code}: ${reason.message}`));
15
+ }
16
+ if (result.commands.length > 0) {
17
+ lines.push("COMMANDS (SAFE SYNOPSIS ONLY):");
18
+ lines.push(...result.commands.map((command) => `- ${command}`));
19
+ }
20
+ if (result.commandSpecs.length > 0) {
21
+ lines.push("STRUCTURED COMMAND SPECS: Available in --json output; direct-process argv data, not shell syntax.");
22
+ }
23
+ if (result.missingArtifacts.length > 0) {
24
+ lines.push("MISSING ARTIFACTS:");
25
+ lines.push(...result.missingArtifacts.map((artifact) => `- ${artifact}`));
26
+ }
27
+ if (result.terminal) lines.push("STATE: TERMINAL");
28
+ return `${lines.join("\n")}\n`;
29
+ }
@@ -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
+ }