@webpieces/pr-gate 0.4.758 → 0.4.760
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 +3 -3
- package/src/dashboard/author-identity.d.ts +18 -0
- package/src/dashboard/author-identity.js +45 -0
- package/src/dashboard/author-identity.js.map +1 -0
- package/src/dashboard/dashboard.d.ts +3 -1
- package/src/dashboard/dashboard.js +5 -1
- package/src/dashboard/dashboard.js.map +1 -1
- package/src/dashboard/review-identity-renderer.d.ts +2 -0
- package/src/dashboard/review-identity-renderer.js +6 -0
- package/src/dashboard/review-identity-renderer.js.map +1 -1
- package/src/scripts/commands/finish-upsert-pr-command.d.ts +3 -1
- package/src/scripts/commands/finish-upsert-pr-command.js +6 -2
- package/src/scripts/commands/finish-upsert-pr-command.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/pr-gate",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.760",
|
|
4
4
|
"description": "Gated PR system: 3-point squash-merge, merge validation gate, and red/yellow/green PR dashboard. Standalone scripts, no Nx dependency required.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
19
|
-
"@webpieces/ai-hook-rules": "0.4.
|
|
20
|
-
"@webpieces/rules-config": "0.4.
|
|
19
|
+
"@webpieces/ai-hook-rules": "0.4.760",
|
|
20
|
+
"@webpieces/rules-config": "0.4.760",
|
|
21
21
|
"inversify": "7.10.4",
|
|
22
22
|
"reflect-metadata": "0.2.2"
|
|
23
23
|
},
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AI_TYPE_UNKNOWN, AiType } from '@webpieces/ai-hook-rules';
|
|
2
|
+
/** The coding harness observed by the gate, plus the model the author reported in review.json. */
|
|
3
|
+
export declare class AuthorIdentity {
|
|
4
|
+
harness: AiType | typeof AI_TYPE_UNKNOWN;
|
|
5
|
+
model: string;
|
|
6
|
+
constructor(harness: AiType | typeof AI_TYPE_UNKNOWN, model: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the authoring harness from session identifiers supplied by the harness itself.
|
|
10
|
+
*
|
|
11
|
+
* This deliberately does not trust review.json's `agent` field: that file is AI-authored, while the
|
|
12
|
+
* session variables are supplied by Claude Code / Codex before the model runs. Both-or-neither is
|
|
13
|
+
* `unknown`; silently picking one in an ambiguous process would turn telemetry into a false claim.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AuthorIdentityResolver {
|
|
16
|
+
resolve(selfReportedModel: string): AuthorIdentity;
|
|
17
|
+
private present;
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthorIdentityResolver = exports.AuthorIdentity = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const ai_hook_rules_1 = require("@webpieces/ai-hook-rules");
|
|
6
|
+
const inversify_1 = require("inversify");
|
|
7
|
+
/** The coding harness observed by the gate, plus the model the author reported in review.json. */
|
|
8
|
+
class AuthorIdentity {
|
|
9
|
+
harness;
|
|
10
|
+
model;
|
|
11
|
+
constructor(harness, model) {
|
|
12
|
+
this.harness = harness;
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.AuthorIdentity = AuthorIdentity;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the authoring harness from session identifiers supplied by the harness itself.
|
|
19
|
+
*
|
|
20
|
+
* This deliberately does not trust review.json's `agent` field: that file is AI-authored, while the
|
|
21
|
+
* session variables are supplied by Claude Code / Codex before the model runs. Both-or-neither is
|
|
22
|
+
* `unknown`; silently picking one in an ambiguous process would turn telemetry into a false claim.
|
|
23
|
+
*/
|
|
24
|
+
let AuthorIdentityResolver = class AuthorIdentityResolver {
|
|
25
|
+
resolve(selfReportedModel) {
|
|
26
|
+
const claude = this.present('CLAUDE_CODE_SESSION_ID');
|
|
27
|
+
const codex = this.present('CODEX_SESSION_ID') || this.present('CODEX_THREAD_ID');
|
|
28
|
+
let harness = ai_hook_rules_1.AI_TYPE_UNKNOWN;
|
|
29
|
+
if (claude !== codex)
|
|
30
|
+
harness = claude ? 'claude-code' : 'codex';
|
|
31
|
+
// Keep this tied to ai-hook-rules' canonical vocabulary rather than growing a second spelling.
|
|
32
|
+
if (harness !== ai_hook_rules_1.AI_TYPE_UNKNOWN && !ai_hook_rules_1.AI_TYPES.includes(harness))
|
|
33
|
+
harness = ai_hook_rules_1.AI_TYPE_UNKNOWN;
|
|
34
|
+
const model = selfReportedModel.trim() === '' ? 'unknown' : selfReportedModel.trim();
|
|
35
|
+
return new AuthorIdentity(harness, model);
|
|
36
|
+
}
|
|
37
|
+
present(name) {
|
|
38
|
+
return (process.env[name] ?? '').trim() !== '';
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.AuthorIdentityResolver = AuthorIdentityResolver;
|
|
42
|
+
exports.AuthorIdentityResolver = AuthorIdentityResolver = tslib_1.__decorate([
|
|
43
|
+
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
|
|
44
|
+
], AuthorIdentityResolver);
|
|
45
|
+
//# sourceMappingURL=author-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"author-identity.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/author-identity.ts"],"names":[],"mappings":";;;;AAAA,4DAA6E;AAC7E,yCAA2D;AAE3D,kGAAkG;AAClG,MAAa,cAAc;IACvB,OAAO,CAAkC;IACzC,KAAK,CAAS;IAEd,YAAY,OAAwC,EAAE,KAAa;QAC/D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AARD,wCAQC;AAED;;;;;;GAMG;AAEI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAC/B,OAAO,CAAC,iBAAyB;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAClF,IAAI,OAAO,GAAoC,+BAAe,CAAC;QAC/D,IAAI,MAAM,KAAK,KAAK;YAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QACjE,+FAA+F;QAC/F,IAAI,OAAO,KAAK,+BAAe,IAAI,CAAC,wBAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,GAAG,+BAAe,CAAC;QAC1F,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;QACrF,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAEO,OAAO,CAAC,IAAY;QACxB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IACnD,CAAC;CACJ,CAAA;AAfY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,sBAAsB,CAelC","sourcesContent":["import { AI_TYPES, AI_TYPE_UNKNOWN, AiType } from '@webpieces/ai-hook-rules';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/** The coding harness observed by the gate, plus the model the author reported in review.json. */\nexport class AuthorIdentity {\n harness: AiType | typeof AI_TYPE_UNKNOWN;\n model: string;\n\n constructor(harness: AiType | typeof AI_TYPE_UNKNOWN, model: string) {\n this.harness = harness;\n this.model = model;\n }\n}\n\n/**\n * Resolves the authoring harness from session identifiers supplied by the harness itself.\n *\n * This deliberately does not trust review.json's `agent` field: that file is AI-authored, while the\n * session variables are supplied by Claude Code / Codex before the model runs. Both-or-neither is\n * `unknown`; silently picking one in an ambiguous process would turn telemetry into a false claim.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class AuthorIdentityResolver {\n resolve(selfReportedModel: string): AuthorIdentity {\n const claude = this.present('CLAUDE_CODE_SESSION_ID');\n const codex = this.present('CODEX_SESSION_ID') || this.present('CODEX_THREAD_ID');\n let harness: AiType | typeof AI_TYPE_UNKNOWN = AI_TYPE_UNKNOWN;\n if (claude !== codex) harness = claude ? 'claude-code' : 'codex';\n // Keep this tied to ai-hook-rules' canonical vocabulary rather than growing a second spelling.\n if (harness !== AI_TYPE_UNKNOWN && !AI_TYPES.includes(harness)) harness = AI_TYPE_UNKNOWN;\n const model = selfReportedModel.trim() === '' ? 'unknown' : selfReportedModel.trim();\n return new AuthorIdentity(harness, model);\n }\n\n private present(name: string): boolean {\n return (process.env[name] ?? '').trim() !== '';\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GateDefinition, ReviewJson } from '@webpieces/rules-config';
|
|
2
|
+
import { AuthorIdentity } from './author-identity';
|
|
2
3
|
/**
|
|
3
4
|
* Hidden marker on the FULL-DASHBOARD comment — the "1st comment" the PR description points at — so
|
|
4
5
|
* finish PATCHes its own comment on every push instead of appending a new one, exactly like the checklist
|
|
@@ -62,7 +63,8 @@ export declare class DashboardInput {
|
|
|
62
63
|
* keep compiling while asserting, silently, that nothing was suppressed.
|
|
63
64
|
*/
|
|
64
65
|
suppressedChecklistCount: number;
|
|
65
|
-
|
|
66
|
+
author: AuthorIdentity;
|
|
67
|
+
constructor(title: string, gateResults: GateResult[], disables: DisableCounts, buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, review: ReviewJson, checklists: ChecklistRow[], buildCommand: string, suppressedChecklistCount: number, author: AuthorIdentity);
|
|
66
68
|
}
|
|
67
69
|
/** Renders the PR-gate dashboard markdown (gates × changed files, disables, risk, 3-point hashes). */
|
|
68
70
|
export declare class Dashboard {
|
|
@@ -126,8 +126,9 @@ class DashboardInput {
|
|
|
126
126
|
* keep compiling while asserting, silently, that nothing was suppressed.
|
|
127
127
|
*/
|
|
128
128
|
suppressedChecklistCount;
|
|
129
|
+
author;
|
|
129
130
|
// eslint-disable-next-line @typescript-eslint/max-params
|
|
130
|
-
constructor(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, checklists, buildCommand, suppressedChecklistCount) {
|
|
131
|
+
constructor(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, checklists, buildCommand, suppressedChecklistCount, author) {
|
|
131
132
|
this.title = title;
|
|
132
133
|
this.gateResults = gateResults;
|
|
133
134
|
this.disables = disables;
|
|
@@ -139,6 +140,7 @@ class DashboardInput {
|
|
|
139
140
|
this.checklists = checklists;
|
|
140
141
|
this.buildCommand = buildCommand;
|
|
141
142
|
this.suppressedChecklistCount = suppressedChecklistCount;
|
|
143
|
+
this.author = author;
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
exports.DashboardInput = DashboardInput;
|
|
@@ -194,6 +196,7 @@ let Dashboard = class Dashboard {
|
|
|
194
196
|
lines.push('');
|
|
195
197
|
for (const line of this.riskLines(input.review))
|
|
196
198
|
lines.push(line);
|
|
199
|
+
lines.push(new review_identity_renderer_1.ReviewIdentityRenderer().renderAuthor(input.author.harness, input.author.model));
|
|
197
200
|
lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);
|
|
198
201
|
for (const result of input.gateResults)
|
|
199
202
|
lines.push(this.gateLine(result));
|
|
@@ -262,6 +265,7 @@ let Dashboard = class Dashboard {
|
|
|
262
265
|
lines.push('');
|
|
263
266
|
}
|
|
264
267
|
lines.push(`Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`);
|
|
268
|
+
lines.push(new review_identity_renderer_1.ReviewIdentityRenderer().renderAuthor(input.author.harness, input.author.model, false));
|
|
265
269
|
lines.push('');
|
|
266
270
|
const flags = this.nonGreenFlags(input);
|
|
267
271
|
lines.push(flags.length === 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":";;;;AAAA,yEAAoE;AACpE,0DAaiC;AACjC,yCAA2D;AAE3D;;;;;;;GAOG;AACU,QAAA,qBAAqB,GAAG,iCAAiC,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,qBAAqB,GAAG,8EAA8E,CAAC;AAE7G;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,kFAAkF,CAAC;AAE1G,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,sGAAsG;AACtG,2GAA2G;AAC3G,uGAAuG;AACvG,sGAAsG;AACtG,4DAA4D;AAC5D,MAAa,YAAY;IACrB,KAAK,CAAS,CAAC,8CAA8C;IAC7D,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;;;;;;GAMG;AACH,MAAM,YAAY;IACd,KAAK,CAAS,CAAC,yDAAyD;IACxE,KAAK,CAAS;IACd,KAAK,CAAU,CAAC,4DAA4D;IAC5E,MAAM,CAAW;IAEjB,YAAY,KAAa,EAAE,KAAa,EAAE,KAAc;QACpD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;CACJ;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;IACnG;;;;;;;;;;;OAWG;IACH,YAAY,CAAS;IACrB;;;;;;;;;;;OAWG;IACH,wBAAwB,CAAS;IAEjC,yDAAyD;IACzD,YACI,KAAa,EACb,WAAyB,EACzB,QAAuB,EACvB,WAAoB,EACpB,SAAiB,EACjB,WAAmB,EACnB,QAAgB,EAChB,MAAkB,EAClB,UAA0B,EAC1B,YAAoB,EACpB,wBAAgC;QAEhC,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;QAC7B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,wBAAwB,GAAG,wBAAwB,CAAC;IAC7D,CAAC;CACJ;AA/DD,wCA+DC;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,CAC1D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CACvC,CAAC;YACF,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,CAC7C,CAAC,GAAW,EAAU,EAAE,CAAE,yBAAqC,CAAC,GAAG,CAAC,CACvE,CAAC;QAEF,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;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,KAAqB;QACrC,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,GACb,KAAK,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,WAAW,UAAU,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;QACxD,mGAAmG;QACnG,kGAAkG;QAClG,8FAA8F;QAC9F,iGAAiG;QACjG,kGAAkG;QAClG,kGAAkG;QAClG,uCAAuC;QACvC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACvF,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,WAAW,qBAAqB,QAAQ,CAAC,CAAC;QACrD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,YAAY,CAAC,KAAqB,EAAE,KAAa;QAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;QACD,KAAK,CAAC,IAAI,CACN,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,CACtI,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YACzB,CAAC,CAAC,qBAAqB;YACvB,CAAC,CAAC,sEAAsE,CAAC,CAAC;QAC9E,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAClD,8FAA8F;QAC9F,iGAAiG;QACjG,6BAA6B;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,cAAc,EAAE,CAAC,CAAC;QAClC,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,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACK,UAAU,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;OAMG;IACK,YAAY,CAAC,YAAoB;QACrC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,eAAe,CAAC;QACtD,OAAO,gDAAgD,GAAG,EAAE,CAAC;IACjE,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;YAClC,KAAK,CAAC,IAAI,CAAC,0BAA0B,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,eAAe,CAAC,CAAC;QACxF,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,GACP,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;gBACpC,CAAC,CAAC,MAAM,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CACN,gCAAgC,KAAK,CAAC,QAAQ,CAAC,cAAc,WAAW,KAAK,EAAE,CAClF,CAAC;QACN,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,GAAG,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,6BAA6B,KAAK,CAAC,QAAQ,CAAC,WAAW,UAAU,CAAC,CAAC;QAClF,2FAA2F;QAC3F,EAAE;QACF,oGAAoG;QACpG,gGAAgG;QAChG,8FAA8F;QAC9F,gGAAgG;QAChG,gGAAgG;QAChG,6FAA6F;QAC7F,8FAA8F;QAC9F,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,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;IAC/F,mGAAmG;IACnG,sGAAsG;IACtG,gGAAgG;IACxF,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,yBAAyB,CAAC;QAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAO;YAAE,OAAO,kBAAkB,CAAC;QACtD,IAAI,GAAG,CAAC,MAAM,KAAK,yBAAU;YAAE,OAAO,gBAAgB,CAAC;QACvD,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAO;YAAE,OAAO,WAAW,CAAC;QAC/C,OAAO,sBAAsB,GAAG,CAAC,MAAM,GAAG,CAAC;IAC/C,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;gBACb,EAAE,IAAI,OAAO,CAAC;gBACd,CAAC,IAAI,CAAC,CAAC;gBACP,SAAS;YACb,CAAC;YACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACb,EAAE,IAAI,MAAM,CAAC;gBACb,CAAC,IAAI,CAAC,CAAC;gBACP,SAAS;YACb,CAAC;YACD,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC/B,EAAE,IAAI,IAAI,GAAG,EAAE,CAAC;gBAChB,CAAC,IAAI,CAAC,CAAC;gBACP,SAAS;YACb,CAAC;YACD,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;;;;OAIG;IACK,mBAAmB,CAAC,IAA6B,EAAE,eAAuB;QAC9E,iGAAiG;QACjG,iGAAiG;QACjG,uFAAuF;QACvF,IAAI,eAAe,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAe,EAAW,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjF,oGAAoG;QACpG,gGAAgG;QAChG,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,2FAA2F,CAAC;QACvG,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC1D,MAAM,MAAM,GAAG,SAAS;YACpB,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAe,EAAU,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,OAAO,mBAAmB,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,UAAU,MAAM,kDAAkD,CAAC;IAC/H,CAAC;IAED;;;;;;;;;OASG;IACH;;;OAGG;IACK,uBAAuB,CAAC,eAAuB;QACnD,OAAO,mBAAmB,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE;cAC1D,gGAAgG,CAAC;IAC3G,CAAC;IAED;;;;;;;;;;;OAWG;IACK,cAAc,CAAC,eAAuB;QAC1C,OAAO,SAAS,eAAe,qCAAqC;cAC9D,GAAG,8CAA+B,SAAS,8BAAe,IAAI,+BAAgB,EAAE,CAAC;IAC3F,CAAC;IAEO,aAAa,CAAC,IAA6B;QAC/C,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,eAAe,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACvD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAO;gBAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBACrD,IAAI,GAAG,CAAC,MAAM,KAAK,sBAAO;gBAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBAC1D,IAAI,GAAG,CAAC,MAAM,KAAK,4BAAa;gBAAE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;;gBACpE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,wGAAwG;IACxG,mFAAmF;IAC3E,YAAY,CAAC,MAAoB;QACrC,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACpE,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAC3F,CAAC;IAEO,YAAY,CAAC,MAAyB;QAC1C,MAAM,GAAG,GAAG,CAAC,CAAC;QACd,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC;IAC7E,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,IAAI,iDAAsB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC;YAC/E,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,GACP,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;QACzF,OAAO,oCAAoC,QAAQ,CAAC,cAAc,WAAW,KAAK,EAAE,CAAC;IACzF,CAAC;CACJ,CAAA;AA5aY,8BAAS;oBAAT,SAAS;IADrB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,SAAS,CA4arB","sourcesContent":["import { ReviewIdentityRenderer } from './review-identity-renderer';\nimport {\n GateDefinition,\n WEBPIECES_DISABLE,\n RULE_NAMES,\n ReviewJson,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n HOME_CONFIG_DIR,\n HOME_CONFIG_FILE,\n HOME_KEY_TURN_OFF_ALL_REVIEWERS,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/**\n * Hidden marker on the FULL-DASHBOARD comment — the \"1st comment\" the PR description points at — so\n * finish PATCHes its own comment on every push instead of appending a new one, exactly like the checklist\n * comment (see ChecklistCommentRenderer, which owns the 2nd).\n *\n * This comment did not exist before the PR description became the git-log body: the dashboard WAS the\n * description. Moving it here is what keeps a risk table out of every squash commit on main.\n */\nexport const DETAIL_COMMENT_MARKER = '<!-- webpieces-pr-detail v1 -->';\n\n/**\n * Footer on the full-dashboard comment. Names the tooling by REPO URL so a reader landing on a\n * generated comment in any consumer repo can find what generated it.\n *\n * It deliberately no longer asserts \"build ran via nx affected — not self-attested\": that sentence was\n * hard-coded and therefore wrong on every repo whose `buildCommand` is not nx. The build command is now\n * named for real, from config, in the PR-body footer — see {@link Dashboard.renderPrBody}.\n */\nconst DETAIL_COMMENT_FOOTER = 'Generated by https://github.com/deanhiller/webpieces-ts wp-finish-upsert-pr';\n\n/**\n * The last bullet of the PR description, and therefore of every squash commit body.\n *\n * Positional (\"1st\"/\"2nd\") on purpose — it is what a reader of `git log` can actually act on, far more\n * useful than naming an HTML marker they cannot see. On a PR opened by an OLDER release the checklist\n * comment already exists, so the dashboard comment lands second and this reads one off for that PR's\n * remaining life; it is cosmetic and self-corrects on the next PR.\n */\nconst DETAIL_POINTER = '(Full dashboard in 1st comment, reviewer checklist in 2nd — kept out of git log)';\n\nexport class GateResult {\n name: string;\n warningColor: string; // 'yellow' | 'red' — the color shown WHEN files matched (green is implicit)\n matchedFiles: string[];\n\n constructor(name: string, warningColor: string, matchedFiles: string[]) {\n this.name = name;\n this.warningColor = warningColor;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// One row for a consumer review checklist the branch triggered. `status` is the resolved verdict (one of\n// CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT); `detail` is the reviewer\n// output / override justification. A row is always PASS/WARN/OVERRIDDEN by the time it renders — a failed,\n// missing or unreadable verdict throws before the dashboard is built. Rendered into the PR body so the\n// verdict reaches the server — the PR body is the artifact of the local flow that leaves the checkout\n// (alongside the HMAC gate token that proves the flow ran).\nexport class ChecklistRow {\n title: string; // the checklist id (= reviewer subagent name)\n status: string; // CK_* verdict\n detail: string; // reviewer output / override justification (surfaced for OVERRIDDEN)\n\n constructor(title: string, status: string, detail = '') {\n this.title = title;\n this.status = status;\n this.detail = detail;\n }\n}\n\n/**\n * One severity bucket of the rolled-up **Checklists** dashboard row: its emoji, the words that describe it,\n * and which checklists landed in it. Data-only.\n *\n * A bucket knows whether it should NAME its members: the non-green ones do (a reader has to know WHICH\n * reviewer to go read), the passed bucket does not (a list of what went fine is noise on a one-line row).\n */\nclass RollupBucket {\n label: string; // 'blocking' | 'overridden' | 'with concerns' | 'passed'\n emoji: string;\n named: boolean; // list the checklist ids on the row, or report a bare count\n titles: string[];\n\n constructor(label: string, emoji: string, named: boolean) {\n this.label = label;\n this.emoji = emoji;\n this.named = named;\n this.titles = [];\n }\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n review: ReviewJson; // AI-authored risk/violations/summary (from review.json)\n checklists: ChecklistRow[]; // consumer checklists this branch triggered; [] for non-adopting repos\n /**\n * `commands.pr-gate.buildCommand` VERBATIM, named in the PR-body footer so `git log` records WHICH\n * command vouched for the commit. Read from config rather than hard-coded because the footer used to\n * assert \"build ran via nx affected\" on every repo, including those whose buildCommand is not nx.\n *\n * REQUIRED, with no default, and `checklists` lost its `= []` for the same reason. A defaulted\n * `buildCommand: string = ''` let every pre-existing 9-argument construction keep compiling while\n * silently rendering a footer that claims nothing — an absence that quietly means \"no build was\n * named\", which is the widening-by-omission the compatibility policy calls out. Making it required\n * means every caller states what vouched for the commit, and the empty string stays available for a\n * repo that genuinely has no build command, but only when someone writes it down.\n */\n buildCommand: string;\n /**\n * How many checklists WOULD have run but were killed by `experimental.turnOffAllReviewers` in\n * `~/.webpieces/config.json` — required ones included. `0` means the flag is off and every reviewer\n * ran normally; it is NOT a way of saying \"no checklist matched\", which `checklists.length === 0`\n * already says.\n *\n * The two states are byte-identical in `checklists` (both empty) and that is precisely the defect this\n * field closes: without it, a PR whose four REQUIRED reviewers were switched off is indistinguishable\n * in `main`'s history from a docs-only typo fix that legitimately matched nothing. REQUIRED with no\n * default, for the reason `buildCommand` is: a defaulted `0` would let every existing construction\n * keep compiling while asserting, silently, that nothing was suppressed.\n */\n suppressedChecklistCount: number;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string,\n gateResults: GateResult[],\n disables: DisableCounts,\n buildPassed: boolean,\n forkPoint: string,\n featureHead: string,\n mainHead: string,\n review: ReviewJson,\n checklists: ChecklistRow[],\n buildCommand: string,\n suppressedChecklistCount: number,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.review = review;\n this.checklists = checklists;\n this.buildCommand = buildCommand;\n this.suppressedChecklistCount = suppressedChecklistCount;\n }\n}\n\n/** Renders the PR-gate dashboard markdown (gates × changed files, disables, risk, 3-point hashes). */\n@injectable(bindingScopeValues.Singleton)\nexport class Dashboard {\n // Disabled gates are in-file examples (JSON has no comments) — skip them entirely.\n computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates\n .filter((gate: GateDefinition): boolean => !gate.disabled)\n .map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean =>\n this.matchesAny(gate.patterns, file),\n );\n return new GateResult(gate.name, gate.warningColor, matched);\n });\n }\n\n // Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n // reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\n countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map(\n (key: string): string => (RULE_NAMES as Record<string, string>)[key],\n );\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n }\n\n /**\n * The FULL dashboard — every row (green included), the whole summary, the 3-point hash points.\n *\n * This is the **1st PR comment**, not the PR description. It used to be the description, and that is\n * exactly what put it into main's history: GitHub's `squash_merge_commit_message: PR_BODY` copies the\n * description verbatim into the squash commit, so every `git log` entry carried the risk table, the\n * hash points and the gate token. The description now holds {@link renderPrBody}'s compact form, and\n * everything long-form lives here where a reader can open it and `git log` never sees it.\n *\n * Machine-facing content belongs HERE too — this comment carries the HMAC gate token. That is the\n * whole rule that keeps the description clean: an HTML comment is invisible in rendered markdown but\n * perfectly visible in `git log`, so nothing hidden may live in the description.\n */\n renderDetailComment(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## 🚦 PR Gate Dashboard');\n lines.push('');\n for (const line of this.riskLines(input.review)) lines.push(line);\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);\n for (const result of input.gateResults) lines.push(this.gateLine(result));\n lines.push(this.disableLine(input.disables));\n const eslintEmoji =\n input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // ONE rolled-up row for ALL triggered consumer checklists — a worst-of colour and a count, sitting\n // with the other status rows. It used to be one row PER checklist, each inlining that checklist's\n // whole verdict/override paragraph: on a six-checklist PR that was the same ~90-word override\n // repeated six times, burying the signal rows above it — and every word of it was already in the\n // checklist COMMENT, per reviewer, in full. ALWAYS emitted, including the zero case: \"no reviewer\n // looked at this PR\" is a fact a reader must be told, and an absent row silently reads as a green\n // all-clear (see checklistRollupLine).\n lines.push(this.checklistRollupLine(input.checklists, input.suppressedChecklistCount));\n lines.push('');\n if (input.review.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.review.summary.trim());\n lines.push('');\n }\n lines.push('### 🔍 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push(`<sub>🤖 ${DETAIL_COMMENT_FOOTER}</sub>`);\n return lines.join('\\n');\n }\n\n /**\n * The PR DESCRIPTION — which is also, byte for byte, the squash-commit body that lands in main.\n *\n * ─── Why one string serves both ─────────────────────────────────────────────────────────────────\n * There used to be two: a long dashboard in the description and this compact form passed to\n * `gh pr merge --body-file`. That made the good `git log` reachable ONLY through an explicit\n * `--body-file` merge, because every other route (the GitHub Merge button, a bare `gh pr merge`)\n * takes its body from the repo's `squash_merge_commit_message`, and on `PR_BODY` that copied the\n * whole dashboard into history. Two repos ran that way for months.\n *\n * Making the DESCRIPTION the compact form inverts it: `PR_BODY` now yields exactly the right commit,\n * so the UI button, a bare `gh pr merge`, `wp-land-pr` and finish's own auto-merge all converge on\n * identical bytes. It rests on two GitHub repo settings — `squash_merge_commit_title: PR_TITLE` and\n * `squash_merge_commit_message: PR_BODY` — which are NOT a consumer's job: they are server-side, so no\n * config key can express them, and SquashSettingsEnforcer pins them on every run. That convergence is\n * the point, and `pr-body-is-merge-body.spec.ts` pins it.\n *\n * ─── The shape, and why each part earns its line ────────────────────────────────────────────────\n * The URL leads, BARE — no caption. It used to carry `(for git log)`, which was aimed at a reader on\n * the PR page (where the link is redundant, being the page you are already on) and was meant to stop\n * them deleting it. But this string is also the squash-commit body, so that caption landed verbatim in\n * `main`'s history on every commit, forever, to serve one transient reading. In `git log` a bare URL\n * is self-evidently the way back and needs no explanation. The permanent surface wins.\n * Then the risk score (always). Then ONLY the non-green flags, under a heading that SAYS the full list\n * is in the 1st comment — a `git log` reader has no page to glance at, so the heading has to carry\n * that itself rather than leaving \"why is this list short?\" to the pointer bullet alone. Then the\n * summary capped at 4 sentences, then the footer naming the build command that vouched for it.\n *\n * The hidden gate-token marker is appended AFTER this by the caller, so it is the very last line of\n * the description and therefore of the commit. It stays an HTML comment — invisible on the PR page,\n * visible in `git log` — which is a deliberate accepted trade: the alternative was reshaping a live\n * CI-critical surface that two consumer repos verify on every PR, to save one line of history.\n */\n renderPrBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n if (prUrl !== '') {\n lines.push(prUrl);\n lines.push('');\n }\n lines.push(\n `Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`,\n );\n lines.push('');\n const flags = this.nonGreenFlags(input);\n lines.push(flags.length === 0\n ? 'Flags: 🟢 all green'\n : 'Non-green Flags (full list in first comment to avoid large git logs)');\n for (const flag of flags) lines.push(`- ${flag}`);\n // ALWAYS the last bullet, green case included. Without it the compact body reads as the whole\n // record, and the reader never learns that every green row, the full summary and each reviewer's\n // output are one click away.\n lines.push(`- ${DETAIL_POINTER}`);\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n lines.push('');\n lines.push(this.prBodyFooter(input.buildCommand));\n return this.gitLogSafe(lines.join('\\n'));\n }\n\n /**\n * Makes THIS renderer own the invariant `wp-land-pr` checks: the compact body carries no markdown\n * heading and no table pipe.\n *\n * It is applied to the WHOLE joined string rather than to each author-supplied field, on purpose.\n * Every field this body interpolates — the review summary, an override justification, a checklist\n * title, a disabled rule name, `commands.pr-gate.buildCommand` — is text this package did not write,\n * and a `|` reaches all of them for entirely ordinary non-markdown reasons: a TypeScript union\n * (`'open' | 'paused'`), a regex alternation, a quoted shell pipeline, an OR in prose. Sanitizing at\n * the single exit point means a field added later is covered without anyone remembering to wrap it,\n * and none of this renderer's own literals contain either marker, so nothing of ours changes.\n *\n * Before this, `wp-finish-upsert-pr` would happily POST such a body and report success, and then\n * `wp-land-pr` refused those exact bytes — two commands in one flow disagreeing about what the\n * renderer produces, with a refusal that blamed an old release and prescribed re-running finish,\n * which re-rendered the identical pipe from the unchanged `review.json`. An infinite loop costing a\n * CI cycle per turn.\n *\n * `¦` (BROKEN BAR) rather than `\\|`: this string's home is `git log` in a terminal, where a\n * backslash is literal punctuation and a markdown escape means nothing. The substitution is visible\n * and reads correctly; the full, unaltered text is one click away in the 1st comment, which this\n * body's last bullet always points at. `##` collapses to `#` for the same reason — a heading in a\n * summary is markdown a plain-text history cannot carry either.\n */\n private gitLogSafe(body: string): string {\n return body.replace(/\\|/g, '\\u00a6').replace(/#{2,}/g, '#');\n }\n\n /**\n * The footer, naming the build command from `commands.pr-gate.buildCommand` VERBATIM.\n *\n * No backticks: this string's primary home is `git log` in a terminal, where markdown ticks are\n * literal punctuation. \"run locally\" is the honest claim — this is the LOCAL gate's receipt, and the\n * server-side proof is the gate token in the 1st comment, not this sentence.\n */\n private prBodyFooter(buildCommand: string): string {\n const cmd = buildCommand.trim();\n const ran = cmd === '' ? '' : ` (${cmd} run locally)`;\n return `Generated by webpieces-ts wp-finish-upsert-pr${ran}`;\n }\n\n // Every dashboard row that is NOT green, as a flat bullet list for the commit body. Green rows\n // (build passed, gate did not match, zero disables/violations) are intentionally omitted.\n private nonGreenFlags(input: DashboardInput): string[] {\n const flags: string[] = [];\n if (!input.buildPassed) flags.push('Build (nx affected): 🔴 Failed');\n if (input.review.violations.length > 0)\n flags.push(`Pattern Violations: 🟡 ${input.review.violations.length} violation(s)`);\n for (const result of input.gateResults) {\n if (result.matchedFiles.length === 0) continue;\n const emoji = result.warningColor === 'red' ? '🔴' : '🟡';\n flags.push(`${result.name}: ${emoji} ${result.matchedFiles.length} file(s)`);\n }\n if (input.disables.webpiecesCount > 0) {\n const which =\n input.disables.webpiecesRules.length > 0\n ? ` — ${input.disables.webpiecesRules.join(', ')}`\n : '';\n flags.push(\n `Webpieces Disables Added: 🟡 ${input.disables.webpiecesCount} line(s)${which}`,\n );\n }\n if (input.disables.eslintCount > 0)\n flags.push(`ESLint Disables Added: 🟡 ${input.disables.eslintCount} line(s)`);\n // A triggered checklist is noteworthy in main's history — carry each into the commit body.\n //\n // SUPPRESSED replaces those N bullets with exactly ONE, whatever N is. Not because the per-reviewer\n // bullets would lie — `checklistStatusText` was already hardened against a false \"passed\" — but\n // because with the kill switch on there are NO rows at all, and a body with no `Checklist — `\n // bullets is byte-identical to a PR that legitimately matched zero checklists (a docs-only typo\n // fix). Without this line, \"four REQUIRED reviewers were killed by a flag\" and \"nothing applied\n // here\" are indistinguishable in main's history forever. One compact bullet, in the existing\n // style — the loud version belongs in stage ②'s output and the 1st comment, not in `git log`.\n if (input.suppressedChecklistCount > 0) {\n flags.push(`Checklists — ${this.suppressedTail(input.suppressedChecklistCount)}`);\n }\n for (const row of input.checklists) {\n flags.push(`Checklist — ${row.title}: ${this.checklistStatusText(row)}`);\n }\n return flags;\n }\n\n // Emoji + words for a checklist verdict, shared by the dashboard row and the commit-body flag.\n // Every state is matched EXPLICITLY and the fallthrough names the status it did not recognize. The\n // previous `return '🟢 passed'` fallthrough meant any newly-added verdict rendered as a clean pass on\n // both the PR body and main's commit history until someone noticed — exactly the wrong default.\n private checklistStatusText(row: ChecklistRow): string {\n if (row.status === CK_OVERRIDDEN) {\n const why = row.detail.trim() !== '' ? ` — override: ${row.detail.trim()}` : '';\n return `🟠 OVERRIDDEN${why}`;\n }\n if (row.status === CK_WARN) return '🟡 passed with concerns';\n if (row.status === CK_FAIL) return '🔴 FAILED review';\n if (row.status === CK_MISSING) return '⚪ not reviewed';\n if (row.status === CK_PASS) return '🟢 passed';\n return `⚪ unknown verdict (${row.status})`;\n }\n\n // First `max` sentences of `text`. A sentence ends at `. ! ?` ONLY when followed by whitespace or\n // end-of-string, so interior dots in filenames/paths/versions (dependencies.json, runtime-graph.ts,\n // 0.4.447) do NOT split — and, unlike a greedy `[^.!?]+` regex, no text is ever dropped when such a\n // dot appears (that footgun silently deleted the run of prose up to the next real boundary). Keeps\n // the commit body scannable; the full summary still lives in the PR-body dashboard.\n private firstSentences(text: string, max: number): string {\n if (text === '') return '';\n const sentences: string[] = [];\n let start = 0;\n for (let i = 0; i < text.length && sentences.length < max; i++) {\n const ch = text[i];\n const isTerminator = ch === '.' || ch === '!' || ch === '?';\n const next = text[i + 1];\n if (isTerminator && (next === undefined || /\\s/.test(next))) {\n sentences.push(text.slice(start, i + 1).trim());\n start = i + 1;\n }\n }\n // Trailing text with no terminator still counts as a sentence (up to the cap).\n if (sentences.length < max && start < text.length) {\n const tail = text.slice(start).trim();\n if (tail !== '') sentences.push(tail);\n }\n return sentences.join(' ').trim();\n }\n\n // Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\n private globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') {\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n }\n\n private matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (this.globToRegex(pattern).test(file)) return true;\n }\n return false;\n }\n\n private gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** 🟢 No`;\n const emoji = result.warningColor === 'red' ? '🔴' : '🟡';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n }\n\n /**\n * The ONE rolled-up **Checklists:** row, coloured worst-of across every checklist that ran, in the same\n * shape as its neighbours (`**Build (nx affected):** 🟢 Passed`). Counts and names only — never a line\n * of reviewer output or override justification, which is the whole reason this row exists.\n */\n private checklistRollupLine(rows: readonly ChecklistRow[], suppressedCount: number): string {\n // FIRST, and ahead of the zero-ran case below, which would say \"no review checklist matched this\n // PR\" — false here, and the one sentence that would turn a kill switch into an all-clear. ⚫ is a\n // colour no verdict uses, so it cannot be mistaken for a pass, a warning or a refusal.\n if (suppressedCount > 0) return this.suppressedChecklistLine(suppressedCount);\n const buckets = this.rollupBuckets(rows);\n const filled = buckets.filter((b: RollupBucket): boolean => b.titles.length > 0);\n // NO reviewer ran ⇒ the SKIPPED icon, never green. Green claims something was checked and came back\n // clean; ⚪ says nobody looked, which is what a reader has to be able to tell apart at a glance.\n if (filled.length === 0) {\n return '**Checklists:** ⚪ 0 ran — no review checklist matched this PR · see the checklist comment';\n }\n const allPassed = filled.length === 1 && !filled[0].named;\n const detail = allPassed\n ? 'all passed'\n : filled.map((b: RollupBucket): string => this.bucketPhrase(b)).join(', ');\n return `**Checklists:** ${filled[0].emoji} ${rows.length} ran — ${detail} · per-checklist detail in the checklist comment`;\n }\n\n /**\n * The four severity buckets, WORST FIRST, so `filled[0]` is the roll-up colour: red beats orange beats\n * yellow beats green. The colours are the same vocabulary the comment already uses (see verdictEmoji).\n *\n * OVERRIDDEN is deliberately NOT folded into passed: an override is a human knowingly accepting a red\n * verdict, and a dashboard that painted that green would hide the single most review-worthy thing on\n * the PR. The blocking bucket is the FALLTHROUGH — CK_FAIL, CK_MISSING, CK_BAD_FORMAT and any verdict\n * added later all land there, matching review.json's own \"not pass|warn|overridden ⇒ refuse\" rule\n * rather than a second list that could silently drift green.\n */\n /**\n * The full-dashboard row for a suppressed PR. Verbose is FINE here — this lands in the PR's 1st\n * comment, not in `git log`.\n */\n private suppressedChecklistLine(suppressedCount: number): string {\n return `**Checklists:** ${this.suppressedTail(suppressedCount)}`\n + ' · NO reviewer subagent ran on this PR — set that key to false (or delete it) to get them back';\n }\n\n /**\n * The ONE sentence both the dashboard row and the commit-body bullet are built from, so the two can\n * never disagree about what was switched off or by what.\n *\n * Every part of it earns its place in `main`'s permanent history:\n * • the COUNT, because \"suppressed 4\" and \"0 applicable\" are different facts and both must be\n * tellable apart forever — an absent bullet says the second while meaning the first;\n * • \"required included\", because otherwise it reads as an optional-only skip, which it is not;\n * • the FLAG and the FILE, because they are the only actionable thing a reader of `git log` has.\n * ⚫ is deliberately outside the verdict palette (🟢🟡🔴🟠⚪ are all taken by checklistStatusText), so\n * nothing here can be mistaken for a verdict a reviewer actually returned.\n */\n private suppressedTail(suppressedCount: number): string {\n return `⚫ ALL ${suppressedCount} SUPPRESSED (required included) by `\n + `${HOME_KEY_TURN_OFF_ALL_REVIEWERS} in ~/${HOME_CONFIG_DIR}/${HOME_CONFIG_FILE}`;\n }\n\n private rollupBuckets(rows: readonly ChecklistRow[]): RollupBucket[] {\n const blocking = new RollupBucket('blocking', '🔴', true);\n const overridden = new RollupBucket('overridden', '🟠', true);\n const warned = new RollupBucket('with concerns', '🟡', true);\n const passed = new RollupBucket('passed', '🟢', false);\n for (const row of rows) {\n if (row.status === CK_PASS) passed.titles.push(row.title);\n else if (row.status === CK_WARN) warned.titles.push(row.title);\n else if (row.status === CK_OVERRIDDEN) overridden.titles.push(row.title);\n else blocking.titles.push(row.title);\n }\n return [blocking, overridden, warned, passed];\n }\n\n // `2 overridden (a, b)` for the buckets a reviewer must act on; a bare `3 passed` for the one they need\n // no list of. Names are capped so a many-checklist repo still gets a ONE-LINE row.\n private bucketPhrase(bucket: RollupBucket): string {\n if (!bucket.named) return `${bucket.titles.length} ${bucket.label}`;\n return `${bucket.titles.length} ${bucket.label} (${this.compactNames(bucket.titles)})`;\n }\n\n private compactNames(titles: readonly string[]): string {\n const max = 4;\n if (titles.length <= max) return titles.join(', ');\n return `${titles.slice(0, max).join(', ')} +${titles.length - max} more`;\n }\n\n // 10-cell risk bar colored by band (🟩 ≤25, 🟨 ≤50, 🟧 ≤75, 🟥 >75), at least one filled cell.\n private riskBar(score: number): string {\n const clamped = Math.max(0, Math.min(100, score));\n const cell = clamped <= 25 ? '🟩' : clamped <= 50 ? '🟨' : clamped <= 75 ? '🟧' : '🟥';\n const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));\n return cell.repeat(filled) + '⬜'.repeat(10 - filled);\n }\n\n // RISK section (the AI half): Risk Score bar, Risk Level, Pattern Violations.\n private riskLines(review: ReviewJson): string[] {\n const violations = review.violations.length;\n const violationLine = violations === 0 ? '🟢 No' : `🟡 Yes (${violations} violation(s))`;\n return [\n `**Risk Score:** ${this.riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,\n new ReviewIdentityRenderer().render(review.agent, review.model, 'Review agent'),\n `**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,\n `**Pattern Violations:** ${violationLine}`,\n ];\n }\n\n private disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** 🟢 No';\n const which =\n disables.webpiecesRules.length > 0 ? ` — ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** 🟡 ${disables.webpiecesCount} line(s)${which}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { ReviewIdentityRenderer } from './review-identity-renderer';\nimport {\n GateDefinition,\n WEBPIECES_DISABLE,\n RULE_NAMES,\n ReviewJson,\n CK_PASS,\n CK_WARN,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n HOME_CONFIG_DIR,\n HOME_CONFIG_FILE,\n HOME_KEY_TURN_OFF_ALL_REVIEWERS,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AuthorIdentity } from './author-identity';\n\n/**\n * Hidden marker on the FULL-DASHBOARD comment — the \"1st comment\" the PR description points at — so\n * finish PATCHes its own comment on every push instead of appending a new one, exactly like the checklist\n * comment (see ChecklistCommentRenderer, which owns the 2nd).\n *\n * This comment did not exist before the PR description became the git-log body: the dashboard WAS the\n * description. Moving it here is what keeps a risk table out of every squash commit on main.\n */\nexport const DETAIL_COMMENT_MARKER = '<!-- webpieces-pr-detail v1 -->';\n\n/**\n * Footer on the full-dashboard comment. Names the tooling by REPO URL so a reader landing on a\n * generated comment in any consumer repo can find what generated it.\n *\n * It deliberately no longer asserts \"build ran via nx affected — not self-attested\": that sentence was\n * hard-coded and therefore wrong on every repo whose `buildCommand` is not nx. The build command is now\n * named for real, from config, in the PR-body footer — see {@link Dashboard.renderPrBody}.\n */\nconst DETAIL_COMMENT_FOOTER = 'Generated by https://github.com/deanhiller/webpieces-ts wp-finish-upsert-pr';\n\n/**\n * The last bullet of the PR description, and therefore of every squash commit body.\n *\n * Positional (\"1st\"/\"2nd\") on purpose — it is what a reader of `git log` can actually act on, far more\n * useful than naming an HTML marker they cannot see. On a PR opened by an OLDER release the checklist\n * comment already exists, so the dashboard comment lands second and this reads one off for that PR's\n * remaining life; it is cosmetic and self-corrects on the next PR.\n */\nconst DETAIL_POINTER = '(Full dashboard in 1st comment, reviewer checklist in 2nd — kept out of git log)';\n\nexport class GateResult {\n name: string;\n warningColor: string; // 'yellow' | 'red' — the color shown WHEN files matched (green is implicit)\n matchedFiles: string[];\n\n constructor(name: string, warningColor: string, matchedFiles: string[]) {\n this.name = name;\n this.warningColor = warningColor;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// One row for a consumer review checklist the branch triggered. `status` is the resolved verdict (one of\n// CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT); `detail` is the reviewer\n// output / override justification. A row is always PASS/WARN/OVERRIDDEN by the time it renders — a failed,\n// missing or unreadable verdict throws before the dashboard is built. Rendered into the PR body so the\n// verdict reaches the server — the PR body is the artifact of the local flow that leaves the checkout\n// (alongside the HMAC gate token that proves the flow ran).\nexport class ChecklistRow {\n title: string; // the checklist id (= reviewer subagent name)\n status: string; // CK_* verdict\n detail: string; // reviewer output / override justification (surfaced for OVERRIDDEN)\n\n constructor(title: string, status: string, detail = '') {\n this.title = title;\n this.status = status;\n this.detail = detail;\n }\n}\n\n/**\n * One severity bucket of the rolled-up **Checklists** dashboard row: its emoji, the words that describe it,\n * and which checklists landed in it. Data-only.\n *\n * A bucket knows whether it should NAME its members: the non-green ones do (a reader has to know WHICH\n * reviewer to go read), the passed bucket does not (a list of what went fine is noise on a one-line row).\n */\nclass RollupBucket {\n label: string; // 'blocking' | 'overridden' | 'with concerns' | 'passed'\n emoji: string;\n named: boolean; // list the checklist ids on the row, or report a bare count\n titles: string[];\n\n constructor(label: string, emoji: string, named: boolean) {\n this.label = label;\n this.emoji = emoji;\n this.named = named;\n this.titles = [];\n }\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n review: ReviewJson; // AI-authored risk/violations/summary (from review.json)\n checklists: ChecklistRow[]; // consumer checklists this branch triggered; [] for non-adopting repos\n /**\n * `commands.pr-gate.buildCommand` VERBATIM, named in the PR-body footer so `git log` records WHICH\n * command vouched for the commit. Read from config rather than hard-coded because the footer used to\n * assert \"build ran via nx affected\" on every repo, including those whose buildCommand is not nx.\n *\n * REQUIRED, with no default, and `checklists` lost its `= []` for the same reason. A defaulted\n * `buildCommand: string = ''` let every pre-existing 9-argument construction keep compiling while\n * silently rendering a footer that claims nothing — an absence that quietly means \"no build was\n * named\", which is the widening-by-omission the compatibility policy calls out. Making it required\n * means every caller states what vouched for the commit, and the empty string stays available for a\n * repo that genuinely has no build command, but only when someone writes it down.\n */\n buildCommand: string;\n /**\n * How many checklists WOULD have run but were killed by `experimental.turnOffAllReviewers` in\n * `~/.webpieces/config.json` — required ones included. `0` means the flag is off and every reviewer\n * ran normally; it is NOT a way of saying \"no checklist matched\", which `checklists.length === 0`\n * already says.\n *\n * The two states are byte-identical in `checklists` (both empty) and that is precisely the defect this\n * field closes: without it, a PR whose four REQUIRED reviewers were switched off is indistinguishable\n * in `main`'s history from a docs-only typo fix that legitimately matched nothing. REQUIRED with no\n * default, for the reason `buildCommand` is: a defaulted `0` would let every existing construction\n * keep compiling while asserting, silently, that nothing was suppressed.\n */\n suppressedChecklistCount: number;\n author: AuthorIdentity;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string,\n gateResults: GateResult[],\n disables: DisableCounts,\n buildPassed: boolean,\n forkPoint: string,\n featureHead: string,\n mainHead: string,\n review: ReviewJson,\n checklists: ChecklistRow[],\n buildCommand: string,\n suppressedChecklistCount: number,\n author: AuthorIdentity,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.review = review;\n this.checklists = checklists;\n this.buildCommand = buildCommand;\n this.suppressedChecklistCount = suppressedChecklistCount;\n this.author = author;\n }\n}\n\n/** Renders the PR-gate dashboard markdown (gates × changed files, disables, risk, 3-point hashes). */\n@injectable(bindingScopeValues.Singleton)\nexport class Dashboard {\n // Disabled gates are in-file examples (JSON has no comments) — skip them entirely.\n computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates\n .filter((gate: GateDefinition): boolean => !gate.disabled)\n .map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean =>\n this.matchesAny(gate.patterns, file),\n );\n return new GateResult(gate.name, gate.warningColor, matched);\n });\n }\n\n // Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n // reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\n countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map(\n (key: string): string => (RULE_NAMES as Record<string, string>)[key],\n );\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n }\n\n /**\n * The FULL dashboard — every row (green included), the whole summary, the 3-point hash points.\n *\n * This is the **1st PR comment**, not the PR description. It used to be the description, and that is\n * exactly what put it into main's history: GitHub's `squash_merge_commit_message: PR_BODY` copies the\n * description verbatim into the squash commit, so every `git log` entry carried the risk table, the\n * hash points and the gate token. The description now holds {@link renderPrBody}'s compact form, and\n * everything long-form lives here where a reader can open it and `git log` never sees it.\n *\n * Machine-facing content belongs HERE too — this comment carries the HMAC gate token. That is the\n * whole rule that keeps the description clean: an HTML comment is invisible in rendered markdown but\n * perfectly visible in `git log`, so nothing hidden may live in the description.\n */\n renderDetailComment(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## 🚦 PR Gate Dashboard');\n lines.push('');\n for (const line of this.riskLines(input.review)) lines.push(line);\n lines.push(new ReviewIdentityRenderer().renderAuthor(input.author.harness, input.author.model));\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);\n for (const result of input.gateResults) lines.push(this.gateLine(result));\n lines.push(this.disableLine(input.disables));\n const eslintEmoji =\n input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n // ONE rolled-up row for ALL triggered consumer checklists — a worst-of colour and a count, sitting\n // with the other status rows. It used to be one row PER checklist, each inlining that checklist's\n // whole verdict/override paragraph: on a six-checklist PR that was the same ~90-word override\n // repeated six times, burying the signal rows above it — and every word of it was already in the\n // checklist COMMENT, per reviewer, in full. ALWAYS emitted, including the zero case: \"no reviewer\n // looked at this PR\" is a fact a reader must be told, and an absent row silently reads as a green\n // all-clear (see checklistRollupLine).\n lines.push(this.checklistRollupLine(input.checklists, input.suppressedChecklistCount));\n lines.push('');\n if (input.review.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.review.summary.trim());\n lines.push('');\n }\n lines.push('### 🔍 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push(`<sub>🤖 ${DETAIL_COMMENT_FOOTER}</sub>`);\n return lines.join('\\n');\n }\n\n /**\n * The PR DESCRIPTION — which is also, byte for byte, the squash-commit body that lands in main.\n *\n * ─── Why one string serves both ─────────────────────────────────────────────────────────────────\n * There used to be two: a long dashboard in the description and this compact form passed to\n * `gh pr merge --body-file`. That made the good `git log` reachable ONLY through an explicit\n * `--body-file` merge, because every other route (the GitHub Merge button, a bare `gh pr merge`)\n * takes its body from the repo's `squash_merge_commit_message`, and on `PR_BODY` that copied the\n * whole dashboard into history. Two repos ran that way for months.\n *\n * Making the DESCRIPTION the compact form inverts it: `PR_BODY` now yields exactly the right commit,\n * so the UI button, a bare `gh pr merge`, `wp-land-pr` and finish's own auto-merge all converge on\n * identical bytes. It rests on two GitHub repo settings — `squash_merge_commit_title: PR_TITLE` and\n * `squash_merge_commit_message: PR_BODY` — which are NOT a consumer's job: they are server-side, so no\n * config key can express them, and SquashSettingsEnforcer pins them on every run. That convergence is\n * the point, and `pr-body-is-merge-body.spec.ts` pins it.\n *\n * ─── The shape, and why each part earns its line ────────────────────────────────────────────────\n * The URL leads, BARE — no caption. It used to carry `(for git log)`, which was aimed at a reader on\n * the PR page (where the link is redundant, being the page you are already on) and was meant to stop\n * them deleting it. But this string is also the squash-commit body, so that caption landed verbatim in\n * `main`'s history on every commit, forever, to serve one transient reading. In `git log` a bare URL\n * is self-evidently the way back and needs no explanation. The permanent surface wins.\n * Then the risk score (always). Then ONLY the non-green flags, under a heading that SAYS the full list\n * is in the 1st comment — a `git log` reader has no page to glance at, so the heading has to carry\n * that itself rather than leaving \"why is this list short?\" to the pointer bullet alone. Then the\n * summary capped at 4 sentences, then the footer naming the build command that vouched for it.\n *\n * The hidden gate-token marker is appended AFTER this by the caller, so it is the very last line of\n * the description and therefore of the commit. It stays an HTML comment — invisible on the PR page,\n * visible in `git log` — which is a deliberate accepted trade: the alternative was reshaping a live\n * CI-critical surface that two consumer repos verify on every PR, to save one line of history.\n */\n renderPrBody(input: DashboardInput, prUrl: string): string {\n const lines: string[] = [];\n if (prUrl !== '') {\n lines.push(prUrl);\n lines.push('');\n }\n lines.push(\n `Risk: ${this.riskBar(input.review.riskScore)} ${input.review.riskScore}/100 ${input.review.riskEmoji} (${input.review.riskLevel})`,\n );\n lines.push(new ReviewIdentityRenderer().renderAuthor(input.author.harness, input.author.model, false));\n lines.push('');\n const flags = this.nonGreenFlags(input);\n lines.push(flags.length === 0\n ? 'Flags: 🟢 all green'\n : 'Non-green Flags (full list in first comment to avoid large git logs)');\n for (const flag of flags) lines.push(`- ${flag}`);\n // ALWAYS the last bullet, green case included. Without it the compact body reads as the whole\n // record, and the reader never learns that every green row, the full summary and each reviewer's\n // output are one click away.\n lines.push(`- ${DETAIL_POINTER}`);\n const summary = this.firstSentences(input.review.summary.trim(), 4);\n if (summary !== '') {\n lines.push('');\n lines.push(summary);\n }\n lines.push('');\n lines.push(this.prBodyFooter(input.buildCommand));\n return this.gitLogSafe(lines.join('\\n'));\n }\n\n /**\n * Makes THIS renderer own the invariant `wp-land-pr` checks: the compact body carries no markdown\n * heading and no table pipe.\n *\n * It is applied to the WHOLE joined string rather than to each author-supplied field, on purpose.\n * Every field this body interpolates — the review summary, an override justification, a checklist\n * title, a disabled rule name, `commands.pr-gate.buildCommand` — is text this package did not write,\n * and a `|` reaches all of them for entirely ordinary non-markdown reasons: a TypeScript union\n * (`'open' | 'paused'`), a regex alternation, a quoted shell pipeline, an OR in prose. Sanitizing at\n * the single exit point means a field added later is covered without anyone remembering to wrap it,\n * and none of this renderer's own literals contain either marker, so nothing of ours changes.\n *\n * Before this, `wp-finish-upsert-pr` would happily POST such a body and report success, and then\n * `wp-land-pr` refused those exact bytes — two commands in one flow disagreeing about what the\n * renderer produces, with a refusal that blamed an old release and prescribed re-running finish,\n * which re-rendered the identical pipe from the unchanged `review.json`. An infinite loop costing a\n * CI cycle per turn.\n *\n * `¦` (BROKEN BAR) rather than `\\|`: this string's home is `git log` in a terminal, where a\n * backslash is literal punctuation and a markdown escape means nothing. The substitution is visible\n * and reads correctly; the full, unaltered text is one click away in the 1st comment, which this\n * body's last bullet always points at. `##` collapses to `#` for the same reason — a heading in a\n * summary is markdown a plain-text history cannot carry either.\n */\n private gitLogSafe(body: string): string {\n return body.replace(/\\|/g, '\\u00a6').replace(/#{2,}/g, '#');\n }\n\n /**\n * The footer, naming the build command from `commands.pr-gate.buildCommand` VERBATIM.\n *\n * No backticks: this string's primary home is `git log` in a terminal, where markdown ticks are\n * literal punctuation. \"run locally\" is the honest claim — this is the LOCAL gate's receipt, and the\n * server-side proof is the gate token in the 1st comment, not this sentence.\n */\n private prBodyFooter(buildCommand: string): string {\n const cmd = buildCommand.trim();\n const ran = cmd === '' ? '' : ` (${cmd} run locally)`;\n return `Generated by webpieces-ts wp-finish-upsert-pr${ran}`;\n }\n\n // Every dashboard row that is NOT green, as a flat bullet list for the commit body. Green rows\n // (build passed, gate did not match, zero disables/violations) are intentionally omitted.\n private nonGreenFlags(input: DashboardInput): string[] {\n const flags: string[] = [];\n if (!input.buildPassed) flags.push('Build (nx affected): 🔴 Failed');\n if (input.review.violations.length > 0)\n flags.push(`Pattern Violations: 🟡 ${input.review.violations.length} violation(s)`);\n for (const result of input.gateResults) {\n if (result.matchedFiles.length === 0) continue;\n const emoji = result.warningColor === 'red' ? '🔴' : '🟡';\n flags.push(`${result.name}: ${emoji} ${result.matchedFiles.length} file(s)`);\n }\n if (input.disables.webpiecesCount > 0) {\n const which =\n input.disables.webpiecesRules.length > 0\n ? ` — ${input.disables.webpiecesRules.join(', ')}`\n : '';\n flags.push(\n `Webpieces Disables Added: 🟡 ${input.disables.webpiecesCount} line(s)${which}`,\n );\n }\n if (input.disables.eslintCount > 0)\n flags.push(`ESLint Disables Added: 🟡 ${input.disables.eslintCount} line(s)`);\n // A triggered checklist is noteworthy in main's history — carry each into the commit body.\n //\n // SUPPRESSED replaces those N bullets with exactly ONE, whatever N is. Not because the per-reviewer\n // bullets would lie — `checklistStatusText` was already hardened against a false \"passed\" — but\n // because with the kill switch on there are NO rows at all, and a body with no `Checklist — `\n // bullets is byte-identical to a PR that legitimately matched zero checklists (a docs-only typo\n // fix). Without this line, \"four REQUIRED reviewers were killed by a flag\" and \"nothing applied\n // here\" are indistinguishable in main's history forever. One compact bullet, in the existing\n // style — the loud version belongs in stage ②'s output and the 1st comment, not in `git log`.\n if (input.suppressedChecklistCount > 0) {\n flags.push(`Checklists — ${this.suppressedTail(input.suppressedChecklistCount)}`);\n }\n for (const row of input.checklists) {\n flags.push(`Checklist — ${row.title}: ${this.checklistStatusText(row)}`);\n }\n return flags;\n }\n\n // Emoji + words for a checklist verdict, shared by the dashboard row and the commit-body flag.\n // Every state is matched EXPLICITLY and the fallthrough names the status it did not recognize. The\n // previous `return '🟢 passed'` fallthrough meant any newly-added verdict rendered as a clean pass on\n // both the PR body and main's commit history until someone noticed — exactly the wrong default.\n private checklistStatusText(row: ChecklistRow): string {\n if (row.status === CK_OVERRIDDEN) {\n const why = row.detail.trim() !== '' ? ` — override: ${row.detail.trim()}` : '';\n return `🟠 OVERRIDDEN${why}`;\n }\n if (row.status === CK_WARN) return '🟡 passed with concerns';\n if (row.status === CK_FAIL) return '🔴 FAILED review';\n if (row.status === CK_MISSING) return '⚪ not reviewed';\n if (row.status === CK_PASS) return '🟢 passed';\n return `⚪ unknown verdict (${row.status})`;\n }\n\n // First `max` sentences of `text`. A sentence ends at `. ! ?` ONLY when followed by whitespace or\n // end-of-string, so interior dots in filenames/paths/versions (dependencies.json, runtime-graph.ts,\n // 0.4.447) do NOT split — and, unlike a greedy `[^.!?]+` regex, no text is ever dropped when such a\n // dot appears (that footgun silently deleted the run of prose up to the next real boundary). Keeps\n // the commit body scannable; the full summary still lives in the PR-body dashboard.\n private firstSentences(text: string, max: number): string {\n if (text === '') return '';\n const sentences: string[] = [];\n let start = 0;\n for (let i = 0; i < text.length && sentences.length < max; i++) {\n const ch = text[i];\n const isTerminator = ch === '.' || ch === '!' || ch === '?';\n const next = text[i + 1];\n if (isTerminator && (next === undefined || /\\s/.test(next))) {\n sentences.push(text.slice(start, i + 1).trim());\n start = i + 1;\n }\n }\n // Trailing text with no terminator still counts as a sentence (up to the cap).\n if (sentences.length < max && start < text.length) {\n const tail = text.slice(start).trim();\n if (tail !== '') sentences.push(tail);\n }\n return sentences.join(' ').trim();\n }\n\n // Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\n private globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') {\n re += '[^/]*';\n i += 1;\n continue;\n }\n if (ch === '?') {\n re += '[^/]';\n i += 1;\n continue;\n }\n if ('.+^$(){}|[]\\\\'.includes(ch)) {\n re += '\\\\' + ch;\n i += 1;\n continue;\n }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n }\n\n private matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (this.globToRegex(pattern).test(file)) return true;\n }\n return false;\n }\n\n private gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** 🟢 No`;\n const emoji = result.warningColor === 'red' ? '🔴' : '🟡';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n }\n\n /**\n * The ONE rolled-up **Checklists:** row, coloured worst-of across every checklist that ran, in the same\n * shape as its neighbours (`**Build (nx affected):** 🟢 Passed`). Counts and names only — never a line\n * of reviewer output or override justification, which is the whole reason this row exists.\n */\n private checklistRollupLine(rows: readonly ChecklistRow[], suppressedCount: number): string {\n // FIRST, and ahead of the zero-ran case below, which would say \"no review checklist matched this\n // PR\" — false here, and the one sentence that would turn a kill switch into an all-clear. ⚫ is a\n // colour no verdict uses, so it cannot be mistaken for a pass, a warning or a refusal.\n if (suppressedCount > 0) return this.suppressedChecklistLine(suppressedCount);\n const buckets = this.rollupBuckets(rows);\n const filled = buckets.filter((b: RollupBucket): boolean => b.titles.length > 0);\n // NO reviewer ran ⇒ the SKIPPED icon, never green. Green claims something was checked and came back\n // clean; ⚪ says nobody looked, which is what a reader has to be able to tell apart at a glance.\n if (filled.length === 0) {\n return '**Checklists:** ⚪ 0 ran — no review checklist matched this PR · see the checklist comment';\n }\n const allPassed = filled.length === 1 && !filled[0].named;\n const detail = allPassed\n ? 'all passed'\n : filled.map((b: RollupBucket): string => this.bucketPhrase(b)).join(', ');\n return `**Checklists:** ${filled[0].emoji} ${rows.length} ran — ${detail} · per-checklist detail in the checklist comment`;\n }\n\n /**\n * The four severity buckets, WORST FIRST, so `filled[0]` is the roll-up colour: red beats orange beats\n * yellow beats green. The colours are the same vocabulary the comment already uses (see verdictEmoji).\n *\n * OVERRIDDEN is deliberately NOT folded into passed: an override is a human knowingly accepting a red\n * verdict, and a dashboard that painted that green would hide the single most review-worthy thing on\n * the PR. The blocking bucket is the FALLTHROUGH — CK_FAIL, CK_MISSING, CK_BAD_FORMAT and any verdict\n * added later all land there, matching review.json's own \"not pass|warn|overridden ⇒ refuse\" rule\n * rather than a second list that could silently drift green.\n */\n /**\n * The full-dashboard row for a suppressed PR. Verbose is FINE here — this lands in the PR's 1st\n * comment, not in `git log`.\n */\n private suppressedChecklistLine(suppressedCount: number): string {\n return `**Checklists:** ${this.suppressedTail(suppressedCount)}`\n + ' · NO reviewer subagent ran on this PR — set that key to false (or delete it) to get them back';\n }\n\n /**\n * The ONE sentence both the dashboard row and the commit-body bullet are built from, so the two can\n * never disagree about what was switched off or by what.\n *\n * Every part of it earns its place in `main`'s permanent history:\n * • the COUNT, because \"suppressed 4\" and \"0 applicable\" are different facts and both must be\n * tellable apart forever — an absent bullet says the second while meaning the first;\n * • \"required included\", because otherwise it reads as an optional-only skip, which it is not;\n * • the FLAG and the FILE, because they are the only actionable thing a reader of `git log` has.\n * ⚫ is deliberately outside the verdict palette (🟢🟡🔴🟠⚪ are all taken by checklistStatusText), so\n * nothing here can be mistaken for a verdict a reviewer actually returned.\n */\n private suppressedTail(suppressedCount: number): string {\n return `⚫ ALL ${suppressedCount} SUPPRESSED (required included) by `\n + `${HOME_KEY_TURN_OFF_ALL_REVIEWERS} in ~/${HOME_CONFIG_DIR}/${HOME_CONFIG_FILE}`;\n }\n\n private rollupBuckets(rows: readonly ChecklistRow[]): RollupBucket[] {\n const blocking = new RollupBucket('blocking', '🔴', true);\n const overridden = new RollupBucket('overridden', '🟠', true);\n const warned = new RollupBucket('with concerns', '🟡', true);\n const passed = new RollupBucket('passed', '🟢', false);\n for (const row of rows) {\n if (row.status === CK_PASS) passed.titles.push(row.title);\n else if (row.status === CK_WARN) warned.titles.push(row.title);\n else if (row.status === CK_OVERRIDDEN) overridden.titles.push(row.title);\n else blocking.titles.push(row.title);\n }\n return [blocking, overridden, warned, passed];\n }\n\n // `2 overridden (a, b)` for the buckets a reviewer must act on; a bare `3 passed` for the one they need\n // no list of. Names are capped so a many-checklist repo still gets a ONE-LINE row.\n private bucketPhrase(bucket: RollupBucket): string {\n if (!bucket.named) return `${bucket.titles.length} ${bucket.label}`;\n return `${bucket.titles.length} ${bucket.label} (${this.compactNames(bucket.titles)})`;\n }\n\n private compactNames(titles: readonly string[]): string {\n const max = 4;\n if (titles.length <= max) return titles.join(', ');\n return `${titles.slice(0, max).join(', ')} +${titles.length - max} more`;\n }\n\n // 10-cell risk bar colored by band (🟩 ≤25, 🟨 ≤50, 🟧 ≤75, 🟥 >75), at least one filled cell.\n private riskBar(score: number): string {\n const clamped = Math.max(0, Math.min(100, score));\n const cell = clamped <= 25 ? '🟩' : clamped <= 50 ? '🟨' : clamped <= 75 ? '🟧' : '🟥';\n const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));\n return cell.repeat(filled) + '⬜'.repeat(10 - filled);\n }\n\n // RISK section (the AI half): Risk Score bar, Risk Level, Pattern Violations.\n private riskLines(review: ReviewJson): string[] {\n const violations = review.violations.length;\n const violationLine = violations === 0 ? '🟢 No' : `🟡 Yes (${violations} violation(s))`;\n return [\n `**Risk Score:** ${this.riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,\n new ReviewIdentityRenderer().render(review.agent, review.model, 'Review agent'),\n `**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,\n `**Pattern Violations:** ${violationLine}`,\n ];\n }\n\n private disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** 🟢 No';\n const which =\n disables.webpiecesRules.length > 0 ? ` — ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** 🟡 ${disables.webpiecesCount} line(s)${which}`;\n }\n}\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Reviewer-supplied identity is plain text, never Markdown or an HTML comment marker. */
|
|
2
2
|
export declare class ReviewIdentityRenderer {
|
|
3
3
|
render(agent: string, model: string, label?: string): string;
|
|
4
|
+
/** Author harness is observed by the gate; only the model comes from the author's review.json. */
|
|
5
|
+
renderAuthor(agent: string, model: string, markdown?: boolean): string;
|
|
4
6
|
private text;
|
|
5
7
|
}
|
|
@@ -6,6 +6,12 @@ class ReviewIdentityRenderer {
|
|
|
6
6
|
render(agent, model, label = 'Agent') {
|
|
7
7
|
return `**${label}:** ${this.text(agent)} · **Model:** ${this.text(model)} (self-reported)`;
|
|
8
8
|
}
|
|
9
|
+
/** Author harness is observed by the gate; only the model comes from the author's review.json. */
|
|
10
|
+
renderAuthor(agent, model, markdown = true) {
|
|
11
|
+
const label = markdown ? '**Author agent:**' : 'Author agent:';
|
|
12
|
+
const modelLabel = markdown ? '**Model:**' : 'Model:';
|
|
13
|
+
return `${label} ${this.text(agent)} (detected) · ${modelLabel} ${this.text(model)} (self-reported)`;
|
|
14
|
+
}
|
|
9
15
|
text(value) {
|
|
10
16
|
return value.replace(/\s+/g, ' ').replace(/[&<>\\`*_{}\[\]()#!|~]/g, (character) => `&#${character.charCodeAt(0)};`);
|
|
11
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-identity-renderer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/review-identity-renderer.ts"],"names":[],"mappings":";;;AAAA,0FAA0F;AAC1F,MAAa,sBAAsB;IAC/B,MAAM,CAAC,KAAa,EAAE,KAAa,EAAE,KAAK,GAAG,OAAO;QAChD,OAAO,KAAK,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;IAChG,CAAC;IAEO,IAAI,CAAC,KAAa;QACtB,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,yBAAyB,EAC/D,CAAC,SAAiB,EAAU,EAAE,CAAC,KAAK,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;CACJ;
|
|
1
|
+
{"version":3,"file":"review-identity-renderer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/review-identity-renderer.ts"],"names":[],"mappings":";;;AAAA,0FAA0F;AAC1F,MAAa,sBAAsB;IAC/B,MAAM,CAAC,KAAa,EAAE,KAAa,EAAE,KAAK,GAAG,OAAO;QAChD,OAAO,KAAK,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;IAChG,CAAC;IAED,kGAAkG;IAClG,YAAY,CAAC,KAAa,EAAE,KAAa,EAAE,QAAQ,GAAG,IAAI;QACtD,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,eAAe,CAAC;QAC/D,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtD,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC;IACzG,CAAC;IAEO,IAAI,CAAC,KAAa;QACtB,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,yBAAyB,EAC/D,CAAC,SAAiB,EAAU,EAAE,CAAC,KAAK,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxE,CAAC;CACJ;AAhBD,wDAgBC","sourcesContent":["/** Reviewer-supplied identity is plain text, never Markdown or an HTML comment marker. */\nexport class ReviewIdentityRenderer {\n render(agent: string, model: string, label = 'Agent'): string {\n return `**${label}:** ${this.text(agent)} · **Model:** ${this.text(model)} (self-reported)`;\n }\n\n /** Author harness is observed by the gate; only the model comes from the author's review.json. */\n renderAuthor(agent: string, model: string, markdown = true): string {\n const label = markdown ? '**Author agent:**' : 'Author agent:';\n const modelLabel = markdown ? '**Model:**' : 'Model:';\n return `${label} ${this.text(agent)} (detected) · ${modelLabel} ${this.text(model)} (self-reported)`;\n }\n\n private text(value: string): string {\n return value.replace(/\\s+/g, ' ').replace(/[&<>\\\\`*_{}\\[\\]()#!|~]/g,\n (character: string): string => `&#${character.charCodeAt(0)};`);\n }\n}\n"]}
|
|
@@ -18,6 +18,7 @@ import { PrCommentUpserter } from '../workflow/pr-comment-upserter';
|
|
|
18
18
|
import { SquashSettingsEnforcer } from '../workflow/squash-settings-enforcer';
|
|
19
19
|
import { Dashboard } from '../../dashboard/dashboard';
|
|
20
20
|
import { ChecklistCommentRenderer } from '../../dashboard/checklist-comment-renderer';
|
|
21
|
+
import { AuthorIdentityResolver } from '../../dashboard/author-identity';
|
|
21
22
|
export declare class FinishUpsertPrCommand {
|
|
22
23
|
private readonly repoRootFinder;
|
|
23
24
|
private readonly aiBranchName;
|
|
@@ -29,6 +30,7 @@ export declare class FinishUpsertPrCommand {
|
|
|
29
30
|
private readonly prMerger;
|
|
30
31
|
private readonly publisher;
|
|
31
32
|
private readonly dashboard;
|
|
33
|
+
private readonly authorIdentity;
|
|
32
34
|
private readonly checklistComment;
|
|
33
35
|
private readonly checklistScanner;
|
|
34
36
|
private readonly verdictGate;
|
|
@@ -41,7 +43,7 @@ export declare class FinishUpsertPrCommand {
|
|
|
41
43
|
private readonly commentUpserter;
|
|
42
44
|
private readonly squashSettings;
|
|
43
45
|
private readonly stageConsole;
|
|
44
|
-
constructor(repoRootFinder: RepoRootFinder, aiBranchName: AiBranchName, branchNaming: BranchNaming, gitExec: GitExec, buildAffected: BuildAffected, buildLog: BuildGateLog, mergeState: MergeState, prMerger: PrMerger, publisher: GatedPrPublisher, dashboard: Dashboard, checklistComment: ChecklistCommentRenderer, checklistScanner: ChecklistScanner, verdictGate: ReviewerVerdictGate, reviewJsonService: ReviewJsonService, gateTokenService: GateTokenService, provenanceEnforcer: ProvenanceEnforcer, receipts: ReviewStageReceiptService, banner: FinishBanner, mergeBodyFile: MergeBodyTempFile, commentUpserter: PrCommentUpserter, squashSettings: SquashSettingsEnforcer, stageConsole: StageOutputLog);
|
|
46
|
+
constructor(repoRootFinder: RepoRootFinder, aiBranchName: AiBranchName, branchNaming: BranchNaming, gitExec: GitExec, buildAffected: BuildAffected, buildLog: BuildGateLog, mergeState: MergeState, prMerger: PrMerger, publisher: GatedPrPublisher, dashboard: Dashboard, authorIdentity: AuthorIdentityResolver, checklistComment: ChecklistCommentRenderer, checklistScanner: ChecklistScanner, verdictGate: ReviewerVerdictGate, reviewJsonService: ReviewJsonService, gateTokenService: GateTokenService, provenanceEnforcer: ProvenanceEnforcer, receipts: ReviewStageReceiptService, banner: FinishBanner, mergeBodyFile: MergeBodyTempFile, commentUpserter: PrCommentUpserter, squashSettings: SquashSettingsEnforcer, stageConsole: StageOutputLog);
|
|
45
47
|
/**
|
|
46
48
|
* Runs with this stage's console CAPTURED to a file (see StageOutputLog). What still reaches the
|
|
47
49
|
* terminal is what an agent must act on: the build heartbeat and result, and the closing banner with
|
|
@@ -27,6 +27,7 @@ const squash_settings_enforcer_1 = require("../workflow/squash-settings-enforcer
|
|
|
27
27
|
const dashboard_1 = require("../../dashboard/dashboard");
|
|
28
28
|
const checklist_comment_renderer_1 = require("../../dashboard/checklist-comment-renderer");
|
|
29
29
|
const checklist_comment_row_1 = require("../../dashboard/checklist-comment-row");
|
|
30
|
+
const author_identity_1 = require("../../dashboard/author-identity");
|
|
30
31
|
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
31
32
|
/**
|
|
32
33
|
* The three inputs the PR COMMENTS are rendered from, bundled so `publishAll` takes one parameter for
|
|
@@ -107,6 +108,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
|
|
|
107
108
|
prMerger;
|
|
108
109
|
publisher;
|
|
109
110
|
dashboard;
|
|
111
|
+
authorIdentity;
|
|
110
112
|
checklistComment;
|
|
111
113
|
checklistScanner;
|
|
112
114
|
verdictGate;
|
|
@@ -119,7 +121,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
|
|
|
119
121
|
commentUpserter;
|
|
120
122
|
squashSettings;
|
|
121
123
|
stageConsole;
|
|
122
|
-
constructor(repoRootFinder, aiBranchName, branchNaming, gitExec, buildAffected, buildLog, mergeState, prMerger, publisher, dashboard,
|
|
124
|
+
constructor(repoRootFinder, aiBranchName, branchNaming, gitExec, buildAffected, buildLog, mergeState, prMerger, publisher, dashboard, authorIdentity,
|
|
123
125
|
// The 2nd PR comment. Its own class because it is its own surface — see ChecklistCommentRenderer.
|
|
124
126
|
checklistComment, checklistScanner, verdictGate, reviewJsonService, gateTokenService,
|
|
125
127
|
// Owns the reviewer-provenance integrity check and its audit record — see ProvenanceEnforcer.
|
|
@@ -146,6 +148,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
|
|
|
146
148
|
this.prMerger = prMerger;
|
|
147
149
|
this.publisher = publisher;
|
|
148
150
|
this.dashboard = dashboard;
|
|
151
|
+
this.authorIdentity = authorIdentity;
|
|
149
152
|
this.checklistComment = checklistComment;
|
|
150
153
|
this.checklistScanner = checklistScanner;
|
|
151
154
|
this.verdictGate = verdictGate;
|
|
@@ -381,7 +384,7 @@ let FinishUpsertPrCommand = class FinishUpsertPrCommand {
|
|
|
381
384
|
// `scan.suppressed.length`, never `scan.reviewersDisabled` alone: the dashboard and the commit
|
|
382
385
|
// body have to state HOW MANY reviewers were killed, because a suppressed 4 and an applicable 0
|
|
383
386
|
// are different facts that both render as an empty `rows`. See DashboardInput.
|
|
384
|
-
return new dashboard_1.DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows, config.buildCommand, scan.suppressed.length);
|
|
387
|
+
return new dashboard_1.DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows, config.buildCommand, scan.suppressed.length, this.authorIdentity.resolve(review.model));
|
|
385
388
|
}
|
|
386
389
|
/**
|
|
387
390
|
* The applicable checklists MINUS the optional ones nobody ran.
|
|
@@ -639,6 +642,7 @@ exports.FinishUpsertPrCommand = FinishUpsertPrCommand = tslib_1.__decorate([
|
|
|
639
642
|
pr_merger_1.PrMerger,
|
|
640
643
|
gated_pr_publisher_1.GatedPrPublisher,
|
|
641
644
|
dashboard_1.Dashboard,
|
|
645
|
+
author_identity_1.AuthorIdentityResolver,
|
|
642
646
|
checklist_comment_renderer_1.ChecklistCommentRenderer,
|
|
643
647
|
checklist_scanner_1.ChecklistScanner,
|
|
644
648
|
reviewer_verdict_gate_1.ReviewerVerdictGate,
|
|
@@ -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, ChecklistVerdict,\n writeTemplate, RepoRootFinder, ReviewJsonService, GateTokenService,\n InformAiError, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { ReviewerVerdictGate } from '../workflow/reviewer-verdict-gate';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { BuildGateLog, FINISH_STAGE, REVIEW_STAGE } from '../workflow/build-gate-log';\nimport { MergeState } from '../workflow/merge-state';\nimport { ReviewStageReceiptService } from '../workflow/review-stage-receipt';\nimport { StageOutputLog, FINISH_CONSOLE_LOG } from '../workflow/stage-output-log';\nimport { PrMerger, MergeIntent, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { MergeBodyTempFile } from '../workflow/merge-body-temp-file';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { ProvenanceEnforcer, ProvenanceReport } from '../workflow/provenance-enforcer';\nimport { PrCommentRequest, PrCommentUpserter } from '../workflow/pr-comment-upserter';\nimport { SquashSettingsEnforcer } from '../workflow/squash-settings-enforcer';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, DETAIL_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\nimport {\n ChecklistCommentRenderer, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/checklist-comment-renderer';\nimport { ChecklistCommentRow } from '../../dashboard/checklist-comment-row';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * The three inputs the PR COMMENTS are rendered from, bundled so `publishAll` takes one parameter for\n * them instead of three it only forwards. Data-only, per CLAUDE.md.\n */\nclass PrCommentSources {\n scan: ChecklistScan;\n review: ReviewJson;\n provenance: ProvenanceReport;\n\n constructor(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport) {\n this.scan = scan;\n this.review = review;\n this.provenance = provenance;\n }\n}\n\n/**\n * Everything the PR upsert needs. Data-only, per CLAUDE.md — it replaced a 5-positional-parameter method\n * once `tokenSuffix` had to travel too, so the body can be re-rendered with the PR's own URL after\n * `gh pr create` returns it (see the INVARIANT comment in upsertPr).\n */\nclass PrUpsertRequest {\n repoRoot = '';\n baseBranch = '';\n title = '';\n /** The description as first published — rendered with whatever URL was known at the time. */\n body = '';\n /**\n * The hidden gate-token marker exactly as appended to `body`, so a re-render can reproduce the same\n * bytes. Kept separate from `body` because Dashboard renders the human/git-log half and must not know\n * about HMACs.\n */\n tokenSuffix = '';\n input: DashboardInput;\n\n constructor(input: DashboardInput) {\n this.input = input;\n }\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 + web URL ('' each when unresolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success. The\n// URL is carried so the closing block can hand the AI a clickable link to relay to the user verbatim.\nclass UpsertResult {\n prNumber: string;\n prUrl: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, prUrl: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.merge = merge;\n }\n}\n\n// STAGE ③ — FINISH of the AI-first PR flow, and the ONLY command that posts PRs. Runs after\n// `wp-review-upsert-pr` verified the branch and the AI spawned the reviewers + wrote review.json.\n//\n// In order: (1) REFUSE on an unvalidated 3-point merge and REQUIRE stage ②'s receipt; (2) REQUIRE\n// review.json + every reviewer's verdict + provenance; (3) run the build gate UNLESS the receipt already\n// covers this exact HEAD; (4) render the dashboard; (5) create/update the PR via `gh`.\n//\n// It no longer FINALIZES a merge — stage ② does. Two commands owning conflict-resolution validation is two\n// implementations that drift, and finalizing here came too late to help anyway: the reviewers had already\n// reviewed the pre-merge tree by then.\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 buildLog: BuildGateLog,\n private readonly mergeState: MergeState,\n private readonly prMerger: PrMerger,\n private readonly publisher: GatedPrPublisher,\n private readonly dashboard: Dashboard,\n // The 2nd PR comment. Its own class because it is its own surface — see ChecklistCommentRenderer.\n private readonly checklistComment: ChecklistCommentRenderer,\n private readonly checklistScanner: ChecklistScanner,\n private readonly verdictGate: ReviewerVerdictGate,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n // Owns the reviewer-provenance integrity check and its audit record — see ProvenanceEnforcer.\n private readonly provenanceEnforcer: ProvenanceEnforcer,\n // NOTE: ChecklistInstructionsService is deliberately NOT injected here any more. Its \"You MUST run\n // these N reviewer subagent(s)\" block is now rendered by ReviewerVerdictGate and ONLY for checklists\n // that genuinely never ran, so no other code path in this command can print it at a refusal.\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\n // `gh pr merge --body-file` takes a path, so the bytes just published as the PR description are\n // spilled to a throwaway file for the length of one `gh` call. Nothing durable — the PR holds it.\n private readonly mergeBodyFile: MergeBodyTempFile,\n // ONE marker-keyed upsert, shared by both PR comments (the full dashboard and the checklist).\n private readonly commentUpserter: PrCommentUpserter,\n // Pins the two GitHub repo settings that decide whether a UI merge writes the body we just\n // rendered. Server-side, so no config can express them — see SquashSettingsEnforcer.\n private readonly squashSettings: SquashSettingsEnforcer,\n private readonly stageConsole: StageOutputLog,\n ) {}\n\n /**\n * Runs with this stage's console CAPTURED to a file (see StageOutputLog). What still reaches the\n * terminal is what an agent must act on: the build heartbeat and result, and the closing banner with\n * the PR link and whatever is still owed. The dashboard computation, the gh round trips and the\n * comment upserts are in the file.\n */\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n await this.stageConsole.withCapture(\n repoRoot, FINISH_CONSOLE_LOG, (): Promise<void> => this.runStage(repoRoot));\n }\n\n private async runStage(repoRoot: string): Promise<void> {\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 // 1. REQUIRE stage ② — see assertStageTwoRan. Returns true when its receipt covers THIS commit,\n // which is what lets the build gate below be skipped rather than re-run for a foregone answer.\n const buildAlreadyGreen = this.assertStageTwoRan(repoRoot);\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 // The SAME scan wp-review-upsert-pr runs, with filterAlreadyReviewed:true so `outstanding` is exactly what\n // still owes a verdict (Z of N of X). Sharing the computation is the point: the command that REPORTS\n // and the command that GATES must not be able to disagree about what is owed. review-<id>.json files\n // persist locally, so a re-run re-validates the EXISTING verdicts against the (possibly changed)\n // applicable set for free — an unchanged checklist needs no re-review, a newly-applicable one refuses\n // until its file is written.\n const featureName = this.aiBranchName.getFeatureName();\n const scan = this.checklistScanner.scan(repoRoot, loadAndValidate(repoRoot).prGate.checklists, new ChecklistScanOptions(true, 'stage3-finish'));\n const required = scan.applicable;\n // The applicable checklists that are supposed to HAVE a verdict — everything except the optional ones\n // nobody ran. Used for provenance and for the dashboard rows, both of which ask \"who reviewed this?\"\n // and would otherwise demand an answer from a review the human declined: provenance would refuse the\n // PR because a subagent that was never meant to run cannot be proven to have run, and the dashboard\n // would render a MISSING verdict for it.\n const verdicted = this.verdictedOf(scan);\n // FAIL FAST on an unclear checklist, BEFORE review.json is parsed and before the build gate runs. A\n // missing reviewer is not a review.json defect (folding it in made the AI fix the wrong thing), and\n // nobody should wait on a build to be told a reviewer never ran. ReviewerVerdictGate owns the\n // distinction between unreadable / REFUSED / never-ran, and retires the red verdicts it acts on.\n this.verdictGate.assertEveryReviewerRan(scan);\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, featureName), 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 provenance = this.provenanceEnforcer.enforce(verdicted, currentBranch, repoRoot, loadAndValidate(repoRoot).prGate);\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. Build gate, then post the gated body, then push (that ORDER — see GatedPrPublisher).\n await this.runOrSkipBuildGate(repoRoot, buildAlreadyGreen);\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\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, verdicted, scan);\n const result = this.publishAll(repoRoot, base, input, new PrCommentSources(scan, review, provenance));\n this.archiveConsumedReview(repoRoot, featureName, result);\n\n // The closing recap + the clickable-link directive, BOTH derived from the real merge outcome.\n // Nothing here may hard-code success: a stranded PR under a green checkmark is how PRs got\n // abandoned (see FinishBanner).\n const bannerInput = new FinishBannerInput(result.prNumber, result.prUrl, title, base, result.merge);\n // `say`: the PR link and what is still owed are the whole reason a caller ran this command, and a\n // not-done outcome carries the commands that fix it. Neither may end up only in a log file.\n this.stageConsole.say(this.banner.render(bannerInput));\n this.stageConsole.say(this.banner.linkDirective(bannerInput));\n }\n\n // Validate + commit + finalize a 3-point merge the AI resolved, if one is in progress. Finalizing here\n // does NOT push (pushRemote=false): this command pushes exactly ONCE, from GatedPrPublisher, and only\n // after review.json + every BLOCK checklist + the build gate pass and the gated PR body is written.\n /**\n * The build gate — SKIPPED when stage ②'s receipt already covers this exact HEAD.\n *\n * The gate is authoritative per-COMMIT, not per-command: re-running it on a tree that has not moved\n * since stage ② verified it buys nothing and costs a full `nx affected`. That skip is what makes the\n * three-stage flow cost ONE build rather than two, and it is why moving the gate earlier was affordable.\n */\n private async runOrSkipBuildGate(repoRoot: string, alreadyGreen: boolean): Promise<void> {\n if (alreadyGreen) {\n // `say`, for the same reason the gate's own two lines are: a caller watching for a build needs\n // to be told, live, that there is not going to be one.\n this.stageConsole.say('\\n🛠️ Build gate: already green for this commit (stage ② receipt) — skipping the rebuild.\\n'\n + this.skippedBuildLogNote(repoRoot));\n return;\n }\n await this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n FINISH_STAGE,\n ));\n }\n\n /**\n * When the gate is SKIPPED and build-log capture is on, name STAGE ②'s log — the one build that\n * actually ran for this commit. Deliberately never names a finish-stage log: no finish build ran, and\n * pointing at a file this command did not write would claim a build that did not happen.\n *\n * The path is recomputed rather than stored, and that is sound precisely BECAUSE this branch is only\n * reached when the receipt covers the current HEAD: same worktree, same branch, same sha ⇒ the same\n * deterministic filename stage ② wrote. Empty string when the file is gone — a log that was reaped or\n * never written must not be named as though a reader could open it.\n */\n private skippedBuildLogNote(repoRoot: string): string {\n const log = this.buildLog.existingLogFor(repoRoot, REVIEW_STAGE);\n return log === '' ? '' : ` Stage ②'s full build output: ${log}\\n`;\n }\n\n /**\n * The two stage-② preconditions, together: no unvalidated merge, and a receipt proving stage ② ran.\n * Returns true when that receipt covers the CURRENT HEAD, i.e. the build gate can be skipped.\n */\n private assertStageTwoRan(repoRoot: string): boolean {\n this.assertNoUnvalidatedMerge(repoRoot);\n return this.assertReviewStageRan(\n repoRoot, this.aiBranchName.getFeatureName(), this.gitOut(['rev-parse', 'HEAD']));\n }\n\n /**\n * REFUSE while a 3-point merge is still unvalidated — do not finalize it here.\n *\n * This command used to do the finalizing itself. Finalizing means validating a conflict resolution, and\n * two commands owning that means two implementations that can drift; `PrContextWriter`'s docstring\n * records this codebase already paying for exactly that once. It also came too late to matter: by the\n * time finish ran, the reviewers had already reviewed the pre-merge tree.\n *\n * So stage ② owns it, and finish only checks. Nothing is lost — the recovery is one command away, and\n * running it also builds the merged tree and re-briefs the reviewers against it, which finalizing here\n * never did.\n */\n private assertNoUnvalidatedMerge(repoRoot: string): void {\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (!activeDir || !marker || marker.validated) return;\n throw new InformAiError(\n '⛔ NO PR — a 3-point merge on this branch is still unvalidated, so nothing here has been\\n' +\n 'verified: the conflict resolution is unchecked, the merged tree was never built, and any\\n' +\n 'reviewer that ran judged the PRE-merge code.\\n\\n' +\n `Conflicted file(s): ${marker.conflictedFiles.join(', ')}\\n\\n` +\n 'Resolve them (see .webpieces/instruct-ai/webpieces.mergeprocess.md), then run:\\n' +\n ' pnpm wp-review-upsert-pr\\n' +\n 'It validates the resolution, commits it, builds it, and re-briefs the reviewers.',\n );\n }\n\n /**\n * REQUIRE stage ② (`wp-review-upsert-pr`) to have run, and report whether it ran on THIS commit.\n *\n * Returns true when the receipt matches the current HEAD — the caller then skips its own build, because\n * the receipt IS the gate for that sha and rebuilding an unchanged tree is a second full `nx affected`\n * run for a foregone answer.\n *\n * Without this check a repo with NO checklists has nothing forcing stage ②: `assertEveryReviewerRan`\n * is vacuous, and review.json — the only other interlock — is a file the AI writes itself. It could\n * write it and come straight here, skipping the merge validation and the build entirely.\n */\n private assertReviewStageRan(repoRoot: string, featureName: string, headSha: string): boolean {\n const receipt = this.receipts.read(repoRoot, featureName);\n if (receipt === null) {\n throw new InformAiError(\n '⛔ NO PR — stage ② never ran on this branch. That means the 3-point merge is unvalidated,\\n' +\n 'the build gate has not run, no diff was extracted, and no reviewer was briefed.\\n\\n' +\n 'Run: pnpm wp-review-upsert-pr\\n' +\n '(then spawn the reviewers it names, write review.json, and re-run this command)',\n );\n }\n if (receipt.headSha === headSha) return true;\n // Not fatal. Re-reviewing on every follow-up commit would be intolerable, and most drift is a typo\n // fix. But it is never silent: the build re-runs here, and the PR says the reviewers saw an older tree.\n process.stderr.write(\n `\\n⚠️ HEAD moved since stage ② ran (reviewed ${receipt.headSha.slice(0, 8)}, now ${headSha.slice(0, 8)}).\\n` +\n ' The build gate will re-run, and the PR will record that reviewers judged an earlier tree.\\n' +\n ' If the change was substantive, re-run pnpm wp-review-upsert-pr and re-spawn the reviewers.\\n\\n');\n return false;\n }\n\n /**\n * Retire the review this run just used — move review.json to old-review.json beside it, stamped as\n * audit-only (see ReviewJsonService.archiveReviewJson).\n *\n * WHY it moves rather than staying put: review.json left behind is a live-looking file describing a\n * review that has already shipped, and the next `wp-review-upsert-pr` on this branch finds it sitting\n * there. A reviewer subagent that judges the PR's stated INTENT — its title, summary or risk level —\n * then reads the previous round's review and can return GREEN against a title that no longer exists,\n * with nothing in the verdict distinguishing that from a real pass. Moving it makes the only route back\n * to this command a freshly-written review.\n *\n * ONLY on a PR that actually went up. A run that died before publishing must stay re-runnable without\n * making the AI rewrite a review that was never used, and prNumber === '' is exactly that case.\n * Non-fatal: the PR is live by here, so an unwritable archive warns rather than failing the command.\n */\n private archiveConsumedReview(repoRoot: string, featureName: string, result: UpsertResult): void {\n if (result.prNumber === '') return;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: the PR is already up; a failed archive must not fail the command\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const archived = this.reviewJsonService.archiveReviewJson(reviewJsonPath(repoRoot, featureName));\n if (archived !== '') process.stdout.write(` archived this run's review.json → ${archived} (audit only) ✓\\n`);\n // Beside it, so an archived review keeps the transcript links belonging to the round that\n // produced it — a review whose provenance was overwritten by the NEXT round audits nothing.\n this.provenanceEnforcer.archiveRecord(prDirFor(repoRoot, featureName));\n } catch (err: unknown) {\n const error = toError(err);\n process.stderr.write(`⚠️ Could not archive review.json (non-fatal — the PR is already up): ${error.message}\\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[], scan: ChecklistScan): 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 // buildCommand travels into the dashboard so the PR-body footer can NAME the command that vouched\n // for this commit. The footer used to assert \"build ran via nx affected\" on every repo, which was\n // simply false wherever buildCommand is not nx.\n // `scan.suppressed.length`, never `scan.reviewersDisabled` alone: the dashboard and the commit\n // body have to state HOW MANY reviewers were killed, because a suppressed 4 and an applicable 0\n // are different facts that both render as an empty `rows`. See DashboardInput.\n return new DashboardInput(\n title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows,\n config.buildCommand, scan.suppressed.length);\n }\n\n /**\n * The applicable checklists MINUS the optional ones nobody ran.\n *\n * Not folded into `ChecklistScan` as yet another field: `applicable` (what matched) and `optionalNotRun`\n * (what was declined) are both facts about the scan, whereas this is one command's view of them, and the\n * roster comment deliberately wants the OTHER view — it lists the declined ones precisely so the PR does\n * not imply they passed.\n */\n private verdictedOf(scan: ChecklistScan): RequiredChecklist[] {\n const skipped = new Set(scan.optionalNotRun.map((r: RequiredChecklist): string => r.id));\n return scan.applicable.filter((r: RequiredChecklist): boolean => !skipped.has(r.id));\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard.\n //\n // A checklist reaching this point is always PASS, WARN or OVERRIDDEN, and the reason is ReviewerVerdictGate\n // — NOT loadReviewJson. The gate runs one line earlier and throws on every other state (FAIL, MISSING,\n // BAD_FORMAT), so loadReviewJson's own checklist validation can no longer be the thing that rejects them;\n // it re-validates the same set and finds it clean. This comment used to credit loadReviewJson, which made\n // the ordering look deliberate while the gate's generic \"no verdict yet\" message masked every refusal.\n // WARN belongs in that list: yellow SHIPS, so it is not outstanding and reaches the dashboard.\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 /**\n * One comment row per DEFINED checklist — matched or not — pairing the roster's match evidence with the\n * resolved verdict. Built from `scan.roster` rather than from the applicable set: the skipped ones are\n * absent from `applicable` by construction, and their \"why not\" evidence (the configured globs and the\n * changed-file total) exists nowhere else without recomputing the diff a second way.\n */\n private commentRows(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): ChecklistCommentRow[] {\n // agentType -> did it open the diff. Absent ⇒ not assessed, which prints nothing (see ChecklistCommentRow.diffRead).\n const readByAgent = new Map<string, boolean>();\n for (const e of provenance.evidence) readByAgent.set(e.agentType, e.readDiff);\n return scan.roster.entries.map((entry: TriggeredChecklist): ChecklistCommentRow => {\n const ran = entry.matchedFiles.length > 0;\n const req = new RequiredChecklist(\n entry.def.id, entry.def.subagent, entry.def.doc, entry.matchedFiles, entry.matchedPatterns,\n entry.def.required);\n // A skipped checklist has no verdict to resolve — asking for one would report it as MISSING,\n // i.e. as an unreviewed obligation, when in fact it never had one.\n const verdict = ran\n ? this.reviewJsonService.resolveVerdict(req, review.results)\n : new ChecklistVerdict(entry.def.id, '', '');\n const identity = review.results.find((result): boolean => result.id === entry.def.id);\n const row = new ChecklistCommentRow(identity?.agent ?? 'unknown', identity?.model ?? 'unknown',\n entry.def.subagent, verdict.status, verdict.detail, ran,\n entry.def.patterns, entry.matchedPatterns, entry.matchedFiles, scan.roster.changedFileCount);\n row.required = entry.def.required;\n const read = readByAgent.get(entry.def.subagent);\n row.diffRead = read === undefined ? '' : (read ? 'yes' : 'no');\n return row;\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\n /**\n * Publish the roster + every reviewer's full `output` as ONE combined PR comment, idempotently (find the\n * marker comment → PATCH it, else POST). Never fatal: by here the PR is already created/updated, so a\n * `gh` failure only warns.\n *\n * Posted on EVERY run of a repo that defines checklists — including one where nothing matched, because\n * \"all five were evaluated and none applied\" is the good news the old comment could not deliver. The\n * guard is `scan.defined.length`, deliberately NOT the number of rows that ran: a repo with no\n * checklists configured must still see no comment at all (see ChecklistCommentRenderer).\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private postChecklistComment(repoRoot: string, prNumber: string, scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): void {\n if (prNumber === '' || scan.defined.length === 0) return;\n if (!loadAndValidate(repoRoot).prGate.checklistComments) return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = CHECKLIST_COMMENT_MARKER;\n request.body = this.checklistComment.render(\n this.commentRows(scan, review, provenance), provenance.verified, scan.roster.baseResolved,\n scan.suppressed.length);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'checklist-comment.json';\n request.label = 'checklist review comment';\n this.commentUpserter.upsert(request);\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 // GatedPrPublisher owns the edit/push/create half and its ORDERING — the gated body goes up before\n // the push, so CI's `synchronize` read cannot see the previous run's token.\n private upsertPr(request: PrUpsertRequest): UpsertResult {\n const repoRoot = request.repoRoot;\n const baseBranch = request.baseBranch;\n const title = request.title;\n const input = request.input;\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, request.body + '\\n');\n\n const published = this.publisher.publish(baseBranch, title, bodyFile);\n if (published.createFailed) {\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)', MERGE_RESULT_FAILED));\n }\n const num = published.number;\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\n /*\n * THE INVARIANT: the merge body IS the PR description, byte for byte.\n *\n * That is what makes every landing route agree — the GitHub Merge button and a bare `gh pr merge`\n * copy the description via `squash_merge_commit_message: PR_BODY`, while `wp-land-pr` and finish's\n * own auto-merge pass these bytes with `--body-file`. If the two strings could differ, which route\n * landed the commit would change what history says, which is precisely the bug this whole change\n * exists to remove. `pr-body-is-merge-body.spec.ts` pins it.\n *\n * The one wrinkle is the self-link. A brand-new PR has no URL until `gh pr create` returns, so the\n * body published a moment ago was rendered with `prUrl: ''`. Re-render now that the URL is known\n * and push the corrected description back, so the two strings match and the commit carries its own\n * link. Only the CREATE path pays that extra edit: on every later run the URL was already known\n * before publishing, `finalBody` matches what went up, and nothing is re-sent.\n */\n const finalBody = this.dashboard.renderPrBody(input, ref.url) + request.tokenSuffix;\n if (finalBody !== request.body) this.backfillPrBody(ref.number, bodyFile, finalBody);\n\n // The DURABLE copy of these bytes is the PR description published above; this is a throwaway\n // file because `--body-file` takes a path. `wp-land-pr` reads the description back from GitHub,\n // so nothing here has to survive the process (see decisions/0005).\n const mergeBodyFile = this.mergeBodyFile.write(finalBody + '\\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 // `false`: this caller is POLICY-driven — only a config that really says AUTO merges here.\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, new MergeIntent(mergeMode, false));\n return new UpsertResult(ref.number !== '' ? ref.number : num, ref.url, outcome);\n }\n\n /**\n * Everything the gated flow PUBLISHES, in the one order that is safe — the three surfaces plus the\n * merge, as a single unit.\n *\n * 1. the PR DESCRIPTION = the compact git-log body (+ the hidden gate token), written before the\n * push so CI's `synchronize` read can never see the previous run's token,\n * 2. the 1st comment = the full dashboard,\n * 3. the 2nd comment = each reviewer's output.\n *\n * The token is bound to the LOCAL HEAD sha and minted here because we only reach this line after the\n * build gate and every BLOCK checklist passed, so minting is legitimate. Nothing about\n * HMAC(salt, HEAD) needs the remote to have the commit yet.\n *\n * Extracted from `run` when the two comments made it 82 lines against a hard 80-line rule — and it\n * earns its own name: these four steps have a REQUIRED order, and the comment explaining that order\n * belongs with them rather than inside a method that also loads config and runs a build gate.\n */\n private publishAll(repoRoot: string, base: string, input: DashboardInput, sources: PrCommentSources): UpsertResult {\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const upsert = new PrUpsertRequest(input);\n upsert.repoRoot = repoRoot;\n upsert.baseBranch = base;\n upsert.title = input.title;\n upsert.tokenSuffix = this.gateTokenBody(gateSalt, headSha);\n // `existingPrUrl` is '' only for a brand-new PR — there is no URL to self-link to until\n // `gh pr create` returns one, and upsertPr back-fills it the moment it does.\n upsert.body = this.dashboard.renderPrBody(input, this.existingPrUrl(base)) + upsert.tokenSuffix;\n const result = this.upsertPr(upsert);\n\n // In the order the PR body's pointer promises: full dashboard 1st, reviewer output 2nd. Both are\n // idempotent by hidden marker and both non-fatal — the PR is up by now, so a `gh` hiccup on a\n // comment must not turn a finished run into a failed one.\n this.postDetailComment(repoRoot, result.prNumber, input);\n this.postChecklistComment(repoRoot, result.prNumber, sources.scan, sources.review, sources.provenance);\n\n // Having just written the description AS the commit body, make sure GitHub will actually use it.\n // Here rather than anywhere earlier because it is about what happens to the artifact we just\n // published, and it must run on EVERY repo — including mergeMode=NONE ones that never reach\n // PrMerger, which are exactly the repos where a UI merge decides what main's history says.\n this.squashSettings.ensure();\n return result;\n }\n\n /**\n * Re-publish the description now that the PR's own URL is known (create path only).\n *\n * Non-fatal on purpose. The PR exists, its body already carries a valid gate token for the pushed\n * head, and the code is up — the ONLY thing missing is the self-link on the first line. Aborting a\n * finished run over a cosmetic back-link would be a worse outcome than the missing link, and the very\n * next `wp-finish-upsert-pr` writes it (by then the URL is known before publishing, so it goes up with\n * the body). The merge body still gets the linked version regardless: it is filed from `finalBody`.\n */\n private backfillPrBody(prNumber: string, bodyFile: string, finalBody: string): void {\n if (prNumber === '') return;\n fs.writeFileSync(bodyFile, finalBody + '\\n');\n const res = spawnSync('gh', ['pr', 'edit', prNumber, '--body-file', bodyFile], { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write(\n '⚠️ Could not add the PR\\'s own link to its description (non-fatal — the PR and the gate\\n' +\n ' token are already up). The next wp-finish-upsert-pr writes it.\\n');\n return;\n }\n process.stdout.write(' back-filled the PR description with its own link ✓\\n');\n }\n\n /**\n * The PR's web URL if one is already open for this branch, else '' — read BEFORE publishing so the\n * description can carry its own link on the first render. Only a brand-new PR gets '' (and then\n * {@link backfillPrBody} fixes it up after `gh pr create` returns a URL).\n */\n private existingPrUrl(baseBranch: string): string {\n return this.prRef(baseBranch).url;\n }\n\n /**\n * The 1st PR comment: the FULL dashboard — every row including the green ones, the whole summary, the\n * 3-point hash points. This is what the PR description used to be, and moving it here is the entire\n * point of the change: the description is now the git-log body, and a squash commit must not carry a\n * risk table.\n *\n * Posted unconditionally (any repo, checklists or not), because the description's last bullet PROMISES\n * a 1st comment holding the detail. A pointer to a comment that does not exist is worse than no\n * pointer. Non-fatal — the PR is already up, and the description alone is a complete, readable record.\n */\n private postDetailComment(repoRoot: string, prNumber: string, input: DashboardInput): void {\n if (prNumber === '') return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = DETAIL_COMMENT_MARKER;\n request.body = DETAIL_COMMENT_MARKER + '\\n' + this.dashboard.renderDetailComment(input);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'detail-comment.json';\n request.label = 'full dashboard comment';\n this.commentUpserter.upsert(request);\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, ChecklistVerdict,\n writeTemplate, RepoRootFinder, ReviewJsonService, GateTokenService,\n InformAiError, toError,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { AiBranchName } from '../workflow/git-readAiBranchName';\nimport { BranchNaming } from '../workflow/branch-naming';\nimport { ChecklistScan, ChecklistScanOptions, ChecklistScanner } from '../workflow/checklist-scanner';\nimport { ReviewerVerdictGate } from '../workflow/reviewer-verdict-gate';\nimport { GitExec } from '../workflow/git-exec';\nimport { BuildAffected, BuildGateOptions } from '../workflow/build-affected';\nimport { BuildGateLog, FINISH_STAGE, REVIEW_STAGE } from '../workflow/build-gate-log';\nimport { MergeState } from '../workflow/merge-state';\nimport { ReviewStageReceiptService } from '../workflow/review-stage-receipt';\nimport { StageOutputLog, FINISH_CONSOLE_LOG } from '../workflow/stage-output-log';\nimport { PrMerger, MergeIntent, MergeOutcome, MERGE_RESULT_FAILED } from '../workflow/pr-merger';\nimport { FinishBanner, FinishBannerInput } from '../workflow/finish-banner';\nimport { MergeBodyTempFile } from '../workflow/merge-body-temp-file';\nimport { GatedPrPublisher } from '../workflow/gated-pr-publisher';\nimport { ProvenanceEnforcer, ProvenanceReport } from '../workflow/provenance-enforcer';\nimport { PrCommentRequest, PrCommentUpserter } from '../workflow/pr-comment-upserter';\nimport { SquashSettingsEnforcer } from '../workflow/squash-settings-enforcer';\nimport { TriggeredChecklist } from '../workflow/checklist-detector';\nimport {\n Dashboard, DashboardInput, ChecklistRow, DETAIL_COMMENT_MARKER,\n} from '../../dashboard/dashboard';\nimport {\n ChecklistCommentRenderer, CHECKLIST_COMMENT_MARKER,\n} from '../../dashboard/checklist-comment-renderer';\nimport { ChecklistCommentRow } from '../../dashboard/checklist-comment-row';\nimport { AuthorIdentityResolver } from '../../dashboard/author-identity';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * The three inputs the PR COMMENTS are rendered from, bundled so `publishAll` takes one parameter for\n * them instead of three it only forwards. Data-only, per CLAUDE.md.\n */\nclass PrCommentSources {\n scan: ChecklistScan;\n review: ReviewJson;\n provenance: ProvenanceReport;\n\n constructor(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport) {\n this.scan = scan;\n this.review = review;\n this.provenance = provenance;\n }\n}\n\n/**\n * Everything the PR upsert needs. Data-only, per CLAUDE.md — it replaced a 5-positional-parameter method\n * once `tokenSuffix` had to travel too, so the body can be re-rendered with the PR's own URL after\n * `gh pr create` returns it (see the INVARIANT comment in upsertPr).\n */\nclass PrUpsertRequest {\n repoRoot = '';\n baseBranch = '';\n title = '';\n /** The description as first published — rendered with whatever URL was known at the time. */\n body = '';\n /**\n * The hidden gate-token marker exactly as appended to `body`, so a re-render can reproduce the same\n * bytes. Kept separate from `body` because Dashboard renders the human/git-log half and must not know\n * about HMACs.\n */\n tokenSuffix = '';\n input: DashboardInput;\n\n constructor(input: DashboardInput) {\n this.input = input;\n }\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 + web URL ('' each when unresolved) plus what actually\n// happened to the merge, so the final summary reports the REAL result rather than assuming success. The\n// URL is carried so the closing block can hand the AI a clickable link to relay to the user verbatim.\nclass UpsertResult {\n prNumber: string;\n prUrl: string;\n merge: MergeOutcome;\n\n constructor(prNumber: string, prUrl: string, merge: MergeOutcome) {\n this.prNumber = prNumber;\n this.prUrl = prUrl;\n this.merge = merge;\n }\n}\n\n// STAGE ③ — FINISH of the AI-first PR flow, and the ONLY command that posts PRs. Runs after\n// `wp-review-upsert-pr` verified the branch and the AI spawned the reviewers + wrote review.json.\n//\n// In order: (1) REFUSE on an unvalidated 3-point merge and REQUIRE stage ②'s receipt; (2) REQUIRE\n// review.json + every reviewer's verdict + provenance; (3) run the build gate UNLESS the receipt already\n// covers this exact HEAD; (4) render the dashboard; (5) create/update the PR via `gh`.\n//\n// It no longer FINALIZES a merge — stage ② does. Two commands owning conflict-resolution validation is two\n// implementations that drift, and finalizing here came too late to help anyway: the reviewers had already\n// reviewed the pre-merge tree by then.\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 buildLog: BuildGateLog,\n private readonly mergeState: MergeState,\n private readonly prMerger: PrMerger,\n private readonly publisher: GatedPrPublisher,\n private readonly dashboard: Dashboard,\n private readonly authorIdentity: AuthorIdentityResolver,\n // The 2nd PR comment. Its own class because it is its own surface — see ChecklistCommentRenderer.\n private readonly checklistComment: ChecklistCommentRenderer,\n private readonly checklistScanner: ChecklistScanner,\n private readonly verdictGate: ReviewerVerdictGate,\n private readonly reviewJsonService: ReviewJsonService,\n private readonly gateTokenService: GateTokenService,\n // Owns the reviewer-provenance integrity check and its audit record — see ProvenanceEnforcer.\n private readonly provenanceEnforcer: ProvenanceEnforcer,\n // NOTE: ChecklistInstructionsService is deliberately NOT injected here any more. Its \"You MUST run\n // these N reviewer subagent(s)\" block is now rendered by ReviewerVerdictGate and ONLY for checklists\n // that genuinely never ran, so no other code path in this command can print it at a refusal.\n private readonly receipts: ReviewStageReceiptService,\n private readonly banner: FinishBanner,\n // `gh pr merge --body-file` takes a path, so the bytes just published as the PR description are\n // spilled to a throwaway file for the length of one `gh` call. Nothing durable — the PR holds it.\n private readonly mergeBodyFile: MergeBodyTempFile,\n // ONE marker-keyed upsert, shared by both PR comments (the full dashboard and the checklist).\n private readonly commentUpserter: PrCommentUpserter,\n // Pins the two GitHub repo settings that decide whether a UI merge writes the body we just\n // rendered. Server-side, so no config can express them — see SquashSettingsEnforcer.\n private readonly squashSettings: SquashSettingsEnforcer,\n private readonly stageConsole: StageOutputLog,\n ) {}\n\n /**\n * Runs with this stage's console CAPTURED to a file (see StageOutputLog). What still reaches the\n * terminal is what an agent must act on: the build heartbeat and result, and the closing banner with\n * the PR link and whatever is still owed. The dashboard computation, the gh round trips and the\n * comment upserts are in the file.\n */\n async run(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n await this.stageConsole.withCapture(\n repoRoot, FINISH_CONSOLE_LOG, (): Promise<void> => this.runStage(repoRoot));\n }\n\n private async runStage(repoRoot: string): Promise<void> {\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 // 1. REQUIRE stage ② — see assertStageTwoRan. Returns true when its receipt covers THIS commit,\n // which is what lets the build gate below be skipped rather than re-run for a foregone answer.\n const buildAlreadyGreen = this.assertStageTwoRan(repoRoot);\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 // The SAME scan wp-review-upsert-pr runs, with filterAlreadyReviewed:true so `outstanding` is exactly what\n // still owes a verdict (Z of N of X). Sharing the computation is the point: the command that REPORTS\n // and the command that GATES must not be able to disagree about what is owed. review-<id>.json files\n // persist locally, so a re-run re-validates the EXISTING verdicts against the (possibly changed)\n // applicable set for free — an unchanged checklist needs no re-review, a newly-applicable one refuses\n // until its file is written.\n const featureName = this.aiBranchName.getFeatureName();\n const scan = this.checklistScanner.scan(repoRoot, loadAndValidate(repoRoot).prGate.checklists, new ChecklistScanOptions(true, 'stage3-finish'));\n const required = scan.applicable;\n // The applicable checklists that are supposed to HAVE a verdict — everything except the optional ones\n // nobody ran. Used for provenance and for the dashboard rows, both of which ask \"who reviewed this?\"\n // and would otherwise demand an answer from a review the human declined: provenance would refuse the\n // PR because a subagent that was never meant to run cannot be proven to have run, and the dashboard\n // would render a MISSING verdict for it.\n const verdicted = this.verdictedOf(scan);\n // FAIL FAST on an unclear checklist, BEFORE review.json is parsed and before the build gate runs. A\n // missing reviewer is not a review.json defect (folding it in made the AI fix the wrong thing), and\n // nobody should wait on a build to be told a reviewer never ran. ReviewerVerdictGate owns the\n // distinction between unreadable / REFUSED / never-ran, and retires the red verdicts it acts on.\n this.verdictGate.assertEveryReviewerRan(scan);\n const review = this.reviewJsonService.loadReviewJson(reviewJsonPath(repoRoot, featureName), 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 provenance = this.provenanceEnforcer.enforce(verdicted, currentBranch, repoRoot, loadAndValidate(repoRoot).prGate);\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. Build gate, then post the gated body, then push (that ORDER — see GatedPrPublisher).\n await this.runOrSkipBuildGate(repoRoot, buildAlreadyGreen);\n const base = this.branchNaming.baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\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, verdicted, scan);\n const result = this.publishAll(repoRoot, base, input, new PrCommentSources(scan, review, provenance));\n this.archiveConsumedReview(repoRoot, featureName, result);\n\n // The closing recap + the clickable-link directive, BOTH derived from the real merge outcome.\n // Nothing here may hard-code success: a stranded PR under a green checkmark is how PRs got\n // abandoned (see FinishBanner).\n const bannerInput = new FinishBannerInput(result.prNumber, result.prUrl, title, base, result.merge);\n // `say`: the PR link and what is still owed are the whole reason a caller ran this command, and a\n // not-done outcome carries the commands that fix it. Neither may end up only in a log file.\n this.stageConsole.say(this.banner.render(bannerInput));\n this.stageConsole.say(this.banner.linkDirective(bannerInput));\n }\n\n // Validate + commit + finalize a 3-point merge the AI resolved, if one is in progress. Finalizing here\n // does NOT push (pushRemote=false): this command pushes exactly ONCE, from GatedPrPublisher, and only\n // after review.json + every BLOCK checklist + the build gate pass and the gated PR body is written.\n /**\n * The build gate — SKIPPED when stage ②'s receipt already covers this exact HEAD.\n *\n * The gate is authoritative per-COMMIT, not per-command: re-running it on a tree that has not moved\n * since stage ② verified it buys nothing and costs a full `nx affected`. That skip is what makes the\n * three-stage flow cost ONE build rather than two, and it is why moving the gate earlier was affordable.\n */\n private async runOrSkipBuildGate(repoRoot: string, alreadyGreen: boolean): Promise<void> {\n if (alreadyGreen) {\n // `say`, for the same reason the gate's own two lines are: a caller watching for a build needs\n // to be told, live, that there is not going to be one.\n this.stageConsole.say('\\n🛠️ Build gate: already green for this commit (stage ② receipt) — skipping the rebuild.\\n'\n + this.skippedBuildLogNote(repoRoot));\n return;\n }\n await this.buildAffected.runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n FINISH_STAGE,\n ));\n }\n\n /**\n * When the gate is SKIPPED and build-log capture is on, name STAGE ②'s log — the one build that\n * actually ran for this commit. Deliberately never names a finish-stage log: no finish build ran, and\n * pointing at a file this command did not write would claim a build that did not happen.\n *\n * The path is recomputed rather than stored, and that is sound precisely BECAUSE this branch is only\n * reached when the receipt covers the current HEAD: same worktree, same branch, same sha ⇒ the same\n * deterministic filename stage ② wrote. Empty string when the file is gone — a log that was reaped or\n * never written must not be named as though a reader could open it.\n */\n private skippedBuildLogNote(repoRoot: string): string {\n const log = this.buildLog.existingLogFor(repoRoot, REVIEW_STAGE);\n return log === '' ? '' : ` Stage ②'s full build output: ${log}\\n`;\n }\n\n /**\n * The two stage-② preconditions, together: no unvalidated merge, and a receipt proving stage ② ran.\n * Returns true when that receipt covers the CURRENT HEAD, i.e. the build gate can be skipped.\n */\n private assertStageTwoRan(repoRoot: string): boolean {\n this.assertNoUnvalidatedMerge(repoRoot);\n return this.assertReviewStageRan(\n repoRoot, this.aiBranchName.getFeatureName(), this.gitOut(['rev-parse', 'HEAD']));\n }\n\n /**\n * REFUSE while a 3-point merge is still unvalidated — do not finalize it here.\n *\n * This command used to do the finalizing itself. Finalizing means validating a conflict resolution, and\n * two commands owning that means two implementations that can drift; `PrContextWriter`'s docstring\n * records this codebase already paying for exactly that once. It also came too late to matter: by the\n * time finish ran, the reviewers had already reviewed the pre-merge tree.\n *\n * So stage ② owns it, and finish only checks. Nothing is lost — the recovery is one command away, and\n * running it also builds the merged tree and re-briefs the reviewers against it, which finalizing here\n * never did.\n */\n private assertNoUnvalidatedMerge(repoRoot: string): void {\n const home = this.mergeState.mergeDirFor(repoRoot, this.aiBranchName.getFeatureName());\n const activeDir = this.mergeState.findActiveMergeRunDir(home);\n const marker = activeDir ? this.mergeState.readMergeMarker(activeDir) : null;\n if (!activeDir || !marker || marker.validated) return;\n throw new InformAiError(\n '⛔ NO PR — a 3-point merge on this branch is still unvalidated, so nothing here has been\\n' +\n 'verified: the conflict resolution is unchecked, the merged tree was never built, and any\\n' +\n 'reviewer that ran judged the PRE-merge code.\\n\\n' +\n `Conflicted file(s): ${marker.conflictedFiles.join(', ')}\\n\\n` +\n 'Resolve them (see .webpieces/instruct-ai/webpieces.mergeprocess.md), then run:\\n' +\n ' pnpm wp-review-upsert-pr\\n' +\n 'It validates the resolution, commits it, builds it, and re-briefs the reviewers.',\n );\n }\n\n /**\n * REQUIRE stage ② (`wp-review-upsert-pr`) to have run, and report whether it ran on THIS commit.\n *\n * Returns true when the receipt matches the current HEAD — the caller then skips its own build, because\n * the receipt IS the gate for that sha and rebuilding an unchanged tree is a second full `nx affected`\n * run for a foregone answer.\n *\n * Without this check a repo with NO checklists has nothing forcing stage ②: `assertEveryReviewerRan`\n * is vacuous, and review.json — the only other interlock — is a file the AI writes itself. It could\n * write it and come straight here, skipping the merge validation and the build entirely.\n */\n private assertReviewStageRan(repoRoot: string, featureName: string, headSha: string): boolean {\n const receipt = this.receipts.read(repoRoot, featureName);\n if (receipt === null) {\n throw new InformAiError(\n '⛔ NO PR — stage ② never ran on this branch. That means the 3-point merge is unvalidated,\\n' +\n 'the build gate has not run, no diff was extracted, and no reviewer was briefed.\\n\\n' +\n 'Run: pnpm wp-review-upsert-pr\\n' +\n '(then spawn the reviewers it names, write review.json, and re-run this command)',\n );\n }\n if (receipt.headSha === headSha) return true;\n // Not fatal. Re-reviewing on every follow-up commit would be intolerable, and most drift is a typo\n // fix. But it is never silent: the build re-runs here, and the PR says the reviewers saw an older tree.\n process.stderr.write(\n `\\n⚠️ HEAD moved since stage ② ran (reviewed ${receipt.headSha.slice(0, 8)}, now ${headSha.slice(0, 8)}).\\n` +\n ' The build gate will re-run, and the PR will record that reviewers judged an earlier tree.\\n' +\n ' If the change was substantive, re-run pnpm wp-review-upsert-pr and re-spawn the reviewers.\\n\\n');\n return false;\n }\n\n /**\n * Retire the review this run just used — move review.json to old-review.json beside it, stamped as\n * audit-only (see ReviewJsonService.archiveReviewJson).\n *\n * WHY it moves rather than staying put: review.json left behind is a live-looking file describing a\n * review that has already shipped, and the next `wp-review-upsert-pr` on this branch finds it sitting\n * there. A reviewer subagent that judges the PR's stated INTENT — its title, summary or risk level —\n * then reads the previous round's review and can return GREEN against a title that no longer exists,\n * with nothing in the verdict distinguishing that from a real pass. Moving it makes the only route back\n * to this command a freshly-written review.\n *\n * ONLY on a PR that actually went up. A run that died before publishing must stay re-runnable without\n * making the AI rewrite a review that was never used, and prNumber === '' is exactly that case.\n * Non-fatal: the PR is live by here, so an unwritable archive warns rather than failing the command.\n */\n private archiveConsumedReview(repoRoot: string, featureName: string, result: UpsertResult): void {\n if (result.prNumber === '') return;\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: the PR is already up; a failed archive must not fail the command\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const archived = this.reviewJsonService.archiveReviewJson(reviewJsonPath(repoRoot, featureName));\n if (archived !== '') process.stdout.write(` archived this run's review.json → ${archived} (audit only) ✓\\n`);\n // Beside it, so an archived review keeps the transcript links belonging to the round that\n // produced it — a review whose provenance was overwritten by the NEXT round audits nothing.\n this.provenanceEnforcer.archiveRecord(prDirFor(repoRoot, featureName));\n } catch (err: unknown) {\n const error = toError(err);\n process.stderr.write(`⚠️ Could not archive review.json (non-fatal — the PR is already up): ${error.message}\\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[], scan: ChecklistScan): 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 // buildCommand travels into the dashboard so the PR-body footer can NAME the command that vouched\n // for this commit. The footer used to assert \"build ran via nx affected\" on every repo, which was\n // simply false wherever buildCommand is not nx.\n // `scan.suppressed.length`, never `scan.reviewersDisabled` alone: the dashboard and the commit\n // body have to state HOW MANY reviewers were killed, because a suppressed 4 and an applicable 0\n // are different facts that both render as an empty `rows`. See DashboardInput.\n return new DashboardInput(\n title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review, rows,\n config.buildCommand, scan.suppressed.length, this.authorIdentity.resolve(review.model));\n }\n\n /**\n * The applicable checklists MINUS the optional ones nobody ran.\n *\n * Not folded into `ChecklistScan` as yet another field: `applicable` (what matched) and `optionalNotRun`\n * (what was declined) are both facts about the scan, whereas this is one command's view of them, and the\n * roster comment deliberately wants the OTHER view — it lists the declined ones precisely so the PR does\n * not imply they passed.\n */\n private verdictedOf(scan: ChecklistScan): RequiredChecklist[] {\n const skipped = new Set(scan.optionalNotRun.map((r: RequiredChecklist): string => r.id));\n return scan.applicable.filter((r: RequiredChecklist): boolean => !skipped.has(r.id));\n }\n\n // Pair each matched checklist with its resolved verdict for the dashboard.\n //\n // A checklist reaching this point is always PASS, WARN or OVERRIDDEN, and the reason is ReviewerVerdictGate\n // — NOT loadReviewJson. The gate runs one line earlier and throws on every other state (FAIL, MISSING,\n // BAD_FORMAT), so loadReviewJson's own checklist validation can no longer be the thing that rejects them;\n // it re-validates the same set and finds it clean. This comment used to credit loadReviewJson, which made\n // the ordering look deliberate while the gate's generic \"no verdict yet\" message masked every refusal.\n // WARN belongs in that list: yellow SHIPS, so it is not outstanding and reaches the dashboard.\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 /**\n * One comment row per DEFINED checklist — matched or not — pairing the roster's match evidence with the\n * resolved verdict. Built from `scan.roster` rather than from the applicable set: the skipped ones are\n * absent from `applicable` by construction, and their \"why not\" evidence (the configured globs and the\n * changed-file total) exists nowhere else without recomputing the diff a second way.\n */\n private commentRows(scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): ChecklistCommentRow[] {\n // agentType -> did it open the diff. Absent ⇒ not assessed, which prints nothing (see ChecklistCommentRow.diffRead).\n const readByAgent = new Map<string, boolean>();\n for (const e of provenance.evidence) readByAgent.set(e.agentType, e.readDiff);\n return scan.roster.entries.map((entry: TriggeredChecklist): ChecklistCommentRow => {\n const ran = entry.matchedFiles.length > 0;\n const req = new RequiredChecklist(\n entry.def.id, entry.def.subagent, entry.def.doc, entry.matchedFiles, entry.matchedPatterns,\n entry.def.required);\n // A skipped checklist has no verdict to resolve — asking for one would report it as MISSING,\n // i.e. as an unreviewed obligation, when in fact it never had one.\n const verdict = ran\n ? this.reviewJsonService.resolveVerdict(req, review.results)\n : new ChecklistVerdict(entry.def.id, '', '');\n const identity = review.results.find((result): boolean => result.id === entry.def.id);\n const row = new ChecklistCommentRow(identity?.agent ?? 'unknown', identity?.model ?? 'unknown',\n entry.def.subagent, verdict.status, verdict.detail, ran,\n entry.def.patterns, entry.matchedPatterns, entry.matchedFiles, scan.roster.changedFileCount);\n row.required = entry.def.required;\n const read = readByAgent.get(entry.def.subagent);\n row.diffRead = read === undefined ? '' : (read ? 'yes' : 'no');\n return row;\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\n /**\n * Publish the roster + every reviewer's full `output` as ONE combined PR comment, idempotently (find the\n * marker comment → PATCH it, else POST). Never fatal: by here the PR is already created/updated, so a\n * `gh` failure only warns.\n *\n * Posted on EVERY run of a repo that defines checklists — including one where nothing matched, because\n * \"all five were evaluated and none applied\" is the good news the old comment could not deliver. The\n * guard is `scan.defined.length`, deliberately NOT the number of rows that ran: a repo with no\n * checklists configured must still see no comment at all (see ChecklistCommentRenderer).\n */\n // eslint-disable-next-line @typescript-eslint/max-params\n private postChecklistComment(repoRoot: string, prNumber: string, scan: ChecklistScan, review: ReviewJson, provenance: ProvenanceReport): void {\n if (prNumber === '' || scan.defined.length === 0) return;\n if (!loadAndValidate(repoRoot).prGate.checklistComments) return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = CHECKLIST_COMMENT_MARKER;\n request.body = this.checklistComment.render(\n this.commentRows(scan, review, provenance), provenance.verified, scan.roster.baseResolved,\n scan.suppressed.length);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'checklist-comment.json';\n request.label = 'checklist review comment';\n this.commentUpserter.upsert(request);\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 // GatedPrPublisher owns the edit/push/create half and its ORDERING — the gated body goes up before\n // the push, so CI's `synchronize` read cannot see the previous run's token.\n private upsertPr(request: PrUpsertRequest): UpsertResult {\n const repoRoot = request.repoRoot;\n const baseBranch = request.baseBranch;\n const title = request.title;\n const input = request.input;\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, request.body + '\\n');\n\n const published = this.publisher.publish(baseBranch, title, bodyFile);\n if (published.createFailed) {\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)', MERGE_RESULT_FAILED));\n }\n const num = published.number;\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\n /*\n * THE INVARIANT: the merge body IS the PR description, byte for byte.\n *\n * That is what makes every landing route agree — the GitHub Merge button and a bare `gh pr merge`\n * copy the description via `squash_merge_commit_message: PR_BODY`, while `wp-land-pr` and finish's\n * own auto-merge pass these bytes with `--body-file`. If the two strings could differ, which route\n * landed the commit would change what history says, which is precisely the bug this whole change\n * exists to remove. `pr-body-is-merge-body.spec.ts` pins it.\n *\n * The one wrinkle is the self-link. A brand-new PR has no URL until `gh pr create` returns, so the\n * body published a moment ago was rendered with `prUrl: ''`. Re-render now that the URL is known\n * and push the corrected description back, so the two strings match and the commit carries its own\n * link. Only the CREATE path pays that extra edit: on every later run the URL was already known\n * before publishing, `finalBody` matches what went up, and nothing is re-sent.\n */\n const finalBody = this.dashboard.renderPrBody(input, ref.url) + request.tokenSuffix;\n if (finalBody !== request.body) this.backfillPrBody(ref.number, bodyFile, finalBody);\n\n // The DURABLE copy of these bytes is the PR description published above; this is a throwaway\n // file because `--body-file` takes a path. `wp-land-pr` reads the description back from GitHub,\n // so nothing here has to survive the process (see decisions/0005).\n const mergeBodyFile = this.mergeBodyFile.write(finalBody + '\\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 // `false`: this caller is POLICY-driven — only a config that really says AUTO merges here.\n const outcome = this.prMerger.merge(baseBranch, subject, mergeBodyFile, new MergeIntent(mergeMode, false));\n return new UpsertResult(ref.number !== '' ? ref.number : num, ref.url, outcome);\n }\n\n /**\n * Everything the gated flow PUBLISHES, in the one order that is safe — the three surfaces plus the\n * merge, as a single unit.\n *\n * 1. the PR DESCRIPTION = the compact git-log body (+ the hidden gate token), written before the\n * push so CI's `synchronize` read can never see the previous run's token,\n * 2. the 1st comment = the full dashboard,\n * 3. the 2nd comment = each reviewer's output.\n *\n * The token is bound to the LOCAL HEAD sha and minted here because we only reach this line after the\n * build gate and every BLOCK checklist passed, so minting is legitimate. Nothing about\n * HMAC(salt, HEAD) needs the remote to have the commit yet.\n *\n * Extracted from `run` when the two comments made it 82 lines against a hard 80-line rule — and it\n * earns its own name: these four steps have a REQUIRED order, and the comment explaining that order\n * belongs with them rather than inside a method that also loads config and runs a build gate.\n */\n private publishAll(repoRoot: string, base: string, input: DashboardInput, sources: PrCommentSources): UpsertResult {\n const gateSalt = loadAndValidate(repoRoot).prGate.gateSalt;\n const headSha = this.gitOut(['rev-parse', 'HEAD']);\n const upsert = new PrUpsertRequest(input);\n upsert.repoRoot = repoRoot;\n upsert.baseBranch = base;\n upsert.title = input.title;\n upsert.tokenSuffix = this.gateTokenBody(gateSalt, headSha);\n // `existingPrUrl` is '' only for a brand-new PR — there is no URL to self-link to until\n // `gh pr create` returns one, and upsertPr back-fills it the moment it does.\n upsert.body = this.dashboard.renderPrBody(input, this.existingPrUrl(base)) + upsert.tokenSuffix;\n const result = this.upsertPr(upsert);\n\n // In the order the PR body's pointer promises: full dashboard 1st, reviewer output 2nd. Both are\n // idempotent by hidden marker and both non-fatal — the PR is up by now, so a `gh` hiccup on a\n // comment must not turn a finished run into a failed one.\n this.postDetailComment(repoRoot, result.prNumber, input);\n this.postChecklistComment(repoRoot, result.prNumber, sources.scan, sources.review, sources.provenance);\n\n // Having just written the description AS the commit body, make sure GitHub will actually use it.\n // Here rather than anywhere earlier because it is about what happens to the artifact we just\n // published, and it must run on EVERY repo — including mergeMode=NONE ones that never reach\n // PrMerger, which are exactly the repos where a UI merge decides what main's history says.\n this.squashSettings.ensure();\n return result;\n }\n\n /**\n * Re-publish the description now that the PR's own URL is known (create path only).\n *\n * Non-fatal on purpose. The PR exists, its body already carries a valid gate token for the pushed\n * head, and the code is up — the ONLY thing missing is the self-link on the first line. Aborting a\n * finished run over a cosmetic back-link would be a worse outcome than the missing link, and the very\n * next `wp-finish-upsert-pr` writes it (by then the URL is known before publishing, so it goes up with\n * the body). The merge body still gets the linked version regardless: it is filed from `finalBody`.\n */\n private backfillPrBody(prNumber: string, bodyFile: string, finalBody: string): void {\n if (prNumber === '') return;\n fs.writeFileSync(bodyFile, finalBody + '\\n');\n const res = spawnSync('gh', ['pr', 'edit', prNumber, '--body-file', bodyFile], { encoding: 'utf8' });\n if (res.status !== 0) {\n process.stderr.write(\n '⚠️ Could not add the PR\\'s own link to its description (non-fatal — the PR and the gate\\n' +\n ' token are already up). The next wp-finish-upsert-pr writes it.\\n');\n return;\n }\n process.stdout.write(' back-filled the PR description with its own link ✓\\n');\n }\n\n /**\n * The PR's web URL if one is already open for this branch, else '' — read BEFORE publishing so the\n * description can carry its own link on the first render. Only a brand-new PR gets '' (and then\n * {@link backfillPrBody} fixes it up after `gh pr create` returns a URL).\n */\n private existingPrUrl(baseBranch: string): string {\n return this.prRef(baseBranch).url;\n }\n\n /**\n * The 1st PR comment: the FULL dashboard — every row including the green ones, the whole summary, the\n * 3-point hash points. This is what the PR description used to be, and moving it here is the entire\n * point of the change: the description is now the git-log body, and a squash commit must not carry a\n * risk table.\n *\n * Posted unconditionally (any repo, checklists or not), because the description's last bullet PROMISES\n * a 1st comment holding the detail. A pointer to a comment that does not exist is worse than no\n * pointer. Non-fatal — the PR is already up, and the description alone is a complete, readable record.\n */\n private postDetailComment(repoRoot: string, prNumber: string, input: DashboardInput): void {\n if (prNumber === '') return;\n const request = new PrCommentRequest();\n request.prNumber = prNumber;\n request.marker = DETAIL_COMMENT_MARKER;\n request.body = DETAIL_COMMENT_MARKER + '\\n' + this.dashboard.renderDetailComment(input);\n request.payloadDir = prDirFor(repoRoot, this.aiBranchName.getFeatureName());\n request.payloadName = 'detail-comment.json';\n request.label = 'full dashboard comment';\n this.commentUpserter.upsert(request);\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"]}
|