Files
AgentCompany/src/main/task-rubrics.ts
T

101 lines
3.4 KiB
TypeScript

/**
* Task-type-specific business rubrics for quality validation.
*
* Imported by task-quality-gates.ts to extend validateTaskCreation with
* domain-aware checks for research, content, management, and other task types.
*/
import type { TaskType } from "@shared/types/enums";
export interface TaskRubric {
/** Sections the task description should contain (case-insensitive substring match). */
requiredSections: string[];
/** Keywords that signal the brief is grounded in quality criteria. */
qualityKeywords: string[];
/** Minimum description length for this task type. */
minDescriptionLength: number;
/** Human-readable template hint shown in warnings when sections are missing. */
briefTemplate: string;
}
/**
* Default rubric used by task types that carry no type-specific quality
* expectations. Single source of truth so "general" and "code" cannot drift.
*/
const DEFAULT_RUBRIC: TaskRubric = {
requiredSections: [],
qualityKeywords: [],
minDescriptionLength: 50,
briefTemplate: "",
};
const RUBRICS: Record<TaskType, TaskRubric> = {
research: {
requiredSections: ["research question", "methodology", "sources", "expected findings"],
qualityKeywords: ["data", "evidence", "analysis", "comparison", "findings"],
minDescriptionLength: 100,
briefTemplate:
"A research task brief should cover: Research Question, Methodology, Sources to consult, and Expected Findings.",
},
content: {
requiredSections: ["target audience", "brand voice", "key messages", "format"],
qualityKeywords: ["audience", "tone", "call to action", "engagement", "headline"],
minDescriptionLength: 80,
briefTemplate:
"A content task brief should cover: Target Audience, Brand Voice, Key Messages, and Format (e.g., blog post, social copy, email).",
},
management: {
requiredSections: ["objective", "stakeholders", "timeline", "success metrics"],
qualityKeywords: ["milestone", "deliverable", "risk", "dependency", "alignment"],
minDescriptionLength: 80,
briefTemplate:
"A management task brief should cover: Objective, Stakeholders, Timeline, and Success Metrics.",
},
general: DEFAULT_RUBRIC,
code: DEFAULT_RUBRIC,
};
/** Returns the rubric for the given task type, defaulting to "general" for unknown values. */
function getTaskTypeRubric(taskType: string): TaskRubric {
const key = taskType as TaskType;
return RUBRICS[key] ?? DEFAULT_RUBRIC;
}
/**
* Validates a task input against the type-specific rubric.
* Returns warnings (not errors) — missing sections are advisory,
* not blockers, to avoid rejecting valid heterodox formats.
*/
export function validateTaskRubric(input: {
title: string;
description: string;
type: string;
}): { warnings: string[] } {
const rubric = getTaskTypeRubric(input.type);
const warnings: string[] = [];
// No extra checks needed for code/general types
if (rubric.requiredSections.length === 0) {
return { warnings };
}
const lower = input.description.toLowerCase();
const missingSections = rubric.requiredSections.filter(
(section) => !lower.includes(section.toLowerCase()),
);
if (missingSections.length > 0) {
const listed = missingSections.map((s) => `"${s}"`).join(", ");
warnings.push(
`${capitalizeFirst(input.type)} task is missing recommended sections: ${listed}. ${rubric.briefTemplate}`,
);
}
return { warnings };
}
function capitalizeFirst(s: string): string {
return s.charAt(0).toUpperCase() + s.slice(1);
}