@clearlydev/nx-archweave 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/review-verdict.js +8 -249
  3. package/dist/eslint/index.js +28 -321
  4. package/dist/generators/_chunks/chunk-2WSX5YNB.js +274 -0
  5. package/dist/generators/_chunks/chunk-34V6HAJT.js +55 -0
  6. package/dist/generators/_chunks/chunk-3A6RZUCZ.js +114 -0
  7. package/dist/generators/_chunks/chunk-47T7NOPP.js +30 -0
  8. package/dist/generators/_chunks/chunk-4FJ7CLEU.js +189 -0
  9. package/dist/generators/_chunks/chunk-5MI3O4VZ.js +99 -0
  10. package/dist/generators/_chunks/chunk-6ZTIUBB2.js +139 -0
  11. package/dist/generators/_chunks/chunk-AIJCJ7JK.js +203 -0
  12. package/dist/generators/_chunks/chunk-ALSXKTLM.js +1098 -0
  13. package/dist/generators/_chunks/chunk-AQHV7DJS.js +656 -0
  14. package/dist/generators/_chunks/chunk-C2VVUWBA.js +386 -0
  15. package/dist/generators/_chunks/chunk-EXYZLL7A.js +234 -0
  16. package/dist/generators/_chunks/chunk-FQHFT5IH.js +50 -0
  17. package/dist/generators/_chunks/chunk-H222FPLK.js +80 -0
  18. package/dist/generators/_chunks/chunk-IIDOBI42.js +616 -0
  19. package/dist/generators/_chunks/chunk-L2RZ2LBO.js +132 -0
  20. package/dist/generators/_chunks/chunk-MQNQJOWA.js +217 -0
  21. package/dist/generators/_chunks/chunk-QQNDYJTR.js +243 -0
  22. package/dist/generators/_chunks/chunk-SFXU6IUS.js +0 -0
  23. package/dist/generators/_chunks/chunk-SNBIVIMI.js +89 -0
  24. package/dist/generators/_chunks/chunk-TUN34LAQ.js +8 -0
  25. package/dist/generators/_chunks/chunk-TXAQ2GJC.js +20 -0
  26. package/dist/generators/_chunks/chunk-U6LAFHSF.js +49 -0
  27. package/dist/generators/_chunks/chunk-URV6FLUH.js +73 -0
  28. package/dist/generators/_chunks/chunk-YNMLB4DL.js +25 -0
  29. package/dist/generators/_chunks/chunk-YY2UESLM.js +282 -0
  30. package/dist/generators/add-agent-rules/generator.js +11 -842
  31. package/dist/generators/add-backend/generator.js +11 -906
  32. package/dist/generators/add-fsd-lite/generator.js +12 -801
  33. package/dist/generators/add-github-templates/generator.js +13 -116
  34. package/dist/generators/add-integration/generator.js +15 -743
  35. package/dist/generators/add-review/generator.js +12 -1231
  36. package/dist/generators/allow-builds/generator.js +7 -71
  37. package/dist/generators/doctor/generator.js +106 -2044
  38. package/dist/generators/entity/generator.js +15 -604
  39. package/dist/generators/feature/generator.js +15 -604
  40. package/dist/generators/init/generator.js +68 -3376
  41. package/dist/generators/install-integration/generator.js +35 -871
  42. package/dist/generators/page/generator.js +15 -604
  43. package/dist/generators/record-integration/generator.js +12 -778
  44. package/dist/generators/shared/generator.js +15 -604
  45. package/dist/generators/widget/generator.js +15 -604
  46. package/dist/index.js +5 -663
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -209,7 +209,7 @@ Review is fully optional and isn't tied to one provider.
209
209
 
210
210
  - If review is disabled, zero review artifacts are generated.
211
211
  - If review is enabled, the workflow **green-skips** (it never fails) until you register the provider's secret.
212
- - A round marker stops the workflow after `--maxRounds` reviews per PR (1–5, default 3).
212
+ - A round marker stops the workflow after `--maxRounds` reviews per PR (1–5, default 3). A later commit has no AI verdict, so the `review` check fails on it; merge it with your branch ruleset's bypass.
213
213
 
214
214
  | `--provider` | Workflow file | Required secret |
215
215
  | --- | --- | --- |
