KnowledgeVault AI spec
spec artifact · for KnowledgeVault AI · status dispatched
- OQ-1 [EXEC DECISION REQUIRED — BLOCKS F-11]: How is BezelIQ's build speed and cost surfaced in the demo — hidden meta-page, live cost counter in the analytics panel, or talking point only? Blueprint treats it as a talking point pending this decision. If a UI component is required, F-11 scope expands.
- OQ-2 [EXEC DECISION REQUIRED — BLOCKS SELLING PAGE]: AD reseller model — white-label (AD branding only), powered-by badge (KnowledgeVault brand visible), or full private label (no BezelIQ/KnowledgeVault branding)? Determines branding scope for selling page and company dashboard. Does not affect schema.
- OQ-3 [CONFIRM V2 SCOPE]: Mobile offline support — blueprint assumes online-only for demo. Is offline audio buffering and queue sync confirmed out of scope for demo? If moved in, F-02 scope expands significantly.
- OQ-4 [CONFIRM V2 SCOPE]: Voice cloning for Virtual Expert (Gemini suggestion) is scoped to V2. Confirm: voice-cloned audio is out of demo scope. If moved in, F-10 expands and ElevenLabs API key required.
- OQ-5 [CONFIRM V2 SCOPE]: Counter-sales integration (Virtual Expert surfaces reorder recommendation) is scoped to V2. Confirm no counter-sales UI or API endpoint needed for demo.
- OQ-6 [EXEC DECISION REQUIRED — BLOCKS INFRA]: Is the selling page on a separate domain (e.g. knowledgevaultai.com) or a subdirectory of the app domain? Affects Cloudflare Pages routing, DNS setup, and CORS configuration. Must be decided before any deployment work begins.
- OQ-7 [SPEC GAP — BLOCKS F-01, F-07, F-11]: How is coverage_pct computed per product? The blueprint references it in queue ordering, dashboard grid (red/yellow/green), and the is_fully_captured flip condition (≥90%), but no computation formula is specified. Is it: (a) count(completed sessions) / target_session_count, (b) average composite_score across sessions, (c) a coverage of the knowledge_surface vectors from the pre-seed? This must be defined before migrations and queue API can be implemented.
- OQ-8 [SPEC GAP — BLOCKS F-09, RLS]: field_queries.company_id is an FK to companies, but it is nullable. Is an unauthenticated (no company JWT) semantic query permitted in any context, or is a company JWT always required? This determines RLS policy on field_queries and whether the search endpoint has a public mode.
- OQ-9 [SPEC GAP — BLOCKS F-06, F-07]: Is there an admin UI for payment_hold manual review, or is v1 admin resolution handled via direct DB update only? The blueprint specifies a 48h SLA for resolution but defines no resolution mechanism. This must be defined before the payment hold path can be fully implemented. At minimum, an admin notification channel and a DB-level resolution workflow must be specified.
- OQ-10 [SPEC GAP — BLOCKS F-04, F-06]: WER compensation algorithm is unspecified. The blueprint states 'WER compensation logic applied post-transcription' with a 0.75 confidence threshold. Is this: (a) Deepgram's per-word confidence score, (b) a custom re-scoring pass against a trade vocabulary, or (c) a post-processing heuristic? This must be defined before F-04 (signal detection) and F-06 (gate exclusion logic) can be implemented.
- OQ-11 [SCHEMA DELTA — BLOCKS ALL MIGRATIONS]: Phase-1 migration (001_kv_phase1_foundation.sql) defines users + expert_profiles (two tables), skus (basic, no capture-status fields), capture_sessions (simplified, no session_context JSONB or score_object), session_answers, follow_up_questions. The blueprint introduces: experts (single table replacing users+expert_profiles), products (extends skus with has_surge_badge, is_fully_captured, spec_sheet_url), product_pre_seeds, knowledge_chunks (with pgvector 1536-dim), virtual_experts, payments, companies, company_knowledge_access, field_queries. Developer must produce a migration strategy document (additive migration preferred — no drop-and-recreate) before any new migration is written.
spec artifact · for KnowledgeVault AI · status dispatched
No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.
- OQ-1 [EXEC DECISION REQUIRED — BLOCKS F-11]: How is BezelIQ's build speed and cost surfaced in the demo — hidden meta-page, live cost counter in the analytics panel, or talking point only? Blueprint treats it as a talking point pending this decision. If a UI component is required, F-11 scope expands.
- OQ-2 [EXEC DECISION REQUIRED — BLOCKS SELLING PAGE]: AD reseller model — white-label (AD branding only), powered-by badge (KnowledgeVault brand visible), or full private label (no BezelIQ/KnowledgeVault branding)? Determines branding scope for selling page and company dashboard. Does not affect schema.
- OQ-3 [CONFIRM V2 SCOPE]: Mobile offline support — blueprint assumes online-only for demo. Is offline audio buffering and queue sync confirmed out of scope for demo? If moved in, F-02 scope expands significantly.
- OQ-4 [CONFIRM V2 SCOPE]: Voice cloning for Virtual Expert (Gemini suggestion) is scoped to V2. Confirm: voice-cloned audio is out of demo scope. If moved in, F-10 expands and ElevenLabs API key required.
- OQ-5 [CONFIRM V2 SCOPE]: Counter-sales integration (Virtual Expert surfaces reorder recommendation) is scoped to V2. Confirm no counter-sales UI or API endpoint needed for demo.
- OQ-6 [EXEC DECISION REQUIRED — BLOCKS INFRA]: Is the selling page on a separate domain (e.g. knowledgevaultai.com) or a subdirectory of the app domain? Affects Cloudflare Pages routing, DNS setup, and CORS configuration. Must be decided before any deployment work begins.
- OQ-7 [SPEC GAP — BLOCKS F-01, F-07, F-11]: How is coverage_pct computed per product? The blueprint references it in queue ordering, dashboard grid (red/yellow/green), and the is_fully_captured flip condition (≥90%), but no computation formula is specified. Is it: (a) count(completed sessions) / target_session_count, (b) average composite_score across sessions, (c) a coverage of the knowledge_surface vectors from the pre-seed? This must be defined before migrations and queue API can be implemented.
- OQ-8 [SPEC GAP — BLOCKS F-09, RLS]: field_queries.company_id is an FK to companies, but it is nullable. Is an unauthenticated (no company JWT) semantic query permitted in any context, or is a company JWT always required? This determines RLS policy on field_queries and whether the search endpoint has a public mode.
- OQ-9 [SPEC GAP — BLOCKS F-06, F-07]: Is there an admin UI for payment_hold manual review, or is v1 admin resolution handled via direct DB update only? The blueprint specifies a 48h SLA for resolution but defines no resolution mechanism. This must be defined before the payment hold path can be fully implemented. At minimum, an admin notification channel and a DB-level resolution workflow must be specified.
- OQ-10 [SPEC GAP — BLOCKS F-04, F-06]: WER compensation algorithm is unspecified. The blueprint states 'WER compensation logic applied post-transcription' with a 0.75 confidence threshold. Is this: (a) Deepgram's per-word confidence score, (b) a custom re-scoring pass against a trade vocabulary, or (c) a post-processing heuristic? This must be defined before F-04 (signal detection) and F-06 (gate exclusion logic) can be implemented.
- OQ-11 [SCHEMA DELTA — BLOCKS ALL MIGRATIONS]: Phase-1 migration (001_kv_phase1_foundation.sql) defines users + expert_profiles (two tables), skus (basic, no capture-status fields), capture_sessions (simplified, no session_context JSONB or score_object), session_answers, follow_up_questions. The blueprint introduces: experts (single table replacing users+expert_profiles), products (extends skus with has_surge_badge, is_fully_captured, spec_sheet_url), product_pre_seeds, knowledge_chunks (with pgvector 1536-dim), virtual_experts, payments, companies, company_knowledge_access, field_queries. Developer must produce a migration strategy document (additive migration preferred — no drop-and-recreate) before any new migration is written.
Machine-readable source fields
main
KnowledgeVault AI v1
draft
No items captured.
| id | name | done when | scenarios | description | error states |
|---|---|---|---|---|---|
| F-01 | Expert Product Queue | - GET /api/products/queue filters to products where at least one tag or category matches expert.specialties[] AND is_fully_captured=false - Results ordered: has_surge_badge=true first, then ascending coverage_pct (lowest gap first), then created_at DESC - Each product card renders: name, SKU, manufacturer, status indicator (red/yellow/green), earnings_potential range, surge badge when applicable - API p95 response time ≤2000ms on standard Supabase tier - Empty state renders correctly for no-specialty and all-captured scenarios - Expert JWT required — unauthenticated request returns 401 | - Expert with matching specialties sees filtered queue: name: string, then: string, when: string, given: string - Surge badge products appear first: name: string, then: string, when: string, given: string - Fully captured products excluded from queue: name: string, then: string, when: string, given: string - Expert with no specialties sees empty state with CTA: name: string, then: string, when: string, given: string - All matching products fully captured renders completion state: name: string, then: string, when: string, given: string | WHY: Experts need a curated, prioritized list of SKUs matching their specialty so they spend capture time on high-value, knowledge-gap products — not fully-covered or irrelevant SKUs. TODAY: Phase-1 migration has skus table and expert_profiles with specializations[]. No queue endpoint or UI exists. TARGET: Authenticated mobile screen backed by GET /api/products/queue. Returns products filtered by expert.specialties[] (matching products.category or tags), ordered by has_surge_badge DESC then by coverage gap (lowest coverage_pct first) then created_at DESC. Only products where is_fully_captured= | - Expert JWT expired → redirect to login; deep-link returns to /queue on re-auth - Network timeout → last-cached queue shown with stale-data indicator and Retry button - API 500 → error boundary renders with 'Something went wrong — try again' and retry; does not crash the app - products table empty → empty state 'No products in catalog yet' with admin-contact link |
| F-02 | Voice Capture Session | - CaptureSession created within 500ms of expert tapping START - First question fires within 3s of START (requires ProductPreSeed cache hit — all 80 demo SKUs pre-seeded) - Deepgram Nova-2 WebSocket streaming active throughout session duration - capture_sessions.session_context updated after each answered question — not only at session close - Session closes on coverage ≥ 85% or expert DONE tap; completed_at written; status = complete - Micro-objects flushed to knowledge_chunks (is_published=false) on close - SharedExpertContext includes wer_compensation_applied=true when Deepgram confidence fe | - Expert starts session and first question fires from pre-seed: name: string, then: string, when: string, given: string - Expert voice answer transcribed and context written to DB: name: string, then: string, when: string, given: string - Session auto-closes at 85% knowledge surface coverage: name: string, then: string, when: string, given: string - Expert manually taps DONE before 85% coverage: name: string, then: string, when: string, given: string - Crashed session resumes from last written context: name: string, then: string, when: string, given: string | WHY: The core knowledge extraction mechanism. An AI-conducted voice interview extracts tacit expert knowledge that a form cannot — the AI detects signals and digs where it matters. TODAY: Phase-1 has capture_sessions (simplified: status, transcript, recording_storage_path), session_answers, and follow_up_questions tables. No voice streaming, no GPT-4o integration, no SharedExpertContext, no real-time extraction. TARGET: Expert taps product → taps START → CaptureSession created (status: in_progress) → SharedExpertContext constructed from ProductPreSeed cache → GPT-4o initialized with system pro | - ProductPreSeed cache miss → Gemini runs synchronously; if Gemini unavailable → session blocked with 'Product not ready — try again shortly'; expert returned to queue - Deepgram fails to connect → session cannot start; 'Microphone connection failed — check your connection' shown with Retry - Deepgram disconnects mid-session → transcript paused; reconnect attempted twice within 10s; if both fail → session suspended, partial context saved, expert prompted to resume - GPT-4o timeout on question generation → wait up to 5s; fallback to next base question; signal logged; session continues - DB writ |
| F-03 | Real-Time Structuring Panel | - Micro-object cards appear within 3 seconds of GPT-4o completing extraction from an answer segment - All 8 chunk_type values render with correct human-readable labels: failure_mode='Failure Mode', symptom='Symptom', fix='Fix', psi_range='Pressure/Spec Range', safety_warning='Safety Warning', field_tip='Field Tip', tool_required='Tool Required', part_number_reference='Part Number' - Earnings meter updates on each new card showing estimated cumulative earnings (base rate, not final) - Panel is scrollable when card count exceeds viewport height - Cards persist from first extraction to session cl | - Failure mode extracted mid-speech appears as labeled card: name: string, then: string, when: string, given: string - Multiple chunk types extracted from one answer render as separate cards: name: string, then: string, when: string, given: string - Earnings meter ticks on chunk extraction: name: string, then: string, when: string, given: string - Structuring panel cards persist across follow-up questions: name: string, then: string, when: string, given: string | WHY: The key visual moment in the demo. Executives watch tacit knowledge become structured, searchable assets in real time as the expert speaks. This is the product truth — not a transcript dump. TODAY: Nothing exists. TARGET: During a capture session, a left-side structuring panel renders micro-object cards as GPT-4o extracts them from the live transcript. Card types: failure_mode, symptom, fix, psi_range, safety_warning, field_tip, tool_required, part_number_reference. Each card shows: chunk_type label (human-readable, e.g. 'Failure Mode'), content text, confidence indicator. Cards animate i | - GPT-4o fails to extract any micro-objects from an answer → no card appears; transcript segment stored verbatim; no earnings increment for that segment; no error shown to expert (silent degradation) - Panel WebSocket/SSE drops → cards stop appearing; 'Structuring paused — reconnecting' indicator shown; extraction resumes on reconnect; server-side chunks buffered during disconnect (no data lost) - chunk_type value not in known enum → card rendered with label 'Other'; logged server-side for schema review; no user-facing error |
| F-04 | AI Follow-Up Question Engine | - Hesitation and slang signal types correctly detected in integration testing against sample transcripts - All five signal types (hesitation, correction, slang, implicit_knowledge, safety_hedge) logged in audio_signals_flagged with transcript_offset_ms and triggered_followup_id - Follow-up fires as the next question within 2 seconds of signal detection - conversation_arc.followups_fired enforced at exactly 4 — never exceeded across all sessions - Single-level enforcement confirmed: follow-up answer cannot trigger another follow-up (automated test required) - All follow-up questions are context | - Hesitation signal triggers contextual follow-up: name: string, then: string, when: string, given: string - Follow-up depth budget exhausted — no more follow-ups fire: name: string, then: string, when: string, given: string - Follow-up answer cannot trigger a second-level follow-up: name: string, then: string, when: string, given: string - Slang signal detected — follow-up validates the term: name: string, then: string, when: string, given: string | WHY: The AI-first differentiator. A form gets surface answers. GPT-4o detects signals in hesitation, correction, slang, implicit knowledge, and safety hedges — and digs deeper. No human interviewer can do this at scale for every SKU in every distributor catalog. TODAY: Phase-1 has a follow_up_questions table (source: ai|curator|expert, status: pending|answered|dismissed, priority 1–10). No signal detection, no GPT-4o, no real-time firing. TARGET: During a capture session, GPT-4o analyzes each transcript segment for five signal types: hesitation, correction, slang, implicit_knowledge, safety_he | - GPT-4o returns malformed follow-up (not a question, empty, or >200 chars) → follow-up discarded; next base question fires; signal logged with triggered_followup_id=null; followups_fired not incremented - GPT-4o times out generating follow-up → after 3s fallback timer, next base question fires; followups_fired not incremented for the missed follow-up - False positive signal detection (irrelevant follow-up fires) → no automated correction; expert answers normally; logged for model tuning |
| F-05 | Post-Session Evaluation (o3) | - Evaluation queue receives serialized SharedExpertContext within 5s of session.complete - o3 returns complete score_object with all 7 dimension scores within 120s of queue dispatch - capture_sessions.score_object populated; evaluated_at written - model_version stamped in every score_object - Payment record created only after both o3 AND ground-truth gate results available - safety_claims_flagged non-empty → payment_hold triggered unconditionally | - o3 evaluates session across all 7 dimensions: name: string, then: string, when: string, given: string - Score object written within 120s of session.complete: name: string, then: string, when: string, given: string - Safety claim triggers payment_hold regardless of composite score: name: string, then: string, when: string, given: string - Payment record created only after evaluation AND gate both complete: name: string, then: string, when: string, given: string | WHY: AI-driven quality scoring is what makes gig economics viable at scale — it pays for signal, not volume, and is the quality judge experts must trust or they leave. TODAY: Nothing exists for AI scoring. Phase-1 has session_answers.is_reviewed flag only. TARGET: On session.complete, the full SharedExpertContext is serialized and pushed to an async evaluation queue (Supabase Edge Function). o3 receives the full context — every question with its corresponding transcript segment (joined on question_sequence), adjacent audio_signals_flagged, and conversation_arc. o3 scores all 7 dimensions, outp | - o3 API timeout after 120s → retry once; if second attempt fails → capture_sessions.status = 'evaluation_failed'; admin alerted; payment not created; expert notified 'Scoring delayed — we will notify you shortly' - o3 returns score_object missing one or more dimension scores → score rejected; evaluation retried; if retry fails → admin alert with full context; payment blocked - Evaluation queue backlog exceeds 200s average wait → SLA breach alert to admin; expert receives 'Scoring in progress' notification |
| F-06 | Ground-Truth Verification Gate | - Gate runs for every KnowledgeChunk where novelty_score > 0.5, chunk_type=safety_warning, or content contains numeric specifications - No payment_modifier > 1.0 fires on any chunk without a completed gate result (gate is blocking for payment calculation) - ground_truth_verified written to each evaluated chunk - Safety claim contradiction → payment_hold status on capture_sessions; admin alert sent - Numeric spec outside ±15% of spec sheet value → same payment_hold path as safety contradiction - Deepgram confidence < 0.75 on flagged term → excluded from novelty; base rate applied - o3 model ver | - Novel term verified — rarity bonus fires: name: string, then: string, when: string, given: string - Novel term fails verification — base rate only: name: string, then: string, when: string, given: string - Safety claim contradicts trade code — payment hold triggered: name: string, then: string, when: string, given: string - Numeric spec within ±15% of spec sheet value — verified: name: string, then: string, when: string, given: string - Low-confidence WER term excluded from novelty evaluation: name: string, then: string, when: string, given: string | WHY: Novel terms earn a rarity bonus. Without verification, that incentivizes jargon invention. The gate ensures only genuinely novel, verified knowledge earns premium pay — making the scoring ungameable and experts trusting the system is fair. TODAY: Nothing exists. TARGET: Runs in parallel with o3 scoring after session.complete. Triggers on any KnowledgeChunk where: novelty_score > 0.5 (from o3 first-pass) OR chunk_type = safety_warning OR content contains numeric specifications (pressure values, torque specs, temperature ranges). For each triggered chunk, o3 cross-checks against three sourc | - Spec sheet URL unreachable (404 or timeout) → gate runs on remaining two source classes only; if both unavailable, chunk defaults to base rate (ground_truth_verified=false); event logged for admin - ERP master data unavailable → gate skips ERP source; proceeds with spec sheet + trade codes; result still valid - Trade code index not populated for product category → chunk defaults to base rate; admin alerted to seed missing category before payments go live - o3 gate call times out → chunk defaults to base rate; rarity bonus not awarded; if chunk_type=safety_warning → payment_hold fires regardl |
| F-07 | Payment Calculation & Release | - Payment formula implemented exactly per spec: total = (base × quality_multiplier) + rarity_bonus + surge_bonus + completion_bonus - Payment record created only after both o3 score_object AND ground-truth gate results available - hold_until = evaluated_at + exactly 24 hours (not session started_at or completed_at) - quality_multiplier hard-capped at 1.0 in code — no payment exceeds base × 1.0 from quality alone - surge_bonus fired only when COUNT(prior completed sessions for this product) = 0 - pg_cron job runs at minimum hourly; initiates Stripe transfer for all pending_release payments past | - Standard session payment calculated correctly: name: string, then: string, when: string, given: string - Verified novel term adds rarity bonus: name: string, then: string, when: string, given: string - First capture on surge badge product earns surge bonus: name: string, then: string, when: string, given: string - Session causing full capture earns completion bonus: name: string, then: string, when: string, given: string - pg_cron releases payment after 24h hold: name: string, then: string, when: string, given: string - Stripe transfer.paid webhook marks payment released: name: string, then: | WHY: Expert compensation must be transparent, deterministic, automated, and ungameable. The payment model is what brings experts back — quality earns more than volume. TODAY: No payment model, no Stripe integration, no payments table in phase-1 migration. TARGET: After o3 evaluation and ground-truth gate both complete, calculate: base_amount = $2.00 × questions_answered; quality_multiplier = composite_score / 100 (range: 0.0–1.0, capped at 1.0); rarity_bonus = count(chunks where ground_truth_verified=true AND novelty_score > 0.5) × $0.50; surge_bonus = $2.00 if product.has_surge_badge=true AND | - Expert has no stripe_account_id → payment record created with status=payment_hold; expert notified to complete Stripe Connect onboarding; payment retried after stripe account.updated webhook fires with payouts_enabled=true - Stripe transfer fails → retry 3× with exponential backoff (5min, 15min, 45min); after 3 failures → admin alert; payment.status → stripe_failed - payment_hold session (safety claim) → non-disputed chunks released after 48h if admin review not completed; disputed chunks held until admin DB resolution - quality_multiplier > 1.0 returned by o3 (scoring bug) → cap at 1.0; ano |
| F-08 | Expert Signup & Stripe Onboarding | - Signup form renders on 375px viewport with no horizontal scroll; all form controls accessible via mobile keyboard - POST /api/experts/signup creates expert record and returns stripe_onboarding_url on valid input - Duplicate email returns HTTP 409 with user-actionable message - Required field validation enforced client-side AND server-side (not client-only) - specialties field is predefined multi-select — free text input not accepted - Stripe account.updated webhook correctly writes experts.stripe_account_id - Expert redirected to /queue after completing Stripe onboarding | - Valid signup creates expert record and returns Stripe onboarding URL: name: string, then: string, when: string, given: string - Stripe Connect completion populates stripe_account_id: name: string, then: string, when: string, given: string - Duplicate email rejected with descriptive error: name: string, then: string, when: string, given: string - Required field missing — inline validation before submission: name: string, then: string, when: string, given: string | WHY: Experts — retiring tradespeople, many not tech-savvy — must have a mobile-first, low-friction signup that establishes their payment pathway in the same flow. Friction here means lost experts. TODAY: Phase-1 has users + expert_profiles tables. No signup page, no Stripe Connect integration. TARGET: Mobile-first onboarding page (optimized at 375px). Required fields: name, email, role_type (from predefined list: plumber, electrician, HVAC tech, pipefitter, pipefitter, industrial maintenance, other), specialties (multi-select from predefined list, minimum 1). Optional: phone, years_experience | - Stripe API unavailable during signup → expert record created; stripe_account_id=null; expert shown 'Account created — complete payment setup later' with link to retry Stripe flow; retried when Stripe recovers - Invalid email format → client-side validation catches before network request - years_experience submitted as non-numeric → sanitized to null server-side; no error shown - Stripe onboarding URL expires (7-day TTL) before expert completes it → expert can request a new link from account settings page |
| F-09 | Field Tech Semantic Search | - POST /api/query returns top-5 results within 2s p95 for standard query load - Ranking formula applied correctly: (cosine_similarity × 0.7) + (ground_truth_verified::int × 0.2) + (novelty_score × 0.1) - Only is_published=true chunks searchable - field_verified_badge fires if and only if captured_at > now() - interval '60 minutes' - sku_filter, chunk_type_filter, verified_only correctly scope results when provided - All queries logged to field_queries with query_text, company_id, results, created_at - Company JWT required — unauthenticated → 401 | - Semantic query returns relevant chunks with expert attribution: name: string, then: string, when: string, given: string - SKU filter scopes results to one product: name: string, then: string, when: string, given: string - field_verified_badge fires for recently captured chunk: name: string, then: string, when: string, given: string - verified_only=true excludes unverified chunks: name: string, then: string, when: string, given: string - No matching chunks returns 200 with empty results: name: string, then: string, when: string, given: string | WHY: The demand side of the marketplace. Field technicians need instant, accurate answers from captured expert knowledge — on a job site, under pressure. This is the product's promise: expert knowledge available the moment it is captured. TODAY: Nothing exists. Phase-1 has known_terms table (manual/imported) but no pgvector, no embedding pipeline. TARGET: POST /api/query. Accepts: query (text, required), sku_filter (string, optional), chunk_type_filter (string, optional), verified_only (boolean, optional, default false). Query embedded via text-embedding-3-small (1536d). pgvector cosine simila | - OpenAI embedding API failure → HTTP 503 'Search temporarily unavailable'; no stale or unchecked results returned - pgvector index unavailable → HTTP 503; error logged; on-call alert - Company JWT invalid or expired → HTTP 401 - query text empty → HTTP 400 'Query text is required' - query text > 500 chars → truncated to 500 server-side; no user-facing error |
| F-10 | Virtual Expert Query | - POST /api/virtual-experts/:id/query returns 404 for is_published=false experts — server-side enforcement - Top-3 chunks retrieved scoped to expert_id — no cross-expert contamination (automated test) - GPT-4o system prompt contains explicit fabrication prohibition ('Do not fabricate' or equivalent) — verified in unit test that inspects the prompt - field_verified_badge fires if and only if any source chunk captured_at within 60 minutes — identical to F-09 logic - sources array in response includes chunk_id, content, chunk_type, captured_at for all top-3 chunks - Low-similarity fallback (all s | - Field tech asks Bob a question and receives first-person answer: name: string, then: string, when: string, given: string - field_verified_badge fires when source chunk is recent: name: string, then: string, when: string, given: string - Unpublished VirtualExpert returns 404: name: string, then: string, when: string, given: string - No matching chunks — GPT-4o declines to fabricate: name: string, then: string, when: string, given: string | WHY: Field techs do not want a search engine — they want to ask an expert they trust. 'Ask Bob' creates a named, trusted relationship. The 'field-verified 3 minutes ago' badge is the demo gut-punch: the expert just answered and the field tech has it instantly. This is the product's emotional core. TODAY: Nothing exists. TARGET: POST /api/virtual-experts/:id/query. VirtualExpert must be is_published=true (published only after ≥3 sessions with avg composite_score ≥70). Top-3 knowledge_chunks retrieved via pgvector cosine search scoped to knowledge_chunks WHERE expert_id = virtual_expert.expert_i | - GPT-4o API failure → HTTP 503; no cached or hallucinated answer returned - VirtualExpert id not found in DB → HTTP 404 - Expert has no published chunks at all → 'Bob hasn't captured any knowledge yet' — not a 500 - cosine similarity all < 0.3 → low-similarity fallback triggered; GPT-4o not called with low-confidence context |
| F-11 | Company Coverage Dashboard | - API response ≤2000ms p95 — enforced by materialized view; confirmed no vector queries in dashboard path - Demo mode (company_id=demo) returns HTTP 200 with no Authorization header - coverage_grid status: 0% → red, 1–89% → yellow, 90–100% → green — threshold logic confirmed in unit tests - slang_cloud terms sourced from field_queries and knowledge_chunk content, verified flag reflecting ground_truth_verified - top_queries from field_queries table scoped to company access - hours_preserved = SUM(session.duration_s) / 3600 for company's products - Authenticated (non-demo) endpoint scopes all da | - Authenticated company sees their coverage grid: name: string, then: string, when: string, given: string - Demo mode loads without authentication: name: string, then: string, when: string, given: string - Dashboard loads under 2 seconds: name: string, then: string, when: string, given: string - Analytics panel displays preserved hours and ROI gauge: name: string, then: string, when: string, given: string | WHY: AD distribution executives — the primary sales target — need to see business value at a glance. Coverage by SKU, what terms field techs search, ROI signal. This justifies the purchase decision. The demo version must load instantly on a projected screen — no spinner, no loading state. TODAY: Nothing exists. TARGET: GET /api/dashboard/company/:id. Returns: coverage_grid (array of {sku, name, status: 'red'|'yellow'|'green', coverage_pct}), slang_cloud (array of {term, frequency, verified: bool}), top_queries (array of {query_text, count}), stats ({hours_preserved: number, support_call_reduct | - Materialized view stale (last refresh > 5 minutes) → return stale data with X-Data-Age response header; trigger async refresh; do not block response - Materialized view unavailable → HTTP 503 - Demo seed data not found → HTTP 404 'Demo data not available'; production data must never be returned for company_id=demo - Authenticated company has no entries in company_knowledge_access → empty coverage_grid with guidance message; not a 500 |
| F-12 | SKU Pre-Seed Generation (Gemini) | - product.added event triggers pre-seed generation within 60s for all new products - product_pre_seeds row contains question_set (3–5 questions), context_summary with all six sub-fields, key_vectors, model_version, generated_at - model_version format: 'gemini-2.5-pro-YYYY-MM' on every row - Cache hit returns pre-seed in <200ms (DB read only — confirmed no Gemini call in hot path) - 30-day TTL enforced; spec_sheet_url update triggers regeneration regardless of TTL - Batch script pre-seeds all 80 demo SKUs without error; completion verified before presentation date - UNIQUE constraint on product | - New product triggers pre-seed generation: name: string, then: string, when: string, given: string - Cache hit on session start loads pre-seed in under 200ms: name: string, then: string, when: string, given: string - Stale cache triggers regeneration: name: string, then: string, when: string, given: string - Spec sheet URL update triggers immediate regeneration: name: string, then: string, when: string, given: string - All 80 demo SKUs pre-seeded before presentation: name: string, then: string, when: string, given: string | WHY: Gemini 2.5 Pro's multimodal depth creates deeply calibrated, SKU-specific question sets that no generalist prompt produces. Running it offline eliminates real-time Gemini latency from the interview path — the expert's first question fires instantly. TODAY: Nothing exists. Phase-1 migration has skus table (name, sku_code, category, manufacturer, metadata JSONB) — maps to products but lacks spec_sheet_url, image_url, has_surge_badge, is_fully_captured. TARGET: Triggered by product.added or product.spec_updated event. Gemini 2.5 Pro receives: product.image_url, product.description, product.s | - Gemini API unavailable during product.added → event retried with exponential backoff; product visible in catalog but flagged 'pre-seed pending'; session for this product blocked until pre-seed exists - spec_sheet_url returns 404 or times out → Gemini runs on image + catalog description only; pre-seed created with flag in key_vectors (spec_sheet_missing: true); event logged for admin - image_url missing → Gemini runs on catalog description + spec sheet only; pre-seed created; flagged image_missing in key_vectors - Gemini returns fewer than 3 questions → pre-seed rejected; retry once; if retry |
- OQ-1 [EXEC DECISION REQUIRED — BLOCKS F-11]: How is BezelIQ's build speed and cost surfaced in the demo — hidden meta-page, live cost counter in the analytics panel, or talking point only? Blueprint treats it as a talking point pending this decision. If a UI component is required, F-11 scope expands.
- OQ-2 [EXEC DECISION REQUIRED — BLOCKS SELLING PAGE]: AD reseller model — white-label (AD branding only), powered-by badge (KnowledgeVault brand visible), or full private label (no BezelIQ/KnowledgeVault branding)? Determines branding scope for selling page and company dashboard. Does not affect schema.
- OQ-3 [CONFIRM V2 SCOPE]: Mobile offline support — blueprint assumes online-only for demo. Is offline audio buffering and queue sync confirmed out of scope for demo? If moved in, F-02 scope expands significantly.
- OQ-4 [CONFIRM V2 SCOPE]: Voice cloning for Virtual Expert (Gemini suggestion) is scoped to V2. Confirm: voice-cloned audio is out of demo scope. If moved in, F-10 expands and ElevenLabs API key required.
- OQ-5 [CONFIRM V2 SCOPE]: Counter-sales integration (Virtual Expert surfaces reorder recommendation) is scoped to V2. Confirm no counter-sales UI or API endpoint needed for demo.
- OQ-6 [EXEC DECISION REQUIRED — BLOCKS INFRA]: Is the selling page on a separate domain (e.g. knowledgevaultai.com) or a subdirectory of the app domain? Affects Cloudflare Pages routing, DNS setup, and CORS configuration. Must be decided before any deployment work begins.
- OQ-7 [SPEC GAP — BLOCKS F-01, F-07, F-11]: How is coverage_pct computed per product? The blueprint references it in queue ordering, dashboard grid (red/yellow/green), and the is_fully_captured flip condition (≥90%), but no computation formula is specified. Is it: (a) count(completed sessions) / target_session_count, (b) average composite_score across sessions, (c) a coverage of the knowledge_surface vectors from the pre-seed? This must be defined before migrations and queue API can be implemented.
- OQ-8 [SPEC GAP — BLOCKS F-09, RLS]: field_queries.company_id is an FK to companies, but it is nullable. Is an unauthenticated (no company JWT) semantic query permitted in any context, or is a company JWT always required? This determines RLS policy on field_queries and whether the search endpoint has a public mode.
- OQ-9 [SPEC GAP — BLOCKS F-06, F-07]: Is there an admin UI for payment_hold manual review, or is v1 admin resolution handled via direct DB update only? The blueprint specifies a 48h SLA for resolution but defines no resolution mechanism. This must be defined before the payment hold path can be fully implemented. At minimum, an admin notification channel and a DB-level resolution workflow must be specified.
- OQ-10 [SPEC GAP — BLOCKS F-04, F-06]: WER compensation algorithm is unspecified. The blueprint states 'WER compensation logic applied post-transcription' with a 0.75 confidence threshold. Is this: (a) Deepgram's per-word confidence score, (b) a custom re-scoring pass against a trade vocabulary, or (c) a post-processing heuristic? This must be defined before F-04 (signal detection) and F-06 (gate exclusion logic) can be implemented.
- OQ-11 [SCHEMA DELTA — BLOCKS ALL MIGRATIONS]: Phase-1 migration (001_kv_phase1_foundation.sql) defines users + expert_profiles (two tables), skus (basic, no capture-status fields), capture_sessions (simplified, no session_context JSONB or score_object), session_answers, follow_up_questions. The blueprint introduces: experts (single table replacing users+expert_profiles), products (extends skus with has_surge_badge, is_fully_captured, spec_sheet_url), product_pre_seeds, knowledge_chunks (with pgvector 1536-dim), virtual_experts, payments, companies, company_knowledge_access, field_queries. Developer must produce a migration strategy document (additive migration preferred — no drop-and-recreate) before any new migration is written.
A phase-1 foundation migration exists at apps/kv-app/supabase/migrations/001_kv_phase1_foundation.sql. It defines: users (with supabase_auth_id), expert_profiles, skus, capture_sessions (simplified), session_answers, follow_up_questions, known_terms. The blueprint schema introduces significant additions: experts (consolidating users+expert_profiles into one entity), virtual_experts, products (extends skus with capture-status fields), product_pre_seeds, knowledge_chunks (with pgvector 1536-dim embedding), payments, field_queries, companies, company_knowledge_access. Developers must reconcile the existing migration with the blueprint schema before writing new migrations — additive migration path preferred, not replace-and-recreate.