feat(ai-psychosis): /interaction-report adds pushback metrics + reader script
This commit is contained in:
parent
b798e68e93
commit
3041c90115
3 changed files with 251 additions and 5 deletions
90
plugins/ai-psychosis/tests/interaction-report.test.mjs
Normal file
90
plugins/ai-psychosis/tests/interaction-report.test.mjs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Tests for hooks/scripts/report-reader.mjs.
|
||||
// Verifies aggregate computation, domain counting, and backward-compat with
|
||||
// v1.0.0 records that predate pushback / domain_context fields.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execSync } from 'child_process';
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
|
||||
const SCRIPT = join(import.meta.dirname, '..', 'hooks', 'scripts', 'report-reader.mjs');
|
||||
|
||||
function runReader(jsonlContent) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'ia-report-'));
|
||||
const path = join(dir, 'sessions.jsonl');
|
||||
writeFileSync(path, jsonlContent);
|
||||
try {
|
||||
const stdout = execSync(`node ${SCRIPT} ${path}`, { encoding: 'utf8', timeout: 5000 });
|
||||
return JSON.parse(stdout.trim());
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test('pushback_total matches sum across v1.1.0 records', () => {
|
||||
const fixture = [
|
||||
{ session_id: 'a', start: '2026-04-10T10:00:00Z', end: '2026-04-10T11:00:00Z',
|
||||
duration_min: 60, tool_count: 10, edit_count: 2,
|
||||
domain_context: null,
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 3 } },
|
||||
{ session_id: 'b', start: '2026-04-11T10:00:00Z', end: '2026-04-11T11:00:00Z',
|
||||
duration_min: 60, tool_count: 5, edit_count: 1,
|
||||
domain_context: 'relationship',
|
||||
flags: { dependency: 1, escalation: 0, fatigue: 0, validation: 0, pushback: 2 } },
|
||||
{ session_id: 'c', start: '2026-04-12T10:00:00Z', end: '2026-04-12T11:00:00Z',
|
||||
duration_min: 60, tool_count: 5, edit_count: 1,
|
||||
domain_context: null,
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
|
||||
];
|
||||
const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
|
||||
const result = runReader(jsonl);
|
||||
assert.equal(result.pushback_total, 5);
|
||||
assert.equal(result.flags_total.pushback, 5);
|
||||
assert.equal(result.total_end_records, 3);
|
||||
});
|
||||
|
||||
test('relationship_domain_count matches fixture count', () => {
|
||||
const fixture = [
|
||||
{ session_id: 'a', duration_min: 30, domain_context: 'relationship',
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
|
||||
{ session_id: 'b', duration_min: 30, domain_context: 'relationship',
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 1 } },
|
||||
{ session_id: 'c', duration_min: 30, domain_context: null,
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
|
||||
{ session_id: 'd', duration_min: 30,
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 0 } },
|
||||
];
|
||||
const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
|
||||
const result = runReader(jsonl);
|
||||
assert.equal(result.relationship_domain_count, 2);
|
||||
assert.equal(result.null_domain_count, 2);
|
||||
});
|
||||
|
||||
test('backward-compat: v1.0.0 records without pushback/domain do not produce NaN', () => {
|
||||
const fixture = [
|
||||
// v1.0.0 — no pushback in flags, no domain_context at top level
|
||||
{ session_id: 'old', start: '2026-03-01T10:00:00Z', end: '2026-03-01T11:00:00Z',
|
||||
duration_min: 60, tool_count: 10, edit_count: 2,
|
||||
flags: { dependency: 1, escalation: 0, fatigue: 1, validation: 0 } },
|
||||
// v1.1.0 — full schema
|
||||
{ session_id: 'new', start: '2026-04-10T10:00:00Z', end: '2026-04-10T11:00:00Z',
|
||||
duration_min: 60, tool_count: 5, edit_count: 1,
|
||||
domain_context: 'relationship',
|
||||
flags: { dependency: 0, escalation: 0, fatigue: 0, validation: 0, pushback: 4 } },
|
||||
// start-only record (must be skipped)
|
||||
{ session_id: 'start-only', start: '2026-04-10T09:00:00Z', hour: 9, is_late_night: false },
|
||||
// error record (must be skipped)
|
||||
{ session_id: 'err', end: '2026-04-10T12:00:00Z', note: 'no_state_file' },
|
||||
];
|
||||
const jsonl = fixture.map(o => JSON.stringify(o)).join('\n') + '\n';
|
||||
const result = runReader(jsonl);
|
||||
assert.equal(result.pushback_total, 4);
|
||||
assert.equal(Number.isNaN(result.pushback_total), false);
|
||||
assert.equal(result.total_end_records, 2);
|
||||
assert.equal(result.schema_version.v1_0_records, 1);
|
||||
assert.equal(result.schema_version.v1_1_records, 1);
|
||||
assert.equal(result.flags_total.dependency, 1);
|
||||
assert.equal(result.flags_total.fatigue, 1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue