forge-workflow 0.1.0-beta.4 → 0.1.0-beta.6

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 (196) hide show
  1. package/AGENTS.md +18 -7
  2. package/CHANGELOG.md +79 -1
  3. package/CLAUDE.md +0 -12
  4. package/CODING_STANDARDS.md +72 -0
  5. package/README.md +6 -2
  6. package/bin/forge-cmd.js +20 -0
  7. package/bin/forge.js +28 -375
  8. package/docs/INDEX.md +1 -1
  9. package/docs/guides/BEADS_GITHUB_SYNC.md +2 -31
  10. package/docs/guides/MIGRATION.md +4 -4
  11. package/docs/guides/SETUP.md +16 -16
  12. package/docs/reference/COMMANDS.md +8 -5
  13. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
  14. package/docs/reference/INSIGHTS_RECAP.md +9 -20
  15. package/docs/reference/INSTALL.md +4 -0
  16. package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
  17. package/docs/reference/RELEASE.md +5 -3
  18. package/docs/reference/TOOLCHAIN.md +8 -0
  19. package/docs/reference/github-accounts.md +134 -0
  20. package/docs/reference/protected-state-surfaces.md +4 -4
  21. package/docs/reference/shepherd.md +114 -35
  22. package/lefthook.yml +12 -0
  23. package/lib/activation/ensure-forge-home.js +33 -15
  24. package/lib/adapters/pr-state-adapter.js +359 -144
  25. package/lib/audit-evidence.js +71 -110
  26. package/lib/base-remote.js +138 -0
  27. package/lib/beta5-compatibility-evidence.js +1093 -0
  28. package/lib/bun-lockfile-proof.js +413 -0
  29. package/lib/bun-workflow-pins.js +461 -0
  30. package/lib/capabilities/index.js +9 -0
  31. package/lib/capabilities/model.js +141 -0
  32. package/lib/capabilities/probes.js +347 -0
  33. package/lib/capped-jsonl-log.js +236 -0
  34. package/lib/codex-skills.js +2 -2
  35. package/lib/commands/_manifest.js +1 -0
  36. package/lib/commands/_registry.js +50 -20
  37. package/lib/commands/clean.js +252 -32
  38. package/lib/commands/dev.js +4 -33
  39. package/lib/commands/doctor.js +37 -6
  40. package/lib/commands/gate.js +197 -27
  41. package/lib/commands/github.js +215 -0
  42. package/lib/commands/hooks.js +276 -30
  43. package/lib/commands/insights.js +8 -3
  44. package/lib/commands/memory.js +66 -2
  45. package/lib/commands/merge.js +1265 -58
  46. package/lib/commands/plan.js +33 -2
  47. package/lib/commands/pr.js +3 -1
  48. package/lib/commands/preflight.js +21 -4
  49. package/lib/commands/prime.js +21 -8
  50. package/lib/commands/push.js +146 -54
  51. package/lib/commands/recall.js +127 -49
  52. package/lib/commands/recap.js +6 -1
  53. package/lib/commands/release.js +39 -3
  54. package/lib/commands/remember.js +28 -4
  55. package/lib/commands/serve.js +26 -9
  56. package/lib/commands/setup.js +323 -98
  57. package/lib/commands/shepherd.js +591 -73
  58. package/lib/commands/ship.js +36 -91
  59. package/lib/commands/skill.js +127 -11
  60. package/lib/commands/status.js +17 -1
  61. package/lib/commands/team.js +47 -8
  62. package/lib/commands/test.js +187 -38
  63. package/lib/commands/validate.js +65 -21
  64. package/lib/commands/worktree.js +359 -45
  65. package/lib/core/runtime-graph.js +1 -1
  66. package/lib/doc-assertions.js +297 -0
  67. package/lib/existing-tdd-gate.js +253 -0
  68. package/lib/fixtures/beta5-corpus/v1/README.md +9 -0
  69. package/lib/fixtures/beta5-corpus/v1/contract/command-contract.json +26 -0
  70. package/lib/fixtures/beta5-corpus/v1/contract/package-contract.json +13 -0
  71. package/lib/fixtures/beta5-corpus/v1/contract/workflow-stage-matrix.json +8 -0
  72. package/lib/fixtures/beta5-corpus/v1/manifest.json +25 -0
  73. package/lib/fixtures/beta5-corpus/v1/state/comments.jsonl +1 -0
  74. package/lib/fixtures/beta5-corpus/v1/state/config.yaml +6 -0
  75. package/lib/fixtures/beta5-corpus/v1/state/dependencies.jsonl +1 -0
  76. package/lib/fixtures/beta5-corpus/v1/state/issues.jsonl +2 -0
  77. package/lib/fixtures/beta5-corpus/v1/state/kernel.sql +20 -0
  78. package/lib/forge-context.js +1 -4
  79. package/lib/forge-issues.js +134 -32
  80. package/lib/gate-events.js +98 -10
  81. package/lib/git-defaults.js +56 -0
  82. package/lib/github-context.js +308 -0
  83. package/lib/global-flags.js +1 -0
  84. package/lib/harness-capability-matrix.js +3 -3
  85. package/lib/hook-renderer.js +122 -5
  86. package/lib/insights.js +96 -80
  87. package/lib/issue-render.js +19 -0
  88. package/lib/kernel/backing-issue.js +14 -2
  89. package/lib/kernel/broker.js +739 -31
  90. package/lib/kernel/claim-reconciler.js +238 -0
  91. package/lib/kernel/cli-broker-factory.js +12 -1
  92. package/lib/kernel/close-on-merge.js +154 -0
  93. package/lib/kernel/fs-class.js +42 -25
  94. package/lib/kernel/lease-enforcer.js +9 -4
  95. package/lib/kernel/legacy-claim-repair.js +442 -0
  96. package/lib/kernel/live-claim-projection.js +26 -0
  97. package/lib/kernel/migrations.js +118 -3
  98. package/lib/kernel/readiness-model.js +184 -12
  99. package/lib/kernel/schema.js +49 -1
  100. package/lib/kernel/sqlite-driver.js +3435 -172
  101. package/lib/kernel/taxonomy-validator.js +4 -1
  102. package/lib/kernel/windows-private-acl.js +239 -0
  103. package/lib/lefthook-wiring.js +21 -1
  104. package/lib/memory/hygiene.js +191 -0
  105. package/lib/memory/router.js +110 -28
  106. package/lib/memory/usage-evidence.js +4 -0
  107. package/lib/memory-digest.js +106 -15
  108. package/lib/memory-recall-events.js +145 -0
  109. package/lib/memory-recall.js +71 -10
  110. package/lib/merge-rules.js +143 -21
  111. package/lib/npm-publish-workflow.js +465 -0
  112. package/lib/orientation.js +68 -43
  113. package/lib/package-root.js +2 -0
  114. package/lib/plugin-catalog.js +14 -4
  115. package/lib/pr-bundle.js +5 -6
  116. package/lib/pr-monitor/auto-actions.js +169 -28
  117. package/lib/pr-monitor/differ.js +110 -4
  118. package/lib/pr-monitor/events.js +0 -0
  119. package/lib/pr-monitor/flow-monitor.js +1424 -0
  120. package/lib/pr-monitor/gather.js +251 -44
  121. package/lib/pr-monitor/journal.js +18 -39
  122. package/lib/pr-monitor/monitor.js +117 -10
  123. package/lib/pr-monitor/process-identity.js +117 -0
  124. package/lib/pr-monitor/reconcile-executor.js +1129 -470
  125. package/lib/pr-monitor/reconcile.js +0 -0
  126. package/lib/pr-monitor/render-summary.js +293 -0
  127. package/lib/pr-monitor/review-preflight.js +269 -0
  128. package/lib/pr-monitor/shepherd-lease.js +38 -20
  129. package/lib/pr-monitor/verdict.js +438 -0
  130. package/lib/pr-monitor/watch-lifecycle.js +145 -27
  131. package/lib/pr-monitor/watch-owner.js +1414 -0
  132. package/lib/pr-monitor/watch.js +129 -58
  133. package/lib/pr-pull.js +33 -14
  134. package/lib/pr-shepherd.js +51 -11
  135. package/lib/preflight/gates.js +65 -18
  136. package/lib/preflight/runner.js +5 -0
  137. package/lib/project-memory.js +178 -4
  138. package/lib/protected-state-authority.js +1100 -0
  139. package/lib/protected-state-surfaces.js +243 -45
  140. package/lib/release-readiness.js +53 -7
  141. package/lib/review-adapter.js +65 -0
  142. package/lib/shell-utils.js +1 -1
  143. package/lib/skills-sync.js +71 -35
  144. package/lib/smart-merge.js +28 -4
  145. package/lib/symlink-utils.js +74 -26
  146. package/lib/upgrade-safety.js +39 -0
  147. package/lib/using-forge.js +19 -6
  148. package/lib/validation/risk-manifest.js +339 -0
  149. package/lib/workflow/enforce-stage.js +44 -0
  150. package/lib/workflow/plan-authority.js +225 -0
  151. package/package.json +12 -9
  152. package/scripts/commitlint.js +13 -15
  153. package/scripts/doc-asserting-tests.js +158 -0
  154. package/scripts/generate-risk-manifest.js +91 -0
  155. package/scripts/github-context-bridge.sh +10 -0
  156. package/scripts/legacy-claim-repair.js +145 -0
  157. package/scripts/lib/behavioral-eval-runner.js +310 -0
  158. package/scripts/lib/behavioral-eval-runtime.js +457 -0
  159. package/scripts/lib/eval-evidence.js +328 -0
  160. package/scripts/lib/eval-runner.js +81 -41
  161. package/scripts/lib/immutable-eval-corpus.js +309 -0
  162. package/scripts/lib/promotion-evidence-loader.js +94 -0
  163. package/scripts/lib/promotion-scorecard.js +314 -0
  164. package/scripts/npm-release-receipt.js +134 -0
  165. package/scripts/process-tree.js +773 -0
  166. package/scripts/protected-state-check.js +479 -31
  167. package/scripts/run-command-eval.js +29 -1
  168. package/scripts/sync-agent-skills.js +333 -34
  169. package/scripts/sync-d20-audit.js +172 -0
  170. package/scripts/test-full-suite.js +935 -37
  171. package/scripts/test-profile.js +13 -3
  172. package/scripts/test.js +271 -57
  173. package/skills/coverage.json +1 -0
  174. package/skills/review/SKILL.md +6 -11
  175. package/skills/review/evals/scorecard.json +4 -4
  176. package/skills/rollback/SKILL.md +4 -11
  177. package/skills/rollback/evals/scorecard.json +3 -3
  178. package/skills/setup/SKILL.md +18 -0
  179. package/skills/setup/evals/scorecard.json +3 -3
  180. package/skills/shepherd/SKILL.md +39 -16
  181. package/skills/shepherd/evals/scorecard.json +4 -4
  182. package/skills/ship/SKILL.md +4 -12
  183. package/skills/ship/evals/scorecard.json +3 -3
  184. package/skills/validate/SKILL.md +3 -0
  185. package/skills/validate/evals/scorecard.json +1 -1
  186. package/skills/worktree/SKILL.md +6 -1
  187. package/skills/worktree/evals/scorecard.json +2 -2
  188. package/lib/beads-setup.js +0 -538
  189. package/lib/beads-sync-scaffold.js +0 -189
  190. package/lib/pat-setup.js +0 -207
  191. package/lib/pr-monitor/render-sticky.js +0 -206
  192. package/lib/pr-monitor/upsert-sticky.js +0 -169
  193. package/scripts/beads-context.sh +0 -577
  194. package/scripts/beads-migrate-to-dolt.sh +0 -7
  195. package/scripts/beads-upgrade-smoke.sh +0 -284
  196. package/scripts/lib/beads-migrate-to-dolt.mjs +0 -503
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared process-identity probe for shepherd ownership.
5
+ *
6
+ * A bare "this PID exists" answer cannot tell a live owner apart from an
7
+ * unrelated process that inherited the number after the owner died. Every place
8
+ * that decides whether an owner row's controller or watcher is still running
9
+ * must therefore compare the observed process start time against a marker the
10
+ * owner itself wrote: a process that booted materially after that marker cannot
11
+ * be the process that wrote it, so the PID was reused.
12
+ *
13
+ * @module pr-monitor/process-identity
14
+ */
15
+
16
+ const fs = require('node:fs');
17
+
18
+ // Owners stamp their marker while running, so an observed process start later
19
+ // than the marker by more than this slack proves the PID was reused.
20
+ const PID_START_SKEW_MS = 60_000;
21
+ const LINUX_USER_HZ = 100;
22
+
23
+ /**
24
+ * Epoch-ms start time of `pid`, or null when this platform cannot answer. Null is
25
+ * "unknown", never "reused" — callers must not weaken liveness on an unknown.
26
+ * Linux reads /proc without spawning; other platforms have no dependency-free probe
27
+ * here, so they return null and can inject `pidStartedAt` instead.
28
+ *
29
+ * @param {number} pid
30
+ * @returns {number|null}
31
+ */
32
+ function defaultPidStartedAt(pid) {
33
+ if (!Number.isSafeInteger(pid) || pid <= 0) return null;
34
+ if (process.platform !== 'linux') return null;
35
+ try {
36
+ const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
37
+ // Fields after the (possibly space-bearing) comm field; starttime is overall
38
+ // field 22, i.e. index 19 counting from `state`.
39
+ const fields = stat.slice(stat.lastIndexOf(')') + 1).trim().split(/\s+/);
40
+ const ticks = Number(fields[19]);
41
+ const uptimeSeconds = Number(String(fs.readFileSync('/proc/uptime', 'utf8')).trim().split(/\s+/)[0]);
42
+ if (!Number.isFinite(ticks) || !Number.isFinite(uptimeSeconds)) return null;
43
+ return Math.round(Date.now() - uptimeSeconds * 1000 + (ticks / LINUX_USER_HZ) * 1000);
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Normalize an identity marker to epoch ms.
51
+ *
52
+ * @param {string|number|null|undefined} value ISO timestamp, epoch ms, or numeric string
53
+ * @returns {number|null|typeof NaN} epoch ms, null when no marker was supplied, NaN when unparsable
54
+ */
55
+ function markerMs(value) {
56
+ if (value == null) return null;
57
+ if (typeof value === 'number') return Number.isFinite(value) ? value : NaN;
58
+ if (typeof value !== 'string') return NaN;
59
+ const trimmed = value.trim();
60
+ if (trimmed === '') return NaN;
61
+ if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) return Number(trimmed);
62
+ const parsed = Date.parse(trimmed);
63
+ return Number.isFinite(parsed) ? parsed : NaN;
64
+ }
65
+
66
+ /**
67
+ * Classify a PID against the identity marker its owner wrote.
68
+ *
69
+ * - `dead`: the PID is gone.
70
+ * - `reused`: the PID exists but its process booted after the marker, so it is a
71
+ * different process and the owner is gone.
72
+ * - `alive`: the PID exists and nothing disproves its identity.
73
+ * - `unknown`: liveness itself could not be established (non-boolean probe answer
74
+ * or an unusable PID). Callers decide how to fail; this is never "reused".
75
+ *
76
+ * Identity needs BOTH halves from the same source: a caller that injects
77
+ * `isPidAlive` for fabricated PIDs must inject `pidStartedAt` too, or there is
78
+ * nothing honest to compare and the answer is `alive`. A missing marker or an
79
+ * unavailable start-time probe (today: every non-Linux platform) is `alive` for
80
+ * the same reason, keeping behaviour exactly as it was before identity
81
+ * verification existed. A supplied but unparsable marker is `unknown`, never a
82
+ * silent `alive`. `isPidAlive` rejections propagate to the caller, matching the
83
+ * previous bare probes.
84
+ *
85
+ * @param {{pid: number, startedAt: string|number|null, isPidAlive: Function,
86
+ * pidStartedAt?: Function}} input
87
+ * @returns {Promise<'alive'|'dead'|'reused'|'unknown'>}
88
+ */
89
+ async function processIdentityAlive({ pid, startedAt, isPidAlive, pidStartedAt } = {}) {
90
+ const numericPid = Number(pid);
91
+ if (!Number.isSafeInteger(numericPid) || numericPid <= 0) return 'unknown';
92
+ if (typeof isPidAlive !== 'function') return 'unknown';
93
+ const alive = await isPidAlive(numericPid);
94
+ if (alive === false) return 'dead';
95
+ if (alive !== true) return 'unknown';
96
+ // Never fall back to the built-in probe here: only the caller knows whether its
97
+ // liveness answer and a start-time probe describe the same process.
98
+ if (typeof pidStartedAt !== 'function') return 'alive';
99
+ const marker = markerMs(startedAt);
100
+ if (marker === null) return 'alive';
101
+ if (!Number.isFinite(marker)) return 'unknown';
102
+ let observed;
103
+ try {
104
+ observed = await pidStartedAt(numericPid);
105
+ } catch {
106
+ return 'alive';
107
+ }
108
+ const observedMs = Number(observed);
109
+ if (!Number.isFinite(observedMs)) return 'alive';
110
+ return observedMs > marker + PID_START_SKEW_MS ? 'reused' : 'alive';
111
+ }
112
+
113
+ module.exports = {
114
+ PID_START_SKEW_MS,
115
+ defaultPidStartedAt,
116
+ processIdentityAlive,
117
+ };