@@ -1,257 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import "../generators/_chunks/chunk-SFXU6IUS.js";
3
+ import {
4
+ VERDICT_MARKER,
5
+ runVerdictGate
6
+ } from "../generators/_chunks/chunk-ALSXKTLM.js";
2
7
 
3
8
  // src/bin/review-verdict.ts
4
9
  import { spawnSync } from "node:child_process";
5
10
  import { randomBytes } from "node:crypto";
6
11
  import { appendFileSync, realpathSync } from "node:fs";
7
-
8
- // ../review-policy/dist/schema/review-finding.js
9
- function computeVerdict(findings) {
10
- if (findings.some((f) => f.priority === "P0" || f.priority === "P1"))
11
- return "BLOCK";
12
- if (findings.some((f) => f.priority === "P2"))
13
- return "CHANGES_RECOMMENDED";
14
- if (findings.length > 0)
15
- return "APPROVE_WITH_NOTES";
16
- return "APPROVE";
17
- }
18
-
19
- // ../review-policy/dist/verdict-gate/findings-block.js
20
- var FINDINGS_MARKER = "<!-- archweave-findings-v1 -->";
21
- var FINDINGS_SCHEMA_ID = "archweave/findings@1";
22
- var PRIORITIES = ["P0", "P1", "P2", "P3", "Nit"];
23
- function findingKey(f) {
24
- return `${f.file}:${f.lineRange.start}:${f.category}`;
25
- }
26
- function extractFindingsBlock(commentBody) {
27
- const markerIndex = commentBody.lastIndexOf(FINDINGS_MARKER);
28
- if (markerIndex === -1)
29
- return { status: "missing" };
30
- const after = commentBody.slice(markerIndex + FINDINGS_MARKER.length);
31
- const open = /```json[ \t]*\r?\n/.exec(after);
32
- if (!open)
33
- return { status: "invalid" };
34
- const contentStart = open.index + open[0].length;
35
- let parsed;
36
- let found = false;
37
- for (let close = after.indexOf("```", contentStart); close !== -1; close = after.indexOf("```", close + 3)) {
38
- try {
39
- parsed = JSON.parse(after.slice(contentStart, close));
40
- found = true;
41
- break;
42
- } catch {
43
- }
44
- }
45
- if (!found)
46
- return { status: "invalid" };
47
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
48
- return { status: "invalid" };
49
- const obj = parsed;
50
- if (obj.schema !== FINDINGS_SCHEMA_ID || !Array.isArray(obj.findings))
51
- return { status: "invalid" };
52
- const findings = [];
53
- for (const raw of obj.findings) {
54
- const finding = normalizeFinding(raw);
55
- if (finding === null)
56
- return { status: "invalid" };
57
- findings.push(finding);
58
- }
59
- return { status: "ok", findings };
60
- }
61
- function normalizeFinding(raw) {
62
- if (raw === null || typeof raw !== "object")
63
- return null;
64
- const r = raw;
65
- const lineRange = r["lineRange"];
66
- if (!PRIORITIES.includes(r["priority"]) || typeof r["category"] !== "string" || typeof r["title"] !== "string" || typeof r["file"] !== "string" || lineRange === null || lineRange === void 0 || typeof lineRange !== "object" || typeof lineRange.start !== "number" || !Number.isInteger(lineRange.start) || lineRange.start < 1) {
67
- return null;
68
- }
69
- const str = (value) => typeof value === "string" ? value : "";
70
- return {
71
- priority: r["priority"],
72
- category: r["category"],
73
- title: r["title"],
74
- file: r["file"],
75
- lineRange: { start: lineRange.start, ...typeof lineRange.end === "number" ? { end: lineRange.end } : {} },
76
- evidence: str(r["evidence"]),
77
- impact: str(r["impact"]),
78
- reproductionCondition: str(r["reproductionCondition"]),
79
- recommendation: str(r["recommendation"]),
80
- confidence: r["confidence"] === "plausible" ? "plausible" : "confirmed"
81
- };
82
- }
83
-
84
- // ../review-policy/dist/renderers/render-review-comment.js
85
- var COPY = {
86
- en: {
87
- terminationBanner: (maxRounds) => `> \u26A0\uFE0F **Automated review ended.** The maximum of ${maxRounds} review rounds has been reached with blocking findings (P0/P1) still open. Later pushes aren't reviewed by AI, so review them yourself before merging.`,
88
- verdictLines: {
89
- BLOCK: "**Verdict: BLOCK** \u2014 P0/P1 findings must be resolved before this change can merge.",
90
- CHANGES_RECOMMENDED: "**Verdict: CHANGES_RECOMMENDED** \u2014 P2 findings should be addressed.",
91
- APPROVE_WITH_NOTES: "**Verdict: APPROVE_WITH_NOTES** \u2014 only advisory notes below; safe to merge.",
92
- APPROVE: "**Verdict: APPROVE** \u2014 no findings."
93
- },
94
- mainSection: "## Correctness & Security",
95
- overEngineeringSection: "## Over-engineering",
96
- nitSection: "## Nits",
97
- roundHistoryHeading: "**Round history:**",
98
- roundLine: (round, fixed, remaining) => `- round ${round}: ${fixed} fixed, ${remaining} remaining`,
99
- fieldLabels: {
100
- file: "File",
101
- evidence: "Evidence",
102
- impact: "Impact",
103
- reproduction: "Reproduction",
104
- recommendation: "Recommendation",
105
- confidence: "Confidence"
106
- }
107
- },
108
- ko: {
109
- terminationBanner: (maxRounds) => `> \u26A0\uFE0F **\uC790\uB3D9 \uB9AC\uBDF0 \uC885\uB8CC.** \uCD5C\uB300 ${maxRounds}\uD68C\uC758 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC\uC5D0 \uB3C4\uB2EC\uD588\uC9C0\uB9CC \uBCD1\uD569\uC744 \uB9C9\uB294 finding(P0/P1)\uC774 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uC774\uD6C4 push\uB294 AI\uAC00 \uB9AC\uBDF0\uD558\uC9C0 \uC54A\uC73C\uB2C8, \uBCD1\uD569 \uC804\uC5D0 \uC9C1\uC811 \uB9AC\uBDF0\uD558\uC138\uC694.`,
110
- verdictLines: {
111
- BLOCK: "**\uD310\uC815: BLOCK** \u2014 \uBCD1\uD569 \uC804\uC5D0 P0/P1 finding\uC744 \uD574\uACB0\uD574\uC57C \uD569\uB2C8\uB2E4.",
112
- CHANGES_RECOMMENDED: "**\uD310\uC815: CHANGES_RECOMMENDED** \u2014 P2 finding\uC744 \uD574\uACB0\uD558\uB294 \uAC83\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.",
113
- APPROVE_WITH_NOTES: "**\uD310\uC815: APPROVE_WITH_NOTES** \u2014 \uCC38\uACE0\uC6A9 \uB178\uD2B8\uB9CC \uC788\uC2B5\uB2C8\uB2E4. \uBCD1\uD569\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4.",
114
- APPROVE: "**\uD310\uC815: APPROVE** \u2014 finding\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
115
- },
116
- mainSection: "## \uC815\uD655\uC131 & \uBCF4\uC548",
117
- overEngineeringSection: "## \uACFC\uC789 \uC124\uACC4",
118
- nitSection: "## \uC0AC\uC18C\uD55C \uC9C0\uC801 (Nit)",
119
- roundHistoryHeading: "**\uB77C\uC6B4\uB4DC \uC774\uB825:**",
120
- roundLine: (round, fixed, remaining) => `- ${round}\uB77C\uC6B4\uB4DC: \uD574\uACB0 ${fixed}\uAC74, \uBBF8\uD574\uACB0 ${remaining}\uAC74`,
121
- fieldLabels: {
122
- file: "\uD30C\uC77C",
123
- evidence: "\uADFC\uAC70",
124
- impact: "\uC601\uD5A5",
125
- reproduction: "\uC7AC\uD604 \uC870\uAC74",
126
- recommendation: "\uAD8C\uC7A5 \uC870\uCE58",
127
- confidence: "\uD655\uC2E0\uB3C4"
128
- }
129
- }
130
- };
131
- function verdictLine(verdict, locale) {
132
- return COPY[locale].verdictLines[verdict];
133
- }
134
- function terminationBannerLine(maxRounds, locale) {
135
- return COPY[locale].terminationBanner(maxRounds);
136
- }
137
-
138
- // ../review-policy/dist/verdict-gate/verdict-comment.js
139
- var VERDICT_MARKER = "<!-- archweave-verdict -->";
140
- var HISTORY_HEADING = { en: "**Round history:**", ko: "**\uB77C\uC6B4\uB4DC \uC774\uB825:**" };
141
- var ROUND_LINE = {
142
- en: (round, fixed, remaining) => `- round ${round}: ${fixed} fixed, ${remaining} remaining`,
143
- ko: (round, fixed, remaining) => `- ${round}\uCC28: ${fixed}\uAC74 \uD574\uACB0, ${remaining}\uAC74 \uC794\uC874`
144
- };
145
- var UNPARSEABLE_NOTICE = {
146
- en: "\u26A0\uFE0F The review comment could not be machine-read (missing or invalid findings block). The verdict gate stays green; please review this PR manually.",
147
- ko: "\u26A0\uFE0F \uB9AC\uBDF0 \uCF54\uBA58\uD2B8\uB97C \uAE30\uACC4 \uD310\uB3C5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(findings \uBE14\uB85D \uB204\uB77D \uB610\uB294 \uC190\uC0C1). verdict \uAC8C\uC774\uD2B8\uB294 \uB179\uC0C9\uC73C\uB85C \uB450\uB2C8 \uC774 PR\uC740 \uC0AC\uB78C\uC774 \uC9C1\uC811 \uB9AC\uBDF0\uD574 \uC8FC\uC138\uC694."
148
- };
149
- var NO_ROUND_NOTICE = {
150
- en: "\u26A0\uFE0F This run of the review posted no summary comment for the current commit, so there is no verdict for it (an earlier round is never reused). The verdict gate stays green; please review this PR manually.",
151
- ko: "\u26A0\uFE0F \uC774\uBC88 \uB9AC\uBDF0 \uC2E4\uD589\uC774 \uD604\uC7AC \uCEE4\uBC0B\uC5D0 \uB300\uD55C \uC694\uC57D \uCF54\uBA58\uD2B8\uB97C \uAC8C\uC2DC\uD558\uC9C0 \uC54A\uC544 verdict\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4(\uC774\uC804 \uB77C\uC6B4\uB4DC\uB294 \uC7AC\uC0AC\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). verdict \uAC8C\uC774\uD2B8\uB294 \uB179\uC0C9\uC73C\uB85C \uB450\uB2C8 \uC774 PR\uC740 \uC0AC\uB78C\uC774 \uC9C1\uC811 \uB9AC\uBDF0\uD574 \uC8FC\uC138\uC694."
152
- };
153
- var NO_SUMMARY_NOTICE = {
154
- en: "\u26D4 AI review posted no summary for this commit, so there is no verdict for it and the review check fails. Review this PR manually, or re-run all jobs to review it again.",
155
- ko: "\u26D4 AI \uB9AC\uBDF0\uAC00 \uC774 \uCEE4\uBC0B\uC5D0 \uB300\uD55C \uC694\uC57D\uC744 \uAC8C\uC2DC\uD558\uC9C0 \uC54A\uC544 verdict\uAC00 \uC5C6\uACE0, review \uCCB4\uD06C\uB294 \uC2E4\uD328\uD569\uB2C8\uB2E4. \uC774 PR\uC744 \uC9C1\uC811 \uB9AC\uBDF0\uD558\uAC70\uB098, \uBAA8\uB4E0 \uC7A1\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uB2E4\uC2DC \uB9AC\uBDF0\uBC1B\uC73C\uC138\uC694."
156
- };
157
- var UNREADABLE_SUMMARY_NOTICE = {
158
- en: "\u26D4 AI review posted a summary whose findings block could not be machine-read (missing or invalid), so there is no verdict for this commit and the review check fails. Review this PR manually, or re-run all jobs to review it again.",
159
- ko: "\u26D4 AI \uB9AC\uBDF0 \uC694\uC57D\uC758 findings \uBE14\uB85D\uC744 \uAE30\uACC4 \uD310\uB3C5\uD558\uC9C0 \uBABB\uD574(\uB204\uB77D \uB610\uB294 \uC190\uC0C1) \uC774 \uCEE4\uBC0B\uC758 verdict\uAC00 \uC5C6\uACE0, review \uCCB4\uD06C\uB294 \uC2E4\uD328\uD569\uB2C8\uB2E4. \uC774 PR\uC744 \uC9C1\uC811 \uB9AC\uBDF0\uD558\uAC70\uB098, \uBAA8\uB4E0 \uC7A1\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uB2E4\uC2DC \uB9AC\uBDF0\uBC1B\uC73C\uC138\uC694."
160
- };
161
- function renderCommitLine(locale, shortSha) {
162
- return locale === "en" ? `<sub>Commit \`${shortSha}\` \xB7 this comment is updated on every review round.</sub>` : `<sub>\uCEE4\uBC0B \`${shortSha}\` \xB7 \uC774 \uCF54\uBA58\uD2B8\uB294 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC\uB9C8\uB2E4 \uAC31\uC2E0\uB429\uB2C8\uB2E4.</sub>`;
163
- }
164
- var HEAD_LINE = {
165
- en: (sha) => renderCommitLine("en", sha.slice(0, 7)),
166
- ko: (sha) => renderCommitLine("ko", sha.slice(0, 7))
167
- };
168
- function withHead(lines, locale, headSha) {
169
- return (headSha === void 0 ? lines : [...lines, "", HEAD_LINE[locale](headSha)]).join("\n");
170
- }
171
- function renderUnparseableNotice(locale, headSha, failClosed = false) {
172
- return withHead([VERDICT_MARKER, (failClosed ? UNREADABLE_SUMMARY_NOTICE : UNPARSEABLE_NOTICE)[locale]], locale, headSha);
173
- }
174
- function renderNoSummaryNotice(locale, headSha) {
175
- return withHead([VERDICT_MARKER, NO_SUMMARY_NOTICE[locale]], locale, headSha);
176
- }
177
- function renderNoRoundNotice(locale, headSha) {
178
- return withHead([VERDICT_MARKER, NO_ROUND_NOTICE[locale]], locale, headSha);
179
- }
180
- function renderVerdictComment(input) {
181
- const lines = [VERDICT_MARKER];
182
- if (input.isFinalRound && input.verdict === "BLOCK") {
183
- lines.push(terminationBannerLine(input.maxRounds, input.locale), "");
184
- }
185
- lines.push(verdictLine(input.verdict, input.locale));
186
- if (input.history.length > 1) {
187
- lines.push("", HISTORY_HEADING[input.locale]);
188
- for (let i = 0; i < input.history.length; i++) {
189
- if (i === 0)
190
- continue;
191
- const entry = input.history[i];
192
- lines.push(ROUND_LINE[input.locale](entry.round, entry.fixedFindingKeys.length, entry.remainingFindingKeys.length));
193
- }
194
- }
195
- return withHead(lines, input.locale, input.headSha);
196
- }
197
-
198
- // ../review-policy/dist/verdict-gate/verdict-gate.js
199
- var ROUND_MARKER = "<!-- archweave-review-round -->";
200
- function collectRounds(comments) {
201
- return comments.filter((c) => c.body.includes(ROUND_MARKER) && !c.body.includes(VERDICT_MARKER)).map((c, index) => ({ round: index + 1, id: c.id, extraction: extractFindingsBlock(c.body) }));
202
- }
203
- var LINE_DRIFT = 3;
204
- function sameDefect(a, b) {
205
- if (a.file !== b.file)
206
- return false;
207
- const aEnd = a.lineRange.end ?? a.lineRange.start;
208
- const bEnd = b.lineRange.end ?? b.lineRange.start;
209
- return a.lineRange.start <= bEnd + LINE_DRIFT && b.lineRange.start <= aEnd + LINE_DRIFT;
210
- }
211
- function computeRoundHistory(rounds) {
212
- const parsed = rounds.filter((r) => r.extraction.status === "ok");
213
- const history = [];
214
- for (let i = 0; i < parsed.length; i++) {
215
- const keys = new Set(parsed[i].extraction.findings.map(findingKey));
216
- if (i === 0) {
217
- history.push({ round: parsed[i].round, fixedFindingKeys: [], remainingFindingKeys: [...keys] });
218
- continue;
219
- }
220
- const current = parsed[i].extraction.findings;
221
- history.push({
222
- round: parsed[i].round,
223
- fixedFindingKeys: parsed[i - 1].extraction.findings.filter((finding) => !current.some((now) => sameDefect(finding, now))).map(findingKey),
224
- remainingFindingKeys: [...keys]
225
- });
226
- }
227
- return history;
228
- }
229
- function runVerdictGate(input) {
230
- const rounds = collectRounds(input.comments);
231
- const reviewRan = input.reviewRan === true;
232
- if (rounds.length === 0) {
233
- return reviewRan ? { kind: "no-summary", commentBody: renderNoSummaryNotice(input.locale, input.headSha) } : { kind: "no-review" };
234
- }
235
- const latest = rounds[rounds.length - 1];
236
- if (input.roundAfterId !== void 0 && (latest.id === void 0 || latest.id <= input.roundAfterId)) {
237
- return reviewRan ? { kind: "no-summary", commentBody: renderNoSummaryNotice(input.locale, input.headSha) } : { kind: "no-round", commentBody: renderNoRoundNotice(input.locale, input.headSha) };
238
- }
239
- if (latest.extraction.status !== "ok") {
240
- return { kind: "unparseable", commentBody: renderUnparseableNotice(input.locale, input.headSha, reviewRan), failClosed: reviewRan };
241
- }
242
- const verdict = computeVerdict(latest.extraction.findings);
243
- const commentBody = renderVerdictComment({
244
- verdict,
245
- history: computeRoundHistory(rounds),
246
- maxRounds: input.maxRounds,
247
- locale: input.locale,
248
- isFinalRound: rounds.length >= input.maxRounds,
249
- headSha: input.headSha
250
- });
251
- return { kind: "verdict", verdict, commentBody, exitCode: verdict === "BLOCK" ? 1 : 0 };
252
- }
253
-
254
- // src/bin/review-verdict.ts
255
12
  var realExec = (command, args, stdin) => {
256
13
  const result = spawnSync(command, args, { encoding: "utf-8", input: stdin });
257
14
  return { status: result.status ?? null, stdout: result.stdout ?? "" };
@@ -276,13 +33,16 @@ function renderStepOutputs(commentBody, previousId, blocked, reason) {
276
33
  ].join("\n");
277
34
  }
