@@ -66,7 +66,11 @@ func (s *Runtime) Load(ctx context.Context, req *runtimev0.LoadRequest) (*runtim
6666 s .Service .SourceLocation = wd
6767 }
6868
69- return s .Runtime .LoadResponse ()
69+ resp , err := s .Runtime .LoadResponse ()
70+ if resp != nil && resp .Status != nil {
71+ resp .Status .Message = pythonhelpers .AppendRuntimeEvidence (resp .Status .Message , pythonhelpers .RuntimeEvidence (s .Service .SourceLocation ))
72+ }
73+ return resp , err
7074}
7175
7276// Init runs `uv sync` to materialize the venv. Non-fatal on failure.
@@ -150,11 +154,14 @@ func (s *Runtime) Test(ctx context.Context, req *runtimev0.TestRequest) (*runtim
150154 wool .Field ("command" , fspec .Command ), wool .Field ("output" , fspec .Output ))
151155 fStart := time .Now ()
152156 fRun , fErr := pythonhelpers .RunFormulaStructured (ctx , s .Service .SourceLocation , fspec )
157+ evidence := pythonhelpers .RuntimeEvidenceForFormula (s .Service .SourceLocation , cmd , output , env , prov , formulaDerivedFromProject (req , s .Base .Service ))
153158 if fRun == nil {
154- return s .Runtime . TestError (fErr )
159+ return s .testErrorWithEvidence (fErr , evidence )
155160 }
156161 s .Wool .Forwardf ("Tests: %s" , fRun .LegacyTestSummary ().SummaryLine ())
157- return fRun .ToProtoResponse ("formula" , req .Suite , time .Since (fStart )), fErr
162+ resp := fRun .ToProtoResponse ("formula" , req .Suite , time .Since (fStart ))
163+ appendTestResponseEvidence (resp , evidence )
164+ return resp , fErr
158165 }
159166
160167 opts := pythonhelpers.TestOptions {
@@ -195,9 +202,11 @@ func (s *Runtime) Test(ctx context.Context, req *runtimev0.TestRequest) (*runtim
195202 }
196203
197204 if run == nil {
198- return s .Runtime . TestError (runErr )
205+ return s .testErrorWithEvidence (runErr , pythonhelpers . RuntimeEvidence ( s . Service . SourceLocation ) )
199206 }
200- return run .ToProtoResponse ("pytest" , req .Suite , duration ), runErr
207+ resp := run .ToProtoResponse ("pytest" , req .Suite , duration )
208+ appendTestResponseEvidence (resp , pythonhelpers .RuntimeEvidence (s .Service .SourceLocation ))
209+ return resp , runErr
201210}
202211
203212// Lint runs ruff via the core python helper.
@@ -229,12 +238,29 @@ func (s *Runtime) Information(ctx context.Context, req *runtimev0.InformationReq
229238// (resources.Service.Test), else the formula DERIVED from the project, else none
230239// (ok=false → the agent's default runner). Returns the raw language-agnostic
231240// fields; SpecFromFormula does the uv mapping.
241+ //
242+ // A per-call formula with NO command but WITH provisioning/env (a Mind
243+ // environment heal like {cwd: tests} or {with: Werkzeug<3}) is an OVERLAY: it
244+ // rides on top of the configured/derived formula instead of being dropped.
245+ // Mirrors Mind's InProcessRuntime so local == remote — before this, a healed
246+ // work_dir sent without a command was a silent no-op on the agent path.
232247func resolveTestFormula (req * runtimev0.TestRequest , svc * resources.Service , sourceDir string ) (cmd []string , output string , env , prov map [string ]string , ok bool ) {
233- if f := req .GetFormula (); f != nil && len (f .GetCommand ()) > 0 {
234- return f .GetCommand (), f .GetOutput (), f .GetEnv (), f .GetProvisioning (), true
248+ f := req .GetFormula ()
249+ if f != nil && len (f .GetCommand ()) > 0 {
250+ return f .GetCommand (), f .GetOutput (), f .GetEnv (), enrichSuppliedProvisioning (f .GetProvisioning (), sourceDir ), true
251+ }
252+ overlay := func (bcmd []string , boutput string , benv , bprov map [string ]string ) ([]string , string , map [string ]string , map [string ]string , bool ) {
253+ if f != nil {
254+ if o := f .GetOutput (); o != "" {
255+ boutput = o
256+ }
257+ benv = overlayStringMap (benv , f .GetEnv ())
258+ bprov = overlayStringMap (bprov , f .GetProvisioning ())
259+ }
260+ return bcmd , boutput , benv , bprov , true
235261 }
236262 if svc != nil && svc .Test != nil && len (svc .Test .Command ) > 0 {
237- return svc .Test .Command , svc .Test .Output , svc .Test .Env , svc .Test .Provisioning , true
263+ return overlay ( svc .Test .Command , svc .Test .Output , svc .Test .Env , enrichSuppliedProvisioning ( svc .Test .Provisioning , sourceDir ))
238264 }
239265 // No explicit (per-call) or configured (service.yaml) formula → DERIVE it from
240266 // the project: read its own declarations (tox/Makefile/CI/README) for the
@@ -243,12 +269,87 @@ func resolveTestFormula(req *runtimev0.TestRequest, svc *resources.Service, sour
243269 // python plugin "just run the project's tests" — no framework knowledge in Mind.
244270 if sourceDir != "" {
245271 if dcmd , doutput , denv , dprov , dok := pythonhelpers .DeriveFormula (sourceDir ); dok {
246- return dcmd , doutput , denv , dprov , true
272+ return overlay ( dcmd , doutput , denv , dprov )
247273 }
248274 }
249275 return nil , "" , nil , nil , false
250276}
251277
278+ // overlayStringMap returns base with overlay's entries layered on top (overlay
279+ // wins). Used so a per-call heal (provisioning/env without a command) survives
280+ // when the command comes from config/derivation. Returns base unchanged when
281+ // the overlay is empty.
282+ func overlayStringMap (base , overlay map [string ]string ) map [string ]string {
283+ if len (overlay ) == 0 {
284+ return base
285+ }
286+ out := make (map [string ]string , len (base )+ len (overlay ))
287+ for k , v := range base {
288+ out [k ] = v
289+ }
290+ for k , v := range overlay {
291+ out [k ] = v
292+ }
293+ return out
294+ }
295+
296+ // enrichSuppliedProvisioning delegates to the SHARED core helper
297+ // (pythonhelpers.EnrichSuppliedProvisioning) so the agent's Test RPC and
298+ // Mind's in-process runtime resolve identical formulas — the health probe and
299+ // the task-phase test run must see the same derived editable/python/
300+ // build-requires under any supplied keys.
301+ func enrichSuppliedProvisioning (supplied map [string ]string , sourceDir string ) map [string ]string {
302+ return pythonhelpers .EnrichSuppliedProvisioning (supplied , sourceDir )
303+ }
304+
305+ func formulaDerivedFromProject (req * runtimev0.TestRequest , svc * resources.Service ) bool {
306+ if f := req .GetFormula (); f != nil && len (f .GetCommand ()) > 0 {
307+ return false
308+ }
309+ return svc == nil || svc .Test == nil || len (svc .Test .Command ) == 0
310+ }
311+
312+ func (s * Runtime ) testErrorWithEvidence (err error , evidence string ) (* runtimev0.TestResponse , error ) {
313+ if err == nil {
314+ msg := pythonhelpers .AppendRuntimeEvidence ("test runtime errored before tests could execute" , evidence )
315+ return & runtimev0.TestResponse {
316+ Status : & runtimev0.TestStatus {State : runtimev0 .TestStatus_ERROR , Message : msg },
317+ Result : & runtimev0.TestRunResult {State : runtimev0 .TestRunResult_ERRORED , Message : msg },
318+ Counts : & runtimev0.TestCounts {},
319+ }, nil
320+ }
321+ resp , testErr := s .Runtime .TestError (err )
322+ appendTestResponseErrorEvidence (resp , evidence )
323+ return resp , testErr
324+ }
325+
326+ func appendTestResponseEvidence (resp * runtimev0.TestResponse , evidence string ) {
327+ if resp == nil || resp .GetResult ().GetState () != runtimev0 .TestRunResult_ERRORED {
328+ return
329+ }
330+ appendTestResponseErrorEvidence (resp , evidence )
331+ }
332+
333+ func appendTestResponseErrorEvidence (resp * runtimev0.TestResponse , evidence string ) {
334+ if resp == nil {
335+ return
336+ }
337+ if resp .Counts == nil {
338+ resp .Counts = & runtimev0.TestCounts {}
339+ }
340+ if resp .Status != nil {
341+ resp .Status .Message = pythonhelpers .AppendRuntimeEvidence (resp .Status .Message , evidence )
342+ }
343+ if resp .Result != nil {
344+ resp .Result .Message = pythonhelpers .AppendRuntimeEvidence (resp .Result .Message , evidence )
345+ } else {
346+ resp .Result = & runtimev0.TestRunResult {
347+ State : runtimev0 .TestRunResult_ERRORED ,
348+ Message : pythonhelpers .AppendRuntimeEvidence ("" , evidence ),
349+ }
350+ }
351+ }
352+
252353func boolToLintState (ok bool ) runtimev0.LintStatus_Status {
253354 if ok {
254355 return runtimev0 .LintStatus_SUCCESS
0 commit comments