@jphutchins/code-review 0.1.0-alpha.43 → 0.1.0-alpha.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/dist/index.js +41 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema/VERSIONING.md +4 -3
- package/schema/findings.schema.json +15 -2
package/README.md
CHANGED
|
@@ -104,14 +104,14 @@ links to:
|
|
|
104
104
|
[`src/surface.ts`](src/surface.ts).
|
|
105
105
|
|
|
106
106
|
The embedded document is the agent's **complete** findings document — the same `schema_version`
|
|
107
|
-
0.
|
|
107
|
+
0.9.0 contract the review agent is held to and
|
|
108
108
|
[`schema/findings.schema.json`](schema/findings.schema.json) validates, and the same object the
|
|
109
109
|
comment is rendered from. It is embedded verbatim: no field is added or dropped, so the machine
|
|
110
110
|
channel can never carry less than, or drift from, the rendered prose.
|
|
111
111
|
|
|
112
112
|
```json
|
|
113
113
|
{
|
|
114
|
-
"schema_version": "0.
|
|
114
|
+
"schema_version": "0.9.0",
|
|
115
115
|
"verdict": "comment",
|
|
116
116
|
"summary": "...",
|
|
117
117
|
"findings": []
|
|
@@ -121,8 +121,10 @@ links to:
|
|
|
121
121
|
The deterministic **stop signal** for an iterating author-agent rides its own compact marker beside
|
|
122
122
|
the findings blob, `<!-- code-review:signal;base64 <base64> -->`, whose decoded payload is
|
|
123
123
|
`{ "schema_version": "0.8.0", "round": <n>, "convergence": { "score": <s>, "threshold": <t>, "converged": <bool> } }`.
|
|
124
|
-
`round` is the count of completed full-review rounds; `convergence.score`
|
|
125
|
-
|
|
124
|
+
`round` is the count of completed full-review rounds; `convergence.score` sums each finding and
|
|
125
|
+
systemic problem's `floor(severity) + max(0, ceiling − floor) × confidence` (ceilings critical 4 ·
|
|
126
|
+
major 2 · minor 1 · nit 0; floors critical `threshold + 0.01` · major 0.5 · else 0), rounded to 2
|
|
127
|
+
decimals; `threshold` defaults to 1, and `converged` = score ≤ threshold as a literal
|
|
126
128
|
boolean — `converged: true` means the last completed round is at or below the tolerance, so another
|
|
127
129
|
iteration round is not warranted. The commenter computes the signal from the review's own severities
|
|
128
130
|
(the agent never writes it); it appears once at least one full-review round has completed and is
|
|
@@ -137,8 +139,10 @@ links to:
|
|
|
137
139
|
frequencies — from which the re-review seed re-derives the advisory `scope_metastasis` entry it
|
|
138
140
|
hands the next-round agent. It is deliberately NOT embedded in the findings blob: the blob is the
|
|
139
141
|
agent's own document, and a recurrence claim is round state the commenter owns. Each inline review
|
|
140
|
-
comment embeds only its own finding (a `schema_version` + one-finding fragment),
|
|
141
|
-
review-body
|
|
142
|
+
comment embeds only its own finding (a `schema_version` + one-finding fragment), and the
|
|
143
|
+
review-object body only links the sticky — so the **sticky is the sole documented decode surface**
|
|
144
|
+
for the whole-document marker; a decoding agent reads it there. The review body is written only after
|
|
145
|
+
the sticky exists (a failed sticky write aborts the run first), so it never carries the blob itself.
|
|
142
146
|
- **`code-review-transcript`** — the full Claude Code session transcripts for the triage and review
|
|
143
147
|
phases. This is advisory/auditability only: it is never read by the comment job and never affects
|
|
144
148
|
what gets posted.
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var LineNumber = t.refinement(
|
|
|
31
31
|
"LineNumber"
|
|
32
32
|
);
|
|
33
33
|
var Confidence = t.refinement(t.number, (n) => n >= 0 && n <= 1, "Confidence");
|
|
34
|
+
var Likelihood = t.refinement(t.number, (n) => n >= 0 && n <= 1, "Likelihood");
|
|
34
35
|
var SCHEMA_VERSION_RE = /^(0|[1-9]\d*)\.(\d+)\.(\d+)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
35
36
|
var SchemaVersion = t.refinement(
|
|
36
37
|
t.string,
|
|
@@ -55,7 +56,8 @@ var FindingShape = t.intersection([
|
|
|
55
56
|
title: t.string,
|
|
56
57
|
description: t.string,
|
|
57
58
|
reasoning: t.string,
|
|
58
|
-
confidence: Confidence
|
|
59
|
+
confidence: Confidence,
|
|
60
|
+
likelihood: Likelihood
|
|
59
61
|
}),
|
|
60
62
|
FindingRuleCodec,
|
|
61
63
|
t.partial({
|
|
@@ -75,7 +77,8 @@ var SystemicRequired = t.type({
|
|
|
75
77
|
description: t.string,
|
|
76
78
|
severity: SeverityCodec,
|
|
77
79
|
reasoning: t.string,
|
|
78
|
-
confidence: Confidence
|
|
80
|
+
confidence: Confidence,
|
|
81
|
+
likelihood: Likelihood
|
|
79
82
|
});
|
|
80
83
|
var SystemicOptional = t.partial({
|
|
81
84
|
finding_codes: t.array(t.string),
|
|
@@ -202,7 +205,7 @@ var TestSummaryCodec = t.intersection([
|
|
|
202
205
|
failures: t.array(TestFailureCodec)
|
|
203
206
|
})
|
|
204
207
|
]);
|
|
205
|
-
var DEFAULT_SCHEMA_VERSION = "0.
|
|
208
|
+
var DEFAULT_SCHEMA_VERSION = "0.9.0";
|
|
206
209
|
var incompleteFindings = (summary) => ({
|
|
207
210
|
schema_version: DEFAULT_SCHEMA_VERSION,
|
|
208
211
|
summary,
|
|
@@ -585,19 +588,30 @@ var computeSameRootNotes = (priorRounds, findings, currentSha) => {
|
|
|
585
588
|
}
|
|
586
589
|
return Object.fromEntries(entries);
|
|
587
590
|
};
|
|
588
|
-
var
|
|
591
|
+
var CONVERGENCE_CEILINGS = { critical: 4, major: 2, minor: 1, nit: 0 };
|
|
592
|
+
var CRITICAL_FLOOR_MARGIN = 0.01;
|
|
589
593
|
var DEFAULT_CONVERGENCE_THRESHOLD = 1;
|
|
590
|
-
var
|
|
591
|
-
var
|
|
592
|
-
|
|
594
|
+
var convergenceFloor = (severity, threshold) => severity === "critical" ? threshold + CRITICAL_FLOOR_MARGIN : severity === "major" ? 0.5 : 0;
|
|
595
|
+
var round2 = (n) => Math.round((n + Number.EPSILON) * 100) / 100;
|
|
596
|
+
var convergenceScore = (doc, threshold) => round2(
|
|
597
|
+
[...doc.findings, ...doc.systemic_problems ?? []].reduce(
|
|
598
|
+
(sum, { severity, confidence, likelihood }) => {
|
|
599
|
+
const floor = convergenceFloor(severity, threshold);
|
|
600
|
+
return sum + floor + Math.max(0, CONVERGENCE_CEILINGS[severity] - floor) * confidence * likelihood;
|
|
601
|
+
},
|
|
602
|
+
0
|
|
603
|
+
)
|
|
604
|
+
);
|
|
605
|
+
var convergenceSummary = (doc, threshold = DEFAULT_CONVERGENCE_THRESHOLD) => {
|
|
606
|
+
const { score, converged } = convergenceSignal(doc, threshold);
|
|
593
607
|
return converged ? `**Convergence** \u{1F3C1} ${String(score)} \u2264 ${String(threshold)} \u2014 converged` : `**Convergence** \u{1F504} ${String(score)} > ${String(threshold)} \u2014 iterating`;
|
|
594
608
|
};
|
|
595
609
|
var SURFACE_SCHEMA_VERSION = "0.8.0";
|
|
596
|
-
var convergenceSignal = (
|
|
597
|
-
const score = convergenceScore(
|
|
610
|
+
var convergenceSignal = (doc, threshold = DEFAULT_CONVERGENCE_THRESHOLD) => {
|
|
611
|
+
const score = convergenceScore(doc, threshold);
|
|
598
612
|
return { score, threshold, converged: score <= threshold };
|
|
599
613
|
};
|
|
600
|
-
var signalForRound = (round,
|
|
614
|
+
var signalForRound = (round, doc, threshold = DEFAULT_CONVERGENCE_THRESHOLD) => ({ round, convergence: convergenceSignal(doc, threshold) });
|
|
601
615
|
var signalMarker = (signal) => `<!-- code-review:signal;base64 ${Buffer.from(
|
|
602
616
|
JSON.stringify({ schema_version: SURFACE_SCHEMA_VERSION, ...signal }),
|
|
603
617
|
"utf-8"
|
|
@@ -660,12 +674,9 @@ var projectPatch = (patch) => {
|
|
|
660
674
|
return typeof lowered === "string" ? { kind: "suggestion", text: escapeFence(lowered) } : { kind: "patch", raw: escapeFence(patch) };
|
|
661
675
|
};
|
|
662
676
|
var formatConfidence = (n) => n.toFixed(2);
|
|
663
|
-
var reviewBodyPointer = (headSha, stickyUrl
|
|
677
|
+
var reviewBodyPointer = (headSha, stickyUrl) => {
|
|
664
678
|
const sha7 = headSha.slice(0, 7);
|
|
665
|
-
|
|
666
|
-
return marker ? `${marker}
|
|
667
|
-
|
|
668
|
-
${linkLine}` : linkLine;
|
|
679
|
+
return stickyUrl ? `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the [summary comment](${stickyUrl}) for the verdict, walkthrough, and cost.` : `\u{1F916} Automated code review for \`${sha7}\` \u2014 see the summary comment for the verdict, walkthrough, and cost.`;
|
|
669
680
|
};
|
|
670
681
|
var asRecord = (u) => typeof u === "object" && u !== null && !Array.isArray(u) ? u : null;
|
|
671
682
|
var errMsg = (e) => e instanceof Error ? e.message : String(e);
|
|
@@ -1043,7 +1054,6 @@ var render = (input) => {
|
|
|
1043
1054
|
const rounds = input.rounds ?? [];
|
|
1044
1055
|
const sameRootNotes = input.sameRootNotes ?? computeSameRootNotes(rounds.slice(0, -1), input.findings.findings);
|
|
1045
1056
|
const isFullReviewRound = (input.convergenceRound ?? (isConvergenceRound(route, incomplete) && rounds.length > 0)) && isReviewVerdict(input.findings.verdict);
|
|
1046
|
-
const convergenceCounts = rounds[rounds.length - 1] ?? computeRoundCounts(input.findings);
|
|
1047
1057
|
const advisoryAllowed = isFullReviewRound;
|
|
1048
1058
|
return eta.renderString(input.template, {
|
|
1049
1059
|
findings: input.findings,
|
|
@@ -1060,7 +1070,7 @@ var render = (input) => {
|
|
|
1060
1070
|
reviewedSha: input.reviewedSha ?? "0000000000000000000000000000000000000000",
|
|
1061
1071
|
postedAt: input.postedAt ?? "",
|
|
1062
1072
|
severityCounts,
|
|
1063
|
-
convergenceSummary: isFullReviewRound ? convergenceSummary(
|
|
1073
|
+
convergenceSummary: isFullReviewRound ? convergenceSummary(input.findings, input.convergenceThreshold) : "",
|
|
1064
1074
|
strays: (input.strays ?? []).map((f) => sanitizeFinding(f, input.answeredNotes)),
|
|
1065
1075
|
systemic: (input.findings.systemic_problems ?? []).map(sanitizeSystemic),
|
|
1066
1076
|
unanchoredCount: input.unanchoredCount ?? 0,
|
|
@@ -1070,11 +1080,11 @@ var render = (input) => {
|
|
|
1070
1080
|
findingsPointer: input.findingsPointer ?? surfacedFindingsPointer(
|
|
1071
1081
|
input.findings,
|
|
1072
1082
|
// The fallback embeds a signal exactly when the badge renders — never beside a suppressed
|
|
1073
|
-
// badge, and
|
|
1083
|
+
// badge, and scores the same findings the badge does. It assumes a post-style history (the
|
|
1074
1084
|
// caller appends this run's counts last), numbering the round exactly as the trajectory
|
|
1075
1085
|
// label does; post always supplies the marker, so this path cannot disagree with it in
|
|
1076
1086
|
// production (issue #141 review r4).
|
|
1077
|
-
isFullReviewRound && rounds.length > 0 ? signalForRound(rounds.length,
|
|
1087
|
+
isFullReviewRound && rounds.length > 0 ? signalForRound(rounds.length, input.findings, input.convergenceThreshold) : null,
|
|
1078
1088
|
input.jsonUrl
|
|
1079
1089
|
),
|
|
1080
1090
|
roundsMarker: roundsMarker(rounds),
|
|
@@ -1656,6 +1666,14 @@ var findingsTable = [
|
|
|
1656
1666
|
},
|
|
1657
1667
|
{
|
|
1658
1668
|
minor: "0.6",
|
|
1669
|
+
defaultVersion: "0.6.0",
|
|
1670
|
+
schemaFile: "findings.schema.json",
|
|
1671
|
+
codec: FindingsCodec,
|
|
1672
|
+
normalize: identity,
|
|
1673
|
+
latest: false
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
minor: "0.9",
|
|
1659
1677
|
defaultVersion: DEFAULT_SCHEMA_VERSION,
|
|
1660
1678
|
schemaFile: "findings.schema.json",
|
|
1661
1679
|
codec: FindingsCodec,
|
|
@@ -2026,8 +2044,8 @@ var commentPayload = (c) => ({
|
|
|
2026
2044
|
...c.start_line !== void 0 && c.start_side !== void 0 ? { start_line: c.start_line, start_side: c.start_side } : {},
|
|
2027
2045
|
body: formatMarkdown(c.body)
|
|
2028
2046
|
});
|
|
2029
|
-
var postInlineReview = async (repo, prNumber, headSha, comments, inDiff, stickyUrl,
|
|
2030
|
-
const pointer = reviewBodyPointer(headSha, stickyUrl
|
|
2047
|
+
var postInlineReview = async (repo, prNumber, headSha, comments, inDiff, stickyUrl, ghApi) => {
|
|
2048
|
+
const pointer = reviewBodyPointer(headSha, stickyUrl);
|
|
2031
2049
|
const reviewBody = (withComments) => JSON.stringify({
|
|
2032
2050
|
body: pointer,
|
|
2033
2051
|
commit_id: headSha,
|
|
@@ -2472,7 +2490,7 @@ ${dropNote}` : ""}`,
|
|
|
2472
2490
|
roundNumber
|
|
2473
2491
|
)
|
|
2474
2492
|
] : priorRounds;
|
|
2475
|
-
const signal = isRound ? signalForRound(roundNumber,
|
|
2493
|
+
const signal = isRound ? signalForRound(roundNumber, findings, input.convergenceThreshold) : thisIncomplete || !isReviewVerdict(findings.verdict) ? null : priorSignal;
|
|
2476
2494
|
const findingsMarker = findingsMarkerFor(findings, signal);
|
|
2477
2495
|
const markerForm = findingsMarkerForm(findings, input.jsonUrl);
|
|
2478
2496
|
const signalNote = signal !== null || priorSignal !== null ? " (the stop signal still rides the compact marker)" : "";
|
|
@@ -2551,7 +2569,6 @@ ${dropNote}` : ""}`,
|
|
|
2551
2569
|
comments,
|
|
2552
2570
|
inDiff,
|
|
2553
2571
|
stickyRef?.url,
|
|
2554
|
-
findingsMarker,
|
|
2555
2572
|
ghApi
|
|
2556
2573
|
);
|
|
2557
2574
|
process.stderr.write(
|
|
@@ -3664,7 +3681,7 @@ var resolvePrices = (pricesArg) => {
|
|
|
3664
3681
|
return { kind: "absent", path: bundledPath("schema", "prices.example.json") };
|
|
3665
3682
|
};
|
|
3666
3683
|
var TEST_REPORT_DESCRIPTION = 'Path to a JSON test summary: {"passed": number, "failed": number, "total": number, "failures"?: [{"name": string, "message"?: string}]}';
|
|
3667
|
-
var CONVERGENCE_THRESHOLD_DESCRIPTION = "Advisory convergence tolerance: the
|
|
3684
|
+
var CONVERGENCE_THRESHOLD_DESCRIPTION = "Advisory convergence tolerance: the per-finding convergence score (each finding's floor + confidence-weighted headroom; ceilings critical 4 \xB7 major 2 \xB7 minor 1 \xB7 nit 0) at or below which the sticky reads as converged (default: 1)";
|
|
3668
3685
|
var renderCmd = defineCommand({
|
|
3669
3686
|
meta: {
|
|
3670
3687
|
name: "render",
|