Initial curated projectbrain source from 8a7b1e0651dbdab97d744cbd2e653ab3970ac6c5
This commit is contained in:
commit
5aa3e8d685
700 files changed
+132801
No files matched your search
@@ -0,0 +1,424 @@
|
||||
import { ApprovalState, EvidenceStrength, VerificationState, type Prisma } from "@prisma/client";
|
||||
import { prisma } from "../db/prisma.js";
|
||||
import type {
|
||||
AcceptedTruthItemDto,
|
||||
AcceptedTruthKind,
|
||||
AcceptedTruthListFilters,
|
||||
AcceptedTruthStatus,
|
||||
CreateOperatingLogEntryRequestDto,
|
||||
EvidenceStrength as EvidenceStrengthDto,
|
||||
OperatingLogEntryDto,
|
||||
OperatingLogListFilters,
|
||||
ProjectUpdateDto,
|
||||
ProjectUpdateListFilters,
|
||||
ProjectUpdateTargetArea,
|
||||
ReviewMutationResponseDto,
|
||||
} from "../types/api.js";
|
||||
|
||||
type ProjectRecord = { id: string; slug: string };
|
||||
type ReviewRecord = Prisma.ReviewQueueItemGetPayload<Record<string, never>>;
|
||||
type TruthRecord = Prisma.AcceptedTruthItemGetPayload<Record<string, never>>;
|
||||
type UpdateRecord = Prisma.ProjectUpdateGetPayload<Record<string, never>>;
|
||||
type LogRecord = Prisma.OperatingLogEntryGetPayload<Record<string, never>>;
|
||||
|
||||
const findProjectRecord = (projectIdOrSlug: string): Promise<ProjectRecord | null> =>
|
||||
prisma.project.findFirst({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
OR: [{ id: projectIdOrSlug }, { slug: projectIdOrSlug }],
|
||||
},
|
||||
select: { id: true, slug: true },
|
||||
});
|
||||
|
||||
const findReviewRecord = async (
|
||||
projectId: string,
|
||||
reviewItemId: string,
|
||||
): Promise<ReviewRecord | null> =>
|
||||
prisma.reviewQueueItem.findFirst({
|
||||
where: {
|
||||
projectId,
|
||||
deletedAt: null,
|
||||
OR: [{ id: reviewItemId }, { slug: reviewItemId }, { readableId: reviewItemId }],
|
||||
},
|
||||
});
|
||||
|
||||
const toEvidenceStrengthEnum = (value: EvidenceStrengthDto | string | null | undefined): EvidenceStrength => {
|
||||
if (value === "verified") return EvidenceStrength.verified;
|
||||
if (value === "strong") return EvidenceStrength.strong;
|
||||
if (value === "medium") return EvidenceStrength.medium;
|
||||
return EvidenceStrength.weak;
|
||||
};
|
||||
|
||||
const toEvidenceStrengthDto = (value: EvidenceStrength): EvidenceStrengthDto => value;
|
||||
|
||||
const normalizeKind = (type: string): AcceptedTruthKind => {
|
||||
if (type === "memory_rule") return "memory_rule";
|
||||
if (type === "documentation_update") return "documentation";
|
||||
if (type === "open_question") return "open_question";
|
||||
if (type === "feature") return "feature";
|
||||
if (type === "bug") return "bug";
|
||||
if (type === "task") return "task";
|
||||
if (type === "decision") return "decision";
|
||||
if (type === "knowledge") return "knowledge";
|
||||
if (type === "risk") return "risk";
|
||||
if (type === "assumption") return "assumption";
|
||||
return "knowledge";
|
||||
};
|
||||
|
||||
const targetAreaForKind = (kind: AcceptedTruthKind): ProjectUpdateTargetArea => {
|
||||
if (kind === "feature") return "feature-map";
|
||||
if (kind === "bug") return "bugs";
|
||||
if (kind === "task") return "tasks";
|
||||
if (kind === "decision") return "decisions";
|
||||
if (kind === "memory_rule") return "memory";
|
||||
if (kind === "documentation") return "documentation";
|
||||
if (kind === "risk") return "risks";
|
||||
if (kind === "open_question") return "questions";
|
||||
return "knowledge";
|
||||
};
|
||||
|
||||
const toTruthDto = (record: TruthRecord, projectSlug: string): AcceptedTruthItemDto => ({
|
||||
id: record.slug ?? record.readableId ?? record.id,
|
||||
projectId: projectSlug,
|
||||
sourceReviewItemId: record.sourceReviewItemId,
|
||||
kind: record.kind as AcceptedTruthKind,
|
||||
title: record.title,
|
||||
summary: record.summary,
|
||||
status: record.status as AcceptedTruthStatus,
|
||||
source: record.source,
|
||||
confidence: record.confidence,
|
||||
evidenceStrength: toEvidenceStrengthDto(record.evidenceStrength),
|
||||
createdAt: record.createdAt.toISOString(),
|
||||
updatedAt: record.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
const toProjectUpdateDto = (record: UpdateRecord, projectSlug: string): ProjectUpdateDto => ({
|
||||
id: record.slug ?? record.readableId ?? record.id,
|
||||
projectId: projectSlug,
|
||||
sourceTruthItemId: record.sourceTruthItemId,
|
||||
kind: record.kind as AcceptedTruthKind,
|
||||
title: record.title,
|
||||
summary: record.summary,
|
||||
targetArea: record.targetArea as ProjectUpdateTargetArea,
|
||||
status: record.status as ProjectUpdateDto["status"],
|
||||
confidence: record.confidence,
|
||||
evidenceStrength: toEvidenceStrengthDto(record.evidenceStrength),
|
||||
createdAt: record.createdAt.toISOString(),
|
||||
updatedAt: record.updatedAt.toISOString(),
|
||||
...(record.appliedAt ? { appliedAt: record.appliedAt.toISOString() } : {}),
|
||||
});
|
||||
|
||||
const linkedIdsFromJson = (value: Prisma.JsonValue): string[] =>
|
||||
Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
|
||||
|
||||
const toLogDto = (record: LogRecord, projectSlug: string): OperatingLogEntryDto => ({
|
||||
id: record.slug ?? record.readableId ?? record.id,
|
||||
projectId: projectSlug,
|
||||
timestamp: record.timestamp.toISOString(),
|
||||
type: record.type as OperatingLogEntryDto["type"],
|
||||
title: record.title,
|
||||
summary: record.summary,
|
||||
linkedEntityIds: linkedIdsFromJson(record.linkedEntityIdsJson),
|
||||
source: record.source,
|
||||
importance: record.importance as OperatingLogEntryDto["importance"],
|
||||
createdAt: record.createdAt.toISOString(),
|
||||
updatedAt: record.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
const paginateMeta = <T>(
|
||||
items: T[],
|
||||
total: number,
|
||||
page: number,
|
||||
pageSize: number,
|
||||
sort?: string,
|
||||
filters?: Record<string, unknown>,
|
||||
) => ({
|
||||
items,
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
hasNextPage: page < Math.ceil(total / pageSize),
|
||||
hasPreviousPage: page > 1,
|
||||
...(sort ? { sort } : {}),
|
||||
...(filters ? { filters: filters as Record<string, string | number | boolean | string[]> } : {}),
|
||||
});
|
||||
|
||||
export const listAcceptedTruthFromDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
filters: AcceptedTruthListFilters,
|
||||
options: { page: number; pageSize: number; sort?: string },
|
||||
) => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const where: Prisma.AcceptedTruthItemWhereInput = {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
...(filters.kind?.length ? { kind: { in: filters.kind } } : {}),
|
||||
...(filters.status?.length ? { status: { in: filters.status } } : {}),
|
||||
...(filters.confidence !== undefined ? { confidence: filters.confidence } : {}),
|
||||
...(filters.evidenceStrength?.length
|
||||
? { evidenceStrength: { in: filters.evidenceStrength.map(toEvidenceStrengthEnum) } }
|
||||
: {}),
|
||||
...(filters.sourceReviewItemId ? { sourceReviewItemId: filters.sourceReviewItemId } : {}),
|
||||
};
|
||||
const total = await prisma.acceptedTruthItem.count({ where });
|
||||
const records = await prisma.acceptedTruthItem.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: options.sort?.endsWith(":asc") ? "asc" : "desc" },
|
||||
skip: (options.page - 1) * options.pageSize,
|
||||
take: options.pageSize,
|
||||
});
|
||||
return paginateMeta(records.map((record) => toTruthDto(record, project.slug)), total, options.page, options.pageSize, options.sort, filters);
|
||||
};
|
||||
|
||||
export const materializeReviewItemAsTruthInDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
reviewItemId: string,
|
||||
): Promise<ReviewMutationResponseDto | undefined> => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const review = await findReviewRecord(project.id, reviewItemId);
|
||||
if (!review) return undefined;
|
||||
if (!["accepted", "converted", "merged"].includes(review.status)) {
|
||||
return {
|
||||
success: true,
|
||||
message: `Review item '${reviewItemId}' is not accepted, converted or merged yet.`,
|
||||
updatedEntityId: reviewItemId,
|
||||
affectedEntities: [reviewItemId],
|
||||
};
|
||||
}
|
||||
const existing = await prisma.acceptedTruthItem.findFirst({
|
||||
where: { projectId: project.id, sourceReviewItemId: review.id, deletedAt: null },
|
||||
});
|
||||
if (existing) {
|
||||
return {
|
||||
success: true,
|
||||
message: `Accepted truth already exists for review item '${reviewItemId}'.`,
|
||||
updatedEntityId: reviewItemId,
|
||||
createdEntityId: existing.slug ?? existing.readableId ?? existing.id,
|
||||
affectedEntities: [reviewItemId, existing.slug ?? existing.id],
|
||||
};
|
||||
}
|
||||
const createdAt = new Date();
|
||||
const kind = normalizeKind(review.createdEntityType ?? review.type);
|
||||
const slug = `truth-${project.slug}-${createdAt.getTime()}`;
|
||||
const truth = await prisma.acceptedTruthItem.create({
|
||||
data: {
|
||||
projectId: project.id,
|
||||
slug,
|
||||
readableId: slug,
|
||||
sourceReviewItemId: review.id,
|
||||
kind,
|
||||
title: review.title,
|
||||
summary: review.description,
|
||||
status: review.status === "converted" ? "converted" : review.status === "merged" ? "merged" : "accepted",
|
||||
source: review.source,
|
||||
confidence: review.confidence,
|
||||
approvalState: ApprovalState.approved,
|
||||
evidenceStrength: review.evidenceStrength,
|
||||
verificationState: VerificationState.verified,
|
||||
payloadJson: { reviewItemId: review.id, reviewSlug: review.slug, suggestedAction: review.suggestedAction },
|
||||
provenanceJson: { createdFrom: "P6 review materialization" },
|
||||
verifiedAt: createdAt,
|
||||
verifiedBy: "api",
|
||||
},
|
||||
});
|
||||
await createOperatingLogEntryInDatabase(project.slug, {
|
||||
type: "accepted truth",
|
||||
title: `Accepted truth created: ${review.title}`,
|
||||
summary: review.description,
|
||||
linkedEntityIds: [review.id, truth.id],
|
||||
source: "review_queue",
|
||||
importance: review.priority === "critical" ? "critical" : review.priority === "important" ? "important" : "normal",
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: `Review item '${reviewItemId}' materialized as accepted truth.`,
|
||||
updatedEntityId: reviewItemId,
|
||||
createdEntityId: truth.slug ?? truth.id,
|
||||
affectedEntities: [reviewItemId, truth.slug ?? truth.id],
|
||||
};
|
||||
};
|
||||
|
||||
export const listProjectUpdatesFromDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
filters: ProjectUpdateListFilters,
|
||||
options: { page: number; pageSize: number; sort?: string },
|
||||
) => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const where: Prisma.ProjectUpdateWhereInput = {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
...(filters.status?.length ? { status: { in: filters.status } } : {}),
|
||||
...(filters.targetArea?.length ? { targetArea: { in: filters.targetArea } } : {}),
|
||||
...(filters.kind?.length ? { kind: { in: filters.kind } } : {}),
|
||||
...(filters.sourceTruthItemId ? { sourceTruthItemId: filters.sourceTruthItemId } : {}),
|
||||
};
|
||||
const total = await prisma.projectUpdate.count({ where });
|
||||
const records = await prisma.projectUpdate.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: options.sort?.endsWith(":asc") ? "asc" : "desc" },
|
||||
skip: (options.page - 1) * options.pageSize,
|
||||
take: options.pageSize,
|
||||
});
|
||||
return paginateMeta(records.map((record) => toProjectUpdateDto(record, project.slug)), total, options.page, options.pageSize, options.sort, filters);
|
||||
};
|
||||
|
||||
export const createProjectUpdateFromTruthInDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
truthItemId: string,
|
||||
): Promise<ReviewMutationResponseDto | undefined> => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const truth = await prisma.acceptedTruthItem.findFirst({
|
||||
where: {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
OR: [{ id: truthItemId }, { slug: truthItemId }, { readableId: truthItemId }],
|
||||
},
|
||||
});
|
||||
if (!truth) return undefined;
|
||||
const existing = await prisma.projectUpdate.findFirst({
|
||||
where: { projectId: project.id, sourceTruthItemId: truth.id, deletedAt: null },
|
||||
});
|
||||
if (existing) {
|
||||
return {
|
||||
success: true,
|
||||
message: `Project update already exists for accepted truth '${truthItemId}'.`,
|
||||
updatedEntityId: truthItemId,
|
||||
createdEntityId: existing.slug ?? existing.id,
|
||||
affectedEntities: [truthItemId, existing.slug ?? existing.id],
|
||||
};
|
||||
}
|
||||
const createdAt = new Date();
|
||||
const slug = `update-${project.slug}-${createdAt.getTime()}`;
|
||||
const update = await prisma.projectUpdate.create({
|
||||
data: {
|
||||
projectId: project.id,
|
||||
slug,
|
||||
readableId: slug,
|
||||
sourceTruthItemId: truth.id,
|
||||
kind: truth.kind,
|
||||
title: truth.title,
|
||||
summary: truth.summary,
|
||||
targetArea: targetAreaForKind(truth.kind as AcceptedTruthKind),
|
||||
status: "ready",
|
||||
confidence: truth.confidence,
|
||||
approvalState: ApprovalState.pending,
|
||||
evidenceStrength: truth.evidenceStrength,
|
||||
payloadJson: { acceptedTruthItemId: truth.id },
|
||||
provenanceJson: { createdFrom: "P6 accepted truth" },
|
||||
},
|
||||
});
|
||||
await createOperatingLogEntryInDatabase(project.slug, {
|
||||
type: "project update",
|
||||
title: `Project update prepared: ${truth.title}`,
|
||||
summary: truth.summary,
|
||||
linkedEntityIds: [truth.id, update.id],
|
||||
source: "accepted_truth",
|
||||
importance: truth.confidence >= 90 ? "important" : "normal",
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: `Project update prepared from accepted truth '${truthItemId}'.`,
|
||||
updatedEntityId: truthItemId,
|
||||
createdEntityId: update.slug ?? update.id,
|
||||
affectedEntities: [truthItemId, update.slug ?? update.id],
|
||||
};
|
||||
};
|
||||
|
||||
export const applyProjectUpdateInDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
projectUpdateId: string,
|
||||
): Promise<ReviewMutationResponseDto | undefined | "PROJECT_UPDATE_ALREADY_APPLIED"> => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const existing = await prisma.projectUpdate.findFirst({
|
||||
where: {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
OR: [{ id: projectUpdateId }, { slug: projectUpdateId }, { readableId: projectUpdateId }],
|
||||
},
|
||||
});
|
||||
if (!existing) return undefined;
|
||||
const appliedAt = new Date();
|
||||
const claimed = await prisma.projectUpdate.updateMany({
|
||||
where: { id: existing.id, status: "ready" },
|
||||
data: {
|
||||
status: "applied",
|
||||
approvalState: ApprovalState.approved,
|
||||
appliedAt,
|
||||
updatedBy: "api",
|
||||
},
|
||||
});
|
||||
if (claimed.count !== 1) return "PROJECT_UPDATE_ALREADY_APPLIED";
|
||||
const updated = await prisma.projectUpdate.findUniqueOrThrow({ where: { id: existing.id } });
|
||||
await createOperatingLogEntryInDatabase(project.slug, {
|
||||
type: "project update",
|
||||
title: `Project update applied: ${updated.title}`,
|
||||
summary: updated.summary,
|
||||
linkedEntityIds: [updated.id, updated.sourceTruthItemId],
|
||||
source: "project_update",
|
||||
importance: "important",
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: `Project update '${projectUpdateId}' applied.`,
|
||||
updatedEntityId: updated.slug ?? updated.id,
|
||||
affectedEntities: [updated.slug ?? updated.id, updated.sourceTruthItemId],
|
||||
};
|
||||
};
|
||||
|
||||
export const listOperatingLogEntriesFromDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
filters: OperatingLogListFilters,
|
||||
options: { page: number; pageSize: number; sort?: string },
|
||||
) => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const where: Prisma.OperatingLogEntryWhereInput = {
|
||||
projectId: project.id,
|
||||
deletedAt: null,
|
||||
...(filters.type?.length ? { type: { in: filters.type } } : {}),
|
||||
...(filters.source?.length ? { source: { in: filters.source } } : {}),
|
||||
...(filters.importance?.length ? { importance: { in: filters.importance } } : {}),
|
||||
};
|
||||
const total = await prisma.operatingLogEntry.count({ where });
|
||||
const records = await prisma.operatingLogEntry.findMany({
|
||||
where,
|
||||
orderBy: { timestamp: options.sort?.endsWith(":asc") ? "asc" : "desc" },
|
||||
skip: (options.page - 1) * options.pageSize,
|
||||
take: options.pageSize,
|
||||
});
|
||||
const items = records
|
||||
.map((record) => toLogDto(record, project.slug))
|
||||
.filter((entry) => !filters.linkedEntityId || entry.linkedEntityIds.includes(filters.linkedEntityId));
|
||||
return paginateMeta(items, total, options.page, options.pageSize, options.sort, filters);
|
||||
};
|
||||
|
||||
export const createOperatingLogEntryInDatabase = async (
|
||||
projectIdOrSlug: string,
|
||||
body: CreateOperatingLogEntryRequestDto,
|
||||
): Promise<OperatingLogEntryDto | undefined> => {
|
||||
const project = await findProjectRecord(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
const createdAt = new Date();
|
||||
const slug = `log-${project.slug}-${createdAt.getTime()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const record = await prisma.operatingLogEntry.create({
|
||||
data: {
|
||||
projectId: project.id,
|
||||
slug,
|
||||
readableId: slug,
|
||||
timestamp: createdAt,
|
||||
type: body.type,
|
||||
title: body.title,
|
||||
summary: body.summary,
|
||||
linkedEntityIdsJson: body.linkedEntityIds ?? [],
|
||||
source: body.source ?? "manual",
|
||||
importance: body.importance ?? "normal",
|
||||
payloadJson: { createdFrom: "P6 operating log API" },
|
||||
},
|
||||
});
|
||||
return toLogDto(record, project.slug);
|
||||
};
|
||||
Reference in new issue
Block a user