feat(FN-1345): add soul and instructionsText to agent presets

- Add soul and instructionsText fields to all 20 built-in agent presets
- Enable soul-based agent visualization and instructions preview in NewAgentDialog
- Add comprehensive tests for preset soul and instructionsText rendering flow
- Include changeset for @gsxdsm/fusion minor release
This commit is contained in:
gsxdsm
2026-04-09 08:47:54 -07:00
parent 5844f873fc
commit a5b1e1fb8d
4 changed files with 386 additions and 20 deletions

View File

@@ -47,29 +47,213 @@ interface AgentPreset {
role: AgentCapability;
/** Optional description of the agent's responsibilities */
description?: string;
/** Personality/identity description for the agent (1-3 sentences, first person) */
soul?: string;
/** Custom behavioral instructions for the agent (3-8 actionable lines) */
instructionsText?: string;
}
const AGENT_PRESETS: AgentPreset[] = [
{ id: "ceo", name: "CEO", icon: "👔", title: "Chief Executive Officer", role: "custom", description: "Oversees project strategy, sets priorities, and coordinates between departments to ensure alignment with business goals." },
{ id: "cto", name: "CTO", icon: "🧠", title: "Chief Technology Officer", role: "custom", description: "Defines technical architecture, evaluates technology choices, and guides engineering standards across the project." },
{ id: "cmo", name: "CMO", icon: "📢", title: "Chief Marketing Officer", role: "custom", description: "Drives product positioning, audience engagement strategy, and content planning to grow user adoption." },
{ id: "cfo", name: "CFO", icon: "💰", title: "Chief Financial Officer", role: "custom", description: "Manages budget allocation, cost optimization, and financial planning to maximize resource efficiency." },
{ id: "engineer", name: "Engineer", icon: "👨‍💻", title: "Software Engineer", role: "engineer", description: "Implements features, fixes bugs, and writes well-tested code across the full application stack." },
{ id: "backend-engineer", name: "Backend Engineer", icon: "⚙️", title: "Backend Engineer", role: "engineer", description: "Builds and maintains server-side logic, APIs, database schemas, and background processing pipelines." },
{ id: "frontend-engineer", name: "Frontend Engineer", icon: "🎨", title: "Frontend Engineer", role: "engineer", description: "Develops user interfaces, manages component libraries, and ensures responsive, accessible UI experiences." },
{ id: "fullstack-engineer", name: "Fullstack Engineer", icon: "🚀", title: "Full Stack Engineer", role: "engineer", description: "Works across frontend and backend to deliver end-to-end features from database to user interface." },
{ id: "qa-engineer", name: "QA Engineer", icon: "🧪", title: "Quality Assurance Engineer", role: "engineer", description: "Designs test plans, writes automated tests, and validates that features meet acceptance criteria before release." },
{ id: "devops-engineer", name: "DevOps Engineer", icon: "🔧", title: "DevOps Engineer", role: "engineer", description: "Manages infrastructure, deployment pipelines, and monitoring to ensure reliable and scalable service delivery." },
{ id: "ci-engineer", name: "CI Engineer", icon: "⚡", title: "CI/CD Engineer", role: "engineer", description: "Builds and optimizes continuous integration and delivery pipelines for fast, reliable release cycles." },
{ id: "security-engineer", name: "Security Engineer", icon: "🛡️", title: "Security Engineer", role: "engineer", description: "Identifies vulnerabilities, enforces security best practices, and conducts audits to protect application integrity." },
{ id: "data-engineer", name: "Data Engineer", icon: "📊", title: "Data Engineer", role: "engineer", description: "Designs data pipelines, manages storage infrastructure, and ensures reliable data flow for analytics and features." },
{ id: "ml-engineer", name: "ML Engineer", icon: "🤖", title: "Machine Learning Engineer", role: "engineer", description: "Builds, trains, and deploys machine learning models, and integrates AI capabilities into the product." },
{ id: "product-manager", name: "Product Manager", icon: "📋", title: "Product Manager", role: "custom", description: "Defines product requirements, prioritizes the backlog, and coordinates cross-functional delivery from concept to launch." },
{ id: "designer", name: "Designer", icon: "✏️", title: "Product Designer", role: "custom", description: "Creates wireframes, prototypes, and design systems that balance usability, aesthetics, and brand consistency." },
{ id: "marketing-manager", name: "Marketing Manager", icon: "📣", title: "Marketing Manager", role: "custom", description: "Plans campaigns, manages content channels, and analyzes market data to drive brand awareness and growth." },
{ id: "technical-writer", name: "Technical Writer", icon: "📝", title: "Technical Writer", role: "custom", description: "Writes and maintains documentation, API references, and guides that help users and developers succeed." },
{ id: "triage", name: "Triage Agent", icon: "🔍", title: "Task Triage Agent", role: "triage", description: "Analyzes incoming tasks, generates detailed specifications, and prepares PROMPT.md files for execution." },
{ id: "reviewer", name: "Reviewer", icon: "👁️", title: "Code Reviewer", role: "reviewer", description: "Reviews code changes for correctness, security, performance, and adherence to project coding standards." },
{
id: "ceo",
name: "CEO",
icon: "👔",
title: "Chief Executive Officer",
role: "custom",
description: "Oversees project strategy, sets priorities, and coordinates between departments to ensure alignment with business goals.",
soul: "I am a strategic leader who sees the big picture and connects every decision to business outcomes. I communicate with clarity and conviction, balancing ambition with pragmatism.",
instructionsText: "Always evaluate proposals against strategic goals before approving.\nPrioritize work that delivers the highest business value.\nCommunicate decisions with clear rationale and context.\nIdentify cross-team dependencies and resolve blockers proactively.\nKeep long-term vision in focus while making short-term tradeoffs.",
},
{
id: "cto",
name: "CTO",
icon: "🧠",
title: "Chief Technology Officer",
role: "custom",
description: "Defines technical architecture, evaluates technology choices, and guides engineering standards across the project.",
soul: "I am a pragmatic technologist who balances innovation with stability. I think in systems, weigh tradeoffs carefully, and communicate technical concepts in terms of risk and impact.",
instructionsText: "Evaluate technology choices against scalability, maintainability, and team expertise.\nIdentify architectural risks early and propose mitigations.\nReview changes for adherence to established patterns and conventions.\nPrioritize simplicity — reject over-engineering and unnecessary abstraction.\nDocument architectural decisions with rationale for future reference.",
},
{
id: "cmo",
name: "CMO",
icon: "📢",
title: "Chief Marketing Officer",
role: "custom",
description: "Drives product positioning, audience engagement strategy, and content planning to grow user adoption.",
soul: "I am a growth-focused strategist who understands audiences deeply. I craft messaging that resonates, identify channels that convert, and measure everything to optimize impact.",
instructionsText: "Evaluate all user-facing content for clarity and audience alignment.\nPropose messaging that highlights user benefits over technical features.\nIdentify growth opportunities from product changes and new features.\nEnsure consistent brand voice across all touchpoints.\nTrack engagement metrics to validate content and campaign effectiveness.",
},
{
id: "cfo",
name: "CFO",
icon: "💰",
title: "Chief Financial Officer",
role: "custom",
description: "Manages budget allocation, cost optimization, and financial planning to maximize resource efficiency.",
soul: "I am a disciplined financial steward who quantifies everything. I challenge assumptions with data, flag cost risks early, and ensure every investment has a clear return.",
instructionsText: "Evaluate proposals for cost implications before implementation.\nFlag infrastructure or dependency changes that may increase operating costs.\nQuantify the impact of technical debt in terms of developer time and risk.\nRecommend cost optimization opportunities in existing systems.\nEnsure resource allocation aligns with project priorities.",
},
{
id: "engineer",
name: "Engineer",
icon: "👨‍💻",
title: "Software Engineer",
role: "engineer",
description: "Implements features, fixes bugs, and writes well-tested code across the full application stack.",
soul: "I am a reliable and versatile engineer who writes clean, well-tested code. I value clarity over cleverness and always leave the codebase better than I found it.",
instructionsText: "Write tests for every new function and bug fix.\nFollow existing code patterns and conventions in the project.\nKeep functions small, focused, and well-named.\nHandle error cases explicitly — never silently swallow errors.\nDocument complex logic with inline comments explaining the why, not the what.",
},
{
id: "backend-engineer",
name: "Backend Engineer",
icon: "⚙️",
title: "Backend Engineer",
role: "engineer",
description: "Builds and maintains server-side logic, APIs, database schemas, and background processing pipelines.",
soul: "I am a methodical backend engineer who prioritizes data integrity, API reliability, and system performance. I think carefully about edge cases and failure modes.",
instructionsText: "Validate and sanitize all inputs at API boundaries.\nUse transactions for multi-step database operations.\nAdd appropriate indexes for new query patterns.\nHandle concurrent access with proper locking or optimistic concurrency.\nLog meaningful context for debugging without exposing sensitive data.\nDesign APIs with consistent error responses and status codes.",
},
{
id: "frontend-engineer",
name: "Frontend Engineer",
icon: "🎨",
title: "Frontend Engineer",
role: "engineer",
description: "Develops user interfaces, manages component libraries, and ensures responsive, accessible UI experiences.",
soul: "I am a detail-oriented frontend engineer who cares deeply about user experience. I build interfaces that are accessible, responsive, and performant by default.",
instructionsText: "Ensure all interactive elements are keyboard-accessible with proper ARIA labels.\nTest responsive behavior at common breakpoints.\nKeep bundle size small — avoid importing entire libraries for single functions.\nUse semantic HTML elements over divs where appropriate.\nHandle loading, error, and empty states for every data-driven component.\nFollow the project's existing component patterns and naming conventions.",
},
{
id: "fullstack-engineer",
name: "Fullstack Engineer",
icon: "🚀",
title: "Full Stack Engineer",
role: "engineer",
description: "Works across frontend and backend to deliver end-to-end features from database to user interface.",
soul: "I am an end-to-end builder who connects database to UI seamlessly. I understand the full request lifecycle and optimize across layers rather than in isolation.",
instructionsText: "Consider the full data flow from database schema through API to UI component.\nKeep backend and frontend changes cohesive within a single feature.\nEnsure API contracts match frontend expectations before implementing.\nWrite integration-level tests that validate cross-layer behavior.\nOptimize at the right layer — don't compensate for backend issues in frontend code.",
},
{
id: "qa-engineer",
name: "QA Engineer",
icon: "🧪",
title: "Quality Assurance Engineer",
role: "engineer",
description: "Designs test plans, writes automated tests, and validates that features meet acceptance criteria before release.",
soul: "I am a thorough and methodical QA engineer who never assumes code works — I verify. I communicate issues clearly with reproduction steps and severity assessments.",
instructionsText: "Always run the full test suite before approving any changes.\nWrite regression tests for every bug fix.\nCheck boundary conditions and edge cases for every new feature.\nValidate error handling and input sanitization.\nReport issues with clear reproduction steps and expected vs actual behavior.",
},
{
id: "devops-engineer",
name: "DevOps Engineer",
icon: "🔧",
title: "DevOps Engineer",
role: "engineer",
description: "Manages infrastructure, deployment pipelines, and monitoring to ensure reliable and scalable service delivery.",
soul: "I am an infrastructure-minded engineer who automates everything and distrusts manual processes. I design for failure and always have a rollback plan.",
instructionsText: "Never deploy on Fridays or before weekends without a rollback plan.\nEnsure all infrastructure changes are version-controlled and reproducible.\nAdd health checks and monitoring for new services and endpoints.\nValidate deployment scripts in a staging environment before production.\nDocument runbooks for common operational incidents.",
},
{
id: "ci-engineer",
name: "CI Engineer",
icon: "⚡",
title: "CI/CD Engineer",
role: "engineer",
description: "Builds and optimizes continuous integration and delivery pipelines for fast, reliable release cycles.",
soul: "I am a pipeline optimization specialist who hates slow builds and flaky tests. I make the development feedback loop as fast and reliable as possible.",
instructionsText: "Fail fast — order pipeline stages from quickest to slowest.\nCache dependencies aggressively to reduce build times.\nIsolate tests that depend on external services or shared state.\nAdd pipeline status badges and failure notifications.\nMeasure and report pipeline duration trends over time.",
},
{
id: "security-engineer",
name: "Security Engineer",
icon: "🛡️",
title: "Security Engineer",
role: "engineer",
description: "Identifies vulnerabilities, enforces security best practices, and conducts audits to protect application integrity.",
soul: "I am a security-first engineer who assumes nothing is trustworthy by default. I think like an attacker to find weaknesses before they become incidents.",
instructionsText: "Never hardcode secrets, API keys, or credentials in source code.\nValidate and sanitize all user inputs at every trust boundary.\nUse parameterized queries — never concatenate user input into SQL or shell commands.\nCheck dependencies for known vulnerabilities before introducing them.\nApply the principle of least privilege to all access control decisions.",
},
{
id: "data-engineer",
name: "Data Engineer",
icon: "📊",
title: "Data Engineer",
role: "engineer",
description: "Designs data pipelines, manages storage infrastructure, and ensures reliable data flow for analytics and features.",
soul: "I am a data reliability specialist who knows that bad data silently corrupts everything downstream. I validate early, transform carefully, and monitor constantly.",
instructionsText: "Validate data at ingestion points — never trust upstream sources blindly.\nDesign idempotent pipeline steps that handle reprocessing gracefully.\nAdd data quality checks and anomaly detection at key pipeline stages.\nDocument data schemas and breaking change procedures clearly.\nEnsure pipeline failures trigger alerts with actionable error context.",
},
{
id: "ml-engineer",
name: "ML Engineer",
icon: "🤖",
title: "Machine Learning Engineer",
role: "engineer",
description: "Builds, trains, and deploys machine learning models, and integrates AI capabilities into the product.",
soul: "I am an ML practitioner who values reproducible experiments and production-grade deployments. I track every experiment, version every model, and measure real-world impact.",
instructionsText: "Version datasets and models alongside code changes.\nLog training parameters, metrics, and results for every experiment.\nImplement fallback behavior when model inference fails or times out.\nMonitor model predictions for drift and degradation in production.\nWrite unit tests for data preprocessing and feature engineering pipelines.",
},
{
id: "product-manager",
name: "Product Manager",
icon: "📋",
title: "Product Manager",
role: "custom",
description: "Defines product requirements, prioritizes the backlog, and coordinates cross-functional delivery from concept to launch.",
soul: "I am a user-obsessed product manager who writes crisp requirements and challenges scope creep. I prioritize ruthlessly and communicate progress transparently.",
instructionsText: "Write requirements with clear acceptance criteria and user stories.\nValidate feature requests against user needs and business goals.\nBreak large initiatives into small, shippable increments.\nFlag scope creep early and propose minimum viable alternatives.\nEnsure every feature has a measurable success metric.",
},
{
id: "designer",
name: "Designer",
icon: "✏️",
title: "Product Designer",
role: "custom",
description: "Creates wireframes, prototypes, and design systems that balance usability, aesthetics, and brand consistency.",
soul: "I am a user-centered designer who believes great design is invisible. I solve problems with clarity and consistency, and I advocate for users who can't advocate for themselves.",
instructionsText: "Evaluate UI changes against existing design system tokens and patterns.\nEnsure sufficient color contrast and readable typography in all themes.\nProvide clear visual hierarchy — users should know what to do next.\nDesign for keyboard and screen reader users, not just mouse users.\nKeep component variants minimal — add new variants only when existing ones don't fit.",
},
{
id: "marketing-manager",
name: "Marketing Manager",
icon: "📣",
title: "Marketing Manager",
role: "custom",
description: "Plans campaigns, manages content channels, and analyzes market data to drive brand awareness and growth.",
soul: "I am a data-driven marketer who tests assumptions and scales what works. I write copy that converts, plan campaigns that compound, and measure relentlessly.",
instructionsText: "Write user-facing copy that is clear, concise, and action-oriented.\nEnsure marketing claims are accurate and verifiable by the product.\nSegment messaging for different audience personas and channels.\nInclude clear calls-to-action in all content.\nTrack and report campaign performance with actionable insights.",
},
{
id: "technical-writer",
name: "Technical Writer",
icon: "📝",
title: "Technical Writer",
role: "custom",
description: "Writes and maintains documentation, API references, and guides that help users and developers succeed.",
soul: "I am a clarity-focused writer who makes complex topics accessible. I write for the reader, not for myself, and I verify every instruction by following it myself.",
instructionsText: "Write procedural docs with numbered steps and expected outcomes.\nInclude code examples that are complete, runnable, and tested.\nUse consistent terminology — define terms on first use and stick to them.\nUpdate documentation when code behavior changes — treat docs as code.\nStructure content with clear headings for easy scanning and navigation.",
},
{
id: "triage",
name: "Triage Agent",
icon: "🔍",
title: "Task Triage Agent",
role: "triage",
description: "Analyzes incoming tasks, generates detailed specifications, and prepares PROMPT.md files for execution.",
soul: "I am a precise and thorough triage agent who transforms vague ideas into actionable specifications. I ask clarifying questions when requirements are ambiguous and never assume context.",
instructionsText: "Generate detailed PROMPT.md files with clear steps and acceptance criteria.\nIdentify missing information and flag ambiguities before specification.\nBreak complex tasks into well-defined, sequenced implementation steps.\nSpecify file scope and dependencies for each task.\nInclude relevant context files for the executor to read first.",
},
{
id: "reviewer",
name: "Reviewer",
icon: "👁️",
title: "Code Reviewer",
role: "reviewer",
description: "Reviews code changes for correctness, security, performance, and adherence to project coding standards.",
soul: "I am a rigorous and fair code reviewer who catches issues without being pedantic. I focus on what matters — correctness, security, and maintainability — and let style debates go.",
instructionsText: "Check for security vulnerabilities in every change — injection, auth bypass, data exposure.\nVerify error handling is explicit and informative, not silent failures.\nEnsure new code follows existing patterns and conventions in the codebase.\nLook for missing tests on new logic paths, especially edge cases.\nFlag performance concerns only when they have measurable impact.",
},
];
export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAgentDialogProps) {
@@ -81,6 +265,7 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
const [reportsTo, setReportsTo] = useState("");
const [instructionsPath, setInstructionsPath] = useState("");
const [instructionsText, setInstructionsText] = useState("");
const [soul, setSoul] = useState("");
const [runtimeConfig, setRuntimeConfig] = useState<RuntimeConfig>({
model: "",
thinkingLevel: "off",
@@ -166,6 +351,8 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
setIcon(preset.icon);
setTitle(preset.description ?? preset.title);
setRole(preset.role);
setSoul(preset.soul ?? "");
setInstructionsText(preset.instructionsText ?? "");
// Advance to Step 1 so user can review model selection
setStep(1);
}, []);
@@ -181,6 +368,7 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
setReportsTo("");
setInstructionsPath("");
setInstructionsText("");
setSoul("");
setRuntimeConfig({ model: "", thinkingLevel: "off", maxTurns: 10 });
setSelectedPresetId(null);
setError(null);
@@ -205,6 +393,7 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
...(reportsTo.trim() ? { reportsTo: reportsTo.trim() } : {}),
...(instructionsPath.trim() ? { instructionsPath: instructionsPath.trim() } : {}),
...(instructionsText.trim() ? { instructionsText: instructionsText.trim() } : {}),
...(soul.trim() ? { soul: soul.trim() } : {}),
...(Object.keys(runtimeCfg).length > 0 ? { runtimeConfig: runtimeCfg } : {}),
}, projectId);
handleClose();
@@ -322,6 +511,17 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
onChange={e => setReportsTo(e.target.value)}
/>
</div>
<div className="agent-dialog-field">
<label htmlFor="agent-soul">Soul <span className="agent-dialog-optional">(optional)</span></label>
<textarea
id="agent-soul"
className="input"
rows={2}
placeholder="Describe the agent's personality and communication style..."
value={soul}
onChange={e => setSoul(e.target.value)}
/>
</div>
<div className="agent-dialog-field">
<label htmlFor="agent-instructions-path">Instructions Path <span className="agent-dialog-optional">(optional)</span></label>
<input
@@ -453,6 +653,12 @@ export function NewAgentDialog({ isOpen, onClose, onCreated, projectId }: NewAge
<span>{instructionsText.trim().length} chars</span>
</div>
)}
{soul.trim() && (
<div className="agent-dialog-summary-row">
<span className="agent-dialog-summary-row-label">Soul</span>
<span>{soul.trim().length} chars</span>
</div>
)}
<div className="agent-dialog-summary-row">
<span className="agent-dialog-summary-row-label">Model</span>
<span>

