@miller-tech/uap 2.1.6 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/config/capacity-policy.json +32 -0
  2. package/config/classify-thresholds.json +9 -0
  3. package/config/supervise-policy.json +13 -0
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/bin/cli.js +87 -2
  6. package/dist/bin/cli.js.map +1 -1
  7. package/dist/capacity/policy.d.ts +67 -0
  8. package/dist/capacity/policy.d.ts.map +1 -0
  9. package/dist/capacity/policy.js +133 -0
  10. package/dist/capacity/policy.js.map +1 -0
  11. package/dist/capacity/probe.d.ts +23 -0
  12. package/dist/capacity/probe.d.ts.map +1 -0
  13. package/dist/capacity/probe.js +93 -0
  14. package/dist/capacity/probe.js.map +1 -0
  15. package/dist/classify/baseline.d.ts +24 -0
  16. package/dist/classify/baseline.d.ts.map +1 -0
  17. package/dist/classify/baseline.js +140 -0
  18. package/dist/classify/baseline.js.map +1 -0
  19. package/dist/classify/index.d.ts +30 -0
  20. package/dist/classify/index.d.ts.map +1 -0
  21. package/dist/classify/index.js +84 -0
  22. package/dist/classify/index.js.map +1 -0
  23. package/dist/classify/shadow.d.ts +34 -0
  24. package/dist/classify/shadow.d.ts.map +1 -0
  25. package/dist/classify/shadow.js +33 -0
  26. package/dist/classify/shadow.js.map +1 -0
  27. package/dist/classify/types.d.ts +64 -0
  28. package/dist/classify/types.d.ts.map +1 -0
  29. package/dist/classify/types.js +37 -0
  30. package/dist/classify/types.js.map +1 -0
  31. package/dist/cli/classify.d.ts +24 -0
  32. package/dist/cli/classify.d.ts.map +1 -0
  33. package/dist/cli/classify.js +94 -0
  34. package/dist/cli/classify.js.map +1 -0
  35. package/dist/cli/deliver.d.ts.map +1 -1
  36. package/dist/cli/deliver.js +26 -0
  37. package/dist/cli/deliver.js.map +1 -1
  38. package/dist/cli/doctor.d.ts +9 -0
  39. package/dist/cli/doctor.d.ts.map +1 -0
  40. package/dist/cli/doctor.js +49 -0
  41. package/dist/cli/doctor.js.map +1 -0
  42. package/dist/cli/review.d.ts +12 -0
  43. package/dist/cli/review.d.ts.map +1 -0
  44. package/dist/cli/review.js +118 -0
  45. package/dist/cli/review.js.map +1 -0
  46. package/dist/cli/supervise.d.ts +8 -0
  47. package/dist/cli/supervise.d.ts.map +1 -0
  48. package/dist/cli/supervise.js +76 -0
  49. package/dist/cli/supervise.js.map +1 -0
  50. package/dist/delivery/convergence-loop.d.ts +8 -0
  51. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  52. package/dist/delivery/convergence-loop.js +1 -0
  53. package/dist/delivery/convergence-loop.js.map +1 -1
  54. package/dist/delivery/delivery-result.d.ts.map +1 -1
  55. package/dist/delivery/delivery-result.js +5 -0
  56. package/dist/delivery/delivery-result.js.map +1 -1
  57. package/dist/delivery/gate-evidence.d.ts +143 -0
  58. package/dist/delivery/gate-evidence.d.ts.map +1 -0
  59. package/dist/delivery/gate-evidence.js +239 -0
  60. package/dist/delivery/gate-evidence.js.map +1 -0
  61. package/dist/delivery/index.d.ts +1 -0
  62. package/dist/delivery/index.d.ts.map +1 -1
  63. package/dist/delivery/index.js +1 -0
  64. package/dist/delivery/index.js.map +1 -1
  65. package/dist/evals/routing-corpus.d.ts +35 -0
  66. package/dist/evals/routing-corpus.d.ts.map +1 -0
  67. package/dist/evals/routing-corpus.js +142 -0
  68. package/dist/evals/routing-corpus.js.map +1 -0
  69. package/dist/evals/routing-eval.d.ts +104 -0
  70. package/dist/evals/routing-eval.d.ts.map +1 -0
  71. package/dist/evals/routing-eval.js +231 -0
  72. package/dist/evals/routing-eval.js.map +1 -0
  73. package/dist/evals/tfidf.d.ts +35 -0
  74. package/dist/evals/tfidf.d.ts.map +1 -0
  75. package/dist/evals/tfidf.js +132 -0
  76. package/dist/evals/tfidf.js.map +1 -0
  77. package/dist/review/prepass.d.ts +88 -0
  78. package/dist/review/prepass.d.ts.map +1 -0
  79. package/dist/review/prepass.js +428 -0
  80. package/dist/review/prepass.js.map +1 -0
  81. package/dist/review/visual-captures.d.ts +60 -0
  82. package/dist/review/visual-captures.d.ts.map +1 -0
  83. package/dist/review/visual-captures.js +222 -0
  84. package/dist/review/visual-captures.js.map +1 -0
  85. package/dist/supervise/assess.d.ts +73 -0
  86. package/dist/supervise/assess.d.ts.map +1 -0
  87. package/dist/supervise/assess.js +215 -0
  88. package/dist/supervise/assess.js.map +1 -0
  89. package/dist/supervise/config.d.ts +13 -0
  90. package/dist/supervise/config.d.ts.map +1 -0
  91. package/dist/supervise/config.js +81 -0
  92. package/dist/supervise/config.js.map +1 -0
  93. package/dist/supervise/loop.d.ts +78 -0
  94. package/dist/supervise/loop.d.ts.map +1 -0
  95. package/dist/supervise/loop.js +311 -0
  96. package/dist/supervise/loop.js.map +1 -0
  97. package/dist/supervise/observe.d.ts +31 -0
  98. package/dist/supervise/observe.d.ts.map +1 -0
  99. package/dist/supervise/observe.js +172 -0
  100. package/dist/supervise/observe.js.map +1 -0
  101. package/dist/supervise/policy.d.ts +16 -0
  102. package/dist/supervise/policy.d.ts.map +1 -0
  103. package/dist/supervise/policy.js +147 -0
  104. package/dist/supervise/policy.js.map +1 -0
  105. package/dist/supervise/types.d.ts +109 -0
  106. package/dist/supervise/types.d.ts.map +1 -0
  107. package/dist/supervise/types.js +11 -0
  108. package/dist/supervise/types.js.map +1 -0
  109. package/docs/INDEX.md +9 -3
  110. package/docs/architecture/adr/0001-automode-risk-floor.md +75 -0
  111. package/docs/architecture/adr/0002-evidence-bound-ship.md +81 -0
  112. package/docs/guides/CAPACITY_POLICY.md +98 -0
  113. package/docs/performance/automode-risk-calibration.md +72 -0
  114. package/docs/performance/reliability-ladder.md +78 -0
  115. package/docs/plans/system1-uplift-2026-09-18.md +191 -0
  116. package/docs/reference/automode-risk.md +134 -0
  117. package/docs/reference/evidence-bound-ship.md +145 -0
  118. package/docs/reference/review-prepass.md +77 -0
  119. package/docs/reference/routing-evals.md +71 -0
  120. package/docs/reference/semantic-supervisor.md +140 -0
  121. package/docs/reference/system1-classifier.md +103 -0
  122. package/docs/reference/visual-captures.md +113 -0
  123. package/package.json +5 -3
  124. package/scripts/run-anthropic-proxy-continuity.sh +36 -0
  125. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  126. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  127. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  128. package/src/policies/enforcers/enforcement_self_protect.py +25 -2
  129. package/src/policies/enforcers/evidence_bound_ship.py +257 -0
  130. package/src/policies/enforcers/expert_review_required.py +120 -0
  131. package/src/policies/schemas/policies/evidence-bound-ship.md +106 -0
  132. package/src/policies/schemas/policies/visual-verification.md +8 -0
  133. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  134. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  135. package/tools/agents/scripts/anthropic_proxy.py +782 -19
  136. package/tools/agents/scripts/project_telemetry.py +52 -0
  137. package/tools/agents/scripts/tool_risk.py +789 -0
  138. package/tools/agents/scripts/tool_risk_calibrate.py +255 -0
  139. package/tools/agents/tests/fixtures/tool_risk_cases.json +124 -0
  140. package/tools/agents/tests/test_enforcer_escape_hatches.py +13 -0
  141. package/tools/agents/tests/test_gate_evidence.py +56 -0
  142. package/tools/agents/tests/test_tool_risk.py +521 -0
  143. package/tools/agents/tests/test_verbatim_compact.py +629 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * DETERMINISTIC REVIEW PRE-PASS (uplift 0.2)
