architect-coverage2.test.js
1 /** 2 * Comprehensive Coverage Tests for Architect Agent (architect-coverage2) 3 * 4 * Targets uncovered code paths to close the coverage gap. 5 * Covers: detectSemanticDocChanges, getJsFiles, checkComplexity depth issues, 6 * auditDocumentation focus areas, reviewDesign over-engineering patterns, 7 * updateDocumentation commit path, createDesignProposal PO approval paths, 8 * reviewImplementationPlan approved path, checkBranchHealth branch scenarios, 9 * profilePerformance critical severity, processTask string context parsing, and more. 10 * 11 * IMPORTANT: No writes to ./logs/ or ./db/sites.db - all paths use /tmp or in-memory. 12 */ 13 14 import { test, describe, mock } from 'node:test'; 15 import assert from 'node:assert/strict'; 16 import Database from 'better-sqlite3'; 17 import { rmSync, writeFileSync, mkdirSync } from 'fs'; 18 import { join } from 'path'; 19 import { tmpdir } from 'os'; 20 21 process.env.AGENT_IMMEDIATE_INVOCATION = 'false'; 22 process.env.AGENT_REALTIME_NOTIFICATIONS = 'false'; 23 24 // ============================================================ 25 // Mock BEFORE importing architect module 26 // (mock.module calls must come before any imports of architect.js) 27 // ============================================================ 28 29 // Mock agent-claude-api.js to avoid real LLM calls 30 mock.module('../../src/agents/utils/agent-claude-api.js', { 31 namedExports: { 32 generateCode: async (_agentName, _taskId, _target, prompt, _existing) => { 33 // Return responses tailored to the type of request 34 if (prompt && prompt.includes('No documentation updates required')) { 35 return 'No documentation updates required.'; 36 } 37 if (prompt && prompt.includes('semantic changes')) { 38 // Simulate finding some semantic doc changes 39 return `- [README.md]: New API added - Add documentation for new endpoint 40 - [CLAUDE.md]: Pipeline modified - Update pipeline stage documentation`; 41 } 42 // Default: approval response with no high-severity issues 43 return `**Summary**: Test implementation summary 44 45 **Issues**: 46 - [medium] Consider splitting large files 47 48 **Recommendations**: 49 - Add more tests 50 51 **Approval**: yes 52 53 Updated documentation content. 54 This is the complete updated file.`; 55 }, 56 simpleLLMCall: async _prompt => { 57 return 'No documentation updates required.'; 58 }, 59 resetDb: () => {}, 60 }, 61 }); 62 63 // Mock file-operations.js to avoid path whitelist restrictions 64 let mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 65 const mockWriteResult = { success: true, backupPath: '/tmp/backup-doc.md' }; 66 67 mock.module('../../src/agents/utils/file-operations.js', { 68 namedExports: { 69 readFile: async filePath => ({ content: mockReadResult.content, path: filePath }), 70 writeFile: async (_filePath, _content, _options) => mockWriteResult, 71 editFile: async (_filePath, _edits) => ({ success: true }), 72 fileExists: async _filePath => true, 73 listFiles: async _dir => [], 74 resetDb: () => {}, 75 }, 76 }); 77 78 // Dynamic imports AFTER mocks are registered 79 const { ArchitectAgent } = await import('../../src/agents/architect.js'); 80 const { resetDb: resetBaseDb } = await import('../../src/agents/base-agent.js'); 81 const { resetDbConnection: resetTaskManagerDb } = 82 await import('../../src/agents/utils/task-manager.js'); 83 const { resetDb: resetMessageManagerDb } = 84 await import('../../src/agents/utils/message-manager.js'); 85 86 // ============================================================ 87 // Full DB Schema 88 // ============================================================ 89 90 const FULL_SCHEMA = ` 91 CREATE TABLE IF NOT EXISTS agent_tasks ( 92 id INTEGER PRIMARY KEY AUTOINCREMENT, 93 task_type TEXT NOT NULL, 94 assigned_to TEXT NOT NULL, 95 created_by TEXT, 96 status TEXT DEFAULT 'pending', 97 priority INTEGER DEFAULT 5, 98 context_json TEXT, 99 result_json TEXT, 100 parent_task_id INTEGER, 101 error_message TEXT, 102 reviewed_by TEXT, 103 approval_json TEXT, 104 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 105 started_at DATETIME, 106 completed_at DATETIME, 107 retry_count INTEGER DEFAULT 0 108 ); 109 CREATE TABLE IF NOT EXISTS agent_logs ( 110 id INTEGER PRIMARY KEY AUTOINCREMENT, 111 task_id INTEGER, 112 agent_name TEXT NOT NULL, 113 log_level TEXT, 114 message TEXT NOT NULL, 115 data_json TEXT, 116 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 117 ); 118 CREATE TABLE IF NOT EXISTS agent_state ( 119 agent_name TEXT PRIMARY KEY, 120 last_active DATETIME DEFAULT CURRENT_TIMESTAMP, 121 current_task_id INTEGER, 122 status TEXT DEFAULT 'idle', 123 metrics_json TEXT 124 ); 125 CREATE TABLE IF NOT EXISTS agent_llm_usage ( 126 id INTEGER PRIMARY KEY AUTOINCREMENT, 127 agent_name TEXT NOT NULL, 128 task_id INTEGER, 129 model TEXT NOT NULL, 130 prompt_tokens INTEGER NOT NULL, 131 completion_tokens INTEGER NOT NULL, 132 cost_usd REAL NOT NULL, 133 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 134 ); 135 CREATE TABLE IF NOT EXISTS pipeline_metrics ( 136 id INTEGER PRIMARY KEY AUTOINCREMENT, 137 stage_name TEXT NOT NULL, 138 sites_processed INTEGER DEFAULT 0, 139 sites_succeeded INTEGER DEFAULT 0, 140 sites_failed INTEGER DEFAULT 0, 141 duration_ms INTEGER NOT NULL, 142 started_at DATETIME NOT NULL, 143 finished_at DATETIME NOT NULL, 144 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 145 ); 146 CREATE TABLE IF NOT EXISTS agent_outcomes ( 147 id INTEGER PRIMARY KEY AUTOINCREMENT, 148 task_id INTEGER NOT NULL, 149 agent_name TEXT NOT NULL, 150 task_type TEXT NOT NULL, 151 outcome TEXT NOT NULL CHECK(outcome IN ('success', 'failure')), 152 context_json TEXT, 153 result_json TEXT, 154 duration_ms INTEGER, 155 created_at DATETIME DEFAULT CURRENT_TIMESTAMP 156 ); 157 CREATE TABLE IF NOT EXISTS agent_messages ( 158 id INTEGER PRIMARY KEY AUTOINCREMENT, 159 task_id INTEGER, 160 from_agent TEXT NOT NULL, 161 to_agent TEXT NOT NULL, 162 message_type TEXT NOT NULL, 163 content TEXT NOT NULL, 164 metadata_json TEXT, 165 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, 166 read_at DATETIME 167 ); 168 CREATE TABLE IF NOT EXISTS human_review_queue ( 169 id INTEGER PRIMARY KEY AUTOINCREMENT, 170 file TEXT, 171 reason TEXT, 172 type TEXT, 173 priority TEXT DEFAULT 'medium', 174 status TEXT DEFAULT 'pending', 175 created_at TEXT DEFAULT (datetime('now')), 176 reviewed_at TEXT, 177 reviewed_by TEXT, 178 notes TEXT 179 ); 180 INSERT OR IGNORE INTO agent_state (agent_name, status) VALUES ('architect', 'idle'); 181 INSERT OR IGNORE INTO agent_state (agent_name, status) VALUES ('developer', 'idle'); 182 INSERT OR IGNORE INTO agent_state (agent_name, status) VALUES ('monitor', 'idle'); 183 `; 184 185 // ============================================================ 186 // Test helpers 187 // ============================================================ 188 189 function createTestDb(dbPath) { 190 const db = new Database(dbPath); 191 db.pragma('foreign_keys = ON'); 192 db.exec(FULL_SCHEMA); 193 return db; 194 } 195 196 async function createTestEnv(dbPath) { 197 resetBaseDb(); 198 resetTaskManagerDb(); 199 resetMessageManagerDb(); 200 201 try { 202 rmSync(dbPath, { force: true }); 203 } catch (_e) { 204 // ignore 205 } 206 207 const db = createTestDb(dbPath); 208 process.env.DATABASE_PATH = dbPath; 209 210 const agent = new ArchitectAgent(); 211 await agent.initialize(); 212 213 return { 214 db, 215 agent, 216 cleanup: () => { 217 resetBaseDb(); 218 resetTaskManagerDb(); 219 resetMessageManagerDb(); 220 try { 221 db.close(); 222 } catch (_e) { 223 // ignore 224 } 225 try { 226 rmSync(dbPath, { force: true }); 227 } catch (_e) { 228 // ignore 229 } 230 }, 231 }; 232 } 233 234 function insertTask(db, taskType, contextJson) { 235 return db 236 .prepare( 237 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 238 VALUES (?, 'architect', 'running', ?) RETURNING id` 239 ) 240 .get(taskType, contextJson !== undefined ? JSON.stringify(contextJson) : null).id; 241 } 242 243 function getTask(db, taskId) { 244 const row = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 245 if (row && row.context_json && typeof row.context_json === 'string') { 246 try { 247 row.context_json = JSON.parse(row.context_json); 248 } catch (_e) { 249 // ignore 250 } 251 } 252 return row; 253 } 254 255 // ============================================================ 256 // 1. processTask: string context_json parsing path 257 // ============================================================ 258 259 describe('ArchitectAgent Coverage2 - processTask context_json parsing', () => { 260 test('parses string context_json into object before routing', async () => { 261 const dbPath = join(tmpdir(), `arch-cov2-ptparse-${Date.now()}.db`); 262 const { db, agent, cleanup } = await createTestEnv(dbPath); 263 try { 264 // Insert task with string context_json (not yet parsed) - simulate raw DB value 265 const taskId = db 266 .prepare( 267 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) 268 VALUES (?, 'architect', 'running', ?) RETURNING id` 269 ) 270 .get('review_design', JSON.stringify({ files: [] })).id; 271 272 // Get raw row without parsing context_json 273 const row = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 274 // context_json is still a string here - processTask should handle it 275 assert.strictEqual( 276 typeof row.context_json, 277 'string', 278 'context_json should be string before parsing' 279 ); 280 281 // processTask handles string context_json 282 await agent.processTask(row); 283 284 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 285 assert.ok( 286 ['completed', 'failed'].includes(updated.status), 287 `Should complete or fail, got: ${updated.status}` 288 ); 289 } finally { 290 cleanup(); 291 } 292 }); 293 294 test('handles null context_json by defaulting to empty object', async () => { 295 const dbPath = join(tmpdir(), `arch-cov2-ptnull-${Date.now()}.db`); 296 const { db, agent, cleanup } = await createTestEnv(dbPath); 297 try { 298 const taskId = insertTask(db, 'review_design', null); 299 const task = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 300 // Null context_json - processTask sets it to {} 301 task.context_json = null; 302 303 await agent.processTask(task); 304 305 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 306 assert.ok( 307 ['completed', 'failed'].includes(updated.status), 308 `Should complete or fail, got: ${updated.status}` 309 ); 310 } finally { 311 cleanup(); 312 } 313 }); 314 }); 315 316 // ============================================================ 317 // 2. reviewDesign: over-engineering pattern detection 318 // ============================================================ 319 320 describe('ArchitectAgent Coverage2 - reviewDesign over-engineering patterns', () => { 321 test('detects Factory class pattern as over-engineering', async () => { 322 const dbPath = join(tmpdir(), `arch-cov2-rdoe1-${Date.now()}.db`); 323 const { db, agent, cleanup } = await createTestEnv(dbPath); 324 try { 325 // Write a file with a Factory pattern 326 const tmpFile = join(tmpdir(), `factory-test-${Date.now()}.js`); 327 writeFileSync( 328 tmpFile, 329 'class SiteFactory {\n create() { return {}; }\n}\nexport default SiteFactory;\n' 330 ); 331 332 const taskId = insertTask(db, 'review_design', { files: [tmpFile] }); 333 const task = getTask(db, taskId); 334 335 await agent.reviewDesign(task); 336 337 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 338 assert.strictEqual(updated.status, 'completed', 'Should complete'); 339 const result = JSON.parse(updated.result_json || '{}'); 340 assert.ok(Array.isArray(result.issues), 'Should have issues array'); 341 // Should detect over_engineering issue from Factory class 342 const oeIssue = result.issues.find(i => i.type === 'over_engineering'); 343 assert.ok(oeIssue, 'Should detect Factory class as over-engineering'); 344 assert.strictEqual(oeIssue.severity, 'low', 'Over-engineering severity should be low'); 345 346 try { 347 rmSync(tmpFile, { force: true }); 348 } catch (_e) { 349 /* ignore */ 350 } 351 } finally { 352 cleanup(); 353 } 354 }); 355 356 test('detects Builder class pattern as over-engineering', async () => { 357 const dbPath = join(tmpdir(), `arch-cov2-rdoe2-${Date.now()}.db`); 358 const { db, agent, cleanup } = await createTestEnv(dbPath); 359 try { 360 const tmpFile = join(tmpdir(), `builder-test-${Date.now()}.js`); 361 writeFileSync(tmpFile, 'class QueryBuilder {\n build() { return "query"; }\n}\n'); 362 363 const taskId = insertTask(db, 'review_design', { files: [tmpFile] }); 364 const task = getTask(db, taskId); 365 366 await agent.reviewDesign(task); 367 368 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 369 assert.strictEqual(updated.status, 'completed'); 370 const result = JSON.parse(updated.result_json || '{}'); 371 const oeIssue = result.issues.find(i => i.type === 'over_engineering'); 372 assert.ok(oeIssue, 'Should detect Builder class as over-engineering'); 373 374 try { 375 rmSync(tmpFile, { force: true }); 376 } catch (_e) { 377 /* ignore */ 378 } 379 } finally { 380 cleanup(); 381 } 382 }); 383 384 test('detects Strategy class pattern as over-engineering', async () => { 385 const dbPath = join(tmpdir(), `arch-cov2-rdoe3-${Date.now()}.db`); 386 const { db, agent, cleanup } = await createTestEnv(dbPath); 387 try { 388 const tmpFile = join(tmpdir(), `strategy-test-${Date.now()}.js`); 389 writeFileSync(tmpFile, 'class PricingStrategy {\n calculate() { return 100; }\n}\n'); 390 391 const taskId = insertTask(db, 'review_design', { files: [tmpFile] }); 392 const task = getTask(db, taskId); 393 394 await agent.reviewDesign(task); 395 396 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 397 assert.strictEqual(updated.status, 'completed'); 398 const result = JSON.parse(updated.result_json || '{}'); 399 const oeIssue = result.issues.find(i => i.type === 'over_engineering'); 400 assert.ok(oeIssue, 'Should detect Strategy class as over-engineering'); 401 402 try { 403 rmSync(tmpFile, { force: true }); 404 } catch (_e) { 405 /* ignore */ 406 } 407 } finally { 408 cleanup(); 409 } 410 }); 411 412 test('approves design with no high-severity issues', async () => { 413 const dbPath = join(tmpdir(), `arch-cov2-rdapprove-${Date.now()}.db`); 414 const { db, agent, cleanup } = await createTestEnv(dbPath); 415 try { 416 const tmpFile = join(tmpdir(), `simple-test-${Date.now()}.js`); 417 // Simple file with < 150 lines and no anti-patterns 418 writeFileSync(tmpFile, 'export function hello() { return "world"; }\n'); 419 420 const taskId = insertTask(db, 'review_design', { files: [tmpFile] }); 421 const task = getTask(db, taskId); 422 423 await agent.reviewDesign(task); 424 425 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 426 assert.strictEqual(updated.status, 'completed'); 427 const result = JSON.parse(updated.result_json || '{}'); 428 assert.strictEqual(result.approved, true, 'Simple file should be approved'); 429 430 try { 431 rmSync(tmpFile, { force: true }); 432 } catch (_e) { 433 /* ignore */ 434 } 435 } finally { 436 cleanup(); 437 } 438 }); 439 440 test('generates recommendations for found issues', async () => { 441 const dbPath = join(tmpdir(), `arch-cov2-rdrec-${Date.now()}.db`); 442 const { db, agent, cleanup } = await createTestEnv(dbPath); 443 try { 444 const tmpFile = join(tmpdir(), `large-factory-${Date.now()}.js`); 445 // Large file (200 lines) with Factory pattern 446 const lines = Array(200).fill('const x = 1;'); 447 lines.push('class DataFactory { create() {} }'); 448 writeFileSync(tmpFile, lines.join('\n')); 449 450 const taskId = insertTask(db, 'review_design', { files: [tmpFile] }); 451 const task = getTask(db, taskId); 452 453 await agent.reviewDesign(task); 454 455 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 456 assert.strictEqual(updated.status, 'completed'); 457 const result = JSON.parse(updated.result_json || '{}'); 458 assert.ok(Array.isArray(result.recommendations), 'Should have recommendations'); 459 assert.ok(result.recommendations.length > 0, 'Should have at least one recommendation'); 460 461 try { 462 rmSync(tmpFile, { force: true }); 463 } catch (_e) { 464 /* ignore */ 465 } 466 } finally { 467 cleanup(); 468 } 469 }); 470 }); 471 472 // ============================================================ 473 // 3. checkComplexity: deep nesting detection (depth > 4) 474 // ============================================================ 475 476 describe('ArchitectAgent Coverage2 - checkComplexity nesting detection', () => { 477 test('detects max_depth violation in deeply nested code', async () => { 478 const dbPath = join(tmpdir(), `arch-cov2-ccdepth-${Date.now()}.db`); 479 const { db, agent, cleanup } = await createTestEnv(dbPath); 480 try { 481 const tmpFile = join(tmpdir(), `deeply-nested-${Date.now()}.js`); 482 // Create code with 6 levels of nesting (exceeds limit of 4) 483 writeFileSync( 484 tmpFile, 485 `function level1() { 486 if (a) { 487 while (b) { 488 for (let i = 0; i < n; i++) { 489 if (c) { 490 switch (d) { 491 case 'x': { doSomething(); break; } 492 } 493 } 494 } 495 } 496 } 497 }` 498 ); 499 500 const taskId = insertTask(db, 'check_complexity', { files: [tmpFile] }); 501 const task = getTask(db, taskId); 502 503 await agent.checkComplexity(task); 504 505 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 506 assert.strictEqual(updated.status, 'completed'); 507 const result = JSON.parse(updated.result_json || '{}'); 508 assert.ok(Array.isArray(result.issues), 'Should have issues array'); 509 510 const depthIssue = result.issues.find(i => i.type === 'max_depth'); 511 assert.ok(depthIssue, 'Should detect max_depth issue'); 512 assert.strictEqual(depthIssue.limit, 4, 'Depth limit should be 4'); 513 assert.ok(depthIssue.current > 4, `Depth should exceed 4, got ${depthIssue.current}`); 514 515 try { 516 rmSync(tmpFile, { force: true }); 517 } catch (_e) { 518 /* ignore */ 519 } 520 } finally { 521 cleanup(); 522 } 523 }); 524 525 test('creates suggest_refactor task for files with depth violations', async () => { 526 const dbPath = join(tmpdir(), `arch-cov2-ccrefactor-${Date.now()}.db`); 527 const { db, agent, cleanup } = await createTestEnv(dbPath); 528 try { 529 const tmpFile = join(tmpdir(), `nested-code-${Date.now()}.js`); 530 // 5-level nesting exceeds limit 531 writeFileSync( 532 tmpFile, 533 'function a() { if(1) { while(2) { for(;;) { if(3) { doIt(); } } } } }' 534 ); 535 536 const taskId = insertTask(db, 'check_complexity', { files: [tmpFile] }); 537 const task = getTask(db, taskId); 538 539 await agent.checkComplexity(task); 540 541 // Verify suggest_refactor task was created for depth violation 542 const refactorTasks = db 543 .prepare( 544 `SELECT * FROM agent_tasks WHERE task_type = 'suggest_refactor' ORDER BY id DESC LIMIT 5` 545 ) 546 .all(); 547 548 assert.ok( 549 refactorTasks.length > 0, 550 'Should create suggest_refactor tasks for depth violations' 551 ); 552 553 try { 554 rmSync(tmpFile, { force: true }); 555 } catch (_e) { 556 /* ignore */ 557 } 558 } finally { 559 cleanup(); 560 } 561 }); 562 563 test('checkComplexity without files list uses getJsFiles fallback', async () => { 564 const dbPath = join(tmpdir(), `arch-cov2-ccnofiles-${Date.now()}.db`); 565 const { db, agent, cleanup } = await createTestEnv(dbPath); 566 try { 567 // No files in context - should use getJsFiles() to find all JS files 568 const taskId = insertTask(db, 'check_complexity', {}); 569 const task = getTask(db, taskId); 570 571 // This will call getJsFiles() internally (via execSync 'find src -name "*.js"') 572 await agent.checkComplexity(task); 573 574 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 575 assert.strictEqual( 576 updated.status, 577 'completed', 578 'Should complete even scanning all src files' 579 ); 580 const result = JSON.parse(updated.result_json || '{}'); 581 assert.ok(typeof result.files_checked === 'number', 'Should report files_checked count'); 582 // When no files specified, getJsFiles() is called which returns src/ files 583 // files_checked could be 0 if src/ doesn't exist in test context or many files 584 } finally { 585 cleanup(); 586 } 587 }); 588 }); 589 590 // ============================================================ 591 // 4. auditDocumentation: focus_areas filtering 592 // ============================================================ 593 594 describe('ArchitectAgent Coverage2 - auditDocumentation focus areas', () => { 595 test('audits only agent_system focus area', async () => { 596 const dbPath = join(tmpdir(), `arch-cov2-adagent-${Date.now()}.db`); 597 const { db, agent, cleanup } = await createTestEnv(dbPath); 598 try { 599 const taskId = insertTask(db, 'audit_documentation', { 600 scope: 'agent_system', 601 focus_areas: ['agent_system'], 602 }); 603 const task = getTask(db, taskId); 604 605 await agent.auditDocumentation(task); 606 607 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 608 assert.ok( 609 ['completed', 'failed'].includes(updated.status), 610 `Should complete or fail, got: ${updated.status}` 611 ); 612 if (updated.status === 'completed') { 613 const result = JSON.parse(updated.result_json || '{}'); 614 assert.ok( 615 typeof result.total_discrepancies === 'number', 616 'Should have total_discrepancies' 617 ); 618 } 619 } finally { 620 cleanup(); 621 } 622 }); 623 624 test('audits only pipeline_monitoring focus area', async () => { 625 const dbPath = join(tmpdir(), `arch-cov2-adpipeline-${Date.now()}.db`); 626 const { db, agent, cleanup } = await createTestEnv(dbPath); 627 try { 628 const taskId = insertTask(db, 'audit_documentation', { 629 scope: 'pipeline', 630 focus_areas: ['pipeline_monitoring'], 631 }); 632 const task = getTask(db, taskId); 633 634 await agent.auditDocumentation(task); 635 636 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 637 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 638 } finally { 639 cleanup(); 640 } 641 }); 642 643 test('audits only error_detection focus area', async () => { 644 const dbPath = join(tmpdir(), `arch-cov2-aderror-${Date.now()}.db`); 645 const { db, agent, cleanup } = await createTestEnv(dbPath); 646 try { 647 const taskId = insertTask(db, 'audit_documentation', { 648 scope: 'error_detection', 649 focus_areas: ['error_detection'], 650 }); 651 const task = getTask(db, taskId); 652 653 await agent.auditDocumentation(task); 654 655 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 656 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 657 } finally { 658 cleanup(); 659 } 660 }); 661 662 test('audits all focus areas when focus_areas is null (default)', async () => { 663 const dbPath = join(tmpdir(), `arch-cov2-adall-${Date.now()}.db`); 664 const { db, agent, cleanup } = await createTestEnv(dbPath); 665 try { 666 const taskId = insertTask(db, 'audit_documentation', { 667 scope: 'full', 668 // No focus_areas specified → audits all 669 }); 670 const task = getTask(db, taskId); 671 672 await agent.auditDocumentation(task); 673 674 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 675 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 676 } finally { 677 cleanup(); 678 } 679 }); 680 681 test('adds high-severity discrepancies to human review queue', async () => { 682 const dbPath = join(tmpdir(), `arch-cov2-adhigh-${Date.now()}.db`); 683 const { db, agent, cleanup } = await createTestEnv(dbPath); 684 try { 685 const taskId = insertTask(db, 'audit_documentation', { 686 focus_areas: ['agent_system', 'pipeline_monitoring', 'error_detection'], 687 }); 688 const task = getTask(db, taskId); 689 690 await agent.auditDocumentation(task); 691 692 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 693 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 694 695 // If completed, verify result structure 696 if (updated.status === 'completed') { 697 const result = JSON.parse(updated.result_json || '{}'); 698 assert.ok(typeof result.discrepancies !== 'undefined', 'Should have discrepancies field'); 699 assert.ok(typeof result.high_severity === 'number', 'Should have high_severity count'); 700 } 701 } finally { 702 cleanup(); 703 } 704 }); 705 }); 706 707 // ============================================================ 708 // 5. detectSemanticDocChanges 709 // ============================================================ 710 711 describe('ArchitectAgent Coverage2 - detectSemanticDocChanges', () => { 712 test('returns empty array when no code files provided', async () => { 713 const dbPath = join(tmpdir(), `arch-cov2-dsdc1-${Date.now()}.db`); 714 const { agent, cleanup } = await createTestEnv(dbPath); 715 try { 716 const result = await agent.detectSemanticDocChanges([]); 717 assert.ok(Array.isArray(result), 'Should return array'); 718 assert.strictEqual(result.length, 0, 'Should return empty array for no files'); 719 } finally { 720 cleanup(); 721 } 722 }); 723 724 test('returns stale items when LLM detects doc changes', async () => { 725 const dbPath = join(tmpdir(), `arch-cov2-dsdc2-${Date.now()}.db`); 726 const { agent, cleanup } = await createTestEnv(dbPath); 727 try { 728 // Use a real file that likely has git history 729 const result = await agent.detectSemanticDocChanges(['src/agents/architect.js']); 730 assert.ok(Array.isArray(result), 'Should return array'); 731 // Result could be empty or have items depending on mock response 732 } finally { 733 cleanup(); 734 } 735 }); 736 737 test('handles git diff errors gracefully for new files', async () => { 738 const dbPath = join(tmpdir(), `arch-cov2-dsdc3-${Date.now()}.db`); 739 const { agent, cleanup } = await createTestEnv(dbPath); 740 try { 741 // Non-existent file - git diff will fail, should be handled gracefully 742 const result = await agent.detectSemanticDocChanges(['/nonexistent/new-file.js']); 743 assert.ok(Array.isArray(result), 'Should return array even for non-existent files'); 744 } finally { 745 cleanup(); 746 } 747 }); 748 749 test('limits analysis to first 5 files', async () => { 750 const dbPath = join(tmpdir(), `arch-cov2-dsdc4-${Date.now()}.db`); 751 const { agent, cleanup } = await createTestEnv(dbPath); 752 try { 753 // Provide 7 files - should only process 5 754 const files = [ 755 'src/agents/architect.js', 756 'src/agents/base-agent.js', 757 'src/score.js', 758 'src/scrape.js', 759 'src/capture.js', 760 'src/enrich.js', 761 'src/proposals.js', 762 ]; 763 const result = await agent.detectSemanticDocChanges(files); 764 assert.ok(Array.isArray(result), 'Should return array'); 765 } finally { 766 cleanup(); 767 } 768 }); 769 }); 770 771 // ============================================================ 772 // 6. updateDocumentation: code fences removal + commit path 773 // ============================================================ 774 775 describe('ArchitectAgent Coverage2 - updateDocumentation commit paths', () => { 776 test('removes markdown code fences from generated documentation', async () => { 777 const dbPath = join(tmpdir(), `arch-cov2-udcodefence-${Date.now()}.db`); 778 const { db, agent, cleanup } = await createTestEnv(dbPath); 779 try { 780 // The mock generateCode always returns a response - we test the fence stripping 781 // by checking that the process runs correctly end-to-end 782 const taskId = insertTask(db, 'update_documentation', { 783 stale_items: [ 784 { 785 file: 'README.md', 786 reason: 'New feature added', 787 fix: 'Update README with new feature docs', 788 }, 789 ], 790 files: ['src/feature.js'], 791 }); 792 const task = getTask(db, taskId); 793 794 await agent.updateDocumentation(task); 795 796 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 797 assert.ok( 798 ['completed', 'failed'].includes(updated.status), 799 `Should complete or fail, got: ${updated.status}` 800 ); 801 if (updated.status === 'completed') { 802 const result = JSON.parse(updated.result_json || '{}'); 803 assert.ok(Array.isArray(result.updated), 'Should have updated array'); 804 assert.ok(Array.isArray(result.errors), 'Should have errors array'); 805 } 806 } finally { 807 cleanup(); 808 } 809 }); 810 811 test('verifies documentation after updates', async () => { 812 const dbPath = join(tmpdir(), `arch-cov2-udverify-${Date.now()}.db`); 813 const { db, agent, cleanup } = await createTestEnv(dbPath); 814 try { 815 const taskId = insertTask(db, 'update_documentation', { 816 stale_items: [ 817 { file: 'CLAUDE.md', reason: 'Pipeline modified', fix: 'Update pipeline docs' }, 818 ], 819 }); 820 const task = getTask(db, taskId); 821 822 await agent.updateDocumentation(task); 823 824 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 825 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 826 if (updated.status === 'completed') { 827 const result = JSON.parse(updated.result_json || '{}'); 828 // verifyDocumentation is called and result stored 829 assert.ok(result.verification !== undefined, 'Should have verification results'); 830 } 831 } finally { 832 cleanup(); 833 } 834 }); 835 836 test('uses identifyAffectedDocs when files provided but no stale_items', async () => { 837 const dbPath = join(tmpdir(), `arch-cov2-udidentify-${Date.now()}.db`); 838 const { db, agent, cleanup } = await createTestEnv(dbPath); 839 try { 840 const taskId = insertTask(db, 'update_documentation', { 841 // No stale_items - will call identifyAffectedDocs with files 842 files: ['db/migrations/050-test.sql'], 843 change_type: 'new_feature', 844 }); 845 const task = getTask(db, taskId); 846 847 await agent.updateDocumentation(task); 848 849 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 850 assert.ok(['completed', 'failed'].includes(updated.status), `Should complete or fail`); 851 } finally { 852 cleanup(); 853 } 854 }); 855 }); 856 857 // ============================================================ 858 // 7. createDesignProposal: PO approval path (significant change) 859 // ============================================================ 860 861 describe('ArchitectAgent Coverage2 - createDesignProposal PO approval paths', () => { 862 test('requests PO approval for significant changes', async () => { 863 const dbPath = join(tmpdir(), `arch-cov2-cdpsig-${Date.now()}.db`); 864 const { db, agent, cleanup } = await createTestEnv(dbPath); 865 try { 866 const taskId = insertTask(db, 'design_proposal', { 867 feature_description: 'Complete rewrite of the pipeline engine', 868 significance: 'significant', 869 requirements: ['Zero downtime', 'Backward compatible'], 870 }); 871 const task = getTask(db, taskId); 872 873 await agent.createDesignProposal(task); 874 875 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 876 // Significant → needs PO approval → blocked 877 assert.ok( 878 ['completed', 'blocked', 'failed'].includes(updated.status), 879 `Should be completed, blocked, or failed - got: ${updated.status}` 880 ); 881 } finally { 882 cleanup(); 883 } 884 }); 885 886 test('auto-approves minor changes without PO approval', async () => { 887 const dbPath = join(tmpdir(), `arch-cov2-cdpminor-${Date.now()}.db`); 888 const { db, agent, cleanup } = await createTestEnv(dbPath); 889 try { 890 const taskId = insertTask(db, 'design_proposal', { 891 feature_description: 'Add console.log statement for debugging', 892 significance: 'minor', 893 }); 894 const task = getTask(db, taskId); 895 896 await agent.createDesignProposal(task); 897 898 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 899 assert.ok( 900 ['completed', 'blocked', 'failed'].includes(updated.status), 901 `Should resolve to a valid status: ${updated.status}` 902 ); 903 } finally { 904 cleanup(); 905 } 906 }); 907 908 test('fails when context has no feature_description and no error_message', async () => { 909 const dbPath = join(tmpdir(), `arch-cov2-cdpfail-${Date.now()}.db`); 910 const { db, agent, cleanup } = await createTestEnv(dbPath); 911 try { 912 const taskId = insertTask(db, 'design_proposal', { 913 // Neither feature_description nor error_message 914 significance: 'major', 915 }); 916 const task = getTask(db, taskId); 917 918 await agent.createDesignProposal(task); 919 920 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 921 assert.strictEqual(updated.status, 'failed', 'Should fail with missing description'); 922 assert.ok( 923 updated.error_message && updated.error_message.length > 0, 924 'Should have error message' 925 ); 926 } finally { 927 cleanup(); 928 } 929 }); 930 931 test('uses error_message as feature_description for bug fix workflows', async () => { 932 const dbPath = join(tmpdir(), `arch-cov2-cdpbug-${Date.now()}.db`); 933 const { db, agent, cleanup } = await createTestEnv(dbPath); 934 try { 935 const taskId = insertTask(db, 'design_proposal', { 936 error_message: 'ReferenceError: cannot read property of undefined', 937 error_type: 'ReferenceError', 938 significance: 'minor', 939 }); 940 const task = getTask(db, taskId); 941 942 await agent.createDesignProposal(task); 943 944 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 945 // Should NOT fail due to missing feature_description since error_message substitutes 946 if (updated.status === 'failed') { 947 assert.ok( 948 !updated.error_message.includes('feature_description'), 949 'Error should not mention feature_description when error_message was present' 950 ); 951 } else { 952 assert.ok( 953 ['completed', 'blocked'].includes(updated.status), 954 `Should proceed without feature_description error: ${updated.status}` 955 ); 956 } 957 } finally { 958 cleanup(); 959 } 960 }); 961 962 test('analyzes specific files when files_to_analyze provided', async () => { 963 const dbPath = join(tmpdir(), `arch-cov2-cdpfiles-${Date.now()}.db`); 964 const { db, agent, cleanup } = await createTestEnv(dbPath); 965 try { 966 const taskId = insertTask(db, 'design_proposal', { 967 feature_description: 'Refactor scoring module', 968 files_to_analyze: ['src/score.js'], 969 significance: 'minor', 970 }); 971 const task = getTask(db, taskId); 972 973 await agent.createDesignProposal(task); 974 975 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 976 assert.ok( 977 ['completed', 'blocked', 'failed'].includes(updated.status), 978 `Should resolve: ${updated.status}` 979 ); 980 } finally { 981 cleanup(); 982 } 983 }); 984 }); 985 986 // ============================================================ 987 // 8. reviewImplementationPlan: approved path with good coverage 988 // ============================================================ 989 990 describe('ArchitectAgent Coverage2 - reviewImplementationPlan approval paths', () => { 991 test('approves plan with 90% coverage target and no high-severity issues', async () => { 992 const dbPath = join(tmpdir(), `arch-cov2-ripapprove-${Date.now()}.db`); 993 const { db, agent, cleanup } = await createTestEnv(dbPath); 994 try { 995 // Create parent task that will be approved 996 const parentId = db 997 .prepare( 998 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 999 ) 1000 .get( 1001 'implement_feature', 1002 'developer', 1003 'blocked', 1004 JSON.stringify({ feature: 'caching' }) 1005 ).id; 1006 1007 const taskId = insertTask(db, 'technical_review', { 1008 implementation_plan: { 1009 summary: 'Add Redis-style in-memory caching', 1010 files_to_modify: ['README.md'], 1011 documentation_updates: true, 1012 test_plan: { 1013 coverage_target: 90, 1014 test_files: ['tests/cache.test.js'], 1015 }, 1016 breaking_changes: [], 1017 requires_migration: false, 1018 estimated_effort: 2, 1019 }, 1020 original_task_id: parentId, 1021 }); 1022 const task = getTask(db, taskId); 1023 1024 await agent.reviewImplementationPlan(task); 1025 1026 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1027 assert.ok( 1028 ['completed', 'failed'].includes(updated.status), 1029 `Should complete or fail: ${updated.status}` 1030 ); 1031 } finally { 1032 cleanup(); 1033 } 1034 }); 1035 1036 test('fails plan with missing test_plan entirely', async () => { 1037 const dbPath = join(tmpdir(), `arch-cov2-ripnotestplan-${Date.now()}.db`); 1038 const { db, agent, cleanup } = await createTestEnv(dbPath); 1039 try { 1040 const parentId = db 1041 .prepare( 1042 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1043 ) 1044 .get('implement_feature', 'developer', 'blocked', JSON.stringify({})).id; 1045 1046 const taskId = insertTask(db, 'technical_review', { 1047 implementation_plan: { 1048 summary: 'Quick fix', 1049 files_to_modify: ['README.md'], 1050 documentation_updates: true, 1051 // No test_plan at all 1052 }, 1053 original_task_id: parentId, 1054 }); 1055 const task = getTask(db, taskId); 1056 1057 await agent.reviewImplementationPlan(task); 1058 1059 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1060 assert.ok( 1061 ['completed', 'failed'].includes(updated.status), 1062 `Should complete or fail: ${updated.status}` 1063 ); 1064 } finally { 1065 cleanup(); 1066 } 1067 }); 1068 1069 test('flags low coverage target as high-severity issue', async () => { 1070 const dbPath = join(tmpdir(), `arch-cov2-riplowcov-${Date.now()}.db`); 1071 const { db, agent, cleanup } = await createTestEnv(dbPath); 1072 try { 1073 const parentId = db 1074 .prepare( 1075 `INSERT INTO agent_tasks (task_type, assigned_to, status, context_json) VALUES (?, ?, ?, ?) RETURNING id` 1076 ) 1077 .get('implement_feature', 'developer', 'blocked', JSON.stringify({})).id; 1078 1079 const taskId = insertTask(db, 'technical_review', { 1080 implementation_plan: { 1081 summary: 'Quick patch', 1082 files_to_modify: ['README.md'], 1083 documentation_updates: true, 1084 test_plan: { coverage_target: 50 }, // Well below 85% 1085 }, 1086 original_task_id: parentId, 1087 }); 1088 const task = getTask(db, taskId); 1089 1090 await agent.reviewImplementationPlan(task); 1091 1092 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1093 assert.ok( 1094 ['completed', 'failed'].includes(updated.status), 1095 `Should complete or fail: ${updated.status}` 1096 ); 1097 // Low coverage should cause failure 1098 if (updated.status === 'failed') { 1099 assert.ok( 1100 updated.error_message.includes('high-severity'), 1101 'Error should mention high-severity issues' 1102 ); 1103 } 1104 } finally { 1105 cleanup(); 1106 } 1107 }); 1108 }); 1109 1110 // ============================================================ 1111 // 9. checkBranchHealth: diverged autofix branch scenario 1112 // ============================================================ 1113 1114 describe('ArchitectAgent Coverage2 - checkBranchHealth scenarios', () => { 1115 test('handles check with both flags disabled', async () => { 1116 const dbPath = join(tmpdir(), `arch-cov2-cbhboth-${Date.now()}.db`); 1117 const { db, agent, cleanup } = await createTestEnv(dbPath); 1118 try { 1119 const taskId = insertTask(db, 'check_branch_health', { 1120 check_stale_branches: false, 1121 ensure_autofix_aligned: false, 1122 }); 1123 const task = getTask(db, taskId); 1124 1125 await agent.checkBranchHealth(task); 1126 1127 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1128 assert.ok( 1129 ['completed', 'failed'].includes(updated.status), 1130 `Should complete or fail: ${updated.status}` 1131 ); 1132 if (updated.status === 'completed') { 1133 const result = JSON.parse(updated.result_json || '{}'); 1134 assert.strictEqual(result.total_issues, 0, 'No issues when both checks disabled'); 1135 } 1136 } finally { 1137 cleanup(); 1138 } 1139 }); 1140 1141 test('runs with default context when context_json is empty', async () => { 1142 const dbPath = join(tmpdir(), `arch-cov2-cbhdefault-${Date.now()}.db`); 1143 const { db, agent, cleanup } = await createTestEnv(dbPath); 1144 try { 1145 const taskId = insertTask(db, 'check_branch_health', {}); 1146 const task = getTask(db, taskId); 1147 1148 await agent.checkBranchHealth(task); 1149 1150 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1151 assert.ok( 1152 ['completed', 'failed'].includes(updated.status), 1153 `Should complete or fail: ${updated.status}` 1154 ); 1155 } finally { 1156 cleanup(); 1157 } 1158 }); 1159 1160 test('detects issues and counts them correctly in result', async () => { 1161 const dbPath = join(tmpdir(), `arch-cov2-cbhcount-${Date.now()}.db`); 1162 const { db, agent, cleanup } = await createTestEnv(dbPath); 1163 try { 1164 const taskId = insertTask(db, 'check_branch_health', { 1165 check_stale_branches: true, 1166 ensure_autofix_aligned: true, 1167 max_divergence_commits: 0, // Very strict - autofix must be 0 commits behind 1168 }); 1169 const task = getTask(db, taskId); 1170 1171 await agent.checkBranchHealth(task); 1172 1173 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1174 assert.ok( 1175 ['completed', 'failed'].includes(updated.status), 1176 `Should complete or fail: ${updated.status}` 1177 ); 1178 if (updated.status === 'completed') { 1179 const result = JSON.parse(updated.result_json || '{}'); 1180 assert.ok(typeof result.stale_branches === 'number', 'Should have stale_branches count'); 1181 assert.ok( 1182 typeof result.missing_branches === 'number', 1183 'Should have missing_branches count' 1184 ); 1185 assert.strictEqual( 1186 result.total_issues, 1187 result.issues.length, 1188 'total_issues should match issues array length' 1189 ); 1190 } 1191 } finally { 1192 cleanup(); 1193 } 1194 }); 1195 }); 1196 1197 // ============================================================ 1198 // 10. profilePerformance: critical severity bottlenecks 1199 // ============================================================ 1200 1201 describe('ArchitectAgent Coverage2 - profilePerformance severity levels', () => { 1202 test('creates suggest_refactor for critical severity bottleneck (5x threshold)', async () => { 1203 const dbPath = join(tmpdir(), `arch-cov2-pfpcritical-${Date.now()}.db`); 1204 const { db, agent, cleanup } = await createTestEnv(dbPath); 1205 try { 1206 const now = new Date().toISOString(); 1207 const pastHour = new Date(Date.now() - 3600000).toISOString(); 1208 1209 // Insert a critical bottleneck: 600000ms avg when threshold is 60000ms (10x!) 1210 db.prepare( 1211 `INSERT INTO pipeline_metrics (stage_name, sites_processed, sites_succeeded, sites_failed, duration_ms, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?)` 1212 ).run('assets', 10, 5, 5, 600000, pastHour, now); 1213 1214 const taskId = insertTask(db, 'profile_performance', { 1215 threshold_ms: 60000, 1216 days_back: 7, 1217 }); 1218 const task = getTask(db, taskId); 1219 1220 await agent.profilePerformance(task); 1221 1222 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1223 assert.strictEqual(updated.status, 'completed', 'Should complete'); 1224 const result = JSON.parse(updated.result_json || '{}'); 1225 assert.ok(result.bottlenecks.length > 0, 'Should identify assets as bottleneck'); 1226 1227 const assetBottleneck = result.bottlenecks.find(b => b.stage === 'assets'); 1228 assert.ok(assetBottleneck, 'Should identify assets stage'); 1229 assert.strictEqual( 1230 assetBottleneck.severity, 1231 'critical', 1232 'Should be critical severity (10x threshold)' 1233 ); 1234 assert.ok(typeof assetBottleneck.failure_rate === 'number', 'Should have failure_rate'); 1235 1236 // Verify suggest_refactor tasks created for critical severity 1237 const refactorTasks = db 1238 .prepare(`SELECT * FROM agent_tasks WHERE task_type = 'suggest_refactor' ORDER BY id DESC`) 1239 .all(); 1240 assert.ok( 1241 refactorTasks.length > 0, 1242 'Should create suggest_refactor tasks for critical bottlenecks' 1243 ); 1244 1245 // Critical severity gets priority 8 1246 const criticalTask = refactorTasks.find(t => t.priority === 8); 1247 assert.ok(criticalTask, 'Critical bottleneck refactor task should have priority 8'); 1248 } finally { 1249 cleanup(); 1250 } 1251 }); 1252 1253 test('creates suggest_refactor for high severity bottleneck (3-4.9x threshold) with priority 6', async () => { 1254 const dbPath = join(tmpdir(), `arch-cov2-pfphigh-${Date.now()}.db`); 1255 const { db, agent, cleanup } = await createTestEnv(dbPath); 1256 try { 1257 const now = new Date().toISOString(); 1258 const pastHour = new Date(Date.now() - 3600000).toISOString(); 1259 1260 // High severity: 3.5x threshold 1261 db.prepare( 1262 `INSERT INTO pipeline_metrics (stage_name, sites_processed, sites_succeeded, sites_failed, duration_ms, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?)` 1263 ).run('scoring', 20, 18, 2, 210000, pastHour, now); 1264 1265 const taskId = insertTask(db, 'profile_performance', { 1266 threshold_ms: 60000, 1267 days_back: 7, 1268 }); 1269 const task = getTask(db, taskId); 1270 1271 await agent.profilePerformance(task); 1272 1273 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1274 assert.strictEqual(updated.status, 'completed'); 1275 const result = JSON.parse(updated.result_json || '{}'); 1276 const scoringBottleneck = result.bottlenecks.find(b => b.stage === 'scoring'); 1277 assert.ok(scoringBottleneck, 'Should find scoring bottleneck'); 1278 assert.strictEqual(scoringBottleneck.severity, 'high', 'Should be high severity'); 1279 1280 const refactorTasks = db 1281 .prepare(`SELECT * FROM agent_tasks WHERE task_type = 'suggest_refactor' ORDER BY id DESC`) 1282 .all(); 1283 assert.ok(refactorTasks.length > 0, 'Should create refactor tasks for high severity'); 1284 const highTask = refactorTasks.find(t => t.priority === 6); 1285 assert.ok(highTask, 'High severity should have priority 6'); 1286 } finally { 1287 cleanup(); 1288 } 1289 }); 1290 1291 test('does not create refactor task for medium/low severity bottlenecks', async () => { 1292 const dbPath = join(tmpdir(), `arch-cov2-pfpmedium-${Date.now()}.db`); 1293 const { db, agent, cleanup } = await createTestEnv(dbPath); 1294 try { 1295 const now = new Date().toISOString(); 1296 const pastHour = new Date(Date.now() - 3600000).toISOString(); 1297 1298 // Medium severity: 2.5x threshold = 150000ms 1299 db.prepare( 1300 `INSERT INTO pipeline_metrics (stage_name, sites_processed, sites_succeeded, sites_failed, duration_ms, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?)` 1301 ).run('enrichment', 15, 15, 0, 150000, pastHour, now); 1302 1303 const taskId = insertTask(db, 'profile_performance', { 1304 threshold_ms: 60000, 1305 days_back: 7, 1306 }); 1307 const task = getTask(db, taskId); 1308 1309 await agent.profilePerformance(task); 1310 1311 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1312 assert.strictEqual(updated.status, 'completed'); 1313 const result = JSON.parse(updated.result_json || '{}'); 1314 1315 const enrichmentBottleneck = result.bottlenecks.find(b => b.stage === 'enrichment'); 1316 if (enrichmentBottleneck) { 1317 assert.ok( 1318 ['medium', 'low'].includes(enrichmentBottleneck.severity), 1319 `Should be medium or low severity: ${enrichmentBottleneck.severity}` 1320 ); 1321 } 1322 } finally { 1323 cleanup(); 1324 } 1325 }); 1326 1327 test('calculates failure_rate correctly for bottlenecks', async () => { 1328 const dbPath = join(tmpdir(), `arch-cov2-pfpfailrate-${Date.now()}.db`); 1329 const { db, agent, cleanup } = await createTestEnv(dbPath); 1330 try { 1331 const now = new Date().toISOString(); 1332 const pastHour = new Date(Date.now() - 3600000).toISOString(); 1333 1334 // 50% failure rate: 10 successes, 10 failures 1335 db.prepare( 1336 `INSERT INTO pipeline_metrics (stage_name, sites_processed, sites_succeeded, sites_failed, duration_ms, started_at, finished_at) VALUES (?, ?, ?, ?, ?, ?, ?)` 1337 ).run('outreach', 20, 10, 10, 400000, pastHour, now); 1338 1339 const taskId = insertTask(db, 'profile_performance', { 1340 threshold_ms: 60000, 1341 days_back: 7, 1342 }); 1343 const task = getTask(db, taskId); 1344 1345 await agent.profilePerformance(task); 1346 1347 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1348 assert.strictEqual(updated.status, 'completed'); 1349 const result = JSON.parse(updated.result_json || '{}'); 1350 1351 const outreachBottleneck = result.bottlenecks.find(b => b.stage === 'outreach'); 1352 if (outreachBottleneck) { 1353 assert.ok(outreachBottleneck.failure_rate > 0, 'Should report non-zero failure rate'); 1354 // 50% failure rate 1355 assert.ok( 1356 Math.abs(outreachBottleneck.failure_rate - 50) < 1, 1357 `Failure rate should be ~50%, got ${outreachBottleneck.failure_rate}` 1358 ); 1359 } 1360 } finally { 1361 cleanup(); 1362 } 1363 }); 1364 }); 1365 1366 // ============================================================ 1367 // 11. verifyDocumentation: all check types 1368 // Note: verifyDocumentation uses mocked readFile from file-operations.js 1369 // The mock returns '# Documentation\n\nSome content here.\n' by default. 1370 // We test the check logic by temporarily overriding mockReadResult. 1371 // ============================================================ 1372 1373 describe('ArchitectAgent Coverage2 - verifyDocumentation checks', () => { 1374 test('detects TODO markers as documentation warning via mock content', async () => { 1375 const dbPath = join(tmpdir(), `arch-cov2-vdtodo-${Date.now()}.db`); 1376 const { agent, cleanup } = await createTestEnv(dbPath); 1377 try { 1378 // Override mock to return content with TODO 1379 mockReadResult = { content: '# Documentation\n\nTODO: Update this section\n', path: '' }; 1380 1381 const results = await agent.verifyDocumentation(['README.md']); 1382 1383 // Should warn about TODO markers 1384 assert.ok( 1385 results.warnings.length > 0 || results.verified.length > 0, 1386 'Should process documentation files' 1387 ); 1388 // If warnings exist, they should flag no_todo_markers 1389 if (results.warnings.length > 0) { 1390 const warning = results.warnings.find(w => w.issues.includes('no_todo_markers')); 1391 assert.ok(warning, 'Should flag no_todo_markers for TODO content'); 1392 } 1393 } finally { 1394 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1395 cleanup(); 1396 } 1397 }); 1398 1399 test('detects FIXME markers as documentation warning via mock content', async () => { 1400 const dbPath = join(tmpdir(), `arch-cov2-vdfixme-${Date.now()}.db`); 1401 const { agent, cleanup } = await createTestEnv(dbPath); 1402 try { 1403 mockReadResult = { content: '# Documentation\n\nFIXME: This section is wrong\n', path: '' }; 1404 1405 const results = await agent.verifyDocumentation(['CLAUDE.md']); 1406 1407 assert.ok( 1408 results.warnings.length > 0 || results.verified.length > 0, 1409 'Should process documentation files' 1410 ); 1411 if (results.warnings.length > 0) { 1412 // FIXME triggers no_todo_markers check 1413 const hasRelevantWarning = results.warnings.some( 1414 w => w.issues.includes('no_todo_markers') || w.issues.length > 0 1415 ); 1416 assert.ok(hasRelevantWarning, 'Should have documentation warning'); 1417 } 1418 } finally { 1419 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1420 cleanup(); 1421 } 1422 }); 1423 1424 test('detects [placeholder] text as documentation warning via mock content', async () => { 1425 const dbPath = join(tmpdir(), `arch-cov2-vdplaceholder-${Date.now()}.db`); 1426 const { agent, cleanup } = await createTestEnv(dbPath); 1427 try { 1428 mockReadResult = { 1429 content: '# Documentation\n\n[placeholder] content goes here\n', 1430 path: '', 1431 }; 1432 1433 const results = await agent.verifyDocumentation(['docs/test.md']); 1434 1435 assert.ok( 1436 results.warnings.length > 0 || results.verified.length > 0, 1437 'Should process documentation files' 1438 ); 1439 if (results.warnings.length > 0) { 1440 const hasPlaceholderWarning = results.warnings.some( 1441 w => w.issues.includes('no_placeholder_text') || w.issues.length > 0 1442 ); 1443 assert.ok(hasPlaceholderWarning, 'Should have placeholder warning'); 1444 } 1445 } finally { 1446 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1447 cleanup(); 1448 } 1449 }); 1450 1451 test('detects TBD text as documentation warning via mock content', async () => { 1452 const dbPath = join(tmpdir(), `arch-cov2-vdtbd-${Date.now()}.db`); 1453 const { agent, cleanup } = await createTestEnv(dbPath); 1454 try { 1455 mockReadResult = { content: '# Documentation\n\nSection: TBD\n', path: '' }; 1456 1457 const results = await agent.verifyDocumentation(['docs/overview.md']); 1458 1459 assert.ok( 1460 results.warnings.length > 0 || results.verified.length > 0, 1461 'Should process documentation files' 1462 ); 1463 } finally { 1464 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1465 cleanup(); 1466 } 1467 }); 1468 1469 test('detects missing # header in markdown file via mock content', async () => { 1470 const dbPath = join(tmpdir(), `arch-cov2-vdnoheader-${Date.now()}.db`); 1471 const { agent, cleanup } = await createTestEnv(dbPath); 1472 try { 1473 // Content with NO # header - proper_formatting check should fail 1474 mockReadResult = { content: 'This is content without a markdown header.\n', path: '' }; 1475 1476 const results = await agent.verifyDocumentation(['docs/no-header.md']); 1477 1478 assert.ok( 1479 results.warnings.length > 0 || results.verified.length > 0, 1480 'Should process documentation files' 1481 ); 1482 if (results.warnings.length > 0) { 1483 const hasFormattingWarning = results.warnings.some( 1484 w => w.issues.includes('proper_formatting') || w.issues.length > 0 1485 ); 1486 assert.ok(hasFormattingWarning, 'Should flag proper_formatting for missing header'); 1487 } 1488 } finally { 1489 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1490 cleanup(); 1491 } 1492 }); 1493 1494 test('verifies good documentation with no issues', async () => { 1495 const dbPath = join(tmpdir(), `arch-cov2-vdgood-${Date.now()}.db`); 1496 const { agent, cleanup } = await createTestEnv(dbPath); 1497 try { 1498 // Good content - passes all checks 1499 mockReadResult = { 1500 content: '# Documentation\n\nThis is properly formatted content.\n', 1501 path: '', 1502 }; 1503 1504 const results = await agent.verifyDocumentation(['README.md']); 1505 1506 assert.ok(results.verified.length > 0, 'Should verify good documentation'); 1507 assert.strictEqual(results.warnings.length, 0, 'Should have no warnings for good docs'); 1508 } finally { 1509 mockReadResult = { content: '# Documentation\n\nSome content here.\n', path: '' }; 1510 cleanup(); 1511 } 1512 }); 1513 }); 1514 1515 // ============================================================ 1516 // 12. identifyAffectedDocs: pipeline stage detection 1517 // ============================================================ 1518 1519 describe('ArchitectAgent Coverage2 - identifyAffectedDocs pipeline stage detection', () => { 1520 test('detects src/pipeline files as requiring CLAUDE.md update', async () => { 1521 const dbPath = join(tmpdir(), `arch-cov2-iadpipeline-${Date.now()}.db`); 1522 const { agent, cleanup } = await createTestEnv(dbPath); 1523 try { 1524 const affected = agent.identifyAffectedDocs(['src/pipeline-service.js'], 'bug_fix'); 1525 1526 assert.ok(Array.isArray(affected), 'Should return array'); 1527 const claudeMd = affected.find(d => d.file === 'CLAUDE.md'); 1528 assert.ok(claudeMd, 'Should identify CLAUDE.md for pipeline changes'); 1529 assert.ok(claudeMd.reason.includes('Pipeline'), 'Reason should mention pipeline'); 1530 } finally { 1531 cleanup(); 1532 } 1533 }); 1534 1535 test('detects src/stages/ files as requiring CLAUDE.md update', async () => { 1536 const dbPath = join(tmpdir(), `arch-cov2-iadstages-${Date.now()}.db`); 1537 const { agent, cleanup } = await createTestEnv(dbPath); 1538 try { 1539 const affected = agent.identifyAffectedDocs(['src/stages/scoring.js'], 'new_feature'); 1540 1541 assert.ok(Array.isArray(affected), 'Should return array'); 1542 const claudeMd = affected.find(d => d.file === 'CLAUDE.md'); 1543 assert.ok(claudeMd, 'Should identify CLAUDE.md for stage changes'); 1544 } finally { 1545 cleanup(); 1546 } 1547 }); 1548 1549 test('returns empty array for unrelated files', async () => { 1550 const dbPath = join(tmpdir(), `arch-cov2-iadnone-${Date.now()}.db`); 1551 const { agent, cleanup } = await createTestEnv(dbPath); 1552 try { 1553 // File with no env vars and not a recognized pattern 1554 const affected = agent.identifyAffectedDocs(['random-untracked-file.txt'], 'update'); 1555 assert.ok(Array.isArray(affected), 'Should return array'); 1556 // No special patterns recognized 1557 assert.strictEqual(affected.length, 0, 'Should not affect any docs for unrelated files'); 1558 } finally { 1559 cleanup(); 1560 } 1561 }); 1562 }); 1563 1564 // ============================================================ 1565 // 13. parseGitLog: edge cases 1566 // ============================================================ 1567 1568 describe('ArchitectAgent Coverage2 - parseGitLog edge cases', () => { 1569 test('deduplicates files appearing in multiple commits', async () => { 1570 const dbPath = join(tmpdir(), `arch-cov2-pgldedup-${Date.now()}.db`); 1571 const { agent, cleanup } = await createTestEnv(dbPath); 1572 try { 1573 const gitLog = `abc123 feat: first commit 1574 src/feature.js 1575 src/utils.js 1576 1577 def456 fix: second commit 1578 src/feature.js 1579 src/other.js`; 1580 1581 const files = agent.parseGitLog(gitLog); 1582 assert.ok(Array.isArray(files), 'Should return array'); 1583 // feature.js appears twice but should be deduplicated 1584 const featureOccurrences = files.filter(f => f === 'src/feature.js'); 1585 assert.strictEqual( 1586 featureOccurrences.length, 1587 1, 1588 'feature.js should appear only once after dedup' 1589 ); 1590 } finally { 1591 cleanup(); 1592 } 1593 }); 1594 1595 test('handles empty git log gracefully', async () => { 1596 const dbPath = join(tmpdir(), `arch-cov2-pglempty-${Date.now()}.db`); 1597 const { agent, cleanup } = await createTestEnv(dbPath); 1598 try { 1599 const files = agent.parseGitLog(''); 1600 assert.ok(Array.isArray(files), 'Should return array'); 1601 assert.strictEqual(files.length, 0, 'Should return empty array for empty log'); 1602 } finally { 1603 cleanup(); 1604 } 1605 }); 1606 1607 test('skips commit hash lines correctly', async () => { 1608 const dbPath = join(tmpdir(), `arch-cov2-pglhash-${Date.now()}.db`); 1609 const { agent, cleanup } = await createTestEnv(dbPath); 1610 try { 1611 const gitLog = `a1b2c3d feat: add feature 1612 src/new-file.js 1613 1614 e4f5a6b fix: bug fix 1615 src/bugfix.js`; 1616 1617 const files = agent.parseGitLog(gitLog); 1618 // Should only have file paths, not commit hashes 1619 const commitLines = files.filter(f => /^[a-f0-9]{7}/.test(f)); 1620 assert.strictEqual(commitLines.length, 0, 'Should not include commit hash lines'); 1621 assert.ok(files.length >= 2, 'Should include file paths'); 1622 } finally { 1623 cleanup(); 1624 } 1625 }); 1626 }); 1627 1628 // ============================================================ 1629 // 14. checkDocumentationFreshness: specific rule paths 1630 // ============================================================ 1631 1632 describe('ArchitectAgent Coverage2 - checkDocumentationFreshness rule paths', () => { 1633 test('completes with documentation_fresh when no stale items found', async () => { 1634 const dbPath = join(tmpdir(), `arch-cov2-cdffresh-${Date.now()}.db`); 1635 const { db, agent, cleanup } = await createTestEnv(dbPath); 1636 try { 1637 const taskId = insertTask(db, 'check_documentation_freshness', {}); 1638 const task = getTask(db, taskId); 1639 1640 await agent.checkDocumentationFreshness(task); 1641 1642 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1643 assert.ok( 1644 ['completed', 'failed'].includes(updated.status), 1645 `Should complete or fail: ${updated.status}` 1646 ); 1647 if (updated.status === 'completed') { 1648 const result = JSON.parse(updated.result_json || '{}'); 1649 assert.ok(result.stale_items !== undefined, 'Should have stale_items field'); 1650 } 1651 } finally { 1652 cleanup(); 1653 } 1654 }); 1655 1656 test('creates update_documentation task when stale items found', async () => { 1657 const dbPath = join(tmpdir(), `arch-cov2-cdfstale-${Date.now()}.db`); 1658 const { db, agent, cleanup } = await createTestEnv(dbPath); 1659 try { 1660 // The checkDocumentationFreshness reads git log and checks various rules 1661 // We just verify it runs end-to-end without crashing 1662 const taskId = insertTask(db, 'check_documentation_freshness', {}); 1663 const task = getTask(db, taskId); 1664 1665 await agent.checkDocumentationFreshness(task); 1666 1667 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1668 assert.ok( 1669 ['completed', 'failed'].includes(updated.status), 1670 `Should complete or fail: ${updated.status}` 1671 ); 1672 1673 // If it found stale items, it should have created an update task 1674 if (updated.status === 'completed') { 1675 const result = JSON.parse(updated.result_json || '{}'); 1676 if (result.stale_items && result.stale_items.length > 0) { 1677 assert.ok(result.update_task_id, 'Should create update task when stale items found'); 1678 } else { 1679 assert.strictEqual( 1680 result.documentation_fresh, 1681 true, 1682 'Should mark docs as fresh when no stale items' 1683 ); 1684 } 1685 } 1686 } finally { 1687 cleanup(); 1688 } 1689 }); 1690 }); 1691 1692 // ============================================================ 1693 // 15. processTask error propagation 1694 // ============================================================ 1695 1696 describe('ArchitectAgent Coverage2 - processTask error propagation', () => { 1697 test('propagates error when method throws', async () => { 1698 const dbPath = join(tmpdir(), `arch-cov2-pterror-${Date.now()}.db`); 1699 const { db, agent, cleanup } = await createTestEnv(dbPath); 1700 try { 1701 const taskId = insertTask(db, 'review_design', { files: [] }); 1702 const task = getTask(db, taskId); 1703 1704 // Monkey-patch reviewDesign to throw 1705 const origReviewDesign = agent.reviewDesign.bind(agent); 1706 agent.reviewDesign = async _task => { 1707 throw new Error('Forced test error in reviewDesign'); 1708 }; 1709 1710 await assert.rejects( 1711 () => agent.processTask(task), 1712 err => { 1713 assert.ok(err.message.includes('Forced test error'), 'Should propagate the error'); 1714 return true; 1715 } 1716 ); 1717 1718 agent.reviewDesign = origReviewDesign; 1719 } finally { 1720 cleanup(); 1721 } 1722 }); 1723 1724 test('handles all delegatable task types through processTask', async () => { 1725 const delegatableTypes = [ 1726 'fix_bug', 1727 'implement_feature', 1728 'investigate_issue', 1729 'create_automation', 1730 ]; 1731 1732 for (const taskType of delegatableTypes) { 1733 const dbPath = join(tmpdir(), `arch-cov2-ptdelegate-${taskType}-${Date.now()}.db`); 1734 const { db, agent, cleanup } = await createTestEnv(dbPath); 1735 try { 1736 const taskId = insertTask(db, taskType, { description: `Test ${taskType}` }); 1737 const task = getTask(db, taskId); 1738 1739 await agent.processTask(task); 1740 1741 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1742 assert.ok( 1743 ['completed', 'failed', 'pending'].includes(updated.status), 1744 `${taskType} should delegate and resolve: ${updated.status}` 1745 ); 1746 } finally { 1747 cleanup(); 1748 } 1749 } 1750 }); 1751 }); 1752 1753 // ============================================================ 1754 // 16. analyzeCodebase: empty filesToAnalyze path 1755 // ============================================================ 1756 1757 describe('ArchitectAgent Coverage2 - analyzeCodebase edge cases', () => { 1758 test('returns fallback when filesToAnalyze is empty array', async () => { 1759 const dbPath = join(tmpdir(), `arch-cov2-acbempty-${Date.now()}.db`); 1760 const { agent, cleanup } = await createTestEnv(dbPath); 1761 try { 1762 const context = await agent.analyzeCodebase('test feature', []); 1763 // Empty array falls through to general search 1764 assert.ok(typeof context === 'string', 'Should return string'); 1765 } finally { 1766 cleanup(); 1767 } 1768 }); 1769 1770 test('limits specific files to 5 when more are provided', async () => { 1771 const dbPath = join(tmpdir(), `arch-cov2-acblimit-${Date.now()}.db`); 1772 const { agent, cleanup } = await createTestEnv(dbPath); 1773 try { 1774 // The mock readFile always succeeds, so we can test the 5-file limit 1775 const manyFiles = [ 1776 'src/score.js', 1777 'src/scrape.js', 1778 'src/capture.js', 1779 'src/enrich.js', 1780 'src/proposals.js', 1781 'src/outreach.js', // 6th file - should be ignored 1782 ]; 1783 const context = await agent.analyzeCodebase('test feature', manyFiles); 1784 assert.ok(typeof context === 'string', 'Should return string context'); 1785 assert.ok(context.length > 0, 'Context should not be empty'); 1786 } finally { 1787 cleanup(); 1788 } 1789 }); 1790 }); 1791 1792 // ============================================================ 1793 // 17. performAutomatedReviewChecks: edge cases 1794 // ============================================================ 1795 1796 describe('ArchitectAgent Coverage2 - performAutomatedReviewChecks comprehensive', () => { 1797 test('handles plan with no files_to_modify', async () => { 1798 const dbPath = join(tmpdir(), `arch-cov2-parcnofiles-${Date.now()}.db`); 1799 const { agent, cleanup } = await createTestEnv(dbPath); 1800 try { 1801 const plan = { 1802 // No files_to_modify field 1803 test_plan: { coverage_target: 90 }, 1804 documentation_updates: true, 1805 }; 1806 const issues = await agent.performAutomatedReviewChecks(plan); 1807 assert.ok(Array.isArray(issues), 'Should return array'); 1808 // No files → no max_lines_risk issues 1809 const lineIssues = issues.filter(i => i.type === 'max_lines_risk'); 1810 assert.strictEqual(lineIssues.length, 0, 'Should have no line issues when no files provided'); 1811 } finally { 1812 cleanup(); 1813 } 1814 }); 1815 1816 test('checks real file when it exists and has >130 lines', async () => { 1817 const dbPath = join(tmpdir(), `arch-cov2-parcreal-${Date.now()}.db`); 1818 const { agent, cleanup } = await createTestEnv(dbPath); 1819 try { 1820 // architect.js itself has >130 lines (it has ~1859 lines!) 1821 const plan = { 1822 files_to_modify: ['src/agents/architect.js'], 1823 test_plan: { coverage_target: 90 }, 1824 documentation_updates: true, 1825 }; 1826 const issues = await agent.performAutomatedReviewChecks(plan); 1827 assert.ok(Array.isArray(issues), 'Should return array'); 1828 // architect.js is huge so should trigger max_lines_risk 1829 const lineIssue = issues.find(i => i.type === 'max_lines_risk'); 1830 assert.ok(lineIssue, 'Should detect max_lines_risk for architect.js (>130 lines)'); 1831 assert.strictEqual(lineIssue.severity, 'medium', 'max_lines_risk should be medium severity'); 1832 } finally { 1833 cleanup(); 1834 } 1835 }); 1836 }); 1837 1838 // ============================================================ 1839 // 18. reviewDocumentation with warnings triggers human review queue 1840 // ============================================================ 1841 1842 describe('ArchitectAgent Coverage2 - reviewDocumentation human review queue', () => { 1843 test('adds to human review queue when documentation has warnings', async () => { 1844 const dbPath = join(tmpdir(), `arch-cov2-rvdqueue-${Date.now()}.db`); 1845 const { db, agent, cleanup } = await createTestEnv(dbPath); 1846 try { 1847 // Create a file with issues 1848 const tmpFile = join(tmpdir(), `warning-doc-${Date.now()}.md`); 1849 writeFileSync(tmpFile, 'No header - just content with TODO: fix this'); 1850 1851 const taskId = insertTask(db, 'review_documentation', { 1852 files: [tmpFile], 1853 }); 1854 const task = getTask(db, taskId); 1855 1856 await agent.reviewDocumentation(task); 1857 1858 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1859 assert.strictEqual(updated.status, 'completed', 'Should complete'); 1860 const result = JSON.parse(updated.result_json || '{}'); 1861 // verifyDocumentation uses mock readFile, so result depends on mock content 1862 assert.ok( 1863 result.verified !== undefined || result.warnings !== undefined, 1864 'Should have result data' 1865 ); 1866 1867 try { 1868 rmSync(tmpFile, { force: true }); 1869 } catch (_e) { 1870 /* ignore */ 1871 } 1872 } finally { 1873 cleanup(); 1874 } 1875 }); 1876 1877 test('uses default doc files when no files specified in context', async () => { 1878 const dbPath = join(tmpdir(), `arch-cov2-rvddefaults-${Date.now()}.db`); 1879 const { db, agent, cleanup } = await createTestEnv(dbPath); 1880 try { 1881 const taskId = insertTask(db, 'review_documentation', { 1882 // No files - should use defaults: ['README.md', 'CLAUDE.md', 'docs/06-automation/agent-system.md'] 1883 }); 1884 const task = getTask(db, taskId); 1885 1886 await agent.reviewDocumentation(task); 1887 1888 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 1889 assert.strictEqual(updated.status, 'completed', 'Should complete with default files'); 1890 } finally { 1891 cleanup(); 1892 } 1893 }); 1894 }); 1895 1896 // ============================================================ 1897 // 19. classifyPerformanceIssue: all boundary conditions 1898 // ============================================================ 1899 1900 describe('ArchitectAgent Coverage2 - classifyPerformanceIssue boundaries', () => { 1901 test('exactly at 5x threshold returns critical', async () => { 1902 const dbPath = join(tmpdir(), `arch-cov2-cpi5x-${Date.now()}.db`); 1903 const { agent, cleanup } = await createTestEnv(dbPath); 1904 try { 1905 // 5x exactly: 300000 / 60000 = 5.0 → critical 1906 assert.strictEqual(agent.classifyPerformanceIssue(300000, 60000), 'critical'); 1907 } finally { 1908 cleanup(); 1909 } 1910 }); 1911 1912 test('just below 5x returns high', async () => { 1913 const dbPath = join(tmpdir(), `arch-cov2-cpibelow5x-${Date.now()}.db`); 1914 const { agent, cleanup } = await createTestEnv(dbPath); 1915 try { 1916 // 4.9x: 294000 / 60000 = 4.9 → high (not critical) 1917 assert.strictEqual(agent.classifyPerformanceIssue(294000, 60000), 'high'); 1918 } finally { 1919 cleanup(); 1920 } 1921 }); 1922 1923 test('exactly at 3x threshold returns high', async () => { 1924 const dbPath = join(tmpdir(), `arch-cov2-cpi3x-${Date.now()}.db`); 1925 const { agent, cleanup } = await createTestEnv(dbPath); 1926 try { 1927 // 3x exactly: 180000 / 60000 = 3.0 → high 1928 assert.strictEqual(agent.classifyPerformanceIssue(180000, 60000), 'high'); 1929 } finally { 1930 cleanup(); 1931 } 1932 }); 1933 1934 test('exactly at 2x threshold returns medium', async () => { 1935 const dbPath = join(tmpdir(), `arch-cov2-cpi2x-${Date.now()}.db`); 1936 const { agent, cleanup } = await createTestEnv(dbPath); 1937 try { 1938 // 2x exactly: 120000 / 60000 = 2.0 → medium 1939 assert.strictEqual(agent.classifyPerformanceIssue(120000, 60000), 'medium'); 1940 } finally { 1941 cleanup(); 1942 } 1943 }); 1944 1945 test('just below 2x returns low', async () => { 1946 const dbPath = join(tmpdir(), `arch-cov2-cpilow-${Date.now()}.db`); 1947 const { agent, cleanup } = await createTestEnv(dbPath); 1948 try { 1949 // 1.9x: 114000 / 60000 = 1.9 → low 1950 assert.strictEqual(agent.classifyPerformanceIssue(114000, 60000), 'low'); 1951 } finally { 1952 cleanup(); 1953 } 1954 }); 1955 }); 1956 1957 // ============================================================ 1958 // 20. getJsFiles: direct test 1959 // ============================================================ 1960 1961 describe('ArchitectAgent Coverage2 - getJsFiles', () => { 1962 test('returns array of JS files from src/', async () => { 1963 const dbPath = join(tmpdir(), `arch-cov2-getjs-${Date.now()}.db`); 1964 const { agent, cleanup } = await createTestEnv(dbPath); 1965 try { 1966 const files = await agent.getJsFiles(); 1967 assert.ok(Array.isArray(files), 'Should return array'); 1968 // In the 333Method project, src/ has many .js files 1969 if (files.length > 0) { 1970 assert.ok( 1971 files.every(f => f.endsWith('.js')), 1972 'All files should end with .js' 1973 ); 1974 assert.ok( 1975 files.every(f => f.startsWith('src/')), 1976 'All files should be in src/' 1977 ); 1978 } 1979 } finally { 1980 cleanup(); 1981 } 1982 }); 1983 }); 1984 1985 // ============================================================ 1986 // 21. suggestRefactor: all complexity_issues patterns 1987 // ============================================================ 1988 1989 describe('ArchitectAgent Coverage2 - suggestRefactor all patterns', () => { 1990 test('detects all three issue types in one call', async () => { 1991 const dbPath = join(tmpdir(), `arch-cov2-srall-${Date.now()}.db`); 1992 const { db, agent, cleanup } = await createTestEnv(dbPath); 1993 try { 1994 const taskId = insertTask(db, 'suggest_refactor', { 1995 file: 'src/complex-module.js', 1996 complexity_issues: [ 1997 'deeply nested callbacks found', 1998 'too many parameter arguments', 1999 'high cyclomatic complexity detected', 2000 ], 2001 }); 2002 const task = getTask(db, taskId); 2003 2004 await agent.suggestRefactor(task); 2005 2006 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 2007 assert.strictEqual(updated.status, 'completed'); 2008 const result = JSON.parse(updated.result_json || '{}'); 2009 assert.ok(Array.isArray(result.suggestions), 'Should have suggestions'); 2010 assert.ok( 2011 result.suggestions.length >= 3, 2012 'Should have at least 3 suggestions for 3 issue types' 2013 ); 2014 2015 // Verify all suggestion types are present 2016 const types = result.suggestions.map(s => s.type); 2017 assert.ok( 2018 types.includes('extract_function'), 2019 'Should have extract_function suggestion for nested' 2020 ); 2021 assert.ok( 2022 types.includes('configuration_object'), 2023 'Should have configuration_object for parameters' 2024 ); 2025 assert.ok( 2026 types.includes('simplify_conditionals'), 2027 'Should have simplify_conditionals for cyclomatic complexity' 2028 ); 2029 } finally { 2030 cleanup(); 2031 } 2032 }); 2033 2034 test('handles null/undefined complexity_issues gracefully', async () => { 2035 const dbPath = join(tmpdir(), `arch-cov2-srnull-${Date.now()}.db`); 2036 const { db, agent, cleanup } = await createTestEnv(dbPath); 2037 try { 2038 const taskId = insertTask(db, 'suggest_refactor', { 2039 file: 'src/module.js', 2040 // No complexity_issues field 2041 }); 2042 const task = getTask(db, taskId); 2043 2044 await agent.suggestRefactor(task); 2045 2046 const updated = db.prepare('SELECT * FROM agent_tasks WHERE id = ?').get(taskId); 2047 assert.strictEqual(updated.status, 'completed', 'Should complete with no complexity issues'); 2048 const result = JSON.parse(updated.result_json || '{}'); 2049 assert.strictEqual(result.note, 'No refactoring needed', 'Should note no refactoring needed'); 2050 } finally { 2051 cleanup(); 2052 } 2053 }); 2054 }); 2055 2056 // ============================================================ 2057 // 22. checkErrorDetectionWorkflow: various states 2058 // ============================================================ 2059 2060 describe('ArchitectAgent Coverage2 - checkErrorDetectionWorkflow states', () => { 2061 test('returns true when scan_logs task has running status', async () => { 2062 const dbPath = join(tmpdir(), `arch-cov2-cedwrunning-${Date.now()}.db`); 2063 const { db, agent, cleanup } = await createTestEnv(dbPath); 2064 try { 2065 // Insert a running scan_logs task 2066 db.prepare(`INSERT INTO agent_tasks (task_type, assigned_to, status) VALUES (?, ?, ?)`).run( 2067 'scan_logs', 2068 'monitor', 2069 'running' 2070 ); 2071 2072 const result = await agent.checkErrorDetectionWorkflow(); 2073 assert.strictEqual(result, true, 'Should return true when scan_logs task is running'); 2074 } finally { 2075 cleanup(); 2076 } 2077 }); 2078 2079 test('handles database errors gracefully', async () => { 2080 const dbPath = join(tmpdir(), `arch-cov2-cedwdberror-${Date.now()}.db`); 2081 const { agent, cleanup } = await createTestEnv(dbPath); 2082 try { 2083 // Point to invalid DATABASE_PATH to trigger error 2084 process.env.DATABASE_PATH = '/invalid/path/that/does/not/exist.db'; 2085 const result = await agent.checkErrorDetectionWorkflow(); 2086 // Should return false on error (not throw) 2087 assert.strictEqual(result, false, 'Should return false on database error'); 2088 } finally { 2089 // Restore the original db path 2090 process.env.DATABASE_PATH = dbPath; 2091 cleanup(); 2092 } 2093 }); 2094 }); 2095 2096 // ============================================================ 2097 // 23. summarizeChanges: many files truncation 2098 // ============================================================ 2099 2100 describe('ArchitectAgent Coverage2 - summarizeChanges file limits', () => { 2101 test('processes up to 10 files maximum', async () => { 2102 const dbPath = join(tmpdir(), `arch-cov2-sclimit-${Date.now()}.db`); 2103 const { agent, cleanup } = await createTestEnv(dbPath); 2104 try { 2105 // Provide 12 files - should only process 10 2106 const manyFiles = [ 2107 'src/score.js', 2108 'src/scrape.js', 2109 'src/capture.js', 2110 'src/enrich.js', 2111 'src/proposals.js', 2112 'src/outreach.js', 2113 'src/inbound.js', 2114 'src/keywords.js', 2115 'src/pipeline.js', 2116 'src/utils.js', 2117 'src/extra1.js', // 11th 2118 'src/extra2.js', // 12th 2119 ]; 2120 const summary = await agent.summarizeChanges(manyFiles); 2121 assert.ok(typeof summary === 'string', 'Should return string'); 2122 assert.ok(summary.length > 0, 'Summary should not be empty'); 2123 } finally { 2124 cleanup(); 2125 } 2126 }); 2127 });