refactor: standardize level names across all roles (#195 phase 1)

Rename levels to use consistent industry-standard terminology:
- dev: medior → mid
- qa: reviewer → mid, tester → junior, add senior level
- architect: opus → senior, sonnet → junior

Add backward-compatible migration for projects.json and openclaw.json
config via level aliases in selectors and projects parser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lauren ten Hoor
2026-02-15 17:01:16 +08:00
parent 01ad5d326c
commit 6a99752e5f
24 changed files with 273 additions and 162 deletions

View File

@@ -14,29 +14,31 @@ import {
describe("architect tiers", () => {
it("should recognize architect levels", () => {
assert.strictEqual(isArchitectLevel("opus"), true);
assert.strictEqual(isArchitectLevel("sonnet"), true);
assert.strictEqual(isArchitectLevel("medior"), false);
assert.strictEqual(isArchitectLevel("junior"), true);
assert.strictEqual(isArchitectLevel("senior"), true);
assert.strictEqual(isArchitectLevel("mid"), false);
});
it("should map architect levels to role", () => {
assert.strictEqual(levelRole("opus"), "architect");
assert.strictEqual(levelRole("sonnet"), "architect");
// "junior" and "senior" appear in dev first (registry order), so roleForLevel returns "dev"
// This is expected — use isArchitectLevel for architect-specific checks
assert.strictEqual(levelRole("junior"), "dev");
assert.strictEqual(levelRole("senior"), "dev");
});
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");
assert.strictEqual(defaultModel("architect", "senior"), "anthropic/claude-opus-4-5");
assert.strictEqual(defaultModel("architect", "junior"), "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");
const config = { models: { architect: { senior: "custom/model" } } };
assert.strictEqual(resolveModel("architect", "senior", config), "custom/model");
});
it("should have architect emoji", () => {
assert.strictEqual(levelEmoji("architect", "opus"), "🏗️");
assert.strictEqual(levelEmoji("architect", "sonnet"), "📐");
assert.strictEqual(levelEmoji("architect", "senior"), "🏗️");
assert.strictEqual(levelEmoji("architect", "junior"), "📐");
});
});
@@ -81,25 +83,25 @@ describe("architect workflow states", () => {
});
describe("architect model selection", () => {
it("should select sonnet for standard design tasks", () => {
it("should select junior for standard design tasks", () => {
const result = selectLevel("Design: Add caching layer", "Simple caching strategy", "architect");
assert.strictEqual(result.level, "sonnet");
assert.strictEqual(result.level, "junior");
});
it("should select opus for complex design tasks", () => {
it("should select senior for complex design tasks", () => {
const result = selectLevel("Design: System-wide refactor", "Major migration and redesign of the architecture", "architect");
assert.strictEqual(result.level, "opus");
assert.strictEqual(result.level, "senior");
});
});
describe("architect session key parsing", () => {
it("should parse architect session key", () => {
const result = parseDevClawSessionKey("agent:devclaw:subagent:my-project-architect-opus");
const result = parseDevClawSessionKey("agent:devclaw:subagent:my-project-architect-senior");
assert.deepStrictEqual(result, { projectName: "my-project", role: "architect" });
});
it("should parse architect sonnet session key", () => {
const result = parseDevClawSessionKey("agent:devclaw:subagent:webapp-architect-sonnet");
it("should parse architect junior session key", () => {
const result = parseDevClawSessionKey("agent:devclaw:subagent:webapp-architect-junior");
assert.deepStrictEqual(result, { projectName: "webapp", role: "architect" });
});
});