3
+ *
4
+ * A ruleset scanner that runs BEFORE the LLM reviewers in the parallel
5
+ * review protocol (open-code-review precedent: deterministic pipelines first,
6
+ * LLM second). Reviewers receive pre-filtered, line-anchored findings so
7
+ * their tokens go to judgment, not pattern-matching.
8
+ *
9
+ * Design stance: precision over recall. Every rule here fires on a pattern
10
+ * that is almost always worth a human/reviewer glance; ambiguous heuristics
11
+ * belong to the LLM reviewers, not to this pass. Findings are advisory input
12
+ * to review — the blocking behavior stays in the expert-review enforcer,
13
+ * which is unchanged.
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
16
+ import { execFileSync } from 'child_process';
17
+ import { join } from 'path';
18
+ /** git invocation without a shell — this module must pass its own scanner. */
19
+ function git(dir, args) {
20
+ try {
21
+ return execFileSync('git', args, { cwd: dir, encoding: 'utf-8', timeout: 10_000 }).trim();
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ const CODE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.js', '.mjs', '.cjs', '.jsx', '.py']);
28
+ /**
29
+ * Line-anchored rules. Keep each one boringly precise — a finding that cries
30
+ * wolf trains reviewers to ignore the whole pass.
31
+ */
32
+ export const LINE_RULES = [
33
+ {
34
+ name: 'aws-access-key',
35
+ category: 'secrets',
36
+ severity: 'high',
37
+ pattern: /\bAKIA[0-9A-Z]{16}\b/,
38
+ message: 'hardcoded AWS access key id',
39
+ },
40
+ {
41
+ name: 'github-token',
42
+ category: 'secrets',
43
+ severity: 'high',
44
+ pattern: /\b(?:gh[posur]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{22,})\b/,
45
+ message: 'hardcoded GitHub token',
46
+ },
47
+ {
48
+ name: 'slack-token',
49
+ category: 'secrets',
50
+ severity: 'high',
51
+ pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
52
+ message: 'hardcoded Slack token',
53
+ },
54
+ {
55
+ name: 'llm-api-key',
56
+ category: 'secrets',
57
+ severity: 'high',
58
+ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{20,}\b/,
59
+ message: 'hardcoded LLM provider API key',
60
+ },
61
+ {
62
+ name: 'npm-token',
63
+ category: 'secrets',
64
+ severity: 'high',
65
+ pattern: /\bnpm_[A-Za-z0-9]{20,}\b/,
66
+ message: 'hardcoded npm token',
67
+ },
68
+ {
69
+ name: 'jwt-token',
70
+ category: 'secrets',
71
+ severity: 'high',
72
+ pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}\b/,
73
+ message: 'hardcoded JWT',
74
+ },
75
+ {
76
+ name: 'private-key-block',
77
+ category: 'secrets',
78
+ severity: 'high',
79
+ pattern: /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----/,
80
+ message: 'private key material committed to source',
81
+ },
82
+ {
83
+ name: 'hardcoded-secret',
84
+ category: 'secrets',
85
+ severity: 'high',
86
+ // assignment of a quoted literal (>=8 chars) to a secret-named variable
87
+ pattern: /\b(?:password|passwd|secret|api[_-]?key|auth[_-]?token)\b\s*[:=]\s*['"][^'"\n]{8,}['"]/i,
88
+ message: 'secret-looking value assigned as a string literal',
89
+ },
90
+ {
91
+ name: 'eval-call',
92
+ category: 'injection',
93
+ severity: 'high',
94
+ pattern: /(?<![\w.])eval\s*\(/,
95
+ // Message deliberately avoids the literal trigger text so this scanner's
96
+ // own source survives its own dogfood scan.
97
+ message: 'dynamic string execution (eval-family) — injection surface',
98
+ codeOnly: true,
99
+ },
100
+ {
101
+ name: 'new-function',
102
+ category: 'injection',
103
+ severity: 'high',
104
+ pattern: /new\s+Function\s*\(/,
105
+ message: 'dynamic code construction via the Function constructor',
106
+ codeOnly: true,
107
+ },
108
+ {
109
+ name: 'exec-interpolated',
110
+ category: 'injection',
111
+ severity: 'high',
112
+ pattern: /\bexec(?:Sync)?\s*\(\s*`[^`]*\$\{/,
113
+ message: 'child_process exec with an interpolated template string (shell injection)',
114
+ codeOnly: true,
115
+ },
116
+ {
117
+ name: 'exec-concat',
118
+ category: 'injection',
119
+ severity: 'high',
120
+ pattern: /\bexec(?:Sync)?\s*\(\s*['"][^'"]*['"]\s*\+/,
121
+ message: 'child_process exec with string concatenation (shell injection)',
122
+ codeOnly: true,
123
+ },
124
+ {
125
+ name: 'shell-true',
126
+ category: 'injection',
127
+ severity: 'medium',
128
+ pattern: /\bshell\s*[:=]\s*(?:True|true)\b/,
129
+ message: 'command routed through a shell (shell flag) — injection surface',
130
+ codeOnly: true,
131
+ },
132
+ {
133
+ name: 'sql-template-interpolation',
134
+ category: 'sql',
135
+ severity: 'high',
136
+ pattern: /`\s*(?:SELECT|INSERT\s+INTO|UPDATE\s|DELETE\s+FROM)\b[^`]*\$\{/i,
137
+ message: 'SQL built by template interpolation — use parameterized queries',
138
+ codeOnly: true,
139
+ },
140
+ {
141
+ name: 'sql-string-concat',
142
+ category: 'sql',
143
+ severity: 'medium',
144
+ pattern: /['"]\s*(?:SELECT|INSERT\s+INTO|UPDATE\s|DELETE\s+FROM)\b[^'"\n]*['"]\s*\+/i,
145
+ message: 'SQL built by string concatenation — use parameterized queries',
146
+ codeOnly: true,
147
+ },
148
+ {
149
+ name: 'innerhtml-assign',
150
+ category: 'injection',
151
+ severity: 'medium',
152
+ pattern: /\.innerHTML\s*=(?!=)/,
153
+ message: 'innerHTML assignment — XSS surface unless the value is sanitized',
154
+ codeOnly: true,
155
+ },
156
+ {
157
+ // \w* so this rule's own definition line does not match itself
158
+ name: 'dangerously-set-innerhtml',
159
+ category: 'injection',
160
+ severity: 'medium',
161
+ pattern: /dangerously\w*InnerHTML/,
162
+ message: 'React raw-HTML injection prop — XSS surface unless sanitized',
163
+ codeOnly: true,
164
+ },
165
+ {
166
+ name: 'document-write',
167
+ category: 'injection',
168
+ severity: 'medium',
169
+ pattern: /\bdocument\.write\s*\(/,
170
+ message: 'document.write — XSS and parser-blocking surface',
171
+ codeOnly: true,
172
+ },
173
+ {
174
+ name: 'insert-adjacent-html',
175
+ category: 'injection',
176
+ severity: 'medium',
177
+ pattern: /\.insertAdjacentHTML\s*\(/,
178
+ message: 'insertAdjacentHTML — XSS surface unless the value is sanitized',
179
+ codeOnly: true,
180
+ },
181
+ {
182
+ name: 'cors-wildcard',
183
+ category: 'injection',
184
+ severity: 'medium',
185
+ pattern: /Access-Control-Allow-Origin['"]?\s*[:,]\s*['"]?\*/,
186
+ message: 'CORS wildcard origin — verify this endpoint serves no private data',
187
+ },
188
+ {
189
+ name: 'cors-origin-wildcard',
190
+ category: 'injection',
191
+ severity: 'medium',
192
+ pattern: /\borigin\s*:\s*['"]\*['"]/,
193
+ // Message avoids the literal trigger text (dogfood-clean, like the others)
194
+ message: 'CORS wildcard via middleware options — verify no private data is served',
195
+ codeOnly: true,
196
+ },
197
+ ];
198
+ const MAX_SNIPPET_LEN = 120;
199
+ /**
200
+ * Redact secret material from a finding snippet. The artifact's purpose is to
201
+ * be pasted into LLM reviewer prompts — a secrets scanner must not multiply
202
+ * copies of what it detects. The file:line anchor is enough for a reviewer
203
+ * to inspect the secret in place.
204
+ */
205
+ export function redactSecrets(snippet) {
206
+ return snippet
207
+ .replace(/(['"])[^'"\n]{4,}\1/g, '$1***redacted***$1')
208
+ .replace(/\b(?:AKIA[0-9A-Z]{4}[0-9A-Z]{12}|gh[posur]_[A-Za-z0-9]{8,}|github_pat_[A-Za-z0-9_]{8,}|xox[baprs]-[A-Za-z0-9-]{4,}|sk-(?:ant-)?[A-Za-z0-9_-]{8,}|npm_[A-Za-z0-9]{8,}|eyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{3,})\b/g, '***redacted***');
209
+ }
210
+ function fileExtension(file) {
211
+ const base = file.split('/').pop() ?? '';
212
+ const dot = base.lastIndexOf('.');
213
+ return dot >= 0 ? base.slice(dot) : '';
214
+ }
215
+ /** Scan one file's content. `file` is the repo-relative path used in findings. */
216
+ export function scanFileContent(file, content) {
217
+ const ext = fileExtension(file);
218
+ const isCode = CODE_EXTENSIONS.has(ext);
219
+ const findings = [];
220
+ const lines = content.split('\n');
221
+ for (let i = 0; i < lines.length; i++) {
222
+ const text = lines[i];
223
+ for (const rule of LINE_RULES) {
224
+ if (rule.codeOnly && !isCode)
225
+ continue;
226
+ if (rule.pattern.test(text)) {
227
+ const snippet = text.trim().slice(0, MAX_SNIPPET_LEN);
228
+ findings.push({
229
+ rule: rule.name,
230
+ category: rule.category,
231
+ severity: rule.severity,
232
+ file,
233
+ line: i + 1,
234
+ snippet: rule.category === 'secrets' ? redactSecrets(snippet) : snippet,
235
+ message: rule.message,
236
+ });
237
+ }
238
+ }
239
+ }
240
+ findings.push(...scanEmptyCatches(file, content, isCode));
241
+ findings.push(...scanPythonErrorSwallowing(file, lines, isCode));
242
+ findings.push(...scanConcurrencySmells(file, content, isCode));
243
+ return findings;
244
+ }
245
+ /**
246
+ * Whole-content pass for empty catch blocks — a per-line rule can never see
247
+ * the idiomatic multi-line form (`} catch (e) {` newline `}`), which is also
248
+ * the easiest way to deliberately evade the gate.
249
+ */
250
+ function scanEmptyCatches(file, content, isCode) {
251
+ if (!isCode || file.endsWith('.py'))
252
+ return [];
253
+ const findings = [];
254
+ const pattern = /\bcatch\s*(?:\([^)]*\))?\s*\{\s*\}/g;
255
+ for (let match = pattern.exec(content); match !== null; match = pattern.exec(content)) {
256
+ const line = content.slice(0, match.index).split('\n').length;
257
+ findings.push({
258
+ rule: 'empty-catch',
259
+ category: 'error-handling',
260
+ severity: 'medium',
261
+ file,
262
+ line,
263
+ snippet: match[0].replace(/\s+/g, ' ').slice(0, MAX_SNIPPET_LEN),
264
+ message: 'empty catch swallows the error with no trace',
265
+ });
266
+ }
267
+ return findings;
268
+ }
269
+ /** Python `except: ... pass` pairs — a two-line pattern the line rules can't see. */
270
+ function scanPythonErrorSwallowing(file, lines, isCode) {
271
+ if (!isCode || !file.endsWith('.py'))
272
+ return [];
273
+ const findings = [];
274
+ for (let i = 0; i < lines.length; i++) {
275
+ // single-line form: except ...: pass
276
+ if (/^\s*except\b[^:\n]*:\s*(?:pass|\.\.\.)\s*(?:#.*)?$/.test(lines[i])) {
277
+ findings.push({
278
+ rule: 'except-pass',
279
+ category: 'error-handling',
280
+ severity: 'medium',
281
+ file,
282
+ line: i + 1,
283
+ snippet: lines[i].trim().slice(0, MAX_SNIPPET_LEN),
284
+ message: 'except block whose entire body is pass — error swallowed silently',
285
+ });
286
+ continue;
287
+ }
288
+ if (i >= lines.length - 1)
289
+ break;
290
+ if (!/^\s*except\b[^:]*:\s*(?:#.*)?$/.test(lines[i]))
291
+ continue;
292
+ const next = lines[i + 1].trim();
293
+ if (next === 'pass' || next === '...') {
294
+ findings.push({
295
+ rule: 'except-pass',
296
+ category: 'error-handling',
297
+ severity: 'medium',
298
+ file,
299
+ line: i + 1,
300
+ snippet: lines[i].trim().slice(0, MAX_SNIPPET_LEN),
301
+ message: 'except block whose entire body is pass — error swallowed silently',
302
+ });
303
+ }
304
+ }
305
+ return findings;
306
+ }
307
+ /** File-level: Python threads without any lock in the same file. */
308
+ function scanConcurrencySmells(file, content, isCode) {
309
+ if (!isCode || !file.endsWith('.py'))
310
+ return [];
311
+ if (!/\bthreading\.Thread\s*\(/.test(content))
312
+ return [];
313
+ if (/\b(?:threading\.)?Lock\s*\(/.test(content))
314
+ return [];
315
+ return [
316
+ {
317
+ rule: 'threading-no-lock',
318
+ category: 'concurrency',
319
+ severity: 'medium',
320
+ file,
321
+ line: 1,
322
+ snippet: 'threading.Thread without any Lock in file',
323
+ message: 'threads spawned but no Lock in this file — check shared-state access',
324
+ },
325
+ ];
326
+ }
327
+ /** Resolve the target file list for a pre-pass run. */
328
+ export function collectPrePassTargets(options) {
329
+ const dir = options.projectDir ?? process.cwd();
330
+ if (options.files && options.files.length > 0)
331
+ return options.files;
332
+ // Default: everything the branch touched, plus uncommitted/untracked work —
333
+ // the same surface the reviewers are about to read.
334
+ const found = new Set();
335
+ const collect = (args) => {
336
+ const out = git(dir, args);
337
+ if (out === null)
338
+ return;
339
+ for (const line of out.split('\n')) {
340
+ const f = line.trim();
341
+ if (f && !f.includes('node_modules/'))
342
+ found.add(f);
343
+ }
344
+ };
345
+ let baseResolved = false;
346
+ for (const base of ['origin/master', 'origin/main', 'master', 'main']) {
347
+ if (git(dir, ['rev-parse', '--verify', base]) !== null) {
348
+ collect(['diff', '--name-only', `${base}...HEAD`]);
349
+ baseResolved = true;
350
+ break;
351
+ }
352
+ }
353
+ if (!baseResolved)
354
+ collect(['diff', '--name-only', 'HEAD~1..HEAD']);
355
+ collect(['diff', '--name-only', '--cached']);
356
+ collect(['diff', '--name-only']);
357
+ collect(['ls-files', '--others', '--exclude-standard']);
358
+ return [...found].filter((f) => existsSync(join(dir, f)));
359
+ }
360
+ /** Scan a resolved target list. */
361
+ export function runPrePass(options) {
362
+ const dir = options.projectDir ?? process.cwd();
363
+ const targets = collectPrePassTargets(options);
364
+ const findings = [];
365
+ const filesScanned = [];
366
+ for (const file of targets) {
367
+ let content;
368
+ try {
369
+ content = readFileSync(join(dir, file), 'utf-8');
370
+ }
371
+ catch {
372
+ continue; // binary or unreadable — not this pass's job
373
+ }
374
+ filesScanned.push(file);
375
+ findings.push(...scanFileContent(file, content));
376
+ }
377
+ return { findings, filesScanned };
378
+ }
379
+ /** Percent-encode branch to a filename slug — mirrors slug_for() in the
380
+ * expert_review_required enforcer exactly (the artifact must land where the
381
+ * enforcer looks). */
382
+ export function branchSlug(branch) {
383
+ return branch.replace(/%/g, '%25').replace(/\//g, '%2F');
384
+ }
385
+ /**
386
+ * Write the pre-pass artifact to .uap/reviews/<branch-slug>.pre-pass.json.
387
+ *
388
+ * Deliberately a SIBLING of the review artifact (<slug>.json), not a block
389
+ * inside it: the expert-review enforcer treats the existence of <slug>.json
390
+ * as "a review happened", and every substantive check in it is conditional on
391
+ * keys the pre-pass never writes. A pre-pass-only <slug>.json would satisfy
392
+ * the blocking gate with zero reviewers — so this advisory pass must never
393
+ * create that file. Consolidation reads this sibling and merges its findings
394
+ * into the real artifact along with the verdict.
395
+ */
396
+ export function writePrePassArtifact(projectDir, findings, filesScanned) {
397
+ const branch = git(projectDir, ['rev-parse', '--abbrev-ref', 'HEAD']);
398
+ if (!branch || branch === 'HEAD')
399
+ return null; // detached/non-git: nowhere to write
400
+ const head = git(projectDir, ['rev-parse', 'HEAD']);
401
+ const dir = join(projectDir, '.uap', 'reviews');
402
+ mkdirSync(dir, { recursive: true });
403
+ const path = join(dir, `${branchSlug(branch)}.pre-pass.json`);
404
+ let existing = {};
405
+ if (existsSync(path)) {
406
+ try {
407
+ existing = JSON.parse(readFileSync(path, 'utf-8'));
408
+ }
409
+ catch {
410
+ existing = {}; // corrupt artifact: overwrite, don't crash the review
411
+ }
412
+ }
413
+ const artifact = {
414
+ ran_at: new Date().toISOString(),
415
+ head,
416
+ files_scanned: filesScanned,
417
+ finding_count: findings.length,
418
+ findings,
419
+ };
420
+ const merged = { ...existing, pre_pass: artifact };
421
+ // Atomic write: a crash mid-write must not destroy reviewer-written keys —
422
+ // a torn artifact would be reset to {} on next read, erasing a review.
423
+ const tmpPath = `${path}.tmp`;
424
+ writeFileSync(tmpPath, JSON.stringify(merged, null, 2) + '\n');
425
+ renameSync(tmpPath, path);
426
+ return { path, artifact };
427
+ }
428
+ //# sourceMappingURL=prepass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepass.js","sourceRoot":"","sources":["../../src/review/prepass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,8EAA8E;AAC9E,SAAS,GAAG,CAAC,GAAW,EAAE,IAAc;IACtC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAyBD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAE/F;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAe;IACpC;QACE,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,iEAAiE;QAC1E,OAAO,EAAE,wBAAwB;KAClC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,kCAAkC;QAC3C,OAAO,EAAE,uBAAuB;KACjC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,oCAAoC;QAC7C,OAAO,EAAE,gCAAgC;KAC1C;IACD;QACE,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE,qBAAqB;KAC/B;IACD;QACE,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,kEAAkE;QAC3E,OAAO,EAAE,eAAe;KACzB;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,mEAAmE;QAC5E,OAAO,EAAE,0CAA0C;KACpD;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,MAAM;QAChB,wEAAwE;QACxE,OAAO,EAAE,yFAAyF;QAClG,OAAO,EAAE,mDAAmD;KAC7D;IACD;QACE,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,qBAAqB;QAC9B,yEAAyE;QACzE,4CAA4C;QAC5C,OAAO,EAAE,4DAA4D;QACrE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,cAAc;QACpB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,qBAAqB;QAC9B,OAAO,EAAE,wDAAwD;QACjE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,mCAAmC;QAC5C,OAAO,EAAE,2EAA2E;QACpF,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,aAAa;QACnB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,4CAA4C;QACrD,OAAO,EAAE,gEAAgE;QACzE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,YAAY;QAClB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,kCAAkC;QAC3C,OAAO,EAAE,iEAAiE;QAC1E,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,iEAAiE;QAC1E,OAAO,EAAE,iEAAiE;QAC1E,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,4EAA4E;QACrF,OAAO,EAAE,+DAA+D;QACxE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,sBAAsB;QAC/B,OAAO,EAAE,kEAAkE;QAC3E,QAAQ,EAAE,IAAI;KACf;IACD;QACE,+DAA+D;QAC/D,IAAI,EAAE,2BAA2B;QACjC,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,yBAAyB;QAClC,OAAO,EAAE,8DAA8D;QACvE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,wBAAwB;QACjC,OAAO,EAAE,kDAAkD;QAC3D,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,2BAA2B;QACpC,OAAO,EAAE,gEAAgE;QACzE,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,eAAe;QACrB,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,mDAAmD;QAC5D,OAAO,EAAE,oEAAoE;KAC9E;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,QAAQ,EAAE,WAAW;QACrB,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,2BAA2B;QACpC,2EAA2E;QAC3E,OAAO,EAAE,yEAAyE;QAClF,QAAQ,EAAE,IAAI;KACf;CACF,CAAC;AAEF,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,OAAO;SACX,OAAO,CAAC,sBAAsB,EAAE,oBAAoB,CAAC;SACrD,OAAO,CACN,qOAAqO,EACrO,gBAAgB,CACjB,CAAC;AACN,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM;gBAAE,SAAS;YACvC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;gBACtD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI;oBACJ,IAAI,EAAE,CAAC,GAAG,CAAC;oBACX,OAAO,EAAE,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;oBACvE,OAAO,EAAE,IAAI,CAAC,OAAO;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,IAAI,CAAC,GAAG,yBAAyB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjE,QAAQ,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,IAAY,EAAE,OAAe,EAAE,MAAe;IACtE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,qCAAqC,CAAC;IACtD,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAC9D,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE,gBAAgB;YAC1B,QAAQ,EAAE,QAAQ;YAClB,IAAI;YACJ,IAAI;YACJ,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;YAChE,OAAO,EAAE,8CAA8C;SACxD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,qFAAqF;AACrF,SAAS,yBAAyB,CAChC,IAAY,EACZ,KAAe,EACf,MAAe;IAEf,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,qCAAqC;QACrC,IAAI,oDAAoD,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACxE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,gBAAgB;gBAC1B,QAAQ,EAAE,QAAQ;gBAClB,IAAI;gBACJ,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;gBAClD,OAAO,EAAE,mEAAmE;aAC7E,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM;QACjC,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,aAAa;gBACnB,QAAQ,EAAE,gBAAgB;gBAC1B,QAAQ,EAAE,QAAQ;gBAClB,IAAI;gBACJ,IAAI,EAAE,CAAC,GAAG,CAAC;gBACX,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;gBAClD,OAAO,EAAE,mEAAmE;aAC7E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oEAAoE;AACpE,SAAS,qBAAqB,CAC5B,IAAY,EACZ,OAAe,EACf,MAAe;IAEf,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACzD,IAAI,6BAA6B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO;QACL;YACE,IAAI,EAAE,mBAAmB;YACzB,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,QAAQ;YAClB,IAAI;YACJ,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,2CAA2C;YACpD,OAAO,EAAE,sEAAsE;SAChF;KACF,CAAC;AACJ,CAAC;AAQD,uDAAuD;AACvD,MAAM,UAAU,qBAAqB,CAAC,OAAuB;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC;IACpE,4EAA4E;IAC5E,oDAAoD;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,OAAO,GAAG,CAAC,IAAc,EAAQ,EAAE;QACvC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3B,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO;QACzB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC,CAAC;IACF,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,CAAC,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACtE,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACvD,OAAO,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnD,YAAY,GAAG,IAAI,CAAC;YACpB,MAAM;QACR,CAAC;IACH,CAAC;IACD,IAAI,CAAC,YAAY;QAAE,OAAO,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,OAAuB;IAIhD,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,6CAA6C;QACzD,CAAC;QACD,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC;AAED;;sBAEsB;AACtB,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC;AAUD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAkB,EAClB,QAA0B,EAC1B,YAAoB;IAEpB,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,CAAC,qCAAqC;IACpF,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAEpD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAE9D,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAA4B,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,GAAG,EAAE,CAAC,CAAC,sDAAsD;QACvE,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAoB;QAChC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAChC,IAAI;QACJ,aAAa,EAAE,YAAY;QAC3B,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,QAAQ;KACT,CAAC;IACF,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACnD,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,OAAO,GAAG,GAAG,IAAI,MAAM,CAAC;IAC9B,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/D,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,60 @@
1
+ /** UI surface definition — mirrored (with matching case handling) by
2
+ * UI_EXT/UI_DIR_PREFIXES in src/policies/enforcers/expert_review_required.py
3
+ * and visual_verification.py. Keep all three in sync; the parity test in
4
+ * test/visual-captures.test.ts pins the intended surface. */
5
+ export declare const UI_EXT: Set<string>;
6
+ export declare const UI_DIR_PREFIXES: string[];
7
+ export interface CaptureEntry {
8
+ /** Capture tool: agent-browser | tuistory | pty-capture | playwright | ... */
9
+ tool: string;
10
+ /** Pre-change capture path (stored project-relative). */
11
+ before: string;
12
+ /** Post-change capture path (stored project-relative). */
13
+ after: string;
14
+ note?: string;
15
+ /** ISO timestamp when the pair was registered. */
16
+ at: string;
17
+ }
18
+ export interface CapturesArtifact {
19
+ branch: string;
20
+ head: string | null;
21
+ ui_files: string[];
22
+ captures: CaptureEntry[];
23
+ at: string;
24
+ }
25
+ export interface CapturesValidation {
26
+ /** True when the diff vs base touches UI files and captures are mandatory. */
27
+ required: boolean;
28
+ ok: boolean;
29
+ reasons: string[];
30
+ uiFiles: string[];
31
+ artifactPath: string;
32
+ captures: CaptureEntry[];
33
+ }
34
+ export declare function isUiFile(file: string): boolean;
35
+ /** Files changed vs the upstream base (origin/master, origin/main, master,
36
+ * main — first that resolves), or null when no base exists (fail-open). */
37
+ export declare function changedFilesVsBase(projectDir: string): string[] | null;
38
+ export declare function capturesArtifactPath(projectDir: string, branch: string): string;
39
+ /**
40
+ * Register one before/after capture pair for the current branch.
41
+ * Returns the artifact path, or an error string when the pair is unusable.
42
+ */
43
+ export declare function recordCapture(projectDir: string, entry: {
44
+ tool: string;
45
+ before: string;
46
+ after: string;
47
+ note?: string;
48
+ }): {
49
+ path: string;
50
+ artifact: CapturesArtifact;
51
+ } | {
52
+ error: string;
53
+ };
54
+ /**
55
+ * Validate that a UI diff is covered by complete, fresh capture pairs.
56
+ * Fail-open only when no base is resolvable (non-git/detached), matching the
57
+ * expert-review enforcer's posture.
58
+ */
59
+ export declare function validateCaptures(projectDir: string): CapturesValidation;
60
+ //# sourceMappingURL=visual-captures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visual-captures.d.ts","sourceRoot":"","sources":["../../src/review/visual-captures.ts"],"names":[],"mappings":"AA+BA;;;6DAG6D;AAC7D,eAAO,MAAM,MAAM,aAGjB,CAAC;AACH,eAAO,MAAM,eAAe,UAAwC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;IAClB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAeD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAK9C;AAED;2EAC2E;AAC3E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAMtE;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/E;AA6BD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA0ClE;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,CAqFvE"}