prreviewbuddy 0.90.0 → 0.92.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 +58 -0
- package/dist/main.js +14 -2
- package/dist/{mcp_tools-CSj5vcSE.js → mcp_tools-BKPpd9dN.js} +264 -25
- package/dist/server.js +298 -70
- package/package.json +1 -1
- package/static/shell.css +31 -10
- package/static/sidebar.css +198 -6
- package/static/workspace.js +58 -45
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $a as
|
|
1
|
+
import { $a as runnableAgents, $i as explanationOf, $n as authorPreview, $r as kissDecisionsOf, $t as recordRuntimeBestEffort, A as settleQueue, Ai as createWithMembers, An as moveIds, Ar as attemptIdOf, At as UpdateAlreadyRunningError, B as humanizeModelId, Ba as agentIdOf, Bi as queueStamp, Bn as existingPaths, Br as phasesFor, C as parsePullRequestLink, Ca as objectWithAnyOf, Ci as lifecyclePillAction, Cn as createPrState, Cr as followedBranch, Ct as RefreshUnavailableError, D as activateQueued, Da as resolveTarget, Di as sectionOf$1, Dn as markAllRead, Dr as fillFileUrlTemplate, E as relativeTime$1, Ea as kissOutcome, Ei as parseLifecycle, En as listNotifications, Er as forgeResolver, Et as mergeUpdateInto, Fa as shorthandOf, Fi as setMembership, Fn as retireNotification, Fr as pruneJobResidue, Ft as prepareReview, G as reviewTag, Ga as STORE_ROOT, Gi as issuesOutstanding, Gn as storedConversation, Gr as isClaimed, Gt as recordTelemetryUploadConsent, H as findingRef, Ha as readAgentPreference, Hi as doneVerb, Hn as adoptLegacyBaseline, Hr as purposeOf, Ht as record, Ia as git, Ii as updateCollections, In as toneOf, Ir as saveJob, It as settleReview, Ja as agentFor$1, Ji as reviewerDispositions, Jn as isUnchanged, Jr as followedRefName, Jt as BUILD_VERSION, K as reviewTagsIn, Ka as AGENT_IDS, Ki as questionsOutstanding, Kn as unforeseenFailure, Kt as telemetryUploadConsent, L as modelProvenanceOf, La as refExists, Li as queueAdd, Ln as unreadAround, Lt as reanalyseReview, Ma as resolveBase, Mi as nameProblemText, Mn as readNotification, Mr as fail$1, Mt as runningJobs, N as cliEntry, Na as AmbiguousRefError, Ni as readCollections, Nn as readStateOf, Nr as isTerminal, Nt as failureOf$1, O as compactQueue, Oa as originRemote, Oi as collectionsStamp, On as markRead, Or as RUN_RECORD_RETENTION_MS, Ot as updateReview, Pa as shortNameOf, Pi as renameCollection, Pn as removeNotification, Pr as loadJob, Pt as observeReview, Q as markAskRunning, Qa as parkedReason, Qi as explanationActionOf, Qn as UNESTABLISHED_MOTIVATION, Qr as kissDecisionRefusal, Qt as mapRuntimes, R as provenanceDetailLines, Ra as resolveAgent, Ri as queueAddMany, Rn as unreadCount$1, Rr as isLiveJob, Rt as runJob, S as extractPullRequestLinks, Sa as extractJsonText, Si as lifecycleName$1, Sn as createArchitectureNotification, Sr as checkReview, St as FETCH_TIMEOUT_MS, T as runBatch, Ta as WITHHELD_REASON, Ti as offersLifecycle, Tn as eventName, Tr as fetchHead, Tt as cancellableHere, U as findingRefs, Ua as writeAgentPreference, Ui as isIssueOutstanding, Un as prepareConversationWithin, V as findingAtRef, Va as clearAgentPreference, Vi as readQueue, Vn as ASK_REREAD_CEILING_MS, Vr as progressSteps, Vt as readEvents, W as refOf, Wi as isQuestionOutstanding, Wn as saveRefresh, X as isRequestId, Xa as detectAgents, Xi as processDiscussion, Xn as EXPLAIN_SIMPLY_PROMPT, Xr as indexKeptIds, Xt as feedbackUrl, Ya as agentIdentityOf, Yi as verdictClosure, Yn as agentEnvOf, Yr as groupByLineage, Za as isAgentId, Zi as recommendationsOf, Zn as chatConversationSection, Zr as indexReviewGroups, Zt as waitingHistory, _ as endRun, _a as statusLookup, _i as activityAttributes, _n as writeJsonAtomically, _r as requestJobStop, _t as parseWorkspacePort, a as lineageIds, aa as assessmentLookup, ai as mutateWorkspace, an as loadArchitecture, ar as escapeHtml$1, b as stopRun, ba as SEVERITIES, bi as lifecycleChip, bn as subjectOf, br as findExistingReview, bt as writePortPreference, c as discardJob, ca as findingAssessment, ci as previousKissResult, cn as repositoryKey, cr as ReviewBeingDeletedError, ct as serverEntryExists, d as startKissJob, da as gradeScale, dn as trackedFiles, dr as MANAGED_ROOT, ea as isExplanationAction, ei as latestKissRun, en as RUNTIME_WORDS, eo as versionFrom, er as hasAuthorContent, f as isDriving, fa as isGradeFor, fi as summarise, fn as ownersOf, fr as createWorktree, g as completeRun, ga as sourcePhrase, gi as LIFECYCLES, gn as isArchitectureId, gr as removeWorktree, gt as MIN_WORKSPACE_PORT, h as beginRun, ha as reassessIn, hi as workspaceStamps, hn as ancestorsOf, hr as readMarker, ht as MAX_WORKSPACE_PORT, i as deleteReview, ia as analysisRefOf, in as checkArchitecture, ir as CODE_COPY_CLASS, ja as isCommitReview, ji as deleteCollection, jn as pruneNotifications, jr as endedAt, jt as liveUpdateFor, ka as DetachedHeadError, ki as createCollection, kn as markReadForReview, kr as allJobs, kt as askCheckout, l as kissAgentId, la as findingIn, li as recentWorkspaces, ln as storeArchitecture, lr as isBusy, lt as spawnServer, m as RUN_KINDS, ma as promptGrade, mi as workspaceRevision, mn as REPOSITORY_SCOPE, mr as isWorktreeHeld, mt as DEFAULT_WORKSPACE_PORT, n as MCP_TOOL_NAMES, na as ANALYSIS_ONLY, ni as lineagePosition, nn as formatDuration, no as AgentCancelledError, nr as renderMarkdown, o as liveJobsFor, oa as closesFinding, on as partFiles, or as renderInlineText, ot as readServerRecord, p as track, pa as isOffered, pi as touchWorkspace, pn as QUOTE_LIMITS, pr as holdWorktree, pt as writeServerRecord, q as endAskRequest, qa as agentById, qi as resolvedByConversation, qn as refreshPrContext, qr as decideKissRecommendation, r as deleteLineage, ra as NO_STATUS, ri as loadWorkspace, rn as ARCHITECTURE_DIR, ro as AgentUnavailableError, rr as CODE_BLOCK_CLASS, rt as clearServerRecordOwnedBy, s as wasBlocked, sa as currentGradeIn, si as presentedKissRunOf, sn as readArchitecture, ta as withExplanation, ti as lineageKey, tn as describeRuntimes, tr as GITHUB_MARKDOWN, tt as readIndexToken, ua as findingStatus, ui as reviewedCommit, un as storedScopes, ur as withReviewLock, ut as stopChild, v as heldRuns, va as wasReassessed, vi as activityLabel, vn as announceJob, vt as resolveWorkspacePort, w as unsupportedReason, wa as describeAuthorship, wi as lifecyclePillLabel, wn as createPrUpdate, wr as reviewedShaOf, wt as cancelUpdate, x as batchIdentity, xa as asString, xi as lifecycleItemLabel, xn as categoryOf, xr as checkCodeFreshness, y as reportRun, ya as followUpFindingId, yi as isLifecycle, yn as branchOf, yt as statedWorkspacePort, z as agentDisplayName, za as resolveModel, zi as queueRemove, zn as withUsageRecorded, zr as isTerminalJobPhase } from "./mcp_tools-BKPpd9dN.js";
|
|
2
2
|
import { basename, delimiter, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { execFile, spawn } from "node:child_process";
|
|
4
4
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -182,6 +182,7 @@ function composeReview(session) {
|
|
|
182
182
|
schemaVersion: 1,
|
|
183
183
|
summary: primary.summary,
|
|
184
184
|
...primary.motivation ? { motivation: primary.motivation } : {},
|
|
185
|
+
...primary.orientation ? { orientation: primary.orientation } : {},
|
|
185
186
|
recommendation: primary.recommendation,
|
|
186
187
|
nextSteps: primary.nextSteps,
|
|
187
188
|
overallRisk,
|
|
@@ -569,7 +570,7 @@ function isCollectionId(value) {
|
|
|
569
570
|
* filter gives, and only its star says it is pinned.
|
|
570
571
|
*/
|
|
571
572
|
function indexSectionOf(pinned, lifecycle, activity, filter) {
|
|
572
|
-
return pinned && !ordersExplicitly(filter) ? "pinned" : sectionOf(lifecycle, activity);
|
|
573
|
+
return pinned && !ordersExplicitly(filter) ? "pinned" : sectionOf$1(lifecycle, activity);
|
|
573
574
|
}
|
|
574
575
|
/**
|
|
575
576
|
* Where a review's own card is drawn, or absent when it is drawn only inside its collections.
|
|
@@ -595,7 +596,7 @@ function homeSection(pinned, lifecycle, activity, collected, filter) {
|
|
|
595
596
|
*/
|
|
596
597
|
function collectionPlacement(pinned, lifecycle, activity, filter) {
|
|
597
598
|
if (pinned || ordersExplicitly(filter)) return void 0;
|
|
598
|
-
return sectionOf(lifecycle, activity) === "done" ? "completed" : "active";
|
|
599
|
+
return sectionOf$1(lifecycle, activity) === "done" ? "completed" : "active";
|
|
599
600
|
}
|
|
600
601
|
/** A collection heading's count: what still needs attention, and the members drawn in Pinned. */
|
|
601
602
|
function collectionCountLabel(active, pinned) {
|
|
@@ -902,7 +903,7 @@ function svg(paths) {
|
|
|
902
903
|
* Buddy is asked about can never disagree. That is also why no severity, theme or status appears:
|
|
903
904
|
* those sort the findings for the reviewer and are not part of what the author is told.
|
|
904
905
|
*/
|
|
905
|
-
function count(n, noun) {
|
|
906
|
+
function count$1(n, noun) {
|
|
906
907
|
return `${n} ${noun}${n === 1 ? "" : "s"}`;
|
|
907
908
|
}
|
|
908
909
|
/**
|
|
@@ -919,8 +920,8 @@ function feedbackSelectionParts(issues, questions) {
|
|
|
919
920
|
const total = issues + questions;
|
|
920
921
|
return {
|
|
921
922
|
total: total === 0 ? "Nothing chosen" : `${total} chosen`,
|
|
922
|
-
issues: issues ? count(issues, "issue") : "",
|
|
923
|
-
questions: questions ? count(questions, "question") : ""
|
|
923
|
+
issues: issues ? count$1(issues, "issue") : "",
|
|
924
|
+
questions: questions ? count$1(questions, "question") : ""
|
|
924
925
|
};
|
|
925
926
|
}
|
|
926
927
|
/** "5 chosen · 2 issues · 3 questions", "1 chosen · 1 question", "Nothing chosen". */
|
|
@@ -1522,6 +1523,101 @@ function splitFilePath(path) {
|
|
|
1522
1523
|
};
|
|
1523
1524
|
}
|
|
1524
1525
|
//#endregion
|
|
1526
|
+
//#region ../../packages/review-harness/src/domain/ui/components/impact_groups.ts
|
|
1527
|
+
/** The pane's folds: each section, and the closed drawer under them. */
|
|
1528
|
+
var ISSUE_FOLDS = [
|
|
1529
|
+
"failure",
|
|
1530
|
+
"risk",
|
|
1531
|
+
"intent",
|
|
1532
|
+
"maintenance",
|
|
1533
|
+
"other",
|
|
1534
|
+
"closed"
|
|
1535
|
+
];
|
|
1536
|
+
/** Display order: what is wrong, what may be, what only the reviewer can judge, what costs later. */
|
|
1537
|
+
var ISSUE_SECTIONS = [
|
|
1538
|
+
"failure",
|
|
1539
|
+
"risk",
|
|
1540
|
+
"intent",
|
|
1541
|
+
"maintenance",
|
|
1542
|
+
"other"
|
|
1543
|
+
];
|
|
1544
|
+
var ISSUE_SECTION_TITLES = {
|
|
1545
|
+
failure: "Failures",
|
|
1546
|
+
risk: "Possible risks",
|
|
1547
|
+
intent: "Intent to confirm",
|
|
1548
|
+
maintenance: "Maintenance",
|
|
1549
|
+
other: "Other issues"
|
|
1550
|
+
};
|
|
1551
|
+
function sectionOf(issue) {
|
|
1552
|
+
return issue.consequence?.group ?? "other";
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Issues by section, each section by severity as the reviewer now holds it, ties in the order the
|
|
1556
|
+
* analysis gave them. Severity orders within a section and never moves an issue between sections.
|
|
1557
|
+
*/
|
|
1558
|
+
function groupIssues(issues, gradeOf) {
|
|
1559
|
+
const rank = (issue) => {
|
|
1560
|
+
const index = SEVERITIES.indexOf(gradeOf(issue) ?? "");
|
|
1561
|
+
return index < 0 ? SEVERITIES.length : index;
|
|
1562
|
+
};
|
|
1563
|
+
return ISSUE_SECTIONS.map((section) => ({
|
|
1564
|
+
section,
|
|
1565
|
+
issues: issues.map((issue, index) => ({
|
|
1566
|
+
issue,
|
|
1567
|
+
index
|
|
1568
|
+
})).filter(({ issue }) => sectionOf(issue) === section).sort((a, b) => rank(a.issue) - rank(b.issue) || a.index - b.index).map(({ issue }) => issue)
|
|
1569
|
+
})).filter((group) => group.issues.length > 0);
|
|
1570
|
+
}
|
|
1571
|
+
function tallyEntries(entries) {
|
|
1572
|
+
const tally = {
|
|
1573
|
+
bySection: {},
|
|
1574
|
+
verify: 0,
|
|
1575
|
+
decide: 0,
|
|
1576
|
+
fix: 0,
|
|
1577
|
+
step: 0
|
|
1578
|
+
};
|
|
1579
|
+
for (const entry of entries) {
|
|
1580
|
+
tally.bySection[entry.section] = (tally.bySection[entry.section] ?? 0) + 1;
|
|
1581
|
+
if (entry.next) tally[entry.next] += 1;
|
|
1582
|
+
}
|
|
1583
|
+
return tally;
|
|
1584
|
+
}
|
|
1585
|
+
function tallyIssues(issues) {
|
|
1586
|
+
return tallyEntries(issues.map((issue) => {
|
|
1587
|
+
const step = issue.consequence?.next;
|
|
1588
|
+
return {
|
|
1589
|
+
section: sectionOf(issue),
|
|
1590
|
+
...step ? { next: step.kind ?? "step" } : {}
|
|
1591
|
+
};
|
|
1592
|
+
}));
|
|
1593
|
+
}
|
|
1594
|
+
var count = (n, one, many) => `${n} ${n === 1 ? one : many}`;
|
|
1595
|
+
/**
|
|
1596
|
+
* One factual line over the sections: how many issues are outstanding, and how many of them name
|
|
1597
|
+
* a step, by the kind the analysis gave it. The per-section counts are the nav's, so they are not
|
|
1598
|
+
* repeated here. Always "issues": Questions keep their own tab and their own count.
|
|
1599
|
+
*/
|
|
1600
|
+
function issuesAttentionLine(tally) {
|
|
1601
|
+
const total = Object.values(tally.bySection).reduce((sum, n) => sum + (n ?? 0), 0);
|
|
1602
|
+
if (total === 0) return "No issues are outstanding from this analysis.";
|
|
1603
|
+
const steps = [
|
|
1604
|
+
tally.verify ? count(tally.verify, "verification step", "verification steps") : "",
|
|
1605
|
+
tally.decide ? count(tally.decide, "decision", "decisions") : "",
|
|
1606
|
+
tally.fix ? count(tally.fix, "fix", "fixes") : "",
|
|
1607
|
+
tally.step ? count(tally.step, "next step", "next steps") : ""
|
|
1608
|
+
].filter(Boolean);
|
|
1609
|
+
return [count(total, "outstanding issue", "outstanding issues"), ...steps].join(" · ");
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Buttons to each section with its count. Buttons, not `#` links: the hash routes the review page.
|
|
1613
|
+
* Drawn only when there are two sections to choose between; a single one is already in view.
|
|
1614
|
+
*/
|
|
1615
|
+
function renderImpactNavItems(tally) {
|
|
1616
|
+
const present = ISSUE_SECTIONS.filter((section) => (tally.bySection[section] ?? 0) > 0);
|
|
1617
|
+
if (present.length < 2) return "";
|
|
1618
|
+
return present.map((section) => `<button type="button" class="pr-helper-issue-nav-item" data-issue-nav="${section}">${escapeHtml$1(ISSUE_SECTION_TITLES[section])}<span class="pr-helper-issue-nav-count">${tally.bySection[section]}</span></button>`).join("");
|
|
1619
|
+
}
|
|
1620
|
+
//#endregion
|
|
1525
1621
|
//#region ../../packages/review-harness/src/domain/ai_review/review_outcome.ts
|
|
1526
1622
|
/**
|
|
1527
1623
|
* The one reading of a session, for every surface.
|
|
@@ -3292,16 +3388,6 @@ function renderInvestigationNote(note) {
|
|
|
3292
3388
|
return `<p class="pr-helper-investigation-note">${renderInlineText(note)}</p>`;
|
|
3293
3389
|
}
|
|
3294
3390
|
/**
|
|
3295
|
-
* The bar above the two groups, worded the way the Questions tab words its own.
|
|
3296
|
-
*
|
|
3297
|
-
* Only drawn once a reconciliation has closed something. On a first analysis every finding is
|
|
3298
|
-
* open, "15 of 15 still open" is a sentence with no information in it, and a review that has
|
|
3299
|
-
* never been updated should not grow a progress bar to tell the reviewer they have made none.
|
|
3300
|
-
*/
|
|
3301
|
-
function findingsProgressBody(total, open, verb = "resolved") {
|
|
3302
|
-
return open === 0 ? `<span class="pr-helper-questions-progress-check" aria-hidden="true">\u2713</span><span class="pr-helper-questions-progress-count">All ${total} issues ${verb}</span>` : `<span class="pr-helper-questions-progress-count">${open} of ${total} still open</span>`;
|
|
3303
|
-
}
|
|
3304
|
-
/**
|
|
3305
3391
|
* Work that is done, kept and closed.
|
|
3306
3392
|
*
|
|
3307
3393
|
* Collapsed rather than dropped: the reviewer asked whether these were fixed, and "they are gone
|
|
@@ -3309,8 +3395,8 @@ function findingsProgressBody(total, open, verb = "resolved") {
|
|
|
3309
3395
|
* ten resolved findings above five open ones is the layout that made the count feel wrong in the
|
|
3310
3396
|
* first place.
|
|
3311
3397
|
*/
|
|
3312
|
-
function renderClosedFindings(summary, cards, kind, nothingOpen, waiting) {
|
|
3313
|
-
return `<details class="pr-helper-closed-findings" data-closed-findings="${kind}"${nothingOpen ? " data-nothing-open" : ""}>
|
|
3398
|
+
function renderClosedFindings(summary, cards, kind, nothingOpen, waiting, open = false) {
|
|
3399
|
+
return `<details class="pr-helper-closed-findings" data-closed-findings="${kind}"${nothingOpen ? " data-nothing-open" : ""}${open ? " open" : ""}>
|
|
3314
3400
|
<summary class="pr-helper-closed-findings-summary" data-closed-findings-summary>${escapeHtml$1(summary)}${renderWaitingSuggestions(waiting)}</summary>
|
|
3315
3401
|
<div class="pr-helper-closed-findings-list">${cards}</div>
|
|
3316
3402
|
</details>`;
|
|
@@ -3346,13 +3432,45 @@ function renderIssuesTab(session, investigatingId, options) {
|
|
|
3346
3432
|
const isDismissed = dispositions.isDismissed(issue);
|
|
3347
3433
|
const stale = dispositions.isStale(issue);
|
|
3348
3434
|
const assessment = assess("issue", issue);
|
|
3435
|
+
const impact = issue.consequence;
|
|
3436
|
+
const fixed = issue.reconciliationStatus === "resolved" ? issue.resolvedBehaviour?.trim() : void 0;
|
|
3437
|
+
const framed = Boolean(impact || fixed);
|
|
3438
|
+
const headline = fixed ?? impact?.title ?? issue.title;
|
|
3439
|
+
const explanation = renderCardExplanation(explanationOf(session, "issue", issue.id), options);
|
|
3440
|
+
const technical = `<p class="pr-helper-issue-desc">${renderInlineText(issue.description)}</p>${framed ? "" : explanation}
|
|
3441
|
+
${framed ? "" : renderReconciliationNote(issue.reconciliationNote)}
|
|
3442
|
+
${renderStatusHistory(issue.statusHistory, now)}
|
|
3443
|
+
${renderInvestigationNote(issue.investigationNote)}
|
|
3444
|
+
${renderExistingDiscussion(issue.existingDiscussion)}
|
|
3445
|
+
${renderFileLocation(issue.file, "pr-helper-issue-loc", issue.line)}
|
|
3446
|
+
${renderRelatedFiles(issue.relatedFiles)}
|
|
3447
|
+
${renderInvestigationProvenance("issue", issue.id, records, now)}`;
|
|
3448
|
+
const actions = `<div class="pr-helper-issue-actions">
|
|
3449
|
+
${renderInvestigationActions("issue", issue.id, records, investigatingId)}
|
|
3450
|
+
${renderDismissToggle(issue, isDismissed, stale, dismissals.get(issue.id), dispositions.isClosedByVerdict(issue))}
|
|
3451
|
+
</div>`;
|
|
3452
|
+
const body = framed ? `${fixed ? "" : renderConsequence(impact)}${explanation}
|
|
3453
|
+
${renderReconciliationNote(issue.reconciliationNote)}
|
|
3454
|
+
<div class="pr-helper-card-footer">
|
|
3455
|
+
<details class="pr-helper-issue-technical">
|
|
3456
|
+
<summary class="pr-helper-issue-technical-summary">Technical details</summary>
|
|
3457
|
+
<div class="pr-helper-issue-technical-body">
|
|
3458
|
+
${fixed && impact ? renderOriginalConsequence(impact) : ""}
|
|
3459
|
+
<p class="pr-helper-issue-technical-title">${renderInlineText(issue.title)}</p>
|
|
3460
|
+
${technical}
|
|
3461
|
+
</div>
|
|
3462
|
+
</details>
|
|
3463
|
+
${actions}
|
|
3464
|
+
</div>` : `${technical}
|
|
3465
|
+
${actions}`;
|
|
3466
|
+
const next = impact?.next ? ` data-issue-next="${impact.next.kind ?? "step"}"` : "";
|
|
3349
3467
|
return `
|
|
3350
|
-
<div class="pr-helper-issue-card ${cardGradeClass("issue", assessment.current)}${issue.reconciliationStatus ? ` pr-helper-issue-${issue.reconciliationStatus}` : ""}${issue.investigationImpact ? ` pr-helper-issue-investigation-${issue.investigationImpact}` : ""}${isDismissed && !stale ? " pr-helper-issue-card-dismissed" : ""}" data-finding-card="${escapeHtml$1(issue.id)}">
|
|
3468
|
+
<div class="pr-helper-issue-card ${cardGradeClass("issue", assessment.current)}${issue.reconciliationStatus ? ` pr-helper-issue-${issue.reconciliationStatus}` : ""}${issue.investigationImpact ? ` pr-helper-issue-investigation-${issue.investigationImpact}` : ""}${isDismissed && !stale ? " pr-helper-issue-card-dismissed" : ""}" data-finding-card="${escapeHtml$1(issue.id)}" data-issue-section="${sectionOf(issue)}"${next}>
|
|
3351
3469
|
<div class="pr-helper-issue-header">
|
|
3352
3470
|
<div class="pr-helper-issue-title-row">
|
|
3353
3471
|
${options?.feedbackSelect ? renderFeedbackPick("issue", issue.title, issueFeedbackMarkdown(issue), ref) : ""}
|
|
3354
3472
|
${renderFindingRef(ref)}
|
|
3355
|
-
<p class="pr-helper-issue-title">${renderInlineText(
|
|
3473
|
+
<p class="pr-helper-issue-title">${renderInlineText(headline)}</p>
|
|
3356
3474
|
${renderFindingCopySource(issueMarkdown(issue, assessment), "issue")}
|
|
3357
3475
|
${options?.askBuddy ? renderAskBuddyMenu("issue") : ""}
|
|
3358
3476
|
</div>
|
|
@@ -3367,27 +3485,62 @@ function renderIssuesTab(session, investigatingId, options) {
|
|
|
3367
3485
|
</div>
|
|
3368
3486
|
${renderStaleDismissal(stale)}
|
|
3369
3487
|
${renderAssessmentRegion("issue", assessment, grading, statusOf("issue", issue.id))}
|
|
3370
|
-
|
|
3371
|
-
${renderReconciliationNote(issue.reconciliationNote)}
|
|
3372
|
-
${renderStatusHistory(issue.statusHistory, now)}
|
|
3373
|
-
${renderInvestigationNote(issue.investigationNote)}
|
|
3374
|
-
${renderExistingDiscussion(issue.existingDiscussion)}
|
|
3375
|
-
${renderFileLocation(issue.file, "pr-helper-issue-loc", issue.line)}
|
|
3376
|
-
${renderRelatedFiles(issue.relatedFiles)}
|
|
3377
|
-
${renderInvestigationProvenance("issue", issue.id, records, now)}
|
|
3378
|
-
<div class="pr-helper-issue-actions">
|
|
3379
|
-
${renderInvestigationActions("issue", issue.id, records, investigatingId)}
|
|
3380
|
-
${renderDismissToggle(issue, isDismissed, stale, dismissals.get(issue.id), dispositions.isClosedByVerdict(issue))}
|
|
3381
|
-
</div>
|
|
3488
|
+
${body}
|
|
3382
3489
|
</div>
|
|
3383
3490
|
`;
|
|
3384
3491
|
};
|
|
3385
3492
|
const counts = issuesOutstanding(result.issues, dispositions);
|
|
3386
|
-
|
|
3387
|
-
const open = result.issues.filter((issue) => isIssueOutstanding(issue, dispositions)).map(card).join("");
|
|
3493
|
+
const outstanding = result.issues.filter((issue) => isIssueOutstanding(issue, dispositions));
|
|
3388
3494
|
const closedIssues = result.issues.filter((issue) => !isIssueOutstanding(issue, dispositions));
|
|
3389
|
-
const
|
|
3390
|
-
|
|
3495
|
+
const folds = options?.issueFolds ?? {};
|
|
3496
|
+
const tally = tallyIssues(outstanding);
|
|
3497
|
+
const grouped = new Map(groupIssues(outstanding, (issue) => assess("issue", issue).current).map((group) => [group.section, group.issues]));
|
|
3498
|
+
const present = new Set(result.issues.map(sectionOf));
|
|
3499
|
+
const headless = present.size === 1 && present.has("other");
|
|
3500
|
+
const groups = ISSUE_SECTIONS.filter((section) => present.has(section)).map((section) => {
|
|
3501
|
+
const issues = grouped.get(section) ?? [];
|
|
3502
|
+
if (headless) return `<div class="pr-helper-issue-group" data-impact-group="${section}"${issues.length ? "" : " hidden"}>
|
|
3503
|
+
<div class="pr-helper-issue-group-list">${issues.map(card).join("")}</div>
|
|
3504
|
+
</div>`;
|
|
3505
|
+
return `<details class="pr-helper-issue-group" data-impact-group="${section}"${folds[section] === false ? "" : " open"}${issues.length ? "" : " hidden"}>
|
|
3506
|
+
<summary class="pr-helper-issue-group-summary"><h4 class="pr-helper-issue-group-title">${escapeHtml$1(ISSUE_SECTION_TITLES[section])}</h4><span class="pr-helper-issue-group-count" data-issue-group-count>${issues.length}</span></summary>
|
|
3507
|
+
<div class="pr-helper-issue-group-list">${issues.map(card).join("")}</div>
|
|
3508
|
+
</details>`;
|
|
3509
|
+
}).join("");
|
|
3510
|
+
const nav = renderImpactNavItems(tally);
|
|
3511
|
+
const orientation = result.orientation?.trim();
|
|
3512
|
+
const head = `<div class="pr-helper-issues-orientation">
|
|
3513
|
+
${orientation ? `<p class="pr-helper-issues-orientation-text">${renderInlineText(orientation)}</p>` : ""}
|
|
3514
|
+
<p class="pr-helper-issues-attention" role="status" data-issues-attention>${escapeHtml$1(issuesAttentionLine(tally))}</p>
|
|
3515
|
+
<nav class="pr-helper-issue-nav" aria-label="Issues by kind" data-issue-nav-list${nav ? "" : " hidden"}>${nav}</nav>
|
|
3516
|
+
</div>`;
|
|
3517
|
+
if (closedIssues.length === 0) return `${head}${groups}`;
|
|
3518
|
+
return `${head}${groups}${renderClosedFindings(closedFindingsSummary(counts), closedIssues.map(card).join(""), "issues", counts.open === 0, closedIssues.filter((issue) => assess("issue", issue).pending || statusOf("issue", issue.id).pending).length, folds.closed === true)}`;
|
|
3519
|
+
}
|
|
3520
|
+
/** What the issue means, read first: the scenario and the step that would settle it. */
|
|
3521
|
+
function renderConsequence(impact) {
|
|
3522
|
+
if (!impact) return "";
|
|
3523
|
+
return `<div class="pr-helper-issue-impact">
|
|
3524
|
+
<p class="pr-helper-issue-scenario">${renderInlineText(impact.scenario)}</p>
|
|
3525
|
+
${renderConsequenceStep(impact.next)}
|
|
3526
|
+
</div>`;
|
|
3527
|
+
}
|
|
3528
|
+
var STEP_LABELS = {
|
|
3529
|
+
verify: "To verify:",
|
|
3530
|
+
decide: "To decide:",
|
|
3531
|
+
fix: "To fix:"
|
|
3532
|
+
};
|
|
3533
|
+
function renderConsequenceStep(step) {
|
|
3534
|
+
if (!step) return "";
|
|
3535
|
+
return `<p class="pr-helper-issue-next"><span class="pr-helper-issue-next-label">${step.kind ? STEP_LABELS[step.kind] : "Next step:"}</span> ${renderInlineText(step.text)}</p>`;
|
|
3536
|
+
}
|
|
3537
|
+
/** A fixed issue's original consequence, kept with the technical account once the fix heads the card. */
|
|
3538
|
+
function renderOriginalConsequence(impact) {
|
|
3539
|
+
return `<div class="pr-helper-issue-original">
|
|
3540
|
+
<h5>Before the fix</h5>
|
|
3541
|
+
<p class="pr-helper-issue-original-title">${renderInlineText(impact.title)}</p>
|
|
3542
|
+
<p class="pr-helper-issue-scenario">${renderInlineText(impact.scenario)}</p>
|
|
3543
|
+
</div>`;
|
|
3391
3544
|
}
|
|
3392
3545
|
/**
|
|
3393
3546
|
* What to call the closed pile, by what is actually in it.
|
|
@@ -3470,13 +3623,47 @@ function renderQuestionCard(q, sections, answered, records, now = Date.now(), in
|
|
|
3470
3623
|
const answerToggle = `<button type="button" class="pr-helper-question-answer-toggle" data-question-answered="${escapeHtml$1(q.id)}" data-answered="${answered}"${settled ? " data-settled=\"true\"" : ""}${verdictClosed ? " data-verdict-closed=\"true\"" : ""}>${answered ? "Mark as unanswered" : "Mark answered"}</button>`;
|
|
3471
3624
|
const investigationAnswer = q.investigationAnswer?.trim() ? `<h5>Answer from investigation</h5><p class="pr-helper-issue-desc pr-helper-investigation-answer">${renderInlineText(q.investigationAnswer)}</p>` : "";
|
|
3472
3625
|
const reconciliationAnswer = q.reconciliationAnswer?.trim() ? `<h5>${escapeHtml$1(reconciledAnswerHeading(q.reconciledAgainst))}</h5><p class="pr-helper-issue-desc pr-helper-investigation-answer">${renderInlineText(q.reconciliationAnswer)}</p>` : "";
|
|
3626
|
+
const why = q.whyThisMatters ? `<h5>Why this matters</h5><p class="pr-helper-issue-desc">${renderInlineText(q.whyThisMatters)}</p>` : "";
|
|
3627
|
+
const files = q.file || q.relatedFiles?.length ? `<h5>Files</h5>${renderFileLocation(q.file, "pr-helper-issue-loc", void 0)}${renderRelatedFiles(q.relatedFiles)}` : "";
|
|
3628
|
+
const provenance = renderInvestigationProvenance("question", q.id, records, now);
|
|
3629
|
+
const framing = q.framing;
|
|
3630
|
+
const actions = `<div class="pr-helper-question-actions">
|
|
3631
|
+
${jumpLink}
|
|
3632
|
+
<div class="pr-helper-question-actions-group">
|
|
3633
|
+
${renderInvestigationActions("question", q.id, records, investigatingId)}
|
|
3634
|
+
${answerToggle}
|
|
3635
|
+
</div>
|
|
3636
|
+
</div>`;
|
|
3637
|
+
const answers = `${reconciliationAnswer}
|
|
3638
|
+
${renderReconciliationNote(q.reconciliationNote)}
|
|
3639
|
+
${investigationAnswer}`;
|
|
3640
|
+
const body = framing ? `${renderQuestionFraming(framing)}${renderCardExplanation(explanation, options)}
|
|
3641
|
+
${answers}
|
|
3642
|
+
<div class="pr-helper-card-footer">
|
|
3643
|
+
<details class="pr-helper-issue-technical">
|
|
3644
|
+
<summary class="pr-helper-issue-technical-summary">Technical context</summary>
|
|
3645
|
+
<div class="pr-helper-issue-technical-body">
|
|
3646
|
+
<p class="pr-helper-issue-technical-title">${renderInlineText(q.question)}</p>
|
|
3647
|
+
${why}
|
|
3648
|
+
${renderInvestigationNote(q.investigationNote)}
|
|
3649
|
+
${files}
|
|
3650
|
+
${provenance}
|
|
3651
|
+
</div>
|
|
3652
|
+
</details>
|
|
3653
|
+
${actions}
|
|
3654
|
+
</div>` : `${why}${renderCardExplanation(explanation, options)}
|
|
3655
|
+
${answers}
|
|
3656
|
+
${renderInvestigationNote(q.investigationNote)}
|
|
3657
|
+
${files}
|
|
3658
|
+
${provenance}
|
|
3659
|
+
${actions}`;
|
|
3473
3660
|
return `
|
|
3474
3661
|
<div class="pr-helper-question-card${answered ? " pr-helper-question-card-answered" : ""}${q.investigationImpact ? ` pr-helper-issue-investigation-${q.investigationImpact}` : ""}" data-finding-card="${escapeHtml$1(q.id)}"${questionHome(q) ? ` data-section-id="${escapeHtml$1(questionHome(q))}"` : ""}>
|
|
3475
3662
|
<div class="pr-helper-issue-header">
|
|
3476
3663
|
<div class="pr-helper-issue-title-row">
|
|
3477
3664
|
${options?.feedbackSelect ? renderFeedbackPick("question", q.question, questionFeedbackMarkdown(q), ref) : ""}
|
|
3478
3665
|
${renderFindingRef(ref)}
|
|
3479
|
-
<p class="pr-helper-issue-title">${renderInlineText(q.question)}</p>
|
|
3666
|
+
<p class="pr-helper-issue-title">${renderInlineText(framing?.question ?? q.question)}</p>
|
|
3480
3667
|
${renderFindingCopySource(questionMarkdown(q, assessment), "question")}
|
|
3481
3668
|
${options?.askBuddy ? renderAskBuddyMenu("question") : ""}
|
|
3482
3669
|
</div>
|
|
@@ -3493,23 +3680,17 @@ function renderQuestionCard(q, sections, answered, records, now = Date.now(), in
|
|
|
3493
3680
|
</div>
|
|
3494
3681
|
</div>
|
|
3495
3682
|
${renderAssessmentRegion("question", assessment, grading, status)}
|
|
3496
|
-
${
|
|
3497
|
-
${reconciliationAnswer}
|
|
3498
|
-
${renderReconciliationNote(q.reconciliationNote)}
|
|
3499
|
-
${investigationAnswer}
|
|
3500
|
-
${renderInvestigationNote(q.investigationNote)}
|
|
3501
|
-
${q.file || q.relatedFiles?.length ? `<h5>Files</h5>${renderFileLocation(q.file, "pr-helper-issue-loc", void 0)}${renderRelatedFiles(q.relatedFiles)}` : ""}
|
|
3502
|
-
${renderInvestigationProvenance("question", q.id, records, now)}
|
|
3503
|
-
<div class="pr-helper-question-actions">
|
|
3504
|
-
${jumpLink}
|
|
3505
|
-
<div class="pr-helper-question-actions-group">
|
|
3506
|
-
${renderInvestigationActions("question", q.id, records, investigatingId)}
|
|
3507
|
-
${answerToggle}
|
|
3508
|
-
</div>
|
|
3509
|
-
</div>
|
|
3683
|
+
${body}
|
|
3510
3684
|
</div>
|
|
3511
3685
|
`;
|
|
3512
3686
|
}
|
|
3687
|
+
/** What the question means, read first: why the answer matters and what is needed. */
|
|
3688
|
+
function renderQuestionFraming(framing) {
|
|
3689
|
+
return `<div class="pr-helper-issue-impact">
|
|
3690
|
+
<p class="pr-helper-issue-scenario">${renderInlineText(framing.why)}</p>
|
|
3691
|
+
${framing.needed ? `<p class="pr-helper-issue-next pr-helper-question-needed"><span class="pr-helper-issue-next-label">Needed:</span> ${renderInlineText(framing.needed)}</p>` : ""}
|
|
3692
|
+
</div>`;
|
|
3693
|
+
}
|
|
3513
3694
|
/**
|
|
3514
3695
|
* The counter's contents, split out so a caller that toggles one question can refresh it without
|
|
3515
3696
|
* re-rendering the tab.
|
|
@@ -6783,7 +6964,8 @@ function renderStandardViews(view, sections) {
|
|
|
6783
6964
|
askBuddy: true,
|
|
6784
6965
|
feedbackSelect: true,
|
|
6785
6966
|
reassess: true,
|
|
6786
|
-
findingRefs: true
|
|
6967
|
+
findingRefs: true,
|
|
6968
|
+
...view.reviewLayout?.issueGroups ? { issueFolds: view.reviewLayout.issueGroups } : {}
|
|
6787
6969
|
})}
|
|
6788
6970
|
</section>
|
|
6789
6971
|
<section class="prb-view" data-view="questions" hidden>
|
|
@@ -6810,20 +6992,28 @@ function renderStandardViews(view, sections) {
|
|
|
6810
6992
|
* When the "already raised" claims below were last true.
|
|
6811
6993
|
*
|
|
6812
6994
|
* Every attribution on this view carries a thread's state — "still unresolved", "since marked
|
|
6813
|
-
* resolved" — and that state was frozen the moment the conversation was imported. Nothing
|
|
6814
|
-
* re-checks it
|
|
6815
|
-
*
|
|
6995
|
+
* resolved" — and that state was frozen the moment the conversation was imported. Nothing on this
|
|
6996
|
+
* view re-checks it; the PR context view does, on request. Until then a card can go on telling the
|
|
6997
|
+
* reviewer that Sergei's thread is open an hour after Sergei closed it.
|
|
6816
6998
|
*
|
|
6817
6999
|
* Every other kind of staleness in this product degrades into vagueness. This one degrades into a
|
|
6818
7000
|
* confident, specific, wrong claim about what a named colleague currently thinks, which is worth
|
|
6819
|
-
* one line above the list
|
|
7001
|
+
* one line above the list: its age always in view, the explanation one disclosure away, and the way
|
|
7002
|
+
* to the re-check beside it.
|
|
6820
7003
|
*
|
|
6821
7004
|
* Only where a conversation was attached: there are no such claims to qualify otherwise.
|
|
6822
7005
|
*/
|
|
6823
7006
|
function renderAttributionAge(view) {
|
|
6824
7007
|
if (!view.prContext) return "";
|
|
6825
7008
|
const imported = relativeTime$1(view.prContext.prContext.discussion.fetchedAt);
|
|
6826
|
-
return `<
|
|
7009
|
+
return `<div class="prb-discussion-age">
|
|
7010
|
+
<details class="prb-discussion-age-detail">
|
|
7011
|
+
<summary class="prb-discussion-age-summary">PR discussion checked ${escapeHtml$1(imported)}</summary>
|
|
7012
|
+
<p class="prb-discussion-age-body">Any “already raised” note below, and whether each thread was still open, describes the pull request as it stood when its discussion was fetched. A reply or a resolved thread since then is not reflected here until the conversation is re-checked.</p>
|
|
7013
|
+
</details>
|
|
7014
|
+
<span class="prb-discussion-age-sep" aria-hidden="true">·</span>
|
|
7015
|
+
<button type="button" class="prb-doc-note-link" data-view-nav="prcontext">Re-check in PR context</button>
|
|
7016
|
+
</div>`;
|
|
6827
7017
|
}
|
|
6828
7018
|
/**
|
|
6829
7019
|
* The link out to this file on the pull request.
|
|
@@ -10136,7 +10326,7 @@ function renderReviewsPage(view) {
|
|
|
10136
10326
|
const collected = (memberOf.get(group.key) ?? []).length > 0;
|
|
10137
10327
|
const home = homeSection(group.latest.pinned === true, group.latest.lifecycle, activityOf(group), collected, "all");
|
|
10138
10328
|
if (home === void 0) collectedHome.add(group);
|
|
10139
|
-
sections[home ?? sectionOf(group.latest.lifecycle, activityOf(group))].push(group);
|
|
10329
|
+
sections[home ?? sectionOf$1(group.latest.lifecycle, activityOf(group))].push(group);
|
|
10140
10330
|
}
|
|
10141
10331
|
const storedReviews = view.groups.reduce((total, group) => total + group.history.length, 0);
|
|
10142
10332
|
const renderSectionAt = (section) => renderSection(section, sections[section], (groups) => renderCards(groups, "home", (group) => collectedHome.has(group)), sectionOpen(section, view.sectionsCollapsed), sections[section].filter((group) => !inProgressOnly.has(group.latest.id) && !collectedHome.has(group)).length);
|
|
@@ -11000,10 +11190,11 @@ function isRunsSection(value) {
|
|
|
11000
11190
|
/**
|
|
11001
11191
|
* How the reviewer last laid out a review page, kept so the next review opens the same way.
|
|
11002
11192
|
*
|
|
11003
|
-
*
|
|
11193
|
+
* Four things, each the layout as it last stood rather than a log of what was pressed: whether
|
|
11004
11194
|
* Buddy AI was open or closed, whether a surface was expanded (Buddy AI itself, or the document,
|
|
11005
|
-
* whichever pane that is on the next review),
|
|
11006
|
-
* nobody has changed has no key, so the page's own
|
|
11195
|
+
* whichever pane that is on the next review), which parts of the rail were folded, and which
|
|
11196
|
+
* sections of the Issues pane were. A field nobody has changed has no key, so the page's own
|
|
11197
|
+
* defaults still decide for a new reviewer.
|
|
11007
11198
|
*
|
|
11008
11199
|
* Pure, so the page and the server read it with the same rules. Stored in `ui.json`; see
|
|
11009
11200
|
* `preferences.ts`.
|
|
@@ -11014,35 +11205,41 @@ var RAIL_FOLDS = [
|
|
|
11014
11205
|
"prb-rail-path",
|
|
11015
11206
|
"prb-rail-kiss"
|
|
11016
11207
|
];
|
|
11017
|
-
function
|
|
11208
|
+
function readFolds(value, known) {
|
|
11018
11209
|
if (!value || typeof value !== "object") return void 0;
|
|
11019
|
-
const
|
|
11020
|
-
for (const fold of
|
|
11210
|
+
const folds = {};
|
|
11211
|
+
for (const fold of known) {
|
|
11021
11212
|
const open = value[fold];
|
|
11022
|
-
if (typeof open === "boolean")
|
|
11213
|
+
if (typeof open === "boolean") folds[fold] = open;
|
|
11023
11214
|
}
|
|
11024
|
-
return Object.keys(
|
|
11215
|
+
return Object.keys(folds).length > 0 ? folds : void 0;
|
|
11025
11216
|
}
|
|
11217
|
+
var readRail = (value) => readFolds(value, RAIL_FOLDS);
|
|
11218
|
+
var readIssueGroups = (value) => readFolds(value, ISSUE_FOLDS);
|
|
11026
11219
|
/** A stored layout, keeping only what this page knows how to draw. Anything else reads as unset. */
|
|
11027
11220
|
function readReviewLayout(value) {
|
|
11028
11221
|
if (!value || typeof value !== "object") return {};
|
|
11029
|
-
const { ask, expanded, rail } = value;
|
|
11222
|
+
const { ask, expanded, rail, issueGroups } = value;
|
|
11030
11223
|
const folds = readRail(rail);
|
|
11224
|
+
const groups = readIssueGroups(issueGroups);
|
|
11031
11225
|
return {
|
|
11032
11226
|
...ask === "open" || ask === "closed" ? { ask } : {},
|
|
11033
11227
|
...expanded === "buddy" || expanded === "document" ? { expanded } : {},
|
|
11034
|
-
...folds ? { rail: folds } : {}
|
|
11228
|
+
...folds ? { rail: folds } : {},
|
|
11229
|
+
...groups ? { issueGroups: groups } : {}
|
|
11035
11230
|
};
|
|
11036
11231
|
}
|
|
11037
11232
|
/** A patch from the page, or null when it holds nothing this page could have sent. */
|
|
11038
11233
|
function readReviewLayoutPatch(value) {
|
|
11039
11234
|
if (!value || typeof value !== "object") return null;
|
|
11040
|
-
const { ask, expanded, rail } = value;
|
|
11235
|
+
const { ask, expanded, rail, issueGroups } = value;
|
|
11041
11236
|
const folds = readRail(rail);
|
|
11237
|
+
const groups = readIssueGroups(issueGroups);
|
|
11042
11238
|
const patch = {
|
|
11043
11239
|
...ask === "open" || ask === "closed" ? { ask } : {},
|
|
11044
11240
|
...expanded === "buddy" || expanded === "document" || expanded === null ? { expanded } : {},
|
|
11045
|
-
...folds ? { rail: folds } : {}
|
|
11241
|
+
...folds ? { rail: folds } : {},
|
|
11242
|
+
...groups ? { issueGroups: groups } : {}
|
|
11046
11243
|
};
|
|
11047
11244
|
return Object.keys(patch).length > 0 ? patch : null;
|
|
11048
11245
|
}
|
|
@@ -11056,6 +11253,10 @@ function applyReviewLayoutPatch(layout, patch) {
|
|
|
11056
11253
|
...patch.rail ? { rail: {
|
|
11057
11254
|
...layout.rail,
|
|
11058
11255
|
...patch.rail
|
|
11256
|
+
} } : {},
|
|
11257
|
+
...patch.issueGroups ? { issueGroups: {
|
|
11258
|
+
...layout.issueGroups,
|
|
11259
|
+
...patch.issueGroups
|
|
11059
11260
|
} } : {}
|
|
11060
11261
|
};
|
|
11061
11262
|
}
|
|
@@ -12522,6 +12723,33 @@ feature: E</pre>
|
|
|
12522
12723
|
reads to answer your questions about the change, and what Update review starts from. After
|
|
12523
12724
|
that it is removed. The review itself stays, and a later question makes a new checkout for
|
|
12524
12725
|
itself.</p>`
|
|
12726
|
+
},
|
|
12727
|
+
{
|
|
12728
|
+
id: "reading-issues",
|
|
12729
|
+
title: "Reading issues",
|
|
12730
|
+
body: `<p>Issues opens with a sentence on what the change touches and how many issues are
|
|
12731
|
+
outstanding. Each issue is grouped by the decision it asks of you: <b>Failures</b> the code shows
|
|
12732
|
+
going wrong under a stated condition, <b>Possible risks</b> still to be verified, <b>Intent to
|
|
12733
|
+
confirm</b> where the behaviour is right only if it was meant, and <b>Maintenance</b> for what it
|
|
12734
|
+
costs later. Within each group the most severe come first. Severity and confidence are shown on
|
|
12735
|
+
each card, separately from its group.</p>
|
|
12736
|
+
<p>A card leads with what the issue means and when it happens, then the step it calls for, when
|
|
12737
|
+
there is one: <b>To verify</b> settles whether it happens, <b>To decide</b> asks whether it was
|
|
12738
|
+
meant, and <b>To fix</b> names the change that would correct it. The technical account, where it
|
|
12739
|
+
is in the code and its history are under <b>Technical details</b> on the card. Folding a group is
|
|
12740
|
+
remembered for the next review.</p>
|
|
12741
|
+
<p>Issues from older reviews have no summary yet and are under <b>Other issues</b>. The next
|
|
12742
|
+
Update that finds new commits or new discussion writes one for each issue still outstanding,
|
|
12743
|
+
from the same evidence; it does not re-check the issue. An Update that finds nothing new changes
|
|
12744
|
+
nothing.</p>
|
|
12745
|
+
<p>Questions read the same way: each leads with the question in plain terms, why the answer
|
|
12746
|
+
matters and what is needed, with the original question and its files under <b>Technical
|
|
12747
|
+
context</b>. Any answer so far stays on the card.</p>
|
|
12748
|
+
<p>When the review came with a pull request discussion, the line above the issues says when it
|
|
12749
|
+
was last checked. Its “already raised” notes describe the discussion as it was then; <b>Re-check
|
|
12750
|
+
in PR context</b> takes you to bring it up to date.</p>
|
|
12751
|
+
<p>Resolved and dismissed issues are in the closed list at the bottom. One the new commits fixed
|
|
12752
|
+
leads with what the code now does, when the update could see it.</p>`
|
|
12525
12753
|
},
|
|
12526
12754
|
{
|
|
12527
12755
|
id: "updating-and-reanalysing",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prreviewbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.92.0",
|
|
4
4
|
"description": "Understand code changes before you approve them. Local branch and pull request reviews with Claude Code or Codex.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://prreviewbuddy.com",
|
package/static/shell.css
CHANGED
|
@@ -2386,10 +2386,6 @@ a.prb-rail-item { text-decoration: none; }
|
|
|
2386
2386
|
line-height: var(--lh-body);
|
|
2387
2387
|
}
|
|
2388
2388
|
|
|
2389
|
-
/* A caveat above a list, in the light pane. Pane literals, not shell tokens — see
|
|
2390
|
-
pane_palette.test.ts for why this is a rule and not a preference. */
|
|
2391
|
-
/* Full width, because it introduces the cards below it and a narrower block reads as a floating
|
|
2392
|
-
aside rather than as the heading note it is. */
|
|
2393
2389
|
/* The control named in the note, as the control rather than as bold text. It goes to the view that
|
|
2394
2390
|
holds it, because a sentence that tells the reviewer where something is and cannot take them
|
|
2395
2391
|
there is asking them to do the navigating twice. */
|
|
@@ -2405,16 +2401,41 @@ a.prb-rail-item { text-decoration: none; }
|
|
|
2405
2401
|
}
|
|
2406
2402
|
.prb-doc-note-link:hover { color: var(--pane-ink); }
|
|
2407
2403
|
|
|
2408
|
-
.
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2404
|
+
/* The age of the "already raised" claims, as one quiet line above the Issues orientation. */
|
|
2405
|
+
.prb-discussion-age {
|
|
2406
|
+
display: flex;
|
|
2407
|
+
flex-wrap: wrap;
|
|
2408
|
+
align-items: baseline;
|
|
2409
|
+
column-gap: 6px;
|
|
2410
|
+
margin: 0 0 12px;
|
|
2414
2411
|
color: var(--pane-ink-muted);
|
|
2415
2412
|
font-size: var(--t-meta);
|
|
2416
2413
|
line-height: var(--lh-body);
|
|
2417
2414
|
}
|
|
2415
|
+
.prb-discussion-age-summary {
|
|
2416
|
+
cursor: pointer;
|
|
2417
|
+
list-style: none;
|
|
2418
|
+
}
|
|
2419
|
+
.prb-discussion-age-summary::-webkit-details-marker { display: none; }
|
|
2420
|
+
.prb-discussion-age-summary::before {
|
|
2421
|
+
content: '';
|
|
2422
|
+
display: inline-block;
|
|
2423
|
+
margin-right: 6px;
|
|
2424
|
+
vertical-align: 1px;
|
|
2425
|
+
border-left: 5px solid currentColor;
|
|
2426
|
+
border-top: 4px solid transparent;
|
|
2427
|
+
border-bottom: 4px solid transparent;
|
|
2428
|
+
}
|
|
2429
|
+
.prb-discussion-age-detail[open] > .prb-discussion-age-summary::before { transform: rotate(90deg); }
|
|
2430
|
+
.prb-discussion-age-summary:hover { color: var(--pane-ink); }
|
|
2431
|
+
/* Open, the explanation takes the row, and the re-check follows it on its own line. */
|
|
2432
|
+
.prb-discussion-age-detail[open] { flex-basis: 100%; }
|
|
2433
|
+
.prb-discussion-age-detail[open] ~ .prb-discussion-age-sep { display: none; }
|
|
2434
|
+
.prb-discussion-age-body {
|
|
2435
|
+
margin: 6px 0 4px;
|
|
2436
|
+
padding-left: 11px;
|
|
2437
|
+
border-left: 2px solid var(--pane-line);
|
|
2438
|
+
}
|
|
2418
2439
|
|
|
2419
2440
|
.prb-pr-asof {
|
|
2420
2441
|
flex-basis: 100%;
|