Repository navigation
feat(staged): add no-verify and deferred push options to the PR button menu - #1052
Conversation
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3981268b1d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| launch_resume::detect_at_launch(app.handle(), &store); | ||
| // Deferred pushes ("Push after current work") left armed by | ||
| // the last run. After the offer is known, so a branch the | ||
| // offer may put work back on waits for the answer. | ||
| launch_resume::kick_deferred_pushes_at_launch(app.handle(), &store); |
There was a problem hiding this comment.
Establish launch holds before queue recovery kicks
When a restart has an armed deferred push, a queued row, and an interrupted branch or project session awaiting the launch-resume decision, kick_all_with_queued_work schedules that branch before detect_at_launch records the hold. That already-scheduled drain does not consult deferred_push_holds, so after the queued row finishes it can fire or clear the deferred push while the interrupted session is still terminal, causing resumed work to remain unpushed. Detect and establish the launch holds before issuing any startup queue kicks, or make the drain explicitly honor those holds.
Useful? React with 👍 / 👎.
| store | ||
| .set_branch_deferred_push(branch_id, None) | ||
| .map_err(|e| e.to_string())?; |
There was a problem hiding this comment.
Clear only the deferred request that was checked
If new branch work starts after check_deferred_push reports idle and the user re-arms the branch with a different variant before this write, the unconditional clear deletes that newer request and the old variant is then queued behind the new work. The same lost-update race exists in the Satisfied arm above. Clear with a compare-and-swap on the checked variant/timestamp, or keep the check and clear under the branch launch lock, so re-arming cannot be overwritten.
Useful? React with 👍 / 👎.
| // Deferred push ("Push after current work") | ||
| // ============================================================================= | ||
| // | ||
| // A push armed on the branch row (`branches.deferred_push`, see | ||
| // `store::DeferredPush`) that fires once the branch and its project are idle. |
There was a problem hiding this comment.
Split deferred-push scheduling out of prs.rs
Move the deferred-push state machine, completion analysis, commands, and tests into a focused submodule rather than adding this large new responsibility to prs.rs; this change grows the file by roughly 1,450 lines to nearly 5,000 lines, making queue and push behavior substantially harder to navigate and maintain. The repository explicitly requires growing Rust modules to be split before they become difficult to navigate.
AGENTS.md reference: apps/staged/AGENTS.md:L28-L31
Useful? React with 👍 / 👎.
`src-tauri/src/prs.rs` had grown to nearly 5,000 lines on this branch. Per AGENTS.md (split growing Rust modules by responsibility before they become hard to navigate) and the PR #1052 review, it is now `src-tauri/src/prs/`, split along every responsibility it held, not only the deferred push: - `mod.rs`: module docs, declarations and the re-exports that keep `crate::prs::*` paths working for `web_server`, `session_commands`, `session_completion`, `pr_poll_scheduler`, `pipeline_dependency`, `branches`, `pr_detection` and `upstream_repair`. - `context.rs`: resolving a branch to its repository and working directory (`resolve_branch_repo_and_subpath`, `BranchPipelineContext`, `resolve_branch_pipeline_context`, `base_branch_name`). - `pipeline.rs`: the session-row plumbing every pipeline shares: `BranchPipelineResponse`, the prompt constants and `pipeline_prompt`, `find_queued_pipeline`, `branch_has_work_in_flight`, `RunningPipelineSession` + `launch_running_pipeline_session`, and `start_pipeline_for_branch`. - `git_commands.rs`: the fetch/push shell commands with the HTTPS fallback and `push_refspec`. - `commit.rs`: rebase and squash, including "Rebase and force push" and the queued-commit dequeue (`start_queued_commit_pipeline_for_branch`). - `push.rs`: `PushVariant`, `build_push_pipeline_steps`, push queueing and per-variant dedupe, the follow-up force push row, `start_or_queue_push_pipeline_for_branch` and `has_unpushed_commits_impl`. - `pull.rs`: the immediate fast-forward with its marker session, the queued pull, and `build_pull_pipeline_steps`. - `git_pipeline.rs`: rebuilding a queued push or pull on dequeue (`rebuild_queued_git_pipeline`, `start_queued_git_pipeline_for_branch`). - `deferred.rs`: "Push after current work": blockers, the satisfied-by-a- completed-push check, arm / cancel / fire. - `create_pr.rs`: the create-PR pipeline and its handoff prompt. - `pr_status.rs`: `PrStatusEvent`, single-branch and per-project refresh with the bounded pool, `BranchRefresh` collection, and clearing stale status. - `commands.rs`: the thirteen `#[tauri::command]` wrappers and `get_store`, following `project_transfer::commands` / `actions::commands`. - `tests.rs` + `tests/{commit,push,pull,git_pipeline,deferred,create_pr, pr_status}.rs`: the test module, with the shared fixtures in `tests.rs` and each test next to the module it covers (the `project_transfer` layout). The move is behavior-neutral: no function body, prompt, command or event changed, and no item was renamed. The only code edits are visibility (`pub(super)` on items now shared between sibling modules or reached from the tests), the per-module `use` lists, module docs, and seven intra-doc links qualified with their new module path. Call-site changes outside the tree: the `generate_handler!` list in `lib.rs` names `prs::commands::<command>`, because tauri's `#[command]` macros are `#[macro_export]`ed at the crate root and cannot be re-exported through `prs/mod.rs`; the two source-text tests in `session_commands.rs` that `include_str!` the dequeue paths now read `prs/commit.rs` and `prs/git_pipeline.rs`; and a doc comment in `timeline.rs` names the new command path. Verified: `cargo check --lib --tests`; `cargo clippy --lib --tests` with no warnings in the new tree (the five it reports are pre-existing in `note_transfer.rs` and `orchestrator_tests.rs`); `cargo test --lib -- prs:: session_commands::tests::queued_start branches:: pipeline_dependency:: session_queue:: launch_resume:: session_completion::` (199 passed); the test and function counts before and after are equal (66 tests, 161 fns), and a line-level diff of the old file against the tree differs only in `use` lines; `rustfmt --check` on the tree. Nothing frontend-visible changed, so `pnpm -s check` was not run. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…widen the menu to every PR state
The PR button's chevron menu only existed in the Create PR state. It now
shows whenever it has entries. Its items come from a pure builder
(`prButtonMenu.ts`); the component maps each item id to an icon and a
handler.
Push without verifying (`git push --no-verify`):
- A `PushVariant { force, no_verify }` replaces the bare `force` flag
through the push path. `--no-verify` goes into both halves of the HTTPS
fallback, and the force-push handoff template spells it out so the
agent's retry skips the hook too.
- Two new prompts. Both contain the strings
`infer_branch_resume_session_type` matches on, so `push-completed`
still fires.
- `PipelineExecution.push_no_verify` is persisted in the JSON pipeline
column (no migration). Queued pushes dedupe per variant, and dequeue
rebuilds the steps with the flag through a new
`rebuild_queued_git_pipeline`.
- `push_branch` and its web arm take `noVerify`. The frontend carries it
through `pushStateStore`, untracked-push adoption, Retry and the
rejection dialog.
Menu, by state:
- Local ahead: "Push without verifying" (with Option: "Force push
without verifying", no dialog).
- Diverged: "Force push without verifying…", which opens the force-push
dialog (Option skips it).
- With a PR: "View PR #N" (unless the button already opens it) and "Copy
PR URL", through a shared `resolvePrUrl()`.
- Create PR state: unchanged.
The force-push dialog is now titled "Push Rejected" only after a
rejection and "Force Push" otherwise. When the push skips the hook, it
says so and its button reads "Force Push Without Verifying". Option is
read from the shared `trackAltKey()`, which resets on blur, instead of
the button's own listeners.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
…R button menu
A push armed from the PR button's menu that fires once the branch *and* its
project have nothing running, disconnected or queued. All four variants are
offered: push, force push, and both without verifying (`--no-verify`).
New backend fields (for review, per AGENTS.md):
- Migration `0042-add-branch-deferred-push`: two nullable columns on
`branches`, `deferred_push TEXT` (one of `push`, `force`, `push-no-verify`,
`force-no-verify`) and `deferred_push_requested_at INTEGER` (ms). Existing
rows read as not armed; `migration_tests.rs` covers both.
- `store::DeferredPush { force, no_verify, requested_at }` with
`as_column_name()` / `parse()`, and `Branch.deferred_push:
Option<DeferredPush>`. The store does not depend on `prs::PushVariant`;
`prs` maps between the two.
- `BranchWithWorkdir.deferred_push: Option<&'static str>` and
`deferred_push_requested_at: Option<i64>` on the wire; the frontend
`Branch` gains `deferredPush` / `deferredPushRequestedAt`.
- `KickSource::ProjectSessionEnded`.
Why a flag on `branches` and not a queued push row: a queued row sits in the
branch FIFO (`drain_branch_queue` stops at it, so it would block work queued
behind it) and the queue cannot see project sessions. The fired push is an
ordinary push pipeline, so the per-variant dedupe, the prompts and the
`push-completed` handling all apply unchanged.
Backend:
- `set_branch_deferred_push` / `branch_ids_with_deferred_push(project_id)`
on the store, publishing `Branch` so clients refetch (no new event), and
`project_has_sessions_in_flight` (sessions joined to `project_notes`,
running / disconnected / queued).
- Commands `defer_branch_push(branch_id, provider, force, no_verify)` and
`cancel_deferred_push(branch_id)`, registered in `lib.rs` with
`web_server.rs` arms. Arming takes the branch launch lock for the
check-and-set; if the branch and project are already idle it pushes right
away instead (`{ armed: false, push }`), closing the gap between the menu
rendering and the click.
- Firing: the tail of `drain_branch_queue`, when nothing queued could start
(`prs::fire_deferred_push_if_idle`). It inherits the drain's quitting /
provisioning / transferring gates and runs on the kicker's task. The flag is
cleared before the push starts so the push's own terminal-state kick cannot
fire it twice; a start failure re-arms it. The fired push runs with
`provider: None`, like a queued row.
- `drain_queued_after_terminal_state` no longer returns early for branchless
sessions: a project-note session's end resolves its project and kicks that
project's armed branches with `KickSource::ProjectSessionEnded`.
- Startup: `launch_resume::kick_deferred_pushes_at_launch` runs after
`detect_at_launch`. Armed branches the launch-resume offer names (their own
interrupted session, or an interrupted project session of their project)
are held in `LaunchResumeState` and kicked when the offer is answered —
immediately on a dismissal, after the run otherwise — so the push cannot
fire ahead of a resumed session's commits. The rest are kicked with
`KickSource::Startup`.
Menu (`prButtonMenu.ts`), a new section between the push-now rows and the PR
links, PR-gated like `pushItems` and shown only while `workInFlight`
(branch sessions, git pipelines, or any running session in the project via
`projectStateStore.hasRunningSessions`):
- Local ahead and in sync: "Push after current work" and "Push after current
work without verifying"; Option swaps both to their force variants.
- Diverged: the force pair with "…", opening the force-push dialog (Option
skips it). The dialog is titled "Force Push After Current Work" and its
button says so.
- While armed, the section is a single "Cancel deferred push" row in every
state (the flag can coexist with a queued or running push); its tooltip
names the armed variant.
Timeline: a `git-deferred-push` footer row below the queued-pull row shows
the armed variant's label, "Waiting for branch and project sessions to
finish", and a Cancel button (`pushDeferred` on `TimelineRow`). The row is
the undo, which is why the plain deferred rows need no confirm.
Tests: the `it.each` table and separator invariant in `prButtonMenu.test.ts`
cover every state ± Option, armed and work-in-flight; `prs.rs` covers a
running branch session, running / queued / disconnected project sessions
with each of the four variants firing exactly once, immediate push when
already idle, waiting behind a queued push instead of stacking, re-arming on
a failed start, re-arm / cancel, and the store round trip;
`migration_tests.rs` covers 0042; `session_queue/tests.rs` asserts the
`ProjectSessionEnded` kicks with `BranchQueueKicker::recording()`;
`launch_resume/tests.rs` covers the startup hold.
Verified with `pnpm -s check` (0 errors), the affected vitest files, and
the new tests under `cargo test` in `src-tauri`. Full suites were not run:
the machine was under heavy load.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Svelte trims the leading whitespace of an `{#if}` block body, so the
newline before "once the current branch…" and before "The pre-push hook
won't run." vanished, and the description rendered as "local versiononce
the current…", "finish.The pre-push hook won't run." and, for the
immediate no-verify dialog, "version.The pre-push hook won't run."
The joining spaces are now explicit `{' '}` expressions at the start of
each block, so every deferred × noVerify combination renders with a single
space (the rejected title is unaffected). Verified by inspecting the
compiled client output: the static template is ` <!>.<!>` and both
conditional text nodes begin with a space. There is no component test for
this file (the card test stubs the button out), so none was added.
`pnpm -s check` passes with 0 errors.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
…ork" feature Four findings from the review of c5ed5ab, plus a stale test left by 2d29b03. 1. A deferred push no longer stacks on a same-variant queued push (prs.rs). When the drain tail finds the branch and project idle, it first looks for a push session of the same variant (same force / no_verify) that completed successfully on the branch at or after `deferred_push.requested_at`. If one exists the flag is cleared and logged instead of fired, so one intent ends as one push session. "Successfully" is the pipeline's own verdict (completed without the agent, or every step succeeded or skipped); a different variant, a cancelled push, or one that completed before the arm leaves the deferred push to fire. New store query `get_completed_sessions_for_branch_since(branch_id, since)`; `updated_at` stands in for a completion time. The firing step now goes through a `DeferredPushCheck` (NotArmed / Blocked / Satisfied / Ready). 2. A wait that never ends is now visible (store/sessions.rs, prs.rs, UI). Disconnected project sessions still hold the push (a disconnected remote session may still be working). `project_has_sessions_in_flight` became `project_session_ids_in_flight`, and when the drain tail declines to fire it logs the blocking branch and project session ids at info level. The `git-deferred-push` footer row also names what it waits on, e.g. "Waiting for 2 branch sessions and 1 project session to finish", falling back to the generic text when this client can see nothing (a queued project session, or a snapshot not loaded yet). This is computed on the frontend from live state (`deferredPushWaitingMeta`), so no wire field was added: a per-branch count on `BranchWithWorkdir` would only refresh on a `branch-changed` publish, which session ends do not trigger, and would have gone stale as sessions finished. 3. The frontend's `workInFlight` now matches the backend's idle definition (BranchCard.svelte). It counts this branch's timeline sessions and git actions plus the project's own sessions: running ones from `projectStateStore.getRunningProjectSessionIds` (running sessions the registry holds without a branch, i.e. project-note sessions) and disconnected ones from `disconnectedSessionsStore.projectSessionsForProject`. Sessions on sibling branches no longer offer a wait the backend would not perform. The menu tooltips say so. `countGitActionsInFlight` joins `isGitActionInFlight` in `branchCardHelpers.ts`. 4. A second selection during the `deferBranchPush` round trip can no longer arm twice or start two immediate pushes (BranchCardPrButton.svelte). A local `deferPushPending` guards `deferPush` and `cancelDeferredPush`, and `buildPrMenu` takes it as `deferPushPending`, marking the deferred rows `disabled` while it is set; the dropdown renders that. Also: 2d29b03 added migration 0042 and its own test but left the sixteen older `assert_eq!(version, 41)` assertions in `migration_tests.rs` at 41, and the 0040 and 0041 fixtures had no `branches` table for 0042 to alter, so `store::migration_tests` failed on this branch. The assertions now read 42 and both fixtures create a minimal `branches` table. Tests: `prs.rs` — the stacking test asserts the flag is cleared and exactly one push session results; new tests cover a completed push of another variant (three pairs) not satisfying the deferred one, and a cancelled or pre-arm same-variant push still firing. `prButtonMenu.test.ts` covers the pending flag disabling only the deferred rows and `deferredPushWaitingMeta`'s wording. `branchCardHelpers.test.ts` covers the count. `disconnectedSessions.test.ts` and a new `projectState.test.ts` cover the project-only queries. Verified with `pnpm -s check` (0 errors), the affected vitest files (prButtonMenu, branchCardHelpers, disconnectedSessions, projectState, BranchCard.component), and `cargo test --lib -- prs::tests store::` plus the `launch_resume` deferred-push tests in `src-tauri` (all passing). The full suites were not run: the machine was under heavy load from another build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…hen nothing ended after it Addresses the review of ed9ef0b: the issue in prs.rs, the sessions.rs warning about bulk-rewritten `updated_at`, and the BranchCard warning about branchless sessions. 1. A same-variant push now satisfies the deferred push only when it is the last thing that ended on the branch (prs.rs, store/sessions.rs). The counterexample: push P queued behind the current work, "Push after current work" armed on top, P completes after the arm, then a session S queued behind P commits and completes. P could not have carried S's commits, yet the old check found P "completed since the arm", cleared the flag and never pushed them. `push_satisfying_deferred` (was `push_of_variant_completed_since`) now also asks the new store query `branch_session_ids_ended_since(branch_id, since, except_session_id)` — terminal sessions (`completed`, `error`, `cancelled`) other than the push whose row was written at or after the push finished — and treats any hit, including a same-millisecond tie, as "not satisfied". Of several qualifying pushes the latest is judged. Every terminal status counts because a cancelled or failed agent session may still have committed. The decision is logged. 2. The push's completion time is no longer the row's `updated_at`, which `project_transfer` and `branch_move` rewrite for every session on a branch (so an old push could suddenly look newer than the arm and clear the flag without pushing). It is now `PipelineExecution::finished_at()`: the latest step `completed_at`, written only by the pipeline runner and never rewritten after the pipeline ends. No new field or migration. For a pipeline that handed off to the agent this is the handoff time, earlier than the agent's finish, which only errs toward an extra push. The ordering check still has to read other sessions' `updated_at`, which later writes only ever move later; the doc comment on `get_completed_sessions_for_branch_since` now describes it as a pre-filter that over-selects rather than a completion time. 3. The frontend's project-side count (BranchCard.svelte, projectState.svelte.ts, disconnectedSessions.svelte.ts) is unchanged in code, with the invariant written down: a session only reaches either store with a project id, and the backend resolves a branchless session's project through `project_notes` alone — `project_active_session` (snapshot and Blox reconcile events), `get_project_id_for_session` (terminal emits) and the resume plan's event context fall back to the branch's project otherwise, and the one `running` emit that stamps a project id without a lookup is `start_project_session`, which creates the note. Pikchr diagram child sessions, the other branchless rows, resolve to no project and emit no status event, so they are never registered. "Has a project id, no branch id" is therefore exactly "joined to `project_notes`", matching `project_session_ids_in_flight` apart from queued project sessions, which only err toward not offering the wait. Tests: `prs.rs` adds the counterexample (P completes after the arm, S completes after P, idle → the deferred push fires exactly once) and the rewritten-row case (an old same-variant push's `agent_id`/`updated_at` bumped as a transfer does → still fires); `complete_push_successfully` now records step finish times, and the stacking test lets the millisecond clock advance between the note ending and the push completing, since a tie now counts as "after". `store/tests.rs` covers the new query (every terminal status, ties, the exclusion by id, other branches and running rows left out). Verified with `pnpm -s check` (0 errors), prettier on the touched frontend files, the affected vitest files (projectState, disconnectedSessions, prButtonMenu, branchCardHelpers, BranchCard.component: 140 passing), and `cargo test --lib -- prs::tests store::tests launch_resume session_queue session_completion` (307 passing) plus `cargo clippy --lib --tests` with no warnings in the touched files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
`src-tauri/src/prs.rs` had grown to nearly 5,000 lines on this branch. Per AGENTS.md (split growing Rust modules by responsibility before they become hard to navigate) and the PR #1052 review, it is now `src-tauri/src/prs/`, split along every responsibility it held, not only the deferred push: - `mod.rs`: module docs, declarations and the re-exports that keep `crate::prs::*` paths working for `web_server`, `session_commands`, `session_completion`, `pr_poll_scheduler`, `pipeline_dependency`, `branches`, `pr_detection` and `upstream_repair`. - `context.rs`: resolving a branch to its repository and working directory (`resolve_branch_repo_and_subpath`, `BranchPipelineContext`, `resolve_branch_pipeline_context`, `base_branch_name`). - `pipeline.rs`: the session-row plumbing every pipeline shares: `BranchPipelineResponse`, the prompt constants and `pipeline_prompt`, `find_queued_pipeline`, `branch_has_work_in_flight`, `RunningPipelineSession` + `launch_running_pipeline_session`, and `start_pipeline_for_branch`. - `git_commands.rs`: the fetch/push shell commands with the HTTPS fallback and `push_refspec`. - `commit.rs`: rebase and squash, including "Rebase and force push" and the queued-commit dequeue (`start_queued_commit_pipeline_for_branch`). - `push.rs`: `PushVariant`, `build_push_pipeline_steps`, push queueing and per-variant dedupe, the follow-up force push row, `start_or_queue_push_pipeline_for_branch` and `has_unpushed_commits_impl`. - `pull.rs`: the immediate fast-forward with its marker session, the queued pull, and `build_pull_pipeline_steps`. - `git_pipeline.rs`: rebuilding a queued push or pull on dequeue (`rebuild_queued_git_pipeline`, `start_queued_git_pipeline_for_branch`). - `deferred.rs`: "Push after current work": blockers, the satisfied-by-a- completed-push check, arm / cancel / fire. - `create_pr.rs`: the create-PR pipeline and its handoff prompt. - `pr_status.rs`: `PrStatusEvent`, single-branch and per-project refresh with the bounded pool, `BranchRefresh` collection, and clearing stale status. - `commands.rs`: the thirteen `#[tauri::command]` wrappers and `get_store`, following `project_transfer::commands` / `actions::commands`. - `tests.rs` + `tests/{commit,push,pull,git_pipeline,deferred,create_pr, pr_status}.rs`: the test module, with the shared fixtures in `tests.rs` and each test next to the module it covers (the `project_transfer` layout). The move is behavior-neutral: no function body, prompt, command or event changed, and no item was renamed. The only code edits are visibility (`pub(super)` on items now shared between sibling modules or reached from the tests), the per-module `use` lists, module docs, and seven intra-doc links qualified with their new module path. Call-site changes outside the tree: the `generate_handler!` list in `lib.rs` names `prs::commands::<command>`, because tauri's `#[command]` macros are `#[macro_export]`ed at the crate root and cannot be re-exported through `prs/mod.rs`; the two source-text tests in `session_commands.rs` that `include_str!` the dequeue paths now read `prs/commit.rs` and `prs/git_pipeline.rs`; and a doc comment in `timeline.rs` names the new command path. Verified: `cargo check --lib --tests`; `cargo clippy --lib --tests` with no warnings in the new tree (the five it reports are pre-existing in `note_transfer.rs` and `orchestrator_tests.rs`); `cargo test --lib -- prs:: session_commands::tests::queued_start branches:: pipeline_dependency:: session_queue:: launch_resume:: session_completion::` (199 passed); the test and function counts before and after are equal (66 tests, 161 fns), and a line-level diff of the old file against the tree differs only in `use` lines; `rustfmt --check` on the tree. Nothing frontend-visible changed, so `pnpm -s check` was not run. Signed-off-by: Matt Toohey <contact@matttoohey.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
…the module split CI on PR #1052 failed in one test, `web_server::blocking_guard_tests::async_commands_and_arms_keep_blocking_calls_off_the_runtime`. Its `ALLOWED` table matches file paths exactly, and the entry excusing the `pull_branch_ff_only_impl` closure (which `pull_or_queue_branch_with` runs under `spawn_blocking`) still named `prs.rs`. Since 581eb84 moved `pull_or_queue_branch_for_branch` into `src/prs/pull.rs`, the entry no longer matched and the walk reported fourteen false offences on `pull_or_queue_branch`. The file key is now `prs/pull.rs`. No code outside the test changed. Verified: `cargo test --lib -- web_server::blocking_guard_tests` passes (1 passed) in `src-tauri`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Matt Toohey <contact@matttoohey.com>
Extends the PR button's chevron menu in Staged. It now appears in every PR state, not just Create PR, and gains push variants that skip the pre-push hook or wait for in-flight work to finish.
Menu
git push --no-verify), plus force-push variants (Option-click skips the dialog). APushVariant { force, no_verify }replaces the bareforceflag along the whole push path, including the HTTPS fallback, the agent handoff prompts, queued-push dedupe and Retry/rejection flows.push_no_verifylives in the JSON pipeline column, so no migration is needed for it.prButtonMenu.ts). The force-push dialog title and text now reflect rejected vs. manual and verify vs. no-verify.Backend (new fields, for review)
0042-add-branch-deferred-push: nullablebranches.deferred_push(push/force/push-no-verify/force-no-verify) anddeferred_push_requested_at(ms). These surface asBranch.deferred_pushand on the wire asdeferredPush/deferredPushRequestedAt.defer_branch_push/cancel_deferred_push, with matching web arms. Arming runs under the branch launch lock. If everything is already idle, it pushes right away instead.drain_branch_queue. The flag is cleared before the push starts and re-armed if the push fails to start. When a project-note session ends, its project's armed branches are kicked (KickSource::ProjectSessionEnded).completed_at, not fromupdated_at, because transfers and branch moves rewriteupdated_at.branchesis used instead of a queued push row. A queued row would block the branch FIFO, and the queue can't see project sessions.Tests
Adds
prButtonMenu.test.ts, plus store, migration,prs,session_queueandlaunch_resumetests covering arming, firing, stacking and the ordering edge cases.🤖 Generated with Claude Code