@webpieces/rules-config 0.4.535 → 0.4.536

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/rules-config",
3
- "version": "0.4.535",
3
+ "version": "0.4.536",
4
4
  "description": "Shared webpieces.config.json loader. Single source of truth for validation rule configuration consumed by @webpieces/ai-hook-rules, @webpieces/code-rules, and @webpieces/nx-webpieces-rules.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The DATA-ONLY classes and status constants of the PR review system — the verdict a reviewer writes, the
3
+ * checklist that demanded it, the resolved outcome, and the diff context handed to reviewers.
4
+ *
5
+ * Split out of review-json.ts, which holds the SERVICE that reads and writes them. The split is purely by
6
+ * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and
7
+ * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.
8
+ */
9
+ export declare const VERDICT_GREEN = "green";
10
+ export declare const VERDICT_YELLOW = "yellow";
11
+ export declare const VERDICT_RED = "red";
12
+ export declare const VERDICT_STATUSES: readonly ["green", "yellow", "red"];
13
+ export declare class ChecklistResult {
14
+ id: string;
15
+ status: string;
16
+ output: string;
17
+ override: string;
18
+ problem: string;
19
+ constructor(id: string, status: string, output: string, override: string, problem?: string);
20
+ }
21
+ export declare class RequiredChecklist {
22
+ id: string;
23
+ subagent: string;
24
+ doc: string;
25
+ matchedFiles: string[];
26
+ matchedPatterns: string[];
27
+ constructor(id: string, subagent: string, doc: string, matchedFiles: string[], matchedPatterns?: string[]);
28
+ }
29
+ /**
30
+ * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base
31
+ * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in
32
+ * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand
33
+ * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.
34
+ */
35
+ export declare class ChecklistReviewContext {
36
+ baseSha: string;
37
+ prContextPath: string;
38
+ /**
39
+ * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the
40
+ * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a
41
+ * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives
42
+ * this string from the same range the file set came from.
43
+ */
44
+ fileDiffCommand: string;
45
+ diffDir: string;
46
+ dirty: boolean;
47
+ constructor(baseSha?: string, prContextPath?: string, fileDiffCommand?: string, diffDir?: string, dirty?: boolean);
48
+ }
49
+ export declare class ReviewJson {
50
+ title: string;
51
+ riskScore: number;
52
+ riskLevel: string;
53
+ riskEmoji: string;
54
+ summary: string;
55
+ violations: string[];
56
+ risks: string[];
57
+ filesToReview: string[];
58
+ results: ChecklistResult[];
59
+ constructor(title: string, riskScore: number, riskLevel: string, riskEmoji: string, summary: string, violations: string[], risks: string[], filesToReview: string[], results?: ChecklistResult[]);
60
+ }
61
+ export declare const CK_PASS = "pass";
62
+ export declare const CK_WARN = "warn";
63
+ export declare const CK_OVERRIDDEN = "overridden";
64
+ export declare const CK_FAIL = "fail";
65
+ export declare const CK_MISSING = "missing";
66
+ export declare const CK_BAD_FORMAT = "bad-format";
67
+ export declare class ChecklistVerdict {
68
+ id: string;
69
+ status: string;
70
+ detail: string;
71
+ constructor(id: string, status: string, detail: string);
72
+ }
73
+ export declare class PrContext {
74
+ base: string;
75
+ /**
76
+ * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be
77
+ * compared later to detect that the tree moved under a review, and it reads as a range that was never
78
+ * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.
79
+ */
80
+ head: string;
81
+ changedFiles: string[];
82
+ dirty: boolean;
83
+ dirtyFiles: string[];
84
+ diffCommand: string;
85
+ diffDir: string;
86
+ generatedAt: string;
87
+ /**
88
+ * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge
89
+ * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B
90
+ * under the review side's older names.
91
+ *
92
+ * The review side used to record only A and B, so nothing could answer "did main move while this was
93
+ * under review?" — the question you most want answered when a review looks stale. '' when origin/main
94
+ * is unresolvable. Purely informational; nothing gates on it.
95
+ */
96
+ hashMainHead: string;
97
+ constructor(base: string, head: string, changedFiles: string[], dirty?: boolean, dirtyFiles?: string[], diffCommand?: string, diffDir?: string, generatedAt?: string, hashMainHead?: string);
98
+ }
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ /**
3
+ * The DATA-ONLY classes and status constants of the PR review system — the verdict a reviewer writes, the
4
+ * checklist that demanded it, the resolved outcome, and the diff context handed to reviewers.
5
+ *
6
+ * Split out of review-json.ts, which holds the SERVICE that reads and writes them. The split is purely by
7
+ * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and
8
+ * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.PrContext = exports.ChecklistVerdict = exports.CK_BAD_FORMAT = exports.CK_MISSING = exports.CK_FAIL = exports.CK_OVERRIDDEN = exports.CK_WARN = exports.CK_PASS = exports.ReviewJson = exports.ChecklistReviewContext = exports.RequiredChecklist = exports.ChecklistResult = exports.VERDICT_STATUSES = exports.VERDICT_RED = exports.VERDICT_YELLOW = exports.VERDICT_GREEN = void 0;
12
+ // The three colors a reviewer subagent may report in `review-<id>.json`. A TRI-state, not a boolean,
13
+ // because the boolean it replaced gave a reviewer no way to say "this passes, but a human should look at
14
+ // X" — the only way to raise a concern was to FAIL the PR and then override your own failure, which reads
15
+ // on the dashboard as a deliberately-accepted defect rather than as a note.
16
+ exports.VERDICT_GREEN = 'green';
17
+ exports.VERDICT_YELLOW = 'yellow';
18
+ exports.VERDICT_RED = 'red';
19
+ exports.VERDICT_STATUSES = [exports.VERDICT_GREEN, exports.VERDICT_YELLOW, exports.VERDICT_RED];
20
+ // The verdict a reviewer SUBAGENT writes into `.webpieces/pr-review/<branch>/review-<id>.json`, one per
21
+ // matched checklist. One file per checklist so N concurrent reviewer subagents never clobber a shared
22
+ // file. It records the OUTCOME:
23
+ // status:'green' → PASS
24
+ // status:'yellow' → WARN (passes; the concern is published on the PR, nothing is blocked)
25
+ // status:'red' + override non-empty → OVERRIDDEN (pass; the free-text justification reaches the PR)
26
+ // status:'red' + no override → FAIL (refuse; `output` is printed verbatim)
27
+ // `override` is deliberately free text, not a boolean — it forces the ship-anyway decision to be stated
28
+ // in words and surfaces it on the dashboard, where a human sees it. Data-only (per CLAUDE.md).
29
+ class ChecklistResult {
30
+ id;
31
+ status; // one of VERDICT_STATUSES; anything else is reported via `problem`
32
+ output; // what the reviewer found; printed verbatim when the checklist fails
33
+ override; // '' = no override; non-empty = ship-anyway justification (renders 🟠 overridden)
34
+ // '' = a well-formed verdict. Non-empty = the file exists and parses but its verdict cannot be READ
35
+ // (most often: it still uses the removed `success` field). Carried as data rather than thrown so the
36
+ // complaint can be reported by BOTH wp-review-upsert-pr and wp-finish-upsert-pr in identical words, and so a
37
+ // legacy file is never silently mistaken for a missing one.
38
+ problem;
39
+ // eslint-disable-next-line @typescript-eslint/max-params
40
+ constructor(id, status, output, override, problem = '') {
41
+ this.id = id;
42
+ this.status = status;
43
+ this.output = output;
44
+ this.override = override;
45
+ this.problem = problem;
46
+ }
47
+ }
48
+ exports.ChecklistResult = ChecklistResult;
49
+ // What the pr-gate command computed from the diff: a checklist this branch MATCHED (its patterns hit the
50
+ // diff, so its reviewer subagent must run). Drives review-<id>.json enforcement, provenance, the schema
51
+ // hint, and the dashboard. Data-only.
52
+ class RequiredChecklist {
53
+ id; // = subagent name; keys review-<id>.json
54
+ subagent; // reviewer agent that must run (agentType the harness stamps)
55
+ doc; // REPO-RELATIVE guidance doc the reviewer reads ('' → it just reads the diff)
56
+ matchedFiles; // the changed files that matched it (for the dashboard + hint)
57
+ // Which of the checklist's OWN globs actually fired. Printed so a reviewer can judge how coarse the
58
+ // match was — a precise `db/migrations/**` hit means something different from a blanket `**` — and the
59
+ // template tells reviewers that matching IS deliberately coarse. [] = no patterns (matches every PR).
60
+ matchedPatterns;
61
+ // eslint-disable-next-line @typescript-eslint/max-params
62
+ constructor(id, subagent, doc, matchedFiles, matchedPatterns = []) {
63
+ this.id = id;
64
+ this.subagent = subagent;
65
+ this.doc = doc;
66
+ this.matchedFiles = matchedFiles;
67
+ this.matchedPatterns = matchedPatterns;
68
+ }
69
+ }
70
+ exports.RequiredChecklist = RequiredChecklist;
71
+ /**
72
+ * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base
73
+ * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in
74
+ * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand
75
+ * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.
76
+ */
77
+ class ChecklistReviewContext {
78
+ baseSha; // the 3-point merge-base sha
79
+ prContextPath; // path of pr-context.json — the AUTHORITATIVE full changed-file set
80
+ /**
81
+ * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the
82
+ * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a
83
+ * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives
84
+ * this string from the same range the file set came from.
85
+ */
86
+ fileDiffCommand;
87
+ diffDir; // dir of the MATERIALIZED diff (diff/ALL.diff + diff/files/…); '' when not written
88
+ dirty; // true ⇒ the range includes uncommitted + untracked work, and must be said out loud
89
+ // eslint-disable-next-line @typescript-eslint/max-params
90
+ constructor(baseSha = '', prContextPath = '', fileDiffCommand = '', diffDir = '', dirty = false) {
91
+ this.baseSha = baseSha;
92
+ this.prContextPath = prContextPath;
93
+ this.fileDiffCommand = fileDiffCommand;
94
+ this.diffDir = diffDir;
95
+ this.dirty = dirty;
96
+ }
97
+ }
98
+ exports.ChecklistReviewContext = ChecklistReviewContext;
99
+ // The AI-authored review for a PR. The AI writes review.json itself between `wp-start-upsert-pr` (which
100
+ // prints the schema) and `wp-finish-upsert-pr` (which reads it); reviewer subagents write the per-checklist
101
+ // review-<id>.json files. Data-only (per CLAUDE.md).
102
+ class ReviewJson {
103
+ title; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)
104
+ riskScore; // 0–100, drives the risk bar
105
+ riskLevel; // 'green' | 'yellow' | 'red'
106
+ riskEmoji; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted
107
+ summary; // rendered in the dashboard Summary section
108
+ violations; // pattern/architecture violations; length = the Pattern Violations count
109
+ risks;
110
+ filesToReview;
111
+ results; // resolved per-checklist verdicts (from review-<id>.json); [] when none
112
+ // eslint-disable-next-line @typescript-eslint/max-params
113
+ constructor(title, riskScore, riskLevel, riskEmoji, summary, violations, risks, filesToReview, results = []) {
114
+ this.title = title;
115
+ this.riskScore = riskScore;
116
+ this.riskLevel = riskLevel;
117
+ this.riskEmoji = riskEmoji;
118
+ this.summary = summary;
119
+ this.violations = violations;
120
+ this.risks = risks;
121
+ this.filesToReview = filesToReview;
122
+ this.results = results;
123
+ }
124
+ }
125
+ exports.ReviewJson = ReviewJson;
126
+ // A checklist's resolved outcome, shared by review.json enforcement and the dashboard so both agree.
127
+ // PASS, WARN and OVERRIDDEN all ship; FAIL, MISSING and BAD_FORMAT all refuse the PR.
128
+ exports.CK_PASS = 'pass'; // review-<id>.json status:'green'
129
+ exports.CK_WARN = 'warn'; // review-<id>.json status:'yellow' → 🟡 passes WITH concerns
130
+ exports.CK_OVERRIDDEN = 'overridden'; // review-<id>.json status:'red' + non-empty override → 🟠
131
+ exports.CK_FAIL = 'fail'; // review-<id>.json status:'red' + no override → refuse
132
+ exports.CK_MISSING = 'missing'; // no review-<id>.json written → refuse
133
+ exports.CK_BAD_FORMAT = 'bad-format'; // written, but its verdict is unreadable (e.g. legacy `success`)
134
+ class ChecklistVerdict {
135
+ id;
136
+ status; // one of CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT
137
+ detail; // reviewer output / override justification / format complaint (dashboard + errors)
138
+ constructor(id, status, detail) {
139
+ this.id = id;
140
+ this.status = status;
141
+ this.detail = detail;
142
+ }
143
+ }
144
+ exports.ChecklistVerdict = ChecklistVerdict;
145
+ // The PR's diff context, written by wp-start-upsert-pr into `.webpieces/pr-review/<branch>/pr-context.json`
146
+ // so a reviewer subagent knows the exact 3-point base the gate used and the full changed-file set — then
147
+ // reads any file's actual diff with `git diff <base> HEAD -- <file>`. This is what lets a checklist match
148
+ // coarsely by path (in the config) while the subagent makes the fine, content-level judgment. Data-only.
149
+ class PrContext {
150
+ base; // the 3-point merge-base sha the gate diffs against
151
+ /**
152
+ * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be
153
+ * compared later to detect that the tree moved under a review, and it reads as a range that was never
154
+ * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.
155
+ */
156
+ head;
157
+ changedFiles; // every file changed in the range (NOT tsOnly — includes .sql/.gql/Dockerfile/…)
158
+ dirty; // true ⇒ changedFiles includes uncommitted + untracked work
159
+ dirtyFiles; // exactly which paths are uncommitted/untracked — why `dirty` is true
160
+ diffCommand; // the command that reproduces the WHOLE diff (see DiffBasis; correct when dirty)
161
+ diffDir; // dir holding the materialized per-file diffs + ALL.diff; '' when not materialized
162
+ generatedAt; // ISO timestamp, so a stale context is detectable rather than silently trusted
163
+ /**
164
+ * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge
165
+ * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B
166
+ * under the review side's older names.
167
+ *
168
+ * The review side used to record only A and B, so nothing could answer "did main move while this was
169
+ * under review?" — the question you most want answered when a review looks stale. '' when origin/main
170
+ * is unresolvable. Purely informational; nothing gates on it.
171
+ */
172
+ hashMainHead;
173
+ // eslint-disable-next-line @typescript-eslint/max-params
174
+ constructor(base, head, changedFiles, dirty = false, dirtyFiles = [], diffCommand = '', diffDir = '', generatedAt = '', hashMainHead = '') {
175
+ this.hashMainHead = hashMainHead;
176
+ this.base = base;
177
+ this.head = head;
178
+ this.changedFiles = changedFiles;
179
+ this.dirty = dirty;
180
+ this.dirtyFiles = dirtyFiles;
181
+ this.diffCommand = diffCommand;
182
+ this.diffDir = diffDir;
183
+ this.generatedAt = generatedAt;
184
+ }
185
+ }
186
+ exports.PrContext = PrContext;
187
+ //# sourceMappingURL=review-json-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-json-data.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json-data.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The DATA-ONLY classes and status constants of the PR review system — the verdict a reviewer writes, the\n * checklist that demanded it, the resolved outcome, and the diff context handed to reviewers.\n *\n * Split out of review-json.ts, which holds the SERVICE that reads and writes them. The split is purely by\n * kind (data vs behaviour): review-json.ts re-exports every name below, so `from './review-json'` and\n * `from '@webpieces/rules-config'` keep resolving exactly as before and no consumer changes.\n */\n\n// The three colors a reviewer subagent may report in `review-<id>.json`. A TRI-state, not a boolean,\n// because the boolean it replaced gave a reviewer no way to say \"this passes, but a human should look at\n// X\" — the only way to raise a concern was to FAIL the PR and then override your own failure, which reads\n// on the dashboard as a deliberately-accepted defect rather than as a note.\nexport const VERDICT_GREEN = 'green';\nexport const VERDICT_YELLOW = 'yellow';\nexport const VERDICT_RED = 'red';\nexport const VERDICT_STATUSES = [VERDICT_GREEN, VERDICT_YELLOW, VERDICT_RED] as const;\n\n// The verdict a reviewer SUBAGENT writes into `.webpieces/pr-review/<branch>/review-<id>.json`, one per\n// matched checklist. One file per checklist so N concurrent reviewer subagents never clobber a shared\n// file. It records the OUTCOME:\n// status:'green' → PASS\n// status:'yellow' → WARN (passes; the concern is published on the PR, nothing is blocked)\n// status:'red' + override non-empty → OVERRIDDEN (pass; the free-text justification reaches the PR)\n// status:'red' + no override → FAIL (refuse; `output` is printed verbatim)\n// `override` is deliberately free text, not a boolean — it forces the ship-anyway decision to be stated\n// in words and surfaces it on the dashboard, where a human sees it. Data-only (per CLAUDE.md).\nexport class ChecklistResult {\n id: string;\n status: string; // one of VERDICT_STATUSES; anything else is reported via `problem`\n output: string; // what the reviewer found; printed verbatim when the checklist fails\n override: string; // '' = no override; non-empty = ship-anyway justification (renders 🟠 overridden)\n // '' = a well-formed verdict. Non-empty = the file exists and parses but its verdict cannot be READ\n // (most often: it still uses the removed `success` field). Carried as data rather than thrown so the\n // complaint can be reported by BOTH wp-review-upsert-pr and wp-finish-upsert-pr in identical words, and so a\n // legacy file is never silently mistaken for a missing one.\n problem: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(id: string, status: string, output: string, override: string, problem = '') {\n this.id = id;\n this.status = status;\n this.output = output;\n this.override = override;\n this.problem = problem;\n }\n}\n\n// What the pr-gate command computed from the diff: a checklist this branch MATCHED (its patterns hit the\n// diff, so its reviewer subagent must run). Drives review-<id>.json enforcement, provenance, the schema\n// hint, and the dashboard. Data-only.\nexport class RequiredChecklist {\n id: string; // = subagent name; keys review-<id>.json\n subagent: string; // reviewer agent that must run (agentType the harness stamps)\n doc: string; // REPO-RELATIVE guidance doc the reviewer reads ('' → it just reads the diff)\n matchedFiles: string[]; // the changed files that matched it (for the dashboard + hint)\n // Which of the checklist's OWN globs actually fired. Printed so a reviewer can judge how coarse the\n // match was — a precise `db/migrations/**` hit means something different from a blanket `**` — and the\n // template tells reviewers that matching IS deliberately coarse. [] = no patterns (matches every PR).\n matchedPatterns: string[];\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(id: string, subagent: string, doc: string, matchedFiles: string[], matchedPatterns: string[] = []) {\n this.id = id;\n this.subagent = subagent;\n this.doc = doc;\n this.matchedFiles = matchedFiles;\n this.matchedPatterns = matchedPatterns;\n }\n}\n\n/**\n * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base\n * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in\n * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand\n * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.\n */\nexport class ChecklistReviewContext {\n baseSha: string; // the 3-point merge-base sha\n prContextPath: string; // path of pr-context.json — the AUTHORITATIVE full changed-file set\n /**\n * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the\n * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a\n * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives\n * this string from the same range the file set came from.\n */\n fileDiffCommand: string;\n diffDir: string; // dir of the MATERIALIZED diff (diff/ALL.diff + diff/files/…); '' when not written\n dirty: boolean; // true ⇒ the range includes uncommitted + untracked work, and must be said out loud\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(baseSha = '', prContextPath = '', fileDiffCommand = '', diffDir = '', dirty = false) {\n this.baseSha = baseSha;\n this.prContextPath = prContextPath;\n this.fileDiffCommand = fileDiffCommand;\n this.diffDir = diffDir;\n this.dirty = dirty;\n }\n}\n\n// The AI-authored review for a PR. The AI writes review.json itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it); reviewer subagents write the per-checklist\n// review-<id>.json files. Data-only (per CLAUDE.md).\nexport class ReviewJson {\n title: string; // human PR title describing the change; used as the `gh pr` title (empty → caller falls back)\n riskScore: number; // 0–100, drives the risk bar\n riskLevel: string; // 'green' | 'yellow' | 'red'\n riskEmoji: string; // '🟢' | '🟡' | '🔴' — derived from riskLevel when omitted\n summary: string; // rendered in the dashboard Summary section\n violations: string[]; // pattern/architecture violations; length = the Pattern Violations count\n risks: string[];\n filesToReview: string[];\n results: ChecklistResult[]; // resolved per-checklist verdicts (from review-<id>.json); [] when none\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n title: string,\n riskScore: number,\n riskLevel: string,\n riskEmoji: string,\n summary: string,\n violations: string[],\n risks: string[],\n filesToReview: string[],\n results: ChecklistResult[] = [],\n ) {\n this.title = title;\n this.riskScore = riskScore;\n this.riskLevel = riskLevel;\n this.riskEmoji = riskEmoji;\n this.summary = summary;\n this.violations = violations;\n this.risks = risks;\n this.filesToReview = filesToReview;\n this.results = results;\n }\n}\n\n// A checklist's resolved outcome, shared by review.json enforcement and the dashboard so both agree.\n// PASS, WARN and OVERRIDDEN all ship; FAIL, MISSING and BAD_FORMAT all refuse the PR.\nexport const CK_PASS = 'pass'; // review-<id>.json status:'green'\nexport const CK_WARN = 'warn'; // review-<id>.json status:'yellow' → 🟡 passes WITH concerns\nexport const CK_OVERRIDDEN = 'overridden'; // review-<id>.json status:'red' + non-empty override → 🟠\nexport const CK_FAIL = 'fail'; // review-<id>.json status:'red' + no override → refuse\nexport const CK_MISSING = 'missing'; // no review-<id>.json written → refuse\nexport const CK_BAD_FORMAT = 'bad-format'; // written, but its verdict is unreadable (e.g. legacy `success`)\n\nexport class ChecklistVerdict {\n id: string;\n status: string; // one of CK_PASS | CK_WARN | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_BAD_FORMAT\n detail: string; // reviewer output / override justification / format complaint (dashboard + errors)\n\n constructor(id: string, status: string, detail: string) {\n this.id = id;\n this.status = status;\n this.detail = detail;\n }\n}\n\n// The PR's diff context, written by wp-start-upsert-pr into `.webpieces/pr-review/<branch>/pr-context.json`\n// so a reviewer subagent knows the exact 3-point base the gate used and the full changed-file set — then\n// reads any file's actual diff with `git diff <base> HEAD -- <file>`. This is what lets a checklist match\n// coarsely by path (in the config) while the subagent makes the fine, content-level judgment. Data-only.\nexport class PrContext {\n base: string; // the 3-point merge-base sha the gate diffs against\n /**\n * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be\n * compared later to detect that the tree moved under a review, and it reads as a range that was never\n * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.\n */\n head: string;\n changedFiles: string[]; // every file changed in the range (NOT tsOnly — includes .sql/.gql/Dockerfile/…)\n dirty: boolean; // true ⇒ changedFiles includes uncommitted + untracked work\n dirtyFiles: string[]; // exactly which paths are uncommitted/untracked — why `dirty` is true\n diffCommand: string; // the command that reproduces the WHOLE diff (see DiffBasis; correct when dirty)\n diffDir: string; // dir holding the materialized per-file diffs + ALL.diff; '' when not materialized\n generatedAt: string; // ISO timestamp, so a stale context is detectable rather than silently trusted\n /**\n * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge\n * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B\n * under the review side's older names.\n *\n * The review side used to record only A and B, so nothing could answer \"did main move while this was\n * under review?\" — the question you most want answered when a review looks stale. '' when origin/main\n * is unresolvable. Purely informational; nothing gates on it.\n */\n hashMainHead: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(\n base: string, head: string, changedFiles: string[],\n dirty = false, dirtyFiles: string[] = [], diffCommand = '', diffDir = '', generatedAt = '',\n hashMainHead = '',\n ) {\n this.hashMainHead = hashMainHead;\n this.base = base;\n this.head = head;\n this.changedFiles = changedFiles;\n this.dirty = dirty;\n this.dirtyFiles = dirtyFiles;\n this.diffCommand = diffCommand;\n this.diffDir = diffDir;\n this.generatedAt = generatedAt;\n }\n}\n"]}
@@ -1,94 +1,6 @@
1
1
  import { DotWebpieces } from './state-dir';
