@@ -12,6 +12,7 @@ const baseCtx: SetupPromptContext = {
1212 handoff : 'claude-code' ,
1313 mode : 'implement' ,
1414 installedSkills : [ 'stash-encryption' , 'stash-drizzle' , 'stash-cli' ] ,
15+ usesProxy : false ,
1516}
1617
1718describe ( 'renderSetupPrompt — orient + route (implement mode)' , ( ) => {
@@ -380,3 +381,179 @@ describe('renderSetupPrompt — plan mode default when planStep is unset', () =>
380381 )
381382 } )
382383} )
384+
385+ describe ( 'renderSetupPrompt — usesProxy conditional' , ( ) => {
386+ describe ( 'implement mode with usesProxy: false (SDK-only)' , ( ) => {
387+ it ( 'drops db push step from add-new-column flow' , ( ) => {
388+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : false } )
389+ expect ( out ) . not . toMatch ( / 5 \. \s * R e g i s t e r t h e e n c r y p t i o n c o n f i g / )
390+ // Step 5 should now be the wire-the-column step, not db push
391+ expect ( out ) . toMatch ( / 5 \. \s * W i r e t h e c o l u m n t h r o u g h / )
392+ } )
393+
394+ it ( 'drops register-pending-config step from rollout path' , ( ) => {
395+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : false } )
396+ // Should have only "Schema-add" as step 1, then "Dual-write" as step 2
397+ // (no "Register pending config" in between)
398+ expect ( out ) . toMatch ( / 1 \. \s * \* \* S c h e m a - a d d / )
399+ expect ( out ) . toMatch ( / 2 \. \s * \* \* D u a l - w r i t e / )
400+ // Register pending config should not appear in the rollout section
401+ const rolloutSection = out . substring (
402+ out . indexOf ( '#### Encryption rollout' ) ,
403+ out . indexOf ( '⛔' ) ,
404+ )
405+ expect ( rolloutSection ) . not . toMatch ( / R e g i s t e r p e n d i n g c o n f i g / )
406+ } )
407+
408+ it ( 'keeps encrypt cutover invocation and notes the pending-config workaround' , ( ) => {
409+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : false } )
410+ const cutoverSection = out . substring (
411+ out . indexOf ( '#### Encryption cutover' ) ,
412+ )
413+ // Should mention encrypt cutover
414+ expect ( cutoverSection ) . toMatch ( / e n c r y p t c u t o v e r / )
415+ // SDK-only setups still hit the pending-config gap, so the cutover
416+ // step must call out the `db push` workaround for that error.
417+ expect ( cutoverSection ) . toMatch ( / N o p e n d i n g E Q L c o n f i g u r a t i o n / )
418+ expect ( cutoverSection ) . toMatch ( / d b p u s h / )
419+ } )
420+ } )
421+
422+ describe ( 'implement mode with usesProxy: true (Proxy)' , ( ) => {
423+ it ( 'includes db push step in add-new-column flow' , ( ) => {
424+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : true } )
425+ expect ( out ) . toMatch ( / 5 \. \s * R e g i s t e r t h e e n c r y p t i o n c o n f i g .* d b p u s h / )
426+ expect ( out ) . toMatch ( / 6 \. \s * \* \* I f d b p u s h w r o t e p e n d i n g \* \* .* d b a c t i v a t e / )
427+ } )
428+
429+ it ( 'includes register-pending-config step in rollout path' , ( ) => {
430+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : true } )
431+ expect ( out ) . toMatch ( / 2 \. \s * \* \* R e g i s t e r p e n d i n g c o n f i g .* d b p u s h / )
432+ expect ( out ) . toMatch ( / 3 \. \s * \* \* D u a l - w r i t e / )
433+ } )
434+
435+ it ( 'includes full db push in cutover step' , ( ) => {
436+ const out = renderSetupPrompt ( { ...baseCtx , usesProxy : true } )
437+ const cutoverSection = out . substring (
438+ out . indexOf ( '#### Encryption cutover' ) ,
439+ )
440+ expect ( cutoverSection ) . toMatch ( / 5 \. \s * \* \* S w i t c h t h e s c h e m a a n d r e - p u s h / )
441+ expect ( cutoverSection ) . toMatch ( / R u n .* d b p u s h .* a g a i n / )
442+ } )
443+ } )
444+
445+ describe ( 'plan mode (rollout) with usesProxy conditional' , ( ) => {
446+ it ( 'mentions db push in rollout plan summary when usesProxy: true' , ( ) => {
447+ const out = renderSetupPrompt ( {
448+ ...baseCtx ,
449+ mode : 'plan' ,
450+ planStep : 'rollout' ,
451+ usesProxy : true ,
452+ } )
453+ expect ( out ) . toMatch (
454+ / E n c r y p t i o n r o l l o u t .* d u a l - w r i t e c o d e , a n d .* d b p u s h .* w r i t e s p e n d i n g / ,
455+ )
456+ } )
457+
458+ it ( 'notes db push as Proxy-only in rollout plan summary when usesProxy: false' , ( ) => {
459+ const out = renderSetupPrompt ( {
460+ ...baseCtx ,
461+ mode : 'plan' ,
462+ planStep : 'rollout' ,
463+ usesProxy : false ,
464+ } )
465+ expect ( out ) . toMatch (
466+ / E n c r y p t i o n r o l l o u t .* d u a l - w r i t e c o d e .* p l u s .* d b p u s h .* P r o x y u s e r s o n l y / ,
467+ )
468+ } )
469+
470+ it ( 'includes db push in rollout PR contents when usesProxy: true' , ( ) => {
471+ const out = renderSetupPrompt ( {
472+ ...baseCtx ,
473+ mode : 'plan' ,
474+ planStep : 'rollout' ,
475+ usesProxy : true ,
476+ } )
477+ expect ( out ) . toMatch ( / s c h e m a - a d d .* d b p u s h .* p e n d i n g .* d u a l - w r i t e c o d e / )
478+ } )
479+
480+ it ( 'drops db push from rollout PR contents when usesProxy: false' , ( ) => {
481+ const out = renderSetupPrompt ( {
482+ ...baseCtx ,
483+ mode : 'plan' ,
484+ planStep : 'rollout' ,
485+ usesProxy : false ,
486+ } )
487+ expect ( out ) . toMatch ( / s c h e m a - a d d .* d u a l - w r i t e c o d e / )
488+ // Should not mention "db push (pending)" in the rollout PR contents
489+ const prSection = out . substring (
490+ out . indexOf ( 'migrate columns: what the rollout PR contains' ) ,
491+ )
492+ expect ( prSection ) . not . toMatch ( / d b p u s h .* p e n d i n g / )
493+ } )
494+ } )
495+
496+ describe ( 'plan mode (cutover) with usesProxy conditional' , ( ) => {
497+ it ( 'includes db push in schema-rename when usesProxy: true' , ( ) => {
498+ const out = renderSetupPrompt ( {
499+ ...baseCtx ,
500+ mode : 'plan' ,
501+ planStep : 'cutover' ,
502+ usesProxy : true ,
503+ } )
504+ expect ( out ) . toMatch ( / S c h e m a r e n a m e a n d r e - p u s h / )
505+ expect ( out ) . toMatch ( / d b p u s h .* r e g i s t e r s t h e r e n a m e d / )
506+ } )
507+
508+ it ( 'separates schema rename from db push when usesProxy: false' , ( ) => {
509+ const out = renderSetupPrompt ( {
510+ ...baseCtx ,
511+ mode : 'plan' ,
512+ planStep : 'cutover' ,
513+ usesProxy : false ,
514+ } )
515+ expect ( out ) . toMatch ( / \* \* S c h e m a r e n a m e \. \* \* .* o r i g i n a l c o l u m n / )
516+ expect ( out ) . toMatch ( / P r o x y u s e r s o n l y .* d b p u s h / )
517+ } )
518+
519+ it ( 'notes db push as Proxy-only in prose when usesProxy: false' , ( ) => {
520+ const out = renderSetupPrompt ( {
521+ ...baseCtx ,
522+ mode : 'plan' ,
523+ planStep : 'cutover' ,
524+ usesProxy : false ,
525+ } )
526+ expect ( out ) . toMatch ( / s c h e m a - e d i t s t e p .* e x a c t r e n a m e p a t t e r n / )
527+ expect ( out ) . not . toMatch ( / s c h e m a - e d i t .* d b p u s h .* s t e p / i)
528+ } )
529+ } )
530+
531+ describe ( 'plan mode (complete) with usesProxy conditional' , ( ) => {
532+ it ( 'includes db push steps in full lifecycle when usesProxy: true' , ( ) => {
533+ const out = renderSetupPrompt ( {
534+ ...baseCtx ,
535+ mode : 'plan' ,
536+ planStep : 'complete' ,
537+ usesProxy : true ,
538+ } )
539+ expect ( out ) . toMatch ( / d b p u s h .* b a c k f i l l .* s c h e m a r e n a m e .* d b p u s h .* c u t o v e r / )
540+ } )
541+
542+ it ( 'drops both db push mentions from full lifecycle when usesProxy: false' , ( ) => {
543+ const out = renderSetupPrompt ( {
544+ ...baseCtx ,
545+ mode : 'plan' ,
546+ planStep : 'complete' ,
547+ usesProxy : false ,
548+ } )
549+ expect ( out ) . toMatch ( / s c h e m a - a d d .* d u a l - w r i t e c o d e .* b a c k f i l l .* s c h e m a r e n a m e / )
550+ // The lifecycle line should not have "db push" twice
551+ const migrateSection = out . substring (
552+ out . indexOf ( '**Migrate existing columns**' ) ,
553+ )
554+ const firstLine = migrateSection . split ( '\n' ) [ 0 ]
555+ const dbPushCount = ( firstLine . match ( / d b p u s h / g) || [ ] ) . length
556+ expect ( dbPushCount ) . toBe ( 0 )
557+ } )
558+ } )
559+ } )
0 commit comments