# Phase 1 Data Model: Product Knowledge Management & Retrieval All new models use `cuid()` ids. Refines `docs/06-database-schema.md`'s conceptual `KnowledgeEntry`/`Runbook` shapes with an explicit version-history mechanism (research.md). ## KnowledgeEntry | Field | Type | Notes | |---|---|---| | id | String @id @default(cuid()) | | | code | String | `KB--` style, e.g. `KB-DQ-102` — logical identifier shared across versions | | version | Int @default(1) | | | isCurrentVersion | Boolean @default(true) | Exactly one `true` row per `code` at a time (research.md) | | productId | String | FK → `Product` | | feature | String? | | | type | String | `known_issue` \| `faq` \| `resolution_procedure` \| `operations` | | problem | String? | | | symptoms | String? | | | errorCode | String? | Free-text reference for display; structured linkage is via `ErrorCode`/`KnownIssue` separately | | cause | String? | | | recommendedSolution | String? | | | verificationSteps | String? | | | escalationGuidance | String? | | | status | String @default("draft") | `draft` \| `published` \| `unpublished` | | effectiveDate | DateTime? | Null = effective immediately once published | | categoryScope | String[] | | | validationStatus | String @default("unvalidated") | `unvalidated` \| `validated` | | owner | String? | | | lastReview | DateTime? | | | source | String? | | | createdAt | DateTime @default(now()) | | **Constraints**: `@@unique([code, version])`. Index on `(productId, isCurrentVersion, status, effectiveDate)` — the exact shape retrieval queries on. **Retrieval eligibility rule** (not a DB constraint, enforced in the repository's query): `isCurrentVersion = true AND status = 'published' AND (effectiveDate IS NULL OR effectiveDate <= now())`. ## ErrorCode | Field | Type | Notes | |---|---|---| | id | String @id @default(cuid()) | | | code | String | e.g. `LAYOUT_PARSE_042` | | productId | String | FK → `Product` | | description | String | | **Constraints**: `@@unique([productId, code])` — unique within a product, matching FR-006. ## KnownIssue | Field | Type | Notes | |---|---|---| | id | String @id @default(cuid()) | | | productId | String | FK → `Product` | | errorCodeId | String? | FK → `ErrorCode` | | description | String | | | status | String @default("open") | | ## Runbook | Field | Type | Notes | |---|---|---| | id | String @id @default(cuid()) | | | key | String | Logical identifier shared across versions, e.g. `PDF_HTML_CONVERSION_FAILURE` | | version | Int @default(1) | | | isCurrentVersion | Boolean @default(true) | Same version-history mechanism as `KnowledgeEntry` | | productId | String | FK → `Product` | | steps | Json | Ordered array — order preserved exactly as authored (FR-008) | | active | Boolean @default(true) | | **Constraints**: `@@unique([key, productId, version])`. Lookup-by-key queries filter `isCurrentVersion = true AND active = true`. ## Product (relations added by this feature) `knowledgeEntries KnowledgeEntry[]`, `errorCodes ErrorCode[]`, `knownIssues KnownIssue[]`, `runbooks Runbook[]` — the forward relations doc 06 already specified on `Product` but that couldn't be added until these models existed.