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,514 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { prisma } from "../db/prisma.js";
|
||||
import type {
|
||||
CurrentStateSummaryDto,
|
||||
FocusAreaDto,
|
||||
HealthSummaryDto,
|
||||
ProjectDetailDto,
|
||||
ProjectDna,
|
||||
ProjectStatus,
|
||||
ProjectSummaryDto,
|
||||
CreateProjectRequestDto,
|
||||
UpdateActiveContextPackRequestDto,
|
||||
UpdateProjectRequestDto,
|
||||
WorkspaceReadDto,
|
||||
WorkspaceStateDto,
|
||||
} from "../types/api.js";
|
||||
import {
|
||||
mergeProjectSetupState,
|
||||
projectSetupActiveContextPackId,
|
||||
projectSetupWorkspaceState,
|
||||
sanitizeProjectSetupState,
|
||||
} from "./projectSetupState.js";
|
||||
import { humanAttentionReviewWhere } from "./reviewAttentionPolicy.js";
|
||||
import { projectHealth } from "../services/projectHealthSemantics.js";
|
||||
|
||||
const projectRelationsInclude = {
|
||||
dna: true,
|
||||
workspaceState: true,
|
||||
giteaRepositories: { select: { id: true, syncStatus: true, lastSyncAt: true } },
|
||||
sourceItems: { orderBy: { updatedAt: "desc" as const } },
|
||||
repositoryFindings: { where: { status: { notIn: ["resolved", "suppressed"] } }, select: { severity: true, status: true, lastSeenAt: true } },
|
||||
_count: {
|
||||
select: {
|
||||
features: { where: { deletedAt: null } },
|
||||
contextPacks: { where: { deletedAt: null } },
|
||||
deliveryPlans: true,
|
||||
documentationItems: { where: { deletedAt: null } },
|
||||
knowledgeItems: { where: { deletedAt: null } },
|
||||
reviewQueueItems: { where: humanAttentionReviewWhere },
|
||||
initiatives: { where: { deletedAt: null, reviewItemId: null, approvalState: { in: ["pending", "blocked"] } } },
|
||||
},
|
||||
},
|
||||
} as const satisfies Prisma.ProjectInclude;
|
||||
|
||||
type ProjectWithP2Relations = Prisma.ProjectGetPayload<{ include: typeof projectRelationsInclude }>;
|
||||
|
||||
type WorkspaceJsonPayload = {
|
||||
workspaceState?: WorkspaceStateDto;
|
||||
focusAreas?: FocusAreaDto[];
|
||||
activeFocusAreaId?: string;
|
||||
activeContextPackId?: string;
|
||||
currentStateSummary?: CurrentStateSummaryDto;
|
||||
healthSummary?: HealthSummaryDto;
|
||||
setupState?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null;
|
||||
|
||||
const slugify = (value: string): string => {
|
||||
const slug = value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "");
|
||||
|
||||
return slug || "project";
|
||||
};
|
||||
|
||||
const toStringArray = (value: Prisma.JsonValue): string[] =>
|
||||
Array.isArray(value)
|
||||
? value.filter((item): item is string => typeof item === "string")
|
||||
: [];
|
||||
|
||||
const isDisposableProject = (project: Pick<ProjectWithP2Relations, "name" | "description">) =>
|
||||
/\b(smoke|disposable|test project|proof project)\b/i.test(`${project.name} ${project.description}`);
|
||||
|
||||
const toDna = (
|
||||
dna: NonNullable<ProjectWithP2Relations["dna"]>,
|
||||
): ProjectDna => ({
|
||||
vision: dna.vision,
|
||||
goals: toStringArray(dna.goalsJson),
|
||||
nonGoals: toStringArray(dna.nonGoalsJson),
|
||||
targetAudience: dna.targetAudience,
|
||||
principles: toStringArray(dna.principlesJson),
|
||||
successMetrics: Array.isArray(dna.metricsJson)
|
||||
? (dna.metricsJson as ProjectDna["successMetrics"])
|
||||
: [],
|
||||
maturityStage: dna.maturityStage as ProjectDna["maturityStage"],
|
||||
revision: dna.revision,
|
||||
updatedAt: dna.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
const projectLastSync = (project: ProjectWithP2Relations) => {
|
||||
const timestamps = [
|
||||
project.updatedAt.getTime(),
|
||||
...project.giteaRepositories.flatMap((repository) => repository.lastSyncAt ? [repository.lastSyncAt.getTime()] : []),
|
||||
...project.repositoryFindings.map((finding) => finding.lastSeenAt.getTime()),
|
||||
];
|
||||
return new Date(Math.max(...timestamps)).toISOString();
|
||||
};
|
||||
|
||||
const toProjectSummary = (
|
||||
project: ProjectWithP2Relations,
|
||||
): ProjectSummaryDto => {
|
||||
const health = projectHealth(project);
|
||||
const workspacePayload = project.workspaceState ? toWorkspacePayload(project.workspaceState.notesJson) : {};
|
||||
const configuredSkills = Array.isArray(workspacePayload.setupState?.codexSkills)
|
||||
? workspacePayload.setupState.codexSkills
|
||||
: [];
|
||||
return {
|
||||
id: project.slug,
|
||||
name: project.name,
|
||||
description: project.description,
|
||||
health: health.score,
|
||||
healthStatus: health.healthStatus,
|
||||
lastSync: projectLastSync(project),
|
||||
domain: project.domain,
|
||||
status: project.status as ProjectStatus,
|
||||
createdAt: project.createdAt.toISOString(),
|
||||
updatedAt: project.updatedAt.toISOString(),
|
||||
activeContextPackId: project.activeContextPackId ?? undefined,
|
||||
resourceCounts: {
|
||||
repositories: project.giteaRepositories.length,
|
||||
readyRepositories: project.giteaRepositories.filter((repository) => repository.syncStatus === "ready").length,
|
||||
sources: project.sourceItems.filter((source) => source.status !== "archived").length,
|
||||
documentation: project._count.documentationItems,
|
||||
pendingReviews: project._count.reviewQueueItems,
|
||||
attentionItems: project._count.reviewQueueItems + project._count.initiatives,
|
||||
activeFindings: health.activeFindingCount,
|
||||
criticalFindings: health.criticalCount,
|
||||
warningFindings: health.warningCount,
|
||||
regressedFindings: health.regressedCount,
|
||||
contextPacks: project._count.contextPacks,
|
||||
features: project._count.features,
|
||||
deliveryPlans: project._count.deliveryPlans,
|
||||
knowledge: project._count.knowledgeItems,
|
||||
enabledSkills: configuredSkills.filter((skill) => isRecord(skill) && skill.enabled === true).length,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const toWorkspacePayload = (
|
||||
value: Prisma.JsonValue,
|
||||
): WorkspaceJsonPayload => {
|
||||
if (!isRecord(value)) return {};
|
||||
|
||||
return value as WorkspaceJsonPayload;
|
||||
};
|
||||
|
||||
const toWorkspaceState = (
|
||||
project: ProjectWithP2Relations,
|
||||
): WorkspaceStateDto | undefined => {
|
||||
if (!project.workspaceState) return undefined;
|
||||
|
||||
const payload = toWorkspacePayload(project.workspaceState.notesJson);
|
||||
if (payload.workspaceState) return payload.workspaceState;
|
||||
|
||||
return {
|
||||
projectId: project.slug,
|
||||
activeContextPackId:
|
||||
project.workspaceState.activeContextPackId ?? undefined,
|
||||
primaryObjective: project.workspaceState.primaryObjective,
|
||||
focusAreas: [],
|
||||
openTasksCount: 0,
|
||||
openBugCount: 0,
|
||||
activeSection: project.workspaceState
|
||||
.activeSection as WorkspaceStateDto["activeSection"],
|
||||
updatedAt: project.workspaceState.updatedAt.toISOString(),
|
||||
notes: [],
|
||||
};
|
||||
};
|
||||
|
||||
const toProjectDetail = (
|
||||
project: ProjectWithP2Relations,
|
||||
): ProjectDetailDto | undefined => {
|
||||
if (!project.dna) return undefined;
|
||||
|
||||
const workspacePayload = project.workspaceState
|
||||
? toWorkspacePayload(project.workspaceState.notesJson)
|
||||
: {};
|
||||
|
||||
return mergeProjectSetupState(workspacePayload.setupState, {
|
||||
...toProjectSummary(project),
|
||||
dna: toDna(project.dna),
|
||||
constraints: Array.isArray(workspacePayload.setupState?.constraints)
|
||||
? workspacePayload.setupState.constraints as ProjectDetailDto["constraints"]
|
||||
: [],
|
||||
principles: Array.isArray(workspacePayload.setupState?.principles)
|
||||
? workspacePayload.setupState.principles as ProjectDetailDto["principles"]
|
||||
: [],
|
||||
initiatives: [],
|
||||
featureGroups: Array.isArray(workspacePayload.setupState?.featureGroups)
|
||||
? workspacePayload.setupState.featureGroups as ProjectDetailDto["featureGroups"]
|
||||
: [],
|
||||
memoryRules: Array.isArray(workspacePayload.setupState?.memoryRules)
|
||||
? workspacePayload.setupState.memoryRules as ProjectDetailDto["memoryRules"]
|
||||
: [],
|
||||
promptTemplates: Array.isArray(workspacePayload.setupState?.promptTemplates)
|
||||
? workspacePayload.setupState.promptTemplates as ProjectDetailDto["promptTemplates"]
|
||||
: [],
|
||||
sourceLibrary: project.sourceItems.map((source) => ({
|
||||
id: source.id,
|
||||
projectId: project.slug,
|
||||
title: source.title,
|
||||
kind: source.kind,
|
||||
inputMode: source.inputMode,
|
||||
originalName: source.path ?? undefined,
|
||||
url: source.url ?? undefined,
|
||||
text: source.content,
|
||||
summary: source.summary,
|
||||
status: source.status,
|
||||
suggestedDocumentationTargets: [],
|
||||
suggestedContextPackIds: [],
|
||||
extractedAssetIds: [],
|
||||
createdAt: source.createdAt.toISOString(),
|
||||
updatedAt: source.updatedAt.toISOString(),
|
||||
repositoryPath: source.path ?? undefined,
|
||||
repositoryBranch: source.branch ?? undefined,
|
||||
commitSha: source.commitSha ?? undefined,
|
||||
lastSyncedAt: source.lastSyncedAt?.toISOString(),
|
||||
attribution: { createdBy: "imported", sourceRun: source.commitSha ?? undefined, approvalState: "approved" },
|
||||
})),
|
||||
workspaceState: toWorkspaceState(project),
|
||||
} as ProjectDetailDto & Record<string, unknown>);
|
||||
};
|
||||
|
||||
const defaultDnaForProject = (
|
||||
input: CreateProjectRequestDto,
|
||||
): Omit<ProjectDna, "updatedAt"> => ({
|
||||
vision: input.dna?.vision || input.primaryGoal || input.description,
|
||||
goals: input.dna?.goals ?? (input.primaryGoal ? [input.primaryGoal] : []),
|
||||
nonGoals: input.dna?.nonGoals ?? [],
|
||||
targetAudience: input.dna?.targetAudience || input.targetAudience || "Delivery teams",
|
||||
principles: input.dna?.principles ?? [],
|
||||
successMetrics: input.dna?.successMetrics ?? [],
|
||||
maturityStage: input.dna?.maturityStage ?? "Discovery",
|
||||
revision: input.dna?.revision ?? 1,
|
||||
});
|
||||
|
||||
const defaultWorkspacePayload = (
|
||||
projectSlug: string,
|
||||
input: CreateProjectRequestDto,
|
||||
): WorkspaceJsonPayload => {
|
||||
const setupWorkspace = projectSetupWorkspaceState(input.setupState);
|
||||
const workspaceState = {
|
||||
...setupWorkspace,
|
||||
projectId: projectSlug,
|
||||
primaryObjective: typeof setupWorkspace?.primaryObjective === "string"
|
||||
? setupWorkspace.primaryObjective
|
||||
: input.primaryGoal || input.description,
|
||||
focusAreas: Array.isArray(setupWorkspace?.focusAreas) ? setupWorkspace.focusAreas : [],
|
||||
openTasksCount: typeof setupWorkspace?.openTasksCount === "number" ? setupWorkspace.openTasksCount : 0,
|
||||
openBugCount: typeof setupWorkspace?.openBugCount === "number" ? setupWorkspace.openBugCount : 0,
|
||||
activeSection: typeof setupWorkspace?.activeSection === "string" ? setupWorkspace.activeSection : "workspace",
|
||||
updatedAt: new Date().toISOString(),
|
||||
notes: Array.isArray(setupWorkspace?.notes) ? setupWorkspace.notes : [],
|
||||
...(projectSetupActiveContextPackId(input.setupState)
|
||||
? { activeContextPackId: projectSetupActiveContextPackId(input.setupState) }
|
||||
: {}),
|
||||
} as WorkspaceStateDto;
|
||||
|
||||
return {
|
||||
workspaceState,
|
||||
focusAreas: workspaceState.focusAreas,
|
||||
healthSummary: {
|
||||
health: 100,
|
||||
healthStatus: "healthy",
|
||||
openTasksCount: workspaceState.openTasksCount,
|
||||
openBugCount: workspaceState.openBugCount,
|
||||
riskCount: 0,
|
||||
pendingReviewCount: 0,
|
||||
},
|
||||
setupState: sanitizeProjectSetupState(input.setupState),
|
||||
};
|
||||
};
|
||||
|
||||
const findProjectRecordBySlugOrId = (projectIdOrSlug: string) =>
|
||||
prisma.project.findFirst({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
OR: [{ id: projectIdOrSlug }, { slug: projectIdOrSlug }],
|
||||
},
|
||||
include: projectRelationsInclude,
|
||||
});
|
||||
|
||||
export const listProjects = async (): Promise<ProjectSummaryDto[]> => {
|
||||
const projects = await prisma.project.findMany({
|
||||
where: {
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
include: projectRelationsInclude,
|
||||
});
|
||||
|
||||
return projects.filter((project) => !isDisposableProject(project)).map(toProjectSummary);
|
||||
};
|
||||
|
||||
export const createProject = async (
|
||||
input: CreateProjectRequestDto,
|
||||
): Promise<ProjectDetailDto> => {
|
||||
const slug = slugify(input.id || input.name);
|
||||
const existing = await prisma.project.findFirst({
|
||||
where: {
|
||||
slug,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
throw new Error("PROJECT_SLUG_ALREADY_EXISTS");
|
||||
}
|
||||
|
||||
const dna = defaultDnaForProject(input);
|
||||
const workspacePayload = defaultWorkspacePayload(slug, input);
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
slug,
|
||||
readableId: slug,
|
||||
name: input.name.trim(),
|
||||
description: input.description.trim(),
|
||||
domain: input.domain?.trim() || "general",
|
||||
status: "active",
|
||||
health: 100,
|
||||
healthStatus: "healthy",
|
||||
activeContextPackId: projectSetupActiveContextPackId(input.setupState),
|
||||
createdBy: "frontend",
|
||||
updatedBy: "frontend",
|
||||
sourceType: "project_setup",
|
||||
dna: {
|
||||
create: {
|
||||
vision: dna.vision,
|
||||
goalsJson: dna.goals,
|
||||
nonGoalsJson: dna.nonGoals,
|
||||
targetAudience: dna.targetAudience,
|
||||
principlesJson: dna.principles,
|
||||
metricsJson: dna.successMetrics,
|
||||
maturityStage: dna.maturityStage,
|
||||
revision: dna.revision,
|
||||
createdBy: "frontend",
|
||||
updatedBy: "frontend",
|
||||
sourceType: "project_setup",
|
||||
},
|
||||
},
|
||||
workspaceState: {
|
||||
create: {
|
||||
primaryObjective: workspacePayload.workspaceState?.primaryObjective ?? input.primaryGoal ?? input.description.trim(),
|
||||
activeContextPackId: projectSetupActiveContextPackId(input.setupState),
|
||||
activeSection: workspacePayload.workspaceState?.activeSection ?? "workspace",
|
||||
notesJson: workspacePayload as Prisma.InputJsonValue,
|
||||
createdBy: "frontend",
|
||||
updatedBy: "frontend",
|
||||
sourceType: "project_setup",
|
||||
},
|
||||
},
|
||||
},
|
||||
include: projectRelationsInclude,
|
||||
});
|
||||
|
||||
const detail = toProjectDetail(project);
|
||||
if (!detail) {
|
||||
throw new Error("PROJECT_CREATE_FAILED");
|
||||
}
|
||||
|
||||
return detail;
|
||||
};
|
||||
|
||||
export const updateProject = async (
|
||||
projectIdOrSlug: string,
|
||||
input: UpdateProjectRequestDto,
|
||||
): Promise<ProjectDetailDto | undefined> => {
|
||||
const existing = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!existing) return undefined;
|
||||
|
||||
const project = await prisma.project.update({
|
||||
where: {
|
||||
id: existing.id,
|
||||
},
|
||||
data: {
|
||||
...(input.name !== undefined ? { name: input.name.trim() } : {}),
|
||||
...(input.description !== undefined ? { description: input.description.trim() } : {}),
|
||||
...(input.domain !== undefined ? { domain: input.domain.trim() || "general" } : {}),
|
||||
...(input.status !== undefined ? { status: input.status } : {}),
|
||||
updatedBy: "frontend",
|
||||
},
|
||||
include: projectRelationsInclude,
|
||||
});
|
||||
|
||||
return toProjectDetail(project);
|
||||
};
|
||||
|
||||
export const updateActiveContextPack = async (
|
||||
projectIdOrSlug: string,
|
||||
input: UpdateActiveContextPackRequestDto,
|
||||
): Promise<WorkspaceReadDto | undefined> => {
|
||||
const existing = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!existing?.dna) return undefined;
|
||||
|
||||
const currentPayload = existing.workspaceState
|
||||
? toWorkspacePayload(existing.workspaceState.notesJson)
|
||||
: {};
|
||||
const currentWorkspace = toWorkspaceState(existing);
|
||||
const updatedAt = new Date().toISOString();
|
||||
const nextWorkspaceState: WorkspaceStateDto = {
|
||||
projectId: existing.slug,
|
||||
primaryObjective:
|
||||
currentWorkspace?.primaryObjective ??
|
||||
existing.workspaceState?.primaryObjective ??
|
||||
existing.description,
|
||||
focusAreas: currentWorkspace?.focusAreas ?? currentPayload.focusAreas ?? [],
|
||||
openTasksCount: currentWorkspace?.openTasksCount ?? 0,
|
||||
openBugCount: currentWorkspace?.openBugCount ?? 0,
|
||||
activeSection: currentWorkspace?.activeSection ?? "context-packs",
|
||||
updatedAt,
|
||||
notes: currentWorkspace?.notes ?? [],
|
||||
activeContextPackId: input.contextPackId,
|
||||
};
|
||||
|
||||
const payload: WorkspaceJsonPayload = {
|
||||
...currentPayload,
|
||||
workspaceState: nextWorkspaceState,
|
||||
focusAreas: currentPayload.focusAreas ?? nextWorkspaceState.focusAreas,
|
||||
activeContextPackId: input.contextPackId,
|
||||
};
|
||||
|
||||
await prisma.project.update({
|
||||
where: {
|
||||
id: existing.id,
|
||||
},
|
||||
data: {
|
||||
activeContextPackId: input.contextPackId,
|
||||
updatedBy: "frontend",
|
||||
workspaceState: {
|
||||
upsert: {
|
||||
create: {
|
||||
activeContextPackId: input.contextPackId,
|
||||
primaryObjective: nextWorkspaceState.primaryObjective,
|
||||
activeSection: nextWorkspaceState.activeSection,
|
||||
notesJson: payload as Prisma.InputJsonValue,
|
||||
createdBy: "frontend",
|
||||
updatedBy: "frontend",
|
||||
sourceType: "workspace_update",
|
||||
},
|
||||
update: {
|
||||
activeContextPackId: input.contextPackId,
|
||||
primaryObjective: nextWorkspaceState.primaryObjective,
|
||||
activeSection: nextWorkspaceState.activeSection,
|
||||
notesJson: payload as Prisma.InputJsonValue,
|
||||
updatedBy: "frontend",
|
||||
sourceType: "workspace_update",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return getProjectWorkspace(existing.slug);
|
||||
};
|
||||
|
||||
export const getProjectBySlugOrId = async (
|
||||
projectIdOrSlug: string,
|
||||
): Promise<ProjectDetailDto | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project) return undefined;
|
||||
|
||||
return toProjectDetail(project);
|
||||
};
|
||||
|
||||
export const getProjectDNA = async (
|
||||
projectIdOrSlug: string,
|
||||
): Promise<ProjectDna | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project?.dna) return undefined;
|
||||
|
||||
return toDna(project.dna);
|
||||
};
|
||||
|
||||
export const getProjectWorkspace = async (
|
||||
projectIdOrSlug: string,
|
||||
): Promise<WorkspaceReadDto | undefined> => {
|
||||
const project = await findProjectRecordBySlugOrId(projectIdOrSlug);
|
||||
if (!project?.dna) return undefined;
|
||||
|
||||
const workspaceState = toWorkspaceState(project);
|
||||
const payload = project.workspaceState
|
||||
? toWorkspacePayload(project.workspaceState.notesJson)
|
||||
: {};
|
||||
const activeContextPackId =
|
||||
workspaceState?.activeContextPackId ??
|
||||
project.activeContextPackId ??
|
||||
undefined;
|
||||
const operationalHealth = projectHealth(project);
|
||||
|
||||
return {
|
||||
project: toProjectSummary(project),
|
||||
dna: toDna(project.dna),
|
||||
activeFocusAreaId: payload.activeFocusAreaId,
|
||||
activeContextPackId,
|
||||
focusAreas: payload.focusAreas ?? workspaceState?.focusAreas ?? [],
|
||||
workspaceState,
|
||||
currentStateSummary: payload.currentStateSummary,
|
||||
healthSummary: {
|
||||
...(payload.healthSummary ?? {}),
|
||||
health: operationalHealth.score,
|
||||
healthStatus: operationalHealth.healthStatus,
|
||||
openTasksCount: workspaceState?.openTasksCount ?? 0,
|
||||
openBugCount: workspaceState?.openBugCount ?? 0,
|
||||
riskCount: payload.healthSummary?.riskCount ?? 0,
|
||||
pendingReviewCount: payload.healthSummary?.pendingReviewCount ?? 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user