@echopath-labs/forgerail 0.1.0-alpha.1

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 (86) hide show
  1. package/.codex-plugin/plugin.json +37 -0
  2. package/CHANGELOG.md +17 -0
  3. package/CONTRIBUTING.md +7 -0
  4. package/LICENSE +201 -0
  5. package/NOTICE +4 -0
  6. package/PLUGIN.md +11 -0
  7. package/README.md +65 -0
  8. package/README.zh-CN.md +39 -0
  9. package/SECURITY.md +7 -0
  10. package/adapters/claude-code.json +20 -0
  11. package/adapters/codex.json +20 -0
  12. package/adapters/cursor.json +20 -0
  13. package/contracts/adoption-plan.schema.json +53 -0
  14. package/contracts/capability-pack.schema.json +23 -0
  15. package/contracts/effective-profile.schema.json +42 -0
  16. package/contracts/host-adapter.schema.json +43 -0
  17. package/contracts/host-binding-receipt.schema.json +47 -0
  18. package/contracts/launch-contract.schema.json +15 -0
  19. package/contracts/profile-change-candidate.schema.json +21 -0
  20. package/contracts/return-receipt.schema.json +23 -0
  21. package/contracts/task-envelope.schema.json +21 -0
  22. package/docs/adoption.md +51 -0
  23. package/docs/adoption.zh-CN.md +51 -0
  24. package/docs/agw-coverage-baseline.json +21 -0
  25. package/docs/agw-coverage-baseline.md +21 -0
  26. package/docs/agw-frozen-baseline.json +51 -0
  27. package/docs/architecture-acceptance.md +38 -0
  28. package/docs/context-cost.md +13 -0
  29. package/docs/external-capability-packs.md +25 -0
  30. package/docs/installation.md +88 -0
  31. package/docs/installation.zh-CN.md +60 -0
  32. package/docs/migration-from-agw.md +12 -0
  33. package/docs/pack-authoring.md +17 -0
  34. package/docs/release.md +50 -0
  35. package/docs/release.zh-CN.md +50 -0
  36. package/package.json +48 -0
  37. package/packs/workspace-health-review.json +15 -0
  38. package/scripts/disposable-consumer.mjs +143 -0
  39. package/scripts/fixtures/contracts/adoption-plan.multi-host.valid.json +44 -0
  40. package/scripts/fixtures/contracts/adoption-plan.mutating.invalid.json +18 -0
  41. package/scripts/fixtures/contracts/adoption-plan.single-host.valid.json +27 -0
  42. package/scripts/fixtures/contracts/effective-profile.valid.json +29 -0
  43. package/scripts/fixtures/contracts/host-adapter.claude-code.profile-only.valid.json +14 -0
  44. package/scripts/fixtures/contracts/host-adapter.codex.valid.json +17 -0
  45. package/scripts/fixtures/contracts/host-adapter.cursor.profile-only.valid.json +14 -0
  46. package/scripts/fixtures/contracts/host-binding-receipt.unverified.invalid.json +27 -0
  47. package/scripts/fixtures/contracts/host-binding-receipt.valid.json +27 -0
  48. package/scripts/fixtures/contracts/launch-contract.valid.json +19 -0
  49. package/scripts/fixtures/contracts/profile-change-candidate.valid.json +13 -0
  50. package/scripts/fixtures/contracts/profile-input.available-pack.json +26 -0
  51. package/scripts/fixtures/contracts/profile-input.conflict.json +20 -0
  52. package/scripts/fixtures/contracts/profile-input.external-rulesets.json +11 -0
  53. package/scripts/fixtures/contracts/profile-input.orchestration-available.json +11 -0
  54. package/scripts/fixtures/contracts/return-receipt.deviation.invalid.json +15 -0
  55. package/scripts/fixtures/contracts/return-receipt.valid.json +15 -0
  56. package/scripts/fixtures/contracts/task-envelope.overlap.invalid.json +13 -0
  57. package/scripts/fixtures/contracts/task-envelope.valid.json +13 -0
  58. package/scripts/fixtures/workspaces/empty-records/README.md +3 -0
  59. package/scripts/fixtures/workspaces/markdown-existing/AGENTS.md +3 -0
  60. package/scripts/fixtures/workspaces/markdown-existing/docs/adr/0001-record-strategy.md +3 -0
  61. package/scripts/fixtures/workspaces/markdown-existing/package.json +7 -0
  62. package/scripts/forgerail.mjs +210 -0
  63. package/scripts/lib/adoption.mjs +158 -0
  64. package/scripts/lib/bundle.mjs +77 -0
  65. package/scripts/lib/composition.mjs +96 -0
  66. package/scripts/lib/contracts.mjs +316 -0
  67. package/scripts/lib/diagnosis.mjs +93 -0
  68. package/scripts/shadow-comparison.mjs +98 -0
  69. package/scripts/validate-external-packs.mjs +27 -0
  70. package/scripts/validate-release.mjs +194 -0
  71. package/skills/forgerail/SKILL.md +53 -0
  72. package/skills/forgerail/agents/openai.yaml +7 -0
  73. package/skills/forgerail/references/adoption.md +13 -0
  74. package/skills/forgerail/references/contracts.md +57 -0
  75. package/skills/forgerail/references/profile-resolution.md +14 -0
  76. package/skills/forgerail-workspace-diagnosis/SKILL.md +42 -0
  77. package/skills/forgerail-workspace-diagnosis/agents/openai.yaml +7 -0
  78. package/skills/forgerail-workspace-diagnosis/references/record-strategies.md +13 -0
  79. package/skills/workspace-health-review/SKILL.md +27 -0
  80. package/skills/workspace-health-review/agents/openai.yaml +7 -0
  81. package/skills/workspace-health-review/references/health-review.md +21 -0
  82. package/templates/FORGERAIL.md +15 -0
  83. package/templates/bindings/claude-code-thin.md +5 -0
  84. package/templates/bindings/codex-compact.md +5 -0
  85. package/templates/bindings/codex-thin.md +5 -0
  86. package/templates/bindings/cursor-thin.mdc +7 -0
@@ -0,0 +1,50 @@
1
+ # ForgeRail 0.1.0-alpha.1 Release Runbook
2
+
3
+ This is the project-owned runbook for the first usable ForgeRail prerelease. It governs `@echopath-labs/forgerail@0.1.0-alpha.1`, the Git tag `v0.1.0-alpha.1`, and the matching EchoPath Labs Marketplace snapshot. The unscoped `forgerail@0.0.0-reserved.0` remains a reservation and is outside the product release path. This document does not authorize any operation by itself.
4
+
5
+ ## Independent Gates
6
+
7
+ The gates are independent and non-transitive:
8
+
9
+ 1. `remote_integration_approval` may push one exact signed commit to `release/0.1.0-alpha.1`, open a Draft PR to an exact `main` base, observe Node.js 22 and 24 CI, and return a receipt.
10
+ 2. `release_approval` may make that exact PR Ready, merge it by the approved method, publish the exact npm prerelease, create the annotated tag and GitHub prerelease, and verify consumer installation.
11
+ 3. `lifecycle_change_approval` is required for any AGW deprecation, redirect, archive, removal, or activation change.
12
+
13
+ Rulesets, branch protection, stable releases, other products, and OpenSpec archival are outside all three gates unless explicitly added to a new approval.
14
+
15
+ ## Source-First Candidate
16
+
17
+ - Canonical source is owned in the EchoPath workspace.
18
+ - Public-only fixes are prohibited. Change canonical source, validate it, generate the deterministic projection, and sign an exact source commit, tree, inventory, manifest digest, and projection receipt.
19
+ - The first public candidate must be an ordinary child of the observed public `main`. After remote integration, a source-first replacement on the same release branch must be an ordinary fast-forward successor of the current release head. Push either form by exact SHA refspec without force; keep the PR base and publication comparison baseline bound to the observed `main`.
20
+ - After squash merge, the resulting public `main` tree must equal the final signed projection tree.
21
+ - Open a Draft PR from `release/0.1.0-alpha.1` to the bound `main` SHA. Any head, base, tree, version, license, or check drift stops the gate.
22
+ - Required PR checks are Plugin Contracts on Node.js 22 and 24, including Core/contracts, progressive adoption, external packs, frozen AGW coverage, release-source validation, and disposable consumer lifecycle.
23
+
24
+ ## Release-Approval Execution
25
+
26
+ Only after a new exact `release_approval`:
27
+
28
+ 1. Confirm the PR remains Open, Draft, mergeable, based on the approved `main`, and at the approved head with all required checks successful.
29
+ 2. Mark only that PR Ready and squash-merge it with an exact-head guard. Confirm merged `main` has the signed candidate tree.
30
+ 3. From a clean checkout of the merged `main`, run Node.js 22 and 24 validation: `npm test`, `npm run test:shadow`, `npm run test:release`, `npm run test:consumer`, `npm pack --dry-run --json`, and `npm audit`.
31
+ 4. Verify npm identity and `@echopath-labs` organization package permission without printing credentials. Confirm `@echopath-labs/forgerail@0.1.0-alpha.1` is absent and record the scoped package's current `latest` and `next` dist-tags. Separately confirm unscoped `forgerail` still contains only `0.0.0-reserved.0` with unchanged reservation tags.
32
+ 5. Publish exactly `@echopath-labs/forgerail@0.1.0-alpha.1` with public access and the `next` tag. Keep provenance disabled unless a separately validated trusted-publishing path replaces credential publication. Do not publish product code to unscoped `forgerail`.
33
+ 6. Verify registry version, shasum, integrity, license, repository, binary shim, and exact isolated install. Run `forgerail validate` and one bounded read-only diagnosis.
34
+ 7. Only after exact-version smoke succeeds, move the scoped package's `latest` to `0.1.0-alpha.1`. Verify exact, scoped `next`, and scoped `latest` isolated installs. Leave every unscoped `forgerail` version and dist-tag unchanged.
35
+ 8. Create annotated `v0.1.0-alpha.1` on the exact merged `main` commit and push it without moving any existing tag.
36
+ 9. Publish a GitHub prerelease titled `ForgeRail v0.1.0-alpha.1` using the versioned CHANGELOG section. ForgeRail has no standalone binary release assets in this version.
37
+ 10. Register the exact tag as a disposable Codex Marketplace, install the main Plugin, start a new Codex task, and verify `$forgerail`, `$forgerail-workspace-diagnosis`, and `$workspace-health-review`. Generate a single-host Codex Adoption Plan, prove planning leaves the workspace unchanged, explicitly approve and apply only its managed block in a disposable project, start another new task or perform the supported equivalent discovery check, and validate a Host Binding Receipt. Confirm no `.forgerail/` state appears. Install and discover each external Capability Pack separately without authenticating or executing it.
38
+ 11. Return one durable receipt binding canonical source, public PR/merge, tree, npm package, dist-tags, Git tag, GitHub prerelease, Plugin discovery, checks, non-mutations, and recovery anchors.
39
+
40
+ ## Stop And Rollback
41
+
42
+ - Before npm publication, stop without remote release mutation when any identity, tree, check, credential, package, or consumer result differs.
43
+ - Do not unpublish or overwrite an immutable npm version. Do not move a published Git tag. Shipped defects require a forward fix and a new version.
44
+ - If exact scoped publication succeeds but consumer smoke fails, a separately approved rollback may remove or move mutable scoped `latest`/`next` to the last verified scoped version. The first scoped prerelease has no earlier scoped runtime version, so the immutable alpha.1 stays published while mutable tags are handled explicitly; never point scoped tags at the unscoped reservation.
45
+ - A PR or merge defect is recovered with an ordinary reviewed revert or forward commit; no force push or history rewrite.
46
+ - AGW remains available during the real compatibility-period canary. This release does not authorize an AGW lifecycle change.
47
+
48
+ ## Required Release Receipt
49
+
50
+ Record exact repository, branch, PR, approved head/base, merged commit/tree, canonical source and projection digests, Node.js 22 and 24 checks, Host Adapter statuses, adoption non-mutation, Codex Host Binding Receipt, pack metadata, npm identity and immutable package metadata, final dist-tags, annotated tag object and peeled commit, GitHub prerelease identity, disposable Plugin/CLI install results, rollback anchors, and confirmation that no `.forgerail/`, Ruleset, branch-protection, stable-release, AGW lifecycle, or OpenSpec archive mutation occurred.
@@ -0,0 +1,50 @@
1
+ # ForgeRail 0.1.0-alpha.1 发布 Runbook
2
+
3
+ 这是 ForgeRail 首个可用预发布版的项目专属 runbook,约束 `@echopath-labs/forgerail@0.1.0-alpha.1`、Git tag `v0.1.0-alpha.1` 与同一 EchoPath Labs Marketplace 快照。未作用域 `forgerail@0.0.0-reserved.0` 继续只作名称占位,不属于产品发布路径;文档本身不授予任何执行权限。
4
+
5
+ ## 独立审批门
6
+
7
+ 三个审批门互不继承:
8
+
9
+ 1. `remote_integration_approval` 只允许将一个精确签名 commit 推到 `release/0.1.0-alpha.1`,基于精确 `main` 创建 Draft PR,观察 Node.js 22 and 24 CI 并返回 receipt。
10
+ 2. `release_approval` 才可把该精确 PR 转 Ready、按批准方式合并、发布精确 npm 预发布版、创建 annotated tag 与 GitHub prerelease,并核验消费者安装。
11
+ 3. 任何 AGW 弃用、重定向、归档、删除或激活切换都需要单独的 `lifecycle_change_approval`。
12
+
13
+ Ruleset、branch protection、稳定版、其他产品与 OpenSpec archive 不在上述授权内,除非新的审批明确加入。
14
+
15
+ ## Source-first 候选
16
+
17
+ - canonical source 只在 EchoPath 工作区维护;禁止 public-only 修复。
18
+ - 每次修正都要重新校验、确定性投影,并绑定精确 source commit、tree、inventory、manifest digest 与 projection receipt。
19
+ - 首个公共候选必须是已观测 `main` 的普通子 commit。remote integration 后若在同一 release branch 上进行 source-first 替换,新候选必须是当前 release head 的普通 fast-forward successor。两种形式都只能用精确 SHA refspec 推送且不得 force push;PR base 与 publication comparison baseline 继续绑定已观测 `main`。
20
+ - squash merge 后的公共 `main` tree 必须等于最终签名 projection tree。
21
+ - Draft PR 必须绑定精确 base/head;任何 SHA、tree、版本、许可证或 check 漂移都停止。
22
+ - 必须通过 Node.js 22 and 24 Plugin Contracts,包括 Core/contracts、渐进式采用、外部 packs、冻结 AGW 覆盖、发布源校验与一次性消费者生命周期。
23
+
24
+ ## 发布审批后的门序
25
+
26
+ 获得新的精确 `release_approval` 后才执行:
27
+
28
+ 1. 复核 PR 仍为 Open/Draft、可合并、base/head 与批准包一致,required checks 全部成功。
29
+ 2. 仅将该 PR 转 Ready,并用 exact-head guard squash merge;确认合并后 `main` tree 等于签名候选 tree。
30
+ 3. 在干净的 merged `main` 上用 Node.js 22 和 24 运行 `npm test`、`npm run test:shadow`、`npm run test:release`、`npm run test:consumer`、`npm pack --dry-run --json` 与 `npm audit`。
31
+ 4. 不暴露凭据地验证 npm 身份及 `@echopath-labs` 组织 package 权限;确认 scoped alpha.1 尚不存在,并记录 scoped package 的 `latest`、`next`。另行确认未作用域 `forgerail` 仍只有 `0.0.0-reserved.0` 且占位 tags 未变。
32
+ 5. 仅以 public access 与 `next` tag 发布 `@echopath-labs/forgerail@0.1.0-alpha.1`;在可信发布链完成独立验证前关闭 provenance。不得向未作用域 `forgerail` 发布产品代码。
33
+ 6. 核验 registry version、shasum、integrity、license、repository、binary shim,并执行精确版本隔离安装、`forgerail validate` 与一次有界只读诊断。
34
+ 7. 精确版本 smoke 通过后才把 scoped package 的 `latest` 移到 alpha.1;核验 exact、scoped `next`、scoped `latest` 隔离安装,并保持未作用域 `forgerail` 的所有 version/dist-tag 不变。
35
+ 8. 在精确 merged `main` 上创建 annotated `v0.1.0-alpha.1` 并推送,不移动任何已有 tag。
36
+ 9. 发布标题为 `ForgeRail v0.1.0-alpha.1` 的 GitHub prerelease,release notes 使用版本化 CHANGELOG;此版本没有 standalone binary assets。
37
+ 10. 在一次性环境中注册精确 tag Marketplace,安装主插件并启动新 Codex 任务,验证三个主 Skill;生成单宿主 Codex Adoption Plan,证明规划不修改工作区,仅在显式批准后向一次性项目写入 managed block,再启动新任务或执行受支持的等价发现检查,并校验 Host Binding Receipt;确认没有 `.forgerail/` 状态。每个外部 Capability Pack 分别安装与发现,不认证、不执行。
38
+ 11. 返回绑定 canonical、PR/merge、tree、npm、dist-tags、tag、prerelease、Plugin discovery、校验、非变更项与恢复锚点的 durable receipt。
39
+
40
+ ## 停止与回滚
41
+
42
+ - npm 发布前任何身份、tree、check、凭据、package 或消费者结果漂移都立即停止,不产生发布副作用。
43
+ - Do not unpublish 或覆盖不可变 npm version;不得移动已发布 Git tag。发布后缺陷通过新版本前向修复。
44
+ - 若 scoped npm 精确发布成功但消费者 smoke 失败,只有得到单独批准后才可删除或移动 scoped `latest`/`next` 到最近已验证的 scoped 版本。首个 scoped prerelease 没有更早 runtime,immutable alpha.1 必须保留;不得把 scoped tags 指向未作用域占位版本。
45
+ - PR 或 merge 缺陷使用普通 review 后的 revert/forward commit;禁止 force push 与历史重写。
46
+ - 真实兼容期 canary 完成前 AGW 继续可用;本次发布不授权 AGW 生命周期变更。
47
+
48
+ ## 必需 receipt
49
+
50
+ 记录精确 repo、branch、PR、批准 head/base、merged commit/tree、canonical 与投影 digest、Node.js 22/24 checks、Host Adapter 状态、采用规划非变更、Codex Host Binding Receipt、pack metadata、npm 身份及不可变 package metadata、最终 dist-tags、annotated tag object/peeled commit、GitHub prerelease、一次性 Plugin/CLI 安装结果、rollback anchors,并确认没有 `.forgerail/`、Ruleset、branch protection、stable release、AGW lifecycle 或 OpenSpec archive 变更。
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@echopath-labs/forgerail",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Deterministic validation and diagnosis CLI for the ForgeRail Agent Plugin.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "forgerail": "scripts/forgerail.mjs"
9
+ },
10
+ "files": [
11
+ ".codex-plugin/",
12
+ "adapters/",
13
+ "contracts/",
14
+ "docs/",
15
+ "marketplace/",
16
+ "packs/",
17
+ "scripts/",
18
+ "skills/",
19
+ "templates/",
20
+ "CHANGELOG.md",
21
+ "CONTRIBUTING.md",
22
+ "LICENSE",
23
+ "NOTICE",
24
+ "PLUGIN.md",
25
+ "README.md",
26
+ "README.zh-CN.md",
27
+ "SECURITY.md"
28
+ ],
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "scripts": {
33
+ "test": "node scripts/forgerail.mjs validate && node scripts/forgerail.mjs validate-fixtures && node scripts/forgerail.mjs validate-adoption",
34
+ "test:consumer": "node scripts/disposable-consumer.mjs",
35
+ "test:release": "node scripts/validate-release.mjs",
36
+ "test:shadow": "node scripts/shadow-comparison.mjs",
37
+ "diagnose:fixture": "node scripts/forgerail.mjs diagnose --workspace scripts/fixtures/workspaces/markdown-existing",
38
+ "prepublishOnly": "npm test && npm run test:shadow && npm run test:release && npm run test:consumer"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/echopath-labs/forgerail.git"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "tag": "next"
47
+ }
48
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "id": "workspace-health-review",
4
+ "purpose": "Audit workspace governance, drift, context debt, ownership, and recovery health without executing implementation work.",
5
+ "entry": "skills/workspace-health-review/SKILL.md",
6
+ "risk": "low",
7
+ "states": ["available", "recommended", "enabled", "required", "blocked", "disabled"],
8
+ "triggers": ["explicit workspace health request", "pre-refactor governance audit", "pre-release governance audit", "recovery difficulty"],
9
+ "inputs": ["workspace boundary evidence", "agent instructions", "durable record inventory", "skill inventory", "recovery entries"],
10
+ "dependencies": [],
11
+ "conflicts": [],
12
+ "approvals": ["durable-write-approval"],
13
+ "validation": ["report separates observations from proposed modifications", "no mutation occurs in Analyze First mode"],
14
+ "receiptExtensions": ["healthStatus", "contextDebt", "recoveryRisks"]
15
+ }
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { createHash } from "node:crypto";
5
+ import { cpSync, existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { resolve } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
11
+ const base = mkdtempSync(resolve(tmpdir(), "forgerail-consumer-"));
12
+ const cache = resolve(base, "npm-cache");
13
+ const consumer = resolve(base, "consumer");
14
+ const target = resolve(base, "target-workspace");
15
+ const priorSource = resolve(base, "prior-source");
16
+ const packageName = "@echopath-labs/forgerail";
17
+ const installedPackageRoot = resolve(consumer, "node_modules", "@echopath-labs", "forgerail");
18
+ mkdirSync(consumer, { recursive: true });
19
+ mkdirSync(resolve(target, "docs/adr"), { recursive: true });
20
+ writeFileSync(resolve(consumer, "package.json"), `${JSON.stringify({ name: "forgerail-disposable-consumer", private: true }, null, 2)}\n`);
21
+ writeFileSync(resolve(target, "AGENTS.md"), "# Target\n\nUse the existing Markdown ADRs.\n");
22
+ writeFileSync(resolve(target, "docs/adr/0001.md"), "# ADR 0001\n\nUse Markdown decisions.\n");
23
+ cpSync(root, priorSource, { recursive: true });
24
+ const priorManifest = JSON.parse(readFileSync(resolve(priorSource, "package.json"), "utf8"));
25
+ priorManifest.version = "0.1.0-alpha.0";
26
+ writeFileSync(resolve(priorSource, "package.json"), `${JSON.stringify(priorManifest, null, 2)}\n`);
27
+ const priorPlugin = JSON.parse(readFileSync(resolve(priorSource, ".codex-plugin/plugin.json"), "utf8"));
28
+ priorPlugin.version = "0.1.0-alpha.0";
29
+ writeFileSync(resolve(priorSource, ".codex-plugin/plugin.json"), `${JSON.stringify(priorPlugin, null, 2)}\n`);
30
+
31
+ function run(command, args, cwd = consumer) {
32
+ const result = spawnSync(command, args, { cwd, encoding: "utf8", env: { ...process.env, npm_config_cache: cache }, maxBuffer: 16 * 1024 * 1024 });
33
+ if (result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed: ${(result.stderr || result.stdout).trim()}`);
34
+ return result.stdout.trim();
35
+ }
36
+
37
+ function sha256(value) {
38
+ return createHash("sha256").update(value).digest("hex");
39
+ }
40
+
41
+ function snapshot(path) {
42
+ return readdirSync(path, { recursive: true })
43
+ .sort()
44
+ .map((entry) => {
45
+ const target = resolve(path, entry);
46
+ const stat = statSync(target);
47
+ return stat.isFile() ? `${entry}:file:${sha256(readFileSync(target))}` : `${entry}:directory`;
48
+ });
49
+ }
50
+
51
+ function renderApprovedWrite(write) {
52
+ const targetPath = resolve(target, write.path);
53
+ const prior = existsSync(targetPath) ? readFileSync(targetPath, "utf8") : "";
54
+ if (write.operation === "create") return write.content;
55
+ if (sha256(prior) !== write.baseSha256) throw new Error(`adoption base digest drifted for ${write.path}`);
56
+ if (write.operation === "append-managed-block") return `${prior.replace(/\s*$/, "")}\n\n${write.content}`;
57
+ const start = `<!-- ${write.managedMarker}:start -->`;
58
+ const end = `<!-- ${write.managedMarker}:end -->`;
59
+ const startIndex = prior.indexOf(start);
60
+ const endIndex = prior.indexOf(end, startIndex);
61
+ if (startIndex < 0 || endIndex < 0) throw new Error(`adoption managed block is missing for ${write.path}`);
62
+ return `${prior.slice(0, startIndex)}${write.content}${prior.slice(endIndex + end.length)}`;
63
+ }
64
+
65
+ const priorPack = JSON.parse(run("npm", ["pack", priorSource, "--json"], base))[0];
66
+ const priorTarball = resolve(base, priorPack.filename);
67
+ run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", priorTarball]);
68
+ const priorInstalled = JSON.parse(readFileSync(resolve(installedPackageRoot, "package.json"), "utf8")).version === "0.1.0-alpha.0";
69
+ const pack = JSON.parse(run("npm", ["pack", root, "--json"], base))[0];
70
+ const tarball = resolve(base, pack.filename);
71
+ run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", tarball]);
72
+ const cli = resolve(consumer, "node_modules/.bin/forgerail");
73
+ const firstValidation = JSON.parse(run(cli, ["validate"]));
74
+ const before = JSON.stringify(snapshot(target));
75
+ const diagnosis = JSON.parse(run(cli, ["diagnose", "--workspace", target]));
76
+ const after = JSON.stringify(snapshot(target));
77
+ const adoptionPlan = JSON.parse(run(cli, ["adoption-plan", "--workspace", target, "--host", "codex"]));
78
+ const afterPlan = JSON.stringify(snapshot(target));
79
+ const proposed = adoptionPlan.proposedWrites[0];
80
+ const approvedContent = renderApprovedWrite(proposed);
81
+ writeFileSync(resolve(target, proposed.path), approvedContent);
82
+ const approvedDigest = sha256(approvedContent);
83
+ const discoveredSkills = firstValidation.skills;
84
+ const bindingReceipt = {
85
+ schemaVersion: "1.0",
86
+ planId: adoptionPlan.planId,
87
+ workspace: adoptionPlan.workspace,
88
+ adoptionLevel: "lightweight-adoption",
89
+ contractPath: null,
90
+ hosts: [{
91
+ adapterId: "codex",
92
+ target: proposed.path,
93
+ baseSha256: proposed.baseSha256,
94
+ appliedSha256: approvedDigest,
95
+ status: "verified",
96
+ verification: ["Equivalent supported discovery read the approved managed block and the installed Plugin Skill inventory from the disposable package."],
97
+ }],
98
+ changedFiles: [proposed.path],
99
+ validationEvidence: ["Applied managed content and exact base digest matched the approved Adoption Plan."],
100
+ discoveredSkills,
101
+ activationVerification: { mode: "equivalent-supported-discovery", verified: approvedContent.includes("forgerail:binding:codex:v1:start") && discoveredSkills.includes("forgerail") },
102
+ confirmedNonMutations: ["No .forgerail directory was created.", "No Capability Pack was enabled or executed."],
103
+ deviations: [],
104
+ closeout: "complete",
105
+ };
106
+ const receiptPath = resolve(base, "host-binding-receipt.json");
107
+ writeFileSync(receiptPath, `${JSON.stringify(bindingReceipt, null, 2)}\n`);
108
+ const receiptValidation = JSON.parse(run(cli, ["validate-contract", "--type", "binding-receipt", "--file", receiptPath]));
109
+ const launch = JSON.parse(run(cli, ["launch", "--profile", resolve(root, "scripts/fixtures/contracts/effective-profile.valid.json"), "--envelope", resolve(root, "scripts/fixtures/contracts/task-envelope.valid.json"), "--host-agent", "Codex"]));
110
+ const upgraded = JSON.parse(readFileSync(resolve(installedPackageRoot, "package.json"), "utf8")).version === pack.version;
111
+ run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", priorTarball]);
112
+ const rolledBack = JSON.parse(readFileSync(resolve(installedPackageRoot, "package.json"), "utf8")).version === "0.1.0-alpha.0";
113
+ run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", tarball]);
114
+ const reinstallValidation = JSON.parse(run(cli, ["validate"]));
115
+ run("npm", ["uninstall", "--no-audit", "--no-fund", packageName]);
116
+
117
+ const result = {
118
+ schemaVersion: "1.0",
119
+ packageName,
120
+ sourceVersion: pack.version,
121
+ tarball: { files: pack.entryCount, bytes: pack.size, shasum: pack.shasum, integrity: pack.integrity },
122
+ install: firstValidation.valid,
123
+ binaryShim: cli.endsWith("node_modules/.bin/forgerail"),
124
+ priorInstall: priorInstalled,
125
+ discovery: firstValidation.skills,
126
+ diagnosis: diagnosis.mode === "read-only" && diagnosis.recommendations.length === 0 && diagnosis.adoption.currentLevel === "plugin-only",
127
+ targetUnchangedByDiagnosis: before === after,
128
+ adoptionPlan: adoptionPlan.strategy === "single-host-managed-block" && adoptionPlan.requiredConfirmation && adoptionPlan.mutations.length === 0 && proposed.path === "AGENTS.md",
129
+ targetUnchangedByPlanner: after === afterPlan,
130
+ explicitApprovedWrite: approvedDigest === sha256(approvedContent) && approvedContent.includes("forgerail:binding:codex:v1:start"),
131
+ equivalentNewTaskDiscovery: bindingReceipt.activationVerification.verified && discoveredSkills.length === 3,
132
+ bindingReceipt: receiptValidation.valid,
133
+ noPersistedGovernance: !existsSync(resolve(target, ".forgerail")),
134
+ launch: launch.valid && launch.launch.executionOwner === "host-agent",
135
+ reinstall: reinstallValidation.valid,
136
+ upgrade: upgraded,
137
+ rollback: rolledBack,
138
+ uninstall: !existsSync(installedPackageRoot),
139
+ disposableRoot: "[disposable]",
140
+ };
141
+ result.passed = result.priorInstall && result.install && result.binaryShim && result.diagnosis && result.targetUnchangedByDiagnosis && result.adoptionPlan && result.targetUnchangedByPlanner && result.explicitApprovedWrite && result.equivalentNewTaskDiscovery && result.bindingReceipt && result.noPersistedGovernance && result.launch && result.upgrade && result.rollback && result.reinstall && result.uninstall;
142
+ console.log(JSON.stringify(result, null, 2));
143
+ if (!result.passed) process.exitCode = 1;
@@ -0,0 +1,44 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "planId": "adoption:multi-host",
4
+ "workspace": "fixture",
5
+ "currentLevel": "plugin-only",
6
+ "proposedLevel": "lightweight-adoption",
7
+ "strategy": "shared-contract-with-thin-bindings",
8
+ "evidence": ["Codex and Claude Code were explicitly selected."],
9
+ "hosts": [
10
+ { "adapterId": "codex", "status": "supported", "bindingTarget": "AGENTS.md", "verificationMode": "new-task-discovery" },
11
+ { "adapterId": "claude-code", "status": "profile-only", "bindingTarget": "CLAUDE.md", "verificationMode": "profile-only" }
12
+ ],
13
+ "proposedWrites": [
14
+ {
15
+ "path": "FORGERAIL.md",
16
+ "operation": "create",
17
+ "baseSha256": null,
18
+ "contentSha256": "ebba8da7ae587252193419a647f0d8e6898074899158b4f441df573351c9de7c",
19
+ "content": "<!-- forgerail:adoption-contract:v1:start -->\nShared contract.\n<!-- forgerail:adoption-contract:v1:end -->\n",
20
+ "managedMarker": "forgerail:adoption-contract:v1"
21
+ },
22
+ {
23
+ "path": "AGENTS.md",
24
+ "operation": "create",
25
+ "baseSha256": null,
26
+ "contentSha256": "989f89220b826a5f355083497552db142fcf1f19cd299febf22b037494c71645",
27
+ "content": "<!-- forgerail:binding:codex:v1:start -->\nRead FORGERAIL.md.\n<!-- forgerail:binding:codex:v1:end -->\n",
28
+ "managedMarker": "forgerail:binding:codex:v1"
29
+ },
30
+ {
31
+ "path": "CLAUDE.md",
32
+ "operation": "create",
33
+ "baseSha256": null,
34
+ "contentSha256": "e72fdc3bd532202c2b9c52095e8e20050d428a150285c3b4f1cfce040d3a481c",
35
+ "content": "<!-- forgerail:binding:claude-code:v1:start -->\nRead FORGERAIL.md.\n<!-- forgerail:binding:claude-code:v1:end -->\n",
36
+ "managedMarker": "forgerail:binding:claude-code:v1"
37
+ }
38
+ ],
39
+ "requiredConfirmation": true,
40
+ "verification": ["Verify Codex in a new task.", "Verify Claude Code before claiming activation."],
41
+ "confirmedNonMutations": ["No files were written."],
42
+ "mutations": [],
43
+ "status": "candidate"
44
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "planId": "adoption:invalid",
4
+ "workspace": "fixture",
5
+ "currentLevel": "plugin-only",
6
+ "proposedLevel": "lightweight-adoption",
7
+ "strategy": "single-host-managed-block",
8
+ "evidence": ["Codex was selected."],
9
+ "hosts": [
10
+ { "adapterId": "codex", "status": "supported", "bindingTarget": "AGENTS.md", "verificationMode": "new-task-discovery" }
11
+ ],
12
+ "proposedWrites": [],
13
+ "requiredConfirmation": true,
14
+ "verification": ["Start a new task."],
15
+ "confirmedNonMutations": ["No files should be written."],
16
+ "mutations": ["AGENTS.md written without confirmation"],
17
+ "status": "candidate"
18
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "planId": "adoption:codex-single",
4
+ "workspace": "fixture",
5
+ "currentLevel": "plugin-only",
6
+ "proposedLevel": "lightweight-adoption",
7
+ "strategy": "single-host-managed-block",
8
+ "evidence": ["Codex was explicitly selected."],
9
+ "hosts": [
10
+ { "adapterId": "codex", "status": "supported", "bindingTarget": "AGENTS.md", "verificationMode": "new-task-discovery" }
11
+ ],
12
+ "proposedWrites": [
13
+ {
14
+ "path": "AGENTS.md",
15
+ "operation": "create",
16
+ "baseSha256": null,
17
+ "contentSha256": "0bccefea0979ec3ad7b80555d1da43a793ab710417b9d4541aa2a15be9ad0810",
18
+ "content": "<!-- forgerail:binding:codex:v1:start -->\nUse ForgeRail.\n<!-- forgerail:binding:codex:v1:end -->\n",
19
+ "managedMarker": "forgerail:binding:codex:v1"
20
+ }
21
+ ],
22
+ "requiredConfirmation": true,
23
+ "verification": ["Start a new Codex task."],
24
+ "confirmedNonMutations": ["No files were written."],
25
+ "mutations": [],
26
+ "status": "candidate"
27
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "workspace": "fixture-workspace",
4
+ "computed": true,
5
+ "rules": [
6
+ {
7
+ "id": "git.primary-branch",
8
+ "value": "main",
9
+ "source": "repository metadata",
10
+ "precedence": 1,
11
+ "status": "observed"
12
+ },
13
+ {
14
+ "id": "records.strategy",
15
+ "value": "markdown-adr",
16
+ "source": "docs/adr/",
17
+ "precedence": 5,
18
+ "status": "observed"
19
+ }
20
+ ],
21
+ "packs": [
22
+ {
23
+ "id": "workspace-health-review",
24
+ "state": "available",
25
+ "reason": "Installed with ForgeRail but not required for this task."
26
+ }
27
+ ],
28
+ "conflicts": []
29
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "id": "claude-code",
4
+ "displayName": "Claude Code",
5
+ "status": "profile-only",
6
+ "instructionDiscovery": "task-start",
7
+ "skillDiscovery": "agent-skills",
8
+ "bindingTarget": "CLAUDE.md",
9
+ "bindingModes": ["managed-block", "thin-reference"],
10
+ "managedMarker": "forgerail:binding:claude-code:v1",
11
+ "activationBoundary": "host-specific-verification-required",
12
+ "verification": { "mode": "profile-only", "expectedSkills": [] },
13
+ "limitations": ["End-to-end activation has not been verified for alpha.1."]
14
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "id": "codex",
4
+ "displayName": "Codex",
5
+ "status": "supported",
6
+ "instructionDiscovery": "task-start",
7
+ "skillDiscovery": "agent-plugin-skills",
8
+ "bindingTarget": "AGENTS.md",
9
+ "bindingModes": ["managed-block", "thin-reference"],
10
+ "managedMarker": "forgerail:binding:codex:v1",
11
+ "activationBoundary": "new-task-required",
12
+ "verification": {
13
+ "mode": "new-task-discovery",
14
+ "expectedSkills": ["forgerail", "forgerail-workspace-diagnosis", "workspace-health-review"]
15
+ },
16
+ "limitations": ["A new task is required after adoption."]
17
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "id": "cursor",
4
+ "displayName": "Cursor",
5
+ "status": "profile-only",
6
+ "instructionDiscovery": "rules",
7
+ "skillDiscovery": "unknown",
8
+ "bindingTarget": ".cursor/rules/forgerail.mdc",
9
+ "bindingModes": ["thin-reference"],
10
+ "managedMarker": "forgerail:binding:cursor:v1",
11
+ "activationBoundary": "host-specific-verification-required",
12
+ "verification": { "mode": "profile-only", "expectedSkills": [] },
13
+ "limitations": ["End-to-end activation has not been verified for alpha.1."]
14
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "planId": "adoption:codex-single",
4
+ "workspace": "fixture",
5
+ "adoptionLevel": "lightweight-adoption",
6
+ "contractPath": null,
7
+ "hosts": [
8
+ {
9
+ "adapterId": "codex",
10
+ "target": "AGENTS.md",
11
+ "baseSha256": null,
12
+ "appliedSha256": "0bccefea0979ec3ad7b80555d1da43a793ab710417b9d4541aa2a15be9ad0810",
13
+ "status": "unverified",
14
+ "verification": ["No new Codex task was started."]
15
+ }
16
+ ],
17
+ "changedFiles": ["AGENTS.md"],
18
+ "validationEvidence": ["The file digest matched."],
19
+ "discoveredSkills": [],
20
+ "activationVerification": {
21
+ "mode": "new-task",
22
+ "verified": false
23
+ },
24
+ "confirmedNonMutations": ["No .forgerail directory was created."],
25
+ "deviations": [],
26
+ "closeout": "complete"
27
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "planId": "adoption:codex-single",
4
+ "workspace": "fixture",
5
+ "adoptionLevel": "lightweight-adoption",
6
+ "contractPath": null,
7
+ "hosts": [
8
+ {
9
+ "adapterId": "codex",
10
+ "target": "AGENTS.md",
11
+ "baseSha256": null,
12
+ "appliedSha256": "0bccefea0979ec3ad7b80555d1da43a793ab710417b9d4541aa2a15be9ad0810",
13
+ "status": "verified",
14
+ "verification": ["A new Codex task discovered the binding and expected Skills."]
15
+ }
16
+ ],
17
+ "changedFiles": ["AGENTS.md"],
18
+ "validationEvidence": ["Applied content digest matched the approved Adoption Plan."],
19
+ "discoveredSkills": ["forgerail", "forgerail-workspace-diagnosis", "workspace-health-review"],
20
+ "activationVerification": {
21
+ "mode": "new-task",
22
+ "verified": true
23
+ },
24
+ "confirmedNonMutations": ["No .forgerail directory was created."],
25
+ "deviations": [],
26
+ "closeout": "complete"
27
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "envelope": {
4
+ "schemaVersion": "1.0",
5
+ "taskId": "fixture-task-1",
6
+ "intent": "Update local documentation with ForgeRail guidance.",
7
+ "nonGoals": ["Publish a release"],
8
+ "ownerWorkspace": "fixture-workspace",
9
+ "allowedOperations": ["read files", "edit documentation", "run local validation"],
10
+ "prohibitedOperations": ["push", "merge", "tag", "release"],
11
+ "packs": [],
12
+ "approvalGates": ["remote-integration-approval", "release-approval"],
13
+ "validation": ["git diff --check passes"],
14
+ "returnContract": "forgerail-return-receipt-v1"
15
+ },
16
+ "effectiveRuleSources": ["AGENTS.md", "ForgeRail Core"],
17
+ "hostAgent": "Codex",
18
+ "executionOwner": "host-agent"
19
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "candidateId": "candidate:fixture-validation-rule",
4
+ "workspace": "fixture-workspace",
5
+ "ruleId": "validation.docs-links",
6
+ "proposedValue": "run documentation link validation after navigation changes",
7
+ "evidence": ["three completed tasks required the same missing link check"],
8
+ "targetOwner": "fixture-workspace",
9
+ "targetSource": "AGENTS.md",
10
+ "reason": "The repeated validation requirement should become an explicit project rule.",
11
+ "requiresConfirmation": true,
12
+ "status": "candidate"
13
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "workspace": "fixture-workspace",
3
+ "rules": [
4
+ {
5
+ "id": "git.primary-branch",
6
+ "value": "main",
7
+ "source": "ForgeRail portable default",
8
+ "precedence": 6,
9
+ "status": "default"
10
+ },
11
+ {
12
+ "id": "git.primary-branch",
13
+ "value": "release",
14
+ "source": "AGENTS.md",
15
+ "precedence": 2,
16
+ "status": "confirmed"
17
+ }
18
+ ],
19
+ "packs": [
20
+ {
21
+ "id": "workspace-health-review",
22
+ "state": "available",
23
+ "reason": "Installed but irrelevant to the current task."
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "workspace": "fixture-workspace",
3
+ "rules": [
4
+ {
5
+ "id": "release.test-branch",
6
+ "value": "release",
7
+ "source": "AGENTS.md",
8
+ "precedence": 2,
9
+ "status": "confirmed"
10
+ },
11
+ {
12
+ "id": "release.test-branch",
13
+ "value": "develop",
14
+ "source": "docs/release.md",
15
+ "precedence": 2,
16
+ "status": "confirmed"
17
+ }
18
+ ],
19
+ "packs": []
20
+ }