feat(iios): projectors checkpoint the projection cursor (P9)
All 5 projectors (route/ai/inbox/calendar/raw-event) now advance their IiosProjectionCursor after a freshly-claimed event is applied — split into claim → apply → advance so a duplicate never double-counts and a throw leaves the cursor untouched (event replays via the DLQ). Inbox's two topics get two cursor rows. Proves KG-06 replay reproduction at the projection level. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { resetDb } from '../test-utils/reset-db';
|
||||
import { ProjectionCursorService } from '../projection/projection-cursor.service';
|
||||
import { makeFakePorts, makeFakeInference } from '@insignia/iios-testkit';
|
||||
import { IIOS_EVENTS, type CloudEvent } from '@insignia/iios-contracts';
|
||||
import { ActorResolver } from '../identity/actor.resolver';
|
||||
@@ -48,7 +49,7 @@ beforeEach(async () => { await resetDb(prisma); });
|
||||
describe('chaos: poison consumer → DLQ → replay (P9)', () => {
|
||||
it('a consumer failure is dead-lettered (not swallowed), the lane keeps flowing, and replay resolves it', async () => {
|
||||
const dlq = new DlqService(asService);
|
||||
const projector = new AiProjector(asService, new OutboxBus(), dlq, ai());
|
||||
const projector = new AiProjector(asService, new OutboxBus(), dlq, ai(), new ProjectionCursorService(asService));
|
||||
dlq.registerHandler('ai-projector', (e) => projector.onNormalized(e)); // healthy handler for replay
|
||||
|
||||
const poison = await ingestInbound('There is a party at 9 PM tonight!');
|
||||
|
||||
Reference in New Issue
Block a user