2
- export declare const VERDICT_GREEN = "green";
3
- export declare const VERDICT_YELLOW = "yellow";
4
- export declare const VERDICT_RED = "red";
5
- export declare const VERDICT_STATUSES: readonly ["green", "yellow", "red"];
6
- export declare class ChecklistResult {
7
- id: string;
8
- status: string;
9
- output: string;
10
- override: string;
11
- problem: string;
12
- constructor(id: string, status: string, output: string, override: string, problem?: string);
13
- }
14
- export declare class RequiredChecklist {
15
- id: string;
16
- subagent: string;
17
- doc: string;
18
- matchedFiles: string[];
19
- matchedPatterns: string[];
20
- constructor(id: string, subagent: string, doc: string, matchedFiles: string[], matchedPatterns?: string[]);
21
- }
22
- /**
23
- * The per-PR facts every reviewer subagent needs GIVEN to it, alongside its own checklist: the exact base
24
- * sha the gate diffs against and the file holding the complete changed-file set. Both used to live only in
25
- * a doc the printed instruction told the AI to go read, one indirection away from the instruction to hand
26
- * them over — so the printed block could not stand on its own. Data-only; empty = omit those lines.
27
- */
28
- export declare class ChecklistReviewContext {
29
- baseSha: string;
30
- prContextPath: string;
31
- /**
32
- * The exact command that reproduces ONE file's diff, with a `-- <file>` tail — NOT assembled by the
33
- * caller. This used to be hardcoded as `git diff <baseSha> HEAD -- <file>`, which returns NOTHING on a
34
- * dirty tree because the changed-file set is computed base→working-tree. See DiffBasis, which derives
35
- * this string from the same range the file set came from.
36
- */
37
- fileDiffCommand: string;
38
- diffDir: string;
39
- dirty: boolean;
40
- constructor(baseSha?: string, prContextPath?: string, fileDiffCommand?: string, diffDir?: string, dirty?: boolean);
41
- }
42
- export declare class ReviewJson {
43
- title: string;
44
- riskScore: number;
45
- riskLevel: string;
46
- riskEmoji: string;
47
- summary: string;
48
- violations: string[];
49
- risks: string[];
50
- filesToReview: string[];
51
- results: ChecklistResult[];
52
- constructor(title: string, riskScore: number, riskLevel: string, riskEmoji: string, summary: string, violations: string[], risks: string[], filesToReview: string[], results?: ChecklistResult[]);
53
- }
54
- export declare const CK_PASS = "pass";
55
- export declare const CK_WARN = "warn";
56
- export declare const CK_OVERRIDDEN = "overridden";
57
- export declare const CK_FAIL = "fail";
58
- export declare const CK_MISSING = "missing";
59
- export declare const CK_BAD_FORMAT = "bad-format";
60
- export declare class ChecklistVerdict {
61
- id: string;
62
- status: string;
63
- detail: string;
64
- constructor(id: string, status: string, detail: string);
65
- }
66
- export declare class PrContext {
67
- base: string;
68
- /**
69
- * The real HEAD sha. This was once the literal string 'HEAD', which is not a fact — it cannot be
70
- * compared later to detect that the tree moved under a review, and it reads as a range that was never
71
- * actually diffed. Its only reader (reviewContextFor) takes `base`, so recording the sha is free.
72
- */
73
- head: string;
74
- changedFiles: string[];
75
- dirty: boolean;
76
- dirtyFiles: string[];
77
- diffCommand: string;
78
- diffDir: string;
79
- generatedAt: string;
80
- /**
81
- * Main's head as this clone last saw it — the THIRD hash point, matching the trio the 3-point merge
82
- * records in `merge-info/<branch>/updatemain-hashes.json`. `base`/`head` above are points A and B
83
- * under the review side's older names.
84
- *
85
- * The review side used to record only A and B, so nothing could answer "did main move while this was
86
- * under review?" — the question you most want answered when a review looks stale. '' when origin/main
87
- * is unresolvable. Purely informational; nothing gates on it.
88
- */
89
- hashMainHead: string;
90
- constructor(base: string, head: string, changedFiles: string[], dirty?: boolean, dirtyFiles?: string[], diffCommand?: string, diffDir?: string, generatedAt?: string, hashMainHead?: string);
91
- }
2
+ import { VERDICT_GREEN, VERDICT_YELLOW, VERDICT_RED, VERDICT_STATUSES, ChecklistResult, RequiredChecklist, ChecklistReviewContext, ReviewJson, CK_PASS, CK_WARN, CK_OVERRIDDEN, CK_FAIL, CK_MISSING, CK_BAD_FORMAT, ChecklistVerdict, PrContext } from './review-json-data';
3
+ export { VERDICT_GREEN, VERDICT_YELLOW, VERDICT_RED, VERDICT_STATUSES, ChecklistResult, RequiredChecklist, ChecklistReviewContext, ReviewJson, CK_PASS, CK_WARN, CK_OVERRIDDEN, CK_FAIL, CK_MISSING, CK_BAD_FORMAT, ChecklistVerdict, PrContext, };
92
4
  /** Locates + loads/validates the AI-authored review.json. `@injectable(bindingScopeValues.Singleton)` so it's drawn in the design. */
