prreviewbuddy 0.64.2 → 0.70.2
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/CHANGELOG.md +128 -0
- package/dist/{delete_review-l4p_p6oh.js → delete_review-CRPRIRKg.js} +7013 -6596
- package/dist/main.js +2 -328
- package/dist/server.js +697 -7
- package/package.json +1 -1
- package/static/chunk-BsU84lX4.js +1 -0
- package/static/chunk-C8kkSv38.js +1 -0
- package/static/chunk-DqEA-C0S.js +1 -0
- package/static/help.js +1 -1
- package/static/notifications.js +1 -1
- package/static/reviews.js +1 -1
- package/static/settings.js +1 -1
- package/static/shell.css +84 -0
- package/static/sidebar.css +2 -0
- package/static/workspace.js +63 -63
- package/static/chunk-BadSI8AL.js +0 -1
- package/static/chunk-CXf6hwXL.js +0 -1
- package/static/chunk-CndkgJPh.js +0 -1
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $
|
|
2
|
+
import { $ as MAX_WORKSPACE_PORT, $r as resolveAgent, A as relativeTime, B as readIndexToken, Dn as MANAGED_ROOT, F as runBatch, Fn as describeSubject, G as reviewsUrl, Gn as loadJob, In as findExistingReview, Jr as resolveTarget, Kr as WITHHELD_REASON, Mn as removeWorktree, Pn as describeMovement, Q as DEFAULT_WORKSPACE_PORT, Qn as clearClaim, Qr as git, R as queueForReview, Tn as modelHelpLines, U as ensureServer, V as bootstrapUrl, Vn as allJobs, X as workspaceUrl, Xn as progressSteps, Y as stopServer, Zn as purposeOf, _ as liveUpdateFor, _r as reviewedRepositories, _t as telemetryUploadConsent, a as wasBlocked, ai as STORE_ROOT, b as recordKissRun, bn as forgeResolver, bt as PACKAGE_NAME, ct as reanalyseReview, d as mergeUpdateInto, dr as mutateWorkspace, dt as append, ei as resolveModel, et as MIN_WORKSPACE_PORT, f as updateReview, fr as positionInLineage, g as UpdateAlreadyRunningError, gt as recordTelemetryUploadConsent, hr as recentWorkspaces, ht as deliver, i as liveJobsFor, ii as CONFIG_PATH, it as writePortPreference, j as batchIdentity, jn as readMarker, li as detectAgents, lr as loadWorkspace, mt as stamp, n as deleteReview$1, ni as readAgentPreference, oi as AGENT_IDS, ot as prepareReview, pt as record$1, qn as PHASES, qr as kissOutcome, ri as writeAgentPreference, rt as statedWorkspacePort, si as agentById, st as settleReview, tr as followedRefName, tt as parseWorkspacePort, ur as matchingWorkspaceIds, ut as startJob, v as runningJobs, vr as summarise, vt as telemetryUploadEnabled, yt as BUILD_VERSION } from "./delete_review-CRPRIRKg.js";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -605,217 +605,6 @@ async function pick(ask, options) {
|
|
|
605
605
|
return null;
|
|
606
606
|
}
|
|
607
607
|
//#endregion
|
|
608
|
-
//#region ../../packages/review-harness/src/workspace/existing_review.ts
|
|
609
|
-
/**
|
|
610
|
-
* Is there already a review of what I am standing in?
|
|
611
|
-
*
|
|
612
|
-
* `prreviewbuddy review` used to mean "make another review", every time, which made the most-typed
|
|
613
|
-
* command in the product the one way to end up with two reviews of one branch and no idea which was
|
|
614
|
-
* which. It now means **"give me the review of this branch"**: it creates one the first time and
|
|
615
|
-
* hands back the existing one afterwards. Starting again is `--fresh`, deliberately its own flag.
|
|
616
|
-
*
|
|
617
|
-
* Three properties matter here and all of them are about the answer being trustworthy rather than
|
|
618
|
-
* merely fast:
|
|
619
|
-
*
|
|
620
|
-
* - **It runs before the change set is built.** Building one reads every changed file and its
|
|
621
|
-
* patch, which on a large change is seconds of work thrown away the moment the answer turns out
|
|
622
|
-
* to be "you already have this". The lookup is a branch name and a directory.
|
|
623
|
-
* - **It says how old the answer is.** Reviews are kept for thirty days, so on a long-lived branch
|
|
624
|
-
* the honest answer is often "yes, from three weeks and fifty commits ago". Handing back a link
|
|
625
|
-
* with no age attached would turn a helpful reply into a trap.
|
|
626
|
-
* - **It answers the question that was asked.** A branch is not the whole of what a review is of:
|
|
627
|
-
* `--base develop` and `--base main` are two different comparisons of the same commits. Keying
|
|
628
|
-
* only on the branch meant a reviewer who named a base was handed a review made against a
|
|
629
|
-
* different one, under a sentence saying it was a review of what they asked for. See
|
|
630
|
-
* `findExistingReview`.
|
|
631
|
-
*/
|
|
632
|
-
/**
|
|
633
|
-
* The latest review of a resolved target, or null.
|
|
634
|
-
*
|
|
635
|
-
* Takes a target rather than a path because the current branch stopped being the answer the
|
|
636
|
-
* moment a review could run in a worktree. Reviewing `!45` from `main` must find the lineage of
|
|
637
|
-
* `app-1276`, and file into it, or the one-review-per-branch rule holds only for reviewers who
|
|
638
|
-
* happen to be standing in the right place.
|
|
639
|
-
*
|
|
640
|
-
* **The ref is part of the question, not a detail of the answer.** `--branch feat/foo` and
|
|
641
|
-
* `--branch origin/feat/foo` name one branch and can be two different commits, and a review
|
|
642
|
-
* follows exactly one of them for the rest of its life. So the review of the local branch is not
|
|
643
|
-
* an answer to a command that named the remote ref, even on the day they are level. A target with
|
|
644
|
-
* no ref is read as `refs/heads/<branch>`, which is both the pull request case and every review
|
|
645
|
-
* stored before 0.15.0; see `lineageKeyFor`.
|
|
646
|
-
*
|
|
647
|
-
* **An explicit base narrows the lineage.** A base the reviewer named is part of what they asked
|
|
648
|
-
* for, so only a review made against that same base can be the answer to it; the newest review of
|
|
649
|
-
* this branch against some other base is a different comparison wearing the right branch name. A
|
|
650
|
-
* base nobody named narrows nothing, because the command claimed nothing about it: any review of
|
|
651
|
-
* the branch is a truthful answer to "give me the review of this branch".
|
|
652
|
-
*
|
|
653
|
-
* **A pull request number narrows it too.** A request target has no ref, so its lineage key is the
|
|
654
|
-
* head branch name, and two fork requests both called `patch-1` share one. Only a review recording
|
|
655
|
-
* the same number answers a named request. One recording no number is unknown rather than a
|
|
656
|
-
* match, the same strictness as an empty base: the cost is one redundant review, never the wrong one.
|
|
657
|
-
*/
|
|
658
|
-
async function findExistingReview(target) {
|
|
659
|
-
const groups = groupByLineage(recentWorkspaces(Number.MAX_SAFE_INTEGER));
|
|
660
|
-
const wanted = target.commit ? lineageKeyFor({
|
|
661
|
-
kind: "commit",
|
|
662
|
-
repoPath: target.originRepoPath,
|
|
663
|
-
sha: target.commit.sha
|
|
664
|
-
}) : lineageKeyFor({
|
|
665
|
-
kind: "branch",
|
|
666
|
-
repoPath: target.originRepoPath,
|
|
667
|
-
branch: target.branch,
|
|
668
|
-
followedRef: target.ref
|
|
669
|
-
});
|
|
670
|
-
const group = groups.find((entry) => entry.key === wanted);
|
|
671
|
-
if (!group) return null;
|
|
672
|
-
const requested = target.pullRequest?.number;
|
|
673
|
-
const history = target.commit ? group.history : group.history.filter((review) => target.base ? madeAgainst(target.base)(review) : true).filter((review) => requested ? review.prNumber === requested : true);
|
|
674
|
-
if (history.length === 0) return null;
|
|
675
|
-
const summary = history[0];
|
|
676
|
-
const found = {
|
|
677
|
-
id: summary.id,
|
|
678
|
-
token: summary.token,
|
|
679
|
-
title: summary.title,
|
|
680
|
-
branch: summary.branch,
|
|
681
|
-
baseRef: summary.baseRef,
|
|
682
|
-
...summary.headResolvedRef ? { headResolvedRef: summary.headResolvedRef } : {},
|
|
683
|
-
...summary.targetType === "commit" ? { targetType: "commit" } : {},
|
|
684
|
-
createdAt: summary.createdAt,
|
|
685
|
-
commitSha: summary.commitSha ?? null,
|
|
686
|
-
commitsBehind: null,
|
|
687
|
-
baselineGone: false,
|
|
688
|
-
total: history.length,
|
|
689
|
-
inProgress: false
|
|
690
|
-
};
|
|
691
|
-
const workspace = loadWorkspace(summary.id);
|
|
692
|
-
if (!workspace) return found;
|
|
693
|
-
const inProgress = isRunning(workspace.jobId);
|
|
694
|
-
try {
|
|
695
|
-
const { freshness, availability } = await checkReview(workspace);
|
|
696
|
-
if (availability) return {
|
|
697
|
-
...found,
|
|
698
|
-
inProgress,
|
|
699
|
-
commitAvailable: availability.state === "available"
|
|
700
|
-
};
|
|
701
|
-
const code = freshness.code;
|
|
702
|
-
const baselineGone = code.rewritten || code.reason === "missing-commit";
|
|
703
|
-
return {
|
|
704
|
-
...found,
|
|
705
|
-
inProgress,
|
|
706
|
-
commitSha: code.reviewedSha ?? found.commitSha,
|
|
707
|
-
commitsBehind: baselineGone ? null : code.state === "current" ? 0 : code.newCommits,
|
|
708
|
-
baselineGone
|
|
709
|
-
};
|
|
710
|
-
} catch {
|
|
711
|
-
return {
|
|
712
|
-
...found,
|
|
713
|
-
inProgress
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
/**
|
|
718
|
-
* Was this review made against the base the reviewer named?
|
|
719
|
-
*
|
|
720
|
-
* Compared in `displayRef`'s short form, which is the form the store holds, so `--base origin/main`
|
|
721
|
-
* and a review made against the default remote branch recognise each other. Anything else is
|
|
722
|
-
* compared as typed: a tag or a sha is a base like any other, and two different spellings of one
|
|
723
|
-
* commit read here as two comparisons, which costs a redundant review rather than a wrong answer.
|
|
724
|
-
*
|
|
725
|
-
* An empty `baseRef` is a review that stopped before it worked out what it was comparing against,
|
|
726
|
-
* and it never matches. Unknown is not the same as "the one you asked for", and this is the one
|
|
727
|
-
* place where guessing would put a review of some other base behind a sentence naming this one.
|
|
728
|
-
* The cost of being strict is small and bounded: a second review starts, of the comparison that
|
|
729
|
-
* was actually requested.
|
|
730
|
-
*/
|
|
731
|
-
function madeAgainst(requested) {
|
|
732
|
-
const wanted = displayRef(requested);
|
|
733
|
-
return (review) => review.baseRef !== "" && displayRef(review.baseRef) === wanted;
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Whether the job behind a stored review is still working. A review with no job never was.
|
|
737
|
-
*
|
|
738
|
-
* A job whose driver has gone is not working, whatever phase its record was left at. Calling it in
|
|
739
|
-
* progress told the reviewer who had just closed a terminal that there was "nothing to do but wait",
|
|
740
|
-
* for a process that no longer existed, until the sweep called it failed half an hour later.
|
|
741
|
-
*/
|
|
742
|
-
function isRunning(jobId) {
|
|
743
|
-
if (!jobId) return false;
|
|
744
|
-
const job = loadJob(jobId);
|
|
745
|
-
return !!job && !isTerminal(job) && !driverGone(job.id);
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* What the review is of, as the clause both surfaces hang off "Made two hours ago".
|
|
749
|
-
*
|
|
750
|
-
* The base is in it because a review is of a comparison rather than of a branch, and the one thing
|
|
751
|
-
* a reviewer cannot see from the outside is which comparison they are being handed. The command
|
|
752
|
-
* that named a base now gets one made against it, but the far commoner case is the command that
|
|
753
|
-
* named none, and there this clause is the only thing that says what was compared.
|
|
754
|
-
*
|
|
755
|
-
* Each half is dropped when it is not known, rather than rendered as a placeholder: a review made
|
|
756
|
-
* before commits were recorded, or one that stopped before it resolved its base, says less rather
|
|
757
|
-
* than saying something untrue.
|
|
758
|
-
*/
|
|
759
|
-
function describeSubject(existing) {
|
|
760
|
-
const parts = [...existing.commitSha ? [`of commit ${existing.commitSha.slice(0, 7)}`] : [], ...existing.baseRef ? [`against ${existing.baseRef}`] : []];
|
|
761
|
-
return parts.length ? `, ${parts.join(" ")}` : "";
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* How far the branch has moved since the review was made, in one sentence.
|
|
765
|
-
*
|
|
766
|
-
* Exported because both surfaces say it and they say the same thing. The sentences around it
|
|
767
|
-
* differ, because one is addressed to a session that must decide not to review anything and the
|
|
768
|
-
* other to a person deciding whether to press update, but this fact reads identically to both.
|
|
769
|
-
*
|
|
770
|
-
* It names the branch rather than the reader's checkout, which 0.15.0 corrected rather than
|
|
771
|
-
* changed. `commitsBehind` comes from `codeFreshness`, which measures by ref name and never reads
|
|
772
|
-
* HEAD, so the fact underneath was always about the branch and the sentence merely called it
|
|
773
|
-
* something else. `--branch` turns that from loose into wrong: a review of `feat/foo` made while
|
|
774
|
-
* standing on `main` would report `feat/foo`'s movement in a sentence claiming it was the
|
|
775
|
-
* reviewer's own working tree.
|
|
776
|
-
*/
|
|
777
|
-
function describeMovement(existing) {
|
|
778
|
-
if (existing.targetType === "commit") return existing.commitAvailable === false ? "That commit is no longer in this repository, so the review can be read but not analysed again." : "A commit does not move, so the review still describes exactly that commit.";
|
|
779
|
-
const subject = followedRefName(existing.branch, existing.headResolvedRef) ?? existing.branch;
|
|
780
|
-
if (existing.baselineGone) return `${subject} has been rebuilt since, so that commit is no longer on it.`;
|
|
781
|
-
if (existing.commitsBehind === null) return `How far ${subject} has moved since could not be worked out.`;
|
|
782
|
-
if (existing.commitsBehind === 0) return `It is of the latest commit on ${subject}.`;
|
|
783
|
-
return `${subject} has moved ${existing.commitsBehind} commit${existing.commitsBehind === 1 ? "" : "s"} since.`;
|
|
784
|
-
}
|
|
785
|
-
//#endregion
|
|
786
|
-
//#region src/batch_schedule.ts
|
|
787
|
-
/**
|
|
788
|
-
* Run `run` over every item, at most `concurrency` at once, and report each one's ending.
|
|
789
|
-
*
|
|
790
|
-
* Never throws for an item: one that rejects is recorded and the rest carry on. Once `signal` is
|
|
791
|
-
* aborted nothing more starts, and what is already running is waited for, because it is the work's
|
|
792
|
-
* own job to notice the abort and stop.
|
|
793
|
-
*/
|
|
794
|
-
async function runBatch(items, options) {
|
|
795
|
-
const outcomes = items.map(() => ({ kind: "not-started" }));
|
|
796
|
-
let next = 0;
|
|
797
|
-
const lane = async () => {
|
|
798
|
-
while (next < items.length && !options.signal?.aborted) {
|
|
799
|
-
const index = next;
|
|
800
|
-
next += 1;
|
|
801
|
-
try {
|
|
802
|
-
outcomes[index] = {
|
|
803
|
-
kind: "done",
|
|
804
|
-
value: await options.run(items[index], index)
|
|
805
|
-
};
|
|
806
|
-
} catch (error) {
|
|
807
|
-
outcomes[index] = {
|
|
808
|
-
kind: "threw",
|
|
809
|
-
error
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
const lanes = Math.max(1, Math.min(options.concurrency, items.length));
|
|
815
|
-
await Promise.all(Array.from({ length: lanes }, lane));
|
|
816
|
-
return outcomes;
|
|
817
|
-
}
|
|
818
|
-
//#endregion
|
|
819
608
|
//#region src/progress.ts
|
|
820
609
|
/**
|
|
821
610
|
* What the review is doing, while it does it.
|
|
@@ -2023,7 +1812,7 @@ async function reviewBatch(command, repoPath, deps = realDeps()) {
|
|
|
2023
1812
|
row.state = { kind: "not-started" };
|
|
2024
1813
|
return;
|
|
2025
1814
|
}
|
|
2026
|
-
const identity =
|
|
1815
|
+
const identity = batchIdentity(target);
|
|
2027
1816
|
const first = claimed.get(identity);
|
|
2028
1817
|
if (first !== void 0) {
|
|
2029
1818
|
row.state = {
|
|
@@ -2636,109 +2425,6 @@ function makeCommandFor(command) {
|
|
|
2636
2425
|
if (command.commitRef) return `prreviewbuddy review --commit ${command.commitRef}`;
|
|
2637
2426
|
return "prreviewbuddy review";
|
|
2638
2427
|
}
|
|
2639
|
-
/** Why an unfinished review is being picked up, when nothing recorded a failure because nothing could. */
|
|
2640
|
-
var DRIVER_GONE = "the command that was running it ended before it finished, most often because its terminal closed.";
|
|
2641
|
-
function failureOf(workspaceId) {
|
|
2642
|
-
const workspace = loadWorkspace(workspaceId);
|
|
2643
|
-
if (!workspace?.jobId) return null;
|
|
2644
|
-
const job = loadJob(workspace.jobId);
|
|
2645
|
-
if (!job) return null;
|
|
2646
|
-
const abandoned = !isTerminal(job) && driverGone(job.id);
|
|
2647
|
-
if (!abandoned && (!isTerminal(job) || job.phase !== "failed")) return null;
|
|
2648
|
-
if (isClaimed(job.id)) return null;
|
|
2649
|
-
return {
|
|
2650
|
-
message: abandoned ? DRIVER_GONE : job.failure?.message ?? "The reason was not recorded.",
|
|
2651
|
-
stopped: !abandoned && job.failure?.stopped === true,
|
|
2652
|
-
agentId: job.env.agentId ?? DEFAULT_AGENT_ID,
|
|
2653
|
-
...job.requestedModel ? { requestedModel: job.requestedModel } : {}
|
|
2654
|
-
};
|
|
2655
|
-
}
|
|
2656
|
-
/**
|
|
2657
|
-
* What to run for a resolved target, decided and started, with nothing printed.
|
|
2658
|
-
*
|
|
2659
|
-
* The first half of `review`, and the half a batch shares: a batch says all of it in one summary
|
|
2660
|
-
* at the end rather than as it goes, so the deciding and the saying have to be apart.
|
|
2661
|
-
*
|
|
2662
|
-
* A review that exists is usually a review that finished, and handing it back rather than making a
|
|
2663
|
-
* second one is the whole rule. A review whose job failed is the exception, and handing that one
|
|
2664
|
-
* back is worse than useless: the reviewer typed a command and would get a link to a page reporting
|
|
2665
|
-
* the failure they just caused, with the way out being a button in a browser. It is the same resume
|
|
2666
|
-
* the workspace's Retry offers, taken here because this is where the person is.
|
|
2667
|
-
*
|
|
2668
|
-
* The target is the one already resolved, not re-derived. A request reference is resolved by
|
|
2669
|
-
* spawning an agent, and resolving it twice risks pinning the job to a different commit than the one
|
|
2670
|
-
* the existing-review check just looked at. It carries the base too, which is why none is passed
|
|
2671
|
-
* alongside it: two copies of one answer make one of them the authority without saying which.
|
|
2672
|
-
*
|
|
2673
|
-
* The agent is the resolved one, not the flag. They are the same string only when somebody typed
|
|
2674
|
-
* one: a machine default, a review's own agent and the first-run picker all reach a decision the
|
|
2675
|
-
* flag never held, and passing the flag threw all three away. The harness reads an absent id as
|
|
2676
|
-
* "whatever the default is", so a configured default of Codex announced Codex and then analysed
|
|
2677
|
-
* with Claude, and nothing said so until the failure named the wrong tool.
|
|
2678
|
-
*/
|
|
2679
|
-
async function prepareReview(target, agentId, fresh, requestedModel) {
|
|
2680
|
-
if (!fresh) {
|
|
2681
|
-
const existing = await findExistingReview(target);
|
|
2682
|
-
if (existing) {
|
|
2683
|
-
const failure = failureOf(existing.id);
|
|
2684
|
-
return failure ? {
|
|
2685
|
-
kind: "resume",
|
|
2686
|
-
existing,
|
|
2687
|
-
failure
|
|
2688
|
-
} : {
|
|
2689
|
-
kind: "existing",
|
|
2690
|
-
existing
|
|
2691
|
-
};
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
return {
|
|
2695
|
-
kind: "new",
|
|
2696
|
-
workspaceId: await startJob({
|
|
2697
|
-
target,
|
|
2698
|
-
agentId,
|
|
2699
|
-
...requestedModel ? { requestedModel } : {}
|
|
2700
|
-
})
|
|
2701
|
-
};
|
|
2702
|
-
}
|
|
2703
|
-
/**
|
|
2704
|
-
* Run a review's job to its end and say whether a review came out of it, with nothing printed.
|
|
2705
|
-
*
|
|
2706
|
-
* Any ending that is not `done`, not only the one that says `failed`. A run that stopped without
|
|
2707
|
-
* recording why still stopped, and reading "not failed" as "finished" is what let a job left at
|
|
2708
|
-
* `analysing` print `Review ready` over a review that does not exist.
|
|
2709
|
-
*
|
|
2710
|
-
* The standard review is also checked for a result. A finished analysis always leaves one -- a
|
|
2711
|
-
* reply that could not be read is a failure and never reaches here -- so a null one is a bug, and
|
|
2712
|
-
* `Review ready . Risk not rated . 0 findings` is the one sentence it must never be reported as:
|
|
2713
|
-
* that reads as a clean bill of health, which is the opposite of what happened. A KISS run's
|
|
2714
|
-
* answer is checked by `drive`, which knows which run it is.
|
|
2715
|
-
*/
|
|
2716
|
-
async function settleReview(workspaceId, reviewType = "standard", signal) {
|
|
2717
|
-
let job;
|
|
2718
|
-
try {
|
|
2719
|
-
job = await runJob(workspaceId, {}, reviewType, signal ? { signal } : {});
|
|
2720
|
-
} catch (error) {
|
|
2721
|
-
return {
|
|
2722
|
-
ok: false,
|
|
2723
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
2724
|
-
stopped: false
|
|
2725
|
-
};
|
|
2726
|
-
}
|
|
2727
|
-
if (job.phase !== "done") return {
|
|
2728
|
-
ok: false,
|
|
2729
|
-
reason: failureMessage(job),
|
|
2730
|
-
stopped: job.failure?.stopped === true
|
|
2731
|
-
};
|
|
2732
|
-
if (reviewType === "standard" && !loadWorkspace(workspaceId)?.session?.result) return {
|
|
2733
|
-
ok: false,
|
|
2734
|
-
reason: "The analysis finished without recording a review.",
|
|
2735
|
-
stopped: false
|
|
2736
|
-
};
|
|
2737
|
-
return {
|
|
2738
|
-
ok: true,
|
|
2739
|
-
job
|
|
2740
|
-
};
|
|
2741
|
-
}
|
|
2742
2428
|
/**
|
|
2743
2429
|
* Run the job to its end, saying what it is doing while it does it.
|
|
2744
2430
|
*
|
|
@@ -2787,18 +2473,6 @@ async function drive(workspaceId, url, kissRun) {
|
|
|
2787
2473
|
return 0;
|
|
2788
2474
|
}
|
|
2789
2475
|
/**
|
|
2790
|
-
* Why a run ended, when it did not end by finishing.
|
|
2791
|
-
*
|
|
2792
|
-
* A job that recorded a failure says so in its own words. One that did not is a driver that went
|
|
2793
|
-
* away mid-phase -- killed, or crashed past the point where it could write -- and the honest thing
|
|
2794
|
-
* to say is which phase it was in, because that is the phase the next run picks up from.
|
|
2795
|
-
*/
|
|
2796
|
-
function failureMessage(job) {
|
|
2797
|
-
if (job.failure?.message) return job.failure.message;
|
|
2798
|
-
const doing = PHASES[job.phase].doing;
|
|
2799
|
-
return `The reason was not recorded. It was ${doing.charAt(0).toLowerCase()}${doing.slice(1)} when it stopped.`;
|
|
2800
|
-
}
|
|
2801
|
-
/**
|
|
2802
2476
|
* Every link this command prints, and the reason they are not plain workspace URLs.
|
|
2803
2477
|
*
|
|
2804
2478
|
* A reviewer who only ever runs this command was never sent to the reviews index, so their browser
|