tryassay 0.15.1 → 0.17.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.
@@ -0,0 +1,307 @@
1
+ /**
2
+ * IntegrationVerifier — deterministic wiring checks for generated applications.
3
+ *
4
+ * Catches four classes of integration failure that compile clean but don't work:
5
+ * 1. Missing Router provider (pages use react-router hooks but no Router wraps them)
6
+ * 2. Missing IPC handlers (preload invokes channels with no main-process handler)
7
+ * 3. Stub functions (async functions that return literal objects without doing real work)
8
+ * 4. Unreachable pages (page files exist but are never imported from the entry point)
9
+ *
10
+ * Zero LLM calls. Deterministic. Uses only node:fs/promises, node:path, and regex.
11
+ */
12
+ import { readFile } from 'node:fs/promises';
13
+ import { relative, basename, extname } from 'node:path';
14
+ import { findFiles, traceImports, findMatchingBrace } from './fs-helpers.js';
15
+ import { CheckExecutor } from './check-definitions.js';
16
+ import { CheckStore } from './check-store.js';
17
+ export class IntegrationVerifier {
18
+ projectPath;
19
+ framework;
20
+ constructor(projectPath, framework) {
21
+ this.projectPath = projectPath;
22
+ this.framework = framework;
23
+ }
24
+ async verify() {
25
+ const start = Date.now();
26
+ const checks = [];
27
+ // Run all checks
28
+ checks.push(...await this.checkRoutingProvider());
29
+ if (this.framework === 'electron' || await this.hasPreloadFiles()) {
30
+ checks.push(...await this.checkIpcHandlerCoverage());
31
+ checks.push(...await this.checkStubFunctions());
32
+ }
33
+ checks.push(...await this.checkUnreachablePages());
34
+ // Dynamic checks from CheckStore
35
+ try {
36
+ const store = new CheckStore();
37
+ const activeChecks = await store.getActive(this.framework);
38
+ if (activeChecks.length > 0) {
39
+ const executor = new CheckExecutor();
40
+ for (const def of activeChecks) {
41
+ checks.push(...await executor.execute(def, this.projectPath));
42
+ }
43
+ }
44
+ }
45
+ catch {
46
+ // CheckStore unavailable — continue with hardcoded checks only
47
+ }
48
+ const passedCount = checks.filter(c => c.verdict === 'PASS').length;
49
+ const failedCount = checks.filter(c => c.verdict === 'FAIL').length;
50
+ return {
51
+ checks,
52
+ passedCount,
53
+ failedCount,
54
+ verdict: failedCount > 0 ? 'FAIL' : 'PASS',
55
+ framework: this.framework,
56
+ totalDurationMs: Date.now() - start,
57
+ };
58
+ }
59
+ // ── Check 1: Routing Provider ──────────────────────────────
60
+ /**
61
+ * Scan all .tsx files for react-router hook usage.
62
+ * If found, verify a Router provider exists in the entry point chain.
63
+ */
64
+ async checkRoutingProvider() {
65
+ const tsxFiles = await findFiles(this.projectPath, /\.(tsx|jsx)$/);
66
+ const routerHookPattern = /\b(useNavigate|useParams|useLocation|useSearchParams|useMatch|useRoutes)\b/;
67
+ const routerProviderPattern = /\b(BrowserRouter|HashRouter|MemoryRouter|RouterProvider|createBrowserRouter|createHashRouter|createMemoryRouter)\b/;
68
+ // Find files that use router hooks
69
+ const filesUsingHooks = [];
70
+ for (const file of tsxFiles) {
71
+ const content = await readFile(file, 'utf-8');
72
+ if (routerHookPattern.test(content)) {
73
+ filesUsingHooks.push(file);
74
+ }
75
+ }
76
+ if (filesUsingHooks.length === 0) {
77
+ return []; // No router hooks used — nothing to check
78
+ }
79
+ // Check if ANY file in the project contains a Router provider
80
+ let providerFound = false;
81
+ let providerFile = '';
82
+ for (const file of tsxFiles) {
83
+ const content = await readFile(file, 'utf-8');
84
+ if (routerProviderPattern.test(content)) {
85
+ providerFound = true;
86
+ providerFile = relative(this.projectPath, file);
87
+ break;
88
+ }
89
+ }
90
+ if (providerFound) {
91
+ return [{
92
+ type: 'routing_provider',
93
+ verdict: 'PASS',
94
+ evidence: `Router provider found in ${providerFile}. ${filesUsingHooks.length} file(s) use router hooks.`,
95
+ file: providerFile,
96
+ severity: 'critical',
97
+ }];
98
+ }
99
+ const hookFiles = filesUsingHooks.map(f => relative(this.projectPath, f)).join(', ');
100
+ return [{
101
+ type: 'routing_provider',
102
+ verdict: 'FAIL',
103
+ evidence: `${filesUsingHooks.length} file(s) use react-router hooks (${hookFiles}) but no Router provider (BrowserRouter, HashRouter, etc.) found in any .tsx file.`,
104
+ file: hookFiles,
105
+ severity: 'critical',
106
+ }];
107
+ }
108
+ // ── Check 2: IPC Handler Coverage ──────────────────────────
109
+ /**
110
+ * Electron only. Scan preload files for ipcRenderer.invoke calls.
111
+ * Scan main/ipc files for ipcMain.handle calls.
112
+ * Report any channel invoked but not handled.
113
+ */
114
+ async checkIpcHandlerCoverage() {
115
+ const allFiles = await findFiles(this.projectPath, /\.(ts|tsx|js|jsx)$/);
116
+ // Find channels invoked in preload (renderer-side)
117
+ const invokedChannels = new Map(); // channel -> file
118
+ const invokePattern = /ipcRenderer\.invoke\(\s*['"`]([^'"`]+)['"`]/g;
119
+ for (const file of allFiles) {
120
+ const relPath = relative(this.projectPath, file);
121
+ if (!relPath.includes('preload'))
122
+ continue;
123
+ const content = await readFile(file, 'utf-8');
124
+ let match;
125
+ while ((match = invokePattern.exec(content)) !== null) {
126
+ invokedChannels.set(match[1], relPath);
127
+ }
128
+ }
129
+ if (invokedChannels.size === 0) {
130
+ return []; // No IPC invoke calls found
131
+ }
132
+ // Find channels handled in main process
133
+ const handledChannels = new Set();
134
+ const handlePattern = /ipcMain\.handle\(\s*['"`]([^'"`]+)['"`]/g;
135
+ // Also check for IPC_CHANNELS.CONSTANT patterns that resolve to string literals
136
+ const channelConstPattern = /ipcMain\.handle\(\s*(?:IPC_CHANNELS\.\w+|[\w.]+)\s*,/g;
137
+ for (const file of allFiles) {
138
+ const relPath = relative(this.projectPath, file);
139
+ // Check main process files (main/, ipc/, handlers/)
140
+ if (!relPath.includes('main') && !relPath.includes('ipc') && !relPath.includes('handler'))
141
+ continue;
142
+ const content = await readFile(file, 'utf-8');
143
+ // Direct string channel handles
144
+ let match;
145
+ while ((match = handlePattern.exec(content)) !== null) {
146
+ handledChannels.add(match[1]);
147
+ }
148
+ // Also resolve IPC_CHANNELS constants by scanning for their definitions
149
+ const constDefPattern = /['"`]([^'"`]+)['"`]\s*(?:,|as\s+const)/g;
150
+ if (relPath.includes('handler') || relPath.includes('ipc')) {
151
+ while ((match = constDefPattern.exec(content)) !== null) {
152
+ // Only add if it looks like a channel name (contains colon)
153
+ if (match[1].includes(':')) {
154
+ handledChannels.add(match[1]);
155
+ }
156
+ }
157
+ }
158
+ }
159
+ // Also scan shared types for IPC_CHANNELS constant definitions
160
+ for (const file of allFiles) {
161
+ const relPath = relative(this.projectPath, file);
162
+ if (!relPath.includes('shared') && !relPath.includes('types'))
163
+ continue;
164
+ const content = await readFile(file, 'utf-8');
165
+ const channelValuePattern = /:\s*['"`]([^'"`]+)['"`]/g;
166
+ if (content.includes('IPC_CHANNELS')) {
167
+ let match;
168
+ while ((match = channelValuePattern.exec(content)) !== null) {
169
+ if (match[1].includes(':')) {
170
+ handledChannels.add(match[1]);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ const checks = [];
176
+ for (const [channel, invokeFile] of invokedChannels) {
177
+ if (handledChannels.has(channel)) {
178
+ checks.push({
179
+ type: 'ipc_handler_coverage',
180
+ verdict: 'PASS',
181
+ evidence: `Channel '${channel}' invoked in ${invokeFile} has a matching handler.`,
182
+ file: invokeFile,
183
+ severity: 'critical',
184
+ });
185
+ }
186
+ else {
187
+ checks.push({
188
+ type: 'ipc_handler_coverage',
189
+ verdict: 'FAIL',
190
+ evidence: `Channel '${channel}' invoked in ${invokeFile} has NO handler in any main-process file.`,
191
+ file: invokeFile,
192
+ severity: 'critical',
193
+ });
194
+ }
195
+ }
196
+ return checks;
197
+ }
198
+ // ── Check 3: Stub Functions ────────────────────────────────
199
+ /**
200
+ * Find async functions in preload whose body is ONLY a return of a literal object
201
+ * without ipcRenderer.invoke, await, .query(), or fetch().
202
+ */
203
+ async checkStubFunctions() {
204
+ const allFiles = await findFiles(this.projectPath, /\.(ts|tsx|js|jsx)$/);
205
+ const checks = [];
206
+ for (const file of allFiles) {
207
+ const relPath = relative(this.projectPath, file);
208
+ if (!relPath.includes('preload'))
209
+ continue;
210
+ const content = await readFile(file, 'utf-8');
211
+ const lines = content.split('\n');
212
+ // Find async arrow functions that return a literal object without real work
213
+ // Pattern: async (params) => { return { ... }; } or async (params): Promise<...> => { return { ... }; }
214
+ // But NOT functions that contain ipcRenderer.invoke, await (something), fetch, .query()
215
+ const stubPattern = /async\s+\(([^)]*)\)\s*(?::\s*Promise<[^>]+>)?\s*=>\s*\{/g;
216
+ let match;
217
+ while ((match = stubPattern.exec(content)) !== null) {
218
+ const startIdx = match.index + match[0].length;
219
+ const bodyEnd = findMatchingBrace(content, startIdx - 1);
220
+ if (bodyEnd === -1)
221
+ continue;
222
+ const body = content.slice(startIdx, bodyEnd).trim();
223
+ const hasRealWork = /ipcRenderer\.invoke|await\s+[^{]|\.query\(|fetch\(|\.send\(|\.emit\(/.test(body);
224
+ const isLiteralReturn = /^\s*return\s+\{[^}]*\}\s*;?\s*$/.test(body);
225
+ if (isLiteralReturn && !hasRealWork) {
226
+ // Find the function name by looking at what precedes the async keyword
227
+ const preceding = content.slice(Math.max(0, match.index - 80), match.index);
228
+ const nameMatch = preceding.match(/(\w+)\s*:\s*$/);
229
+ const funcName = nameMatch ? nameMatch[1] : 'anonymous';
230
+ // Find line number
231
+ const lineNum = content.slice(0, match.index).split('\n').length;
232
+ checks.push({
233
+ type: 'stub_function',
234
+ verdict: 'FAIL',
235
+ evidence: `Function '${funcName}' at line ${lineNum} is a stub: returns a literal object without ipcRenderer.invoke or any real operation. Body: ${body.trim()}`,
236
+ file: relPath,
237
+ severity: 'high',
238
+ });
239
+ }
240
+ }
241
+ }
242
+ return checks;
243
+ }
244
+ // ── Check 4: Unreachable Pages ─────────────────────────────
245
+ /**
246
+ * Collect page files from pages/ directories.
247
+ * Build import graph from entry point.
248
+ * Report any page file not transitively imported.
249
+ */
250
+ async checkUnreachablePages() {
251
+ // Find page files
252
+ const allFiles = await findFiles(this.projectPath, /\.(tsx|jsx|ts|js)$/);
253
+ const pageFiles = allFiles.filter(f => {
254
+ const rel = relative(this.projectPath, f);
255
+ return rel.includes('pages/') || rel.includes('pages\\');
256
+ });
257
+ if (pageFiles.length === 0) {
258
+ return []; // No pages directory
259
+ }
260
+ // Find entry point (file with createRoot or ReactDOM.render)
261
+ let entryFile = null;
262
+ for (const file of allFiles) {
263
+ const content = await readFile(file, 'utf-8');
264
+ if (/createRoot|ReactDOM\.render/.test(content)) {
265
+ entryFile = file;
266
+ break;
267
+ }
268
+ }
269
+ if (!entryFile) {
270
+ return []; // No entry point found — can't trace imports
271
+ }
272
+ // Build transitive import graph from entry point
273
+ const reachable = new Set();
274
+ await traceImports(entryFile, reachable, allFiles);
275
+ const checks = [];
276
+ for (const pageFile of pageFiles) {
277
+ const relPath = relative(this.projectPath, pageFile);
278
+ const pageName = basename(pageFile, extname(pageFile));
279
+ if (reachable.has(pageFile)) {
280
+ checks.push({
281
+ type: 'unreachable_page',
282
+ verdict: 'PASS',
283
+ evidence: `Page '${pageName}' (${relPath}) is reachable from entry point.`,
284
+ file: relPath,
285
+ severity: 'high',
286
+ });
287
+ }
288
+ else {
289
+ checks.push({
290
+ type: 'unreachable_page',
291
+ verdict: 'FAIL',
292
+ evidence: `Page '${pageName}' (${relPath}) is NOT imported from the entry point chain. It exists as a file but is unreachable at runtime.`,
293
+ file: relPath,
294
+ severity: 'high',
295
+ });
296
+ }
297
+ }
298
+ return checks;
299
+ }
300
+ // ── Helpers ────────────────────────────────────────────────
301
+ /** Check if the project has preload files (indicating Electron). */
302
+ async hasPreloadFiles() {
303
+ const files = await findFiles(this.projectPath, /\.(ts|tsx|js|jsx)$/);
304
+ return files.some(f => relative(this.projectPath, f).includes('preload'));
305
+ }
306
+ }
307
+ //# sourceMappingURL=integration-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-verifier.js","sourceRoot":"","sources":["../../src/runtime/integration-verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAQ,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAiB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,OAAO,mBAAmB;IACb,WAAW,CAAS;IACpB,SAAS,CAAS;IAEnC,YAAY,WAAmB,EAAE,SAAiB;QAChD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,MAAM,GAAuB,EAAE,CAAC;QAEtC,iBAAiB;QACjB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAElD,IAAI,IAAI,CAAC,SAAS,KAAK,UAAU,IAAI,MAAM,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAClE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;QAEnD,iCAAiC;QACjC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,UAAU,EAAE,CAAC;YAC/B,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3D,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;gBACrC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;oBAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QACpE,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAEpE,OAAO;YACL,MAAM;YACN,WAAW;YACX,WAAW;YACX,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;YAC1C,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,eAAe,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;SACpC,CAAC;IACJ,CAAC;IAED,8DAA8D;IAE9D;;;OAGG;IACH,KAAK,CAAC,oBAAoB;QACxB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACnE,MAAM,iBAAiB,GAAG,4EAA4E,CAAC;QACvG,MAAM,qBAAqB,GAAG,oHAAoH,CAAC;QAEnJ,mCAAmC;QACnC,MAAM,eAAe,GAAa,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC,CAAC,0CAA0C;QACvD,CAAC;QAED,8DAA8D;QAC9D,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACxC,aAAa,GAAG,IAAI,CAAC;gBACrB,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBAChD,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC;oBACN,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,MAAM;oBACf,QAAQ,EAAE,4BAA4B,YAAY,KAAK,eAAe,CAAC,MAAM,4BAA4B;oBACzG,IAAI,EAAE,YAAY;oBAClB,QAAQ,EAAE,UAAU;iBACrB,CAAC,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC;gBACN,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,MAAM;gBACf,QAAQ,EAAE,GAAG,eAAe,CAAC,MAAM,oCAAoC,SAAS,oFAAoF;gBACpK,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAC;IACL,CAAC;IAED,8DAA8D;IAE9D;;;;OAIG;IACH,KAAK,CAAC,uBAAuB;QAC3B,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;QAEzE,mDAAmD;QACnD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,kBAAkB;QACrE,MAAM,aAAa,GAAG,8CAA8C,CAAC;QAErE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,SAAS;YAC3C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,KAA6B,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACtD,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;QAED,IAAI,eAAe,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC,CAAC,4BAA4B;QACzC,CAAC;QAED,wCAAwC;QACxC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;QAC1C,MAAM,aAAa,GAAG,0CAA0C,CAAC;QACjE,gFAAgF;QAChF,MAAM,mBAAmB,GAAG,uDAAuD,CAAC;QAEpF,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACjD,oDAAoD;YACpD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,SAAS;YACpG,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAE9C,gCAAgC;YAChC,IAAI,KAA6B,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACtD,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAChC,CAAC;YAED,wEAAwE;YACxE,MAAM,eAAe,GAAG,yCAAyC,CAAC;YAClE,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,OAAO,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;oBACxD,4DAA4D;oBAC5D,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC3B,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,+DAA+D;QAC/D,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YACxE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;YACvD,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBACrC,IAAI,KAA6B,CAAC;gBAClC,OAAO,CAAC,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;oBAC5D,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;wBAC3B,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAuB,EAAE,CAAC;QAEtC,KAAK,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,eAAe,EAAE,CAAC;YACpD,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,sBAAsB;oBAC5B,OAAO,EAAE,MAAM;oBACf,QAAQ,EAAE,YAAY,OAAO,gBAAgB,UAAU,0BAA0B;oBACjF,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,UAAU;iBACrB,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,sBAAsB;oBAC5B,OAAO,EAAE,MAAM;oBACf,QAAQ,EAAE,YAAY,OAAO,gBAAgB,UAAU,2CAA2C;oBAClG,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE,UAAU;iBACrB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8DAA8D;IAE9D;;;OAGG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;QACzE,MAAM,MAAM,GAAuB,EAAE,CAAC;QAEtC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,SAAS;YAE3C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAElC,4EAA4E;YAC5E,wGAAwG;YACxG,wFAAwF;YACxF,MAAM,WAAW,GAAG,0DAA0D,CAAC;YAE/E,IAAI,KAA6B,CAAC;YAClC,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC/C,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;gBACzD,IAAI,OAAO,KAAK,CAAC,CAAC;oBAAE,SAAS;gBAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;gBACrD,MAAM,WAAW,GAAG,sEAAsE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtG,MAAM,eAAe,GAAG,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAErE,IAAI,eAAe,IAAI,CAAC,WAAW,EAAE,CAAC;oBACpC,uEAAuE;oBACvE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBAC5E,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;oBACnD,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;oBAExD,mBAAmB;oBACnB,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;oBAEjE,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,eAAe;wBACrB,OAAO,EAAE,MAAM;wBACf,QAAQ,EAAE,aAAa,QAAQ,aAAa,OAAO,gGAAgG,IAAI,CAAC,IAAI,EAAE,EAAE;wBAChK,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,MAAM;qBACjB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8DAA8D;IAE9D;;;;OAIG;IACH,KAAK,CAAC,qBAAqB;QACzB,kBAAkB;QAClB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;YAC1C,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,CAAC,CAAC,qBAAqB;QAClC,CAAC;QAED,6DAA6D;QAC7D,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,6BAA6B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAChD,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,EAAE,CAAC,CAAC,6CAA6C;QAC1D,CAAC;QAED,iDAAiD;QACjD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,MAAM,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YAEvD,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,MAAM;oBACf,QAAQ,EAAE,SAAS,QAAQ,MAAM,OAAO,kCAAkC;oBAC1E,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,kBAAkB;oBACxB,OAAO,EAAE,MAAM;oBACf,QAAQ,EAAE,SAAS,QAAQ,MAAM,OAAO,kGAAkG;oBAC1I,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,8DAA8D;IAE9D,oEAAoE;IAC5D,KAAK,CAAC,eAAe;QAC3B,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5E,CAAC;CACF"}
@@ -1378,6 +1378,8 @@ export type AppCreatePhase = {
1378
1378
  readonly attempt: number;
1379
1379
  readonly maxAttempts: number;
1380
1380
  readonly errorCount: number;
1381
+ } | {
1382
+ readonly phase: 'integration_verifying';
1381
1383
  } | {
1382
1384
  readonly phase: 'cross_verifying';
1383
1385
  } | {
@@ -1402,6 +1404,7 @@ export interface AppCreateResult {
1402
1404
  readonly plan: ArchitecturePlan | null;
1403
1405
  readonly featureResults: readonly FeatureBuildResult[];
1404
1406
  readonly buildVerification: BuildVerificationResult | null;
1407
+ readonly integrationVerification: IntegrationVerificationResult | null;
1405
1408
  readonly crossVerification: CrossFeatureVerification | null;
1406
1409
  readonly totalDurationMs: number;
1407
1410
  readonly auditTrail: readonly AuditEntry[];
@@ -1431,6 +1434,22 @@ export interface CrossFeatureCheck {
1431
1434
  readonly verdict: 'PASS' | 'FAIL';
1432
1435
  readonly evidence: string;
1433
1436
  }
1437
+ export type IntegrationCheckType = 'routing_provider' | 'ipc_handler_coverage' | 'stub_function' | 'unreachable_page';
1438
+ export interface IntegrationCheck {
1439
+ readonly type: IntegrationCheckType;
1440
+ readonly verdict: 'PASS' | 'FAIL';
1441
+ readonly evidence: string;
1442
+ readonly file: string;
1443
+ readonly severity: 'critical' | 'high' | 'medium';
1444
+ }
1445
+ export interface IntegrationVerificationResult {
1446
+ readonly checks: readonly IntegrationCheck[];
1447
+ readonly passedCount: number;
1448
+ readonly failedCount: number;
1449
+ readonly verdict: 'PASS' | 'FAIL';
1450
+ readonly framework: string;
1451
+ readonly totalDurationMs: number;
1452
+ }
1434
1453
  export type BuildStepStatus = 'pass' | 'fail' | 'skip' | 'timeout';
1435
1454
  export interface BuildStepResult {
1436
1455
  readonly step: 'install' | 'build' | 'start' | 'health_check';
@@ -1477,3 +1496,47 @@ export interface AppCreateOptions {
1477
1496
  readonly maxBuildRepairAttempts?: number;
1478
1497
  readonly skipBuildVerification?: boolean;
1479
1498
  }
1499
+ export type CheckStrategyType = 'pattern_presence' | 'pattern_absence' | 'cross_reference' | 'import_reachability';
1500
+ export interface PatternPresenceStrategy {
1501
+ readonly type: 'pattern_presence';
1502
+ readonly fileGlob: string;
1503
+ readonly pattern: string;
1504
+ readonly requireIn: 'any' | 'all';
1505
+ readonly contextGlob?: string;
1506
+ readonly contextPattern?: string;
1507
+ }
1508
+ export interface PatternAbsenceStrategy {
1509
+ readonly type: 'pattern_absence';
1510
+ readonly fileGlob: string;
1511
+ readonly pattern: string;
1512
+ readonly allowIn?: readonly string[];
1513
+ }
1514
+ export interface CrossReferenceStrategy {
1515
+ readonly type: 'cross_reference';
1516
+ readonly sourceGlob: string;
1517
+ readonly sourcePattern: string;
1518
+ readonly targetGlob: string;
1519
+ readonly targetPattern: string;
1520
+ }
1521
+ export interface ImportReachabilityStrategy {
1522
+ readonly type: 'import_reachability';
1523
+ readonly pageGlob: string;
1524
+ readonly entryPattern: string;
1525
+ }
1526
+ export type CheckStrategy = PatternPresenceStrategy | PatternAbsenceStrategy | CrossReferenceStrategy | ImportReachabilityStrategy;
1527
+ export interface IntegrationCheckDefinition {
1528
+ readonly id: string;
1529
+ readonly name: string;
1530
+ readonly type: string;
1531
+ readonly strategy: CheckStrategy;
1532
+ readonly severity: 'critical' | 'high' | 'medium';
1533
+ readonly frameworks: readonly string[];
1534
+ readonly status: 'proposed' | 'shadow' | 'active' | 'deprecated';
1535
+ readonly createdAt: string;
1536
+ readonly evidenceTemplate: string;
1537
+ }
1538
+ export interface CheckLoopResult {
1539
+ readonly proposedChecks: readonly IntegrationCheckDefinition[];
1540
+ readonly duplicatesSkipped: number;
1541
+ readonly classificationErrors: number;
1542
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tryassay",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "description": "AI code verification CLI — find bugs that tests miss, linters ignore, and code review overlooks",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",