@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.
Files changed (63) hide show
  1. package/.codex-plugin/plugin.json +2 -3
  2. package/CHANGELOG.md +22 -1
  3. package/CODE_OF_CONDUCT.md +34 -0
  4. package/CONTRIBUTING.md +68 -4
  5. package/README.md +126 -49
  6. package/README.zh-CN.md +131 -28
  7. package/SECURITY.md +48 -4
  8. package/SUPPORT.md +37 -0
  9. package/adapters/claude-code.json +6 -1
  10. package/adapters/codex.json +6 -0
  11. package/adapters/cursor.json +5 -0
  12. package/contracts/adoption-plan.schema.json +39 -18
  13. package/contracts/effective-profile.schema.json +4 -4
  14. package/contracts/host-adapter.schema.json +66 -4
  15. package/contracts/host-binding-receipt.schema.json +1 -1
  16. package/contracts/launch-contract.schema.json +38 -2
  17. package/contracts/profile-change-candidate.schema.json +1 -1
  18. package/contracts/return-receipt.schema.json +1 -1
  19. package/contracts/task-envelope.schema.json +1 -1
  20. package/directory/README.md +1 -1
  21. package/directory/release-notes-alpha3.md +7 -0
  22. package/directory/release-notes-alpha4.md +9 -0
  23. package/directory/submission-candidate.json +5 -6
  24. package/docs/adoption.md +63 -26
  25. package/docs/adoption.zh-CN.md +62 -25
  26. package/docs/architecture-acceptance.md +1 -1
  27. package/docs/composable-autonomy.zh-CN.md +16 -22
  28. package/docs/installation.md +72 -40
  29. package/docs/installation.zh-CN.md +90 -31
  30. package/docs/release-alpha3.md +25 -0
  31. package/docs/release-alpha3.zh-CN.md +25 -0
  32. package/docs/release-alpha4.md +33 -0
  33. package/docs/release-alpha4.zh-CN.md +33 -0
  34. package/package.json +7 -3
  35. package/scripts/adoption-closeout-regressions.mjs +100 -0
  36. package/scripts/build-universal-directory-candidate.mjs +2 -2
  37. package/scripts/disposable-consumer.mjs +11 -18
  38. package/scripts/fixtures/contracts/adoption-plan.multi-host.valid.json +16 -7
  39. package/scripts/fixtures/contracts/adoption-plan.mutating.invalid.json +6 -3
  40. package/scripts/fixtures/contracts/adoption-plan.single-host.valid.json +9 -4
  41. package/scripts/fixtures/contracts/effective-profile.duplicate-rule.invalid.json +1 -1
  42. package/scripts/fixtures/contracts/effective-profile.valid.json +3 -4
  43. package/scripts/fixtures/contracts/host-adapter.claude-code.profile-only.valid.json +6 -1
  44. package/scripts/fixtures/contracts/host-adapter.codex.valid.json +6 -0
  45. package/scripts/fixtures/contracts/host-adapter.cursor.profile-only.valid.json +5 -0
  46. package/scripts/fixtures/contracts/host-adapter.false-supported.invalid.json +6 -1
  47. package/scripts/fixtures/contracts/launch-contract.execution-owner.invalid.json +5 -1
  48. package/scripts/fixtures/contracts/launch-contract.valid.json +5 -1
  49. package/scripts/fixtures/open-source-docs/cases.json +65 -0
  50. package/scripts/forgerail.mjs +61 -16
  51. package/scripts/integrity-regressions.mjs +1261 -0
  52. package/scripts/lib/adoption.mjs +666 -51
  53. package/scripts/lib/bounded-read.mjs +80 -0
  54. package/scripts/lib/composition.mjs +77 -7
  55. package/scripts/lib/contracts.mjs +126 -40
  56. package/scripts/lib/diagnosis.mjs +146 -39
  57. package/scripts/shadow-comparison.mjs +52 -34
  58. package/scripts/validate-open-source-docs.mjs +132 -0
  59. package/scripts/validate-release.mjs +77 -13
  60. package/scripts/validate-universal-directory.mjs +17 -5
  61. package/skills/forgerail/references/adoption.md +2 -2
  62. package/skills/forgerail/references/contracts.md +2 -2
  63. package/scripts/lib/bundle.mjs +0 -77
@@ -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 arg(name) { const index = process.argv.indexOf(name); return index >= 0 ? process.argv[index + 1] : undefined; }
18
- function args(name) {
16
+ function optionValues(name) {
19
17
  const values = [];
20
- process.argv.forEach((value, index) => { if (value === name && process.argv[index + 1]) values.push(process.argv[index + 1]); });
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.2") errors.push("Plugin version must be 0.1.0-alpha.2");
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 (multi?.hosts?.find((host) => host.adapterId === "claude-code")?.status !== "profile-only" || multi?.hosts?.find((host) => host.adapterId === "cursor")?.status !== "profile-only") errors.push("unverified hosts must remain profile-only");
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 || hosts.length === 0) fail("adoption-plan requires --workspace and at least one --host");
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 result = createLaunchContract(effectiveProfile, readJson(resolve(envelope)), hostAgent); emit(result); if (!result.valid) process.exitCode = 1;
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 if (command === "build-bundle") {
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");