278
35
  var UNREAD_COMMENTS_ERROR = "::error title=Review verdict not computed::Could not read PR comments, so the verdict could not be computed. Re-run the failed jobs to retry. If it fails again, check that the review job has pull-requests: read.";
36
+ var MISCONFIGURED_ERROR = "::error title=Review verdict not computed::The verdict gate is missing GITHUB_REPOSITORY or ARCHWEAVE_PR_NUMBER, or ARCHWEAVE_MAX_ROUNDS is not a whole number from 1, so no verdict was computed. Regenerate the workflow with add-review --onConflict=overwrite if it was edited by hand.";
279
37
  function runReviewVerdictBin(env, exec, writeOutput = realWriteOutput) {
280
38
  const logs = [];
281
39
  const repo = env["GITHUB_REPOSITORY"];
282
40
  const pr = env["ARCHWEAVE_PR_NUMBER"];
283
41
  const maxRounds = Number(env["ARCHWEAVE_MAX_ROUNDS"]);
284
42
  const locale = env["ARCHWEAVE_LOCALE"] === "ko" ? "ko" : "en";
43
+ const outputPath = outputModePath(env);
285
44
  if (!repo || !pr || !Number.isInteger(maxRounds) || maxRounds < 1) {
45
+ if (outputPath !== void 0) return { exitCode: 1, logs: [MISCONFIGURED_ERROR] };
286
46
  logs.push("verdict gate: required environment missing or malformed \u2014 skipping (check stays green).");
287
47
  return { exitCode: 0, logs };
288
48
  }
@@ -290,7 +50,6 @@ function runReviewVerdictBin(env, exec, writeOutput = realWriteOutput) {
290
50
  const roundAfterId = watermark !== void 0 && /^\d+$/.test(watermark) ? Number(watermark) : void 0;
291
51
  const sha = env["ARCHWEAVE_HEAD_SHA"];
292
52
  const headSha = sha !== void 0 && /^[0-9a-f]{7,40}$/i.test(sha) ? sha : void 0;
293
- const outputPath = outputModePath(env);
294
53
  const fetched = exec("gh", ["api", `repos/${repo}/issues/${pr}/comments`, "--paginate"]);
295
54
  const pages = fetched.status === 0 ? splitConcatenatedJson(fetched.stdout) : null;
296
55
  if (pages === null) {