feat(FN-1444): complete Step 2 — include soul in agent instructions assembly

This commit is contained in:
gsxdsm
2026-04-10 20:47:04 -07:00
parent 37c91e806d
commit 429eb4c4f9
2 changed files with 76 additions and 0 deletions

View File

@@ -78,6 +78,12 @@ describe("resolveAgentInstructions", () => {
expect(result).toBe(""); expect(result).toBe("");
}); });
it("returns soul-only agent with no instructions", async () => {
const agent = makeAgent({ soul: "Be thorough and analytical." });
const result = await resolveAgentInstructions(agent, testDir);
expect(result).toBe("## Soul\n\nBe thorough and analytical.");
});
it("returns instructionsText when set", async () => { it("returns instructionsText when set", async () => {
const agent = makeAgent({ instructionsText: "Always write tests." }); const agent = makeAgent({ instructionsText: "Always write tests." });
const result = await resolveAgentInstructions(agent, testDir); const result = await resolveAgentInstructions(agent, testDir);
@@ -204,6 +210,36 @@ describe("resolveAgentInstructions", () => {
expect(result.length).toBe(50000); expect(result.length).toBe(50000);
}); });
it("truncates oversized soul", async () => {
const oversized = "s".repeat(10010);
const agent = makeAgent({ soul: oversized });
const result = await resolveAgentInstructions(agent, testDir);
expect(result.length).toBe(10000 + "## Soul\n\n".length);
});
it("places soul section after instructionsText and before performance feedback", async () => {
const filePath = join(testDir, "file-instructions.md");
await writeFile(filePath, "File-based instructions here.");
const agent = makeAgent({
instructionsText: "Inline instructions.",
instructionsPath: "file-instructions.md",
soul: "Be methodical and detailed.",
});
const result = await resolveAgentInstructions(agent, testDir);
// Verify section order
const soulIndex = result.indexOf("## Soul");
const instructionsTextIndex = result.indexOf("Inline instructions.");
const instructionsFileIndex = result.indexOf("File-based instructions here.");
expect(instructionsTextIndex).toBeLessThan(soulIndex);
expect(instructionsFileIndex).toBeLessThan(soulIndex);
expect(soulIndex).toBeLessThan(result.indexOf("## Soul") + 10); // Soul section is present
});
}); });
describe("resolveAgentInstructions with rating summary", () => { describe("resolveAgentInstructions with rating summary", () => {
@@ -243,6 +279,29 @@ describe("resolveAgentInstructions with rating summary", () => {
expect(result).toContain(' - "Could communicate blockers sooner" (score: 4.0)'); expect(result).toContain(' - "Could communicate blockers sooner" (score: 4.0)');
}); });
it("places soul section before performance feedback and after instructions", async () => {
const agent = makeAgent({
instructionsText: "Implement the feature.",
soul: "Be pragmatic and efficient.",
});
const summary = makeRatingSummary({
totalRatings: 3,
trend: "stable",
});
const result = await resolveAgentInstructions(agent, testDir, summary);
// Verify section order: instructionsText → soul → Performance Feedback
const instructionsIndex = result.indexOf("Implement the feature.");
const soulIndex = result.indexOf("## Soul");
const feedbackIndex = result.indexOf("## Performance Feedback");
expect(instructionsIndex).toBeLessThan(soulIndex);
expect(soulIndex).toBeLessThan(feedbackIndex);
expect(result).toContain("## Soul");
expect(result).toContain("## Performance Feedback");
});
it("shows the correct trend indicator for all trend states", async () => { it("shows the correct trend indicator for all trend states", async () => {
const agent = makeAgent({ instructionsText: "Base instructions" }); const agent = makeAgent({ instructionsText: "Base instructions" });
const trends: Array<[AgentRatingSummary["trend"], string]> = [ const trends: Array<[AgentRatingSummary["trend"], string]> = [

View File

@@ -4,6 +4,7 @@ import type { Agent, AgentRatingSummary, AgentStore } from "@fusion/core";
const MAX_INSTRUCTIONS_PATH_LENGTH = 500; const MAX_INSTRUCTIONS_PATH_LENGTH = 500;
const MAX_INSTRUCTIONS_TEXT_LENGTH = 50_000; const MAX_INSTRUCTIONS_TEXT_LENGTH = 50_000;
const MAX_SOUL_LENGTH = 10_000;
function trimAndClamp(value: string, maxLength: number, label: string, agentId: string): string { function trimAndClamp(value: string, maxLength: number, label: string, agentId: string): string {
const trimmed = value.trim(); const trimmed = value.trim();
@@ -77,6 +78,14 @@ function getTrendLabel(trend: AgentRatingSummary["trend"]): string {
} }
} }
function formatSoulSection(soul: string, agentId: string): string {
const trimmed = trimAndClamp(soul, MAX_SOUL_LENGTH, "soul", agentId);
if (!trimmed) {
return "";
}
return `## Soul\n\n${trimmed}`;
}
function formatPerformanceFeedbackSection(ratingSummary: AgentRatingSummary): string { function formatPerformanceFeedbackSection(ratingSummary: AgentRatingSummary): string {
const lines: string[] = [ const lines: string[] = [
"## Performance Feedback", "## Performance Feedback",
@@ -170,6 +179,14 @@ export async function resolveAgentInstructions(
} }
} }
// Soul/personality section (after instructions, before performance feedback)
if (agent.soul?.trim()) {
const soulSection = formatSoulSection(agent.soul, agent.id);
if (soulSection) {
parts.push(soulSection);
}
}
if (ratingSummary && ratingSummary.totalRatings > 0) { if (ratingSummary && ratingSummary.totalRatings > 0) {
parts.push(formatPerformanceFeedbackSection(ratingSummary)); parts.push(formatPerformanceFeedbackSection(ratingSummary));
} }