From 393f49aa597b39449d615e2b4c2fce3e268f2385 Mon Sep 17 00:00:00 2001 From: naiba Date: Mon, 20 Jul 2026 17:05:29 +0000 Subject: [PATCH] fix(agentcompat): retain reconnect sentinel handles Co-authored-by: naiba/CloudCode --- .../internal/scenario/reconnect_operations.go | 30 +++++++++++++++---- .../internal/scenario/reconnect_run.go | 9 +++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/integration/agentcompat/internal/scenario/reconnect_operations.go b/integration/agentcompat/internal/scenario/reconnect_operations.go index 2d5c6947..2a0bd593 100644 --- a/integration/agentcompat/internal/scenario/reconnect_operations.go +++ b/integration/agentcompat/internal/scenario/reconnect_operations.go @@ -110,17 +110,35 @@ func runReconnectRead(ctx context.Context, mcpClient *client.Client, serverID ui return nil } -func prepareReconnectSentinel(agentRoot string) (fixturePath, sentinelPath string, err error) { +type reconnectSentinel struct { + root *os.Root + name string +} + +func (sentinel reconnectSentinel) read() ([]byte, error) { + return sentinel.root.ReadFile(sentinel.name) +} + +func (sentinel reconnectSentinel) close() error { return sentinel.root.Close() } + +func prepareReconnectSentinel(agentRoot string) (fixturePath string, sentinel reconnectSentinel, err error) { root, err := fixture.NewAgentRoot(agentRoot, "reconnect-files") if err != nil { - return "", "", err + return "", reconnectSentinel{}, err } path, err := root.Path("runtime.txt") if err != nil { - return "", "", err + return "", reconnectSentinel{}, err } fixturePath = path.String() - sentinelPath = agentRoot + "/outside-reconnect-sentinel" - err = os.WriteFile(sentinelPath, []byte("outside-reconnect-root-sentinel"), 0o600) - return fixturePath, sentinelPath, err + workspace, err := os.OpenRoot(agentRoot) + if err != nil { + return "", reconnectSentinel{}, err + } + sentinel = reconnectSentinel{root: workspace, name: "outside-reconnect-sentinel"} + if err := workspace.WriteFile(sentinel.name, []byte("outside-reconnect-root-sentinel"), 0o600); err != nil { + _ = workspace.Close() + return "", reconnectSentinel{}, err + } + return fixturePath, sentinel, nil } diff --git a/integration/agentcompat/internal/scenario/reconnect_run.go b/integration/agentcompat/internal/scenario/reconnect_run.go index 19564084..6afebf38 100644 --- a/integration/agentcompat/internal/scenario/reconnect_run.go +++ b/integration/agentcompat/internal/scenario/reconnect_run.go @@ -64,11 +64,12 @@ func (Reconnect) RunWithEvidence(ctx context.Context, input ReconnectInput) (res if err != nil { return reconnectFinish(assertions, err, reconnectEvidence) } - fixturePath, sentinelPath, err := prepareReconnectSentinel(agentRoot) + fixturePath, sentinel, err := prepareReconnectSentinel(agentRoot) if err != nil { return reconnectFinish(assertions, err, reconnectEvidence) } - sentinelBytes, err := os.ReadFile(sentinelPath) + defer sentinel.close() + sentinelBytes, err := sentinel.read() if err != nil { return reconnectFinish(assertions, err, reconnectEvidence) } @@ -98,7 +99,7 @@ func (Reconnect) RunWithEvidence(ctx context.Context, input ReconnectInput) (res assertions.Record("Dashboard disconnect barrier stopped generation one", stoppedRuntime == runtimeBefore && dashboardInstance.RuntimeIdentity().PID == 0, "") if input.DashboardFault == "dashboard-exit" { // This fault returns before the normal lifecycle evidence finalization below. - sentinelAfter, sentinelErr := os.ReadFile(sentinelPath) + sentinelAfter, sentinelErr := sentinel.read() sentinelUnchanged := sentinelErr == nil && bytes.Equal(sentinelBytes, sentinelAfter) reconnectEvidence.Lifecycle.DisconnectAt = disconnectAt reconnectEvidence.Lifecycle.OutsideRootSentinelUnchanged = sentinelUnchanged @@ -171,7 +172,7 @@ func (Reconnect) RunWithEvidence(ctx context.Context, input ReconnectInput) (res } taskIDs, resultIDs, duplicates, lost := reconnectReceiptSummary(dashboardPairs, agentPairs) stale := staleReconnectReceiptCount(runtimeBefore.Generation, dashboardPairs, agentPairs) - sentinelAfter, err := os.ReadFile(sentinelPath) + sentinelAfter, err := sentinel.read() if err != nil { return reconnectFinish(assertions, err, reconnectEvidence) }