@jphutchins/code-review 0.1.0-alpha.39 → 0.1.0-alpha.40
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 +44 -1
- package/dist/index.js +660 -229
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema/VERSIONING.md +21 -1
- package/schema/findings.schema.json +58 -1
- package/templates/comment.eta +32 -1
- package/templates/inline.eta +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jphutchins/code-review",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.40",
|
|
4
4
|
"description": "Deterministic commenter for agentic PR review — gather, render, inline, post, adapt, extract, cost, validate, print-schema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/schema/VERSIONING.md
CHANGED
|
@@ -68,7 +68,27 @@ ignored); a version outside the supported set degrades to a §5.5 sticky notice.
|
|
|
68
68
|
| `v0.2.0` | superseded | Adds required `schema_version`; optional `code`/`code_url` finding fields; normative `suggestion` `""`/`null` semantics; abstract vendor-neutral envelope (see SPEC §6.1). |
|
|
69
69
|
| `v0.3.0` | superseded | Adds optional `reasoning` finding field. |
|
|
70
70
|
| `v0.4.0` | superseded | Breaking: renames finding `body` → `description`; makes `reasoning` and `confidence` **required**; adds optional `recommendation` (prose fix); removes the free-text `suggestion` field (a `patch`, now `string \| null`, is the sole mechanical fix, projected into a suggestion by the commenter). |
|
|
71
|
-
| `v0.5.0` |
|
|
71
|
+
| `v0.5.0` | superseded | Widens the `verdict` enum with a pipeline-reserved `error` value: a run that produced no verdict about the diff (operational failure or security refusal) now carries `verdict: "error"` with `findings: []`, so its machine-readable blob is no longer byte-identical to a clean pass. Backwards-compatible (a `0.4` document is a valid `0.5` document); the CLI keeps resolving `0.4` via an identity upcast, so a sticky embedded by a `0.4` CLI still seeds a re-review. |
|
|
72
|
+
| `v0.6.0` | **current** | Adds optional `systemic_problems` — an array of cross-cutting observations that tie findings together and are hard to express with a line range, each item with required `title`/`description`/`severity`/`reasoning`/`confidence` and optional `code`/`code_url`/`finding_codes`/`paths` (no line anchors). Refocuses `summary` on justifying the overall verdict rather than restating findings. Backwards-compatible (a `0.5` document is a valid `0.6` document); the CLI keeps resolving `0.4`/`0.5` via identity upcasts, so stickies embedded by earlier CLIs still seed a re-review. |
|
|
73
|
+
|
|
74
|
+
### Surfaced findings document
|
|
75
|
+
|
|
76
|
+
The commenter does not embed the agent's raw findings document in review comments — it embeds a
|
|
77
|
+
**surfaced** copy (`<!-- code-review:findings-json -->`): the same fields, stamped with a surface
|
|
78
|
+
version and the pipeline-computed `convergence` (`{score, threshold, converged}` — a literal
|
|
79
|
+
boolean, so a decoding agent cannot re-derive the weights) and `round` (the count of completed
|
|
80
|
+
full-review rounds) of the last completed full-review round. The agent never writes these fields
|
|
81
|
+
(it cannot know the score — the weights and threshold are commenter-side), so the findings schema
|
|
82
|
+
above describes only the agent-written document; the surfaced document has its own version axis:
|
|
83
|
+
|
|
84
|
+
| Version | Status | Notes |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `v0.7.0` | **current** | The surfaced document carries `convergence` + `round` — the deterministic stop signal an iterating author-agent decodes instead of the prose (issue #141). Both are omitted until at least one full-review round has completed, and both survive the in-progress banner (carried forward verbatim with the marker). `stripSurfaceFields` drops them when a surfaced blob feeds back into the agent channel (the re-review seed), restoring the draft version. This surface axis is deliberately **distinct** from the draft axis (now `v0.6.0` after issue #134) so a surfaced doc is never mistaken for an agent-written draft. |
|
|
87
|
+
|
|
88
|
+
The surfaced axis is independent of the draft-version registry: `v0.7.0` is the surface shape's
|
|
89
|
+
marker contract, while the agent-written document above remains at `v0.6.0`. `stripSurfaceFields`
|
|
90
|
+
and `parseSurfaceSignal` are version-gated on the surface axis, so a future draft bump can never be
|
|
91
|
+
mistaken for a surfaced document.
|
|
72
92
|
|
|
73
93
|
### Price-map schema
|
|
74
94
|
|
|
@@ -14,13 +14,70 @@
|
|
|
14
14
|
},
|
|
15
15
|
"summary": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"description": "1-4 sentence markdown
|
|
17
|
+
"description": "1-4 sentence markdown justification of the overall verdict — the shape of the review and why the verdict follows from it. Do not restate individual findings (those live in `findings`) or cross-cutting observations (those live in `systemic_problems`); the summary explains the verdict, the arrays carry the evidence."
|
|
18
18
|
},
|
|
19
19
|
"verdict": {
|
|
20
20
|
"type": "string",
|
|
21
21
|
"enum": ["approve", "comment", "changes", "error"],
|
|
22
22
|
"description": "Overall stance. The review agent emits one of `approve`/`comment`/`changes`. `error` is reserved for the pipeline: it marks a run that produced no verdict about the diff (an operational failure, a security refusal, or an empty diff with nothing to review — always with `findings: []`), so a consumer switching on `verdict` gets a case it does not recognise rather than a false clean pass."
|
|
23
23
|
},
|
|
24
|
+
"systemic_problems": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"description": "Zero or more cross-cutting observations that tie several findings together or describe a pattern spanning the change, and are hard to express with a single line range. Omit the field when there are none; the summary must not restate them.",
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"required": ["title", "description", "severity", "reasoning", "confidence"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"title": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "One-line summary of the systemic problem."
|
|
35
|
+
},
|
|
36
|
+
"description": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "1-3 sentence markdown explanation of the pattern and why it matters — how it ties the related findings together. Details belong in the individual findings' `reasoning`."
|
|
39
|
+
},
|
|
40
|
+
"severity": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["critical", "major", "minor", "nit"],
|
|
43
|
+
"description": "Overall severity of the systemic problem — the severity of the pattern itself, not of any single finding. It does not count toward the findings severity histogram."
|
|
44
|
+
},
|
|
45
|
+
"reasoning": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Rationale/evidence for why the systemic problem holds, so a human or downstream agent can judge its soundness — distinct from `description`, which explains the pattern to the reader. Mirrors the finding field of the same name."
|
|
48
|
+
},
|
|
49
|
+
"confidence": {
|
|
50
|
+
"type": "number",
|
|
51
|
+
"minimum": 0,
|
|
52
|
+
"maximum": 1,
|
|
53
|
+
"description": "0..1 confidence in the systemic problem itself. Mirrors the finding field of the same name."
|
|
54
|
+
},
|
|
55
|
+
"code": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Stable rule identifier (e.g. \"repeated-null-check\") for rule-based filtering, suppression, and cross-run dedup — mirrors the finding field of the same name."
|
|
58
|
+
},
|
|
59
|
+
"code_url": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"format": "uri",
|
|
62
|
+
"description": "URL documenting the rule named by code."
|
|
63
|
+
},
|
|
64
|
+
"finding_codes": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"description": "The `code` values of the findings this systemic problem ties together, when those findings carry codes."
|
|
70
|
+
},
|
|
71
|
+
"paths": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"items": {
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"description": "Repo-relative file paths the systemic problem spans."
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
24
81
|
"findings": {
|
|
25
82
|
"type": "array",
|
|
26
83
|
"description": "Zero or more specific findings.",
|
package/templates/comment.eta
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<!-- code-review -->
|
|
2
2
|
<!-- reviewed-sha: <%= it.reviewedSha %> -->
|
|
3
|
+
<% if (!it.incomplete && it.route) { -%>
|
|
4
|
+
<!-- reviewed-route: <%= it.route %> -->
|
|
5
|
+
<% } -%>
|
|
3
6
|
<% if (!it.incomplete) { -%>
|
|
4
7
|
<!-- review-complete -->
|
|
5
8
|
<% } -%>
|
|
@@ -20,17 +23,41 @@
|
|
|
20
23
|
<% if (it.convergenceSummary) { -%>
|
|
21
24
|
<sub><%~ it.convergenceSummary %></sub>
|
|
22
25
|
<% } -%>
|
|
26
|
+
<% if (it.metastasisNote) { -%>
|
|
27
|
+
<%~ it.metastasisNote %>
|
|
28
|
+
<% } -%>
|
|
23
29
|
<% } -%>
|
|
24
30
|
|
|
25
31
|
<%~ it.findings.summary %>
|
|
26
32
|
|
|
33
|
+
<% if (!it.incomplete && it.systemic.length > 0) { -%>
|
|
34
|
+
|
|
35
|
+
### 🔗 Systemic problems
|
|
36
|
+
|
|
37
|
+
<% it.systemic.forEach(function(s) { -%>
|
|
38
|
+
#### <%= it.severityEmoji(s.severity) %> (<%= s.severity %>) <%= s.title %> · confidence <%= it.formatConfidence(s.confidence) %>
|
|
39
|
+
<% const meta = [] %><% if (s.paths && s.paths.length > 0) { meta.push('Affects: ' + s.paths.map(function(p) { return '`' + p + '`' }).join(', ')) } %><% if (s.finding_codes && s.finding_codes.length > 0) { meta.push('Ties together: ' + s.finding_codes.map(function(c) { return '`' + c + '`' }).join(', ')) } %><% if (meta.length > 0) { %>_<%~ meta.join(' · ') %>_<% } %>
|
|
40
|
+
|
|
41
|
+
<%~ s.description %>
|
|
42
|
+
|
|
43
|
+
> [!TIP]
|
|
44
|
+
> <details><summary>Reasoning</summary>
|
|
45
|
+
>
|
|
46
|
+
<% s.reasoning.split("\n").forEach(function(rl) { -%>
|
|
47
|
+
> <%= rl %>
|
|
48
|
+
<% }) -%>
|
|
49
|
+
>
|
|
50
|
+
> </details>
|
|
51
|
+
<% }) -%>
|
|
52
|
+
<% } -%>
|
|
53
|
+
|
|
27
54
|
---
|
|
28
55
|
|
|
29
56
|
<% const c = it.severityCounts %><% const findingsTotal = c.critical + c.major + c.minor + c.nit %>
|
|
30
57
|
<% if (findingsTotal > 0) { %>
|
|
31
58
|
<% const parts = [] %><% if (c.critical) { parts.push(it.severityEmoji('critical') + ' ' + c.critical) } %><% if (c.major) { parts.push(it.severityEmoji('major') + ' ' + c.major) } %><% if (c.minor) { parts.push(it.severityEmoji('minor') + ' ' + c.minor) } %><% if (c.nit) { parts.push(it.severityEmoji('nit') + ' ' + c.nit) } %>
|
|
32
59
|
**Findings:** <%~ parts.join(' · ') %>
|
|
33
|
-
<% } else if (!it.incomplete) { %>
|
|
60
|
+
<% } else if (!it.incomplete && it.systemic.length === 0) { %>
|
|
34
61
|
_No findings — clean review._
|
|
35
62
|
<% } %>
|
|
36
63
|
|
|
@@ -52,6 +79,10 @@ _<%= it.unanchoredCount %> finding<%= it.unanchoredCount === 1 ? '' : 's' %> bel
|
|
|
52
79
|
#### <%= it.severityEmoji(f.severity) %> (<%= f.severity %>) `<%= f.path %>:<%= f.start_line %><% if (f.start_line !== f.end_line) { %>–<%= f.end_line %><% } %>` — <%= f.title %> · confidence <%= it.formatConfidence(f.confidence) %>
|
|
53
80
|
|
|
54
81
|
<%~ f.description %>
|
|
82
|
+
<% if (f.code && it.sameRootNotes && Object.prototype.hasOwnProperty.call(it.sameRootNotes, f.code)) { -%>
|
|
83
|
+
|
|
84
|
+
> ⚠️ <%~ it.sameRootNotes[f.code] %>
|
|
85
|
+
<% } -%>
|
|
55
86
|
<% if (f.recommendation) { -%>
|
|
56
87
|
|
|
57
88
|
**Recommended fix:** <%~ f.recommendation %>
|
package/templates/inline.eta
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
<%= it.severityEmoji(it.severity) %> <%= it.severity.charAt(0).toUpperCase() + it.severity.slice(1) %>: **<%= it.title %>** · <%= it.formatConfidence(it.confidence) %> confidence
|
|
5
5
|
|
|
6
6
|
<%~ it.description %>
|
|
7
|
+
<% if (it.sameRootNote) { -%>
|
|
8
|
+
|
|
9
|
+
> ⚠️ <%~ it.sameRootNote %>
|
|
10
|
+
<% } -%>
|
|
7
11
|
<% if (it.recommendation) { -%>
|
|
8
12
|
|
|
9
13
|
**Recommended fix:** <%~ it.recommendation %>
|