@@ -3507,6 +3507,108 @@ mod tests {
35073507 ) ;
35083508 }
35093509
3510+ /// The delivery seam, harness half: a prompt reloaded from a file on the
3511+ /// desktop must reach the ACP `session/new` request byte-for-byte after the
3512+ /// agent restarts.
3513+ ///
3514+ /// A restart writes the definition's prompt into `BUZZ_ACP_SYSTEM_PROMPT`,
3515+ /// which lands in `Config::system_prompt`, is framed by
3516+ /// `pool::combined_system_prompt` and handed to `session_new_full` through
3517+ /// `pool::session_new_system_prompt`. This drives real file bytes through
3518+ /// that composition and that transport choice, and asserts what the adapter
3519+ /// actually receives, for both framings. The adapter does not receive the
3520+ /// bare file — the harness frames it in `<system>` — so the falsifiable
3521+ /// claim is that the file's bytes survive the framing verbatim.
3522+ ///
3523+ /// This test guards the harness on its own (`cargo test -p buzz-acp`, with
3524+ /// no desktop build). The whole chain from the file on disk through the
3525+ /// desktop reload and restart into this request is
3526+ /// `a_reloaded_prompt_file_reaches_the_adapter_after_a_restart` in
3527+ /// `desktop/src-tauri/src/commands/personas/prompt_source/tests.rs`.
3528+ #[ tokio:: test]
3529+ async fn session_new_delivers_reloaded_prompt_source_file_bytes ( ) {
3530+ // Layout characters, non-ASCII and a trailing newline: everything a
3531+ // hand-edited prompt file carries that a naive trim would eat.
3532+ let file_text = "You are the PM.\n \n \t Keep a decision log — ünïcode, emoji 🐝.\n " ;
3533+ let path = std:: env:: temp_dir ( ) . join ( format ! (
3534+ "buzz-prompt-source-{}-{}.md" ,
3535+ std:: process:: id( ) ,
3536+ uuid:: Uuid :: new_v4( )
3537+ ) ) ;
3538+ std:: fs:: write ( & path, file_text) . expect ( "write prompt file" ) ;
3539+ let prompt = std:: fs:: read_to_string ( & path) . expect ( "read prompt file back" ) ;
3540+ std:: fs:: remove_file ( & path) . ok ( ) ;
3541+ assert_eq ! ( prompt, file_text, "the reload carries the file's bytes" ) ;
3542+
3543+ // `read -r`: without it bash eats the backslashes in the JSON string
3544+ // escapes, and the echo-back would corrupt exactly the bytes under test.
3545+ let script = r#"
3546+ read -r -t 2 _init
3547+ echo '{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":2,"agentCapabilities":{}}}'
3548+ read -r -t 2 REQ
3549+ printf '%s\n' '{"jsonrpc":"2.0","id":1,"result":{"sessionId":"ses_reload","_receivedRequest":'"$REQ"'}}'
3550+ sleep 1
3551+ "# ;
3552+
3553+ // A base prompt is present, as on every spawn that does not pass
3554+ // `--no-base-prompt`, so this asserts the file's bytes survive the
3555+ // framing rather than asserting the framing is absent.
3556+ let composed = crate :: pool:: combined_system_prompt (
3557+ "/tmp" ,
3558+ Some ( "Base harness instructions." ) ,
3559+ Some ( prompt. as_str ( ) ) ,
3560+ None ,
3561+ None ,
3562+ None ,
3563+ None ,
3564+ )
3565+ . expect ( "a system prompt composes" ) ;
3566+ assert ! (
3567+ composed. contains( file_text) ,
3568+ "the composed prompt must carry the file's bytes verbatim: {composed:?}"
3569+ ) ;
3570+
3571+ // Field framing (buzz-agent and every non-Claude adapter on v2).
3572+ let mut client = spawn_script ( script) . await ;
3573+ client
3574+ . initialize ( )
3575+ . await
3576+ . expect ( "initialize should succeed" ) ;
3577+ let transport =
3578+ crate :: pool:: session_new_system_prompt ( false , 2 , "buzz-agent" , Some ( composed. as_str ( ) ) ) ;
3579+ let resp = client
3580+ . session_new_full ( "/tmp" , vec ! [ ] , transport, None )
3581+ . await
3582+ . expect ( "session_new_full should succeed" ) ;
3583+ assert_eq ! (
3584+ resp. raw[ "_receivedRequest" ] [ "params" ] [ "systemPrompt" ] . as_str( ) ,
3585+ Some ( composed. as_str( ) ) ,
3586+ "the adapter must receive the prompt file's bytes unchanged"
3587+ ) ;
3588+
3589+ // Claude framing keeps the adapter's own preset and appends ours.
3590+ let mut client = spawn_script ( script) . await ;
3591+ client
3592+ . initialize ( )
3593+ . await
3594+ . expect ( "initialize should succeed" ) ;
3595+ let transport = crate :: pool:: session_new_system_prompt (
3596+ false ,
3597+ 2 ,
3598+ crate :: pool:: CLAUDE_AGENT_ACP_NAME ,
3599+ Some ( composed. as_str ( ) ) ,
3600+ ) ;
3601+ let resp = client
3602+ . session_new_full ( "/tmp" , vec ! [ ] , transport, None )
3603+ . await
3604+ . expect ( "session_new_full should succeed" ) ;
3605+ assert_eq ! (
3606+ resp. raw[ "_receivedRequest" ] [ "params" ] [ "_meta" ] [ "systemPrompt" ] [ "append" ] . as_str( ) ,
3607+ Some ( composed. as_str( ) ) ,
3608+ "the Claude framing must append the prompt file's bytes unchanged"
3609+ ) ;
3610+ }
3611+
35103612 #[ tokio:: test]
35113613 async fn goose_system_prompt_request_uses_set_contract ( ) {
35123614 let script = r#"
0 commit comments