#!/usr/bin/env node /** * Local acceptance: seed task_outcome rows, run reflect(), verify execution_pattern. * * Usage: * node scripts/verify-experience-reflect-local.mjs * node scripts/verify-experience-reflect-local.mjs --seed-only * node scripts/verify-experience-reflect-local.mjs --reflect-only */ import { loadH5Environment } from './load-env.mjs'; import { createDbPool } from '../db.mjs'; import { createExperienceService } from '../experience-service.mjs'; const PROBLEM = 'Experience reflect local acceptance probe'; function parseArgs(argv) { return { seedOnly: argv.includes('--seed-only'), reflectOnly: argv.includes('--reflect-only'), }; } async function countRows(pool, { kind = null, status = 'active' } = {}) { const clauses = ['status = ?']; const params = [status]; if (kind) { clauses.push('kind = ?'); params.push(kind); } const [rows] = await pool.query( `SELECT COUNT(*) AS count FROM h5_experience WHERE ${clauses.join(' AND ')}`, params, ); return Number(rows[0]?.count ?? 0); } async function seedTaskOutcomes(service, count = 3) { const created = []; for (let i = 0; i < count; i += 1) { created.push(await service.record({ kind: 'task_outcome', title: `Reflect probe ${i + 1}`, body: `Local acceptance body ${i + 1}`, problem: PROBLEM, result: 'success', confidence: 0.75 + i * 0.05, environment: { runtime: 'local-dev', components: ['portal', 'goose'] }, action: { executor: 'goose', steps: [`step-${i + 1}`] }, tags: ['reflect-acceptance'], })); } return created; } async function main() { loadH5Environment(import.meta.dirname); const options = parseArgs(process.argv.slice(2)); const pool = createDbPool(); const service = createExperienceService(pool, { productEventsPool: pool }); try { const beforePatterns = await countRows(pool, { kind: 'execution_pattern' }); const beforeActiveOutcomes = await countRows(pool, { kind: 'task_outcome', status: 'active' }); if (!options.reflectOnly) { const seeded = await seedTaskOutcomes(service, 3); console.log(`[reflect-acceptance] seeded ${seeded.length} task_outcome rows`); } if (!options.seedOnly) { const result = await service.reflect({ minGroupSize: 3 }); console.log('[reflect-acceptance] reflect result:', JSON.stringify({ ok: result.ok, created: result.created, archived: result.archived, scope: result.scope, })); if (!result.created) { console.log('[reflect-acceptance] note: no new pattern (group may already exist or below threshold)'); } } const afterPatterns = await countRows(pool, { kind: 'execution_pattern' }); const afterArchivedOutcomes = await countRows(pool, { kind: 'task_outcome', status: 'archived' }); const [patterns] = await pool.query( `SELECT id, title, kind, result, confidence, created_at FROM h5_experience WHERE kind = 'execution_pattern' AND problem = ? ORDER BY created_at DESC LIMIT 3`, [PROBLEM], ); console.log('[reflect-acceptance] summary:', JSON.stringify({ executionPatternsBefore: beforePatterns, executionPatternsAfter: afterPatterns, activeTaskOutcomesBefore: beforeActiveOutcomes, archivedTaskOutcomesAfter: afterArchivedOutcomes, latestPatterns: patterns, }, null, 2)); const [events] = await pool.query( `SELECT event_type, data_json, created_at FROM h5_memory_v2_product_events WHERE event_type IN ('experience_saved', 'experience_recalled') ORDER BY created_at DESC LIMIT 5`, ).catch(() => [[]]); if (events.length > 0) { console.log('[reflect-acceptance] recent product events:', events); } else { console.log('[reflect-acceptance] product events table empty or unavailable (ok on fresh DB)'); } const ok = afterPatterns >= beforePatterns && patterns.length > 0; if (!ok && !options.seedOnly) { process.exitCode = 1; console.error('[reflect-acceptance] FAILED: expected at least one execution_pattern for probe problem'); } else { console.log('[reflect-acceptance] OK'); } } finally { await pool.end(); } } main().catch((error) => { console.error('[reflect-acceptance] error:', error instanceof Error ? error.message : error); process.exit(1); });