prreviewbuddy 0.95.2 → 0.96.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.96.0
4
+
5
+ **Quick Review.** Press Space on a review in the Reviews list, or choose Quick Review from its menu
6
+ or above the list, to read its brief in a window over the list: why the change exists, how things
7
+ work today and what changes, what it depends on and what depends on it, its issues and questions by
8
+ impact, what needs your decision, and the commit it describes with whether newer commits have
9
+ arrived. J and K scroll, H and L (or the arrow keys) go to the previous and next review in the list
10
+ as filtered and sorted, M opens the review's menu, and Escape returns to the list where you left it.
11
+ Opening a brief calls no model and marks nothing as read or reviewed. The review menu on the list
12
+ moves from Space to M, which the first Quick Review says once, and each control shows its key. Deleting a
13
+ review from its menu in Quick Review keeps Quick Review open on the next review.
14
+
15
+ **Your outcome.** Record Reviewed: no concerns, Needs changes, Needs a decision / answer, Needs
16
+ deeper review or Later, kept in this workspace only and never sent to the forge. The first three
17
+ also mark the review Reviewed and set the point that "Since your last review" is measured from:
18
+ the new commits, which findings were addressed in code or marked resolved through discussion, what
19
+ is new and what is still open. Needs deeper review and Later are shown beside your verdict and never
20
+ replace it. An outcome recorded on an earlier commit or an earlier analysis of
21
+ the same commit is shown as such, and one recorded against a brief that has since changed is
22
+ refused and the brief refreshed.
23
+
24
+ **Dependencies.** Stacked pull requests are detected from the base and head repository and branch,
25
+ with unclear cases offered as "Possibly stacked". You can link one review to another, and a
26
+ description that says it depends on a reviewed pull request is offered as a suggestion to confirm
27
+ or dismiss. A stacked branch can be marked Not a dependency, kept on view with what was detected
28
+ and undone, and detected afresh if either branch changes.
29
+
30
+ **Before and after.** New analyses and updates describe how the code behaves at the base and how
31
+ it behaves after the change. Earlier reviews say this isn't available rather than guessing.
32
+
3
33
  ## 0.95.2
4
34
 
5
35
  `review --no-wait` without `--json` no longer prints advice meant for MCP clients, such as tool
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $a as TargetError, $r as findExistingReview, $t as statedWorkspacePort, An as BUILD_VERSION, Br as modelHelpLines, Bt as readServerRecord, Ci as driverGone, Di as followedRefName, Dn as recordTelemetryUploadConsent, Dt as endAskRequest, E as relativeTime, En as deliver$1, Eo as cleanName, Fi as loadWorkspace, Fo as STORE_ROOT, Ft as readIndexToken, Gt as stopServer, Hi as reviewedCommit, I as collectionHash, Ii as matchingWorkspaceIds, It as bootstrapUrl, Ja as WITHHELD_REASON, Jr as readMarker, Jt as DEFAULT_WORKSPACE_PORT, Ki as workspaceRevision, Kt as workspaceUrl, Na as findingStatus, Nt as readAskRequest, On as telemetryUploadConsent, Ot as forgetAskRequest, Pi as lineagePosition, Pt as recordAskAccepted, Qr as describeSubject, Ri as positionInLineage, Rt as ensureServer, Sa as explanationOf, Si as clearClaim, Sn as append, St as findingRef, T as runBatch, Tn as stamp, Tt as reviewTag, Ui as reviewedRepositories, Vi as recentWorkspaces, Vt as reviewsUrl, Wi as summarise, Wr as MANAGED_ROOT, Xa as resolveTarget, Xt as MIN_WORKSPACE_PORT, Ya as kissOutcome, Yr as removeWorktree, Yt as MAX_WORKSPACE_PORT, Zr as describeMovement, Zt as parseWorkspacePort, _a as reviewerDispositions, _n as prepareReview, _o as agentIdentityOf, _t as modelProvenanceOf, ai as forgeResolver, ao as git, bi as progressSteps, bo as parkedReason, ci as allJobs, co as resolveModel, do as readAgentPreference, dt as RUNNER_KINDS, en as writePortPreference, et as deliver, fa as isIssueOutstanding, fi as isTerminal$1, fn as UpdateAlreadyRunningError, fo as writeAgentPreference, gi as PHASES, gt as applyUpdate, hn as failureOf, ho as agentById, ht as spawnRunner, i as deleteReview$1, ja as findingAssessment, jn as PACKAGE_NAME, jt as isTerminalAsk, kn as telemetryUploadEnabled, kt as interruption, li as attemptIdOf, lo as agentIdOf, lt as queueForReview, mn as runningJobs, mo as AGENT_IDS, mt as runDetached, no as AmbiguousRefError, nt as missedDelivery, o as liveJobsFor, pa as isQuestionOutstanding, pi as loadJob, pn as liveUpdateFor, po as CONFIG_PATH, rt as openDestination, s as wasBlocked, sn as parkedUpdateRefusal, so as resolveAgent, t as MCP_SERVER_INFO_NAME, tn as COMMIT_REVIEW_CANNOT_UPDATE, tt as isDelivered, u as recordKissRun, va as verdictClosure, vn as settleReview, vo as detectAgents, wi as isClaimed, x as batchIdentity, xi as purposeOf, xn as startJob, xo as runnableAgents, yn as reanalyseReview, yo as isAgentId, zt as healthyPort } from "./mcp_tools-DDbcXys2.js";
2
+ import { $r as describeSubject, An as telemetryUploadEnabled, Ao as readAgentPreference, At as interruption, Bi as matchingWorkspaceIds, Bo as runnableAgents, Bt as healthyPort, Ci as clearClaim, Cn as append, Ct as findingRef, Dn as deliver$1, Do as resolveModel, E as relativeTime, En as stamp, Eo as resolveAgent, Et as reviewTag, Ft as recordAskAccepted, Go as cleanName, Gr as MANAGED_ROOT, Ht as reviewsUrl, Ia as reviewerDispositions, Io as agentIdentityOf, It as readIndexToken, Ji as reviewedRepositories, Ki as recentWorkspaces, Kt as stopServer, L as collectionHash, La as verdictClosure, Lo as detectAgents, Lt as bootstrapUrl, Ma as isQuestionOutstanding, Mn as PACKAGE_NAME, Mo as CONFIG_PATH, Mt as isTerminalAsk, No as AGENT_IDS, Oi as followedRefName, On as recordTelemetryUploadConsent, Oo as agentIdOf, Ot as endAskRequest, Po as agentById, Pt as readAskRequest, Qa as findingStatus, Qr as describeMovement, Qt as parseWorkspacePort, Ri as lineagePosition, Ro as isAgentId, Si as purposeOf, Sn as startJob, T as runBatch, Ti as isClaimed, Ui as positionInLineage, Va as explanationOf, Vr as modelHelpLines, Vt as readServerRecord, Xa as findingAssessment, Xr as removeWorktree, Xt as MAX_WORKSPACE_PORT, Yi as summarise, Yr as readMarker, Yt as DEFAULT_WORKSPACE_PORT, Zi as workspaceRevision, Zt as MIN_WORKSPACE_PORT, _i as PHASES, _t as applyUpdate, bn as reanalyseReview, cn as parkedUpdateRefusal, ei as findExistingReview, en as statedWorkspacePort, ft as RUNNER_KINDS, gn as failureOf, gt as spawnRunner, hn as runningJobs, ho as resolveTarget, ht as runDetached, i as deleteReview$1, it as openDestination, ja as isIssueOutstanding, jn as BUILD_VERSION, jo as writeAgentPreference, kn as telemetryUploadConsent, kt as forgetAskRequest, li as allJobs, mi as loadJob, mn as liveUpdateFor, mo as kissOutcome, nn as COMMIT_REVIEW_CANNOT_UPDATE, nt as isDelivered, o as liveJobsFor, oi as forgeResolver, pi as isTerminal$1, pn as UpdateAlreadyRunningError, po as WITHHELD_REASON, qi as reviewedCommit, qt as workspaceUrl, rt as missedDelivery, s as wasBlocked, t as MCP_SERVER_INFO_NAME, tn as writePortPreference, ts as STORE_ROOT, tt as deliver, u as recordKissRun, ui as attemptIdOf, ut as queueForReview, vn as prepareReview, vo as TargetError, vt as modelProvenanceOf, wi as driverGone, wo as git, x as batchIdentity, xi as progressSteps, xo as AmbiguousRefError, yn as settleReview, zi as loadWorkspace, zo as parkedReason, zt as ensureServer } from "./mcp_tools-DEx9NxAc.js";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
@@ -6556,6 +6556,159 @@ function lifecycleItemLabel(target, lifecycle, activity) {
6556
6556
  return lifecycleAction(target);
6557
6557
  }
