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.
- package/AGENTS.md +18 -7
- package/CHANGELOG.md +79 -1
- package/CLAUDE.md +0 -12
- package/CODING_STANDARDS.md +72 -0
- package/README.md +6 -2
- package/bin/forge-cmd.js +20 -0
- package/bin/forge.js +28 -375
- package/docs/INDEX.md +1 -1
- package/docs/guides/BEADS_GITHUB_SYNC.md +2 -31
- package/docs/guides/MIGRATION.md +4 -4
- package/docs/guides/SETUP.md +16 -16
- package/docs/reference/COMMANDS.md +8 -5
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
- package/docs/reference/INSIGHTS_RECAP.md +9 -20
- package/docs/reference/INSTALL.md +4 -0
- package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
- package/docs/reference/RELEASE.md +5 -3
- package/docs/reference/TOOLCHAIN.md +8 -0
- package/docs/reference/github-accounts.md +134 -0
- package/docs/reference/protected-state-surfaces.md +4 -4
- package/docs/reference/shepherd.md +114 -35
- package/lefthook.yml +12 -0
- package/lib/activation/ensure-forge-home.js +33 -15
- package/lib/adapters/pr-state-adapter.js +359 -144
- package/lib/audit-evidence.js +71 -110
- package/lib/base-remote.js +138 -0
- package/lib/beta5-compatibility-evidence.js +1093 -0
- package/lib/bun-lockfile-proof.js +413 -0
- package/lib/bun-workflow-pins.js +461 -0
- package/lib/capabilities/index.js +9 -0
- package/lib/capabilities/model.js +141 -0
- package/lib/capabilities/probes.js +347 -0
- package/lib/capped-jsonl-log.js +236 -0
- package/lib/codex-skills.js +2 -2
- package/lib/commands/_manifest.js +1 -0
- package/lib/commands/_registry.js +50 -20
- package/lib/commands/clean.js +252 -32
- package/lib/commands/dev.js +4 -33
- package/lib/commands/doctor.js +37 -6
- package/lib/commands/gate.js +197 -27
- package/lib/commands/github.js +215 -0
- package/lib/commands/hooks.js +276 -30
- package/lib/commands/insights.js +8 -3
- package/lib/commands/memory.js +66 -2
- package/lib/commands/merge.js +1265 -58
- package/lib/commands/plan.js +33 -2
- package/lib/commands/pr.js +3 -1
- package/lib/commands/preflight.js +21 -4
- package/lib/commands/prime.js +21 -8
- package/lib/commands/push.js +146 -54
- package/lib/commands/recall.js +127 -49
- package/lib/commands/recap.js +6 -1
- package/lib/commands/release.js +39 -3
- package/lib/commands/remember.js +28 -4
- package/lib/commands/serve.js +26 -9
- package/lib/commands/setup.js +323 -98
- package/lib/commands/shepherd.js +591 -73
- package/lib/commands/ship.js +36 -91
- package/lib/commands/skill.js +127 -11
- package/lib/commands/status.js +17 -1
- package/lib/commands/team.js +47 -8
- package/lib/commands/test.js +187 -38
- package/lib/commands/validate.js +65 -21
- package/lib/commands/worktree.js +359 -45
- package/lib/core/runtime-graph.js +1 -1
- package/lib/doc-assertions.js +297 -0
- package/lib/existing-tdd-gate.js +253 -0
- package/lib/fixtures/beta5-corpus/v1/README.md +9 -0
- package/lib/fixtures/beta5-corpus/v1/contract/command-contract.json +26 -0
- package/lib/fixtures/beta5-corpus/v1/contract/package-contract.json +13 -0
- package/lib/fixtures/beta5-corpus/v1/contract/workflow-stage-matrix.json +8 -0
- package/lib/fixtures/beta5-corpus/v1/manifest.json +25 -0
- package/lib/fixtures/beta5-corpus/v1/state/comments.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/config.yaml +6 -0
- package/lib/fixtures/beta5-corpus/v1/state/dependencies.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/issues.jsonl +2 -0
- package/lib/fixtures/beta5-corpus/v1/state/kernel.sql +20 -0
- package/lib/forge-context.js +1 -4
- package/lib/forge-issues.js +134 -32
- package/lib/gate-events.js +98 -10
- package/lib/git-defaults.js +56 -0
- package/lib/github-context.js +308 -0
- package/lib/global-flags.js +1 -0
- package/lib/harness-capability-matrix.js +3 -3
- package/lib/hook-renderer.js +122 -5
- package/lib/insights.js +96 -80
- package/lib/issue-render.js +19 -0
- package/lib/kernel/backing-issue.js +14 -2
- package/lib/kernel/broker.js +739 -31
- package/lib/kernel/claim-reconciler.js +238 -0
- package/lib/kernel/cli-broker-factory.js +12 -1
- package/lib/kernel/close-on-merge.js +154 -0
- package/lib/kernel/fs-class.js +42 -25
- package/lib/kernel/lease-enforcer.js +9 -4
- package/lib/kernel/legacy-claim-repair.js +442 -0
- package/lib/kernel/live-claim-projection.js +26 -0
- package/lib/kernel/migrations.js +118 -3
- package/lib/kernel/readiness-model.js +184 -12
- package/lib/kernel/schema.js +49 -1
- package/lib/kernel/sqlite-driver.js +3435 -172
- package/lib/kernel/taxonomy-validator.js +4 -1
- package/lib/kernel/windows-private-acl.js +239 -0
- package/lib/lefthook-wiring.js +21 -1
- package/lib/memory/hygiene.js +191 -0
- package/lib/memory/router.js +110 -28
- package/lib/memory/usage-evidence.js +4 -0
- package/lib/memory-digest.js +106 -15
- package/lib/memory-recall-events.js +145 -0
- package/lib/memory-recall.js +71 -10
- package/lib/merge-rules.js +143 -21
- package/lib/npm-publish-workflow.js +465 -0
- package/lib/orientation.js +68 -43
- package/lib/package-root.js +2 -0
- package/lib/plugin-catalog.js +14 -4
- package/lib/pr-bundle.js +5 -6
- package/lib/pr-monitor/auto-actions.js +169 -28
- package/lib/pr-monitor/differ.js +110 -4
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/flow-monitor.js +1424 -0
- package/lib/pr-monitor/gather.js +251 -44
- package/lib/pr-monitor/journal.js +18 -39
- package/lib/pr-monitor/monitor.js +117 -10
- package/lib/pr-monitor/process-identity.js +117 -0
- package/lib/pr-monitor/reconcile-executor.js +1129 -470
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +293 -0
- package/lib/pr-monitor/review-preflight.js +269 -0
- package/lib/pr-monitor/shepherd-lease.js +38 -20
- package/lib/pr-monitor/verdict.js +438 -0
- package/lib/pr-monitor/watch-lifecycle.js +145 -27
- package/lib/pr-monitor/watch-owner.js +1414 -0
- package/lib/pr-monitor/watch.js +129 -58
- package/lib/pr-pull.js +33 -14
- package/lib/pr-shepherd.js +51 -11
- package/lib/preflight/gates.js +65 -18
- package/lib/preflight/runner.js +5 -0
- package/lib/project-memory.js +178 -4
- package/lib/protected-state-authority.js +1100 -0
- package/lib/protected-state-surfaces.js +243 -45
- package/lib/release-readiness.js +53 -7
- package/lib/review-adapter.js +65 -0
- package/lib/shell-utils.js +1 -1
- package/lib/skills-sync.js +71 -35
- package/lib/smart-merge.js +28 -4
- package/lib/symlink-utils.js +74 -26
- package/lib/upgrade-safety.js +39 -0
- package/lib/using-forge.js +19 -6
- package/lib/validation/risk-manifest.js +339 -0
- package/lib/workflow/enforce-stage.js +44 -0
- package/lib/workflow/plan-authority.js +225 -0
- package/package.json +12 -9
- package/scripts/commitlint.js +13 -15
- package/scripts/doc-asserting-tests.js +158 -0
- package/scripts/generate-risk-manifest.js +91 -0
- package/scripts/github-context-bridge.sh +10 -0
- package/scripts/legacy-claim-repair.js +145 -0
- package/scripts/lib/behavioral-eval-runner.js +310 -0
- package/scripts/lib/behavioral-eval-runtime.js +457 -0
- package/scripts/lib/eval-evidence.js +328 -0
- package/scripts/lib/eval-runner.js +81 -41
- package/scripts/lib/immutable-eval-corpus.js +309 -0
- package/scripts/lib/promotion-evidence-loader.js +94 -0
- package/scripts/lib/promotion-scorecard.js +314 -0
- package/scripts/npm-release-receipt.js +134 -0
- package/scripts/process-tree.js +773 -0
- package/scripts/protected-state-check.js +479 -31
- package/scripts/run-command-eval.js +29 -1
- package/scripts/sync-agent-skills.js +333 -34
- package/scripts/sync-d20-audit.js +172 -0
- package/scripts/test-full-suite.js +935 -37
- package/scripts/test-profile.js +13 -3
- package/scripts/test.js +271 -57
- package/skills/coverage.json +1 -0
- package/skills/review/SKILL.md +6 -11
- package/skills/review/evals/scorecard.json +4 -4
- package/skills/rollback/SKILL.md +4 -11
- package/skills/rollback/evals/scorecard.json +3 -3
- package/skills/setup/SKILL.md +18 -0
- package/skills/setup/evals/scorecard.json +3 -3
- package/skills/shepherd/SKILL.md +39 -16
- package/skills/shepherd/evals/scorecard.json +4 -4
- package/skills/ship/SKILL.md +4 -12
- package/skills/ship/evals/scorecard.json +3 -3
- package/skills/validate/SKILL.md +3 -0
- package/skills/validate/evals/scorecard.json +1 -1
- package/skills/worktree/SKILL.md +6 -1
- package/skills/worktree/evals/scorecard.json +2 -2
- package/lib/beads-setup.js +0 -538
- package/lib/beads-sync-scaffold.js +0 -189
- package/lib/pat-setup.js +0 -207
- package/lib/pr-monitor/render-sticky.js +0 -206
- package/lib/pr-monitor/upsert-sticky.js +0 -169
- package/scripts/beads-context.sh +0 -577
- package/scripts/beads-migrate-to-dolt.sh +0 -7
- package/scripts/beads-upgrade-smoke.sh +0 -284
- 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
|
+
};
|