KnowledgeVault AI plan-module M-04
internal prototype · canonical JSON + Dreamborn Forge HTML
internal generated
plan-module · supabase_json

KnowledgeVault AI plan-module M-04

plan-module artifact · for KnowledgeVault AI · phase M-04 · status draft

Planning Surface

Use this to decide what happens next.

Status

draft

Phase

M-04

tasks
idrolebrieftitlecompletiondepends on
1developerCreate file apps/knowledgevault/supabase/migrations/004_capture_sessions_and_chunks.sql. First inspect the existing migrations directory and use the correct next sequential number per POL-ARCH-001 (never modify existing migration files; no sequence gaps). Create enum types idempotently (use DO $$ BEGIN … EXCEPTION WHEN duplicate_object THEN null; END $$): capture_session_status ('in_progress','complete','abandoned'); knowledge_chunk_type ('failure_mode','symptom','fix','psi_range','safety_warning','field_tip','tool_required','part_number_reference'). CREATE TABLE IF NOT EXISTS `captureDB migration: capture_sessions, knowledge_chunks, evaluation_queue- type: file - evaluate: Migration file applies idempotently: both enum types created, all three tables present with correct column definitions and ON DELETE behaviour, RLS enabled on capture_sessions and knowledge_chunks with all specified policies, all eight indexes created, commented rollback block present; passes POL-ARCH-001 (sequential naming, no existing file modified) and POL-SEC-001 (RLS on all user tables) and POL-DATA-001 (all _id columns have FK constraints with explicit ON DELETE). - min length: 1500
2developerCreate apps/knowledgevault/src/lib/server/shared-expert-context.ts. This TypeScript module exports a singleton in-process Map-based service maintaining per-session live state during voice capture sessions. It is keyed by session_id (string). Use TypeScript strict mode; no bare any types (POL-STYLE-001). Export interface SharedExpertContextObject: { session_id: string; question_sequence: string[]; answered_question_count: number; knowledge_surface_coverage_pct: number; context_summary: string; wer_compensation_applied: boolean; }. Implement four exported functions: 1. init(session_id: stSharedExpertContext in-process session state service- type: file - evaluate: TypeScript module exports init(), update(), freeze(), get(), and abandonStaleSessions() with correct signatures; update() increments answered_question_count by 1 per call and appends next_question; freeze() evicts session from Map so subsequent get() returns null; auto-abandon timer fires at 300 seconds and sets capture_sessions.status='abandoned' plus inserts evaluation_queue row; passes POL-STYLE-001 (strict=true, no bare any, no @ts-ignore) and POL-STYLE-002 (no empty catch blocks). - min length: 400
3developerCreate apps/knowledgevault/src/app/api/sessions/start/route.ts (Next.js 15 App Router, named export POST). This route creates a new CaptureSession and returns session context to the expert. Auth: call supabase.auth.getUser() — if no user or JWT role claim ≠ 'expert' return 401 { error:'Unauthorized', code:'UNAUTHORIZED', status:401 } (POL-SEC-002). Never skip getUser. Parse body JSON { product_id: string }. If product_id missing return 400 { error:'product_id required', code:'MISSING_PRODUCT_ID', status:400 }. Sanitize product_id via sanitizeInput() before any DB call (POL-SEC-003). LPOST /api/sessions/start route: create capture session- type: file - evaluate: Route exports POST handler that: calls supabase.auth.getUser() and verifies role=expert before any DB operation (POL-SEC-002); sanitizes product_id before DB writes (POL-SEC-003); returns 201 with all five required fields (session_id, first_question, product_context, earnings_estimate_range); inserts capture_sessions row with status='in_progress'; calls SharedExpertContext.init(); returns correct error codes for all specified failure cases (400/401/422/503); passes POL-STYLE-001. - min length: 350- 1 - 2
4developerCreate apps/knowledgevault/src/app/api/sessions/[id]/audio/route.ts (Next.js 15 App Router, named export POST). This route accepts a binary audio chunk, transcribes via Deepgram Nova-2, extracts micro-objects via GPT-4o, and updates session state. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Read raw binary body (Content-Type: audio/webm or audio/opus). If body empty return 400 { error:'Audio chunk required', code:'MISSING_AUDIO', status:400 }. Validate session: query capture_sessions WHERE id=param via service POST /api/sessions/:id/audio route: transcribe and extract- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); opens Deepgram Nova-2 WebSocket per call and closes after transcript; sets wer_compensation_applied=true when any word confidence < 0.75; calls GPT-4o with SharedExpertContext system prompt; inserts one knowledge_chunks row per micro_object (is_published=false); updates capture_sessions.session_context JSONB immediately; accumulates cost_breakdown fields without reset; broadcasts to Supabase Realtime channel session:<id>:chunks per micro_object; returns session_status='session_complete_ready' when- 1 - 2 - 3
5developerCreate apps/knowledgevault/src/app/api/sessions/[id]/complete/route.ts (Next.js 15 App Router, named export POST). This route closes an in-progress capture session and enqueues it for M-05 evaluation. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Body is {} (empty — do not attempt to parse meaningful fields). Validation: query capture_sessions WHERE id=param via service role. If no row or expert_id≠user.id return 404 { error:'Session not found', code:'SESSION_NOT_FOUND', status:404 }. If status is 'complete' or 'POST /api/sessions/:id/complete route: close session- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); returns 400 when answered_question_count=0; sets capture_sessions.status='complete' and completed_at and session_duration_s; calls SharedExpertContext.freeze() so subsequent get() returns null; inserts evaluation_queue row; returns chunks_captured and earnings_estimate and session_duration_s; all error codes correct (400/401/404/409); passes POL-STYLE-001. - min length: 250- 1 - 2 - 3 - 4
6developerCreate apps/knowledgevault/src/screens/ActiveCaptureSessionScreen.tsx (React Native, TypeScript strict). Receives navigation params: session_id (string, uuid), first_question (string), product_context ({sku, name, category, manufacturer}), earnings_estimate_range ({min: number, max: number}). Layout — full-screen two-panel: - Top 60%: (1) Current question text — large centered, role='status', aria-live='polite' (C-03-a-1). (2) Audio waveform showing microphone state listening/processing/idle, aria-label='Microphone status: <state>' (C-03-a-2). (3) Live transcript feed, aria-live='polite', aActiveCaptureSessionScreen (S-03-a) React Native component- type: file - evaluate: Component renders within 1000ms from nav param (no mount API call for first question); DONE button is disabled at answered_question_count=0 and enabled at ≥1; subscribes to Supabase Realtime channel session:<id>:chunks on mount and unsubscribes on unmount; auto-close prompt appears on session_status='session_complete_ready'; abandon requires confirmation modal; all eight C-03-a accessibility attributes present (role=status, aria-live=polite on question/transcript/panel, aria-label on waveform); all eight chunk type display labels exactly match spec; passes POL-STYLE-00- 3 - 4 - 5
7developerCreate apps/knowledgevault/src/screens/SessionCompleteScreen.tsx (React Native, TypeScript strict). Receives navigation params: chunks_captured (number/integer), earnings_estimate ({min: number, max: number}), session_duration_s (number/integer), session_id (string, uuid — passed through for CTA navigation). This screen makes zero network calls. All content comes from navigation params. Renders immediately on mount with no loading state. Layout — full-screen centered: - Single summary card (C-03-b-1) with accessibilityRole='summary' or equivalent (role=status per spec). Card contains: SessionCompleteScreen (S-03-b) React Native component- type: file - evaluate: Component makes zero network calls; headline text exactly equals 'Session submitted'; body renders '<n> knowledge objects captured' using chunks_captured param; earnings displays '$<min.toFixed(2)>–$<max.toFixed(2)>'; CTA label exactly 'View results when ready' and navigates to S-04-a with session_id; back navigation disabled; summary card has accessibility role=status; passes POL-STYLE-001 (strict=true, no bare any). - min length: 150- 5 - 6
Agent Handoff
Start Here
idrolebrieftitlecompletiondepends on
1developerCreate file apps/knowledgevault/supabase/migrations/004_capture_sessions_and_chunks.sql. First inspect the existing migrations directory and use the correct next sequential number per POL-ARCH-001 (never modify existing migration files; no sequence gaps). Create enum types idempotently (use DO $$ BEGIN … EXCEPTION WHEN duplicate_object THEN null; END $$): capture_session_status ('in_progress','complete','abandoned'); knowledge_chunk_type ('failure_mode','symptom','fix','psi_range','safety_warning','field_tip','tool_required','part_number_reference'). CREATE TABLE IF NOT EXISTS `captureDB migration: capture_sessions, knowledge_chunks, evaluation_queue- type: file - evaluate: Migration file applies idempotently: both enum types created, all three tables present with correct column definitions and ON DELETE behaviour, RLS enabled on capture_sessions and knowledge_chunks with all specified policies, all eight indexes created, commented rollback block present; passes POL-ARCH-001 (sequential naming, no existing file modified) and POL-SEC-001 (RLS on all user tables) and POL-DATA-001 (all _id columns have FK constraints with explicit ON DELETE). - min length: 1500
2developerCreate apps/knowledgevault/src/lib/server/shared-expert-context.ts. This TypeScript module exports a singleton in-process Map-based service maintaining per-session live state during voice capture sessions. It is keyed by session_id (string). Use TypeScript strict mode; no bare any types (POL-STYLE-001). Export interface SharedExpertContextObject: { session_id: string; question_sequence: string[]; answered_question_count: number; knowledge_surface_coverage_pct: number; context_summary: string; wer_compensation_applied: boolean; }. Implement four exported functions: 1. init(session_id: stSharedExpertContext in-process session state service- type: file - evaluate: TypeScript module exports init(), update(), freeze(), get(), and abandonStaleSessions() with correct signatures; update() increments answered_question_count by 1 per call and appends next_question; freeze() evicts session from Map so subsequent get() returns null; auto-abandon timer fires at 300 seconds and sets capture_sessions.status='abandoned' plus inserts evaluation_queue row; passes POL-STYLE-001 (strict=true, no bare any, no @ts-ignore) and POL-STYLE-002 (no empty catch blocks). - min length: 400
3developerCreate apps/knowledgevault/src/app/api/sessions/start/route.ts (Next.js 15 App Router, named export POST). This route creates a new CaptureSession and returns session context to the expert. Auth: call supabase.auth.getUser() — if no user or JWT role claim ≠ 'expert' return 401 { error:'Unauthorized', code:'UNAUTHORIZED', status:401 } (POL-SEC-002). Never skip getUser. Parse body JSON { product_id: string }. If product_id missing return 400 { error:'product_id required', code:'MISSING_PRODUCT_ID', status:400 }. Sanitize product_id via sanitizeInput() before any DB call (POL-SEC-003). LPOST /api/sessions/start route: create capture session- type: file - evaluate: Route exports POST handler that: calls supabase.auth.getUser() and verifies role=expert before any DB operation (POL-SEC-002); sanitizes product_id before DB writes (POL-SEC-003); returns 201 with all five required fields (session_id, first_question, product_context, earnings_estimate_range); inserts capture_sessions row with status='in_progress'; calls SharedExpertContext.init(); returns correct error codes for all specified failure cases (400/401/422/503); passes POL-STYLE-001. - min length: 350- 1 - 2
4developerCreate apps/knowledgevault/src/app/api/sessions/[id]/audio/route.ts (Next.js 15 App Router, named export POST). This route accepts a binary audio chunk, transcribes via Deepgram Nova-2, extracts micro-objects via GPT-4o, and updates session state. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Read raw binary body (Content-Type: audio/webm or audio/opus). If body empty return 400 { error:'Audio chunk required', code:'MISSING_AUDIO', status:400 }. Validate session: query capture_sessions WHERE id=param via service POST /api/sessions/:id/audio route: transcribe and extract- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); opens Deepgram Nova-2 WebSocket per call and closes after transcript; sets wer_compensation_applied=true when any word confidence < 0.75; calls GPT-4o with SharedExpertContext system prompt; inserts one knowledge_chunks row per micro_object (is_published=false); updates capture_sessions.session_context JSONB immediately; accumulates cost_breakdown fields without reset; broadcasts to Supabase Realtime channel session:<id>:chunks per micro_object; returns session_status='session_complete_ready' when- 1 - 2 - 3
5developerCreate apps/knowledgevault/src/app/api/sessions/[id]/complete/route.ts (Next.js 15 App Router, named export POST). This route closes an in-progress capture session and enqueues it for M-05 evaluation. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Body is {} (empty — do not attempt to parse meaningful fields). Validation: query capture_sessions WHERE id=param via service role. If no row or expert_id≠user.id return 404 { error:'Session not found', code:'SESSION_NOT_FOUND', status:404 }. If status is 'complete' or 'POST /api/sessions/:id/complete route: close session- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); returns 400 when answered_question_count=0; sets capture_sessions.status='complete' and completed_at and session_duration_s; calls SharedExpertContext.freeze() so subsequent get() returns null; inserts evaluation_queue row; returns chunks_captured and earnings_estimate and session_duration_s; all error codes correct (400/401/404/409); passes POL-STYLE-001. - min length: 250- 1 - 2 - 3 - 4
6developerCreate apps/knowledgevault/src/screens/ActiveCaptureSessionScreen.tsx (React Native, TypeScript strict). Receives navigation params: session_id (string, uuid), first_question (string), product_context ({sku, name, category, manufacturer}), earnings_estimate_range ({min: number, max: number}). Layout — full-screen two-panel: - Top 60%: (1) Current question text — large centered, role='status', aria-live='polite' (C-03-a-1). (2) Audio waveform showing microphone state listening/processing/idle, aria-label='Microphone status: <state>' (C-03-a-2). (3) Live transcript feed, aria-live='polite', aActiveCaptureSessionScreen (S-03-a) React Native component- type: file - evaluate: Component renders within 1000ms from nav param (no mount API call for first question); DONE button is disabled at answered_question_count=0 and enabled at ≥1; subscribes to Supabase Realtime channel session:<id>:chunks on mount and unsubscribes on unmount; auto-close prompt appears on session_status='session_complete_ready'; abandon requires confirmation modal; all eight C-03-a accessibility attributes present (role=status, aria-live=polite on question/transcript/panel, aria-label on waveform); all eight chunk type display labels exactly match spec; passes POL-STYLE-00- 3 - 4 - 5
7developerCreate apps/knowledgevault/src/screens/SessionCompleteScreen.tsx (React Native, TypeScript strict). Receives navigation params: chunks_captured (number/integer), earnings_estimate ({min: number, max: number}), session_duration_s (number/integer), session_id (string, uuid — passed through for CTA navigation). This screen makes zero network calls. All content comes from navigation params. Renders immediately on mount with no loading state. Layout — full-screen centered: - Single summary card (C-03-b-1) with accessibilityRole='summary' or equivalent (role=status per spec). Card contains: SessionCompleteScreen (S-03-b) React Native component- type: file - evaluate: Component makes zero network calls; headline text exactly equals 'Session submitted'; body renders '<n> knowledge objects captured' using chunks_captured param; earnings displays '$<min.toFixed(2)>–$<max.toFixed(2)>'; CTA label exactly 'View results when ready' and navigates to S-04-a with session_id; back navigation disabled; summary card has accessibility role=status; passes POL-STYLE-001 (strict=true, no bare any). - min length: 150- 5 - 6
Completion Evidence

