33// field), Advanced stays collapsed, submitting drafts a system prompt
44// via Myra before deploying, a Suggestions card runs the exact same
55// drafting flow with its name+purpose prefilled, and a failed draft
6- // fails closed — Advanced opens with a manual system prompt field
7- // rather than falling back to a silent template. Settings' global
6+ // fails closed — the panel surfaces the failure, keeps Advanced
7+ // collapsed, and the disabled primary names the blocker rather than
8+ // silently opening Advanced or falling back to a silent template. Settings' global
89// "Agents" directory (this form's original entry point) was cut in
910// CL-6121 — agent configuration lives per-workbench now; the chat page's
1011// "+ New chat" picker moved to instant creation (CL-6081) and no longer
@@ -27,6 +28,17 @@ function json(body: unknown, status = 200) {
2728 } ) ;
2829}
2930
31+ const CATALOG_MODEL = {
32+ id : "mdl_1" ,
33+ tenantId : "tenant_1" ,
34+ canonicalName : "anthropic/claude-sonnet-4" ,
35+ displayName : "Claude Sonnet 4" ,
36+ description : null ,
37+ disabled : false ,
38+ createdAt : "2026-01-01T00:00:00.000Z" ,
39+ updatedAt : "2026-01-01T00:00:00.000Z" ,
40+ } ;
41+
3042const CREATED_DEFINITION = {
3143 id : "wfd_new" ,
3244 tenantId : "tenant_1" ,
@@ -42,6 +54,7 @@ const CREATED_DEFINITION = {
4254function stubFetch ( overrides : {
4355 draft ?: ( body : Record < string , unknown > ) => Response ;
4456 create ?: ( body : Record < string , unknown > ) => Response ;
57+ models ?: readonly unknown [ ] ;
4558} ) : void {
4659 globalThis . fetch = ( ( input : RequestInfo | URL , init ?: RequestInit ) => {
4760 const url = String ( input ) ;
@@ -50,7 +63,9 @@ function stubFetch(overrides: {
5063 ? ( JSON . parse ( String ( init . body ) ) as Record < string , unknown > )
5164 : { } ;
5265 if ( url . includes ( "/catalog/models" ) ) {
53- return Promise . resolve ( json ( { data : [ ] , nextCursor : null } ) ) ;
66+ return Promise . resolve (
67+ json ( { data : overrides . models ?? [ ] , nextCursor : null } ) ,
68+ ) ;
5469 }
5570 if ( url . endsWith ( "/skills" ) ) {
5671 return Promise . resolve ( json ( { skills : [ ] } ) ) ;
@@ -166,16 +181,17 @@ describe("CreateAgentPanel happy path", () => {
166181 ] . find ( ( el ) => ( el . textContent ?? "" ) . trim ( ) === "New agent" ) ;
167182 expect ( heading ) . toBeDefined ( ) ;
168183 expect ( document . body . textContent ) . not . toContain ( "New workbench" ) ;
184+ fillField ( "create-agent-name" , "Research Buddy" ) ;
169185 const cta = findButton ( "Get started" ) ;
170186 expect ( cta ) . toBeDefined ( ) ;
171187 expect ( cta ?. textContent ) . not . toMatch ( / w o r k b e n c h / i) ;
172188 } ) ;
173189
174190 test ( "a name alone is enough — purpose is optional, never a gate" , async ( ) => {
175191 await mount ( ) ;
176- const button = findButton ( "Get started " ) ;
177- expect ( button ?. hasAttribute ( "disabled" ) ) . toBe ( true ) ;
178- expect ( document . body . textContent ) . toContain ( "Add a name to continue." ) ;
192+ const emptyCta = findButton ( "Add a name to continue. " ) ;
193+ expect ( emptyCta ?. hasAttribute ( "disabled" ) ) . toBe ( true ) ;
194+ expect ( findButton ( "Get started" ) ) . toBeUndefined ( ) ;
179195
180196 fillField ( "create-agent-name" , "Research Buddy" ) ;
181197 expect ( findButton ( "Get started" ) ?. hasAttribute ( "disabled" ) ) . toBe ( false ) ;
@@ -293,9 +309,10 @@ describe("CreateAgentPanel Suggestions", () => {
293309} ) ;
294310
295311describe ( "CreateAgentPanel drafting failure — fails closed" , ( ) => {
296- test ( "a failed draft opens Advanced and blocks submission until a manual system prompt is written " , async ( ) => {
312+ test ( "a failed draft keeps Advanced collapsed and names the blocker on the disabled primary " , async ( ) => {
297313 let createCalled = false ;
298314 stubFetch ( {
315+ models : [ CATALOG_MODEL ] ,
299316 draft : ( ) =>
300317 json (
301318 {
@@ -314,6 +331,7 @@ describe("CreateAgentPanel drafting failure — fails closed", () => {
314331 } ) ;
315332
316333 await mount ( ) ;
334+ await settle ( ) ;
317335 fillField ( "create-agent-name" , "Research Buddy" ) ;
318336
319337 const button = findButton ( "Get started" ) ;
@@ -326,13 +344,20 @@ describe("CreateAgentPanel drafting failure — fails closed", () => {
326344 const details = document . querySelector (
327345 ".create-agent-advanced" ,
328346 ) as HTMLDetailsElement | null ;
329- expect ( details ?. open ) . toBe ( true ) ;
347+ expect ( details ?. open ) . toBe ( false ) ;
330348 expect ( document . body . textContent ) . toContain (
331349 "Myra couldn't draft a starting prompt for that." ,
332350 ) ;
333351 expect ( document . body . textContent ) . toContain ( "Reference: ref_1" ) ;
352+ expect ( findButton ( "Get started" ) ) . toBeUndefined ( ) ;
353+ const blockedCta = findButton (
354+ "Open Advanced and write a system prompt to continue." ,
355+ ) ;
356+ expect ( blockedCta ) . toBeDefined ( ) ;
357+ expect ( blockedCta ?. hasAttribute ( "disabled" ) ) . toBe ( true ) ;
358+ expect ( document . body . textContent ) . not . toContain ( "Myra picks one" ) ;
334359 expect ( document . body . textContent ) . toContain (
335- "Write a system prompt below to continue ." ,
360+ "Left unset, the agent uses the workbench default ." ,
336361 ) ;
337362
338363 const manualField = document . getElementById (
@@ -381,6 +406,10 @@ describe("CreateAgentPanel drafting failure — fails closed", () => {
381406 expect ( document . body . textContent ) . toContain ( "Reference: ref_sink_1" ) ;
382407 expect ( document . body . textContent ) . not . toContain ( "The server answered 500." ) ;
383408 expect ( document . body . textContent ) . not . toContain ( "at draft" ) ;
409+ const details = document . querySelector (
410+ ".create-agent-advanced" ,
411+ ) as HTMLDetailsElement | null ;
412+ expect ( details ?. open ) . toBe ( false ) ;
384413 } ) ;
385414} ) ;
386415
0 commit comments