refactor: migrate role handling from tiers to roles module
- Removed the deprecated tiers.ts file and migrated all related functionality to roles/index.js. - Updated tests and tools to reflect the new role structure, replacing references to "dev", "qa", and "architect" with "developer", "tester", and "architect". - Adjusted workflow configurations and state management to accommodate the new role naming conventions. - Enhanced project registration and health check tools to support dynamic role handling. - Updated task creation, update, and completion processes to align with the new role definitions. - Improved documentation and comments to clarify role responsibilities and usage.
This commit is contained in:
@@ -18,7 +18,6 @@ import { log as auditLog } from "../audit.js";
|
||||
import { checkWorkerHealth, scanOrphanedLabels, fetchGatewaySessions, type SessionLookup } from "./health.js";
|
||||
import { projectTick } from "./tick.js";
|
||||
import { createProvider } from "../providers/index.js";
|
||||
import { getAllRoleIds } from "../roles/index.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -307,13 +306,13 @@ async function performHealthPass(
|
||||
const { provider } = await createProvider({ repo: project.repo, provider: project.provider });
|
||||
let fixedCount = 0;
|
||||
|
||||
for (const role of getAllRoleIds()) {
|
||||
for (const role of Object.keys(project.workers)) {
|
||||
// Check worker health (session liveness, label consistency, etc)
|
||||
const healthFixes = await checkWorkerHealth({
|
||||
workspaceDir,
|
||||
groupId,
|
||||
project,
|
||||
role: role as any,
|
||||
role,
|
||||
sessions,
|
||||
autoFix: true,
|
||||
provider,
|
||||
@@ -325,7 +324,7 @@ async function performHealthPass(
|
||||
workspaceDir,
|
||||
groupId,
|
||||
project,
|
||||
role: role as any,
|
||||
role,
|
||||
autoFix: true,
|
||||
provider,
|
||||
});
|
||||
@@ -336,10 +335,10 @@ async function performHealthPass(
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a project has active work (dev or qa).
|
||||
* Check if a project has any active worker.
|
||||
*/
|
||||
async function checkProjectActive(workspaceDir: string, groupId: string): Promise<boolean> {
|
||||
const fresh = (await readProjects(workspaceDir)).projects[groupId];
|
||||
if (!fresh) return false;
|
||||
return fresh.dev.active || fresh.qa.active;
|
||||
return Object.values(fresh.workers).some(w => w.active);
|
||||
}
|
||||
|
||||
@@ -17,40 +17,6 @@ import {
|
||||
type WorkflowConfig,
|
||||
} from "../workflow.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Backward compatibility exports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated Use getCompletionRule() from workflow.ts instead.
|
||||
* Kept for backward compatibility.
|
||||
*/
|
||||
export const COMPLETION_RULES: Record<string, CompletionRule> = {
|
||||
"dev:done": { from: "Doing", to: "To Test", gitPull: true, detectPr: true },
|
||||
"qa:pass": { from: "Testing", to: "Done", closeIssue: true },
|
||||
"qa:fail": { from: "Testing", to: "To Improve", reopenIssue: true },
|
||||
"qa:refine": { from: "Testing", to: "Refining" },
|
||||
"dev:blocked": { from: "Doing", to: "Refining" },
|
||||
"qa:blocked": { from: "Testing", to: "Refining" },
|
||||
"architect:done": { from: "Designing", to: "Planning" },
|
||||
"architect:blocked": { from: "Designing", to: "Refining" },
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use getNextStateDescription() from workflow.ts instead.
|
||||
*/
|
||||
export const NEXT_STATE: Record<string, string> = {
|
||||
"dev:done": "QA queue",
|
||||
"dev:blocked": "moved to Refining - needs human input",
|
||||
"qa:pass": "Done!",
|
||||
"qa:fail": "back to DEV",
|
||||
"qa:refine": "awaiting human decision",
|
||||
"qa:blocked": "moved to Refining - needs human input",
|
||||
"architect:done": "Planning — ready for review",
|
||||
"architect:blocked": "moved to Refining - needs clarification",
|
||||
};
|
||||
|
||||
// Re-export CompletionRule type for backward compatibility
|
||||
export type { CompletionRule };
|
||||
|
||||
export type CompletionOutput = {
|
||||
@@ -72,7 +38,7 @@ export function getRule(
|
||||
result: string,
|
||||
workflow: WorkflowConfig = DEFAULT_WORKFLOW,
|
||||
): CompletionRule | undefined {
|
||||
return getCompletionRule(workflow, role as "dev" | "qa", result) ?? undefined;
|
||||
return getCompletionRule(workflow, role, result) ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +47,7 @@ export function getRule(
|
||||
export async function executeCompletion(opts: {
|
||||
workspaceDir: string;
|
||||
groupId: string;
|
||||
role: "dev" | "qa" | "architect";
|
||||
role: string;
|
||||
result: string;
|
||||
issueId: number;
|
||||
summary?: string;
|
||||
|
||||
@@ -13,26 +13,6 @@ import {
|
||||
type Role,
|
||||
} from "../workflow.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated Use string labels from workflow config instead.
|
||||
* Kept for backward compatibility.
|
||||
*/
|
||||
export type QueueLabel = "To Improve" | "To Test" | "To Do";
|
||||
|
||||
/**
|
||||
* @deprecated Use getQueuePriority() instead.
|
||||
* Kept for backward compatibility.
|
||||
*/
|
||||
export const QUEUE_PRIORITY: Record<string, number> = {
|
||||
"To Improve": 3,
|
||||
"To Test": 2,
|
||||
"To Do": 1,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Workflow-driven helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -11,9 +11,9 @@ import { createProvider } from "../providers/index.js";
|
||||
import { selectLevel } from "../model-selector.js";
|
||||
import { getWorker, getSessionForLevel, readProjects } from "../projects.js";
|
||||
import { dispatchTask } from "../dispatch.js";
|
||||
import { getAllRoleIds, getLevelsForRole, getAllLevels, roleForLevel } from "../roles/index.js";
|
||||
import { getLevelsForRole, getAllLevels, roleForLevel } from "../roles/index.js";
|
||||
import { loadConfig } from "../config/index.js";
|
||||
import {
|
||||
DEFAULT_WORKFLOW,
|
||||
getQueueLabels,
|
||||
getAllQueueLabels,
|
||||
getActiveLabel,
|
||||
@@ -22,25 +22,6 @@ import {
|
||||
type Role,
|
||||
} from "../workflow.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Backward compatibility exports (deprecated)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated Use getQueueLabels(workflow, "dev") instead.
|
||||
*/
|
||||
export const DEV_LABELS: StateLabel[] = getQueueLabels(DEFAULT_WORKFLOW, "dev");
|
||||
|
||||
/**
|
||||
* @deprecated Use getQueueLabels(workflow, "qa") instead.
|
||||
*/
|
||||
export const QA_LABELS: StateLabel[] = getQueueLabels(DEFAULT_WORKFLOW, "qa");
|
||||
|
||||
/**
|
||||
* @deprecated Use getAllQueueLabels(workflow) instead.
|
||||
*/
|
||||
export const PRIORITY_ORDER: StateLabel[] = getAllQueueLabels(DEFAULT_WORKFLOW);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared helpers (used by tick, work-start, auto-pickup)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -68,7 +49,7 @@ export function detectLevelFromLabels(labels: string[]): string | null {
|
||||
*/
|
||||
export function detectRoleFromLabel(
|
||||
label: StateLabel,
|
||||
workflow: WorkflowConfig = DEFAULT_WORKFLOW,
|
||||
workflow: WorkflowConfig,
|
||||
): Role | null {
|
||||
return workflowDetectRole(workflow, label);
|
||||
}
|
||||
@@ -76,7 +57,7 @@ export function detectRoleFromLabel(
|
||||
export async function findNextIssueForRole(
|
||||
provider: Pick<IssueProvider, "listIssuesByLabel">,
|
||||
role: Role,
|
||||
workflow: WorkflowConfig = DEFAULT_WORKFLOW,
|
||||
workflow: WorkflowConfig,
|
||||
): Promise<{ issue: Issue; label: StateLabel } | null> {
|
||||
const labels = getQueueLabels(workflow, role);
|
||||
for (const label of labels) {
|
||||
@@ -93,8 +74,8 @@ export async function findNextIssueForRole(
|
||||
*/
|
||||
export async function findNextIssue(
|
||||
provider: Pick<IssueProvider, "listIssuesByLabel">,
|
||||
role?: Role,
|
||||
workflow: WorkflowConfig = DEFAULT_WORKFLOW,
|
||||
role: Role | undefined,
|
||||
workflow: WorkflowConfig,
|
||||
): Promise<{ issue: Issue; label: StateLabel } | null> {
|
||||
const labels = role
|
||||
? getQueueLabels(workflow, role)
|
||||
@@ -156,15 +137,20 @@ export async function projectTick(opts: {
|
||||
const {
|
||||
workspaceDir, groupId, agentId, sessionKey, pluginConfig, dryRun,
|
||||
maxPickups, targetRole, runtime,
|
||||
workflow = DEFAULT_WORKFLOW,
|
||||
} = opts;
|
||||
|
||||
const project = (await readProjects(workspaceDir)).projects[groupId];
|
||||
if (!project) return { pickups: [], skipped: [{ reason: `Project not found: ${groupId}` }] };
|
||||
|
||||
const resolvedConfig = await loadConfig(workspaceDir, project.name);
|
||||
const workflow = opts.workflow ?? resolvedConfig.workflow;
|
||||
|
||||
const provider = opts.provider ?? (await createProvider({ repo: project.repo, provider: project.provider })).provider;
|
||||
const roleExecution = project.roleExecution ?? "parallel";
|
||||
const roles: Role[] = targetRole ? [targetRole] : getAllRoleIds() as Role[];
|
||||
const enabledRoles = Object.entries(resolvedConfig.roles)
|
||||
.filter(([, r]) => r.enabled)
|
||||
.map(([id]) => id);
|
||||
const roles: Role[] = targetRole ? [targetRole] : enabledRoles;
|
||||
|
||||
const pickups: TickAction[] = [];
|
||||
const skipped: TickResult["skipped"] = [];
|
||||
@@ -186,8 +172,8 @@ export async function projectTick(opts: {
|
||||
continue;
|
||||
}
|
||||
// Check sequential role execution: any other role must be inactive
|
||||
const otherRoles = getAllRoleIds().filter(r => r !== role);
|
||||
if (roleExecution === "sequential" && otherRoles.some(r => getWorker(fresh, r as any).active)) {
|
||||
const otherRoles = enabledRoles.filter((r: string) => r !== role);
|
||||
if (roleExecution === "sequential" && otherRoles.some((r: string) => getWorker(fresh, r).active)) {
|
||||
skipped.push({ role, reason: "Sequential: other role active" });
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user