@@ -42,8 +42,10 @@ fn run_policy_local(runner: &mut OpenShellRunner) -> ScenarioFuture<'_> {
4242 let name = format ! ( "ct-{}-pl" , runner. id( ) ) ;
4343 create_sandbox ( runner, & name, None ) . await ?;
4444 enable_proposals ( runner, & name) . await ?;
45+ let binary = sandbox_bash_path ( runner, & name) . await ?;
4546
4647 let started = Instant :: now ( ) ;
48+ let readiness_path = format ! ( "/v1/proposals/ct-{}-readiness" , runner. id( ) ) ;
4749 loop {
4850 match request_policy_local ( runner, & name, "/v1/policy/current" ) . await {
4951 Ok ( response)
@@ -52,7 +54,20 @@ fn run_policy_local(runner: &mut OpenShellRunner) -> ScenarioFuture<'_> {
5254 . as_str ( )
5355 . is_some_and ( |yaml| yaml. contains ( "version: 1" ) ) =>
5456 {
55- break ;
57+ // The current-policy route is local; proposal submission also
58+ // needs the supervisor's workspace and gateway lookup session.
59+ match request_policy_local_http ( runner, & name, "GET" , & readiness_path, "" , 404 )
60+ . await
61+ {
62+ Ok ( lookup) if lookup[ "error" ] == "chunk_not_found" => break ,
63+ Ok ( lookup) => {
64+ return Err ( format ! (
65+ "policy.local proposal lookup returned an invalid readiness response: {lookup}"
66+ ) ) ;
67+ }
68+ Err ( error) if started. elapsed ( ) >= READY_TIMEOUT => return Err ( error) ,
69+ Err ( _) => { }
70+ }
5671 }
5772 Ok ( response) => {
5873 return Err ( format ! (
@@ -89,7 +104,7 @@ fn run_policy_local(runner: &mut OpenShellRunner) -> ScenarioFuture<'_> {
89104 "enforcement" : "enforce" ,
90105 "rules" : [ { "allow" : { "method" : "GET" , "path" : "/conformance" } } ]
91106 } ] ,
92- "binaries" : [ { "path" : "/usr/bin/bash" } ]
107+ "binaries" : [ { "path" : & binary } ]
93108 }
94109 }
95110 } ]
@@ -115,7 +130,7 @@ fn run_policy_local(runner: &mut OpenShellRunner) -> ScenarioFuture<'_> {
115130 request_policy_local ( runner, & name, & format ! ( "/v1/proposals/{chunk_id}" ) ) . await ?;
116131 if state[ "chunk_id" ] != chunk_id
117132 || state[ "rule_name" ] != rule_name
118- || state[ "binary" ] != "/usr/bin/bash"
133+ || state[ "binary" ] != binary
119134 || !matches ! ( state[ "status" ] . as_str( ) , Some ( "pending" | "approved" ) )
120135 {
121136 return Err ( format ! ( "policy.local returned the wrong proposal: {state}" ) ) ;
@@ -138,6 +153,32 @@ fn run_policy_local(runner: &mut OpenShellRunner) -> ScenarioFuture<'_> {
138153 } )
139154}
140155
156+ async fn sandbox_bash_path ( runner : & OpenShellRunner , name : & str ) -> Result < String , String > {
157+ let result = runner
158+ . step ( "bash-binary" )
159+ . description ( "the sandbox's Bash executable has a canonical path" )
160+ . with_timeout ( COMMAND_TIMEOUT )
161+ . run ( & [
162+ "sandbox" ,
163+ "exec" ,
164+ "--name" ,
165+ name,
166+ "--no-tty" ,
167+ "--" ,
168+ "bash" ,
169+ "-c" ,
170+ "readlink -f /proc/$$/exe" ,
171+ ] )
172+ . await
173+ . map_err ( |error| error. to_string ( ) ) ?;
174+ result. require_success ( ) ?;
175+ let binary = result. stdout ( ) . trim ( ) ;
176+ if !binary. starts_with ( '/' ) || binary. contains ( '\n' ) {
177+ return Err ( result. failure_diagnostic ( "one absolute Bash executable path" ) ) ;
178+ }
179+ Ok ( binary. to_string ( ) )
180+ }
181+
141182async fn enable_proposals ( runner : & OpenShellRunner , name : & str ) -> Result < ( ) , String > {
142183 let set = runner
143184 . step ( "enable-policy-advisor" )
@@ -310,7 +351,13 @@ network_policies: {}
310351 . run ( & [ "rule" , "get" , & name] )
311352 . await
312353 . map_err ( |error| error. to_string ( ) ) ?;
313- draft. require_success ( ) ?;
354+ if !draft. success ( ) {
355+ if started. elapsed ( ) >= PROPOSAL_TIMEOUT {
356+ return Err ( draft. failure_diagnostic ( "the reviewer inbox is readable" ) ) ;
357+ }
358+ sleep ( POLL_INTERVAL ) . await ;
359+ continue ;
360+ }
314361 if !draft. stdout ( ) . contains ( "Chunk:" ) {
315362 if started. elapsed ( ) >= PROPOSAL_TIMEOUT {
316363 return Err ( draft. failure_diagnostic ( & format ! (
0 commit comments