6558
6558
  //#endregion
6559
+ //#region ../../packages/review-harness/src/workspace/reviewer_outcome.ts
6560
+ var REVIEWER_OUTCOMES = [
6561
+ {
6562
+ id: "no-concerns",
6563
+ name: "Reviewed: no concerns",
6564
+ baseline: true
6565
+ },
6566
+ {
6567
+ id: "needs-changes",
6568
+ name: "Needs changes",
6569
+ baseline: true
6570
+ },
6571
+ {
6572
+ id: "needs-decision",
6573
+ name: "Needs a decision / answer",
6574
+ baseline: true
6575
+ },
6576
+ {
6577
+ id: "deeper-review",
6578
+ name: "Needs deeper review",
6579
+ baseline: false
6580
+ },
6581
+ {
6582
+ id: "later",
6583
+ name: "Later",
6584
+ baseline: false
6585
+ }
6586
+ ];
6587
+ function parseOutcomeSnapshot(value) {
6588
+ if (typeof value !== "object" || value === null) return null;
6589
+ const raw = value;
6590
+ if (typeof raw.sessionId !== "string" || typeof raw.commitSha !== "string" || typeof raw.analysisRevision !== "string") return null;
6591
+ return {
6592
+ sessionId: raw.sessionId,
6593
+ commitSha: raw.commitSha,
6594
+ analysisRevision: raw.analysisRevision
6595
+ };
6596
+ }
6597
+ function outcomeDefinition(kind) {
6598
+ return REVIEWER_OUTCOMES.find((outcome) => outcome.id === kind);
6599
+ }
6600
+ function isBaselineKind(kind) {
6601
+ return outcomeDefinition(kind)?.baseline === true;
6602
+ }
6603
+ function parseStoredOutcome(value) {
6604
+ if (typeof value !== "object" || value === null) return void 0;
6605
+ const raw = value;
6606
+ if (typeof raw.kind !== "string" || !outcomeDefinition(raw.kind)) return void 0;
6607
+ if (typeof raw.at !== "number" || !Number.isFinite(raw.at)) return void 0;
6608
+ if (typeof raw.commitSha !== "string" || typeof raw.sessionId !== "string") return void 0;
6609
+ return {
6610
+ kind: raw.kind,
6611
+ at: raw.at,
6612
+ commitSha: raw.commitSha,
6613
+ sessionId: raw.sessionId,
6614
+ analysisRevision: typeof raw.analysisRevision === "string" ? raw.analysisRevision : "",
6615
+ ...typeof raw.recordId === "string" ? { recordId: raw.recordId } : {},
6616
+ ...typeof raw.analysisJobId === "string" ? { analysisJobId: raw.analysisJobId } : {},
6617
+ ...Array.isArray(raw.answeredQuestions) ? { answeredQuestions: raw.answeredQuestions.filter((q) => typeof q === "string") } : {}
6618
+ };
6619
+ }
6620
+ function parseOutcomes(value) {
6621
+ if (!Array.isArray(value)) return [];
6622
+ return value.map(parseStoredOutcome).filter((outcome) => outcome !== void 0);
6623
+ }
6624
+ /** Newest last, capped. The baseline lives in its own field, so nothing here has to survive pruning. */
6625
+ function appendOutcome(history, outcome) {
6626
+ return [...history, outcome].slice(-20);
6627
+ }
6628
+ /** The newer of two baselines; a non-baseline outcome never replaces one. */
6629
+ function nextBaseline(current, outcome) {
6630
+ if (!isBaselineKind(outcome.kind)) return current;
6631
+ return !current || outcome.at >= current.at ? outcome : current;
6632
+ }
6633
+ /** The newest baseline across a lineage's records, so a re-analysis that made a new record keeps it. */
6634
+ function lastBaseline(baselines) {
6635
+ let newest;
6636
+ for (const baseline of baselines) if (baseline && (!newest || baseline.at > newest.at)) newest = baseline;
6637
+ return newest;
6638
+ }
6639
+ function sameSha(a, b) {
6640
+ if (!a || !b) return false;
6641
+ const length = Math.min(a.length, b.length);
6642
+ return length >= 7 && a.slice(0, length) === b.slice(0, length);
6643
+ }
6644
+ /** How an outcome relates to the analysis on screen. */
6645
+ function outcomeStanding(outcome, snapshot) {
6646
+ if (!sameSha(outcome.commitSha, snapshot.commitSha)) return "earlier-snapshot";
6647
+ if (outcome.sessionId !== snapshot.sessionId || outcome.analysisRevision !== snapshot.analysisRevision) return "earlier-analysis";
6648
+ return "current";
6649
+ }
6650
+ /**
6651
+ * Verdicts come from the history and from the stored baseline, which is never pruned: twenty
6652
+ * deferrals after a verdict push it out of the history, but not out of the reading.
6653
+ */
6654
+ function readOutcomes(history, snapshot, baseline) {
6655
+ const verdicts = history.filter((outcome) => isBaselineKind(outcome.kind));
6656
+ if (baseline && isBaselineKind(baseline.kind) && !verdicts.some((outcome) => outcome.at === baseline.at && outcome.kind === baseline.kind)) verdicts.push(baseline);
6657
+ verdicts.sort((a, b) => a.at - b.at);
6658
+ const newestVerdict = verdicts.at(-1);
6659
+ if (!snapshot) return newestVerdict ? { previous: {
6660
+ outcome: newestVerdict,
6661
+ standing: "earlier-snapshot"
6662
+ } } : {};
6663
+ const verdict = verdicts.filter((outcome) => outcomeStanding(outcome, snapshot) === "current").at(-1);
6664
+ const deferral = history.filter((outcome) => !isBaselineKind(outcome.kind) && outcomeStanding(outcome, snapshot) === "current" && (!verdict || outcome.at > verdict.at)).at(-1);
6665
+ const reading = {};
6666
+ if (verdict) reading.verdict = verdict;
6667
+ else if (newestVerdict) reading.previous = {
6668
+ outcome: newestVerdict,
6669
+ standing: outcomeStanding(newestVerdict, snapshot)
6670
+ };
6671
+ if (deferral) reading.deferral = deferral;
6672
+ return reading;
6673
+ }
6674
+ /** "Needs changes", or how an older one reads beside a newer analysis. */
6675
+ function outcomeLabel(outcome, standing) {
6676
+ const name = outcomeDefinition(outcome.kind)?.name ?? outcome.kind;
6677
+ if (standing === "current") return name;
6678
+ if (standing === "earlier-analysis") return `Outcome on an earlier analysis: ${name}`;
6679
+ return `${name} on ${outcome.commitSha.slice(0, 7)} (earlier snapshot)`;
6680
+ }
6681
+ //#endregion
6682
+ //#region ../../packages/review-harness/src/workspace/analysis_revision.ts
6683
+ /**
6684
+ * A short fingerprint of the analysis a reviewer is reading: its findings, statuses, notes and text.
6685
+ *
6686
+ * The session id and the commit do not move when a Re-check reconciles findings at the same commit,
6687
+ * so a verdict checked against those two alone would be accepted for findings the reviewer never saw.
6688
+ * The reviewer's own ticks are not part of it: those are the reviewer's acts, not the analysis.
6689
+ */
6690
+ function analysisRevision(workspace) {
6691
+ const result = workspace.session.result;
6692
+ if (!result) return "";
6693
+ return createHash("sha256").update(JSON.stringify(result)).digest("hex").slice(0, 16);
6694
+ }
6695
+ /** The analysed commit, falling back the way `reviewedCommit` does. */
6696
+ function analysedCommit(workspace) {
6697
+ return workspace.session.metadata?.commitSha || workspace.changeSet.data.pullRequest.head.sha || "";
6698
+ }
6699
+ function analysisSnapshot(workspace) {
6700
+ if (!workspace.session.result) return null;
6701
+ return {
6702
+ sessionId: workspace.session.id,
6703
+ commitSha: analysedCommit(workspace),
6704
+ analysisRevision: analysisRevision(workspace)
6705
+ };
6706
+ }
6707
+ /** When the analysis on screen was last produced or reconciled: the upper time bound for its history. */
6708
+ function analysedAt(workspace) {
6709
+ return Math.max(workspace.session.metadata?.generatedAt ?? 0, workspace.lastUpdate?.at ?? 0, workspace.lastRecheck?.at ?? 0, workspace.createdAt);
6710
+ }
6711
+ //#endregion
6559
6712
  //#region ../../packages/review-harness/src/workspace/store.ts
