// SQLite-backed dev persistence (M5.9). The model is stored as a JSON snapshot // per project + a changelog of applied ops. This is the smallest shape that // gives us "refresh persists state" without committing to the full // event-sourced architecture in docs/sync.md (that lands later). // // Switch `provider` to "postgresql" + set DATABASE_URL=postgres://… to move // to a real DB later — schema is portable. generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = "file:./dev.db" } model Project { id String @id name String scope String tagline String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt snapshots ModelSnapshot[] changes ChangelogEntry[] threads SocratesThread[] findings Finding[] } model ModelSnapshot { id String @id @default(cuid()) projectId String version Int json String // serialized SysMLModel createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@unique([projectId, version]) @@index([projectId, version]) } model ChangelogEntry { id String @id @default(cuid()) projectId String version Int // resulting model version after these ops landed ops String // JSON-encoded ModelOp[] reason String? // optional human/Socrates-supplied rationale createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) @@index([projectId, version]) } model SocratesThread { id String @id @default(cuid()) projectId String /// Model element this thread is anchored to, if any (block id, req id, …). anchorElementId String? status String @default("open") // open | archived | resolved title String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) messages SocratesMessage[] @@index([projectId]) } model SocratesMessage { id String @id @default(cuid()) threadId String role String // "user" | "assistant" /// JSON: { text, options?: [{n,label,sub?}] } content String /// Provider + model that produced this turn (assistant turns only). provider String? model String? inputTokens Int? outputTokens Int? createdAt DateTime @default(now()) thread SocratesThread @relation(fields: [threadId], references: [id], onDelete: Cascade) @@index([threadId, createdAt]) } /// Background-detected finding (assumption / risk / inconsistency). /// Findings are scoped to the model version that produced them; on every /// detection run we delete the project's previous open findings and write /// the new set so we don't accumulate stale ones across model edits. model Finding { id String @id @default(cuid()) projectId String /// "assumption" | "risk" | "inconsistency" kind String text String /// JSON-encoded string[] of element ids this finding references. linkedElementIds String confidence Float /// Risks only: "low" | "medium" | "high" severity String? /// Inconsistencies only: optional structural-rule code (S1, M2, T1, …) validationCode String? /// Lifecycle: "open" | "dismissed" | "resolved" status String @default("open") /// Model version this finding was detected against. modelVersion Int /// Provider + model that emitted the finding. provider String? llmModel String? createdAt DateTime @default(now()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) research ResearchFinding[] @@index([projectId, status]) @@index([projectId, kind]) } /// Web research results from validating a finding via Tavily / similar. /// Deferred to a follow-up commit; schema is here so we don't have to /// migrate again later. model ResearchFinding { id String @id @default(cuid()) findingId String query String url String title String? snippet String? /// "supports" | "contradicts" | "neutral" stance String @default("neutral") createdAt DateTime @default(now()) finding Finding @relation(fields: [findingId], references: [id], onDelete: Cascade) @@index([findingId]) }