@webpieces/pr-gate 0.4.719 โ†’ 0.4.721

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.
Files changed (36) hide show
  1. package/package.json +4 -6
  2. package/src/dashboard/checklist-comment-renderer.js +2 -4
  3. package/src/dashboard/checklist-comment-renderer.js.map +1 -1
  4. package/src/dashboard/dashboard.js +0 -2
  5. package/src/dashboard/dashboard.js.map +1 -1
  6. package/src/scripts/commands/finish-upsert-pr-command.js +9 -10
  7. package/src/scripts/commands/finish-upsert-pr-command.js.map +1 -1
  8. package/src/scripts/commands/review-upsert-pr-command.d.ts +2 -2
  9. package/src/scripts/commands/review-upsert-pr-command.js +5 -5
  10. package/src/scripts/commands/review-upsert-pr-command.js.map +1 -1
  11. package/src/scripts/pr-gate-app.d.ts +2 -13
  12. package/src/scripts/pr-gate-app.js +1 -20
  13. package/src/scripts/pr-gate-app.js.map +1 -1
  14. package/src/scripts/workflow/checklist-scanner.d.ts +21 -24
  15. package/src/scripts/workflow/checklist-scanner.js +35 -40
  16. package/src/scripts/workflow/checklist-scanner.js.map +1 -1
  17. package/src/scripts/workflow/review-report.d.ts +3 -3
  18. package/src/scripts/workflow/review-report.js +3 -3
  19. package/src/scripts/workflow/review-report.js.map +1 -1
  20. package/src/scripts/workflow/reviewer-verdict-gate.js +2 -2
  21. package/src/scripts/workflow/reviewer-verdict-gate.js.map +1 -1
  22. package/src/scripts/commands/authorize-command.d.ts +0 -82
  23. package/src/scripts/commands/authorize-command.js +0 -205
  24. package/src/scripts/commands/authorize-command.js.map +0 -1
  25. package/src/scripts/commands/check-auth-command.d.ts +0 -33
  26. package/src/scripts/commands/check-auth-command.js +0 -99
  27. package/src/scripts/commands/check-auth-command.js.map +0 -1
  28. package/src/scripts/workflow/authorization-context-resolver.d.ts +0 -63
  29. package/src/scripts/workflow/authorization-context-resolver.js +0 -107
  30. package/src/scripts/workflow/authorization-context-resolver.js.map +0 -1
  31. package/src/scripts/wp-authorize.d.ts +0 -2
  32. package/src/scripts/wp-authorize.js +0 -22
  33. package/src/scripts/wp-authorize.js.map +0 -1
  34. package/src/scripts/wp-check-auth.d.ts +0 -2
  35. package/src/scripts/wp-check-auth.js +0 -16
  36. package/src/scripts/wp-check-auth.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/pr-gate",
3
- "version": "0.4.719",
3
+ "version": "0.4.721",
4
4
  "description": "Gated PR system: 3-point squash-merge, merge validation gate, and red/yellow/green PR dashboard. Standalone scripts, no Nx dependency required.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -16,8 +16,8 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@inversifyjs/binding-decorators": "1.1.5",
19
- "@webpieces/ai-hook-rules": "0.4.719",
20
- "@webpieces/rules-config": "0.4.719",
19
+ "@webpieces/ai-hook-rules": "0.4.721",
20
+ "@webpieces/rules-config": "0.4.721",
21
21
  "inversify": "7.10.4",
22
22
  "reflect-metadata": "0.2.2"
23
23
  },
@@ -37,8 +37,6 @@
37
37
  "wp-check-pr": "./src/scripts/wp-check-pr.js",
38
38
  "wp-review-upsert-pr": "./src/scripts/wp-review-upsert-pr.js",
39
39
  "wp-push-dev": "./src/scripts/wp-push-dev.js",
40
- "wp-finish-push-dev": "./src/scripts/wp-finish-push-dev.js",
41
- "wp-authorize": "./src/scripts/wp-authorize.js",
42
- "wp-check-auth": "./src/scripts/wp-check-auth.js"
40
+ "wp-finish-push-dev": "./src/scripts/wp-finish-push-dev.js"
43
41
  }
44
42
  }
@@ -166,7 +166,7 @@ let ChecklistCommentRenderer = class ChecklistCommentRenderer {
166
166
  return '๐ŸŸก';
167
167
  if (row.status === rules_config_1.CK_OVERRIDDEN)
168
168
  return '๐ŸŸ ';
169
- if (row.status === rules_config_1.CK_FAIL || row.status === rules_config_1.CK_UNAUTHORIZED)
169
+ if (row.status === rules_config_1.CK_FAIL)
170
170
  return '๐Ÿ”ด';
171
171
  return 'โšช';
172
172
  }
