plan-module · supabase_json
KnowledgeVault AI plan-module M-07
plan-module artifact · for KnowledgeVault AI · phase M-07 · status draft
tasks
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0007_field_queries.sql — SQL migration for the field_queries table, which persists every semantic search query submitted by company field technicians. This table is also consumed by M-09 (top_queries, slang_cloud analytics).
IMPORTANT: Check if field_queries was already created in M-00 migrations. If it exists, produce an ALTER TABLE migration adding any missing columns/indexes instead of CREATE TABLE. If it does not exist, create it with CREATE TABLE IF NOT EXISTS.
Table columns:
- `id uuid NOT NULL DEFAULT gen_random_uuid() PRI | Create field_queries table migration | - type: file - evaluate: SQL file creates or extends field_queries table with columns (id uuid PK, company_id uuid NOT NULL FK companies, query_text text NOT NULL, query_embedding vector(1536) nullable with -- nullable comment, results jsonb nullable with -- nullable comment, created_at timestamptz NOT NULL), creates 3 indexes (company_id, created_at DESC, IVFFlat on query_embedding per POL-PERF-001), enables RLS with company SELECT and service INSERT policies per POL-SEC-001, all statements idempotent per POL-ARCH-001. - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/lib/auth/companyAuthMiddleware.ts — reusable TypeScript server-only middleware that validates company JWTs for all company-authenticated routes in M-07, M-08, and M-09. Do not duplicate — all modules import from this single file.
Export: A single async function:
``typescript
export async function companyAuthMiddleware(
req: NextRequest
): Promise<{ companyId: string } | NextResponse>
`
If JWT valid and role correct → return { companyId: string }. If invalid → return a NextResponse error directly.
Logic:
1. Read Authorization: Bearer <jwt | Implement companyAuthMiddleware service | - type: file - evaluate: File exports a single async companyAuthMiddleware function, reads role from app_metadata (not user_metadata) with code comment, returns correct 401/403 response shapes for all 5 failure cases, calls supabase.auth.getUser() per POL-SEC-002, queries companies table to verify company exists, marked 'use server', no bare any (POL-STYLE-001), not importable from client bundle paths (POL-ARCH-003). - min length: 120 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/auth/company-login/route.ts — Next.js App Router API route that mints a Supabase Auth session for a company user. Public endpoint — no prior JWT required.
Request: POST body { company_id: string (uuid), demo_secret: string }
Success 200: { access_token: string, token_type: 'bearer', expires_in: 3600, company_id: string }
Error cases:
- Missing company_id field → 422 { error: 'company_id required', code: 'VALIDATION_ERROR', status: 422 }
- Invalid demo_secret (doesn't match COMPANY_DEMO_SECRET env var) → 401 `{ error: 'Invalid creden | Implement POST /api/auth/company-login route | - type: file - evaluate: Route handles all 5 error cases with correct status codes and error body shapes, mints a valid Supabase Auth JWT with app_metadata.role='company' and app_metadata.company_id set, returned token passes companyAuthMiddleware, COMPANY_DEMO_SECRET never appears in response/logs (POL-SEC-004), SUPABASE_SERVICE_KEY used server-side only (POL-ARCH-003), no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/api/query/route.ts — semantic search endpoint for company-authenticated field technicians.
Auth: Required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (call first — POL-SEC-002).
Request: POST body:
``json
{ "query": "string (required, 1-500 chars)", "sku_filter": "string (optional)", "chunk_type_filter": "string (optional)", "verified_only": "boolean (optional)" }
`
Success 200:
``json
{ "results": [{ "chunk_id", "content", "chunk_type", "sku", "expert_name", "ground_truth_verified", "captured_at", "relevance_ | Implement POST /api/query semantic search route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, validates query length (422 on >500 chars), calls OpenAI text-embedding-3-small, applies 3-factor ranking formula (cosine×0.7 + ground_truth×0.2 + novelty×0.1), returns top-5 with all 9 fields, logs to field_queries with silent swallow, returns results:[] (not 404) on no matches, returns 503+Retry-After on OpenAI failure, applies sanitizeInput per POL-SEC-003, no bare any (POL-STYLE-001). - min length: 300 | - 2 - 3 |
| 5 | developer | Create apps/knowledgevault/src/app/search/page.tsx — two-screen search UI (S-06-a and S-06-b) for company-authenticated field technicians.
S-06-a — KnowledgeSearchScreen (/search):
- Full-screen, prominent centered search bar (role='search', aria-label='Search knowledge base', placeholder='Ask anything about a product or process…')
- Optional SKU filter input below search bar
- Recent queries list (last 5 from GET /api/query/recent) — tapping prefills search bar
- Powered by KnowledgeVault badge at bottom (aria-label='Powered by KnowledgeVault')
- Submitting: POST /api/query with `{ | Implement Knowledge Search UI screens | - type: file - evaluate: S-06-a renders search bar with role=search and aria-label, SKU filter input, recent queries list from GET /api/query/recent, and Powered by badge; S-06-b renders sticky search bar, up to 5 result cards with all required fields (content, chunk_type badge, expert_name, ground_truth badge, field-verified badge), correct empty state text ('No answers yet'), error card with Retry CTA on timeout; GET /api/query/recent route exists at apps/knowledgevault/src/app/api/query/recent/route.ts returning 401 unauthenticated and array authenticated; back navigation returns to S-06-a | - 3 - 4 |
Agent Handoff
Start Here
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0007_field_queries.sql — SQL migration for the field_queries table, which persists every semantic search query submitted by company field technicians. This table is also consumed by M-09 (top_queries, slang_cloud analytics).
IMPORTANT: Check if field_queries was already created in M-00 migrations. If it exists, produce an ALTER TABLE migration adding any missing columns/indexes instead of CREATE TABLE. If it does not exist, create it with CREATE TABLE IF NOT EXISTS.
Table columns:
- `id uuid NOT NULL DEFAULT gen_random_uuid() PRI | Create field_queries table migration | - type: file - evaluate: SQL file creates or extends field_queries table with columns (id uuid PK, company_id uuid NOT NULL FK companies, query_text text NOT NULL, query_embedding vector(1536) nullable with -- nullable comment, results jsonb nullable with -- nullable comment, created_at timestamptz NOT NULL), creates 3 indexes (company_id, created_at DESC, IVFFlat on query_embedding per POL-PERF-001), enables RLS with company SELECT and service INSERT policies per POL-SEC-001, all statements idempotent per POL-ARCH-001. - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/lib/auth/companyAuthMiddleware.ts — reusable TypeScript server-only middleware that validates company JWTs for all company-authenticated routes in M-07, M-08, and M-09. Do not duplicate — all modules import from this single file.
Export: A single async function:
``typescript
export async function companyAuthMiddleware(
req: NextRequest
): Promise<{ companyId: string } | NextResponse>
`
If JWT valid and role correct → return { companyId: string }. If invalid → return a NextResponse error directly.
Logic:
1. Read Authorization: Bearer <jwt | Implement companyAuthMiddleware service | - type: file - evaluate: File exports a single async companyAuthMiddleware function, reads role from app_metadata (not user_metadata) with code comment, returns correct 401/403 response shapes for all 5 failure cases, calls supabase.auth.getUser() per POL-SEC-002, queries companies table to verify company exists, marked 'use server', no bare any (POL-STYLE-001), not importable from client bundle paths (POL-ARCH-003). - min length: 120 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/auth/company-login/route.ts — Next.js App Router API route that mints a Supabase Auth session for a company user. Public endpoint — no prior JWT required.
Request: POST body { company_id: string (uuid), demo_secret: string }
Success 200: { access_token: string, token_type: 'bearer', expires_in: 3600, company_id: string }
Error cases:
- Missing company_id field → 422 { error: 'company_id required', code: 'VALIDATION_ERROR', status: 422 }
- Invalid demo_secret (doesn't match COMPANY_DEMO_SECRET env var) → 401 `{ error: 'Invalid creden | Implement POST /api/auth/company-login route | - type: file - evaluate: Route handles all 5 error cases with correct status codes and error body shapes, mints a valid Supabase Auth JWT with app_metadata.role='company' and app_metadata.company_id set, returned token passes companyAuthMiddleware, COMPANY_DEMO_SECRET never appears in response/logs (POL-SEC-004), SUPABASE_SERVICE_KEY used server-side only (POL-ARCH-003), no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/api/query/route.ts — semantic search endpoint for company-authenticated field technicians.
Auth: Required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (call first — POL-SEC-002).
Request: POST body:
``json
{ "query": "string (required, 1-500 chars)", "sku_filter": "string (optional)", "chunk_type_filter": "string (optional)", "verified_only": "boolean (optional)" }
`
Success 200:
``json
{ "results": [{ "chunk_id", "content", "chunk_type", "sku", "expert_name", "ground_truth_verified", "captured_at", "relevance_ | Implement POST /api/query semantic search route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, validates query length (422 on >500 chars), calls OpenAI text-embedding-3-small, applies 3-factor ranking formula (cosine×0.7 + ground_truth×0.2 + novelty×0.1), returns top-5 with all 9 fields, logs to field_queries with silent swallow, returns results:[] (not 404) on no matches, returns 503+Retry-After on OpenAI failure, applies sanitizeInput per POL-SEC-003, no bare any (POL-STYLE-001). - min length: 300 | - 2 - 3 |
| 5 | developer | Create apps/knowledgevault/src/app/search/page.tsx — two-screen search UI (S-06-a and S-06-b) for company-authenticated field technicians.
S-06-a — KnowledgeSearchScreen (/search):
- Full-screen, prominent centered search bar (role='search', aria-label='Search knowledge base', placeholder='Ask anything about a product or process…')
- Optional SKU filter input below search bar
- Recent queries list (last 5 from GET /api/query/recent) — tapping prefills search bar
- Powered by KnowledgeVault badge at bottom (aria-label='Powered by KnowledgeVault')
- Submitting: POST /api/query with `{ | Implement Knowledge Search UI screens | - type: file - evaluate: S-06-a renders search bar with role=search and aria-label, SKU filter input, recent queries list from GET /api/query/recent, and Powered by badge; S-06-b renders sticky search bar, up to 5 result cards with all required fields (content, chunk_type badge, expert_name, ground_truth badge, field-verified badge), correct empty state text ('No answers yet'), error card with Retry CTA on timeout; GET /api/query/recent route exists at apps/knowledgevault/src/app/api/query/recent/route.ts returning 401 unauthenticated and array authenticated; back navigation returns to S-06-a | - 3 - 4 |
Completion Evidence
No explicit evidence field yet. Require tests, screenshots, linked PRs, or reviewed outputs before marking complete.
Artifact Shape
- notes: company JWT middleware (D-07-02) is reused by M-08 and M-09 without duplication. field_queries table (D-07-01) is consumed by M-09 for analytics. Note: M-00 spec also lists field_queries in the foundational schema — if M-00 migration already created field_queries, replace D-07-01 task output with an ALTER TABLE migration adding missing columns instead of CREATE TABLE.
- tasks: 5 items
- module id: M-07
- module name: Knowledge Retrieval
- schema version: 1.0
- depends on modules: 1 item
Structured Payload
Machine-readable source fields
notes
company JWT middleware (D-07-02) is reused by M-08 and M-09 without duplication. field_queries table (D-07-01) is consumed by M-09 for analytics. Note: M-00 spec also lists field_queries in the foundational schema — if M-00 migration already created field_queries, replace D-07-01 task output with an ALTER TABLE migration adding missing columns instead of CREATE TABLE.
tasks
| id | role | brief | title | completion | depends on |
|---|---|---|---|---|---|
| 1 | developer | Create apps/knowledgevault/supabase/migrations/0007_field_queries.sql — SQL migration for the field_queries table, which persists every semantic search query submitted by company field technicians. This table is also consumed by M-09 (top_queries, slang_cloud analytics).
IMPORTANT: Check if field_queries was already created in M-00 migrations. If it exists, produce an ALTER TABLE migration adding any missing columns/indexes instead of CREATE TABLE. If it does not exist, create it with CREATE TABLE IF NOT EXISTS.
Table columns:
- `id uuid NOT NULL DEFAULT gen_random_uuid() PRI | Create field_queries table migration | - type: file - evaluate: SQL file creates or extends field_queries table with columns (id uuid PK, company_id uuid NOT NULL FK companies, query_text text NOT NULL, query_embedding vector(1536) nullable with -- nullable comment, results jsonb nullable with -- nullable comment, created_at timestamptz NOT NULL), creates 3 indexes (company_id, created_at DESC, IVFFlat on query_embedding per POL-PERF-001), enables RLS with company SELECT and service INSERT policies per POL-SEC-001, all statements idempotent per POL-ARCH-001. - min length: 200 | |
| 2 | developer | Create apps/knowledgevault/src/lib/auth/companyAuthMiddleware.ts — reusable TypeScript server-only middleware that validates company JWTs for all company-authenticated routes in M-07, M-08, and M-09. Do not duplicate — all modules import from this single file.
Export: A single async function:
``typescript
export async function companyAuthMiddleware(
req: NextRequest
): Promise<{ companyId: string } | NextResponse>
`
If JWT valid and role correct → return { companyId: string }. If invalid → return a NextResponse error directly.
Logic:
1. Read Authorization: Bearer <jwt | Implement companyAuthMiddleware service | - type: file - evaluate: File exports a single async companyAuthMiddleware function, reads role from app_metadata (not user_metadata) with code comment, returns correct 401/403 response shapes for all 5 failure cases, calls supabase.auth.getUser() per POL-SEC-002, queries companies table to verify company exists, marked 'use server', no bare any (POL-STYLE-001), not importable from client bundle paths (POL-ARCH-003). - min length: 120 | - 1 |
| 3 | developer | Create apps/knowledgevault/src/app/api/auth/company-login/route.ts — Next.js App Router API route that mints a Supabase Auth session for a company user. Public endpoint — no prior JWT required.
Request: POST body { company_id: string (uuid), demo_secret: string }
Success 200: { access_token: string, token_type: 'bearer', expires_in: 3600, company_id: string }
Error cases:
- Missing company_id field → 422 { error: 'company_id required', code: 'VALIDATION_ERROR', status: 422 }
- Invalid demo_secret (doesn't match COMPANY_DEMO_SECRET env var) → 401 `{ error: 'Invalid creden | Implement POST /api/auth/company-login route | - type: file - evaluate: Route handles all 5 error cases with correct status codes and error body shapes, mints a valid Supabase Auth JWT with app_metadata.role='company' and app_metadata.company_id set, returned token passes companyAuthMiddleware, COMPANY_DEMO_SECRET never appears in response/logs (POL-SEC-004), SUPABASE_SERVICE_KEY used server-side only (POL-ARCH-003), no bare any (POL-STYLE-001). - min length: 200 | - 2 |
| 4 | developer | Create apps/knowledgevault/src/app/api/query/route.ts — semantic search endpoint for company-authenticated field technicians.
Auth: Required via companyAuthMiddleware from src/lib/auth/companyAuthMiddleware.ts (call first — POL-SEC-002).
Request: POST body:
``json
{ "query": "string (required, 1-500 chars)", "sku_filter": "string (optional)", "chunk_type_filter": "string (optional)", "verified_only": "boolean (optional)" }
`
Success 200:
``json
{ "results": [{ "chunk_id", "content", "chunk_type", "sku", "expert_name", "ground_truth_verified", "captured_at", "relevance_ | Implement POST /api/query semantic search route | - type: file - evaluate: Route calls companyAuthMiddleware first per POL-SEC-002, validates query length (422 on >500 chars), calls OpenAI text-embedding-3-small, applies 3-factor ranking formula (cosine×0.7 + ground_truth×0.2 + novelty×0.1), returns top-5 with all 9 fields, logs to field_queries with silent swallow, returns results:[] (not 404) on no matches, returns 503+Retry-After on OpenAI failure, applies sanitizeInput per POL-SEC-003, no bare any (POL-STYLE-001). - min length: 300 | - 2 - 3 |
| 5 | developer | Create apps/knowledgevault/src/app/search/page.tsx — two-screen search UI (S-06-a and S-06-b) for company-authenticated field technicians.
S-06-a — KnowledgeSearchScreen (/search):
- Full-screen, prominent centered search bar (role='search', aria-label='Search knowledge base', placeholder='Ask anything about a product or process…')
- Optional SKU filter input below search bar
- Recent queries list (last 5 from GET /api/query/recent) — tapping prefills search bar
- Powered by KnowledgeVault badge at bottom (aria-label='Powered by KnowledgeVault')
- Submitting: POST /api/query with `{ | Implement Knowledge Search UI screens | - type: file - evaluate: S-06-a renders search bar with role=search and aria-label, SKU filter input, recent queries list from GET /api/query/recent, and Powered by badge; S-06-b renders sticky search bar, up to 5 result cards with all required fields (content, chunk_type badge, expert_name, ground_truth badge, field-verified badge), correct empty state text ('No answers yet'), error card with Retry CTA on timeout; GET /api/query/recent route exists at apps/knowledgevault/src/app/api/query/recent/route.ts returning 401 unauthenticated and array authenticated; back navigation returns to S-06-a | - 3 - 4 |
module id
M-07
module name
Knowledge Retrieval
schema version
1.0
depends on modules
- M-05