@reneturcios/greenlight 0.3.10 → 0.4.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 +26 -2
- package/dist/automate.d.ts +14 -1
- package/dist/automate.js +30 -12
- package/dist/automate.js.map +1 -1
- package/dist/ci-triage.d.ts +20 -0
- package/dist/ci-triage.js +47 -10
- package/dist/ci-triage.js.map +1 -1
- package/dist/cli.js +98 -7
- package/dist/cli.js.map +1 -1
- package/dist/digest.d.ts +212 -0
- package/dist/digest.js +541 -0
- package/dist/digest.js.map +1 -0
- package/dist/friction.d.ts +7 -0
- package/dist/friction.js +36 -0
- package/dist/friction.js.map +1 -1
- package/dist/guard.d.ts +5 -3
- package/dist/guard.js +65 -6
- package/dist/guard.js.map +1 -1
- package/dist/index.js +56 -33
- package/dist/index.js.map +1 -1
- package/dist/protocol-fetch.d.ts +142 -0
- package/dist/protocol-fetch.js +350 -0
- package/dist/protocol-fetch.js.map +1 -0
- package/dist/protocol.d.ts +232 -0
- package/dist/protocol.js +484 -0
- package/dist/protocol.js.map +1 -0
- package/integrations/README.md +4 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Protocol wiring (Greenlight v0.4.0): fetch layer, status assembly,
|
|
3
|
+
* and the check/guard integration points.
|
|
4
|
+
*
|
|
5
|
+
* The engine itself (src/protocol.ts) is pure: it never touches the network.
|
|
6
|
+
* This module assembles the engine's inputs from the GitHub API, computes
|
|
7
|
+
* issue status payloads, and merges protocol verdicts with the legacy
|
|
8
|
+
* check verdicts. All network access goes through injectable fetchers
|
|
9
|
+
* (the GuardDeps seam pattern), so every path is unit-testable without
|
|
10
|
+
* touching the API.
|
|
11
|
+
*
|
|
12
|
+
* Precedence (protocol vs legacy verdict), documented here because two
|
|
13
|
+
* engines now speak: the protocol verdict overrides legacy *claim
|
|
14
|
+
* inference* only. It never overrides gates, and it never overrides
|
|
15
|
+
* cross-referenced PR evidence. When both the protocol and a
|
|
16
|
+
* cross-referenced PR say TAKEN, the reasons merge.
|
|
17
|
+
*/
|
|
18
|
+
import { apiGet, ApiNotFoundError, buildIssueSnapshot, fetchLinkedPRs, } from "./github.js";
|
|
19
|
+
import { scoreIssue } from "./verdict.js";
|
|
20
|
+
import { computeClaimState, parseClaimsPolicy, parseClaimComment, defaultClaimsPolicy, buildIssueStatus, } from "./protocol.js";
|
|
21
|
+
async function paginate(path) {
|
|
22
|
+
const out = [];
|
|
23
|
+
for (let page = 1; page <= 5; page++) {
|
|
24
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
25
|
+
const data = (await apiGet(`${path}${sep}per_page=100&page=${page}`));
|
|
26
|
+
out.push(...data);
|
|
27
|
+
if (data.length < 100)
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
async function defaultFetchComments(owner, repo, number) {
|
|
33
|
+
const data = await paginate(`/repos/${owner}/${repo}/issues/${number}/comments`);
|
|
34
|
+
return data.map((c) => ({
|
|
35
|
+
id: typeof c.id === "number" ? c.id : String(c.id ?? ""),
|
|
36
|
+
author: String(c.user?.login ?? "unknown"),
|
|
37
|
+
body: String(c.body ?? ""),
|
|
38
|
+
createdAt: String(c.created_at ?? ""),
|
|
39
|
+
url: typeof c.html_url === "string" ? c.html_url : undefined,
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
async function defaultFetchReactions(owner, repo, commentId) {
|
|
43
|
+
try {
|
|
44
|
+
const data = (await apiGet(`/repos/${owner}/${repo}/issues/comments/${commentId}/reactions`));
|
|
45
|
+
return (data ?? []).map((r) => ({
|
|
46
|
+
commentId,
|
|
47
|
+
user: String(r.user?.login ?? "unknown"),
|
|
48
|
+
content: String(r.content ?? ""),
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read `.greenlight/claims.json` from the repo's default branch. 404 means
|
|
57
|
+
* the repo has not adopted the protocol (honored: false); any other failure
|
|
58
|
+
* degrades the same way rather than claiming protocol state we cannot see.
|
|
59
|
+
*/
|
|
60
|
+
async function defaultFetchPolicy(owner, repo) {
|
|
61
|
+
try {
|
|
62
|
+
const repoInfo = (await apiGet(`/repos/${owner}/${repo}`));
|
|
63
|
+
const branch = typeof repoInfo.default_branch === "string" ? repoInfo.default_branch : "main";
|
|
64
|
+
const file = (await apiGet(`/repos/${owner}/${repo}/contents/.greenlight/claims.json?ref=${encodeURIComponent(branch)}`));
|
|
65
|
+
if (typeof file.content !== "string") {
|
|
66
|
+
return { honored: false, policy: defaultClaimsPolicy(), invalid: false };
|
|
67
|
+
}
|
|
68
|
+
const text = Buffer.from(file.content, "base64").toString("utf8");
|
|
69
|
+
const policy = parseClaimsPolicy(text);
|
|
70
|
+
return { honored: true, policy, invalid: !!policy.invalid };
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err instanceof ApiNotFoundError) {
|
|
74
|
+
return { honored: false, policy: defaultClaimsPolicy(), invalid: false };
|
|
75
|
+
}
|
|
76
|
+
return { honored: false, policy: defaultClaimsPolicy(), invalid: false };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Assemble a protocol state from already-fetched data. Pure: the unit
|
|
81
|
+
* tests drive every integration path through this without the network.
|
|
82
|
+
*/
|
|
83
|
+
export function assembleProtocolState(owner, repo, number, comments, reactions, linkedPrInputs, mergedPrNumber, policyFetch, nowIso) {
|
|
84
|
+
const inputs = comments.map((c) => ({
|
|
85
|
+
author: c.author,
|
|
86
|
+
body: c.body,
|
|
87
|
+
createdAt: c.createdAt,
|
|
88
|
+
id: c.id,
|
|
89
|
+
url: c.url,
|
|
90
|
+
}));
|
|
91
|
+
const reactionInputs = reactions.map((r) => ({
|
|
92
|
+
commentId: r.commentId,
|
|
93
|
+
user: r.user,
|
|
94
|
+
content: r.content,
|
|
95
|
+
}));
|
|
96
|
+
const computation = computeClaimState({
|
|
97
|
+
comments: inputs,
|
|
98
|
+
reactions: reactionInputs,
|
|
99
|
+
linkedPrs: linkedPrInputs,
|
|
100
|
+
mergedPrNumber,
|
|
101
|
+
policy: policyFetch.policy,
|
|
102
|
+
now: nowIso,
|
|
103
|
+
repo: `${owner}/${repo}`,
|
|
104
|
+
issue: number,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
computation,
|
|
108
|
+
repoProtocol: policyFetch.honored
|
|
109
|
+
? { honored: true, lease_days: policyFetch.policy.lease_days }
|
|
110
|
+
: null,
|
|
111
|
+
policyInvalid: policyFetch.invalid,
|
|
112
|
+
issueUrl: `https://github.com/${owner}/${repo}/issues/${number}`,
|
|
113
|
+
linkedPrDetails: [],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Full protocol fetch for one issue: comments, reactions on marker
|
|
118
|
+
* comments only, the repo policy, and timing detail for closing-strength
|
|
119
|
+
* linked PRs. Best-effort: anything unreadable degrades to "no evidence".
|
|
120
|
+
*/
|
|
121
|
+
export async function fetchProtocolState(owner, repo, number, deps = {}) {
|
|
122
|
+
const nowIso = (deps.now ?? (() => new Date().toISOString()))();
|
|
123
|
+
const fetchComments = deps.fetchComments ?? defaultFetchComments;
|
|
124
|
+
const fetchReactions = deps.fetchReactions ?? defaultFetchReactions;
|
|
125
|
+
const fetchPRs = deps.fetchLinkedPRs ?? fetchLinkedPRs;
|
|
126
|
+
const fetchPolicy = deps.fetchPolicy ?? defaultFetchPolicy;
|
|
127
|
+
let comments = [];
|
|
128
|
+
try {
|
|
129
|
+
comments = await fetchComments(owner, repo, number);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
comments = [];
|
|
133
|
+
}
|
|
134
|
+
const markerComments = comments.filter((c) => parseClaimComment(c.body).isClaim);
|
|
135
|
+
// Reactions are fetched only for marker comments: the heartbeat signal
|
|
136
|
+
// exists only where a protocol claim exists, and each reaction list is
|
|
137
|
+
// its own API call.
|
|
138
|
+
const reactions = [];
|
|
139
|
+
for (const mc of markerComments) {
|
|
140
|
+
try {
|
|
141
|
+
const rs = await fetchReactions(owner, repo, mc.id);
|
|
142
|
+
for (const r of rs)
|
|
143
|
+
reactions.push({ ...r, commentId: mc.id });
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// A missing reactions list just means no heartbeats from reactions.
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
let policyFetch;
|
|
150
|
+
try {
|
|
151
|
+
policyFetch = await fetchPolicy(owner, repo);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
policyFetch = { honored: false, policy: defaultClaimsPolicy(), invalid: false };
|
|
155
|
+
}
|
|
156
|
+
let linked = [];
|
|
157
|
+
try {
|
|
158
|
+
linked = await fetchPRs(owner, repo, number);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
linked = [];
|
|
162
|
+
}
|
|
163
|
+
const closing = linked.filter((p) => p.linkStrength === "closing");
|
|
164
|
+
// Detail for closing PRs: author and created_at feed the status payload
|
|
165
|
+
// and the engine's link timing. The engine only needs timing when
|
|
166
|
+
// protocol claims exist, but the payload wants authors either way, so one
|
|
167
|
+
// pass serves both. A failed detail fetch degrades: the PR stays in the
|
|
168
|
+
// payload from snapshot data, and merged ones still reach the engine
|
|
169
|
+
// through the mergedPrNumber fallback (the engine synthesizes timing for
|
|
170
|
+
// those; see DECISION 5 in protocol.ts).
|
|
171
|
+
const details = [];
|
|
172
|
+
const linkedPrInputs = [];
|
|
173
|
+
let mergedPrNumber = null;
|
|
174
|
+
for (const p of closing) {
|
|
175
|
+
try {
|
|
176
|
+
const d = (await apiGet(`/repos/${owner}/${repo}/pulls/${p.number}`));
|
|
177
|
+
const merged = d.merged_at != null;
|
|
178
|
+
const state = merged ? "merged" : p.state === "closed" ? "closed" : "open";
|
|
179
|
+
const createdAt = typeof d.created_at === "string" ? d.created_at : null;
|
|
180
|
+
const author = String(d.user?.login ?? "unknown");
|
|
181
|
+
details.push({ number: p.number, author, state, url: p.url, createdAt });
|
|
182
|
+
if (createdAt) {
|
|
183
|
+
linkedPrInputs.push({ number: p.number, state, createdAt });
|
|
184
|
+
}
|
|
185
|
+
else if (merged && mergedPrNumber === null) {
|
|
186
|
+
mergedPrNumber = p.number;
|
|
187
|
+
}
|
|
188
|
+
if (merged && mergedPrNumber === null)
|
|
189
|
+
mergedPrNumber = p.number;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
details.push({ number: p.number, author: "unknown", state: p.state, url: p.url, createdAt: null });
|
|
193
|
+
if (p.state === "merged" && mergedPrNumber === null)
|
|
194
|
+
mergedPrNumber = p.number;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const result = assembleProtocolState(owner, repo, number, comments, reactions, linkedPrInputs, mergedPrNumber, policyFetch, nowIso);
|
|
198
|
+
result.linkedPrDetails = details;
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The full pre-flight computation for one issue: the legacy verdict
|
|
203
|
+
* machinery plus the protocol state, assembled into the SPEC 5.1 payload.
|
|
204
|
+
*/
|
|
205
|
+
export async function computeIssueStatus(owner, repo, number, opts = {}) {
|
|
206
|
+
const deps = opts.deps ?? {};
|
|
207
|
+
const fetchSnapshot = deps.fetchSnapshot ?? buildIssueSnapshot;
|
|
208
|
+
const snapshot = await fetchSnapshot(owner, repo, number);
|
|
209
|
+
if (opts.externalGates)
|
|
210
|
+
snapshot.externalGates = opts.externalGates;
|
|
211
|
+
const legacy = scoreIssue(snapshot);
|
|
212
|
+
const proto = await fetchProtocolState(owner, repo, number, deps);
|
|
213
|
+
const competingPrs = proto.linkedPrDetails.map((d) => ({
|
|
214
|
+
number: d.number,
|
|
215
|
+
author: d.author,
|
|
216
|
+
state: d.state,
|
|
217
|
+
url: d.url,
|
|
218
|
+
}));
|
|
219
|
+
const gates = legacy.gates.map((g) => ({
|
|
220
|
+
kind: g.kind,
|
|
221
|
+
detected: g.detected,
|
|
222
|
+
...(typeof g.evidence === "string" && g.evidence ? { evidence: g.evidence } : {}),
|
|
223
|
+
}));
|
|
224
|
+
const status = buildIssueStatus({
|
|
225
|
+
issueUrl: snapshot.sourceUrl,
|
|
226
|
+
computation: proto.computation,
|
|
227
|
+
competingPrs,
|
|
228
|
+
gates,
|
|
229
|
+
repoProtocol: proto.repoProtocol,
|
|
230
|
+
checkedAt: new Date().toISOString(),
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
snapshot,
|
|
234
|
+
legacy,
|
|
235
|
+
status,
|
|
236
|
+
computation: proto.computation,
|
|
237
|
+
repoProtocol: proto.repoProtocol,
|
|
238
|
+
policyInvalid: proto.policyInvalid,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/* ------------------------------------------------------------------ */
|
|
242
|
+
/* Verdict merge: the precedence rule */
|
|
243
|
+
/* ------------------------------------------------------------------ */
|
|
244
|
+
/**
|
|
245
|
+
* Reason prefixes produced by the legacy verdict engine's claim rules
|
|
246
|
+
* (rules 4, 5/5b, 6/6b, and warning W in verdict.ts). When the protocol
|
|
247
|
+
* holds a live claim, these lines are the ones the protocol supersedes;
|
|
248
|
+
* every other reason (PR evidence, gates, duplicates, bounty, decay)
|
|
249
|
+
* survives the merge.
|
|
250
|
+
*/
|
|
251
|
+
const CLAIM_REASON_PREFIXES = [
|
|
252
|
+
"claim race: ",
|
|
253
|
+
"claimed by ",
|
|
254
|
+
"stale claim by ",
|
|
255
|
+
"stale partial claim by ",
|
|
256
|
+
"soft claim by ",
|
|
257
|
+
];
|
|
258
|
+
function isClaimDerivedReason(reason) {
|
|
259
|
+
if (CLAIM_REASON_PREFIXES.some((p) => reason.startsWith(p)))
|
|
260
|
+
return true;
|
|
261
|
+
// Rule 5b fresh partial: "<author> claimed the <scope> part ...; the rest may still be open"
|
|
262
|
+
return / claimed .*; the rest may still be open$/.test(reason);
|
|
263
|
+
}
|
|
264
|
+
/** Flags the legacy engine sets for claim inference; the protocol replaces them. */
|
|
265
|
+
const CLAIM_DERIVED_FLAGS = ["claimed", "stale_claim", "partial-claim", "weak-claim"];
|
|
266
|
+
/**
|
|
267
|
+
* Merge the protocol verdict with the legacy verdict.
|
|
268
|
+
*
|
|
269
|
+
* Precedence: the protocol verdict overrides legacy *claim inference*.
|
|
270
|
+
* It never overrides gates, and it never overrides cross-referenced PR
|
|
271
|
+
* evidence. When both the protocol and a cross-referenced PR say TAKEN,
|
|
272
|
+
* the reasons merge (protocol reason first, PR evidence kept).
|
|
273
|
+
*/
|
|
274
|
+
export function mergeProtocolVerdict(legacy, status) {
|
|
275
|
+
const protocolField = {
|
|
276
|
+
verdict: status.verdict,
|
|
277
|
+
reason: status.reason,
|
|
278
|
+
claim: status.claim,
|
|
279
|
+
protocol_note: status.protocol_note,
|
|
280
|
+
repo_protocol: status.repo_protocol,
|
|
281
|
+
};
|
|
282
|
+
const protocolLines = [];
|
|
283
|
+
if (status.verdict === "TAKEN" && status.claim) {
|
|
284
|
+
const c = status.claim;
|
|
285
|
+
protocolLines.push(`protocol claim: @${c.claimant} holds this issue until ${c.expires_at.slice(0, 10)} (state: ${c.state})`);
|
|
286
|
+
if (c.queue.length > 0) {
|
|
287
|
+
protocolLines.push(`queue: ${c.queue.map((q) => `@${q.claimant} (#${q.position})`).join(", ")}`);
|
|
288
|
+
}
|
|
289
|
+
if (status.protocol_note)
|
|
290
|
+
protocolLines.push(status.protocol_note);
|
|
291
|
+
if (legacy.verdict === "GATED") {
|
|
292
|
+
// Gates are ritual, not inference: a repo that blocks outside PRs
|
|
293
|
+
// blocks them no matter who holds a claim. The protocol becomes
|
|
294
|
+
// context, not a verdict.
|
|
295
|
+
return {
|
|
296
|
+
verdict: "GATED",
|
|
297
|
+
reasons: [...legacy.reasons, status.reason ?? "protocol claim active"],
|
|
298
|
+
flags: [...legacy.flags, "protocol-claim"],
|
|
299
|
+
claims: legacy.claims,
|
|
300
|
+
gates: legacy.gates,
|
|
301
|
+
protocol: protocolField,
|
|
302
|
+
protocolLines,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
// Protocol wins over the legacy claim assessment; PR-derived evidence
|
|
306
|
+
// (open closing PR, cross-referenced PRs) survives the merge.
|
|
307
|
+
const keptReasons = legacy.reasons.filter((r) => !isClaimDerivedReason(r));
|
|
308
|
+
const keptFlags = legacy.flags.filter((f) => !CLAIM_DERIVED_FLAGS.includes(f));
|
|
309
|
+
return {
|
|
310
|
+
verdict: "TAKEN",
|
|
311
|
+
reasons: [status.reason ?? "protocol claim active", ...keptReasons],
|
|
312
|
+
flags: ["protocol-claim", ...keptFlags],
|
|
313
|
+
claims: legacy.claims,
|
|
314
|
+
gates: legacy.gates,
|
|
315
|
+
protocol: protocolField,
|
|
316
|
+
protocolLines,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (status.verdict === "GO") {
|
|
320
|
+
// Expired-only history: the protocol has nothing live, so the verdict
|
|
321
|
+
// stands and the note rides along.
|
|
322
|
+
protocolLines.push(status.protocol_note ?? "past protocol claims have all ended; no active lease");
|
|
323
|
+
return {
|
|
324
|
+
verdict: legacy.verdict,
|
|
325
|
+
reasons: [...legacy.reasons, status.reason ?? status.protocol_note ?? "protocol: no active claims"],
|
|
326
|
+
flags: [...legacy.flags, "protocol-expired"],
|
|
327
|
+
claims: legacy.claims,
|
|
328
|
+
gates: legacy.gates,
|
|
329
|
+
protocol: protocolField,
|
|
330
|
+
protocolLines,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
// No protocol claims at all: legacy stands, one quiet line.
|
|
334
|
+
protocolLines.push(status.protocol_note ?? "no protocol claims on this issue");
|
|
335
|
+
return {
|
|
336
|
+
verdict: legacy.verdict,
|
|
337
|
+
reasons: legacy.reasons,
|
|
338
|
+
flags: legacy.flags,
|
|
339
|
+
claims: legacy.claims,
|
|
340
|
+
gates: legacy.gates,
|
|
341
|
+
protocol: protocolField,
|
|
342
|
+
protocolLines,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/** The `check` command's full pipeline: legacy verdict merged with protocol. */
|
|
346
|
+
export async function checkIssueWithProtocol(owner, repo, number, opts = {}) {
|
|
347
|
+
const { snapshot, legacy, status } = await computeIssueStatus(owner, repo, number, opts);
|
|
348
|
+
return { snapshot, legacy, status, merged: mergeProtocolVerdict(legacy, status) };
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=protocol-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-fetch.js","sourceRoot":"","sources":["../src/protocol-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Greenlight Claim Protocol core engine (Phase 1, Greenlight v0.4.0).
|
|
3
|
+
*
|
|
4
|
+
* Implements SPEC.md sections 3 (the claim), 4 (policy file) and 5.1 (status
|
|
5
|
+
* payload) as pure functions over injected data: no network, no dates other
|
|
6
|
+
* than the caller's `now`, no exceptions on bad input.
|
|
7
|
+
*
|
|
8
|
+
* Design commitments carried over from the spec:
|
|
9
|
+
* - GitHub is the source of truth: a claim is a comment carrying the marker
|
|
10
|
+
* `<!-- greenlight-claim v1 -->`, ordered by the comment's own created_at
|
|
11
|
+
* (ties broken by comment id). The engine never invents timestamps.
|
|
12
|
+
* - Claims are leases, not property: expiry is computed lazily by any reader.
|
|
13
|
+
* - The state machine replays events chronologically (claims, heartbeats,
|
|
14
|
+
* releases, PR links), so a later build phase (the Ledger indexer) and the
|
|
15
|
+
* client-side CLI share one truth.
|
|
16
|
+
*
|
|
17
|
+
* Spec ambiguities resolved here (each is flagged DECISION in a comment):
|
|
18
|
+
* 1. A second protocol claim by the same author while their earlier claim is
|
|
19
|
+
* still alive is treated as a heartbeat, not a new claim (anti-squatting).
|
|
20
|
+
* 2. A promoted claim restarts its lease at promotion time: its expiry is
|
|
21
|
+
* promotion + lease (capped by promotion + tenure cap). Without this, long
|
|
22
|
+
* queues would be born dead.
|
|
23
|
+
* 3. A rocket reaction carries no timestamp in our input shape, so it counts
|
|
24
|
+
* as a heartbeat "as of now" (extends expiry to now + lease).
|
|
25
|
+
* 4. A linked PR in state "closed" (not merged) keeps the claim "linked"
|
|
26
|
+
* (lease frozen); the claim does not quietly resume.
|
|
27
|
+
* 5. A mergedPrNumber that is not among linkedPrs is synthesized as a linked
|
|
28
|
+
* merged PR with createdAt = now. It freezes the current holder; it does
|
|
29
|
+
* not resurrect a claim that already expired before the merge was known
|
|
30
|
+
* (expiry is lazy but materialized, not speculative).
|
|
31
|
+
* 6. Heartbeats only count after a claim's activation; a queued claimant's
|
|
32
|
+
* earlier comments do not pre-extend the lease.
|
|
33
|
+
* 7. Comments with unparseable createdAt are skipped entirely (they cannot be
|
|
34
|
+
* ordered, and ordering is the whole game).
|
|
35
|
+
*/
|
|
36
|
+
import type { Verdict } from "./types.js";
|
|
37
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
38
|
+
export declare const CLAIM_MARKER = "<!-- greenlight-claim v1 -->";
|
|
39
|
+
export declare const RELEASE_MARKER = "<!-- greenlight-claim release -->";
|
|
40
|
+
/** Spec 3.4: squatting budget, indexed active claims per account. */
|
|
41
|
+
export declare const MAX_ACTIVE_CLAIMS_PER_ACCOUNT = 5;
|
|
42
|
+
/** Repo claim policy, from `.greenlight/claims.json`. */
|
|
43
|
+
export interface ClaimsPolicy {
|
|
44
|
+
protocol: 1;
|
|
45
|
+
lease_days: number;
|
|
46
|
+
tenure_cap_days: number;
|
|
47
|
+
queue: boolean;
|
|
48
|
+
who_can_claim: string;
|
|
49
|
+
maintainers: string[];
|
|
50
|
+
/** True when the policy text could not be parsed or named another protocol
|
|
51
|
+
* version; the rest of the fields are the safe defaults. */
|
|
52
|
+
invalid?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function defaultClaimsPolicy(): ClaimsPolicy;
|
|
55
|
+
/**
|
|
56
|
+
* Parse a `.greenlight/claims.json` body. Never throws: bad JSON (or a
|
|
57
|
+
* protocol version this engine cannot read) degrades to the defaults with
|
|
58
|
+
* `invalid: true`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseClaimsPolicy(jsonText: string): ClaimsPolicy;
|
|
61
|
+
export interface ParsedClaimComment {
|
|
62
|
+
isClaim: boolean;
|
|
63
|
+
isRelease: boolean;
|
|
64
|
+
plan: string | null;
|
|
65
|
+
etaDays: number | null;
|
|
66
|
+
queueOk: boolean | null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Detect a protocol claim (or release) comment and parse the optional
|
|
70
|
+
* structured lines after the marker. Unknown lines are ignored; the marker
|
|
71
|
+
* alone is a valid claim.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseClaimComment(body: string): ParsedClaimComment;
|
|
74
|
+
/** One issue comment, as seen by the protocol engine. */
|
|
75
|
+
export interface ProtocolCommentInput {
|
|
76
|
+
author: string;
|
|
77
|
+
body: string;
|
|
78
|
+
createdAt: string;
|
|
79
|
+
id: number | string;
|
|
80
|
+
url?: string;
|
|
81
|
+
}
|
|
82
|
+
/** A reaction on a comment. No timestamp in this shape (see DECISION 3). */
|
|
83
|
+
export interface ProtocolReactionInput {
|
|
84
|
+
commentId: number | string;
|
|
85
|
+
user: string;
|
|
86
|
+
content: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A PR with closing intent toward the issue. Callers pre-classify link
|
|
90
|
+
* strength (reuse guard's extractors); this engine only needs the timing.
|
|
91
|
+
*/
|
|
92
|
+
export interface ProtocolLinkedPrInput {
|
|
93
|
+
number: number;
|
|
94
|
+
state: "open" | "closed" | "merged";
|
|
95
|
+
createdAt: string;
|
|
96
|
+
}
|
|
97
|
+
export interface ClaimStateInput {
|
|
98
|
+
comments: ProtocolCommentInput[];
|
|
99
|
+
reactions?: ProtocolReactionInput[];
|
|
100
|
+
linkedPrs?: ProtocolLinkedPrInput[];
|
|
101
|
+
/** A PR known to have merged fixing this issue (used when linkedPrs lacks it). */
|
|
102
|
+
mergedPrNumber?: number | null;
|
|
103
|
+
policy?: ClaimsPolicy;
|
|
104
|
+
/** ISO "now". Defaults to the real current time; pass explicitly in tests. */
|
|
105
|
+
now?: string;
|
|
106
|
+
repo?: string;
|
|
107
|
+
issue?: number;
|
|
108
|
+
}
|
|
109
|
+
export type ClaimStateName = "active" | "linked" | "completed" | "released" | "expired" | "queued";
|
|
110
|
+
export interface ClaimHistoryEvent {
|
|
111
|
+
event: string;
|
|
112
|
+
at: string;
|
|
113
|
+
by: string;
|
|
114
|
+
}
|
|
115
|
+
/** One protocol claim in the chain, fully resolved. */
|
|
116
|
+
export interface ResolvedClaim {
|
|
117
|
+
/** 1-based position in the chronological chain (1 = head). */
|
|
118
|
+
position: number;
|
|
119
|
+
claimant: string;
|
|
120
|
+
claimedAt: string;
|
|
121
|
+
commentId: number | string;
|
|
122
|
+
commentUrl: string | null;
|
|
123
|
+
plan: string | null;
|
|
124
|
+
etaDays: number | null;
|
|
125
|
+
queueOk: boolean | null;
|
|
126
|
+
state: ClaimStateName;
|
|
127
|
+
/** When this claim's lease started (claimedAt, or promotion time). */
|
|
128
|
+
activatedAt: string;
|
|
129
|
+
expiresAt: string;
|
|
130
|
+
lastHeartbeatAt: string;
|
|
131
|
+
linkedPr: number | null;
|
|
132
|
+
history: ClaimHistoryEvent[];
|
|
133
|
+
}
|
|
134
|
+
/** The SPEC 3.2 record shape, as stored in the Ledger and returned by the API. */
|
|
135
|
+
export interface ProtocolClaimRecord {
|
|
136
|
+
v: 1;
|
|
137
|
+
repo: string | null;
|
|
138
|
+
issue: number | null;
|
|
139
|
+
claimant: string;
|
|
140
|
+
claimed_at: string;
|
|
141
|
+
comment_url: string | null;
|
|
142
|
+
state: ClaimStateName;
|
|
143
|
+
lease_days: number;
|
|
144
|
+
expires_at: string;
|
|
145
|
+
last_heartbeat_at: string;
|
|
146
|
+
linked_pr: number | null;
|
|
147
|
+
queue_position: number;
|
|
148
|
+
history: ClaimHistoryEvent[];
|
|
149
|
+
over_cap?: boolean;
|
|
150
|
+
}
|
|
151
|
+
export interface ProtocolComputation {
|
|
152
|
+
/** Every protocol claim ever posted, in claimed_at order (ties by id). */
|
|
153
|
+
claims: ResolvedClaim[];
|
|
154
|
+
/** The claim holding the issue at `now`, or null when the issue is free. */
|
|
155
|
+
active: ResolvedClaim | null;
|
|
156
|
+
/** Claims waiting behind the holder (positions 2+). */
|
|
157
|
+
queue: ResolvedClaim[];
|
|
158
|
+
/** The SPEC 3.2 record for the active claim, or null. */
|
|
159
|
+
activeRecord: ProtocolClaimRecord | null;
|
|
160
|
+
policy: ClaimsPolicy;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Replay the claim chain for one issue and resolve every claim's state at
|
|
164
|
+
* `now`. Pure: no network, no exceptions on messy input.
|
|
165
|
+
*/
|
|
166
|
+
export declare function computeClaimState(input: ClaimStateInput): ProtocolComputation;
|
|
167
|
+
/**
|
|
168
|
+
* Spec 3.4: mark claims whose claimant holds more than the squatting budget
|
|
169
|
+
* of active claims. `activeCounts` maps login -> active claim count (the
|
|
170
|
+
* caller supplies it from the Ledger or another computation; nothing is
|
|
171
|
+
* fetched here). Returns new records; the input is untouched.
|
|
172
|
+
*/
|
|
173
|
+
export declare function applyOverCap(records: ProtocolClaimRecord[], activeCounts: Record<string, number>): ProtocolClaimRecord[];
|
|
174
|
+
export interface StatusCompetingPr {
|
|
175
|
+
number: number;
|
|
176
|
+
author: string;
|
|
177
|
+
state: "open" | "closed" | "merged";
|
|
178
|
+
url?: string;
|
|
179
|
+
}
|
|
180
|
+
export interface StatusGate {
|
|
181
|
+
kind: string;
|
|
182
|
+
detected: boolean;
|
|
183
|
+
evidence?: string;
|
|
184
|
+
}
|
|
185
|
+
export interface IssueStatusArgs {
|
|
186
|
+
issueUrl: string;
|
|
187
|
+
computation: ProtocolComputation;
|
|
188
|
+
competingPrs?: StatusCompetingPr[];
|
|
189
|
+
gates?: StatusGate[];
|
|
190
|
+
/** Null when the repo has no policy file (protocol works as a signal only). */
|
|
191
|
+
repoProtocol: {
|
|
192
|
+
honored: boolean;
|
|
193
|
+
lease_days: number;
|
|
194
|
+
} | null;
|
|
195
|
+
checkedAt?: string;
|
|
196
|
+
staleAfterSeconds?: number;
|
|
197
|
+
}
|
|
198
|
+
export interface IssueStatusPayload {
|
|
199
|
+
issue: string;
|
|
200
|
+
/** Null when there are no protocol claims: the verdict is the caller's. */
|
|
201
|
+
verdict: Verdict | null;
|
|
202
|
+
reason: string | null;
|
|
203
|
+
claim: {
|
|
204
|
+
state: ClaimStateName;
|
|
205
|
+
claimant: string;
|
|
206
|
+
claimed_at: string;
|
|
207
|
+
expires_at: string;
|
|
208
|
+
queue: Array<{
|
|
209
|
+
claimant: string;
|
|
210
|
+
position: number;
|
|
211
|
+
claimed_at: string;
|
|
212
|
+
}>;
|
|
213
|
+
over_cap?: boolean;
|
|
214
|
+
} | null;
|
|
215
|
+
protocol_note: string | null;
|
|
216
|
+
competing_prs: StatusCompetingPr[];
|
|
217
|
+
gates: StatusGate[];
|
|
218
|
+
repo_protocol: {
|
|
219
|
+
honored: boolean;
|
|
220
|
+
lease_days: number;
|
|
221
|
+
} | null;
|
|
222
|
+
checked_at: string;
|
|
223
|
+
stale_after_seconds: number;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Build the SPEC 5.1 issue-status payload from a protocol computation plus
|
|
227
|
+
* caller-supplied context (competing PRs, gates). Verdict mapping:
|
|
228
|
+
* active protocol claim -> TAKEN (expiry in the reason); a completed claim
|
|
229
|
+
* -> TAKEN naming the merged PR; expired-only history -> GO with a note;
|
|
230
|
+
* no protocol claims at all -> verdict null, claim null.
|
|
231
|
+
*/
|
|
232
|
+
export declare function buildIssueStatus(args: IssueStatusArgs): IssueStatusPayload;
|