6560
6713
  var WORKSPACES_DIR = join(STORE_ROOT, "workspaces");
6561
6714
  /**
@@ -6750,6 +6903,18 @@ function workspaceRevision(workspace) {
6750
6903
  }
6751
6904
  }
6752
6905
  /**
6906
+ * Moves whenever any review is written or deleted, by this process or another: every write stages a
6907
+ * file in the directory and renames it into place, and a delete unlinks one. One stat, where
6908
+ * `workspaceStamps` is one per review, for a cache to ask whether anything changed at all.
6909
+ */
6910
+ function workspacesRevision() {
6911
+ try {
6912
+ return statSync(WORKSPACES_DIR).mtimeMs;
6913
+ } catch {
6914
+ return 0;
6915
+ }
6916
+ }
6917
+ /**
6753
6918
  * Every stored review as an id and a revision token, without reading one of them.
6754
6919
  *
6755
6920
  * Here rather than in `reviews_revision.ts` because this module owns the layout of `workspaces/`,
@@ -7031,6 +7196,14 @@ function groupByLineage(reviews) {
7031
7196
  }
7032
7197
  return [...groups].map(([key, members]) => {
7033
7198
  const history = [...members].sort((a, b) => b.createdAt - a.createdAt);
7199
+ const [newest] = history;
7200
+ if (newest.outcomeInputs && history.length > 1) {
7201
+ const { outcome: _outcome, deferral: _deferral, ...rest } = newest;
7202
+ history[0] = {
7203
+ ...rest,
7204
+ ...lineageOutcome(newest.outcomeInputs, history.map((review) => review.outcomeInputs))
7205
+ };
7206
+ }
7034
7207
  return {
7035
7208
  key,
7036
7209
  latest: history[0],
@@ -7161,6 +7334,8 @@ function summarise(workspace) {
7161
7334
  })(),
7162
7335
  lastViewedAt: workspace.lastViewedAt,
7163
7336
  completedAt: workspace.completedAt,
7337
+ outcomeInputs: outcomeInputsOf(workspace),
7338
+ ...outcomeSummary(outcomeInputsOf(workspace)),
7164
7339
  pinned: workspace.pinned === true,
7165
7340
  lifecycle: parseLifecycle(workspace.lifecycle),
7166
7341
  paths: files.slice(0, MAX_INDEXED_PATHS).map((file) => file.name),
@@ -7171,6 +7346,42 @@ function summarise(workspace) {
7171
7346
  authorName: workspace.prContext?.pullRequest.author?.name || void 0
7172
7347
  };
7173
7348
  }
7349
+ function outcomeInputsOf(workspace) {
7350
+ const baseline = parseStoredOutcome(workspace.reviewedBaseline);
7351
+ return {
7352
+ history: parseOutcomes(workspace.reviewerOutcomes),
7353
+ snapshot: analysisSnapshot(workspace),
7354
+ ...baseline ? { baseline } : {}
7355
+ };
7356
+ }
7357
+ /**
7358
+ * The newest record's outcomes read with the newest baseline of its whole lineage, as Quick Review
7359
+ * reads them. A fresh analysis makes a record with no baseline of its own, and the verdict on the
7360
+ * one before must still show on the card, as an earlier one, instead of vanishing from it.
7361
+ */
7362
+ function lineageOutcome(newest, lineage) {
7363
+ return outcomeSummary(newest, lastBaseline([newest.baseline, ...lineage.map((inputs) => inputs?.baseline)]));
7364
+ }
7365
+ function outcomeSummary(inputs, baseline = inputs.baseline) {
7366
+ const reading = readOutcomes(inputs.history, inputs.snapshot, baseline);
7367
+ const verdict = reading.verdict ? {
7368
+ outcome: reading.verdict,
7369
+ standing: "current"
7370
+ } : reading.previous;
7371
+ return {
7372
+ ...verdict ? { outcome: {
7373
+ kind: verdict.outcome.kind,
7374
+ standing: verdict.standing,
7375
+ label: outcomeLabel(verdict.outcome, verdict.standing),
7376
+ at: verdict.outcome.at
7377
+ } } : {},
7378
+ ...reading.deferral ? { deferral: {
7379
+ kind: reading.deferral.kind,
7380
+ label: outcomeLabel(reading.deferral, "current"),
7381
+ at: reading.deferral.at
7382
+ } } : {}
7383
+ };
7384
+ }
7174
7385
  /**
7175
7386
  * The commit a review was made from, however the record spells it. Null when it recorded none.
7176
7387
  *
@@ -8386,6 +8597,14 @@ function sanitisePullRequest(value) {
8386
8597
  if (template) pr.fileUrlTemplate = template;
8387
8598
  const author = pullRequestAuthor(value.author);
8388
8599
  if (author) pr.author = author;
8600
+ if (isRecord$1(value.headRepository)) {
8601
+ const owner = label(value.headRepository.owner);
8602
+ const repo = label(value.headRepository.repo);
8603
+ if (owner && repo) pr.headRepository = {
8604
+ owner,
8605
+ repo
8606
+ };
8607
+ }
8389
8608
  return pr;
8390
8609
  }
8391
8610
  /**
@@ -9093,7 +9312,7 @@ function slug(repo) {
9093
9312
  * `baseRefOid` is a diff taken against the wrong *commit*, which is worse because the branch name
9094
9313
  * is right and everything reads as normal. A list nobody asserts is a list somebody tidies.
9095
9314
  */
