Implement event pub/sub messaging for federation to enable real-time
event streaming between federated instances.
Features:
- Event subscription management (subscribe/unsubscribe)
- Event publishing to subscribed instances
- Event acknowledgment protocol
- Server-side event filtering based on subscriptions
- Full signature verification and connection validation
Implementation:
- FederationEventSubscription model for storing subscriptions
- EventService with complete event lifecycle management
- EventController with authenticated and public endpoints
- EventMessage, EventAck, and SubscriptionDetails types
- Comprehensive DTOs for all event operations
API Endpoints:
- POST /api/v1/federation/events/subscribe
- POST /api/v1/federation/events/unsubscribe
- POST /api/v1/federation/events/publish
- GET /api/v1/federation/events/subscriptions
- GET /api/v1/federation/events/messages
- POST /api/v1/federation/incoming/event (public)
- POST /api/v1/federation/incoming/event/ack (public)
Testing:
- 18 unit tests for EventService (89.09% coverage)
- 11 unit tests for EventController (83.87% coverage)
- All 29 tests passing
- Follows TDD red-green-refactor cycle
Technical Notes:
- Reuses existing FederationMessage model with eventType field
- Follows patterns from QueryService and CommandService
- Uses existing signature and connection infrastructure
- Supports hierarchical event type naming (e.g., "task.created")
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented optimistic locking with version field and SELECT FOR UPDATE
transactions to prevent data corruption from concurrent job status updates.
Changes:
- Added version field to RunnerJob schema for optimistic locking
- Created migration 20260202_add_runner_job_version_for_concurrency
- Implemented ConcurrentUpdateException for conflict detection
- Updated RunnerJobsService methods with optimistic locking:
* updateStatus() - with version checking and retry logic
* updateProgress() - with version checking and retry logic
* cancel() - with version checking and retry logic
- Updated CoordinatorIntegrationService with SELECT FOR UPDATE:
* updateJobStatus() - transaction with row locking
* completeJob() - transaction with row locking
* failJob() - transaction with row locking
* updateJobProgress() - optimistic locking
- Added retry mechanism (3 attempts) with exponential backoff
- Added comprehensive concurrency tests (10 tests, all passing)
- Updated existing test mocks to support updateMany
Test Results:
- All 10 concurrency tests passing ✓
- Tests cover concurrent status updates, progress updates, completions,
cancellations, retry logic, and exponential backoff
This fix prevents race conditions that could cause:
- Lost job results (double completion)
- Lost progress updates
- Invalid status transitions
- Data corruption under concurrent access
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add composite index [jobId, timestamp] to improve query performance
for the most common job_events access patterns.
Changes:
- Add @@index([jobId, timestamp]) to JobEvent model in schema.prisma
- Create migration 20260202122655_add_job_events_composite_index
- Add performance tests to validate index effectiveness
- Document index design rationale in scratchpad
- Fix lint errors in api-key.guard, herald.service, runner-jobs.service
Rationale:
The composite index [jobId, timestamp] optimizes the dominant query
pattern used across all services:
- JobEventsService.getEventsByJobId (WHERE jobId, ORDER BY timestamp)
- RunnerJobsService.streamEvents (WHERE jobId + timestamp range)
- RunnerJobsService.findOne (implicit jobId filter + timestamp order)
This index provides:
- Fast filtering by jobId (highly selective)
- Efficient timestamp-based ordering
- Optimal support for timestamp range queries
- Backward compatibility with jobId-only queries
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Issues resolved:
- #68: pgvector Setup
* Added pgvector vector index migration for knowledge_embeddings
* Vector index uses HNSW algorithm with cosine distance
* Optimized for 1536-dimension OpenAI embeddings
- #69: Embedding Generation Pipeline
* Created EmbeddingService with OpenAI integration
* Automatic embedding generation on entry create/update
* Batch processing endpoint for existing entries
* Async generation to avoid blocking API responses
* Content preparation with title weighting
- #70: Semantic Search API
* POST /api/knowledge/search/semantic - pure vector search
* POST /api/knowledge/search/hybrid - RRF combined search
* POST /api/knowledge/embeddings/batch - batch generation
* Comprehensive test coverage
* Full documentation in docs/SEMANTIC_SEARCH.md
Technical details:
- Uses OpenAI text-embedding-3-small model (1536 dims)
- HNSW index for O(log n) similarity search
- Reciprocal Rank Fusion for hybrid search
- Graceful degradation when OpenAI not configured
- Async embedding generation for performance
Configuration:
- Added OPENAI_API_KEY to .env.example
- Optional feature - disabled if API key not set
- Falls back to keyword search in hybrid mode
- Add Personality model to Prisma schema with FormalityLevel enum
- Create migration and seed with 6 default personalities
- Implement CRUD API with TDD approach (97.67% coverage)
* PersonalitiesService: findAll, findOne, findDefault, create, update, remove
* PersonalitiesController: REST endpoints with auth guards
* Comprehensive test coverage (21 passing tests)
- Add Personality types to shared package
- Create frontend components:
* PersonalitySelector: dropdown for choosing personality
* PersonalityPreview: preview personality style and system prompt
* PersonalityForm: create/edit personalities with validation
* Settings page: manage personalities with CRUD operations
- Integrate with Ollama API:
* Support personalityId in chat endpoint
* Auto-inject system prompt from personality
* Fall back to default personality if not specified
- API client for frontend personality management
All tests passing with 97.67% backend coverage (exceeds 85% requirement)
- Create workspace listing page at /settings/workspaces
- List all user workspaces with role badges
- Create new workspace functionality
- Display member count per workspace
- Create workspace detail page at /settings/workspaces/[id]
- Workspace settings (name, ID, created date)
- Member management with role editing
- Invite member functionality
- Delete workspace (owner only)
- Add workspace components:
- WorkspaceCard: Display workspace info with role badge
- WorkspaceSettings: Edit workspace settings and delete
- MemberList: Display and manage workspace members
- InviteMember: Send invitations with role selection
- Add WorkspaceMemberWithUser type to shared package
- Follow existing app patterns for styling and structure
- Use mock data (ready for API integration)
Implements #9, #10
- Team model with workspace membership
- TeamMember model with role-based access (OWNER, ADMIN, MEMBER)
- Row-Level Security policies for tenant isolation on 19 tables
- Helper functions: current_user_id(), is_workspace_member(), is_workspace_admin()
- Developer utilities in src/lib/db-context.ts for easy RLS integration
- Comprehensive documentation in docs/design/multi-tenant-rls.md
Database migrations:
- 20260129220941_add_team_model: Adds Team and TeamMember tables
- 20260129221004_add_rls_policies: Enables RLS and creates policies
Security features:
- Complete database-level tenant isolation
- Automatic query filtering based on workspace membership
- Defense-in-depth security with application and database layers
- Performance-optimized with indexes on workspace_id