forge-workflow 0.1.0-beta.5 → 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 +4 -0
- package/CHANGELOG.md +36 -0
- package/CLAUDE.md +0 -12
- package/CODING_STANDARDS.md +72 -0
- package/bin/forge.js +12 -1
- package/docs/guides/MIGRATION.md +3 -3
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
- package/docs/reference/INSTALL.md +4 -0
- package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
- package/docs/reference/RELEASE.md +4 -4
- package/docs/reference/github-accounts.md +134 -0
- package/docs/reference/shepherd.md +63 -13
- package/lib/adapters/pr-state-adapter.js +15 -2
- 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/codex-skills.js +2 -2
- package/lib/commands/_manifest.js +1 -0
- package/lib/commands/_registry.js +48 -18
- package/lib/commands/clean.js +57 -1
- 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 +54 -6
- package/lib/commands/memory.js +66 -2
- package/lib/commands/merge.js +720 -73
- package/lib/commands/plan.js +33 -2
- package/lib/commands/pr.js +2 -0
- package/lib/commands/preflight.js +10 -2
- package/lib/commands/push.js +108 -6
- package/lib/commands/recall.js +95 -61
- package/lib/commands/release.js +23 -2
- package/lib/commands/remember.js +28 -4
- package/lib/commands/serve.js +26 -9
- package/lib/commands/setup.js +132 -4
- package/lib/commands/shepherd.js +578 -72
- package/lib/commands/ship.js +15 -69
- package/lib/commands/skill.js +8 -0
- package/lib/commands/team.js +47 -8
- package/lib/commands/test.js +163 -4
- package/lib/commands/validate.js +65 -21
- package/lib/commands/worktree.js +155 -19
- 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-issues.js +78 -0
- package/lib/gate-events.js +98 -10
- package/lib/github-context.js +308 -0
- package/lib/global-flags.js +1 -0
- package/lib/hook-renderer.js +29 -1
- package/lib/issue-render.js +19 -0
- package/lib/kernel/broker.js +723 -31
- package/lib/kernel/claim-reconciler.js +238 -0
- 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 +3322 -183
- package/lib/kernel/taxonomy-validator.js +4 -1
- package/lib/kernel/windows-private-acl.js +239 -0
- package/lib/memory/hygiene.js +191 -0
- package/lib/memory/router.js +94 -27
- package/lib/memory/usage-evidence.js +4 -0
- package/lib/memory-digest.js +59 -0
- package/lib/merge-rules.js +135 -17
- package/lib/npm-publish-workflow.js +233 -40
- package/lib/package-root.js +2 -0
- 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 +0 -37
- package/lib/pr-monitor/monitor.js +117 -10
- package/lib/pr-monitor/process-identity.js +117 -0
- package/lib/pr-monitor/reconcile-executor.js +1101 -625
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +121 -24
- package/lib/pr-monitor/review-preflight.js +269 -0
- package/lib/pr-monitor/shepherd-lease.js +28 -19
- package/lib/pr-monitor/verdict.js +438 -0
- package/lib/pr-monitor/watch-lifecycle.js +144 -38
- package/lib/pr-monitor/watch-owner.js +1414 -0
- package/lib/pr-monitor/watch.js +129 -58
- package/lib/pr-shepherd.js +17 -3
- package/lib/project-memory.js +145 -3
- package/lib/protected-state-authority.js +799 -4
- package/lib/protected-state-surfaces.js +181 -3
- package/lib/release-readiness.js +2 -3
- package/lib/review-adapter.js +65 -0
- package/lib/skills-sync.js +65 -32
- 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 +8 -4
- package/scripts/commitlint.js +13 -15
- 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-runtime.js +3 -2
- package/scripts/process-tree.js +14 -2
- package/scripts/protected-state-check.js +440 -17
- package/scripts/sync-agent-skills.js +333 -34
- package/scripts/test-full-suite.js +704 -18
- package/scripts/test-profile.js +13 -3
- package/scripts/test.js +95 -14
- package/skills/coverage.json +1 -0
- package/skills/review/SKILL.md +2 -0
- package/skills/review/evals/scorecard.json +2 -2
- package/skills/setup/SKILL.md +18 -0
- package/skills/setup/evals/scorecard.json +3 -3
- package/skills/shepherd/SKILL.md +19 -2
- package/skills/shepherd/evals/scorecard.json +3 -3
- package/skills/validate/SKILL.md +3 -0
- package/skills/validate/evals/scorecard.json +1 -1
|
@@ -102,6 +102,9 @@ function classifyAuthError(error) {
|
|
|
102
102
|
if (status === 401 || /HTTP 401|bad credentials|token expired/i.test(text)) {
|
|
103
103
|
return { class: 'expired' };
|
|
104
104
|
}
|
|
105
|
+
if (status === 429 || /HTTP 429/i.test(text)) {
|
|
106
|
+
return { class: 'rate-limit', retryAfter };
|
|
107
|
+
}
|
|
105
108
|
if (status === 403 || /HTTP 403/i.test(text)) {
|
|
106
109
|
if (retryAfter || /rate limit|secondary rate/i.test(text)) {
|
|
107
110
|
return { class: 'rate-limit', retryAfter };
|
|
@@ -116,6 +119,7 @@ class PrStateAdapter {
|
|
|
116
119
|
* @param {object} [options]
|
|
117
120
|
* @param {Function} [options.gh] - Runner for `gh` (cmd, args[]) → string.
|
|
118
121
|
* @param {Function} [options.git] - Runner for `git` (cmd, args[]) → string.
|
|
122
|
+
* @param {string} [options.repository] - Exact owner/repo for numeric PR reads.
|
|
119
123
|
*/
|
|
120
124
|
constructor(options = {}) {
|
|
121
125
|
this.id = options.id || 'pr-state-adapter';
|
|
@@ -131,6 +135,7 @@ class PrStateAdapter {
|
|
|
131
135
|
});
|
|
132
136
|
this._gh = options.gh || defaultRunner;
|
|
133
137
|
this._git = options.git || defaultRunner;
|
|
138
|
+
this._repository = options.repository || null;
|
|
134
139
|
this.lastProtectionStatus = null;
|
|
135
140
|
}
|
|
136
141
|
|
|
@@ -141,7 +146,11 @@ class PrStateAdapter {
|
|
|
141
146
|
* @returns {Promise<{ headSha: string, mergeable: string, mergeStateStatus: string, checks: object[], threads: object[] }>}
|
|
142
147
|
*/
|
|
143
148
|
async readState(pr) {
|
|
144
|
-
const raw = this._gh('gh', [
|
|
149
|
+
const raw = this._gh('gh', [
|
|
150
|
+
'pr', 'view', String(pr),
|
|
151
|
+
...(this._repository ? ['--repo', this._repository] : []),
|
|
152
|
+
'--json', PR_VIEW_FIELDS,
|
|
153
|
+
]);
|
|
145
154
|
const data = JSON.parse(raw || '{}');
|
|
146
155
|
const lifecycleReadable = typeof data.state === 'string'
|
|
147
156
|
&& ['OPEN', 'MERGED', 'CLOSED'].includes(data.state.toUpperCase())
|
|
@@ -732,7 +741,11 @@ class PrStateAdapter {
|
|
|
732
741
|
* @returns {Promise<number|null>} epoch ms of the head commit, or null.
|
|
733
742
|
*/
|
|
734
743
|
async readHeadCommitTime({ pr }) {
|
|
735
|
-
const raw = this._gh('gh', [
|
|
744
|
+
const raw = this._gh('gh', [
|
|
745
|
+
'pr', 'view', String(pr),
|
|
746
|
+
...(this._repository ? ['--repo', this._repository] : []),
|
|
747
|
+
'--json', 'commits', '-q', '.commits[-1].committedDate',
|
|
748
|
+
]);
|
|
736
749
|
const t = Date.parse(String(raw || '').trim());
|
|
737
750
|
return Number.isFinite(t) ? t : null;
|
|
738
751
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execFileSync } = require('node:child_process');
|
|
4
|
+
|
|
5
|
+
const BASE_REMOTE_CANDIDATES = ['upstream', 'origin'];
|
|
6
|
+
const DEFAULT_BRANCH_CANDIDATES = ['main', 'master'];
|
|
7
|
+
const GIT_PROBE_TIMEOUT_MS = 120000;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Quiet git probe options. `stdio: 'pipe'` keeps failed probes from leaking to
|
|
11
|
+
* the terminal — these are speculative queries, not user-facing commands.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} cwd - Working directory for the probe.
|
|
14
|
+
* @returns {object} execFileSync options.
|
|
15
|
+
*/
|
|
16
|
+
function getQuietProbeOptions(cwd) {
|
|
17
|
+
return { encoding: 'utf8', cwd, timeout: GIT_PROBE_TIMEOUT_MS, stdio: 'pipe' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve `refs/remotes/<remote>/HEAD` to the ref it points at, verifying the
|
|
22
|
+
* target actually exists.
|
|
23
|
+
*
|
|
24
|
+
* @param {Function} [exec] - Injected execFileSync.
|
|
25
|
+
* @param {string} [cwd] - Repository directory.
|
|
26
|
+
* @param {string} [remoteName] - Remote to probe.
|
|
27
|
+
* @returns {string|null} Fully-qualified ref, or null.
|
|
28
|
+
*/
|
|
29
|
+
function resolveRemoteHeadTarget(exec = execFileSync, cwd = process.cwd(), remoteName = undefined) {
|
|
30
|
+
if (!remoteName) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const symbolicRef = exec('git', ['symbolic-ref', `refs/remotes/${remoteName}/HEAD`], getQuietProbeOptions(cwd)).trim();
|
|
35
|
+
if (!symbolicRef) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
exec('git', ['rev-parse', '--verify', symbolicRef], getQuietProbeOptions(cwd));
|
|
39
|
+
return symbolicRef;
|
|
40
|
+
} catch (_error) { // NOSONAR S2486 - intentional: missing ref is the expected probe failure
|
|
41
|
+
void _error;
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* True when the remote has fetched tracking refs we can treat as a base:
|
|
48
|
+
* a recorded HEAD, or one of the conventional default branches.
|
|
49
|
+
*
|
|
50
|
+
* @param {Function} [exec] - Injected execFileSync.
|
|
51
|
+
* @param {string} [cwd] - Repository directory.
|
|
52
|
+
* @param {string} [remoteName] - Remote to probe.
|
|
53
|
+
* @returns {boolean} Whether a usable tracking base exists.
|
|
54
|
+
*/
|
|
55
|
+
function remoteHasTrackingBase(exec = execFileSync, cwd = process.cwd(), remoteName = undefined) {
|
|
56
|
+
if (!remoteName) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (resolveRemoteHeadTarget(exec, cwd, remoteName)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const candidate of DEFAULT_BRANCH_CANDIDATES) {
|
|
64
|
+
try {
|
|
65
|
+
exec('git', ['rev-parse', '--verify', `refs/remotes/${remoteName}/${candidate}`], getQuietProbeOptions(cwd));
|
|
66
|
+
return true;
|
|
67
|
+
} catch (_error) { // NOSONAR S2486 - intentional: speculative probe, try next candidate
|
|
68
|
+
void _error;
|
|
69
|
+
// Probe next candidate.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve the remote that owns the integration base. `upstream` wins over
|
|
78
|
+
* `origin` so a fork-style checkout resolves the official repository rather
|
|
79
|
+
* than the contributor's fork.
|
|
80
|
+
*
|
|
81
|
+
* @param {Function} [exec] - Injected execFileSync.
|
|
82
|
+
* @param {string} [cwd] - Repository directory.
|
|
83
|
+
* @returns {string} Remote name ('origin' when nothing else qualifies).
|
|
84
|
+
*/
|
|
85
|
+
function resolveBaseRemote(exec = execFileSync, cwd = process.cwd()) {
|
|
86
|
+
for (const candidate of BASE_REMOTE_CANDIDATES) {
|
|
87
|
+
try {
|
|
88
|
+
exec('git', ['remote', 'get-url', candidate], getQuietProbeOptions(cwd));
|
|
89
|
+
if (remoteHasTrackingBase(exec, cwd, candidate)) {
|
|
90
|
+
return candidate;
|
|
91
|
+
}
|
|
92
|
+
} catch (_error) { // NOSONAR S2486 - intentional: speculative probe, try next candidate
|
|
93
|
+
void _error;
|
|
94
|
+
// Probe next candidate.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return 'origin';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the base remote's default branch name.
|
|
103
|
+
*
|
|
104
|
+
* @param {Function} [exec] - Injected execFileSync.
|
|
105
|
+
* @param {string} [cwd] - Repository directory.
|
|
106
|
+
* @param {string} [remoteName] - Remote to probe; defaults to the base remote.
|
|
107
|
+
* @returns {string} Branch name ('master' when nothing else resolves).
|
|
108
|
+
*/
|
|
109
|
+
function resolveBaseBranch(exec = execFileSync, cwd = process.cwd(), remoteName = resolveBaseRemote(exec, cwd)) {
|
|
110
|
+
const symbolicRef = resolveRemoteHeadTarget(exec, cwd, remoteName);
|
|
111
|
+
if (symbolicRef) {
|
|
112
|
+
const match = new RegExp(`^refs/remotes/${remoteName}/(.+)$`).exec(symbolicRef);
|
|
113
|
+
if (match?.[1]) {
|
|
114
|
+
return match[1];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const candidate of DEFAULT_BRANCH_CANDIDATES) {
|
|
119
|
+
try {
|
|
120
|
+
exec('git', ['rev-parse', '--verify', `refs/remotes/${remoteName}/${candidate}`], getQuietProbeOptions(cwd));
|
|
121
|
+
return candidate;
|
|
122
|
+
} catch (_error) { // NOSONAR S2486 - intentional: speculative probe, try next candidate
|
|
123
|
+
void _error;
|
|
124
|
+
// Probe next candidate.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return 'master';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
BASE_REMOTE_CANDIDATES,
|
|
133
|
+
DEFAULT_BRANCH_CANDIDATES,
|
|
134
|
+
resolveRemoteHeadTarget,
|
|
135
|
+
remoteHasTrackingBase,
|
|
136
|
+
resolveBaseRemote,
|
|
137
|
+
resolveBaseBranch,
|
|
138
|
+
};
|