9096
- var PR_FIELDS = "number,url,title,body,headRefName,headRefOid,baseRefName,baseRefOid,changedFiles,author";
9315
+ var PR_FIELDS = "number,url,title,body,headRefName,headRefOid,baseRefName,baseRefOid,changedFiles,author,headRepository,headRepositoryOwner";
9097
9316
  async function viewRequest(repo, reference, cwd, env) {
9098
9317
  return await ghJson([
9099
9318
  "pr",
@@ -9142,7 +9361,11 @@ function identity(raw) {
9142
9361
  author: author ? {
9143
9362
  name: text$2(author.name),
9144
9363
  handle: text$2(author.login)
9145
- } : null
9364
+ } : null,
9365
+ ...text$2(raw.headRepositoryOwner?.login) && text$2(raw.headRepository?.name) ? { headRepository: {
9366
+ owner: text$2(raw.headRepositoryOwner?.login),
9367
+ repo: text$2(raw.headRepository?.name)
9368
+ } } : {}
9146
9369
  };
9147
9370
  }
9148
9371
  /**
@@ -11337,6 +11560,32 @@ ${diff}`;
11337
11560
  };
11338
11561
  }
11339
11562
  //#endregion
11563
+ //#region ../../packages/review-harness/src/domain/ai_review/behaviour_change.ts
11564
+ /** Both sides present and non-empty, or nothing: half an explanation is not one. */
11565
+ function parseBehaviourChange(value) {
11566
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
11567
+ const raw = value;
11568
+ const before = typeof raw.before === "string" ? raw.before.trim() : "";
11569
+ const after = typeof raw.after === "string" ? raw.after.trim() : "";
11570
+ return before && after ? {
11571
+ before,
11572
+ after
11573
+ } : void 0;
11574
+ }
11575
+ function withBehaviourChange(change) {
11576
+ return change ? { behaviourChange: change } : {};
11577
+ }
11578
+ /**
11579
+ * The behaviourChange an update keeps.
11580
+ *
11581
+ * Restated: the new one. New code and none restated: dropped, because the old one describes a head
11582
+ * that no longer exists. A conversation-only pass read no new code, so the previous one still holds.
11583
+ */
11584
+ function mergeBehaviourChange(previous, restated, codeChanged) {
11585
+ if (restated) return restated;
11586
+ return codeChanged ? void 0 : previous;
11587
+ }
11588
+ //#endregion
11340
11589
  //#region ../../packages/review-harness/src/domain/utils/html.ts
11341
11590
  function escapeHtml(value) {
11342
11591
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -12652,6 +12901,10 @@ var CONSEQUENCE_GUIDANCE = `"consequence" on an issue: the same issue told as wh
12652
12901
  - An issue names a concrete consequence. A request for missing requirements with no concrete consequence is a question for the author, not an issue, and is never raised as both.`;
12653
12902
  /** What "orientation" is for, as the first analysis and an update ask for it. */
12654
12903
  var ORIENTATION_GUIDANCE = `"orientation": one sentence, at most about 25 words, naming the behaviour or product area this change touches, for a reviewer about to decide on its issues (e.g. "This change affects receipt extraction and how duplicate receipt details are combined."). Say what it touches, not whether it is safe, and do not summarise the issues.`;
12904
+ /** The behaviourChange shape, written out whole for every prompt that asks for it. */
12905
+ var BEHAVIOUR_CHANGE_FIELD = "\"behaviourChange\"?: { \"before\": string, \"after\": string }";
12906
+ /** What "behaviourChange" is for, as the first analysis and an update ask for it. */
12907
+ var BEHAVIOUR_CHANGE_GUIDANCE = `"behaviourChange": how the behaviour this pull request touches works today and how it works with the whole pull request applied, for a reviewer who knows the product. "before" is the behaviour at the base the pull request targets; "after" is the behaviour at the head you analysed. One to three plain sentences each, leading with what a user or the system experiences, not with the implementation (e.g. before: "A failed payment is shown as an error and the order is cancelled.", after: "A failed payment is retried twice before the order is cancelled, and the customer is emailed after the last attempt."). Describe the whole pull request, not one commit. Omit the field entirely when the code you read does not establish how it works today: never guess "before".`;
12655
12908
  /**
12656
12909
  * How an update keeps a prior finding's consequence and resolution true, beside its status.
12657
12910
  *
@@ -13458,6 +13711,7 @@ var STRUCTURED_OUTPUT_INSTRUCTIONS = `In addition to the review principles above
13458
13711
  },
13459
13712
  "overallRisk": "low" | "medium" | "high",
13460
13713
  "orientation": string,
13714
+ ${BEHAVIOUR_CHANGE_FIELD},
13461
13715
  "sections": [{
13462
13716
  "id": string,
13463
13717
  "order": number,
@@ -13495,6 +13749,7 @@ Field guidance:
13495
13749
  - ${CONSEQUENCE_GUIDANCE}
13496
13750
  - ${QUESTION_FRAMING_GUIDANCE}
13497
13751
  - ${ORIENTATION_GUIDANCE}
13752
+ - ${BEHAVIOUR_CHANGE_GUIDANCE}
13498
13753
  - "existingDiscussion" on an issue: set this only when a thread in the PR DISCUSSION CONTEXT block already raises this same concern, and you have independently confirmed from the diff that it still applies. "threadId" must be copied verbatim from that thread's [T_...] marker — never invent an id, never guess, and omit the field entirely if no thread matches. Set "relation" to "same" when the thread already makes this exact point, or "related" when it discusses this area without covering the finding — a reviewer acts differently on "someone said this already" than on "someone was looking near here". Still write the issue normally; this field only records that the point was raised before, so the reviewer is not shown it as a fresh discovery. Do not use it to pad the review with issues you would otherwise have left out.
13499
13754
  - "confidence" on an issue: how sure you are the issue is real, on the same high/medium/low scale as a question's "importance". This is a separate axis from "severity", which is how much it would matter if real — a critical issue you are unsure about and a nit you are certain of are different things to act on, so do not let one field stand in for the other. Use LOW when you are reasoning from a diff excerpt without the surrounding code, and say what would confirm it in the description. Omit the field rather than guessing a middle value.
13500
13755
  - "questions": each one is a genuine open item the reviewer needs answered before they can sign off, not a rhetorical prompt. "importance" is how much attention it deserves, on the same high/medium/low scale as a section's "reviewAttention" (e.g. a question about a possibly-exposed secret is "high"; a naming preference is "low"). "whyThisMatters" is 1-2 sentences explaining the concrete consequence of the question going unanswered — not a restatement of the question itself.
@@ -13553,6 +13808,7 @@ function parseUnchecked(raw) {
13553
13808
  summary,
13554
13809
  overallRisk: RISKS.includes(obj.overallRisk) ? obj.overallRisk : "medium",
13555
13810
  ...asString$1(obj.orientation).trim() ? { orientation: asString$1(obj.orientation).trim() } : {},
13811
+ ...withBehaviourChange(parseBehaviourChange(obj.behaviourChange)),
13556
13812
  sections: normalizeSections(obj.sections),
13557
13813
  issues: normalizeIssues(obj.issues),
13558
13814
  questions: normalizeQuestions(obj.questions),
@@ -18226,6 +18482,7 @@ var RECONCILIATION_SHAPE = `{
18226
18482
  }],
18227
18483
  "summary": string,
18228
18484
  "orientation": string,
18485
+ ${BEHAVIOUR_CHANGE_FIELD},
18229
18486
  "overallRisk": "low" | "medium" | "high",
18230
18487
  "recommendation": string,
18231
18488
  "nextSteps": string[]
@@ -18333,6 +18590,12 @@ var GRADE_TASK = {
18333
18590
  * written to fix a finding is precisely where a regression gets introduced. A run that only
18334
18591
  * reassessed the list has done half the work, and it is the half that looks complete.
18335
18592
  */
18593
+ /** The earlier before-and-after, quoted so the model can restate it rather than start over. */
18594
+ function previousBehaviourChange(previous) {
18595
+ const change = previous.behaviourChange;
18596
+ if (!change) return "";
18597
+ return ` (the earlier analysis said: before: ${JSON.stringify(change.before)}; after: ${JSON.stringify(change.after)}. Keep what still holds, correct what the new changes alter)`;
18598
+ }
18336
18599
  function buildReconciliationInstructions(input, options = {}) {
18337
18600
  const { previous, totalCommits, changedFiles, possiblyTruncated } = input;
18338
18601
  const reassess = input.reassess ?? /* @__PURE__ */ new Map();
@@ -18366,7 +18629,7 @@ ${REASSESS_TASK}` : ""}
18366
18629
  ${GRADE_TASK.question}`] : [],
18367
18630
  conversationOnly ? `No commits were pushed, so there is no new code to review. Report a problem in "newIssues" only where the conversation points at something in the code the earlier analysis missed and you have confirmed it by reading the code. Do NOT repeat a previous finding here: if it still stands, that is what "still-open" is for.` : `Read the changed code above as a reviewer, not only as an auditor of the numbered list. These commits have not been reviewed by anyone. Report every genuinely NEW problem you find in them in "newIssues", whether or not it relates to anything on the list — code written to fix a finding is exactly where a regression gets introduced, and reassessing the list and stopping there is half the job. Do NOT repeat a previous finding here: if it still stands, that is what "still-open" is for.`,
18368
18631
  `If the new changes introduce an area the earlier analysis had no section for, add it to "newSections". Otherwise return an empty array. Do not restate or restructure the earlier analysis's existing sections.`,
18369
- `Give a fresh "summary", "overallRisk", "recommendation" and "nextSteps" for the pull request AS IT NOW STANDS, taking both the earlier findings that survive and the new changes into account. Unlike a partial review, you have been shown the earlier analysis in full and are expected to revise its verdict where the new changes justify it. ${SUMMARY_GUIDANCE} ${ORIENTATION_GUIDANCE}`
18632
+ `Give a fresh "summary", "overallRisk", "recommendation" and "nextSteps" for the pull request AS IT NOW STANDS, taking both the earlier findings that survive and the new changes into account. Unlike a partial review, you have been shown the earlier analysis in full and are expected to revise its verdict where the new changes justify it. ${SUMMARY_GUIDANCE} ${ORIENTATION_GUIDANCE} ${BEHAVIOUR_CHANGE_GUIDANCE} Restate "behaviourChange" for the whole pull request as it now stands${previousBehaviourChange(previous)}, or omit it when the evidence does not establish it.`
18370
18633
  ].map((task, index) => `${index + 1}. ${task}`).join("\n");
18371
18634
  return `${conversationOnly ? `You are RECONCILING an earlier analysis of this pull request against what has changed in its conversation since that analysis was made.
18372
18635
 
@@ -18540,6 +18803,7 @@ function parseReconciliationResult(raw, priorCount, options = {}) {
18540
18803
  })),
