Skip to content

Commit a8f962d

Browse files
committed
refactor(agent-loop): dedup director tests, coordinator guard, error coercion
Summary: - Drop per-director package-test assertions covered by the central registry loop; keep content-specific assertions per director. - Extract the shared director test harness into one helper module. - Merge the coordinator try/catch/rethrow/log/fallback skeletons into one generic helper preserving labels, fallbacks, rethrow semantics. - Merge twin close blocks in disposeSubAgentSession into a loop. - Add one shared errorMessage helper reused lane-wide. - Collapse repeated skill-list literals in identity.test.ts into one shared const. Verification: - Baseline (pristine): bun test src/agent src/subagent src/workflows src/director.test.ts --randomize --seed 424242 -> 1471 pass, 0 fail. - After: same command -> 1395 pass, 0 fail (delta is removed dupes). - bun run typecheck -> exit 0. bun run lint -> clean. bun run check:dead-exports -> 0 violations.
1 parent 810fd77 commit a8f962d

36 files changed

Lines changed: 226 additions & 641 deletions

src/agent/codex-read-raw-file.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from "../plugins/secret-guard-plugin.js";
3333
import type { PermissionGate } from "../permission/gate.js";
3434
import type { CodexReadRawFile } from "./codex-tool-proxies.js";
35+
import { errorMessage } from "./error-message.js";
3536

3637
/** `path` is workspace-relative (apply_patch's parser rejects absolute paths). */
3738
export function createCodexReadRawFile(
@@ -96,7 +97,7 @@ export function createCodexReadRawFile(
9697
return { content: `path is a directory: ${path}`, isError: true };
9798
}
9899
return {
99-
content: err instanceof Error ? err.message : String(err),
100+
content: errorMessage(err),
100101
isError: true,
101102
};
102103
}