No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.

Artifact Shape
  • tasks: 7 items
  • module id: M-04
  • module name: Capture Orchestrator
  • schema version: 1.0
  • depends on modules: 2 items
Structured Payload

Machine-readable source fields

tasks
idrolebrieftitlecompletiondepends on
1developerCreate file apps/knowledgevault/supabase/migrations/004_capture_sessions_and_chunks.sql. First inspect the existing migrations directory and use the correct next sequential number per POL-ARCH-001 (never modify existing migration files; no sequence gaps). Create enum types idempotently (use DO $$ BEGIN … EXCEPTION WHEN duplicate_object THEN null; END $$): capture_session_status ('in_progress','complete','abandoned'); knowledge_chunk_type ('failure_mode','symptom','fix','psi_range','safety_warning','field_tip','tool_required','part_number_reference'). CREATE TABLE IF NOT EXISTS `captureDB migration: capture_sessions, knowledge_chunks, evaluation_queue- type: file - evaluate: Migration file applies idempotently: both enum types created, all three tables present with correct column definitions and ON DELETE behaviour, RLS enabled on capture_sessions and knowledge_chunks with all specified policies, all eight indexes created, commented rollback block present; passes POL-ARCH-001 (sequential naming, no existing file modified) and POL-SEC-001 (RLS on all user tables) and POL-DATA-001 (all _id columns have FK constraints with explicit ON DELETE). - min length: 1500
2developerCreate apps/knowledgevault/src/lib/server/shared-expert-context.ts. This TypeScript module exports a singleton in-process Map-based service maintaining per-session live state during voice capture sessions. It is keyed by session_id (string). Use TypeScript strict mode; no bare any types (POL-STYLE-001). Export interface SharedExpertContextObject: { session_id: string; question_sequence: string[]; answered_question_count: number; knowledge_surface_coverage_pct: number; context_summary: string; wer_compensation_applied: boolean; }. Implement four exported functions: 1. init(session_id: stSharedExpertContext in-process session state service- type: file - evaluate: TypeScript module exports init(), update(), freeze(), get(), and abandonStaleSessions() with correct signatures; update() increments answered_question_count by 1 per call and appends next_question; freeze() evicts session from Map so subsequent get() returns null; auto-abandon timer fires at 300 seconds and sets capture_sessions.status='abandoned' plus inserts evaluation_queue row; passes POL-STYLE-001 (strict=true, no bare any, no @ts-ignore) and POL-STYLE-002 (no empty catch blocks). - min length: 400
3developerCreate apps/knowledgevault/src/app/api/sessions/start/route.ts (Next.js 15 App Router, named export POST). This route creates a new CaptureSession and returns session context to the expert. Auth: call supabase.auth.getUser() — if no user or JWT role claim ≠ 'expert' return 401 { error:'Unauthorized', code:'UNAUTHORIZED', status:401 } (POL-SEC-002). Never skip getUser. Parse body JSON { product_id: string }. If product_id missing return 400 { error:'product_id required', code:'MISSING_PRODUCT_ID', status:400 }. Sanitize product_id via sanitizeInput() before any DB call (POL-SEC-003). LPOST /api/sessions/start route: create capture session- type: file - evaluate: Route exports POST handler that: calls supabase.auth.getUser() and verifies role=expert before any DB operation (POL-SEC-002); sanitizes product_id before DB writes (POL-SEC-003); returns 201 with all five required fields (session_id, first_question, product_context, earnings_estimate_range); inserts capture_sessions row with status='in_progress'; calls SharedExpertContext.init(); returns correct error codes for all specified failure cases (400/401/422/503); passes POL-STYLE-001. - min length: 350- 1 - 2
4developerCreate apps/knowledgevault/src/app/api/sessions/[id]/audio/route.ts (Next.js 15 App Router, named export POST). This route accepts a binary audio chunk, transcribes via Deepgram Nova-2, extracts micro-objects via GPT-4o, and updates session state. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Read raw binary body (Content-Type: audio/webm or audio/opus). If body empty return 400 { error:'Audio chunk required', code:'MISSING_AUDIO', status:400 }. Validate session: query capture_sessions WHERE id=param via service POST /api/sessions/:id/audio route: transcribe and extract- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); opens Deepgram Nova-2 WebSocket per call and closes after transcript; sets wer_compensation_applied=true when any word confidence < 0.75; calls GPT-4o with SharedExpertContext system prompt; inserts one knowledge_chunks row per micro_object (is_published=false); updates capture_sessions.session_context JSONB immediately; accumulates cost_breakdown fields without reset; broadcasts to Supabase Realtime channel session:<id>:chunks per micro_object; returns session_status='session_complete_ready' when- 1 - 2 - 3
5developerCreate apps/knowledgevault/src/app/api/sessions/[id]/complete/route.ts (Next.js 15 App Router, named export POST). This route closes an in-progress capture session and enqueues it for M-05 evaluation. Auth: supabase.auth.getUser() — reject 401 if no user or role≠expert (POL-SEC-002). Read path param id (session uuid). Body is {} (empty — do not attempt to parse meaningful fields). Validation: query capture_sessions WHERE id=param via service role. If no row or expert_id≠user.id return 404 { error:'Session not found', code:'SESSION_NOT_FOUND', status:404 }. If status is 'complete' or 'POST /api/sessions/:id/complete route: close session- type: file - evaluate: Route exports POST handler that: verifies JWT role=expert (POL-SEC-002); returns 400 when answered_question_count=0; sets capture_sessions.status='complete' and completed_at and session_duration_s; calls SharedExpertContext.freeze() so subsequent get() returns null; inserts evaluation_queue row; returns chunks_captured and earnings_estimate and session_duration_s; all error codes correct (400/401/404/409); passes POL-STYLE-001. - min length: 250- 1 - 2 - 3 - 4
6developerCreate apps/knowledgevault/src/screens/ActiveCaptureSessionScreen.tsx (React Native, TypeScript strict). Receives navigation params: session_id (string, uuid), first_question (string), product_context ({sku, name, category, manufacturer}), earnings_estimate_range ({min: number, max: number}). Layout — full-screen two-panel: - Top 60%: (1) Current question text — large centered, role='status', aria-live='polite' (C-03-a-1). (2) Audio waveform showing microphone state listening/processing/idle, aria-label='Microphone status: <state>' (C-03-a-2). (3) Live transcript feed, aria-live='polite', aActiveCaptureSessionScreen (S-03-a) React Native component- type: file - evaluate: Component renders within 1000ms from nav param (no mount API call for first question); DONE button is disabled at answered_question_count=0 and enabled at ≥1; subscribes to Supabase Realtime channel session:<id>:chunks on mount and unsubscribes on unmount; auto-close prompt appears on session_status='session_complete_ready'; abandon requires confirmation modal; all eight C-03-a accessibility attributes present (role=status, aria-live=polite on question/transcript/panel, aria-label on waveform); all eight chunk type display labels exactly match spec; passes POL-STYLE-00- 3 - 4 - 5
7developerCreate apps/knowledgevault/src/screens/SessionCompleteScreen.tsx (React Native, TypeScript strict). Receives navigation params: chunks_captured (number/integer), earnings_estimate ({min: number, max: number}), session_duration_s (number/integer), session_id (string, uuid — passed through for CTA navigation). This screen makes zero network calls. All content comes from navigation params. Renders immediately on mount with no loading state. Layout — full-screen centered: - Single summary card (C-03-b-1) with accessibilityRole='summary' or equivalent (role=status per spec). Card contains: SessionCompleteScreen (S-03-b) React Native component- type: file - evaluate: Component makes zero network calls; headline text exactly equals 'Session submitted'; body renders '<n> knowledge objects captured' using chunks_captured param; earnings displays '$<min.toFixed(2)>–$<max.toFixed(2)>'; CTA label exactly 'View results when ready' and navigates to S-04-a with session_id; back navigation disabled; summary card has accessibility role=status; passes POL-STYLE-001 (strict=true, no bare any). - min length: 150- 5 - 6
module id

M-04

module name

Capture Orchestrator

schema version

1.0

depends on modules
  • M-02
  • M-03