gsd-pi 2.60.0-dev.2580e65 → 2.60.0-dev.d9052f5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/ask-user-questions.js +4 -7
- package/dist/resources/extensions/gsd/auto/phases.js +7 -15
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -21
- package/dist/resources/extensions/gsd/auto-dispatch.js +3 -6
- package/dist/resources/extensions/gsd/auto-model-selection.js +9 -58
- package/dist/resources/extensions/gsd/auto-post-unit.js +2 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +20 -36
- package/dist/resources/extensions/gsd/auto-recovery.js +18 -37
- package/dist/resources/extensions/gsd/auto-start.js +5 -9
- package/dist/resources/extensions/gsd/auto-timers.js +5 -11
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +3 -5
- package/dist/resources/extensions/gsd/auto-verification.js +2 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +55 -120
- package/dist/resources/extensions/gsd/auto.js +17 -39
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -6
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +10 -4
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +1 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +0 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -11
- package/dist/resources/extensions/gsd/commands/catalog.js +0 -2
- package/dist/resources/extensions/gsd/commands-codebase.js +21 -48
- package/dist/resources/extensions/gsd/commands-inspect.js +1 -2
- package/dist/resources/extensions/gsd/commands-maintenance.js +19 -32
- package/dist/resources/extensions/gsd/complexity-classifier.js +4 -8
- package/dist/resources/extensions/gsd/custom-verification.js +2 -3
- package/dist/resources/extensions/gsd/gsd-db.js +13 -33
- package/dist/resources/extensions/gsd/guided-flow.js +9 -19
- package/dist/resources/extensions/gsd/init-wizard.js +0 -12
- package/dist/resources/extensions/gsd/markdown-renderer.js +9 -11
- package/dist/resources/extensions/gsd/md-importer.js +4 -5
- package/dist/resources/extensions/gsd/milestone-actions.js +2 -3
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -2
- package/dist/resources/extensions/gsd/model-router.js +121 -156
- package/dist/resources/extensions/gsd/parallel-merge.js +3 -5
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +14 -26
- package/dist/resources/extensions/gsd/preferences-types.js +0 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +0 -45
- package/dist/resources/extensions/gsd/preferences.js +3 -15
- package/dist/resources/extensions/gsd/prompt-loader.js +2 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/rule-registry.js +6 -7
- package/dist/resources/extensions/gsd/safe-fs.js +8 -6
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +2 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -3
- package/dist/resources/extensions/gsd/tools/complete-task.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +1 -2
- package/dist/resources/extensions/gsd/tools/reopen-task.js +1 -2
- package/dist/resources/extensions/gsd/tools/replan-slice.js +1 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +1 -2
- package/dist/resources/extensions/gsd/triage-resolution.js +4 -11
- package/dist/resources/extensions/gsd/workflow-events.js +1 -2
- package/dist/resources/extensions/gsd/workflow-logger.js +4 -37
- package/dist/resources/extensions/gsd/workflow-migration.js +12 -14
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +14 -26
- package/dist/resources/extensions/shared/interview-ui.js +1 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +0 -5
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +0 -16
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +0 -26
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +1 -6
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +0 -6
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +0 -19
- package/packages/pi-coding-agent/src/core/extensions/types.ts +0 -26
- package/packages/pi-coding-agent/src/core/lsp/config.ts +1 -7
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/src/resources/extensions/ask-user-questions.ts +3 -7
- package/src/resources/extensions/gsd/auto/phases.ts +7 -17
- package/src/resources/extensions/gsd/auto-dashboard.ts +8 -22
- package/src/resources/extensions/gsd/auto-dispatch.ts +3 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +15 -77
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +20 -37
- package/src/resources/extensions/gsd/auto-recovery.ts +18 -38
- package/src/resources/extensions/gsd/auto-start.ts +9 -10
- package/src/resources/extensions/gsd/auto-timers.ts +5 -12
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +2 -6
- package/src/resources/extensions/gsd/auto-verification.ts +6 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +55 -121
- package/src/resources/extensions/gsd/auto.ts +17 -40
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +1 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +0 -8
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +10 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +0 -2
- package/src/resources/extensions/gsd/commands-codebase.ts +20 -52
- package/src/resources/extensions/gsd/commands-inspect.ts +1 -2
- package/src/resources/extensions/gsd/commands-maintenance.ts +19 -28
- package/src/resources/extensions/gsd/complexity-classifier.ts +4 -9
- package/src/resources/extensions/gsd/custom-verification.ts +2 -3
- package/src/resources/extensions/gsd/gsd-db.ts +14 -12
- package/src/resources/extensions/gsd/guided-flow.ts +8 -9
- package/src/resources/extensions/gsd/init-wizard.ts +0 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +17 -11
- package/src/resources/extensions/gsd/md-importer.ts +4 -5
- package/src/resources/extensions/gsd/milestone-actions.ts +2 -3
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -2
- package/src/resources/extensions/gsd/model-router.ts +173 -199
- package/src/resources/extensions/gsd/parallel-merge.ts +3 -5
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +14 -18
- package/src/resources/extensions/gsd/preferences-types.ts +0 -13
- package/src/resources/extensions/gsd/preferences-validation.ts +0 -45
- package/src/resources/extensions/gsd/preferences.ts +3 -16
- package/src/resources/extensions/gsd/prompt-loader.ts +2 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/rule-registry.ts +6 -7
- package/src/resources/extensions/gsd/safe-fs.ts +5 -6
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +2 -27
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/model-router.test.ts +3 -403
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -62
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +0 -21
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -3
- package/src/resources/extensions/gsd/tools/complete-task.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-slice.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-task.ts +3 -2
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -4
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +3 -2
- package/src/resources/extensions/gsd/tools/reopen-task.ts +3 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -2
- package/src/resources/extensions/gsd/triage-resolution.ts +4 -11
- package/src/resources/extensions/gsd/types.ts +0 -1
- package/src/resources/extensions/gsd/workflow-events.ts +1 -2
- package/src/resources/extensions/gsd/workflow-logger.ts +5 -52
- package/src/resources/extensions/gsd/workflow-migration.ts +12 -14
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +14 -16
- package/src/resources/extensions/shared/interview-ui.ts +1 -3
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +0 -47
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +0 -1
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +0 -70
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +0 -347
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +0 -1188
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +0 -841
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +0 -284
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +0 -120
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +0 -144
- /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_ssgManifest.js +0 -0
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// GSD2 — Regression test for LSP legacy server key aliases
|
|
2
|
-
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* When a default server key is renamed (e.g., kotlin-language-server → kotlin-lsp),
|
|
6
|
-
* user overrides referencing the old key must still merge correctly via LEGACY_ALIASES.
|
|
7
|
-
*
|
|
8
|
-
* This test exercises the merge path through loadConfig() with a temp project
|
|
9
|
-
* containing an lsp.json that uses the legacy key.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
13
|
-
import assert from "node:assert/strict";
|
|
14
|
-
import * as fs from "node:fs";
|
|
15
|
-
import * as path from "node:path";
|
|
16
|
-
import * as os from "node:os";
|
|
17
|
-
import { loadConfig } from "./config.js";
|
|
18
|
-
|
|
19
|
-
describe("LSP legacy server key aliases", () => {
|
|
20
|
-
let tmpDir: string;
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "lsp-alias-test-"));
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("merges user override with legacy key 'kotlin-language-server' into 'kotlin-lsp'", () => {
|
|
31
|
-
// Write an lsp.json that uses the old key name with a command that exists (node)
|
|
32
|
-
// so resolveCommand doesn't filter it out.
|
|
33
|
-
const overrideConfig = {
|
|
34
|
-
servers: {
|
|
35
|
-
"kotlin-language-server": {
|
|
36
|
-
command: "node",
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
fs.writeFileSync(
|
|
41
|
-
path.join(tmpDir, "lsp.json"),
|
|
42
|
-
JSON.stringify(overrideConfig),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// Also add root markers so the server is detected
|
|
46
|
-
fs.writeFileSync(path.join(tmpDir, "build.gradle.kts"), "");
|
|
47
|
-
|
|
48
|
-
const config = loadConfig(tmpDir);
|
|
49
|
-
|
|
50
|
-
// The merged config should have kotlin-lsp (new key) with the user's command override
|
|
51
|
-
const kotlinServer = config.servers["kotlin-lsp"];
|
|
52
|
-
assert.ok(kotlinServer, "kotlin-lsp should exist in merged config");
|
|
53
|
-
assert.equal(
|
|
54
|
-
kotlinServer.command,
|
|
55
|
-
"node",
|
|
56
|
-
"command should be overridden from user config via legacy alias",
|
|
57
|
-
);
|
|
58
|
-
assert.ok(
|
|
59
|
-
kotlinServer.fileTypes.includes(".kt"),
|
|
60
|
-
"fileTypes should be inherited from defaults",
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// The old key should NOT appear as a separate entry
|
|
64
|
-
assert.equal(
|
|
65
|
-
config.servers["kotlin-language-server"],
|
|
66
|
-
undefined,
|
|
67
|
-
"legacy key should not appear as separate server",
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
// GSD Extension — Capability-Aware Router Tests
|
|
2
|
-
// Tests for new capability scoring functions and data tables (Plan 01-01)
|
|
3
|
-
|
|
4
|
-
import { describe, test } from "node:test";
|
|
5
|
-
import assert from "node:assert/strict";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
scoreModel,
|
|
9
|
-
computeTaskRequirements,
|
|
10
|
-
scoreEligibleModels,
|
|
11
|
-
getEligibleModels,
|
|
12
|
-
resolveModelForComplexity,
|
|
13
|
-
MODEL_CAPABILITY_PROFILES,
|
|
14
|
-
BASE_REQUIREMENTS,
|
|
15
|
-
defaultRoutingConfig,
|
|
16
|
-
} from "../model-router.js";
|
|
17
|
-
import type { ModelCapabilities, DynamicRoutingConfig, RoutingDecision } from "../model-router.js";
|
|
18
|
-
|
|
19
|
-
// ─── scoreModel ──────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
describe("scoreModel", () => {
|
|
22
|
-
const sonnetProfile: ModelCapabilities = {
|
|
23
|
-
coding: 85, debugging: 80, research: 75, reasoning: 80,
|
|
24
|
-
speed: 60, longContext: 75, instruction: 85,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
test("produces correct weighted average for single dimension", () => {
|
|
28
|
-
// Only coding weight 1.0 → result should be the coding score
|
|
29
|
-
const score = scoreModel(sonnetProfile, { coding: 1.0 });
|
|
30
|
-
assert.equal(score, 85);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("produces correct weighted average for two dimensions (coding 0.9, instruction 0.7)", () => {
|
|
34
|
-
// (0.9*85 + 0.7*85) / (0.9+0.7) = (76.5+59.5)/1.6 = 136/1.6 = 85.0
|
|
35
|
-
const score = scoreModel(sonnetProfile, { coding: 0.9, instruction: 0.7 });
|
|
36
|
-
assert.ok(Math.abs(score - 85.0) < 0.01, `Expected ~85.0, got ${score}`);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("returns 50 when requirements is empty", () => {
|
|
40
|
-
const score = scoreModel(sonnetProfile, {});
|
|
41
|
-
assert.equal(score, 50);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("uses 50 as fallback for unknown dimension in requirements", () => {
|
|
45
|
-
// 'unknown' dimension not in profile → treated as 50
|
|
46
|
-
const score = scoreModel(sonnetProfile, { coding: 0.5, unknown: 1.0 } as any);
|
|
47
|
-
// (0.5*85 + 1.0*50) / (0.5+1.0) = (42.5+50)/1.5 = 92.5/1.5 = 61.67
|
|
48
|
-
assert.ok(score > 61 && score < 62, `Expected ~61.67, got ${score}`);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// ─── computeTaskRequirements ─────────────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
describe("computeTaskRequirements", () => {
|
|
55
|
-
test("execute-task with no metadata returns base requirements", () => {
|
|
56
|
-
const req = computeTaskRequirements("execute-task", undefined);
|
|
57
|
-
assert.deepStrictEqual(req, { coding: 0.9, instruction: 0.7, speed: 0.3 });
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("execute-task with docs tag returns docs-adjusted requirements", () => {
|
|
61
|
-
const req = computeTaskRequirements("execute-task", { tags: ["docs"] });
|
|
62
|
-
assert.equal(req.instruction, 0.9);
|
|
63
|
-
assert.equal(req.coding, 0.3);
|
|
64
|
-
assert.equal(req.speed, 0.7);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("execute-task with readme tag returns docs-adjusted requirements", () => {
|
|
68
|
-
const req = computeTaskRequirements("execute-task", { tags: ["readme"] });
|
|
69
|
-
assert.equal(req.instruction, 0.9);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("execute-task with concurrency keyword boosts debugging and reasoning", () => {
|
|
73
|
-
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["concurrency"] });
|
|
74
|
-
assert.equal(req.debugging, 0.9);
|
|
75
|
-
assert.equal(req.reasoning, 0.8);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("execute-task with compatibility keyword boosts debugging and reasoning", () => {
|
|
79
|
-
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["compatibility"] });
|
|
80
|
-
assert.equal(req.debugging, 0.9);
|
|
81
|
-
assert.equal(req.reasoning, 0.8);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("execute-task with migration keyword boosts reasoning and coding", () => {
|
|
85
|
-
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["migration"] });
|
|
86
|
-
assert.equal(req.reasoning, 0.9);
|
|
87
|
-
assert.equal(req.coding, 0.8);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("execute-task with architecture keyword boosts reasoning and coding", () => {
|
|
91
|
-
const req = computeTaskRequirements("execute-task", { complexityKeywords: ["architecture"] });
|
|
92
|
-
assert.equal(req.reasoning, 0.9);
|
|
93
|
-
assert.equal(req.coding, 0.8);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("execute-task with fileCount >= 6 boosts coding and reasoning", () => {
|
|
97
|
-
const req = computeTaskRequirements("execute-task", { fileCount: 8 });
|
|
98
|
-
assert.equal(req.coding, 0.9);
|
|
99
|
-
assert.equal(req.reasoning, 0.7);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("execute-task with fileCount exactly 6 triggers large-file boost", () => {
|
|
103
|
-
const req = computeTaskRequirements("execute-task", { fileCount: 6 });
|
|
104
|
-
assert.equal(req.coding, 0.9);
|
|
105
|
-
assert.equal(req.reasoning, 0.7);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("execute-task with estimatedLines >= 500 boosts coding and reasoning", () => {
|
|
109
|
-
const req = computeTaskRequirements("execute-task", { estimatedLines: 500 });
|
|
110
|
-
assert.equal(req.coding, 0.9);
|
|
111
|
-
assert.equal(req.reasoning, 0.7);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("research-milestone with no metadata returns base requirements", () => {
|
|
115
|
-
const req = computeTaskRequirements("research-milestone", undefined);
|
|
116
|
-
assert.deepStrictEqual(req, { research: 0.9, longContext: 0.7, reasoning: 0.5 });
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("unknown unit type returns default reasoning requirement", () => {
|
|
120
|
-
const req = computeTaskRequirements("unknown-type", undefined);
|
|
121
|
-
assert.deepStrictEqual(req, { reasoning: 0.5 });
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// ─── MODEL_CAPABILITY_PROFILES ───────────────────────────────────────────────
|
|
126
|
-
|
|
127
|
-
describe("MODEL_CAPABILITY_PROFILES", () => {
|
|
128
|
-
test("contains all 9 required models", () => {
|
|
129
|
-
const required = [
|
|
130
|
-
"claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5",
|
|
131
|
-
"gpt-4o", "gpt-4o-mini", "gemini-2.5-pro", "gemini-2.0-flash",
|
|
132
|
-
"deepseek-chat", "o3",
|
|
133
|
-
];
|
|
134
|
-
for (const model of required) {
|
|
135
|
-
assert.ok(MODEL_CAPABILITY_PROFILES[model], `Missing profile for ${model}`);
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("each profile has all 7 capability dimensions", () => {
|
|
140
|
-
const dims: Array<keyof ModelCapabilities> = [
|
|
141
|
-
"coding", "debugging", "research", "reasoning",
|
|
142
|
-
"speed", "longContext", "instruction",
|
|
143
|
-
];
|
|
144
|
-
for (const [modelId, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
|
|
145
|
-
for (const dim of dims) {
|
|
146
|
-
assert.ok(profile[dim] !== undefined, `${modelId} missing dimension ${dim}`);
|
|
147
|
-
assert.ok(profile[dim] >= 0 && profile[dim] <= 100, `${modelId}.${dim} out of range`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test("claude-opus-4-6 has high reasoning and coding", () => {
|
|
153
|
-
const opus = MODEL_CAPABILITY_PROFILES["claude-opus-4-6"];
|
|
154
|
-
assert.ok(opus.reasoning >= 90, `Expected reasoning >= 90, got ${opus.reasoning}`);
|
|
155
|
-
assert.ok(opus.coding >= 90, `Expected coding >= 90, got ${opus.coding}`);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test("claude-haiku-4-5 has high speed but lower reasoning", () => {
|
|
159
|
-
const haiku = MODEL_CAPABILITY_PROFILES["claude-haiku-4-5"];
|
|
160
|
-
assert.ok(haiku.speed >= 90, `Expected speed >= 90, got ${haiku.speed}`);
|
|
161
|
-
assert.ok(haiku.reasoning < 70, `Expected reasoning < 70, got ${haiku.reasoning}`);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// ─── BASE_REQUIREMENTS ───────────────────────────────────────────────────────
|
|
166
|
-
|
|
167
|
-
describe("BASE_REQUIREMENTS", () => {
|
|
168
|
-
test("contains all 11 unit types", () => {
|
|
169
|
-
const required = [
|
|
170
|
-
"execute-task", "research-milestone", "research-slice",
|
|
171
|
-
"plan-milestone", "plan-slice", "replan-slice",
|
|
172
|
-
"reassess-roadmap", "complete-slice", "run-uat",
|
|
173
|
-
"discuss-milestone", "complete-milestone",
|
|
174
|
-
];
|
|
175
|
-
for (const unitType of required) {
|
|
176
|
-
assert.ok(BASE_REQUIREMENTS[unitType], `Missing requirements for ${unitType}`);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// ─── scoreEligibleModels ─────────────────────────────────────────────────────
|
|
182
|
-
|
|
183
|
-
describe("scoreEligibleModels", () => {
|
|
184
|
-
test("returns array sorted by score descending", () => {
|
|
185
|
-
const requirements = { research: 0.9, longContext: 0.7, reasoning: 0.5 };
|
|
186
|
-
const results = scoreEligibleModels(["claude-sonnet-4-6", "gpt-4o"], requirements);
|
|
187
|
-
assert.ok(results.length === 2);
|
|
188
|
-
assert.ok(results[0].score >= results[1].score, "Should be sorted descending by score");
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
test("returns single model when only one eligible", () => {
|
|
192
|
-
const requirements = { coding: 0.9 };
|
|
193
|
-
const results = scoreEligibleModels(["claude-sonnet-4-6"], requirements);
|
|
194
|
-
assert.equal(results.length, 1);
|
|
195
|
-
assert.equal(results[0].modelId, "claude-sonnet-4-6");
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test("models without profiles get uniform 50s score", () => {
|
|
199
|
-
const requirements = { coding: 1.0 };
|
|
200
|
-
const results = scoreEligibleModels(["unknown-model-xyz"], requirements);
|
|
201
|
-
assert.equal(results[0].score, 50);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
test("when two models score within 2 points, prefers cheaper model", () => {
|
|
205
|
-
// gemini-2.0-flash is cheaper than gpt-4o-mini ($0.0001 vs $0.00015)
|
|
206
|
-
// Use a requirement that causes similar scores for both
|
|
207
|
-
const requirements = { speed: 1.0 };
|
|
208
|
-
const results = scoreEligibleModels(["gpt-4o-mini", "gemini-2.0-flash"], requirements);
|
|
209
|
-
// Both are high-speed: gpt-4o-mini=90, gemini-2.0-flash=95 — scores differ by 5, not within 2
|
|
210
|
-
// So top should be gemini-2.0-flash by score
|
|
211
|
-
assert.equal(results[0].modelId, "gemini-2.0-flash");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("tie-breaks by lexicographic model ID when cost and score are equal", () => {
|
|
215
|
-
// Use models without cost entries — both get Infinity cost
|
|
216
|
-
const requirements = { coding: 1.0 };
|
|
217
|
-
const results = scoreEligibleModels(["model-z", "model-a"], requirements);
|
|
218
|
-
// Both unknown → score=50, cost=Infinity → tiebreak by ID
|
|
219
|
-
assert.equal(results[0].modelId, "model-a");
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test("scoreEligibleModels respects capabilityOverrides", () => {
|
|
223
|
-
const requirements = { coding: 1.0 };
|
|
224
|
-
// Override claude-sonnet-4-6's coding to 30 (worse)
|
|
225
|
-
const results = scoreEligibleModels(
|
|
226
|
-
["claude-sonnet-4-6", "gpt-4o"],
|
|
227
|
-
requirements,
|
|
228
|
-
{ "claude-sonnet-4-6": { coding: 30 } },
|
|
229
|
-
);
|
|
230
|
-
// gpt-4o coding=80 should beat overridden sonnet coding=30
|
|
231
|
-
assert.equal(results[0].modelId, "gpt-4o");
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// ─── getEligibleModels ───────────────────────────────────────────────────────
|
|
236
|
-
|
|
237
|
-
describe("getEligibleModels", () => {
|
|
238
|
-
const MODELS = [
|
|
239
|
-
"claude-opus-4-6", // heavy
|
|
240
|
-
"claude-sonnet-4-6", // standard
|
|
241
|
-
"claude-haiku-4-5", // light
|
|
242
|
-
"gpt-4o-mini", // light
|
|
243
|
-
];
|
|
244
|
-
|
|
245
|
-
test("returns light-tier models sorted by cost when no explicit config", () => {
|
|
246
|
-
const config: DynamicRoutingConfig = defaultRoutingConfig();
|
|
247
|
-
const result = getEligibleModels("light", MODELS, config);
|
|
248
|
-
assert.ok(result.length >= 1);
|
|
249
|
-
// All results should be light-tier
|
|
250
|
-
for (const id of result) {
|
|
251
|
-
assert.ok(
|
|
252
|
-
["claude-haiku-4-5", "gpt-4o-mini"].includes(id),
|
|
253
|
-
`Expected light-tier model, got ${id}`,
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test("returns explicit tier_models when configured and available", () => {
|
|
259
|
-
const config: DynamicRoutingConfig = {
|
|
260
|
-
...defaultRoutingConfig(),
|
|
261
|
-
tier_models: { light: "gpt-4o-mini" },
|
|
262
|
-
};
|
|
263
|
-
const result = getEligibleModels("light", MODELS, config);
|
|
264
|
-
assert.deepStrictEqual(result, ["gpt-4o-mini"]);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test("returns empty array when no eligible models for tier", () => {
|
|
268
|
-
const config: DynamicRoutingConfig = defaultRoutingConfig();
|
|
269
|
-
// Only heavy model available, requesting light
|
|
270
|
-
const result = getEligibleModels("light", ["claude-opus-4-6"], config);
|
|
271
|
-
assert.equal(result.length, 0);
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
// ─── DynamicRoutingConfig extension ─────────────────────────────────────────
|
|
276
|
-
|
|
277
|
-
describe("DynamicRoutingConfig.capability_routing", () => {
|
|
278
|
-
test("defaultRoutingConfig includes capability_routing: true", () => {
|
|
279
|
-
const config = defaultRoutingConfig();
|
|
280
|
-
assert.equal(config.capability_routing, true);
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// ─── RoutingDecision.selectionMethod ─────────────────────────────────────────
|
|
285
|
-
|
|
286
|
-
describe("RoutingDecision.selectionMethod", () => {
|
|
287
|
-
const MODELS = ["claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "gpt-4o-mini"];
|
|
288
|
-
|
|
289
|
-
function makeClassification(tier: "light" | "standard" | "heavy") {
|
|
290
|
-
return { tier, reason: "test", downgraded: false };
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
test("returns selectionMethod: tier-only when routing is disabled", () => {
|
|
294
|
-
const config = { ...defaultRoutingConfig(), enabled: false };
|
|
295
|
-
const result: RoutingDecision = resolveModelForComplexity(
|
|
296
|
-
makeClassification("light"),
|
|
297
|
-
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
298
|
-
config,
|
|
299
|
-
MODELS,
|
|
300
|
-
);
|
|
301
|
-
assert.equal(result.selectionMethod, "tier-only");
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test("returns selectionMethod: tier-only for no phase config passthrough", () => {
|
|
305
|
-
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
306
|
-
const result: RoutingDecision = resolveModelForComplexity(
|
|
307
|
-
makeClassification("light"),
|
|
308
|
-
undefined,
|
|
309
|
-
config,
|
|
310
|
-
MODELS,
|
|
311
|
-
);
|
|
312
|
-
assert.equal(result.selectionMethod, "tier-only");
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
test("returns selectionMethod: tier-only for unknown model passthrough", () => {
|
|
316
|
-
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
317
|
-
const result: RoutingDecision = resolveModelForComplexity(
|
|
318
|
-
makeClassification("light"),
|
|
319
|
-
{ primary: "custom-provider/my-model-v3", fallbacks: [] },
|
|
320
|
-
config,
|
|
321
|
-
["custom-provider/my-model-v3", ...MODELS],
|
|
322
|
-
);
|
|
323
|
-
assert.equal(result.selectionMethod, "tier-only");
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test("returns selectionMethod: tier-only for no-downgrade passthrough", () => {
|
|
327
|
-
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
328
|
-
const result: RoutingDecision = resolveModelForComplexity(
|
|
329
|
-
makeClassification("heavy"),
|
|
330
|
-
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
331
|
-
config,
|
|
332
|
-
MODELS,
|
|
333
|
-
);
|
|
334
|
-
assert.equal(result.selectionMethod, "tier-only");
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test("returns selectionMethod: tier-only when downgraded", () => {
|
|
338
|
-
const config = { ...defaultRoutingConfig(), enabled: true };
|
|
339
|
-
const result: RoutingDecision = resolveModelForComplexity(
|
|
340
|
-
makeClassification("light"),
|
|
341
|
-
{ primary: "claude-opus-4-6", fallbacks: [] },
|
|
342
|
-
config,
|
|
343
|
-
MODELS,
|
|
344
|
-
);
|
|
345
|
-
assert.equal(result.selectionMethod, "tier-only");
|
|
346
|
-
});
|
|
347
|
-
});
|