import { ApprovalState, EntityType, EvidenceStrength, FeatureLifecycle, FeatureStatus, ReviewStatus, VerificationState } from "@prisma/client"; import { prisma } from "../db/prisma.js"; import type { Initiative, Prisma } from "@prisma/client"; export type InitiativeDto = { id: string; projectId: string; title: string; intent: string; status: "proposed" | "planned" | "active" | "complete" | "paused"; featureIds: string[]; priority: "critical" | "important" | "normal" | "low"; confidence?: number; approvalState?: string; sourceBrainstormId?: string; reviewItemId?: string; sourceType?: string; metadata?: unknown; provenance?: unknown; createdAt?: string; updatedAt?: string; }; const slugify = (value: string) => value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 56) || "initiative"; const toJson = (value: unknown): Prisma.InputJsonValue => JSON.parse(JSON.stringify(value)) as Prisma.InputJsonValue; export type CreateInitiativeInput = { title: string; intent: string; priority?: InitiativeDto["priority"]; status?: InitiativeDto["status"]; confidence?: number; featureIds?: string[]; sourceType?: string; }; export type UpdateInitiativeInput = Partial & { approvalState?: string; }; const toStringArray = (value: unknown): string[] => Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : []; const toStatus = (value: string): InitiativeDto["status"] => ["proposed", "planned", "active", "complete", "paused"].includes(value) ? value as InitiativeDto["status"] : "proposed"; const toPriority = (value: string): InitiativeDto["priority"] => ["critical", "important", "normal", "low"].includes(value) ? value as InitiativeDto["priority"] : "normal"; const getProject = async (projectIdOrSlug: string) => { const project = await prisma.project.findFirst({ where: { OR: [{ id: projectIdOrSlug }, { slug: projectIdOrSlug }, { readableId: projectIdOrSlug }], deletedAt: null }}); if (!project) throw new Error(`Project '${projectIdOrSlug}' not found`); return project; }; const toDto = (projectSlug: string, initiative: Initiative): InitiativeDto => ({ id: initiative.slug ?? initiative.id, projectId: projectSlug, title: initiative.title, intent: initiative.intent, status: toStatus(initiative.status), featureIds: toStringArray(initiative.featureIdsJson), priority: toPriority(initiative.priority), confidence: initiative.confidence, approvalState: initiative.approvalState, sourceBrainstormId: initiative.sourceBrainstormId ?? undefined, reviewItemId: initiative.reviewItemId ?? undefined, sourceType: initiative.sourceType, metadata: initiative.metadataJson ?? undefined, provenance: initiative.provenanceJson ?? undefined, createdAt: initiative.createdAt.toISOString(), updatedAt: initiative.updatedAt.toISOString(), }); export const listInitiativesForProject = async (projectIdOrSlug: string): Promise => { const project = await getProject(projectIdOrSlug); const items = await prisma.initiative.findMany({ where: { projectId: project.id, deletedAt: null }, orderBy: [{ status: "asc" }, { updatedAt: "desc" }], }); return items.map((item) => toDto(project.slug, item)); }; export const markInitiativePlannedFromReview = async (projectId: string, initiativeId: string, reviewItemId: string): Promise => { const existing = await prisma.initiative.findFirst({ where: { projectId, deletedAt: null, OR: [{ id: initiativeId }, { slug: initiativeId }, { readableId: initiativeId }] }, }); if (!existing) return undefined; const updated = await prisma.initiative.update({ where: { id: existing.id }, data: { status: "planned", approvalState: ApprovalState.approved, reviewItemId, updatedBy: "review_queue", }, }); return updated.slug ?? updated.id; }; export const getInitiativeForProject = async (projectIdOrSlug: string, initiativeId: string): Promise => { const project = await getProject(projectIdOrSlug); const item = await prisma.initiative.findFirst({ where: { projectId: project.id, deletedAt: null, OR: [{ id: initiativeId }, { slug: initiativeId }, { readableId: initiativeId }] }, }); return item ? toDto(project.slug, item) : undefined; }; export const createInitiativeForProject = async (projectIdOrSlug: string, input: CreateInitiativeInput): Promise => { const project = await getProject(projectIdOrSlug); const slug = `${slugify(input.title)}-${Date.now().toString(36)}`; const item = await prisma.initiative.create({ data: { projectId: project.id, slug, readableId: slug, title: input.title, intent: input.intent, priority: input.priority ?? "normal", status: input.status ?? "proposed", confidence: input.confidence ?? 72, featureIdsJson: toJson(input.featureIds ?? []), approvalState: ApprovalState.pending, sourceType: input.sourceType ?? "manual_initiative", metadataJson: toJson({ reviewBeforeTruth: true, createdFrom: "initiative_endpoint" }), provenanceJson: toJson({ source: input.sourceType ?? "manual_initiative" }), }, }); await prisma.operatingLogEntry.create({ data: { projectId: project.id, slug: `log-initiative-created-${slug}`, readableId: `log-initiative-created-${slug}`, type: "initiative created", title: `Initiative created: ${input.title}`, summary: "A roadmap initiative was created as a review-gated planning object.", linkedEntityIdsJson: toJson([item.slug ?? item.id]), source: "initiative_center", importance: input.priority === "critical" ? "critical" : input.priority === "important" ? "important" : "normal", payloadJson: toJson({ initiativeId: item.slug ?? item.id, status: item.status }), provenanceJson: toJson({ source: input.sourceType ?? "manual_initiative" }), }, }); return toDto(project.slug, item); }; export const updateInitiativeForProject = async (projectIdOrSlug: string, initiativeId: string, input: UpdateInitiativeInput): Promise => { const project = await getProject(projectIdOrSlug); const existing = await prisma.initiative.findFirst({ where: { projectId: project.id, deletedAt: null, OR: [{ id: initiativeId }, { slug: initiativeId }, { readableId: initiativeId }] }, }); if (!existing) return undefined; const updated = await prisma.initiative.update({ where: { id: existing.id }, data: { title: input.title, intent: input.intent, priority: input.priority, status: input.status, confidence: input.confidence, featureIdsJson: input.featureIds ? toJson(input.featureIds) : undefined, approvalState: input.approvalState === "approved" ? ApprovalState.approved : input.approvalState === "rejected" ? ApprovalState.rejected : undefined, updatedBy: "initiative_endpoint", }, }); return toDto(project.slug, updated); }; const suggestedFeatureTitlesFor = (initiative: InitiativeDto): string[] => { const text = `${initiative.title} ${initiative.intent}`.toLowerCase(); if (text.includes("brainstorm")) return ["Brainstorm review detail", "Outcome conversion guardrails", "Idea graph comparison panel"]; if (text.includes("blueprint") || text.includes("documentation")) return ["Blueprint acceptance workflow", "Documentation export audit", "Stale document regeneration queue"]; if (text.includes("database") || text.includes("backend")) return ["Database smoke evidence panel", "API contract regression pack", "Persistence recovery checklist"]; if (text.includes("ux") || text.includes("usability")) return ["Guided empty-state actions", "Workflow-based navigation polish", "Mobile action rail refinement"]; return ["Initiative scope brief", "Feature candidate breakdown", "Acceptance criteria draft"]; }; export type InitiativeFeatureCandidateDto = { id: string; title: string; confidence: number; reviewItemId: string; targetRoute: string; materializedFeatureId?: string; }; export type InitiativeReviewSyncResultDto = { applied: boolean; action: "accept" | "reject"; reviewItemId: string; initiativeId?: string; featureId?: string; acceptedTruthId?: string; affectedEntities: string[]; message: string; }; export const createFeatureCandidatesForInitiative = async (projectIdOrSlug: string, initiativeId: string): Promise<{ initiative: InitiativeDto; candidates: InitiativeFeatureCandidateDto[] } | undefined> => { const project = await getProject(projectIdOrSlug); const initiative = await getInitiativeForProject(projectIdOrSlug, initiativeId); if (!initiative) return undefined; const titles = suggestedFeatureTitlesFor(initiative); const candidates: InitiativeFeatureCandidateDto[] = []; for (const [index, title] of titles.entries()) { const slug = `rq-initiative-feature-${slugify(initiative.title)}-${index}-${Date.now().toString(36)}`; const review = await prisma.reviewQueueItem.create({ data: { projectId: project.id, slug, readableId: slug, type: "feature", title: `Feature candidate: ${title}`, description: `Initiative '${initiative.title}' proposes this feature candidate.\n\nIntent: ${initiative.intent}`, priority: initiative.priority === "critical" ? "critical" : initiative.priority === "important" ? "important" : "normal", status: ReviewStatus.pending, source: "initiative_center", confidence: Math.max(62, (initiative.confidence ?? 72) - index * 6), approvalState: ApprovalState.pending, evidenceStrength: EvidenceStrength.medium, verificationState: VerificationState.needs_evidence, suggestedAction: "Review whether this feature should be promoted into the Feature Map.", createdBy: "api", updatedBy: "api", sourceType: "initiative_feature_candidate", targetEntityType: undefined, targetEntityId: initiative.id, payloadJson: toJson({ initiativeId: initiative.id, candidateTitle: title, candidateIndex: index }), provenanceJson: toJson({ createdFrom: "Initiative feature generation", initiativeId: initiative.id }), }, }); candidates.push({ id: `${initiative.id}-candidate-${index}`, title, confidence: Math.max(62, (initiative.confidence ?? 72) - index * 6), reviewItemId: review.slug ?? review.id, targetRoute: `/projects/${project.slug}/review-queue` }); } await prisma.operatingLogEntry.create({ data: { projectId: project.id, slug: `log-initiative-feature-candidates-${slugify(initiative.title)}-${Date.now().toString(36)}`, readableId: `log-initiative-feature-candidates-${slugify(initiative.title)}-${Date.now().toString(36)}`, type: "initiative planning", title: `Feature candidates generated for ${initiative.title}`, summary: `${candidates.length} feature candidates were generated and queued for review.`, linkedEntityIdsJson: toJson([initiative.id, ...candidates.map((item) => item.reviewItemId)]), source: "initiative_center", importance: initiative.priority === "critical" ? "critical" : initiative.priority === "important" ? "important" : "normal", payloadJson: toJson({ initiativeId: initiative.id, candidateCount: candidates.length }), provenanceJson: toJson({ source: "initiative_feature_generation" }), }, }); return { initiative, candidates }; }; const safeRecord = (value: unknown): Record => typeof value === "object" && value !== null ? value as Record : {}; const getString = (value: unknown, fallback = ""): string => typeof value === "string" && value.trim() ? value : fallback; const acceptedTruthKindForInitiativeReview = (reviewType: string) => reviewType === "feature" ? "feature" : reviewType; const ensureInitiativeFeatureGroup = async (projectId: string) => prisma.featureGroup.upsert({ where: { projectId_slug: { projectId, slug: "initiative-roadmap" } }, create: { projectId, slug: "initiative-roadmap", readableId: "initiative-roadmap", name: "Initiative Roadmap", description: "Features materialized from accepted roadmap initiative candidates.", focusArea: "Roadmap", }, update: {}, }); export const syncInitiativeReviewAction = async ( projectIdOrSlug: string, reviewItemId: string, action: "accept" | "reject", ): Promise => { const project = await getProject(projectIdOrSlug); const review = await prisma.reviewQueueItem.findFirst({ where: { projectId: project.id, deletedAt: null, OR: [{ id: reviewItemId }, { slug: reviewItemId }, { readableId: reviewItemId }], }, }); if (!review || review.sourceType !== "initiative_feature_candidate") return undefined; const payload = safeRecord(review.payloadJson); const initiativeId = getString(payload.initiativeId, review.targetEntityId ?? ""); const candidateTitle = getString(payload.candidateTitle, review.title.replace(/^Feature candidate:\s*/i, "")); const affectedEntities = [review.slug ?? review.id]; let featureId: string | undefined; let acceptedTruthId: string | undefined; if (initiativeId) affectedEntities.push(initiativeId); if (action === "accept") { const group = await ensureInitiativeFeatureGroup(project.id); const featureSlug = `initiative-feature-${slugify(candidateTitle)}-${Date.now().toString(36)}`; const feature = await prisma.feature.create({ data: { projectId: project.id, featureGroupId: group.id, slug: featureSlug, readableId: featureSlug, name: candidateTitle, description: review.description, status: FeatureStatus.todo, lifecycle: FeatureLifecycle.planned, intent: `Materialized from accepted roadmap initiative candidate. Initiative: ${initiativeId || "unknown"}.`, confidence: review.confidence, health: 65, approvalState: ApprovalState.approved, evidenceStrength: review.evidenceStrength, verificationState: VerificationState.needs_evidence, trustJson: toJson({ initiativeId, reviewItemId: review.slug ?? review.id, materializedFrom: "initiative_feature_candidate" }), provenanceJson: toJson({ source: "initiative_review_accept", initiativeId, reviewItemId: review.id, candidateTitle }), sourceType: "initiative_review_accept", createdBy: "review_queue", updatedBy: "review_queue", }, }); featureId = feature.slug ?? feature.id; affectedEntities.push(featureId); if (initiativeId) { const initiative = await prisma.initiative.findFirst({ where: { projectId: project.id, deletedAt: null, OR: [{ id: initiativeId }, { slug: initiativeId }, { readableId: initiativeId }] }, }); if (initiative) { const existingFeatureIds = toStringArray(initiative.featureIdsJson); await prisma.initiative.update({ where: { id: initiative.id }, data: { status: initiative.status === "proposed" ? "planned" : initiative.status, approvalState: ApprovalState.approved, featureIdsJson: toJson(Array.from(new Set([...existingFeatureIds, featureId]))), updatedBy: "review_queue", }, }); } } const existingTruth = await prisma.acceptedTruthItem.findFirst({ where: { projectId: project.id, sourceReviewItemId: review.id, deletedAt: null } }); if (existingTruth) acceptedTruthId = existingTruth.slug ?? existingTruth.id; else { const slug = `truth-initiative-feature-${slugify(candidateTitle)}-${Date.now().toString(36)}`; const truth = await prisma.acceptedTruthItem.create({ data: { projectId: project.id, slug, readableId: slug, sourceReviewItemId: review.id, kind: acceptedTruthKindForInitiativeReview(review.type), title: `Accepted initiative feature: ${candidateTitle}`, summary: review.description, status: "accepted", source: "initiative_center", confidence: review.confidence, approvalState: ApprovalState.approved, evidenceStrength: review.evidenceStrength, verificationState: VerificationState.verified, payloadJson: toJson({ initiativeId, featureId, reviewItemId: review.slug ?? review.id, candidateTitle, targetRoute: `/projects/${project.slug}/features/${featureId}` }), provenanceJson: toJson({ createdFrom: "Initiative feature candidate review acceptance", sourceType: review.sourceType }), verifiedAt: new Date(), verifiedBy: "api", }, }); acceptedTruthId = truth.slug ?? truth.id; } if (acceptedTruthId) affectedEntities.push(acceptedTruthId); } await prisma.operatingLogEntry.create({ data: { projectId: project.id, slug: `log-initiative-review-${slugify(candidateTitle)}-${Date.now().toString(36)}`, readableId: `log-initiative-review-${slugify(candidateTitle)}-${Date.now().toString(36)}`, type: "initiative review sync", title: action === "accept" ? `Initiative feature accepted: ${candidateTitle}` : `Initiative feature rejected: ${candidateTitle}`, summary: action === "accept" ? "Accepted initiative feature candidate was materialized into the Feature Map and Accepted Truth." : "Rejected initiative feature candidate was left out of the Feature Map.", linkedEntityIdsJson: toJson(affectedEntities), source: "initiative_center", importance: action === "accept" ? "important" : "normal", payloadJson: toJson({ action, initiativeId, featureId, acceptedTruthId }), provenanceJson: toJson({ reviewItemId: review.id, sourceType: review.sourceType }), }, }); return { applied: true, action, reviewItemId: review.slug ?? review.id, initiativeId, featureId, acceptedTruthId, affectedEntities, message: action === "accept" ? "Initiative feature candidate accepted and materialized into Feature Map." : "Initiative feature candidate rejected without materializing a feature.", }; };