@@ -185,9 +185,7 @@ let ChecklistCommentRenderer = class ChecklistCommentRenderer {
185
185
  if (row.status === rules_config_1.CK_WARN)
186
186
  return 'passed with concerns';
187
187
  if (row.status === rules_config_1.CK_OVERRIDDEN)
188
- return 'OVERRIDDEN โ€” a human authorized shipping it (pnpm wp-authorize)';
189
- if (row.status === rules_config_1.CK_UNAUTHORIZED)
190
- return 'REFUSED โ€” override claimed, but no human authorized it';
188
+ return 'OVERRIDDEN โ€” shipped with a stated justification';
191
189
  if (row.status === rules_config_1.CK_FAIL)
192
190
  return 'FAILED review';
193
191
  if (row.status === rules_config_1.CK_MISSING)
@@ -1 +1 @@
1
- {"version":3,"file":"checklist-comment-renderer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/checklist-comment-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n formatFileList, CK_PASS, CK_WARN, CK_OVERRIDDEN, CK_FAIL, CK_MISSING, CK_UNAUTHORIZED,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { ChecklistCommentRow } from './checklist-comment-row';\n\n/**\n * Hidden marker on the checklist review COMMENT โ€” the \"2nd comment\" the PR description points at โ€” so\n * wp-finish can find and PATCH its own comment on every push instead of appending a new one. Versioned so\n * the format can evolve without matching an old shape. v2 = the full roster (every DEFINED checklist,\n * matched or not) + tri-state verdicts.\n */\nexport const CHECKLIST_COMMENT_MARKER = '<!-- webpieces-checklists v2 -->';\n\nconst COMMENT_LIMIT = 65000; // under GitHub's 65536-char cap, with headroom for the marker + roll-up.\n\n// One checklist section for the combined comment (heading + verbatim reviewer output), so oversize\n// truncation can shrink the longest BODY without ever dropping a verdict heading.\nclass CommentSection {\n heading: string;\n body: string;\n\n constructor(heading: string, body: string) {\n this.heading = heading;\n this.body = body;\n }\n}\n\n/**\n * Renders the 2nd PR comment: the reviewer checklist โ€” full roster plus each reviewer's verbatim output.\n *\n * Split out of `Dashboard` when the PR-description/commit-body swap pushed that file over the 700-line\n * cap, and the seam was already there to be found: this is ONE of the three surfaces the gated flow\n * writes, it shares no rendering helper with the other two (it speaks `ChecklistCommentRow`, where the\n * dashboard speaks `ChecklistRow`), and it owns the only size-fitting logic in the package. One class per\n * surface is the shape the rest of this change assumes โ€” see `Dashboard.renderPrBody` and\n * `Dashboard.renderDetailComment`.\n *\n * `@injectable(bindingScopeValues.Singleton)` so it is drawn in the DI design and injected by type.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class ChecklistCommentRenderer {\n /**\n * The ONE combined PR comment. Two halves, in this order:\n *\n * 1. A roll-up plus the FULL ROSTER โ€” every DEFINED checklist as a checkbox, each with a sub-bullet\n * stating exactly which globs fired against which files, or which did not and out of how many.\n * Skipped checklists are listed on purpose: skipping is the normal, healthy outcome, and a comment\n * that names only the reviewers that fired cannot distinguish \"evaluated and irrelevant\" from\n * \"never wired up\" โ€” nor answer \"why did the DB reviewer run on my frontend PR?\".\n * 2. One section per reviewer that RAN, carrying its full `output` โ€” the depth a verdict line throws\n * away. Overridden first, then warned, then passed: a reader should meet the exceptions first.\n *\n * Idempotent: keyed by the hidden marker so wp-finish PATCHes this same comment on every push.\n */\n render(\n rows: readonly ChecklistCommentRow[],\n provenanceVerified: boolean,\n baseResolved: boolean,\n ): string {\n const ran = this.ranOrdered(rows);\n const prov = provenanceVerified\n ? '_Each reviewer ran as its own independent subagent, verified from the Claude Code harness._'\n : '_โš ๏ธ Reviewer provenance was NOT verified (no Claude Code session) โ€” treat these as unverified._';\n // The roster lives in the HEADER, never in a section: fitComment only ever shrinks section bodies,\n // so a roster line can never be the thing an oversize comment silently drops.\n const lines: string[] = [CHECKLIST_COMMENT_MARKER, this.rollupHeader(rows, baseResolved)];\n // No reviewer ran โ‡’ no provenance claim to make. Printing one either way would attest to nothing.\n if (ran.length > 0) lines.push(prov);\n lines.push('', `### Checklists (all ${rows.length})`);\n for (const row of rows) lines.push(this.rosterBullet(row));\n const header = lines.join('\\n');\n if (ran.length === 0) {\n return `${header}\\n\\n${this.nothingRanNote(rows)}`;\n }\n return this.fitComment(\n `${header}\\n\\n### Reviews that ran`,\n ran.map((r: ChecklistCommentRow): CommentSection => this.commentSection(r)),\n );\n }\n\n /**\n * The closing note when no reviewer produced a verdict. The original wording โ€” \"every configured\n * checklist was evaluated and none of them applied\" โ€” is an all-clear, and it becomes FALSE the moment a\n * checklist did apply and was declined. That sentence under a PR nobody reviewed is precisely the\n * misreport this feature could otherwise introduce, so the declined case gets its own words.\n */\n private nothingRanNote(rows: readonly ChecklistCommentRow[]): string {\n const declined = rows.filter((r: ChecklistCommentRow): boolean => this.declined(r));\n if (declined.length === 0) {\n return '_No reviewer had to run on this diff โ€” every configured checklist was evaluated and none of them applied._';\n }\n return (\n `_No reviewer ran. ${declined.length} OPTIONAL checklist(s) DID apply to this diff and were not ` +\n `run; the rest were evaluated and did not apply._`\n );\n }\n\n // The roll-up line. `baseResolved:false` replaces it entirely: with no fork point the changed-file set is\n // EMPTY, so nothing matched โ€” including patternless ALWAYS-RUNS checklists โ€” and reporting that as\n // \"all skipped โœ…\" would post a green all-clear for a PR where nothing was actually evaluated.\n private rollupHeader(rows: readonly ChecklistCommentRow[], baseResolved: boolean): string {\n if (!baseResolved) {\n return (\n `## ๐Ÿ” Company review checklists โ€” โš ๏ธ NOT EVALUATED (${rows.length} defined)\\n` +\n `_No diff base (fork point of main) could be resolved, so no checklist was matched against ` +\n `anything. This is **not** an all-clear._`\n );\n }\n const ran = rows.filter((r: ChecklistCommentRow): boolean => this.reviewerRan(r));\n const declined = rows.filter((r: ChecklistCommentRow): boolean => this.declined(r));\n const skipped = rows.length - ran.length - declined.length;\n const parts: string[] = [];\n for (const pair of this.rollupCounts(ran)) parts.push(pair);\n const breakdown = parts.length > 0 ? ` (${parts.join(' ยท ')})` : '';\n const skip = skipped > 0 ? ` ยท ${skipped} skipped โœ…` : '';\n // Counted SEPARATELY from \"skipped\", and without a โœ…. A declined optional review is a legitimate\n // outcome, but it is not the same good news as a checklist that had nothing to look at โ€” folding the\n // two together would let a PR that declined every optional review read as fully covered.\n const notRun = declined.length > 0 ? ` ยท ${declined.length} optional not run` : '';\n return `## ๐Ÿ” Company review checklists โ€” ${rows.length} defined ยท ${ran.length} ran${breakdown}${skip}${notRun}`;\n }\n\n // `๐ŸŸข 2 ยท ๐ŸŸก 1` โ€” only the non-zero buckets, so a clean run reads as one number rather than four.\n private rollupCounts(ran: readonly ChecklistCommentRow[]): string[] {\n const counts: string[] = [];\n const emojiFor: string[] = ['๐ŸŸข', '๐ŸŸก', '๐ŸŸ '];\n const statusFor: string[] = [CK_PASS, CK_WARN, CK_OVERRIDDEN];\n statusFor.forEach((status: string, i: number): void => {\n const n = ran.filter((r: ChecklistCommentRow): boolean => r.status === status).length;\n if (n > 0) counts.push(`${emojiFor[i]} ${n}`);\n });\n return counts;\n }\n\n // One roster line + its why sub-bullet. A checked box means a reviewer ran; an unchecked one means the\n // checklist was evaluated and did not apply, which the words state as the good news it is.\n private rosterBullet(row: ChecklistCommentRow): string {\n const box = this.reviewerRan(row) ? '- [x]' : '- [ ]';\n return (\n `${box} ${this.verdictEmoji(row)} **${row.subagent}**${this.optionalTag(row)} โ€” ` +\n `${this.verdictWords(row)}${this.evidenceSuffix(row)}\\n` +\n ` - ${this.whyLine(row)}`\n );\n }\n\n /**\n * Did a reviewer actually produce a verdict for this row?\n *\n * `row.ran` is really \"this checklist APPLIED to the diff\" โ€” for a required checklist the two are the\n * same thing, because the PR cannot open otherwise, and the field was named before optional checklists\n * existed. For a DECLINED optional one they diverge, and using `ran` alone would put a checked box and a\n * reviewer section on a review that nobody performed.\n */\n private reviewerRan(row: ChecklistCommentRow): boolean {\n return row.ran && !this.declined(row);\n }\n\n // Applied, optional, and carrying no verdict โ€” i.e. the human was offered this review and said no (or\n // `--no-optional` skipped the offer). Never true of a required checklist: one of those with no verdict\n // does not reach a PR at all.\n private declined(row: ChecklistCommentRow): boolean {\n return row.ran && !row.required && (row.status === CK_MISSING || row.status === '');\n }\n\n // Marks which rows the human could have declined. Without it a reader cannot tell a review that was\n // skippable from one that simply passed, and so cannot judge how much this PR was actually reviewed.\n private optionalTag(row: ChecklistCommentRow): string {\n return row.required ? '' : ' _(optional)_';\n }\n\n /**\n * Whether the reviewer demonstrably opened the diff, read from its own transcript. A QUALITY signal and\n * never a blocker (see SubagentProvenanceService.evidenceFor) โ€” but published, because \"wrote a verdict\n * without reading the change\" is exactly what a reader of this comment would want to weigh.\n */\n private evidenceSuffix(row: ChecklistCommentRow): string {\n if (!this.reviewerRan(row) || row.diffRead === '') return '';\n return row.diffRead === 'yes' ? ' _(diff read โœ“)_' : ' _(โš ๏ธ no diff read recorded)_';\n }\n\n private verdictEmoji(row: ChecklistCommentRow): string {\n if (!this.reviewerRan(row)) return 'โšช';\n if (row.status === CK_PASS) return '๐ŸŸข';\n if (row.status === CK_WARN) return '๐ŸŸก';\n if (row.status === CK_OVERRIDDEN) return '๐ŸŸ ';\n if (row.status === CK_FAIL || row.status === CK_UNAUTHORIZED) return '๐Ÿ”ด';\n return 'โšช';\n }\n\n // SHORT words for a roster line / section heading. Short on purpose: the reviewer's own output and any\n // override justification get their own section below, and a roster exists to be scanned.\n private verdictWords(row: ChecklistCommentRow): string {\n // Two different unchecked boxes, two different sentences. \"Not applicable\" is the diff's doing;\n // \"not run\" is a person's, and reporting the second as the first would quietly credit a review that\n // a human deliberately declined.\n if (this.declined(row)) return 'OPTIONAL โ€” applied to this diff but was NOT run (not selected)';\n if (!row.ran) return 'skipped, not applicable to this diff (expected โœ…)';\n if (row.status === CK_PASS) return 'passed';\n if (row.status === CK_WARN) return 'passed with concerns';\n if (row.status === CK_OVERRIDDEN) return 'OVERRIDDEN โ€” a human authorized shipping it (pnpm wp-authorize)';\n if (row.status === CK_UNAUTHORIZED) return 'REFUSED โ€” override claimed, but no human authorized it';\n if (row.status === CK_FAIL) return 'FAILED review';\n if (row.status === CK_MISSING) return 'no verdict written';\n return `unknown verdict (${row.status})`;\n }\n\n /**\n * WHY this checklist ran or did not โ€” the line that answers \"why was this reviewer involved?\". Branches\n * on `configuredPatterns`, NEVER on `firedPatterns.length`: a patternless checklist and a skipped one\n * both fired zero globs and they mean opposite things, so keying off the fired list would tell every\n * skipped checklist's reader that the whole diff had been in its scope.\n */\n private whyLine(row: ChecklistCommentRow): string {\n const total = row.changedFileCount;\n if (row.configuredPatterns.length === 0) {\n // State the fact, not a suspicion. Patternless is a deliberate configuration โ€” an always-runs\n // gate (every PR names a ticket, every PR has an owner) is exactly what it is FOR โ€” so telling\n // every such row to \"add `patterns` if that is not intended\" nags the repos that meant it, on\n // every PR, forever. A reader who wants to know whether it was intended can read the config.\n return (\n `ALWAYS RUNS (no patterns) โ€” whole diff in scope, ${total} changed file(s): ` +\n `${formatFileList(row.matchedFiles)}`\n );\n }\n const configured = this.asCode(row.configuredPatterns);\n if (row.firedPatterns.length === 0) {\n return `${configured} matched 0 of ${total} changed file(s)`;\n }\n return (\n `matched ${this.asCode(row.firedPatterns)} โ†’ ${row.matchedFiles.length} of ${total} ` +\n `changed file(s): ${formatFileList(row.matchedFiles)}`\n );\n }\n\n private asCode(patterns: readonly string[]): string {\n return patterns.map((p: string): string => `\\`${p}\\``).join(', ');\n }\n\n // Reviewers that ran, exceptions first (overridden โ†’ warned โ†’ passed) so a reader meets what needs\n // attention before a wall of green.\n private ranOrdered(rows: readonly ChecklistCommentRow[]): ChecklistCommentRow[] {\n const rank: string[] = [CK_OVERRIDDEN, CK_WARN, CK_PASS];\n return rows\n .filter((r: ChecklistCommentRow): boolean => this.reviewerRan(r))\n .slice()\n .sort(\n (a: ChecklistCommentRow, b: ChecklistCommentRow): number =>\n this.rankOf(rank, a.status) - this.rankOf(rank, b.status),\n );\n }\n\n private rankOf(rank: readonly string[], status: string): number {\n const idx = rank.indexOf(status);\n return idx < 0 ? rank.length : idx;\n }\n\n private commentSection(row: ChecklistCommentRow): CommentSection {\n const heading = `#### ${this.verdictEmoji(row)} ${row.subagent} โ€” ${this.verdictWords(row)}`;\n const body =\n row.detail.trim() !== '' ? row.detail.trim() : '_(reviewer recorded no output)_';\n return new CommentSection(heading, body);\n }\n\n // Keep the comment under GitHub's size cap by shrinking the LONGEST section body first (so a short\n // overridden note is never cut to make room for a long passing one), never dropping a verdict heading.\n private fitComment(header: string, sections: CommentSection[]): string {\n const assemble = (): string =>\n `${header}\\n\\n${sections.map((s: CommentSection): string => `${s.heading}\\n\\n${s.body}`).join('\\n\\n')}`;\n const trunc = '\\n\\nโ€ฆ_[truncated to fit the GitHub comment size limit]_';\n let out = assemble();\n while (out.length > COMMENT_LIMIT) {\n const idx = this.longestBodyIndex(sections);\n if (idx < 0 || sections[idx].body.length <= trunc.length + 1) break;\n const over = out.length - COMMENT_LIMIT;\n const keep = Math.max(0, sections[idx].body.length - over - trunc.length - 8);\n sections[idx].body = sections[idx].body.slice(0, keep).trimEnd() + trunc;\n out = assemble();\n }\n return out;\n }\n\n private longestBodyIndex(sections: readonly CommentSection[]): number {\n let idx = -1;\n let max = -1;\n sections.forEach((s: CommentSection, i: number): void => {\n if (s.body.length > max) {\n max = s.body.length;\n idx = i;\n }\n });\n return idx;\n }\n}\n"]}
1
+ {"version":3,"file":"checklist-comment-renderer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/checklist-comment-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n formatFileList, CK_PASS, CK_WARN, CK_OVERRIDDEN, CK_FAIL, CK_MISSING,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { ChecklistCommentRow } from './checklist-comment-row';\n\n/**\n * Hidden marker on the checklist review COMMENT โ€” the \"2nd comment\" the PR description points at โ€” so\n * wp-finish can find and PATCH its own comment on every push instead of appending a new one. Versioned so\n * the format can evolve without matching an old shape. v2 = the full roster (every DEFINED checklist,\n * matched or not) + tri-state verdicts.\n */\nexport const CHECKLIST_COMMENT_MARKER = '<!-- webpieces-checklists v2 -->';\n\nconst COMMENT_LIMIT = 65000; // under GitHub's 65536-char cap, with headroom for the marker + roll-up.\n\n// One checklist section for the combined comment (heading + verbatim reviewer output), so oversize\n// truncation can shrink the longest BODY without ever dropping a verdict heading.\nclass CommentSection {\n heading: string;\n body: string;\n\n constructor(heading: string, body: string) {\n this.heading = heading;\n this.body = body;\n }\n}\n\n/**\n * Renders the 2nd PR comment: the reviewer checklist โ€” full roster plus each reviewer's verbatim output.\n *\n * Split out of `Dashboard` when the PR-description/commit-body swap pushed that file over the 700-line\n * cap, and the seam was already there to be found: this is ONE of the three surfaces the gated flow\n * writes, it shares no rendering helper with the other two (it speaks `ChecklistCommentRow`, where the\n * dashboard speaks `ChecklistRow`), and it owns the only size-fitting logic in the package. One class per\n * surface is the shape the rest of this change assumes โ€” see `Dashboard.renderPrBody` and\n * `Dashboard.renderDetailComment`.\n *\n * `@injectable(bindingScopeValues.Singleton)` so it is drawn in the DI design and injected by type.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class ChecklistCommentRenderer {\n /**\n * The ONE combined PR comment. Two halves, in this order:\n *\n * 1. A roll-up plus the FULL ROSTER โ€” every DEFINED checklist as a checkbox, each with a sub-bullet\n * stating exactly which globs fired against which files, or which did not and out of how many.\n * Skipped checklists are listed on purpose: skipping is the normal, healthy outcome, and a comment\n * that names only the reviewers that fired cannot distinguish \"evaluated and irrelevant\" from\n * \"never wired up\" โ€” nor answer \"why did the DB reviewer run on my frontend PR?\".\n * 2. One section per reviewer that RAN, carrying its full `output` โ€” the depth a verdict line throws\n * away. Overridden first, then warned, then passed: a reader should meet the exceptions first.\n *\n * Idempotent: keyed by the hidden marker so wp-finish PATCHes this same comment on every push.\n */\n render(\n rows: readonly ChecklistCommentRow[],\n provenanceVerified: boolean,\n baseResolved: boolean,\n ): string {\n const ran = this.ranOrdered(rows);\n const prov = provenanceVerified\n ? '_Each reviewer ran as its own independent subagent, verified from the Claude Code harness._'\n : '_โš ๏ธ Reviewer provenance was NOT verified (no Claude Code session) โ€” treat these as unverified._';\n // The roster lives in the HEADER, never in a section: fitComment only ever shrinks section bodies,\n // so a roster line can never be the thing an oversize comment silently drops.\n const lines: string[] = [CHECKLIST_COMMENT_MARKER, this.rollupHeader(rows, baseResolved)];\n // No reviewer ran โ‡’ no provenance claim to make. Printing one either way would attest to nothing.\n if (ran.length > 0) lines.push(prov);\n lines.push('', `### Checklists (all ${rows.length})`);\n for (const row of rows) lines.push(this.rosterBullet(row));\n const header = lines.join('\\n');\n if (ran.length === 0) {\n return `${header}\\n\\n${this.nothingRanNote(rows)}`;\n }\n return this.fitComment(\n `${header}\\n\\n### Reviews that ran`,\n ran.map((r: ChecklistCommentRow): CommentSection => this.commentSection(r)),\n );\n }\n\n /**\n * The closing note when no reviewer produced a verdict. The original wording โ€” \"every configured\n * checklist was evaluated and none of them applied\" โ€” is an all-clear, and it becomes FALSE the moment a\n * checklist did apply and was declined. That sentence under a PR nobody reviewed is precisely the\n * misreport this feature could otherwise introduce, so the declined case gets its own words.\n */\n private nothingRanNote(rows: readonly ChecklistCommentRow[]): string {\n const declined = rows.filter((r: ChecklistCommentRow): boolean => this.declined(r));\n if (declined.length === 0) {\n return '_No reviewer had to run on this diff โ€” every configured checklist was evaluated and none of them applied._';\n }\n return (\n `_No reviewer ran. ${declined.length} OPTIONAL checklist(s) DID apply to this diff and were not ` +\n `run; the rest were evaluated and did not apply._`\n );\n }\n\n // The roll-up line. `baseResolved:false` replaces it entirely: with no fork point the changed-file set is\n // EMPTY, so nothing matched โ€” including patternless ALWAYS-RUNS checklists โ€” and reporting that as\n // \"all skipped โœ…\" would post a green all-clear for a PR where nothing was actually evaluated.\n private rollupHeader(rows: readonly ChecklistCommentRow[], baseResolved: boolean): string {\n if (!baseResolved) {\n return (\n `## ๐Ÿ” Company review checklists โ€” โš ๏ธ NOT EVALUATED (${rows.length} defined)\\n` +\n `_No diff base (fork point of main) could be resolved, so no checklist was matched against ` +\n `anything. This is **not** an all-clear._`\n );\n }\n const ran = rows.filter((r: ChecklistCommentRow): boolean => this.reviewerRan(r));\n const declined = rows.filter((r: ChecklistCommentRow): boolean => this.declined(r));\n const skipped = rows.length - ran.length - declined.length;\n const parts: string[] = [];\n for (const pair of this.rollupCounts(ran)) parts.push(pair);\n const breakdown = parts.length > 0 ? ` (${parts.join(' ยท ')})` : '';\n const skip = skipped > 0 ? ` ยท ${skipped} skipped โœ…` : '';\n // Counted SEPARATELY from \"skipped\", and without a โœ…. A declined optional review is a legitimate\n // outcome, but it is not the same good news as a checklist that had nothing to look at โ€” folding the\n // two together would let a PR that declined every optional review read as fully covered.\n const notRun = declined.length > 0 ? ` ยท ${declined.length} optional not run` : '';\n return `## ๐Ÿ” Company review checklists โ€” ${rows.length} defined ยท ${ran.length} ran${breakdown}${skip}${notRun}`;\n }\n\n // `๐ŸŸข 2 ยท ๐ŸŸก 1` โ€” only the non-zero buckets, so a clean run reads as one number rather than four.\n private rollupCounts(ran: readonly ChecklistCommentRow[]): string[] {\n const counts: string[] = [];\n const emojiFor: string[] = ['๐ŸŸข', '๐ŸŸก', '๐ŸŸ '];\n const statusFor: string[] = [CK_PASS, CK_WARN, CK_OVERRIDDEN];\n statusFor.forEach((status: string, i: number): void => {\n const n = ran.filter((r: ChecklistCommentRow): boolean => r.status === status).length;\n if (n > 0) counts.push(`${emojiFor[i]} ${n}`);\n });\n return counts;\n }\n\n // One roster line + its why sub-bullet. A checked box means a reviewer ran; an unchecked one means the\n // checklist was evaluated and did not apply, which the words state as the good news it is.\n private rosterBullet(row: ChecklistCommentRow): string {\n const box = this.reviewerRan(row) ? '- [x]' : '- [ ]';\n return (\n `${box} ${this.verdictEmoji(row)} **${row.subagent}**${this.optionalTag(row)} โ€” ` +\n `${this.verdictWords(row)}${this.evidenceSuffix(row)}\\n` +\n ` - ${this.whyLine(row)}`\n );\n }\n\n /**\n * Did a reviewer actually produce a verdict for this row?\n *\n * `row.ran` is really \"this checklist APPLIED to the diff\" โ€” for a required checklist the two are the\n * same thing, because the PR cannot open otherwise, and the field was named before optional checklists\n * existed. For a DECLINED optional one they diverge, and using `ran` alone would put a checked box and a\n * reviewer section on a review that nobody performed.\n */\n private reviewerRan(row: ChecklistCommentRow): boolean {\n return row.ran && !this.declined(row);\n }\n\n // Applied, optional, and carrying no verdict โ€” i.e. the human was offered this review and said no (or\n // `--no-optional` skipped the offer). Never true of a required checklist: one of those with no verdict\n // does not reach a PR at all.\n private declined(row: ChecklistCommentRow): boolean {\n return row.ran && !row.required && (row.status === CK_MISSING || row.status === '');\n }\n\n // Marks which rows the human could have declined. Without it a reader cannot tell a review that was\n // skippable from one that simply passed, and so cannot judge how much this PR was actually reviewed.\n private optionalTag(row: ChecklistCommentRow): string {\n return row.required ? '' : ' _(optional)_';\n }\n\n /**\n * Whether the reviewer demonstrably opened the diff, read from its own transcript. A QUALITY signal and\n * never a blocker (see SubagentProvenanceService.evidenceFor) โ€” but published, because \"wrote a verdict\n * without reading the change\" is exactly what a reader of this comment would want to weigh.\n */\n private evidenceSuffix(row: ChecklistCommentRow): string {\n if (!this.reviewerRan(row) || row.diffRead === '') return '';\n return row.diffRead === 'yes' ? ' _(diff read โœ“)_' : ' _(โš ๏ธ no diff read recorded)_';\n }\n\n private verdictEmoji(row: ChecklistCommentRow): string {\n if (!this.reviewerRan(row)) return 'โšช';\n if (row.status === CK_PASS) return '๐ŸŸข';\n if (row.status === CK_WARN) return '๐ŸŸก';\n if (row.status === CK_OVERRIDDEN) return '๐ŸŸ ';\n if (row.status === CK_FAIL) return '๐Ÿ”ด';\n return 'โšช';\n }\n\n // SHORT words for a roster line / section heading. Short on purpose: the reviewer's own output and any\n // override justification get their own section below, and a roster exists to be scanned.\n private verdictWords(row: ChecklistCommentRow): string {\n // Two different unchecked boxes, two different sentences. \"Not applicable\" is the diff's doing;\n // \"not run\" is a person's, and reporting the second as the first would quietly credit a review that\n // a human deliberately declined.\n if (this.declined(row)) return 'OPTIONAL โ€” applied to this diff but was NOT run (not selected)';\n if (!row.ran) return 'skipped, not applicable to this diff (expected โœ…)';\n if (row.status === CK_PASS) return 'passed';\n if (row.status === CK_WARN) return 'passed with concerns';\n if (row.status === CK_OVERRIDDEN) return 'OVERRIDDEN โ€” shipped with a stated justification';\n if (row.status === CK_FAIL) return 'FAILED review';\n if (row.status === CK_MISSING) return 'no verdict written';\n return `unknown verdict (${row.status})`;\n }\n\n /**\n * WHY this checklist ran or did not โ€” the line that answers \"why was this reviewer involved?\". Branches\n * on `configuredPatterns`, NEVER on `firedPatterns.length`: a patternless checklist and a skipped one\n * both fired zero globs and they mean opposite things, so keying off the fired list would tell every\n * skipped checklist's reader that the whole diff had been in its scope.\n */\n private whyLine(row: ChecklistCommentRow): string {\n const total = row.changedFileCount;\n if (row.configuredPatterns.length === 0) {\n // State the fact, not a suspicion. Patternless is a deliberate configuration โ€” an always-runs\n // gate (every PR names a ticket, every PR has an owner) is exactly what it is FOR โ€” so telling\n // every such row to \"add `patterns` if that is not intended\" nags the repos that meant it, on\n // every PR, forever. A reader who wants to know whether it was intended can read the config.\n return (\n `ALWAYS RUNS (no patterns) โ€” whole diff in scope, ${total} changed file(s): ` +\n `${formatFileList(row.matchedFiles)}`\n );\n }\n const configured = this.asCode(row.configuredPatterns);\n if (row.firedPatterns.length === 0) {\n return `${configured} matched 0 of ${total} changed file(s)`;\n }\n return (\n `matched ${this.asCode(row.firedPatterns)} โ†’ ${row.matchedFiles.length} of ${total} ` +\n `changed file(s): ${formatFileList(row.matchedFiles)}`\n );\n }\n\n private asCode(patterns: readonly string[]): string {\n return patterns.map((p: string): string => `\\`${p}\\``).join(', ');\n }\n\n // Reviewers that ran, exceptions first (overridden โ†’ warned โ†’ passed) so a reader meets what needs\n // attention before a wall of green.\n private ranOrdered(rows: readonly ChecklistCommentRow[]): ChecklistCommentRow[] {\n const rank: string[] = [CK_OVERRIDDEN, CK_WARN, CK_PASS];\n return rows\n .filter((r: ChecklistCommentRow): boolean => this.reviewerRan(r))\n .slice()\n .sort(\n (a: ChecklistCommentRow, b: ChecklistCommentRow): number =>\n this.rankOf(rank, a.status) - this.rankOf(rank, b.status),\n );\n }\n\n private rankOf(rank: readonly string[], status: string): number {\n const idx = rank.indexOf(status);\n return idx < 0 ? rank.length : idx;\n }\n\n private commentSection(row: ChecklistCommentRow): CommentSection {\n const heading = `#### ${this.verdictEmoji(row)} ${row.subagent} โ€” ${this.verdictWords(row)}`;\n const body =\n row.detail.trim() !== '' ? row.detail.trim() : '_(reviewer recorded no output)_';\n return new CommentSection(heading, body);\n }\n\n // Keep the comment under GitHub's size cap by shrinking the LONGEST section body first (so a short\n // overridden note is never cut to make room for a long passing one), never dropping a verdict heading.\n private fitComment(header: string, sections: CommentSection[]): string {\n const assemble = (): string =>\n `${header}\\n\\n${sections.map((s: CommentSection): string => `${s.heading}\\n\\n${s.body}`).join('\\n\\n')}`;\n const trunc = '\\n\\nโ€ฆ_[truncated to fit the GitHub comment size limit]_';\n let out = assemble();\n while (out.length > COMMENT_LIMIT) {\n const idx = this.longestBodyIndex(sections);\n if (idx < 0 || sections[idx].body.length <= trunc.length + 1) break;\n const over = out.length - COMMENT_LIMIT;\n const keep = Math.max(0, sections[idx].body.length - over - trunc.length - 8);\n sections[idx].body = sections[idx].body.slice(0, keep).trimEnd() + trunc;\n out = assemble();\n }\n return out;\n }\n\n private longestBodyIndex(sections: readonly CommentSection[]): number {\n let idx = -1;\n let max = -1;\n sections.forEach((s: CommentSection, i: number): void => {\n if (s.body.length > max) {\n max = s.body.length;\n idx = i;\n }\n });\n return idx;\n }\n}\n"]}
@@ -343,8 +343,6 @@ let Dashboard = class Dashboard {
343
343
  const why = row.detail.trim() !== '' ? ` โ€” override: ${row.detail.trim()}` : '';
344
344
  return `๐ŸŸ  OVERRIDDEN${why}`;
345
345
  }
