@blockspool/mcp 0.4.3 → 0.5.1

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,427 @@
1
+ /**
2
+ * Pre-Scout Codebase Index — lightweight structural map built at session start.
3
+ *
4
+ * Walks directories 2 levels deep using `fs` only. No AST parsing, no heavy deps.
5
+ * Provides module map, dependency edges, test gaps, complexity hotspots, and entrypoints.
6
+ */
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ // ---------------------------------------------------------------------------
10
+ // Source file extensions
11
+ // ---------------------------------------------------------------------------
12
+ const SOURCE_EXTENSIONS = new Set([
13
+ '.ts', '.js', '.py', '.rs', '.go', '.rb', '.java', '.cs', '.ex', '.php', '.swift',
14
+ ]);
15
+ // ---------------------------------------------------------------------------
16
+ // Purpose inference from directory name
17
+ // ---------------------------------------------------------------------------
18
+ const PURPOSE_MAP = {
19
+ api: 'api',
20
+ apis: 'api',
21
+ routes: 'api',
22
+ handlers: 'api',
23
+ controllers: 'api',
24
+ endpoints: 'api',
25
+ services: 'services',
26
+ service: 'services',
27
+ lib: 'services',
28
+ core: 'services',
29
+ test: 'tests',
30
+ tests: 'tests',
31
+ __tests__: 'tests',
32
+ spec: 'tests',
33
+ specs: 'tests',
34
+ ui: 'ui',
35
+ components: 'ui',
36
+ views: 'ui',
37
+ pages: 'ui',
38
+ screens: 'ui',
39
+ utils: 'utils',
40
+ util: 'utils',
41
+ helpers: 'utils',
42
+ shared: 'utils',
43
+ common: 'utils',
44
+ config: 'config',
45
+ configs: 'config',
46
+ configuration: 'config',
47
+ settings: 'config',
48
+ };
49
+ function inferPurpose(dirName) {
50
+ const lower = dirName.toLowerCase();
51
+ return PURPOSE_MAP[lower] ?? 'unknown';
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Import regex patterns
55
+ // ---------------------------------------------------------------------------
56
+ // JS/TS: import ... from '...' or require('...')
57
+ const JS_IMPORT_RE = /(?:import\s+.*?\s+from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g;
58
+ // Python: from X import ... or import X
59
+ const PY_IMPORT_RE = /(?:from\s+([\w.]+)\s+import|^import\s+([\w.]+))/gm;
60
+ // Go: import "..."
61
+ const GO_IMPORT_RE = /import\s+"([^"]+)"/g;
62
+ function extractImports(content, filePath) {
63
+ const ext = path.extname(filePath);
64
+ const imports = [];
65
+ if (ext === '.ts' || ext === '.js') {
66
+ for (const m of content.matchAll(JS_IMPORT_RE)) {
67
+ const spec = m[1] ?? m[2];
68
+ if (spec)
69
+ imports.push(spec);
70
+ }
71
+ }
72
+ else if (ext === '.py') {
73
+ for (const m of content.matchAll(PY_IMPORT_RE)) {
74
+ const spec = m[1] ?? m[2];
75
+ if (spec)
76
+ imports.push(spec);
77
+ }
78
+ }
79
+ else if (ext === '.go') {
80
+ for (const m of content.matchAll(GO_IMPORT_RE)) {
81
+ if (m[1])
82
+ imports.push(m[1]);
83
+ }
84
+ }
85
+ return imports;
86
+ }
87
+ /**
88
+ * Resolve a relative import specifier to a module path relative to projectRoot.
89
+ * Returns null for non-relative (package) imports.
90
+ */
91
+ function resolveImportToModule(specifier, sourceFile, projectRoot, modulePaths) {
92
+ // Only resolve relative imports
93
+ if (!specifier.startsWith('.'))
94
+ return null;
95
+ const sourceDir = path.dirname(sourceFile);
96
+ const resolved = path.resolve(sourceDir, specifier);
97
+ const relative = path.relative(projectRoot, resolved);
98
+ // Find which module this resolved path falls under
99
+ for (const mod of modulePaths) {
100
+ if (relative === mod || relative.startsWith(mod + '/') || relative.startsWith(mod + path.sep)) {
101
+ return mod;
102
+ }
103
+ }
104
+ return null;
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // buildCodebaseIndex
108
+ // ---------------------------------------------------------------------------
109
+ export function buildCodebaseIndex(projectRoot, excludeDirs = []) {
110
+ const excludeSet = new Set(excludeDirs.map(d => d.toLowerCase()));
111
+ // Step 1: Module map — walk dirs 2 levels deep
112
+ const modules = [];
113
+ const sourceFilesByModule = new Map();
114
+ function shouldExclude(name) {
115
+ return excludeSet.has(name.toLowerCase()) || name.startsWith('.');
116
+ }
117
+ function walkForModules(dir, depth) {
118
+ if (modules.length >= 50)
119
+ return;
120
+ let entries;
121
+ try {
122
+ entries = fs.readdirSync(dir, { withFileTypes: true });
123
+ }
124
+ catch {
125
+ return;
126
+ }
127
+ const sourceFiles = [];
128
+ const subdirs = [];
129
+ for (const entry of entries) {
130
+ if (entry.isFile() && SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
131
+ sourceFiles.push(path.join(dir, entry.name));
132
+ }
133
+ else if (entry.isDirectory() && !shouldExclude(entry.name)) {
134
+ subdirs.push(entry);
135
+ }
136
+ }
137
+ // Register this dir as a module if it has source files
138
+ if (sourceFiles.length > 0 && depth > 0) {
139
+ const relPath = path.relative(projectRoot, dir);
140
+ if (relPath && modules.length < 50) {
141
+ modules.push({
142
+ path: relPath,
143
+ file_count: sourceFiles.length,
144
+ purpose: inferPurpose(path.basename(dir)),
145
+ });
146
+ sourceFilesByModule.set(relPath, sourceFiles);
147
+ }
148
+ }
149
+ // Recurse into subdirs (up to depth 2)
150
+ if (depth < 2) {
151
+ for (const sub of subdirs) {
152
+ if (modules.length >= 50)
153
+ break;
154
+ walkForModules(path.join(dir, sub.name), depth + 1);
155
+ }
156
+ }
157
+ }
158
+ walkForModules(projectRoot, 0);
159
+ // Also count source files at root level for modules registered at depth > 0
160
+ // (root files don't form a module — they become entrypoints)
161
+ const modulePaths = modules.map(m => m.path);
162
+ // Step 2: Import scanning — build dependency_edges + record file mtimes
163
+ const dependencyEdges = {};
164
+ const sampledFileMtimes = {};
165
+ for (const mod of modules) {
166
+ const files = sourceFilesByModule.get(mod.path) ?? [];
167
+ const deps = new Set();
168
+ // Read first 50 lines of up to 5 files
169
+ const filesToScan = files.slice(0, 5);
170
+ for (const filePath of filesToScan) {
171
+ try {
172
+ // Record mtime for change detection
173
+ const relFile = path.relative(projectRoot, filePath);
174
+ sampledFileMtimes[relFile] = fs.statSync(filePath).mtimeMs;
175
+ const fd = fs.openSync(filePath, 'r');
176
+ const buf = Buffer.alloc(4096); // ~50 lines worth
177
+ const bytesRead = fs.readSync(fd, buf, 0, 4096, 0);
178
+ fs.closeSync(fd);
179
+ const content = buf.toString('utf8', 0, bytesRead);
180
+ // Trim to ~50 lines
181
+ const lines = content.split('\n').slice(0, 50).join('\n');
182
+ const imports = extractImports(lines, filePath);
183
+ for (const spec of imports) {
184
+ const resolved = resolveImportToModule(spec, filePath, projectRoot, modulePaths);
185
+ if (resolved && resolved !== mod.path) {
186
+ deps.add(resolved);
187
+ }
188
+ }
189
+ }
190
+ catch {
191
+ // skip unreadable files
192
+ }
193
+ }
194
+ if (deps.size > 0) {
195
+ dependencyEdges[mod.path] = [...deps];
196
+ }
197
+ }
198
+ // Step 3: Test coverage — find untested modules
199
+ const untestedModules = [];
200
+ for (const mod of modules) {
201
+ if (mod.purpose === 'tests')
202
+ continue;
203
+ const modAbsPath = path.join(projectRoot, mod.path);
204
+ const modParent = path.dirname(modAbsPath);
205
+ const modName = path.basename(modAbsPath);
206
+ let hasTesting = false;
207
+ // Check for __tests__/ sibling
208
+ if (existsDir(path.join(modAbsPath, '__tests__'))) {
209
+ hasTesting = true;
210
+ }
211
+ // Check for parallel test/ or tests/ dir at same level
212
+ if (!hasTesting && (existsDir(path.join(modParent, 'test')) || existsDir(path.join(modParent, 'tests')))) {
213
+ hasTesting = true;
214
+ }
215
+ // Check for *.test.* or *.spec.* files within the module
216
+ if (!hasTesting) {
217
+ const files = sourceFilesByModule.get(mod.path) ?? [];
218
+ hasTesting = files.some(f => {
219
+ const base = path.basename(f);
220
+ return base.includes('.test.') || base.includes('.spec.');
221
+ });
222
+ }
223
+ if (!hasTesting) {
224
+ untestedModules.push(mod.path);
225
+ }
226
+ }
227
+ // Step 4: Large files — stat.size / 40 heuristic for LOC
228
+ const largeFiles = [];
229
+ for (const mod of modules) {
230
+ const files = sourceFilesByModule.get(mod.path) ?? [];
231
+ for (const filePath of files) {
232
+ if (largeFiles.length >= 20)
233
+ break;
234
+ try {
235
+ const stat = fs.statSync(filePath);
236
+ const estimatedLines = Math.round(stat.size / 40);
237
+ if (estimatedLines > 300) {
238
+ largeFiles.push({
239
+ path: path.relative(projectRoot, filePath),
240
+ lines: estimatedLines,
241
+ });
242
+ }
243
+ }
244
+ catch {
245
+ // skip
246
+ }
247
+ }
248
+ if (largeFiles.length >= 20)
249
+ break;
250
+ }
251
+ // Step 5: Entrypoints
252
+ const entrypoints = [];
253
+ const entrypointNames = [
254
+ 'index.ts', 'index.js', 'main.ts', 'main.js',
255
+ 'app.ts', 'app.js', 'server.ts', 'server.js',
256
+ 'main.py', 'app.py', 'main.go', 'main.rs',
257
+ 'index.php', 'main.swift', 'main.rb', 'main.ex',
258
+ ];
259
+ const searchDirs = [projectRoot, path.join(projectRoot, 'src')];
260
+ for (const dir of searchDirs) {
261
+ for (const name of entrypointNames) {
262
+ if (entrypoints.length >= 10)
263
+ break;
264
+ const full = path.join(dir, name);
265
+ try {
266
+ if (fs.statSync(full).isFile()) {
267
+ entrypoints.push(path.relative(projectRoot, full));
268
+ }
269
+ }
270
+ catch {
271
+ // doesn't exist
272
+ }
273
+ }
274
+ }
275
+ return {
276
+ built_at: new Date().toISOString(),
277
+ modules,
278
+ dependency_edges: dependencyEdges,
279
+ untested_modules: untestedModules,
280
+ large_files: largeFiles,
281
+ entrypoints,
282
+ sampled_file_mtimes: sampledFileMtimes,
283
+ };
284
+ }
285
+ // ---------------------------------------------------------------------------
286
+ // refreshCodebaseIndex — incremental reindex, only re-scans changed modules
287
+ // ---------------------------------------------------------------------------
288
+ /**
289
+ * Incrementally refresh the codebase index. Re-walks the directory tree (cheap)
290
+ * but only re-scans imports for modules whose file count changed or that are new.
291
+ * Unchanged modules keep their existing dependency edges.
292
+ */
293
+ export function refreshCodebaseIndex(existing, projectRoot, excludeDirs = []) {
294
+ const fresh = buildCodebaseIndex(projectRoot, excludeDirs);
295
+ // Build lookup of old modules by path
296
+ const oldByPath = new Map(existing.modules.map(m => [m.path, m]));
297
+ // For unchanged modules (same path + same file_count), keep old dependency edges
298
+ // to avoid re-reading file contents. New/changed modules get fresh edges.
299
+ const mergedEdges = {};
300
+ for (const mod of fresh.modules) {
301
+ const old = oldByPath.get(mod.path);
302
+ if (old && old.file_count === mod.file_count) {
303
+ // Module unchanged — reuse existing edges
304
+ const oldEdges = existing.dependency_edges[mod.path];
305
+ if (oldEdges) {
306
+ mergedEdges[mod.path] = oldEdges;
307
+ }
308
+ }
309
+ else {
310
+ // New or changed module — use freshly scanned edges
311
+ const freshEdges = fresh.dependency_edges[mod.path];
312
+ if (freshEdges) {
313
+ mergedEdges[mod.path] = freshEdges;
314
+ }
315
+ }
316
+ }
317
+ fresh.dependency_edges = mergedEdges;
318
+ return fresh;
319
+ }
320
+ // ---------------------------------------------------------------------------
321
+ // hasStructuralChanges — cheap mtime check to detect external modifications
322
+ // ---------------------------------------------------------------------------
323
+ /**
324
+ * Check if the codebase structure has changed since the last index build.
325
+ *
326
+ * Two-layer detection:
327
+ * 1. Directory mtimes — catches file additions/deletions, new dirs, git pulls, branch switches.
328
+ * 2. Sampled file mtimes — catches content edits to files we scanned for imports (dependency edges).
329
+ *
330
+ * Cost: ≤60 dir statSync + ≤250 file statSync. Zero file reads, zero tokens.
331
+ */
332
+ export function hasStructuralChanges(index, projectRoot) {
333
+ const builtAt = new Date(index.built_at).getTime();
334
+ // Layer 1: Check directory mtimes (structural changes — add/delete/rename)
335
+ const dirsToCheck = new Set();
336
+ for (const mod of index.modules) {
337
+ const absPath = path.join(projectRoot, mod.path);
338
+ dirsToCheck.add(absPath);
339
+ // Also check parent dir (catches new sibling modules)
340
+ dirsToCheck.add(path.dirname(absPath));
341
+ }
342
+ // Always check root and src/ for new top-level dirs
343
+ dirsToCheck.add(projectRoot);
344
+ dirsToCheck.add(path.join(projectRoot, 'src'));
345
+ for (const dir of dirsToCheck) {
346
+ try {
347
+ if (fs.statSync(dir).mtimeMs > builtAt) {
348
+ return true;
349
+ }
350
+ }
351
+ catch {
352
+ // Dir was removed — structural change if it was a known module
353
+ const rel = path.relative(projectRoot, dir);
354
+ if (index.modules.some(m => m.path === rel)) {
355
+ return true;
356
+ }
357
+ }
358
+ }
359
+ // Layer 2: Check sampled file mtimes (content edits that may change imports)
360
+ for (const [relFile, oldMtime] of Object.entries(index.sampled_file_mtimes)) {
361
+ try {
362
+ const currentMtime = fs.statSync(path.join(projectRoot, relFile)).mtimeMs;
363
+ if (currentMtime !== oldMtime) {
364
+ return true;
365
+ }
366
+ }
367
+ catch {
368
+ // File was deleted — structural change
369
+ return true;
370
+ }
371
+ }
372
+ return false;
373
+ }
374
+ function existsDir(p) {
375
+ try {
376
+ return fs.statSync(p).isDirectory();
377
+ }
378
+ catch {
379
+ return false;
380
+ }
381
+ }
382
+ // ---------------------------------------------------------------------------
383
+ // formatIndexForPrompt — chunked rendering
384
+ // ---------------------------------------------------------------------------
385
+ const CHUNK_SIZE = 15;
386
+ export function formatIndexForPrompt(index, scoutCycle) {
387
+ const { modules, dependency_edges, untested_modules, large_files, entrypoints } = index;
388
+ if (modules.length === 0) {
389
+ return '## Codebase Structure\n\nNo modules detected.';
390
+ }
391
+ const totalChunks = Math.max(1, Math.ceil(modules.length / CHUNK_SIZE));
392
+ const chunkIndex = scoutCycle % totalChunks;
393
+ const offset = chunkIndex * CHUNK_SIZE;
394
+ const focusModules = modules.slice(offset, offset + CHUNK_SIZE);
395
+ const otherModules = modules.filter((_, i) => i < offset || i >= offset + CHUNK_SIZE);
396
+ const parts = [];
397
+ parts.push(`## Codebase Structure (chunk ${chunkIndex + 1}/${totalChunks})`);
398
+ parts.push('');
399
+ parts.push('### Modules in Focus This Cycle');
400
+ for (const mod of focusModules) {
401
+ const deps = dependency_edges[mod.path];
402
+ const depStr = deps ? ` → imports: ${deps.join(', ')}` : '';
403
+ parts.push(`${mod.path}/ — ${mod.file_count} files (${mod.purpose})${depStr}`);
404
+ }
405
+ if (otherModules.length > 0) {
406
+ parts.push('');
407
+ parts.push('### Other Modules (not in focus — available for future cycles)');
408
+ parts.push(otherModules.map(m => m.path + '/').join(', '));
409
+ }
410
+ if (untested_modules.length > 0) {
411
+ parts.push('');
412
+ parts.push('### Untested Modules (high-value scout targets)');
413
+ parts.push(untested_modules.map(m => m + '/').join(', '));
414
+ }
415
+ if (large_files.length > 0) {
416
+ parts.push('');
417
+ parts.push('### Complexity Hotspots (>300 LOC)');
418
+ parts.push(large_files.map(f => `${f.path} (${f.lines})`).join(', '));
419
+ }
420
+ if (entrypoints.length > 0) {
421
+ parts.push('');
422
+ parts.push('### Entrypoints');
423
+ parts.push(entrypoints.join(', '));
424
+ }
425
+ return parts.join('\n');
426
+ }
427
+ //# sourceMappingURL=codebase-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codebase-index.js","sourceRoot":"","sources":["../src/codebase-index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"event-processor.d.ts","sourceRoot":"","sources":["../src/event-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAMxD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,eAAe,EACnB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,aAAa,CAAC,CAgcxB"}
1
+ {"version":3,"file":"event-processor.d.ts","sourceRoot":"","sources":["../src/event-processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAOxD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,YAAY,CAChC,GAAG,EAAE,UAAU,EACf,EAAE,EAAE,eAAe,EACnB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,aAAa,CAAC,CAmjBxB"}
@@ -8,6 +8,8 @@ import { repos } from '@blockspool/core';
8
8
  import { filterAndCreateTickets } from './proposals.js';
9
9
  import { deriveScopePolicy, validatePlanScope } from './scope-policy.js';
10
10
  import { recordDiff, recordCommandFailure, recordPlanHash } from './spindle.js';
11
+ import { addLearning, confirmLearning, extractTags } from './learnings.js';
12
+ const MAX_SCOUT_RETRIES = 2;
11
13
  export async function processEvent(run, db, type, payload) {
12
14
  const s = run.require();
13
15
  switch (type) {
@@ -28,8 +30,13 @@ export async function processEvent(run, db, type, payload) {
28
30
  }
29
31
  }
30
32
  // Extract proposals from payload
31
- const MAX_SCOUT_RETRIES = 2;
32
33
  const rawProposals = (payload['proposals'] ?? []);
34
+ // Build exploration log entry (before empty-check so retries also get logged)
35
+ const explorationSummary = (payload['exploration_summary'] ?? '');
36
+ const logEntry = explorationSummary
37
+ ? `Attempt ${s.scout_retries + 1}: Explored ${exploredDirs.join(', ') || '(unknown)'}. Found ${rawProposals.length} proposals. ${explorationSummary}`
38
+ : `Attempt ${s.scout_retries + 1}: Explored ${exploredDirs.join(', ') || '(unknown)'}. Found ${rawProposals.length} proposals.`;
39
+ s.scout_exploration_log.push(logEntry);
33
40
  if (rawProposals.length === 0) {
34
41
  if (s.scout_retries < MAX_SCOUT_RETRIES) {
35
42
  s.scout_retries++;
@@ -49,10 +56,64 @@ export async function processEvent(run, db, type, payload) {
49
56
  message: 'No proposals in scout output after all retries, transitioning to DONE',
50
57
  };
51
58
  }
52
- // Filter, dedup, score, create tickets
53
- const result = await filterAndCreateTickets(run, db, rawProposals);
59
+ // Store proposals as pending for adversarial review (instead of creating tickets immediately)
60
+ s.pending_proposals = rawProposals;
54
61
  // Save proposals artifact
55
- run.saveArtifact(`${s.step_count}-scout-proposals.json`, JSON.stringify({ raw: rawProposals, result }, null, 2));
62
+ run.saveArtifact(`${s.step_count}-scout-proposals.json`, JSON.stringify({ raw: rawProposals, pending_review: true }, null, 2));
63
+ return {
64
+ processed: true,
65
+ phase_changed: false,
66
+ message: `${rawProposals.length} proposals pending adversarial review`,
67
+ };
68
+ }
69
+ // -----------------------------------------------------------------
70
+ // Proposal review (adversarial critique pass)
71
+ // -----------------------------------------------------------------
72
+ case 'PROPOSALS_REVIEWED': {
73
+ if (s.phase !== 'SCOUT') {
74
+ return { processed: true, phase_changed: false, message: 'PROPOSALS_REVIEWED outside SCOUT phase, ignored' };
75
+ }
76
+ const pendingProposals = s.pending_proposals;
77
+ if (!pendingProposals || pendingProposals.length === 0) {
78
+ return { processed: true, phase_changed: false, message: 'No pending proposals to review' };
79
+ }
80
+ // Apply revised scores from review
81
+ const reviewedItems = (payload['reviewed_proposals'] ?? []);
82
+ // Merge reviewed scores back into pending proposals
83
+ for (const reviewed of reviewedItems) {
84
+ if (!reviewed.title)
85
+ continue;
86
+ const match = pendingProposals.find(p => p.title === reviewed.title);
87
+ if (match) {
88
+ // Record learning if confidence lowered >20 pts
89
+ if (s.learnings_enabled && typeof reviewed.confidence === 'number' && typeof match.confidence === 'number') {
90
+ const drop = match.confidence - reviewed.confidence;
91
+ if (drop > 20) {
92
+ addLearning(run.rootPath, {
93
+ text: `Proposal "${reviewed.title}" had inflated confidence (${match.confidence}→${reviewed.confidence})`,
94
+ category: 'warning',
95
+ source: { type: 'review_downgrade', detail: reviewed.review_note },
96
+ tags: extractTags(match.files ?? match.allowed_paths ?? [], []),
97
+ });
98
+ }
99
+ }
100
+ if (typeof reviewed.confidence === 'number')
101
+ match.confidence = reviewed.confidence;
102
+ if (typeof reviewed.impact_score === 'number')
103
+ match.impact_score = reviewed.impact_score;
104
+ }
105
+ }
106
+ // Clear pending
107
+ s.pending_proposals = null;
108
+ // Now filter and create tickets with revised scores
109
+ const result = await filterAndCreateTickets(run, db, pendingProposals);
110
+ // Update exploration log with rejection info
111
+ const lastIdx = s.scout_exploration_log.length - 1;
112
+ if (lastIdx >= 0) {
113
+ s.scout_exploration_log[lastIdx] += ` ${result.accepted.length} accepted, ${result.rejected.length} rejected (${result.rejected.map(r => r.reason).slice(0, 3).join('; ')}).`;
114
+ }
115
+ // Save reviewed artifact
116
+ run.saveArtifact(`${s.step_count}-scout-proposals-reviewed.json`, JSON.stringify({ reviewed: reviewedItems, result }, null, 2));
56
117
  if (result.created_ticket_ids.length > 0) {
57
118
  s.scout_retries = 0;
58
119
  run.setPhase('NEXT_TICKET');
@@ -60,16 +121,16 @@ export async function processEvent(run, db, type, payload) {
60
121
  processed: true,
61
122
  phase_changed: true,
62
123
  new_phase: 'NEXT_TICKET',
63
- message: `Created ${result.created_ticket_ids.length} tickets from ${rawProposals.length} proposals (${result.rejected.length} rejected)`,
124
+ message: `Created ${result.created_ticket_ids.length} tickets after review (${result.rejected.length} rejected)`,
64
125
  };
65
126
  }
66
- // All proposals rejected
127
+ // All proposals rejected after review
67
128
  if (s.scout_retries < MAX_SCOUT_RETRIES) {
68
129
  s.scout_retries++;
69
130
  return {
70
131
  processed: true,
71
132
  phase_changed: false,
72
- message: `All ${rawProposals.length} proposals rejected (attempt ${s.scout_retries}/${MAX_SCOUT_RETRIES + 1}). Retrying with deeper analysis.`,
133
+ message: `All proposals rejected after review (attempt ${s.scout_retries}/${MAX_SCOUT_RETRIES + 1}). Retrying.`,
73
134
  };
74
135
  }
75
136
  run.setPhase('DONE');
@@ -77,7 +138,7 @@ export async function processEvent(run, db, type, payload) {
77
138
  processed: true,
78
139
  phase_changed: true,
79
140
  new_phase: 'DONE',
80
- message: `All ${rawProposals.length} proposals rejected after all retries: ${result.rejected.map(r => r.reason).join('; ')}`,
141
+ message: `All proposals rejected after review and all retries: ${result.rejected.map(r => r.reason).join('; ')}`,
81
142
  };
82
143
  }
83
144
  case 'PROPOSALS_FILTERED': {
@@ -146,6 +207,15 @@ export async function processEvent(run, db, type, payload) {
146
207
  if (!scopeResult.valid) {
147
208
  s.plan_rejections++;
148
209
  run.appendEvent('PLAN_REJECTED', { reason: scopeResult.reason, attempt: s.plan_rejections });
210
+ // Record learning on plan rejection
211
+ if (s.learnings_enabled) {
212
+ addLearning(run.rootPath, {
213
+ text: `Plan rejected: ${scopeResult.reason}`.slice(0, 200),
214
+ category: 'gotcha',
215
+ source: { type: 'plan_rejection', detail: scopeResult.reason ?? undefined },
216
+ tags: extractTags(plan.files_to_touch.map(f => f.path), []),
217
+ });
218
+ }
149
219
  return {
150
220
  processed: true,
151
221
  phase_changed: false,
@@ -239,6 +309,17 @@ export async function processEvent(run, db, type, payload) {
239
309
  };
240
310
  }
241
311
  if (status === 'failed') {
312
+ // Record learning on ticket failure
313
+ if (s.learnings_enabled) {
314
+ const reason = payload['reason'] ?? 'Execution failed';
315
+ const ticket = s.current_ticket_id ? await repos.tickets.getById(db, s.current_ticket_id) : null;
316
+ addLearning(run.rootPath, {
317
+ text: `Ticket failed on ${ticket?.title ?? 'unknown'} — ${reason}`.slice(0, 200),
318
+ category: 'warning',
319
+ source: { type: 'ticket_failure', detail: reason },
320
+ tags: extractTags(ticket?.allowedPaths ?? [], ticket?.verificationCommands ?? []),
321
+ });
322
+ }
242
323
  // Fail the ticket, move to next
243
324
  if (s.current_ticket_id) {
244
325
  await repos.tickets.updateStatus(db, s.current_ticket_id, 'blocked');
@@ -281,6 +362,13 @@ export async function processEvent(run, db, type, payload) {
281
362
  if (s.phase !== 'QA') {
282
363
  return { processed: true, phase_changed: false, message: 'QA passed outside QA phase' };
283
364
  }
365
+ // Confirm injected learnings on success
366
+ if (s.learnings_enabled && s.injected_learning_ids.length > 0) {
367
+ for (const id of s.injected_learning_ids) {
368
+ confirmLearning(run.rootPath, id);
369
+ }
370
+ s.injected_learning_ids = [];
371
+ }
284
372
  // Mark ticket done in DB
285
373
  if (s.current_ticket_id) {
286
374
  await repos.tickets.updateStatus(db, s.current_ticket_id, 'done');
@@ -315,6 +403,18 @@ export async function processEvent(run, db, type, payload) {
315
403
  }, null, 2));
316
404
  s.qa_retries++;
317
405
  if (s.qa_retries >= 3) {
406
+ // Record learning on final QA failure
407
+ if (s.learnings_enabled) {
408
+ const failedCmds = (payload['failed_commands'] ?? payload['command'] ?? '');
409
+ const errorSummary = (payload['error'] ?? payload['output'] ?? '').slice(0, 100);
410
+ const ticket = s.current_ticket_id ? await repos.tickets.getById(db, s.current_ticket_id) : null;
411
+ addLearning(run.rootPath, {
412
+ text: `QA fails on ${ticket?.title ?? 'unknown'} — ${errorSummary || failedCmds}`.slice(0, 200),
413
+ category: 'gotcha',
414
+ source: { type: 'qa_failure', detail: failedCmds },
415
+ tags: extractTags(ticket?.allowedPaths ?? [], ticket?.verificationCommands ?? []),
416
+ });
417
+ }
318
418
  // Give up on this ticket
319
419
  if (s.current_ticket_id) {
320
420
  await repos.tickets.updateStatus(db, s.current_ticket_id, 'blocked');