4 Commits

Author SHA1 Message Date
Sai-karthik
a43b7dd9dd Align recovery curator CTA with roleplay 2026-07-01 13:16:36 +00:00
Sai-karthik
4ec3f57210 Clean resume proof curator copy 2026-07-01 12:37:02 +00:00
Sai-karthik
9df22e01e7 Restrict curator streak tasks to active services 2026-07-01 12:27:31 +00:00
Sai-karthik
35052a0ced Merge remote-tracking branch 'origin/fix/curator-streak-agent-mapping' into staging 2026-07-01 11:16:09 +00:00
2 changed files with 85 additions and 4 deletions

View File

@@ -119,8 +119,17 @@ const QSCORE_TASK_SERVICE_REPLACEMENTS: Record<CuratorTaskType, CuratorServiceId
recovery: "roleplay-service",
};
const ACTIVE_CURATOR_SERVICES = new Set<CuratorServiceId>([
"interview-service",
"resume-service",
"roleplay-service",
"courses-service",
"matchmaking-service",
]);
function curatorAssignableServiceId(serviceId: CuratorServiceId, taskType: CuratorTaskType): CuratorServiceId {
if (serviceId === "qscore-service") return QSCORE_TASK_SERVICE_REPLACEMENTS[taskType];
if (serviceId === "cover-letter-service") return "resume-service";
if (serviceId === "social-branding-service") return taskType === "practice" ? "roleplay-service" : "resume-service";
if (serviceId === "assessment-service") return "matchmaking-service";
return serviceId;
@@ -897,6 +906,73 @@ function seed(
return { taskType, serviceId: assignedServiceId, title: copy.title, subtitle: copy.subtitle, effort, qxImpact, cta: copy.cta, signals: copy.signals };
}
function curatorTaskCopyForService(task: TaskSeed, serviceId: CuratorServiceId): TaskSeed {
if (task.serviceId === serviceId && ACTIVE_CURATOR_SERVICES.has(serviceId)) return task;
if (serviceId === "resume-service") {
return {
...task,
serviceId,
title: task.title.replace(/social proof|social profile|public credibility|visibility|cover letter/gi, "resume proof"),
subtitle: task.subtitle.replace(/social proof|social profile|public credibility|visibility|public positioning|cover letter/gi, "resume proof"),
cta: "Open resume workspace",
signals: task.signals
.map((signal) => signal.replace(/public proof|social proof|social|public|visibility|cover letter/gi, "resume proof"))
.map((signal) => signal.replace(/\bresume proof\s+proof\b/gi, "resume proof")),
};
}
if (serviceId === "matchmaking-service") {
return {
...task,
serviceId,
title: task.title
.replace(/Q Score|QScore|assessment|pathways/gi, "job matches")
.replace(/social proof|social profile|public credibility|visibility/gi, "job match"),
subtitle: task.subtitle
.replace(/Q Score|QScore|assessment service|assessment|pathways/gi, "job matching")
.replace(/social proof|social profile|public credibility|visibility|public positioning/gi, "job matching"),
cta: "View matches",
signals: task.signals.map((signal) => signal.replace(/q\s?score|assessment|social|public|visibility/gi, "job matching")),
};
}
if (serviceId === "roleplay-service") {
return {
...task,
serviceId,
title: task.title.replace(/social proof|social profile|public credibility|visibility|Q Score|QScore/gi, "roleplay practice"),
subtitle: task.subtitle.replace(/social proof|social profile|public credibility|visibility|public positioning|Q Score|QScore/gi, "roleplay practice"),
cta: "Open roleplay preview",
signals: task.signals.map((signal) => signal.replace(/social|public|visibility|q\s?score/gi, "roleplay practice")),
};
}
if (serviceId === "interview-service") {
return {
...task,
serviceId,
title: task.title.replace(/Q Score|QScore|assessment/gi, "interview practice"),
subtitle: task.subtitle.replace(/Q Score|QScore|assessment/gi, "interview practice"),
cta: "Open interview preview",
signals: task.signals.map((signal) => signal.replace(/q\s?score|assessment/gi, "interview practice")),
};
}
return {
...task,
serviceId,
};
}
function normalizeActiveCuratorTaskSeed(task: TaskSeed): TaskSeed {
const assignedServiceId = curatorAssignableServiceId(task.serviceId, task.taskType);
if (ACTIVE_CURATOR_SERVICES.has(assignedServiceId)) {
return curatorTaskCopyForService(task, assignedServiceId);
}
return curatorTaskCopyForService(task, QSCORE_TASK_SERVICE_REPLACEMENTS[task.taskType]);
}
function todayIso(date = new Date()) {
return date.toISOString().slice(0, 10);
}
@@ -1036,10 +1112,14 @@ function latestImprovementSignal(
}
function normalizeEventBackedTask(task: PlannedTask, planDay: PlanDaySeed, recentRows: Awaited<ReturnType<typeof loadRecentContextRows>>): PlannedTask {
const normalizedTask = normalizeActiveCuratorTaskSeed(task);
if (normalizedTask.serviceId !== task.serviceId) {
return makePlannedTask(normalizedTask, planDay.weekTheme, planDay.weekSummary);
}
if (hasDirectServiceCompletion(task.serviceId)) return task;
if (task.taskType === "proof") {
if (task.serviceId === "social-branding-service") return task;
return makePlannedTask(
seed(
"proof",
@@ -1430,12 +1510,12 @@ function recoveryTaskSeed(previousDayIndex: number, openedIncompleteCount: numbe
: `${skippedCount} planned task${skippedCount === 1 ? "" : "s"} had no opened or completion events`;
return seed(
"recovery",
"qscore-service",
"roleplay-service",
`Recover Day ${previousDayIndex} momentum`,
`Yesterday's event trail shows ${reason}. Review the blocker, pick one constructive adjustment, and keep the streak aligned.`,
"5 min",
"+5 projected",
"Review Q Score",
"Open roleplay preview",
["recovery", "alignment", "event trail"],
);
}
@@ -1571,6 +1651,7 @@ function buildTask(
focusDate: string,
targetRole?: string,
): CuratorTask {
seedTask = normalizeActiveCuratorTaskSeed(seedTask);
const dayOfWeek = dayIndexInWeek(sprintStartDate, dayIndex);
const id = taskIdFor(sprintStartDate, dayIndex, seedTask.taskType);
const missionInstanceId = sprintIdFor(sprintStartDate);

View File

@@ -499,7 +499,7 @@ export function buildCuratorTools(ctx: { userId: string; date: string; conversat
prepare_qscore_review: tool({
description: "Disabled for curator task handoffs; QScore is read-only for dashboard scoring and should not be assigned as a curator task.",
inputSchema: z.object({ taskId: z.string().optional() }),
execute: async () => ({ error: "qscore_curator_handoff_disabled", replacementServices: ["assessment-service", "interview-service", "roleplay-service", "resume-service", "matchmaking-service", "courses-service", "social-branding-service"] }),
execute: async () => ({ error: "qscore_curator_handoff_disabled", replacementServices: ["interview-service", "roleplay-service", "resume-service", "matchmaking-service", "courses-service"] }),
}),
emit_curator_event: tool({