Adds the Architect role for design/architecture investigations with persistent sessions and structured design proposals. ## New Features - **Architect role** with opus (complex) and sonnet (standard) levels - **design_task tool** — Creates To Design issues and dispatches architect - **Workflow states:** To Design → Designing → Planning - **Completion rules:** architect:done → Planning, architect:blocked → Refining - **Auto-level selection** based on complexity keywords ## Files Changed (22 files, 546 additions) ### New Files - lib/tools/design-task.ts — design_task tool implementation - lib/tools/design-task.test.ts — 16 tests for architect functionality ### Core Changes - lib/tiers.ts — ARCHITECT_LEVELS, WorkerRole type, models, emoji - lib/workflow.ts — toDesign/designing states, completion rules - lib/projects.ts — architect WorkerState on Project type - lib/dispatch.ts — architect role support in dispatch pipeline - lib/services/pipeline.ts — architect completion rules - lib/model-selector.ts — architect level selection heuristic ### Integration - index.ts — Register design_task tool, architect config schema - lib/notify.ts — architect role in notifications - lib/bootstrap-hook.ts — architect session key parsing - lib/services/tick.ts — architect in queue processing - lib/services/heartbeat.ts — architect in health checks - lib/tools/health.ts — architect in health scans - lib/tools/status.ts — architect in status dashboard - lib/tools/work-start.ts — architect role option - lib/tools/work-finish.ts — architect validation - lib/tools/project-register.ts — architect labels + role scaffolding - lib/templates.ts — architect instructions + AGENTS.md updates - lib/setup/workspace.ts — architect role file scaffolding - lib/setup/smart-model-selector.ts — architect in model assignment - lib/setup/llm-model-selector.ts — architect in LLM prompt
106 lines
4.1 KiB
TypeScript
106 lines
4.1 KiB
TypeScript
/**
|
|
* Tests for architect role, design_task tool, and workflow integration.
|
|
* Run with: npx tsx --test lib/tools/design-task.test.ts
|
|
*/
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert";
|
|
import { parseDevClawSessionKey } from "../bootstrap-hook.js";
|
|
import { isArchitectLevel, levelRole, resolveModel, defaultModel, levelEmoji } from "../tiers.js";
|
|
import { selectLevel } from "../model-selector.js";
|
|
import {
|
|
DEFAULT_WORKFLOW, getQueueLabels, getActiveLabel, getCompletionRule,
|
|
getCompletionEmoji, detectRoleFromLabel, getStateLabels,
|
|
} from "../workflow.js";
|
|
|
|
describe("architect tiers", () => {
|
|
it("should recognize architect levels", () => {
|
|
assert.strictEqual(isArchitectLevel("opus"), true);
|
|
assert.strictEqual(isArchitectLevel("sonnet"), true);
|
|
assert.strictEqual(isArchitectLevel("medior"), false);
|
|
});
|
|
|
|
it("should map architect levels to role", () => {
|
|
assert.strictEqual(levelRole("opus"), "architect");
|
|
assert.strictEqual(levelRole("sonnet"), "architect");
|
|
});
|
|
|
|
it("should resolve default architect models", () => {
|
|
assert.strictEqual(defaultModel("architect", "opus"), "anthropic/claude-opus-4-5");
|
|
assert.strictEqual(defaultModel("architect", "sonnet"), "anthropic/claude-sonnet-4-5");
|
|
});
|
|
|
|
it("should resolve architect model from config", () => {
|
|
const config = { models: { architect: { opus: "custom/model" } } };
|
|
assert.strictEqual(resolveModel("architect", "opus", config), "custom/model");
|
|
});
|
|
|
|
it("should have architect emoji", () => {
|
|
assert.strictEqual(levelEmoji("architect", "opus"), "🏗️");
|
|
assert.strictEqual(levelEmoji("architect", "sonnet"), "📐");
|
|
});
|
|
});
|
|
|
|
describe("architect workflow states", () => {
|
|
it("should include To Design and Designing in state labels", () => {
|
|
const labels = getStateLabels(DEFAULT_WORKFLOW);
|
|
assert.ok(labels.includes("To Design"));
|
|
assert.ok(labels.includes("Designing"));
|
|
});
|
|
|
|
it("should have To Design as architect queue label", () => {
|
|
const queues = getQueueLabels(DEFAULT_WORKFLOW, "architect");
|
|
assert.deepStrictEqual(queues, ["To Design"]);
|
|
});
|
|
|
|
it("should have Designing as architect active label", () => {
|
|
assert.strictEqual(getActiveLabel(DEFAULT_WORKFLOW, "architect"), "Designing");
|
|
});
|
|
|
|
it("should detect architect role from To Design label", () => {
|
|
assert.strictEqual(detectRoleFromLabel(DEFAULT_WORKFLOW, "To Design"), "architect");
|
|
});
|
|
|
|
it("should have architect:done completion rule", () => {
|
|
const rule = getCompletionRule(DEFAULT_WORKFLOW, "architect", "done");
|
|
assert.ok(rule);
|
|
assert.strictEqual(rule!.from, "Designing");
|
|
assert.strictEqual(rule!.to, "Planning");
|
|
});
|
|
|
|
it("should have architect:blocked completion rule", () => {
|
|
const rule = getCompletionRule(DEFAULT_WORKFLOW, "architect", "blocked");
|
|
assert.ok(rule);
|
|
assert.strictEqual(rule!.from, "Designing");
|
|
assert.strictEqual(rule!.to, "Refining");
|
|
});
|
|
|
|
it("should have architect completion emoji", () => {
|
|
assert.strictEqual(getCompletionEmoji("architect", "done"), "🏗️");
|
|
assert.strictEqual(getCompletionEmoji("architect", "blocked"), "🚫");
|
|
});
|
|
});
|
|
|
|
describe("architect model selection", () => {
|
|
it("should select sonnet for standard design tasks", () => {
|
|
const result = selectLevel("Design: Add caching layer", "Simple caching strategy", "architect");
|
|
assert.strictEqual(result.level, "sonnet");
|
|
});
|
|
|
|
it("should select opus for complex design tasks", () => {
|
|
const result = selectLevel("Design: System-wide refactor", "Major migration and redesign of the architecture", "architect");
|
|
assert.strictEqual(result.level, "opus");
|
|
});
|
|
});
|
|
|
|
describe("architect session key parsing", () => {
|
|
it("should parse architect session key", () => {
|
|
const result = parseDevClawSessionKey("agent:devclaw:subagent:my-project-architect-opus");
|
|
assert.deepStrictEqual(result, { projectName: "my-project", role: "architect" });
|
|
});
|
|
|
|
it("should parse architect sonnet session key", () => {
|
|
const result = parseDevClawSessionKey("agent:devclaw:subagent:webapp-architect-sonnet");
|
|
assert.deepStrictEqual(result, { projectName: "webapp", role: "architect" });
|
|
});
|
|
});
|