18541
18804
  summary: asString$1(obj.summary).trim(),
18542
18805
  orientation: asString$1(obj.orientation).trim(),
18806
+ ...withBehaviourChange(parseBehaviourChange(obj.behaviourChange)),
18543
18807
  overallRisk: RISKS.includes(obj.overallRisk) ? obj.overallRisk : null,
18544
18808
  recommendation: asString$1(obj.recommendation).trim(),
18545
18809
  nextSteps: asStringArray(obj.nextSteps)
@@ -18817,6 +19081,15 @@ function findingsChanged(counts) {
18817
19081
  }
18818
19082
  //#endregion
18819
19083
  //#region ../../packages/review-harness/src/domain/ai_review/update_reconciliation.ts
19084
+ /**
19085
+ * Folding an update's answer onto the analysis it brings up to date.
19086
+ *
19087
+ * One function for every client that updates a review, so the workspace and the extension cannot
19088
+ * come to read the same evidence differently: what a cited comment may settle, when a resolution is
19089
+ * marked for reassessment instead, which findings a changed comment reopens, what each status is
19090
+ * recorded against, and which threads a new finding may claim. A client differs only in how it
19091
+ * fetched the changes and ran the model.
19092
+ */
18820
19093
  function reconciledAgainstOf(basis) {
18821
19094
  return basis.code ? basis.discussion ? "commits-and-conversation" : "commits" : "conversation";
18822
19095
  }
