Add local script to verify Experience reflect aggregation end-to-end.
Seeds task outcomes, runs reflect(), and checks execution_pattern rows plus experience_saved product events for local acceptance. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,131 @@
|
||||
#!/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);
|
||||
});
|
||||
Reference in New Issue
Block a user