@webpieces/rules-config 0.4.456 → 0.4.458

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ReviewJsonService = exports.ReviewJson = void 0;
3
+ exports.ReviewJsonService = exports.ReviewJson = exports.RequiredChecklist = exports.ChecklistAck = void 0;
4
4
  exports.prDirFor = prDirFor;
5
5
  exports.reviewJsonPath = reviewJsonPath;
6
6
  exports.reviewJsonSchemaHint = reviewJsonSchemaHint;
@@ -10,8 +10,43 @@ const fs = tslib_1.__importStar(require("fs"));
10
10
  const path = tslib_1.__importStar(require("path"));
11
11
  const inversify_1 = require("inversify");
12
12
  const constants_1 = require("./constants");
13
+ const checklist_config_1 = require("./checklist-config");
13
14
  const inform_ai_error_1 = require("./inform-ai-error");
14
15
  const to_error_1 = require("./to-error");
16
+ // One entry the AI writes into review.json's `checklists[]`: "I read the doc for <id> and walked it".
17
+ // acknowledged: true is the AI attesting it did the walk — a SURFACING/AUDIT signal, not authorization.
18
+ class ChecklistAck {
19
+ id;
20
+ acknowledged;
21
+ notes; // per-item findings the AI chose to record (optional; [] when none)
22
+ constructor(id, acknowledged, notes) {
23
+ this.id = id;
24
+ this.acknowledged = acknowledged;
25
+ this.notes = notes;
26
+ }
27
+ }
28
+ exports.ChecklistAck = ChecklistAck;
29
+ // What the CALLER (the pr-gate command) computed from the diff: the checklists this branch triggered.
30
+ // Drives BOTH review.json validation (BLOCK must be acknowledged) AND the printed schema hint (so the
31
+ // AI is told, at the moment it writes review.json, exactly which docs to read). Data-only.
32
+ class RequiredChecklist {
33
+ id;
34
+ title;
35
+ severity; // 'BLOCK' | 'WARN'
36
+ docs;
37
+ blockMessage;
38
+ matchedFiles; // the changed files that triggered it (for the dashboard + hint)
39
+ // eslint-disable-next-line @typescript-eslint/max-params
40
+ constructor(id, title, severity, docs, blockMessage, matchedFiles) {
41
+ this.id = id;
42
+ this.title = title;
43
+ this.severity = severity;
44
+ this.docs = docs;
45
+ this.blockMessage = blockMessage;
46
+ this.matchedFiles = matchedFiles;
47
+ }
48
+ }
49
+ exports.RequiredChecklist = RequiredChecklist;
15
50
  // The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which
16
51
  // prints the schema) and `wp-finish-upsert-pr` (which reads it). Data-only (per CLAUDE.md).
