fix(notes): preserve phase1 transcript event lineage
This commit is contained in:
parent
6ffc2f8755
commit
9ecc7c6bf5
@ -5,6 +5,7 @@ import { join } from 'node:path';
|
|||||||
import {
|
import {
|
||||||
buildPhase1NoteImport,
|
buildPhase1NoteImport,
|
||||||
loadLatestTranscriptArtifact,
|
loadLatestTranscriptArtifact,
|
||||||
|
loadLatestTranscriptCaptureEvent,
|
||||||
persistPhase1NoteOutputs,
|
persistPhase1NoteOutputs,
|
||||||
} from './ecosystem-phase1.js';
|
} from './ecosystem-phase1.js';
|
||||||
|
|
||||||
@ -55,10 +56,45 @@ describe('ecosystem phase1 note import', () => {
|
|||||||
`${JSON.stringify(transcriptArtifact, null, 2)}\n`,
|
`${JSON.stringify(transcriptArtifact, null, 2)}\n`,
|
||||||
'utf-8'
|
'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 loaded = await loadLatestTranscriptArtifact(root);
|
||||||
|
const transcriptCaptureEvent = await loadLatestTranscriptCaptureEvent(root);
|
||||||
const generated = buildPhase1NoteImport({
|
const generated = buildPhase1NoteImport({
|
||||||
transcriptArtifact: loaded,
|
transcriptArtifact: loaded,
|
||||||
|
transcriptCaptureEvent,
|
||||||
workspaceId: 'ws_phase1',
|
workspaceId: 'ws_phase1',
|
||||||
userId: 'user_saravana',
|
userId: 'user_saravana',
|
||||||
now: '2026-04-03T18:17:00.000Z',
|
now: '2026-04-03T18:17:00.000Z',
|
||||||
@ -88,6 +124,18 @@ describe('ecosystem phase1 note import', () => {
|
|||||||
expect(savedNoteArtifact.payload.noteFormat).toBe('markdown');
|
expect(savedNoteArtifact.payload.noteFormat).toBe('markdown');
|
||||||
expect(savedLinkedEvent.payload.targetArtifactId).toBe('art_transcript_123');
|
expect(savedLinkedEvent.payload.targetArtifactId).toBe('art_transcript_123');
|
||||||
expect(savedLinkedEvent.payload.relation).toBe('summarizes');
|
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 });
|
await rm(root, { recursive: true, force: true });
|
||||||
});
|
});
|
||||||
|
|||||||
@ -64,6 +64,11 @@ type EcosystemEvent = {
|
|||||||
payload: Record<string, unknown>;
|
payload: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TranscriptCaptureEvent = EcosystemEvent & {
|
||||||
|
eventName: 'capture.transcript.created';
|
||||||
|
artifactId: string;
|
||||||
|
};
|
||||||
|
|
||||||
type NoteArtifactEnvelope = {
|
type NoteArtifactEnvelope = {
|
||||||
id: string;
|
id: string;
|
||||||
artifactType: 'note';
|
artifactType: 'note';
|
||||||
@ -108,8 +113,23 @@ export async function loadLatestTranscriptArtifact(root = getPhase1Root()): Prom
|
|||||||
return JSON.parse(raw) as TranscriptArtifact;
|
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: {
|
export function buildPhase1NoteImport(params: {
|
||||||
transcriptArtifact: TranscriptArtifact;
|
transcriptArtifact: TranscriptArtifact;
|
||||||
|
transcriptCaptureEvent?: TranscriptCaptureEvent | null;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
now?: string;
|
now?: string;
|
||||||
@ -233,8 +253,8 @@ export function buildPhase1NoteImport(params: {
|
|||||||
},
|
},
|
||||||
trace: {
|
trace: {
|
||||||
correlationId,
|
correlationId,
|
||||||
causationId: null,
|
causationId: params.transcriptCaptureEvent?.eventId ?? null,
|
||||||
parentEventId: null,
|
parentEventId: params.transcriptCaptureEvent?.eventId ?? null,
|
||||||
},
|
},
|
||||||
payload: {
|
payload: {
|
||||||
artifactType: 'note',
|
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.created', `${params.createdEvent.eventId}.json`), params.createdEvent);
|
||||||
await writeJson(join(root, 'events', 'artifact.linked', `${params.linkedEvent.eventId}.json`), params.linkedEvent);
|
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.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> {
|
async function writeJson(path: string, payload: unknown): Promise<void> {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { createNoteArtifact } from '../note-artifacts/repository.js';
|
|||||||
import {
|
import {
|
||||||
buildPhase1NoteImport,
|
buildPhase1NoteImport,
|
||||||
loadLatestTranscriptArtifact,
|
loadLatestTranscriptArtifact,
|
||||||
|
loadLatestTranscriptCaptureEvent,
|
||||||
persistPhase1NoteOutputs,
|
persistPhase1NoteOutputs,
|
||||||
} from '../../lib/ecosystem-phase1.js';
|
} from '../../lib/ecosystem-phase1.js';
|
||||||
|
|
||||||
@ -23,8 +24,10 @@ export async function ecosystemPhase1Routes(app: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const transcriptArtifact = await loadLatestTranscriptArtifact();
|
const transcriptArtifact = await loadLatestTranscriptArtifact();
|
||||||
|
const transcriptCaptureEvent = await loadLatestTranscriptCaptureEvent();
|
||||||
const generated = buildPhase1NoteImport({
|
const generated = buildPhase1NoteImport({
|
||||||
transcriptArtifact,
|
transcriptArtifact,
|
||||||
|
transcriptCaptureEvent,
|
||||||
workspaceId: parsed.data.workspaceId,
|
workspaceId: parsed.data.workspaceId,
|
||||||
userId: auth.sub,
|
userId: auth.sub,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user