346
- if (row.status === rules_config_1.CK_UNAUTHORIZED)
347
- return '๐Ÿ”ด REFUSED โ€” override written with no human authorization';
348
346
  if (row.status === rules_config_1.CK_WARN)
349
347
  return '๐ŸŸก passed with concerns';
350
348
  if (row.status === rules_config_1.CK_FAIL)
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GateDefinition,\n WEBPIECES_DISABLE,\n RULE_NAMES,\n ReviewJson,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_UNAUTHORIZED,\n CK_FAIL,\n CK_MISSING,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/**\n * Hidden marker on the FULL-DASHBOARD comment โ€” the \"1st comment\" the PR description points at โ€” so\n * finish PATCHes its own comment on every push instead of appending a new one, exactly like the checklist\n * comment (see ChecklistCommentRenderer, which owns the 2nd).\n *\n * This comment did not exist before the PR description became the git-log body: the dashboard WAS the\n * description. Moving it here is what keeps a risk table out of every squash commit on main.\n */\nexport const DETAIL_COMMENT_MARKER = '<!-- webpieces-pr-detail v1 -->';\n\n/**\n * Footer on the full-dashboard comment. Names the tooling by REPO URL so a reader landing on a\n * generated comment in any consumer repo can find what generated it.\n *\n * It deliberately no longer asserts \"build ran via nx affected โ€” not self-attested\": that sentence was\n * hard-coded and therefore wrong on every repo whose `buildCommand` is not nx. The build command is now\n * named for real, from config, in the PR-body footer โ€” see {@link Dashboard.renderPrBody}.\n */\nconst DETAIL_COMMENT_FOOTER = 'Generated by https://github.com/deanhiller/webpieces-ts wp-finish-upsert-pr';\n\n/**\n * The last bullet of the PR description, and therefore of every squash commit body.\n *\n * Positional (\"1st\"/\"2nd\") on purpose โ€” it is what a reader of `git log` can actually act on, far more\n * useful than naming an HTML marker they cannot see. On a PR opened by an OLDER release the checklist\n * comment already exists, so the dashboard comment lands second and this reads one off for that PR's\n * remaining life; it is cosmetic and self-corrects on the next PR.\n */\nconst DETAIL_POINTER = '(Full dashboard in 1st comment, reviewer checklist in 2nd โ€” kept out of git log)';\n\nexport class GateResult {\n name: string;\n warningColor: string; // 'yellow' | 'red' โ€” the color shown WHEN files matched (green is implicit)\n matchedFiles: string[];\n\n constructor(name: string, warningColor: string, matchedFiles: string[]) {\n this.name = name;\n this.warningColor = warningColor;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// One row for a consumer review checklist the branch triggered. `status` is the resolved verdict (one of\n// CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT); `detail` is the reviewer\n// output / override justification. A row is always PASS/WARN/OVERRIDDEN by the time it renders โ€” a failed,\n// missing or unreadable verdict throws before the dashboard is built. Rendered into the PR body so the\n// verdict reaches the server โ€” the PR body is the artifact of the local flow that leaves the checkout\n// (alongside the HMAC gate token that proves the flow ran).\nexport class ChecklistRow {\n title: string; // the checklist id (= reviewer subagent name)\n status: string; // CK_* verdict\n detail: string; // reviewer output / override justification (surfaced for OVERRIDDEN)\n\n constructor(title: string, status: string, detail = '') {\n this.title = title;\n this.status = status;\n this.detail = detail;\n }\n}\n\n/**\n * One severity bucket of the rolled-up **Checklists** dashboard row: its emoji, the words that describe it,\n * and which checklists landed in it. Data-only.\n *\n * A bucket knows whether it should NAME its members: the non-green ones do (a reader has to know WHICH\n * reviewer to go read), the passed bucket does not (a list of what went fine is noise on a one-line row).\n */\nclass RollupBucket {\n label: string; // 'blocking' | 'overridden' | 'with concerns' | 'passed'\n emoji: string;\n named: boolean; // list the checklist ids on the row, or report a bare count\n titles: string[];\n\n constructor(label: string, emoji: string, named: boolean) {\n this.label = label;\n this.emoji = emoji;\n this.named = named;\n this.titles = [];\n }\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n review: ReviewJson; // AI-authored risk/violations/summary (from review.json)\n checklists: ChecklistRow[]; // consumer checklists this branch triggered; [] for non-adopting repos\n /**\n * `commands.pr-gate.buildCommand` VERBATIM, named in the PR-body footer so `git log` records WHICH\n * command vouched for the commit. Read from config rather than hard-coded because the footer used to\n * assert \"build ran via nx affected\" on every repo, including those whose buildCommand is not nx.\n *\n * REQUIRED, with no default, and `checklists` lost its `= []` for the same reason. A defaulted\n * `buildCommand: string = ''` let every pre-existing 9-argument construction keep compiling while\n * silently rendering a footer that claims nothing โ€” an absence that quietly means \"no build was\n * named\", which is the widening-by-omission the compatibility policy calls out. Making it required\n * means every caller states what vouched for the commit, and the empty string stays available for a\n * repo that genuinely has no build command, but only when someone writes it down.\n */\n buildCommand: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string,\n gateResults: GateResult[],\n disables: DisableCounts,\n buildPassed: boolean,\n forkPoint: string,\n featureHead: string,\n mainHead: string,\n review: ReviewJson,\n checklists: ChecklistRow[],\n buildCommand: string,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.review = review;\n this.checklists = checklists;\n this.buildCommand = buildCommand;\n }\n}\n\n/** Renders the PR-gate dashboard markdown (gates ร— changed files, disables, risk, 3-point hashes). */\n@injectable(bindingScopeValues.Singleton)\nexport class Dashboard {\n // Disabled gates are in-file examples (JSON has no comments) โ€” skip them entirely.\n computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates\n .filter((gate: GateDefinition): boolean => !gate.disabled)\n .map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean =>\n this.matchesAny(gate.patterns, file),\n );\n return new GateResult(gate.name, gate.warningColor, matched);\n });\n }\n\n // Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n // reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\n countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map(\n (key: string): string => (RULE_NAMES as Record<string, string>)[key],\n );\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n }\n\n /**\n * The FULL dashboard โ€” every row (green included), the whole summary, the 3-point hash points.\n *\n * This is the **1st PR comment**, not the PR description. It used to be the description, and that is\n * exactly what put it into main's history: GitHub's `squash_merge_commit_message: PR_BODY` copies the\n * description verbatim into the squash commit, so every `git log` entry carried the risk table, the\n * hash points and the gate token. The description now holds {@link renderPrBody}'s compact form, and\n * everything long-form lives here where a reader can open it and `git log` never sees it.\n *\n * Machine-facing content belongs HERE too โ€” this comment carries the HMAC gate token. That is the\n * whole rule that keeps the description clean: an HTML comment is invisible in rendered markdown but\n * perfectly visible in `git log`, so nothing hidden may live in the description.\n */\n renderDetailComment(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## ๐Ÿšฆ PR Gate Dashboard');\n lines.push('');\n for (const line of this.riskLines(input.review)) lines.push(line);\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '๐ŸŸข Passed' : '๐Ÿ”ด Failed'}`);\n for (const result of input.gateResults) lines.push(this.gateLine(result));\n lines.push(this.disableLine(input.disables));\n const eslintEmoji =\n input.disables.eslintCount === 0 ? '๐ŸŸข No' : `๐ŸŸก ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // ONE rolled-up row for ALL triggered consumer checklists โ€” a worst-of colour and a count, sitting\n // with the other status rows. It used to be one row PER checklist, each inlining that checklist's\n // whole verdict/override paragraph: on a six-checklist PR that was the same ~90-word override\n // repeated six times, burying the signal rows above it โ€” and every word of it was already in the\n // checklist COMMENT, per reviewer, in full. ALWAYS emitted, including the zero case: \"no reviewer\n // looked at this PR\" is a fact a reader must be told, and an absent row silently reads as a green\n // all-clear (see checklistRollupLine).\n lines.push(this.checklistRollupLine(input.checklists));\n lines.push('');\n if (input.review.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.review.summary.trim());\n lines.push('');\n }\n lines.push('### ๐Ÿ” 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push(`<sub>๐Ÿค– ${DETAIL_COMMENT_FOOTER}</sub>`);\n return lines.join('\\n');\n }\n\n /**\n * The PR DESCRIPTION โ€” which is also, byte for byte, the squash-commit body that lands in main.\n *\n * โ”€โ”€โ”€ Why one string serves both โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * There used to be two: a long dashboard in the description and this compact form passed to\n * `gh pr merge --body-file`. That made the good `git log` reachable ONLY through an explicit\n * `--body-file` merge, because every other route (the GitHub Merge button, a bare `gh pr merge`)\n * takes its body from the repo's `squash_merge_commit_message`, and on `PR_BODY` that copied the\n * whole dashboard into history. Two repos ran that way for months.\n *\n * Making the DESCRIPTION the compact form inverts it: `PR_BODY` now yields exactly the right commit,\n * so the UI button, a bare `gh pr merge`, `wp-land-pr` and finish's own auto-merge all converge on\n * identical bytes. It rests on two GitHub repo settings โ€” `squash_merge_commit_title: PR_TITLE` and\n * `squash_merge_commit_message: PR_BODY` โ€” which are NOT a consumer's job: they are server-side, so no\n * config key can express them, and SquashSettingsEnforcer pins them on every run. That convergence is\n * the point, and `pr-body-is-merge-body.spec.ts` pins it.\n *\n * โ”€โ”€โ”€ The shape, and why each part earns its line โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * The URL leads, BARE โ€” no caption. It used to carry `(for git log)`, which was aimed at a reader on\n * the PR page (where the link is redundant, being the page you are already on) and was meant to stop\n * them deleting it. But this string is also the squash-commit body, so that caption landed verbatim in\n * `main`'s history on every commit, forever, to serve one transient reading. In `git log` a bare URL\n * is self-evidently the way back and needs no explanation. The permanent surface wins.\n * Then the risk score (always). Then ONLY the non-green flags, under a heading that SAYS the full list\n * is in the 1st comment โ€” a `git log` reader has no page to glance at, so the heading has to carry\n * that itself rather than leaving \"why is this list short?\" to the pointer bullet alone. Then the\n * summary capped at 4 sentences, then the footer naming the build command that vouched for it.\n *\n * The hidden gate-token marker is appended AFTER this by the caller, so it is the very last line of\n * the description and therefore of the commit. It stays an HTML comment โ€” invisible on the PR page,\n * visible in `git log` โ€” which is a deliberate accepted trade: the alternative was reshaping a live\n * CI-critical surface that two consumer repos verify on every PR, to save one line of history.\n */\n renderPrBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n if (prUrl !== '') {\n lines.push(prUrl);\n lines.push('');\n }\n lines.push(\n `Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`,\n );\n lines.push('');\n const flags = this.nonGreenFlags(input);\n lines.push(flags.length === 0\n ? 'Flags: ๐ŸŸข all green'\n : 'Non-green Flags (full list in first comment to avoid large git logs)');\n for (const flag of flags) lines.push(`- ${flag}`);\n // ALWAYS the last bullet, green case included. Without it the compact body reads as the whole\n // record, and the reader never learns that every green row, the full summary and each reviewer's\n // output are one click away.\n lines.push(`- ${DETAIL_POINTER}`);\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n lines.push('');\n lines.push(this.prBodyFooter(input.buildCommand));\n return this.gitLogSafe(lines.join('\\n'));\n }\n\n /**\n * Makes THIS renderer own the invariant `wp-land-pr` checks: the compact body carries no markdown\n * heading and no table pipe.\n *\n * It is applied to the WHOLE joined string rather than to each author-supplied field, on purpose.\n * Every field this body interpolates โ€” the review summary, an override justification, a checklist\n * title, a disabled rule name, `commands.pr-gate.buildCommand` โ€” is text this package did not write,\n * and a `|` reaches all of them for entirely ordinary non-markdown reasons: a TypeScript union\n * (`'open' | 'paused'`), a regex alternation, a quoted shell pipeline, an OR in prose. Sanitizing at\n * the single exit point means a field added later is covered without anyone remembering to wrap it,\n * and none of this renderer's own literals contain either marker, so nothing of ours changes.\n *\n * Before this, `wp-finish-upsert-pr` would happily POST such a body and report success, and then\n * `wp-land-pr` refused those exact bytes โ€” two commands in one flow disagreeing about what the\n * renderer produces, with a refusal that blamed an old release and prescribed re-running finish,\n * which re-rendered the identical pipe from the unchanged `review.json`. An infinite loop costing a\n * CI cycle per turn.\n *\n * `ยฆ` (BROKEN BAR) rather than `\\|`: this string's home is `git log` in a terminal, where a\n * backslash is literal punctuation and a markdown escape means nothing. The substitution is visible\n * and reads correctly; the full, unaltered text is one click away in the 1st comment, which this\n * body's last bullet always points at. `##` collapses to `#` for the same reason โ€” a heading in a\n * summary is markdown a plain-text history cannot carry either.\n */\n private gitLogSafe(body: string): string {\n return body.replace(/\\|/g, '\\u00a6').replace(/#{2,}/g, '#');\n }\n\n /**\n * The footer, naming the build command from `commands.pr-gate.buildCommand` VERBATIM.\n *\n * No backticks: this string's primary home is `git log` in a terminal, where markdown ticks are\n * literal punctuation. \"run locally\" is the honest claim โ€” this is the LOCAL gate's receipt, and the\n * server-side proof is the gate token in the 1st comment, not this sentence.\n */\n private prBodyFooter(buildCommand: string): string {\n const cmd = buildCommand.trim();\n const ran = cmd === '' ? '' : ` (${cmd} run locally)`;\n return `Generated by webpieces-ts wp-finish-upsert-pr${ran}`;\n }\n\n // Every dashboard row that is NOT green, as a flat bullet list for the commit body. Green rows\n // (build passed, gate did not match, zero disables/violations) are intentionally omitted.\n private nonGreenFlags(input: DashboardInput): string[] {\n const flags: string[] = [];\n if (!input.buildPassed) flags.push('Build (nx affected): ๐Ÿ”ด Failed');\n if (input.review.violations.length > 0)\n flags.push(`Pattern Violations: ๐ŸŸก ${input.review.violations.length} violation(s)`);\n for (const result of input.gateResults) {\n if (result.matchedFiles.length === 0) continue;\n const emoji = result.warningColor === 'red' ? '๐Ÿ”ด' : '๐ŸŸก';\n flags.push(`${result.name}: ${emoji} ${result.matchedFiles.length} file(s)`);\n }\n if (input.disables.webpiecesCount > 0) {\n const which =\n input.disables.webpiecesRules.length > 0\n ? ` โ€” ${input.disables.webpiecesRules.join(', ')}`\n : '';\n flags.push(\n `Webpieces Disables Added: ๐ŸŸก ${input.disables.webpiecesCount} line(s)${which}`,\n );\n }\n if (input.disables.eslintCount > 0)\n flags.push(`ESLint Disables Added: ๐ŸŸก ${input.disables.eslintCount} line(s)`);\n // A triggered checklist is noteworthy in main's history โ€” carry each into the commit body.\n for (const row of input.checklists) {\n flags.push(`Checklist โ€” ${row.title}: ${this.checklistStatusText(row)}`);\n }\n return flags;\n }\n\n // Emoji + words for a checklist verdict, shared by the dashboard row and the commit-body flag.\n // Every state is matched EXPLICITLY and the fallthrough names the status it did not recognize. The\n // previous `return '๐ŸŸข passed'` fallthrough meant any newly-added verdict rendered as a clean pass on\n // both the PR body and main's commit history until someone noticed โ€” exactly the wrong default.\n private checklistStatusText(row: ChecklistRow): string {\n if (row.status === CK_OVERRIDDEN) {\n const why = row.detail.trim() !== '' ? ` โ€” override: ${row.detail.trim()}` : '';\n return `๐ŸŸ  OVERRIDDEN${why}`;\n }\n if (row.status === CK_UNAUTHORIZED) return '๐Ÿ”ด REFUSED โ€” override written with no human authorization';\n if (row.status === CK_WARN) return '๐ŸŸก passed with concerns';\n if (row.status === CK_FAIL) return '๐Ÿ”ด FAILED review';\n if (row.status === CK_MISSING) return 'โšช not reviewed';\n if (row.status === CK_PASS) return '๐ŸŸข passed';\n return `โšช unknown verdict (${row.status})`;\n }\n\n // First `max` sentences of `text`. A sentence ends at `. ! ?` ONLY when followed by whitespace or\n // end-of-string, so interior dots in filenames/paths/versions (dependencies.json, runtime-graph.ts,\n // 0.4.447) do NOT split โ€” and, unlike a greedy `[^.!?]+` regex, no text is ever dropped when such a\n // dot appears (that footgun silently deleted the run of prose up to the next real boundary). Keeps\n // the commit body scannable; the full summary still lives in the PR-body dashboard.\n private firstSentences(text: string, max: number): string {\n if (text === '') return '';\n const sentences: string[] = [];\n let start = 0;\n for (let i = 0; i < text.length && sentences.length < max; i++) {\n const ch = text[i];\n const isTerminator = ch === '.' || ch === '!' || ch === '?';\n const next = text[i + 1];\n if (isTerminator && (next === undefined || /\\s/.test(next))) {\n sentences.push(text.slice(start, i + 1).trim());\n start = i + 1;\n }\n }\n // Trailing text with no terminator still counts as a sentence (up to the cap).\n if (sentences.length < max && start < text.length) {\n const tail = text.slice(start).trim();\n if (tail !== '') sentences.push(tail);\n }\n return sentences.join(' ').trim();\n }\n\n // Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\n private globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') {\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n }\n\n private matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (this.globToRegex(pattern).test(file)) return true;\n }\n return false;\n }\n\n private gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** ๐ŸŸข No`;\n const emoji = result.warningColor === 'red' ? '๐Ÿ”ด' : '๐ŸŸก';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n }\n\n /**\n * The ONE rolled-up **Checklists:** row, coloured worst-of across every checklist that ran, in the same\n * shape as its neighbours (`**Build (nx affected):** ๐ŸŸข Passed`). Counts and names only โ€” never a line\n * of reviewer output or override justification, which is the whole reason this row exists.\n */\n private checklistRollupLine(rows: readonly ChecklistRow[]): string {\n const buckets = this.rollupBuckets(rows);\n const filled = buckets.filter((b: RollupBucket): boolean => b.titles.length > 0);\n // NO reviewer ran โ‡’ the SKIPPED icon, never green. Green claims something was checked and came back\n // clean; โšช says nobody looked, which is what a reader has to be able to tell apart at a glance.\n if (filled.length === 0) {\n return '**Checklists:** โšช 0 ran โ€” no review checklist matched this PR ยท see the checklist comment';\n }\n const allPassed = filled.length === 1 && !filled[0].named;\n const detail = allPassed\n ? 'all passed'\n : filled.map((b: RollupBucket): string => this.bucketPhrase(b)).join(', ');\n return `**Checklists:** ${filled[0].emoji} ${rows.length} ran โ€” ${detail} ยท per-checklist detail in the checklist comment`;\n }\n\n /**\n * The four severity buckets, WORST FIRST, so `filled[0]` is the roll-up colour: red beats orange beats\n * yellow beats green. The colours are the same vocabulary the comment already uses (see verdictEmoji).\n *\n * OVERRIDDEN is deliberately NOT folded into passed: an override is a human knowingly accepting a red\n * verdict, and a dashboard that painted that green would hide the single most review-worthy thing on\n * the PR. The blocking bucket is the FALLTHROUGH โ€” CK_FAIL, CK_MISSING, CK_BAD_FORMAT and any verdict\n * added later all land there, matching review.json's own \"not pass|warn|overridden โ‡’ refuse\" rule\n * rather than a second list that could silently drift green.\n */\n private rollupBuckets(rows: readonly ChecklistRow[]): RollupBucket[] {\n const blocking = new RollupBucket('blocking', '๐Ÿ”ด', true);\n const overridden = new RollupBucket('overridden', '๐ŸŸ ', true);\n const warned = new RollupBucket('with concerns', '๐ŸŸก', true);\n const passed = new RollupBucket('passed', '๐ŸŸข', false);\n for (const row of rows) {\n if (row.status === CK_PASS) passed.titles.push(row.title);\n else if (row.status === CK_WARN) warned.titles.push(row.title);\n else if (row.status === CK_OVERRIDDEN) overridden.titles.push(row.title);\n else blocking.titles.push(row.title);\n }\n return [blocking, overridden, warned, passed];\n }\n\n // `2 overridden (a, b)` for the buckets a reviewer must act on; a bare `3 passed` for the one they need\n // no list of. Names are capped so a many-checklist repo still gets a ONE-LINE row.\n private bucketPhrase(bucket: RollupBucket): string {\n if (!bucket.named) return `${bucket.titles.length} ${bucket.label}`;\n return `${bucket.titles.length} ${bucket.label} (${this.compactNames(bucket.titles)})`;\n }\n\n private compactNames(titles: readonly string[]): string {\n const max = 4;\n if (titles.length <= max) return titles.join(', ');\n return `${titles.slice(0, max).join(', ')} +${titles.length - max} more`;\n }\n\n // 10-cell risk bar colored by band (๐ŸŸฉ โ‰ค25, ๐ŸŸจ โ‰ค50, ๐ŸŸง โ‰ค75, ๐ŸŸฅ >75), at least one filled cell.\n private riskBar(score: number): string {\n const clamped = Math.max(0, Math.min(100, score));\n const cell = clamped <= 25 ? '๐ŸŸฉ' : clamped <= 50 ? '๐ŸŸจ' : clamped <= 75 ? '๐ŸŸง' : '๐ŸŸฅ';\n const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));\n return cell.repeat(filled) + 'โฌœ'.repeat(10 - filled);\n }\n\n // RISK section (the AI half): Risk Score bar, Risk Level, Pattern Violations.\n private riskLines(review: ReviewJson): string[] {\n const violations = review.violations.length;\n const violationLine = violations === 0 ? '๐ŸŸข No' : `๐ŸŸก Yes (${violations} violation(s))`;\n return [\n `**Risk Score:** ${this.riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,\n `**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,\n `**Pattern Violations:** ${violationLine}`,\n ];\n }\n\n private disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** ๐ŸŸข No';\n const which =\n disables.webpiecesRules.length > 0 ? ` โ€” ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** ๐ŸŸก ${disables.webpiecesCount} line(s)${which}`;\n }\n}\n"]}
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GateDefinition,\n WEBPIECES_DISABLE,\n RULE_NAMES,\n ReviewJson,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/**\n * Hidden marker on the FULL-DASHBOARD comment โ€” the \"1st comment\" the PR description points at โ€” so\n * finish PATCHes its own comment on every push instead of appending a new one, exactly like the checklist\n * comment (see ChecklistCommentRenderer, which owns the 2nd).\n *\n * This comment did not exist before the PR description became the git-log body: the dashboard WAS the\n * description. Moving it here is what keeps a risk table out of every squash commit on main.\n */\nexport const DETAIL_COMMENT_MARKER = '<!-- webpieces-pr-detail v1 -->';\n\n/**\n * Footer on the full-dashboard comment. Names the tooling by REPO URL so a reader landing on a\n * generated comment in any consumer repo can find what generated it.\n *\n * It deliberately no longer asserts \"build ran via nx affected โ€” not self-attested\": that sentence was\n * hard-coded and therefore wrong on every repo whose `buildCommand` is not nx. The build command is now\n * named for real, from config, in the PR-body footer โ€” see {@link Dashboard.renderPrBody}.\n */\nconst DETAIL_COMMENT_FOOTER = 'Generated by https://github.com/deanhiller/webpieces-ts wp-finish-upsert-pr';\n\n/**\n * The last bullet of the PR description, and therefore of every squash commit body.\n *\n * Positional (\"1st\"/\"2nd\") on purpose โ€” it is what a reader of `git log` can actually act on, far more\n * useful than naming an HTML marker they cannot see. On a PR opened by an OLDER release the checklist\n * comment already exists, so the dashboard comment lands second and this reads one off for that PR's\n * remaining life; it is cosmetic and self-corrects on the next PR.\n */\nconst DETAIL_POINTER = '(Full dashboard in 1st comment, reviewer checklist in 2nd โ€” kept out of git log)';\n\nexport class GateResult {\n name: string;\n warningColor: string; // 'yellow' | 'red' โ€” the color shown WHEN files matched (green is implicit)\n matchedFiles: string[];\n\n constructor(name: string, warningColor: string, matchedFiles: string[]) {\n this.name = name;\n this.warningColor = warningColor;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// One row for a consumer review checklist the branch triggered. `status` is the resolved verdict (one of\n// CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT); `detail` is the reviewer\n// output / override justification. A row is always PASS/WARN/OVERRIDDEN by the time it renders โ€” a failed,\n// missing or unreadable verdict throws before the dashboard is built. Rendered into the PR body so the\n// verdict reaches the server โ€” the PR body is the artifact of the local flow that leaves the checkout\n// (alongside the HMAC gate token that proves the flow ran).\nexport class ChecklistRow {\n title: string; // the checklist id (= reviewer subagent name)\n status: string; // CK_* verdict\n detail: string; // reviewer output / override justification (surfaced for OVERRIDDEN)\n\n constructor(title: string, status: string, detail = '') {\n this.title = title;\n this.status = status;\n this.detail = detail;\n }\n}\n\n/**\n * One severity bucket of the rolled-up **Checklists** dashboard row: its emoji, the words that describe it,\n * and which checklists landed in it. Data-only.\n *\n * A bucket knows whether it should NAME its members: the non-green ones do (a reader has to know WHICH\n * reviewer to go read), the passed bucket does not (a list of what went fine is noise on a one-line row).\n */\nclass RollupBucket {\n label: string; // 'blocking' | 'overridden' | 'with concerns' | 'passed'\n emoji: string;\n named: boolean; // list the checklist ids on the row, or report a bare count\n titles: string[];\n\n constructor(label: string, emoji: string, named: boolean) {\n this.label = label;\n this.emoji = emoji;\n this.named = named;\n this.titles = [];\n }\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n review: ReviewJson; // AI-authored risk/violations/summary (from review.json)\n checklists: ChecklistRow[]; // consumer checklists this branch triggered; [] for non-adopting repos\n /**\n * `commands.pr-gate.buildCommand` VERBATIM, named in the PR-body footer so `git log` records WHICH\n * command vouched for the commit. Read from config rather than hard-coded because the footer used to\n * assert \"build ran via nx affected\" on every repo, including those whose buildCommand is not nx.\n *\n * REQUIRED, with no default, and `checklists` lost its `= []` for the same reason. A defaulted\n * `buildCommand: string = ''` let every pre-existing 9-argument construction keep compiling while\n * silently rendering a footer that claims nothing โ€” an absence that quietly means \"no build was\n * named\", which is the widening-by-omission the compatibility policy calls out. Making it required\n * means every caller states what vouched for the commit, and the empty string stays available for a\n * repo that genuinely has no build command, but only when someone writes it down.\n */\n buildCommand: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string,\n gateResults: GateResult[],\n disables: DisableCounts,\n buildPassed: boolean,\n forkPoint: string,\n featureHead: string,\n mainHead: string,\n review: ReviewJson,\n checklists: ChecklistRow[],\n buildCommand: string,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.review = review;\n this.checklists = checklists;\n this.buildCommand = buildCommand;\n }\n}\n\n/** Renders the PR-gate dashboard markdown (gates ร— changed files, disables, risk, 3-point hashes). */\n@injectable(bindingScopeValues.Singleton)\nexport class Dashboard {\n // Disabled gates are in-file examples (JSON has no comments) โ€” skip them entirely.\n computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates\n .filter((gate: GateDefinition): boolean => !gate.disabled)\n .map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean =>\n this.matchesAny(gate.patterns, file),\n );\n return new GateResult(gate.name, gate.warningColor, matched);\n });\n }\n\n // Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n // reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\n countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map(\n (key: string): string => (RULE_NAMES as Record<string, string>)[key],\n );\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n }\n\n /**\n * The FULL dashboard โ€” every row (green included), the whole summary, the 3-point hash points.\n *\n * This is the **1st PR comment**, not the PR description. It used to be the description, and that is\n * exactly what put it into main's history: GitHub's `squash_merge_commit_message: PR_BODY` copies the\n * description verbatim into the squash commit, so every `git log` entry carried the risk table, the\n * hash points and the gate token. The description now holds {@link renderPrBody}'s compact form, and\n * everything long-form lives here where a reader can open it and `git log` never sees it.\n *\n * Machine-facing content belongs HERE too โ€” this comment carries the HMAC gate token. That is the\n * whole rule that keeps the description clean: an HTML comment is invisible in rendered markdown but\n * perfectly visible in `git log`, so nothing hidden may live in the description.\n */\n renderDetailComment(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## ๐Ÿšฆ PR Gate Dashboard');\n lines.push('');\n for (const line of this.riskLines(input.review)) lines.push(line);\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '๐ŸŸข Passed' : '๐Ÿ”ด Failed'}`);\n for (const result of input.gateResults) lines.push(this.gateLine(result));\n lines.push(this.disableLine(input.disables));\n const eslintEmoji =\n input.disables.eslintCount === 0 ? '๐ŸŸข No' : `๐ŸŸก ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // ONE rolled-up row for ALL triggered consumer checklists โ€” a worst-of colour and a count, sitting\n // with the other status rows. It used to be one row PER checklist, each inlining that checklist's\n // whole verdict/override paragraph: on a six-checklist PR that was the same ~90-word override\n // repeated six times, burying the signal rows above it โ€” and every word of it was already in the\n // checklist COMMENT, per reviewer, in full. ALWAYS emitted, including the zero case: \"no reviewer\n // looked at this PR\" is a fact a reader must be told, and an absent row silently reads as a green\n // all-clear (see checklistRollupLine).\n lines.push(this.checklistRollupLine(input.checklists));\n lines.push('');\n if (input.review.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.review.summary.trim());\n lines.push('');\n }\n lines.push('### ๐Ÿ” 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push(`<sub>๐Ÿค– ${DETAIL_COMMENT_FOOTER}</sub>`);\n return lines.join('\\n');\n }\n\n /**\n * The PR DESCRIPTION โ€” which is also, byte for byte, the squash-commit body that lands in main.\n *\n * โ”€โ”€โ”€ Why one string serves both โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * There used to be two: a long dashboard in the description and this compact form passed to\n * `gh pr merge --body-file`. That made the good `git log` reachable ONLY through an explicit\n * `--body-file` merge, because every other route (the GitHub Merge button, a bare `gh pr merge`)\n * takes its body from the repo's `squash_merge_commit_message`, and on `PR_BODY` that copied the\n * whole dashboard into history. Two repos ran that way for months.\n *\n * Making the DESCRIPTION the compact form inverts it: `PR_BODY` now yields exactly the right commit,\n * so the UI button, a bare `gh pr merge`, `wp-land-pr` and finish's own auto-merge all converge on\n * identical bytes. It rests on two GitHub repo settings โ€” `squash_merge_commit_title: PR_TITLE` and\n * `squash_merge_commit_message: PR_BODY` โ€” which are NOT a consumer's job: they are server-side, so no\n * config key can express them, and SquashSettingsEnforcer pins them on every run. That convergence is\n * the point, and `pr-body-is-merge-body.spec.ts` pins it.\n *\n * โ”€โ”€โ”€ The shape, and why each part earns its line โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * The URL leads, BARE โ€” no caption. It used to carry `(for git log)`, which was aimed at a reader on\n * the PR page (where the link is redundant, being the page you are already on) and was meant to stop\n * them deleting it. But this string is also the squash-commit body, so that caption landed verbatim in\n * `main`'s history on every commit, forever, to serve one transient reading. In `git log` a bare URL\n * is self-evidently the way back and needs no explanation. The permanent surface wins.\n * Then the risk score (always). Then ONLY the non-green flags, under a heading that SAYS the full list\n * is in the 1st comment โ€” a `git log` reader has no page to glance at, so the heading has to carry\n * that itself rather than leaving \"why is this list short?\" to the pointer bullet alone. Then the\n * summary capped at 4 sentences, then the footer naming the build command that vouched for it.\n *\n * The hidden gate-token marker is appended AFTER this by the caller, so it is the very last line of\n * the description and therefore of the commit. It stays an HTML comment โ€” invisible on the PR page,\n * visible in `git log` โ€” which is a deliberate accepted trade: the alternative was reshaping a live\n * CI-critical surface that two consumer repos verify on every PR, to save one line of history.\n */\n renderPrBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n if (prUrl !== '') {\n lines.push(prUrl);\n lines.push('');\n }\n lines.push(\n `Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`,\n );\n lines.push('');\n const flags = this.nonGreenFlags(input);\n lines.push(flags.length === 0\n ? 'Flags: ๐ŸŸข all green'\n : 'Non-green Flags (full list in first comment to avoid large git logs)');\n for (const flag of flags) lines.push(`- ${flag}`);\n // ALWAYS the last bullet, green case included. Without it the compact body reads as the whole\n // record, and the reader never learns that every green row, the full summary and each reviewer's\n // output are one click away.\n lines.push(`- ${DETAIL_POINTER}`);\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n lines.push('');\n lines.push(this.prBodyFooter(input.buildCommand));\n return this.gitLogSafe(lines.join('\\n'));\n }\n\n /**\n * Makes THIS renderer own the invariant `wp-land-pr` checks: the compact body carries no markdown\n * heading and no table pipe.\n *\n * It is applied to the WHOLE joined string rather than to each author-supplied field, on purpose.\n * Every field this body interpolates โ€” the review summary, an override justification, a checklist\n * title, a disabled rule name, `commands.pr-gate.buildCommand` โ€” is text this package did not write,\n * and a `|` reaches all of them for entirely ordinary non-markdown reasons: a TypeScript union\n * (`'open' | 'paused'`), a regex alternation, a quoted shell pipeline, an OR in prose. Sanitizing at\n * the single exit point means a field added later is covered without anyone remembering to wrap it,\n * and none of this renderer's own literals contain either marker, so nothing of ours changes.\n *\n * Before this, `wp-finish-upsert-pr` would happily POST such a body and report success, and then\n * `wp-land-pr` refused those exact bytes โ€” two commands in one flow disagreeing about what the\n * renderer produces, with a refusal that blamed an old release and prescribed re-running finish,\n * which re-rendered the identical pipe from the unchanged `review.json`. An infinite loop costing a\n * CI cycle per turn.\n *\n * `ยฆ` (BROKEN BAR) rather than `\\|`: this string's home is `git log` in a terminal, where a\n * backslash is literal punctuation and a markdown escape means nothing. The substitution is visible\n * and reads correctly; the full, unaltered text is one click away in the 1st comment, which this\n * body's last bullet always points at. `##` collapses to `#` for the same reason โ€” a heading in a\n * summary is markdown a plain-text history cannot carry either.\n */\n private gitLogSafe(body: string): string {\n return body.replace(/\\|/g, '\\u00a6').replace(/#{2,}/g, '#');\n }\n\n /**\n * The footer, naming the build command from `commands.pr-gate.buildCommand` VERBATIM.\n *\n * No backticks: this string's primary home is `git log` in a terminal, where markdown ticks are\n * literal punctuation. \"run locally\" is the honest claim โ€” this is the LOCAL gate's receipt, and the\n * server-side proof is the gate token in the 1st comment, not this sentence.\n */\n private prBodyFooter(buildCommand: string): string {\n const cmd = buildCommand.trim();\n const ran = cmd === '' ? '' : ` (${cmd} run locally)`;\n return `Generated by webpieces-ts wp-finish-upsert-pr${ran}`;\n }\n\n // Every dashboard row that is NOT green, as a flat bullet list for the commit body. Green rows\n // (build passed, gate did not match, zero disables/violations) are intentionally omitted.\n private nonGreenFlags(input: DashboardInput): string[] {\n const flags: string[] = [];\n if (!input.buildPassed) flags.push('Build (nx affected): ๐Ÿ”ด Failed');\n if (input.review.violations.length > 0)\n flags.push(`Pattern Violations: ๐ŸŸก ${input.review.violations.length} violation(s)`);\n for (const result of input.gateResults) {\n if (result.matchedFiles.length === 0) continue;\n const emoji = result.warningColor === 'red' ? '๐Ÿ”ด' : '๐ŸŸก';\n flags.push(`${result.name}: ${emoji} ${result.matchedFiles.length} file(s)`);\n }\n if (input.disables.webpiecesCount > 0) {\n const which =\n input.disables.webpiecesRules.length > 0\n ? ` โ€” ${input.disables.webpiecesRules.join(', ')}`\n : '';\n flags.push(\n `Webpieces Disables Added: ๐ŸŸก ${input.disables.webpiecesCount} line(s)${which}`,\n );\n }\n if (input.disables.eslintCount > 0)\n flags.push(`ESLint Disables Added: ๐ŸŸก ${input.disables.eslintCount} line(s)`);\n // A triggered checklist is noteworthy in main's history โ€” carry each into the commit body.\n for (const row of input.checklists) {\n flags.push(`Checklist โ€” ${row.title}: ${this.checklistStatusText(row)}`);\n }\n return flags;\n }\n\n // Emoji + words for a checklist verdict, shared by the dashboard row and the commit-body flag.\n // Every state is matched EXPLICITLY and the fallthrough names the status it did not recognize. The\n // previous `return '๐ŸŸข passed'` fallthrough meant any newly-added verdict rendered as a clean pass on\n // both the PR body and main's commit history until someone noticed โ€” exactly the wrong default.\n private checklistStatusText(row: ChecklistRow): string {\n if (row.status === CK_OVERRIDDEN) {\n const why = row.detail.trim() !== '' ? ` โ€” override: ${row.detail.trim()}` : '';\n return `๐ŸŸ  OVERRIDDEN${why}`;\n }\n if (row.status === CK_WARN) return '๐ŸŸก passed with concerns';\n if (row.status === CK_FAIL) return '๐Ÿ”ด FAILED review';\n if (row.status === CK_MISSING) return 'โšช not reviewed';\n if (row.status === CK_PASS) return '๐ŸŸข passed';\n return `โšช unknown verdict (${row.status})`;\n }\n\n // First `max` sentences of `text`. A sentence ends at `. ! ?` ONLY when followed by whitespace or\n // end-of-string, so interior dots in filenames/paths/versions (dependencies.json, runtime-graph.ts,\n // 0.4.447) do NOT split โ€” and, unlike a greedy `[^.!?]+` regex, no text is ever dropped when such a\n // dot appears (that footgun silently deleted the run of prose up to the next real boundary). Keeps\n // the commit body scannable; the full summary still lives in the PR-body dashboard.\n private firstSentences(text: string, max: number): string {\n if (text === '') return '';\n const sentences: string[] = [];\n let start = 0;\n for (let i = 0; i < text.length && sentences.length < max; i++) {\n const ch = text[i];\n const isTerminator = ch === '.' || ch === '!' || ch === '?';\n const next = text[i + 1];\n if (isTerminator && (next === undefined || /\\s/.test(next))) {\n sentences.push(text.slice(start, i + 1).trim());\n start = i + 1;\n }\n }\n // Trailing text with no terminator still counts as a sentence (up to the cap).\n if (sentences.length < max && start < text.length) {\n const tail = text.slice(start).trim();\n if (tail !== '') sentences.push(tail);\n }\n return sentences.join(' ').trim();\n }\n\n // Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\n private globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') {\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n }\n\n private matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (this.globToRegex(pattern).test(file)) return true;\n }\n return false;\n }\n\n private gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** ๐ŸŸข No`;\n const emoji = result.warningColor === 'red' ? '๐Ÿ”ด' : '๐ŸŸก';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n }\n\n /**\n * The ONE rolled-up **Checklists:** row, coloured worst-of across every checklist that ran, in the same\n * shape as its neighbours (`**Build (nx affected):** ๐ŸŸข Passed`). Counts and names only โ€” never a line\n * of reviewer output or override justification, which is the whole reason this row exists.\n */\n private checklistRollupLine(rows: readonly ChecklistRow[]): string {\n const buckets = this.rollupBuckets(rows);\n const filled = buckets.filter((b: RollupBucket): boolean => b.titles.length > 0);\n // NO reviewer ran โ‡’ the SKIPPED icon, never green. Green claims something was checked and came back\n // clean; โšช says nobody looked, which is what a reader has to be able to tell apart at a glance.\n if (filled.length === 0) {\n return '**Checklists:** โšช 0 ran โ€” no review checklist matched this PR ยท see the checklist comment';\n }\n const allPassed = filled.length === 1 && !filled[0].named;\n const detail = allPassed\n ? 'all passed'\n : filled.map((b: RollupBucket): string => this.bucketPhrase(b)).join(', ');\n return `**Checklists:** ${filled[0].emoji} ${rows.length} ran โ€” ${detail} ยท per-checklist detail in the checklist comment`;\n }\n\n /**\n * The four severity buckets, WORST FIRST, so `filled[0]` is the roll-up colour: red beats orange beats\n * yellow beats green. The colours are the same vocabulary the comment already uses (see verdictEmoji).\n *\n * OVERRIDDEN is deliberately NOT folded into passed: an override is a human knowingly accepting a red\n * verdict, and a dashboard that painted that green would hide the single most review-worthy thing on\n * the PR. The blocking bucket is the FALLTHROUGH โ€” CK_FAIL, CK_MISSING, CK_BAD_FORMAT and any verdict\n * added later all land there, matching review.json's own \"not pass|warn|overridden โ‡’ refuse\" rule\n * rather than a second list that could silently drift green.\n */\n private rollupBuckets(rows: readonly ChecklistRow[]): RollupBucket[] {\n const blocking = new RollupBucket('blocking', '๐Ÿ”ด', true);\n const overridden = new RollupBucket('overridden', '๐ŸŸ ', true);\n const warned = new RollupBucket('with concerns', '๐ŸŸก', true);\n const passed = new RollupBucket('passed', '๐ŸŸข', false);\n for (const row of rows) {\n if (row.status === CK_PASS) passed.titles.push(row.title);\n else if (row.status === CK_WARN) warned.titles.push(row.title);\n else if (row.status === CK_OVERRIDDEN) overridden.titles.push(row.title);\n else blocking.titles.push(row.title);\n }\n return [blocking, overridden, warned, passed];\n }\n\n // `2 overridden (a, b)` for the buckets a reviewer must act on; a bare `3 passed` for the one they need\n // no list of. Names are capped so a many-checklist repo still gets a ONE-LINE row.\n private bucketPhrase(bucket: RollupBucket): string {\n if (!bucket.named) return `${bucket.titles.length} ${bucket.label}`;\n return `${bucket.titles.length} ${bucket.label} (${this.compactNames(bucket.titles)})`;\n }\n\n private compactNames(titles: readonly string[]): string {\n const max = 4;\n if (titles.length <= max) return titles.join(', ');\n return `${titles.slice(0, max).join(', ')} +${titles.length - max} more`;\n }\n\n // 10-cell risk bar colored by band (๐ŸŸฉ โ‰ค25, ๐ŸŸจ โ‰ค50, ๐ŸŸง โ‰ค75, ๐ŸŸฅ >75), at least one filled cell.\n private riskBar(score: number): string {\n const clamped = Math.max(0, Math.min(100, score));\n const cell = clamped <= 25 ? '๐ŸŸฉ' : clamped <= 50 ? '๐ŸŸจ' : clamped <= 75 ? '๐ŸŸง' : '๐ŸŸฅ';\n const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));\n return cell.repeat(filled) + 'โฌœ'.repeat(10 - filled);\n }\n\n // RISK section (the AI half): Risk Score bar, Risk Level, Pattern Violations.\n private riskLines(review: ReviewJson): string[] {\n const violations = review.violations.length;\n const violationLine = violations === 0 ? '๐ŸŸข No' : `๐ŸŸก Yes (${violations} violation(s))`;\n return [\n `**Risk Score:** ${this.riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,\n `**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,\n `**Pattern Violations:** ${violationLine}`,\n ];\n }\n\n private disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** ๐ŸŸข No';\n const which =\n disables.webpiecesRules.length > 0 ? ` โ€” ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** ๐ŸŸก ${disables.webpiecesCount} line(s)${which}`;\n }\n}\n"]}
@@ -185,8 +185,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
185
185
  // applicable set for free โ€” an unchanged checklist needs no re-review, a newly-applicable one refuses
186
186
  // until its file is written.
187
187
  const featureName = this.aiBranchName.getFeatureName();
188
- const prGate = (0, rules_config_1.loadAndValidate)(repoRoot).prGate;
189
- const scan = this.checklistScanner.scan(repoRoot, prGate.checklists, prGate.gateSalt, new checklist_scanner_1.ChecklistScanOptions(true, 'stage3-finish'));
188
+ const scan = this.checklistScanner.scan(repoRoot, (0, rules_config_1.loadAndValidate)(repoRoot).prGate.checklists, new checklist_scanner_1.ChecklistScanOptions(true, 'stage3-finish'));
190
189
  const required = scan.applicable;
191
190
  // The applicable checklists that are supposed to HAVE a verdict โ€” everything except the optional ones
192
191
  // nobody ran. Used for provenance and for the dashboard rows, both of which ask "who reviewed this?"
@@ -199,12 +198,12 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
199
198
  // nobody should wait on a build to be told a reviewer never ran. ReviewerVerdictGate owns the
200
199
  // distinction between unreadable / REFUSED / never-ran, and retires the red verdicts it acts on.
201
200
  this.verdictGate.assertEveryReviewerRan(scan);
202
- const review = this.reviewJsonService.loadReviewJson((0, rules_config_1.reviewJsonPath)(repoRoot, featureName), required, scan.authorized);
201
+ const review = this.reviewJsonService.loadReviewJson((0, rules_config_1.reviewJsonPath)(repoRoot, featureName), required);
203
202
  // 2c. For any BLOCK checklist that names a reviewer `subagent`, VERIFY (from the harness's own
204
203
  // artifacts) that such a subagent actually ran on this branch โ€” the coding agent may not
205
204
  // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).
206
205
  const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
207
- const provenance = this.provenanceEnforcer.enforce(verdicted, currentBranch, repoRoot, prGate);
206
+ const provenance = this.provenanceEnforcer.enforce(verdicted, currentBranch, repoRoot, (0, rules_config_1.loadAndValidate)(repoRoot).prGate);
208
207
  // 2b. The build gate validates the WORKING TREE but we push HEAD โ€” so they MUST be identical.
209
208
  this.gitExec.assertCleanTree(repoRoot);
210
209
  // 3. Build gate, then post the gated body, then push (that ORDER โ€” see GatedPrPublisher).
@@ -212,7 +211,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
212
211
  const base = this.branchNaming.baseBranchName((0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim());
213
212
  process.stdout.write('\n' + SEP + '๐Ÿ“‹ Dashboard + PR\n' + SEP + '\n');
214
213
  const title = this.prTitleFrom(review);
215
- const input = this.computeDashboardInput(repoRoot, true, review, title, verdicted, scan.authorized);
214
+ const input = this.computeDashboardInput(repoRoot, true, review, title, verdicted);
216
215
  const result = this.publishAll(repoRoot, base, input, new PrCommentSources(scan, review, provenance));
217
216
  this.archiveConsumedReview(repoRoot, featureName, result);
218
217
  // The closing recap + the clickable-link directive, BOTH derived from the real merge outcome.
@@ -365,7 +364,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
365
364
  return this.aiBranchName.getFeatureName().replace(/[-/]+/g, ' ').trim();
366
365
  }
367
366
  // eslint-disable-next-line @typescript-eslint/max-params
368
- computeDashboardInput(repoRoot, buildPassed, review, title, required, authorized) {
367
+ computeDashboardInput(repoRoot, buildPassed, review, title, required) {
369
368
  const config = (0, rules_config_1.loadAndValidate)(repoRoot).prGate;
370
369
  const forkPoint = this.gitOut(['merge-base', 'origin/main', 'HEAD']);
371
370
  const featureHead = this.gitOut(['rev-parse', 'HEAD']);
@@ -375,7 +374,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
375
374
  const patch = this.gitOut(['diff', range]);
376
375
  const gateResults = this.dashboard.computeGateResults(config.gates, changedFiles);
377
376
  const disables = this.dashboard.countAddedDisables(patch);
378
- const rows = this.checklistRows(required, review, authorized);
377
+ const rows = this.checklistRows(required, review);
379
378
  // buildCommand travels into the dashboard so the PR-body footer can NAME the command that vouched
380
379
  // for this commit. The footer used to assert "build ran via nx affected" on every repo, which was
381
380
  // simply false wherever buildCommand is not nx.
@@ -401,9 +400,9 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
401
400
  // it re-validates the same set and finds it clean. This comment used to credit loadReviewJson, which made
402
401
  // the ordering look deliberate while the gate's generic "no verdict yet" message masked every refusal.
403
402
  // WARN belongs in that list: yellow SHIPS, so it is not outstanding and reaches the dashboard.
404
- checklistRows(required, review, authorized) {
403
+ checklistRows(required, review) {
405
404
  return required.map((req) => {
406
- const verdict = this.reviewJsonService.resolveVerdict(req, review.results, authorized);
405
+ const verdict = this.reviewJsonService.resolveVerdict(req, review.results);
407
406
  return new dashboard_1.ChecklistRow(req.id, verdict.status, verdict.detail);
408
407
  });
409
408
  }
@@ -424,7 +423,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
424
423
  // A skipped checklist has no verdict to resolve โ€” asking for one would report it as MISSING,
425
424
  // i.e. as an unreviewed obligation, when in fact it never had one.
426
425
  const verdict = ran
427
- ? this.reviewJsonService.resolveVerdict(req, review.results, scan.authorized)
426
+ ? this.reviewJsonService.resolveVerdict(req, review.results)
428
427
  : new rules_config_1.ChecklistVerdict(entry.def.id, '', '');
429
428
  const row = new checklist_comment_row_1.ChecklistCommentRow(entry.def.subagent, verdict.status, verdict.detail, ran, entry.def.patterns, entry.matchedPatterns, entry.matchedFiles, scan.roster.changedFileCount);
430
429
  row.required = entry.def.required;