@cassiomc1/forgeloop 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/project-loop.mdc +6 -3
- package/.github/copilot-instructions.md +5 -0
- package/AGENTS.md +6 -0
- package/CLAUDE.md +6 -0
- package/DOCS_INDEX.md +5 -0
- package/ENG/accessibility-eng.md +12 -2
- package/ENG/design-code-eng.md +22 -1
- package/LOOP_ENGINEERING.md +28 -0
- package/PROTOCOL_INTEGRATION.md +26 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +11 -0
- package/THREAT_MODEL.md +24 -0
- package/completions/_forgeloop +4 -1
- package/completions/forgeloop.bash +7 -1
- package/completions/forgeloop.fish +19 -1
- package/docs/AGENT_PROTOCOL_SUMMARY.md +6 -1
- package/docs/ARTIFACT_REFERENCE.md +128 -0
- package/docs/CLI_REFERENCE.md +84 -1
- package/docs/KNOWLEDGE_SOURCES.md +161 -0
- package/docs/MCP.md +1 -1
- package/docs/RECIPES.md +31 -0
- package/docs/STRUCTURAL_QUALITY.md +350 -0
- package/docs/TROUBLESHOOTING.md +107 -0
- package/package.json +3 -1
- package/schemas/config.schema.json +46 -0
- package/schemas/preflight.schema.json +2 -1
- package/schemas/structural-quality.schema.json +175 -0
- package/src/cli.js +18 -0
- package/src/commands/quality-baseline.js +28 -0
- package/src/commands/quality-status.js +34 -0
- package/src/commands/quality-verify.js +30 -0
- package/src/core/artifact-registry.js +12 -0
- package/src/core/audit.js +38 -0
- package/src/core/bundles.js +134 -1
- package/src/core/cli-command-definitions.js +45 -0
- package/src/core/command-executors.js +16 -0
- package/src/core/command-input.js +12 -0
- package/src/core/completion-artifacts.js +2 -0
- package/src/core/completion.js +42 -0
- package/src/core/config.js +3 -0
- package/src/core/error-codes.js +73 -0
- package/src/core/filesystem.js +18 -3
- package/src/core/inspect.js +64 -0
- package/src/core/integration-invocation-policy.js +15 -0
- package/src/core/integration-resources.js +17 -0
- package/src/core/next-action-model.js +11 -1
- package/src/core/next-action-phases.js +84 -5
- package/src/core/phase.js +9 -1
- package/src/core/preflight.js +33 -0
- package/src/core/protocol-info.js +15 -0
- package/src/core/runtime-context.js +27 -0
- package/src/core/schema-validation.js +1 -0
- package/src/core/structural-quality/artifacts.js +329 -0
- package/src/core/structural-quality/constants.js +67 -0
- package/src/core/structural-quality/policy.js +227 -0
- package/src/core/structural-quality/provider.js +287 -0
- package/src/core/structural-quality/sentrux-mcp.js +477 -0
- package/src/core/structural-quality/service.js +1138 -0
- package/src/core/structural-quality/source-fingerprint.js +112 -0
- package/src/core/structural-quality/status.js +3 -0
- package/src/core/task-paths.js +24 -0
- package/src/core/templates.js +1 -0
- package/src/integration.d.ts +25 -0
- package/src/integration.js +14 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "forgeloop://schemas/structural-quality.schema.json",
|
|
4
|
+
"title": "ForgeLoop structural quality evidence",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "protocolVersion", "role", "taskId", "capturedAt", "verificationCycle", "attempt", "bindings", "provider", "scope", "status", "reasonCodes"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": { "const": 1 },
|
|
9
|
+
"protocolVersion": { "const": 1 },
|
|
10
|
+
"role": { "enum": ["BASELINE", "EVALUATION"] },
|
|
11
|
+
"taskId": { "type": "string", "minLength": 1 },
|
|
12
|
+
"capturedAt": { "type": "string", "minLength": 1 },
|
|
13
|
+
"verificationCycle": { "oneOf": [{ "type": "integer", "minimum": 1 }, { "type": "null" }] },
|
|
14
|
+
"attempt": { "type": "integer", "minimum": 1 },
|
|
15
|
+
"status": { "enum": ["PASS", "FAIL", "BLOCKED", "NOT_OBSERVED"] },
|
|
16
|
+
"reasonCodes": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
17
|
+
"errorCode": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
18
|
+
"baselineSignal": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
19
|
+
"currentSignal": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
20
|
+
"bindings": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["contractFingerprint", "routeFingerprint", "policyFingerprint", "scopeFingerprint"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
25
|
+
"routeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
26
|
+
"policyFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
27
|
+
"scopeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
28
|
+
"baselineFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
29
|
+
"sourceMaterialFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
30
|
+
"stateRevision": { "type": "integer", "minimum": 0 }
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": false
|
|
33
|
+
},
|
|
34
|
+
"sourceObservation": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"required": ["beforeFingerprint", "afterFingerprint", "stable"],
|
|
37
|
+
"properties": {
|
|
38
|
+
"beforeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
39
|
+
"afterFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
40
|
+
"stable": { "type": "boolean" }
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
"provider": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"required": ["id", "version", "transport", "executionMode"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"id": { "type": "string", "minLength": 1 },
|
|
49
|
+
"version": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
50
|
+
"transport": { "type": "string", "minLength": 1 },
|
|
51
|
+
"executionMode": { "type": "string", "minLength": 1 },
|
|
52
|
+
"measurementModel": { "type": "string", "minLength": 1 },
|
|
53
|
+
"compatibilityKey": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }
|
|
54
|
+
},
|
|
55
|
+
"additionalProperties": false
|
|
56
|
+
},
|
|
57
|
+
"detection": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["available", "providerId", "transport", "reasonCode"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"available": { "type": "boolean" },
|
|
62
|
+
"providerId": { "type": "string", "minLength": 1 },
|
|
63
|
+
"providerVersion": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
64
|
+
"transport": { "type": "string", "minLength": 1 },
|
|
65
|
+
"measurementModel": { "type": "string", "minLength": 1 },
|
|
66
|
+
"compatibilityKey": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
67
|
+
"reasonCode": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }
|
|
68
|
+
},
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
},
|
|
71
|
+
"scope": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["kind", "projectRoot"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"kind": { "const": "PROJECT" },
|
|
76
|
+
"projectRoot": { "const": "." },
|
|
77
|
+
"providerConfigFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
78
|
+
"architectureRulesFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
79
|
+
},
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
},
|
|
82
|
+
"snapshot": { "$ref": "#/$defs/snapshot" },
|
|
83
|
+
"comparison": { "$ref": "#/$defs/comparison" }
|
|
84
|
+
},
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"$defs": {
|
|
87
|
+
"rootCause": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"required": ["score", "raw"],
|
|
90
|
+
"properties": {
|
|
91
|
+
"score": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
92
|
+
"raw": { "type": "number" }
|
|
93
|
+
},
|
|
94
|
+
"additionalProperties": false
|
|
95
|
+
},
|
|
96
|
+
"snapshot": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["qualitySignal", "bottleneck", "rootCauses", "statistics", "diagnostics"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"qualitySignal": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
101
|
+
"bottleneck": { "enum": ["modularity", "acyclicity", "depth", "equality", "redundancy"] },
|
|
102
|
+
"rootCauses": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"required": ["modularity", "acyclicity", "depth", "equality", "redundancy"],
|
|
105
|
+
"properties": {
|
|
106
|
+
"modularity": { "$ref": "#/$defs/rootCause" },
|
|
107
|
+
"acyclicity": { "$ref": "#/$defs/rootCause" },
|
|
108
|
+
"depth": { "$ref": "#/$defs/rootCause" },
|
|
109
|
+
"equality": { "$ref": "#/$defs/rootCause" },
|
|
110
|
+
"redundancy": { "$ref": "#/$defs/rootCause" }
|
|
111
|
+
},
|
|
112
|
+
"additionalProperties": false
|
|
113
|
+
},
|
|
114
|
+
"statistics": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["files", "lines", "importEdges", "crossModuleEdges"],
|
|
117
|
+
"properties": {
|
|
118
|
+
"files": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
119
|
+
"lines": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
120
|
+
"importEdges": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
121
|
+
"crossModuleEdges": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] }
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"diagnostics": { "oneOf": [{ "type": "object" }, { "type": "null" }] }
|
|
126
|
+
},
|
|
127
|
+
"additionalProperties": false
|
|
128
|
+
},
|
|
129
|
+
"comparison": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"required": ["comparable", "qualityDelta", "rootCauseDeltas", "status", "reasonCodes"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"comparable": { "type": "boolean" },
|
|
134
|
+
"qualityDelta": { "oneOf": [{ "type": "integer" }, { "type": "null" }] },
|
|
135
|
+
"rootCauseDeltas": { "type": "object" },
|
|
136
|
+
"failedConditions": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
137
|
+
"status": { "enum": ["PASS", "FAIL", "BLOCKED", "NOT_OBSERVED"] },
|
|
138
|
+
"reasonCodes": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
139
|
+
},
|
|
140
|
+
"additionalProperties": false
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"allOf": [
|
|
144
|
+
{
|
|
145
|
+
"if": { "properties": { "role": { "const": "BASELINE" } } },
|
|
146
|
+
"then": {
|
|
147
|
+
"required": ["sourceObservation"],
|
|
148
|
+
"properties": {
|
|
149
|
+
"bindings": {
|
|
150
|
+
"required": ["sourceMaterialFingerprint"],
|
|
151
|
+
"properties": {
|
|
152
|
+
"sourceMaterialFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"sourceObservation": { "properties": { "stable": { "const": true } } }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"if": { "properties": { "status": { "enum": ["PASS", "FAIL"] } } },
|
|
161
|
+
"then": {
|
|
162
|
+
"required": ["sourceObservation"],
|
|
163
|
+
"properties": {
|
|
164
|
+
"bindings": {
|
|
165
|
+
"required": ["sourceMaterialFingerprint"],
|
|
166
|
+
"properties": {
|
|
167
|
+
"sourceMaterialFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"sourceObservation": { "properties": { "stable": { "const": true } } }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -13,6 +13,9 @@ import { formatValidateProtocolResult } from "./commands/validate-protocol.js";
|
|
|
13
13
|
import { formatActivateResult } from "./commands/activate.js";
|
|
14
14
|
import { formatAdvanceResult } from "./commands/advance.js";
|
|
15
15
|
import { formatPreflightResult } from "./commands/preflight.js";
|
|
16
|
+
import { formatQualityBaselineResult } from "./commands/quality-baseline.js";
|
|
17
|
+
import { formatQualityVerifyResult } from "./commands/quality-verify.js";
|
|
18
|
+
import { formatQualityStatusResult } from "./commands/quality-status.js";
|
|
16
19
|
import { formatCompleteResult } from "./commands/complete.js";
|
|
17
20
|
import { formatAuditResult } from "./commands/audit.js";
|
|
18
21
|
import { formatReportResult } from "./commands/report.js";
|
|
@@ -379,6 +382,21 @@ export const COMMAND_HANDLERS = Object.freeze({
|
|
|
379
382
|
renderJsonOr(options, result, formatPreflightResult);
|
|
380
383
|
return exitCode;
|
|
381
384
|
},
|
|
385
|
+
"quality-baseline": async ({ target, packageRoot, options }) => {
|
|
386
|
+
const { result, exitCode } = await COMMAND_EXECUTORS["quality-baseline"]({ target, packageRoot, options });
|
|
387
|
+
renderJsonOr(options, result, formatQualityBaselineResult);
|
|
388
|
+
return exitCode;
|
|
389
|
+
},
|
|
390
|
+
"quality-verify": async ({ target, packageRoot, options }) => {
|
|
391
|
+
const { result, exitCode } = await COMMAND_EXECUTORS["quality-verify"]({ target, packageRoot, options });
|
|
392
|
+
renderJsonOr(options, result, formatQualityVerifyResult);
|
|
393
|
+
return exitCode;
|
|
394
|
+
},
|
|
395
|
+
"quality-status": async ({ target, packageRoot, options }) => {
|
|
396
|
+
const { result, exitCode } = await COMMAND_EXECUTORS["quality-status"]({ target, packageRoot, options });
|
|
397
|
+
renderJsonOr(options, result, formatQualityStatusResult);
|
|
398
|
+
return exitCode;
|
|
399
|
+
},
|
|
382
400
|
advance: async ({ target, packageRoot, options }) => {
|
|
383
401
|
const { result } = await COMMAND_EXECUTORS.advance({ target, packageRoot, options });
|
|
384
402
|
renderJsonOr(options, result, formatAdvanceResult);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
|
+
import { captureStructuralQualityBaseline } from "../core/structural-quality/service.js";
|
|
3
|
+
|
|
4
|
+
export async function runQualityBaseline({ target, packageRoot, taskId, replace = false, timeoutMs, runtimeContext } = {}) {
|
|
5
|
+
return withTaskMutation(target, { taskId, packageRoot }, "quality-baseline", async (ctx) => captureStructuralQualityBaseline({
|
|
6
|
+
target,
|
|
7
|
+
packageRoot,
|
|
8
|
+
taskId: ctx?.taskId ?? taskId,
|
|
9
|
+
replace,
|
|
10
|
+
timeoutMs,
|
|
11
|
+
runtimeContext,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function formatQualityBaselineResult(result) {
|
|
16
|
+
return [
|
|
17
|
+
"FORGELOOP STRUCTURAL QUALITY BASELINE",
|
|
18
|
+
`status: ${result.status}`,
|
|
19
|
+
`mode: ${result.mode}`,
|
|
20
|
+
`provider: ${result.provider?.id ?? "not-requested"}`,
|
|
21
|
+
`provider-version: ${result.provider?.version ?? "unknown"}`,
|
|
22
|
+
`quality: ${result.baseline?.snapshot?.qualitySignal ?? "none"}`,
|
|
23
|
+
`bottleneck: ${result.baseline?.snapshot?.bottleneck ?? "none"}`,
|
|
24
|
+
`artifact: ${result.artifactRef ?? "none"}`,
|
|
25
|
+
`next: ${result.mode === "gate" ? "VERIFY_STRUCTURAL_QUALITY" : "none"}`,
|
|
26
|
+
"",
|
|
27
|
+
].join("\n");
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
|
+
import { projectStructuralQualityStatus } from "../core/structural-quality/status.js";
|
|
3
|
+
|
|
4
|
+
export async function runQualityStatus({ target, packageRoot, taskId, runtimeContext } = {}) {
|
|
5
|
+
return withResolvedTask(target, { taskId, packageRoot }, async (ctx) => projectStructuralQualityStatus({
|
|
6
|
+
target,
|
|
7
|
+
packageRoot,
|
|
8
|
+
taskId: ctx?.taskId ?? taskId,
|
|
9
|
+
runtimeContext,
|
|
10
|
+
}), { explicitRequired: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function formatQualityStatusResult(result) {
|
|
14
|
+
const status = result.mode === "off"
|
|
15
|
+
? "OFF"
|
|
16
|
+
: result.current.status === "PASS"
|
|
17
|
+
? "PASS"
|
|
18
|
+
: result.current.status === "FAIL"
|
|
19
|
+
? "FAIL"
|
|
20
|
+
: result.current.status === "BLOCKED" || (result.mode === "gate" && result.baseline.status !== "OBSERVED")
|
|
21
|
+
? "BLOCKED"
|
|
22
|
+
: "NOT OBSERVED";
|
|
23
|
+
return [
|
|
24
|
+
`STRUCTURAL QUALITY: ${status}`,
|
|
25
|
+
`mode: ${result.mode}`,
|
|
26
|
+
`provider: ${result.provider ?? "none"}`,
|
|
27
|
+
`baseline: ${result.baseline.status}`,
|
|
28
|
+
`current: ${result.current.status}`,
|
|
29
|
+
`quality: ${result.current.qualitySignal ?? result.baseline.qualitySignal ?? "none"}`,
|
|
30
|
+
`delta: ${result.current.delta ?? "none"}`,
|
|
31
|
+
`next: ${result.next ?? "none"}`,
|
|
32
|
+
"",
|
|
33
|
+
].join("\n");
|
|
34
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { evaluateStructuralQuality } from "../core/structural-quality/service.js";
|
|
2
|
+
|
|
3
|
+
export async function runQualityVerify({ target, packageRoot, taskId, timeoutMs, authorityContext, runtimeContext } = {}) {
|
|
4
|
+
return evaluateStructuralQuality({
|
|
5
|
+
target,
|
|
6
|
+
packageRoot,
|
|
7
|
+
taskId,
|
|
8
|
+
timeoutMs,
|
|
9
|
+
authorityContext,
|
|
10
|
+
runtimeContext,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function formatQualityVerifyResult(result) {
|
|
15
|
+
return [
|
|
16
|
+
"FORGELOOP STRUCTURAL QUALITY VERIFICATION",
|
|
17
|
+
`status: ${result.evaluation?.status ?? result.status}`,
|
|
18
|
+
`cycle: ${result.evaluation?.verificationCycle ?? "none"}`,
|
|
19
|
+
`attempt: ${result.evaluation?.attempt ?? "none"}`,
|
|
20
|
+
`provider: ${result.evaluation?.provider?.id ?? "none"}`,
|
|
21
|
+
`provider-version: ${result.evaluation?.provider?.version ?? "unknown"}`,
|
|
22
|
+
`quality: ${result.evaluation?.currentSignal ?? result.evaluation?.snapshot?.qualitySignal ?? "none"}`,
|
|
23
|
+
`delta: ${result.evaluation?.comparison?.qualityDelta ?? "none"}`,
|
|
24
|
+
`bottleneck: ${result.evaluation?.snapshot?.bottleneck ?? "none"}`,
|
|
25
|
+
`artifact: ${result.evaluation?.artifactRef ?? "none"}`,
|
|
26
|
+
`check: ${result.check?.status ?? "not-recorded"}`,
|
|
27
|
+
`next: ${result.evaluation?.status === "FAIL" ? "DIAGNOSE_STRUCTURAL_QUALITY_REGRESSION" : result.evaluation?.status === "BLOCKED" ? "RESOLVE_STRUCTURAL_QUALITY_BLOCKER" : "none"}`,
|
|
28
|
+
"",
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
@@ -283,6 +283,18 @@ export const ARTIFACT_REGISTRY = Object.freeze({
|
|
|
283
283
|
isPersisted: true,
|
|
284
284
|
description: "Immutable trajectory evaluation results compiled from the canonical trace against a local reference scenario.",
|
|
285
285
|
}),
|
|
286
|
+
structuralQuality: Object.freeze({
|
|
287
|
+
key: "structuralQuality",
|
|
288
|
+
scope: "TASK",
|
|
289
|
+
path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.structuralQuality}/baseline.json`,
|
|
290
|
+
schema: "structural-quality",
|
|
291
|
+
owner: "PROTOCOL_COMPILED",
|
|
292
|
+
mutability: "BASELINE_IMMUTABLE_AFTER_EXECUTION",
|
|
293
|
+
trustRole: "STRUCTURAL_QUALITY_EVIDENCE",
|
|
294
|
+
isPublic: true,
|
|
295
|
+
isPersisted: true,
|
|
296
|
+
description: "Typed, provider-neutral structural-quality baseline and evaluation evidence bound to task, route, policy, scope, and provider identity.",
|
|
297
|
+
}),
|
|
286
298
|
usage: Object.freeze({
|
|
287
299
|
key: "usage",
|
|
288
300
|
scope: "TASK",
|
package/src/core/audit.js
CHANGED
|
@@ -11,6 +11,7 @@ import { validateActionLedgerConsistency } from "./actions.js";
|
|
|
11
11
|
import { readCodeManifest } from "./code-manifest.js";
|
|
12
12
|
import { readAttestationStatement, validateAttestationStatement } from "./attestation.js";
|
|
13
13
|
import { assertAttestationStatementBindings, verifyCodeManifestContent } from "./attestation-verifier.js";
|
|
14
|
+
import { projectStructuralQualityStatus } from "./structural-quality/status.js";
|
|
14
15
|
|
|
15
16
|
function sortErrors(errors) {
|
|
16
17
|
return [...errors].sort((left, right) => left.code.localeCompare(right.code)
|
|
@@ -244,6 +245,33 @@ export async function evaluateAudit({
|
|
|
244
245
|
policyStatus = { status: "NOT_APPLICABLE", provenRules: 0, inertRules: 0, unsupportedRules: 0, baselineViolations: 0, drift: false };
|
|
245
246
|
}
|
|
246
247
|
|
|
248
|
+
let structuralQuality = {
|
|
249
|
+
mode: "off",
|
|
250
|
+
provider: null,
|
|
251
|
+
baseline: { status: "NOT_REQUESTED", qualitySignal: null, artifactRef: null, fingerprint: null },
|
|
252
|
+
current: { status: "NOT_OBSERVED", verificationCycle: null, attempt: null, qualitySignal: null, delta: null, bottleneck: null, artifactRef: null },
|
|
253
|
+
comparable: null,
|
|
254
|
+
completionRequired: false,
|
|
255
|
+
reasonCodes: [],
|
|
256
|
+
next: null,
|
|
257
|
+
};
|
|
258
|
+
if (taskId) {
|
|
259
|
+
try {
|
|
260
|
+
structuralQuality = await projectStructuralQualityStatus({ target, packageRoot, taskId });
|
|
261
|
+
} catch (error) {
|
|
262
|
+
structuralQuality = {
|
|
263
|
+
...structuralQuality,
|
|
264
|
+
mode: "unknown",
|
|
265
|
+
reasonCodes: [error.code ?? "E_STRUCTURAL_QUALITY_EVIDENCE_STALE"],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const qualityEvidenceKind = structuralQuality.current.status === "PASS" || structuralQuality.current.status === "FAIL"
|
|
270
|
+
? "OBSERVED"
|
|
271
|
+
: structuralQuality.current.status === "BLOCKED" || (structuralQuality.mode === "gate" && structuralQuality.baseline.status !== "OBSERVED")
|
|
272
|
+
? "BLOCKED"
|
|
273
|
+
: structuralQuality.mode === "off" ? "NOT_REQUESTED" : "NOT_VERIFIED";
|
|
274
|
+
|
|
247
275
|
return {
|
|
248
276
|
schemaVersion: 1,
|
|
249
277
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -256,6 +284,15 @@ export async function evaluateAudit({
|
|
|
256
284
|
manifest: Boolean(manifest),
|
|
257
285
|
},
|
|
258
286
|
policy: policyStatus,
|
|
287
|
+
structuralQuality: {
|
|
288
|
+
...structuralQuality,
|
|
289
|
+
policy: structuralQuality.mode === "off" ? null : {
|
|
290
|
+
mode: structuralQuality.mode,
|
|
291
|
+
provider: structuralQuality.provider,
|
|
292
|
+
result: structuralQuality.current.status,
|
|
293
|
+
},
|
|
294
|
+
evidenceKind: qualityEvidenceKind,
|
|
295
|
+
},
|
|
259
296
|
completion,
|
|
260
297
|
attestation: {
|
|
261
298
|
mode: attestation.mode,
|
|
@@ -282,6 +319,7 @@ export async function evaluateAudit({
|
|
|
282
319
|
route: routeRel,
|
|
283
320
|
state: stateRel,
|
|
284
321
|
receipt: receiptRel,
|
|
322
|
+
structuralQuality: taskId ? taskArtifactPath(taskId, "structuralQuality") : null,
|
|
285
323
|
},
|
|
286
324
|
};
|
|
287
325
|
}
|
package/src/core/bundles.js
CHANGED
|
@@ -8,7 +8,7 @@ import { validateChecksExecutionProvenance } from "./completion-artifacts.js";
|
|
|
8
8
|
import { readExecutionArtifact } from "./execution.js";
|
|
9
9
|
import { assertContinuitySemantics } from "./continuity.js";
|
|
10
10
|
import { validateEventLedger } from "./events.js";
|
|
11
|
-
import { taskArtifactPath, taskDirectory } from "./task-paths.js";
|
|
11
|
+
import { taskArtifactPath, taskDirectory, taskStructuralQualityDirectory } from "./task-paths.js";
|
|
12
12
|
import { resolveTaskClaimState } from "./task-claim-state.js";
|
|
13
13
|
import { E_TASK_CLAIM_OWNERSHIP_INCONSISTENT } from "./error-codes.js";
|
|
14
14
|
import { listActions } from "./actions.js";
|
|
@@ -19,6 +19,13 @@ import { validateWorkspaceBinding } from "./workspace-binding.js";
|
|
|
19
19
|
import { validateCodeManifest, validateCodeManifestBindings } from "./code-manifest.js";
|
|
20
20
|
import { assertAttestationStatementBindings } from "./attestation-verifier.js";
|
|
21
21
|
import { validateAttestationStatement } from "./attestation.js";
|
|
22
|
+
import {
|
|
23
|
+
listStructuralQualityEvaluations,
|
|
24
|
+
readStructuralQualityBaseline,
|
|
25
|
+
validateStructuralQualityArtifact,
|
|
26
|
+
validateStructuralQualityBindings,
|
|
27
|
+
} from "./structural-quality/artifacts.js";
|
|
28
|
+
import { normalizeStructuralQualityConfig, structuralQualityPolicyFingerprint } from "./structural-quality/policy.js";
|
|
22
29
|
|
|
23
30
|
export const BUNDLE_SCHEMA_VERSION = 1;
|
|
24
31
|
const BUNDLE_ROOT = ".forgeloop/tasks";
|
|
@@ -42,6 +49,96 @@ function bundleBindingError(code, message) {
|
|
|
42
49
|
return error;
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
function structuralQualityBundleKind(artifact) {
|
|
53
|
+
if (artifact === "structural-quality/baseline.json") return "baseline";
|
|
54
|
+
if (/^structural-quality\/evaluations\/cycle-\d+-attempt-\d+\.json$/u.test(artifact)) return "evaluation";
|
|
55
|
+
if (artifact.startsWith("structural-quality/")) {
|
|
56
|
+
throw bundleBindingError("E_BUNDLE_PATH_INVALID", `Unknown structural-quality bundle artifact: ${artifact}`);
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function bundleQualityReference(reference, taskId) {
|
|
62
|
+
if (typeof reference !== "string" || reference.trim() === "") {
|
|
63
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", "Structural-quality check has no artifact reference");
|
|
64
|
+
}
|
|
65
|
+
const normalized = reference.replaceAll("\\", "/");
|
|
66
|
+
const sourceRoot = taskStructuralQualityDirectory(taskId).replaceAll("\\", "/");
|
|
67
|
+
const suffix = normalized.startsWith(`${sourceRoot}/`)
|
|
68
|
+
? normalized.slice(sourceRoot.length + 1)
|
|
69
|
+
: normalized.startsWith("structural-quality/")
|
|
70
|
+
? normalized.slice("structural-quality/".length)
|
|
71
|
+
: null;
|
|
72
|
+
if (!suffix || !(suffix === "baseline.json" || /^evaluations\/cycle-\d+-attempt-\d+\.json$/u.test(suffix))) {
|
|
73
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", "Structural-quality artifact reference escapes its task quality directory");
|
|
74
|
+
}
|
|
75
|
+
return `structural-quality/${suffix}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function structuralQualityCheckProjection(value) {
|
|
79
|
+
if (value.status === "PASS") return { status: "passed", evidenceKind: "OBSERVED" };
|
|
80
|
+
if (value.status === "FAIL") return { status: "failed", evidenceKind: "OBSERVED" };
|
|
81
|
+
if (value.status === "BLOCKED") return { status: "blocked", evidenceKind: "BLOCKED" };
|
|
82
|
+
return { status: "not-run", evidenceKind: "NOT_VERIFIED" };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function assertStructuralQualityBundleEvidence({ loaded, manifest, taskId }) {
|
|
86
|
+
const quality = loaded.structuralQuality;
|
|
87
|
+
if (!quality) return;
|
|
88
|
+
const baseline = quality.baseline;
|
|
89
|
+
const evaluations = quality.evaluations ?? [];
|
|
90
|
+
const qualityArtifacts = new Map();
|
|
91
|
+
if (baseline) qualityArtifacts.set("structural-quality/baseline.json", baseline);
|
|
92
|
+
for (const evaluation of evaluations) {
|
|
93
|
+
const name = `structural-quality/evaluations/cycle-${evaluation.verificationCycle}-attempt-${evaluation.attempt}.json`;
|
|
94
|
+
qualityArtifacts.set(name, evaluation);
|
|
95
|
+
}
|
|
96
|
+
for (const value of [baseline, ...evaluations].filter(Boolean)) {
|
|
97
|
+
if (value.taskId !== taskId) throw bundleBindingError("E_BUNDLE_TASK_MISMATCH", "Structural-quality artifact taskId does not match its bundle task");
|
|
98
|
+
const bindingErrors = validateStructuralQualityBindings(value);
|
|
99
|
+
if (bindingErrors.length > 0) throw bundleBindingError(bindingErrors[0].code, bindingErrors[0].message);
|
|
100
|
+
}
|
|
101
|
+
const baselineFingerprint = baseline ? canonicalFingerprint(baseline) : null;
|
|
102
|
+
const bundledConfig = loaded.config?.structuralQuality;
|
|
103
|
+
let policy = null;
|
|
104
|
+
if (bundledConfig) policy = normalizeStructuralQualityConfig(bundledConfig);
|
|
105
|
+
const expectedContractFingerprint = loaded.contract ? canonicalFingerprint(loaded.contract) : null;
|
|
106
|
+
const expectedRouteFingerprint = loaded.route ? canonicalFingerprint(loaded.route) : null;
|
|
107
|
+
for (const value of [baseline, ...evaluations].filter(Boolean)) {
|
|
108
|
+
if (baselineFingerprint && value.role === "EVALUATION" && value.bindings?.baselineFingerprint !== baselineFingerprint) {
|
|
109
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH", "Structural-quality evaluation does not bind the bundled baseline");
|
|
110
|
+
}
|
|
111
|
+
if (policy && value.bindings?.policyFingerprint !== structuralQualityPolicyFingerprint(policy)) {
|
|
112
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH", "Structural-quality artifact policy binding does not match bundled configuration");
|
|
113
|
+
}
|
|
114
|
+
if (expectedContractFingerprint && value.bindings?.contractFingerprint !== expectedContractFingerprint) {
|
|
115
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH", "Structural-quality contract binding does not match the bundled contract");
|
|
116
|
+
}
|
|
117
|
+
if (expectedRouteFingerprint && value.bindings?.routeFingerprint !== expectedRouteFingerprint) {
|
|
118
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH", "Structural-quality route binding does not match the bundled route");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const checks = [
|
|
122
|
+
...(loaded.state?.checks ?? []),
|
|
123
|
+
...(loaded.receipt?.checks ?? []),
|
|
124
|
+
].filter((check) => check?.kind === "structural-quality");
|
|
125
|
+
for (const check of checks) {
|
|
126
|
+
const bundleReference = bundleQualityReference(check.details?.artifactRef, taskId);
|
|
127
|
+
const value = qualityArtifacts.get(bundleReference);
|
|
128
|
+
if (!value) throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", `Bundled structural-quality check references missing ${bundleReference}`);
|
|
129
|
+
if (check.details?.artifactFingerprint !== canonicalFingerprint(value)) {
|
|
130
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", `Bundled structural-quality check fingerprint does not match ${bundleReference}`);
|
|
131
|
+
}
|
|
132
|
+
const expected = structuralQualityCheckProjection(value);
|
|
133
|
+
if (check.status !== expected.status || check.evidenceKind !== expected.evidenceKind) {
|
|
134
|
+
throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", `Bundled structural-quality check does not match ${bundleReference}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const artifact of manifest.artifacts.filter((item) => item.startsWith("structural-quality/"))) {
|
|
138
|
+
if (!qualityArtifacts.has(artifact)) throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", `Bundled structural-quality manifest entry was not loaded: ${artifact}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
45
142
|
function assertBundledCodeManifestBindings({ loaded, ledger, taskId }) {
|
|
46
143
|
const manifest = loaded.codeManifest;
|
|
47
144
|
if (!manifest) return;
|
|
@@ -191,6 +288,22 @@ export async function exportTaskBundle(target, taskId, packageRoot) {
|
|
|
191
288
|
if (destinationName === "verification-scope.json") exportedVerificationScope = copied;
|
|
192
289
|
}
|
|
193
290
|
|
|
291
|
+
// Structural-quality evidence is provider output made portable by
|
|
292
|
+
// ForgeLoop. Copy typed artifacts rather than raw process output, and keep
|
|
293
|
+
// every immutable evaluation so an audit can inspect the complete attempt
|
|
294
|
+
// history without rescanning the project.
|
|
295
|
+
const qualityBaseline = await readStructuralQualityBaseline(target, taskId, packageRoot);
|
|
296
|
+
const qualityEvaluations = await listStructuralQualityEvaluations(target, taskId, packageRoot);
|
|
297
|
+
if (qualityBaseline) {
|
|
298
|
+
await writeJsonArtifact(target, `${directory}/structural-quality/baseline.json`, qualityBaseline.value, "structural-quality", packageRoot);
|
|
299
|
+
artifacts.push("structural-quality/baseline.json");
|
|
300
|
+
}
|
|
301
|
+
for (const evaluation of qualityEvaluations) {
|
|
302
|
+
const destination = `structural-quality/evaluations/cycle-${evaluation.value.verificationCycle}-attempt-${evaluation.value.attempt}.json`;
|
|
303
|
+
await writeJsonArtifact(target, `${directory}/${destination}`, evaluation.value, "structural-quality", packageRoot);
|
|
304
|
+
artifacts.push(destination);
|
|
305
|
+
}
|
|
306
|
+
|
|
194
307
|
if (exportedWorkspaceBinding?.value) {
|
|
195
308
|
exportedWorkspaceBinding.value = await validateWorkspaceBinding(exportedWorkspaceBinding.value, packageRoot);
|
|
196
309
|
if (exportedWorkspaceBinding.value.taskId !== taskId) {
|
|
@@ -335,6 +448,22 @@ export async function readTaskBundle(target, taskId, packageRoot) {
|
|
|
335
448
|
};
|
|
336
449
|
const executions = {};
|
|
337
450
|
for (const artifact of manifest.value.artifacts) {
|
|
451
|
+
const qualityKind = structuralQualityBundleKind(artifact);
|
|
452
|
+
if (qualityKind) {
|
|
453
|
+
const qualityArtifact = await readJsonArtifact(target, `${directory}/${artifact}`, "structural-quality", packageRoot);
|
|
454
|
+
validateStructuralQualityArtifact(qualityArtifact.value, artifact);
|
|
455
|
+
if (qualityArtifact.value.taskId !== taskId) {
|
|
456
|
+
throw bundleBindingError("E_BUNDLE_TASK_MISMATCH", `Structural-quality ${qualityKind} taskId does not match its bundle task`);
|
|
457
|
+
}
|
|
458
|
+
loaded.structuralQuality ??= { baseline: null, evaluations: [] };
|
|
459
|
+
if (qualityKind === "baseline") {
|
|
460
|
+
if (loaded.structuralQuality.baseline) throw bundleBindingError("E_STRUCTURAL_QUALITY_EVIDENCE_STALE", "A bundle cannot contain more than one structural-quality baseline");
|
|
461
|
+
loaded.structuralQuality.baseline = qualityArtifact.value;
|
|
462
|
+
} else {
|
|
463
|
+
loaded.structuralQuality.evaluations.push(qualityArtifact.value);
|
|
464
|
+
}
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
338
467
|
if (artifact.startsWith("executions/") && artifact.endsWith(".json")) {
|
|
339
468
|
const execution = await readJsonArtifact(target, `${directory}/${artifact}`, "execution", packageRoot);
|
|
340
469
|
executions[execution.value.executionId] = execution.value;
|
|
@@ -415,6 +544,10 @@ export async function readTaskBundle(target, taskId, packageRoot) {
|
|
|
415
544
|
}
|
|
416
545
|
loaded[mapping[0]] = loadedArtifact.value;
|
|
417
546
|
}
|
|
547
|
+
if (loaded.structuralQuality) {
|
|
548
|
+
loaded.structuralQuality.evaluations.sort((left, right) => left.verificationCycle - right.verificationCycle || left.attempt - right.attempt);
|
|
549
|
+
assertStructuralQualityBundleEvidence({ loaded, manifest: manifest.value, taskId });
|
|
550
|
+
}
|
|
418
551
|
const bundledLedger = loaded.codeManifest && manifest.value.artifacts.includes("events.ndjson")
|
|
419
552
|
? await validateEventLedger(target, packageRoot, { taskId, eventsPath: `${directory}/events.ndjson` })
|
|
420
553
|
: null;
|
|
@@ -162,6 +162,51 @@ export const CLI_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
162
162
|
mayExecuteExternalProcess: false,
|
|
163
163
|
description: "Evaluates pre-implementation contract, routing, and gates; synchronizes work state when READY.",
|
|
164
164
|
}),
|
|
165
|
+
"quality-baseline": Object.freeze({
|
|
166
|
+
name: "quality-baseline",
|
|
167
|
+
category: "verification",
|
|
168
|
+
mutation: "EXTERNAL_EXECUTION",
|
|
169
|
+
options: Object.freeze({
|
|
170
|
+
...CLI_COMMON_OPTIONS,
|
|
171
|
+
...CLI_TASK_OPTION,
|
|
172
|
+
"--replace": Object.freeze({ targetKey: "replace", parseType: "boolean", takesValue: false, description: "replace a different baseline before EXECUTING" }),
|
|
173
|
+
"--timeout-ms": Object.freeze({ targetKey: "timeoutMs", parseType: "non-negative-integer", takesValue: true, valueName: "number", missingValueMessage: "--timeout-ms requires a non-negative integer", description: "bounded analyzer timeout in milliseconds" }),
|
|
174
|
+
"--json": Object.freeze({ targetKey: "json", parseType: "boolean", takesValue: false, description: "emit baseline result as JSON" }),
|
|
175
|
+
}),
|
|
176
|
+
writes: [".forgeloop/task-state/<taskKey>/structural-quality/baseline.json", ".forgeloop/task-state/<taskKey>/events.ndjson"],
|
|
177
|
+
removes: [],
|
|
178
|
+
mayExecuteExternalProcess: true,
|
|
179
|
+
description: "Captures an immutable provider-neutral structural-quality baseline through the trusted analyzer adapter.",
|
|
180
|
+
}),
|
|
181
|
+
"quality-verify": Object.freeze({
|
|
182
|
+
name: "quality-verify",
|
|
183
|
+
category: "verification",
|
|
184
|
+
mutation: "EXTERNAL_EXECUTION",
|
|
185
|
+
options: Object.freeze({
|
|
186
|
+
...CLI_COMMON_OPTIONS,
|
|
187
|
+
...CLI_TASK_OPTION,
|
|
188
|
+
"--timeout-ms": Object.freeze({ targetKey: "timeoutMs", parseType: "non-negative-integer", takesValue: true, valueName: "number", missingValueMessage: "--timeout-ms requires a non-negative integer", description: "bounded analyzer timeout in milliseconds" }),
|
|
189
|
+
"--json": Object.freeze({ targetKey: "json", parseType: "boolean", takesValue: false, description: "emit structural-quality verification as JSON" }),
|
|
190
|
+
}),
|
|
191
|
+
writes: [".forgeloop/task-state/<taskKey>/structural-quality/evaluations/cycle-<n>-attempt-<n>.json", ".forgeloop/task-state/<taskKey>/work-state.json", ".forgeloop/task-state/<taskKey>/execution-receipt.json", ".forgeloop/task-state/<taskKey>/events.ndjson"],
|
|
192
|
+
removes: [],
|
|
193
|
+
mayExecuteExternalProcess: true,
|
|
194
|
+
description: "Captures one bounded structural-quality evaluation and projects it into canonical verification evidence.",
|
|
195
|
+
}),
|
|
196
|
+
"quality-status": Object.freeze({
|
|
197
|
+
name: "quality-status",
|
|
198
|
+
category: "verification",
|
|
199
|
+
mutation: "READ_ONLY",
|
|
200
|
+
options: Object.freeze({
|
|
201
|
+
...CLI_COMMON_OPTIONS,
|
|
202
|
+
...CLI_TASK_OPTION,
|
|
203
|
+
"--json": Object.freeze({ targetKey: "json", parseType: "boolean", takesValue: false, description: "emit persisted structural-quality status as JSON" }),
|
|
204
|
+
}),
|
|
205
|
+
writes: [],
|
|
206
|
+
removes: [],
|
|
207
|
+
mayExecuteExternalProcess: false,
|
|
208
|
+
description: "Projects persisted structural-quality baseline and evaluation status without invoking a provider.",
|
|
209
|
+
}),
|
|
165
210
|
advance: Object.freeze({
|
|
166
211
|
name: "advance",
|
|
167
212
|
category: "lifecycle",
|
|
@@ -11,6 +11,9 @@ import { runUpdate } from "../commands/update.js";
|
|
|
11
11
|
import { runActivate } from "../commands/activate.js";
|
|
12
12
|
import { runAdvance } from "../commands/advance.js";
|
|
13
13
|
import { runPreflight } from "../commands/preflight.js";
|
|
14
|
+
import { runQualityBaseline } from "../commands/quality-baseline.js";
|
|
15
|
+
import { runQualityVerify } from "../commands/quality-verify.js";
|
|
16
|
+
import { runQualityStatus } from "../commands/quality-status.js";
|
|
14
17
|
import { runComplete } from "../commands/complete.js";
|
|
15
18
|
import { runAudit } from "../commands/audit.js";
|
|
16
19
|
import { runReport } from "../commands/report.js";
|
|
@@ -132,6 +135,19 @@ export const COMMAND_EXECUTORS = {
|
|
|
132
135
|
const result = await runPreflight({ target, packageRoot, strict: options.strict, taskId: options.taskId });
|
|
133
136
|
return { result, exitCode: result.status === "READY" ? 0 : 1 };
|
|
134
137
|
},
|
|
138
|
+
"quality-baseline": async ({ target, packageRoot, options, runtimeContext }) => {
|
|
139
|
+
const result = await runQualityBaseline({ target, packageRoot, taskId: options.taskId, replace: options.replace, timeoutMs: options.timeoutMs, runtimeContext });
|
|
140
|
+
return { result, exitCode: ["CAPTURED", "EXISTING", "REPLACED", "NOT_REQUESTED"].includes(result.status) ? 0 : 1 };
|
|
141
|
+
},
|
|
142
|
+
"quality-verify": async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
143
|
+
const result = await runQualityVerify({ target, packageRoot, taskId: options.taskId, timeoutMs: options.timeoutMs, authorityContext, runtimeContext });
|
|
144
|
+
const status = result.evaluation?.status ?? result.status;
|
|
145
|
+
return { result, exitCode: ["PASS", "NOT_OBSERVED", "CONVERGED", "NOT_REQUESTED"].includes(status) ? 0 : 1 };
|
|
146
|
+
},
|
|
147
|
+
"quality-status": async ({ target, packageRoot, options }) => ({
|
|
148
|
+
result: await runQualityStatus({ target, packageRoot, taskId: options.taskId }),
|
|
149
|
+
exitCode: 0,
|
|
150
|
+
}),
|
|
135
151
|
advance: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => ({
|
|
136
152
|
result: await runAdvance({
|
|
137
153
|
target,
|