# fix: address review + green CI for trail context injection

`1144985`→[main](/content/gh/entireio/cli/commits/main/index.html)·

dipree·1mo ago·6 files·+39 added/-93 removed

\- Track once-per-session injection on session.State (ContextInjectionDecided)
instead of a separate .injected marker file. The marker lived in
.git/entire-sessions/ and broke integration tests that count dir entries
and read stateFiles[0] (it sorted before .json). Removes ClaimContextInjection.
\- Bound NewAuthenticatedAPIClient under the same probe timeout as the HTTP
call (it can do /.well-known discovery + token exchange) — Copilot review.
\- Drain the TrailsEnabled response body before close for connection reuse —
Copilot review.
\- gofmt inject_test.go (lint).

## Sessions

2df0d7859de4View transcript

[?\
Inject Trail Context into Agent ModelPi·Opus 4.8·1 step](/content/gh/entireio/cli/session/019ecbec-8bcf-7f35-a647-2fc26c08a22e#timeline-2df0d7859de4/index.html)

## Changes

6

- cmd/entire/cli

- agent

- Minject_test.go+1

- api

- Mtrails.go+3

- Mlifecycle.go+28/-17

- session

- Mstate.go+7

- strategy

- Msession_state.go-29

- Msession_state_test.go-47

```go
51 unmodified lines

```

```go
type injectorStub struct{ Agent }

func (injectorStub) InjectionEvent() EventType { return TurnStart }

//nolint:unparam // signature is dictated by the ContextInjector interface
func (injectorStub) RenderContextInjection(ContextInjection) ([]byte, error) {
	return []byte("x"), nil
}
```

Mcmd/entire/cli/agent/inject_test.go+1

```go
2 unmodified lines

```

```go
import (
	"context"
	"fmt"
	"io"
	"net/http"
)

```

```go
11 unmodified lines
	
	return false, fmt.Errorf("probe trails enablement: %w", err)
}

defer resp.Body.Close()
// Drain (bounded) so net/http can reuse the connection; the body is unused.
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<16)) //nolint:errcheck // best-effort drain
return resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices, nil
}
```

Mcmd/entire/cli/api/trails.go+3

```go
// trailsEnabledForRepo reports whether Entire trails are literally enabled for
// this repo on the API. It resolves the origin remote to a supported forge and
// probes the trails endpoint; a successful response means trails are
// provisioned/enabled. Best-effort and bounded by a short timeout — an
// unresolved remote, missing auth, or any API/transport error reports false, so
// we never advertise trails we can't confirm are enabled.
func trailsEnabledForRepo(ctx context.Context) bool {
	forge, owner, repo, err := resolveTrailRemote(ctx)
	if err != nil {
		return false
	}
	client, err := NewAuthenticatedAPIClient(ctx, false)
	if err != nil {
		return false // not authenticated → trails aren't enabled for us
	}
	probeCtx, cancel := context.WithTimeout(ctx, trailsEnabledProbeTimeout)
	defer cancel()
	client, err := NewAuthenticatedAPIClient(probeCtx, false)
	if err != nil {
		return false // not authenticated → trails aren't enabled for us
	}
	enabled, err := client.TrailsEnabled(probeCtx, forge, owner, repo)
	return err == nil && enabled
}
```

Mcmd/entire/cli/lifecycle.go+28/-17

```go
224 unmodified lines

```

```go
// ClaimContextInjection records that Entire's model-facing context injection
// has been emitted for a session, so the injection happens at most once per
// session. First-writer-wins, mirroring ClaimSessionStartBanner (and cleaned up
// by ClearSessionState, which removes every "<sessionID>" file).
//
// Returns (claimed=true) when this call won the race and the caller should emit
// the injection; (claimed=false) when an earlier call already claimed it.
func ClaimContextInjection(ctx context.Context, sessionID string) (claimed bool, err error) {
	if vErr := validation.ValidateSessionID(sessionID); vErr != nil {
		return false, fmt.Errorf("invalid session ID: %w", vErr)
	}

root, rErr := openSessionStateRoot(ctx)
	if rErr != nil {
		return false, rErr
	}
	defer root.Close()

f, oErr := root.OpenFile(sessionID+".injected", os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
	if oErr != nil {
		if errors.Is(oErr, os.ErrExist) {
			return false, nil
		}
		return false, fmt.Errorf("failed to create injection marker file: %w", oErr)
	}
	_ = f.Close()
	return true, nil
}
```

Mcmd/entire/cli/session/state.go+7

```go
372 unmodified lines

```

```go
// LoadAgentTypeHint reads the agent type hint written by SessionStart.
// Returns empty string if the hint file doesn't exist, can't be read, or the
// value isn't a registered agent type.
```

Mcmd/entire/cli/strategy/session_state.go-29

```go
997 unmodified lines

```

```go
	}
}

func TestClaimContextInjection_FirstWriterWins(t *testing.T) {
	dir := t.TempDir()
	_, err := git.PlainInit(dir, false)
	require.NoError(t, err)
	t.Chdir(dir)

ctx := context.Background()
	sessionID := "2026-01-01-injection-claim"

claimed, err := ClaimContextInjection(ctx, sessionID)
	require.NoError(t, err)
	require.True(t, claimed, "first call must win the injection claim")

claimed, err = ClaimContextInjection(ctx, sessionID)
	require.NoError(t, err)
	require.False(t, claimed, "subsequent calls must report the injection already claimed")
}

func TestClaimContextInjection_InvalidSessionID_ReturnsError(t *testing.T) {
	dir := t.TempDir()
	_, err := git.PlainInit(dir, false)
	require.NoError(t, err)
	t.Chdir(dir)

_, err = ClaimContextInjection(context.Background(), "../../../etc/passwd")
	require.Error(t, err)
}

func TestClearSessionState_RemovesInjectionMarker(t *testing.T) {
	dir := t.TempDir()
	_, err := git.PlainInit(dir, false)
	require.NoError(t, err)
	t.Chdir(dir)

ctx := context.Background()
	sessionID := "2026-01-01-clear-injection"

_, err = ClaimContextInjection(ctx, sessionID)
	require.NoError(t, err)
	require.NoError(t, ClearSessionState(ctx, sessionID))

// After clear, the marker is gone — the next claim wins again.
	claimed, err := ClaimContextInjection(ctx, sessionID)
	require.NoError(t, err)
	require.True(t, claimed, "ClearSessionState should remove the injection marker")
}
```

Mcmd/entire/cli/strategy/session_state_test.go-47
