@webpieces/pr-gate 0.4.475 β†’ 0.4.477

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/pr-gate",
3
- "version": "0.4.475",
3
+ "version": "0.4.477",
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",
@@ -24,7 +24,7 @@
24
24
  "directory": "packages/tooling/pr-gate"
25
25
  },
26
26
  "dependencies": {
27
- "@webpieces/rules-config": "0.4.475",
27
+ "@webpieces/rules-config": "0.4.477",
28
28
  "@inversifyjs/binding-decorators": "1.1.5",
29
29
  "inversify": "7.10.4",
30
30
  "reflect-metadata": "0.2.2"
@@ -1,4 +1,5 @@
1
1
  import { GateDefinition, ReviewJson } from '@webpieces/rules-config';
2
+ export declare const CHECKLIST_COMMENT_MARKER = "<!-- webpieces-checklists v1 -->";
2
3
  export declare class GateResult {
3
4
  name: string;
4
5
  warningColor: string;
@@ -34,6 +35,10 @@ export declare class Dashboard {
34
35
  computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[];
35
36
  countAddedDisables(patch: string): DisableCounts;
36
37
  renderDashboard(input: DashboardInput): string;
38
+ renderChecklistComment(rows: readonly ChecklistRow[], provenanceVerified: boolean): string;
39
+ private commentSection;
40
+ private fitComment;
41
+ private longestBodyIndex;
37
42
  renderCommitBody(input: DashboardInput, prUrl: string): string;
38
43
  private nonGreenFlags;
39
44
  private checklistStatusText;
@@ -1,9 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Dashboard = exports.DashboardInput = exports.DisableCounts = exports.ChecklistRow = exports.GateResult = void 0;
3
+ exports.Dashboard = exports.DashboardInput = exports.DisableCounts = exports.ChecklistRow = exports.GateResult = exports.CHECKLIST_COMMENT_MARKER = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const rules_config_1 = require("@webpieces/rules-config");
6
6
  const inversify_1 = require("inversify");
7
+ // Hidden marker on the checklist review COMMENT, so wp-finish can find + PATCH its own comment on every
8
+ // push instead of appending a new one. Versioned so the format can evolve without matching an old shape.
9
+ exports.CHECKLIST_COMMENT_MARKER = '<!-- webpieces-checklists v1 -->';
10
+ const COMMENT_LIMIT = 65000; // under GitHub's 65536-char cap, with headroom for the marker + roll-up.
11
+ // One checklist section for the combined comment (heading + verbatim reviewer output), so oversize
12
+ // truncation can shrink the longest BODY without ever dropping a verdict heading.
13
+ class CommentSection {
14
+ heading;
15
+ body;
16
+ constructor(heading, body) {
17
+ this.heading = heading;
18
+ this.body = body;
19
+ }
20
+ }
7
21
  class GateResult {
8
22
  name;
9
23
  warningColor; // 'yellow' | 'red' β€” the color shown WHEN files matched (green is implicit)
@@ -130,6 +144,58 @@ let Dashboard = class Dashboard {
130
144
  lines.push('<sub>πŸ€– Generated by `pnpm wp-finish-upsert-pr` (build ran via nx affected β€” not self-attested).</sub>');
131
145
  return lines.join('\n');
132
146
  }
147
+ // The ONE combined PR comment carrying every reviewer's full `output` β€” the depth the body-line
148
+ // verdict throws away. Overridden sections first (the only non-green verdicts reaching a PR β€” a
149
+ // hard FAIL refuses the PR before this runs), then passing reviews. Idempotent: keyed by the hidden
150
+ // marker so wp-finish PATCHes this same comment on every push.
151
+ renderChecklistComment(rows, provenanceVerified) {
152
+ const overridden = rows.filter((r) => r.status === rules_config_1.CK_OVERRIDDEN);
153
+ const passed = rows.filter((r) => r.status !== rules_config_1.CK_OVERRIDDEN);
154
+ const roll = overridden.length > 0
155
+ ? `## πŸ” Company review checklists β€” ${rows.length} reviewed, 🟑 ${overridden.length} overridden`
156
+ : `## πŸ” Company review checklists β€” 🟒 ${rows.length} reviewed, all passed`;
157
+ const prov = provenanceVerified
158
+ ? '_Each reviewer ran as its own independent subagent, verified from the Claude Code harness._'
159
+ : '_⚠️ Reviewer provenance was NOT verified (no Claude Code session) β€” treat these as unverified._';
160
+ const header = `${exports.CHECKLIST_COMMENT_MARKER}\n${roll}\n${prov}`;
161
+ const sections = [...overridden, ...passed].map((r) => this.commentSection(r));
162
+ return this.fitComment(header, sections);
163
+ }
164
+ commentSection(row) {
165
+ const heading = row.status === rules_config_1.CK_OVERRIDDEN
166
+ ? `### 🟑 ${row.title} β€” OVERRIDDEN`
167
+ : `### 🟒 ${row.title} β€” passed`;
168
+ const body = row.detail.trim() !== '' ? row.detail.trim() : '_(reviewer recorded no output)_';
169
+ return new CommentSection(heading, body);
170
+ }
171
+ // Keep the comment under GitHub's size cap by shrinking the LONGEST section body first (so a short
172
+ // overridden note is never cut to make room for a long passing one), never dropping a verdict heading.
173
+ fitComment(header, sections) {
174
+ const assemble = () => `${header}\n\n${sections.map((s) => `${s.heading}\n\n${s.body}`).join('\n\n')}`;
175
+ const trunc = '\n\n…_[truncated to fit the GitHub comment size limit]_';
176
+ let out = assemble();
177
+ while (out.length > COMMENT_LIMIT) {
178
+ const idx = this.longestBodyIndex(sections);
179
+ if (idx < 0 || sections[idx].body.length <= trunc.length + 1)
180
+ break;
181
+ const over = out.length - COMMENT_LIMIT;
182
+ const keep = Math.max(0, sections[idx].body.length - over - trunc.length - 8);
183
+ sections[idx].body = sections[idx].body.slice(0, keep).trimEnd() + trunc;
184
+ out = assemble();
185
+ }
186
+ return out;
187
+ }
188
+ longestBodyIndex(sections) {
189
+ let idx = -1;
190
+ let max = -1;
191
+ sections.forEach((s, i) => {
192
+ if (s.body.length > max) {
193
+ max = s.body.length;
194
+ idx = i;
195
+ }
196
+ });
197
+ return idx;
198
+ }
133
199
  // The squash-merge COMMIT body that lands in main's history (subject is the PR title, passed to
134
200
  // `gh pr merge --subject`). Deliberately compact β€” unlike the full PR-body dashboard: the risk score
135
201
  // (always), every NON-green flag (green rows omitted β€” a commit log should surface only what stands
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":";;;;AAAA,0DAGiC;AACjC,yCAA2D;AAE3D,MAAa,UAAU;IACnB,IAAI,CAAS;IACb,YAAY,CAAS,CAAC,4EAA4E;IAClG,YAAY,CAAW;IAEvB,YAAY,IAAY,EAAE,YAAoB,EAAE,YAAsB;QAClE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;CACJ;AAVD,gCAUC;AAED,yGAAyG;AACzG,yGAAyG;AACzG,0GAA0G;AAC1G,uGAAuG;AACvG,yGAAyG;AACzG,qEAAqE;AACrE,MAAa,YAAY;IACrB,KAAK,CAAS,CAAE,8CAA8C;IAC9D,MAAM,CAAS,CAAC,eAAe;IAC/B,MAAM,CAAS,CAAC,qEAAqE;IAErF,YAAY,KAAa,EAAE,MAAc,EAAE,MAAM,GAAG,EAAE;QAClD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;CACJ;AAVD,oCAUC;AAED,MAAa,aAAa;IACtB,cAAc,CAAS;IACvB,WAAW,CAAS;IACpB,cAAc,CAAW;IAEzB,YAAY,cAAsB,EAAE,WAAmB,EAAE,cAAwB;QAC7E,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACzC,CAAC;CACJ;AAVD,sCAUC;AAED,MAAa,cAAc;IACvB,KAAK,CAAS;IACd,WAAW,CAAe;IAC1B,QAAQ,CAAgB;IACxB,WAAW,CAAU;IACrB,SAAS,CAAS;IAClB,WAAW,CAAS;IACpB,QAAQ,CAAS;IACjB,MAAM,CAAa,CAAC,yDAAyD;IAC7E,UAAU,CAAiB,CAAC,uEAAuE;IAEnG,yDAAyD;IACzD,YACI,KAAa,EAAE,WAAyB,EAAE,QAAuB,EACjE,WAAoB,EAAE,SAAiB,EAAE,WAAmB,EAAE,QAAgB,EAAE,MAAkB,EAClG,aAA6B,EAAE;QAE/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC;CACJ;AA3BD,wCA2BC;AAED,sGAAsG;AAE/F,IAAM,SAAS,GAAf,MAAM,SAAS;IAClB,mFAAmF;IACnF,kBAAkB,CAAC,KAAuB,EAAE,YAAsB;QAC9D,OAAO,KAAK;aACP,MAAM,CAAC,CAAC,IAAoB,EAAW,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;aACzD,GAAG,CAAC,CAAC,IAAoB,EAAc,EAAE;YACtC,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;YACrG,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACX,CAAC;IAED,+FAA+F;IAC/F,0FAA0F;IAC1F,kBAAkB,CAAC,KAAa;QAC5B,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,yBAAU,CAAC,CAAC,GAAG,CAAC,CAAC,GAAW,EAAU,EAAE,CAAE,yBAAqC,CAAC,GAAG,CAAC,CAAC,CAAC;QAExH,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC9D,IAAI,IAAI,CAAC,QAAQ,CAAC,gCAAiB,CAAC,EAAE,CAAC;gBACnC,cAAc,IAAI,CAAC,CAAC;gBACpB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;oBAChC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;wBAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC/C,CAAC;YACL,CAAC;YACD,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBAAE,WAAW,IAAI,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,aAAa,CAAC,cAAc,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,eAAe,CAAC,KAAqB;QACjC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,4BAA4B,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACxF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,WAAW,UAAU,CAAC;QAC5G,KAAK,CAAC,IAAI,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;QACxD,gGAAgG;QAChG,kCAAkC;QAClC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;QACzC,KAAK,CAAC,IAAI,CAAC,uBAAuB,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,yBAAyB,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,sBAAsB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,wGAAwG,CAAC,CAAC;QACrH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,gGAAgG;IAChG,qGAAqG;IACrG,oGAAoG;IACpG,mGAAmG;IACnG,gBAAgB,CAAC,KAAqB,EAAE,KAAa;QACjD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,QAAQ,KAAK,CAAC,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;QAChJ,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACjC,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QACpE,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,+FAA+F;IAC/F,0FAA0F;IAClF,aAAa,CAAC,KAAqB;QACvC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACrE,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,0BAA0B,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,eAAe,CAAC,CAAC;QAC5H,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,UAAU,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/G,KAAK,CAAC,IAAI,CAAC,gCAAgC,KAAK,CAAC,QAAQ,CAAC,cAAc,WAAW,KAAK,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,6BAA6B,KAAK,CAAC,QAAQ,CAAC,WAAW,UAAU,CAAC,CAAC;QAClH,2FAA2F;QAC3F,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,+FAA+F;IACvF,mBAAmB,CAAC,GAAiB;QACzC,IAAI,GAAG,CAAC,MAAM,KAAK,4BAAa,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,gBAAgB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChF,OAAO,gBAAgB,GAAG,EAAE,CAAC;QACjC,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAO;YAAE,OAAO,kBAAkB,CAAC;QACtD,IAAI,GAAG,CAAC,MAAM,KAAK,yBAAU;YAAE,OAAO,gBAAgB,CAAC;QACvD,OAAO,WAAW,CAAC,CAAC,UAAU;IAClC,CAAC;IAED,kGAAkG;IAClG,oGAAoG;IACpG,oGAAoG;IACpG,mGAAmG;IACnG,oFAAoF;IAC5E,cAAc,CAAC,IAAY,EAAE,GAAW;QAC5C,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7D,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,MAAM,YAAY,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,CAAC;YAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,YAAY,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC1D,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBAChD,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;QACL,CAAC;QACD,+EAA+E;QAC/E,IAAI,SAAS,CAAC,MAAM,GAAG,GAAG,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,IAAI,KAAK,EAAE;gBAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACtC,CAAC;IAED,yFAAyF;IACjF,WAAW,CAAC,OAAe;QAC/B,IAAI,EAAE,GAAG,EAAE,CAAC;QACZ,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,EAAE,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACvC,EAAE,IAAI,IAAI,CAAC;gBACX,CAAC,IAAI,CAAC,CAAC;gBACP,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;oBAAE,CAAC,IAAI,CAAC,CAAC;gBAC/B,SAAS;YACb,CAAC;YACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBAAC,EAAE,IAAI,OAAO,CAAC;gBAAC,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACpD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBAAC,EAAE,IAAI,MAAM,CAAC;gBAAC,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACnD,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBAAC,EAAE,IAAI,IAAI,GAAG,EAAE,CAAC;gBAAC,CAAC,IAAI,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACxE,EAAE,IAAI,EAAE,CAAC;YACT,CAAC,IAAI,CAAC,CAAC;QACX,CAAC;QACD,OAAO,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IACtC,CAAC;IAEO,UAAU,CAAC,QAAkB,EAAE,IAAY;QAC/C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1D,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEO,QAAQ,CAAC,MAAkB;QAC/B,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC;QACzE,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1D,OAAO,KAAK,MAAM,CAAC,IAAI,OAAO,KAAK,SAAS,MAAM,CAAC,YAAY,CAAC,MAAM,WAAW,CAAC;IACtF,CAAC;IAED,+FAA+F;IACvF,aAAa,CAAC,GAAiB;QACnC,OAAO,iBAAiB,GAAG,CAAC,KAAK,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5E,CAAC;IAED,+FAA+F;IACvF,OAAO,CAAC,KAAa;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACvF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;IACzD,CAAC;IAED,8EAA8E;IACtE,SAAS,CAAC,MAAkB;QAChC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC5C,MAAM,aAAa,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,UAAU,gBAAgB,CAAC;QACzF,OAAO;YACH,mBAAmB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,MAAM,CAAC,SAAS,UAAU,MAAM,CAAC,SAAS,EAAE;YACnG,mBAAmB,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,SAAS,IAAI;YAC7D,2BAA2B,aAAa,EAAE;SAC7C,CAAC;IACN,CAAC;IAEO,WAAW,CAAC,QAAuB;QACvC,IAAI,QAAQ,CAAC,cAAc,KAAK,CAAC;YAAE,OAAO,qCAAqC,CAAC;QAChF,MAAM,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnG,OAAO,oCAAoC,QAAQ,CAAC,cAAc,WAAW,KAAK,EAAE,CAAC;IACzF,CAAC;CACJ,CAAA;AAlNY,8BAAS;oBAAT,SAAS;IADrB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,SAAS,CAkNrB","sourcesContent":["import {\n GateDefinition, WEBPIECES_DISABLE, RULE_NAMES, ReviewJson,\n CK_OVERRIDDEN, CK_FAIL, CK_MISSING,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\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_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_ACKED); `detail` is the reviewer output / override\n// justification. A BLOCK row is always PASS/OVERRIDDEN/ACKED by the time it renders β€” a failed or missing\n// BLOCK throws before the dashboard is built; a WARN row may render in any state. Rendered into the PR\n// body so the verdict reaches the server β€” the PR body is the artifact of the local flow that leaves the\n// checkout (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\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 // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string, gateResults: GateResult[], disables: DisableCounts,\n buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, review: ReviewJson,\n checklists: ChecklistRow[] = [],\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 }\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 => this.matchesAny(gate.patterns, file));\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((key: string): string => (RULE_NAMES as Record<string, string>)[key]);\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 renderDashboard(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 = input.disables.eslintCount === 0 ? '🟒 No' : `🟑 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // One row per triggered consumer checklist β€” only when some fired, so non-adopting repos see no\n // change to the dashboard at all.\n for (const row of input.checklists) lines.push(this.checklistLine(row));\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>πŸ€– Generated by `pnpm wp-finish-upsert-pr` (build ran via nx affected β€” not self-attested).</sub>');\n return lines.join('\\n');\n }\n\n // The squash-merge COMMIT body that lands in main's history (subject is the PR title, passed to\n // `gh pr merge --subject`). Deliberately compact β€” unlike the full PR-body dashboard: the risk score\n // (always), every NON-green flag (green rows omitted β€” a commit log should surface only what stands\n // out), the summary capped at 4 sentences, and a quick link back to the PR for the full dashboard.\n renderCommitBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n lines.push(`Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`);\n lines.push('');\n const flags = this.nonGreenFlags(input);\n if (flags.length === 0) {\n lines.push('Flags: 🟒 all green');\n } else {\n lines.push('Flags (non-green):');\n for (const flag of flags) lines.push(`- ${flag}`);\n }\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n if (prUrl !== '') {\n lines.push('');\n lines.push(`PR: ${prUrl}`);\n }\n return lines.join('\\n');\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) 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 = input.disables.webpiecesRules.length > 0 ? ` β€” ${input.disables.webpiecesRules.join(', ')}` : '';\n flags.push(`Webpieces Disables Added: 🟑 ${input.disables.webpiecesCount} line(s)${which}`);\n }\n if (input.disables.eslintCount > 0) 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 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_FAIL) return 'πŸ”΄ FAILED review';\n if (row.status === CK_MISSING) return 'βšͺ not reviewed';\n return '🟒 passed'; // CK_PASS\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 === '*') { re += '[^/]*'; i += 1; continue; }\n if (ch === '?') { re += '[^/]'; i += 1; continue; }\n if ('.+^$(){}|[]\\\\'.includes(ch)) { re += '\\\\' + ch; i += 1; continue; }\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 // A triggered consumer checklist row: the resolved verdict (passed / overridden / failed / …).\n private checklistLine(row: ChecklistRow): string {\n return `**Checklist β€” ${row.title}:** ${this.checklistStatusText(row)}`;\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 = 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, WEBPIECES_DISABLE, RULE_NAMES, ReviewJson,\n CK_OVERRIDDEN, CK_FAIL, CK_MISSING,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n// Hidden marker on the checklist review COMMENT, so wp-finish can find + PATCH its own comment on every\n// push instead of appending a new one. Versioned so the format can evolve without matching an old shape.\nexport const CHECKLIST_COMMENT_MARKER = '<!-- webpieces-checklists v1 -->';\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\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_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_ACKED); `detail` is the reviewer output / override\n// justification. A BLOCK row is always PASS/OVERRIDDEN/ACKED by the time it renders β€” a failed or missing\n// BLOCK throws before the dashboard is built; a WARN row may render in any state. Rendered into the PR\n// body so the verdict reaches the server β€” the PR body is the artifact of the local flow that leaves the\n// checkout (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\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 // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string, gateResults: GateResult[], disables: DisableCounts,\n buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, review: ReviewJson,\n checklists: ChecklistRow[] = [],\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 }\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 => this.matchesAny(gate.patterns, file));\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((key: string): string => (RULE_NAMES as Record<string, string>)[key]);\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 renderDashboard(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 = input.disables.eslintCount === 0 ? '🟒 No' : `🟑 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // One row per triggered consumer checklist β€” only when some fired, so non-adopting repos see no\n // change to the dashboard at all.\n for (const row of input.checklists) lines.push(this.checklistLine(row));\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>πŸ€– Generated by `pnpm wp-finish-upsert-pr` (build ran via nx affected β€” not self-attested).</sub>');\n return lines.join('\\n');\n }\n\n // The ONE combined PR comment carrying every reviewer's full `output` β€” the depth the body-line\n // verdict throws away. Overridden sections first (the only non-green verdicts reaching a PR β€” a\n // hard FAIL refuses the PR before this runs), then passing reviews. Idempotent: keyed by the hidden\n // marker so wp-finish PATCHes this same comment on every push.\n renderChecklistComment(rows: readonly ChecklistRow[], provenanceVerified: boolean): string {\n const overridden = rows.filter((r: ChecklistRow): boolean => r.status === CK_OVERRIDDEN);\n const passed = rows.filter((r: ChecklistRow): boolean => r.status !== CK_OVERRIDDEN);\n const roll = overridden.length > 0\n ? `## πŸ” Company review checklists β€” ${rows.length} reviewed, 🟑 ${overridden.length} overridden`\n : `## πŸ” Company review checklists β€” 🟒 ${rows.length} reviewed, all passed`;\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 const header = `${CHECKLIST_COMMENT_MARKER}\\n${roll}\\n${prov}`;\n const sections = [...overridden, ...passed].map((r: ChecklistRow): CommentSection => this.commentSection(r));\n return this.fitComment(header, sections);\n }\n\n private commentSection(row: ChecklistRow): CommentSection {\n const heading = row.status === CK_OVERRIDDEN\n ? `### 🟑 ${row.title} β€” OVERRIDDEN`\n : `### 🟒 ${row.title} β€” passed`;\n const body = 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 => `${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) { max = s.body.length; idx = i; }\n });\n return idx;\n }\n\n // The squash-merge COMMIT body that lands in main's history (subject is the PR title, passed to\n // `gh pr merge --subject`). Deliberately compact β€” unlike the full PR-body dashboard: the risk score\n // (always), every NON-green flag (green rows omitted β€” a commit log should surface only what stands\n // out), the summary capped at 4 sentences, and a quick link back to the PR for the full dashboard.\n renderCommitBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n lines.push(`Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`);\n lines.push('');\n const flags = this.nonGreenFlags(input);\n if (flags.length === 0) {\n lines.push('Flags: 🟒 all green');\n } else {\n lines.push('Flags (non-green):');\n for (const flag of flags) lines.push(`- ${flag}`);\n }\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n if (prUrl !== '') {\n lines.push('');\n lines.push(`PR: ${prUrl}`);\n }\n return lines.join('\\n');\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) 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 = input.disables.webpiecesRules.length > 0 ? ` β€” ${input.disables.webpiecesRules.join(', ')}` : '';\n flags.push(`Webpieces Disables Added: 🟑 ${input.disables.webpiecesCount} line(s)${which}`);\n }\n if (input.disables.eslintCount > 0) 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 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_FAIL) return 'πŸ”΄ FAILED review';\n if (row.status === CK_MISSING) return 'βšͺ not reviewed';\n return '🟒 passed'; // CK_PASS\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 === '*') { re += '[^/]*'; i += 1; continue; }\n if (ch === '?') { re += '[^/]'; i += 1; continue; }\n if ('.+^$(){}|[]\\\\'.includes(ch)) { re += '\\\\' + ch; i += 1; continue; }\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 // A triggered consumer checklist row: the resolved verdict (passed / overridden / failed / …).\n private checklistLine(row: ChecklistRow): string {\n return `**Checklist β€” ${row.title}:** ${this.checklistStatusText(row)}`;\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 = disables.webpiecesRules.length > 0 ? ` β€” ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** 🟑 ${disables.webpiecesCount} line(s)${which}`;\n }\n}\n"]}
@@ -32,6 +32,8 @@ export declare class FinishUpsertPrCommand {
32
32
  private gateTokenBody;
33
33
  private postGateStatus;
34
34
  private enforceProvenance;
35
+ private postChecklistComment;
36
+ private findChecklistCommentId;
35
37
  private upsertPr;
36
38
  private prRef;
37
39
  }
@@ -97,7 +97,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
97
97
  // artifacts) that such a subagent actually ran on this branch β€” the coding agent may not
98
98
  // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).
99
99
  const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
100
- this.enforceProvenance(required, currentBranch);
100
+ const provenanceVerified = this.enforceProvenance(required, currentBranch);
101
101
  // 2b. The build gate validates the WORKING TREE but we push HEAD β€” so they MUST be identical.
102
102
  this.gitExec.assertCleanTree(repoRoot);
103
103
  // 3. Authoritative build gate, then push, then post.
@@ -116,6 +116,9 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
116
116
  const result = this.upsertPr(repoRoot, base, body, title, input);
117
117
  // Race-free required check: post the commit status on the head sha AFTER the body edit (see method).
118
118
  this.postGateStatus(headSha, gateSalt);
119
+ // Publish each reviewer's full output as ONE combined PR comment (idempotent, opt-out-aware). Never
120
+ // fatal β€” the PR is already up by now, so a comment failure only warns.
121
+ this.postChecklistComment(repoRoot, result.prNumber, input.checklists, provenanceVerified);
119
122
  const prNum = result.prNumber;
120
123
  process.stdout.write('\n' + SEP + 'βœ… PR finished β€” here is exactly what I did\n' + SEP + '\n' +
121
124
  ` 1. validated the build gate (authoritative)\n` +
@@ -189,9 +192,11 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
189
192
  // passes silently; no session id warns but passes; any missing reviewer throws so the PR does not open.
190
193
  enforceProvenance(required, branch) {
191
194
  const errors = [];
195
+ let verified = true; // no reviewers to verify β‡’ vacuously true
192
196
  const subagents = required.map((r) => r.subagent.trim()).filter((s) => s !== '');
193
197
  if (subagents.length > 0) {
194
198
  const result = this.provenance.verifyDistinct(subagents, branch);
199
+ verified = result.status === rules_config_1.PROVENANCE_OK;
195
200
  if (result.status === rules_config_1.PROVENANCE_MISSING) {
196
201
  errors.push(result.detail);
197
202
  }
@@ -204,6 +209,42 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
204
209
  errors.map((e) => ` β€’ ${e}`).join('\n') +
205
210
  `\n\nSpawn the named reviewer subagent to review the checklist on THIS branch, then re-run.`);
206
211
  }
212
+ return verified;
213
+ }
214
+ // Publish every reviewer's full `output` as ONE combined PR comment, idempotently (find the marker
215
+ // comment β†’ PATCH it, else POST). No-op when there is no PR number or no matched checklists. Never
216
+ // fatal: by here the PR is already created/updated, so a `gh` failure only warns.
217
+ postChecklistComment(repoRoot, prNumber, rows, provenanceVerified) {
218
+ if (prNumber === '' || rows.length === 0)
219
+ return;
220
+ if (!(0, rules_config_1.loadAndValidate)(repoRoot).prGate.checklistComments)
221
+ return;
222
+ const body = this.dashboard.renderChecklistComment(rows, provenanceVerified);
223
+ const prDir = (0, rules_config_1.prDirFor)(repoRoot, this.aiBranchName.getFeatureName());
224
+ fs.mkdirSync(prDir, { recursive: true });
225
+ const payload = path.join(prDir, 'checklist-comment.json');
226
+ fs.writeFileSync(payload, JSON.stringify({ body }));
227
+ const commentId = this.findChecklistCommentId(prNumber);
228
+ const args = commentId !== ''
229
+ ? ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${commentId}`, '--input', payload]
230
+ : ['api', '--method', 'POST', `repos/{owner}/{repo}/issues/${prNumber}/comments`, '--input', payload];
231
+ const res = (0, child_process_1.spawnSync)('gh', args, { encoding: 'utf8' });
232
+ if (res.status !== 0) {
233
+ process.stderr.write('⚠️ Could not post the checklist review comment (non-fatal β€” the PR is already up).\n');
234
+ }
235
+ else {
236
+ process.stdout.write(` ${commentId !== '' ? 'updated' : 'posted'} the checklist review comment βœ“\n`);
237
+ }
238
+ }
239
+ // The id of THIS tool's existing checklist comment on the PR (by the hidden marker), or '' if none.
240
+ findChecklistCommentId(prNumber) {
241
+ const res = (0, child_process_1.spawnSync)('gh', [
242
+ 'api', '--paginate', `repos/{owner}/{repo}/issues/${prNumber}/comments`,
243
+ '--jq', `.[] | select(.body | contains("${dashboard_1.CHECKLIST_COMMENT_MARKER}")) | .id`,
244
+ ], { encoding: 'utf8' });
245
+ if (res.status !== 0)
246
+ return '';
247
+ return (res.stdout ?? '').trim().split('\n')[0] ?? '';
207
248
  }
208
249
  // The PR, the remote branch, and the local branch all share the one stable feature name. Look up /
209
250
  // create / merge against `baseBranch` (baseBranchName tolerates a leftover `…wpN` mid-transition).
@@ -1 +1 @@
1
- {"version":3,"file":"finish-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/finish-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, prDirFor, reviewJsonPath, ReviewJson, RequiredChecklist,\n writeTemplate, RepoRootFinder, ReviewJsonService, ChecklistManifestService,\n GateTokenService, SubagentProvenanceService, PROVENANCE_MISSING, PROVENANCE_SKIPPED,\n InformAiError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistDetector } from '../workflow/checklist-detector';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { MergeState } from '../workflow/merge-state';\nimport { MergeEnd } from '../workflow/merge-end';\nimport { MergeContext } from '../workflow/merge-start';\nimport { PrMerger, MergeOutcome } from '../workflow/pr-merger';\nimport { Dashboard, DashboardInput, ChecklistRow } from '../../dashboard/dashboard';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// A resolved PR's number + web URL. Both '' when the PR can't be resolved (e.g. create failed).\nclass PrRef {\n number: string;\n url: string;\n\n constructor(number: string, url: string) {\n this.number = number;\n this.url = url;\n }\n}\n\n// The outcome of the whole upsert: the PR number ('' when it could not be resolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success.\nclass UpsertResult {\n prNumber: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.merge = merge;\n }\n}\n\n// FINISH of the AI-first PR flow. Runs after the AI wrote review.json. In order: (1) if a 3-point merge\n// was in progress, validate + commit + FINALIZE via merge-END; (2) REQUIRE review.json; (3) run the\n// authoritative build gate; (4) render the dashboard; (5) create/update the PR via `gh`. The ONLY\n// command that posts PRs.\n@injectable(bindingScopeValues.Singleton)\nexport class FinishUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly mergeState: MergeState,\n private readonly mergeEnd: MergeEnd,\n private readonly prMerger: PrMerger,\n private readonly dashboard: Dashboard,\n private readonly checklistDetector: ChecklistDetector,\n private readonly manifestService: ChecklistManifestService,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n private readonly provenance: SubagentProvenanceService,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n\n // 1. Finish any in-progress conflict resolution: validate + commit + finalize the branch swap.\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (activeDir && marker && !marker.validated) {\n await this.mergeEnd.mergeEnd(\n repoRoot, 'wp-finish-upsert-pr', activeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n marker.conflictedFiles,\n );\n }\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n // Compute the consumer checklists this diff triggered FIRST so an unacknowledged BLOCK throws\n // here β€” BEFORE any `gh pr create` β€” matching the guarantee buildCommand already provides.\n const defs = this.manifestService.load(repoRoot, loadAndValidate(repoRoot).prGate.checklistDoc);\n const required = this.checklistDetector.toRequired(this.checklistDetector.detectForRepo(repoRoot, defs));\n // review-<id>.json files persist locally between runs, so a re-run after a push re-validates the\n // EXISTING verdicts against the (possibly changed) triggered set for free: an unchanged checklist\n // needs no re-review, a newly-triggered one refuses until its file is written. That is the\n // \"full review only when the checklist surface changes\" behavior β€” no special-casing here.\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, this.aiBranchName.getFeatureName()), required);\n\n // 2c. For any BLOCK checklist that names a reviewer `subagent`, VERIFY (from the harness's own\n // artifacts) that such a subagent actually ran on this branch β€” the coding agent may not\n // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n this.enforceProvenance(required, currentBranch);\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD β€” so they MUST be identical.\n this.gitExec.assertCleanTree(repoRoot);\n\n // 3. Authoritative build gate, then push, then post.\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n 'πŸ› οΈ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed β€” no PR created/updated.',\n ));\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n this.gitExec.ensurePushed(base);\n\n process.stdout.write('\\n' + SEP + 'πŸ“‹ Dashboard + PR\\n' + SEP + '\\n');\n const title = this.prTitleFrom(review);\n const input = this.computeDashboardInput(repoRoot, true, review, title, required);\n // Append the hidden HMAC gate token bound to the pushed HEAD sha. A valid token in the PR body is\n // proof this gated flow ran + passed on this exact commit β€” CI (`wp-check-pr`) recomputes it. We\n // reach here only after the build gate + every BLOCK checklist passed, so minting is legitimate.\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const body = this.dashboard.renderDashboard(input) + this.gateTokenBody(gateSalt, headSha);\n const result = this.upsertPr(repoRoot, base, body, title, input);\n // Race-free required check: post the commit status on the head sha AFTER the body edit (see method).\n this.postGateStatus(headSha, gateSalt);\n const prNum = result.prNumber;\n\n process.stdout.write(\n '\\n' + SEP + 'βœ… PR finished β€” here is exactly what I did\\n' + SEP + '\\n' +\n ` 1. validated the build gate (authoritative)\\n` +\n ` 2. force-pushed your work to origin/${base}\\n` +\n ` 3. ${prNum ? `updated/created PR #${prNum}` : 'created the PR'} titled: \"${title}\"\\n` +\n ` 4. ${result.merge.message}\\n` +\n ` You are on ${base} β€” same name as the remote branch and the PR head.\\n\\n`,\n );\n }\n\n private gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n }\n\n // The user-facing PR title: the AI-authored review.title, or β€” if omitted β€” a readable fallback\n // derived from the stable feature name (NEVER the internal `Squash merge of <branch>` commit subject).\n private prTitleFrom(review: ReviewJson): string {\n if (review.title !== '') return review.title;\n return this.aiBranchName.getFeatureName().replace(/[-/]+/g, ' ').trim();\n }\n\n // eslint-disable-next-line @typescript-eslint/max-params\n private computeDashboardInput(repoRoot: string, buildPassed: boolean, review: ReviewJson, title: string, required: readonly RequiredChecklist[]): DashboardInput {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = this.gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = this.gitOut(['rev-parse', 'HEAD']);\n const mainHead = this.gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = this.gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = this.gitOut(['diff', range]);\n\n const gateResults = this.dashboard.computeGateResults(config.gates, changedFiles);\n const disables = this.dashboard.countAddedDisables(patch);\n const rows = this.checklistRows(required, review);\n return new DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows);\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard. (A checklist reaching this\n // point is always PASS/OVERRIDDEN β€” loadReviewJson already threw on FAIL/MISSING.)\n private checklistRows(required: readonly RequiredChecklist[], review: ReviewJson): ChecklistRow[] {\n return required.map((req: RequiredChecklist): ChecklistRow => {\n const verdict = this.reviewJsonService.resolveVerdict(req, review.results);\n return new ChecklistRow(req.id, verdict.status, verdict.detail);\n });\n }\n\n // Hidden HMAC gate-token marker (with a leading blank line) to append to the PR body, or '' when the\n // repo sets no gateSalt (byte-identical body to before this feature). Bound to the pushed HEAD sha.\n private gateTokenBody(gateSalt: string, headSha: string): string {\n const marker = this.gateTokenService.gateTokenMarker(gateSalt, headSha);\n return marker === '' ? '' : `\\n\\n${marker}\\n`;\n }\n\n // Post `webpieces/pr-gate = success` as a commit status on the head sha. This is the authoritative,\n // race-free required check: it is attached to the sha, so unlike the PR body it cannot be read before\n // it exists. No-op when the repo sets no gateSalt. A failure to post (missing statuses:write) is only\n // a warning β€” the CI wp-check-pr workflow still enforces the gate.\n private postGateStatus(headSha: string, gateSalt: string): void {\n if (gateSalt.trim() === '' || headSha === '') return;\n const res = spawnSync('gh', [\n 'api', '--method', 'POST', `repos/{owner}/{repo}/statuses/${headSha}`,\n '-f', 'state=success',\n '-f', 'context=webpieces/pr-gate',\n '-f', 'description=gated flow ran and passed',\n ], { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write(\n '⚠️ Could not post the webpieces/pr-gate commit status (needs a token with statuses:write). ' +\n 'The CI wp-check-pr workflow still enforces the gate.\\n',\n );\n } else {\n process.stdout.write(` posted webpieces/pr-gate βœ“ status on ${headSha.slice(0, 12)}\\n`);\n }\n }\n\n // Enforce that EACH matched checklist was reviewed by its OWN named subagent, as a DISTINCT run β€”\n // the coding agent may not self-certify, and one reviewer may not stand in for several. A verified set\n // passes silently; no session id warns but passes; any missing reviewer throws so the PR does not open.\n private enforceProvenance(required: readonly RequiredChecklist[], branch: string): void {\n const errors: string[] = [];\n const subagents = required.map((r: RequiredChecklist): string => r.subagent.trim()).filter((s: string): boolean => s !== '');\n if (subagents.length > 0) {\n const result = this.provenance.verifyDistinct(subagents, branch);\n if (result.status === PROVENANCE_MISSING) {\n errors.push(result.detail);\n } else if (result.status === PROVENANCE_SKIPPED) {\n process.stderr.write(`⚠️ ${result.detail}\\n`);\n }\n }\n if (errors.length > 0) {\n throw new InformAiError(\n `${errors.length} checklist(s) require an independent reviewer subagent that did not run β€” fix, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` β€’ ${e}`).join('\\n') +\n `\\n\\nSpawn the named reviewer subagent to review the checklist on THIS branch, then re-run.`,\n );\n }\n }\n\n // The PR, the remote branch, and the local branch all share the one stable feature name. Look up /\n // create / merge against `baseBranch` (baseBranchName tolerates a leftover `…wpN` mid-transition).\n private upsertPr(repoRoot: string, baseBranch: string, body: string, title: string, input: DashboardInput): UpsertResult {\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, body + '\\n');\n\n const prNumber = spawnSync(\n 'gh', ['pr', 'list', '--head', baseBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';\n\n if (num === '') {\n process.stdout.write('Creating PR...\\n');\n const create = spawnSync('gh', ['pr', 'create', '--head', baseBranch, '--base', 'main', '--title', title, '--body-file', bodyFile], { stdio: 'inherit' });\n if (create.status !== 0) {\n process.stderr.write('⚠️ gh pr create failed β€” create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return new UpsertResult('', new MergeOutcome(false, false,\n '⚠️ did NOT merge β€” there is no PR to merge (gh pr create failed above)'));\n }\n } else {\n process.stdout.write(`Updating PR #${num}...\\n`);\n const edit = spawnSync('gh', ['pr', 'edit', num, '--title', title, '--body-file', bodyFile], { stdio: 'inherit' });\n if (edit.status !== 0) {\n process.stderr.write(`⚠️ gh pr edit failed β€” PR #${num} still shows its OLD title/body. The new body is in:\\n ` + bodyFile + '\\n');\n }\n }\n\n // Set the squash-merge SUBJECT to the PR title (+ the `(#N)` GitHub normally appends, which an\n // explicit --subject would otherwise drop) and the BODY to the compact commit summary, so main's\n // history carries the PR title + risk/flags/link β€” NOT the internal `Squash merge of <branch>`\n // subject GitHub would inherit from the single squash commit on the branch.\n const ref = this.prRef(baseBranch);\n const subject = ref.number !== '' ? `${title} (#${ref.number})` : title;\n const mergeBodyFile = path.join(prDir, 'merge-commit-body.md');\n fs.writeFileSync(mergeBodyFile, this.dashboard.renderCommitBody(input, ref.url) + '\\n');\n // PrMerger owns the direct-merge / auto-merge-fallback decision AND checks every gh status, so a\n // merge that did not happen is reported as such instead of being swallowed (see pr-merger.ts).\n // REQUIRED config β€” no default here on purpose. A missing value (an older published\n // rules-config that has no such field) reaches PrMerger as '' and is treated as \"do not merge\".\n const mergeMode = loadAndValidate(repoRoot).prGate.mergeMode ?? '';\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, mergeMode);\n return new UpsertResult(ref.number !== '' ? ref.number : num, outcome);\n }\n\n // The PR's number + web URL (for the merge subject `(#N)` and the commit-body back-link). Both ''\n // if it can't be resolved. Rendered via jq into one tab-separated line so no JSON parsing is needed.\n private prRef(baseBranch: string): PrRef {\n const result = spawnSync(\n 'gh', ['pr', 'view', baseBranch, '--json', 'number,url', '--jq', '\"\\\\(.number)\\\\t\\\\(.url)\"'],\n { encoding: 'utf8' },\n );\n if (result.status !== 0) {\n return new PrRef('', '');\n }\n const parts = (result.stdout ?? '').trim().split('\\t');\n return new PrRef(parts[0] ?? '', parts[1] ?? '');\n }\n}\n"]}
1
+ {"version":3,"file":"finish-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/finish-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate, prDirFor, reviewJsonPath, ReviewJson, RequiredChecklist,\n writeTemplate, RepoRootFinder, ReviewJsonService, ChecklistManifestService,\n GateTokenService, SubagentProvenanceService, PROVENANCE_OK, PROVENANCE_MISSING, PROVENANCE_SKIPPED,\n InformAiError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistDetector } from '../workflow/checklist-detector';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { MergeState } from '../workflow/merge-state';\nimport { MergeEnd } from '../workflow/merge-end';\nimport { MergeContext } from '../workflow/merge-start';\nimport { PrMerger, MergeOutcome } from '../workflow/pr-merger';\nimport { Dashboard, DashboardInput, ChecklistRow, CHECKLIST_COMMENT_MARKER } from '../../dashboard/dashboard';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// A resolved PR's number + web URL. Both '' when the PR can't be resolved (e.g. create failed).\nclass PrRef {\n number: string;\n url: string;\n\n constructor(number: string, url: string) {\n this.number = number;\n this.url = url;\n }\n}\n\n// The outcome of the whole upsert: the PR number ('' when it could not be resolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success.\nclass UpsertResult {\n prNumber: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.merge = merge;\n }\n}\n\n// FINISH of the AI-first PR flow. Runs after the AI wrote review.json. In order: (1) if a 3-point merge\n// was in progress, validate + commit + FINALIZE via merge-END; (2) REQUIRE review.json; (3) run the\n// authoritative build gate; (4) render the dashboard; (5) create/update the PR via `gh`. The ONLY\n// command that posts PRs.\n@injectable(bindingScopeValues.Singleton)\nexport class FinishUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly gitExec: GitExec,\n private readonly buildAffected: BuildAffected,\n private readonly mergeState: MergeState,\n private readonly mergeEnd: MergeEnd,\n private readonly prMerger: PrMerger,\n private readonly dashboard: Dashboard,\n private readonly checklistDetector: ChecklistDetector,\n private readonly manifestService: ChecklistManifestService,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n private readonly provenance: SubagentProvenanceService,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n\n // 1. Finish any in-progress conflict resolution: validate + commit + finalize the branch swap.\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (activeDir && marker && !marker.validated) {\n await this.mergeEnd.mergeEnd(\n repoRoot, 'wp-finish-upsert-pr', activeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n marker.conflictedFiles,\n );\n }\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n // Compute the consumer checklists this diff triggered FIRST so an unacknowledged BLOCK throws\n // here β€” BEFORE any `gh pr create` β€” matching the guarantee buildCommand already provides.\n const defs = this.manifestService.load(repoRoot, loadAndValidate(repoRoot).prGate.checklistDoc);\n const required = this.checklistDetector.toRequired(this.checklistDetector.detectForRepo(repoRoot, defs));\n // review-<id>.json files persist locally between runs, so a re-run after a push re-validates the\n // EXISTING verdicts against the (possibly changed) triggered set for free: an unchanged checklist\n // needs no re-review, a newly-triggered one refuses until its file is written. That is the\n // \"full review only when the checklist surface changes\" behavior β€” no special-casing here.\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, this.aiBranchName.getFeatureName()), required);\n\n // 2c. For any BLOCK checklist that names a reviewer `subagent`, VERIFY (from the harness's own\n // artifacts) that such a subagent actually ran on this branch β€” the coding agent may not\n // self-certify. Absent CLAUDE_CODE_SESSION_ID this skips with a warning (CI / plain terminal).\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const provenanceVerified = this.enforceProvenance(required, currentBranch);\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD β€” so they MUST be identical.\n this.gitExec.assertCleanTree(repoRoot);\n\n // 3. Authoritative build gate, then push, then post.\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n 'πŸ› οΈ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed β€” no PR created/updated.',\n ));\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n this.gitExec.ensurePushed(base);\n\n process.stdout.write('\\n' + SEP + 'πŸ“‹ Dashboard + PR\\n' + SEP + '\\n');\n const title = this.prTitleFrom(review);\n const input = this.computeDashboardInput(repoRoot, true, review, title, required);\n // Append the hidden HMAC gate token bound to the pushed HEAD sha. A valid token in the PR body is\n // proof this gated flow ran + passed on this exact commit β€” CI (`wp-check-pr`) recomputes it. We\n // reach here only after the build gate + every BLOCK checklist passed, so minting is legitimate.\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const body = this.dashboard.renderDashboard(input) + this.gateTokenBody(gateSalt, headSha);\n const result = this.upsertPr(repoRoot, base, body, title, input);\n // Race-free required check: post the commit status on the head sha AFTER the body edit (see method).\n this.postGateStatus(headSha, gateSalt);\n // Publish each reviewer's full output as ONE combined PR comment (idempotent, opt-out-aware). Never\n // fatal β€” the PR is already up by now, so a comment failure only warns.\n this.postChecklistComment(repoRoot, result.prNumber, input.checklists, provenanceVerified);\n const prNum = result.prNumber;\n\n process.stdout.write(\n '\\n' + SEP + 'βœ… PR finished β€” here is exactly what I did\\n' + SEP + '\\n' +\n ` 1. validated the build gate (authoritative)\\n` +\n ` 2. force-pushed your work to origin/${base}\\n` +\n ` 3. ${prNum ? `updated/created PR #${prNum}` : 'created the PR'} titled: \"${title}\"\\n` +\n ` 4. ${result.merge.message}\\n` +\n ` You are on ${base} β€” same name as the remote branch and the PR head.\\n\\n`,\n );\n }\n\n private gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n }\n\n // The user-facing PR title: the AI-authored review.title, or β€” if omitted β€” a readable fallback\n // derived from the stable feature name (NEVER the internal `Squash merge of <branch>` commit subject).\n private prTitleFrom(review: ReviewJson): string {\n if (review.title !== '') return review.title;\n return this.aiBranchName.getFeatureName().replace(/[-/]+/g, ' ').trim();\n }\n\n // eslint-disable-next-line @typescript-eslint/max-params\n private computeDashboardInput(repoRoot: string, buildPassed: boolean, review: ReviewJson, title: string, required: readonly RequiredChecklist[]): DashboardInput {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = this.gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = this.gitOut(['rev-parse', 'HEAD']);\n const mainHead = this.gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = this.gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = this.gitOut(['diff', range]);\n\n const gateResults = this.dashboard.computeGateResults(config.gates, changedFiles);\n const disables = this.dashboard.countAddedDisables(patch);\n const rows = this.checklistRows(required, review);\n return new DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows);\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard. (A checklist reaching this\n // point is always PASS/OVERRIDDEN β€” loadReviewJson already threw on FAIL/MISSING.)\n private checklistRows(required: readonly RequiredChecklist[], review: ReviewJson): ChecklistRow[] {\n return required.map((req: RequiredChecklist): ChecklistRow => {\n const verdict = this.reviewJsonService.resolveVerdict(req, review.results);\n return new ChecklistRow(req.id, verdict.status, verdict.detail);\n });\n }\n\n // Hidden HMAC gate-token marker (with a leading blank line) to append to the PR body, or '' when the\n // repo sets no gateSalt (byte-identical body to before this feature). Bound to the pushed HEAD sha.\n private gateTokenBody(gateSalt: string, headSha: string): string {\n const marker = this.gateTokenService.gateTokenMarker(gateSalt, headSha);\n return marker === '' ? '' : `\\n\\n${marker}\\n`;\n }\n\n // Post `webpieces/pr-gate = success` as a commit status on the head sha. This is the authoritative,\n // race-free required check: it is attached to the sha, so unlike the PR body it cannot be read before\n // it exists. No-op when the repo sets no gateSalt. A failure to post (missing statuses:write) is only\n // a warning β€” the CI wp-check-pr workflow still enforces the gate.\n private postGateStatus(headSha: string, gateSalt: string): void {\n if (gateSalt.trim() === '' || headSha === '') return;\n const res = spawnSync('gh', [\n 'api', '--method', 'POST', `repos/{owner}/{repo}/statuses/${headSha}`,\n '-f', 'state=success',\n '-f', 'context=webpieces/pr-gate',\n '-f', 'description=gated flow ran and passed',\n ], { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write(\n '⚠️ Could not post the webpieces/pr-gate commit status (needs a token with statuses:write). ' +\n 'The CI wp-check-pr workflow still enforces the gate.\\n',\n );\n } else {\n process.stdout.write(` posted webpieces/pr-gate βœ“ status on ${headSha.slice(0, 12)}\\n`);\n }\n }\n\n // Enforce that EACH matched checklist was reviewed by its OWN named subagent, as a DISTINCT run β€”\n // the coding agent may not self-certify, and one reviewer may not stand in for several. A verified set\n // passes silently; no session id warns but passes; any missing reviewer throws so the PR does not open.\n private enforceProvenance(required: readonly RequiredChecklist[], branch: string): boolean {\n const errors: string[] = [];\n let verified = true; // no reviewers to verify β‡’ vacuously true\n const subagents = required.map((r: RequiredChecklist): string => r.subagent.trim()).filter((s: string): boolean => s !== '');\n if (subagents.length > 0) {\n const result = this.provenance.verifyDistinct(subagents, branch);\n verified = result.status === PROVENANCE_OK;\n if (result.status === PROVENANCE_MISSING) {\n errors.push(result.detail);\n } else if (result.status === PROVENANCE_SKIPPED) {\n process.stderr.write(`⚠️ ${result.detail}\\n`);\n }\n }\n if (errors.length > 0) {\n throw new InformAiError(\n `${errors.length} checklist(s) require an independent reviewer subagent that did not run β€” fix, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` β€’ ${e}`).join('\\n') +\n `\\n\\nSpawn the named reviewer subagent to review the checklist on THIS branch, then re-run.`,\n );\n }\n return verified;\n }\n\n // Publish every reviewer's full `output` as ONE combined PR comment, idempotently (find the marker\n // comment β†’ PATCH it, else POST). No-op when there is no PR number or no matched checklists. Never\n // fatal: by here the PR is already created/updated, so a `gh` failure only warns.\n private postChecklistComment(repoRoot: string, prNumber: string, rows: readonly ChecklistRow[], provenanceVerified: boolean): void {\n if (prNumber === '' || rows.length === 0) return;\n if (!loadAndValidate(repoRoot).prGate.checklistComments) return;\n const body = this.dashboard.renderChecklistComment(rows, provenanceVerified);\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const payload = path.join(prDir, 'checklist-comment.json');\n fs.writeFileSync(payload, JSON.stringify({ body }));\n const commentId = this.findChecklistCommentId(prNumber);\n const args = commentId !== ''\n ? ['api', '--method', 'PATCH', `repos/{owner}/{repo}/issues/comments/${commentId}`, '--input', payload]\n : ['api', '--method', 'POST', `repos/{owner}/{repo}/issues/${prNumber}/comments`, '--input', payload];\n const res = spawnSync('gh', args, { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write('⚠️ Could not post the checklist review comment (non-fatal β€” the PR is already up).\\n');\n } else {\n process.stdout.write(` ${commentId !== '' ? 'updated' : 'posted'} the checklist review comment βœ“\\n`);\n }\n }\n\n // The id of THIS tool's existing checklist comment on the PR (by the hidden marker), or '' if none.\n private findChecklistCommentId(prNumber: string): string {\n const res = spawnSync('gh', [\n 'api', '--paginate', `repos/{owner}/{repo}/issues/${prNumber}/comments`,\n '--jq', `.[] | select(.body | contains(\"${CHECKLIST_COMMENT_MARKER}\")) | .id`,\n ], { encoding: 'utf8' });\n if (res.status !== 0) return '';\n return (res.stdout ?? '').trim().split('\\n')[0] ?? '';\n }\n\n // The PR, the remote branch, and the local branch all share the one stable feature name. Look up /\n // create / merge against `baseBranch` (baseBranchName tolerates a leftover `…wpN` mid-transition).\n private upsertPr(repoRoot: string, baseBranch: string, body: string, title: string, input: DashboardInput): UpsertResult {\n const prDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, body + '\\n');\n\n const prNumber = spawnSync(\n 'gh', ['pr', 'list', '--head', baseBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';\n\n if (num === '') {\n process.stdout.write('Creating PR...\\n');\n const create = spawnSync('gh', ['pr', 'create', '--head', baseBranch, '--base', 'main', '--title', title, '--body-file', bodyFile], { stdio: 'inherit' });\n if (create.status !== 0) {\n process.stderr.write('⚠️ gh pr create failed β€” create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return new UpsertResult('', new MergeOutcome(false, false,\n '⚠️ did NOT merge β€” there is no PR to merge (gh pr create failed above)'));\n }\n } else {\n process.stdout.write(`Updating PR #${num}...\\n`);\n const edit = spawnSync('gh', ['pr', 'edit', num, '--title', title, '--body-file', bodyFile], { stdio: 'inherit' });\n if (edit.status !== 0) {\n process.stderr.write(`⚠️ gh pr edit failed β€” PR #${num} still shows its OLD title/body. The new body is in:\\n ` + bodyFile + '\\n');\n }\n }\n\n // Set the squash-merge SUBJECT to the PR title (+ the `(#N)` GitHub normally appends, which an\n // explicit --subject would otherwise drop) and the BODY to the compact commit summary, so main's\n // history carries the PR title + risk/flags/link β€” NOT the internal `Squash merge of <branch>`\n // subject GitHub would inherit from the single squash commit on the branch.\n const ref = this.prRef(baseBranch);\n const subject = ref.number !== '' ? `${title} (#${ref.number})` : title;\n const mergeBodyFile = path.join(prDir, 'merge-commit-body.md');\n fs.writeFileSync(mergeBodyFile, this.dashboard.renderCommitBody(input, ref.url) + '\\n');\n // PrMerger owns the direct-merge / auto-merge-fallback decision AND checks every gh status, so a\n // merge that did not happen is reported as such instead of being swallowed (see pr-merger.ts).\n // REQUIRED config β€” no default here on purpose. A missing value (an older published\n // rules-config that has no such field) reaches PrMerger as '' and is treated as \"do not merge\".\n const mergeMode = loadAndValidate(repoRoot).prGate.mergeMode ?? '';\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, mergeMode);\n return new UpsertResult(ref.number !== '' ? ref.number : num, outcome);\n }\n\n // The PR's number + web URL (for the merge subject `(#N)` and the commit-body back-link). Both ''\n // if it can't be resolved. Rendered via jq into one tab-separated line so no JSON parsing is needed.\n private prRef(baseBranch: string): PrRef {\n const result = spawnSync(\n 'gh', ['pr', 'view', baseBranch, '--json', 'number,url', '--jq', '\"\\\\(.number)\\\\t\\\\(.url)\"'],\n { encoding: 'utf8' },\n );\n if (result.status !== 0) {\n return new PrRef('', '');\n }\n const parts = (result.stdout ?? '').trim().split('\\t');\n return new PrRef(parts[0] ?? '', parts[1] ?? '');\n }\n}\n"]}
@@ -19,6 +19,7 @@ export declare class StartUpsertPrCommand {
19
19
  constructor(repoRootFinder: RepoRootFinder, aiBranchName: AiBranchName, branchNaming: BranchNaming, buildAffected: BuildAffected, gitExec: GitExec, runUpdate: RunUpdate, checklistDetector: ChecklistDetector, manifestService: ChecklistManifestService, reviewJsonService: ReviewJsonService, diffScope: DiffScope);
20
20
  run(): Promise<void>;
21
21
  private writePrContext;
22
+ private alreadyReviewed;
22
23
  private printChecklistPlan;
23
24
  private scaffoldCiWorkflow;
24
25
  private updateBranchFromMain;
@@ -64,15 +64,20 @@ let StartUpsertPrCommand = class StartUpsertPrCommand {
64
64
  const triggered = this.checklistDetector.detectForRepo(repoRoot, defs);
65
65
  const required = this.checklistDetector.toRequired(triggered);
66
66
  const reviewPath = (0, rules_config_1.reviewJsonPath)(repoRoot, this.aiBranchName.getFeatureName());
67
+ // REVIEW ONCE PER BRANCH: a checklist that already has a passing/overridden review-<id>.json from an
68
+ // earlier cycle is NOT re-reviewed β€” only the ones still needing a verdict are handed to the AI. The
69
+ // review files persist in .webpieces, so a second wp-start/wp-finish cycle re-instructs nothing.
70
+ const results = this.reviewJsonService.loadChecklistResults(reviewPath, required);
71
+ const toReview = required.filter((req) => !this.alreadyReviewed(req, results));
67
72
  // Persist the review-format + process instructions where any failure message can cite them.
68
73
  (0, rules_config_1.writeTemplate)(repoRoot, 'webpieces.review-checklists.md');
69
74
  // Persist the PR diff context (base sha + changed files) so reviewer subagents can `git diff` for
70
75
  // content instead of the tooling matching on regexes. Written whenever a base resolves.
71
76
  this.writePrContext(repoRoot);
72
- this.printChecklistPlan(defs.length, triggered);
77
+ this.printChecklistPlan(triggered, toReview);
73
78
  process.stdout.write('\n' + SEP + 'β‘’ Review the PR, then finish\n' + SEP + '\n');
74
79
  process.stdout.write(`Branch is updated, pushed, and the build gate passed. Now review your own changes and\n` +
75
- `${(0, rules_config_1.reviewJsonSchemaHint)(reviewPath, required)}\n\n` +
80
+ `${(0, rules_config_1.reviewJsonSchemaHint)(reviewPath, toReview)}\n\n` +
76
81
  `Then run: pnpm wp-finish-upsert-pr\n` +
77
82
  `(It re-validates the build, renders the dashboard with your risk/violations, and creates/updates the PR.)\n\n`);
78
83
  }
@@ -89,18 +94,29 @@ let StartUpsertPrCommand = class StartUpsertPrCommand {
89
94
  const p = this.reviewJsonService.writePrContext(repoRoot, this.aiBranchName.getFeatureName(), new rules_config_1.PrContext(range.base, head, changed));
90
95
  process.stdout.write(`\nπŸ“‚ Wrote PR diff context (${changed.length} changed file(s)) β†’ ${p}\n`);
91
96
  }
92
- // Show which review checklists the diff matched (spawn a SEPARATE subagent for each) and which were
93
- // skipped because their patterns did not match β€” so it is visible WHY fewer than all of them ran.
94
- printChecklistPlan(total, triggered) {
95
- if (total === 0)
97
+ // A checklist already reviewed on this branch β€” its review-<id>.json resolves to PASS or OVERRIDDEN β€”
98
+ // so it is NOT handed back to the AI to re-review (review once per branch).
99
+ alreadyReviewed(req, results) {
100
+ const status = this.reviewJsonService.resolveVerdict(req, results).status;
101
+ return status === rules_config_1.CK_PASS || status === rules_config_1.CK_OVERRIDDEN;
102
+ }
103
+ // Show which matched checklists still need a reviewer subagent (spawn each as a distinct one) and which
104
+ // are already reviewed (reused, not re-run) β€” so a second cycle re-instructs nothing.
105
+ printChecklistPlan(triggered, toReview) {
106
+ if (triggered.length === 0)
96
107
  return;
97
108
  process.stdout.write('\n' + SEP + 'πŸ“‹ Review checklists\n' + SEP + '\n');
98
- if (triggered.length === 0) {
99
- process.stdout.write('No checklist patterns matched this diff β€” no reviewer subagents needed.\n');
109
+ const toReviewIds = new Set(toReview.map((r) => r.id));
110
+ const reused = triggered.filter((t) => !toReviewIds.has(t.def.id));
111
+ for (const t of reused) {
112
+ process.stdout.write(` βœ“ ${t.def.subagent} β€” already reviewed on this branch (reusing its review-${t.def.id}.json)\n`);
113
+ }
114
+ if (toReview.length === 0) {
115
+ process.stdout.write('All matched checklists are already reviewed β€” nothing to re-run. Just write review.json and finish.\n');
100
116
  return;
101
117
  }
102
118
  process.stdout.write('Spawn EACH of these as a SEPARATE subagent (a different one per checklist β€” do not self-certify):\n');
103
- for (const t of triggered) {
119
+ for (const t of triggered.filter((x) => toReviewIds.has(x.def.id))) {
104
120
  process.stdout.write(` β€’ subagent "${t.def.subagent}"`);
105
121
  if (t.def.doc.trim() !== '')
106
122
  process.stdout.write(` β€” reads ${t.def.doc}`);
@@ -1 +1 @@
1
- {"version":3,"file":"start-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/start-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync } from 'child_process';\nimport { loadAndValidate, reviewJsonPath, reviewJsonSchemaHint, writeTemplate, writeTemplateIfMissing, CliExitError, RepoRootFinder, ChecklistManifestService, ReviewJsonService, DiffScope, ChangedFilesOptions, PrContext } from '@webpieces/rules-config';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { ChecklistDetector } from '../workflow/checklist-detector';\nimport { GitExec } from '../workflow/git-exec';\nimport { RunUpdate } from '../workflow/run-update';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// START of the AI-first PR flow: the deterministic setup β€” update from main, push, run the advisory\n// build gate β€” then hand the AI instructions to WRITE review.json and run `wp-finish-upsert-pr` (which\n// reads it and posts the PR). This command NEVER creates/updates a PR; all `gh` posting lives in finish.\n@injectable(bindingScopeValues.Singleton)\nexport class StartUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly buildAffected: BuildAffected,\n private readonly gitExec: GitExec,\n private readonly runUpdate: RunUpdate,\n private readonly checklistDetector: ChecklistDetector,\n private readonly manifestService: ChecklistManifestService,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly diffScope: DiffScope,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n // When this repo has opted into server-side enforcement (a committed gateSalt), scaffold the CI\n // workflow into the gitignored instruct-ai dir (never .github directly β€” that would dirty the tree\n // before the clean-tree check) and tell the human to copy + require it. IfMissing so it is written\n // once and never clobbers a customized copy.\n this.scaffoldCiWorkflow(repoRoot);\n\n // Precondition: a fully-committed tree. This flow updates, pushes HEAD, and builds β€” the tooling\n // must not commit your work for you, and pushing HEAD while building the working tree would let\n // an uncommitted change build green yet push a stale commit. Fail early if dirty.\n this.gitExec.assertCleanTree(repoRoot);\n\n await this.updateBranchFromMain(repoRoot);\n // Local branch, remote branch, and PR share the one stable feature name.\n this.gitExec.ensurePushed(this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim()));\n\n // Advisory build gate β€” early feedback before the AI writes review.json. wp-finish-upsert-pr\n // runs the authoritative one. Both go through the same runBuildGate (only the framing differs).\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n 'β‘‘ Build gate (nx affected)', 'pnpm wp-start-upsert-pr', 'Build failed β€” fix it before reviewing.',\n ));\n\n // Hand the AI its next step: write review.json, then run finish (which posts the PR). Compute the\n // checklists this diff MATCHED (from the manifest doc) so the hint names each reviewer subagent to\n // spawn BEFORE review.json is written (empty for repos with no checklist doc β‡’ the hint is unchanged).\n const defs = this.manifestService.load(repoRoot, loadAndValidate(repoRoot).prGate.checklistDoc);\n const triggered = this.checklistDetector.detectForRepo(repoRoot, defs);\n const required = this.checklistDetector.toRequired(triggered);\n const reviewPath = reviewJsonPath(repoRoot, this.aiBranchName.getFeatureName());\n // Persist the review-format + process instructions where any failure message can cite them.\n writeTemplate(repoRoot, 'webpieces.review-checklists.md');\n // Persist the PR diff context (base sha + changed files) so reviewer subagents can `git diff` for\n // content instead of the tooling matching on regexes. Written whenever a base resolves.\n this.writePrContext(repoRoot);\n this.printChecklistPlan(defs.length, triggered);\n process.stdout.write('\\n' + SEP + 'β‘’ Review the PR, then finish\\n' + SEP + '\\n');\n process.stdout.write(\n `Branch is updated, pushed, and the build gate passed. Now review your own changes and\\n` +\n `${reviewJsonSchemaHint(reviewPath, required)}\\n\\n` +\n `Then run: pnpm wp-finish-upsert-pr\\n` +\n `(It re-validates the build, renders the dashboard with your risk/violations, and creates/updates the PR.)\\n\\n`,\n );\n }\n\n // Write pr-context.json (base/head sha + the full changed-file set, tsOnly:false) so a reviewer\n // subagent knows the exact base the gate uses and can `git diff <base> HEAD -- <file>` for content.\n private writePrContext(repoRoot: string): void {\n const range = this.diffScope.resolveBase(repoRoot);\n if (!range.base) return;\n const opts = new ChangedFilesOptions();\n opts.tsOnly = false;\n const changed = this.diffScope.getChangedFiles(repoRoot, range.base, range.head, opts);\n const head = range.head && range.head.trim() !== '' ? range.head : 'HEAD';\n const p = this.reviewJsonService.writePrContext(\n repoRoot, this.aiBranchName.getFeatureName(), new PrContext(range.base, head, changed),\n );\n process.stdout.write(`\\nπŸ“‚ Wrote PR diff context (${changed.length} changed file(s)) β†’ ${p}\\n`);\n }\n\n // Show which review checklists the diff matched (spawn a SEPARATE subagent for each) and which were\n // skipped because their patterns did not match β€” so it is visible WHY fewer than all of them ran.\n private printChecklistPlan(total: number, triggered: readonly TriggeredChecklist[]): void {\n if (total === 0) return;\n process.stdout.write('\\n' + SEP + 'πŸ“‹ Review checklists\\n' + SEP + '\\n');\n if (triggered.length === 0) {\n process.stdout.write('No checklist patterns matched this diff β€” no reviewer subagents needed.\\n');\n return;\n }\n process.stdout.write('Spawn EACH of these as a SEPARATE subagent (a different one per checklist β€” do not self-certify):\\n');\n for (const t of triggered) {\n process.stdout.write(` β€’ subagent \"${t.def.subagent}\"`);\n if (t.def.doc.trim() !== '') process.stdout.write(` β€” reads ${t.def.doc}`);\n process.stdout.write(` (matched: ${t.matchedFiles.slice(0, 4).join(', ')})\\n`);\n }\n process.stdout.write('See .webpieces/instruct-ai/webpieces.review-checklists.md for the review-<id>.json format each must write.\\n');\n }\n\n // Scaffold the server-side CI check when (and only when) this repo set a gateSalt. Written to the\n // gitignored instruct-ai dir so it never dirties the tree; the human copies it to .github/workflows\n // and marks it required (webpieces can't set branch protection). No-op for repos with no gateSalt.\n private scaffoldCiWorkflow(repoRoot: string): void {\n if (loadAndValidate(repoRoot).prGate.gateSalt.trim() === '') return;\n writeTemplateIfMissing(repoRoot, 'webpieces-pr-gate.yml');\n process.stdout.write(\n `\\nℹ️ Server-side gate enforcement is ON (gateSalt set). If you have not already:\\n` +\n ` β€’ copy .webpieces/instruct-ai/webpieces-pr-gate.yml β†’ .github/workflows/ and commit it\\n` +\n ` β€’ mark the \"webpieces-pr-gate\" check REQUIRED in branch protection (repo admin only)\\n`,\n );\n }\n\n // Bring the branch up to date with main via the shared 3-point engine (in-process). On conflict the\n // merge process doc it writes names `wp-finish-upsert-pr` as the finish command.\n private async updateBranchFromMain(repoRoot: string): Promise<void> {\n process.stdout.write('\\n' + SEP + 'β‘  Updating branch from main\\n' + SEP + '\\n');\n const outcome = await this.runUpdate.runUpdateFromMain(repoRoot, 'wp-start-upsert-pr', 'wp-finish-upsert-pr');\n if (outcome === 'conflict' || outcome === 'unvalidatedResume') {\n throw new CliExitError(2,\n '\\n⏸️ Conflicts β€” resolve them, then run pnpm wp-finish-upsert-pr (it validates the merge AND finishes the PR).',\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"start-upsert-pr-command.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/commands/start-upsert-pr-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync } from 'child_process';\nimport { loadAndValidate, reviewJsonPath, reviewJsonSchemaHint, writeTemplate, writeTemplateIfMissing, CliExitError, RepoRootFinder, ChecklistManifestService, ReviewJsonService, DiffScope, ChangedFilesOptions, PrContext, RequiredChecklist, ChecklistResult, CK_PASS, CK_OVERRIDDEN } from '@webpieces/rules-config';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { ChecklistDetector } from '../workflow/checklist-detector';\nimport { GitExec } from '../workflow/git-exec';\nimport { RunUpdate } from '../workflow/run-update';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// START of the AI-first PR flow: the deterministic setup β€” update from main, push, run the advisory\n// build gate β€” then hand the AI instructions to WRITE review.json and run `wp-finish-upsert-pr` (which\n// reads it and posts the PR). This command NEVER creates/updates a PR; all `gh` posting lives in finish.\n@injectable(bindingScopeValues.Singleton)\nexport class StartUpsertPrCommand {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly aiBranchName: AiBranchName,\n private readonly branchNaming: BranchNaming,\n private readonly buildAffected: BuildAffected,\n private readonly gitExec: GitExec,\n private readonly runUpdate: RunUpdate,\n private readonly checklistDetector: ChecklistDetector,\n private readonly manifestService: ChecklistManifestService,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly diffScope: DiffScope,\n ) {}\n\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n // When this repo has opted into server-side enforcement (a committed gateSalt), scaffold the CI\n // workflow into the gitignored instruct-ai dir (never .github directly β€” that would dirty the tree\n // before the clean-tree check) and tell the human to copy + require it. IfMissing so it is written\n // once and never clobbers a customized copy.\n this.scaffoldCiWorkflow(repoRoot);\n\n // Precondition: a fully-committed tree. This flow updates, pushes HEAD, and builds β€” the tooling\n // must not commit your work for you, and pushing HEAD while building the working tree would let\n // an uncommitted change build green yet push a stale commit. Fail early if dirty.\n this.gitExec.assertCleanTree(repoRoot);\n\n await this.updateBranchFromMain(repoRoot);\n // Local branch, remote branch, and PR share the one stable feature name.\n this.gitExec.ensurePushed(this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim()));\n\n // Advisory build gate β€” early feedback before the AI writes review.json. wp-finish-upsert-pr\n // runs the authoritative one. Both go through the same runBuildGate (only the framing differs).\n this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n 'β‘‘ Build gate (nx affected)', 'pnpm wp-start-upsert-pr', 'Build failed β€” fix it before reviewing.',\n ));\n\n // Hand the AI its next step: write review.json, then run finish (which posts the PR). Compute the\n // checklists this diff MATCHED (from the manifest doc) so the hint names each reviewer subagent to\n // spawn BEFORE review.json is written (empty for repos with no checklist doc β‡’ the hint is unchanged).\n const defs = this.manifestService.load(repoRoot, loadAndValidate(repoRoot).prGate.checklistDoc);\n const triggered = this.checklistDetector.detectForRepo(repoRoot, defs);\n const required = this.checklistDetector.toRequired(triggered);\n const reviewPath = reviewJsonPath(repoRoot, this.aiBranchName.getFeatureName());\n // REVIEW ONCE PER BRANCH: a checklist that already has a passing/overridden review-<id>.json from an\n // earlier cycle is NOT re-reviewed β€” only the ones still needing a verdict are handed to the AI. The\n // review files persist in .webpieces, so a second wp-start/wp-finish cycle re-instructs nothing.\n const results = this.reviewJsonService.loadChecklistResults(reviewPath, required);\n const toReview = required.filter((req: RequiredChecklist): boolean => !this.alreadyReviewed(req, results));\n // Persist the review-format + process instructions where any failure message can cite them.\n writeTemplate(repoRoot, 'webpieces.review-checklists.md');\n // Persist the PR diff context (base sha + changed files) so reviewer subagents can `git diff` for\n // content instead of the tooling matching on regexes. Written whenever a base resolves.\n this.writePrContext(repoRoot);\n this.printChecklistPlan(triggered, toReview);\n process.stdout.write('\\n' + SEP + 'β‘’ Review the PR, then finish\\n' + SEP + '\\n');\n process.stdout.write(\n `Branch is updated, pushed, and the build gate passed. Now review your own changes and\\n` +\n `${reviewJsonSchemaHint(reviewPath, toReview)}\\n\\n` +\n `Then run: pnpm wp-finish-upsert-pr\\n` +\n `(It re-validates the build, renders the dashboard with your risk/violations, and creates/updates the PR.)\\n\\n`,\n );\n }\n\n // Write pr-context.json (base/head sha + the full changed-file set, tsOnly:false) so a reviewer\n // subagent knows the exact base the gate uses and can `git diff <base> HEAD -- <file>` for content.\n private writePrContext(repoRoot: string): void {\n const range = this.diffScope.resolveBase(repoRoot);\n if (!range.base) return;\n const opts = new ChangedFilesOptions();\n opts.tsOnly = false;\n const changed = this.diffScope.getChangedFiles(repoRoot, range.base, range.head, opts);\n const head = range.head && range.head.trim() !== '' ? range.head : 'HEAD';\n const p = this.reviewJsonService.writePrContext(\n repoRoot, this.aiBranchName.getFeatureName(), new PrContext(range.base, head, changed),\n );\n process.stdout.write(`\\nπŸ“‚ Wrote PR diff context (${changed.length} changed file(s)) β†’ ${p}\\n`);\n }\n\n // A checklist already reviewed on this branch β€” its review-<id>.json resolves to PASS or OVERRIDDEN β€”\n // so it is NOT handed back to the AI to re-review (review once per branch).\n private alreadyReviewed(req: RequiredChecklist, results: readonly ChecklistResult[]): boolean {\n const status = this.reviewJsonService.resolveVerdict(req, results).status;\n return status === CK_PASS || status === CK_OVERRIDDEN;\n }\n\n // Show which matched checklists still need a reviewer subagent (spawn each as a distinct one) and which\n // are already reviewed (reused, not re-run) β€” so a second cycle re-instructs nothing.\n private printChecklistPlan(triggered: readonly TriggeredChecklist[], toReview: readonly RequiredChecklist[]): void {\n if (triggered.length === 0) return;\n process.stdout.write('\\n' + SEP + 'πŸ“‹ Review checklists\\n' + SEP + '\\n');\n const toReviewIds = new Set(toReview.map((r: RequiredChecklist): string => r.id));\n const reused = triggered.filter((t: TriggeredChecklist): boolean => !toReviewIds.has(t.def.id));\n for (const t of reused) {\n process.stdout.write(` βœ“ ${t.def.subagent} β€” already reviewed on this branch (reusing its review-${t.def.id}.json)\\n`);\n }\n if (toReview.length === 0) {\n process.stdout.write('All matched checklists are already reviewed β€” nothing to re-run. Just write review.json and finish.\\n');\n return;\n }\n process.stdout.write('Spawn EACH of these as a SEPARATE subagent (a different one per checklist β€” do not self-certify):\\n');\n for (const t of triggered.filter((x: TriggeredChecklist): boolean => toReviewIds.has(x.def.id))) {\n process.stdout.write(` β€’ subagent \"${t.def.subagent}\"`);\n if (t.def.doc.trim() !== '') process.stdout.write(` β€” reads ${t.def.doc}`);\n process.stdout.write(` (matched: ${t.matchedFiles.slice(0, 4).join(', ')})\\n`);\n }\n process.stdout.write('See .webpieces/instruct-ai/webpieces.review-checklists.md for the review-<id>.json format each must write.\\n');\n }\n\n // Scaffold the server-side CI check when (and only when) this repo set a gateSalt. Written to the\n // gitignored instruct-ai dir so it never dirties the tree; the human copies it to .github/workflows\n // and marks it required (webpieces can't set branch protection). No-op for repos with no gateSalt.\n private scaffoldCiWorkflow(repoRoot: string): void {\n if (loadAndValidate(repoRoot).prGate.gateSalt.trim() === '') return;\n writeTemplateIfMissing(repoRoot, 'webpieces-pr-gate.yml');\n process.stdout.write(\n `\\nℹ️ Server-side gate enforcement is ON (gateSalt set). If you have not already:\\n` +\n ` β€’ copy .webpieces/instruct-ai/webpieces-pr-gate.yml β†’ .github/workflows/ and commit it\\n` +\n ` β€’ mark the \"webpieces-pr-gate\" check REQUIRED in branch protection (repo admin only)\\n`,\n );\n }\n\n // Bring the branch up to date with main via the shared 3-point engine (in-process). On conflict the\n // merge process doc it writes names `wp-finish-upsert-pr` as the finish command.\n private async updateBranchFromMain(repoRoot: string): Promise<void> {\n process.stdout.write('\\n' + SEP + 'β‘  Updating branch from main\\n' + SEP + '\\n');\n const outcome = await this.runUpdate.runUpdateFromMain(repoRoot, 'wp-start-upsert-pr', 'wp-finish-upsert-pr');\n if (outcome === 'conflict' || outcome === 'unvalidatedResume') {\n throw new CliExitError(2,\n '\\n⏸️ Conflicts β€” resolve them, then run pnpm wp-finish-upsert-pr (it validates the merge AND finishes the PR).',\n );\n }\n }\n}\n"]}