forge-workflow 0.1.0-beta.5 → 0.1.0-beta.7

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 (268) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +48 -0
  3. package/CLAUDE.md +0 -12
  4. package/CODING_STANDARDS.md +72 -0
  5. package/bin/forge.js +12 -1
  6. package/docs/guides/MIGRATION.md +3 -3
  7. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
  8. package/docs/reference/INSTALL.md +4 -0
  9. package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
  10. package/docs/reference/RELEASE.md +4 -4
  11. package/docs/reference/github-accounts.md +134 -0
  12. package/docs/reference/shepherd.md +63 -13
  13. package/lib/adapters/pr-state-adapter.js +15 -2
  14. package/lib/base-remote.js +138 -0
  15. package/lib/beta5-compatibility-evidence.js +1093 -0
  16. package/lib/bun-lockfile-proof.js +413 -0
  17. package/lib/bun-workflow-pins.js +461 -0
  18. package/lib/capabilities/index.js +9 -0
  19. package/lib/capabilities/model.js +141 -0
  20. package/lib/capabilities/probes.js +347 -0
  21. package/lib/codex-skills.js +2 -2
  22. package/lib/commands/_manifest.js +1 -0
  23. package/lib/commands/_registry.js +48 -18
  24. package/lib/commands/clean.js +57 -1
  25. package/lib/commands/doctor.js +37 -6
  26. package/lib/commands/gate.js +197 -27
  27. package/lib/commands/github.js +215 -0
  28. package/lib/commands/hooks.js +54 -6
  29. package/lib/commands/memory.js +66 -2
  30. package/lib/commands/merge.js +720 -73
  31. package/lib/commands/plan.js +33 -2
  32. package/lib/commands/pr.js +2 -0
  33. package/lib/commands/preflight.js +10 -2
  34. package/lib/commands/push.js +120 -9
  35. package/lib/commands/recall.js +95 -61
  36. package/lib/commands/release.js +23 -2
  37. package/lib/commands/remember.js +28 -4
  38. package/lib/commands/serve.js +26 -9
  39. package/lib/commands/setup.js +132 -4
  40. package/lib/commands/shepherd.js +578 -72
  41. package/lib/commands/ship.js +15 -69
  42. package/lib/commands/skill.js +8 -0
  43. package/lib/commands/team.js +47 -8
  44. package/lib/commands/test.js +163 -4
  45. package/lib/commands/validate.js +78 -22
  46. package/lib/commands/worktree.js +155 -19
  47. package/lib/fixtures/beta5-corpus/v1/README.md +9 -0
  48. package/lib/fixtures/beta5-corpus/v1/contract/command-contract.json +26 -0
  49. package/lib/fixtures/beta5-corpus/v1/contract/package-contract.json +13 -0
  50. package/lib/fixtures/beta5-corpus/v1/contract/workflow-stage-matrix.json +8 -0
  51. package/lib/fixtures/beta5-corpus/v1/manifest.json +25 -0
  52. package/lib/fixtures/beta5-corpus/v1/state/comments.jsonl +1 -0
  53. package/lib/fixtures/beta5-corpus/v1/state/config.yaml +6 -0
  54. package/lib/fixtures/beta5-corpus/v1/state/dependencies.jsonl +1 -0
  55. package/lib/fixtures/beta5-corpus/v1/state/issues.jsonl +2 -0
  56. package/lib/fixtures/beta5-corpus/v1/state/kernel.sql +20 -0
  57. package/lib/forge-issues.js +78 -0
  58. package/lib/gate-events.js +98 -10
  59. package/lib/github-context.js +308 -0
  60. package/lib/global-flags.js +1 -0
  61. package/lib/hook-renderer.js +29 -1
  62. package/lib/issue-render.js +19 -0
  63. package/lib/kernel/broker.js +723 -31
  64. package/lib/kernel/claim-reconciler.js +238 -0
  65. package/lib/kernel/lease-enforcer.js +9 -4
  66. package/lib/kernel/legacy-claim-repair.js +442 -0
  67. package/lib/kernel/live-claim-projection.js +26 -0
  68. package/lib/kernel/migrations.js +118 -3
  69. package/lib/kernel/readiness-model.js +184 -12
  70. package/lib/kernel/schema.js +49 -1
  71. package/lib/kernel/sqlite-driver.js +3322 -183
  72. package/lib/kernel/taxonomy-validator.js +4 -1
  73. package/lib/kernel/windows-private-acl.js +239 -0
  74. package/lib/memory/hygiene.js +191 -0
  75. package/lib/memory/router.js +94 -27
  76. package/lib/memory/usage-evidence.js +4 -0
  77. package/lib/memory-digest.js +59 -0
  78. package/lib/merge-rules.js +135 -17
  79. package/lib/npm-publish-workflow.js +233 -40
  80. package/lib/package-root.js +2 -0
  81. package/lib/pr-monitor/auto-actions.js +169 -28
  82. package/lib/pr-monitor/differ.js +110 -4
  83. package/lib/pr-monitor/events.js +0 -0
  84. package/lib/pr-monitor/flow-monitor.js +1424 -0
  85. package/lib/pr-monitor/gather.js +251 -44
  86. package/lib/pr-monitor/journal.js +0 -37
  87. package/lib/pr-monitor/monitor.js +117 -10
  88. package/lib/pr-monitor/process-identity.js +117 -0
  89. package/lib/pr-monitor/reconcile-executor.js +1101 -625
  90. package/lib/pr-monitor/reconcile.js +0 -0
  91. package/lib/pr-monitor/render-summary.js +121 -24
  92. package/lib/pr-monitor/review-preflight.js +269 -0
  93. package/lib/pr-monitor/shepherd-lease.js +28 -19
  94. package/lib/pr-monitor/verdict.js +438 -0
  95. package/lib/pr-monitor/watch-lifecycle.js +144 -38
  96. package/lib/pr-monitor/watch-owner.js +1414 -0
  97. package/lib/pr-monitor/watch.js +129 -58
  98. package/lib/pr-shepherd.js +17 -3
  99. package/lib/project-memory.js +145 -3
  100. package/lib/protected-state-authority.js +799 -4
  101. package/lib/protected-state-surfaces.js +181 -3
  102. package/lib/release-readiness.js +2 -3
  103. package/lib/review-adapter.js +65 -0
  104. package/lib/skills-sync.js +65 -32
  105. package/lib/validation/risk-manifest.js +339 -0
  106. package/lib/validation-receipt.js +190 -0
  107. package/lib/workflow/enforce-stage.js +44 -0
  108. package/lib/workflow/plan-authority.js +225 -0
  109. package/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  110. package/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  111. package/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  112. package/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  113. package/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  114. package/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  115. package/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  116. package/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  117. package/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  118. package/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  119. package/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  120. package/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  121. package/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  122. package/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  123. package/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  124. package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  125. package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  126. package/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  127. package/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  128. package/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  129. package/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  130. package/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  131. package/node_modules/@forge/contracts/index.js +32 -0
  132. package/node_modules/@forge/contracts/package.json +35 -0
  133. package/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  134. package/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  135. package/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  136. package/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  137. package/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  138. package/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  139. package/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  140. package/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  141. package/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  142. package/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  143. package/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  144. package/node_modules/@forge/contracts/src/baseline.js +23 -0
  145. package/node_modules/@forge/contracts/src/canonical.js +151 -0
  146. package/node_modules/@forge/contracts/src/definitions.js +176 -0
  147. package/node_modules/@forge/contracts/src/identity.js +42 -0
  148. package/node_modules/@forge/contracts/src/schema.js +72 -0
  149. package/node_modules/@forge/contracts/src/validate.js +305 -0
  150. package/node_modules/@forge/flow/index.js +114 -0
  151. package/node_modules/@forge/flow/package.json +34 -0
  152. package/node_modules/@forge/flow/src/bounded-loop.js +415 -0
  153. package/node_modules/@forge/flow/src/efficiency-supervisor.js +89 -0
  154. package/node_modules/@forge/flow/src/executor.js +279 -0
  155. package/node_modules/@forge/flow/src/monitor-durability.js +419 -0
  156. package/node_modules/@forge/flow/src/monitor-runtime.js +460 -0
  157. package/node_modules/@forge/flow/src/process-lifecycle.js +469 -0
  158. package/node_modules/@forge/flow/src/skill-runtime.js +343 -0
  159. package/node_modules/@forge/memory/index.js +331 -0
  160. package/node_modules/@forge/memory/package.json +34 -0
  161. package/node_modules/@forge/memory/src/authority-provider.js +67 -0
  162. package/node_modules/@forge/memory/src/backend-registry.js +166 -0
  163. package/node_modules/@forge/memory/src/feedback-intake.js +243 -0
  164. package/node_modules/@forge/memory/src/pr-lifecycle-authority.js +946 -0
  165. package/node_modules/@forge/memory/src/usage-evidence.js +205 -0
  166. package/package.json +16 -4
  167. package/packages/flow/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  168. package/packages/flow/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  169. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  170. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  171. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  172. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  173. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  174. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  175. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  176. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  177. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  178. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  179. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  180. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  181. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  182. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  183. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  184. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  185. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  186. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  187. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  188. package/packages/flow/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  189. package/packages/flow/node_modules/@forge/contracts/index.js +32 -0
  190. package/packages/flow/node_modules/@forge/contracts/package.json +35 -0
  191. package/packages/flow/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  192. package/packages/flow/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  193. package/packages/flow/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  194. package/packages/flow/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  195. package/packages/flow/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  196. package/packages/flow/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  197. package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  198. package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  199. package/packages/flow/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  200. package/packages/flow/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  201. package/packages/flow/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  202. package/packages/flow/node_modules/@forge/contracts/src/baseline.js +23 -0
  203. package/packages/flow/node_modules/@forge/contracts/src/canonical.js +151 -0
  204. package/packages/flow/node_modules/@forge/contracts/src/definitions.js +176 -0
  205. package/packages/flow/node_modules/@forge/contracts/src/identity.js +42 -0
  206. package/packages/flow/node_modules/@forge/contracts/src/schema.js +72 -0
  207. package/packages/flow/node_modules/@forge/contracts/src/validate.js +305 -0
  208. package/packages/memory/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
  209. package/packages/memory/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
  210. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
  211. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
  212. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
  213. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
  214. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
  215. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
  216. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
  217. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
  218. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
  219. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
  220. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
  221. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
  222. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
  223. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
  224. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
  225. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
  226. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
  227. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
  228. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
  229. package/packages/memory/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
  230. package/packages/memory/node_modules/@forge/contracts/index.js +32 -0
  231. package/packages/memory/node_modules/@forge/contracts/package.json +35 -0
  232. package/packages/memory/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
  233. package/packages/memory/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
  234. package/packages/memory/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
  235. package/packages/memory/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
  236. package/packages/memory/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
  237. package/packages/memory/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
  238. package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
  239. package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
  240. package/packages/memory/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
  241. package/packages/memory/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
  242. package/packages/memory/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
  243. package/packages/memory/node_modules/@forge/contracts/src/baseline.js +23 -0
  244. package/packages/memory/node_modules/@forge/contracts/src/canonical.js +151 -0
  245. package/packages/memory/node_modules/@forge/contracts/src/definitions.js +176 -0
  246. package/packages/memory/node_modules/@forge/contracts/src/identity.js +42 -0
  247. package/packages/memory/node_modules/@forge/contracts/src/schema.js +72 -0
  248. package/packages/memory/node_modules/@forge/contracts/src/validate.js +305 -0
  249. package/scripts/commitlint.js +13 -15
  250. package/scripts/generate-risk-manifest.js +91 -0
  251. package/scripts/github-context-bridge.sh +10 -0
  252. package/scripts/legacy-claim-repair.js +145 -0
  253. package/scripts/lib/behavioral-eval-runtime.js +3 -2
  254. package/scripts/process-tree.js +14 -2
  255. package/scripts/protected-state-check.js +440 -17
  256. package/scripts/sync-agent-skills.js +333 -34
  257. package/scripts/test-full-suite.js +704 -18
  258. package/scripts/test-profile.js +13 -3
  259. package/scripts/test.js +96 -15
  260. package/skills/coverage.json +1 -0
  261. package/skills/review/SKILL.md +2 -0
  262. package/skills/review/evals/scorecard.json +2 -2
  263. package/skills/setup/SKILL.md +18 -0
  264. package/skills/setup/evals/scorecard.json +3 -3
  265. package/skills/shepherd/SKILL.md +19 -2
  266. package/skills/shepherd/evals/scorecard.json +3 -3
  267. package/skills/validate/SKILL.md +3 -0
  268. package/skills/validate/evals/scorecard.json +1 -1
