Put backlog behind a feature flag by default#175
Merged
Conversation
…klog flag The "backlog" feature flag already defaulted off and gated ConnectRPC calls and nav visibility (66b1d83), but two surfaces still bypassed it: - server/mcp/server.go registered backlog/goal MCP tools via a dead `storage != nil` check, so an agent session could read/mutate backlog data over MCP even with the flag off. Added a live `enabledCheck func() bool` to backlogHandlers/goalHandlers (checked on every call, so a runtime toggle takes effect without restarting the MCP server) and gated registration itself at startup. Threaded backlogCtrl.IsEnabled through the HTTP transport and cfg.GetFeatureFlag through the stdio transport. - web-app/src/app/settings/backlog-sources had no feature-flag guard and was reachable by direct URL when disabled. Added a layout.tsx redirect guard matching backlog/layout.tsx, and hid its settings-page nav link when the flag is off. Also reconciled three stale StatusStable badges in server/features/backlog.go to StatusExperimental to match the hidden-by-default framing. Backlog item: df0d5872-9da4-4498-8a31-da355fb6ee07 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Backlog item: df0d5872-9da4-4498-8a31-da355fb6ee07 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ssing A rework/reopen cycle can leave a session's stored worktree path pointing at a directory that no longer exists (recreated elsewhere, or removed by a concurrent cleanup). IsDirty() had no error-caching path, so a broken worktree got re-checked (and re-spawned a git subprocess) on every poller tick indefinitely — 439 failed spawns in 20 minutes observed live on one session. Cache the failure with a 60s backoff, same mechanism already used for dirty/clean results.
Added to the generated unit: - MemoryHigh/MemoryMax (60%/80% of detected system RAM) so a runaway burst of concurrent agent sessions is contained to this service's cgroup instead of taking down the whole box, the way the 2026-07-12 OOM incident did. - A mild OOMScoreAdjust=-500 nudging survival odds toward the coordinator. - StartLimitIntervalSec/Burst raised so systemd doesn't permanently disable the unit after a burst of OOM-kill/restart cycles during sustained pressure (default 5-restarts/10s can trip in one bad episode).
…ion audit trail Resilience fixes (2026-07-12 OOM follow-up): - WIP cap now counts live work sessions across both in_progress and review status. AutoReopenAfterFailedReview intentionally leaves a work session alive polling for a verdict once an item flips back to "review" — the old in_progress-only count made those sessions invisible to the concurrent agent limit. - Cold-restore now re-detects the Claude conversation UUID immediately via the live process's open files instead of leaving it blank until some unrelated caller triggers detection later. A second crash/restart in that window previously lost conversation resumability entirely. - Headless triage sessions get a 30-minute staleness threshold instead of the general 2h one — an open headless-triage row reliably means dead, not slow, so operators were waiting up to 2h for a notification. - New reconcileUnprocessedReviewVerdicts detector: a review session can die (crash/OOM/restart) after recording its verdict but before its exit event reaches the code that acts on it (push+PR on PASS, auto-reopen otherwise). The existing zombie detector only fires when EVERY session on the item is confirmed dead, but AutoReopenAfterFailedReview's live-session-reuse keeps a work session open polling for the verdict — so the item never looked like a zombie even though the review side was the one that died. Found live: a work session correctly determined its item's PASS verdict was already recorded with nothing left to build, but had no way to force the review->done transition itself, and looped forever re-requesting a review the backlog system correctly rejected. Audit trail (the data behind the above was already being recorded but never reached the UI): - BacklogStatusEvent.note (the human-readable reason for a transition, e.g. "auto-reopened after FAIL verdict") is now exposed over the wire and shown in the item's Workflow timeline. - New BacklogProgressNote proto message + GetBacklogItem eager-load surfaces the implementer's full report_progress call history as a "Progress History" section. - Review verdicts (already wired to the frontend, never rendered) now show per-criterion outcome + evidence + summary under each session. - The triggeredBy actor is now always shown, not just when it's "user".
Two test fixture builders were missed when the progressNotes field was added to the BacklogItem domain type in the audit-log work — left them failing tsc's structural check.
…isibility
Rework-cap override:
- New nilable BacklogItem.rework_cap_override column (nil = global default,
0 = unlimited for this item, >0 = this item's own cap), threaded through
the repository DTOs, proto, and UpdateBacklogItem RPC.
- The three automatic-retry call sites (AutoReopenAfterFailedReview,
AutoReopenForPRFix, AutoRespawnReview) now read effectiveReworkCap(item)
instead of only the global default.
- The Unfinished tab's rework_cap card gets a real inline control — set a
new cap or remove it entirely, which immediately reopens the item —
replacing static guidance text that only pointed at buttons elsewhere.
Autonomous turn-cap visibility ("conversions limit"):
- New StuckReasonAutonomousStuck / STUCK_REASON_AUTONOMOUS_STUCK. When an
autonomous driver run stops without a DONE signal, onAutonomousDriverComplete
now writes a durable BacklogStuckState row (additive to the existing
ephemeral notification), so a turn-cap stop shows up in the Unfinished tab
like every other stuck reason instead of only reaching the Alerts feed.
- Treated as event-shaped (resolved only via an explicit action, not a
status anchor) because a stuck work-role run still transitions
in_progress->review regardless of outcome — anchoring on in_progress would
false-resolve the row before an operator ever saw it. That existing
transition behavior is unchanged; flagged in comments as a separate,
not-yet-made decision.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FnJx6jAdjfXRVV1uJtkaeZ
TransitionGuard only matched from==review, so a pr_pending item could reach done via a bare TransitionBacklogItemStatus call with no verdict or shipped-code check at all. Found live: a real backlog item reached done while its GitHub PR was still open with merge conflicts, permanently orphaning that PR from ReconcilePRPending's monitoring (which only polls pr_pending-status items) — most likely a manual "Approve" click that didn't notice a fresh conflict had just been surfaced. Widens the guard to `to == done`, the only two edges (review->done and pr_pending->done) that reach it in validTransitions. The automated ReconcilePRPending path that legitimately drives pr_pending->done already verifies IsPRMerged() itself first, so its behavior is unchanged — this only closes the gap for every other caller.
… call CreatePR reused an existing PR's URL correctly but derived its number from a separate `gh pr view --head` subprocess call whose failure was silently swallowed, leaving prNumber at 0. That 0 then flowed into EnablePRAutoMerge(0), which predictably failed with "no pull requests found" — so auto-merge silently never got enabled for a PR that otherwise pushed and tracked correctly. Parses the number directly from the URL (a plain string op that can't fail the way a subprocess can) first, falling back to the gh-view lookup only if the URL doesn't parse.
3 was tripping routinely on real, ultimately-fixable items (a flaky diff/review harness needing a few extra passes, a straightforward merge conflict) well before the work was actually stuck, forcing manual "Reopen for Revision" clicks for otherwise-recoverable items. Genuinely stuck items still get caught, just later. Per-item overrides (BacklogItemData. ReworkCapOverride, added earlier) remain available for items that need to go further still.
…ces page.tsx Previous review flagged AC1 as PARTIAL: the guard existed only in a sibling layout.tsx, not on page.tsx as the acceptance criterion specifies. Wraps the page's own render output in RequireFeatureFlag so it can never render gated content even outside the layout tree.
…ee bootstrap context RequireFeatureFlag wraps page render output behind a feature flag check via useFeatureFlags(), for Server Component pages that need a client-side gate without losing their static metadata export. Also refreshes the backlog worktree bootstrap files (.backlog-context.md, backlog/review command) for this item's rerun.
…Unknown Session" placeholder tmux session creation was failing with a bare "exit status 1" across multiple backlog items with no way to diagnose why. Route stderr to a scratch file (not a buffer/pipe — a detached tmux server inherits and holds the fd open, so a buffer-based read blocks forever) so the real tmux error surfaces in the returned error. Separately, sessionless notification events (e.g. notifyReworkCapHit) were defaulting sessionName to "Unknown Session", which won the NotificationPanel/Toast fallback chain over the notification's own real title. Default to empty string instead so the real title shows. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FnJx6jAdjfXRVV1uJtkaeZ
…x stderr test The stderr capture landed in the prior commit surfaced the real tmux failure across every backlog auto-fix/review spawn: "command too long". install-service.sh embeds "$PATH:<fallbacks>" verbatim into the systemd unit / LaunchAgent on every install, with no dedup — repeated installs from a shell whose own PATH already carries duplicates (e.g. nested tool/plugin PATH prepends) compound further each time, since the next install's shell inherits the bloated unit-derived PATH. Here that reached 360 entries / ~11.8KB, and re-embedding it via `-e PATH=...` in every spawned tmux session pushed the total `new-session` command past tmux's message-size limit, breaking session creation project-wide. dedup_path collapses it back to first-occurrence order (360 entries / 11.8KB -> 46 / 1.4KB here); also removes a duplicated copy-pasted PATH block in install_linux. Also add TestStartTmuxSession_IncludesTmuxStderrOnFailure covering the stderr-capture change from the prior commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FnJx6jAdjfXRVV1uJtkaeZ
Resolves conflicts by keeping this branch's backlogEnabled gating (MCP tool registration + settings page RequireFeatureFlag guard) alongside main's independent changes (thin-client MCP proxy fast path, review-trigger wiring, Pipeline Modes settings link, and the generalized zero-criteria golden test fixture). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YLti8cQKbKqAh7GsnDocaf
Contributor
✅ Registry ValidationTest Coverage: 13/174 features have
|
Contributor
UX Analysis
|
Contributor
Go Benchmarks (Tier 1) |
Contributor
🎬 E2E Feature Demos2 shard(s) recorded feature flows for this PR. recordings shard 1 Demo preview opens directly in browser (single-file HTML). Raw WebM recordings in ZIP. Expires after 30 days. |
Contributor
E2E RPC Latency |
Contributor
📊 Feature E2E CoverageFeature coverage report unavailable
|
Contributor
Frontend Terminal Throughput |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This diff doesn't match the branch.
backlog/stapler-squad-gate-backlog-mcp-toolsimplies gating the MCP backlog/goal tools behindbacklogCtrl.IsEnabled(per the deleted.backlog-context.md's acceptance criteria:server/mcp/tools_backlog.go,useFeatureFlags()inbacklog-sources/page.tsx,server/features/backlog.gobadge reconciliation) — none of that appears here.What's actually in the diff:- Deletion of the stale.backlog-context.md/.claude/commands/backlog/*triage-session artifacts (same cleanup pattern as the earlier branches)- A rewrite of.claude/commands/perf/make-it-faster.mdto usego tool pprof -topinstead of rawcurlcaptures, plus updated "known hotspots" tables- Acodesigning.mddoc addition explaining whyInfo.plistmust live inmacos/not the repo root- Two new rules:prefer-go-git-over-subshells.mdandsdd-planning-artifacts-commit.md- A new skillbacklog-feature-improvement/SKILL.mdIt's also cut off mid-sentence at the end ("...deserv"), so there may be more, but none of what's visible touchestools_backlog.go, the feature-flags page, orfeatures/backlog.go.Before I write a PR description, want me to pull the full/actual diff for this branch (git diff main...backlog/stapler-squad-gate-backlog-mcp-toolsor similar) so I'm describing what's really there instead of the branch's stated intent?