diff --git a/internal/client/agent/executor.go b/internal/client/agent/executor.go index e09ca12..2a82a8d 100644 --- a/internal/client/agent/executor.go +++ b/internal/client/agent/executor.go @@ -132,12 +132,13 @@ func (executor *Executor) launch(ctx context.Context, issue domain.UUID, revisio return executor.reject(ctx, issue, revision, err) } identity := process.Identity() - if err := executor.Store.SetLaunchPhase(ctx, issue, domain.LaunchPhasePrepared, identity.Context, 0); err != nil { + pid := process.PID() + if err := executor.Store.SetLaunchPhase(ctx, issue, domain.LaunchPhasePrepared, identity.Context, pid); err != nil { _, _ = executor.Supervisor.Signal(context.Background(), process, supervisor.SignalKill) cancel() return err } - if err := executor.Store.SetLaunchPhase(ctx, issue, domain.LaunchPhaseAuthorized, identity.Context, 0); err != nil { + if err := executor.Store.SetLaunchPhase(ctx, issue, domain.LaunchPhaseAuthorized, identity.Context, pid); err != nil { _, _ = executor.Supervisor.Signal(context.Background(), process, supervisor.SignalKill) cancel() return err diff --git a/internal/client/supervisor/supervisor.go b/internal/client/supervisor/supervisor.go index 407f4e4..004deea 100644 --- a/internal/client/supervisor/supervisor.go +++ b/internal/client/supervisor/supervisor.go @@ -130,6 +130,9 @@ func windowsExecutionContext(value string) (rvboxv1.WindowsExecutionContext, boo type Process interface { IssueUUID() domain.UUID Identity() EffectiveIdentity + // PID is immutable process evidence used only for diagnostics/recovery + // correlation; callers must never signal a process by PID alone. + PID() uint32 // Release crosses the durable launch-authorized barrier. A native Windows // process is created suspended and must not execute before this call. Release(context.Context) error diff --git a/internal/client/supervisor/windows/native_exec.go b/internal/client/supervisor/windows/native_exec.go index 73ad781..bb3542b 100644 --- a/internal/client/supervisor/windows/native_exec.go +++ b/internal/client/supervisor/windows/native_exec.go @@ -116,6 +116,13 @@ func (process *execProcess) IssueUUID() domain.UUID { return process.issue } func (process *execProcess) Identity() supervisor.EffectiveIdentity { return process.identity } +func (process *execProcess) PID() uint32 { + if process == nil { + return 0 + } + return process.pid +} + // Release is the second half of the durable launch barrier. The portable // adapter has no suspended native handle, so its release is intentionally a // no-op; the Windows adapter supplies a ResumeThread closure. diff --git a/internal/client/supervisor/windows/native_other_test.go b/internal/client/supervisor/windows/native_other_test.go index 3a65980..b35fb09 100644 --- a/internal/client/supervisor/windows/native_other_test.go +++ b/internal/client/supervisor/windows/native_other_test.go @@ -48,6 +48,9 @@ func TestPortableSupervisorCapturesOutputAndSupportsStdin_HP_SUPERVISOR_03(t *te if err != nil { t.Fatal(err) } + if process.PID() == 0 { + t.Fatal("portable supervisor did not expose process identity PID") + } if err := process.WriteStdin(context.Background(), []byte("hello"), true); err != nil { t.Fatal(err) }