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:
Lauren ten Hoor
2026-02-15 18:32:10 +08:00
parent 6a99752e5f
commit 0e24a68882
44 changed files with 1162 additions and 762 deletions

View File

@@ -13,10 +13,9 @@ import { selectLevel } from "../model-selector.js";
import { getWorker } from "../projects.js";
import { dispatchTask } from "../dispatch.js";
import { findNextIssue, detectRoleFromLabel, detectLevelFromLabels } from "../services/tick.js";
import { isDevLevel } from "../tiers.js";
import { getAllRoleIds } from "../roles/index.js";
import { getAllRoleIds, isLevelForRole } from "../roles/index.js";
import { requireWorkspaceDir, resolveProject, resolveProvider, getPluginConfig } from "../tool-helpers.js";
import { DEFAULT_WORKFLOW, getActiveLabel } from "../workflow.js";
import { loadWorkflow, getActiveLabel } from "../workflow.js";
export function createWorkStartTool(api: OpenClawPluginApi) {
return (ctx: ToolContext) => ({
@@ -36,7 +35,7 @@ export function createWorkStartTool(api: OpenClawPluginApi) {
async execute(_id: string, params: Record<string, unknown>) {
const issueIdParam = params.issueId as number | undefined;
const roleParam = params.role as "dev" | "qa" | "architect" | undefined;
const roleParam = params.role as string | undefined;
const groupId = params.projectGroupId as string;
const levelParam = (params.level ?? params.tier) as string | undefined;
const workspaceDir = requireWorkspaceDir(ctx);
@@ -45,8 +44,7 @@ export function createWorkStartTool(api: OpenClawPluginApi) {
const { project } = await resolveProject(workspaceDir, groupId);
const { provider } = await resolveProvider(project);
// TODO: Load per-project workflow when supported
const workflow = DEFAULT_WORKFLOW;
const workflow = await loadWorkflow(workspaceDir, project.name);
// Find issue
let issue: { iid: number; title: string; description: string; labels: string[]; web_url: string; state: string };
@@ -73,8 +71,11 @@ export function createWorkStartTool(api: OpenClawPluginApi) {
const worker = getWorker(project, role);
if (worker.active) throw new Error(`${role.toUpperCase()} already active on ${project.name} (issue: ${worker.issueId})`);
if ((project.roleExecution ?? "parallel") === "sequential") {
const other = role === "dev" ? "qa" : "dev";
if (getWorker(project, other).active) throw new Error(`Sequential roleExecution: ${other.toUpperCase()} is active`);
for (const [otherRole, otherWorker] of Object.entries(project.workers)) {
if (otherRole !== role && otherWorker.active) {
throw new Error(`Sequential roleExecution: ${otherRole.toUpperCase()} is active`);
}
}
}
// Get target label from workflow
@@ -87,9 +88,13 @@ export function createWorkStartTool(api: OpenClawPluginApi) {
} else {
const labelLevel = detectLevelFromLabels(issue.labels);
if (labelLevel) {
if (role === "qa" && isDevLevel(labelLevel)) { const s = selectLevel(issue.title, issue.description ?? "", role); selectedLevel = s.level; levelReason = `QA overrides dev level "${labelLevel}"`; levelSource = "role-override"; }
else if (role === "dev" && !isDevLevel(labelLevel)) { const s = selectLevel(issue.title, issue.description ?? "", role); selectedLevel = s.level; levelReason = s.reason; levelSource = "heuristic"; }
else { selectedLevel = labelLevel; levelReason = `Label: "${labelLevel}"`; levelSource = "label"; }
if (!isLevelForRole(labelLevel, role)) {
// Label level belongs to a different role — use heuristic for this role
const s = selectLevel(issue.title, issue.description ?? "", role);
selectedLevel = s.level; levelReason = `${role} overrides other role's level "${labelLevel}"`; levelSource = "role-override";
} else {
selectedLevel = labelLevel; levelReason = `Label: "${labelLevel}"`; levelSource = "label";
}
} else {
const s = selectLevel(issue.title, issue.description ?? "", role);
selectedLevel = s.level; levelReason = s.reason; levelSource = "heuristic";