@reneturcios/greenlight 0.5.0 → 0.7.0
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/README.md +52 -1
- package/dist/babysit.d.ts +132 -0
- package/dist/babysit.js +439 -0
- package/dist/babysit.js.map +1 -0
- package/dist/cli.js +197 -25
- package/dist/cli.js.map +1 -1
- package/dist/digest.d.ts +1 -1
- package/dist/digest.js +1 -1
- package/dist/evidence.d.ts +48 -0
- package/dist/evidence.js +254 -0
- package/dist/evidence.js.map +1 -0
- package/dist/guard.d.ts +63 -3
- package/dist/guard.js +131 -4
- package/dist/guard.js.map +1 -1
- package/dist/help.js +75 -8
- package/dist/help.js.map +1 -1
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/dist/init.js +1 -1
- package/dist/passport.d.ts +52 -0
- package/dist/passport.js +218 -0
- package/dist/passport.js.map +1 -0
- package/integrations/README.md +15 -1
- package/integrations/claude-code/CLAUDE.snippet.md +21 -10
- package/integrations/cline/README.md +1 -1
- package/integrations/copilot/copilot-instructions.snippet.md +17 -7
- package/integrations/cursor/greenlight.mdc +17 -7
- package/integrations/windsurf/README.md +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/evidence.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Verified PR evidence pack. One markdown document per PR that a
|
|
3
|
+
* reader can check without trusting Greenlight's word: every claim in
|
|
4
|
+
* it cites its source (the check run, the review anchor, the claim
|
|
5
|
+
* comment URL).
|
|
6
|
+
*
|
|
7
|
+
* Assembled from the existing engines: the guard, CI triage, review
|
|
8
|
+
* triage, and the Claim Protocol state of the PR's linked issues. The
|
|
9
|
+
* test-suite delta is included only when the caller passes a prior
|
|
10
|
+
* `greenlight verify --json` report via --verify-json: the pack never
|
|
11
|
+
* runs suites itself (they take minutes and belong where the code is).
|
|
12
|
+
*/
|
|
13
|
+
import { apiGet, parsePullUrl, parseIssueUrl } from "./github.js";
|
|
14
|
+
import { guardPr } from "./guard.js";
|
|
15
|
+
import { triagePr } from "./ci-triage.js";
|
|
16
|
+
import { triageReview as defaultTriageReview } from "./review-triage.js";
|
|
17
|
+
import { fetchProtocolState } from "./protocol-fetch.js";
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
const GREENLIGHT_VERSION = (() => {
|
|
21
|
+
try {
|
|
22
|
+
return createRequire(import.meta.url)("../package.json").version;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return "unknown";
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
async function defaultFetchPrMeta(owner, repo, number) {
|
|
29
|
+
const p = (await apiGet(`/repos/${owner}/${repo}/pulls/${number}`));
|
|
30
|
+
return {
|
|
31
|
+
createdAt: String(p.created_at ?? ""),
|
|
32
|
+
headSha: String(p.head?.sha ?? ""),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function defaultReadVerifyJson(path) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
38
|
+
if (parsed && typeof parsed === "object" && typeof parsed.outcome === "string")
|
|
39
|
+
return parsed;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function cite(url) {
|
|
47
|
+
return url && url.length > 0 ? url : "(no link recorded)";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Assemble the evidence pack. Best-effort throughout: a source that
|
|
51
|
+
* cannot be read becomes an explicit "could not be read" line, never a
|
|
52
|
+
* silent gap and never invented content.
|
|
53
|
+
*/
|
|
54
|
+
export async function buildEvidencePack(prUrl, opts = {}) {
|
|
55
|
+
const deps = opts.deps ?? {};
|
|
56
|
+
const now = opts.now ?? new Date();
|
|
57
|
+
const { owner, repo, number } = parsePullUrl(prUrl);
|
|
58
|
+
const guard = deps.guard ?? guardPr;
|
|
59
|
+
const triageCi = deps.triageCi ?? triagePr;
|
|
60
|
+
const triageReview = deps.triageReview ?? defaultTriageReview;
|
|
61
|
+
const fetchPrMeta = deps.fetchPrMeta ?? defaultFetchPrMeta;
|
|
62
|
+
const protocolStatus = deps.protocolStatus ?? fetchProtocolState;
|
|
63
|
+
const readVerifyJson = deps.readVerifyJson ?? defaultReadVerifyJson;
|
|
64
|
+
const [guardRes, ciRes, reviewRes, meta] = await Promise.all([
|
|
65
|
+
guard(prUrl).catch((e) => ({ error: e instanceof Error ? e.message : String(e) })),
|
|
66
|
+
triageCi(prUrl).catch((e) => ({ error: e instanceof Error ? e.message : String(e) })),
|
|
67
|
+
triageReview(prUrl).catch((e) => ({ error: e instanceof Error ? e.message : String(e) })),
|
|
68
|
+
fetchPrMeta(owner, repo, number).catch(() => ({ createdAt: "", headSha: "" })),
|
|
69
|
+
]);
|
|
70
|
+
const guardOk = (r) => typeof r === "object" && r !== null && !("error" in r);
|
|
71
|
+
const ciOk = (r) => typeof r === "object" && r !== null && !("error" in r);
|
|
72
|
+
const reviewOk = (r) => typeof r === "object" && r !== null && !("error" in r);
|
|
73
|
+
// Protocol status for each linked issue the guard found.
|
|
74
|
+
const issueProtocols = [];
|
|
75
|
+
if (guardOk(guardRes)) {
|
|
76
|
+
for (const li of guardRes.linkedIssues) {
|
|
77
|
+
try {
|
|
78
|
+
const { owner: iOwner, repo: iRepo, number: iNumber } = parseIssueUrl(li.url);
|
|
79
|
+
const proto = await protocolStatus(iOwner, iRepo, iNumber).catch(() => null);
|
|
80
|
+
const active = proto?.computation.active ?? null;
|
|
81
|
+
issueProtocols.push({
|
|
82
|
+
issue: `${iOwner}/${iRepo}#${iNumber}`,
|
|
83
|
+
url: li.url,
|
|
84
|
+
activeClaim: active
|
|
85
|
+
? {
|
|
86
|
+
claimant: active.claimant,
|
|
87
|
+
state: active.state,
|
|
88
|
+
claimedAt: active.claimedAt,
|
|
89
|
+
expiresAt: active.expiresAt,
|
|
90
|
+
commentUrl: active.commentUrl,
|
|
91
|
+
}
|
|
92
|
+
: null,
|
|
93
|
+
queue: (proto?.computation.queue ?? []).map((q) => q.claimant),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
issueProtocols.push({ issue: li.url, url: li.url, activeClaim: null, queue: [] });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let verifySection;
|
|
102
|
+
let verifyJson = null;
|
|
103
|
+
if (opts.verifyJsonPath) {
|
|
104
|
+
const vr = await readVerifyJson(opts.verifyJsonPath).catch(() => null);
|
|
105
|
+
if (vr && vr.delta) {
|
|
106
|
+
verifyJson = { outcome: vr.outcome, delta: vr.delta };
|
|
107
|
+
const d = vr.delta;
|
|
108
|
+
verifySection =
|
|
109
|
+
`Outcome: **${vr.outcome}** (from the provided verify report; the pack does not run suites itself).\n\n` +
|
|
110
|
+
`- newly failing (yours): ${d.newlyFailing.length > 0 ? d.newlyFailing.join(", ") : "none"}\n` +
|
|
111
|
+
`- newly passing (fixed by this PR): ${d.newlyPassing.length > 0 ? d.newlyPassing.join(", ") : "none"}\n` +
|
|
112
|
+
`- still failing (pre-existing): ${d.stillFailing.length > 0 ? d.stillFailing.join(", ") : "none"}\n` +
|
|
113
|
+
`- base ${d.baseTotal.passed}/${d.baseTotal.failed} passed/failed; head ${d.headTotal.passed}/${d.headTotal.failed} passed/failed`;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
verifySection = `No usable verify report at \`${opts.verifyJsonPath}\` (unreadable or not a verify result). Run \`greenlight verify --json\` where the code lives and pass it with --verify-json.`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
verifySection = "Not included. Run `greenlight verify --json` where the code lives and pass the report with --verify-json to add the honest red-green delta.";
|
|
121
|
+
}
|
|
122
|
+
const lines = [];
|
|
123
|
+
const title = guardOk(guardRes) ? guardRes.title : "(title unavailable)";
|
|
124
|
+
const author = guardOk(guardRes) ? guardRes.author : "(unknown)";
|
|
125
|
+
lines.push(`# Evidence pack: ${owner}/${repo}#${number}`);
|
|
126
|
+
lines.push("");
|
|
127
|
+
lines.push(`"${title}" by @${author}. PR opened ${meta.createdAt ? meta.createdAt.slice(0, 10) : "(date unavailable)"}, head ${meta.headSha ? meta.headSha.slice(0, 7) : "(unknown)"}.`);
|
|
128
|
+
lines.push(`Assembled ${now.toISOString().slice(0, 10)} by Greenlight v${GREENLIGHT_VERSION}. Every claim below cites its source.`);
|
|
129
|
+
lines.push("");
|
|
130
|
+
// Guard section
|
|
131
|
+
lines.push("## Guard verdict");
|
|
132
|
+
if (guardOk(guardRes)) {
|
|
133
|
+
lines.push(guardRes.summary);
|
|
134
|
+
const flags = guardRes.findings.filter((f) => f.severity === "flag");
|
|
135
|
+
if (flags.length === 0) {
|
|
136
|
+
lines.push("No flags. Source: the guard's five checks (competing fix, prior claims, artifacts, first-time, protocol claims).");
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push("| Finding | Evidence |");
|
|
141
|
+
lines.push("|---|---|");
|
|
142
|
+
for (const f of flags) {
|
|
143
|
+
lines.push(`| ${f.title} | ${f.evidence.join(" ")} |`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const notes = guardRes.findings.filter((f) => f.severity === "note");
|
|
147
|
+
if (notes.length > 0) {
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push("Notes: " + notes.map((f) => f.evidence.join(" ")).join(" "));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
lines.push(`Could not be read this run: ${guardRes.error}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push("");
|
|
156
|
+
// CI section
|
|
157
|
+
lines.push("## CI triage");
|
|
158
|
+
if (ciOk(ciRes)) {
|
|
159
|
+
lines.push(`Verdict: **${ciRes.verdict}**.`);
|
|
160
|
+
if (ciRes.checks.length === 0) {
|
|
161
|
+
lines.push("No failing checks on the PR head. Source: the check runs on the PR head.");
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push("| Check | Class | Evidence | Source |");
|
|
166
|
+
lines.push("|---|---|---|---|");
|
|
167
|
+
for (const c of ciRes.checks) {
|
|
168
|
+
lines.push(`| \`${c.name}\` | ${c.class}${c.lean ? ` (lean: ${c.lean})` : ""} | ${c.evidence} | ${cite(c.detailsUrl)} |`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
lines.push(`Could not be read this run: ${ciRes.error}`);
|
|
174
|
+
}
|
|
175
|
+
lines.push("");
|
|
176
|
+
// Review section
|
|
177
|
+
lines.push("## Review triage");
|
|
178
|
+
if (reviewOk(reviewRes)) {
|
|
179
|
+
const c = reviewRes.counts;
|
|
180
|
+
lines.push(`Verdict: **${reviewRes.verdict}** (${c.likelyReal} likely-real, ${c.needsDecision} needs-decision, ${c.likelyNoise} likely-noise).`);
|
|
181
|
+
const actionable = reviewRes.findings.filter((f) => f.class === "likely-real" || f.class === "needs-decision");
|
|
182
|
+
if (actionable.length === 0) {
|
|
183
|
+
lines.push("No actionable findings. Source: review and issue comments on the PR.");
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
lines.push("");
|
|
187
|
+
lines.push("| Finding | Class | Reviewer | Source |");
|
|
188
|
+
lines.push("|---|---|---|---|");
|
|
189
|
+
for (const f of actionable) {
|
|
190
|
+
const anchor = f.path ? `${f.path}${f.line ? `:${f.line}` : ""}` : "(review-level)";
|
|
191
|
+
lines.push(`| ${f.summary} | ${f.class} | @${f.author} (${f.authorKind}) | ${anchor} |`);
|
|
192
|
+
}
|
|
193
|
+
lines.push("");
|
|
194
|
+
lines.push("Reply drafts for each finding are in the review triage output; this pack records the findings, not the replies.");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
lines.push(`Could not be read this run: ${reviewRes.error}`);
|
|
199
|
+
}
|
|
200
|
+
lines.push("");
|
|
201
|
+
// Protocol section
|
|
202
|
+
lines.push("## Claim Protocol status of linked issues");
|
|
203
|
+
if (issueProtocols.length === 0) {
|
|
204
|
+
lines.push("No linked issues with readable protocol state.");
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
for (const ip of issueProtocols) {
|
|
208
|
+
if (!ip.activeClaim) {
|
|
209
|
+
lines.push(`- ${ip.issue}: no protocol claims. Source: ${cite(ip.url)}`);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const a = ip.activeClaim;
|
|
213
|
+
lines.push(`- ${ip.issue}: **${a.state}** claim held by @${a.claimant}, claimed ${a.claimedAt.slice(0, 10)}, expires ${a.expiresAt.slice(0, 10)}. Source: ${cite(a.commentUrl)}`);
|
|
214
|
+
if (ip.queue.length > 0)
|
|
215
|
+
lines.push(` Queue: ${ip.queue.map((q) => `@${q}`).join(", ")}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
lines.push("");
|
|
220
|
+
// Verify section
|
|
221
|
+
lines.push("## Test-suite delta");
|
|
222
|
+
lines.push(verifySection);
|
|
223
|
+
lines.push("");
|
|
224
|
+
// Sources
|
|
225
|
+
lines.push("## Sources");
|
|
226
|
+
lines.push(`- PR: ${cite(prUrl)}`);
|
|
227
|
+
if (guardOk(guardRes)) {
|
|
228
|
+
for (const li of guardRes.linkedIssues)
|
|
229
|
+
lines.push(`- Linked issue: ${cite(li.url)}`);
|
|
230
|
+
}
|
|
231
|
+
lines.push(`- Pack generator: Greenlight v${GREENLIGHT_VERSION} (guard + ci_triage + review_triage + protocol engines).`);
|
|
232
|
+
lines.push("");
|
|
233
|
+
const markdown = lines.join("\n");
|
|
234
|
+
return {
|
|
235
|
+
pr: `${owner}/${repo}#${number}`,
|
|
236
|
+
title,
|
|
237
|
+
author,
|
|
238
|
+
assembledAt: now.toISOString(),
|
|
239
|
+
version: GREENLIGHT_VERSION,
|
|
240
|
+
markdown,
|
|
241
|
+
json: {
|
|
242
|
+
pr: `${owner}/${repo}#${number}`,
|
|
243
|
+
title,
|
|
244
|
+
author,
|
|
245
|
+
version: GREENLIGHT_VERSION,
|
|
246
|
+
guard: guardOk(guardRes) ? { summary: guardRes.summary, flags: guardRes.findings.filter((f) => f.severity === "flag").map((f) => ({ check: f.check, title: f.title, evidence: f.evidence })) } : { error: guardRes.error },
|
|
247
|
+
ci: ciOk(ciRes) ? { verdict: ciRes.verdict, checks: ciRes.checks } : { error: ciRes.error },
|
|
248
|
+
review: reviewOk(reviewRes) ? { verdict: reviewRes.verdict, counts: reviewRes.counts, findings: reviewRes.findings } : { error: reviewRes.error },
|
|
249
|
+
protocol: issueProtocols,
|
|
250
|
+
verify: verifyJson,
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"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"}
|
package/dist/guard.d.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import type { CommentSignal, LinkedPR } from "./types.js";
|
|
2
2
|
import { type ProtocolFetchResult } from "./protocol-fetch.js";
|
|
3
|
-
export declare const GUARD_VERSION = "0.
|
|
3
|
+
export declare const GUARD_VERSION = "0.6.0";
|
|
4
4
|
/** The hidden marker the GitHub Action uses to find its own comment and update it. */
|
|
5
5
|
export declare const GUARD_COMMENT_MARKER = "<!-- greenlight-guard -->";
|
|
6
6
|
/** Check ids, also the keys of .greenlight-guard.json. */
|
|
7
7
|
export declare const GUARD_CHECKS: readonly ["competing-fix", "other-claim", "missing-artifact", "first-time", "protocol-claim"];
|
|
8
8
|
export type GuardCheckId = (typeof GUARD_CHECKS)[number];
|
|
9
|
-
export
|
|
9
|
+
export interface AutopilotConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Machine check id (e.g. "competing-fix", "protocol-claim",
|
|
12
|
+
* "changeset-missing") -> label name to apply. Only findings with
|
|
13
|
+
* severity "flag" can earn a label; notes never do.
|
|
14
|
+
*/
|
|
15
|
+
labels: Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* When true, the guard comment includes ready-to-copy stale-claim nudge
|
|
18
|
+
* lines for the maintainer. The guard never posts them anywhere itself.
|
|
19
|
+
*/
|
|
20
|
+
nudge: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface GuardConfig extends Record<GuardCheckId, boolean> {
|
|
23
|
+
/** Autopilot is off when absent: the default. Maintainer opts in. */
|
|
24
|
+
autopilot?: AutopilotConfig | null;
|
|
25
|
+
}
|
|
10
26
|
export declare function defaultGuardConfig(): GuardConfig;
|
|
11
|
-
/**
|
|
27
|
+
/** Parse the autopilot section of a raw config object. Anything malformed means off. */
|
|
28
|
+
export declare function parseAutopilotConfig(raw: unknown): AutopilotConfig | null;
|
|
29
|
+
/** Read .greenlight-guard.json from a directory (default cwd). Best-effort: anything unreadable means all checks on and autopilot off. */
|
|
12
30
|
export declare function readGuardConfig(dir?: string): GuardConfig;
|
|
13
31
|
export interface GuardLinkedIssue {
|
|
14
32
|
number: number;
|
|
@@ -61,6 +79,21 @@ export interface GuardResult {
|
|
|
61
79
|
summary: string;
|
|
62
80
|
/** True when at least one finding has severity "flag". */
|
|
63
81
|
hasFindings: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Autopilot label decisions: label names to apply, computed from the
|
|
84
|
+
* config's label map over severity-"flag" findings only. Empty unless
|
|
85
|
+
* the maintainer enabled autopilot in .greenlight-guard.json. The guard
|
|
86
|
+
* never applies these itself; the bundled Action does.
|
|
87
|
+
*/
|
|
88
|
+
autopilotLabels: string[];
|
|
89
|
+
/** The check-by-check decisions behind autopilotLabels (for dry-run display). */
|
|
90
|
+
autopilotLabelDecisions: AutopilotLabelDecision[];
|
|
91
|
+
/**
|
|
92
|
+
* Ready-to-copy stale-claim nudge lines for the maintainer, included in
|
|
93
|
+
* the guard comment when autopilot.nudge is on. The guard never posts
|
|
94
|
+
* them anywhere itself.
|
|
95
|
+
*/
|
|
96
|
+
staleClaimNudges: string[];
|
|
64
97
|
/** The comment-ready markdown. */
|
|
65
98
|
markdown: string;
|
|
66
99
|
}
|
|
@@ -154,6 +187,28 @@ export declare function checkMissingArtifacts(args: {
|
|
|
154
187
|
prAuthor: string;
|
|
155
188
|
prComments: CommentSignal[];
|
|
156
189
|
}): GuardFinding[];
|
|
190
|
+
/** A claim is stale when it is 14+ days old and no open PR has appeared on its issue. */
|
|
191
|
+
export declare const STALE_CLAIM_NUDGE_DAYS = 14;
|
|
192
|
+
export interface AutopilotLabelDecision {
|
|
193
|
+
check: string;
|
|
194
|
+
title: string;
|
|
195
|
+
label: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Decide autopilot labels: the config's label map over severity-"flag"
|
|
199
|
+
* findings only. Pure, so fixtures can prove the mapping and the default-off.
|
|
200
|
+
* Notes never earn labels.
|
|
201
|
+
*/
|
|
202
|
+
export declare function decideAutopilotLabels(findings: GuardFinding[], autopilot: AutopilotConfig | null | undefined): AutopilotLabelDecision[];
|
|
203
|
+
/**
|
|
204
|
+
* Build ready-to-copy nudge lines for stale foreign claims: 14+ days old
|
|
205
|
+
* with no OTHER open PR on the same issue (the guarded PR is the new
|
|
206
|
+
* arrival; another open PR means the claim is not stale). The maintainer
|
|
207
|
+
* copies and posts them on the issue; the guard never posts on the issue
|
|
208
|
+
* itself (sovereignty). Pure over the guard's own claims and competing
|
|
209
|
+
* PRs.
|
|
210
|
+
*/
|
|
211
|
+
export declare function buildStaleClaimNudges(claims: GuardClaim[], competingPRs: GuardCompetingPR[], guardedPrNumber: number): string[];
|
|
157
212
|
/** Light author-history signal. Null means the lookup failed: no finding. */
|
|
158
213
|
export declare function checkFirstTime(author: string, priorMerged: number | null): GuardFinding | null;
|
|
159
214
|
export declare function renderGuardMarkdown(result: Omit<GuardResult, "markdown">): string;
|
|
@@ -175,3 +230,8 @@ export interface GuardDeps {
|
|
|
175
230
|
* be read degrades to "no evidence" and its check is skipped.
|
|
176
231
|
*/
|
|
177
232
|
export declare function guardPr(prUrl: string, deps?: GuardDeps, config?: GuardConfig): Promise<GuardResult>;
|
|
233
|
+
/**
|
|
234
|
+
* The `--labels-dry-run` rendering: what autopilot WOULD do, in plain
|
|
235
|
+
* text, without touching anything. Never posts, never labels.
|
|
236
|
+
*/
|
|
237
|
+
export declare function renderLabelsDryRun(result: GuardResult): string;
|
package/dist/guard.js
CHANGED
|
@@ -44,6 +44,22 @@
|
|
|
44
44
|
* "other-claim", "missing-artifact", "first-time", "protocol-claim");
|
|
45
45
|
* unknown keys are ignored, a missing or unreadable file means every
|
|
46
46
|
* check is on.
|
|
47
|
+
*
|
|
48
|
+
* Autopilot (check 6, maintainer-side action, off by default): the same
|
|
49
|
+
* config file may carry an "autopilot" section with a label map and a
|
|
50
|
+
* nudge toggle:
|
|
51
|
+
*
|
|
52
|
+
* { "autopilot": { "labels": { "protocol-claim": "claim-conflict",
|
|
53
|
+
* "competing-fix": "duplicate-effort",
|
|
54
|
+
* "changeset-missing": "needs-changeset" },
|
|
55
|
+
* "nudge": true } }
|
|
56
|
+
*
|
|
57
|
+
* When enabled, the guard COMPUTES label decisions (check id -> label
|
|
58
|
+
* name, flags only) and ready-to-copy stale-claim nudge lines. It never
|
|
59
|
+
* applies labels or posts comments itself: the bundled GitHub Action
|
|
60
|
+
* reads the decisions from the guard JSON and applies the labels with
|
|
61
|
+
* the Actions token. The CLI reports the decisions with
|
|
62
|
+
* --labels-dry-run.
|
|
47
63
|
*/
|
|
48
64
|
import { readFileSync } from "node:fs";
|
|
49
65
|
import { apiGet, parsePullUrl, fetchLinkedPRs, fetchComments } from "./github.js";
|
|
@@ -53,15 +69,35 @@ import { detectClaPath, detectChangesetConvention } from "./prep-detail.js";
|
|
|
53
69
|
import { titleSlug } from "./automate.js";
|
|
54
70
|
import { parseGates } from "./gates.js";
|
|
55
71
|
import { fetchProtocolState } from "./protocol-fetch.js";
|
|
56
|
-
export const GUARD_VERSION = "0.
|
|
72
|
+
export const GUARD_VERSION = "0.6.0";
|
|
57
73
|
/** The hidden marker the GitHub Action uses to find its own comment and update it. */
|
|
58
74
|
export const GUARD_COMMENT_MARKER = "<!-- greenlight-guard -->";
|
|
59
75
|
/** Check ids, also the keys of .greenlight-guard.json. */
|
|
60
76
|
export const GUARD_CHECKS = ["competing-fix", "other-claim", "missing-artifact", "first-time", "protocol-claim"];
|
|
61
77
|
export function defaultGuardConfig() {
|
|
62
|
-
return { "competing-fix": true, "other-claim": true, "missing-artifact": true, "first-time": true, "protocol-claim": true };
|
|
78
|
+
return { "competing-fix": true, "other-claim": true, "missing-artifact": true, "first-time": true, "protocol-claim": true, autopilot: null };
|
|
79
|
+
}
|
|
80
|
+
/** Parse the autopilot section of a raw config object. Anything malformed means off. */
|
|
81
|
+
export function parseAutopilotConfig(raw) {
|
|
82
|
+
if (!raw || typeof raw !== "object")
|
|
83
|
+
return null;
|
|
84
|
+
const obj = raw;
|
|
85
|
+
const ap = obj["autopilot"];
|
|
86
|
+
if (!ap || typeof ap !== "object")
|
|
87
|
+
return null;
|
|
88
|
+
const apObj = ap;
|
|
89
|
+
const labels = {};
|
|
90
|
+
const rawLabels = apObj["labels"];
|
|
91
|
+
if (rawLabels && typeof rawLabels === "object") {
|
|
92
|
+
for (const [k, v] of Object.entries(rawLabels)) {
|
|
93
|
+
if (typeof k === "string" && k.trim() && typeof v === "string" && v.trim()) {
|
|
94
|
+
labels[k.trim()] = v.trim();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { labels, nudge: apObj["nudge"] === true };
|
|
63
99
|
}
|
|
64
|
-
/** Read .greenlight-guard.json from a directory (default cwd). Best-effort: anything unreadable means all checks on. */
|
|
100
|
+
/** Read .greenlight-guard.json from a directory (default cwd). Best-effort: anything unreadable means all checks on and autopilot off. */
|
|
65
101
|
export function readGuardConfig(dir = process.cwd()) {
|
|
66
102
|
const config = defaultGuardConfig();
|
|
67
103
|
try {
|
|
@@ -70,9 +106,10 @@ export function readGuardConfig(dir = process.cwd()) {
|
|
|
70
106
|
if (typeof raw[id] === "boolean")
|
|
71
107
|
config[id] = raw[id];
|
|
72
108
|
}
|
|
109
|
+
config.autopilot = parseAutopilotConfig(raw);
|
|
73
110
|
}
|
|
74
111
|
catch {
|
|
75
|
-
// Missing or broken config: all checks stay on.
|
|
112
|
+
// Missing or broken config: all checks stay on, autopilot stays off.
|
|
76
113
|
}
|
|
77
114
|
return config;
|
|
78
115
|
}
|
|
@@ -279,6 +316,57 @@ export function checkMissingArtifacts(args) {
|
|
|
279
316
|
}
|
|
280
317
|
return findings;
|
|
281
318
|
}
|
|
319
|
+
/** A claim is stale when it is 14+ days old and no open PR has appeared on its issue. */
|
|
320
|
+
export const STALE_CLAIM_NUDGE_DAYS = 14;
|
|
321
|
+
/**
|
|
322
|
+
* Decide autopilot labels: the config's label map over severity-"flag"
|
|
323
|
+
* findings only. Pure, so fixtures can prove the mapping and the default-off.
|
|
324
|
+
* Notes never earn labels.
|
|
325
|
+
*/
|
|
326
|
+
export function decideAutopilotLabels(findings, autopilot) {
|
|
327
|
+
if (!autopilot)
|
|
328
|
+
return [];
|
|
329
|
+
const seen = new Set();
|
|
330
|
+
const decisions = [];
|
|
331
|
+
for (const f of findings) {
|
|
332
|
+
if (f.severity !== "flag")
|
|
333
|
+
continue;
|
|
334
|
+
const label = autopilot.labels[f.check];
|
|
335
|
+
if (typeof label !== "string" || label.trim().length === 0)
|
|
336
|
+
continue;
|
|
337
|
+
const key = label.trim();
|
|
338
|
+
if (seen.has(key))
|
|
339
|
+
continue;
|
|
340
|
+
seen.add(key);
|
|
341
|
+
decisions.push({ check: f.check, title: f.title, label: key });
|
|
342
|
+
}
|
|
343
|
+
return decisions;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Build ready-to-copy nudge lines for stale foreign claims: 14+ days old
|
|
347
|
+
* with no OTHER open PR on the same issue (the guarded PR is the new
|
|
348
|
+
* arrival; another open PR means the claim is not stale). The maintainer
|
|
349
|
+
* copies and posts them on the issue; the guard never posts on the issue
|
|
350
|
+
* itself (sovereignty). Pure over the guard's own claims and competing
|
|
351
|
+
* PRs.
|
|
352
|
+
*/
|
|
353
|
+
export function buildStaleClaimNudges(claims, competingPRs, guardedPrNumber) {
|
|
354
|
+
const nudges = [];
|
|
355
|
+
for (const c of claims) {
|
|
356
|
+
if (c.claimedByAuthor)
|
|
357
|
+
continue;
|
|
358
|
+
if (c.ageDays < STALE_CLAIM_NUDGE_DAYS)
|
|
359
|
+
continue;
|
|
360
|
+
const ref = c.issueRef ?? undefined;
|
|
361
|
+
const hasCompeting = competingPRs.some((p) => p.issueNumber === c.issueNumber && (p.issueRef ?? undefined) === ref);
|
|
362
|
+
if (hasCompeting)
|
|
363
|
+
continue;
|
|
364
|
+
const label = c.issueRef ?? `#${c.issueNumber}`;
|
|
365
|
+
nudges.push(`@${c.author} claimed ${label} ${c.ageDays} days ago and never opened a PR. ` +
|
|
366
|
+
`This PR (#${guardedPrNumber}) now addresses it — if you're still working on it, reply to keep your claim; otherwise the claim lapses.`);
|
|
367
|
+
}
|
|
368
|
+
return nudges;
|
|
369
|
+
}
|
|
282
370
|
/** Light author-history signal. Null means the lookup failed: no finding. */
|
|
283
371
|
export function checkFirstTime(author, priorMerged) {
|
|
284
372
|
if (priorMerged === null)
|
|
@@ -326,6 +414,13 @@ export function renderGuardMarkdown(result) {
|
|
|
326
414
|
}
|
|
327
415
|
lines.push("");
|
|
328
416
|
}
|
|
417
|
+
if (result.staleClaimNudges.length > 0) {
|
|
418
|
+
lines.push("For the maintainer (autopilot nudge lines — copy and paste onto the issue; the guard never posts these itself):");
|
|
419
|
+
for (const n of result.staleClaimNudges) {
|
|
420
|
+
lines.push(`> ${n}`);
|
|
421
|
+
}
|
|
422
|
+
lines.push("");
|
|
423
|
+
}
|
|
329
424
|
lines.push("---");
|
|
330
425
|
lines.push(`_Checked with [Greenlight guard](https://github.com/CloudCorpRecords/greenlight) v${GUARD_VERSION}; findings are signals, verify before acting._`);
|
|
331
426
|
return lines.join("\n");
|
|
@@ -644,6 +739,12 @@ export async function guardPr(prUrl, deps = {}, config = defaultGuardConfig()) {
|
|
|
644
739
|
findings.push(f);
|
|
645
740
|
}
|
|
646
741
|
const { summary, hasFindings } = buildGuardSummary(findings);
|
|
742
|
+
// Autopilot: computed, never applied, here. Labels come from the
|
|
743
|
+
// maintainer's label map over severity-"flag" findings; nudge lines are
|
|
744
|
+
// ready-to-copy text the maintainer posts on the issue themselves.
|
|
745
|
+
const autopilot = config.autopilot ?? null;
|
|
746
|
+
const labelDecisions = decideAutopilotLabels(findings, autopilot);
|
|
747
|
+
const staleClaimNudges = autopilot?.nudge ? buildStaleClaimNudges(claims, competingPRs, number) : [];
|
|
647
748
|
const result = {
|
|
648
749
|
pr: prUrl,
|
|
649
750
|
number,
|
|
@@ -656,9 +757,35 @@ export async function guardPr(prUrl, deps = {}, config = defaultGuardConfig()) {
|
|
|
656
757
|
findings,
|
|
657
758
|
summary,
|
|
658
759
|
hasFindings,
|
|
760
|
+
autopilotLabels: labelDecisions.map((d) => d.label),
|
|
761
|
+
autopilotLabelDecisions: labelDecisions,
|
|
762
|
+
staleClaimNudges,
|
|
659
763
|
markdown: "",
|
|
660
764
|
};
|
|
661
765
|
result.markdown = renderGuardMarkdown(result);
|
|
662
766
|
return result;
|
|
663
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* The `--labels-dry-run` rendering: what autopilot WOULD do, in plain
|
|
770
|
+
* text, without touching anything. Never posts, never labels.
|
|
771
|
+
*/
|
|
772
|
+
export function renderLabelsDryRun(result) {
|
|
773
|
+
const lines = [`Autopilot label dry run for ${result.repo}#${result.number}`];
|
|
774
|
+
const decisions = result.autopilotLabelDecisions;
|
|
775
|
+
if (decisions.length === 0) {
|
|
776
|
+
lines.push("No autopilot labels: the maintainer has not enabled autopilot, or no mapped flag fired.");
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
lines.push(`Would apply ${decisions.length} label${decisions.length === 1 ? "" : "s"}: ${decisions.map((d) => d.label).join(", ")}`);
|
|
780
|
+
for (const d of decisions) {
|
|
781
|
+
lines.push(` "${d.check}" (${d.title}) -> "${d.label}"`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (result.staleClaimNudges.length > 0) {
|
|
785
|
+
lines.push("Stale-claim nudge lines (for the maintainer to post on the issue):");
|
|
786
|
+
for (const n of result.staleClaimNudges)
|
|
787
|
+
lines.push(` > ${n}`);
|
|
788
|
+
}
|
|
789
|
+
return lines.join("\n");
|
|
790
|
+
}
|
|
664
791
|
//# sourceMappingURL=guard.js.map
|