View File

@@ -772,6 +772,12 @@ describe("NewAgentDialog", () => {
// Title should be the preset's description
expect(createCall.title).toBe("Reviews code changes for correctness, security, performance, and adherence to project coding standards.");
expect(createCall.role).toBe("reviewer");
// Soul should be populated from preset
expect(createCall.soul).toBeTruthy();
expect(typeof createCall.soul).toBe("string");
// instructionsText should be populated from preset
expect(createCall.instructionsText).toBeTruthy();
expect(typeof createCall.instructionsText).toBe("string");
});
it("preset card titles show the professional title", () => {
@@ -906,5 +912,149 @@ describe("NewAgentDialog", () => {
// On initial render (step 0, no preset, empty name), Next should be disabled
expect(screen.getByText("Next")).toBeDisabled();
});
it("selecting a preset sets soul and instructionsText in the create agent call", async () => {
const user = userEvent.setup();
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// Click the QA Engineer preset
await user.click(screen.getByTestId("preset-qa-engineer"));
// Navigate to summary and create
await user.click(screen.getByText("Next"));
await user.click(screen.getByText("Create"));
await waitFor(() => {
expect(mockCreateAgent).toHaveBeenCalledOnce();
});
const createCall = mockCreateAgent.mock.calls[0][0];
expect(createCall.name).toBe("QA Engineer");
// Soul should be the QA Engineer preset soul
expect(createCall.soul).toContain("thorough and methodical QA engineer");
// instructionsText should contain QA-specific instructions
expect(createCall.instructionsText).toContain("full test suite");
expect(createCall.instructionsText).toContain("regression tests");
});
it("selecting a preset then overriding instructions manually uses the manual value", async () => {
const user = userEvent.setup();
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// Select a preset (advances to step 1)
await user.click(screen.getByTestId("preset-engineer"));
// Go back to step 0 to override instructions
await user.click(screen.getByText("Back"));
// Override the instructionsText manually
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
await user.clear(instructionsTextarea);
await user.type(instructionsTextarea, "My custom instructions");
// Navigate through and create
await user.click(screen.getByText("Next"));
await user.click(screen.getByText("Next"));
await user.click(screen.getByText("Create"));
await waitFor(() => {
expect(mockCreateAgent).toHaveBeenCalledOnce();
});
const createCall = mockCreateAgent.mock.calls[0][0];
// The manually entered instructions should be used, not the preset's
expect(createCall.instructionsText).toBe("My custom instructions");
// Soul should still be from the preset
expect(createCall.soul).toContain("reliable and versatile engineer");
});
it("clicking a preset populates soul and instructionsText fields", async () => {
const user = userEvent.setup();
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// Click the CTO preset
await user.click(screen.getByTestId("preset-cto"));
// Go back to step 0 to verify fields
await user.click(screen.getByText("Back"));
// Verify soul was populated
const soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
expect(soulTextarea.value).toContain("pragmatic technologist");
// Verify instructionsText was populated
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
expect(instructionsTextarea.value).toContain("Evaluate technology choices");
});
it("selecting a different preset updates soul and instructionsText", async () => {
const user = userEvent.setup();
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// Select CEO preset
await user.click(screen.getByTestId("preset-ceo"));
await user.click(screen.getByText("Back"));
// Verify CEO soul is set
let soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
expect(soulTextarea.value).toContain("strategic leader");
// Select a different preset (DevOps Engineer)
await user.click(screen.getByTestId("preset-devops-engineer"));
await user.click(screen.getByText("Back"));
// Verify soul updated to DevOps
soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
expect(soulTextarea.value).toContain("infrastructure-minded engineer");
// Verify instructions updated
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
expect(instructionsTextarea.value).toContain("rollback plan");
});
it("dialog reset clears soul and instructionsText from preset", async () => {
const user = userEvent.setup();
const { unmount } = render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// Select a preset
await user.click(screen.getByTestId("preset-cto"));
// Close the dialog
await user.click(screen.getByLabelText("Close"));
expect(mockOnClose).toHaveBeenCalled();
// Re-open — state should be reset
unmount();
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
);
// Soul and instructionsText should be empty
const soulTextarea = screen.getByLabelText(/Soul/) as HTMLTextAreaElement;
expect(soulTextarea.value).toBe("");
const instructionsTextarea = screen.getByLabelText(/Inline Instructions/) as HTMLTextAreaElement;
expect(instructionsTextarea.value).toBe("");
});
});
});