@jphutchins/code-review 0.1.0-alpha.28 → 0.1.0-alpha.29

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/dist/index.js CHANGED
@@ -186,6 +186,8 @@ var parseReviewedSha = (body) => {
186
186
  const sha = /<!-- reviewed-sha: ([0-9a-fA-F]{40}) -->/.exec(body)?.[1]?.toLowerCase();
187
187
  return sha && sha !== ZERO_SHA ? sha : null;
188
188
  };
189
+ var REVIEW_COMPLETE_MARKER = "<!-- review-complete -->";
190
+ var parseReviewComplete = (body) => body.includes(REVIEW_COMPLETE_MARKER);
189
191
  var parseFindingsMarker = (body) => {
190
192
  const match = /<!-- code-review:findings-json;base64 ([A-Za-z0-9+/=]+) -->/.exec(body);
191
193
  const b64 = match?.[1];
@@ -240,6 +242,8 @@ var computeSeverityCounts = (findings) => findings.reduce(
240
242
  var render = (input) => {
241
243
  const eta = new Eta({ autoTrim: false });
242
244
  const usageAvailable = input.envelope !== null;
245
+ const hasUsage = input.envelope !== null && input.envelope.models.length > 0;
246
+ const incomplete = input.incomplete ?? input.envelope?.incomplete ?? false;
243
247
  const costReport = input.envelope ? computeCost(input.envelope.models, input.prices) : null;
244
248
  const pricesProvided = input.pricesProvided ?? true;
245
249
  const route = input.route ?? input.envelope?.route ?? null;
@@ -249,6 +253,8 @@ var render = (input) => {
249
253
  findings: input.findings,
250
254
  envelope: input.envelope,
251
255
  usageAvailable,
256
+ hasUsage,
257
+ incomplete,
252
258
  costReport,
253
259
  pricesProvided,
254
260
  route,
@@ -602,7 +608,12 @@ var ResultEnvelopeCodec = t.intersection([
602
608
  t.partial({
603
609
  vendor_cost_usd: t.union([t.number, t.null]),
604
610
  route: t.string,
605
- effort: t.string
611
+ effort: t.string,
612
+ // The run produced a notice rather than a completed review (security-gate block, agent kill, no
613
+ // recoverable findings). An empty `findings` array alone can't say this — a genuine clean review
614
+ // is also empty — so the render suppresses "clean review" and the sticky precedence guard refuses
615
+ // to bury a completed review under it. Absent ⇒ a completed review.
616
+ incomplete: t.boolean
606
617
  })
607
618
  ]);
608
619
  var ModelPricesCodec = t.type({
@@ -959,6 +970,35 @@ var formatMarkdown = (md) => {
959
970
  };
960
971
  var pad2 = (n) => String(n).padStart(2, "0");
961
972
  var formatUtc = (d) => `${String(d.getUTCFullYear())}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())} UTC`;
973
+
974
+ // src/notice.ts
975
+ var isNoticeKind = (s) => s === "security-blocked" || s === "setup-failed" || s === "diff-apply-failed" || s === "no-output";
976
+ var blockquote = (text) => text.replaceAll("\n", "\n> ");
977
+ var noticeSummary = (kind, reasons) => {
978
+ switch (kind) {
979
+ case "security-blocked":
980
+ return reasons !== void 0 && reasons.trim() !== "" ? `### \u{1F6D1} Code review skipped by the security gate
981
+
982
+ The diff was flagged as unsafe to apply and execute:
983
+
984
+ > ${blockquote(reasons)}` : "### \u{1F6D1} Code review skipped by the security gate\n\nThe security triage returned an unsafe verdict without a reason. See workflow logs.";
985
+ case "setup-failed":
986
+ return "### \u{1F6E0}\uFE0F Review did not run\n\nThe review job failed before the security triage could run (e.g. dependency install or environment setup). See the workflow logs \u2014 this is an infrastructure failure, not a security verdict.";
987
+ case "diff-apply-failed":
988
+ return "### \u26A0\uFE0F Could not apply the diff\n\nThe PR diff could not be applied to the checked-out base commit, so the review was skipped rather than run against an unmodified tree. See workflow logs.";
989
+ case "no-output":
990
+ return "### \u26A0\uFE0F Review did not complete\n\nThe diff passed triage but the review produced no output. See workflow logs.";
991
+ }
992
+ };
993
+ var buildNoticeEnvelope = (kind, reasons) => ({
994
+ schema_version: DEFAULT_SCHEMA_VERSION,
995
+ findings: noticeFindings(noticeSummary(kind, reasons)),
996
+ models: [],
997
+ turns: 0,
998
+ duration_ms: 0,
999
+ vendor_cost_usd: null,
1000
+ incomplete: true
1001
+ });
962
1002
  var identity = (decoded) => decoded;
963
1003
  var findingsTable = [
964
1004
  {
@@ -1417,6 +1457,15 @@ var post = async (input, ghApi = runGhApi) => {
1417
1457
  DEFAULT_MARKER,
1418
1458
  ghApi
1419
1459
  );
1460
+ const existingComplete = existingSticky !== null && parseReviewComplete(existingSticky.body);
1461
+ const wouldBuryCompleted = (incomplete) => incomplete && existingComplete;
1462
+ const leaveInPlace = () => {
1463
+ process.stderr.write(
1464
+ `Review did not complete and the sticky already reflects a completed review \u2014 leaving it in place
1465
+ `
1466
+ );
1467
+ process.exit(0);
1468
+ };
1420
1469
  const prices = JSON.parse(readFileSync(input.pricesPath, "utf-8"));
1421
1470
  const decodedPrices = PriceMapCodec.decode(prices);
1422
1471
  if (decodedPrices._tag === "Left") {
@@ -1428,6 +1477,7 @@ var post = async (input, ghApi = runGhApi) => {
1428
1477
  render({
1429
1478
  findings: noticeFindings(`### \u26A0\uFE0F ${message}`),
1430
1479
  envelope: null,
1480
+ incomplete: true,
1431
1481
  prices: decodedPrices.right,
1432
1482
  pricesProvided: input.pricesProvided,
1433
1483
  template,
@@ -1440,6 +1490,7 @@ var post = async (input, ghApi = runGhApi) => {
1440
1490
  })
1441
1491
  );
1442
1492
  if (isEmptyDiff(diff)) {
1493
+ if (wouldBuryCompleted(true)) leaveInPlace();
1443
1494
  await upsertSticky(
1444
1495
  input.repo,
1445
1496
  prNumber,
@@ -1451,6 +1502,7 @@ var post = async (input, ghApi = runGhApi) => {
1451
1502
  }
1452
1503
  const findingsResult = loadFindings(input.findingsPath);
1453
1504
  if (findingsResult.kind !== "ok") {
1505
+ if (wouldBuryCompleted(true)) leaveInPlace();
1454
1506
  await upsertSticky(
1455
1507
  input.repo,
1456
1508
  prNumber,
@@ -1487,6 +1539,8 @@ var post = async (input, ghApi = runGhApi) => {
1487
1539
  );
1488
1540
  process.exit(0);
1489
1541
  }
1542
+ const thisIncomplete = envelope.incomplete === true;
1543
+ if (wouldBuryCompleted(thisIncomplete)) leaveInPlace();
1490
1544
  const findingsMarker = findingsPointer(findings, input.jsonUrl);
1491
1545
  const {
1492
1546
  comments: rawComments,
@@ -1510,6 +1564,7 @@ var post = async (input, ghApi = runGhApi) => {
1510
1564
  const commonRenderInput = {
1511
1565
  findings,
1512
1566
  envelope,
1567
+ incomplete: thisIncomplete,
1513
1568
  prices: decodedPrices.right,
1514
1569
  pricesProvided: input.pricesProvided,
1515
1570
  template,
@@ -1624,7 +1679,7 @@ var announce = async (input, ghApi = runGhApi) => {
1624
1679
  DEFAULT_MARKER,
1625
1680
  ghApi
1626
1681
  );
1627
- if (existing !== null && parseReviewedSha(existing.body) === input.headSha.toLowerCase()) {
1682
+ if (existing !== null && parseReviewComplete(existing.body) && parseReviewedSha(existing.body) === input.headSha.toLowerCase()) {
1628
1683
  process.stderr.write(
1629
1684
  `Sticky already reflects a completed review of ${input.headSha} \u2014 leaving it in place
1630
1685
  `
@@ -2365,6 +2420,7 @@ var buildEnvelope = (telemetry, native, agentFilePath, agentFileFallbackPath) =>
2365
2420
  findings: noticeFindings(`### \u26A0\uFE0F Review did not complete
2366
2421
 
2367
2422
  ${outcome.reason}`),
2423
+ incomplete: true,
2368
2424
  ...telemetry
2369
2425
  };
2370
2426
  }
@@ -3125,6 +3181,30 @@ var adaptCmd = defineCommand({
3125
3181
  `);
3126
3182
  }
3127
3183
  });
3184
+ var noticeCmd = defineCommand({
3185
+ meta: {
3186
+ name: "notice",
3187
+ description: "Emit an abstract envelope for a run that produced no completed review (security block, setup failure, unapplied diff, or empty run) \u2014 flagged incomplete so the commenter renders it honestly and won't bury a real review"
3188
+ },
3189
+ args: {
3190
+ kind: {
3191
+ type: "positional",
3192
+ description: "One of: security-blocked, setup-failed, diff-apply-failed, no-output",
3193
+ required: true
3194
+ },
3195
+ reasons: {
3196
+ type: "string",
3197
+ description: "security-blocked only: the triage's fail-closed reason string (empty/omitted \u21D2 the no-reason wording)"
3198
+ }
3199
+ },
3200
+ run: ({ args }) => {
3201
+ const kind = isNoticeKind(args.kind) ? args.kind : fail(
3202
+ `Unknown notice kind "${args.kind}" \u2014 expected one of: security-blocked, setup-failed, diff-apply-failed, no-output`
3203
+ );
3204
+ process.stdout.write(`${JSON.stringify(buildNoticeEnvelope(kind, args.reasons), null, 2)}
3205
+ `);
3206
+ }
3207
+ });
3128
3208
  var isExtractSchemaKind = (s) => s === "findings" || s === "triage";
3129
3209
  var requireExtractSchemaKind = (name) => isExtractSchemaKind(name) ? name : fail(`Unknown kind "${name}" for extract \u2014 expected one of: findings, triage`);
3130
3210
  var failClosedTriage = (outcome) => ({
@@ -3728,6 +3808,7 @@ var main = defineCommand({
3728
3808
  validate: validateCmd,
3729
3809
  "seed-draft": seedDraftCmd,
3730
3810
  adapt: adaptCmd,
3811
+ notice: noticeCmd,
3731
3812
  extract: extractCmd,
3732
3813
  "validate-patches": validatePatchesCmd,
3733
3814
  "print-schema": printSchemaCmd,