93
5
  export declare class ReviewJsonService {
94
6
  private readonly dotDir;
@@ -113,11 +25,18 @@ export declare class ReviewJsonService {
113
25
  */
114
26
  archiveReviewJson(reviewJsonFilePath: string): string;
115
27
  /**
116
- * The archived bytes: the original review with the AUDIT-ONLY note as its FIRST key, so anything that
117
- * opens the file — human or AI — reads what it is before it reads any of its content.
28
+ * The archived bytes: the original JSON with an AUDIT-ONLY note as its FIRST key, so anything that opens
29
+ * the file — human or AI — reads what it is before it reads any of its content.
30
+ *
31
+ * `note` is a parameter rather than a constant because two different files are archived here (review.json
32
+ * and review-<id>.json) and they need to say different things, while the stamping MECHANICS — parse,
33
+ * note first, original keys in order, fall back to raw — are identical. One implementation, two texts;
34
+ * a second copy of this method would be the thing that drifts.
118
35
  *
119
- * Falls back to the raw bytes when they do not parse. By here `loadReviewJson` has already accepted the
120
- * file, so that is close to impossible; preserving the original beats losing it to a stamping failure.
36
+ * Falls back to the raw bytes when they do not parse. For review.json `loadReviewJson` has already
37
+ * accepted the file so that is close to impossible, but a verdict file is written by a subagent and may
38
+ * be half-written or not an object at all — and preserving the original always beats losing it to a
39
+ * stamping failure, since the archive exists precisely to be the record.
121
40
  */
122
41
  private archivedBody;
123
42
  private tryParseObject;
@@ -147,6 +66,32 @@ export declare class ReviewJsonService {
147
66
  */
148
67
  private archivedReviewHint;
149
68
  checklistResultPath(reviewJsonFilePath: string, checklistId: string): string;
69
+ /**
70
+ * Where a RETIRED verdict for one checklist goes: `review-<id>.json.old`, beside the live path.
71
+ *
72
+ * Mirrors {@link oldReviewJsonPath} deliberately, including its single-slot rule: ALWAYS the same path,
73
+ * so it holds the last retired verdict and only the last one. A series (`.old.old`, `.old.1`) would read
74
+ * as though the number of retirements meant something, and nothing downstream can interpret that — the
75
+ * one fact worth keeping is "this checklist refused before, here is what it said".
76
+ */
77
+ oldChecklistResultPath(reviewJsonFilePath: string, checklistId: string): string;
78
+ /**
79
+ * Retire one checklist's verdict: MOVE review-<id>.json to review-<id>.json.old, stamped with a note
80
+ * saying what it is. Returns the archive path, or '' when there was nothing to archive.
81
+ *
82
+ * The point is the MOVE, exactly as in {@link archiveReviewJson}. A red verdict left on the live path is
83
+ * re-read by the next run and re-reported as the CURRENT state of the branch, so the branch keeps being
84
+ * refused for a finding that may already be fixed — and the fix, when it comes, silently overwrites the
85
+ * only record that the gate ever refused anything. Moving it makes the refusal durable and makes a fresh
86
+ * reviewer run the only way forward, which is the honest requirement: the old verdict judged code that
87
+ * has since changed.
88
+ *
89
+ * Safe by construction for RED verdicts specifically, which is why the caller only archives on CK_FAIL:
90
+ * a red verdict is never reusable — it always blocks — so nothing is lost by moving it. Green and yellow
91
+ * verdicts ARE deliberately reused across finish attempts, and retiring one would force a needless (and
92
+ * expensive) subagent re-run.
93
+ */
94
+ archiveChecklistResult(reviewJsonFilePath: string, checklistId: string): string;
150
95
  /**
151
96
  * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,
152
97
  * unparseable, or structurally wrong. `required` is the set of checklists the diff matched: every one
@@ -161,6 +106,34 @@ export declare class ReviewJsonService {
161
106
  * second run and reads as though the earlier verdict did not count.
162
107
  */
163
108
  pendingChecklists(required: readonly RequiredChecklist[], results: readonly ChecklistResult[]): RequiredChecklist[];
109
+ /**
110
+ * The checklists that REFUSED: a reviewer ran, judged the change, and said no (CK_FAIL — status red with
111
+ * no override). A strict subset of {@link pendingChecklists}, split out because it demands a completely
112
+ * different action from the reader.
113
+ *
114
+ * Public so every command agrees on the set. When "refused" was computed ad hoc, a refusal and a
115
+ * never-ran reviewer landed in one bucket and produced one message — "you MUST run these N reviewer
116
+ * subagent(s)" — handed to an AI, which obediently re-spawned a reviewer that had already answered. It
117
+ * refused again for the same reason, and the loop cost a full subagent run per pass while the reviewer's
118
+ * actual finding was never shown to anyone. A refusal is a RESULT, not a missing step.
119
+ */
120
+ refusedChecklists(required: readonly RequiredChecklist[], results: readonly ChecklistResult[]): RequiredChecklist[];
121
+ /**
122
+ * THE renderer for "this reviewer refused" — one wording, wherever the refusal surfaces. It exists as a
123
+ * method because the text was previously inlined in {@link requiredChecklistErrors}, reachable only
124
+ * through review.json validation, while the command layer refused earlier with its own generic message.
125
+ * Two messages for one event is how the useful one became unreachable; there is now exactly one.
126
+ *
127
+ * It always quotes the reviewer's own `output` verbatim: the finding is the whole point, and an error
128
+ * that names a checklist without saying what it objected to gives the reader nothing to fix.
129
+ *
130
+ * `archivedPath` non-empty ⇒ the verdict has just been RETIRED (moved) to that path, so the message must
131
+ * change in two ways. It says where the record went — otherwise the move reads as data loss — and,
132
+ * critically, it must NOT tell the reader to "set override in review-<id>.json", because that file no
133
+ * longer exists. The escape hatch is therefore worded as writing a FRESH verdict file (the body can be
134
+ * copied back out of the archive) with a human-authored override.
135
+ */
136
+ refusalError(req: RequiredChecklist, verdict: ChecklistVerdict, archivedPath?: string): string;
164
137
  loadChecklistResults(reviewJsonFilePath: string, required: readonly RequiredChecklist[]): ChecklistResult[];
165
138
  resolveVerdict(req: RequiredChecklist, results: readonly ChecklistResult[]): ChecklistVerdict;
166
139
  /**