Skip to content

fix(import-external-session): commit missing generated proto bindings - #445

Merged
tstapler merged 7 commits into
mainfrom
fix/import-session-ci-breakage
Aug 13, 2026
Merged

fix(import-external-session): commit missing generated proto bindings#445
tstapler merged 7 commits into
mainfrom
fix/import-session-ci-breakage

Conversation

@tstapler

@tstapler tstapler commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

main's CI was red because PR #433 (feat(import-external-session)) added proto/session/v1/import.proto and the Go/TS source that consumes its generated types, but never committed the generated output — and a separate ordering bug in .github/workflows/lint.yml (Jest ran before protobuf codegen) meant this went undetected: Jest only "worked" for every other proto because those generated files happened to be historically committed via git add -f, not because of any documented policy.

This PR now fixes the root causes properly rather than just papering over the missing files:

  1. git rm --cached every generated protobuf file under gen/proto/go/session/v1/ and web-app/src/gen/session/v1/ (30 files). These paths are already gitignored; they'd been force-added inconsistently with no ADR or documented policy behind it. Every make target that consumes generated code (build, test, lint) already depends on proto-gen — committing the output on top of that was redundant and created exactly the drift risk that broke feat(import-external-session): import external tmux sessions into stapler-squad #433.
  2. Fixed lint.yml's step orderbuf generate proto / ent codegen now run before Jest, golangci-lint, the import-cycle check, and feature-catalog validation (matching the correct order already used by .github/actions/prepare/action.yml).
  3. New CI guard: .github/workflows/generated-proto-guard.yml (matching the existing backlog-scaffolding-guard.yml pattern) — fails any PR that commits a file under gen/ or web-app/src/gen/, regardless of how it got there. This is specifically to stop AI coding agents (and humans) from git add -f-ing a generated file to make a local build pass, which is exactly how this incident happened and has happened before.
  4. Isolated the 3 import_commit_test.go tests that reach a real instance.Start() (PersistsAndLinksAndSuspends, CompensatingDeletesInstance, ReturnsError_When_AliveCheckerRejects) using the existing NewTmuxSessionWithServerSocket-style isolation (InstanceOptions.TmuxServerSocket, threaded through a new CommitImportParams.TmuxServerSocket field), reusing instance_cold_restore_test.go's coldRestoreSocket(t) helper. These tests previously hit the shared default tmux server — which is single-threaded and contends with every other test doing real tmux operations under CI's full parallel -race suite. TestCommitImportExternalSession_PersistsAndLinksAndSuspends_When_StartAndSuspendSucceed's CI failure ("cold start: tmux dead" → 10s of failed tmux list-sessions calls → timeout) is exactly this contention pattern, already documented as a known flake class in this codebase's own session_service_program_test.go/server_integration_test.go comments.

Test plan

  • go build ./server/services/... ./gen/... ./session/... — clean
  • go test -race ./session — full package passes (fresh, go clean -testcache first)
  • go clean -testcache && go test -race ./session -run TestCommitImportExternalSession -v — all 7 subtests pass individually
  • cd web-app && npx jest --testPathPatterns="ImportPreviewDialog|ConfirmKillDialog|ImportSessionsContainer|ImportExternalSessionsPanel" — 4/4 suites, 27/27 tests pass
  • npx tsc --noEmit — clean
  • actionlint .github/workflows/lint.yml .github/workflows/generated-proto-guard.yml — clean
  • make proto-gen from a clean state regenerates all files identically, confirmed via git status showing no diff against the gitignored paths

🤖 Generated with Claude Code

https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx

@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 120 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 15 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 46 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 8 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 8 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 14 feature files to /tmp/tmp.SveAs4WuQf/backend
Wrote 6 feature files to /tmp/tmp.SveAs4WuQf/backend

