Skip to content

fix(web): dead-band terminal resize loop, RPC dedup, and WebGL fallback - #272

Merged
tstapler merged 2067 commits into
mainfrom
backlog/stapler-squad-terminal-resize-loop-fix
Jul 28, 2026
Merged

fix(web): dead-band terminal resize loop, RPC dedup, and WebGL fallback#272
tstapler merged 2067 commits into
mainfrom
backlog/stapler-squad-terminal-resize-loop-fix

Conversation

@tstapler

Copy link
Copy Markdown
Owner

Summary

  • The tiled/multi-pane terminal view could enter an infinite fit() <-> ResizeObserver feedback loop: a WebGL cell-width measurement mismatch (actual vs. expected px/col) meant FitAddon.proposeDimensions() never reached a fixpoint, so every resize observation re-triggered another sub-pixel resize, flooding the server with TerminalResize RPCs and pegging CPU.
  • This fixes the loop at its source (dead-band the observer, dedup the RPC, and correct/fall back on the WebGL mismatch) rather than papering over symptoms.

What Changed

  • Dead-band ResizeObserver (XtermTerminal.tsx): only schedules fit() when proposeDimensions() reports integer cols/rows that differ from the currently-applied size AND repeat on two consecutive ticks — closes both the sub-cell-jitter loop and boundary-flapping near an exact cell-width boundary.
  • RPC value-dedup (useTerminalFlowControl.ts): resize() skips sending TerminalResize (and the follow-up currentPaneRequest) when the incoming (cols, rows) equals the last pair actually sent — independent of the existing 200ms time throttle.
  • Explicit force-bypass at the two call sites that must skip dedup (reconnect-resync, manual "Resize" button) via a literal force: true third argument, with regression tests asserting that literal argument at each call site.
  • WebGL mismatch tracker + one-directional Canvas fallback: sustained actual-vs-expected px/col mismatch beyond a defined tolerance trips a one-way fallback from the WebGL renderer to the Canvas renderer (using Number.isFinite guards against proposeDimensions() returning Infinity), so fit() has a stable fixpoint even when the WebGL glyph metrics disagree with the DOM measurement.
  • New/updated Jest coverage in XtermTerminal.test.tsx, TerminalOutput.test.tsx, and useTerminalFlowControl.test.ts (32 tests) simulating sub-cell jitter, boundary-flapping, WebGL mismatch escalation, and both force-bypass call sites — each asserting fit()/RPC/dispose fire at most the expected number of times, not once per observed frame.
  • Full SDD planning artifacts under project_plans/terminal-resize-fit-loop/ (requirements, research, architecture/adversarial review, plan, validation, pre-mortem).

Test plan

  • npx jest XtermTerminal TerminalOutput useTerminalFlowControl — 3 suites, 32/32 passing
  • Manual verification against an isolated stapler-squad instance with 3 concurrently-mounted terminal sessions (closest available proxy in this codebase to a literal tiled-pane layout — no same-page multi-terminal-mount view exists here):
    • Simulated tab background (10s hidden) -> resume on all 3 concurrently: zero [XtermTerminal] resize/fit log lines fired in an 8s watch window.
    • Single window/viewport resize on all 3: exactly 4 log lines each (Container resized... -> Terminal dimensions BEFORE fit... -> Sending resize to server: 172x37 -> Sampler confirmed resize, fit applied: 172 cols x 37 rows), then zero further lines over the next 8s.
    • Typed input in each of the 3 echoed back immediately, no perceptible lag.
    • Manual "Resize" button: exactly 1 RPC line per click (force-bypass path works end-to-end, not just in unit tests).
    • Forced a WebGL -> Canvas fallback trip via the debug hook: fired cleanly, and the terminal continued rendering correct glyphs/colors/cursor and accepting input afterward.

Originally opened as TylerStaplerAtFanatics/stapler-squad#191; moved here per request.

github-actions Bot and others added 30 commits July 1, 2026 23:53
…n is always visible

Button was pushed off-screen on narrow viewports due to flex-row layout with
flexGrow:1 on the text. Switch to column direction so the button always renders
below the error message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…detection

Show GitHubBadge inline in SessionRow (row/list view) so PR status is
visible without switching to card view. Previously the badge only
rendered in SessionCard (card view).

Switch getCurrentBranchName from subprocess (git rev-parse) to go-git
direct file read — no subprocess overhead. Add exported
GetCurrentBranchName wrapper and CurrentBranch() method on Instance
that falls back to live git read for directory sessions (Branch field
is always empty for non-worktree sessions). Add UpdatePRStatus() helper
for atomic in-memory PR status updates from PRStatusPoller.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: repair broken release pipeline and build-from-source path

Every GoReleaser release since v1.9.0 has failed with "found 3 builds
with the ID 'stapler-squad'" because none of the three build entries
in .goreleaser.yaml declared an explicit id, so GoReleaser assigned
them all the same default. This is why brew install pulls the ancient
1.9.0 build (Formula/stapler-squad.rb hasn't updated since) and why
install.sh's release-asset download has had nothing to fetch for
every tag from v1.20.1 through v1.32.0. Give each build block an
explicit unique id.

Also fixes two things blocking the build-from-source path:
- config/executor.go: lookPathOnlyExecutor.Command used a raw
  exec.Command instead of safeexec.CommandContext, tripping the
  norawexec custom lint rule and failing `make build` outright.
- Makefile: `go build` never set the version ldflag, so both
  `make build` and plain `go build .` reported the stale hardcoded
  "1.1.2" regardless of what was actually built. Derive VERSION from
  `git describe` and pass it via -ldflags, matching what GoReleaser
  already does for tagged releases.

Verified locally: `make build` now succeeds end-to-end and
`./stapler-squad version` reports the real git-described version.
`goreleaser check` and a full `goreleaser release --snapshot --clean`
(with the GITHUB_* env vars CI provides) both succeed, including
Homebrew formula generation.

Fixes #143

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

* fix: isolate TestGetConfigDir from ambient STAPLER_SQUAD_* env vars

GetConfigDir() checks STAPLER_SQUAD_TEST_DIR and STAPLER_SQUAD_INSTANCE
before falling through to test-mode auto-detection. When the test
process inherits either from its environment (e.g. running inside a
stapler-squad-managed session), the "uses test mode isolation for
tests" subtest short-circuits on the ambient value instead of
exercising auto-detection, and fails. Clear both for the duration of
the subtest and restore them afterward.

Verified with `go test ./config/... -run TestGetConfigDir -count=3`
and a full `go test ./config/... -count=1`.

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

* fix: sanitize VERSION and wire it into build-embedded too

Code review on this branch surfaced two real gaps in the version-ldflag
fix:

1. Security: git tag names may legally contain shell metacharacters
   (backtick, $()). Make's $(VERSION) substitution is pure text
   substitution done before the shell parses the recipe line, so those
   characters land as live shell syntax inside the double-quoted
   `-ldflags` argument — anyone who can get a maliciously-tagged ref
   fetched into a checkout gets command execution on `make build` /
   `make install-service`. Strip VERSION to a safe charset before it
   ever reaches the shell.

   (Checked whether the analogous `VERSION=$(git describe ...)` in
   .github/workflows/build.yml has the same problem: it doesn't. That's
   a bash variable expansion of an already-computed string, not a
   macro substitution before the shell parses the command — bash does
   not re-evaluate `$()`/backticks embedded in an expanded variable's
   value. Verified empirically. Left that file alone.)

2. Completeness: `build-embedded` (the tmux-bundled single-binary
   target used by `make build-tmux` -> `make build-embedded`) builds
   the same stapler-squad binary as the primary `stapler-squad` target
   but wasn't wired to the new LDFLAGS, so it would have kept shipping
   the exact stale "1.1.2" version string issue #143 complains about.

Verified: `make build` still succeeds and reports a correct, sanitized
version. `make -n build-embedded` confirms the ldflags now appear in
that target's go build invocation.

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

* ci: add goreleaser check as a regression guard for .goreleaser.yaml

The build-ID collision this PR fixes broke every release for 15+
months with zero visibility: the only place it ever surfaced was a
failed Action run on a tag push (release.yml only runs `goreleaser
release` on `push: tags: v*`), which nobody was watching closely
enough to catch. Add a small, fast, dedicated workflow that runs
`goreleaser check` on every change to .goreleaser.yaml, so a config
mistake like this one fails a PR check immediately instead of silently
breaking every subsequent release.

`goreleaser check` also fails non-zero for known-but-accepted
deprecation warnings, not just genuine invalidity, so a naive `args:
check` step would have gone red on day one against this repo's
existing config (it still uses the classic `brews` publisher, which
GoReleaser wants migrated to `homebrew_casks` — a real behavioral
change for end users, not a syntax rename: casks use different install
semantics, code-signing/Gatekeeper expectations, and app-bundle
lifecycle hooks that don't apply to a plain CLI binary, and would very
likely break the `brew install` command this repo's README documents.
That migration needs its own careful, tested PR, not a blind swap
bundled into an install-bug fix). Fixed the two safe, pure-syntax
deprecations in the same commit (`archives.format`/
`format_overrides.format` -> `formats`, now a list — verified via a
full snapshot build that archive naming/extension per-OS is
unchanged) and left `brews` alone. The new workflow's check step
distinguishes "configuration is invalid" (hard fail) from "valid, but
uses deprecated properties" (pass, tracked separately) by output
content rather than exit code, so it stays a real regression guard
instead of either being permanently red on accepted debt or silently
disabled.

Verified locally:
- `goreleaser check` on the current config: valid, only the accepted
  `brews` deprecation remains.
- Simulated the exact original bug (duplicate build ids) against a
  scratch copy of the config: the same check logic correctly reports
  "configuration is invalid" and would fail CI.
- Full `goreleaser release --snapshot --clean` still succeeds
  end-to-end after the formats-list migration, archive names/
  extensions unchanged.

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

* fix: sync registry validation with github_user.proto and add missing feature files

CI's Registry Validation check was failing on this PR (unrelated to the
actual fix, but blocking it from going green): `tools/scanner/validate-registry.sh`
never scans `proto/session/v1/github_user.proto`, even though the
Makefile's `registry-generate-backend` target does. Both were last
touched independently, and the validation script's hardcoded proto
list was never updated when github_user.proto's RPCs (added in
3be7e09, well before this branch existed) were registered. The
result: `docs/registry/features/backend/*.json` never had entries for
ListGitHubAccounts/PollGitHubDeviceAuth/RevokeGitHubToken/
StartGitHubDeviceAuth, and the validation script would report them as
"Removed RPCs" (154 committed vs. 147 generated, 4.55% divergence)
forever, regardless of whether the per-feature files existed — the
scanner it runs simply never looks at that proto file.

- Added the missing `github_user.proto` scan step to
  validate-registry.sh, matching the Makefile.
- Ran `make registry-generate` to create the 4 missing per-feature
  JSON files these RPCs were always supposed to have.

Verified: `./tools/scanner/validate-registry.sh` now reports
"Committed: 154  Generated: 154  Divergence: 0.0%" and exits 0.

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>
…rsing crash) (#150)

* fix: shell-quote claude launch args to stop injection and flag-parsing crash

Backlog/triage spawned sessions died on launch: the prompt is interpolated
into a shell command (tmux launches programs through a shell), and Go's %q
produces double quotes, which do not suppress backtick/$(...)/$VAR
expansion. Backlog prompts are full of backtick-wrapped tokens
(`/backlog/done-N`, etc.), so the shell executed each as a command instead
of passing it to claude. Separately, backlog prompts begin with
"--- BACKLOG ITEM DATA ---", which claude's arg parser rejected as an
unrecognized flag once quoting was fixed.

Add shellQuote (POSIX single-quoting, the same style already used for
--mcp-config) and apply it to every claude flag value that gets
interpolated into the shell command: --append-system-prompt, --allowedTools,
--permission-mode, and the positional prompt. Insert a bare "--" before the
prompt so a leading "--" in the prompt text is treated as data, not flags.

Verified against the real claude CLI that both -- as an end-of-options
separator and --append-system-prompt-file are accepted, and confirmed via
a real shell execution that a $(...) payload in a backlog-shaped prompt no
longer executes.

Fixes #148

* fix: close remaining shell-injection gaps found by review

Multi-agent review of the shellQuote fix found the same vulnerability
class still present two call sites over:

- --resume value: claudeSessionID traces back to the client-supplied
  resume_id field on CreateSessionRequest with no format validation, and
  was still interpolated unquoted into the shell-executed launch command
  in the same function that was just patched.
- claudeMCPConfigFlag hand-rolled its own shell single-quoting (a literal
  '...' wrapper) instead of reusing shellQuote, leaving a second,
  untested implementation of the same job living next to the new one.
  Not currently exploitable (MCPServerURL/UUID aren't attacker-supplied
  today) but a latent gap in the same file that just added the primitive
  meant to prevent this.

Also add regression tests the review flagged as missing: --allowedTools
and --permission-mode had zero shell-safety coverage even though
shellQuote was applied to both, so a partial revert of just those two
lines would have passed the full suite silently. Reworked the two
existing Prompt/AppendSystemPrompt regression tests to assert against
hand-written expected literals instead of calling shellQuote() again,
so they don't just verify the function against itself. Added
only-single-quote, embedded-newline, and combined backtick+quote cases
to TestShellQuote's table.

Confirmed session/claude_command_builder.go's separate --resume path is
not affected: it validates the session ID against a strict UUID v4
regex before use, and is not wired into any production call site today.

---------

Co-authored-by: Tyler Stapler <tystapler@gmail.com>
…detection (#149)

* fix(analytics): escape analytics session_id mismatch and dead mangle detection

Escape event rows were tagged with the tmux session name instead of the
stable session UUID, so the web UI (which queries by stable UUID) never
found any data even though capture itself was working (185K+ rows in the
live DB). Mangle detection was fully implemented and unit-tested but never
wired into production — SetCorrelator was never called, emitEventWithStageAndSeq
always recorded Stage 1 observations instead of checking Stage 2 against them,
and the Stage 2 tap computed session_seq from the wrong buffer offset.

- Thread instance.GetStableID() into the escape parser via a new
  ResponseStream.SetStableSessionID, scoped narrowly so cc.sessionName's
  other use sites (PTY naming, persistence dirs, rate limiting) are untouched
- Wire MangleCorrelator per-parser with its eviction loop tied to stream
  lifetime; branch RecordStage1 vs CheckStage2 by stage instead of always
  recording
- Fix Stage 2 session_seq to use the coalesced frame's start offset, not its
  end offset, so it aligns with Stage 1's numbering
- Convert totalSequences/totalMangled to atomic.Int64 (both stages write
  through the same parser instance from different goroutines)
- Mirror escape analytics defaults into DefaultConfig() to match
  LoadConfigFromPath, per the existing "must mirror" comment

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

* fix(analytics): redesign mangle correlation to be offset-independent

Code review on PR #149 found the byte-offset arithmetic fix for Stage 2
correlation couldn't work regardless of the arithmetic: streamViaControlMode's
data comes from a separate tmux control-mode client, not the same producer as
Stage 1's raw PTY read, so the two sides have no shared byte-offset numbering.

Verified empirically (live tmux experiment with two simultaneous client
attachments) that the two streams carry identical content in the same order,
just offset by a constant that resets on each client's own connect/resize
redraw — a calibration problem, not a content mismatch. Redesigned
MangleCorrelator to correlate ordinally per (session, sequence type) instead
of by byte position, which is robust to that offset entirely.

Also addresses the review's MAJOR findings: sessionID is now
atomic.Pointer[string] instead of an unsynchronized plain string; the parser
setter is renamed SetStableSessionID to stop colliding with a tmux-name-keyed
SetSessionID called 4 lines away; the correlator eviction goroutine is now
tracked by ResponseStream's WaitGroup (so Stop() actually blocks on it) and
panic-recovered; and the production wiring line in ClaudeController.Start()
now has a test that would catch a regression back to the tmux name.

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

* chore: restore .claude/scheduled_tasks.lock accidentally deleted in prior commit

Unrelated to this PR's changes — an environment-local lock file got staged as
deleted before this session started and was swept up by a non-path-scoped
git commit. Restoring it to match origin/main.

---------

Co-authored-by: Tyler Stapler <tystapler@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
… related bugs (#152)

* fix(backlog): resolve GitHub URLs in repo path, add first-visit tour

The Repository Path field silently accepted a GitHub URL and used it
verbatim as a filesystem path, producing garbage paths and silent
triage failures (stapler-squad#148's "Related" section). It also had
no guidance on what it expected, so users had no way to know a URL
wasn't a valid local path.

- BacklogService now resolves GitHub URLs/shorthand in repo_path to a
  local clone (same machinery CreateSession already uses for the
  Omnibar), or returns a clear validation error instead of storing
  garbage. Covers both CreateBacklogItem and the UpdateBacklogItem
  fix-up path.
- RepoPathInput gained an optional hint line and live GitHub-URL
  detection ("Will clone owner/repo to ~/.stapler-squad/repos/...").
- BacklogItemForm explains the two previously-unlabeled checkboxes
  and shows "Cloning repository…" while a fresh clone is in flight.
- New BacklogTourModal walks first-time visitors through the item
  lifecycle, the repo-path gotcha explicitly, and what the skip flags
  do; reopenable via a "?" button in the page header.

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

* fix: shell-quote claude launch prompts, stop triage poll from losing edits

Two backlog-adjacent bugs Carl filed while debugging the repo-path
issue above:

- stapler-squad#148: backlog/triage session prompts were interpolated
  into the shell command with Go's %q (double quotes), so backtick-
  wrapped tokens and $(...) in the auto-generated prompt were executed
  by the shell, and a leading "--" was parsed as a claude CLI flag —
  spawned sessions died on launch. Now single-quoted (shellQuote, which
  suppresses all shell expansion) with a "--" separator before the
  prompt.
- stapler-squad#146: BacklogItemDetail's full-screen loading guard
  unmounted <BacklogItemForm> on every 5s triage-status poll, so any
  unsaved acceptance criteria typed during triage were silently
  discarded. The loader now only shows on the initial load, and the
  poll is suspended while the edit form is open.

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

* chore(e2e): install missing test deps, extend server-boot timeout

allure-playwright (declared in package.json) was missing from the
committed node_modules, breaking `npx playwright test` outright.
Installed it and its transitive deps.

Also bumped the test-server health-check timeout from 30s to 90s: a
cold test-mode boot (DB init + demo seeding) was observed taking
~30-45s before /health responds, right at the old cap.

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

* revert(e2e): don't commit node_modules lock manifest without the packages

The previous commit updated .package-lock.json (npm's per-tree
manifest) after `npm install` pulled in allure-playwright and ~350
transitive deps, but those package directories are gitignored and
weren't force-added — committing just the manifest without the actual
files would claim the tree is in sync when it isn't.

tests/e2e/node_modules is vendored (git-tracked despite .gitignore),
so fully fixing the missing-dependency gap means force-adding ~thousands
of new files, which is out of scope for this PR. Leaving the
test-server.ts timeout bump from the previous commit in place since
that's independently correct; flagging the vendoring gap separately.

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

* fix: address code review findings (shell-quote gap, tour checkbox bug, path traversal)

Multi-dimension code review (Testing, Code Quality, Architecture, Security)
on PR #152 surfaced two CRITICALs, both cross-validated by 2-3 independent
reviewers, plus several MAJOR issues:

- CRITICAL: AllowedTools/PermissionMode in instance_tmux.go still used Go's
  %q instead of the new shellQuote — the exact same shell-injection class
  this PR fixes for AppendSystemPrompt/Prompt, just on two sibling fields
  populated directly from client RPC input.
- CRITICAL: BacklogTourModal's "Don't show this again" checkbox was a no-op
  — onClose (mapped to setTourComplete) unconditionally persisted
  onboarded=true regardless of the checkbox state. Fixed by changing the
  modal's callback contract to onComplete(persist: boolean), with a new
  hideTour() on the hook for the non-persisting path.
- MAJOR (security): GitHub owner/repo regexes in repo_path.go didn't reject
  "." / ".." segments, so a crafted repo_path could resolve the clone
  directory outside ~/.stapler-squad/repos/github.com/. Added an
  isTraversalSegment guard across all 4 parse branches.
- MAJOR: hardcoded 24px margin replaced with the vars.space token; extracted
  BacklogTourModal's reused modal-chrome styles out of OnboardingModal's own
  CSS module into a new shared components/ui/ModalTour.css.ts (OnboardingModal
  re-exports from it, so OnboardingModal.tsx needed no changes).
- MAJOR (testing): replaced two circular shellQuote()-derived test oracles
  with hardcoded literals, added a message-content assertion the Update-path
  resolver-error test was missing, and strengthened the poll-suspended-while-
  editing test to assert the actual unsaved acceptance criterion survives
  rather than just checking a fetch call count.

Deferred (documented, not blocking): DRY duplication between
backlog_service.go/session_service.go's GitHub resolution, a matching
hardcoded path format in the frontend hint text, and the pre-existing
synchronous-clone-in-RPC-handler pattern this PR extends to a second call
site (mirrors existing CreateSession behavior).

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>
#155)

* fix: make web-build generate proto bindings on a fresh clone

`make web-build` builds `web-app/out` without depending on `proto-gen`,
so a clean checkout fails with "Module not found:
'@/gen/session/v1/session_pb'" because the TypeScript protobuf
bindings were never generated. `make build` was unaffected since it
lists `proto-gen` as a direct prerequisite of the top-level target.

Add `proto-gen` as a prerequisite of `web-app/out` so the TS bindings
exist before the Next.js build runs, regardless of which entry point
is used. `proto-gen` is a no-op when the bindings are already
up to date, so this doesn't slow down repeat builds.

Fixes #144 (Bug 1). Bug 2 (go-m1cpu SIGSEGV) is already resolved —
the repo depends on gopsutil/v4, which dropped the go-m1cpu cgo
dependency.

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

* test: add CI smoke test for standalone `make web-build`

The existing CI pipeline never exercises the Makefile's own dependency
graph: `.github/actions/prepare` hand-runs `buf generate` and
`pnpm run build` directly, bypassing `make` entirely. That's exactly
why the missing `proto-gen` prerequisite on `web-app/out` (previous
commit, fixes #144) went undetected - no CI job ever invoked
`make web-build` or `make build` as a fresh clone would.

Add a standalone job that checks out cleanly (no shared artifacts,
no manual buf/pnpm pre-steps) and runs `make web-build` directly,
then asserts the generated TS proto bindings exist. Verified this
job's steps fail against the pre-fix Makefile with the exact reported
error ("Module not found: '@/gen/session/v1/session_pb'") and pass
against the fix.

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

* chore: untrack stale generated proto files that were force-committed

gen/, web-app/src/gen/, and .proto-gen.stamp are already in .gitignore,
but 19 generated files under gen/proto/go/session/v1/ and
web-app/src/gen/session/v1/ were force-committed into git anyway
(going back through at least PR #60, #51, #54) and never cleaned up.

The tracked set was also incomplete/stale - e.g. session.pb.go and
session_pb.ts (generated from session.proto, the largest proto file)
were never committed at all, while sessionv1connect/session.connect.go
(which references types defined in session.pb.go) was. This is exactly
what produced the "undefined: v1.CreateSessionRequest" compile errors
and "Module not found '@/gen/session/v1/session_pb'" webpack errors
in #144 on any workflow that skipped `proto-gen` - the stale committed
files gave inconsistent partial signals instead of a clean "not
generated yet" failure.

`git rm --cached` only removes them from the index; the working-tree
copies (freshly regenerated by `make web-build` in the previous
commits) are untouched, and .gitignore now actually takes effect for
this tree going forward.

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>
github-actions Bot and others added 14 commits July 25, 2026 18:58
* docs: add requirements.md for GitHub issue/PR link surfacing

Reset this branch onto origin/main first (it was ~1989 commits stale
and missing the backlog feature entirely). Requirements grounded in
the actual current session/backlog_* and server/services/backlog_service.go
code per the item's 10 acceptance criteria.

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

* docs: add phase-2 research for GitHub issue/PR link surfacing

Stack, features, architecture, pitfalls, and build-vs-buy research
for adding ExternalURL end-to-end (ent schema, plugin mapping, prompt
rendering, sync backfill). Key findings: stdlib-only (no new dep),
ent auto-migration via existing --feature sql/upsert generate command,
fact line belongs inside the prompt's trusted-data boundary while the
closingKeywordFor instruction line stays outside it, and SyncOne's
anyField gate needs an explicit fix so it doesn't skip the unconditional
ExternalURL backfill when title/description/priority are all user-modified.

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

* docs: add implementation plan, ADR-001, and reviews for GitHub issue link

Plan covers 6 phases / 7 epics / 13 stories / 32 tasks with a concrete
Given-When-Then example per acceptance criterion. ADR-001 records two
non-obvious decisions: SyncOne's anyField must be set independently of
UserModifiedFields gating for the ExternalURL backfill, and the fact
line renders inside BuildSessionInitialPrompt's inert-data boundary
while the closingKeywordFor instruction line stays outside it.

Architecture and adversarial review both caught the same real bug pre-
implementation: the instruction-line format string would have rendered
"Fixes: <url>" (extra colon) instead of AC3's literal "Fixes <url>" /
"Related: <url>" asymmetric punctuation. Fixed in plan.md and both
reviews confirmed RESOLVED after a repair-loop pass.

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

* docs: validation plan, pre-mortem, and a real defect fix from sdd:4-validate

Validation.md maps all 9 ACs to concrete tests and closes 3 test-coverage
gaps the plan's task list hadn't named explicitly (a migration NULL-safety
test, a Create/Get round-trip test, and an AttachSessionToItem integration
test — the last one closes exactly the kind of silent two-literal drift
this whole feature exists to fix).

Pre-mortem's P1 finding was a real, confirmed defect, not just an
unverified assumption: fetched GitHub's own docs and confirmed the
closing-keyword parser only recognizes "#N"/"owner/repo#N", never a bare
URL. The original design (Fixes <full-url>) would have silently failed
to auto-close anything. Added a githubShortRefFor helper (ADR-001
Decision 3) to derive and render the correct short reference instead,
and propagated the fix through plan.md's tasks/tests and validation.md.

Cross-artifact consistency check also caught a stale requirements.md
line claiming the AC6 backfill "bypasses" BacklogItemUpdate, when the
actual (correct) design routes through it and only bypasses the
UserModifiedFields gate — fixed.

Product Triad Review: all three legs (Product/UX/Engineering) ready,
zero blockers.

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

* feat(backlog): surface linked GitHub issue/PR to agent prompts

Imported backlog items now carry the originating GitHub issue/PR URL
end to end, so an agent working the item can reference it in its PR
and GitHub can auto-close/cross-reference on merge.

- ent schema/BacklogItemData/BacklogItemUpdate gain ExternalURL
  (session/ent/schema/backlog_item.go, session/repository.go),
  populated by both GitHubIssuesPlugin and GitHubPRsPlugin
  (capped at 500 chars, matching the existing Title/Description
  truncation pattern).
- SyncOne backfills ExternalURL on pre-existing rows unconditionally,
  bypassing UserModifiedFields local-wins, with anyField set
  independently so the backfill fires even when title/description/
  priority are all user-locked (ADR-001 Decision 1). Only backfills
  items still returned by the plugin's state=open Fetch — a
  documented, tested limitation, not a bug.
- BuildSessionInitialPrompt renders a "Linked GitHub Issue/PR: <url>"
  fact line inside the prompt's inert-data boundary, and a
  deterministic closing-keyword instruction line outside it
  (ADR-001 Decision 2), gated on ExternalURL being non-empty so the
  prompt is byte-identical to today when it's absent.
- githubShortRefFor derives the actual "owner/repo#N" reference
  GitHub's closing-keyword parser recognizes (confirmed via GitHub's
  own docs during sdd:4-validate — a bare full URL is NOT a
  recognized closing-keyword form, only #N/owner/repo#N are).
  Fixes a defect the original design would have shipped silently
  (ADR-001 Decision 3).
- Both hand-built ent.BacklogItem{} literals in
  server/services/backlog_service.go (SpawnSessionFromItem,
  AttachSessionToItem) now include ExternalURL.

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

* fix(backlog): harden githubShortRefFor and sanitize ExternalURL in prompt

sdd:6-verify Layer 1/2 review findings (0 blockers, applying the two
worthwhile SUGGEST items inline):

- githubShortRefFor now strips query string/fragment/trailing-slash
  noise from the parsed issue/PR number, so a URL like
  ".../issues/42?tab=comments" renders "acme/widget#42" instead of a
  malformed "acme/widget#42?tab=comments" that GitHub wouldn't
  recognize. Added cross-reference comments between closingKeywordFor
  and githubShortRefFor noting they use different URL-shape strictness
  by design (both real producers are github.com URLs, so they can't
  disagree today) rather than restructuring tested pure functions this
  late for a currently-unreachable case.
- item.ExternalURL is now run through sanitizeField before rendering
  in both the fact line and instruction line, matching the existing
  convention for every other externally-sourced field (Description,
  Notes, LastCommitMessage) rather than being the one exception.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…-terminal-resize-loop-fix

# Conflicts:
#	server/dependencies.go
#	server/server.go
#	server/services/workspace_service.go
#	session/capture_test.go
#	session/history_linker.go
#	session/history_linker_test.go
#	session/instance.go
#	session/tmux/tmux.go
#	session/tmux/tmux_test.go
#	web-app/jest.config.js
#	web-app/jest.setup.js
#	web-app/package-lock.json
#	web-app/package.json
#	web-app/src/app/page.tsx
#	web-app/src/components/sessions/SessionCard.module.css
#	web-app/src/components/sessions/SessionCard.tsx
#	web-app/src/components/sessions/SessionList.tsx
#	web-app/src/components/sessions/TerminalOutput.tsx
#	web-app/src/components/sessions/XtermTerminal.tsx
#	web-app/src/components/sessions/__tests__/XtermTerminal.test.tsx
#	web-app/src/lib/hooks/useSessionService.ts
#	web-app/src/lib/hooks/useTerminalFlowControl.ts
#	web-app/src/lib/hooks/useTerminalStream.ts
Fixes surfaced by running the merged test suite after merging origin/main
into the terminal-resize-fit-loop branch (commit 65afd7b):

- XtermTerminal.tsx: remove the reintroduced synchronous initial onResize()
  call. Upstream deliberately deleted this (commit acb0750, "prevent
  premature resize from corrupting dimension cache") because it fired
  onResize(80,24) with xterm's construction-time defaults before fitAddon.fit()
  ever ran, corrupting TerminalOutput's dimension cache. Re-adding it during
  the merge reintroduced that exact regression, caught by the upstream
  regression suite XtermTerminalBug.test.tsx ("Bug 1").

- XtermTerminal.test.tsx: add a buffer stub to MockTerminal (origin's new
  updateScrollbar() reads buffer.active unconditionally), stub a minimal
  WebGL2RenderingContext global (jsdom has none, and the merged component now
  gates its WebGL load behind `typeof WebGL2RenderingContext !== 'undefined'`
  per xterm.js issue #2033), flush the dynamic import('@xterm/addon-webgl')
  microtask chain after mount, and recalibrate the resize-sampler test
  helpers' fake-timer advances for the flat 150ms debounce (replacing the old
  adaptive 10ms-for-first-3 debounce) instead of the old adaptive timing.

- TerminalOutput.test.tsx: add the supporting context/hook mocks
  (AnalyticsContext, ApprovalsContext, useHandedness, TerminalStreamManager,
  etc.) TerminalOutput now depends on, update the Fit button's aria-label and
  account for it living behind the (now-collapsible) toolbar toggle, and flush
  the XtermTerminal lazy-import Suspense boundary plus the new 250ms
  post-connection resize delay before asserting.

- BacklogItemForm.test.tsx: fix a pre-existing tsc error (unrelated to this
  merge, inherited from origin/main) by typing the onSubmit mock instead of
  letting it infer a zero-arg jest.Mock.

- pnpm-lock.yaml: regenerated after adding @xterm/addon-canvas back to
  package.json (needed for the Canvas-fallback feature; origin/main's
  dependency bump to xterm.js 6.x never had this addon since upstream lacks
  the WebGL->Canvas fallback feature).

All fixes verified with `go build ./...`, `go test ./...`, `npx tsc --noEmit`,
and the full `npx jest` suite (2840 passing; 3 pre-existing failures on
origin/main unrelated to this merge — SessionCard.click.test.tsx and
SessionCard.approval-suppression.test.tsx are missing an AnalyticsContext
mock that predates this branch, and BacklogEmptyState.test.tsx has an
unhandled promise rejection from mockRejectedValue, also pre-existing).

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

- useTerminalFlowControl.ts: cancel any pending deferred resize timer
  unconditionally BEFORE the value-dedup early-return in resize(), so a
  bounce-back call (A -> B deferred -> back to A) can no longer dedup-return
  while leaving a stale deferred send for B still scheduled. Added a
  regression test reproducing the exact bounce-back scenario.
- XtermTerminal.tsx: guard the async @xterm/addon-webgl import with a
  `cancelled` flag set in the effect's unmount cleanup, preventing
  loadAddon() from running against an already-disposed terminal and leaking
  an orphaned WebGL context on a fast session-switch unmount.
- useTerminalFlowControl.test.ts: remove dead duplicate jest.mock('@bufbuild/protobuf', ...)
  merge-conflict leftover.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSdgF9zfk4PcYuQE61ooHi
triggerCanvasFallback() lacked the same cancelled-guard the async WebGL
loader uses, so a post-unmount onContextLoss could call loadAddon() on a
disposed terminal (caught but logged spuriously). The post-resize
currentPaneRequest timer in useTerminalFlowControl was also never tracked
or cleared on unmount, unlike its sibling pendingResizeTimerRef.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HafE8LJ1P3qsX6TokT9Fc
…-terminal-resize-loop-fix

# Conflicts:
#	.claude/commands/perf/make-it-faster.md
#	.claude/docs/codesigning.md
#	.github/workflows/benchmark.yml
#	.github/workflows/build.yml
#	.github/workflows/demo-publish.yml
#	.github/workflows/release-please.yml
#	.github/workflows/release.yml
#	.github/workflows/ux-analysis.yml
#	.gitignore
#	.golangci.yml
#	.release-please-manifest.json
#	CHANGELOG.md
#	CLAUDE.md
#	Formula/stapler-squad.rb
#	Makefile
#	config/config.go
#	config/config_test.go
#	config/defaults_test.go
#	config/types.go
#	docs/api/features/unfinished-work.md
#	docs/demos/accessibility-Accessibilit-2de24-ndary-routes-are-accessible-chromium-dom.gif
#	docs/demos/accessibility-Accessibilit-2de24-ndary-routes-are-accessible.gif
#	docs/demos/accessibility-Accessibilit-8ae87-us-accessibility-violations-chromium-dom.gif
#	docs/demos/accessibility-Accessibilit-8ae87-us-accessibility-violations.gif
#	docs/demos/backlog-Backlog-Backlog-To-b1401-after-it-has-been-dismissed-chromium-dom.gif
#	docs/demos/backlog-Backlog-Backlog-To-b1401-after-it-has-been-dismissed.gif
#	docs/demos/backlog-Backlog-Backlog-To-e4158-g-it-persists-across-reload-chromium-dom.gif
#	docs/demos/backlog-Backlog-Backlog-To-e4158-g-it-persists-across-reload.gif
#	docs/demos/backlog-Backlog-Empty-Stat-1590e--button-reveals-inline-form-chromium-dom.gif
#	docs/demos/backlog-Backlog-Empty-Stat-1590e--button-reveals-inline-form.gif
#	docs/demos/backlog-Backlog-Empty-Stat-2f4c8--button-disabled-when-empty-chromium-dom.gif
#	docs/demos/backlog-Backlog-Empty-Stat-2f4c8--button-disabled-when-empty.gif
#	docs/demos/backlog-Backlog-Empty-Stat-82f48--Clicking-cancel-hides-form-chromium-dom.gif
#	docs/demos/backlog-Backlog-Empty-Stat-82f48--Clicking-cancel-hides-form.gif
#	docs/demos/backlog-Backlog-Empty-Stat-96beb-ycle-diagram-and-CTA-button-chromium-dom.gif
#	docs/demos/backlog-Backlog-Empty-Stat-96beb-ycle-diagram-and-CTA-button.gif
#	docs/demos/backlog-Backlog-Filter-Zer-700a3-lters-button-resets-filters-chromium-dom.gif
#	docs/demos/backlog-Backlog-Filter-Zer-700a3-lters-button-resets-filters.gif
#	docs/demos/backlog-Backlog-Filter-Zer-9d4df-filters-shows-empty-message-chromium-dom.gif
#	docs/demos/backlog-Backlog-Filter-Zer-9d4df-filters-shows-empty-message.gif
#	docs/demos/backlog-Backlog-Item-Creat-a24f6-t-item-via-empty-state-form-chromium-dom.gif
#	docs/demos/backlog-Backlog-Item-Creat-a24f6-t-item-via-empty-state-form.gif
#	docs/demos/backlog-Backlog-Item-Creat-e6bc5--the-list-after-empty-state-chromium-dom.gif
#	docs/demos/backlog-Backlog-Item-Creat-e6bc5--the-list-after-empty-state.gif
#	docs/demos/backlog-Backlog-Item-Creat-ee2ef-d-with-default-priority-P3--chromium-dom.gif
#	docs/demos/backlog-Backlog-Item-Creat-ee2ef-d-with-default-priority-P3-.gif
#	docs/demos/backlog-Backlog-Page-Navig-eb97f-s-accessible-and-functional-chromium-dom.gif
#	docs/demos/backlog-Backlog-Page-Navig-eb97f-s-accessible-and-functional.gif
#	docs/demos/backlog-Backlog-Page-Navig-f014a-age-loads-and-is-accessible-chromium-dom.gif
#	docs/demos/backlog-Backlog-Page-Navig-f014a-age-loads-and-is-accessible.gif
#	docs/demos/backlog-Backlog-Status-Tra-98946-ed-to-ready-via-detail-pane-chromium-dom.gif
#	docs/demos/backlog-Backlog-Status-Tra-98946-ed-to-ready-via-detail-pane.gif
#	docs/demos/backlog-Backlog-Status-Tra-a3f73-Item-button-appears-in-list-chromium-dom.gif
#	docs/demos/backlog-Backlog-Status-Tra-a3f73-Item-button-appears-in-list.gif
#	docs/demos/backlog-Backlog-Status-Tra-a8d60-ture-not-yet-exposed-in-UI--chromium-dom.gif
#	docs/demos/backlog-Backlog-Status-Tra-a8d60-ture-not-yet-exposed-in-UI-.gif
#	docs/demos/backlog-Backlog-Triage-e2e-000e4-bled-when-repoPath-is-empty-chromium-dom.gif
#	docs/demos/backlog-Backlog-Triage-e2e-000e4-bled-when-repoPath-is-empty.gif
#	docs/demos/backlog-sources-settings-b-1d1f3-urce-and-see-it-in-the-list.gif
#	docs/demos/backlog-sources-settings-b-2c08b-le-a-source-s-enabled-state-chromium-dom.gif
#	docs/demos/backlog-sources-settings-b-2c08b-le-a-source-s-enabled-state.gif
#	docs/demos/backlog-sources-settings-b-6f2de-ce-removes-it-from-the-list-chromium-dom.gif
#	docs/demos/backlog-sources-settings-b-6f2de-ce-removes-it-from-the-list.gif
#	docs/demos/browser-passthrough-browse-5468d-tate-when-cdp-not-connected-chromium-dom.gif
#	docs/demos/browser-passthrough-browse-5468d-tate-when-cdp-not-connected.gif
#	docs/demos/browser-passthrough-browse-fb705-wser-tab-when-cdp-available-chromium-dom.gif
#	docs/demos/browser-passthrough-browse-fb705-wser-tab-when-cdp-available.gif
#	docs/demos/bulk-select-bulk-select-bu-1b31d-sessions-show-paused-status-chromium-dom.gif
#	docs/demos/bulk-select-bulk-select-bu-1b31d-sessions-show-paused-status.gif
#	docs/demos/bulk-select-bulk-select-bu-468ba--sessions-removed-from-list-chromium-dom.gif
#	docs/demos/bulk-select-bulk-select-bu-468ba--sessions-removed-from-list.gif
#	docs/demos/bulk-select-bulk-select-es-e26ba-xes-hidden-and-toolbar-gone-chromium-dom.gif
#	docs/demos/bulk-select-bulk-select-es-e26ba-xes-hidden-and-toolbar-gone.gif
#	docs/demos/bulk-select-bulk-select-sh-fb302-row-3-rows-1-3-are-selected-chromium-dom.gif
#	docs/demos/bulk-select-bulk-select-sh-fb302-row-3-rows-1-3-are-selected.gif
#	docs/demos/bulk-select-bulk-select-un-f128e--in-toast-sessions-reappear-chromium-dom.gif
#	docs/demos/bulk-select-bulk-select-un-f128e--in-toast-sessions-reappear.gif
#	docs/demos/demo-Demo-Flow.gif
#	docs/demos/enter-detection-enter-dete-79093-hould-loadPageWithoutErrors-chromium-dom.gif
#	docs/demos/enter-detection-enter-dete-79093-hould-loadPageWithoutErrors.gif
#	docs/demos/history-search-History-Sea-95175-ory-search-UI-is-accessible-chromium-dom.gif
#	docs/demos/history-search-History-Sea-95175-ory-search-UI-is-accessible.gif
#	docs/demos/insights-insights-dashboar-36984-ingOrData-When-apiAvailable-chromium-dom.gif
#	docs/demos/mobile-navigation-mobile-n-efd32-sionList-When-sessionsExist-chromium-dom.gif
#	docs/demos/mobile-navigation-mobile-n-efd32-sionList-When-sessionsExist.gif
#	docs/demos/nav-navigation-nav-navigat-11cca-hen-session-param-is-in-URL-chromium-dom.gif
#	docs/demos/nav-navigation-nav-navigat-11cca-hen-session-param-is-in-URL.gif
#	docs/demos/nav-navigation-nav-navigat-399f4-s-back-from-unfinished-page-chromium-dom.gif
#	docs/demos/nav-navigation-nav-navigat-399f4-s-back-from-unfinished-page.gif
#	docs/demos/nav-navigation-nav-navigat-84fe4-avigates-from-sessions-page-chromium-dom.gif
#	docs/demos/nav-navigation-nav-navigat-84fe4-avigates-from-sessions-page.gif
#	docs/demos/nav-navigation-nav-navigat-98d3c-avigates-from-sessions-page-chromium-dom.gif
#	docs/demos/nav-navigation-nav-navigat-98d3c-avigates-from-sessions-page.gif
#	docs/demos/nav-navigation-nav-navigat-ed5f6-hen-session-param-is-in-URL-chromium-dom.gif
#	docs/demos/nav-navigation-nav-navigat-ed5f6-hen-session-param-is-in-URL.gif
#	docs/demos/onboarding-hook-install-on-a4529-n-the-final-onboarding-step-chromium-dom.gif
#	docs/demos/onboarding-hook-install-on-a4529-n-the-final-onboarding-step.gif
#	docs/demos/review-queue-Review-Queue--12c9a-tem-from-DOM-optimistic-UI--chromium-dom.gif
#	docs/demos/review-queue-Review-Queue--12c9a-tem-from-DOM-optimistic-UI-.gif
#	docs/demos/review-queue-Review-Queue--45ba3--present-after-page-renders-chromium-dom.gif
#	docs/demos/review-queue-Review-Queue--45ba3--present-after-page-renders.gif
#	docs/demos/review-queue-Review-Queue--bf4d4-eue-page-loads-successfully-chromium-dom.gif
#	docs/demos/review-queue-Review-Queue--bf4d4-eue-page-loads-successfully.gif
#	docs/demos/review-queue-Review-Queue--e61a4-ies-acknowledge-data-testid-chromium-dom.gif
#	docs/demos/review-queue-Review-Queue--e61a4-ies-acknowledge-data-testid.gif
#	docs/demos/review-queue-Session-Creat-7ee30-eation-wizard-has-all-steps-chromium-dom.gif
#	docs/demos/review-queue-Session-Creat-7ee30-eation-wizard-has-all-steps.gif
#	docs/demos/review-queue-Session-Creat-8345c--form-has-required-test-IDs-chromium-dom.gif
#	docs/demos/review-queue-Session-Creat-8345c--form-has-required-test-IDs.gif
#	docs/demos/rules-yaml-import-rules-ya-19648--state-has-explanatory-text-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-19648--state-has-explanatory-text.gif
#	docs/demos/rules-yaml-import-rules-ya-3b89e-rt-duplicate-overwrite-mode-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-3b89e-rt-duplicate-overwrite-mode.gif
#	docs/demos/rules-yaml-import-rules-ya-552d0-les-and-shows-preview-cards-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-552d0-les-and-shows-preview-cards.gif
#	docs/demos/rules-yaml-import-rules-ya-62f98-ws-inline-validation-errors-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-62f98-ws-inline-validation-errors.gif
#	docs/demos/rules-yaml-import-rules-ya-6a69d-d-rules-and-refreshes-table-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-6a69d-d-rules-and-refreshes-table.gif
#	docs/demos/rules-yaml-import-rules-ya-7d21c--yaml-button-downloads-file-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-7d21c--yaml-button-downloads-file.gif
#	docs/demos/rules-yaml-import-rules-ya-daed9--import-duplicate-skip-mode-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-daed9--import-duplicate-skip-mode.gif
#	docs/demos/rules-yaml-import-rules-ya-fcaa2-port-modal-opens-and-closes-chromium-dom.gif
#	docs/demos/rules-yaml-import-rules-ya-fcaa2-port-modal-opens-and-closes.gif
#	docs/demos/session-create-directory-d-0e2cd-ory-session-type-in-payload-chromium-dom.gif
#	docs/demos/session-create-directory-d-0e2cd-ory-session-type-in-payload.gif
#	docs/demos/session-create-directory-d-11fc0--is-disabled-without-a-path-chromium-dom.gif
#	docs/demos/session-create-directory-d-11fc0--is-disabled-without-a-path.gif
#	docs/demos/session-create-directory-d-45cc2-ry-field-for-directory-mode-chromium-dom.gif
#	docs/demos/session-create-directory-d-45cc2-ry-field-for-directory-mode.gif
#	docs/demos/session-create-directory-d-7ec73-irectory-type-is-selectable-chromium-dom.gif
#	docs/demos/session-create-directory-d-7ec73-irectory-type-is-selectable.gif
#	docs/demos/session-create-directory-d-a9bd3--when-directory-is-selected-chromium-dom.gif
#	docs/demos/session-create-directory-d-a9bd3--when-directory-is-selected.gif
#	docs/demos/session-create-existing-wo-3a5aa-when-worktree-path-is-empty-chromium-dom.gif
#	docs/demos/session-create-existing-wo-3a5aa-when-worktree-path-is-empty.gif
#	docs/demos/session-create-existing-wo-6a4ba--for-existing-worktree-mode-chromium-dom.gif
#	docs/demos/session-create-existing-wo-6a4ba--for-existing-worktree-mode.gif
#	docs/demos/session-create-existing-wo-7131c-rktree-option-is-selectable-chromium-dom.gif
#	docs/demos/session-create-existing-wo-7131c-rktree-option-is-selectable.gif
#	docs/demos/session-create-existing-wo-79c0c-th-worktree-path-in-payload-chromium-dom.gif
#	docs/demos/session-create-existing-wo-79c0c-th-worktree-path-in-payload.gif
#	docs/demos/session-create-existing-wo-cde14-isting-worktree-is-selected-chromium-dom.gif
#	docs/demos/session-create-existing-wo-cde14-isting-worktree-is-selected.gif
#	docs/demos/session-create-existing-wo-dbe1a-isting-worktree-is-selected-chromium-dom.gif
#	docs/demos/session-create-existing-wo-dbe1a-isting-worktree-is-selected.gif
#	docs/demos/session-create-new-project-057be-irectory-hides-branch-field-chromium-dom.gif
#	docs/demos/session-create-new-project-057be-irectory-hides-branch-field.gif
#	docs/demos/session-create-new-project-1a3d0-parent-dir-and-project-name-chromium-dom.gif
#	docs/demos/session-create-new-project-1a3d0-parent-dir-and-project-name.gif
#	docs/demos/session-create-new-project-651a4-up-defaults-to-New-Worktree-chromium-dom.gif
#	docs/demos/session-create-new-project-651a4-up-defaults-to-New-Worktree.gif
#	docs/demos/session-create-new-project-789fd-and-project-name-are-filled-chromium-dom.gif
#	docs/demos/session-create-new-project-789fd-and-project-name-are-filled.gif
#	docs/demos/session-create-new-project-8f754-m-sends-correct-RPC-payload-chromium-dom.gif
#	docs/demos/session-create-new-project-8f754-m-sends-correct-RPC-payload.gif
#	docs/demos/session-create-new-project-90413-rectory-path-does-not-exist-chromium-dom.gif
#	docs/demos/session-create-new-project-90413-rectory-path-does-not-exist.gif
#	docs/demos/session-create-new-project-c4ade-it-without-creating-session-chromium-dom.gif
#	docs/demos/session-create-new-project-c4ade-it-without-creating-session.gif
#	docs/demos/session-create-new-project-d589d-s-visible-in-creation-panel-chromium-dom.gif
#	docs/demos/session-create-new-project-d589d-s-visible-in-creation-panel.gif
#	docs/demos/session-create-new-project-e22ba-hen-New-Project-is-selected-chromium-dom.gif
#	docs/demos/session-create-new-project-e22ba-hen-New-Project-is-selected.gif
#	docs/demos/session-create-new-project-ef382-dir-and-project-name-fields-chromium-dom.gif
#	docs/demos/session-create-new-project-ef382-dir-and-project-name-fields.gif
#	docs/demos/session-create-new-project-fb84d-t-with-createIfMissing-true-chromium-dom.gif
#	docs/demos/session-create-new-project-fb84d-t-with-createIfMissing-true.gif
#	docs/demos/session-create-new-worktre-0fc57-ee-is-the-default-selection-chromium-dom.gif
#	docs/demos/session-create-new-worktre-0fc57-ee-is-the-default-selection.gif
#	docs/demos/session-create-new-worktre-7f600-type-with-branch-in-payload-chromium-dom.gif
#	docs/demos/session-create-new-worktre-7f600-type-with-branch-in-payload.gif
#	docs/demos/session-create-new-worktre-9dd2f-ckbox-for-new-worktree-mode-chromium-dom.gif
#	docs/demos/session-create-new-worktre-9dd2f-ckbox-for-new-worktree-mode.gif
#	docs/demos/session-create-new-worktre-ce636-itle-as-branch-is-unchecked-chromium-dom.gif
#	docs/demos/session-create-new-worktre-ce636-itle-as-branch-is-unchecked.gif
#	docs/demos/session-create-new-worktre-ce772--title-as-branch-is-checked-chromium-dom.gif
#	docs/demos/session-create-new-worktre-ce772--title-as-branch-is-checked.gif
#	docs/demos/session-lifecycle-Session--10237-Session-status-filter-works-chromium-dom.gif
#	docs/demos/session-lifecycle-Session--10237-Session-status-filter-works.gif
#	docs/demos/session-lifecycle-Session--2a57f-ion-create-UI-is-accessible-chromium-dom.gif
#	docs/demos/session-lifecycle-Session--2a57f-ion-create-UI-is-accessible.gif
#	docs/demos/session-lifecycle-Session--7b493-ssion-management-page-loads-chromium-dom.gif
#	docs/demos/session-lifecycle-Session--7b493-ssion-management-page-loads.gif
#	docs/demos/session-lifecycle-Session--995da-paused-sessions-are-visible-chromium-dom.gif
#	docs/demos/session-lifecycle-Session--995da-paused-sessions-are-visible.gif
#	docs/demos/shell-tabs-shell-tabs-shel-39ca1-ellDialog-When-ctrlTPressed-chromium-dom.gif
#	docs/demos/shell-tabs-shell-tabs-shel-39ca1-ellDialog-When-ctrlTPressed.gif
#	docs/demos/shell-tabs-shell-tabs-shel-684a1-lTab-When-plusButtonClicked-chromium-dom.gif
#	docs/demos/shell-tabs-shell-tabs-shel-684a1-lTab-When-plusButtonClicked.gif
#	docs/demos/shell-tabs-shell-tabs-shel-70877-actionMenuSpawnShellClicked-chromium-dom.gif
#	docs/demos/shell-tabs-shell-tabs-shel-70877-actionMenuSpawnShellClicked.gif
#	docs/demos/shell-tabs-shell-tabs-shel-e3a96-ab-When-deleteButtonClicked-chromium-dom.gif
#	docs/demos/shell-tabs-shell-tabs-shel-e3a96-ab-When-deleteButtonClicked.gif
#	docs/demos/smoke-Smoke-Tests-home-page-loads-successfully-chromium-dom.gif
#	docs/demos/smoke-Smoke-Tests-home-page-loads-successfully.gif
#	docs/demos/smoke-Smoke-Tests-navigation-header-is-present-chromium-dom.gif
#	docs/demos/smoke-Smoke-Tests-navigation-header-is-present.gif
#	docs/demos/smoke-Smoke-Tests-review-queue-page-loads-successfully-chromium-dom.gif
#	docs/demos/smoke-Smoke-Tests-review-queue-page-loads-successfully.gif
#	docs/demos/terminal-mobile-overflow-m-50056--visible-on-mobile-viewport-chromium-dom.gif
#	docs/demos/terminal-mobile-overflow-m-50056--visible-on-mobile-viewport.gif
#	docs/demos/terminal-mobile-overflow-m-53f8a-le-without-opening-overflow-chromium-dom.gif
#	docs/demos/terminal-mobile-overflow-m-53f8a-le-without-opening-overflow.gif
#	docs/demos/terminal-mobile-overflow-m-64891--row-with-secondary-buttons-chromium-dom.gif
#	docs/demos/terminal-mobile-overflow-m-64891--row-with-secondary-buttons.gif
#	docs/demos/terminal-mobile-overflow-m-76599-Less-hides-the-overflow-row-chromium-dom.gif
#	docs/demos/terminal-mobile-overflow-m-76599-Less-hides-the-overflow-row.gif
#	docs/demos/terminal-resize-terminal-r-9b065-tays-connected-after-resize.gif
#	docs/demos/terminal-resize-terminal-r-e5784--after-resize-dom-renderer--chromium-dom.gif
#	docs/demos/theme-background-theme-bac-3d682-background-under-dark-theme-chromium-dom.gif
#	docs/demos/theme-background-theme-bac-3d682-background-under-dark-theme.gif
#	docs/demos/touch-targets-Touch-target-0c83a--keyboard-keys-are-≥44×44px-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-0c83a--keyboard-keys-are-≥44×44px.gif
#	docs/demos/touch-targets-Touch-target-31878-tton-is-≥44×44px-on-desktop-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-31878-tton-is-≥44×44px-on-desktop.gif
#	docs/demos/touch-targets-Touch-target-66543--session-button-is-≥44×44px-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-66543--session-button-is-≥44×44px.gif
#	docs/demos/touch-targets-Touch-target-b4371--actions-button-is-≥44×44px-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-b4371--actions-button-is-≥44×44px.gif
#	docs/demos/touch-targets-Touch-target-cbd53--toolbar-toggle-is-≥44×44px-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-cbd53--toolbar-toggle-is-≥44×44px.gif
#	docs/demos/touch-targets-Touch-target-e8172-om-nav-items-are-≥44px-tall-chromium-dom.gif
#	docs/demos/touch-targets-Touch-target-e8172-om-nav-items-are-≥44px-tall.gif
#	docs/demos/visual-regression-omnibar-open-chromium-dom.gif
#	docs/demos/visual-regression-omnibar-open.gif
#	docs/demos/visual-regression-session-list-empty-state-chromium-dom.gif
#	docs/demos/visual-regression-session-list-empty-state.gif
#	docs/demos/workspace-management-Works-debfe-e-information-is-accessible-chromium-dom.gif
#	docs/demos/workspace-management-Works-debfe-e-information-is-accessible.gif
#	docs/demos/workspace-management-Works-ee243-h---Review-queue-page-loads-chromium-dom.gif
#	docs/demos/workspace-management-Works-ee243-h---Review-queue-page-loads.gif
#	docs/registry/features/backend/ImportGitHubIssue.json
#	docs/registry/features/backend/ListGitHubIssues.json
#	docs/registry/features/backend/SearchGitHubRepos.json
#	docs/registry/features/backend/backlog/approve-plan.json
#	docs/registry/features/backend/backlog/archive-item.json
#	docs/registry/features/backend/backlog/attach-session.json
#	docs/registry/features/backend/backlog/cancel-triage.json
#	docs/registry/features/backend/backlog/create-item.json
#	docs/registry/features/backend/backlog/create-source.json
#	docs/registry/features/backend/backlog/delete-item.json
#	docs/registry/features/backend/backlog/delete-source.json
#	docs/registry/features/backend/backlog/get-item.json
#	docs/registry/features/backend/backlog/get-sync-history.json
#	docs/registry/features/backend/backlog/list-items.json
#	docs/registry/features/backend/backlog/list-sources.json
#	docs/registry/features/backend/backlog/override-verdict.json
#	docs/registry/features/backend/backlog/spawn-session.json
#	docs/registry/features/backend/backlog/suggest-next.json
#	docs/registry/features/backend/backlog/transition-status.json
#	docs/registry/features/backend/backlog/trigger-re-review.json
#	docs/registry/features/backend/backlog/trigger-sync.json
#	docs/registry/features/backend/backlog/trigger-triage.json
#	docs/registry/features/backend/backlog/update-item.json
#	docs/registry/features/backend/backlog/update-source.json
#	docs/registry/features/backend/session/run-one-shot.json
#	docs/registry/features/frontend/ui/backlog-board.json
#	docs/registry/features/frontend/ui/backlog-item-card.json
#	docs/registry/features/frontend/ui/backlog-item-detail.json
#	docs/registry/features/frontend/ui/backlog-item-form.json
#	docs/registry/features/frontend/ui/backlog-list-page.json
#	docs/tasks/completed/system-service-autostart.md
#	gen/proto/go/session/v1/backlog.pb.go
#	gen/proto/go/session/v1/session.pb.go
#	gen/proto/go/session/v1/sessionv1connect/backlog.connect.go
#	gen/proto/go/session/v1/types.pb.go
#	github/client.go
#	github/clone.go
#	github/etag_cache.go
#	github/http_client.go
#	github/keychain.go
#	github/repos.go
#	github/user_pr_cache.go
#	go.mod
#	go.sum
#	main.go
#	pkg/events/types.go
#	profiling/profiling.go
#	proto/session/v1/backlog.proto
#	proto/session/v1/session.proto
#	proto/session/v1/types.proto
#	scripts/build-tmux.sh
#	scripts/install-service.sh
#	scripts/setup-codesign.sh
#	server/adapters/instance_adapter.go
#	server/adapters/review_queue_adapter.go
#	server/adapters/review_queue_adapter_test.go
#	server/analytics/subscriber.go
#	server/analytics/subscriber_test.go
#	server/dependencies.go
#	server/dependencies_test.go
#	server/events/forward.go
#	server/features/backlog.go
#	server/mcp/server.go
#	server/mcp/server_integration_test.go
#	server/mcp/tools_backlog.go
#	server/mcp/tools_backlog_test.go
#	server/mcp/tools_github.go
#	server/mcp/tools_goal.go
#	server/mcp/tools_goal_test.go
#	server/mcp/tools_terminal.go
#	server/mcp/tools_terminal_test.go
#	server/push/subscriber.go
#	server/push/subscriber_test.go
#	server/review_queue_manager.go
#	server/review_queue_manager_test.go
#	server/server.go
#	server/server_integration_test.go
#	server/services/approval_handler.go
#	server/services/autonomous_orchestration_service.go
#	server/services/autonomous_orchestration_service_test.go
#	server/services/backlog_github_rpc_test.go
#	server/services/backlog_service.go
#	server/services/backlog_service_test.go
#	server/services/backlog_triage_harness_test.go
#	server/services/connectrpc_websocket.go
#	server/services/defaults_service.go
#	server/services/defaults_service_test.go
#	server/services/event_converter_test.go
#	server/services/feature_flag_service.go
#	server/services/file_service.go
#	server/services/file_service_test.go
#	server/services/github_user_service.go
#	server/services/hook_injector.go
#	server/services/hook_injector_test.go
#	server/services/hook_receivers.go
#	server/services/local_file_service.go
#	server/services/local_file_service_test.go
#	server/services/mcp_injector.go
#	server/services/mcp_injector_test.go
#	server/services/oneshot_test.go
#	server/services/path_completion_service.go
#	server/services/path_completion_service_test.go
#	server/services/push_service.go
#	server/services/search_service.go
#	server/services/session_service.go
#	server/services/session_service_create_test.go
#	server/services/session_service_shells.go
#	server/services/session_service_stream_terminal_test.go
#	server/services/session_service_test.go
#	server/services/terminal_service.go
#	server/services/unfinished_work_service.go
#	server/services/unfinished_work_test.go
#	server/services/workspace_service_test.go
#	session/actor.go
#	session/autonomous_driver.go
#	session/autonomous_driver_test.go
#	session/backlog.go
#	session/backlog_commands.go
#	session/backlog_commands_test.go
#	session/backlog_context.go
#	session/backlog_context_test.go
#	session/backlog_integration_test.go
#	session/backlog_lifecycle.go
#	session/backlog_lifecycle_test.go
#	session/backlog_plugin_github.go
#	session/backlog_plugin_github_prs.go
#	session/backlog_plugin_github_test.go
#	session/backlog_review.go
#	session/backlog_review_test.go
#	session/backlog_sync.go
#	session/backlog_sync_test.go
#	session/backlog_test.go
#	session/backlog_triage.go
#	session/backlog_triage_test.go
#	session/capture_test.go
#	session/circular_buffer.go
#	session/claude_command_builder.go
#	session/claude_controller.go
#	session/detection/approval.go
#	session/detection/detector_test.go
#	session/detection/pattern_set.go
#	session/detection/pattern_set_test.go
#	session/detection/proto_mapping.go
#	session/ent/backlogitem.go
#	session/ent/backlogitem/backlogitem.go
#	session/ent/backlogitem/where.go
#	session/ent/backlogitem_create.go
#	session/ent/backlogitem_query.go
#	session/ent/backlogitem_update.go
#	session/ent/backlogstatusevent.go
#	session/ent/backlogstatusevent/backlogstatusevent.go
#	session/ent/backlogstatusevent/where.go
#	session/ent/backlogstatusevent_create.go
#	session/ent/backlogstatusevent_update.go
#	session/ent/client.go
#	session/ent/ent.go
#	session/ent/hook/hook.go
#	session/ent/itemsession.go
#	session/ent/itemsession/itemsession.go
#	session/ent/itemsession/where.go
#	session/ent/itemsession_create.go
#	session/ent/itemsession_update.go
#	session/ent/migrate/schema.go
#	session/ent/mutation.go
#	session/ent/predicate/predicate.go
#	session/ent/runtime.go
#	session/ent/schema/backlog_item.go
#	session/ent/schema/backlog_status_event.go
#	session/ent/schema/item_session.go
#	session/ent/schema/session_goal.go
#	session/ent/sessiongoal.go
#	session/ent/sessiongoal/sessiongoal.go
#	session/ent/sessiongoal/where.go
#	session/ent/sessiongoal_create.go
#	session/ent/sessiongoal_update.go
#	session/ent/tx.go
#	session/ent_repository.go
#	session/ent_repository_backlog.go
#	session/ent_repository_backlog_test.go
#	session/external_tmux_streamer.go
#	session/git/util.go
#	session/git/worktree.go
#	session/git/worktree_creation_test.go
#	session/git/worktree_git.go
#	session/git/worktree_git_test.go
#	session/git/worktree_ops.go
#	session/git_worktree_manager.go
#	session/headless/caller.go
#	session/headless/client.go
#	session/headless/fake_runner.go
#	session/headless/features.go
#	session/headless/features_test.go
#	session/headless/integration_test.go
#	session/headless/pool_test.go
#	session/headless/runner.go
#	session/health.go
#	session/health_test.go
#	session/hibernation_sweeper_test.go
#	session/instance.go
#	session/instance_actor_setters.go
#	session/instance_approval.go
#	session/instance_checkpoint.go
#	session/instance_claude.go
#	session/instance_controller.go
#	session/instance_hibernate.go
#	session/instance_lifecycle_test.go
#	session/instance_serialization.go
#	session/instance_shells.go
#	session/instance_state.go
#	session/instance_terminal.go
#	session/instance_tmux.go
#	session/instance_tmux_test.go
#	session/instance_workspace.go
#	session/instance_workspace_test.go
#	session/instance_worktree.go
#	session/integration_test.go
#	session/memory/reader.go
#	session/mux/multiplexer.go
#	session/mux/testmain_test.go
#	session/orphan_sweep.go
#	session/orphan_sweep_test.go
#	session/pr_status_poller.go
#	session/pty_discovery.go
#	session/pty_discovery_test.go
#	session/repo_path.go
#	session/repository.go
#	session/review_queue_determiner.go
#	session/review_queue_determiner_test.go
#	session/review_queue_poller.go
#	session/review_queue_poller_test.go
#	session/review_state.go
#	session/session_driver.go
#	session/session_driver_test.go
#	session/startup_scanner_test.go
#	session/state_machine_test.go
#	session/status_mapping.go
#	session/status_mapping_test.go
#	session/storage.go
#	session/storage_backlog.go
#	session/storage_goal_test.go
#	session/storage_test.go
#	session/tmux/control_mode.go
#	session/tmux/server_registry.go
#	session/tmux/server_registry_integration_test.go
#	session/tmux/testmain_test.go
#	session/tmux/tmux.go
#	session/tmux/tmux_test.go
#	session/tmux_backend_test.go
#	session/tmux_process_manager.go
#	session/unfinished/gogit_vcs_reader.go
#	session/unfinished/gogit_vcs_reader_limits_test.go
#	session/unfinished/scanner.go
#	session/unfinished/scanner_test.go
#	session/worktree_pr_poller.go
#	telemetry/telemetry.go
#	tests/e2e/accessibility.spec.ts
#	tests/e2e/backlog-sources-settings.spec.ts
#	tests/e2e/backlog.spec.ts
#	tests/e2e/fixtures/clean-theme.json
#	tests/e2e/fixtures/cyberpunk77-theme.json
#	tests/e2e/fixtures/matrix-theme.json
#	tests/e2e/fixtures/wh40k-theme.json
#	tests/e2e/nav-navigation.spec.ts
#	tests/e2e/package-lock.json
#	tests/e2e/pages/BacklogPage.ts
#	tests/e2e/session-create-directory.spec.ts
#	tests/e2e/session-create-existing-worktree.spec.ts
#	tests/e2e/touch-targets.spec.ts
#	tests/e2e/unfinished-work.spec.ts
#	third_party/tmux~origin_main
#	tools/lint/cmd/linter/main.go
#	tools/scanner/backend/proto_scanner.go
#	web-app/.eslintrc.json
#	web-app/jest.config.js
#	web-app/jest.setup.js
#	web-app/lighthouserc.json
#	web-app/package-lock.json
#	web-app/package.json
#	web-app/pnpm-lock.yaml
#	web-app/src/__mocks__/styleMock.js
#	web-app/src/app/backlog/backlog.css.ts
#	web-app/src/app/backlog/board/page.tsx
#	web-app/src/app/backlog/page.tsx
#	web-app/src/app/config/ConfigPageContent.tsx
#	web-app/src/app/config/config.css.ts
#	web-app/src/app/files/page.tsx
#	web-app/src/app/insights/InsightsDashboard.tsx
#	web-app/src/app/insights/SessionDetailDrawer.css.ts
#	web-app/src/app/insights/SessionDetailDrawer.tsx
#	web-app/src/app/insights/SessionsTable.css.ts
#	web-app/src/app/insights/SessionsTable.tsx
#	web-app/src/app/page.tsx
#	web-app/src/app/rules/page.css.ts
#	web-app/src/app/rules/page.tsx
#	web-app/src/app/settings/backlog-sources/page.tsx
#	web-app/src/app/settings/features/page.tsx
#	web-app/src/app/settings/page.tsx
#	web-app/src/app/settings/settings.css.ts
#	web-app/src/app/unfinished/UnfinishedTab.css.ts
#	web-app/src/app/unfinished/UnfinishedTab.tsx
#	web-app/src/app/unfinished/page.tsx
#	web-app/src/components/backlog/BacklogBoard.css.ts
#	web-app/src/components/backlog/BacklogBoard.tsx
#	web-app/src/components/backlog/BacklogItemBadge.tsx
#	web-app/src/components/backlog/BacklogItemCard.css.ts
#	web-app/src/components/backlog/BacklogItemCard.tsx
#	web-app/src/components/backlog/BacklogItemDetail.css.ts
#	web-app/src/components/backlog/BacklogItemDetail.regression.test.tsx
#	web-app/src/components/backlog/BacklogItemDetail.tsx
#	web-app/src/components/backlog/BacklogItemForm.css.ts
#	web-app/src/components/backlog/BacklogItemForm.test.tsx
#	web-app/src/components/backlog/BacklogItemForm.tsx
#	web-app/src/components/backlog/BacklogItemPanel.css.ts
#	web-app/src/components/backlog/BacklogItemPanel.tsx
#	web-app/src/components/backlog/GateVerdictBox.css.ts
#	web-app/src/components/backlog/GateVerdictBox.test.tsx
#	web-app/src/components/backlog/GateVerdictBox.tsx
#	web-app/src/components/backlog/GitHubIssuePicker.css.ts
#	web-app/src/components/backlog/GitHubIssuePicker.tsx
#	web-app/src/components/backlog/SessionMonitor.css.ts
#	web-app/src/components/backlog/SessionMonitor.tsx
#	web-app/src/components/backlog/TriageReviewPanel.css.ts
#	web-app/src/components/backlog/TriageReviewPanel.test.tsx
#	web-app/src/components/backlog/TriageReviewPanel.tsx
#	web-app/src/components/files/LocalFileBrowser.css.ts
#	web-app/src/components/files/LocalFileBrowser.tsx
#	web-app/src/components/layout/DrawerNav.tsx
#	web-app/src/components/layout/Header.css.ts
#	web-app/src/components/layout/Header.tsx
#	web-app/src/components/layout/__tests__/DrawerNav.test.tsx
#	web-app/src/components/layout/__tests__/Header.test.tsx
#	web-app/src/components/pane/PaneHeader.tsx
#	web-app/src/components/pane/PaneSplitRenderer.tsx
#	web-app/src/components/rules/RulePreview.tsx
#	web-app/src/components/sessions/ApprovalAnalyticsPanel.css.ts
#	web-app/src/components/sessions/DetectionEventsPanel.tsx
#	web-app/src/components/sessions/FileContentViewer.css.ts
#	web-app/src/components/sessions/FileContentViewer.tsx
#	web-app/src/components/sessions/FileTree.css.ts
#	web-app/src/components/sessions/FileTree.tsx
#	web-app/src/components/sessions/FilesTab.css.ts
#	web-app/src/components/sessions/FilesTab.tsx
#	web-app/src/components/sessions/NewShellDialog.css.ts
#	web-app/src/components/sessions/NewShellDialog.tsx
#	web-app/src/components/sessions/Omnibar.tsx
#	web-app/src/components/sessions/OmnibarCreationPanel.tsx
#	web-app/src/components/sessions/QuickOpenPalette.css.ts
#	web-app/src/components/sessions/QuickOpenPalette.tsx
#	web-app/src/components/sessions/RecentFilesSection.css.ts
#	web-app/src/components/sessions/RecentFilesSection.tsx
#	web-app/src/components/sessions/ReviewQueuePanel.css.ts
#	web-app/src/components/sessions/ReviewQueuePanel.tsx
#	web-app/src/components/sessions/SessionActionsOverflow.tsx
#	web-app/src/components/sessions/SessionCard.tsx
#	web-app/src/components/sessions/SessionDetailView.tsx
#	web-app/src/components/sessions/SessionList.css.ts
#	web-app/src/components/sessions/SessionList.tsx
#	web-app/src/components/sessions/SessionRow.tsx
#	web-app/src/components/sessions/StatusBadge.tsx
#	web-app/src/components/sessions/SubStatusChip.tsx
#	web-app/src/components/sessions/TerminalOutput.tsx
#	web-app/src/components/sessions/VcsPanel.css.ts
#	web-app/src/components/sessions/VcsPanel.tsx
#	web-app/src/components/sessions/XtermTerminal.tsx
#	web-app/src/components/sessions/__tests__/FileTree.test.tsx
#	web-app/src/components/sessions/__tests__/NewShellDialog.test.tsx
#	web-app/src/components/sessions/__tests__/Omnibar.alias.test.tsx
#	web-app/src/components/sessions/__tests__/Omnibar.pathcompletion.test.tsx
#	web-app/src/components/sessions/__tests__/OmnibarCreationPanel.attach.test.tsx
#	web-app/src/components/sessions/__tests__/ReviewQueuePanel.test.tsx
#	web-app/src/components/sessions/__tests__/SessionActionsOverflow.test.tsx
#	web-app/src/components/sessions/__tests__/SessionCard.approval-suppression.test.tsx
#	web-app/src/components/sessions/__tests__/SessionCard.click.test.tsx
#	web-app/src/components/sessions/__tests__/SessionDetail.embedded.test.tsx
#	web-app/src/components/sessions/__tests__/StatusBadge.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutput.enter-detection.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutput.logstream.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutput.reconnect.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutput.toolbar-analytics.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutput.upload.test.tsx
#	web-app/src/components/sessions/__tests__/TerminalOutputBug.test.tsx
#	web-app/src/components/sessions/__tests__/XtermTerminal.test.tsx
#	web-app/src/components/settings/GlobalDefaultsForm.css.ts
#	web-app/src/components/settings/GlobalDefaultsForm.tsx
#	web-app/src/components/shared/DiffRenderer.css.ts
#	web-app/src/components/shared/DiffRenderer.tsx
#	web-app/src/components/ui/NotificationPanel.css.ts
#	web-app/src/components/ui/RepoPathInput.tsx
#	web-app/src/components/unfinished/CommitPushModal.css.ts
#	web-app/src/components/unfinished/CommitPushModal.tsx
#	web-app/src/components/unfinished/UnfinishedItem.css.ts
#	web-app/src/components/unfinished/UnfinishedItemDetail.css.ts
#	web-app/src/components/unfinished/UnfinishedItemDetail.tsx
#	web-app/src/components/unfinished/WorktreeDiffModal.css.ts
#	web-app/src/components/workflows/WorkflowForm.tsx
#	web-app/src/gen/session/v1/backlog_pb.ts
#	web-app/src/gen/session/v1/session_pb.ts
#	web-app/src/gen/session/v1/types_pb.ts
#	web-app/src/lib/backlog/status.ts
#	web-app/src/lib/contexts/FeatureFlagsContext.tsx
#	web-app/src/lib/contexts/NotificationContext.tsx
#	web-app/src/lib/contexts/OmnibarContext.tsx
#	web-app/src/lib/contexts/SessionServiceContext.tsx
#	web-app/src/lib/features/features/unfinished-work.ts
#	web-app/src/lib/hooks/__tests__/useBrowserLogStream.test.ts
#	web-app/src/lib/hooks/__tests__/useSessionNotifications.test.ts
#	web-app/src/lib/hooks/__tests__/useSessionService.test.ts
#	web-app/src/lib/hooks/__tests__/useTerminalFlowControl.test.ts
#	web-app/src/lib/hooks/useBacklogService.ts
#	web-app/src/lib/hooks/useBrowserLogStream.ts
#	web-app/src/lib/hooks/useGitHubIssuePicker.ts
#	web-app/src/lib/hooks/useSessionNotifications.ts
#	web-app/src/lib/hooks/useSessionService.ts
#	web-app/src/lib/hooks/useTerminalFlowControl.ts
#	web-app/src/lib/hooks/useTerminalStream.ts
#	web-app/src/lib/hooks/useVcsStatus.ts
#	web-app/src/lib/hooks/useWorktreeSuggestions.ts
#	web-app/src/lib/nav-pages.ts
#	web-app/src/lib/omnibar/actions/dispatch.test.ts
#	web-app/src/lib/omnibar/actions/dispatch.ts
#	web-app/src/lib/omnibar/actions/types.ts
#	web-app/src/lib/omnibar/detector.test.ts
#	web-app/src/lib/omnibar/detectors/CommandDetector.ts
#	web-app/src/lib/omnibar/types.ts
#	web-app/src/lib/routes.ts
#	web-app/src/lib/store/__tests__/reviewQueueSlice.test.ts
#	web-app/src/lib/store/__tests__/sessionsSlice.test.ts
#	web-app/src/lib/store/store.ts
#	web-app/src/lib/utils/__tests__/deriveWorkingState.test.ts
#	web-app/src/lib/utils/deriveWorkingState.ts
#	web-app/src/lib/utils/issuePickerCache.ts
#	web-app/src/lib/utils/parseDiff.ts
#	web-app/src/styles/pane/paneHeader.css.ts
#	web-app/src/styles/theme-contract.css.ts
#	web-app/src/styles/theme.css.ts
#	web-app/tests/e2e/navigation.spec.ts
…nflict resolution

Resolving the origin/main merge (LFS history cutover left this branch far
behind main's history) required bulk-resolving hundreds of untouched-by-this-PR
conflicts to main's side. That correctly dropped stale duplicate content, but
also silently dropped a handful of pre-existing fixes/features that exist only
on this branch (inherited from an earlier, now-unreachable main state) with no
main-side equivalent:

- session/detection/proto_mapping.go: restore DetectedStatusToSubStatus,
  which proto_mapping_test.go (auto-merged, unaffected by the bulk resolve)
  already exercises.
- web-app/src/components/sessions/SubStatusChip.tsx: restore the
  forward-compatible "render nothing" default case for unrecognized
  SubStatus values instead of the throwing assertNever main's revision uses
  — proto enums are forward-compatible and one unrecognized wire value must
  not crash the sessions UI.
- TerminalOutput{,.reconnect,.enter-detection,.logstream,.toolbar-analytics,
  .upload}.test.tsx and TerminalOutputBug.test.tsx: add the
  requestFullResync/markResyncComplete/markPaneResponseReceived mock fields
  useVisibilityResync (only present on this branch, not main) calls
  unconditionally on unmount/session-id change, plus the XtermTerminal
  mock's resize() stub the pre-sizing effect now calls.

Found by running the full frontend/backend test suites after the merge
rather than trusting the conflict resolution alone.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

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

Building backend scanner...
Scanning backend features...
Wrote 117 feature files to /tmp/tmp.QXXVuu6qXk/backend
Wrote 15 feature files to /tmp/tmp.QXXVuu6qXk/backend
Wrote 45 feature files to /tmp/tmp.QXXVuu6qXk/backend
Wrote 7 feature files to /tmp/tmp.QXXVuu6qXk/backend
Wrote 11 feature files to /tmp/tmp.QXXVuu6qXk/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 19/179 features have testIds (10.6%)

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

@github-actions

github-actions Bot commented Jul 28, 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
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                
                                            │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                                            │              sec/op              │   sec/op     vs base              │
CircularBufferWrite_4KB-4                                          80.67n ± 2%   82.86n ± 1%  +2.71% (p=0.001 n=8)
CircularBufferWrite_4KB_Allocs-4                                   80.88n ± 0%   83.58n ± 0%  +3.34% (p=0.000 n=8)
CircularBufferGetRecent_4KB-4                                      532.5n ± 7%   501.3n ± 3%  -5.86% (p=0.007 n=7)
CircularBufferGetAll-4                                             3.944µ ± 2%   3.878µ ± 1%  -1.66% (p=0.028 n=8)
GetTimeSinceLastMeaningfulOutput_HotPath-4                         65.86n ± 1%   65.80n ± 0%       ~ (p=0.343 n=8)
GetTimeSinceLastMeaningfulOutput_ColdPath-4                        32.82n ± 1%   32.77n ± 0%       ~ (p=0.079 n=7)
geomean                                                            175.9n        175.3n       -0.33%

                                            │ benchmarks/go/tier1-baseline.txt │           tier1-bench.txt            │
                                            │               B/op               │     B/op      vs base                │
CircularBufferWrite_4KB-4                                         0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferGetRecent_4KB-4                                   4.000Ki ± 0%     4.000Ki ± 0%       ~ (p=1.000 n=7) ¹
CircularBufferGetAll-4                                          40.00Ki ± 0%     40.00Ki ± 0%       ~ (p=1.000 n=8) ¹
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=7) ¹
geomean                                                                      ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                            │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                                            │            allocs/op             │ allocs/op   vs base                │
CircularBufferWrite_4KB-4                                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferGetRecent_4KB-4                                     1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=7) ¹
CircularBufferGetAll-4                                            1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=8) ¹
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=7) ¹
geomean                                                                      ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                              │               B/s                │     B/s       vs base              │
CircularBufferWrite_4KB-4                           47.29Gi ± 2%   46.04Gi ± 0%  -2.63% (p=0.001 n=8)
CircularBufferGetRecent_4KB-4                       7.164Gi ± 7%   7.609Gi ± 3%  +6.21% (p=0.007 n=7)
geomean                                             18.41Gi        18.72Gi       +1.69%

pkg: github.com/tstapler/stapler-squad/session/detection/ratelimit
                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                              │              sec/op              │   sec/op     vs base              │
StripANSI_PlainText-4                                6.882n ± 0%   6.867n ± 1%       ~ (p=0.185 n=8)
StripANSI_WithEscapes-4                              749.0n ± 0%   748.4n ± 0%       ~ (p=0.593 n=8)
ProcessOutput_InactiveState-4                        6.337n ± 1%   6.340n ± 1%       ~ (p=0.666 n=8)
geomean                                              31.97n        31.94n       -0.08%

                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                              │               B/op               │    B/op     vs base                │
StripANSI_PlainText-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
StripANSI_WithEscapes-4                             136.0 ± 0%     136.0 ± 0%       ~ (p=1.000 n=8) ¹
ProcessOutput_InactiveState-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                        ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                              │            allocs/op             │ allocs/op   vs base                │
StripANSI_PlainText-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
StripANSI_WithEscapes-4                             5.000 ± 0%     5.000 ± 0%       ~ (p=1.000 n=8) ¹
ProcessOutput_InactiveState-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                        ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/queue
                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                              │              sec/op              │   sec/op     vs base              │
ReviewQueue_ConcurrentReads-4                        85.34n ± 6%   84.79n ± 4%       ~ (p=0.130 n=8)
ReviewQueue_Add-4                                    511.9n ± 1%   504.2n ± 0%  -1.51% (p=0.001 n=8)
geomean                                              209.0n        206.8n       -1.08%

                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                              │               B/op               │    B/op     vs base                │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
ReviewQueue_Add-4                                   640.0 ± 0%     640.0 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                        ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                              │            allocs/op             │ allocs/op   vs base                │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
ReviewQueue_Add-4                                   4.000 ± 0%     4.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                        ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/scrollback
                                      │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                                      │              sec/op              │   sec/op     vs base               │
CircularBuffer_ConcurrentReadWrite-4                         3.977µ ± 2%   3.955µ ± 2%        ~ (p=0.169 n=8)
CircularBuffer_BurstAppend-4                                 102.7µ ± 1%   102.2µ ± 2%        ~ (p=0.195 n=8)
CircularBuffer_GetLastN_LargeBuffer-4                        20.34µ ± 0%   20.01µ ± 1%   -1.61% (p=0.010 n=8)
CircularBuffer_GetRange_Sequential-4                         11.04µ ± 2%   13.63µ ± 5%  +23.43% (p=0.000 n=8)
CircularBufferAppend-4                                       98.39n ± 1%   99.95n ± 1%   +1.58% (p=0.000 n=8)
CircularBufferGetLastN-4                                     2.260µ ± 2%   2.554µ ± 2%  +13.03% (p=0.000 n=8)
CircularBufferConcurrentAppend-4                             130.3n ± 0%   125.1n ± 0%   -3.99% (p=0.000 n=8)
geomean                                                      3.085µ        3.211µ        +4.09%

                                      │ benchmarks/go/tier1-baseline.txt │           tier1-bench.txt            │
                                      │               B/op               │     B/op      vs base                │
CircularBuffer_ConcurrentReadWrite-4                        6.062Ki ± 0%   6.062Ki ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_BurstAppend-4                                62.50Ki ± 0%   62.50Ki ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_GetLastN_LargeBuffer-4                       56.00Ki ± 0%   56.00Ki ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_GetRange_Sequential-4                        28.00Ki ± 0%   28.00Ki ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferAppend-4                                        24.00 ± 0%     24.00 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferGetLastN-4                                    6.000Ki ± 0%   6.000Ki ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferConcurrentAppend-4                              32.00 ± 0%     32.00 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                     3.077Ki        3.077Ki       +0.00%
¹ all samples are equal

                                      │ benchmarks/go/tier1-baseline.txt │           tier1-bench.txt           │
                                      │            allocs/op             │  allocs/op   vs base                │
CircularBuffer_ConcurrentReadWrite-4                          2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_BurstAppend-4                                 1.000k ± 0%   1.000k ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_GetLastN_LargeBuffer-4                         1.000 ± 0%    1.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBuffer_GetRange_Sequential-4                          1.000 ± 0%    1.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferAppend-4                                        1.000 ± 0%    1.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferGetLastN-4                                      1.000 ± 0%    1.000 ± 0%       ~ (p=1.000 n=8) ¹
CircularBufferConcurrentAppend-4                              1.000 ± 0%    1.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                       2.962         2.962       +0.00%
¹ all samples are equal

                             │ benchmarks/go/tier1-baseline.txt │        tier1-bench.txt        │
                             │               B/s                │     B/s       vs base         │
CircularBuffer_BurstAppend-4                       594.0Mi ± 0%   597.3Mi ± 3%  ~ (p=0.195 n=8)

pkg: github.com/tstapler/stapler-squad/session/tmux
                             │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                             │              sec/op              │   sec/op     vs base              │
StripANSICodes_PlainText-4                          6.927n ± 4%   6.885n ± 1%       ~ (p=0.224 n=8)
StripANSICodes_WithEscapes-4                        688.7n ± 1%   689.5n ± 0%       ~ (p=0.959 n=8)
IsBanner_PlainText-4                                478.5n ± 0%   477.9n ± 0%       ~ (p=0.738 n=8)
geomean                                             131.7n        131.4n       -0.20%

                             │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                             │               B/op               │    B/op     vs base                │
StripANSICodes_PlainText-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
StripANSICodes_WithEscapes-4                       56.00 ± 0%     56.00 ± 0%       ~ (p=1.000 n=8) ¹
IsBanner_PlainText-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                       ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                             │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                             │            allocs/op             │ allocs/op   vs base                │
StripANSICodes_PlainText-4                         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
StripANSICodes_WithEscapes-4                       4.000 ± 0%     4.000 ± 0%       ~ (p=1.000 n=8) ¹
IsBanner_PlainText-4                               0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                       ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/tokens
                                   │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                                   │              sec/op              │   sec/op     vs base              │
TokenParser_ProcessUserEntry-4                            5.403m ± 2%   5.287m ± 1%  -2.15% (p=0.003 n=8)
DetectCommandsInText/NoSlash-4                            7.494n ± 0%   7.494n ± 0%       ~ (p=0.819 n=8)
DetectCommandsInText/WithCommand-4                        1.674µ ± 1%   1.664µ ± 1%       ~ (p=0.077 n=8)
geomean                                                   4.077µ        4.040µ       -0.91%

                                   │ benchmarks/go/tier1-baseline.txt │           tier1-bench.txt            │
                                   │               B/op               │     B/op      vs base                │
TokenParser_ProcessUserEntry-4                         11.02Mi ± 0%     11.02Mi ± 0%       ~ (p=0.097 n=8)
DetectCommandsInText/NoSlash-4                           0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
DetectCommandsInText/WithCommand-4                       433.0 ± 0%       433.0 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                             ²                 -0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                                   │            allocs/op             │ allocs/op   vs base                │
TokenParser_ProcessUserEntry-4                           34.00 ± 0%     34.00 ± 0%       ~ (p=1.000 n=8) ¹
DetectCommandsInText/NoSlash-4                           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
DetectCommandsInText/WithCommand-4                       6.000 ± 0%     6.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                             ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/unfinished
                               │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt          │
                               │              sec/op              │   sec/op     vs base              │
DiffShortstat/GitVCSReader-4                          3.168m ± 1%   3.142m ± 1%  -0.80% (p=0.005 n=8)
DiffShortstat/GoGitVCSReader-4                        76.83n ± 1%   76.70n ± 0%       ~ (p=0.195 n=8)
DiffShortstatCached-4                                 76.06n ± 1%   75.58n ± 2%       ~ (p=0.195 n=8)
geomean                                               2.645µ        2.631µ       -0.54%

                               │ benchmarks/go/tier1-baseline.txt │           tier1-bench.txt            │
                               │               B/op               │     B/op      vs base                │
DiffShortstat/GitVCSReader-4                       62.57Ki ± 0%     62.57Ki ± 0%       ~ (p=0.170 n=8)
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
DiffShortstatCached-4                                0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                         ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                               │ benchmarks/go/tier1-baseline.txt │          tier1-bench.txt           │
                               │            allocs/op             │ allocs/op   vs base                │
DiffShortstat/GitVCSReader-4                         360.0 ± 0%     360.0 ± 0%       ~ (p=1.000 n=8) ¹
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
DiffShortstatCached-4                                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=8) ¹
geomean                                                         ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 7ms (▲ slower +44.9%; baseline: 5ms)
list-sessions-total-mean: 8ms (▲ slower +1.6%; baseline: 8ms)

@github-actions

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

Copy link
Copy Markdown
Contributor

📊 Feature E2E Coverage

Feature coverage report unavailable

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

…e peer dep

@xterm/xterm was bumped to 6.0.0 in an unrelated PR after PR #272 added
@xterm/addon-canvas for the WebGL->Canvas fallback (ADR-001). Upstream
removed the canvas renderer from the xterm.js monorepo entirely as of
6.0.0 (xtermjs/xterm.js#5105), so addon-canvas@0.7.0's peer dependency
(^5.0.0) will never be updated -- pnpm install only warns, it doesn't
fail, so CI stayed green with no signal either way.

Verified ground truth instead of trusting the peer-dep warning: a real,
unmocked CanvasAddon activates, resizes, writes, and disposes cleanly
against a real, unmocked xterm 6 Terminal (jsdom has no real 2D canvas
context, so a minimal fake one stands in, same workaround already used
for @xterm/addon-serialize). Added a regression test proving this and
documented the finding in ADR-001's addendum and inline in
XtermTerminal.tsx, so a future xterm major bump gets caught instead of
silently shipping a dead fallback behind a green peer-dep warning.

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

github-actions Bot commented Jul 28, 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

Copy link
Copy Markdown
Contributor

✅ Registry Validation

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

Building backend scanner...
Scanning backend features...
Wrote 117 feature files to /tmp/tmp.QLU8lqUtWE/backend
Wrote 15 feature files to /tmp/tmp.QLU8lqUtWE/backend
Wrote 45 feature files to /tmp/tmp.QLU8lqUtWE/backend
Wrote 7 feature files to /tmp/tmp.QLU8lqUtWE/backend
Wrote 11 feature files to /tmp/tmp.QLU8lqUtWE/backend

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

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 19/179 features have testIds (10.6%)

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

@github-actions

Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 14 KB/s ▲ +15.7% (baseline: 12 KB/s)
terminal-throughput-p50: 16 KB/s ▲ +2.0% (baseline: 15 KB/s)

@tstapler
tstapler marked this pull request as ready for review July 28, 2026 01:32
@tstapler
tstapler merged commit 9651edd into main Jul 28, 2026
21 of 24 checks passed
@tstapler
tstapler deleted the backlog/stapler-squad-terminal-resize-loop-fix branch July 28, 2026 01:32
tstapler added a commit that referenced this pull request Jul 28, 2026
…#272's merge

commit 591059a already removed these from git and gitignored benchmarks/
(baselines are persisted via GitHub Actions cache instead) — but PR #272's
branch predated that fix and still had them tracked, so merging it
resurrected all four as tracked files again. .gitignore doesn't stop a
merge from reintroducing already-tracked-on-one-side content, so this
needs its own removal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3683CH7Fs9zYR2yP3Dpba
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.

3 participants