@@ -18849,6 +19122,7 @@ function applyReconciliation(previous, parsed, context) {
18849
19122
  schemaVersion: 1,
18850
19123
  summary: parsed.summary || previous.summary,
18851
19124
  ...parsed.orientation || previous.orientation ? { orientation: parsed.orientation || previous.orientation } : {},
19125
+ ...withBehaviourChange(mergeBehaviourChange(previous.behaviourChange, parsed.behaviourChange, context.codeChanged)),
18852
19126
  ...motivation ? { motivation } : {},
18853
19127
  overallRisk: parsed.overallRisk ?? previous.overallRisk,
18854
19128
  recommendation: parsed.recommendation || previous.recommendation,
@@ -22175,4 +22449,4 @@ var MCP_TOOL_NAMES = [
22175
22449
  "ask_buddy"
22176
22450
  ];
22177
22451
  //#endregion
22178
- export { statusPillLabel as $, TargetError as $a, lifecycleItemLabel as $i, writeJsonAtomically as $n, findExistingReview as $r, statedWorkspacePort as $t, anythingRunning as A, currentGradeIn as Aa, indexReviewGroups as Ai, BUILD_VERSION as An, deleteCollection as Ao, chatConversationSection as Ar, isRequestId as At, completedLabel as B, statusLookup as Ba, previousKissResult as Bi, checkArchitecture as Bn, modelHelpLines as Br, readServerRecord as Bt, parsePullRequestLink as C, isExplanationAction as Ca, driverGone as Ci, readEvents as Cn, AGENT_RUN_MARKER as Co, ASK_REREAD_CEILING_MS as Cr, findingRefs as Ct, INDEX_SECTIONS as D, analysisRefOf as Da, followedRefName as Di, recordTelemetryUploadConsent as Dn, collectionsStamp as Do, isUnchanged as Dr, endAskRequest as Dt, relativeTime as E, NO_STATUS as Ea, decideKissRecommendation as Ei, deliver$1 as En, cleanName as Eo, storedConversation as Er, reviewTagsIn as Et, collectionFold as F, isGradeFor as Fa, loadWorkspace as Fi, recordRuntimeBestEffort as Fn, STORE_ROOT as Fo, renderMarkdown as Fr, readIndexToken as Ft, markItemLabel as G, extractJsonText as Ga, touchWorkspace as Gi, storeArchitecture as Gn, createWorktree as Gr, stopServer as Gt, homeSection as H, followUpFindingId as Ha, reviewedCommit as Hi, partFiles as Hn, isBusy as Hr, serverEntryExists as Ht, collectionHash as I, isOffered as Ia, matchingWorkspaceIds as Ii, RUNTIME_WORDS as In, CODE_BLOCK_CLASS as Ir, bootstrapUrl as It, queueItemLabel as J, WITHHELD_REASON as Ja, LIFECYCLES as Ji, ownersOf as Jn, readMarker as Jr, DEFAULT_WORKSPACE_PORT as Jt, omittedText as K, objectWithAnyOf as Ka, workspaceRevision as Ki, storedScopes as Kn, holdWorktree as Kr, workspaceUrl as Kt, collectionPlacement as L, promptGrade as La, mutateWorkspace as Li, describeRuntimes as Ln, CODE_COPY_CLASS as Lr, clearServerRecordOwnedBy as Lt, authorOptions as M, findingIn as Ma, kissDecisionsOf as Mi, feedbackUrl as Mn, readCollections as Mo, authorPreview as Mr, markAskRunning as Mt, collectionCountLabel as N, findingStatus as Na, latestKissRun as Ni, waitingHistory as Nn, renameCollection as No, hasAuthorContent as Nr, readAskRequest as Nt, INDEX_SECTION_NAMES as O, assessmentLookup as Oa, groupByLineage as Oi, telemetryUploadConsent as On, createCollection as Oo, agentEnvOf as Or, forgetAskRequest as Ot, collectionEmptyText as P, gradeScale as Pa, lineagePosition as Pi, mapRuntimes as Pn, setMembership as Po, GITHUB_MARKDOWN as Pr, recordAskAccepted as Pt, statusLabel as Q, DetachedHeadError as Qa, lifecycleChip as Qi, isArchitectureId as Qn, describeSubject as Qr, resolveWorkspacePort as Qt, collectionsLine as R, reassessIn as Ra, positionInLineage as Ri, formatDuration as Rn, escapeHtml as Rr, ensureServer as Rt, extractPullRequestLinks as S, explanationOf as Sa, clearClaim as Si, append as Sn, versionFrom as So, existingPaths as Sr, findingRef as St, runBatch as T, ANALYSIS_ONLY as Ta, CLAIM_LEASE_MS as Ti, stamp as Tn, AgentUnavailableError as To, saveRefresh as Tr, reviewTag as Tt, isCollectionId as U, SEVERITIES as Ua, reviewedRepositories as Ui, readArchitecture as Un, withReviewLock as Ur, spawnServer as Ut, countLabel as V, wasReassessed as Va, recentWorkspaces as Vi, loadArchitecture as Vn, ReviewBeingDeletedError as Vr, reviewsUrl as Vt, isFoldKey as W, asString$1 as Wa, summarise as Wi, repositoryKey as Wn, MANAGED_ROOT as Wr, stopChild as Wt, sectionOpen as X, resolveTarget as Xa, activityLabel as Xi, REPOSITORY_SCOPE as Xn, requestJobStop as Xr, MIN_WORKSPACE_PORT as Xt, queueOptionLabel as Y, kissOutcome as Ya, activityAttributes as Yi, QUOTE_LIMITS as Yn, removeWorktree as Yr, MAX_WORKSPACE_PORT as Yt, selectAllLabel as Z, originRemote as Za, isLifecycle as Zi, ancestorsOf as Zn, describeMovement as Zr, parseWorkspacePort as Zt, endRun as _, reviewerDispositions as _a, isLiveJob as _i, prepareReview as _n, agentIdentityOf as _o, retireNotification as _r, modelProvenanceOf as _t, lineageIds as a, sectionOf as aa, forgeResolver as ai, cancellableHere as an, git as ao, createPrState as ar, lineageKeysFor as at, stopRun as b, recommendationsOf as ba, progressSteps as bi, runJob as bn, parkedReason as bo, unreadCount as br, humanizeModelId as bt, discardJob as c, queueRemove as ca, allJobs as ci, recheckConversation as cn, resolveModel as co, listNotifications as cr, compactQueue as ct, startKissJob as d, doneVerb as da, fail as di, findingsChanged as dn, readAgentPreference as do, markReadForReview as dr, RUNNER_KINDS as dt, lifecycleName$1 as ea, checkCodeFreshness as ei, writePortPreference as en, isCommitReview as eo, announceJob as er, deliver as et, isDriving as f, isIssueOutstanding as fa, isTerminal as fi, UpdateAlreadyRunningError as fn, writeAgentPreference as fo, moveIds as fr, RUNNER_MARKER as ft, completeRun as g, resolvedByConversation as ga, PHASES as gi, observeReview as gn, agentFor as go, removeNotification as gr, applyUpdate as gt, beginRun as h, questionsOutstanding as ha, saveJob as hi, failureOf as hn, agentById as ho, readStateOf as hr, spawnRunner as ht, deleteReview as i, parseLifecycle as ia, fetchHead as ii, cancelUpdate as in, shorthandOf as io, createArchitectureNotification as ir, retryableMiss as it, authorOptionLabel as j, findingAssessment as ja, kissDecisionRefusal as ji, PACKAGE_NAME as jn, nameProblemText as jo, UNESTABLISHED_MOTIVATION as jr, isTerminalAsk as jt, PR_FILTER_OPTIONS as k, closesFinding as ka, indexKeptIds as ki, telemetryUploadEnabled as kn, createWithMembers as ko, EXPLAIN_SIMPLY_PROMPT as kr, interruption as kt, kissAgentId as l, queueStamp as la, attemptIdOf as li, updateReview as ln, agentIdOf as lo, markAllRead as lr, queueForReview as lt, RUN_KINDS as m, issuesOutstanding as ma, pruneJobResidue as mi, runningJobs as mn, AGENT_IDS as mo, readNotification as mr, runDetached as mt, MCP_TOOL_NAMES as n, lifecyclePillLabel as na, followedBranch as ni, FETCH_TIMEOUT_MS as nn, AmbiguousRefError as no, subjectOf as nr, missedDelivery as nt, liveJobsFor as o, queueAdd as oa, fillFileUrlTemplate as oi, mergeUpdateInto as on, refExists as oo, createPrUpdate as or, settleCollections as ot, track as p, isQuestionOutstanding as pa, loadJob as pi, liveUpdateFor as pn, CONFIG_PATH as po, pruneNotifications as pr, cliEntry as pt, pinItemLabel as q, describeAuthorship as qa, workspaceStamps as qi, trackedFiles as qn, isWorktreeHeld as qr, writeServerRecord as qt, deleteLineage as r, offersLifecycle as ra, reviewedShaOf as ri, RefreshUnavailableError as rn, shortNameOf as ro, categoryOf as rr, openDestination as rt, wasBlocked as s, queueAddMany as sa, RUN_RECORD_RETENTION_MS as si, parkedUpdateRefusal as sn, resolveAgent as so, eventName as sr, activateQueued as st, MCP_SERVER_INFO_NAME as t, lifecyclePillAction as ta, checkReview as ti, COMMIT_REVIEW_CANNOT_UPDATE as tn, resolveBase as to, branchOf as tr, isDelivered as tt, recordKissRun as u, readQueue as ua, endedAt as ui, askCheckout as un, clearAgentPreference as uo, markRead as ur, settleQueue as ut, heldRuns as v, verdictClosure as va, isTerminalJobPhase as vi, settleReview as vn, detectAgents as vo, toneOf as vr, provenanceDetailLines as vt, unsupportedReason as w, withExplanation as wa, isClaimed as wi, record as wn, AgentCancelledError as wo, prepareConversationWithin as wr, refOf as wt, batchIdentity as x, explanationActionOf as xa, purposeOf as xi, startJob as xn, runnableAgents as xo, withUsageRecorded as xr, findingAtRef as xt, reportRun as y, processDiscussion as ya, phasesFor as yi, reanalyseReview as yn, isAgentId as yo, unreadAround as yr, agentDisplayName as yt, completedFold as z, sourcePhrase as za, presentedKissRunOf as zi, ARCHITECTURE_DIR as zn, renderInlineText as zr, healthyPort as zt };
22452
+ export { statusLabel as $, gradeScale as $a, workspacesRevision as $i, isArchitectureId as $n, setMembership as $o, describeSubject as $r, resolveWorkspacePort as $t, PR_FILTER_OPTIONS as A, doneVerb as Aa, indexKeptIds as Ai, telemetryUploadEnabled as An, readAgentPreference as Ao, EXPLAIN_SIMPLY_PROMPT as Ar, interruption as At, completedFold as B, explanationActionOf as Ba, matchingWorkspaceIds as Bi, ARCHITECTURE_DIR as Bn, runnableAgents as Bo, renderInlineText as Br, healthyPort as Bt, parsePullRequestLink as C, parseLifecycle as Ca, clearClaim as Ci, append as Cn, shorthandOf as Co, existingPaths as Cr, findingRef as Ct, INDEX_SECTIONS as D, queueRemove as Da, decideKissRecommendation as Di, deliver$1 as Dn, resolveModel as Do, storedConversation as Dr, reviewTagsIn as Dt, relativeTime as E, queueAddMany as Ea, CLAIM_LEASE_MS as Ei, stamp as En, resolveAgent as Eo, saveRefresh as Er, reviewTag as Et, collectionEmptyText as F, resolvedByConversation as Fa, lineageKey as Fi, mapRuntimes as Fn, agentFor as Fo, GITHUB_MARKDOWN as Fr, recordAskAccepted as Ft, isFoldKey as G, NO_STATUS as Ga, previousKissResult as Gi, repositoryKey as Gn, cleanName as Go, MANAGED_ROOT as Gr, stopChild as Gt, countLabel as H, isExplanationAction as Ha, outcomeInputsOf as Hi, loadArchitecture as Hn, AGENT_RUN_MARKER as Ho, ReviewBeingDeletedError as Hr, reviewsUrl as Ht, collectionFold as I, reviewerDispositions as Ia, lineageKeyFor as Ii, recordRuntimeBestEffort as In, agentIdentityOf as Io, renderMarkdown as Ir, readIndexToken as It, pinItemLabel as J, closesFinding as Ja, reviewedRepositories as Ji, trackedFiles as Jn, createWithMembers as Jo, isWorktreeHeld as Jr, writeServerRecord as Jt, markItemLabel as K, analysisRefOf as Ka, recentWorkspaces as Ki, storeArchitecture as Kn, collectionsStamp as Ko, createWorktree as Kr, stopServer as Kt, collectionHash as L, verdictClosure as La, lineageOutcome as Li, RUNTIME_WORDS as Ln, detectAgents as Lo, CODE_BLOCK_CLASS as Lr, bootstrapUrl as Lt, authorOptionLabel as M, isQuestionOutstanding as Ma, kissDecisionRefusal as Mi, PACKAGE_NAME as Mn, CONFIG_PATH as Mo, UNESTABLISHED_MOTIVATION as Mr, isTerminalAsk as Mt, authorOptions as N, issuesOutstanding as Na, kissDecisionsOf as Ni, feedbackUrl as Nn, AGENT_IDS as No, authorPreview as Nr, markAskRunning as Nt, INDEX_SECTION_NAMES as O, queueStamp as Oa, followedRefName as Oi, recordTelemetryUploadConsent as On, agentIdOf as Oo, isUnchanged as Or, endAskRequest as Ot, collectionCountLabel as P, questionsOutstanding as Pa, latestKissRun as Pi, waitingHistory as Pn, agentById as Po, hasAuthorContent as Pr, readAskRequest as Pt, selectAllLabel as Q, findingStatus as Qa, workspaceStamps as Qi, ancestorsOf as Qn, renameCollection as Qo, describeMovement as Qr, parseWorkspacePort as Qt, collectionPlacement as R, processDiscussion as Ra, lineagePosition as Ri, describeRuntimes as Rn, isAgentId as Ro, CODE_COPY_CLASS as Rr, clearServerRecordOwnedBy as Rt, extractPullRequestLinks as S, offersLifecycle as Sa, purposeOf as Si, startJob as Sn, shortNameOf as So, withUsageRecorded as Sr, findingAtRef as St, runBatch as T, queueAdd as Ta, isClaimed as Ti, record as Tn, refExists as To, prepareConversationWithin as Tr, refOf as Tt, homeSection as U, withExplanation as Ua, positionInLineage as Ui, partFiles as Un, AgentCancelledError as Uo, isBusy as Ur, serverEntryExists as Ut, completedLabel as V, explanationOf as Va, mutateWorkspace as Vi, checkArchitecture as Vn, versionFrom as Vo, modelHelpLines as Vr, readServerRecord as Vt, isCollectionId as W, ANALYSIS_ONLY as Wa, presentedKissRunOf as Wi, readArchitecture as Wn, AgentUnavailableError as Wo, withReviewLock as Wr, spawnServer as Wt, queueOptionLabel as X, findingAssessment as Xa, touchWorkspace as Xi, QUOTE_LIMITS as Xn, nameProblemText as Xo, removeWorktree as Xr, MAX_WORKSPACE_PORT as Xt, queueItemLabel as Y, currentGradeIn as Ya, summarise as Yi, ownersOf as Yn, deleteCollection as Yo, readMarker as Yr, DEFAULT_WORKSPACE_PORT as Yt, sectionOpen as Z, findingIn as Za, workspaceRevision as Zi, REPOSITORY_SCOPE as Zn, readCollections as Zo, requestJobStop as Zr, MIN_WORKSPACE_PORT as Zt, endRun as _, lifecycleChip as _a, PHASES as _i, observeReview as _n, DetachedHeadError as _o, removeNotification as _r, applyUpdate as _t, lineageIds as a, nextBaseline as aa, fetchHead as ai, cancelUpdate as an, statusLookup as ao, createArchitectureNotification as ar, retryableMiss as at, stopRun as b, lifecyclePillAction as ba, phasesFor as bi, reanalyseReview as bn, resolveBase as bo, unreadAround as br, agentDisplayName as bt, discardJob as c, outcomeStanding as ca, RUN_RECORD_RETENTION_MS as ci, parkedUpdateRefusal as cn, SEVERITIES as co, eventName as cr, activateQueued as ct, startKissJob as d, readOutcomes as da, endedAt as di, askCheckout as dn, objectWithAnyOf as do, markRead as dr, settleQueue as dt, analysedAt as ea, findExistingReview as ei, statedWorkspacePort as en, isGradeFor as eo, writeJsonAtomically as er, withWriteLock as es, statusPillLabel as et, isDriving as f, sameSha as fa, fail as fi, findingsChanged as fn, describeAuthorship as fo, markReadForReview as fr, RUNNER_KINDS as ft, completeRun as g, isLifecycle as ga, saveJob as gi, failureOf as gn, originRemote as go, readStateOf as gr, spawnRunner as gt, beginRun as h, activityLabel as ha, pruneJobResidue as hi, runningJobs as hn, resolveTarget as ho, readNotification as hr, runDetached as ht, deleteReview as i, lastBaseline as ia, reviewedShaOf as ii, RefreshUnavailableError as in, sourcePhrase as io, categoryOf as ir, openDestination as it, anythingRunning as j, isIssueOutstanding as ja, indexReviewGroups as ji, BUILD_VERSION as jn, writeAgentPreference as jo, chatConversationSection as jr, isRequestId as jt, LIST_SECTIONS as k, readQueue as ka, groupByLineage as ki, telemetryUploadConsent as kn, clearAgentPreference as ko, agentEnvOf as kr, forgetAskRequest as kt, kissAgentId as l, parseOutcomeSnapshot as la, allJobs as li, recheckConversation as ln, asString$1 as lo, listNotifications as lr, compactQueue as lt, RUN_KINDS as m, activityAttributes as ma, loadJob as mi, liveUpdateFor as mn, kissOutcome as mo, pruneNotifications as mr, cliEntry as mt, MCP_TOOL_NAMES as n, REVIEWER_OUTCOMES as na, checkReview as ni, COMMIT_REVIEW_CANNOT_UPDATE as nn, promptGrade as no, branchOf as nr, isDelivered as nt, liveJobsFor as o, outcomeDefinition as oa, forgeResolver as oi, cancellableHere as on, wasReassessed as oo, createPrState as or, lineageKeysFor as ot, track as p, LIFECYCLES as pa, isTerminal as pi, UpdateAlreadyRunningError as pn, WITHHELD_REASON as po, moveIds as pr, RUNNER_MARKER as pt, omittedText as q, assessmentLookup as qa, reviewedCommit as qi, storedScopes as qn, createCollection as qo, holdWorktree as qr, workspaceUrl as qt, deleteLineage as r, appendOutcome as ra, followedBranch as ri, FETCH_TIMEOUT_MS as rn, reassessIn as ro, subjectOf as rr, missedDelivery as rt, wasBlocked as s, outcomeLabel as sa, fillFileUrlTemplate as si, mergeUpdateInto as sn, followUpFindingId as so, createPrUpdate as sr, settleCollections as st, MCP_SERVER_INFO_NAME as t, analysisSnapshot as ta, checkCodeFreshness as ti, writePortPreference as tn, isOffered as to, announceJob as tr, STORE_ROOT as ts, deliver as tt, recordKissRun as u, parseOutcomes as ua, attemptIdOf as ui, updateReview as un, extractJsonText as uo, markAllRead as ur, queueForReview as ut, heldRuns as v, lifecycleItemLabel as va, isLiveJob as vi, prepareReview as vn, TargetError as vo, retireNotification as vr, modelProvenanceOf as vt, unsupportedReason as w, sectionOf as wa, driverGone as wi, readEvents as wn, git as wo, ASK_REREAD_CEILING_MS as wr, findingRefs as wt, batchIdentity as x, lifecyclePillLabel as xa, progressSteps as xi, runJob as xn, AmbiguousRefError as xo, unreadCount as xr, humanizeModelId as xt, reportRun as y, lifecycleName$1 as ya, isTerminalJobPhase as yi, settleReview as yn, isCommitReview as yo, toneOf as yr, provenanceDetailLines as yt, collectionsLine as z, recommendationsOf as za, loadWorkspace as zi, formatDuration as zn, parkedReason as zo, escapeHtml as zr, ensureServer as zt };