Skip to content

fix(backlog): self-heal already-tracked scaffolding files, add CI backstop#195

Merged
tstapler merged 1 commit into
mainfrom
worktree-agent-ab35fa4f2d7f57286
Jul 20, 2026
Merged

fix(backlog): self-heal already-tracked scaffolding files, add CI backstop#195
tstapler merged 1 commit into
mainfrom
worktree-agent-ab35fa4f2d7f57286

Conversation

@tstapler

Copy link
Copy Markdown
Owner

Summary

Fixes a chronic, confirmed bug: .backlog-context.md and .claude/commands/backlog/*.md — scaffolding files stapler-squad writes into every backlog work-session worktree — keep getting committed to target repos despite being "gitignored." This repo's own history has ~20 manual chore(backlog): untrack backlog context/command files commits, and two currently-open PRs (#169, #170) each carry an incidental deletion of a stale, committed .backlog-context.md left over from an unrelated item's context.

Root cause

WriteBacklogContextFile/WriteSlashCommands (session/backlog_commands.go) write scaffolding files, then call addWorktreeExcludes to add patterns to $GIT_DIR/info/exclude. That — and a tracked .gitignore entry — only stop new files from being staged by a broad git add/git commit -a. Neither does anything once a file is already tracked in the branch's history, which is exactly what a long chain of manual "untrack" commits has been working around.

What changed

1. Self-healing untrack (session/backlog_commands.go)

WriteSlashCommands and WriteBacklogContextFile now call selfHealWorktreeScaffolding before writing. It opens the worktree's git index directly via go-git (repo.Storer.Index() / idx.Remove() / repo.Storer.SetIndex() — no subshell, per .claude/rules/prefer-go-git-over-subshells.md), finds any entry matching backlogExcludePatterns, and untracks it (git-rm-cached semantics: index entry removed, working-tree file left alone). A branch that ever got one of these files committed — however it happened — self-heals the very next time a session is spawned, reattached, or reopened on it, closing the loop that's needed ~20 manual commits so far.

2. CI backstop (.github/workflows/backlog-scaffolding-guard.yml)

New workflow fails any PR whose diff adds/modifies a file matching backlogExcludePatterns. This is a second, independent layer for stapler-squad's own repo only — it structurally cannot reach into the arbitrary target repos backlog items point at (stapler-squad has no mechanism to impose new CI on repos it doesn't own), which is why item 1 above, not this workflow, is the primary defense for those.

3. "Scoped by session/backlog item" — decision: harden in place, don't relocate

The request asked me to evaluate moving .backlog-context.md out of the worktree entirely (e.g. ~/.stapler-squad/backlog-context/<item-id>/...) so it can never be tracked in the first place. I considered this (option b) against hardening the existing worktree-relative storage (option a) and chose (a):

  • Both writer functions already do a full atomic overwrite (WriteBacklogContextFile's tmp-file-then-rename) on every spawn/reattach/reopen — no session ever reads content left behind by a different item or session. The actual "reused poorly" risk was exclusively the already-tracked git pollution, not content staleness — that risk is closed by fix ci: fix forbidigo pattern field for golangci-lint 1.60.1 #1.
  • Relocating storage is a real, separate architecture change: it requires plumbing a session UUID into SpawnSessionFromItem's writeSessionFiles call site, which today runs before the session (and its UUID) is created; it requires rewriting taskProtocolBlock's literal .backlog-context.md references (session/backlog_context.go rules 5–6) to an absolute path; and .claude/commands/backlog/ (unlike the context file) must stay inside the worktree for Claude Code to discover it as project-scoped slash commands, so only half the scaffolding could move anyway.
  • Two other agents' PRs (fix(backlog): auto-respawn triage for items orphaned in idea #169, fix(backlog): escalate bouncing items to the rework-cap terminal state #170) are actively touching session/backlog_commands.go and session/backlog_lifecycle.go this week — a storage relocation meaningfully increases collision surface with that work for a single bug-fix session.

I'm flagging this explicitly as a scoped-out follow-up rather than hand-waving it: relocating storage is worth doing eventually (a file that was never inside the git working directory can never be accidentally committed, full stop), but it's an ADR-sized decision, not a bug-fix-sized one.

4. CleanupSlashCommands/CleanupBacklogContextFile — audited, left uncalled on purpose

Neither function has a production call site (confirmed via grep — only tests call them). I initially assumed this was a bug ("cleanup isn't wired up") but tracing shipViaAgentOrFallback (session/backlog_lifecycle.go) surfaced why: it relies on ship.md still existing in the worktree after a work session exits review, so it can re-invoke /backlog/ship as a one-shot headless call. Wiring CleanupSlashCommands into any review-exit teardown would delete ship.md out from under that path. I documented this directly in both functions' doc comments so a future pass doesn't "fix" this dead code in a way that breaks shipping. Worktree teardown (Instance.Kill/Instance.Pause) already removes the whole directory anyway, and untracked files never appear in a git diff/PR regardless of how long they linger on disk — so there was no actual gap to close here beyond fix #1.

Test plan

  • New regression tests in session/backlog_commands_test.go:
    • TestWriteBacklogContextFile_UntracksPreviouslyCommittedContextFile — commits .backlog-context.md to a real git repo, then verifies the next WriteBacklogContextFile call untracks it (still on disk, no longer in the index, fresh content).
    • TestWriteSlashCommands_UntracksPreviouslyCommittedSlashCommandFiles — same for .claude/commands/backlog/status.md.
    • TestWriteBacklogContextFile_NoStaleContentLeaksAcrossRespawn — writes for item A, then item B, at the same worktree path; asserts no cross-contamination.
    • TestUntrackTrackedScaffolding_NoErrorOnNonGitDirectory / TestUntrackTrackedScaffolding_LeavesUnrelatedTrackedFilesAlone — safety/scoping checks on the new helper.
  • go test ./session/... — 3069 passed
  • go test ./server/services/... — passed
  • make build — succeeds (Go + web UI)
  • make lint custom-lint failures present are pre-existing and unrelated (session/unfinished/gogitstore/*_test.go norawexec findings, untouched by this diff); golangci-lint run ./session/... reports zero findings for the changed file
  • actionlint clean on the new workflow file; regex manually verified against sample paths

One unrelated, confirmed-flaky test (TestEnsureServerRunning_NoOp in session/tmux, real tmux server contention) failed once under make test and passed cleanly in isolation and in a full -count=1 re-run of the whole package (228/228) — not related to this change.

Reviewer notes

…kstop

.backlog-context.md and .claude/commands/backlog/*.md are written into every
backlog work-session worktree, then git-excluded via $GIT_DIR/info/exclude.
That only stops NEW files from being staged — it does nothing once a file is
already tracked, which is why this repo's own history has ~20 manual
"chore(backlog): untrack backlog context/command files" commits, and why two
currently-open PRs (#169, #170) both carry an incidental deletion of a stale,
committed .backlog-context.md from an unrelated item's context.

WriteSlashCommands and WriteBacklogContextFile now call
selfHealWorktreeScaffolding before writing: it walks the git index (via
go-git, not a subshell) for any entry matching backlogExcludePatterns and
untracks it (git-rm-cached semantics — working tree file is left alone), so
a branch that ever got one of these files committed self-heals on the very
next spawn/reattach/reopen instead of requiring a manual untrack commit.

Also adds a CI backstop (.github/workflows/backlog-scaffolding-guard.yml)
that fails any PR whose diff adds/modifies a backlogExcludePatterns file, as
a second, independent layer for this repo. This only protects stapler-squad's
own repo — it cannot reach into the arbitrary target repos backlog items
point at, which is why the self-heal above (not CI) is the primary fix.

On the "scoped by session/backlog item so it's not reused poorly" half of
the request: chose to harden the existing worktree-relative storage rather
than relocate it to a session/item-scoped path outside the worktree
(~/.stapler-squad/backlog-context/<item>/...). Both WriteBacklogContextFile
and WriteSlashCommands already do a full atomic overwrite on every
spawn/reattach/reopen, so no session ever reads content from a different
item/session — the actual "reused poorly" risk was exclusively the
already-tracked git pollution this fix closes. Relocating storage is a
larger, real architecture change (touches taskProtocolBlock's literal
`.backlog-context.md` references, needs a session UUID that doesn't exist
yet at the pre-spawn write callsite, and increases collision surface with
other agents' concurrent work in these same files) and is left as a
follow-up rather than folded into this bug fix.

CleanupSlashCommands/CleanupBacklogContextFile remain uncalled from any
production teardown path, and that's intentional, not an oversight:
shipViaAgentOrFallback relies on ship.md still existing after a work session
exits review to re-invoke /backlog/ship as a one-shot call. Wiring cleanup
into a review-exit teardown would delete ship.md out from under that path.
Documented this directly in both functions' doc comments so it isn't
"fixed" again by mistake.

Regression tests: a worktree with .backlog-context.md (or
.claude/commands/backlog/status.md) already committed gets it untracked on
the next Write call with fresh on-disk content; respawning the same
worktree path for a different item never leaks the prior item's content;
the self-heal helper no-ops cleanly on a non-git directory and leaves
unrelated tracked files alone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BxNAMeGteuzNyN46Q4zAn1
@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

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

Building backend scanner...
Scanning backend features...
Wrote 117 feature files to /tmp/tmp.0u8ZQBESGB/backend
Wrote 15 feature files to /tmp/tmp.0u8ZQBESGB/backend
Wrote 44 feature files to /tmp/tmp.0u8ZQBESGB/backend
Wrote 7 feature files to /tmp/tmp.0u8ZQBESGB/backend
Wrote 11 feature files to /tmp/tmp.0u8ZQBESGB/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 17/178 features have testIds (9.6%)

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

@github-actions

Copy link
Copy Markdown
Contributor

Go Benchmarks (Tier 1)

benchmarks/go/tier1-baseline.txt:97: missing iteration count
benchmarks/go/tier1-baseline.txt:197: 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                         84.87n ± 2%
CircularBufferWrite_4KB_Allocs-4                  84.07n ± 3%
CircularBufferGetRecent_4KB-4                     499.8n ± 1%
CircularBufferGetAll-4                            3.883µ ± 1%
GetTimeSinceLastMeaningfulOutput_HotPath-4        65.81n ± 2%
GetTimeSinceLastMeaningfulOutput_ColdPath-4       32.78n ± 0%
geomean                                           176.1n

                                            │ 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          44.95Gi ± 0%
CircularBufferGetRecent_4KB-4      7.633Gi ± 1%
geomean                            18.52Gi

cpu: INTEL(R) XEON(R) PLATINUM 8573C
                                            │ benchmarks/go/tier1-baseline.txt │
                                            │              sec/op              │
CircularBufferWrite_4KB-4                                          172.4n ± 3%
CircularBufferWrite_4KB_Allocs-4                                   168.7n ± 3%
CircularBufferGetRecent_4KB-4                                      489.8n ± 1%
CircularBufferGetAll-4                                             3.703µ ± 2%
GetTimeSinceLastMeaningfulOutput_HotPath-4                         50.80n ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                        26.22n ± 1%
geomean                                                            203.1n

                                            │ 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                           22.13Gi ± 2%
CircularBufferGetRecent_4KB-4                       7.788Gi ± 1%
geomean                                             13.13Gi

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             746.4n ± 0%
ProcessOutput_InactiveState-4       6.295n ± 1%
geomean                             31.84n

                              │ 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: INTEL(R) XEON(R) PLATINUM 8573C
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
StripANSI_PlainText-4                                5.037n ± 3%
StripANSI_WithEscapes-4                              566.9n ± 1%
ProcessOutput_InactiveState-4                        15.50n ± 0%
geomean                                              35.37n

                              │ 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       84.95n ± 5%
ReviewQueue_Add-4                   508.6n ± 1%
geomean                             207.9n

                              │ 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: INTEL(R) XEON(R) PLATINUM 8573C
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
ReviewQueue_ConcurrentReads-4                        105.9n ± 5%
ReviewQueue_Add-4                                    427.0n ± 2%
geomean                                              212.7n

                              │ 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.960µ ± 2%
CircularBuffer_BurstAppend-4                101.7µ ± 1%
CircularBuffer_GetLastN_LargeBuffer-4       19.85µ ± 1%
CircularBuffer_GetRange_Sequential-4        11.20µ ± 5%
CircularBufferAppend-4                      97.94n ± 0%
CircularBufferGetLastN-4                    2.307µ ± 2%
CircularBufferConcurrentAppend-4            126.7n ± 1%
geomean                                     3.069µ

                                      │ 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      600.1Mi ± 0%

cpu: INTEL(R) XEON(R) PLATINUM 8573C
                                      │ benchmarks/go/tier1-baseline.txt │
                                      │              sec/op              │
CircularBuffer_ConcurrentReadWrite-4                         2.950µ ± 2%
CircularBuffer_BurstAppend-4                                 109.9µ ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                        16.70µ ± 2%
CircularBuffer_GetRange_Sequential-4                         10.74µ ± 2%
CircularBufferAppend-4                                       106.8n ± 0%
CircularBufferGetLastN-4                                     2.186µ ± 2%
CircularBufferConcurrentAppend-4                             153.2n ± 0%
geomean                                                      2.979µ

                                      │ 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                       555.5Mi ± 0%

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

                             │ 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: INTEL(R) XEON(R) PLATINUM 8573C
                             │ benchmarks/go/tier1-baseline.txt │
                             │              sec/op              │
StripANSICodes_PlainText-4                          4.518n ± 0%
StripANSICodes_WithEscapes-4                        527.8n ± 1%
IsBanner_PlainText-4                                369.0n ± 1%
geomean                                             95.82n

                             │ 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.207m ± 0%
DetectCommandsInText/NoSlash-4           7.494n ± 0%
DetectCommandsInText/WithCommand-4       1.660µ ± 0%
geomean                                  4.016µ

                                   │ 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: INTEL(R) XEON(R) PLATINUM 8573C
                                   │ benchmarks/go/tier1-baseline.txt │
                                   │              sec/op              │
TokenParser_ProcessUserEntry-4                            3.976m ± 0%
DetectCommandsInText/NoSlash-4                            4.220n ± 2%
DetectCommandsInText/WithCommand-4                        1.357µ ± 0%
geomean                                                   2.834µ

                                   │ 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.135m ± 1%
DiffShortstat/GoGitVCSReader-4       76.73n ± 0%
DiffShortstatCached-4                75.75n ± 1%
geomean                              2.631µ

                               │ tier1-bench.txt │
                               │      B/op       │
DiffShortstat/GitVCSReader-4      56.57Ki ± 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: INTEL(R) XEON(R) PLATINUM 8573C
                               │ benchmarks/go/tier1-baseline.txt │
                               │              sec/op              │
DiffShortstat/GitVCSReader-4                          2.187m ± 0%
DiffShortstat/GoGitVCSReader-4                        60.80n ± 0%
DiffShortstatCached-4                                 60.84n ± 1%
geomean                                               2.007µ

                               │ benchmarks/go/tier1-baseline.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

                               │ 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

Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 5ms (▼ faster -35.0%; baseline: 7ms)
list-sessions-total-mean: 12ms (▲ slower +30.1%; baseline: 9ms)

@github-actions

Copy link
Copy Markdown
Contributor

📊 Feature E2E Coverage

Feature E2E coverage: 5/178 tested (3%)

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

@github-actions

Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 14 KB/s ▼ -10.2% (baseline: 16 KB/s)
terminal-throughput-p50: 16 KB/s ▲ +0.9% (baseline: 16 KB/s)

@tstapler
tstapler merged commit bd7933c into main Jul 20, 2026
22 checks passed
@tstapler
tstapler deleted the worktree-agent-ab35fa4f2d7f57286 branch July 20, 2026 20:49
tstapler added a commit that referenced this pull request Jul 22, 2026
…olding files (#206)

* fix(backlog): guard commit/push staging against already-tracked scaffolding files

.backlog-context.md and .claude/commands/backlog/* were only kept out of the
repo at write time (selfHealWorktreeScaffolding, added in #195) and via
gitignore/info-exclude — neither stops a later `git add .` in
GitWorktree.CommitChanges/PushChanges or QuickCommitPush from restaging a
copy that's already tracked in the branch's history, which is how ~20 manual
"chore(backlog): untrack ..." commits accumulated over time.

GitWorktree.StageAllExceptScaffolding (session/git/worktree_git.go) now runs
`git add .` and then untracks any path matching the shared
git.ScaffoldingExcludePatterns list, used by both CommitChanges and
PushChanges; HasStagedChanges lets both skip the commit gracefully instead of
erroring when the only staged change was scaffolding that just got untracked.
QuickCommitPush applies the identical guard via the same GitWorktree methods.

Consolidated the previously-duplicated exclude-path list (backlogExcludePatterns
in session/backlog_commands.go, plus hardcoded strings elsewhere) into a single
exported git.ScaffoldingExcludePatterns (session/git/scaffolding.go), consumed
by the worktree-write side, the new staging guard, and QuickCommitPush. Added a
test asserting it stays in sync with .gitignore.

CleanupBacklogContextFile/CleanupSlashCommands were dead code with zero
production call sites; ReconcilePRPending now invokes them once an item's PR
is confirmed merged and it reaches done — after ship.md's one-shot-reinvoke
constraint no longer applies.

Skipped a scripted mass remediation across the ~5 backlog/* branches still
carrying tracked copies: origin/main is already clean, and the remaining
branches are actively-cycling autonomous backlog sessions (one on revision
r7) rather than dead branches — the self-heal plus this commit-time guard
already untrack them the next time each is touched, so an out-of-band push
was unnecessary risk for no real benefit.

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

* address code review: serialize UntrackScaffolding per worktree, dedupe stage/commit, fix test gap

- UntrackScaffolding now takes a per-worktree-path mutex before its index
  read-modify-write: go-git's Storer.Index()/SetIndex() don't participate in
  git's index.lock protocol, and adding more call sites (CommitChanges,
  PushChanges, QuickCommitPush) on top of the existing write-time self-heal
  meaningfully increases the odds of two concurrent callers racing on the
  same worktree's index.
- Extracted stageAndCommit as the one shared stage/commit/skip-if-empty
  sequence used by both CommitChanges and PushChanges, removing the
  duplicated block.
- Softened StageAllExceptScaffolding's doc comment, which overclaimed
  scaffolding files "can never be committed" when the untrack step
  deliberately fails open on error (mitigated by the separate CI backstop
  workflow, not a behavior change).
- Fixed TestStageAllExceptScaffolding_LeavesUnrelatedTrackedFilesAlone, which
  never actually exercised the "except scaffolding" half of its own name — it
  only asserted a normal file gets staged. Now also asserts a scaffolding
  file added alongside it is excluded.

Found via /code:review (testing, code-quality, architecture, security agents
+ skeptic pass) on PR #206.

---------

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