plan-module · supabase_json
KnowledgeVault AI plan-module M-08
plan-module artifact · for KnowledgeVault AI · phase M-08 · status draft
tasks
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0008_field_queries_ve.sql — SQL migration ensuring field_queries has the columns needed for virtual expert query logging (answer, sources, field_verified_badge, virtual_expert_id, queried_at).
IMPORTANT: Check if field_queries already exists from M-07 migration (0007_field_queries.sql). If it does:
- Use ALTER TABLE field_queries ADD COLUMN IF NOT EXISTS for each missing column
- Do NOT recreate the table
If field_queries does NOT exist, create it fresh with CREATE TABLE IF NOT EXISTS.
**Target schema (final state after both M-07 and | Create field_queries virtual-expert columns migration | - type: file - evaluate: SQL migration is fully idempotent using ADD COLUMN IF NOT EXISTS for columns that may already exist from M-07; final field_queries schema includes virtual_expert_id FK (NOT NULL, REFERENCES virtual_experts CASCADE), company_id FK, query_text NOT NULL, answer nullable with -- nullable comment, sources jsonb NOT NULL DEFAULT '[]', field_verified_badge boolean NOT NULL DEFAULT false, queried_at and created_at NOT NULL; RLS enabled with company SELECT policy; no existing migration files modified (POL-ARCH-001, POL-DATA-002, POL-SEC-001). - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/route.ts — Next.js App Router API route listing all published virtual experts for company-authenticated field technicians.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (from M-07 — do not rewrite this middleware). Call it first per POL-SEC-002.
Request: GET, no body, no query parameters.
Success 200:
```json
{ "experts": [{ "id": "uuid", "name": "string", "role_type": "string", "description": "string|null", "session_count": "integer", "avg_accuracy_score": "float|null" | Implement GET /api/virtual-experts route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, returns 200 with experts array containing exactly 7 fields (id, name, role_type, description, session_count, avg_accuracy_score, knowledge_chunk_count), ordered by session_count DESC, filters to is_published=true only, returns 401 for missing/invalid JWT, returns 500 on DB error, no bare any (POL-STYLE-001). - min length: 100 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/[id]/query/route.ts — virtual expert query endpoint. Field technicians ask a named AI expert persona and receive a GPT-4o synthesised first-person answer.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (POL-SEC-002).
Request: POST, URL param :id (virtual expert uuid), body { query: string (required, 1-500 chars) }
Success 200:
```json
{ "answer": "string", "expert_name": "string", "role_type": "string", "sources": [{"chunk_id": "uuid", "content": "string", "chunk_typ | Implement POST /api/virtual-experts/[id]/query route | - type: file - evaluate: Route performs all 12 steps in order (auth → validate → sanitize per POL-SEC-003 → fetch expert → embed → pgvector search → zero-chunk short-circuit without GPT-4o call → GPT-4o with 'Do not fabricate' in system prompt → field_verified_badge → field_queries INSERT via service role → return 200), correct HTTP codes for all error cases, typed catch on every async call (POL-STYLE-002), no bare any (POL-STYLE-001), POL-ARCH-003 for service key. - min length: 350 | - 1 - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/(company)/experts/page.tsx — React page displaying published virtual experts for company-authenticated field technicians to select.
Behavior:
- Fetch GET /api/virtual-experts on mount
- Render 4 ExpertCardSkeleton components (from src/components/experts/ExpertCardSkeleton.tsx) immediately while loading
- On response: render ExpertCard components (from src/components/experts/ExpertCard.tsx) in a CSS grid: 2 columns at ≥768px, 1 column at <768px
- On empty array: render empty state — icon + 'No experts available yet' headline + 'Check back soon | Implement VirtualExpertSelection screen | - type: file - evaluate: Page fetches GET /api/virtual-experts on mount, renders 4 ExpertCardSkeleton immediately, switches to ExpertCard grid (2-col ≥768px, 1-col <768px) on response, renders empty state ('No experts available yet') on empty array, PoweredByBadge always at bottom; ExpertCard has role=button and correct aria-label; companion components ExpertCard.tsx, ExpertCardSkeleton.tsx, PoweredByBadge.tsx exist in components/experts/; no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 5 | developer | Create apps/knowledgevault/src/app/(company)/experts/[id]/page.tsx — React Client Component for field technicians to query a specific virtual expert and read their synthesised first-person answer.
On mount: Fetch expert detail (use GET /api/virtual-experts and filter by [id]). Render ExpertIdentityHeader skeleton immediately.
ExpertIdentityHeader (component src/components/experts/ExpertIdentityHeader.tsx):
- <h1> with expert name
- Subtitle: role_type, formatted accuracy score
- Back link to /experts
QueryInput (component src/components/experts/QueryInput.tsx):
- | Implement VirtualExpertChat screen | - type: file - evaluate: Page renders ExpertIdentityHeader skeleton on mount, QueryInput placeholder reads 'Ask <first_name> anything', submitting shows AnswerCard loading skeleton before response, 200 response renders answer with 'Based on N captured answers' expandable sources (content, chunk_type, sku, time-ago per source), field_verified_badge renders 'Field-verified N minutes ago' when true and is absent when false, 503 renders unavailable message with Try again button that re-submits last query, back link returns to /experts, companion components ExpertIdentityHeader/QueryInput/AnswerCar | - 3 |
Agent Handoff
Start Here
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0008_field_queries_ve.sql — SQL migration ensuring field_queries has the columns needed for virtual expert query logging (answer, sources, field_verified_badge, virtual_expert_id, queried_at).
IMPORTANT: Check if field_queries already exists from M-07 migration (0007_field_queries.sql). If it does:
- Use ALTER TABLE field_queries ADD COLUMN IF NOT EXISTS for each missing column
- Do NOT recreate the table
If field_queries does NOT exist, create it fresh with CREATE TABLE IF NOT EXISTS.
**Target schema (final state after both M-07 and | Create field_queries virtual-expert columns migration | - type: file - evaluate: SQL migration is fully idempotent using ADD COLUMN IF NOT EXISTS for columns that may already exist from M-07; final field_queries schema includes virtual_expert_id FK (NOT NULL, REFERENCES virtual_experts CASCADE), company_id FK, query_text NOT NULL, answer nullable with -- nullable comment, sources jsonb NOT NULL DEFAULT '[]', field_verified_badge boolean NOT NULL DEFAULT false, queried_at and created_at NOT NULL; RLS enabled with company SELECT policy; no existing migration files modified (POL-ARCH-001, POL-DATA-002, POL-SEC-001). - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/route.ts — Next.js App Router API route listing all published virtual experts for company-authenticated field technicians.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (from M-07 — do not rewrite this middleware). Call it first per POL-SEC-002.
Request: GET, no body, no query parameters.
Success 200:
```json
{ "experts": [{ "id": "uuid", "name": "string", "role_type": "string", "description": "string|null", "session_count": "integer", "avg_accuracy_score": "float|null" | Implement GET /api/virtual-experts route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, returns 200 with experts array containing exactly 7 fields (id, name, role_type, description, session_count, avg_accuracy_score, knowledge_chunk_count), ordered by session_count DESC, filters to is_published=true only, returns 401 for missing/invalid JWT, returns 500 on DB error, no bare any (POL-STYLE-001). - min length: 100 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/[id]/query/route.ts — virtual expert query endpoint. Field technicians ask a named AI expert persona and receive a GPT-4o synthesised first-person answer.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (POL-SEC-002).
Request: POST, URL param :id (virtual expert uuid), body { query: string (required, 1-500 chars) }
Success 200:
```json
{ "answer": "string", "expert_name": "string", "role_type": "string", "sources": [{"chunk_id": "uuid", "content": "string", "chunk_typ | Implement POST /api/virtual-experts/[id]/query route | - type: file - evaluate: Route performs all 12 steps in order (auth → validate → sanitize per POL-SEC-003 → fetch expert → embed → pgvector search → zero-chunk short-circuit without GPT-4o call → GPT-4o with 'Do not fabricate' in system prompt → field_verified_badge → field_queries INSERT via service role → return 200), correct HTTP codes for all error cases, typed catch on every async call (POL-STYLE-002), no bare any (POL-STYLE-001), POL-ARCH-003 for service key. - min length: 350 | - 1 - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/(company)/experts/page.tsx — React page displaying published virtual experts for company-authenticated field technicians to select.
Behavior:
- Fetch GET /api/virtual-experts on mount
- Render 4 ExpertCardSkeleton components (from src/components/experts/ExpertCardSkeleton.tsx) immediately while loading
- On response: render ExpertCard components (from src/components/experts/ExpertCard.tsx) in a CSS grid: 2 columns at ≥768px, 1 column at <768px
- On empty array: render empty state — icon + 'No experts available yet' headline + 'Check back soon | Implement VirtualExpertSelection screen | - type: file - evaluate: Page fetches GET /api/virtual-experts on mount, renders 4 ExpertCardSkeleton immediately, switches to ExpertCard grid (2-col ≥768px, 1-col <768px) on response, renders empty state ('No experts available yet') on empty array, PoweredByBadge always at bottom; ExpertCard has role=button and correct aria-label; companion components ExpertCard.tsx, ExpertCardSkeleton.tsx, PoweredByBadge.tsx exist in components/experts/; no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 5 | developer | Create apps/knowledgevault/src/app/(company)/experts/[id]/page.tsx — React Client Component for field technicians to query a specific virtual expert and read their synthesised first-person answer.
On mount: Fetch expert detail (use GET /api/virtual-experts and filter by [id]). Render ExpertIdentityHeader skeleton immediately.
ExpertIdentityHeader (component src/components/experts/ExpertIdentityHeader.tsx):
- <h1> with expert name
- Subtitle: role_type, formatted accuracy score
- Back link to /experts
QueryInput (component src/components/experts/QueryInput.tsx):
- | Implement VirtualExpertChat screen | - type: file - evaluate: Page renders ExpertIdentityHeader skeleton on mount, QueryInput placeholder reads 'Ask <first_name> anything', submitting shows AnswerCard loading skeleton before response, 200 response renders answer with 'Based on N captured answers' expandable sources (content, chunk_type, sku, time-ago per source), field_verified_badge renders 'Field-verified N minutes ago' when true and is absent when false, 503 renders unavailable message with Try again button that re-submits last query, back link returns to /experts, companion components ExpertIdentityHeader/QueryInput/AnswerCar | - 3 |
Completion Evidence
No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.
Artifact Shape
- notes: Reuses companyAuthMiddleware from M-07 (src/lib/auth/companyAuthMiddleware.ts). D-08-01 creates a field_queries migration with additional columns for virtual expert query context (answer, sources, field_verified_badge) — if field_queries already exists from M-07, this migration should ALTER TABLE to add the missing columns rather than CREATE TABLE. Quinn must check M-07 migration state before applying.
- tasks: 5 items
- module id: M-08
- module name: Virtual Expert Query
- schema version: 1.0
- depends on modules: 2 items
Structured Payload
Machine-readable source fields
notes
Reuses companyAuthMiddleware from M-07 (src/lib/auth/companyAuthMiddleware.ts). D-08-01 creates a field_queries migration with additional columns for virtual expert query context (answer, sources, field_verified_badge) — if field_queries already exists from M-07, this migration should ALTER TABLE to add the missing columns rather than CREATE TABLE. Quinn must check M-07 migration state before applying.
tasks
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0008_field_queries_ve.sql — SQL migration ensuring field_queries has the columns needed for virtual expert query logging (answer, sources, field_verified_badge, virtual_expert_id, queried_at).
IMPORTANT: Check if field_queries already exists from M-07 migration (0007_field_queries.sql). If it does:
- Use ALTER TABLE field_queries ADD COLUMN IF NOT EXISTS for each missing column
- Do NOT recreate the table
If field_queries does NOT exist, create it fresh with CREATE TABLE IF NOT EXISTS.
**Target schema (final state after both M-07 and | Create field_queries virtual-expert columns migration | - type: file - evaluate: SQL migration is fully idempotent using ADD COLUMN IF NOT EXISTS for columns that may already exist from M-07; final field_queries schema includes virtual_expert_id FK (NOT NULL, REFERENCES virtual_experts CASCADE), company_id FK, query_text NOT NULL, answer nullable with -- nullable comment, sources jsonb NOT NULL DEFAULT '[]', field_verified_badge boolean NOT NULL DEFAULT false, queried_at and created_at NOT NULL; RLS enabled with company SELECT policy; no existing migration files modified (POL-ARCH-001, POL-DATA-002, POL-SEC-001). - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/route.ts — Next.js App Router API route listing all published virtual experts for company-authenticated field technicians.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (from M-07 — do not rewrite this middleware). Call it first per POL-SEC-002.
Request: GET, no body, no query parameters.
Success 200:
```json
{ "experts": [{ "id": "uuid", "name": "string", "role_type": "string", "description": "string|null", "session_count": "integer", "avg_accuracy_score": "float|null" | Implement GET /api/virtual-experts route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, returns 200 with experts array containing exactly 7 fields (id, name, role_type, description, session_count, avg_accuracy_score, knowledge_chunk_count), ordered by session_count DESC, filters to is_published=true only, returns 401 for missing/invalid JWT, returns 500 on DB error, no bare any (POL-STYLE-001). - min length: 100 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/virtual-experts/[id]/query/route.ts — virtual expert query endpoint. Field technicians ask a named AI expert persona and receive a GPT-4o synthesised first-person answer.
Auth: Company JWT required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (POL-SEC-002).
Request: POST, URL param :id (virtual expert uuid), body { query: string (required, 1-500 chars) }
Success 200:
```json
{ "answer": "string", "expert_name": "string", "role_type": "string", "sources": [{"chunk_id": "uuid", "content": "string", "chunk_typ | Implement POST /api/virtual-experts/[id]/query route | - type: file - evaluate: Route performs all 12 steps in order (auth → validate → sanitize per POL-SEC-003 → fetch expert → embed → pgvector search → zero-chunk short-circuit without GPT-4o call → GPT-4o with 'Do not fabricate' in system prompt → field_verified_badge → field_queries INSERT via service role → return 200), correct HTTP codes for all error cases, typed catch on every async call (POL-STYLE-002), no bare any (POL-STYLE-001), POL-ARCH-003 for service key. - min length: 350 | - 1 - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/(company)/experts/page.tsx — React page displaying published virtual experts for company-authenticated field technicians to select.
Behavior:
- Fetch GET /api/virtual-experts on mount
- Render 4 ExpertCardSkeleton components (from src/components/experts/ExpertCardSkeleton.tsx) immediately while loading
- On response: render ExpertCard components (from src/components/experts/ExpertCard.tsx) in a CSS grid: 2 columns at ≥768px, 1 column at <768px
- On empty array: render empty state — icon + 'No experts available yet' headline + 'Check back soon | Implement VirtualExpertSelection screen | - type: file - evaluate: Page fetches GET /api/virtual-experts on mount, renders 4 ExpertCardSkeleton immediately, switches to ExpertCard grid (2-col ≥768px, 1-col <768px) on response, renders empty state ('No experts available yet') on empty array, PoweredByBadge always at bottom; ExpertCard has role=button and correct aria-label; companion components ExpertCard.tsx, ExpertCardSkeleton.tsx, PoweredByBadge.tsx exist in components/experts/; no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 5 | developer | Create apps/knowledgevault/src/app/(company)/experts/[id]/page.tsx — React Client Component for field technicians to query a specific virtual expert and read their synthesised first-person answer.
On mount: Fetch expert detail (use GET /api/virtual-experts and filter by [id]). Render ExpertIdentityHeader skeleton immediately.
ExpertIdentityHeader (component src/components/experts/ExpertIdentityHeader.tsx):
- <h1> with expert name
- Subtitle: role_type, formatted accuracy score
- Back link to /experts
QueryInput (component src/components/experts/QueryInput.tsx):
- | Implement VirtualExpertChat screen | - type: file - evaluate: Page renders ExpertIdentityHeader skeleton on mount, QueryInput placeholder reads 'Ask <first_name> anything', submitting shows AnswerCard loading skeleton before response, 200 response renders answer with 'Based on N captured answers' expandable sources (content, chunk_type, sku, time-ago per source), field_verified_badge renders 'Field-verified N minutes ago' when true and is absent when false, 503 renders unavailable message with Try again button that re-submits last query, back link returns to /experts, companion components ExpertIdentityHeader/QueryInput/AnswerCar | - 3 |
module id
M-08
module name
Virtual Expert Query
schema version
1.0
depends on modules
- M-05
- M-07