=== Backend Registry Diff ===
Committed: 193  Generated: 193  Divergence: 0.0%
⚠️  110 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 46/193 features have testIds (23.8%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Go Benchmarks (Tier 1)

benchmarks/go/tier1-baseline.txt:98: missing iteration count
benchmarks/go/tier1-baseline.txt:198: missing iteration count
goos: linux
goarch: amd64
pkg: github.com/tstapler/stapler-squad/session
cpu: AMD EPYC 9V74 80-Core Processor                
                                            │ benchmarks/go/tier1-baseline.txt │
                                            │              sec/op              │
CircularBufferWrite_4KB-4                                          81.06n ± 2%
CircularBufferWrite_4KB_Allocs-4                                   79.81n ± 1%
CircularBufferGetRecent_4KB-4                                      529.5n ± 2%
CircularBufferGetAll-4                                             3.958µ ± 2%
GetTimeSinceLastMeaningfulOutput_HotPath-4                         70.18n ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                        34.52n ± 0%
geomean                                                            179.0n

                                            │ benchmarks/go/tier1-baseline.txt │
                                            │               B/op               │
CircularBufferWrite_4KB-4                                         0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%
CircularBufferGetRecent_4KB-4                                   4.000Ki ± 0%
CircularBufferGetAll-4                                          40.00Ki ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%
geomean                                                                      ¹
¹ summaries must be >0 to compute geomean

                                            │ benchmarks/go/tier1-baseline.txt │
                                            │            allocs/op             │
CircularBufferWrite_4KB-4                                         0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%
CircularBufferGetRecent_4KB-4                                     1.000 ± 0%
CircularBufferGetAll-4                                            1.000 ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%
geomean                                                                      ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/s                │
CircularBufferWrite_4KB-4                           47.06Gi ± 1%
CircularBufferGetRecent_4KB-4                       7.205Gi ± 2%
geomean                                             18.41Gi

pkg: github.com/tstapler/stapler-squad/session/detection/ratelimit
cpu: AMD EPYC 7763 64-Core Processor                
                              │ tier1-bench.txt │
                              │     sec/op      │
StripANSI_PlainText-4               6.869n ± 0%
StripANSI_WithEscapes-4             744.4n ± 0%
ProcessOutput_InactiveState-4       6.365n ± 0%
geomean                             31.93n

                              │ tier1-bench.txt │
                              │      B/op       │
StripANSI_PlainText-4              0.000 ± 0%
StripANSI_WithEscapes-4            136.0 ± 0%
ProcessOutput_InactiveState-4      0.000 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

                              │ tier1-bench.txt │
                              │    allocs/op    │
StripANSI_PlainText-4              0.000 ± 0%
StripANSI_WithEscapes-4            5.000 ± 0%
ProcessOutput_InactiveState-4      0.000 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
StripANSI_PlainText-4                                7.043n ± 6%
StripANSI_WithEscapes-4                              654.1n ± 8%
ProcessOutput_InactiveState-4                        6.764n ± 2%
geomean                                              31.47n

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/op               │
StripANSI_PlainText-4                               0.000 ± 0%
StripANSI_WithEscapes-4                             136.0 ± 0%
ProcessOutput_InactiveState-4                       0.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │            allocs/op             │
StripANSI_PlainText-4                               0.000 ± 0%
StripANSI_WithEscapes-4                             5.000 ± 0%
ProcessOutput_InactiveState-4                       0.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/queue
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
ReviewQueue_ConcurrentReads-4                       82.82n ± 24%
ReviewQueue_Add-4                                   502.1n ±  2%
geomean                                             203.9n

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/op               │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%
ReviewQueue_Add-4                                   640.0 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │            allocs/op             │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%
ReviewQueue_Add-4                                   4.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/scrollback
cpu: AMD EPYC 7763 64-Core Processor                
                                      │ tier1-bench.txt │
                                      │     sec/op      │
CircularBuffer_ConcurrentReadWrite-4        3.885µ ± 2%
CircularBuffer_BurstAppend-4                102.0µ ± 1%
CircularBuffer_GetLastN_LargeBuffer-4       19.86µ ± 2%
CircularBuffer_GetRange_Sequential-4        10.94µ ± 4%
CircularBufferAppend-4                      97.89n ± 0%
CircularBufferGetLastN-4                    2.165µ ± 1%
CircularBufferConcurrentAppend-4            127.1n ± 1%
geomean                                     3.025µ

                                      │ tier1-bench.txt │
                                      │      B/op       │
CircularBuffer_ConcurrentReadWrite-4       6.062Ki ± 0%
CircularBuffer_BurstAppend-4               62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4      56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4       28.00Ki ± 0%
CircularBufferAppend-4                       24.00 ± 0%
CircularBufferGetLastN-4                   6.000Ki ± 0%
CircularBufferConcurrentAppend-4             32.00 ± 0%
geomean                                    3.077Ki

                                      │ tier1-bench.txt │
                                      │    allocs/op    │
CircularBuffer_ConcurrentReadWrite-4         2.000 ± 0%
CircularBuffer_BurstAppend-4                1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4        1.000 ± 0%
CircularBuffer_GetRange_Sequential-4         1.000 ± 0%
CircularBufferAppend-4                       1.000 ± 0%
CircularBufferGetLastN-4                     1.000 ± 0%
CircularBufferConcurrentAppend-4             1.000 ± 0%
geomean                                      2.962

                             │ tier1-bench.txt │
                             │       B/s       │
CircularBuffer_BurstAppend-4      598.6Mi ± 1%

cpu: AMD EPYC 9V74 80-Core Processor                
                                      │ benchmarks/go/tier1-baseline.txt │
                                      │              sec/op              │
CircularBuffer_ConcurrentReadWrite-4                         3.482µ ± 2%
CircularBuffer_BurstAppend-4                                 106.5µ ± 1%
CircularBuffer_GetLastN_LargeBuffer-4                        20.03µ ± 0%
CircularBuffer_GetRange_Sequential-4                         10.65µ ± 3%
CircularBufferAppend-4                                       103.3n ± 1%
CircularBufferGetLastN-4                                     2.327µ ± 2%
CircularBufferConcurrentAppend-4                             137.0n ± 0%
geomean                                                      3.076µ

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │               B/op               │
CircularBuffer_ConcurrentReadWrite-4                        6.062Ki ± 0%
CircularBuffer_BurstAppend-4                                62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                       56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4                        28.00Ki ± 0%
CircularBufferAppend-4                                        24.00 ± 0%
CircularBufferGetLastN-4                                    6.000Ki ± 0%
CircularBufferConcurrentAppend-4                              32.00 ± 0%
geomean                                                     3.077Ki

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │            allocs/op             │
CircularBuffer_ConcurrentReadWrite-4                          2.000 ± 0%
CircularBuffer_BurstAppend-4                                 1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                         1.000 ± 0%
CircularBuffer_GetRange_Sequential-4                          1.000 ± 0%
CircularBufferAppend-4                                        1.000 ± 0%
CircularBufferGetLastN-4                                      1.000 ± 0%
CircularBufferConcurrentAppend-4                              1.000 ± 0%
geomean                                                       2.962

                             │ benchmarks/go/tier1-baseline.txt │
                             │               B/s                │
CircularBuffer_BurstAppend-4                       572.9Mi ± 2%

pkg: github.com/tstapler/stapler-squad/session/tmux
cpu: AMD EPYC 7763 64-Core Processor                
                             │ tier1-bench.txt │
                             │     sec/op      │
StripANSICodes_PlainText-4         6.877n ± 1%
StripANSICodes_WithEscapes-4       691.9n ± 0%
IsBanner_PlainText-4               473.2n ± 0%
geomean                            131.1n

                             │ tier1-bench.txt │
                             │      B/op       │
StripANSICodes_PlainText-4        0.000 ± 0%
StripANSICodes_WithEscapes-4      56.00 ± 0%
IsBanner_PlainText-4              0.000 ± 0%
geomean                                      ¹
¹ summaries must be >0 to compute geomean

                             │ tier1-bench.txt │
                             │    allocs/op    │
StripANSICodes_PlainText-4        0.000 ± 0%
StripANSICodes_WithEscapes-4      4.000 ± 0%
IsBanner_PlainText-4              0.000 ± 0%
geomean                                      ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                             │ benchmarks/go/tier1-baseline.txt │
                             │              sec/op              │
StripANSICodes_PlainText-4                         7.185n ±  3%
StripANSICodes_WithEscapes-4                       623.5n ±  1%
IsBanner_PlainText-4                               469.2n ± 27%
geomean                                            128.1n

                             │ benchmarks/go/tier1-baseline.txt │
                             │               B/op               │
StripANSICodes_PlainText-4                         0.000 ± 0%
StripANSICodes_WithEscapes-4                       56.00 ± 0%
IsBanner_PlainText-4                               0.000 ± 0%
geomean                                                       ¹
¹ summaries must be >0 to compute geomean

                             │ benchmarks/go/tier1-baseline.txt │
                             │            allocs/op             │
StripANSICodes_PlainText-4                         0.000 ± 0%
StripANSICodes_WithEscapes-4                       4.000 ± 0%
IsBanner_PlainText-4                               0.000 ± 0%
geomean                                                       ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/tokens
cpu: AMD EPYC 7763 64-Core Processor                
                                   │ tier1-bench.txt │
                                   │     sec/op      │
TokenParser_ProcessUserEntry-4           5.159m ± 1%
DetectCommandsInText/NoSlash-4           7.488n ± 0%
DetectCommandsInText/WithCommand-4       1.659µ ± 0%
geomean                                  4.002µ

                                   │ tier1-bench.txt │
                                   │      B/op       │
TokenParser_ProcessUserEntry-4        1.060Mi ± 4%
DetectCommandsInText/NoSlash-4          0.000 ± 0%
DetectCommandsInText/WithCommand-4      433.0 ± 0%
geomean                                            ¹
¹ summaries must be >0 to compute geomean

                                   │ tier1-bench.txt │
                                   │    allocs/op    │
TokenParser_ProcessUserEntry-4          33.00 ± 0%
DetectCommandsInText/NoSlash-4          0.000 ± 0%
DetectCommandsInText/WithCommand-4      6.000 ± 0%
geomean                                            ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                                   │ benchmarks/go/tier1-baseline.txt │
                                   │              sec/op              │
TokenParser_ProcessUserEntry-4                            4.774m ± 2%
DetectCommandsInText/NoSlash-4                            6.660n ± 2%
DetectCommandsInText/WithCommand-4                        1.493µ ± 0%
geomean                                                   3.621µ

                                   │ benchmarks/go/tier1-baseline.txt │
                                   │               B/op               │
TokenParser_ProcessUserEntry-4                         1.037Mi ± 2%
DetectCommandsInText/NoSlash-4                           0.000 ± 0%
DetectCommandsInText/WithCommand-4                       433.0 ± 0%
geomean                                                             ¹
¹ summaries must be >0 to compute geomean

                                   │ benchmarks/go/tier1-baseline.txt │
                                   │            allocs/op             │
TokenParser_ProcessUserEntry-4                           33.00 ± 0%
DetectCommandsInText/NoSlash-4                           0.000 ± 0%
DetectCommandsInText/WithCommand-4                       6.000 ± 0%
geomean                                                             ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/unfinished
                               │ benchmarks/go/tier1-baseline.txt │
                               │              sec/op              │
DiffShortstat/GitVCSReader-4                          3.390m ± 1%
DiffShortstat/GoGitVCSReader-4                        80.94n ± 0%
DiffShortstatCached-4                                 80.59n ± 1%
geomean                                               2.807µ

                               │ benchmarks/go/tier1-baseline.txt │
                               │               B/op               │
DiffShortstat/GitVCSReader-4                       62.58Ki ± 0%
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%
DiffShortstatCached-4                                0.000 ± 0%
geomean                                                         ¹
¹ summaries must be >0 to compute geomean

                               │ benchmarks/go/tier1-baseline.txt │
                               │            allocs/op             │
DiffShortstat/GitVCSReader-4                         360.0 ± 0%
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%
DiffShortstatCached-4                                0.000 ± 0%
geomean                                                         ¹
¹ summaries must be >0 to compute geomean

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

UX Analysis

Check Status Details
✅ Axe Core (WCAG 2.1 AA) success Critical/serious violations block merge
⚠️ Lighthouse Performance Score: unknown Warning if < 70 (non-blocking)
🤖 Claude UX Analysis Advisory See docs/qa/ for findings

Axe Core excludes terminal rendering areas (intentional design).
Lighthouse runs in desktop preset for this developer tool.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 8ms (▼ faster +0.0%; baseline: 8ms)
list-sessions-total-mean: 14ms (▲ slower +10.8%; baseline: 13ms)

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 14 KB/s ▼ -11.8% (baseline: 16 KB/s)
terminal-throughput-p50: 16 KB/s ▼ -0.7% (baseline: 16 KB/s)

@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 120 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 15 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 46 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 8 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 8 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 14 feature files to /tmp/tmp.Lx2oNurIyA/backend
Wrote 6 feature files to /tmp/tmp.Lx2oNurIyA/backend

=== Backend Registry Diff ===
Committed: 193  Generated: 193  Divergence: 0.0%
⚠️  110 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 46/193 features have testIds (23.8%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@tstapler

Copy link
Copy Markdown
Owner Author

Test job status: pre-existing flake, not caused by this PR's changes

Investigated the Test (Go, -race) failure on this PR's latest run: TestCommitImportExternalSession_PersistsAndLinksAndSuspends_When_StartAndSuspendSucceed still fails with cold restore Start failed for 'imported-0': timed out waiting for tmux session staplersquad_imported-0, even with both commits on this branch (proto bindings + the CI-ordering fix).

This confirms it is not related to the missing-proto-bindings root cause this PR fixes:

  • Both this PR's own local run and mine passed 5/5 locally without -race.
  • It only reproduces under CI's exact -race invocation with the pinned tmux 3.4 binary — tmux list-sessions returns exit status 1 continuously for the full 10s sessionCreateTimeout window (session/tmux/tmux.go:197), suggesting the tmux session (running program: "true", which exits instantly) is being reaped before remain-on-exit takes effect, specifically under -race's slower scheduling + CI resource contention.
  • EnsureServerRunning (session/tmux/tmux.go:515) only applies the server-wide remain-on-exit on default the first time it starts a given tmux server socket (session/tmux/tmux.go:544) — worth checking whether this test's server socket is shared with an earlier test in the same run that already passed that point, or whether there's a genuine ordering race between session creation and that option taking effect.

This is a distinct root cause from AC0–AC6 in backlog item 41cca909Recommend filing it as its own backlog item scoped to session/tmux session-creation timing under -race, rather than blocking this PR on it — consistent with .claude/rules/fix-flaky-tests-dont-defer.md's exception for when a fix would expand blast radius beyond the current change (this would mean touching core tmux session-lifecycle code shared by every session, without being able to reproduce and verify a fix locally).

@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 120 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 15 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 46 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 8 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 8 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 14 feature files to /tmp/tmp.qwRS3pMeZT/backend
Wrote 6 feature files to /tmp/tmp.qwRS3pMeZT/backend

=== Backend Registry Diff ===
Committed: 193  Generated: 193  Divergence: 0.0%
⚠️  110 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 46/193 features have testIds (23.8%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 120 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 15 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 46 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 8 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 8 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 14 feature files to /tmp/tmp.D0S6Mf0lwZ/backend
Wrote 6 feature files to /tmp/tmp.D0S6Mf0lwZ/backend

=== Backend Registry Diff ===
Committed: 193  Generated: 193  Divergence: 0.0%
⚠️  110 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 46/193 features have testIds (23.8%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@tstapler

Copy link
Copy Markdown
Owner Author

Status update

Confirmed fixed (this run, commit ba18e0d):

  • lintpass (was failing on 4 Jest suites; ordering fix + missing files both landed)
  • no-checked-in-generated-protos (new guard) — pass
  • no-backlog-scaffolding-in-diff, Registry Validation, all Build (*) matrix jobs — pass

Still failing, unrelated to this PR's diff (confirmed transient GitHub infra, not this repo's code):

  • Benchmarks/E2E RPC Latencybuf binary download returned a 503 HTML error page instead of the binary (syntax error near unexpected token '<' trying to execute it as a script). Pure download flake.
  • The Lint failure on the previous push (14b4598, now superseded) was golangci-lint's own binary download hitting a 503 the same way.

Still failing, investigated but not resolved — Test: TestCommitImportExternalSession_PersistsAndLinksAndSuspends_When_StartAndSuspendSucceed. Applied two real fixes (per-test tmux server socket isolation via the existing NewTmuxSessionWithServerSocket pattern, then a CI-only timeout widen from 10s→30s) — verified both are correctly wired (30.17s failure duration confirms the override is read) and neither fully resolves it: the test now fails consistently for the entire 30s window with zero successful tmux list-sessions calls, not an occasional near-miss. That pattern doesn't match "occasionally too slow under load" — it looks like the isolated tmux server may not be coming up at all in this specific CI environment, which I can't diagnose further without direct shell access to a CI runner (can't reproduce locally, including under -race with the full package suite). Leaving both changes in (they're safe/env-gated, no production behavior change, and do narrow the problem) but this specific test's CI failure needs someone with CI runner access to continue — noting on backlog item 41cca909.

tstapler added a commit that referenced this pull request Aug 12, 2026
Diagnostic-only change for the still-unresolved TestCommitImportExternalSession
CI flake (see PR #445 comments): DoesSessionExistNoCache's failure log only
ever included the generic Go error ("exit status 1"), never tmux's own
stderr text -- the one piece of evidence that would distinguish "server
never came up" from some other failure mode. Also logs immediately after
a successful `new-session` command, including its stderr, since the
CI failure's wrapped err is <nil> (new-session itself reports success)
while the subsequent list-sessions poll never finds it for the entire
timeout window -- a pattern that already reproduced identically before
this PR's socket-isolation and timeout changes, meaning neither of
those addressed the actual root cause. This commit exists purely to get
the missing evidence on the next CI run; expect a fast follow-up once
the actual tmux error text is visible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
tstapler added a commit that referenced this pull request Aug 12, 2026
… with fast-exiting programs

Root cause of TestCommitImportExternalSession_PersistsAndLinksAndSuspends_
When_StartAndSuspendSucceed's CI flake, found via the diagnostic logging
added in the previous commit. CI evidence (PR #445):

  new-session command succeeded  session=... serverSocket=test_coldrestore_...
  DoesSessionExistNoCache: ...   sessions=[""]                (not visible yet)
  DoesSessionExistNoCache: ...   output="no server running on <socket>"
  [repeats for the entire poll window -- never recovers]

`new-session -d` reports success, but every subsequent list-sessions call
says the SERVER isn't running at all -- not "session not found yet", the
whole server is gone. This test's candidate uses Program: "true", which
exits in microseconds. t.setRemainOnExit() (which prevents tmux's default
behavior of destroying a pane/window/session when its program exits) was
only ever called AFTER Start()'s poll loop confirms the session exists --
but "true" can already have exited and torn down the session (and, since
it was the server's only session on a freshly-isolated socket, the server
itself) before that point is ever reached. This race is always present,
not new to this PR's socket-isolation work: it likely also explains the
identical failure signature seen pre-isolation, on the shared default
socket's first-ever invocation.

Fix: set remain-on-exit as a server-wide default (`set-option -g`) BEFORE
the new-session command, not after. This command's own invocation safely
spins up a sessionless server if none exists for this socket yet (a zero-
session server doesn't exit-on-empty until it's HAD a session), so a
server that has to be freshly created for this call already has the
option active from its very first session -- before any program, however
fast, gets a chance to run. Best-effort (log + continue on failure),
matching every other non-essential tmux option set in this function.

No behavior change for the common case: every session already ends up
with remain-on-exit on via the existing (unchanged) later call -- this
only moves that same eventual state earlier, closing the window rather
than changing the outcome.

Verified: go build clean; session/tmux and session packages pass under
-race (45s / 62s); 10 consecutive runs of the previously-flaky test pass
under `taskset -c 0 GOMAXPROCS=1` (single-core constrained, closer to a
GitHub Actions runner's profile than this sandbox's 24 cores).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
@tstapler
tstapler marked this pull request as ready for review August 12, 2026 21:12
@tstapler

Copy link
Copy Markdown
Owner Author

Root cause found and fixed — `Test` now passes

Dug into this properly instead of leaving it as an open flake. Root cause, confirmed empirically (not guesswork):

The bug: `TestCommitImportExternalSession_PersistsAndLinksAndSuspends_When_StartAndSuspendSucceed`'s candidate uses `Program: "true"`, which exits in microseconds. `remain-on-exit` (which stops tmux from destroying a session the instant its program exits) was only ever set after `Start()`'s poll loop confirmed the session existed — but on a brand-new isolated tmux server, tmux's `exit-empty` option (default `on`) kills a server the moment it reaches zero sessions. So the sequence was: `new-session` succeeds → `true` exits almost instantly → session destroyed → server (now empty) exits → every subsequent `list-sessions` poll reports "no server running", for the entire timeout window. This was never a cross-test contention issue (the previous socket-isolation and timeout-widening commits, while reasonable engineering, didn't address the actual cause) — it's a startup race present on any brand-new tmux server, isolated or shared, that usually wins locally (fast machine) and loses under CI's slower syscalls.

First fix attempt failed too, and told us why: tried `set-option -g remain-on-exit on` as a separate command before `new-session`. It itself failed with "error connecting to <socket>" — `set-option` (unlike `new-session`) doesn't implicitly start a server. Re-ran in CI, same failure, but the new diagnostic logging (added specifically to catch this) revealed exactly why.

Actual fix: chain `start-server ; set-option -g exit-empty off ; set-option -g remain-on-exit on` into one tmux invocation before creating the session. Manually reproduced both the failure and the fix with raw `tmux` invocations before touching Go code — confirmed a server survives its own zero-session startup window only when `exit-empty`/`remain-on-exit` are set in the same invocation that starts it; two separate invocations lose the server in between every time.

Verified:

  • The specific test now runs in ~0.08-0.1s locally (down from 1.66s) — hits the fast existence-check path immediately instead of falling through the 5-retry poll loop
  • 20/20 consecutive runs pass under `taskset -c 0 GOMAXPROCS=1` (single-core constrained)
  • Full `session`/`session/tmux` packages pass under `-race`
  • CI confirms it: this PR's `Test` job (which was the one consistently failing) now passes — run 31642746180

All jobs on this PR are now green (build matrix, install check, web-build smoke test, and Test). Lint dispatched separately to confirm on the final commit.

tstapler and others added 7 commits August 12, 2026 14:58
Jest ran before proto generation, so a PR that forgets to commit its
generated proto bindings (as #433 did) passes lint's Jest step locally
against stale/missing gen/ output instead of failing fast at the same
step that would have caught it — the exact gap that let main's Jest job
go red without any single CI step surfacing "these files are missing."

Reordering closes AC6 of backlog item 41cca909 (main branch CI red):
buf generate proto now runs first, so any future gap between committed
generated files and a fresh regen fails Jest module resolution in CI
immediately rather than silently reaching main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ng, isolate tmux-flaky import tests

Three related fixes, all surfaced by the same incident (see this PR's
first commit and #444's comments):

1. git rm --cached every currently-tracked file under gen/proto/go/
   and web-app/src/gen/. These paths are gitignored, but historically
   force-added (git add -f) as an inconsistent, undocumented practice
   -- confirmed no ADR or written policy exists for this. Every make
   target that consumes generated code (build/test/lint) already
   depends on proto-gen; committing generated output on top of that
   is redundant and creates exactly the drift risk that broke #433
   (an incomplete/stale force-add went unnoticed).

2. Fix .github/workflows/lint.yml: the Jest step ran BEFORE the
   "Generate protobuf code" step. This only ever "worked" because
   generated files happened to be pre-committed -- once (1) removes
   that crutch, the step order bug becomes a hard failure instead of
   a latent one, so it must be fixed in the same commit. Moved
   protobuf/ent codegen before Jest, golangci-lint, the import-cycle
   check, and the feature-catalog validation step -- matching the
   correct order already used by .github/actions/prepare/action.yml
   and documented in build.yml's #144 comment.

3. Add .github/workflows/generated-proto-guard.yml: a hard CI
   backstop (matching backlog-scaffolding-guard.yml's established
   pattern) that fails any PR whose diff adds/modifies a file under
   gen/ or web-app/src/gen/, regardless of how it got there. This has
   happened more than once, including via AI-agent-driven commits
   that git add -f a generated file to make a local build pass --
   this makes the mistake structurally impossible to merge instead of
   relying on review catching it.

4. Isolate the three session/import_commit_test.go tests that reach a
   real instance.Start() (PersistsAndLinksAndSuspends,
   CompensatingDeletesInstance, ReturnsError_When_AliveCheckerRejects)
   via the existing NewTmuxSessionWithServerSocket-style isolation
   (InstanceOptions.TmuxServerSocket, threaded through a new
   CommitImportParams.TmuxServerSocket field), reusing
   instance_cold_restore_test.go's coldRestoreSocket(t) helper. These
   tests previously hit the shared default tmux server, which is
   single-threaded and contends with every other test doing real tmux
   operations under CI's full parallel -race suite --
   TestCommitImportExternalSession_PersistsAndLinksAndSuspends_
   When_StartAndSuspendSucceed's CI failure ("cold start: tmux dead"
   -> 10s of failed tmux list-sessions calls -> timeout) is exactly
   this contention pattern, already documented as a known class of
   flake in this codebase's own session_service_program_test.go and
   server_integration_test.go comments.

Verified: go build clean, full `session` package passes under -race
(67s), all 4 previously-broken Jest suites pass (27/27),
actionlint clean on both changed/new workflow files, `make proto-gen`
regenerates all files identically from a clean state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
…dable)

The socket-isolation fix (previous commit) removed cross-test tmux
contention, but TestCommitImportExternalSession_PersistsAndLinksAndSuspends_
When_StartAndSuspendSucceed still failed deterministically twice in the
same CI run (Makefile's coverage-then-verbose-rerun fallback) with the
same "timed out waiting for tmux session" error -- a fresh, isolated
tmux -L server still has to fork and become responsive within
sessionCreateTimeout, and a fully CPU-saturated CI runner (every
package's -race suite running concurrently) can push that past 10s on
pure scheduling delay, independent of any lock/socket contention.

sessionCreateTimeout is now a var, computed once from the optional
STAPLER_SQUAD_TMUX_CREATE_TIMEOUT_SECONDS env var (unset/invalid ->
unchanged 10s default -- zero production behavior change). Set to 30s
in build.yml's "Run tests with coverage" step only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
Diagnostic-only change for the still-unresolved TestCommitImportExternalSession
CI flake (see PR #445 comments): DoesSessionExistNoCache's failure log only
ever included the generic Go error ("exit status 1"), never tmux's own
stderr text -- the one piece of evidence that would distinguish "server
never came up" from some other failure mode. Also logs immediately after
a successful `new-session` command, including its stderr, since the
CI failure's wrapped err is <nil> (new-session itself reports success)
while the subsequent list-sessions poll never finds it for the entire
timeout window -- a pattern that already reproduced identically before
this PR's socket-isolation and timeout changes, meaning neither of
those addressed the actual root cause. This commit exists purely to get
the missing evidence on the next CI run; expect a fast follow-up once
the actual tmux error text is visible.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
… with fast-exiting programs

Root cause of TestCommitImportExternalSession_PersistsAndLinksAndSuspends_
When_StartAndSuspendSucceed's CI flake, found via the diagnostic logging
added in the previous commit. CI evidence (PR #445):

  new-session command succeeded  session=... serverSocket=test_coldrestore_...
  DoesSessionExistNoCache: ...   sessions=[""]                (not visible yet)
  DoesSessionExistNoCache: ...   output="no server running on <socket>"
  [repeats for the entire poll window -- never recovers]

`new-session -d` reports success, but every subsequent list-sessions call
says the SERVER isn't running at all -- not "session not found yet", the
whole server is gone. This test's candidate uses Program: "true", which
exits in microseconds. t.setRemainOnExit() (which prevents tmux's default
behavior of destroying a pane/window/session when its program exits) was
only ever called AFTER Start()'s poll loop confirms the session exists --
but "true" can already have exited and torn down the session (and, since
it was the server's only session on a freshly-isolated socket, the server
itself) before that point is ever reached. This race is always present,
not new to this PR's socket-isolation work: it likely also explains the
identical failure signature seen pre-isolation, on the shared default
socket's first-ever invocation.

Fix: set remain-on-exit as a server-wide default (`set-option -g`) BEFORE
the new-session command, not after. This command's own invocation safely
spins up a sessionless server if none exists for this socket yet (a zero-
session server doesn't exit-on-empty until it's HAD a session), so a
server that has to be freshly created for this call already has the
option active from its very first session -- before any program, however
fast, gets a chance to run. Best-effort (log + continue on failure),
matching every other non-essential tmux option set in this function.

No behavior change for the common case: every session already ends up
with remain-on-exit on via the existing (unchanged) later call -- this
only moves that same eventual state earlier, closing the window rather
than changing the outcome.

Verified: go build clean; session/tmux and session packages pass under
-race (45s / 62s); 10 consecutive runs of the previously-flaky test pass
under `taskset -c 0 GOMAXPROCS=1` (single-core constrained, closer to a
GitHub Actions runner's profile than this sandbox's 24 cores).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
…one invocation

Previous commit's fix (separate set-option -g remain-on-exit before
new-session) did not actually work -- confirmed by re-running in CI,
which failed identically, plus a new log line proving why: the
set-option command itself failed with "error connecting to <socket>"
on a brand-new socket, because set-option (unlike new-session) does
not implicitly start a server.

Root cause, now fully nailed down empirically (manual `tmux -L <socket>
start-server` / `set-option` reproduction, not guesswork): a tmux
server that reaches zero sessions exits almost instantly by default
(the `exit-empty` option defaults to on). `start-server` alone
succeeds, but the server it starts is already gone by the time ANY
subsequent, separate tmux invocation connects to check or configure
it -- there is no window to run a second command against the same
server unless it's chained into the SAME invocation.

Fix: `tmux -L <socket> start-server \; set-option -g exit-empty off \;
set-option -g remain-on-exit on` as one chained command (tmux's own
`;` command-separator syntax, parsed by tmux from distinct argv
elements -- no shell is involved via exec.Cmd, so no escaping needed).
This keeps the server alive through its own zero-session startup
window (exit-empty off) AND protects the session new-session is about
to create from being destroyed the instant a fast-exiting program
(e.g. Program="true") exits (remain-on-exit on) -- both options are
active before any program ever gets a chance to run.

Verified empirically before touching the test suite: manually
reproduced the exact failure (start-server succeeds, very next
`set-option` invocation reports "no server running") and the exact
fix (chained invocation keeps the session visible after a `true`
program exits) via raw tmux invocations, matching buildTmuxCommand's
plain-argv (no-shell) construction exactly.

Verified in Go: TestCommitImportExternalSession_PersistsAndLinksAndSuspends_
When_StartAndSuspendSucceed now completes in ~0.08-0.1s (down from
1.66s) -- it hits the fast existence-check path immediately after
creation instead of falling through the 5-retry poll loop, meaning the
session is now reliably visible right away, not eventually. 20/20
consecutive runs pass under `taskset -c 0 GOMAXPROCS=1 -count=20`.
Full session and session/tmux packages pass under -race (61s / 45s).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SW6Nbaqim2AS5knaHVY5dx
t.start() was already at cognitive complexity 48 (over the gocognit
threshold of 40) on main before this branch's tmux race-condition fix
added ~1 more point. Pulling the new start-server/set-option logic into
preconfigureServerBeforeSession() keeps start() at its pre-existing 48
(verified via gocognit) instead of nudging it to 49, so the lint
complexity gate doesn't flag this PR's diff for pre-existing debt it
didn't introduce.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RetjyvJVU24VieQ3GUMJqm
@tstapler
tstapler force-pushed the fix/import-session-ci-breakage branch from 60f9183 to 6a2bb49 Compare August 12, 2026 21:59
@github-actions

Copy link
Copy Markdown
Contributor

📊 Feature E2E Coverage

Feature coverage report unavailable

Run make e2e-report locally to view the full Allure report.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 120 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 15 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 46 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 8 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 8 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 14 feature files to /tmp/tmp.wSFCEdr4iI/backend
Wrote 6 feature files to /tmp/tmp.wSFCEdr4iI/backend

=== Backend Registry Diff ===
Committed: 193  Generated: 193  Divergence: 0.0%
⚠️  110 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 46/193 features have testIds (23.8%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@tstapler
tstapler merged commit ad72dec into main Aug 13, 2026
29 of 33 checks passed
tstapler added a commit that referenced this pull request Aug 25, 2026
…Session's failure detection

Adversarial code review of PR #595 correctly flagged that
startServerSucceededDespiteError only checks "is a server running", so a
start-server success followed by a failing trailing set-option in the
chained preconfigure command is silently treated as full success with no
log at all -- worse than the prior best-effort code, which always logged a
warning on any failure.

Splitting the chain to detect this precisely was considered and rejected:
a separate start-server + set-option invocation reintroduces the exact
race PR #445 fixed (a zero-session server can exit before the second call
reaches it), and exit-empty/remain-on-exit are built-in global options that
essentially cannot fail on a server confirmed running, so this is an
accepted, documented tradeoff rather than a functional change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tstapler added a commit that referenced this pull request Aug 25, 2026
…Session's failure detection (#629)

* perf(tmux): add registry-gated fast path to DoesSessionExistNoCache

Mirrors the existing TmuxServerRegistry.SessionExists()/IsHealthy()
trust-asymmetric pattern already used by DoesSessionExist(): a
healthy registry's positive answer is authoritative and skips the
subprocess call, while a false/unhealthy registry always falls
through to the subprocess check, preserving this function's
always-fresh, no-false-negatives contract.

* refactor(tmux): extract shared registry fast-path helper and close test coverage gap

DoesSessionExist and DoesSessionExistNoCache each inlined the identical
"registry confirms session exists" fast-path check. Extract it into
registryConfirmsExists() so the logic and its "false is never trusted"
caveat live in one place. Also add
TestDoesSessionExist_FallsBackWhenRegistrySaysFalse, mirroring the
NoCache variant's existing coverage for this case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(gogitstore): bound buildPackedFixtureOnce with a whole-attempt timeout

Root cause: git gc -q --aggressive can wedge under CI/-race contention.
gitRunErr's own per-call retry (5x30-90s) only bounds a single git
invocation, not the whole buildPackedFixtureOnce attempt, so a
consistently-wedged gc could consume 5*90s=450s in ONE attempt with no
outer bound — 3 such attempts blew past go test's 10-minute default,
producing the observed TestRegistry_Prune_should_neverEvict_When_RefCountNonzero
10-minute hang (sync.Once blocks every caller requesting that numCommits
fixture, not just the first).

- Add gitRunErrCtx as the common base for gitRunErr/gitRunErrWithTimeout,
  taking an explicit parent context so a per-call timeout can never
  outlive an outer deadline it's called under (context.WithTimeout fires
  at whichever deadline is earlier).
- Add fixtureBuildTimeout (3m) and wrap each buildPackedFixtureOnce
  attempt in its own context.WithTimeout, tracking whether any attempt
  actually timed out for a clearer final error message.
- Add -c gc.autoDetach=false to the explicit gc call, belt-and-suspenders
  alongside the existing gc.auto=0/maintenance.auto=false.

Verified: go test ./session/unfinished/gogitstore/... -race -v passes in
579s (under the 10m default, no -timeout flag) across all 46 tests,
including a run where the gc call actually wedged, retried, hit
fixtureBuildTimeout, and successfully rebuilt from scratch on the next
attempt rather than hanging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(tests): resolve flaky test failures across services, session, and headless packages

Root-causes fixed:
- server/services/slack_notifier_test.go: remove t.Parallel() from a test that
  calls slog.SetDefault, which races with other parallel tests reading the
  global logger.
- server/services/session_service_create_test.go: reorder baseDir/HOME/
  explicitPath setup before service construction so the explicit-path test
  doesn't race the scratch-dir generation.
- session/git/worktree_ops.go, session/headless/caller.go,
  session/session_creation_test.go, session/tmux/control_mode_refcount_test.go,
  server/services/path_completion_service.go,
  server/services/backlog_service_events.go: structural fixes for
  tmux-server-startup contention and related flakes surfaced under make ci.
- .github/workflows/build.yml: CI adjustments supporting the above.

Verified with go test ./server/services -v (clean PASS, no FAIL) after
memory-pressure-induced kills were ruled out as environmental, not code
regressions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: restore -p 1 CI serialization and root-cause worktree_ops retry bug

Root-cause fixes from PR #595 adversarial review instead of margin-widening:

- .github/workflows/build.yml: restore -p 1 test-binary serialization
  (removed by mistake) and the ADR-001 guardrail comment explaining why.
  Its removal caused -race CPU contention that had been masked by widening
  unrelated timeouts elsewhere.
- server/services/path_completion_service.go + test: revert
  listWorktreesTimeout 20s->5s now that -p 1 removes the contention that
  motivated widening it.
- session/session_creation_test.go: revert require.Less bounds widened
  5s->20s/10s->30s back to their original values for the same reason.
- session/git/worktree_ops.go: fix retry loop that discarded its own
  branchRefExists() result and always fell through to
  setupFromExistingBranch(), masking real git lock/corruption errors.
  Now returns the original error when the branch was never confirmed
  to exist.
- server/services/autonomous_orchestration_service_test.go: remove
  t.Parallel() from captureLogs and its three callers — it swaps the
  process-global slog.Default() logger, so a parallel sibling logging
  during that window is a real data race under -race.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(review): address Gate 2 code review findings for PR 595

- session/git: extract the lock-race retry loop into retryBranchRefExists
  with named constants (lockRaceRetryAttempts/Delay), fix it to sleep only
  between attempts, and add deterministic unit tests driving the retry/
  give-up paths directly instead of relying on real git-subprocess timing
- session/headless: use bytes.NewReader instead of strings.NewReader(string(data))
  to avoid an unnecessary buffer copy when decoding JSON
- server/services: add a test proving testAfterSubscribeHook is isolated
  per *events.EventBus and doesn't leak across concurrent bus instances
- trim two over-long doc comments to their load-bearing "why"
- session/unfinished/gogitstore: add a deterministic test proving the
  parent-context preemption mechanism fixtureBuildTimeout relies on
  actually fires before a wedged subprocess's own per-call timeout

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(tests): retry tmux server preconfigure and widen timing assertions under parallel load

preconfigureServerBeforeSession() silently swallowed failures and let start()
proceed into new-session regardless; wrap it in the existing
ensureServerRunningWithRetry machinery so it retries like the rest of the
file. Widen three hardcoded 5s/10s session-creation timing assertions to
15s/20s to account for legitimate retry/backoff time when sibling
t.Parallel() subtests contend for CPU.

Root-caused per .claude/rules/fix-flaky-tests-dont-defer.md rather than
re-excused as a known flake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: remove -p 1 serialization for session/session-mux/session-tmux tests

Root-caused the flake that justified -p 1: TestBuildClaudeCommand_LargePromptUsesTempFileNotInline
overrode promptArg's background cleanup delay to 10ms even though it doesn't test cleanup
timing, racing its own os.ReadFile against that goroutine under CPU/scheduler contention.
Letting it use the real 30s default delay removes the race. Also widened the sibling
cleanup-timing test's polling deadline (2s -> 10s) to absorb the same contention.

Validated with a clean -race run of ./session ./session/mux ./session/tmux together, no -p 1
(session 172.3s, session/mux 6.5s, session/tmux 55.1s, zero FAILs), so the blanket -p 1 in
build.yml's tmux test invocation is no longer needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(headless): update stale CallBlocking call site to current signature

pool_test.go's first test was calling the pre-FeatureKey/CostSink CallBlocking
signature (3 return values, no sink arg) while every other test in the file
already matched the current one — go build doesn't compile _test.go files, so
this only surfaced via golangci-lint's typecheck pass in CI.

* fix(lint): gofmt session/comprehensive_session_creation_test.go

Picked up unformatted from an origin/main merge; CI's PR-changed-files
gofmt check caught it.

* fix(server/services): use per-test unique remote names to prevent SSH pool collision

TestTestRemoteConnection_ReportsMismatch_NotHostKeyUnknown_When_TrustedKeyChanged
flaked intermittently in CI because several tests shared literal remote names
("test-remote", "draft-remote"), colliding in tmux's process-wide
defaultSSHClientPool (keyed only by SSHTarget.Name, never torn down on last
Release). A prior test's successful dial could leave a live pooled entry that
a later test reused via the pool's fast path, skipping HostKeyCallback and
thus the host-key-mismatch check entirely.

Add uniqueRemoteName(t) and use it in every test that performs a live dial
through TestRemoteConnection/TrustRemoteHostKey, so no two tests can ever
share a pool entry regardless of execution order or async eviction timing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(session/tmux): document accepted gap in preconfigureServerBeforeSession's failure detection

Adversarial code review of PR #595 correctly flagged that
startServerSucceededDespiteError only checks "is a server running", so a
start-server success followed by a failing trailing set-option in the
chained preconfigure command is silently treated as full success with no
log at all -- worse than the prior best-effort code, which always logged a
warning on any failure.

Splitting the chain to detect this precisely was considered and rejected:
a separate start-server + set-option invocation reintroduces the exact
race PR #445 fixed (a zero-session server can exit before the second call
reaches it), and exit-empty/remain-on-exit are built-in global options that
essentially cannot fail on a server confirmed running, so this is an
accepted, documented tradeoff rather than a functional change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@tstapler
tstapler deleted the fix/import-session-ci-breakage branch August 29, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant