@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.
- package/.cursor/rules/project-loop.mdc +12 -1
- package/.github/copilot-instructions.md +11 -0
- package/AGENTS.md +13 -1
- package/AGENT_COMPATIBILITY.md +14 -1
- package/CLAUDE.md +11 -1
- package/ENG/accessibility-eng.md +5 -0
- package/ENG/clean-code-eng.md +1 -0
- package/ENG/design-code-eng.md +5 -0
- package/ENG/games-code-design-web-eng.md +6 -0
- package/ENG/perf-code-eng.md +3 -0
- package/ENG/premium-sites-studio-eng.md +9 -0
- package/ENG/sec-code-eng.md +5 -0
- package/ENG/test-code-eng.md +3 -0
- package/GUIDE_ROUTER.md +16 -0
- package/LOOP_ENGINEERING.md +163 -4
- package/ORCHESTRATOR_INTEGRATION.md +15 -0
- package/QUALITY_SCORECARD.md +16 -0
- package/README.md +64 -1
- package/THREAT_MODEL.md +6 -0
- package/conformance/README.md +47 -0
- package/conformance/backend-auth/EXPECTED_ROUTE.json +7 -0
- package/conformance/backend-auth/REQUEST.md +4 -0
- package/conformance/backend-auth/REQUIRED_EVIDENCE.json +3 -0
- package/conformance/backend-auth/REQUIRED_GATES.json +3 -0
- package/conformance/blind-premium-website/EXPECTED_ROUTE.json +7 -0
- package/conformance/blind-premium-website/REQUEST.md +6 -0
- package/conformance/blind-premium-website/REQUIRED_EVIDENCE.json +14 -0
- package/conformance/blind-premium-website/REQUIRED_GATES.json +3 -0
- package/conformance/complete-website/EXPECTED_ROUTE.json +7 -0
- package/conformance/complete-website/REQUEST.md +6 -0
- package/conformance/complete-website/REQUIRED_EVIDENCE.json +3 -0
- package/conformance/complete-website/REQUIRED_GATES.json +3 -0
- package/conformance/docs-only/EXPECTED_ROUTE.json +7 -0
- package/conformance/docs-only/REQUEST.md +4 -0
- package/conformance/docs-only/REQUIRED_EVIDENCE.json +3 -0
- package/conformance/docs-only/REQUIRED_GATES.json +3 -0
- package/conformance/runs/2026-08-11-codex-first-live.md +98 -0
- package/conformance/runs/2026-08-11-codex-second-live.md +87 -0
- package/conformance/simple-bug/EXPECTED_ROUTE.json +7 -0
- package/conformance/simple-bug/REQUEST.md +4 -0
- package/conformance/simple-bug/REQUIRED_EVIDENCE.json +3 -0
- package/conformance/simple-bug/REQUIRED_GATES.json +3 -0
- package/package.json +4 -3
- package/schemas/activation.schema.json +15 -0
- package/schemas/check.schema.json +23 -0
- package/schemas/config.schema.json +16 -0
- package/schemas/current-contract.schema.json +50 -0
- package/schemas/event.schema.json +20 -0
- package/schemas/evidence-coverage.schema.json +17 -0
- package/schemas/execution-receipt.schema.json +10 -0
- package/schemas/gate.schema.json +32 -0
- package/schemas/policy.schema.json +19 -0
- package/schemas/preflight.schema.json +23 -0
- package/schemas/routing-result.schema.json +1 -0
- package/schemas/source-registry.schema.json +29 -0
- package/schemas/task-bundle.schema.json +14 -0
- package/schemas/work-state.schema.json +7 -1
- package/src/cli.js +249 -9
- package/src/commands/activate.js +11 -0
- package/src/commands/advance.js +12 -0
- package/src/commands/audit.js +12 -0
- package/src/commands/bundle.js +10 -0
- package/src/commands/complete.js +18 -0
- package/src/commands/next.js +29 -0
- package/src/commands/policy.js +19 -0
- package/src/commands/preflight.js +12 -0
- package/src/commands/prepare-completion.js +18 -0
- package/src/commands/record-check.js +20 -0
- package/src/commands/report.js +10 -0
- package/src/commands/route.js +14 -2
- package/src/commands/validate-receipt.js +7 -6
- package/src/core/activation.js +16 -0
- package/src/core/artifacts.js +132 -0
- package/src/core/audit.js +88 -0
- package/src/core/bundles.js +120 -0
- package/src/core/checks.js +115 -0
- package/src/core/completion-artifacts.js +321 -0
- package/src/core/completion-relationships.js +141 -0
- package/src/core/completion.js +266 -0
- package/src/core/config.js +35 -0
- package/src/core/conformance.js +7 -0
- package/src/core/contract.js +141 -0
- package/src/core/coverage.js +89 -0
- package/src/core/events.js +158 -0
- package/src/core/execution-prerequisites.js +200 -0
- package/src/core/gate-artifact.js +54 -0
- package/src/core/gates.js +55 -0
- package/src/core/guide-metadata.js +62 -0
- package/src/core/next-action.js +709 -0
- package/src/core/phase.js +212 -0
- package/src/core/policies.js +69 -0
- package/src/core/preflight.js +409 -0
- package/src/core/profile.js +48 -0
- package/src/core/protocol.js +34 -0
- package/src/core/receipt.js +40 -1
- package/src/core/report.js +49 -0
- package/src/core/repository.js +18 -0
- package/src/core/route-artifact.js +33 -0
- package/src/core/router.js +16 -8
- package/src/core/schema-validation.js +11 -0
- package/src/core/sources.js +86 -0
- package/src/core/templates.js +11 -0
- 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 ||
|
|
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 (
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
+
}
|