@@ -66,6 +66,15 @@ type Runtime struct {
6666 runnerEnvironment runners.RunnerEnvironment
6767 runner * runnerHandle
6868
69+ // interpreter is the absolute path of the python `uv run` executes the app
70+ // with, as uv resolved it for the launch that produced s.runner (see
71+ // startedartifact.go). The launch that publishes the handle publishes it,
72+ // a Start that keeps that handle re-reports it unchanged, and the teardown
73+ // that drops the environment drops it too — a stale one would name a
74+ // program nothing is running. Guarded by runnerMu, like the handle it
75+ // describes.
76+ interpreter string
77+
6978 // runnerMu serializes the runner lifecycle. Start, Stop and the supervisor
7079 // goroutines are concurrent and all replace s.runner; without it two
7180 // overlapping Starts can each stop the same process and leave one of their
@@ -659,7 +668,12 @@ func (s *Runtime) Start(ctx context.Context, req *runtimev0.StartRequest) (*runt
659668 // an Init between two identical Starts can republish the ports,
660669 // and the process bound to the old one is not what this request
661670 // describes however unchanged the request is.
662- return s .Base .Runtime .StartResponse ()
671+ //
672+ // It still names what it runs: StartResponse consumes the report,
673+ // so keeping a process without describing it again would stamp no
674+ // artifact on this start and have a native run refuse a service
675+ // that is up and unchanged.
676+ return s .started ()
663677 default :
664678 // Different inputs only reach the service through a new process:
665679 // --reload re-executes the app inside the process it was launched
@@ -722,6 +736,13 @@ func (s *Runtime) Start(ctx context.Context, req *runtimev0.StartRequest) (*runt
722736 proc .WithEnvironmentVariables (ctx , resources .Env ("CODEFLY_CONNECT_PORT" , s .connectPort ))
723737 }
724738
739+ // What this launch runs, asked of uv in the environment this process is
740+ // being given — the overrides above included, since one of them can move
741+ // the project environment uv resolves (startedartifact.go). It is a
742+ // property of the launch, so it is resolved here rather than in Init and
743+ // re-reported unchanged by a later Start that keeps this process.
744+ s .interpreter = s .launchedInterpreter (ctx , startEnvs )
745+
725746 // The process must outlive this RPC, so it runs under a background context
726747 // — cancellable, so Stop, Destroy and a replacement can tell the supervisor
727748 // below that the exit it is about to see was asked for.
@@ -767,13 +788,27 @@ func (s *Runtime) Start(ctx context.Context, req *runtimev0.StartRequest) (*runt
767788 // STARTED is recorded before the supervisor is armed: revoking a status
768789 // that was never committed would leave the orchestrator reading STARTED for
769790 // a process that is already gone.
770- resp , err := s .Base . Runtime . StartResponse ()
791+ resp , err := s .started ()
771792 go s .superviseRunner (runningContext , handle )
772793
773794 s .Wool .Debug ("start done" )
774795 return resp , err
775796}
776797
798+ // started names what this start ran and answers STARTED.
799+ //
800+ // Every successful Start returns through here, because core consumes the
801+ // report at each StartResponse: an agent that names its build at the first
802+ // start and not at the restart reports nothing for the restart, which a
803+ // consumer refuses rather than carrying the previous process's answer over to
804+ // the one now running. The failure paths deliberately do not go through it —
805+ // StartError discards any report, so a build named for a process that never
806+ // served is never stamped on the attempt that does.
807+ func (s * Runtime ) started () (* runtimev0.StartResponse , error ) {
808+ s .Base .Runtime .ReportStartedArtifact (s .startedArtifact ())
809+ return s .Base .Runtime .StartResponse ()
810+ }
811+
777812// defaultServeTimeout bounds how long Start waits for the service to answer.
778813// It covers `uv run` resolving the environment, the app import and its
779814// lifespan startup; a service that has not answered by then is not coming up.
@@ -1279,6 +1314,7 @@ func (s *Runtime) endExecution(ctx context.Context) (bool, error) {
12791314 // a fresh one rather than hand Code and the REPL a dead handle.
12801315 released = true
12811316 s .runnerEnvironment = nil
1317+ s .interpreter = ""
12821318 s .FastAPI .Service .SetActiveEnvironment (nil )
12831319 }
12841320 }
0 commit comments