@echopath-labs/forgerail 0.1.0-alpha.2 → 0.1.0-alpha.4
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/.codex-plugin/plugin.json +2 -3
- package/CHANGELOG.md +22 -1
- package/CODE_OF_CONDUCT.md +34 -0
- package/CONTRIBUTING.md +68 -4
- package/README.md +126 -49
- package/README.zh-CN.md +131 -28
- package/SECURITY.md +48 -4
- package/SUPPORT.md +37 -0
- package/adapters/claude-code.json +6 -1
- package/adapters/codex.json +6 -0
- package/adapters/cursor.json +5 -0
- package/contracts/adoption-plan.schema.json +39 -18
- package/contracts/effective-profile.schema.json +4 -4
- package/contracts/host-adapter.schema.json +66 -4
- package/contracts/host-binding-receipt.schema.json +1 -1
- package/contracts/launch-contract.schema.json +38 -2
- package/contracts/profile-change-candidate.schema.json +1 -1
- package/contracts/return-receipt.schema.json +1 -1
- package/contracts/task-envelope.schema.json +1 -1
- package/directory/README.md +1 -1
- package/directory/release-notes-alpha3.md +7 -0
- package/directory/release-notes-alpha4.md +9 -0
- package/directory/submission-candidate.json +5 -6
- package/docs/adoption.md +63 -26
- package/docs/adoption.zh-CN.md +62 -25
- package/docs/architecture-acceptance.md +1 -1
- package/docs/composable-autonomy.zh-CN.md +16 -22
- package/docs/installation.md +72 -40
- package/docs/installation.zh-CN.md +90 -31
- package/docs/release-alpha3.md +25 -0
- package/docs/release-alpha3.zh-CN.md +25 -0
- package/docs/release-alpha4.md +33 -0
- package/docs/release-alpha4.zh-CN.md +33 -0
- package/package.json +7 -3
- package/scripts/adoption-closeout-regressions.mjs +100 -0
- package/scripts/build-universal-directory-candidate.mjs +2 -2
- package/scripts/disposable-consumer.mjs +11 -18
- package/scripts/fixtures/contracts/adoption-plan.multi-host.valid.json +16 -7
- package/scripts/fixtures/contracts/adoption-plan.mutating.invalid.json +6 -3
- package/scripts/fixtures/contracts/adoption-plan.single-host.valid.json +9 -4
- package/scripts/fixtures/contracts/effective-profile.duplicate-rule.invalid.json +1 -1
- package/scripts/fixtures/contracts/effective-profile.valid.json +3 -4
- package/scripts/fixtures/contracts/host-adapter.claude-code.profile-only.valid.json +6 -1
- package/scripts/fixtures/contracts/host-adapter.codex.valid.json +6 -0
- package/scripts/fixtures/contracts/host-adapter.cursor.profile-only.valid.json +5 -0
- package/scripts/fixtures/contracts/host-adapter.false-supported.invalid.json +6 -1
- package/scripts/fixtures/contracts/launch-contract.execution-owner.invalid.json +5 -1
- package/scripts/fixtures/contracts/launch-contract.valid.json +5 -1
- package/scripts/fixtures/open-source-docs/cases.json +65 -0
- package/scripts/forgerail.mjs +61 -16
- package/scripts/integrity-regressions.mjs +1261 -0
- package/scripts/lib/adoption.mjs +666 -51
- package/scripts/lib/bounded-read.mjs +80 -0
- package/scripts/lib/composition.mjs +77 -7
- package/scripts/lib/contracts.mjs +126 -40
- package/scripts/lib/diagnosis.mjs +146 -39
- package/scripts/shadow-comparison.mjs +52 -34
- package/scripts/validate-open-source-docs.mjs +132 -0
- package/scripts/validate-release.mjs +77 -13
- package/scripts/validate-universal-directory.mjs +17 -5
- package/skills/forgerail/references/adoption.md +2 -2
- package/skills/forgerail/references/contracts.md +2 -2
- package/scripts/lib/bundle.mjs +0 -77
package/scripts/forgerail.mjs
CHANGED
|
@@ -5,7 +5,6 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
5
5
|
import { dirname, relative, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { loadHostAdapters, planAdoption } from "./lib/adoption.mjs";
|
|
8
|
-
import { buildBundle } from "./lib/bundle.mjs";
|
|
9
8
|
import { createLaunchContract, resolveProfile, verifyReceipt } from "./lib/composition.mjs";
|
|
10
9
|
import { contractSchemaNames, contractTypes, readJson, validateContract } from "./lib/contracts.mjs";
|
|
11
10
|
import { diagnoseWorkspace } from "./lib/diagnosis.mjs";
|
|
@@ -14,12 +13,50 @@ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
14
13
|
|
|
15
14
|
function fail(message) { console.error(`forgerail: ${message}`); process.exit(1); }
|
|
16
15
|
function emit(value) { console.log(JSON.stringify(value, null, 2)); }
|
|
17
|
-
function
|
|
18
|
-
function args(name) {
|
|
16
|
+
function optionValues(name) {
|
|
19
17
|
const values = [];
|
|
20
|
-
process.argv.forEach((value, index) => {
|
|
18
|
+
process.argv.forEach((value, index) => {
|
|
19
|
+
if (value !== name) return;
|
|
20
|
+
const candidate = process.argv[index + 1];
|
|
21
|
+
if (candidate === undefined || candidate.startsWith("--")) fail(`${name} requires a value`);
|
|
22
|
+
values.push(candidate);
|
|
23
|
+
});
|
|
21
24
|
return values;
|
|
22
25
|
}
|
|
26
|
+
function arg(name) {
|
|
27
|
+
const values = optionValues(name);
|
|
28
|
+
if (values.length > 1) fail(`${name} may be provided only once`);
|
|
29
|
+
return values[0];
|
|
30
|
+
}
|
|
31
|
+
function args(name) {
|
|
32
|
+
return optionValues(name);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function validateCommandOptions(command) {
|
|
36
|
+
const allowedByCommand = new Map([
|
|
37
|
+
["validate", []],
|
|
38
|
+
["validate-fixtures", []],
|
|
39
|
+
["validate-fixture-matrix", []],
|
|
40
|
+
["validate-adoption", []],
|
|
41
|
+
["validate-contract", ["--type", "--file"]],
|
|
42
|
+
["diagnose", ["--workspace"]],
|
|
43
|
+
["adoption-plan", ["--workspace", "--host", "--level", "--selection"]],
|
|
44
|
+
["resolve-profile", ["--file", "--pack-manifest"]],
|
|
45
|
+
["launch", ["--profile", "--envelope", "--host-agent", "--pack-manifest"]],
|
|
46
|
+
["verify-receipt", ["--receipt", "--workspace"]],
|
|
47
|
+
]);
|
|
48
|
+
const allowed = allowedByCommand.get(command);
|
|
49
|
+
if (!allowed) return;
|
|
50
|
+
const values = process.argv.slice(3);
|
|
51
|
+
for (let index = 0; index < values.length; index += 2) {
|
|
52
|
+
const option = values[index];
|
|
53
|
+
if (typeof option !== "string" || !option.startsWith("--")) fail(`unexpected positional argument: ${option ?? ""}`);
|
|
54
|
+
if (option.includes("=")) fail(`option values must be provided separately: ${option.split("=", 1)[0]}`);
|
|
55
|
+
if (!allowed.includes(option)) fail(`unknown option for ${command}: ${option}`);
|
|
56
|
+
const candidate = values[index + 1];
|
|
57
|
+
if (candidate === undefined || candidate.startsWith("--")) fail(`${option} requires a value`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
23
60
|
|
|
24
61
|
function collectSchemaRefs(value, refs = []) {
|
|
25
62
|
if (Array.isArray(value)) value.forEach((item) => collectSchemaRefs(item, refs));
|
|
@@ -229,7 +266,7 @@ function validatePlugin() {
|
|
|
229
266
|
const manifestPath = resolve(root, ".codex-plugin/plugin.json");
|
|
230
267
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
231
268
|
if (manifest.name !== "forgerail") errors.push("Plugin name must be forgerail");
|
|
232
|
-
if (manifest.version !== "0.1.0-alpha.
|
|
269
|
+
if (manifest.version !== "0.1.0-alpha.4") errors.push("Plugin version must be 0.1.0-alpha.4");
|
|
233
270
|
if (manifest.license !== "Apache-2.0") errors.push("Plugin license must be Apache-2.0");
|
|
234
271
|
const expectedSkills = ["architecture-convergence-audit", "forgerail", "forgerail-workspace-diagnosis", "workspace-health-review"];
|
|
235
272
|
const actualSkills = readdirSync(resolve(root, "skills"), { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
@@ -595,17 +632,24 @@ function validateAdoption() {
|
|
|
595
632
|
const before = JSON.stringify(workspaceSnapshot(workspace));
|
|
596
633
|
let single;
|
|
597
634
|
let multi;
|
|
635
|
+
let detected;
|
|
636
|
+
let available;
|
|
598
637
|
try {
|
|
599
638
|
single = planAdoption(root, workspace, ["codex"]);
|
|
600
639
|
multi = planAdoption(root, workspace, ["codex", "claude-code", "cursor"]);
|
|
640
|
+
detected = planAdoption(root, workspace);
|
|
641
|
+
available = planAdoption(root, workspace, [], "lightweight-adoption", "all-available");
|
|
601
642
|
} catch (error) {
|
|
602
643
|
errors.push(error.message);
|
|
603
644
|
}
|
|
604
645
|
const after = JSON.stringify(workspaceSnapshot(workspace));
|
|
605
646
|
if (before !== after) errors.push("adoption planning mutated its fixture workspace");
|
|
606
647
|
if (single?.strategy !== "single-host-managed-block" || single?.proposedWrites?.length !== 1 || single?.proposedWrites?.[0]?.path !== "AGENTS.md") errors.push("single-host plan is not a bounded AGENTS.md managed block");
|
|
648
|
+
if (single?.hostSelection?.mode !== "explicit" || Object.keys(single?.hostSelection?.hosts ?? {}).join(",") !== "codex") errors.push("single-host plan did not retain its explicit selection");
|
|
607
649
|
if (multi?.strategy !== "shared-contract-with-thin-bindings" || !multi?.proposedWrites?.some((write) => write.path === "FORGERAIL.md")) errors.push("multi-host plan is missing the shared Adoption Contract");
|
|
608
|
-
if (
|
|
650
|
+
if (detected?.hostSelection?.mode !== "all-detected" || Object.keys(detected?.hostSelection?.hosts ?? {}).join(",") !== "codex") errors.push("default adoption planning did not resolve detected hosts");
|
|
651
|
+
if (available?.hostSelection?.mode !== "all-available" || Object.keys(available?.hostSelection?.hosts ?? {}).length !== registry.adapters.length) errors.push("all-available adoption planning did not resolve the current registry");
|
|
652
|
+
if (multi?.hostSelection?.hosts?.["claude-code"]?.status !== "profile-only" || multi?.hostSelection?.hosts?.cursor?.status !== "profile-only") errors.push("unverified hosts must remain profile-only");
|
|
609
653
|
if ([...(single?.proposedWrites ?? []), ...(multi?.proposedWrites ?? [])].some((write) => write.path === ".forgerail" || write.path.startsWith(".forgerail/"))) errors.push("alpha.1 adoption plan cannot propose .forgerail state");
|
|
610
654
|
try {
|
|
611
655
|
planAdoption(root, workspace, ["codex"], "persisted-governance");
|
|
@@ -617,6 +661,7 @@ function validateAdoption() {
|
|
|
617
661
|
}
|
|
618
662
|
|
|
619
663
|
const [command] = process.argv.slice(2);
|
|
664
|
+
validateCommandOptions(command);
|
|
620
665
|
if (command === "validate") {
|
|
621
666
|
const result = validatePlugin(); emit(result); if (!result.valid) process.exitCode = 1;
|
|
622
667
|
} else if (command === "validate-fixtures") {
|
|
@@ -630,11 +675,11 @@ if (command === "validate") {
|
|
|
630
675
|
if (!type || !file) fail("validate-contract requires --type and --file");
|
|
631
676
|
const result = validateContract(type, readJson(resolve(file))); emit(result); if (!result.valid) process.exitCode = 1;
|
|
632
677
|
} else if (command === "diagnose") {
|
|
633
|
-
const workspace = arg("--workspace"); if (!workspace) fail("diagnose requires --workspace"); emit(diagnoseWorkspace(workspace));
|
|
678
|
+
const workspace = arg("--workspace"); if (!workspace) fail("diagnose requires --workspace"); emit(diagnoseWorkspace(workspace, root));
|
|
634
679
|
} else if (command === "adoption-plan") {
|
|
635
|
-
const workspace = arg("--workspace"); const hosts = args("--host"); const level = arg("--level") ?? "lightweight-adoption";
|
|
636
|
-
if (!workspace
|
|
637
|
-
try { emit(planAdoption(root, workspace, hosts, level)); } catch (error) { fail(error.message); }
|
|
680
|
+
const workspace = arg("--workspace"); const hosts = args("--host"); const level = arg("--level") ?? "lightweight-adoption"; const selection = arg("--selection");
|
|
681
|
+
if (!workspace) fail("adoption-plan requires --workspace");
|
|
682
|
+
try { emit(planAdoption(root, workspace, hosts, level, selection)); } catch (error) { fail(error.message); }
|
|
638
683
|
} else if (command === "resolve-profile") {
|
|
639
684
|
const file = arg("--file"); if (!file) fail("resolve-profile requires --file");
|
|
640
685
|
const manifests = [
|
|
@@ -651,13 +696,13 @@ if (command === "validate") {
|
|
|
651
696
|
if (!profile || !envelope || !hostAgent) fail("launch requires --profile, --envelope, and --host-agent");
|
|
652
697
|
const profilePayload = readJson(resolve(profile));
|
|
653
698
|
const effectiveProfile = profilePayload.profile ?? profilePayload;
|
|
654
|
-
const
|
|
699
|
+
const manifests = [
|
|
700
|
+
...readdirSync(resolve(root, "packs")).filter((name) => name.endsWith(".json")).map((name) => readJson(resolve(root, "packs", name))),
|
|
701
|
+
...args("--pack-manifest").map((path) => readJson(resolve(path))),
|
|
702
|
+
];
|
|
703
|
+
const result = createLaunchContract(effectiveProfile, readJson(resolve(envelope)), hostAgent, manifests); emit(result); if (!result.valid) process.exitCode = 1;
|
|
655
704
|
} else if (command === "verify-receipt") {
|
|
656
705
|
const receipt = arg("--receipt"); const workspace = arg("--workspace");
|
|
657
706
|
if (!receipt || !workspace) fail("verify-receipt requires --receipt and --workspace");
|
|
658
707
|
const result = verifyReceipt(readJson(resolve(receipt)), workspace); emit(result); if (!result.valid) process.exitCode = 1;
|
|
659
|
-
} else
|
|
660
|
-
const output = arg("--output"); if (!output) fail("build-bundle requires --output");
|
|
661
|
-
const result = buildBundle(root, output);
|
|
662
|
-
emit(process.argv.includes("--summary") ? { schemaVersion: result.schemaVersion, productId: result.productId, projection: result.projection, fileCount: result.fileCount, totalBytes: result.totalBytes, digest: result.digest, receiptDigest: result.receiptDigest } : result);
|
|
663
|
-
} else fail("usage: forgerail.mjs validate | validate-fixtures | validate-fixture-matrix | validate-adoption | validate-contract | diagnose | adoption-plan | resolve-profile | launch | verify-receipt | build-bundle");
|
|
708
|
+
} else fail("usage: forgerail.mjs validate | validate-fixtures | validate-fixture-matrix | validate-adoption | validate-contract | diagnose | adoption-plan | resolve-profile | launch | verify-receipt");
|