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:
john
2026-09-02 14:52:45 +08:00
parent eb0c697c4e
commit 1d1af5c657
2 changed files with 132 additions and 0 deletions
+1
View File
File diff suppressed because one or more lines are too long
+131
View File
@@ -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);
});