fix(notes): preserve phase1 transcript event lineage

This commit is contained in:
Saravana Achu Mac 2026-04-03 17:40:59 -07:00
parent 6ffc2f8755
commit 9ecc7c6bf5
3 changed files with 75 additions and 2 deletions

View File

@ -5,6 +5,7 @@ import { join } from 'node:path';
import {
buildPhase1NoteImport,
loadLatestTranscriptArtifact,
loadLatestTranscriptCaptureEvent,
persistPhase1NoteOutputs,
} from './ecosystem-phase1.js';
@ -55,10 +56,45 @@ describe('ecosystem phase1 note import', () => {
`${JSON.stringify(transcriptArtifact, null, 2)}\n`,
'utf-8'
);
await writeFile(
join(root, 'indexes', 'latest-transcript-event.json'),
`${JSON.stringify(
{
eventId: 'evt_capture_123',
eventName: 'capture.transcript.created',
eventVersion: 1,
occurredAt: '2026-04-03T18:15:00.000Z',
productId: 'lysnrai',
sourceSurface: 'desktop',
userId: 'user_saravana',
orgId: null,
sessionId: 'sess_123',
runId: null,
artifactId: 'art_transcript_123',
actor: { actorType: 'user', actorId: 'user_saravana' },
trace: {
correlationId: 'corr_123',
causationId: null,
parentEventId: null,
},
payload: {
artifactId: 'art_transcript_123',
durationMs: 1000,
language: 'en',
transcriptSource: 'microphone',
},
},
null,
2
)}\n`,
'utf-8'
);
const loaded = await loadLatestTranscriptArtifact(root);
const transcriptCaptureEvent = await loadLatestTranscriptCaptureEvent(root);
const generated = buildPhase1NoteImport({
transcriptArtifact: loaded,
transcriptCaptureEvent,
workspaceId: 'ws_phase1',
userId: 'user_saravana',
now: '2026-04-03T18:17:00.000Z',
@ -88,6 +124,18 @@ describe('ecosystem phase1 note import', () => {
expect(savedNoteArtifact.payload.noteFormat).toBe('markdown');
expect(savedLinkedEvent.payload.targetArtifactId).toBe('art_transcript_123');
expect(savedLinkedEvent.payload.relation).toBe('summarizes');
expect(generated.createdEvent.trace.causationId).toBe('evt_capture_123');
expect(generated.createdEvent.trace.parentEventId).toBe('evt_capture_123');
expect(savedLinkedEvent.trace.causationId).toBe(generated.createdEvent.eventId);
const savedCreatedEventIndex = JSON.parse(
await readFile(join(root, 'indexes', 'latest-note-created-event.json'), 'utf-8')
);
const savedLinkedEventIndex = JSON.parse(
await readFile(join(root, 'indexes', 'latest-note-linked-event.json'), 'utf-8')
);
expect(savedCreatedEventIndex.eventId).toBe(generated.createdEvent.eventId);
expect(savedLinkedEventIndex.eventId).toBe(generated.linkedEvent.eventId);
await rm(root, { recursive: true, force: true });
});

View File

@ -64,6 +64,11 @@ type EcosystemEvent = {
payload: Record<string, unknown>;
};
type TranscriptCaptureEvent = EcosystemEvent & {
eventName: 'capture.transcript.created';
artifactId: string;
};
type NoteArtifactEnvelope = {
id: string;
artifactType: 'note';
@ -108,8 +113,23 @@ export async function loadLatestTranscriptArtifact(root = getPhase1Root()): Prom
return JSON.parse(raw) as TranscriptArtifact;
}
export async function loadLatestTranscriptCaptureEvent(
root = getPhase1Root()
): Promise<TranscriptCaptureEvent | null> {
try {
const raw = await readFile(join(root, 'indexes', 'latest-transcript-event.json'), 'utf-8');
return JSON.parse(raw) as TranscriptCaptureEvent;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
return null;
}
throw error;
}
}
export function buildPhase1NoteImport(params: {
transcriptArtifact: TranscriptArtifact;
transcriptCaptureEvent?: TranscriptCaptureEvent | null;
workspaceId: string;
userId: string;
now?: string;
@ -233,8 +253,8 @@ export function buildPhase1NoteImport(params: {
},
trace: {
correlationId,
causationId: null,
parentEventId: null,
causationId: params.transcriptCaptureEvent?.eventId ?? null,
parentEventId: params.transcriptCaptureEvent?.eventId ?? null,
},
payload: {
artifactType: 'note',
@ -285,6 +305,8 @@ export async function persistPhase1NoteOutputs(params: {
await writeJson(join(root, 'events', 'artifact.created', `${params.createdEvent.eventId}.json`), params.createdEvent);
await writeJson(join(root, 'events', 'artifact.linked', `${params.linkedEvent.eventId}.json`), params.linkedEvent);
await writeJson(join(root, 'indexes', 'latest-note.json'), params.ecosystemNoteArtifact);
await writeJson(join(root, 'indexes', 'latest-note-created-event.json'), params.createdEvent);
await writeJson(join(root, 'indexes', 'latest-note-linked-event.json'), params.linkedEvent);
}
async function writeJson(path: string, payload: unknown): Promise<void> {

View File

@ -7,6 +7,7 @@ import { createNoteArtifact } from '../note-artifacts/repository.js';
import {
buildPhase1NoteImport,
loadLatestTranscriptArtifact,
loadLatestTranscriptCaptureEvent,
persistPhase1NoteOutputs,
} from '../../lib/ecosystem-phase1.js';
@ -23,8 +24,10 @@ export async function ecosystemPhase1Routes(app: FastifyInstance) {
}
const transcriptArtifact = await loadLatestTranscriptArtifact();
const transcriptCaptureEvent = await loadLatestTranscriptCaptureEvent();
const generated = buildPhase1NoteImport({
transcriptArtifact,
transcriptCaptureEvent,
workspaceId: parsed.data.workspaceId,
userId: auth.sub,
});