@cassiomc1/forgeloop 0.1.14 → 0.1.16
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/.forgeloop/forgeloop.gitignore +1 -0
- package/DOCS_INDEX.md +60 -0
- package/EXECUTION_STATE.md +9 -0
- package/LOOP_ENGINEERING.md +44 -6
- package/LOOP_SYSTEM_DESIGN.md +8 -0
- package/PROTOCOL_INTEGRATION.md +41 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +183 -643
- package/TERMINOLOGY.md +4 -0
- package/THREAT_MODEL.md +14 -0
- package/docs/assets/forgeloop-flow.svg +1 -0
- package/docs/forgeloop-flow.mmd +51 -0
- package/package.json +16 -3
- package/schemas/check.schema.json +2 -0
- package/schemas/continuity.schema.json +57 -0
- package/schemas/execution.schema.json +63 -0
- package/scripts/CI_VALIDATORS.md +32 -0
- package/src/cli.js +351 -196
- package/src/commands/clear-continuity.js +9 -0
- package/src/commands/continuity.js +25 -0
- package/src/commands/doctor.js +17 -2
- package/src/commands/reconcile-continuity.js +23 -0
- package/src/commands/record-continuity.js +63 -0
- package/src/commands/run-check.js +83 -0
- package/src/commands/status.js +14 -1
- package/src/commands/update.js +12 -13
- package/src/commands/validate-protocol.js +37 -0
- package/src/core/artifacts.js +9 -0
- package/src/core/bundles.js +78 -0
- package/src/core/checks.js +16 -0
- package/src/core/command-resolution.js +295 -0
- package/src/core/command-tokenizer.js +122 -0
- package/src/core/completion-artifacts.js +229 -47
- package/src/core/completion.js +19 -1
- package/src/core/conformance.js +8 -2
- package/src/core/continuity-cli-options.js +58 -0
- package/src/core/continuity-conformance.js +46 -0
- package/src/core/continuity-observability.js +20 -0
- package/src/core/continuity-reconciliation.js +224 -0
- package/src/core/continuity.js +245 -0
- package/src/core/evidence-readiness.js +26 -1
- package/src/core/execution.js +185 -0
- package/src/core/inspect.js +9 -1
- package/src/core/installation-authority.js +178 -0
- package/src/core/json-safety.js +17 -13
- package/src/core/next-action-artifacts.js +118 -0
- package/src/core/next-action-continuity.js +65 -0
- package/src/core/next-action-model.js +127 -0
- package/src/core/next-action-phases.js +12 -0
- package/src/core/next-action.js +22 -249
- package/src/core/npm-classifier.js +343 -0
- package/src/core/package-manager-classifiers.js +37 -0
- package/src/core/preflight-consistency.js +221 -0
- package/src/core/preflight-loaders.js +112 -0
- package/src/core/preflight-model.js +83 -0
- package/src/core/preflight.js +34 -444
- package/src/core/protocol.js +7 -0
- package/src/core/schema-validation.js +16 -1
- package/src/core/templates.js +3 -0
- package/src/core/verification-capability.js +31 -495
- package/src/core/verification-constants.js +61 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ARTIFACT_PATHS, readJsonArtifact } from "./artifacts.js";
|
|
2
|
+
import { readContract } from "./contract.js";
|
|
3
|
+
import { readGateIfPresent, validateGateArtifacts } from "./gate-artifact.js";
|
|
4
|
+
import { requiredGatesForGuides } from "./guide-metadata.js";
|
|
5
|
+
import { assertSourceProvenance } from "./sources.js";
|
|
6
|
+
import { readPersistedRoute } from "./route-artifact.js";
|
|
7
|
+
import { ensureWithin, readBytes } from "./filesystem.js";
|
|
8
|
+
import { sha256 } from "./manifest.js";
|
|
9
|
+
import { findProfilePath, validateProfileSources } from "./profile.js";
|
|
10
|
+
import { issue } from "./preflight-model.js";
|
|
11
|
+
|
|
12
|
+
export async function readProfile(target) {
|
|
13
|
+
const relativePath = await findProfilePath(target);
|
|
14
|
+
if (!relativePath) return { status: "missing", fingerprint: null };
|
|
15
|
+
const filePath = ensureWithin(target, relativePath);
|
|
16
|
+
const bytes = await readBytes(filePath);
|
|
17
|
+
const text = bytes.toString("utf8");
|
|
18
|
+
const mode = text.match(/^profile-mode:\s*([^\s]+)\s*$/m)?.[1] ?? null;
|
|
19
|
+
const status = text.match(/^profile-status:\s*([^\s]+)\s*$/m)?.[1] ?? null;
|
|
20
|
+
return {
|
|
21
|
+
status: status === "verified" && mode !== "template" ? "verified" : "unverified",
|
|
22
|
+
mode,
|
|
23
|
+
profileStatus: status,
|
|
24
|
+
fingerprint: sha256(bytes),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function optionalConfig(target, packageRoot, errors) {
|
|
29
|
+
try {
|
|
30
|
+
const artifact = await readJsonArtifact(target, ARTIFACT_PATHS.config, "config", packageRoot);
|
|
31
|
+
return artifact.value;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error.code === "ARTIFACT_MISSING") return { schemaVersion: 1, protocolVersion: 1, complianceMode: "standard" };
|
|
34
|
+
errors.push(issue("E_CONFIG_INVALID", error.message, [ARTIFACT_PATHS.config]));
|
|
35
|
+
return { schemaVersion: 1, protocolVersion: 1, complianceMode: "standard" };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function loadContract(target, packageRoot, errors) {
|
|
40
|
+
try {
|
|
41
|
+
return await readContract(target, packageRoot);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
errors.push(issue(error.code === "ARTIFACT_MISSING" ? "E_CONTRACT_MISSING" : "E_CONTRACT_INVALID", error.message, [ARTIFACT_PATHS.contract]));
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function loadRoute(target, packageRoot, errors) {
|
|
49
|
+
try {
|
|
50
|
+
return await readPersistedRoute(target, packageRoot);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const code = error.code === "ARTIFACT_MISSING"
|
|
53
|
+
? "E_ROUTE_MISSING"
|
|
54
|
+
: ["E_ROUTE_REASON_MISSING", "E_ROUTE_INVALID"].includes(error.code) ? error.code : "E_ROUTE_INVALID";
|
|
55
|
+
errors.push(issue(code, error.message, [ARTIFACT_PATHS.route]));
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function loadSources(target, contract, packageRoot, errors) {
|
|
61
|
+
if (!contract?.value?.sourceRefs?.length) return null;
|
|
62
|
+
let registry;
|
|
63
|
+
try {
|
|
64
|
+
registry = (await readJsonArtifact(target, ARTIFACT_PATHS.sources, "source-registry", packageRoot)).value;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
errors.push(issue(error.code === "ARTIFACT_MISSING" ? "E_PROFILE_SOURCE_MISSING" : "E_PROFILE_SOURCE_UNKNOWN", error.message, [ARTIFACT_PATHS.sources]));
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
assertSourceProvenance(registry, contract.value.sourceRefs);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
errors.push(issue(error.code ?? "E_PROFILE_SOURCE_UNKNOWN", error.message, [ARTIFACT_PATHS.sources]));
|
|
73
|
+
}
|
|
74
|
+
return registry;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function inspectGates(target, contract, route, packageRoot, errors, config = {}) {
|
|
78
|
+
if (!route) return { required: [], satisfied: [], records: {} };
|
|
79
|
+
const guideGates = await requiredGatesForGuides(route.value.guides, packageRoot);
|
|
80
|
+
const required = [...new Set([...guideGates, ...(config.requiredGates ?? [])])].sort();
|
|
81
|
+
const satisfied = [];
|
|
82
|
+
const records = {};
|
|
83
|
+
for (const gate of required) {
|
|
84
|
+
let artifact;
|
|
85
|
+
try {
|
|
86
|
+
artifact = await readGateIfPresent(target, gate, packageRoot);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
errors.push(issue(error.code === "ARTIFACT_MISSING" ? "E_GATE_UNVERIFIED" : "E_GATE_INVALID", error.message, [`${ARTIFACT_PATHS.gates}/${gate}.json`], { gate }));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!artifact) {
|
|
92
|
+
errors.push(issue("E_GATE_UNVERIFIED", `Required gate is missing or unverified: ${gate}`, [`${ARTIFACT_PATHS.gates}/${gate}.json`], { gate }));
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
records[gate] = artifact;
|
|
96
|
+
if (artifact.value.taskId !== contract?.value?.taskId) {
|
|
97
|
+
errors.push(issue("E_GATE_TASK_MISMATCH", `Gate ${gate} belongs to a different task`, [artifact.path], { gate }));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (artifact.value.status !== "satisfied") {
|
|
101
|
+
errors.push(issue("E_GATE_UNVERIFIED", `Required gate is ${artifact.value.status}: ${gate}`, [artifact.path], { gate }));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const stale = await validateGateArtifacts(target, artifact.value, packageRoot);
|
|
105
|
+
if (stale.length > 0) {
|
|
106
|
+
errors.push(issue("E_GATE_STALE", `Gate ${gate} references stale artifacts`, [artifact.path], { gate, stale }));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
satisfied.push(gate);
|
|
110
|
+
}
|
|
111
|
+
return { required, satisfied: satisfied.sort(), records };
|
|
112
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ARTIFACT_PATHS } from "./artifacts.js";
|
|
2
|
+
|
|
3
|
+
export function issue(code, message, artifacts = [], details = {}) {
|
|
4
|
+
return { code, message, artifacts, ...details };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function sortIssues(errors) {
|
|
8
|
+
const unique = [...new Map(errors.map((error) => [
|
|
9
|
+
`${error.code}\0${error.artifacts.join("\0")}\0${error.message}`,
|
|
10
|
+
error,
|
|
11
|
+
])).values()];
|
|
12
|
+
return unique.sort((left, right) => left.code.localeCompare(right.code)
|
|
13
|
+
|| left.artifacts.join("\0").localeCompare(right.artifacts.join("\0"))
|
|
14
|
+
|| left.message.localeCompare(right.message));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function preflightError(code, message, artifacts = []) {
|
|
18
|
+
const error = new Error(message);
|
|
19
|
+
error.code = code;
|
|
20
|
+
error.artifacts = artifacts;
|
|
21
|
+
return error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function sameStringSet(left, right) {
|
|
25
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
26
|
+
const leftSet = new Set(left);
|
|
27
|
+
const rightSet = new Set(right);
|
|
28
|
+
return leftSet.size === left.length
|
|
29
|
+
&& rightSet.size === right.length
|
|
30
|
+
&& leftSet.size === rightSet.size
|
|
31
|
+
&& [...leftSet].every((value) => rightSet.has(value));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function validatePersistedPreflight(persisted, current) {
|
|
35
|
+
const errors = [];
|
|
36
|
+
if (persisted?.status !== "READY") {
|
|
37
|
+
errors.push(issue("E_PREFLIGHT_NOT_READY", "A persisted READY preflight is required", [ARTIFACT_PATHS.preflight]));
|
|
38
|
+
return errors;
|
|
39
|
+
}
|
|
40
|
+
if (current?.status !== "READY") {
|
|
41
|
+
errors.push(issue("E_PREFLIGHT_NOT_READY", "The current preflight evaluation is not READY", [ARTIFACT_PATHS.preflight]));
|
|
42
|
+
}
|
|
43
|
+
if (persisted.taskId !== current?.taskId) {
|
|
44
|
+
errors.push(issue(
|
|
45
|
+
"E_PREFLIGHT_TASK_MISMATCH",
|
|
46
|
+
"Persisted preflight does not belong to the current task",
|
|
47
|
+
[ARTIFACT_PATHS.preflight, ARTIFACT_PATHS.contract],
|
|
48
|
+
));
|
|
49
|
+
}
|
|
50
|
+
if (persisted.fingerprints?.contract !== current?.fingerprints?.contract
|
|
51
|
+
|| persisted.contract?.fingerprint !== current?.contract?.fingerprint) {
|
|
52
|
+
errors.push(issue(
|
|
53
|
+
"E_PREFLIGHT_CONTRACT_STALE",
|
|
54
|
+
"Persisted preflight does not match the current contract fingerprint",
|
|
55
|
+
[ARTIFACT_PATHS.preflight, ARTIFACT_PATHS.contract],
|
|
56
|
+
));
|
|
57
|
+
}
|
|
58
|
+
if (persisted.fingerprints?.routing !== current?.fingerprints?.routing
|
|
59
|
+
|| persisted.routing?.fingerprint !== current?.routing?.fingerprint) {
|
|
60
|
+
errors.push(issue(
|
|
61
|
+
"E_PREFLIGHT_ROUTE_STALE",
|
|
62
|
+
"Persisted preflight does not match the current routing fingerprint",
|
|
63
|
+
[ARTIFACT_PATHS.preflight, ARTIFACT_PATHS.route],
|
|
64
|
+
));
|
|
65
|
+
}
|
|
66
|
+
if (!sameStringSet(persisted.requiredGates, current?.requiredGates)
|
|
67
|
+
|| !sameStringSet(persisted.satisfiedGates, current?.satisfiedGates)) {
|
|
68
|
+
errors.push(issue(
|
|
69
|
+
"E_PREFLIGHT_GATES_STALE",
|
|
70
|
+
"Persisted preflight gate sets do not match the current evaluation",
|
|
71
|
+
[ARTIFACT_PATHS.preflight, ARTIFACT_PATHS.gates],
|
|
72
|
+
));
|
|
73
|
+
}
|
|
74
|
+
return sortIssues(errors);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const PREFLIGHT_IDENTITY_BARRIER_CODES = new Set([
|
|
78
|
+
"E_CONTRACT_STALE",
|
|
79
|
+
"E_GATE_TASK_MISMATCH",
|
|
80
|
+
"E_ROUTE_STALE",
|
|
81
|
+
"E_STATE_TASK_MISMATCH",
|
|
82
|
+
"E_ROUTE_GUIDE_MISMATCH",
|
|
83
|
+
]);
|