17
52
  class ReviewJson {
@@ -23,7 +58,9 @@ class ReviewJson {
23
58
  violations; // pattern/architecture violations; length = the Pattern Violations count
24
59
  risks;
25
60
  filesToReview;
26
- constructor(title, riskScore, riskLevel, riskEmoji, summary, violations, risks, filesToReview) {
61
+ checklists; // consumer-checklist acknowledgments; [] when no checklists were required
62
+ // eslint-disable-next-line @typescript-eslint/max-params
63
+ constructor(title, riskScore, riskLevel, riskEmoji, summary, violations, risks, filesToReview, checklists = []) {
27
64
  this.title = title;
28
65
  this.riskScore = riskScore;
29
66
  this.riskLevel = riskLevel;
@@ -32,6 +69,7 @@ class ReviewJson {
32
69
  this.violations = violations;
33
70
  this.risks = risks;
34
71
  this.filesToReview = filesToReview;
72
+ this.checklists = checklists;
35
73
  }
36
74
  }
37
75
  exports.ReviewJson = ReviewJson;
@@ -47,8 +85,15 @@ let ReviewJsonService = class ReviewJsonService {
47
85
  reviewJsonPath(repoRoot, featureName) {
48
86
  return path.join(this.prDirFor(repoRoot, featureName), 'review.json');
49
87
  }
50
- // Copy-paste schema both commands print (write it / fix it).
51
- reviewJsonSchemaHint(filePath) {
88
+ // Copy-paste schema both commands print (write it / fix it). `required` is the set of consumer
89
+ // checklists the diff triggered; when it is empty the output is byte-identical to before this
90
+ // feature existed (non-adopting repos see no change). When non-empty it grows a `checklists` line
91
+ // in the JSON shape PLUS an instruction block naming the docs to read — diff-derived instructions
92
+ // injected at exactly the moment the AI writes review.json.
93
+ reviewJsonSchemaHint(filePath, required = []) {
94
+ const checklistLine = required.length > 0
95
+ ? `,\n "checklists": [{ "id": "<id from the list below>", "acknowledged": true, "notes": ["what you checked"] }]\n`
96
+ : `\n`;
52
97
  return (`Write your PR review to:\n ${filePath}\n\n` +
53
98
  `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\n\n` +
54
99
  `{\n` +
@@ -58,22 +103,52 @@ let ReviewJsonService = class ReviewJsonService {
58
103
  ` "summary": "5–10 sentence review summary",\n` +
59
104
  ` "violations": ["pattern/architecture violations you found (empty array if none)"],\n` +
60
105
  ` "risks": ["notable risks (empty array if none)"],\n` +
61
- ` "filesToReview": ["paths a human should look at (empty array if none)"]\n` +
62
- `}`);
106
+ ` "filesToReview": ["paths a human should look at (empty array if none)"]` +
107
+ checklistLine +
108
+ `}` +
109
+ this.requiredChecklistHint(required));
110
+ }
111
+ // The diff-triggered instruction block, appended ONLY when the branch triggered a checklist. This
112
+ // is the consumer's review process, re-injected: read doc Y because the diff touched X. BLOCK
113
+ // entries must be acknowledged in `checklists[]` or wp-finish-upsert-pr refuses to open the PR.
114
+ requiredChecklistHint(required) {
115
+ if (required.length === 0)
116
+ return '';
117
+ const lines = ['', '', 'This branch triggered company review checklist(s). BEFORE writing review.json, READ each'];
118
+ lines.push('doc, walk its items against your diff, then add a `checklists[]` entry acknowledging it:');
119
+ lines.push('');
120
+ for (const req of required) {
121
+ const gate = req.severity === checklist_config_1.CHECKLIST_BLOCK
122
+ ? 'BLOCK — the PR will NOT open until you acknowledge it'
123
+ : 'WARN — acknowledge if it applies (never blocks)';
124
+ lines.push(` • [${req.id}] ${req.title} (${gate})`);
125
+ lines.push(` docs to read: ${req.docs.join(', ')}`);
126
+ if (req.blockMessage.trim() !== '')
127
+ lines.push(` ${req.blockMessage.trim()}`);
128
+ if (req.matchedFiles.length > 0)
129
+ lines.push(` triggered by: ${req.matchedFiles.slice(0, 5).join(', ')}`);
130
+ }
131
+ return lines.join('\n');
63
132
  }
64
133
  /**
65
134
  * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,
66
135
  * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.
136
+ *
137
+ * `required` is the set of consumer checklists the diff triggered (empty for non-adopting repos, in
138
+ * which case this behaves byte-identically to before the feature). Every BLOCK entry must appear in
139
+ * review.json's `checklists[]` with `acknowledged: true`, or a validation error is raised alongside
140
+ * the usual ones so the AI gets ONE message. WARN entries are never validated; unknown ids in
141
+ * `checklists[]` are ignored (forward-compat).
67
142
  */
68
143
  // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields
69
- loadReviewJson(filePath) {
144
+ loadReviewJson(filePath, required = []) {
70
145
  if (!fs.existsSync(filePath)) {
71
- throw new inform_ai_error_1.InformAiError(`Required review.json not found.\n\n${this.reviewJsonSchemaHint(filePath)}\n\n` +
146
+ throw new inform_ai_error_1.InformAiError(`Required review.json not found.\n\n${this.reviewJsonSchemaHint(filePath, required)}\n\n` +
72
147
  `Then re-run: pnpm wp-finish-upsert-pr`);
73
148
  }
74
149
  const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);
75
150
  if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
76
- throw new inform_ai_error_1.InformAiError(`review.json must be a JSON object.\n\n${this.reviewJsonSchemaHint(filePath)}`);
151
+ throw new inform_ai_error_1.InformAiError(`review.json must be a JSON object.\n\n${this.reviewJsonSchemaHint(filePath, required)}`);
77
152
  }
78
153
  const errors = [];
79
154
  const riskScore = raw['riskScore'];
@@ -88,17 +163,57 @@ let ReviewJsonService = class ReviewJsonService {
88
163
  if (title === '') {
89
164
  errors.push('"title" must be a non-empty, imperative PR title describing the change (no branch names).');
90
165
  }
166
+ const acks = this.parseChecklistAcks(raw['checklists']);
167
+ for (const err of this.requiredChecklistErrors(required, acks))
168
+ errors.push(err);
91
169
  if (errors.length > 0) {
92
170
  throw new inform_ai_error_1.InformAiError(`review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\n\n` +
93
171
  errors.map((e) => ` • ${e}`).join('\n') +
94
- `\n\n${this.reviewJsonSchemaHint(filePath)}`);
172
+ `\n\n${this.reviewJsonSchemaHint(filePath, required)}`);
95
173
  }
96
174
  const level = riskLevel;
97
175
  const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''
98
176
  ? raw['riskEmoji']
99
177
  : (EMOJI_FOR_LEVEL[level] ?? '🟡');
100
178
  const summary = typeof raw['summary'] === 'string' ? raw['summary'] : '';
101
- return new ReviewJson(title, riskScore, level, emoji, summary, this.asStringArray(raw['violations']), this.asStringArray(raw['risks']), this.asStringArray(raw['filesToReview']));
179
+ return new ReviewJson(title, riskScore, level, emoji, summary, this.asStringArray(raw['violations']), this.asStringArray(raw['risks']), this.asStringArray(raw['filesToReview']), acks);
180
+ }
181
+ // Parse the AI-authored `checklists[]` into typed ChecklistAck[]. Tolerant of a missing/garbage
182
+ // field (→ []) and of non-object entries (skipped) — malformed acks simply fail to satisfy a BLOCK
183
+ // requirement rather than crashing the load.
184
+ // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed here
185
+ parseChecklistAcks(value) {
186
+ if (!Array.isArray(value))
187
+ return [];
188
+ const acks = [];
189
+ for (const entry of value) {
190
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
191
+ continue;
192
+ // webpieces-disable no-any-unknown -- one opaque ack entry, narrowed field-by-field
193
+ const e = entry;
194
+ const id = typeof e['id'] === 'string' ? e['id'] : '';
195
+ if (id === '')
196
+ continue;
197
+ acks.push(new ChecklistAck(id, e['acknowledged'] === true, this.asStringArray(e['notes'])));
198
+ }
199
+ return acks;
200
+ }
201
+ // BLOCK requirements that are not acknowledged → one error each, using the consumer's blockMessage
202
+ // verbatim so the consumer owns the wording and webpieces owns the mechanism.
203
+ requiredChecklistErrors(required, acks) {
204
+ const errors = [];
205
+ for (const req of required) {
206
+ if (req.severity !== checklist_config_1.CHECKLIST_BLOCK)
207
+ continue;
208
+ const ack = acks.find((a) => a.id === req.id);
209
+ if (!ack || !ack.acknowledged) {
210
+ const docs = req.docs.length > 0 ? ` Read: ${req.docs.join(', ')}.` : '';
211
+ const msg = req.blockMessage.trim() !== '' ? `${req.blockMessage.trim()} ` : '';
212
+ errors.push(`Checklist "${req.id}" (${req.title}) is REQUIRED for this diff but not acknowledged. ${msg}` +
213
+ `Add {"id":"${req.id}","acknowledged":true,"notes":[...]} to "checklists" once you have walked it.${docs}`);
214
+ }
215
+ }
216
+ return errors;
102
217
  }
103
218
  // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here
104
219
  asStringArray(value) {
@@ -138,11 +253,11 @@ function reviewJsonPath(repoRoot, featureName) {
138
253
  return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);
139
254
  }
140
255
  // webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
141
- function reviewJsonSchemaHint(filePath) {
142
- return reviewJsonSvc.reviewJsonSchemaHint(filePath);
256
+ function reviewJsonSchemaHint(filePath, required = []) {
257
+ return reviewJsonSvc.reviewJsonSchemaHint(filePath, required);
143
258
  }
144
259
  // webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it
145
- function loadReviewJson(filePath) {
146
- return reviewJsonSvc.loadReviewJson(filePath);
260
+ function loadReviewJson(filePath, required = []) {
261
+ return reviewJsonSvc.loadReviewJson(filePath, required);
147
262
  }
148
263
  //# sourceMappingURL=review-json.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it). 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\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 ) {\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 }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n/** Locates + loads/validates the AI-authored review.json. `@injectable(bindingScopeValues.Singleton)` so it's drawn in the design. */\n@injectable(bindingScopeValues.Singleton)\nexport class ReviewJsonService {\n // The per-feature PR working dir: `.webpieces/pr-review/<feature>`.\n prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n }\n\n // Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.\n reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(this.prDirFor(repoRoot, featureName), 'review.json');\n }\n\n // Copy-paste schema both commands print (write it / fix it).\n reviewJsonSchemaHint(filePath: string): string {\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]\\n` +\n `}`\n );\n }\n\n /**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,\n * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.\n */\n // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields\n loadReviewJson(filePath: string): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${this.reviewJsonSchemaHint(filePath)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${this.reviewJsonSchemaHint(filePath)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n this.asStringArray(raw['violations']),\n this.asStringArray(raw['risks']),\n this.asStringArray(raw['filesToReview']),\n );\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n }\n\n // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.\n // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field\n private parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n }\n}\n\n// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.\nconst reviewJsonSvc = new ReviewJsonService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.prDirFor(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonSchemaHint(filePath: string): string {\n return reviewJsonSvc.reviewJsonSchemaHint(filePath);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function loadReviewJson(filePath: string): ReviewJson {\n return reviewJsonSvc.loadReviewJson(filePath);\n}\n"]}
1
+ {"version":3,"file":"review-json.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/review-json.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { WEBPIECES_TMP_DIR, PR_REVIEW_DIR } from './constants';\nimport { CHECKLIST_BLOCK } from './checklist-config';\nimport { InformAiError } from './inform-ai-error';\nimport { toError } from './to-error';\n\n// One entry the AI writes into review.json's `checklists[]`: \"I read the doc for <id> and walked it\".\n// acknowledged: true is the AI attesting it did the walk — a SURFACING/AUDIT signal, not authorization.\nexport class ChecklistAck {\n id: string;\n acknowledged: boolean;\n notes: string[]; // per-item findings the AI chose to record (optional; [] when none)\n\n constructor(id: string, acknowledged: boolean, notes: string[]) {\n this.id = id;\n this.acknowledged = acknowledged;\n this.notes = notes;\n }\n}\n\n// What the CALLER (the pr-gate command) computed from the diff: the checklists this branch triggered.\n// Drives BOTH review.json validation (BLOCK must be acknowledged) AND the printed schema hint (so the\n// AI is told, at the moment it writes review.json, exactly which docs to read). Data-only.\nexport class RequiredChecklist {\n id: string;\n title: string;\n severity: string; // 'BLOCK' | 'WARN'\n docs: string[];\n blockMessage: string;\n matchedFiles: string[]; // the changed files that triggered it (for the dashboard + hint)\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(id: string, title: string, severity: string, docs: string[], blockMessage: string, matchedFiles: string[]) {\n this.id = id;\n this.title = title;\n this.severity = severity;\n this.docs = docs;\n this.blockMessage = blockMessage;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// The AI-authored review for a PR. The AI writes this file itself between `wp-start-upsert-pr` (which\n// prints the schema) and `wp-finish-upsert-pr` (which reads it). 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 checklists: ChecklistAck[]; // consumer-checklist acknowledgments; [] when no checklists were required\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 checklists: ChecklistAck[] = [],\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.checklists = checklists;\n }\n}\n\nconst RISK_LEVELS = ['green', 'yellow', 'red'] as const;\nconst EMOJI_FOR_LEVEL: Record<string, string> = { green: '🟢', yellow: '🟡', red: '🔴' };\n\n/** Locates + loads/validates the AI-authored review.json. `@injectable(bindingScopeValues.Singleton)` so it's drawn in the design. */\n@injectable(bindingScopeValues.Singleton)\nexport class ReviewJsonService {\n // The per-feature PR working dir: `.webpieces/pr-review/<feature>`.\n prDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, PR_REVIEW_DIR, featureName);\n }\n\n // Absolute path of the review.json for a feature — beside pr-body.md, keyed by branch name.\n reviewJsonPath(repoRoot: string, featureName: string): string {\n return path.join(this.prDirFor(repoRoot, featureName), 'review.json');\n }\n\n // Copy-paste schema both commands print (write it / fix it). `required` is the set of consumer\n // checklists the diff triggered; when it is empty the output is byte-identical to before this\n // feature existed (non-adopting repos see no change). When non-empty it grows a `checklists` line\n // in the JSON shape PLUS an instruction block naming the docs to read — diff-derived instructions\n // injected at exactly the moment the AI writes review.json.\n reviewJsonSchemaHint(filePath: string, required: readonly RequiredChecklist[] = []): string {\n const checklistLine = required.length > 0\n ? `,\\n \"checklists\": [{ \"id\": \"<id from the list below>\", \"acknowledged\": true, \"notes\": [\"what you checked\"] }]\\n`\n : `\\n`;\n return (\n `Write your PR review to:\\n ${filePath}\\n\\n` +\n `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\\n\\n` +\n `{\\n` +\n ` \"title\": \"concise PR title describing the change (imperative, no branch names)\",\\n` +\n ` \"riskScore\": 0, // integer 0–100 (higher = riskier)\\n` +\n ` \"riskLevel\": \"green | yellow | red\",\\n` +\n ` \"summary\": \"5–10 sentence review summary\",\\n` +\n ` \"violations\": [\"pattern/architecture violations you found (empty array if none)\"],\\n` +\n ` \"risks\": [\"notable risks (empty array if none)\"],\\n` +\n ` \"filesToReview\": [\"paths a human should look at (empty array if none)\"]` +\n checklistLine +\n `}` +\n this.requiredChecklistHint(required)\n );\n }\n\n // The diff-triggered instruction block, appended ONLY when the branch triggered a checklist. This\n // is the consumer's review process, re-injected: read doc Y because the diff touched X. BLOCK\n // entries must be acknowledged in `checklists[]` or wp-finish-upsert-pr refuses to open the PR.\n private requiredChecklistHint(required: readonly RequiredChecklist[]): string {\n if (required.length === 0) return '';\n const lines: string[] = ['', '', 'This branch triggered company review checklist(s). BEFORE writing review.json, READ each'];\n lines.push('doc, walk its items against your diff, then add a `checklists[]` entry acknowledging it:');\n lines.push('');\n for (const req of required) {\n const gate = req.severity === CHECKLIST_BLOCK\n ? 'BLOCK — the PR will NOT open until you acknowledge it'\n : 'WARN — acknowledge if it applies (never blocks)';\n lines.push(` • [${req.id}] ${req.title} (${gate})`);\n lines.push(` docs to read: ${req.docs.join(', ')}`);\n if (req.blockMessage.trim() !== '') lines.push(` ${req.blockMessage.trim()}`);\n if (req.matchedFiles.length > 0) lines.push(` triggered by: ${req.matchedFiles.slice(0, 5).join(', ')}`);\n }\n return lines.join('\\n');\n }\n\n /**\n * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,\n * unparseable, or structurally wrong. Returns a fully-populated ReviewJson on success.\n *\n * `required` is the set of consumer checklists the diff triggered (empty for non-adopting repos, in\n * which case this behaves byte-identically to before the feature). Every BLOCK entry must appear in\n * review.json's `checklists[]` with `acknowledged: true`, or a validation error is raised alongside\n * the usual ones so the AI gets ONE message. WARN entries are never validated; unknown ids in\n * `checklists[]` are ignored (forward-compat).\n */\n // webpieces-disable max-lines-new-methods -- one cohesive load+validate pass over the review fields\n loadReviewJson(filePath: string, required: readonly RequiredChecklist[] = []): ReviewJson {\n if (!fs.existsSync(filePath)) {\n throw new InformAiError(\n `Required review.json not found.\\n\\n${this.reviewJsonSchemaHint(filePath, required)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n\n const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new InformAiError(`review.json must be a JSON object.\\n\\n${this.reviewJsonSchemaHint(filePath, required)}`);\n }\n\n const errors: string[] = [];\n\n const riskScore = raw['riskScore'];\n if (typeof riskScore !== 'number' || !Number.isFinite(riskScore) || riskScore < 0 || riskScore > 100) {\n errors.push(`\"riskScore\" must be a number 0–100, got ${JSON.stringify(riskScore)}.`);\n }\n\n const riskLevel = raw['riskLevel'];\n if (typeof riskLevel !== 'string' || !RISK_LEVELS.includes(riskLevel as typeof RISK_LEVELS[number])) {\n errors.push(`\"riskLevel\" must be one of: ${RISK_LEVELS.join(', ')}.`);\n }\n\n const title = typeof raw['title'] === 'string' ? (raw['title'] as string).trim() : '';\n if (title === '') {\n errors.push('\"title\" must be a non-empty, imperative PR title describing the change (no branch names).');\n }\n\n const acks = this.parseChecklistAcks(raw['checklists']);\n for (const err of this.requiredChecklistErrors(required, acks)) errors.push(err);\n\n if (errors.length > 0) {\n throw new InformAiError(\n `review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n errors.map((e: string): string => ` • ${e}`).join('\\n') +\n `\\n\\n${this.reviewJsonSchemaHint(filePath, required)}`,\n );\n }\n\n const level = riskLevel as string;\n const emoji = typeof raw['riskEmoji'] === 'string' && raw['riskEmoji'] !== ''\n ? (raw['riskEmoji'] as string)\n : (EMOJI_FOR_LEVEL[level] ?? '🟡');\n const summary = typeof raw['summary'] === 'string' ? (raw['summary'] as string) : '';\n\n return new ReviewJson(\n title,\n riskScore as number,\n level,\n emoji,\n summary,\n this.asStringArray(raw['violations']),\n this.asStringArray(raw['risks']),\n this.asStringArray(raw['filesToReview']),\n acks,\n );\n }\n\n // Parse the AI-authored `checklists[]` into typed ChecklistAck[]. Tolerant of a missing/garbage\n // field (→ []) and of non-object entries (skipped) — malformed acks simply fail to satisfy a BLOCK\n // requirement rather than crashing the load.\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed here\n private parseChecklistAcks(value: unknown): ChecklistAck[] {\n if (!Array.isArray(value)) return [];\n const acks: ChecklistAck[] = [];\n for (const entry of value) {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) continue;\n // webpieces-disable no-any-unknown -- one opaque ack entry, narrowed field-by-field\n const e = entry as Record<string, unknown>;\n const id = typeof e['id'] === 'string' ? (e['id'] as string) : '';\n if (id === '') continue;\n acks.push(new ChecklistAck(id, e['acknowledged'] === true, this.asStringArray(e['notes'])));\n }\n return acks;\n }\n\n // BLOCK requirements that are not acknowledged → one error each, using the consumer's blockMessage\n // verbatim so the consumer owns the wording and webpieces owns the mechanism.\n private requiredChecklistErrors(required: readonly RequiredChecklist[], acks: readonly ChecklistAck[]): string[] {\n const errors: string[] = [];\n for (const req of required) {\n if (req.severity !== CHECKLIST_BLOCK) continue;\n const ack = acks.find((a: ChecklistAck): boolean => a.id === req.id);\n if (!ack || !ack.acknowledged) {\n const docs = req.docs.length > 0 ? ` Read: ${req.docs.join(', ')}.` : '';\n const msg = req.blockMessage.trim() !== '' ? `${req.blockMessage.trim()} ` : '';\n errors.push(\n `Checklist \"${req.id}\" (${req.title}) is REQUIRED for this diff but not acknowledged. ${msg}` +\n `Add {\"id\":\"${req.id}\",\"acknowledged\":true,\"notes\":[...]} to \"checklists\" once you have walked it.${docs}`,\n );\n }\n }\n return errors;\n }\n\n // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here\n private asStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard\n return value.filter((v: unknown): v is string => typeof v === 'string');\n }\n\n // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.\n // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field\n private parseReviewJson(raw: string, filePath: string): Record<string, unknown> {\n // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed by the caller\n return JSON.parse(raw) as Record<string, unknown>;\n } catch (err: unknown) {\n const error = toError(err);\n throw new InformAiError(\n `review.json is not valid JSON (${error.message}).\\n\\n${this.reviewJsonSchemaHint(filePath)}\\n\\n` +\n `Then re-run: pnpm wp-finish-upsert-pr`,\n );\n }\n }\n}\n\n// Temporary migration delegators to ReviewJsonService — removed once consumers inject it.\nconst reviewJsonSvc = new ReviewJsonService();\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function prDirFor(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.prDirFor(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonPath(repoRoot: string, featureName: string): string {\n return reviewJsonSvc.reviewJsonPath(repoRoot, featureName);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function reviewJsonSchemaHint(filePath: string, required: readonly RequiredChecklist[] = []): string {\n return reviewJsonSvc.reviewJsonSchemaHint(filePath, required);\n}\n\n// webpieces-disable no-function-outside-class -- temporary back-compat delegator to ReviewJsonService; removed once consumers inject it\nexport function loadReviewJson(filePath: string, required: readonly RequiredChecklist[] = []): ReviewJson {\n return reviewJsonSvc.loadReviewJson(filePath, required);\n}\n"]}
@@ -87,6 +87,7 @@ export declare class NoDestructureConfig extends BaseRuleConfig {
87
87
  mode?: ModifiedCodeMode;
88
88
  allowTopLevel?: boolean;
89
89
  disableAllowed?: boolean;
90
+ allowedPaths?: string[];
90
91
  static readonly SCHEMA: SchemaShape<NoDestructureConfig>;
91
92
  }
92
93
  export declare class NoUnmanagedExceptionsConfig extends BaseRuleConfig {
@@ -148,13 +148,19 @@ class PrismaConverterConfig extends BaseRuleConfig {
148
148
  };
149
149
  }
150
150
  exports.PrismaConverterConfig = PrismaConverterConfig;
151
+ // `allowedPaths` exempts whole file trees whose idioms are destructuring by construction (React /
152
+ // React Native components and hooks — `const [x, setX] = useState()`, destructured props — framework
153
+ // glue), matched with the shared glob/prefix/segment semantics of `isPathExcluded`. It is the ONLY
154
+ // escape when `disableAllowed: false`, since that setting deliberately ignores inline disables.
151
155
  class NoDestructureConfig extends BaseRuleConfig {
152
156
  allowTopLevel;
153
157
  disableAllowed;
158
+ allowedPaths;
154
159
  static SCHEMA = {
155
160
  mode: new field_def_1.FieldDef('string', exports.MODIFIED_CODE_MODES),
156
161
  allowTopLevel: field_def_1.FieldDef.optional('boolean'),
157
162
  disableAllowed: field_def_1.FieldDef.optional('boolean'),
163
+ allowedPaths: field_def_1.FieldDef.optional('string[]'),
158
164
  ...exports.BASE_RULE_SCHEMA,
159
165
  };
160
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"rule-configs.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rule-configs.ts"],"names":[],"mappings":"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","sourcesContent":["import { FieldDef, SchemaShape } from './field-def';\n\n// Mode const arrays — TypeScript union types derived from them, and FieldDef enum\n// values reference the same array. Impossible for the type and runtime check to diverge.\n\n// Single source of truth for rule \"mode\" values. Exported so code-rules (and any other\n// consumer) imports these instead of re-declaring the same unions — a rename here ripples\n// everywhere at compile time. The FieldDef SCHEMA below references the same arrays, so the\n// type and the runtime validation can never diverge.\nexport const METHOD_LIMIT_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type MethodLimitMode = typeof METHOD_LIMIT_MODES[number];\n\nexport const FILE_LIMIT_MODES = ['OFF', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type FileLimitMode = typeof FILE_LIMIT_MODES[number];\n\nexport const RETURN_TYPE_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ReturnTypeMode = typeof RETURN_TYPE_MODES[number];\n\nexport const INLINE_TYPE_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type InlineTypeMode = typeof INLINE_TYPE_MODES[number];\n\nexport const MODIFIED_CODE_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ModifiedCodeMode = typeof MODIFIED_CODE_MODES[number];\n\n// PROJECT-level rules (e.g. framework-tag): the check is neither line- nor file-scoped — it runs\n// for a whole project when ANY file the project owns is touched. `MODIFIED_PROJECTS` names that\n// honestly (nx `affected` already narrows execution to the changed projects).\nexport const PROJECT_MODES = ['OFF', 'MODIFIED_PROJECTS'] as const;\nexport type ProjectMode = typeof PROJECT_MODES[number];\n\nexport const PRISMA_DTOS_MODES = ['OFF', 'MODIFIED_CLASS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type PrismaValidateDtosMode = typeof PRISMA_DTOS_MODES[number];\n\nexport const PRISMA_CONVERTER_MODES = ['OFF', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type PrismaConverterMode = typeof PRISMA_CONVERTER_MODES[number];\n\nexport const DIRECT_API_RESOLVER_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type DirectApiResolverMode = typeof DIRECT_API_RESOLVER_MODES[number];\n\nexport const THROW_CAUSE_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE'] as const;\nexport type ThrowCauseMode = typeof THROW_CAUSE_MODES[number];\n\nexport const ON_OFF_MODES = ['ON', 'OFF'] as const;\nexport type OnOffMode = typeof ON_OFF_MODES[number];\n\n// branch-creation-guard modes. ON_NO_SUBBRANCHES is the strict variant: it hard-blocks\n// creating a branch off any non-main branch (no sub-branch affordance), pointing the agent\n// back to `git checkout main && git pull && git checkout -b <branch>`. Temporarily overridable\n// via the universal ignoreModifiedUntilEpoch escape hatch.\nexport const BRANCH_GUARD_MODES = ['ON', 'OFF', 'ON_NO_SUBBRANCHES'] as const;\nexport type BranchGuardMode = typeof BRANCH_GUARD_MODES[number];\n\nexport const VALIDATE_TS_MODES = ['OFF', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ValidateTsMode = typeof VALIDATE_TS_MODES[number];\n\n// Structural / whole-graph rules (import-cycle, runtime-architecture, nx-wiring). They can't be\n// scoped to changed lines/files — a cycle or wiring break can route through a project that wasn't\n// itself edited — so when active they run the FULL check every time (nx-affected already limits\n// them to affected projects externally). RUN_EVERY_TIME replaces the old, vaguer \"ON\".\nexport const STRUCTURAL_MODES = ['OFF', 'RUN_EVERY_TIME'] as const;\nexport type StructuralMode = typeof STRUCTURAL_MODES[number];\n\n// ---------------------------------------------------------------------------\n// Universal escape hatches — EVERY rule supports temporarily disabling itself\n// either while on a named git branch (ignoreRuleWhileOnBranch) or until an\n// epoch passes (ignoreModifiedUntilEpoch). They live on a shared base class so\n// the two fields (and their schema entries) are declared once instead of\n// repeated per rule. `mode` stays per-rule because its allowed values vary\n// (ON/OFF vs NEW_AND_MODIFIED_CODE vs NEW_AND_MODIFIED_METHODS, etc).\n//\n// `ignoreModifiedUntilEpoch` is REQUIRED on every rule so the time-box escape\n// hatch is always present and a rule can be turned off with a one-value edit.\n// Convention: 0 = rule active (epoch is in the past, never skipped); a future\n// unix epoch IN SECONDS = rule temporarily disabled until that moment.\n// `ignoreRuleWhileOnBranch` stays optional.\n// ---------------------------------------------------------------------------\nexport abstract class BaseRuleConfig {\n // `mode` is declared here (loosely typed) so the shared AbstractRule base can read it for\n // on/off. Each concrete *Config narrows it to its own union (e.g. `mode?: ModifiedCodeMode`),\n // which is an assignable (covariant) override.\n mode?: string;\n // TS-optional, but schema-REQUIRED (see BASE_RULE_SCHEMA) — same split as `mode`.\n ignoreModifiedUntilEpoch?: number;\n ignoreRuleWhileOnBranch?: string;\n}\n\nexport const BASE_RULE_SCHEMA = {\n ignoreModifiedUntilEpoch: new FieldDef('number'),\n ignoreRuleWhileOnBranch: FieldDef.optional('string'),\n};\n\nexport class MaxMethodLinesConfig extends BaseRuleConfig {\n declare mode?: MethodLimitMode;\n limit?: number;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<MaxMethodLinesConfig> = {\n mode: new FieldDef('string', METHOD_LIMIT_MODES),\n limit: FieldDef.optional('number'),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MaxFileLinesConfig extends BaseRuleConfig {\n declare mode?: FileLimitMode;\n limit?: number;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<MaxFileLinesConfig> = {\n mode: new FieldDef('string', FILE_LIMIT_MODES),\n limit: FieldDef.optional('number'),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RequireReturnTypeConfig extends BaseRuleConfig {\n declare mode?: ReturnTypeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<RequireReturnTypeConfig> = {\n mode: new FieldDef('string', RETURN_TYPE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoInlineTypeLiteralsConfig extends BaseRuleConfig {\n declare mode?: InlineTypeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoInlineTypeLiteralsConfig> = {\n mode: new FieldDef('string', INLINE_TYPE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoAnyUnknownConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoAnyUnknownConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoImplicitAnyConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoImplicitAnyConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrismaValidateDtosConfig extends BaseRuleConfig {\n declare mode?: PrismaValidateDtosMode;\n disableAllowed?: boolean;\n prismaSchemaPath?: string;\n dtoSourcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<PrismaValidateDtosConfig> = {\n mode: new FieldDef('string', PRISMA_DTOS_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n prismaSchemaPath: FieldDef.optional('string'),\n dtoSourcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrismaConverterConfig extends BaseRuleConfig {\n declare mode?: PrismaConverterMode;\n disableAllowed?: boolean;\n schemaPath?: string;\n convertersPaths?: string[];\n enforcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<PrismaConverterConfig> = {\n mode: new FieldDef('string', PRISMA_CONVERTER_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n schemaPath: FieldDef.optional('string'),\n convertersPaths: FieldDef.optional('string[]'),\n enforcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoDestructureConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n allowTopLevel?: boolean;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoDestructureConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n allowTopLevel: FieldDef.optional('boolean'),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoUnmanagedExceptionsConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoUnmanagedExceptionsConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class CatchErrorPatternConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<CatchErrorPatternConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class ThrowCauseRequiredConfig extends BaseRuleConfig {\n declare mode?: ThrowCauseMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<ThrowCauseRequiredConfig> = {\n mode: new FieldDef('string', THROW_CAUSE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class AngularNoDirectApiInResolverConfig extends BaseRuleConfig {\n declare mode?: DirectApiResolverMode;\n disableAllowed?: boolean;\n enforcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<AngularNoDirectApiInResolverConfig> = {\n mode: new FieldDef('string', DIRECT_API_RESOLVER_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n enforcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Bans hand-written CSS in Angular sources (styles:/styleUrls:/styleUrl: in @Component, and inline\n// style=/[style.x]/[ngStyle] in templates) so teams style with Tailwind utility classes. `allowGlobs`\n// exempts paths WITHIN the Angular scope (e.g. a vendored Fuse kit copied verbatim with its own CSS).\nexport class NoCustomCssConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowGlobs?: string[];\n\n static readonly SCHEMA: SchemaShape<NoCustomCssConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowGlobs: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoSymbolDiTokensConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoSymbolDiTokensConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Flags `process.exit(...)` outside a main()/runMain wrapper (and `import { main }`) so a deep exit\n// can't silently kill a reused server/command. Gradual-rollout knobs via the standard base: mode\n// (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch, branch, and\n// disableAllowed for the inline `// webpieces-disable` escape at genuine terminal boundaries.\nexport class NoProcessExitOutsideMainConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoProcessExitOutsideMainConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Flags a function CREATED OUTSIDE A CLASS at module scope: a top-level `function foo()` declaration\n// or a top-level `const foo = () => {}` / `= function(){}`. The point is that webpieces DI +\n// @DocumentDesign only work when behavior lives in injectable classes — a module-scope function is a\n// dead-end the DI graph can't reach. Inline callbacks, nested functions inside methods, and non-function\n// top-level consts (objects, zod schemas, primitives) are NOT flagged. Standard rollout knobs via the\n// base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch, branch,\n// and disableAllowed for the inline `// webpieces-disable` escape. `allowedPaths` exempts whole file\n// trees that legitimately live outside the class-per-behavior model (e.g. React component/hook files,\n// framework glue), matched with the shared glob/prefix/segment semantics of `isPathExcluded`.\nexport class NoFunctionOutsideClassConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoFunctionOutsideClassConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// inject-annotation-not-needed-for-concrete-class — flags a REDUNDANT `@inject(X)` whose token is\n// textually identical to the parameter's own declared type (`@inject(Foo) private readonly foo: Foo`).\n// In this inversify setup the decorator is pure noise there: reflect-metadata (emitDecoratorMetadata)\n// already resolves a constructor parameter by its class type, so `private readonly foo: Foo` binds on\n// its own (see CLAUDE.md, and the no-symbol-di-tokens rule that pushes the same way). Symbol/interface\n// tokens are NOT flagged because they never equal the type (`@inject(FOO_TOKEN) x: Provider<Foo>`).\n// AI keeps carpet-bombing `@inject`; this fails the build on the redundant form. Standard rollout knobs\n// via the base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch,\n// branch, and disableAllowed for the inline `// webpieces-disable` escape. `allowedPaths` exempts whole\n// file trees, matched with the shared glob/prefix/segment semantics.\nexport class InjectAnnotationNotNeededForConcreteClassConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<InjectAnnotationNotNeededForConcreteClassConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// framework-tag — every project that a changed source file belongs to must carry >=1\n// `framework:<browser|react|angular|node|express>` nx tag in its project.json. Those tags are the\n// project's \"libType\" — the SET of runtime environments it runs in — and the source of truth for\n// the dependencies.json `framework` field and the `library-types-match-client` rule. Multiple tags\n// are allowed (the env set) and values are validated against the known set (`framework:all` is a\n// hard error). `knownTypes` customizes that set (defaults to browser, react, angular, node, express).\nexport class FrameworkTagConfig extends BaseRuleConfig {\n declare mode?: ProjectMode;\n knownTypes?: string[];\n\n static readonly SCHEMA: SchemaShape<FrameworkTagConfig> = {\n mode: new FieldDef('string', PROJECT_MODES),\n knownTypes: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// role-tag — every project that a changed source file belongs to must carry a\n// `role:<server|designed-lib|lib|client>` nx tag in its project.json. That tag is the project's\n// ROLE (orthogonal to `framework` libType) — the source of truth for the dependencies.json `role`\n// field, the `role-dependency` edge rule (apps are never depended upon), and DI-design generation\n// (server→@Controller, designed-lib→@ApiImplementation, lib→none, client→angular design).\n// `knownTypes` customizes the list suggested to the author when a tag is missing.\nexport class RoleTagConfig extends BaseRuleConfig {\n declare mode?: ProjectMode;\n knownTypes?: string[];\n\n static readonly SCHEMA: SchemaShape<RoleTagConfig> = {\n mode: new FieldDef('string', PROJECT_MODES),\n knownTypes: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class BranchCreationGuardConfig extends BaseRuleConfig {\n declare mode?: BranchGuardMode;\n // Naming pattern for stacked SUB-branches only (branches created off another feature branch,\n // which require human approval). Never applied to branches created off main.\n subBranchNaming?: string;\n // Human-sentence instruction telling the AI how to name a NEW branch off main. Surfaced back\n // to the agent in the guard's fix hints. May mirror no-edit-on-main.branchNamingConvention.\n branchFormat?: string;\n // Hard cap on local feature branches (excluding main). Creating one past the cap is BLOCKED until\n // already-merged branches are reaped, which is what keeps the branch list from growing without\n // bound. Branch creation is the gate because it is the only moment cleanup is both cheap and\n // obviously worth it. See merged-branches.ts for how \"already merged\" is determined.\n maxLocalBranches?: number;\n // Hard cap on LINKED worktrees (the primary clone is never counted). A separate budget from\n // maxLocalBranches: every worktree holds a branch, so if worktree-held branches also spent the\n // branch budget, five worktrees would leave room for zero branches. Held branches count here;\n // parked branches count against maxLocalBranches. Enforced at `git worktree add`.\n maxWorktrees?: number;\n // Let the detached background refresher DELETE dead branches on its own, instead of only\n // reporting them. Every candidate is provably dead (merged PR / squash backup / no commits) and\n // recoverable by the SHA logged to branch-mutations.log — but it is still UNATTENDED deletion,\n // so this is schema-REQUIRED like `mode` and `ignoreModifiedUntilEpoch`. \"Every built-in rule\n // must be explicitly configured — no silent defaults\" (validate-config.ts) applies with extra\n // force here: branches disappearing on a preference nobody ever stated is precisely the kind of\n // default that must not exist. Validation makes each consumer answer the question once.\n //\n // TS-optional but schema-required — the same split `mode` uses. Absent at RUNTIME therefore means\n // the config never passed validation, and the only safe reading of \"nobody has answered\" is: do\n // not delete anything.\n autoReapMergedBranches?: boolean;\n\n static readonly SCHEMA: SchemaShape<BranchCreationGuardConfig> = {\n mode: new FieldDef('string', BRANCH_GUARD_MODES),\n subBranchNaming: FieldDef.optional('string'),\n branchFormat: FieldDef.optional('string'),\n maxLocalBranches: FieldDef.optional('number'),\n maxWorktrees: FieldDef.optional('number'),\n autoReapMergedBranches: new FieldDef('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrCreationOrPushGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n // The gated command the guard points agents to instead of direct PR creation OR a manual push.\n // Per-project override; defaults to `pnpm wp-start-upsert-pr` at the point of use.\n upsertPrCommand?: string;\n\n static readonly SCHEMA: SchemaShape<PrCreationOrPushGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n upsertPrCommand: FieldDef.optional('string'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MergeInProgressGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n // The gated command the guard points agents to in order to finish a 3-point merge. Per-project\n // override; defaults to `commands.mergeComplete` (pnpm wp-finish-upsert-pr) at load time.\n mergeCompleteCommand?: string;\n\n static readonly SCHEMA: SchemaShape<MergeInProgressGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n mergeCompleteCommand: FieldDef.optional('string'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrMergeGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n\n static readonly SCHEMA: SchemaShape<PrMergeGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RedirectHowToMergeMainConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n\n static readonly SCHEMA: SchemaShape<RedirectHowToMergeMainConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Comprehensive \"are you on a proper feature branch?\" guard. Replaces the old no-edit-on-main:\n// - on main (synchronous check) → block, create a feature branch\n// - branch already merged into main (merged PR) → block, branch off fresh main\n// - no fork point with origin/main → block, squash onto a new branch\n// - origin/main moved & touches your files → block, merge main first\n// branchNamingConvention is surfaced in the on-main message; hangTimeoutMinutes tunes the detached\n// refresher's stale-lock reclaim window.\nexport class FeatureBranchGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n branchNamingConvention?: string;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<FeatureBranchGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n branchNamingConvention: FieldDef.optional('string'),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Blocks Read while the checked-out branch is stale to read FROM — either main is behind origin/main,\n// or the feature branch's PR is already merged (a pre-merge snapshot). Both mean the AI reads stale\n// FILES, which is the actual damage. Deliberately scoped to Read only: every cure (`git pull origin\n// main`, `git checkout -b <new> origin/main`, `pnpm install`, any upgrade) is a Bash command, and Bash\n// is never touched by this guard, so there is no allowlist to maintain and no way to wedge.\n// (Named main-stale-guard through 0.4.x; the old config key still loads via DEPRECATED_RULE_ALIASES.)\n// hangTimeoutMinutes tunes the detached refresher's stale-lock reclaim window (as on\n// feature-branch-guard, whose cache this guard shares).\nexport class ReadStaleGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<ReadStaleGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// The BASH-side half of the merged-branch protection. feature-branch-guard blocks Write/Edit and\n// read-stale-guard blocks the Read tool when the checked-out branch's PR is already merged — but\n// NOTHING blocked ordinary Bash, so an agent that only ran shell (booting servers, `cat`-ing files,\n// git) sailed through a whole session on a merged branch (the incident this guard closes). It is a\n// bash-scope hookGuard that DEFAULT-DENIES Bash on a merged branch, allowlisting only the recovery /\n// cleanup / read-only-inspection commands that get the agent OFF the branch. Shares the same\n// precomputed cache (main-sync-status.json / branchAlreadyMerged) the two file guards read.\n// hangTimeoutMinutes tunes the detached refresher's stale-lock reclaim window, as on those guards.\nexport class MergedBranchBashGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<MergedBranchBashGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoFileImportCyclesConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n ignoreTypeOnly?: boolean;\n excludePackages?: string[];\n\n static readonly SCHEMA: SchemaShape<NoFileImportCyclesConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ignoreTypeOnly: FieldDef.optional('boolean'),\n excludePackages: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RuntimeArchitectureConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n // NOTE: `servicePaths` / `apiProjectPaths` were removed — they were never read. The runtime graph\n // is derived SOLELY from architecture/dependencies.json apiRelations + project roles (see\n // nx-webpieces-rules/src/lib/runtime-graph.ts), so any config still listing them (a hand-maintained\n // enumeration of api libs) now fails the unknown-field check in validateWebpiecesConfig. There is\n // nothing to enumerate — delete the keys.\n allowedCycles?: string[];\n\n static readonly SCHEMA: SchemaShape<RuntimeArchitectureConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n allowedCycles: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NxWiringConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<NxWiringConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class DiGraphConfig extends BaseRuleConfig {\n // Structural: the DI graph is regenerated whole-project on every build (generate +\n // unchanged gate), so it cannot be scoped to changed lines.\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<DiGraphConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MissingDesignAnnotationConfig extends BaseRuleConfig {\n // Structural: enforced per-project by the di-graph-generate executor, which\n // roots the whole DI design on @DocumentDesign classes — cannot be line-scoped.\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<MissingDesignAnnotationConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoJsFilesConfig extends BaseRuleConfig {\n // File-tier: NEW_AND_MODIFIED_FILES (active) intercepts a .js/.jsx Write — the file being\n // written is inherently a new/modified file, so it's already diff-scoped in practice.\n declare mode?: FileLimitMode;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoJsFilesConfig> = {\n mode: new FieldDef('string', FILE_LIMIT_MODES),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class ValidateTsInSrcConfig extends BaseRuleConfig {\n declare mode?: ValidateTsMode;\n allowedRootFiles?: string[];\n excludePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<ValidateTsInSrcConfig> = {\n mode: new FieldDef('string', VALIDATE_TS_MODES),\n allowedRootFiles: FieldDef.optional('string[]'),\n excludePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
1
+ {"version":3,"file":"rule-configs.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/rule-configs.ts"],"names":[],"mappings":"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","sourcesContent":["import { FieldDef, SchemaShape } from './field-def';\n\n// Mode const arrays — TypeScript union types derived from them, and FieldDef enum\n// values reference the same array. Impossible for the type and runtime check to diverge.\n\n// Single source of truth for rule \"mode\" values. Exported so code-rules (and any other\n// consumer) imports these instead of re-declaring the same unions — a rename here ripples\n// everywhere at compile time. The FieldDef SCHEMA below references the same arrays, so the\n// type and the runtime validation can never diverge.\nexport const METHOD_LIMIT_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type MethodLimitMode = typeof METHOD_LIMIT_MODES[number];\n\nexport const FILE_LIMIT_MODES = ['OFF', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type FileLimitMode = typeof FILE_LIMIT_MODES[number];\n\nexport const RETURN_TYPE_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ReturnTypeMode = typeof RETURN_TYPE_MODES[number];\n\nexport const INLINE_TYPE_MODES = ['OFF', 'NEW_METHODS', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type InlineTypeMode = typeof INLINE_TYPE_MODES[number];\n\nexport const MODIFIED_CODE_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ModifiedCodeMode = typeof MODIFIED_CODE_MODES[number];\n\n// PROJECT-level rules (e.g. framework-tag): the check is neither line- nor file-scoped — it runs\n// for a whole project when ANY file the project owns is touched. `MODIFIED_PROJECTS` names that\n// honestly (nx `affected` already narrows execution to the changed projects).\nexport const PROJECT_MODES = ['OFF', 'MODIFIED_PROJECTS'] as const;\nexport type ProjectMode = typeof PROJECT_MODES[number];\n\nexport const PRISMA_DTOS_MODES = ['OFF', 'MODIFIED_CLASS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type PrismaValidateDtosMode = typeof PRISMA_DTOS_MODES[number];\n\nexport const PRISMA_CONVERTER_MODES = ['OFF', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type PrismaConverterMode = typeof PRISMA_CONVERTER_MODES[number];\n\nexport const DIRECT_API_RESOLVER_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE', 'NEW_AND_MODIFIED_METHODS', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type DirectApiResolverMode = typeof DIRECT_API_RESOLVER_MODES[number];\n\nexport const THROW_CAUSE_MODES = ['OFF', 'NEW_AND_MODIFIED_CODE'] as const;\nexport type ThrowCauseMode = typeof THROW_CAUSE_MODES[number];\n\nexport const ON_OFF_MODES = ['ON', 'OFF'] as const;\nexport type OnOffMode = typeof ON_OFF_MODES[number];\n\n// branch-creation-guard modes. ON_NO_SUBBRANCHES is the strict variant: it hard-blocks\n// creating a branch off any non-main branch (no sub-branch affordance), pointing the agent\n// back to `git checkout main && git pull && git checkout -b <branch>`. Temporarily overridable\n// via the universal ignoreModifiedUntilEpoch escape hatch.\nexport const BRANCH_GUARD_MODES = ['ON', 'OFF', 'ON_NO_SUBBRANCHES'] as const;\nexport type BranchGuardMode = typeof BRANCH_GUARD_MODES[number];\n\nexport const VALIDATE_TS_MODES = ['OFF', 'NEW_AND_MODIFIED_FILES'] as const;\nexport type ValidateTsMode = typeof VALIDATE_TS_MODES[number];\n\n// Structural / whole-graph rules (import-cycle, runtime-architecture, nx-wiring). They can't be\n// scoped to changed lines/files — a cycle or wiring break can route through a project that wasn't\n// itself edited — so when active they run the FULL check every time (nx-affected already limits\n// them to affected projects externally). RUN_EVERY_TIME replaces the old, vaguer \"ON\".\nexport const STRUCTURAL_MODES = ['OFF', 'RUN_EVERY_TIME'] as const;\nexport type StructuralMode = typeof STRUCTURAL_MODES[number];\n\n// ---------------------------------------------------------------------------\n// Universal escape hatches — EVERY rule supports temporarily disabling itself\n// either while on a named git branch (ignoreRuleWhileOnBranch) or until an\n// epoch passes (ignoreModifiedUntilEpoch). They live on a shared base class so\n// the two fields (and their schema entries) are declared once instead of\n// repeated per rule. `mode` stays per-rule because its allowed values vary\n// (ON/OFF vs NEW_AND_MODIFIED_CODE vs NEW_AND_MODIFIED_METHODS, etc).\n//\n// `ignoreModifiedUntilEpoch` is REQUIRED on every rule so the time-box escape\n// hatch is always present and a rule can be turned off with a one-value edit.\n// Convention: 0 = rule active (epoch is in the past, never skipped); a future\n// unix epoch IN SECONDS = rule temporarily disabled until that moment.\n// `ignoreRuleWhileOnBranch` stays optional.\n// ---------------------------------------------------------------------------\nexport abstract class BaseRuleConfig {\n // `mode` is declared here (loosely typed) so the shared AbstractRule base can read it for\n // on/off. Each concrete *Config narrows it to its own union (e.g. `mode?: ModifiedCodeMode`),\n // which is an assignable (covariant) override.\n mode?: string;\n // TS-optional, but schema-REQUIRED (see BASE_RULE_SCHEMA) — same split as `mode`.\n ignoreModifiedUntilEpoch?: number;\n ignoreRuleWhileOnBranch?: string;\n}\n\nexport const BASE_RULE_SCHEMA = {\n ignoreModifiedUntilEpoch: new FieldDef('number'),\n ignoreRuleWhileOnBranch: FieldDef.optional('string'),\n};\n\nexport class MaxMethodLinesConfig extends BaseRuleConfig {\n declare mode?: MethodLimitMode;\n limit?: number;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<MaxMethodLinesConfig> = {\n mode: new FieldDef('string', METHOD_LIMIT_MODES),\n limit: FieldDef.optional('number'),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MaxFileLinesConfig extends BaseRuleConfig {\n declare mode?: FileLimitMode;\n limit?: number;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<MaxFileLinesConfig> = {\n mode: new FieldDef('string', FILE_LIMIT_MODES),\n limit: FieldDef.optional('number'),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RequireReturnTypeConfig extends BaseRuleConfig {\n declare mode?: ReturnTypeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<RequireReturnTypeConfig> = {\n mode: new FieldDef('string', RETURN_TYPE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoInlineTypeLiteralsConfig extends BaseRuleConfig {\n declare mode?: InlineTypeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoInlineTypeLiteralsConfig> = {\n mode: new FieldDef('string', INLINE_TYPE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoAnyUnknownConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoAnyUnknownConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoImplicitAnyConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoImplicitAnyConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrismaValidateDtosConfig extends BaseRuleConfig {\n declare mode?: PrismaValidateDtosMode;\n disableAllowed?: boolean;\n prismaSchemaPath?: string;\n dtoSourcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<PrismaValidateDtosConfig> = {\n mode: new FieldDef('string', PRISMA_DTOS_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n prismaSchemaPath: FieldDef.optional('string'),\n dtoSourcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrismaConverterConfig extends BaseRuleConfig {\n declare mode?: PrismaConverterMode;\n disableAllowed?: boolean;\n schemaPath?: string;\n convertersPaths?: string[];\n enforcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<PrismaConverterConfig> = {\n mode: new FieldDef('string', PRISMA_CONVERTER_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n schemaPath: FieldDef.optional('string'),\n convertersPaths: FieldDef.optional('string[]'),\n enforcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// `allowedPaths` exempts whole file trees whose idioms are destructuring by construction (React /\n// React Native components and hooks — `const [x, setX] = useState()`, destructured props — framework\n// glue), matched with the shared glob/prefix/segment semantics of `isPathExcluded`. It is the ONLY\n// escape when `disableAllowed: false`, since that setting deliberately ignores inline disables.\nexport class NoDestructureConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n allowTopLevel?: boolean;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoDestructureConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n allowTopLevel: FieldDef.optional('boolean'),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoUnmanagedExceptionsConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoUnmanagedExceptionsConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class CatchErrorPatternConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<CatchErrorPatternConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class ThrowCauseRequiredConfig extends BaseRuleConfig {\n declare mode?: ThrowCauseMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<ThrowCauseRequiredConfig> = {\n mode: new FieldDef('string', THROW_CAUSE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class AngularNoDirectApiInResolverConfig extends BaseRuleConfig {\n declare mode?: DirectApiResolverMode;\n disableAllowed?: boolean;\n enforcePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<AngularNoDirectApiInResolverConfig> = {\n mode: new FieldDef('string', DIRECT_API_RESOLVER_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n enforcePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Bans hand-written CSS in Angular sources (styles:/styleUrls:/styleUrl: in @Component, and inline\n// style=/[style.x]/[ngStyle] in templates) so teams style with Tailwind utility classes. `allowGlobs`\n// exempts paths WITHIN the Angular scope (e.g. a vendored Fuse kit copied verbatim with its own CSS).\nexport class NoCustomCssConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowGlobs?: string[];\n\n static readonly SCHEMA: SchemaShape<NoCustomCssConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowGlobs: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoSymbolDiTokensConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoSymbolDiTokensConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Flags `process.exit(...)` outside a main()/runMain wrapper (and `import { main }`) so a deep exit\n// can't silently kill a reused server/command. Gradual-rollout knobs via the standard base: mode\n// (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch, branch, and\n// disableAllowed for the inline `// webpieces-disable` escape at genuine terminal boundaries.\nexport class NoProcessExitOutsideMainConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n\n static readonly SCHEMA: SchemaShape<NoProcessExitOutsideMainConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Flags a function CREATED OUTSIDE A CLASS at module scope: a top-level `function foo()` declaration\n// or a top-level `const foo = () => {}` / `= function(){}`. The point is that webpieces DI +\n// @DocumentDesign only work when behavior lives in injectable classes — a module-scope function is a\n// dead-end the DI graph can't reach. Inline callbacks, nested functions inside methods, and non-function\n// top-level consts (objects, zod schemas, primitives) are NOT flagged. Standard rollout knobs via the\n// base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch, branch,\n// and disableAllowed for the inline `// webpieces-disable` escape. `allowedPaths` exempts whole file\n// trees that legitimately live outside the class-per-behavior model (e.g. React component/hook files,\n// framework glue), matched with the shared glob/prefix/segment semantics of `isPathExcluded`.\nexport class NoFunctionOutsideClassConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoFunctionOutsideClassConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// inject-annotation-not-needed-for-concrete-class — flags a REDUNDANT `@inject(X)` whose token is\n// textually identical to the parameter's own declared type (`@inject(Foo) private readonly foo: Foo`).\n// In this inversify setup the decorator is pure noise there: reflect-metadata (emitDecoratorMetadata)\n// already resolves a constructor parameter by its class type, so `private readonly foo: Foo` binds on\n// its own (see CLAUDE.md, and the no-symbol-di-tokens rule that pushes the same way). Symbol/interface\n// tokens are NOT flagged because they never equal the type (`@inject(FOO_TOKEN) x: Provider<Foo>`).\n// AI keeps carpet-bombing `@inject`; this fails the build on the redundant form. Standard rollout knobs\n// via the base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch,\n// branch, and disableAllowed for the inline `// webpieces-disable` escape. `allowedPaths` exempts whole\n// file trees, matched with the shared glob/prefix/segment semantics.\nexport class InjectAnnotationNotNeededForConcreteClassConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n disableAllowed?: boolean;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<InjectAnnotationNotNeededForConcreteClassConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// framework-tag — every project that a changed source file belongs to must carry >=1\n// `framework:<browser|react|angular|node|express>` nx tag in its project.json. Those tags are the\n// project's \"libType\" — the SET of runtime environments it runs in — and the source of truth for\n// the dependencies.json `framework` field and the `library-types-match-client` rule. Multiple tags\n// are allowed (the env set) and values are validated against the known set (`framework:all` is a\n// hard error). `knownTypes` customizes that set (defaults to browser, react, angular, node, express).\nexport class FrameworkTagConfig extends BaseRuleConfig {\n declare mode?: ProjectMode;\n knownTypes?: string[];\n\n static readonly SCHEMA: SchemaShape<FrameworkTagConfig> = {\n mode: new FieldDef('string', PROJECT_MODES),\n knownTypes: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// role-tag — every project that a changed source file belongs to must carry a\n// `role:<server|designed-lib|lib|client>` nx tag in its project.json. That tag is the project's\n// ROLE (orthogonal to `framework` libType) — the source of truth for the dependencies.json `role`\n// field, the `role-dependency` edge rule (apps are never depended upon), and DI-design generation\n// (server→@Controller, designed-lib→@ApiImplementation, lib→none, client→angular design).\n// `knownTypes` customizes the list suggested to the author when a tag is missing.\nexport class RoleTagConfig extends BaseRuleConfig {\n declare mode?: ProjectMode;\n knownTypes?: string[];\n\n static readonly SCHEMA: SchemaShape<RoleTagConfig> = {\n mode: new FieldDef('string', PROJECT_MODES),\n knownTypes: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class BranchCreationGuardConfig extends BaseRuleConfig {\n declare mode?: BranchGuardMode;\n // Naming pattern for stacked SUB-branches only (branches created off another feature branch,\n // which require human approval). Never applied to branches created off main.\n subBranchNaming?: string;\n // Human-sentence instruction telling the AI how to name a NEW branch off main. Surfaced back\n // to the agent in the guard's fix hints. May mirror no-edit-on-main.branchNamingConvention.\n branchFormat?: string;\n // Hard cap on local feature branches (excluding main). Creating one past the cap is BLOCKED until\n // already-merged branches are reaped, which is what keeps the branch list from growing without\n // bound. Branch creation is the gate because it is the only moment cleanup is both cheap and\n // obviously worth it. See merged-branches.ts for how \"already merged\" is determined.\n maxLocalBranches?: number;\n // Hard cap on LINKED worktrees (the primary clone is never counted). A separate budget from\n // maxLocalBranches: every worktree holds a branch, so if worktree-held branches also spent the\n // branch budget, five worktrees would leave room for zero branches. Held branches count here;\n // parked branches count against maxLocalBranches. Enforced at `git worktree add`.\n maxWorktrees?: number;\n // Let the detached background refresher DELETE dead branches on its own, instead of only\n // reporting them. Every candidate is provably dead (merged PR / squash backup / no commits) and\n // recoverable by the SHA logged to branch-mutations.log — but it is still UNATTENDED deletion,\n // so this is schema-REQUIRED like `mode` and `ignoreModifiedUntilEpoch`. \"Every built-in rule\n // must be explicitly configured — no silent defaults\" (validate-config.ts) applies with extra\n // force here: branches disappearing on a preference nobody ever stated is precisely the kind of\n // default that must not exist. Validation makes each consumer answer the question once.\n //\n // TS-optional but schema-required — the same split `mode` uses. Absent at RUNTIME therefore means\n // the config never passed validation, and the only safe reading of \"nobody has answered\" is: do\n // not delete anything.\n autoReapMergedBranches?: boolean;\n\n static readonly SCHEMA: SchemaShape<BranchCreationGuardConfig> = {\n mode: new FieldDef('string', BRANCH_GUARD_MODES),\n subBranchNaming: FieldDef.optional('string'),\n branchFormat: FieldDef.optional('string'),\n maxLocalBranches: FieldDef.optional('number'),\n maxWorktrees: FieldDef.optional('number'),\n autoReapMergedBranches: new FieldDef('boolean'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrCreationOrPushGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n // The gated command the guard points agents to instead of direct PR creation OR a manual push.\n // Per-project override; defaults to `pnpm wp-start-upsert-pr` at the point of use.\n upsertPrCommand?: string;\n\n static readonly SCHEMA: SchemaShape<PrCreationOrPushGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n upsertPrCommand: FieldDef.optional('string'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MergeInProgressGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n // The gated command the guard points agents to in order to finish a 3-point merge. Per-project\n // override; defaults to `commands.mergeComplete` (pnpm wp-finish-upsert-pr) at load time.\n mergeCompleteCommand?: string;\n\n static readonly SCHEMA: SchemaShape<MergeInProgressGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n mergeCompleteCommand: FieldDef.optional('string'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class PrMergeGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n\n static readonly SCHEMA: SchemaShape<PrMergeGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RedirectHowToMergeMainConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n\n static readonly SCHEMA: SchemaShape<RedirectHowToMergeMainConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Comprehensive \"are you on a proper feature branch?\" guard. Replaces the old no-edit-on-main:\n// - on main (synchronous check) → block, create a feature branch\n// - branch already merged into main (merged PR) → block, branch off fresh main\n// - no fork point with origin/main → block, squash onto a new branch\n// - origin/main moved & touches your files → block, merge main first\n// branchNamingConvention is surfaced in the on-main message; hangTimeoutMinutes tunes the detached\n// refresher's stale-lock reclaim window.\nexport class FeatureBranchGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n branchNamingConvention?: string;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<FeatureBranchGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n branchNamingConvention: FieldDef.optional('string'),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// Blocks Read while the checked-out branch is stale to read FROM — either main is behind origin/main,\n// or the feature branch's PR is already merged (a pre-merge snapshot). Both mean the AI reads stale\n// FILES, which is the actual damage. Deliberately scoped to Read only: every cure (`git pull origin\n// main`, `git checkout -b <new> origin/main`, `pnpm install`, any upgrade) is a Bash command, and Bash\n// is never touched by this guard, so there is no allowlist to maintain and no way to wedge.\n// (Named main-stale-guard through 0.4.x; the old config key still loads via DEPRECATED_RULE_ALIASES.)\n// hangTimeoutMinutes tunes the detached refresher's stale-lock reclaim window (as on\n// feature-branch-guard, whose cache this guard shares).\nexport class ReadStaleGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<ReadStaleGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\n// The BASH-side half of the merged-branch protection. feature-branch-guard blocks Write/Edit and\n// read-stale-guard blocks the Read tool when the checked-out branch's PR is already merged — but\n// NOTHING blocked ordinary Bash, so an agent that only ran shell (booting servers, `cat`-ing files,\n// git) sailed through a whole session on a merged branch (the incident this guard closes). It is a\n// bash-scope hookGuard that DEFAULT-DENIES Bash on a merged branch, allowlisting only the recovery /\n// cleanup / read-only-inspection commands that get the agent OFF the branch. Shares the same\n// precomputed cache (main-sync-status.json / branchAlreadyMerged) the two file guards read.\n// hangTimeoutMinutes tunes the detached refresher's stale-lock reclaim window, as on those guards.\nexport class MergedBranchBashGuardConfig extends BaseRuleConfig {\n declare mode?: OnOffMode;\n hangTimeoutMinutes?: number;\n\n static readonly SCHEMA: SchemaShape<MergedBranchBashGuardConfig> = {\n mode: new FieldDef('string', ON_OFF_MODES),\n hangTimeoutMinutes: FieldDef.optional('number'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoFileImportCyclesConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n ignoreTypeOnly?: boolean;\n excludePackages?: string[];\n\n static readonly SCHEMA: SchemaShape<NoFileImportCyclesConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ignoreTypeOnly: FieldDef.optional('boolean'),\n excludePackages: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class RuntimeArchitectureConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n // NOTE: `servicePaths` / `apiProjectPaths` were removed — they were never read. The runtime graph\n // is derived SOLELY from architecture/dependencies.json apiRelations + project roles (see\n // nx-webpieces-rules/src/lib/runtime-graph.ts), so any config still listing them (a hand-maintained\n // enumeration of api libs) now fails the unknown-field check in validateWebpiecesConfig. There is\n // nothing to enumerate — delete the keys.\n allowedCycles?: string[];\n\n static readonly SCHEMA: SchemaShape<RuntimeArchitectureConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n allowedCycles: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NxWiringConfig extends BaseRuleConfig {\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<NxWiringConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class DiGraphConfig extends BaseRuleConfig {\n // Structural: the DI graph is regenerated whole-project on every build (generate +\n // unchanged gate), so it cannot be scoped to changed lines.\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<DiGraphConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class MissingDesignAnnotationConfig extends BaseRuleConfig {\n // Structural: enforced per-project by the di-graph-generate executor, which\n // roots the whole DI design on @DocumentDesign classes — cannot be line-scoped.\n declare mode?: StructuralMode;\n\n static readonly SCHEMA: SchemaShape<MissingDesignAnnotationConfig> = {\n mode: new FieldDef('string', STRUCTURAL_MODES),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class NoJsFilesConfig extends BaseRuleConfig {\n // File-tier: NEW_AND_MODIFIED_FILES (active) intercepts a .js/.jsx Write — the file being\n // written is inherently a new/modified file, so it's already diff-scoped in practice.\n declare mode?: FileLimitMode;\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoJsFilesConfig> = {\n mode: new FieldDef('string', FILE_LIMIT_MODES),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n\nexport class ValidateTsInSrcConfig extends BaseRuleConfig {\n declare mode?: ValidateTsMode;\n allowedRootFiles?: string[];\n excludePaths?: string[];\n\n static readonly SCHEMA: SchemaShape<ValidateTsInSrcConfig> = {\n mode: new FieldDef('string', VALIDATE_TS_MODES),\n allowedRootFiles: FieldDef.optional('string[]'),\n excludePaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
@@ -4,9 +4,10 @@ export declare function validateWebpiecesConfig(rawRules: Record<string, Record<
4
4
  * Validate the top-level `pr-gate` section. It is REQUIRED (a client that opts out sets mode "OFF").
5
5
  * `buildCommand` is required unless mode is "OFF". Returns human-readable, copy-paste-friendly errors
6
6
  * — never throws. The pr-gate block lives outside the FieldDef-driven `rules` schema because its
7
- * nested `gates` array can't be expressed there, so it gets its own structural validation here.
7
+ * nested `gates`/`checklists` arrays can't be expressed there, so they get structural validation here.
8
+ * `repoRoot` (when known) lets the `checklists[].docs` existence check run.
8
9
  */
9
- export declare function validatePrGateSection(section: unknown): string[];
10
+ export declare function validatePrGateSection(section: unknown, repoRoot?: string): string[];
10
11
  /**
11
12
  * Validate the REQUIRED top-level `excludePaths` block: two independent glob lists (`rules`,
12
13
  * `guards`) that suppress hook enforcement per file path. Required so every client upgrading is
@@ -35,4 +36,4 @@ export declare function validateSectionPlacement(rulesSection: Record<string, Re
35
36
  * optional command-string fields. Also surfaces a migration error if a DEPRECATED top-level `pr-gate`
36
37
  * block is still present, telling the consumer to move it under `commands`.
37
38
  */
38
- export declare function validateCommandsSection(commands: unknown, legacyPrGate: unknown): string[];
39
+ export declare function validateCommandsSection(commands: unknown, legacyPrGate: unknown, repoRoot?: string): string[];