@humanbased/crosscheck 1.3.1-beta.7 → 1.6.0-beta.11
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 +16 -1
- package/assets/demo-block-to-approve.gif +0 -0
- package/assets/demo-block-to-approve.mp4 +0 -0
- package/dist/__tests__/base-ref-recovery.test.d.ts +2 -0
- package/dist/__tests__/base-ref-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/base-ref-recovery.test.js +102 -0
- package/dist/__tests__/base-ref-recovery.test.js.map +1 -0
- package/dist/__tests__/board.test.js +510 -20
- package/dist/__tests__/board.test.js.map +1 -1
- package/dist/__tests__/codex-env.test.js +4 -0
- package/dist/__tests__/codex-env.test.js.map +1 -1
- package/dist/__tests__/credential-free-origin.test.js +134 -2
- package/dist/__tests__/credential-free-origin.test.js.map +1 -1
- package/dist/__tests__/demo-arc.test.d.ts +2 -0
- package/dist/__tests__/demo-arc.test.d.ts.map +1 -0
- package/dist/__tests__/demo-arc.test.js +73 -0
- package/dist/__tests__/demo-arc.test.js.map +1 -0
- package/dist/__tests__/doc-only-verdict.test.d.ts +2 -0
- package/dist/__tests__/doc-only-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/doc-only-verdict.test.js +79 -0
- package/dist/__tests__/doc-only-verdict.test.js.map +1 -0
- package/dist/__tests__/error-classification.test.js +14 -0
- package/dist/__tests__/error-classification.test.js.map +1 -1
- package/dist/__tests__/fix-requires-recheck.test.d.ts +2 -0
- package/dist/__tests__/fix-requires-recheck.test.d.ts.map +1 -0
- package/dist/__tests__/fix-requires-recheck.test.js +56 -0
- package/dist/__tests__/fix-requires-recheck.test.js.map +1 -0
- package/dist/__tests__/human-feedback.test.d.ts +2 -0
- package/dist/__tests__/human-feedback.test.d.ts.map +1 -0
- package/dist/__tests__/human-feedback.test.js +132 -0
- package/dist/__tests__/human-feedback.test.js.map +1 -0
- package/dist/__tests__/inconclusive-review.test.d.ts +2 -0
- package/dist/__tests__/inconclusive-review.test.d.ts.map +1 -0
- package/dist/__tests__/inconclusive-review.test.js +110 -0
- package/dist/__tests__/inconclusive-review.test.js.map +1 -0
- package/dist/__tests__/lock-ownership.test.d.ts +2 -0
- package/dist/__tests__/lock-ownership.test.d.ts.map +1 -0
- package/dist/__tests__/lock-ownership.test.js +167 -0
- package/dist/__tests__/lock-ownership.test.js.map +1 -0
- package/dist/__tests__/merge-gate.test.d.ts +2 -0
- package/dist/__tests__/merge-gate.test.d.ts.map +1 -0
- package/dist/__tests__/merge-gate.test.js +220 -0
- package/dist/__tests__/merge-gate.test.js.map +1 -0
- package/dist/__tests__/no-verdict.test.d.ts +2 -0
- package/dist/__tests__/no-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/no-verdict.test.js +415 -0
- package/dist/__tests__/no-verdict.test.js.map +1 -0
- package/dist/__tests__/pr-spec.test.js +37 -0
- package/dist/__tests__/pr-spec.test.js.map +1 -1
- package/dist/__tests__/run.test.js +50 -1
- package/dist/__tests__/run.test.js.map +1 -1
- package/dist/__tests__/runner.test.js +82 -1
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/standing-verdict-history.test.d.ts +2 -0
- package/dist/__tests__/standing-verdict-history.test.d.ts.map +1 -0
- package/dist/__tests__/standing-verdict-history.test.js +109 -0
- package/dist/__tests__/standing-verdict-history.test.js.map +1 -0
- package/dist/__tests__/step-plan.test.d.ts +2 -0
- package/dist/__tests__/step-plan.test.d.ts.map +1 -0
- package/dist/__tests__/step-plan.test.js +129 -0
- package/dist/__tests__/step-plan.test.js.map +1 -0
- package/dist/cli.js +14 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/merge.d.ts +11 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +188 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/review.d.ts +3 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +117 -9
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +53 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +218 -36
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +41 -26
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +8 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/review-strategy.json +5 -5
- package/dist/github/client.d.ts +18 -1
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +19 -3
- package/dist/github/client.js.map +1 -1
- package/dist/github/merge.d.ts +28 -0
- package/dist/github/merge.d.ts.map +1 -1
- package/dist/github/merge.js +41 -0
- package/dist/github/merge.js.map +1 -1
- package/dist/github/review-status.d.ts +33 -0
- package/dist/github/review-status.d.ts.map +1 -1
- package/dist/github/review-status.js +94 -9
- package/dist/github/review-status.js.map +1 -1
- package/dist/github/webhook.d.ts +1 -0
- package/dist/github/webhook.d.ts.map +1 -1
- package/dist/github/webhook.js.map +1 -1
- package/dist/lib/board.d.ts +60 -4
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +357 -65
- package/dist/lib/board.js.map +1 -1
- package/dist/lib/clone.d.ts +65 -1
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +252 -16
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/human-feedback.d.ts +9 -0
- package/dist/lib/human-feedback.d.ts.map +1 -0
- package/dist/lib/human-feedback.js +83 -0
- package/dist/lib/human-feedback.js.map +1 -0
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +6 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/merge-gate.d.ts +70 -0
- package/dist/lib/merge-gate.d.ts.map +1 -0
- package/dist/lib/merge-gate.js +138 -0
- package/dist/lib/merge-gate.js.map +1 -0
- package/dist/lib/no-verdict.d.ts +104 -0
- package/dist/lib/no-verdict.d.ts.map +1 -0
- package/dist/lib/no-verdict.js +262 -0
- package/dist/lib/no-verdict.js.map +1 -0
- package/dist/lib/pr-lock.d.ts +37 -1
- package/dist/lib/pr-lock.d.ts.map +1 -1
- package/dist/lib/pr-lock.js +176 -29
- package/dist/lib/pr-lock.js.map +1 -1
- package/dist/lib/pr-spec.d.ts +7 -0
- package/dist/lib/pr-spec.d.ts.map +1 -1
- package/dist/lib/pr-spec.js +12 -1
- package/dist/lib/pr-spec.js.map +1 -1
- package/dist/lib/pr-workflow-state.d.ts +39 -0
- package/dist/lib/pr-workflow-state.d.ts.map +1 -1
- package/dist/lib/pr-workflow-state.js +73 -1
- package/dist/lib/pr-workflow-state.js.map +1 -1
- package/dist/lib/review-strategy.d.ts +14 -0
- package/dist/lib/review-strategy.d.ts.map +1 -1
- package/dist/lib/review-strategy.js +32 -1
- package/dist/lib/review-strategy.js.map +1 -1
- package/dist/lib/runner.d.ts +51 -2
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +211 -105
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/tips.d.ts.map +1 -1
- package/dist/lib/tips.js +1 -0
- package/dist/lib/tips.js.map +1 -1
- package/dist/lib/verdict.d.ts +30 -1
- package/dist/lib/verdict.d.ts.map +1 -1
- package/dist/lib/verdict.js +94 -9
- package/dist/lib/verdict.js.map +1 -1
- package/dist/reviewers/codex-env.d.ts.map +1 -1
- package/dist/reviewers/codex-env.js +2 -0
- package/dist/reviewers/codex-env.js.map +1 -1
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +3 -1
- package/dist/reviewers/codex.js.map +1 -1
- package/dist/reviewers/fix.d.ts +2 -2
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +10 -4
- package/dist/reviewers/fix.js.map +1 -1
- package/docs/growth/launch/README.md +59 -0
- package/docs/growth/launch/essay-stop-letting-one-agent-review-its-own-code.md +118 -0
- package/docs/growth/launch/posts.md +175 -0
- package/docs/trust.md +16 -1
- package/get-started.md +17 -0
- package/package.json +11 -3
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { fmtTokensRaw } from './board.js';
|
|
2
|
+
import { shaCovers } from './pr-workflow-state.js';
|
|
3
|
+
/**
|
|
4
|
+
* The verdict that still governs the PR: the newest review or recheck that
|
|
5
|
+
* actually carries one.
|
|
6
|
+
*
|
|
7
|
+
* A review can run, post its findings, and record no verdict at all — that is
|
|
8
|
+
* the `ran_no_verdict` disposition, and it says nothing about the `BLOCK` that
|
|
9
|
+
* was standing before it. Taking the newest review-or-recheck record
|
|
10
|
+
* unconditionally would let one malformed review erase a verdict that still
|
|
11
|
+
* gates the PR, and the report would go quiet about it in exactly the state
|
|
12
|
+
* that most needs saying out loud.
|
|
13
|
+
*/
|
|
14
|
+
export function selectStandingVerdict(history) {
|
|
15
|
+
const judged = history.filter(r => isVerdictStep(r.type) && r.verdict).at(-1);
|
|
16
|
+
if (!judged?.verdict)
|
|
17
|
+
return undefined;
|
|
18
|
+
return { verdict: judged.verdict, ...(judged.sha !== undefined && { sha: judged.sha }) };
|
|
19
|
+
}
|
|
20
|
+
function isVerdictStep(type) {
|
|
21
|
+
return type === 'review' || type === 'recheck';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classifies every verdict-capable step in the workflow. When none is
|
|
25
|
+
* configured, returns a single synthetic `review` entry — the absence is itself
|
|
26
|
+
* the answer, and callers should not have to special-case an empty array.
|
|
27
|
+
*/
|
|
28
|
+
export function describeVerdictSteps(workflowSteps, outcomes) {
|
|
29
|
+
const steps = workflowSteps.filter(s => isVerdictStep(s.type));
|
|
30
|
+
if (steps.length === 0)
|
|
31
|
+
return [{ step: 'review', disposition: 'not_configured' }];
|
|
32
|
+
const ran = new Set(outcomes?.ran ?? []);
|
|
33
|
+
const skipped = new Map((outcomes?.skipped ?? []).map(s => [s.step, s.reason]));
|
|
34
|
+
return steps.map(({ name }) => {
|
|
35
|
+
if (ran.has(name)) {
|
|
36
|
+
// `verdict: null` means the step ran and its output had no parseable
|
|
37
|
+
// verdict. A missing key means the step recorded no verdict field at all,
|
|
38
|
+
// which for a dispatched verdict step means it ran normally.
|
|
39
|
+
const detail = outcomes?.ranDetail?.[name];
|
|
40
|
+
return detail && 'verdict' in detail && detail.verdict === null
|
|
41
|
+
? { step: name, disposition: 'ran_no_verdict' }
|
|
42
|
+
: { step: name, disposition: 'ran' };
|
|
43
|
+
}
|
|
44
|
+
const reason = skipped.get(name);
|
|
45
|
+
if (reason !== undefined)
|
|
46
|
+
return { step: name, disposition: 'skipped', reason };
|
|
47
|
+
return { step: name, disposition: 'not_dispatched' };
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Guards that stop the run deliberately. Re-running the same step walks into the
|
|
51
|
+
// same cap, so these get an explanation instead of a command.
|
|
52
|
+
const DELIBERATE_STOPS = new Set(['max_rounds', 'commit_limit_reached', 'vendor_limit']);
|
|
53
|
+
// Reasons that mean no vendor was available to do the work — a config gap, not
|
|
54
|
+
// a per-run accident.
|
|
55
|
+
const ROUTING_GAPS = new Set(['no_vendor', 'no_reviewer', 'unsupported_vendor', 'human_origin']);
|
|
56
|
+
function describeSkipReason(reason, step, prUrl) {
|
|
57
|
+
if (ROUTING_GAPS.has(reason)) {
|
|
58
|
+
return {
|
|
59
|
+
next: [
|
|
60
|
+
{ text: 'set routing.fallback_reviewer in ~/.crosscheck/config.yml', recommended: true },
|
|
61
|
+
{ text: `ck run ${prUrl} --reviewer <claude|codex>`, recommended: false },
|
|
62
|
+
],
|
|
63
|
+
notes: ['No vendor was available to judge this PR — nothing about the code caused this.'],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (DELIBERATE_STOPS.has(reason)) {
|
|
67
|
+
return {
|
|
68
|
+
next: [],
|
|
69
|
+
notes: [`${step} stopped on \`${reason}\` — crosscheck gave up on purpose, so this needs a human.`],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (reason === 'no_change_since_review') {
|
|
73
|
+
return {
|
|
74
|
+
next: [{ text: `ck run ${prUrl} --steps review`, recommended: false }],
|
|
75
|
+
notes: ['Nothing has changed since the last review, so there was nothing new to judge.'],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { next: [{ text: `ck run ${prUrl} --steps ${step}`, recommended: true }], notes: [] };
|
|
79
|
+
}
|
|
80
|
+
function shortSha(sha) {
|
|
81
|
+
return sha === undefined ? undefined : sha.slice(0, 7);
|
|
82
|
+
}
|
|
83
|
+
function formatRanDetail(detail) {
|
|
84
|
+
const parts = [];
|
|
85
|
+
if (detail.vendor)
|
|
86
|
+
parts.push(detail.vendor);
|
|
87
|
+
if (detail.tokensUsed !== undefined)
|
|
88
|
+
parts.push(`${fmtTokensRaw(detail.tokensUsed)} tokens`);
|
|
89
|
+
if (detail.appliedCount === 0)
|
|
90
|
+
parts.push('no changes applied');
|
|
91
|
+
else if (detail.appliedCount !== undefined)
|
|
92
|
+
parts.push(`${detail.appliedCount} change${detail.appliedCount === 1 ? '' : 's'} applied`);
|
|
93
|
+
if (detail.verdict === null)
|
|
94
|
+
parts.push('no verdict parsed');
|
|
95
|
+
return parts.join(' · ');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Explains an empty verdict and says what to do about it. Pure — every input it
|
|
99
|
+
* needs is already in memory at the reporting site.
|
|
100
|
+
*/
|
|
101
|
+
export function buildNoVerdictReport(input) {
|
|
102
|
+
const { workflowSteps, outcomes, prUrl } = input;
|
|
103
|
+
const verdictSteps = describeVerdictSteps(workflowSteps, outcomes);
|
|
104
|
+
const ran = (outcomes?.ran ?? []).map(step => ({
|
|
105
|
+
step,
|
|
106
|
+
detail: outcomes?.ranDetail?.[step] ? formatRanDetail(outcomes.ranDetail[step]) : '',
|
|
107
|
+
}));
|
|
108
|
+
const why = [];
|
|
109
|
+
let next = [];
|
|
110
|
+
const notes = [];
|
|
111
|
+
// Precedence: a step that ran and produced nothing outranks one that skipped,
|
|
112
|
+
// which outranks one that never ran. The most specific thing that happened is
|
|
113
|
+
// the thing worth reporting.
|
|
114
|
+
// Fix steps by type: conflict-resolve records `applied_count: 0` too, and a
|
|
115
|
+
// branch with no conflicts to resolve says nothing about a review's findings.
|
|
116
|
+
const fixStepNames = new Set(workflowSteps.filter(s => s.type === 'fix').map(s => s.name));
|
|
117
|
+
const noopFixStep = (outcomes?.ran ?? []).find(step => fixStepNames.has(step) && outcomes?.ranDetail?.[step]?.appliedCount === 0);
|
|
118
|
+
// True when every step this run actually ran was a fix that changed nothing —
|
|
119
|
+
// i.e. the run's whole output was nothing at all.
|
|
120
|
+
const scopedFixDidNothing = noopFixStep !== undefined
|
|
121
|
+
&& (outcomes?.ran ?? []).every(step => outcomes?.ranDetail?.[step]?.appliedCount === 0)
|
|
122
|
+
? noopFixStep
|
|
123
|
+
: undefined;
|
|
124
|
+
const ranNoVerdict = verdictSteps.find(v => v.disposition === 'ran_no_verdict');
|
|
125
|
+
const skippedStep = verdictSteps.find(v => v.disposition === 'skipped');
|
|
126
|
+
const allNotConfigured = verdictSteps.every(v => v.disposition === 'not_configured');
|
|
127
|
+
const allNotDispatched = verdictSteps.every(v => v.disposition === 'not_dispatched');
|
|
128
|
+
let cause;
|
|
129
|
+
let expected;
|
|
130
|
+
if (input.strategySkipped) {
|
|
131
|
+
cause = 'strategy_skip';
|
|
132
|
+
expected = true;
|
|
133
|
+
why.push(`PR classified ${input.strategySkipped} — the strategy skipped it, so no step produced a verdict.`);
|
|
134
|
+
}
|
|
135
|
+
else if (allNotConfigured) {
|
|
136
|
+
cause = 'not_configured';
|
|
137
|
+
expected = true;
|
|
138
|
+
why.push('This workflow has no review or recheck step, so it produces no verdict by design.');
|
|
139
|
+
}
|
|
140
|
+
else if (ranNoVerdict) {
|
|
141
|
+
cause = 'ran_no_verdict';
|
|
142
|
+
expected = false;
|
|
143
|
+
why.push(`${ranNoVerdict.step} ran and posted, but its output carried no parseable VERDICT: line.`);
|
|
144
|
+
why.push('The findings are in the comment; the workflow has no verdict to gate on.');
|
|
145
|
+
next = [{ text: `ck run ${prUrl} --steps ${ranNoVerdict.step}`, recommended: true }];
|
|
146
|
+
notes.push('Malformed vendor output is usually transient — a re-run normally settles it.');
|
|
147
|
+
}
|
|
148
|
+
else if (skippedStep) {
|
|
149
|
+
cause = 'skipped';
|
|
150
|
+
expected = false;
|
|
151
|
+
why.push('Only review and recheck produce verdicts, and neither judged this commit.');
|
|
152
|
+
const advice = describeSkipReason(skippedStep.reason ?? 'unknown', skippedStep.step, prUrl);
|
|
153
|
+
next = advice.next;
|
|
154
|
+
notes.push(...advice.notes);
|
|
155
|
+
}
|
|
156
|
+
else if (scopedFixDidNothing) {
|
|
157
|
+
// `ck fix` asks for exactly one step, so a missing verdict is expected and the
|
|
158
|
+
// run would otherwise close green — the same green a fix that rewrote ten files
|
|
159
|
+
// prints. The absent verdict is indeed expected; the absent *change* is not,
|
|
160
|
+
// and it is the only thing the invocation was for.
|
|
161
|
+
cause = 'fix_noop';
|
|
162
|
+
expected = false;
|
|
163
|
+
why.push(`This run was scoped to ${scopedFixDidNothing}, which ran and applied no changes.`);
|
|
164
|
+
why.push('Nothing was written to the PR — the review it read may carry no actionable findings, or they may already be resolved in HEAD.');
|
|
165
|
+
next = [{ text: `ck run ${prUrl} --steps review`, recommended: true }];
|
|
166
|
+
notes.push('A review that could not resolve the diff produces a findings-free body that no fix can act on — check the review comment the fix step read.');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
cause = 'not_dispatched';
|
|
170
|
+
// A run that asked for one step got what it asked for. An unscoped run that
|
|
171
|
+
// dispatched no verdict step did not.
|
|
172
|
+
expected = allNotDispatched && input.stepsExplicitlyScoped === true;
|
|
173
|
+
why.push(expected
|
|
174
|
+
? 'This run was scoped to steps that do not produce verdicts.'
|
|
175
|
+
: 'Only review and recheck produce verdicts, and neither was dispatched.');
|
|
176
|
+
if (!expected) {
|
|
177
|
+
next = [{ text: `ck run ${prUrl} --steps review`, recommended: true }];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Context that holds regardless of cause: what the PR still says, and whether
|
|
181
|
+
// it still describes the code under review.
|
|
182
|
+
if (!expected && input.standingVerdict?.verdict) {
|
|
183
|
+
const sha = shortSha(input.standingVerdict.sha);
|
|
184
|
+
const head = shortSha(input.headSha);
|
|
185
|
+
// shaCovers is the codebase's one definition of "the same commit" — it
|
|
186
|
+
// handles the short/full forms annotations carry either of. A verdict whose
|
|
187
|
+
// SHA is unknown proves nothing about HEAD, so it is reported without the
|
|
188
|
+
// staleness claim rather than guessed at.
|
|
189
|
+
const covers = input.headSha !== undefined && shaCovers(input.standingVerdict.sha, input.headSha);
|
|
190
|
+
why.push(sha !== undefined && head !== undefined && !covers
|
|
191
|
+
? `The PR's standing verdict is ${input.standingVerdict.verdict} on ${sha}, which is not HEAD (${head}).`
|
|
192
|
+
: `The PR's standing verdict is still ${input.standingVerdict.verdict}${sha ? ` on ${sha}` : ''}.`);
|
|
193
|
+
}
|
|
194
|
+
// A fix that applied nothing is the strongest hint available that the findings
|
|
195
|
+
// are already resolved — which is precisely what a recheck confirms. Not repeated
|
|
196
|
+
// under `fix_noop`, where it is already the headline.
|
|
197
|
+
if (!expected && noopFixStep && cause !== 'ran_no_verdict' && cause !== 'fix_noop') {
|
|
198
|
+
why.push(`The ${noopFixStep} step applied no changes — the findings may already be resolved in HEAD.`);
|
|
199
|
+
}
|
|
200
|
+
// Offer the other verdict step as an alternative: recheck judges against the
|
|
201
|
+
// original findings, review sets them aside. Not for malformed output, where
|
|
202
|
+
// the action is to re-run the step that misbehaved and a second command only
|
|
203
|
+
// blurs that.
|
|
204
|
+
const recommendedStep = next.find(n => n.recommended)?.text.match(/--steps (\S+)/)?.[1];
|
|
205
|
+
const alternative = verdictSteps.find(v => v.step !== recommendedStep && v.disposition !== 'not_configured');
|
|
206
|
+
if (cause !== 'ran_no_verdict' && recommendedStep && alternative
|
|
207
|
+
&& !next.some(n => n.text.includes(`--steps ${alternative.step}`))) {
|
|
208
|
+
next.push({ text: `ck run ${prUrl} --steps ${alternative.step}`, recommended: false });
|
|
209
|
+
}
|
|
210
|
+
// Skipped steps of any type bear on the outcome, so all of them are listed. A
|
|
211
|
+
// step that was never dispatched earns a line only when it produces verdicts
|
|
212
|
+
// AND its absence is the problem: a conflict-resolve that sat out explains
|
|
213
|
+
// nothing about a missing verdict, and on a run that was never going to be
|
|
214
|
+
// judged, `why` has already said so in a sentence.
|
|
215
|
+
const order = new Map(workflowSteps.map((s, i) => [s.name, i]));
|
|
216
|
+
const didNotRun = [
|
|
217
|
+
...(outcomes?.skipped ?? []).map(s => ({ step: s.step, note: `skipped — ${s.reason}` })),
|
|
218
|
+
...(expected ? [] : verdictSteps
|
|
219
|
+
.filter(v => v.disposition === 'not_dispatched')
|
|
220
|
+
.map(v => ({ step: v.step, note: 'not dispatched' }))),
|
|
221
|
+
].sort((a, b) => (order.get(a.step) ?? Infinity) - (order.get(b.step) ?? Infinity));
|
|
222
|
+
return { expected, cause, verdictSteps, ran, didNotRun, why, next, notes };
|
|
223
|
+
}
|
|
224
|
+
function pad(entries) {
|
|
225
|
+
return entries.reduce((w, e) => Math.max(w, e.step.length), 0);
|
|
226
|
+
}
|
|
227
|
+
/** Renders the report as plain lines, unindented and uncoloured. The caller
|
|
228
|
+
* owns presentation; this owns what is said and in what order. */
|
|
229
|
+
export function renderNoVerdictReport(report) {
|
|
230
|
+
const lines = ['no verdict this run'];
|
|
231
|
+
const width = Math.max(pad(report.ran), pad(report.didNotRun));
|
|
232
|
+
if (report.ran.length > 0) {
|
|
233
|
+
lines.push('', 'ran');
|
|
234
|
+
for (const { step, detail } of report.ran) {
|
|
235
|
+
lines.push(detail ? ` ${step.padEnd(width)} ${detail}` : ` ${step}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (report.didNotRun.length > 0) {
|
|
239
|
+
lines.push('', 'did not run');
|
|
240
|
+
for (const { step, note } of report.didNotRun) {
|
|
241
|
+
lines.push(` ${step.padEnd(width)} ${note}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (report.why.length > 0) {
|
|
245
|
+
lines.push('', 'why');
|
|
246
|
+
for (const line of report.why)
|
|
247
|
+
lines.push(` ${line}`);
|
|
248
|
+
}
|
|
249
|
+
if (report.next.length > 0) {
|
|
250
|
+
lines.push('', 'next');
|
|
251
|
+
for (const { text, recommended } of report.next) {
|
|
252
|
+
lines.push(recommended ? `→ ${text}` : ` ${text}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (report.notes.length > 0) {
|
|
256
|
+
lines.push('');
|
|
257
|
+
for (const note of report.notes)
|
|
258
|
+
lines.push(` ${note}`);
|
|
259
|
+
}
|
|
260
|
+
return lines;
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=no-verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-verdict.js","sourceRoot":"","sources":["../../src/lib/no-verdict.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/pr-lock.d.ts
CHANGED
|
@@ -1,4 +1,40 @@
|
|
|
1
|
+
export declare const INSTANCE_ID: string;
|
|
2
|
+
/** What a holder writes into its lock file so other instances can reason about it. */
|
|
3
|
+
interface LockRecord {
|
|
4
|
+
instance: string;
|
|
5
|
+
host: string;
|
|
6
|
+
pid: number;
|
|
7
|
+
/** Head SHA being worked on. Diagnostic — the lock itself is per-PR. */
|
|
8
|
+
sha?: string;
|
|
9
|
+
startedAt: number;
|
|
10
|
+
heartbeatAt: number;
|
|
11
|
+
}
|
|
1
12
|
export declare function handleLockSignal(signal: NodeJS.Signals): void;
|
|
2
|
-
|
|
13
|
+
/** Why a lock held by someone else was taken over. Exported for tests and logging. */
|
|
14
|
+
export type StealReason = 'holder_dead' | 'heartbeat_stale' | 'legacy_lock_expired';
|
|
15
|
+
export declare function evaluateExistingLock(record: LockRecord | null, mtimeMs: number, now: number): {
|
|
16
|
+
stealable: true;
|
|
17
|
+
reason: StealReason;
|
|
18
|
+
} | {
|
|
19
|
+
stealable: false;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Take the local lock for a PR. Returns false when another live process holds it.
|
|
23
|
+
*
|
|
24
|
+
* The lock records who holds it, so a peer can tell a live holder from a crashed
|
|
25
|
+
* one instead of guessing from a fixed timeout.
|
|
26
|
+
*/
|
|
27
|
+
export declare function acquirePRLock(owner: string, repo: string, pr: number, sha?: string): boolean;
|
|
28
|
+
/** Describe the current holder of a PR's lock, for diagnostics. Null when free. */
|
|
29
|
+
export declare function inspectPRLock(owner: string, repo: string, pr: number): LockRecord | null;
|
|
30
|
+
/**
|
|
31
|
+
* Release the local lock — but only if this process still owns it.
|
|
32
|
+
*
|
|
33
|
+
* The ownership check is what stops a late release from clearing a lock that a
|
|
34
|
+
* peer has since taken over: without it, an instance whose lock was stolen as
|
|
35
|
+
* stale would delete the new holder's lock on its way out, and a third instance
|
|
36
|
+
* would walk straight in on a PR two others were already working.
|
|
37
|
+
*/
|
|
3
38
|
export declare function releasePRLock(owner: string, repo: string, pr: number, _sha?: string): void;
|
|
39
|
+
export {};
|
|
4
40
|
//# sourceMappingURL=pr-lock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-lock.d.ts","sourceRoot":"","sources":["../../src/lib/pr-lock.ts"],"names":[],"mappings":"AAwBA,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"pr-lock.d.ts","sourceRoot":"","sources":["../../src/lib/pr-lock.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,WAAW,QAAmE,CAAA;AAE3F,sFAAsF;AACtF,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAmBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAU7D;AA8CD,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,iBAAiB,GAAG,qBAAqB,CAAA;AAMnF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAA;CAAE,CAgBjE;AA6BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CA2C5F;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAExF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAW1F"}
|
package/dist/lib/pr-lock.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
import { openSync, closeSync, rmSync, mkdirSync, statSync } from 'fs';
|
|
1
|
+
import { openSync, closeSync, writeSync, readFileSync, rmSync, mkdirSync, statSync, utimesSync } from 'fs';
|
|
2
|
+
import { hostname } from 'os';
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
2
4
|
import { homedir } from 'os';
|
|
3
5
|
import { join } from 'path';
|
|
4
6
|
const LOCK_DIR = join(homedir(), '.crosscheck', 'locks');
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
|
|
7
|
+
// A lock is abandoned when its holder has not heartbeated within this window.
|
|
8
|
+
// The holder refreshes every HEARTBEAT_INTERVAL_MS, so this only trips on a
|
|
9
|
+
// process that died without running its cleanup — a SIGKILL, an OOM, a power cut.
|
|
10
|
+
//
|
|
11
|
+
// It used to be 20 minutes measured from the lock file's CREATION time, which is
|
|
12
|
+
// the wrong clock: a review that legitimately ran longer than 20 minutes (routine
|
|
13
|
+
// on a large PR — the thorough tier alone allows 1200s per reviewer call, before
|
|
14
|
+
// clone, fix and recheck) had its lock declared stale and stolen while it was
|
|
15
|
+
// still running. Measuring from the last heartbeat means a live review is never
|
|
16
|
+
// stale no matter how long it takes, which in turn lets this window be short.
|
|
17
|
+
const STALE_MS = 5 * 60 * 1000;
|
|
18
|
+
const HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
19
|
+
// Identifies this process for the lifetime of the process. The random suffix
|
|
20
|
+
// matters: pids are recycled, so host+pid alone can make a dead holder look alive
|
|
21
|
+
// once the OS hands its pid to something else.
|
|
22
|
+
export const INSTANCE_ID = `${hostname()}:${process.pid}:${randomBytes(4).toString('hex')}`;
|
|
23
|
+
// Locks this process holds, by path, with the heartbeat timer keeping each fresh.
|
|
24
|
+
const heldLocks = new Map();
|
|
8
25
|
let signalHandlersRegistered = false;
|
|
9
26
|
// Exported for unit testing. Cleans up local lock files on signal, then —
|
|
10
27
|
// when no other listener will drive termination — restores the default
|
|
@@ -20,13 +37,14 @@ let signalHandlersRegistered = false;
|
|
|
20
37
|
// watch/serve graceful shutdown handlers referenced in the original
|
|
21
38
|
// comment), we let them drive termination via their own finally blocks.
|
|
22
39
|
export function handleLockSignal(signal) {
|
|
23
|
-
for (const
|
|
40
|
+
for (const [path, held] of heldLocks) {
|
|
41
|
+
clearInterval(held.timer);
|
|
24
42
|
try {
|
|
25
|
-
rmSync(
|
|
43
|
+
rmSync(path);
|
|
26
44
|
}
|
|
27
45
|
catch { /* ignore */ }
|
|
28
46
|
}
|
|
29
|
-
|
|
47
|
+
heldLocks.clear();
|
|
30
48
|
if (process.listenerCount(signal) <= 1) {
|
|
31
49
|
process.removeListener(signal, handleLockSignal);
|
|
32
50
|
process.kill(process.pid, signal);
|
|
@@ -42,47 +60,176 @@ function registerSignalCleanup() {
|
|
|
42
60
|
function lockPath(owner, repo, pr) {
|
|
43
61
|
return join(LOCK_DIR, `${owner}-${repo}-${pr}.lock`);
|
|
44
62
|
}
|
|
45
|
-
function
|
|
63
|
+
function readRecord(path) {
|
|
46
64
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
65
|
+
const raw = readFileSync(path, 'utf8').trim();
|
|
66
|
+
// A lock written by an older crosscheck is an empty file. It carries no owner
|
|
67
|
+
// and no heartbeat, so the only honest reading is the file's mtime.
|
|
68
|
+
if (raw === '')
|
|
69
|
+
return null;
|
|
70
|
+
const parsed = JSON.parse(raw);
|
|
71
|
+
if (typeof parsed.instance !== 'string' || typeof parsed.pid !== 'number')
|
|
72
|
+
return null;
|
|
73
|
+
return {
|
|
74
|
+
instance: parsed.instance,
|
|
75
|
+
host: typeof parsed.host === 'string' ? parsed.host : '',
|
|
76
|
+
pid: parsed.pid,
|
|
77
|
+
...(typeof parsed.sha === 'string' && { sha: parsed.sha }),
|
|
78
|
+
startedAt: typeof parsed.startedAt === 'number' ? parsed.startedAt : 0,
|
|
79
|
+
heartbeatAt: typeof parsed.heartbeatAt === 'number' ? parsed.heartbeatAt : 0,
|
|
80
|
+
};
|
|
49
81
|
}
|
|
50
82
|
catch {
|
|
51
|
-
return
|
|
83
|
+
return null;
|
|
52
84
|
}
|
|
53
85
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const path = lockPath(owner, repo, pr);
|
|
86
|
+
// Whether a process is still running on THIS host. Cross-host liveness is
|
|
87
|
+
// unknowable from here, which is what the heartbeat is for.
|
|
88
|
+
function pidAlive(pid) {
|
|
58
89
|
try {
|
|
59
|
-
|
|
60
|
-
closeSync(fd);
|
|
61
|
-
activeLocks.add(path);
|
|
90
|
+
process.kill(pid, 0);
|
|
62
91
|
return true;
|
|
63
92
|
}
|
|
64
93
|
catch (e) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
94
|
+
// EPERM means the pid exists but belongs to another user — alive, not ours.
|
|
95
|
+
return e.code === 'EPERM';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Decide whether an existing lock may be taken over. Ownership is proven two ways,
|
|
99
|
+
// in order of strength: a same-host holder whose pid is gone is definitively dead;
|
|
100
|
+
// otherwise the heartbeat is the only evidence, and only silence beyond STALE_MS
|
|
101
|
+
// counts. A lock held by a live peer is never stolen, however long it has run.
|
|
102
|
+
export function evaluateExistingLock(record, mtimeMs, now) {
|
|
103
|
+
if (record === null) {
|
|
104
|
+
// Legacy empty lock file: mtime is all there is, and it is never refreshed,
|
|
105
|
+
// so fall back to the old generous window rather than stealing from a run
|
|
106
|
+
// that may still be going.
|
|
107
|
+
return now - mtimeMs > 20 * 60 * 1000
|
|
108
|
+
? { stealable: true, reason: 'legacy_lock_expired' }
|
|
109
|
+
: { stealable: false };
|
|
110
|
+
}
|
|
111
|
+
if (record.host === hostname() && !pidAlive(record.pid)) {
|
|
112
|
+
return { stealable: true, reason: 'holder_dead' };
|
|
113
|
+
}
|
|
114
|
+
if (now - record.heartbeatAt > STALE_MS) {
|
|
115
|
+
return { stealable: true, reason: 'heartbeat_stale' };
|
|
116
|
+
}
|
|
117
|
+
return { stealable: false };
|
|
118
|
+
}
|
|
119
|
+
function writeRecord(path, record) {
|
|
120
|
+
const fd = openSync(path, 'wx');
|
|
121
|
+
try {
|
|
122
|
+
writeSync(fd, JSON.stringify(record));
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
closeSync(fd);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function startHeartbeat(path, record) {
|
|
129
|
+
const timer = setInterval(() => {
|
|
130
|
+
const held = heldLocks.get(path);
|
|
131
|
+
if (!held)
|
|
132
|
+
return;
|
|
133
|
+
held.record.heartbeatAt = Date.now();
|
|
134
|
+
try {
|
|
135
|
+
// Rewrite rather than touch: the timestamp inside the file is what a peer
|
|
136
|
+
// on another host reads, and mtime alone would not reach it.
|
|
137
|
+
const fd = openSync(path, 'w');
|
|
68
138
|
try {
|
|
69
|
-
|
|
139
|
+
writeSync(fd, JSON.stringify(held.record));
|
|
70
140
|
}
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const fd = openSync(path, 'wx');
|
|
141
|
+
finally {
|
|
74
142
|
closeSync(fd);
|
|
75
|
-
activeLocks.add(path);
|
|
76
|
-
return true;
|
|
77
143
|
}
|
|
78
|
-
|
|
144
|
+
utimesSync(path, new Date(), new Date());
|
|
79
145
|
}
|
|
146
|
+
catch { /* best-effort — a failed beat is covered by the next one */ }
|
|
147
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
148
|
+
// Never hold the event loop open on the heartbeat alone.
|
|
149
|
+
timer.unref?.();
|
|
150
|
+
heldLocks.set(path, { record, timer });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Take the local lock for a PR. Returns false when another live process holds it.
|
|
154
|
+
*
|
|
155
|
+
* The lock records who holds it, so a peer can tell a live holder from a crashed
|
|
156
|
+
* one instead of guessing from a fixed timeout.
|
|
157
|
+
*/
|
|
158
|
+
export function acquirePRLock(owner, repo, pr, sha) {
|
|
159
|
+
mkdirSync(LOCK_DIR, { recursive: true });
|
|
160
|
+
registerSignalCleanup();
|
|
161
|
+
const path = lockPath(owner, repo, pr);
|
|
162
|
+
const now = Date.now();
|
|
163
|
+
const record = {
|
|
164
|
+
instance: INSTANCE_ID,
|
|
165
|
+
host: hostname(),
|
|
166
|
+
pid: process.pid,
|
|
167
|
+
...(sha !== undefined && { sha }),
|
|
168
|
+
startedAt: now,
|
|
169
|
+
heartbeatAt: now,
|
|
170
|
+
};
|
|
171
|
+
try {
|
|
172
|
+
writeRecord(path, record);
|
|
173
|
+
startHeartbeat(path, record);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
if (e.code !== 'EEXIST')
|
|
178
|
+
throw e;
|
|
179
|
+
}
|
|
180
|
+
// Re-entering our own lock is not a collision: the same process asking twice for
|
|
181
|
+
// the same PR means a second event arrived for it, and the caller's own inFlight
|
|
182
|
+
// guard decides that. Report it as taken so the caller's skip path runs.
|
|
183
|
+
const existing = readRecord(path);
|
|
184
|
+
if (existing?.instance === INSTANCE_ID)
|
|
185
|
+
return false;
|
|
186
|
+
let mtimeMs = 0;
|
|
187
|
+
try {
|
|
188
|
+
mtimeMs = statSync(path).mtimeMs;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
const verdict = evaluateExistingLock(existing, mtimeMs, now);
|
|
194
|
+
if (!verdict.stealable)
|
|
195
|
+
return false;
|
|
196
|
+
try {
|
|
197
|
+
rmSync(path);
|
|
198
|
+
}
|
|
199
|
+
catch { /* already gone */ }
|
|
200
|
+
try {
|
|
201
|
+
writeRecord(path, record);
|
|
202
|
+
startHeartbeat(path, record);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// Lost the race to another instance that cleared the same stale lock.
|
|
80
207
|
return false;
|
|
81
208
|
}
|
|
82
209
|
}
|
|
210
|
+
/** Describe the current holder of a PR's lock, for diagnostics. Null when free. */
|
|
211
|
+
export function inspectPRLock(owner, repo, pr) {
|
|
212
|
+
return readRecord(lockPath(owner, repo, pr));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Release the local lock — but only if this process still owns it.
|
|
216
|
+
*
|
|
217
|
+
* The ownership check is what stops a late release from clearing a lock that a
|
|
218
|
+
* peer has since taken over: without it, an instance whose lock was stolen as
|
|
219
|
+
* stale would delete the new holder's lock on its way out, and a third instance
|
|
220
|
+
* would walk straight in on a PR two others were already working.
|
|
221
|
+
*/
|
|
83
222
|
export function releasePRLock(owner, repo, pr, _sha) {
|
|
84
223
|
const path = lockPath(owner, repo, pr);
|
|
85
|
-
|
|
224
|
+
const held = heldLocks.get(path);
|
|
225
|
+
if (held)
|
|
226
|
+
clearInterval(held.timer);
|
|
227
|
+
heldLocks.delete(path);
|
|
228
|
+
const existing = readRecord(path);
|
|
229
|
+
// An unreadable or legacy record cannot prove another owner. Removing it matches
|
|
230
|
+
// the pre-ownership behaviour and keeps an upgrade from stranding old locks.
|
|
231
|
+
if (existing !== null && existing.instance !== INSTANCE_ID)
|
|
232
|
+
return;
|
|
86
233
|
try {
|
|
87
234
|
rmSync(path);
|
|
88
235
|
}
|
package/dist/lib/pr-lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-lock.js","sourceRoot":"","sources":["../../src/lib/pr-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"pr-lock.js","sourceRoot":"","sources":["../../src/lib/pr-lock.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/pr-spec.d.ts
CHANGED
|
@@ -4,6 +4,13 @@ export interface PRRef {
|
|
|
4
4
|
number: number;
|
|
5
5
|
/** Canonical https URL for the PR. */
|
|
6
6
|
url: string;
|
|
7
|
+
/**
|
|
8
|
+
* The `#issuecomment-<id>` anchor, when the token carried one. Callers that act
|
|
9
|
+
* on a specific review comment use it to target exactly the comment the user
|
|
10
|
+
* pasted; before this existed the fragment was silently dropped and `ck fix`
|
|
11
|
+
* ran against whatever the latest review happened to be.
|
|
12
|
+
*/
|
|
13
|
+
commentId?: number;
|
|
7
14
|
}
|
|
8
15
|
export interface ParsePRSpecOptions {
|
|
9
16
|
/** Hard cap on the number of PRs a single spec may expand to. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-spec.d.ts","sourceRoot":"","sources":["../../src/lib/pr-spec.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"pr-spec.d.ts","sourceRoot":"","sources":["../../src/lib/pr-spec.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAA;IACX;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,eAAe,MAAM,CAAA;AA2BlC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,KAAK,EAAE,CAsDnF"}
|