Skip to content

fix(backlog): sync PR branch with main before respawning a fix session#163

Merged
tstapler merged 3 commits into
mainfrom
worktree-agent-a0878367c2523521d
Jul 17, 2026
Merged

fix(backlog): sync PR branch with main before respawning a fix session#163
tstapler merged 3 commits into
mainfrom
worktree-agent-a0878367c2523521d

Conversation

@tstapler

@tstapler tstapler commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • AutoReopenForPRFix now proactively merges main into the PR's branch before respawning a fix session, instead of only describing the CI failure in a text note.
  • A clean merge is pushed straight back to the PR's branch on origin; a conflicting merge is aborted (leaving the worktree clean) and the conflicting files are folded into the fix context; an already-synced branch is a silent no-op.

Context

ReconcilePRPending detects CI failures, blocking reviews, and merge conflicts on pr_pending items and calls AutoReopenForPRFix to respawn an autonomous fix session. Previously this only told the respawned session about the failure via a text note — it never verified the branch was actually in sync with main. For CI failures caused by drift (a fix landing on main after the branch was created, or the branch simply going stale) this wasted the respawned session's effort diagnosing something a merge would resolve immediately, and left the PR exposed to the same "drifted from main until it hit a hard conflict" pattern that produced PR #157. The fix makes the sync a programmatic guarantee rather than a prompt instruction, per this codebase's established pattern (see docs/tasks/backlog-feature-improvement.md).

Changes

  • session/git/ops.go: new MergeMainIntoWorktree(worktreePath, mainBranch) — fetches and merges mainBranch, reporting UpToDate / Merged / Conflicted. Up-to-date detection compares HEAD before/after the merge (not merge-output text matching, which is locale/git-version fragile). On conflict it aborts the merge (git merge --abort) so the worktree is always left clean, and reports the conflicting file paths. Non-conflict merge failures (e.g. uncommitted local changes) and fetch failures are propagated as errors rather than misreported as a result state.
  • server/services/backlog_service_triage.go: new BacklogService.syncPRBranchWithMain, called from AutoReopenForPRFix before building the fix context. Finds the worktree behind the currently open PR (most recent completed work ItemSession), merges main into it, and pushes the merge when it brings in new commits. Best-effort — any failure is logged and swallowed, never blocking the fix spawn. The push-failure note gives an explicit git -C <repoPath> push origin <branch> command against the shared repo checkout (not the worktree, which gets cleaned up once the new fix session is spawned) since worktree cleanup never deletes the underlying branch ref.
  • Tests: session/git/ops_test.go (5 tests: up-to-date, clean merge, conflict+abort, fetch failure, non-conflict merge failure) and 7 tests in server/services/backlog_service_triage_test.go covering AutoReopenForPRFix's sync-and-push, push-failure, conflict-folds-into-context, sync-fetch-failure-swallowed, and up-to-date no-op paths end to end.

Impact

  • Scope: session/git and the backlog PR-fix respawn path (server/services/backlog_service_triage.go). Does not touch session/unfinished/gogitstore/ or the VCS widget — those are owned by other in-flight work.
  • Breaking Changes: none — additive, best-effort step ahead of an existing code path.
  • Performance: adds one fetch + merge (and occasionally a push) per PR-fix respawn; negligible relative to spawning a new session. See Known Limitations for a follow-up on bounding worst-case latency.
  • Dependencies: none new.

Testing

  • go test ./session/git/... (5 MergeMainIntoWorktree tests: up-to-date, clean merge, conflict, fetch failure, non-conflict merge failure)
  • go test ./server/services/... (full package, including 7 new AutoReopenForPRFix sync tests)
  • go test ./session/... (full package)
  • golangci-lint run ./session/git/... ./server/services/...
  • go vet ./session/git/... ./server/services/..., gofmt -l on all changed files