src/agent/director.ts

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "./compaction.js";
2424
import { onTurnBoundary } from "./reactor-events.js";
2525
import { isOperatorOriginated } from "./message-provenance.js";
26+
import { errorMessage } from "./error-message.js";
2627
import { type } from "arktype";
2728
import {
2829
applyManageTasks,
@@ -656,79 +657,89 @@ class ChatDirectorImpl extends DefaultDirector {
656657
// is the exception: its sole call site runs mid-turn (tool.done, never the
657658
// turn boundary), so it always degrades to plain inference on a
658659
// coordinator throw and takes no rethrow parameter.
659-
private coordinatorIsActive(rethrowCoordinatorError: boolean): boolean {
660+
//
661+
// The four rethrow-capable consults below share one guard: on a coordinator
662+
// throw, either rethrow (noting it so the turn drops queued notifications)
663+
// or log under the consult's label and resolve the consult's fallback.
664+
private withCoordinatorGuard<T>(
665+
label: string,
666+
fallback: T,
667+
rethrowCoordinatorError: boolean,
668+
consult: () => T,
669+
): T {
660670
try {
661-
return this.workflowCoordinator?.isActive() === true;
671+
return consult();
662672
} catch (err) {
663673
if (rethrowCoordinatorError) {
664674
this.coordinatorRethrowNoted = true;
665675
throw err;
666676
}
667-
logger.warn`workflow-coordinator-isActive-threw error=${err instanceof Error ? err.message : String(err)}`;
668-
return false;
677+
logger.warn`${label} error=${errorMessage(err)}`;
678+
return fallback;
669679
}
670680
}
671681

682+
private coordinatorIsActive(rethrowCoordinatorError: boolean): boolean {
683+
return this.withCoordinatorGuard(
684+
"workflow-coordinator-isActive-threw",
685+
false,
686+
rethrowCoordinatorError,
687+
() => this.workflowCoordinator?.isActive() === true,
688+
);
689+
}
690+
672691
private coordinatorDirective(
673692
rethrowCoordinatorError: boolean,
674693
): string | null {
675-
try {
676-
const directive = this.workflowCoordinator?.directive() ?? null;
677-
if (directive === null) return null;
678-
if (typeof directive !== "string") {
679-
logger.warn`workflow-coordinator-directive-not-string`;
680-
return null;
681-
}
682-
if (directive.length === 0) return null;
683-
if (directive.length > MAX_WORKFLOW_DIRECTIVE_CHARS) {
684-
logger.warn`workflow-coordinator-directive-truncated chars=${String(directive.length)} max=${String(MAX_WORKFLOW_DIRECTIVE_CHARS)}`;
685-
return `${directive.slice(0, MAX_WORKFLOW_DIRECTIVE_CHARS)}\n…[truncated]`;
686-
}
687-
return directive;
688-
} catch (err) {
689-
if (rethrowCoordinatorError) {
690-
this.coordinatorRethrowNoted = true;
691-
throw err;
692-
}
693-
logger.warn`workflow-coordinator-directive-threw error=${err instanceof Error ? err.message : String(err)}`;
694-
return null;
695-
}
694+
return this.withCoordinatorGuard<string | null>(
695+
"workflow-coordinator-directive-threw",
696+
null,
697+
rethrowCoordinatorError,
698+
() => {
699+
const directive = this.workflowCoordinator?.directive() ?? null;
700+
if (directive === null) return null;
701+
if (typeof directive !== "string") {
702+
logger.warn`workflow-coordinator-directive-not-string`;
703+
return null;
704+
}
705+
if (directive.length === 0) return null;
706+
if (directive.length > MAX_WORKFLOW_DIRECTIVE_CHARS) {
707+
logger.warn`workflow-coordinator-directive-truncated chars=${String(directive.length)} max=${String(MAX_WORKFLOW_DIRECTIVE_CHARS)}`;
708+
return `${directive.slice(0, MAX_WORKFLOW_DIRECTIVE_CHARS)}\n…[truncated]`;
709+
}
710+
return directive;
711+
},
712+
);
696713
}
697714

698715
private coordinatorCurrentStepIsGate(
699716
rethrowCoordinatorError: boolean,
700717
): boolean {
701-
try {
702-
return this.workflowCoordinator?.currentStepIsGate() === true;
703-
} catch (err) {
704-
if (rethrowCoordinatorError) {
705-
this.coordinatorRethrowNoted = true;
706-
throw err;
707-
}
708-
logger.warn`workflow-coordinator-gate-threw error=${err instanceof Error ? err.message : String(err)}`;
709-
return false;
710-
}
718+
return this.withCoordinatorGuard(
719+
"workflow-coordinator-gate-threw",
720+
false,
721+
rethrowCoordinatorError,
722+
() => this.workflowCoordinator?.currentStepIsGate() === true,
723+
);
711724
}
712725

713726
private coordinatorCurrentStepId(
714727
rethrowCoordinatorError: boolean,
715728
): string | null {
716-
try {
717-
const stepId = this.workflowCoordinator?.currentStepId() ?? null;
718-
if (stepId === null) return null;
719-
if (typeof stepId !== "string" || stepId.length === 0) {
720-
logger.warn`workflow-coordinator-step-id-not-string`;
721-
return null;
722-
}
723-
return stepId;
724-
} catch (err) {
725-
if (rethrowCoordinatorError) {
726-
this.coordinatorRethrowNoted = true;
727-
throw err;
728-
}
729-
logger.warn`workflow-coordinator-step-id-threw error=${err instanceof Error ? err.message : String(err)}`;
730-
return null;
731-
}
729+
return this.withCoordinatorGuard<string | null>(
730+
"workflow-coordinator-step-id-threw",
731+
null,
732+
rethrowCoordinatorError,
733+
() => {
734+
const stepId = this.workflowCoordinator?.currentStepId() ?? null;
735+
if (stepId === null) return null;
736+
if (typeof stepId !== "string" || stepId.length === 0) {
737+
logger.warn`workflow-coordinator-step-id-not-string`;
738+
return null;
739+
}
740+
return stepId;
741+
},
742+
);
732743
}
733744

734745
private coordinatorHandleToolDone(
@@ -743,7 +754,7 @@ class ChatDirectorImpl extends DefaultDirector {
743754
} catch (err) {
744755
// Mid-turn only (tool.done): a throwing coordinator degrades to plain
745756
// inference rather than failing the turn.
746-
logger.warn`workflow-coordinator-handleToolDone-threw error=${err instanceof Error ? err.message : String(err)}`;
757+
logger.warn`workflow-coordinator-handleToolDone-threw error=${errorMessage(err)}`;
747758
return false;
748759
}
749760
}

src/agent/directors/bruckheimer/package.test.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,6 @@ import { describe, expect, test } from "bun:test";
22
import { bruckheimerPackage } from "./package.js";
33

44
describe("bruckheimerPackage", () => {
5-
test("id matches directory (keep bruckheimer path; identity is Bruckheimer)", () => {
6-
expect(bruckheimerPackage.id).toBe("bruckheimer");
7-
});
8-
9-
test("systemPrompt is real (not Placeholder)", () => {
10-
expect(bruckheimerPackage.systemPrompt.length).toBeGreaterThan(0);
11-
expect(bruckheimerPackage.systemPrompt.startsWith("Placeholder")).toBe(
12-
false,
13-
);
14-
});
15-
16-
test("systemPrompt states PRIMARY INTENT", () => {
17-
expect(bruckheimerPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
18-
});
19-
205
test("systemPrompt identity is Bruckheimer / BruckheimerDirector", () => {
216
const p = bruckheimerPackage.systemPrompt;
227
expect(p).toMatch(/BruckheimerDirector \(Bruckheimer\)/);
@@ -101,26 +86,15 @@ describe("bruckheimerPackage", () => {
10186
expect(p).not.toMatch(/via run_shell/i);
10287
});
10388

104-
test("spawn.maySpawn is false", () => {
105-
expect(bruckheimerPackage.spawn.maySpawn).toBe(false);
106-
});
107-
108-
test("tools.allow is DOCS_TOOLS surface (writes, no shell)", () => {
89+
test("tools.allow has no shell", () => {
10990
const allow = bruckheimerPackage.tools?.allow ?? [];
110-
expect(allow).toContain("write_file");
111-
expect(allow).toContain("edit_file");
112-
expect(allow).toContain("delete_file");
11391
expect(allow).not.toContain("run_shell");
11492
});
11593

11694
test("modelRole is docs", () => {
11795
expect(bruckheimerPackage.modelRole).toBe("docs");
11896
});
11997

120-
test("tier is leaf", () => {
121-
expect(bruckheimerPackage.tier).toBe("leaf");
122-
});
123-
12498
test("primaryIntent and outOfLane match discovery lane", () => {
12599
expect(bruckheimerPackage.primaryIntent).toMatch(/product discovery/i);
126100
expect(bruckheimerPackage.outOfLane).toContain("shipping product code");

src/agent/directors/builder/package.test.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@ import { describe, expect, test } from "bun:test";
22
import { builderPackage } from "./package.js";
33

44
describe("builderPackage", () => {
5-
test("id matches directory / registry id", () => {
6-
expect(builderPackage.id).toBe("builder");
7-
});
8-
9-
test("systemPrompt is non-empty and not a Placeholder", () => {
10-
expect(builderPackage.systemPrompt.length).toBeGreaterThan(0);
11-
expect(builderPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
12-
});
13-
14-
test("systemPrompt mentions PRIMARY INTENT", () => {
15-
expect(builderPackage.systemPrompt).toContain("PRIMARY INTENT");
16-
});
17-
185
test("systemPrompt identity is Builder / BuilderDirector (not job-title language)", () => {
196
const p = builderPackage.systemPrompt;
207
expect(p).toMatch(/BuilderDirector \(Builder\)/);
@@ -108,18 +95,6 @@ describe("builderPackage", () => {
10895
expect(p).not.toMatch(/scheduler/i);
10996
});
11097

111-
test("spawn.maySpawn is false (leaf)", () => {
112-
expect(builderPackage.spawn.maySpawn).toBe(false);
113-
});
114-
115-
test("tools.allow includes product write tools", () => {
116-
const allow = builderPackage.tools?.allow ?? [];
117-
expect(allow).toContain("write_file");
118-
expect(allow).toContain("edit_file");
119-
expect(allow).toContain("delete_file");
120-
expect(allow).toContain("apply_patch");
121-
});
122-
12398
test("modelRole is implement", () => {
12499
expect(builderPackage.modelRole).toBe("implement");
125100
});

src/agent/directors/counsel/package.test.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@ import { describe, expect, test } from "bun:test";
22
import { counselPackage } from "./package.js";
33

44
describe("counselPackage", () => {
5-
test("id matches directory", () => {
6-
expect(counselPackage.id).toBe("counsel");
7-
});
8-
9-
test("systemPrompt is real (not Placeholder)", () => {
10-
expect(counselPackage.systemPrompt.length).toBeGreaterThan(0);
11-
expect(counselPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
12-
});
13-
14-
test("systemPrompt states PRIMARY INTENT", () => {
15-
expect(counselPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
16-
});
17-
185
test("systemPrompt identity is Counsel / CounselDirector (not PlanDirector)", () => {
196
const p = counselPackage.systemPrompt;
207
expect(p).toMatch(/CounselDirector \(Counsel\)/);
@@ -63,16 +50,9 @@ describe("counselPackage", () => {
6350
expect(p).toContain("Headings-only Findings is not done");
6451
});
6552

66-
test("spawn.maySpawn is false", () => {
67-
expect(counselPackage.spawn.maySpawn).toBe(false);
68-
});
69-
70-
test("tools.allow is review surface with product writes", () => {
53+
test("tools.allow mounts read (plan lane)", () => {
7154
const allow = counselPackage.tools?.allow ?? [];
7255
expect(allow).toContain("read_file");
73-
expect(allow).toContain("write_file");
74-
expect(allow).toContain("edit_file");
75-
expect(allow).toContain("delete_file");
7656
});
7757

7858
test("modelRole is plan", () => {

src/agent/directors/critic/package.test.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@ import { describe, expect, test } from "bun:test";
22
import { criticPackage } from "./package.js";
33

44
describe("criticPackage", () => {
5-
test("id matches directory", () => {
6-
expect(criticPackage.id).toBe("critic");
7-
});
8-
9-
test("systemPrompt is real, not a placeholder", () => {
10-
expect(criticPackage.systemPrompt.length).toBeGreaterThan(0);
11-
expect(criticPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
12-
});
13-
14-
test("systemPrompt states PRIMARY INTENT", () => {
15-
expect(criticPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
16-
});
17-
185
test("systemPrompt identity is Critic / CriticDirector", () => {
196
const p = criticPackage.systemPrompt;
207
expect(p).toMatch(/CriticDirector \(Critic\)/);
@@ -103,18 +90,11 @@ describe("criticPackage", () => {
10390
expect(p).not.toMatch(/via run_shell/i);
10491
});
10592

106-
test("spawn.maySpawn is false", () => {
107-
expect(criticPackage.spawn.maySpawn).toBe(false);
108-
});
109-
110-
test("tools.allow is review surface with product writes", () => {
93+
test("tools.allow mounts read plus skill discovery", () => {
11194
const allow = criticPackage.tools?.allow ?? [];
11295
expect(allow).toContain("read_file");
11396
expect(allow).toContain("skill_search");
11497
expect(allow).toContain("use_skill");
115-
expect(allow).toContain("write_file");
116-
expect(allow).toContain("edit_file");
117-
expect(allow).toContain("delete_file");
11898
});
11999

120100
test("modelRole is review", () => {

src/agent/directors/draper/package.test.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,6 @@ import { describe, expect, test } from "bun:test";
22
import { draperPackage } from "./package.js";
33

44
describe("draperPackage", () => {
5-
test("id matches directory", () => {
6-
expect(draperPackage.id).toBe("draper");
7-
});
8-
9-
test("systemPrompt is real, not a placeholder", () => {
10-
expect(draperPackage.systemPrompt.length).toBeGreaterThan(0);
11-
expect(draperPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
12-
});
13-
14-
test("systemPrompt states PRIMARY INTENT", () => {
15-
expect(draperPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
16-
});
17-
185
test("systemPrompt identity is Draper / DraperDirector (package id stays draper)", () => {
196
const p = draperPackage.systemPrompt;
207
expect(p).toMatch(/DraperDirector \(Draper\)/);
@@ -126,23 +113,15 @@ describe("draperPackage", () => {
126113
expect(p).not.toMatch(/## Paths/);
127114
});
128115

129-
test("spawn.maySpawn is false", () => {
130-
expect(draperPackage.spawn.maySpawn).toBe(false);
131-
});
132-
133-
test("tools.allow is review surface with file writes for evidence tests", () => {
116+
test("tools.allow mounts read plus skill discovery", () => {
134117
const allow = draperPackage.tools?.allow ?? [];
135118
expect(allow).toContain("read_file");
136119
expect(allow).toContain("skill_search");
137120
expect(allow).toContain("use_skill");
138-
expect(allow).toContain("write_file");
139-
expect(allow).toContain("edit_file");
140-
expect(allow).toContain("delete_file");
141121
});
142122

143-
test("modelRole is review and tier is leaf", () => {
123+
test("modelRole is review", () => {
144124
expect(draperPackage.modelRole).toBe("review");
145-
expect(draperPackage.tier).toBe("leaf");
146125
});
147126

148127
test("primaryIntent and outOfLane match the restored full-critique lane", () => {

0 commit comments

Comments
 (0)