@@ -31,6 +31,7 @@ const fs = require('node:fs');
31
31
  const path = require('node:path');
32
32
  const crypto = require('node:crypto');
33
33
  const { spawn } = require('node:child_process');
34
+ const { githubWorkerEnvironment } = require('../pr-monitor/watch-lifecycle');
34
35
 
35
36
  // Shared-machine hardening (single-instance lock, owner-only perms + audit,
36
37
  // hash-chained mutation journal). No dependency on serve.js, so a top-level
@@ -237,12 +238,21 @@ async function routeMutation(verb, args, projectRoot, deps = {}) {
237
238
  // Snapshot (GET /data.json) — regenerate on demand, debounced.
238
239
  // ---------------------------------------------------------------------------
239
240
 
240
- function defaultGenerate(projectRoot, dashboardDir) {
241
+ function defaultGenerate(projectRoot, dashboardDir, deps = {}) {
241
242
  return new Promise((resolve, reject) => {
242
243
  const script = path.join(dashboardDir, 'generate-snapshot.mjs');
243
- const child = spawn(process.execPath, [script], { cwd: projectRoot, stdio: 'ignore', windowsHide: true });
244
+ const env = githubWorkerEnvironment(projectRoot, deps);
245
+ let child;
246
+ try {
247
+ child = (deps.spawn || spawn)(process.execPath, [script], {
248
+ cwd: projectRoot, stdio: 'ignore', windowsHide: true, ...(env ? { env } : {}),
249
+ });
250
+ } catch {
251
+ reject(new Error('snapshot generation failed'));
252
+ return;
253
+ }
244
254
  const timer = setTimeout(() => { child.kill(); reject(new Error('snapshot generation timed out')); }, GEN_TIMEOUT_MS);
245
- child.on('error', (err) => { clearTimeout(timer); reject(err); });
255
+ child.on('error', () => { clearTimeout(timer); reject(new Error('snapshot generation failed')); });
246
256
  child.on('exit', (code) => {
247
257
  clearTimeout(timer);
248
258
  if (code === 0) resolve();
@@ -409,7 +419,7 @@ function buildContext(projectRoot, dashboardDir, token, deps = {}) {
409
419
  buffer: null,
410
420
  lastGen: 0,
411
421
  inFlight: null,
412
- generate: deps.generate || defaultGenerate,
422
+ generate: deps.generate || ((root, dir) => defaultGenerate(root, dir, deps)),
413
423
  },
414
424
  };
415
425
  }
@@ -438,13 +448,16 @@ function browserOpener() {
438
448
  return { file: '/usr/bin/xdg-open', args: [] };
439
449
  }
440
450
 
441
- function openBrowser(url) {
451
+ function openBrowser(url, projectRoot = process.cwd(), deps = {}) {
442
452
  try {
443
453
  const { file, args } = browserOpener();
454
+ const env = githubWorkerEnvironment(projectRoot, deps);
444
455
  // windowsHide keeps this detached opener from flashing a console window on
445
456
  // Windows (Node defaults windowsHide to false); background/detached spawns
446
457
  // must stay silent (issue 931e7924).
447
- const child = spawn(file, [...args, url], { detached: true, stdio: 'ignore', windowsHide: true });
458
+ const child = (deps.spawn || spawn)(file, [...args, url], {
459
+ detached: true, stdio: 'ignore', windowsHide: true, ...(env ? { env } : {}),
460
+ });
448
461
  // spawn() failures (e.g. the opener binary is missing) surface ASYNChronously
449
462
  // as an 'error' event, NOT via the try/catch — an unhandled one would crash
450
463
  // `forge serve`. Opening a browser is best-effort; swallow it (URL is printed).
@@ -480,7 +493,7 @@ function announceServe(server, token, options, resolve) {
480
493
  `\nforge serve — interactive dashboard (loopback only)\n ${url}\n`
481
494
  + ' Token dies with this process. Press Ctrl-C to stop.\n\n',
482
495
  );
483
- if (options.open) openBrowser(url);
496
+ if (options.open) openBrowser(url, options.projectRoot, options.deps);
484
497
  installServeShutdown(server, resolve, options.onClose);
485
498
  }
486
499
 
@@ -535,7 +548,7 @@ function verifyJournalAtStartup(projectRoot, warn = (m) => process.stderr.write(
535
548
  return result;
536
549
  }
537
550
 
538
- async function handler(args, flags = {}, projectRoot = process.cwd(), _opts = {}) {
551
+ async function handler(args, flags = {}, projectRoot = process.cwd(), opts = {}) {
539
552
  const { port, open } = parseServeArgs(args, flags);
540
553
  const dashboardDir = path.join(projectRoot, 'web', 'dashboard');
541
554
  if (!fs.existsSync(path.join(dashboardDir, 'index.html'))) {
@@ -558,10 +571,12 @@ async function handler(args, flags = {}, projectRoot = process.cwd(), _opts = {}
558
571
  verifyJournalAtStartup(projectRoot);
559
572
 
560
573
  const token = mintToken();
561
- const ctx = buildContext(projectRoot, dashboardDir, token);
574
+ const ctx = buildContext(projectRoot, dashboardDir, token, opts);
562
575
  const server = createServeServer(ctx);
563
576
  return startListening(server, port, token, {
564
577
  open,
578
+ projectRoot,
579
+ deps: opts,
565
580
  onClose: () => security.releaseLock(projectRoot),
566
581
  });
567
582
  }
@@ -575,6 +590,8 @@ module.exports = {
575
590
  routeMutation,
576
591
  parseServeArgs,
577
592
  buildContext,
593
+ defaultGenerate,
594
+ openBrowser,
578
595
  createServeServer,
579
596
  verifyToken,
580
597
  isLoopbackRequest,
@@ -13,7 +13,7 @@ const fs = require('node:fs');
13
13
  const os = require('node:os');
14
14
  const path = require('node:path');
15
15
  const readline = require('node:readline');
16
- const { execSync } = require('node:child_process');
16
+ const { execFileSync, execSync } = require('node:child_process');
17
17
 
18
18
  // Compute packageDir relative to this file (lib/commands/setup.js -> project root).
19
19
  // packageDir stays a runtime value for asset copying; requires use static
@@ -91,6 +91,10 @@ const initCommand = require('./init');
91
91
  const fileUtils = require('../file-utils');
92
92
  const detectionUtils = require('../detection-utils');
93
93
  const { detectHusky, migrateHusky } = require('../husky-migration');
94
+ const {
95
+ issueGeneratedHarnessClaudeAuthorization,
96
+ completeGeneratedHarnessClaudeAuthorization,
97
+ } = require('../protected-state-authority');
94
98
 
95
99
  // --- Module-level state (deferred from ForgeContext migration) ---
96
100
  // Follow-up tracked in forge-vi7v: migrate setup state to a ForgeContext instance passed via handler args.
@@ -163,6 +167,92 @@ function safeExec(cmd, opts = {}) {
163
167
  }
164
168
  }
165
169
 
170
+ /**
171
+ * Resolve the current source revision without passing Git arguments through a shell.
172
+ * @param {string} cwd - Repository working directory
173
+ * @param {Function} commandRunner - execFileSync-compatible runner for tests
174
+ * @returns {string} Full source HEAD, or empty when Git cannot resolve it
175
+ */
176
+ function resolveCurrentSourceHead(cwd = projectRoot, commandRunner = execFileSync) {
177
+ try {
178
+ return commandRunner('git', ['rev-parse', '--verify', 'HEAD^{commit}'], {
179
+ cwd,
180
+ stdio: 'pipe',
181
+ }).toString().trim();
182
+ } catch (_e) { // NOSONAR — fail closed when the source revision is unavailable
183
+ return '';
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Normalize Git's Windows checkout line endings before authorizing the exact
189
+ * root CLAUDE.md pointer bytes.
190
+ * @param {Buffer|string} content - Existing CLAUDE.md bytes
191
+ * @returns {Buffer} LF-normalized content
192
+ */
193
+ function normalizeClaudePointerContent(content) {
194
+ return Buffer.from(String(content || '').replace(/\r\n/g, '\n'));
195
+ }
196
+
197
+ function readCleanClaudePointerWorktreeContent(cwd, commandRunner) {
198
+ const tempIndexDir = fs.mkdtempSync(path.join(os.tmpdir(), 'forge-claude-index-'));
199
+ const tempIndex = path.join(tempIndexDir, 'index');
200
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
201
+ try {
202
+ commandRunner('git', ['read-tree', 'HEAD'], {
203
+ cwd,
204
+ env,
205
+ stdio: 'pipe',
206
+ });
207
+ commandRunner('git', ['add', '--', 'CLAUDE.md'], {
208
+ cwd,
209
+ env,
210
+ stdio: 'pipe',
211
+ });
212
+ return commandRunner('git', ['show', ':CLAUDE.md'], {
213
+ cwd,
214
+ env,
215
+ encoding: null,
216
+ stdio: 'pipe',
217
+ });
218
+ } finally {
219
+ fs.rmSync(tempIndexDir, { recursive: true, force: true });
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Read the exact staged CLAUDE.md bytes that the protected-state hook will hash.
225
+ * @param {string} cwd - Repository working directory
226
+ * @param {Function} commandRunner - execFileSync-compatible runner for tests
227
+ * @returns {Buffer} Staged bytes, or the worktree bytes when the file is untracked
228
+ */
229
+ function readStagedClaudePointerContent(cwd = projectRoot, commandRunner = execFileSync) {
230
+ try {
231
+ const stagedContent = commandRunner('git', ['show', ':CLAUDE.md'], {
232
+ cwd,
233
+ encoding: null,
234
+ stdio: 'pipe',
235
+ });
236
+ try {
237
+ return readCleanClaudePointerWorktreeContent(cwd, commandRunner);
238
+ } catch (_worktreeError) {
239
+ return stagedContent;
240
+ }
241
+ } catch (showError) {
242
+ try {
243
+ commandRunner('git', ['ls-files', '--error-unmatch', '--', 'CLAUDE.md'], {
244
+ cwd,
245
+ encoding: 'utf8',
246
+ stdio: 'pipe',
247
+ });
248
+ throw showError;
249
+ } catch (trackedError) {
250
+ if (trackedError?.status !== 1) throw showError;
251
+ }
252
+ return readCleanClaudePointerWorktreeContent(cwd, commandRunner);
253
+ }
254
+ }
255
+
166
256
  /**
167
257
  * Detect package manager
168
258
  */
@@ -1649,6 +1739,13 @@ function ensureDirWithNote(dir, purpose) { // eslint-disable-line no-unused-vars
1649
1739
 
1650
1740
  // Setup core documentation and directories
1651
1741
  function setupCoreDocs() {
1742
+ // Review skills reference this root contract in every installed repository.
1743
+ // It is a consumer-owned extension point after creation, so setup never overwrites it.
1744
+ const codingStandardsDest = path.join(projectRoot, 'CODING_STANDARDS.md');
1745
+ if (!fs.existsSync(codingStandardsDest)) {
1746
+ copyFile(path.join(getPackageRoot(packageDir), 'CODING_STANDARDS.md'), 'CODING_STANDARDS.md');
1747
+ }
1748
+
1652
1749
  // docs/planning/ and docs/research/ are created lazily on first use
1653
1750
  // by /plan Phase 1 and Phase 2 respectively, via ensureDirWithNote().
1654
1751
  // TEMPLATE.md and PROGRESS.md are also deferred to first use.
@@ -2053,7 +2150,7 @@ function setupCursorHooksConfig() {
2053
2150
 
2054
2151
  // Helper: Create agent link file
2055
2152
  // When symlinkOnly is true (--symlink flag), skip copy fallback
2056
- function createAgentLinkFile(agent, symlinkOnly = false) {
2153
+ async function createAgentLinkFile(agent, symlinkOnly = false) {
2057
2154
  if (!shouldLinkAgentsMd(agent)) return;
2058
2155
 
2059
2156
  const result = createSymlinkOrCopy('AGENTS.md', agent.linkFile, { symlinkOnly });
@@ -2061,6 +2158,33 @@ function createAgentLinkFile(agent, symlinkOnly = false) {
2061
2158
  const action = result === 'linked' ? 'Linked' : result === 'copied' ? 'Copied' : 'Preserved import';
2062
2159
  console.log(` ${action}: ${agent.linkFile}`);
2063
2160
  }
2161
+ if (agent.linkFile !== 'CLAUDE.md' || result !== 'existing-import') return;
2162
+
2163
+ try {
2164
+ const sourceHead = resolveCurrentSourceHead();
2165
+ const content = readStagedClaudePointerContent();
2166
+ const actor = process.env.FORGE_PROTECTED_STATE_ACTOR || process.env.FORGE_ACTOR || process.env.USER || process.env.USERNAME || 'unknown';
2167
+ const authorization = await issueGeneratedHarnessClaudeAuthorization(projectRoot, {
2168
+ actor,
2169
+ content,
2170
+ sourceHead,
2171
+ });
2172
+ if (!authorization.success) {
2173
+ console.warn(` Warning: Could not authorize existing CLAUDE.md import: ${authorization.error}`);
2174
+ return;
2175
+ }
2176
+ const completion = await completeGeneratedHarnessClaudeAuthorization(projectRoot, {
2177
+ actor,
2178
+ capabilityId: authorization.capabilityId,
2179
+ content,
2180
+ sourceHead,
2181
+ });
2182
+ if (!completion.success) {
2183
+ console.warn(` Warning: Could not complete CLAUDE.md import authorization: ${completion.error}`);
2184
+ }
2185
+ } catch (error) {
2186
+ console.warn(` Warning: Could not record CLAUDE.md import authorization: ${error.message}`);
2187
+ }
2064
2188
  }
2065
2189
 
2066
2190
  // Setup specific agent
@@ -2117,7 +2241,7 @@ async function setupAgent(agentKey, skipFiles = {}, options = {}) {
2117
2241
  }
2118
2242
 
2119
2243
  // Create link file (SYMLINK_ONLY = --symlink flag disables copy fallback)
2120
- createAgentLinkFile(agent, SYMLINK_ONLY);
2244
+ await createAgentLinkFile(agent, SYMLINK_ONLY);
2121
2245
  }
2122
2246
 
2123
2247
 
@@ -3927,8 +4051,9 @@ function dryRunSetup(agents, options = {}) { // NOSONAR — Extracted as-is from
3927
4051
  }
3928
4052
  }
3929
4053
 
3930
- // AGENTS.md
4054
+ // Root policy files
3931
4055
  addFileAction('AGENTS.md', 'Copy workflow documentation');
4056
+ addFileAction('CODING_STANDARDS.md', 'Install Forge coding standards');
3932
4057
 
3933
4058
  // Per-agent planned actions
3934
4059
  for (const agentKey of agents) {
@@ -4526,6 +4651,9 @@ module.exports = {
4526
4651
  repairRuntimeReadiness,
4527
4652
  _showBanner: showBanner,
4528
4653
  backupAndRemoveLegacyCursorRules,
4654
+ resolveCurrentSourceHead,
4655
+ normalizeClaudePointerContent,
4656
+ readStagedClaudePointerContent,
4529
4657
 
4530
4658
  // State accessors for testing
4531
4659
  _getState: () => ({ projectRoot, FORCE_MODE, VERBOSE_MODE, NON_INTERACTIVE, SYMLINK_ONLY, PKG_MANAGER }),