Reviewer Notes

  • Focus areas: syncPRBranchWithMain's worktree-lookup (findMostRecentSessions + GetWorktreeDataBySessionUUID) and the abort-on-conflict behavior in MergeMainIntoWorktree.
  • Known limitations (both pre-existing in this codebase, not introduced by this diff, and out of scope for this bounded fix — flagged here for transparency and tracked as follow-ups):
    1. SpawnSessionFromItem's reopen path always creates a brand-new branch/worktree off the shared repo's local HEAD (buildRevisionTitle appends -rN; see resolveSessionPath/CreateBacklogWorktree) rather than continuing the branch this PR just merged+pushed, and pushAndCreatePR's "reuse existing PR" check never verifies the new branch matches the tracked PR's head. This diff's sync still has real, standalone value — it keeps the currently open PR in sync with main on GitHub immediately (which can resolve or shrink a main-drift-caused CI failure before the new session even starts) — but for CI failures caused by the PR's own diff (the common case), the actual fix commits land on an unrelated branch. Full resolution requires redesigning branch continuity across reopen iterations, which is shared with AutoReopenAfterFailedReview and deserves its own investigation rather than a rushed change here.
    2. The sync step (fetch + merge + push) has no shared timeout ceiling and runs synchronously inside ReconcilePRPending's sequential per-item loop — worst case (~210s: 60s fetch + 60s merge + 30s abort + 60s push) could stall that tick if the remote is slow/hung. Bounded, not unbounded, and matches the existing timeout style used elsewhere in this file; a shared context.Context deadline threaded through MergeMainIntoWorktree would tighten this but wasn't done here to avoid a larger signature change under review-cycle time pressure.
    3. The branch to sync against is hardcoded as "main" (this repo's convention); no fallback to master/other default branches.

…a fix session

AutoReopenForPRFix respawns an autonomous session whenever ReconcilePRPending
detects a CI failure, blocking review, or merge conflict on a pr_pending item,
but it only ever told the new session about the failure via a text note — it
never actually resynced the branch with main first. A CI failure caused by
drift from main (a fix landing on main after the PR's branch was created, or
the branch simply going stale) wasted the respawned session's effort
diagnosing something a merge would have fixed outright, and left the PR
exposed to the same "developed a hard conflict with nobody proactively
resyncing it" pattern that produced PR #157.

AutoReopenForPRFix now merges main into the worktree behind the currently
open PR (found via the most recent completed work ItemSession) before
building the fix context:
  - a clean merge is pushed straight to the PR's branch on origin, so the
    live PR is resynced even before the new session starts;
  - a merge conflict is aborted immediately (via `git merge --abort`, leaving
    the worktree clean) and the conflicting file paths are folded into the
    fix context so resolving them against main becomes part of the fix;
  - an already-up-to-date branch is a silent no-op.

Adds session/git.MergeMainIntoWorktree (fetch + merge + conflict abort/report,
following the existing session/git/ops.go conventions) and
BacklogService.syncPRBranchWithMain, wired in as a best-effort step ahead of
the existing respawn — any sync failure is logged and swallowed, never
blocking the fix spawn.

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

Copy link
Copy Markdown
Contributor

✅ Registry Validation

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

Building backend scanner...
Scanning backend features...
Wrote 116 feature files to /tmp/tmp.HZKI0zSbjy/backend
Wrote 14 feature files to /tmp/tmp.HZKI0zSbjy/backend
Wrote 38 feature files to /tmp/tmp.HZKI0zSbjy/backend
Wrote 6 feature files to /tmp/tmp.HZKI0zSbjy/backend
Wrote 10 feature files to /tmp/tmp.HZKI0zSbjy/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 8/172 features have testIds (4.7%)

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

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 8ms (▲ slower +41.1%; baseline: 6ms)
list-sessions-total-mean: 10ms (▲ slower +25.0%; baseline: 8ms)

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Go Benchmarks (Tier 1)

benchmarks/go/tier1-baseline.txt:98: missing iteration count
benchmarks/go/tier1-baseline.txt:199: missing iteration count
tier1-bench.txt:98: missing iteration count
tier1-bench.txt:198: missing iteration count
goos: linux
goarch: amd64
pkg: github.com/tstapler/stapler-squad/session
cpu: AMD EPYC 7763 64-Core Processor                
                                            │ tier1-bench.txt │
                                            │     sec/op      │
CircularBufferWrite_4KB-4                         83.17n ± 2%
CircularBufferWrite_4KB_Allocs-4                  83.72n ± 3%
CircularBufferGetRecent_4KB-4                     485.6n ± 1%
CircularBufferGetAll-4                            3.764µ ± 2%
GetTimeSinceLastMeaningfulOutput_HotPath-4        65.72n ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4       33.07n ± 0%
geomean                                           173.9n

                                            │ tier1-bench.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

                                            │ tier1-bench.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

                              │ tier1-bench.txt │
                              │       B/s       │
CircularBufferWrite_4KB-4          45.87Gi ± 1%
CircularBufferGetRecent_4KB-4      7.855Gi ± 1%
geomean                            18.98Gi

cpu: AMD EPYC 9V74 80-Core Processor                
                                            │ benchmarks/go/tier1-baseline.txt │
                                            │              sec/op              │
CircularBufferWrite_4KB-4                                          62.90n ± 0%
CircularBufferWrite_4KB_Allocs-4                                   62.25n ± 0%
CircularBufferGetRecent_4KB-4                                      459.4n ± 2%
CircularBufferGetAll-4                                             3.196µ ± 2%
GetTimeSinceLastMeaningfulOutput_HotPath-4                         54.41n ± 1%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                        26.77n ± 1%
geomean                                                            142.5n

                                            │ 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                           60.65Gi ± 1%
CircularBufferGetRecent_4KB-4                       8.304Gi ± 2%
geomean                                             22.44Gi

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.867n ± 0%
StripANSI_WithEscapes-4             742.2n ± 0%
ProcessOutput_InactiveState-4       6.321n ± 0%
geomean                             31.82n

                              │ 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                                5.481n ± 3%
StripANSI_WithEscapes-4                              513.1n ± 1%
ProcessOutput_InactiveState-4                        5.127n ± 0%
geomean                                              24.34n

                              │ 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
cpu: AMD EPYC 7763 64-Core Processor                
                              │ tier1-bench.txt │
                              │     sec/op      │
ReviewQueue_ConcurrentReads-4       90.61n ± 6%
ReviewQueue_Add-4                   509.2n ± 3%
geomean                             214.8n

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

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

cpu: AMD EPYC 9V74 80-Core Processor                
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
ReviewQueue_ConcurrentReads-4                        63.90n ± 0%
ReviewQueue_Add-4                                    396.2n ± 2%
geomean                                              159.1n

                              │ 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.710µ ± 1%
CircularBuffer_BurstAppend-4                101.2µ ± 0%
CircularBuffer_GetLastN_LargeBuffer-4       19.69µ ± 0%
CircularBuffer_GetRange_Sequential-4        10.68µ ± 1%
CircularBufferAppend-4                      97.88n ± 1%
CircularBufferGetLastN-4                    2.237µ ± 1%
CircularBufferConcurrentAppend-4            123.4n ± 1%
geomean                                     2.990µ

                                      │ 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      602.8Mi ± 1%

cpu: AMD EPYC 9V74 80-Core Processor                
                                      │ benchmarks/go/tier1-baseline.txt │
                                      │              sec/op              │
CircularBuffer_ConcurrentReadWrite-4                        2.819µ ±  3%
CircularBuffer_BurstAppend-4                                83.06µ ±  1%
CircularBuffer_GetLastN_LargeBuffer-4                       15.79µ ±  1%
CircularBuffer_GetRange_Sequential-4                        11.61µ ± 13%
CircularBufferAppend-4                                      84.30n ±  1%
CircularBufferGetLastN-4                                    2.019µ ±  3%
CircularBufferConcurrentAppend-4                            106.6n ±  1%
geomean                                                     2.588µ

                                      │ 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                       734.9Mi ± 1%

pkg: github.com/tstapler/stapler-squad/session/tmux
cpu: AMD EPYC 7763 64-Core Processor                
                             │ tier1-bench.txt │
                             │     sec/op      │
StripANSICodes_PlainText-4         6.868n ± 2%
StripANSICodes_WithEscapes-4       691.5n ± 0%
IsBanner_PlainText-4               485.1n ± 0%
geomean                            132.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                          5.737n ± 0%
StripANSICodes_WithEscapes-4                        476.5n ± 1%
IsBanner_PlainText-4                                363.5n ± 1%
geomean                                             99.79n

                             │ 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.274m ± 1%
DetectCommandsInText/NoSlash-4           7.494n ± 0%
DetectCommandsInText/WithCommand-4       1.736µ ± 1%
geomean                                  4.094µ

                                   │ tier1-bench.txt │
                                   │      B/op       │
TokenParser_ProcessUserEntry-4        11.02Mi ± 0%
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          34.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.412m ± 1%
DetectCommandsInText/NoSlash-4                            5.183n ± 5%
DetectCommandsInText/WithCommand-4                        1.171µ ± 1%
geomean                                                   2.992µ

                                   │ benchmarks/go/tier1-baseline.txt │
                                   │               B/op               │
TokenParser_ProcessUserEntry-4                         11.02Mi ± 0%
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                           34.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
cpu: AMD EPYC 7763 64-Core Processor                
                               │ tier1-bench.txt │
                               │     sec/op      │
DiffShortstat/GitVCSReader-4         3.074m ± 0%
DiffShortstat/GoGitVCSReader-4       76.82n ± 0%
DiffShortstatCached-4                76.58n ± 0%
geomean                              2.625µ

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

                               │ tier1-bench.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

cpu: AMD EPYC 9V74 80-Core Processor                
                               │ benchmarks/go/tier1-baseline.txt │
                               │              sec/op              │
DiffShortstat/GitVCSReader-4                          2.687m ± 2%
DiffShortstat/GoGitVCSReader-4                        62.82n ± 2%
DiffShortstatCached-4                                 64.21n ± 1%
geomean                                               2.213µ

                               │ benchmarks/go/tier1-baseline.txt │
                               │               B/op               │
DiffShortstat/GitVCSReader-4                       56.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 Jul 17, 2026

Copy link
Copy Markdown
Contributor

🎬 E2E Feature Demos

2 shard(s) recorded feature flows for this PR.

recordings shard 1
recordings shard 2

Demo preview opens directly in browser (single-file HTML). Raw WebM recordings in ZIP. Expires after 30 days.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 16 KB/s ▲ +0.6% (baseline: 16 KB/s)
terminal-throughput-p50: 16 KB/s ▼ -1.4% (baseline: 16 KB/s)

Code review on the branch-sync fix (PR #163) surfaced two MAJOR issues, now
fixed, plus test-coverage gaps, now filled:

- session/git/ops.go: MergeMainIntoWorktree detected an up-to-date merge by
  substring-matching git's merge output ("Already up to date"), which is
  locale- and git-version-fragile (older git prints "Already up-to-date.").
  Now compares HEAD before/after the merge via the package's existing
  getHeadCommitSHA helper instead.

- backlog_service_triage.go: the push-failure note told the next fix session
  to "push it before continuing," but that session gets a brand-new worktree
  (SpawnSessionFromItem always creates one fresh on reopen), not the one the
  merge landed in — the instruction wasn't actionable from there. The note
  now names the branch and gives an explicit `git -C <repoPath> push origin
  <branch>` command against the shared repo checkout, whose branch ref
  survives worktree cleanup.

- Added missing coverage: fetch failure and non-conflict merge failure in
  session/git/ops_test.go; push failure after a successful merge and a
  swallowed sync-fetch failure in backlog_service_triage_test.go.

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

Copy link
Copy Markdown
Contributor

✅ Registry Validation

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

Building backend scanner...
Scanning backend features...
Wrote 116 feature files to /tmp/tmp.o6HWnKpBfh/backend
Wrote 14 feature files to /tmp/tmp.o6HWnKpBfh/backend
Wrote 38 feature files to /tmp/tmp.o6HWnKpBfh/backend
Wrote 6 feature files to /tmp/tmp.o6HWnKpBfh/backend
Wrote 10 feature files to /tmp/tmp.o6HWnKpBfh/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 8/172 features have testIds (4.7%)

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 116 feature files to /tmp/tmp.XownGGUX7u/backend
Wrote 14 feature files to /tmp/tmp.XownGGUX7u/backend
Wrote 38 feature files to /tmp/tmp.XownGGUX7u/backend
Wrote 6 feature files to /tmp/tmp.XownGGUX7u/backend
Wrote 10 feature files to /tmp/tmp.XownGGUX7u/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 8/172 features have testIds (4.7%)

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

@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.

@tstapler
tstapler merged commit eb5c3bd into main Jul 17, 2026
22 checks passed
@tstapler
tstapler deleted the worktree-agent-a0878367c2523521d branch July 17, 2026 11:35
tstapler added a commit that referenced this pull request Jul 26, 2026
…tor gap (#163)

* fix(terminal): correctly scan OSC/DCS escape sequences to stop render artifacts

stripANSIBytes and sanitizeUTF8Bytes treated any ASCII letter as the end
of an escape sequence. That's only true for CSI (ESC[...letter); OSC
(ESC]...BEL or ESC\) and DCS/PM/APC/SOS (ESC{P,^,_,X}...ESC\ or 0x9C)
terminate differently, and their payloads (window titles, hyperlink
URLs, shell-integration marks) almost always contain a letter before
the real terminator. Claude Code's newer renderer emits more of these
OSC sequences, so their payload tails were leaking through as literal
text in the web terminal and throwing off cursor-column math.

Add a shared scanEscapeSequence helper (mirrors the correct boundary
logic already used by pkg/analytics/escape_code_parser.go) and rewire
both duplicated stripANSIBytes definitions plus sanitizeUTF8Bytes to
consume whole sequences atomically instead of stopping at the first
letter.

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

* fix(terminal): widen CSI final-byte range to 0x40-0x7E, cap OSC/DCS scan size

Code review on PR #156 found that the new scanCSI only accepted A-Z/a-z
as CSI terminators, missing real ECMA-48-valid final bytes like '@'
(0x40, Insert Character) and '~' (0x7E, used by many real xterm
sequences e.g. function/navigation keys). Confirmed empirically:
stripANSIBytes("\x1b[5@Hello") leaked "@hello" instead of "Hello" —
the exact bug class this PR exists to eliminate, just for a different
final byte. Widened to the full 0x40-0x7E range and aligned the
malformed-CSI fallback with pkg/analytics' semantics (give up and
consume only the ESC, rather than swallowing partially-scanned params).

Also:
- Widened pkg/analytics/escape_code_parser.go's parseCSI terminator
  range to match (it was cited as the reference implementation for
  this fix but had the same narrower gap for '~' and other non-letter
  finals in 0x5B-0x60/0x7B-0x7E).
- Added a size cap (mirroring escape_code_parser.go's existing 65536
  bound) to scanUntilTerminator so an unterminated/adversarial OSC or
  DCS payload can't force an unbounded scan.
- Pre-size the bytes.Buffer in stripANSIBytes/sanitizeUTF8Bytes with
  Grow(len(b)) to avoid reallocation growth in this hot path.
- Removed the now-vestigial (*StateGenerator).stripANSIBytes wrapper
  method now that its only caller can use the shared free function
  directly.
- Added regression tests for all of the above, including a mid-buffer
  (start > 0) case and the new size-cap behavior.

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

* refactor: remove dead MOSH-style terminal state-sync infrastructure

Investigating the terminal-artifacts report (PR #156) surfaced that
server/terminal (the package that PR fixed) has zero callers anywhere
in the repo — it's pre-integration code for a "state" streaming mode
that was designed but never wired into the live rendering path. Tracing
further found this same abandoned effort spans three separate, never-
fully-connected implementations, plus the client-side code built to
consume them:

- server/terminal (StateGenerator/DeltaGenerator) — zero callers.
- server/ssp (Mosh-inspired Coordinator) + session/framebuffer (its
  diff engine) — zero callers.
- session/terminal_state.go (regex-based TerminalState) — only called
  from SessionService.StreamTerminal, which browsers never reach: the
  WebSocket bridge (connectrpc_websocket.go) intercepts the same RPC
  procedure and reimplements it with plain raw-byte passthrough.
- server/compression (LZMA) — only exercised by transport_e2e_test.go,
  itself a spec-as-test for streaming modes the server never actually
  implements (its own docstring says so).
- Frontend: StateApplicator/DeltaApplicator/EchoOverlay/lzma.ts and the
  predictive-echo/SSP-negotiation logic in useTerminalFlowControl.ts —
  all gated behind `sspNegotiated`, which never becomes true since the
  server never sends an SSPNegotiation message. Confirmed sendInput
  (the working path) is always used in practice, not sendInputWithEcho.
- The streaming-mode selector (config field, proto field, dropdown UI)
  that drove all of the above: the value was accepted and logged but
  never branched on by the code that actually sends output.

Simplified SessionService.StreamTerminal to plain raw output (removing
its session.TerminalState dependency) rather than deleting it outright,
since the RPC interface still requires an implementation for any future
non-browser Connect client. Also widened the same CSI final-byte gap
(missing '@'/'~' etc.) in pkg/analytics/escape_code_parser.go that
PR #156 fixed in server/terminal, since it was cited as that fix's
reference implementation and had the identical bug.

Left ADR-002 (which documented the abandoned "state" mode decision for
external sessions) in place with its status marked Superseded, rather
than deleting it, to preserve the historical record.

Verification: go build/vet, full Go test suite, npx tsc --noEmit, and
the full Jest suite (2776 tests) all pass. go mod tidy dropped the now-
unused github.com/ulikunitz/xz dependency.

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

* fix(terminal): resolve CSI terminator gap and code-review follow-ups

BUG-025: widen the CSI final-byte character class from [a-zA-Z] to
[@-~] (0x40-0x7E per ECMA-48) everywhere it was copy-pasted narrow,
so sequences terminating on '@' or '~' no longer leak raw escape
bytes past rate-limit/activity detection and tmux banner filtering.

Consolidates the fix into one shared, tested module per language
(pkg/ansi for Go, web-app/src/lib/terminal/stripAnsi.ts for
TypeScript) instead of five independently duplicated regexes, per
code-review findings. Also reserves and deletes the orphaned MOSH
oneof message types left behind in events.proto by the earlier
dead-code removal.

Adding real end-to-end test coverage for StreamTerminal's simplified
output path surfaced two pre-existing concurrency bugs under -race,
now fixed:
- the output goroutine could race Connect's own stream-close write
  since nothing joined it before the handler returned
- session.Instance.Started() read the started field without its
  lock while start() wrote it outside the lock

Also adds a test pinning the WebSocket-vs-general-handler routing
precedence for the StreamTerminal path, previously only documented
in a comment.

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

* fix(terminal): close residual races flagged by PR #163 code review

A /github:pr-ship code-review gate on PR #163 (4 parallel agents:
testing, code quality, architecture, security) found that both
concurrency fixes from the previous commit were real but incomplete:

- StreamTerminal's output goroutine was bounded-and-abandoned on
  timeout rather than actually interrupted, so it could still call
  stream.Send() after the handler returned if the PTY read blocked
  past the 2s bound. Fixed by dup'ing the PTY fd so this goroutine
  gets its own independent *os.File/poll.FD, safe to set a real
  250ms read deadline on without racing the instance's other PTY
  consumers (response stream, command executor) that share the
  original fd.

- session.Instance.started was fixed at exactly the two sites this
  session's new test exercised, leaving ~30 other unguarded read/write
  sites across the package equally racy. Converted the field to
  atomic.Bool everywhere instead, which is race-free by construction
  rather than by per-site lock-discipline audit.

Also: added a deterministic unit test for waitWithTimeout's timeout
branch (previously only indirectly exercised), and exported
ansi.IsCSIFinalByte as a byte-range twin to CSIFinalByteClass so
pkg/analytics/escape_code_parser.go's manual byte-scanning CSI check
(a 6th independent reimplementation the prior consolidation missed)
now derives from the same shared source of truth.

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

* fix(session): close remaining actor/i.mu synchronization gaps post-merge

Follow-up to the main-merge commit: these edits were made while chasing
-race failures during conflict resolution but landed after that commit's
git add, so they weren't included in it.

- Instance.Preview, InstanceStatusManager.GetStatus: read Status via
  Snapshot() instead of a bare/RLock-guarded field read, matching the
  pattern already used by GetStatus/IsActive/etc. Both were caught by
  go test ./... -race (transitionToLocked writes i.Status outside i.mu).
- pkg/ansi: tighten csiRegex/csiFinalByteMin/csiFinalByteMax to unexported
  now that no caller outside the package needs them directly.
- instance_fork_test.go, instance_workspace_test.go: fix two `started:`
  struct-literal fields / bare assignments that go vet flagged after the
  bool -> atomic.Bool conversion.
- instance_timestamp_test.go: wrap the test instance in a LiveInstance so
  TestInstance_TimestampConcurrency's concurrent UpdateTerminalTimestamps
  calls are actually actor-serialized, instead of silently taking the
  no-actor synchronous fallback (send()'s documented behavior when
  liveInstance is nil) with zero cross-goroutine synchronization.
- instance_test.go: TestFromInstanceDataWithMissingWorktree now uses
  ForceStatus instead of a bare `instance.Status = Paused` write, so the
  Paused() check after it doesn't read a stale cached Snapshot().
- instance_serialization.go: ToInstanceData now builds its snapshot via
  sendSyncErr instead of reading the cached Snapshot(). Storage.UpdateInstance
  and SaveInstancesSync mutate Instance fields (Tags, Category) directly and
  expect the next ToInstanceData() call to see it; Snapshot()'s cache only
  refreshes when an actor command republishes it, so those callers were
  reading stale data (TestStorage_UpdateInstance, TestStorage_SaveInstancesSync).
  sendSyncErr gets a fresh buildSnapshot(s.inst) while still serializing
  against any concurrently-running actor commands.
- instance_approval.go: simplify snap.ReviewState.TimeSince* to snap.TimeSince*
  (staticcheck QF1008 — ReviewState is embedded in InstanceSnapshot).
- session_service_stream_terminal_test.go: wire session.NewRegistry +
  SetRegistry, matching server/dependencies.go's production wiring, so
  CreateSession actually wraps the instance in a LiveInstance during the test.

go test ./... -race: 0 data races (verified across multiple full-suite runs).

* fix(server): make PTY fd duplication cross-platform for windows/amd64 build

syscall.Dup (used by StreamTerminal to give its reader goroutine an
independent PTY fd) doesn't exist on Windows, breaking the release build
matrix (windows/amd64) added by .github/workflows/build.yml — the failure
first surfaced on PR #163's post-merge CI run, caused fail-fast cancellation
of the whole Build matrix.

Move the dup behind a dupPTYFile helper, split by build tag like the
existing execSyscall precedent (exec_unix.go/exec_windows.go): syscall.Dup
on Unix, windows.DuplicateHandle (golang.org/x/sys/windows, already a
direct dependency) on Windows. Verified go build ./... for
darwin/{amd64,arm64}, linux/{amd64,arm64}, and windows/amd64.

Also investigated two other CI failures from the same run:
- TestActorNoLeak/TestActorStopIdempotent (goleak "unexpected goroutines"
  from TestMain's own background helpers) — could not reproduce locally
  (isolated run or full `go test ./session -race`), consistent with a
  CI-timing-sensitive flake in this newly-merged upstream test, not
  something introduced here.
- A TmuxSession.ptmx data race (GetPTY's unguarded read racing
  closePTYAndAttachCmd's unguarded close/reconnect) reproduced locally
  under -race -count=15 on TestStreamTerminal_SendsRawOutput. Pre-existing
  in session/tmux (t.ptmx is read/written without synchronization in ~15
  call sites in tmux.go); fixing it properly needs a TmuxSession-level
  mutex refactor, out of scope for this merge-conflict PR. Flagged as a
  known follow-up, not fixed here.

* fix(session): stop TestActorNoLeak/TestActorStopIdempotent flaking on unrelated leaks

Reproduced the CI-only failure (2/2 remote runs) locally by constraining
GOMAXPROCS=2, which changes goroutine scheduling enough to surface it — a
plain -race/-count run on this fast dev machine never hit it.

Two contributing issues, both fixed:

1. TestApprove_FromHibernated_Succeeds and TestApprove_AllSourceStatuses's
   Hibernated->Active case call Approve(), which (via transitionToLocked)
   dispatches resumeFromHibernationLocked's real Start()/StartController()/
   StartSessionDriver() work in a background goroutine. StartSessionDriver's
   ticker loop has no external cancellation and runs for driverTotalTimeout
   (tens of minutes) — these tests never stop it, so it leaks for the rest
   of the test binary's life. Fixed by pre-setting driverRunning (the same
   CAS guard TestStartSessionDriver_Idempotent already relies on) so
   StartSessionDriver is a no-op; the tests aren't exercising driver
   dispatch, just the state transition.

2. The deeper issue: TestActorNoLeak/TestActorStopIdempotent used a bare
   process-wide goleak.VerifyNone(), which asserts "no goroutine is alive
   anywhere in the process" rather than these tests' actual claim ("Stop()
   joins the goroutine THIS test started"). In the large session package's
   shared test binary, ANY earlier, unrelated test that leaves its own
   background goroutine running (a ResponseStream poller, an exec.Cmd wait,
   etc.) trips this on unrelated grounds, nondeterministically depending on
   run order and scheduling — confirmed locally: after fixing #1, a -count=8
   stress run still surfaced two more pre-existing unrelated leaks
   (MangleCorrelator.StartEviction, os.Process.pidWait) before either test
   assertion even reached the actor-specific logic. Auditing and fixing
   every leak source across this large, pre-existing test package is a
   separate, much larger effort - fixed the actual fragility instead:
   goleak.IgnoreCurrent() baselines the goroutine set before the actor work,
   so pre-existing goroutines from other tests are excluded and only a
   genuine actor-goroutine leak fails the test.

Verified: GOMAXPROCS=2 go test ./session -race -v -count=8 — 0 failures
(was reproducing on every run before this fix).

* fix(scanner): register 3 missing backlog GitHub RPCs in methodToID

tools/scanner's TestMethodToIDCompleteness failed in CI (Run scanner unit
tests step of PR #163's Test job): SearchGitHubRepos, ListGitHubIssues, and
ImportGitHubIssue exist in backlog.proto (and have real, if currently
unimplemented/stubbed, server handlers - see the separate
TestSearchGitHubRepos_*/TestListGitHubIssues_* skips) but were never added
to proto_scanner.go's methodToID feature-registry map. Landed via the same
upstream fork-sync merge as the actor-pattern refactor; nothing in this PR
added these RPCs, just the first CI run to reach this check since the merge.

Ran make registry-generate to regenerate the per-feature JSON files this
map drives (docs/registry/features/backend/backlog/{search-github-repos,
list-github-issues,import-github-issue}.json), which also cleaned up a
stale duplicate (top-level DeleteBacklogItem.json, superseded by the
already-committed backlog/delete-item.json from PR #159's merge - unrelated
to this fix, just picked up by the same regenerate pass). registry-diff
divergence back to 0.0%.

* fix(scanner): keep GitHub RPC feature IDs stable to avoid a registry-diff false positive

Follow-up to 94ce8333f: mapping SearchGitHubRepos/ListGitHubIssues/
ImportGitHubIssue to new kebab-case backlog:* IDs relocated their registry
files (docs/registry/features/backend/backlog/*.json), which two things
then treated as brand-new, untested RPCs:

- The "Check new RPCs have tests" CI gate diffs registry files against
  origin/main by path; a relocated file is indistinguishable from a
  genuinely new one there.
- SearchGitHubRepos and ListGitHubIssues already have real, committed tests
  (server/services/backlog_github_rpc_test.go) and tested:true/testIds on
  origin/main under their old top-level path/id — relocating them would
  have discarded that (loadExisting only preserves tested/testIds/
  lastModified when the output *path* is unchanged).

Fixed by mapping these three to their own method name (matching
ScanProto's pre-existing fallback when methodToID has no entry), so the
generated id/path is byte-identical to what's already committed. Restored
SearchGitHubRepos.json/ListGitHubIssues.json/ImportGitHubIssue.json from
origin/main and re-ran make registry-generate: markerFound correctly
recalculates to false (no // +api: marker exists — these RPCs are still
unimplemented stubs, that part hasn't changed), while tested/testIds/
lastModified are preserved via loadExisting's existing-file logic.
ImportGitHubIssue.json (tested:false on both branches, no marker either
side) comes out byte-identical to origin/main; the other two only change
markerFound (true, which was stale, to false) and lastModified, with
tested:true and their real testIds untouched - so neither trips the "new
RPC has no test" per-file check (tested is true, and ImportGitHubIssue
doesn't show up as changed in the origin/main diff at all).

registry-diff: 0.0% divergence. cd tools/scanner && go test ./...: pass.

* chore(tests): untrack tests/e2e/node_modules and fix flaky accessibility waits

node_modules under tests/e2e was accidentally committed since it wasn't
covered by .gitignore. Also swap networkidle waits in accessibility.spec.ts
for element-based waits, since this app polls continuously and network
activity never settles for the required window (matches alias.spec.ts).

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

* fix: include tests/e2e/node_modules/ in .gitignore (missed in prior commit)

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

* fix: bump otel past CVE-2026-41178, fix PreviewFullHistory race, refresh stale docs

- security: bump go.opentelemetry.io/otel, /sdk, /trace from v1.43.0 to v1.44.0.
  v1.43.0 falls inside CVE-2026-41178's affected range (baggage-header-parsing DoS);
  the app registers the baggage propagator globally and otelhttp wraps the network-
  exposed HTTP server as the outermost layer, ahead of auth middleware. go mod tidy
  confirmed otelhttp v0.67.0 and otlptracegrpc v1.39.0 remain compatible with the
  bumped otel core, so those were left untouched; golang.org/x/sys picked up a
  transitive bump to v0.45.0. go mod tidy also dropped an unused gvisor.dev/gvisor
  indirect require (checklocks is installed via `go install ...@latest` in the
  Makefile, not consumed as a module dependency).

- fix(session): PreviewFullHistory() read i.Status directly, unsynchronized with
  transitionToLocked's writes (which happen inside the actor's own serialization,
  not under i.mu) - the same hazard already fixed on Preview() a few lines above.
  Extracted the shared check into a new previewBlocked() helper used by both
  Preview() and PreviewFullHistory() so the two can't diverge again.

- docs(session): updated ReviewState's file-header and UpdateTimestamps doc
  comments, which still described the old "protected by Instance.mu" discipline.
  UpdateTerminalTimestamps (the sole call path) now runs inside the actor's
  i.send() closure instead of holding i.mu; the comments now describe that.

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

---------

Co-authored-by: Tyler Stapler <tystapler@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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