From 0efcdc3af06472ea7f4cb0dffb1bf6a51ba1ceb9 Mon Sep 17 00:00:00 2001 From: Riku Inada Date: Sat, 24 Oct 2026 19:02:17 +0900 Subject: [PATCH] docs(rfc): propose the git branch switcher Record the design #23102 implements so it can be agreed before any code lands: a branch picker in the Git panel header and the composer chip, an owner-only switch on the worktree route's project boundary, and a busy rule that refuses a switch while any chat turn, linked-channel turn, sub-agent, workflow step or ACP prompt in flight works in the repository, ending no session. Splits the implementation into four phases and lists the open questions for the maintainers. Part of #22670 --- docs/request-for-change/README.md | 3 +- .../rfc-git-branch-switcher.md | 376 ++++++++++++++++++ 2 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 docs/request-for-change/rfc-git-branch-switcher.md diff --git a/docs/request-for-change/README.md b/docs/request-for-change/README.md index 4e4632c7b69..b80dc6d43f4 100644 --- a/docs/request-for-change/README.md +++ b/docs/request-for-change/README.md @@ -25,9 +25,10 @@ not been re-audited since 2026-08-03. The durable-run-coordinator row was added verified against `6d3e30bbbd`. The `rfc-global-workflow-library` row was added 2026-08-25 and audited against `749468d42`; its implementation exists only in the active detached worktree. The `rfc-agentcore-identity-gateway` row -was added 2026-08-27. The `rfc-crew-agent-sdk-boundary` row was added 2026-08-28 and verified against `dc88f142b`. The `rfc-transcript-section-markers` row was added 2026-08-30 and verified against `202770d13`. The `rfc-mcp-lifecycle-event-log` row was added 2026-09-01 and verified against `1ee69f225`. The `rfc-terminal-session-ownership` row was added 2026-09-01 and re-verified against `2188f029d`. The `rfc-session-tag-change-event` row was added 2026-09-02 and verified against `6581a04ee`. The `rfc-agent-config-mirror` row was added 2026-09-02 and verified against `f8b3203c0`. The `rfc-chat-core-extraction` row was added 2026-09-05 and verified against `8ed028b0b`. The `rfc-conductor-work-ledger` row was added 2026-09-05 and verified against `049b8c729`. The `rfc-app-session-controls` row was added 2026-08-31 and re-verified against `1d705a03f` on 2026-09-01. The `rfc-central-governance-ceiling` row was added 2026-09-08 and verified against `8a9c269b4`. The `rfc-security-conductor` row was re-audited 2026-09-10 against `f6d38741c`. The `rfc-overload-resilience` row was added 2026-09-12 and verified against `6f056722b`. The `rfc-append-only-ledger` row was added 2026-09-11 and verified against `2f1e2f54a`, and moved to `in-progress` the same day against the open PR named in it rather than against main. The `rfc-webhook-subscriptions` row was added 2026-09-15 and verified against `6163d9a9ca`. The `rfc-arrival-provenance-filing` row was added 2026-09-20 and verified against `f31f25a8a2`. The `rfc-wake-judge` row was added 2026-09-22 and verified against `80bd0a81f`. The `rfc-crewmates-launch` row was added 2026-09-22 and verified against `87553ba866`. The `rfc-conductor-work-ledger` row was re-audited 2026-09-22 against `cc385211d8` for its v4 revision, which moves Phases 3 and 4 to in flight against two open PRs rather than against main. The `rfc-host-credential-vending` row was added 2026-09-23 and verified against `18f9984b04`. It was re-audited 2026-09-24 against `529a8f5e9e` for its second revision, which widens the grant from the `codex` routing to any child whose effective mask hides `.aws` or `.ssh`, adds an ssh-agent proxy beside the AWS route, and replaces the reused-socket claim with a new listener, and again 2026-09-24 against `3d2bfa8f45` for six advisory findings on that revision: the seatbelt window rule, the macOS `cc` mask, the two scrubs, the helper's home, the Windows reach and a codex citation, and once more the same day for a four-decision gate, `AWS_PROFILE`, and the pod-remap cost the design reverses. The `rfc-wake-judge` row was re-audited 2026-09-23 against `2fdadc71ac`, which moves it to `partial` for the LLM lane on main. On 2026-09-24 only its in-flight-core reading was re-read, at `d81bd0ec17`, to settle that document's section 7 Q4; its main-side audit stands where the row's front matter records it. The `rfc-composable-layout-mechanism` row was added 2026-09-24 and verified against `a305e4fba`, and moved to `in-progress` on 2026-09-25 against the open PR named in it ([#13572](https://github.com/kirodotdev/KiroCrew/pull/13572), PR 1 of its §7 sequence) rather than against main. The `rfc-wake-judge` row was revised 2026-09-25 for the reader-fetches/judge-decides placement, read at `db5267bda9` on the branch that implements it rather than against main: the gh-pr reader makes no wake decision, the whole reading reaches the judge as evidence, the one merged-or-closed mapping stays in the auto-nudge core, and the script-cron driver is retired because a sandboxed subprocess holds no decisions provider and so can reach no judge. The same revision corrects the row's main-side reading: the judge core the row described as in flight landed 2026-09-24 as `2d5a800ee`, so `points/nudge_wake.py`, the `nudge_evidence` scope entry, the collectors, `judge_tick` and its tick hook, `judge` on the two arming tools and the transcript notice are all on the base tree, and only the reader placement and the Decisions-card consent switch remain outstanding. The `rfc-solo-spawn-gate` row moved to `in-progress` on 2026-09-25 against `2fbd88d07a`: the removal it records is live in an open PR, and `superseded` is outside the set the First Principles lane reads as a decision. The `rfc-owner-written-mcp-auto-approve` row was added 2026-09-27 and verified against `94bcda2f75`, where the default it changes is still off, so the row records the decision rather than shipped behaviour. The `rfc-wake-judge` row was revised 2026-09-28 for two calibration corrections measured on the landed judge, recorded in that document as decided and NOT on base under a new §8 PR H entry: the action backstop stops overriding a confident `needs_owner` quiet below a new `ACTION_OVERRIDE_MIN_P`, and `owner_acted`'s tool-call limb counts only dispatches that could have changed something, which needs the per-dispatch names carried to `notify_turn_complete` and records a new `tool_names_known` on the label row. Both supersede the earlier reading that only the reader placement and the Decisions-card consent switch remained outstanding; the implementation is [#14663](https://github.com/kirodotdev/KiroCrew/pull/14663), which is not merged, so the row still describes base behaviour. The `rfc-app-sdk-crew-webview` row was added 2026-09-27 and verified against `dceaca3a95`. The `rfc-question-card-pager` row was added 2026-09-24 and verified against `61d5bb577`, and moved to `accepted` the same day on the maintainer decision recorded in that document's §7.1. It was re-read 2026-09-28 against `e88e80778`, where every symbol the row cites is still present. It was revised 2026-09-30 against `f4df23e53` for §3.6, accepted by buluoray on 2026-09-29: the pager is on main, and the blocking tool §3.6 records is not. The `rfc-pptx-maker-server-driven-engine` row was added 2026-09-30 and verified against `ad5b1e670`, and re-read 2026-10-06 against `557c8f8e64`. The `rfc-jev-task-executor` row was added 2026-09-28 and verified against `699083f906`. The same day `rfc-wake-judge` §3.1 was re-pointed to name `rfc-jev-task-executor` §3.2 as the owner of the `noul`/`score` wire types; the wake judge's own status and main-side reading are unchanged. The `rfc-terminal-exit-closes-tab` row was added 2026-09-29 and verified against `df0ea7909`; the same day `rfc-terminal-session-ownership` §8's `exit` outcome, its Phase 4 scope and its open question 4 were revised to close the tab on exit, with that row's status and main-side reading unchanged. The `rfc-retire-chat-autopilot` row was added 2026-09-30 and verified against `2b91a6badd`. The `rfc-terminal-exit-closes-tab` row moved to `accepted` on 2026-09-30 on maintainer bolichen's decision recorded in that document's Open questions, and was re-verified against `c1a02ce976`. The `rfc-jev-task-executor` row moved to `accepted` on 2026-09-29 and was re-read against `df0ea7909c`, where every symbol it cites is unchanged. The `rfc-crew-log-wake` row was added 2026-10-01 and verified against `321fd996a2`, and moved to `in-progress` the same day against the open PR named in it ([#15691](https://github.com/kirodotdev/KiroCrew/pull/15691), which carries the document rather than a standalone docs PR) rather than against main. The `rfc-outbound-instance-transport` row was added 2026-09-23 and re-verified 2026-10-06 against `33d7fc111`. The `rfc-goal-popover-pause-play-controls` row was added 2026-10-02 and verified against `bc80d8d8a2`, in `in-progress` against the open PR that carries it ([#16030](https://github.com/kirodotdev/KiroCrew/pull/16030)) rather than against main. The `rfc-retire-chat-autopilot` row was re-audited 2026-10-02 against `6916ba17f` after backend removal [#15362](https://github.com/kirodotdev/KiroCrew/pull/15362) and frontend removal [#15359](https://github.com/kirodotdev/KiroCrew/pull/15359) merged; it is `partial` because Rollout step 3 still has `StageBoundary` / `_in_stage_execution` references under `src/`. The `rfc-autopilot-stage-budgets` row was added as `superseded` and verified against `6916ba17f`; issue [#14058](https://github.com/kirodotdev/KiroCrew/issues/14058) owns the remaining generic chat-turn cancellation defect. The `rfc-crewmate-dynamic-dashboard` row was added 2026-10-06 and verified against `ffd9136d1c`, in `in-progress` against the open pull request that carries it rather than against main; P1 is one pull request carrying the contract, its plumbing and the twelve built-in pages. The `rfc-remote-crew-sidebar` row was added 2026-10-03 as `draft` and verified against `e698e8ca7a`, and moved to `accepted` the same day on maintainer iamwhatever's decision recorded in that document's Open questions, re-read against `f31e2f7091`. The `rfc-crewmate-session-tree` row was added 2026-10-04 as `draft` and verified against `fd705fb7c7`. The `rfc-goal-loop-approval-hold` row was added 2026-10-05 and verified against `a97659dfc3`, in `in-progress` against the open PR that implements it ([#16993](https://github.com/kirodotdev/KiroCrew/pull/16993)) rather than against main; this document lands on its own first so that PR's First Principles lane can read it from the base. The `rfc-update-architecture` row moved to `in-progress` on 2026-10-02 against the open PR that hardens the managed-venv engine and routes the gateway's unattended apply through it, rather than against main; its main-side reading stays as verified at `80bd0a81f`. The `rfc-conversation-trust-follows-founder` row was added 2026-09-30 and verified against `aec81410e2`, and moved to `in-progress` the same day against the open PR named in it rather than against main. It was revised 2026-10-03 against `8ef5ea40e5` to record maintainer bolichen97's 2026-10-01 acceptance of the design in the document itself, with the status left `in-progress` while the implementing PR is open. The `rfc-crash-only-session-trust` row was added 2026-10-01 and verified against `8ae5dff6ee`. The `rfc-conductor-remote-dispatch` row was added 2026-10-06 and verified against `ebf647989a`. The `rfc-question-card-auto-submit` row was added 2026-10-07 as `draft` and verified against `781294256f`; it amends `rfc-question-card-pager` §3.2 in its own document so that the accepted pager document is not edited and its acceptance does not extend to the amendment. The `rfc-work-ledger-person-wait-hold` row was added 2026-10-08 and verified against `230b9141c8`, and moved to `implemented` on 2026-10-11 against `a84f616557` after [#18109](https://github.com/kirodotdev/KiroCrew/pull/18109) merged. The `rfc-crewmate-guides-and-mate` row was added 2026-10-08 as `accepted` on the product owner's decision of that day and verified against `4f9c5697b9`; it amends sections of `rfc-crewmates-launch`, `rfc-crewmate-greeting` and `rfc-crewmates-chatted-list` in its own document, so those accepted documents are not edited. The `rfc-crewmate-greeting-as-message` row was added 2026-10-09 as `accepted` on the product owner's decision of that day and verified against `d4c3a7334d`; it amends `rfc-crewmate-greeting` §1 and §5 in its own document, so that accepted document is not edited. The `rfc-wake-on-stop-signal` row was added 2026-10-10 and verified against `d32c084921`; it amends `rfc-crew-log-wake` Goal 2, section 3.2 and section 3.2b in its own document, so that implemented document is not edited. The `rfc-mate-landing-once` row was added 2026-10-10 as `draft` and re-verified 2026-10-11 against `73d44b420b` for the rule buluoray asked for in the review of #18903, and moved to `accepted` the same day on buluoray's approval there, re-verified against `a84f616557` with the owed condition that review raised; it amends `rfc-crewmate-guides-and-mate` "When it opens" in its own document, so that accepted document is not edited. The `rfc-team-lead-default-agent` row was added 2026-10-10 and verified against `7612507e11`, in `in-progress` against the open pull request that carries it rather than against main: that pull request ships one default agent built like `kirocrew-research`, so the row records the decision rather than shipped behaviour. The `rfc-crewmate-dynamic-dashboard` row was revised 2026-10-12 against `8f6baa05c9` for a second dated amendment in its own document, which records that a save stages a page rather than landing it, that the template picker and the id-loaded catalog flow are retired in favour of a reference gallery, and that the approval gate is a module of its own so a renderer may name a package it does not publish; the row's status and its main-side reading are unchanged. The row was revised again the same day against `10350b7294` for a third dated amendment, the second of 2026-10-12, which records that the dynamic dashboard is the Dashboard tab for every reader with the "Dynamic Dashboard" Feature Preview removed, closing that document's Open question 3; the row's status and its main-side reading are unchanged. The row was revised 2026-10-15 against `40139dc512` for a fourth dated amendment, which records that a `builtin` or `user` template and the owner's own dashboard package both go live on apply with no preview step, keeps `RENDERABLE_SOURCES` and `AUTHORED_PAGE_REFUSAL` unchanged, and requires a guard test that every renderable template writes values as text only; the row's status and its main-side reading are unchanged. The `rfc-worker-model-inheritance` row was added 2026-10-12 as `accepted` on the product owner's decision of that day and verified against `e5a0b3d3b2`; it amends model-selection.md's "Pin a cheaper model" rule in that document and extends `rfc-crewmate-profile-settings` §3 in its own document, so that accepted document is not edited. The `rfc-crewmate-dashboard-upkeep-skill` row was added 2026-10-11 and verified against `a84f616557`. It was revised 2026-10-12 against `0cda1525d8` in review: the upkeep text is returned by `dashboard_fields` rather than written to a generated `SKILL.md`, which a crewmate with no file tools cannot open, and the generated directory holds the helper scripts only. The `rfc-chat-document-attachments` row was added 2026-10-09 as `draft` and verified against `8c6e7eba90`, and re-verified 2026-10-12 against `2e8831ea52`, where the upload cap it cites had become the configurable `upload_max_bytes()`. The `rfc-mcp-resource-subscriptions` row was added 2026-10-13 as `draft` and verified against `de2d143dc`. The `rfc-efficient-local-inference` row was added 2026-10-08 and re-verified against `67c1304b06` on 2026-10-09, and re-read 2026-10-10 against `d32c084921` after its Phase 1 landed as `235e6b78a0`, and again 2026-10-13 against `fec23afff7`. The `rfc-remote-instance-on-microvm` row was added 2026-10-06 and verified against `f9a07ab3fa`. The `rfc-fold-recorded-edit-cards` row was added 2026-10-16 and verified against `5c790ca239`. The `rfc-remote-computer-use` row was added 2026-10-18 as `draft` and verified against `310ad48b5f`. The `rfc-mate-owned-agent-config` row was re-audited 2026-10-17 against `8892889b91` for its first dated amendment, which records the architecture meeting's conductor/worker decisions: the conductor only dispatches and carries no user MCP server, the file's `prompt` is load-bearing for the worker, the conductor raises a card-confirmed capability change, and step 1 is a behaviour change; the row's status and its main-side reading are unchanged, and the open step-1 pull request ([#21463](https://github.com/kirodotdev/KiroCrew/pull/21463)) predates the amendment, so its conductor shape does not yet match it. The `rfc-python-rust-hybrid-backend` row was re-audited 2026-10-19 against `207ed5ae4c` for a new §2.4, which records measurements of the executor CPU, the GIL holder during loop stalls and the anonymous-memory remainder taken on a second host; the row's status and its main-side reading are unchanged. The `rfc-living-ui` row was added 2026-10-18 as `draft` and verified against `9d088c85ad`, and moved to `implemented` on 2026-10-21 against `0b89e0c6f7` on the maintainer's decision of that day. The `rfc-conductor-rolling-reset` row was added 2026-10-19 as `draft` and verified against `0adc85c233`; it amends `rfc-conductor-round-reset` Goals, Non-goals and section 3 in its own document, so that implemented document is not edited. The `rfc-consolidation-fresh-conversation` row was added 2026-10-19 and verified against `5d1a459f2a`, in `in-progress` against its open implementation pull request ([#22207](https://github.com/kirodotdev/KiroCrew/pull/22207)) rather than against main. The `rfc-review-loop-and-escaped-bug-reduction` row was added 2026-10-19 as `draft` and verified against `2cf3d0d69`; it changes no code. That row moved to `accepted` on 2026-10-20 on the repository owner's decisions recorded in section 2.2 of the document; its main-side reading is unchanged. The `rfc-crew-log-telemetry-export` row was added 2026-10-16 as `draft` and verified against `1a2eb5435c`. The `rfc-gated-pr-poll-loops` row was added 2026-10-20 as `draft` and verified against `6432cad9ea`; it changes no code. The `rfc-crewmate-heartbeat` row was added 2026-10-15 as `accepted` on the product owner's decision of that day and verified against `8da5b41552`. The `rfc-conductor-context-cap` row was added 2026-10-19 as `draft` and verified against `c3f2b27de7`; it supersedes `rfc-conductor-rolling-reset`, whose row moved to `superseded` the same day, before that draft was accepted. The `rfc-crewmate-page-on-goal-wake` row was added 2026-10-21 as `accepted` on the product owner's decision of that day and verified against `c428ea050d`; it amends `rfc-crewmate-goals` §3.5 in its own document, so that accepted document is not edited. The `rfc-crewmate-page-ambient-motion` row was added 2026-10-21 as `accepted` on the product owner's decision of that day and verified against `4533b1eb5b`; it amends `rfc-living-ui` §5.1 rule L and §10 in its own document, so that implemented document is not edited. The `rfc-remove-piper-tts` row was added 2026-10-22 as `accepted` on the repository owner's decision of that day and verified against `69b2e503eb`. The `rfc-crew-enterprise-edition` row was added 2026-10-22 as `draft` and verified against `5c32cf32b`. It was revised 2026-10-23 against `a7ecdf3f1` for the owner's three decisions recorded in its section 5, and moved to `accepted` on the owner's approval of the design that day. The `rfc-conductor-patrol-session` row was added 2026-10-24 as `draft` and verified against `1270c45f4d`; it changes no code. +was added 2026-08-27. The `rfc-crew-agent-sdk-boundary` row was added 2026-08-28 and verified against `dc88f142b`. The `rfc-transcript-section-markers` row was added 2026-08-30 and verified against `202770d13`. The `rfc-mcp-lifecycle-event-log` row was added 2026-09-01 and verified against `1ee69f225`. The `rfc-terminal-session-ownership` row was added 2026-09-01 and re-verified against `2188f029d`. The `rfc-session-tag-change-event` row was added 2026-09-02 and verified against `6581a04ee`. The `rfc-agent-config-mirror` row was added 2026-09-02 and verified against `f8b3203c0`. The `rfc-chat-core-extraction` row was added 2026-09-05 and verified against `8ed028b0b`. The `rfc-conductor-work-ledger` row was added 2026-09-05 and verified against `049b8c729`. The `rfc-app-session-controls` row was added 2026-08-31 and re-verified against `1d705a03f` on 2026-09-01. The `rfc-central-governance-ceiling` row was added 2026-09-08 and verified against `8a9c269b4`. The `rfc-security-conductor` row was re-audited 2026-09-10 against `f6d38741c`. The `rfc-overload-resilience` row was added 2026-09-12 and verified against `6f056722b`. The `rfc-append-only-ledger` row was added 2026-09-11 and verified against `2f1e2f54a`, and moved to `in-progress` the same day against the open PR named in it rather than against main. The `rfc-webhook-subscriptions` row was added 2026-09-15 and verified against `6163d9a9ca`. The `rfc-arrival-provenance-filing` row was added 2026-09-20 and verified against `f31f25a8a2`. The `rfc-wake-judge` row was added 2026-09-22 and verified against `80bd0a81f`. The `rfc-crewmates-launch` row was added 2026-09-22 and verified against `87553ba866`. The `rfc-conductor-work-ledger` row was re-audited 2026-09-22 against `cc385211d8` for its v4 revision, which moves Phases 3 and 4 to in flight against two open PRs rather than against main. The `rfc-host-credential-vending` row was added 2026-09-23 and verified against `18f9984b04`. It was re-audited 2026-09-24 against `529a8f5e9e` for its second revision, which widens the grant from the `codex` routing to any child whose effective mask hides `.aws` or `.ssh`, adds an ssh-agent proxy beside the AWS route, and replaces the reused-socket claim with a new listener, and again 2026-09-24 against `3d2bfa8f45` for six advisory findings on that revision: the seatbelt window rule, the macOS `cc` mask, the two scrubs, the helper's home, the Windows reach and a codex citation, and once more the same day for a four-decision gate, `AWS_PROFILE`, and the pod-remap cost the design reverses. The `rfc-wake-judge` row was re-audited 2026-09-23 against `2fdadc71ac`, which moves it to `partial` for the LLM lane on main. On 2026-09-24 only its in-flight-core reading was re-read, at `d81bd0ec17`, to settle that document's section 7 Q4; its main-side audit stands where the row's front matter records it. The `rfc-composable-layout-mechanism` row was added 2026-09-24 and verified against `a305e4fba`, and moved to `in-progress` on 2026-09-25 against the open PR named in it ([#13572](https://github.com/kirodotdev/KiroCrew/pull/13572), PR 1 of its §7 sequence) rather than against main. The `rfc-wake-judge` row was revised 2026-09-25 for the reader-fetches/judge-decides placement, read at `db5267bda9` on the branch that implements it rather than against main: the gh-pr reader makes no wake decision, the whole reading reaches the judge as evidence, the one merged-or-closed mapping stays in the auto-nudge core, and the script-cron driver is retired because a sandboxed subprocess holds no decisions provider and so can reach no judge. The same revision corrects the row's main-side reading: the judge core the row described as in flight landed 2026-09-24 as `2d5a800ee`, so `points/nudge_wake.py`, the `nudge_evidence` scope entry, the collectors, `judge_tick` and its tick hook, `judge` on the two arming tools and the transcript notice are all on the base tree, and only the reader placement and the Decisions-card consent switch remain outstanding. The `rfc-solo-spawn-gate` row moved to `in-progress` on 2026-09-25 against `2fbd88d07a`: the removal it records is live in an open PR, and `superseded` is outside the set the First Principles lane reads as a decision. The `rfc-owner-written-mcp-auto-approve` row was added 2026-09-27 and verified against `94bcda2f75`, where the default it changes is still off, so the row records the decision rather than shipped behaviour. The `rfc-wake-judge` row was revised 2026-09-28 for two calibration corrections measured on the landed judge, recorded in that document as decided and NOT on base under a new §8 PR H entry: the action backstop stops overriding a confident `needs_owner` quiet below a new `ACTION_OVERRIDE_MIN_P`, and `owner_acted`'s tool-call limb counts only dispatches that could have changed something, which needs the per-dispatch names carried to `notify_turn_complete` and records a new `tool_names_known` on the label row. Both supersede the earlier reading that only the reader placement and the Decisions-card consent switch remained outstanding; the implementation is [#14663](https://github.com/kirodotdev/KiroCrew/pull/14663), which is not merged, so the row still describes base behaviour. The `rfc-app-sdk-crew-webview` row was added 2026-09-27 and verified against `dceaca3a95`. The `rfc-question-card-pager` row was added 2026-09-24 and verified against `61d5bb577`, and moved to `accepted` the same day on the maintainer decision recorded in that document's §7.1. It was re-read 2026-09-28 against `e88e80778`, where every symbol the row cites is still present. It was revised 2026-09-30 against `f4df23e53` for §3.6, accepted by buluoray on 2026-09-29: the pager is on main, and the blocking tool §3.6 records is not. The `rfc-pptx-maker-server-driven-engine` row was added 2026-09-30 and verified against `ad5b1e670`, and re-read 2026-10-06 against `557c8f8e64`. The `rfc-jev-task-executor` row was added 2026-09-28 and verified against `699083f906`. The same day `rfc-wake-judge` §3.1 was re-pointed to name `rfc-jev-task-executor` §3.2 as the owner of the `noul`/`score` wire types; the wake judge's own status and main-side reading are unchanged. The `rfc-terminal-exit-closes-tab` row was added 2026-09-29 and verified against `df0ea7909`; the same day `rfc-terminal-session-ownership` §8's `exit` outcome, its Phase 4 scope and its open question 4 were revised to close the tab on exit, with that row's status and main-side reading unchanged. The `rfc-retire-chat-autopilot` row was added 2026-09-30 and verified against `2b91a6badd`. The `rfc-terminal-exit-closes-tab` row moved to `accepted` on 2026-09-30 on maintainer bolichen's decision recorded in that document's Open questions, and was re-verified against `c1a02ce976`. The `rfc-jev-task-executor` row moved to `accepted` on 2026-09-29 and was re-read against `df0ea7909c`, where every symbol it cites is unchanged. The `rfc-crew-log-wake` row was added 2026-10-01 and verified against `321fd996a2`, and moved to `in-progress` the same day against the open PR named in it ([#15691](https://github.com/kirodotdev/KiroCrew/pull/15691), which carries the document rather than a standalone docs PR) rather than against main. The `rfc-outbound-instance-transport` row was added 2026-09-23 and re-verified 2026-10-06 against `33d7fc111`. The `rfc-goal-popover-pause-play-controls` row was added 2026-10-02 and verified against `bc80d8d8a2`, in `in-progress` against the open PR that carries it ([#16030](https://github.com/kirodotdev/KiroCrew/pull/16030)) rather than against main. The `rfc-retire-chat-autopilot` row was re-audited 2026-10-02 against `6916ba17f` after backend removal [#15362](https://github.com/kirodotdev/KiroCrew/pull/15362) and frontend removal [#15359](https://github.com/kirodotdev/KiroCrew/pull/15359) merged; it is `partial` because Rollout step 3 still has `StageBoundary` / `_in_stage_execution` references under `src/`. The `rfc-autopilot-stage-budgets` row was added as `superseded` and verified against `6916ba17f`; issue [#14058](https://github.com/kirodotdev/KiroCrew/issues/14058) owns the remaining generic chat-turn cancellation defect. The `rfc-crewmate-dynamic-dashboard` row was added 2026-10-06 and verified against `ffd9136d1c`, in `in-progress` against the open pull request that carries it rather than against main; P1 is one pull request carrying the contract, its plumbing and the twelve built-in pages. The `rfc-remote-crew-sidebar` row was added 2026-10-03 as `draft` and verified against `e698e8ca7a`, and moved to `accepted` the same day on maintainer iamwhatever's decision recorded in that document's Open questions, re-read against `f31e2f7091`. The `rfc-crewmate-session-tree` row was added 2026-10-04 as `draft` and verified against `fd705fb7c7`. The `rfc-goal-loop-approval-hold` row was added 2026-10-05 and verified against `a97659dfc3`, in `in-progress` against the open PR that implements it ([#16993](https://github.com/kirodotdev/KiroCrew/pull/16993)) rather than against main; this document lands on its own first so that PR's First Principles lane can read it from the base. The `rfc-update-architecture` row moved to `in-progress` on 2026-10-02 against the open PR that hardens the managed-venv engine and routes the gateway's unattended apply through it, rather than against main; its main-side reading stays as verified at `80bd0a81f`. The `rfc-conversation-trust-follows-founder` row was added 2026-09-30 and verified against `aec81410e2`, and moved to `in-progress` the same day against the open PR named in it rather than against main. It was revised 2026-10-03 against `8ef5ea40e5` to record maintainer bolichen97's 2026-10-01 acceptance of the design in the document itself, with the status left `in-progress` while the implementing PR is open. The `rfc-crash-only-session-trust` row was added 2026-10-01 and verified against `8ae5dff6ee`. The `rfc-conductor-remote-dispatch` row was added 2026-10-06 and verified against `ebf647989a`. The `rfc-question-card-auto-submit` row was added 2026-10-07 as `draft` and verified against `781294256f`; it amends `rfc-question-card-pager` §3.2 in its own document so that the accepted pager document is not edited and its acceptance does not extend to the amendment. The `rfc-work-ledger-person-wait-hold` row was added 2026-10-08 and verified against `230b9141c8`, and moved to `implemented` on 2026-10-11 against `a84f616557` after [#18109](https://github.com/kirodotdev/KiroCrew/pull/18109) merged. The `rfc-crewmate-guides-and-mate` row was added 2026-10-08 as `accepted` on the product owner's decision of that day and verified against `4f9c5697b9`; it amends sections of `rfc-crewmates-launch`, `rfc-crewmate-greeting` and `rfc-crewmates-chatted-list` in its own document, so those accepted documents are not edited. The `rfc-crewmate-greeting-as-message` row was added 2026-10-09 as `accepted` on the product owner's decision of that day and verified against `d4c3a7334d`; it amends `rfc-crewmate-greeting` §1 and §5 in its own document, so that accepted document is not edited. The `rfc-wake-on-stop-signal` row was added 2026-10-10 and verified against `d32c084921`; it amends `rfc-crew-log-wake` Goal 2, section 3.2 and section 3.2b in its own document, so that implemented document is not edited. The `rfc-mate-landing-once` row was added 2026-10-10 as `draft` and re-verified 2026-10-11 against `73d44b420b` for the rule buluoray asked for in the review of #18903, and moved to `accepted` the same day on buluoray's approval there, re-verified against `a84f616557` with the owed condition that review raised; it amends `rfc-crewmate-guides-and-mate` "When it opens" in its own document, so that accepted document is not edited. The `rfc-team-lead-default-agent` row was added 2026-10-10 and verified against `7612507e11`, in `in-progress` against the open pull request that carries it rather than against main: that pull request ships one default agent built like `kirocrew-research`, so the row records the decision rather than shipped behaviour. The `rfc-crewmate-dynamic-dashboard` row was revised 2026-10-12 against `8f6baa05c9` for a second dated amendment in its own document, which records that a save stages a page rather than landing it, that the template picker and the id-loaded catalog flow are retired in favour of a reference gallery, and that the approval gate is a module of its own so a renderer may name a package it does not publish; the row's status and its main-side reading are unchanged. The row was revised again the same day against `10350b7294` for a third dated amendment, the second of 2026-10-12, which records that the dynamic dashboard is the Dashboard tab for every reader with the "Dynamic Dashboard" Feature Preview removed, closing that document's Open question 3; the row's status and its main-side reading are unchanged. The row was revised 2026-10-15 against `40139dc512` for a fourth dated amendment, which records that a `builtin` or `user` template and the owner's own dashboard package both go live on apply with no preview step, keeps `RENDERABLE_SOURCES` and `AUTHORED_PAGE_REFUSAL` unchanged, and requires a guard test that every renderable template writes values as text only; the row's status and its main-side reading are unchanged. The `rfc-worker-model-inheritance` row was added 2026-10-12 as `accepted` on the product owner's decision of that day and verified against `e5a0b3d3b2`; it amends model-selection.md's "Pin a cheaper model" rule in that document and extends `rfc-crewmate-profile-settings` §3 in its own document, so that accepted document is not edited. The `rfc-crewmate-dashboard-upkeep-skill` row was added 2026-10-11 and verified against `a84f616557`. It was revised 2026-10-12 against `0cda1525d8` in review: the upkeep text is returned by `dashboard_fields` rather than written to a generated `SKILL.md`, which a crewmate with no file tools cannot open, and the generated directory holds the helper scripts only. The `rfc-chat-document-attachments` row was added 2026-10-09 as `draft` and verified against `8c6e7eba90`, and re-verified 2026-10-12 against `2e8831ea52`, where the upload cap it cites had become the configurable `upload_max_bytes()`. The `rfc-mcp-resource-subscriptions` row was added 2026-10-13 as `draft` and verified against `de2d143dc`. The `rfc-efficient-local-inference` row was added 2026-10-08 and re-verified against `67c1304b06` on 2026-10-09, and re-read 2026-10-10 against `d32c084921` after its Phase 1 landed as `235e6b78a0`, and again 2026-10-13 against `fec23afff7`. The `rfc-remote-instance-on-microvm` row was added 2026-10-06 and verified against `f9a07ab3fa`. The `rfc-fold-recorded-edit-cards` row was added 2026-10-16 and verified against `5c790ca239`. The `rfc-remote-computer-use` row was added 2026-10-18 as `draft` and verified against `310ad48b5f`. The `rfc-mate-owned-agent-config` row was re-audited 2026-10-17 against `8892889b91` for its first dated amendment, which records the architecture meeting's conductor/worker decisions: the conductor only dispatches and carries no user MCP server, the file's `prompt` is load-bearing for the worker, the conductor raises a card-confirmed capability change, and step 1 is a behaviour change; the row's status and its main-side reading are unchanged, and the open step-1 pull request ([#21463](https://github.com/kirodotdev/KiroCrew/pull/21463)) predates the amendment, so its conductor shape does not yet match it. The `rfc-python-rust-hybrid-backend` row was re-audited 2026-10-19 against `207ed5ae4c` for a new §2.4, which records measurements of the executor CPU, the GIL holder during loop stalls and the anonymous-memory remainder taken on a second host; the row's status and its main-side reading are unchanged. The `rfc-living-ui` row was added 2026-10-18 as `draft` and verified against `9d088c85ad`, and moved to `implemented` on 2026-10-21 against `0b89e0c6f7` on the maintainer's decision of that day. The `rfc-conductor-rolling-reset` row was added 2026-10-19 as `draft` and verified against `0adc85c233`; it amends `rfc-conductor-round-reset` Goals, Non-goals and section 3 in its own document, so that implemented document is not edited. The `rfc-consolidation-fresh-conversation` row was added 2026-10-19 and verified against `5d1a459f2a`, in `in-progress` against its open implementation pull request ([#22207](https://github.com/kirodotdev/KiroCrew/pull/22207)) rather than against main. The `rfc-review-loop-and-escaped-bug-reduction` row was added 2026-10-19 as `draft` and verified against `2cf3d0d69`; it changes no code. That row moved to `accepted` on 2026-10-20 on the repository owner's decisions recorded in section 2.2 of the document; its main-side reading is unchanged. The `rfc-crew-log-telemetry-export` row was added 2026-10-16 as `draft` and verified against `1a2eb5435c`. The `rfc-gated-pr-poll-loops` row was added 2026-10-20 as `draft` and verified against `6432cad9ea`; it changes no code. The `rfc-crewmate-heartbeat` row was added 2026-10-15 as `accepted` on the product owner's decision of that day and verified against `8da5b41552`. The `rfc-conductor-context-cap` row was added 2026-10-19 as `draft` and verified against `c3f2b27de7`; it supersedes `rfc-conductor-rolling-reset`, whose row moved to `superseded` the same day, before that draft was accepted. The `rfc-crewmate-page-on-goal-wake` row was added 2026-10-21 as `accepted` on the product owner's decision of that day and verified against `c428ea050d`; it amends `rfc-crewmate-goals` §3.5 in its own document, so that accepted document is not edited. The `rfc-crewmate-page-ambient-motion` row was added 2026-10-21 as `accepted` on the product owner's decision of that day and verified against `4533b1eb5b`; it amends `rfc-living-ui` §5.1 rule L and §10 in its own document, so that implemented document is not edited. The `rfc-remove-piper-tts` row was added 2026-10-22 as `accepted` on the repository owner's decision of that day and verified against `69b2e503eb`. The `rfc-crew-enterprise-edition` row was added 2026-10-22 as `draft` and verified against `5c32cf32b`. It was revised 2026-10-23 against `a7ecdf3f1` for the owner's three decisions recorded in its section 5, and moved to `accepted` on the owner's approval of the design that day. The `rfc-conductor-patrol-session` row was added 2026-10-24 as `draft` and verified against `1270c45f4d`; it changes no code. The `rfc-git-branch-switcher` row was added 2026-10-24 as `draft` and re-verified 2026-10-25 against `1a7190c711`; it changes no code. | Document | Status | What is actually on main | |---|---|---| +| [rfc-git-branch-switcher.md](rfc-git-branch-switcher.md) | `draft` | Nothing built. Verified at `1a7190c711`: no route under `src/` switches a branch (`GET /api/project/git` in `dashboard/file_api/project_dirs.py` only reads `.git/HEAD`, and the status and log routes in `dashboard/file_api/git_panel.py` are read-only), the composer chip's branch segment copies the name (`CopyBranchButton` in `website/src/components/ChatInput.tsx`), and nothing records which agent work runs in which repository. Proposes a branch picker in the Git panel header and the composer chip, `GET /api/project/git/branches` and an owner-only `POST /api/project/git/switch` on the `POST /api/worktree/create` boundary, refused while any chat turn, linked-channel turn, sub-agent, workflow step or ACP prompt in flight works in the repository, ending no session, in four phases. The implementation is [#23102](https://github.com/kirodotdev/KiroCrew/pull/23102), a draft until this document is accepted. Tracks [#22670](https://github.com/kirodotdev/KiroCrew/issues/22670). | | [rfc-crew-enterprise-edition.md](rfc-crew-enterprise-edition.md) | `accepted` | Nothing built. Verified at `5c32cf32b`: no `src/kiro_crew/edition.py` exists, `make desktop` takes no `EDITION`, Crew starts without a kiro login or a governance ceiling, and `_query_level` in `platform/governance_parts/evaluator.py` returns `_PERMIT_NOT_GOVERNED` for a control the policy does not name. Proposes a build-time `EDITION` constant; an enterprise gate (kiro login, then fetch governance rules, either failing exits, fail-closed, refreshed on `crew_policy_refresh_seconds`); every governable feature off by default with a minimal set per control (66 controls); and deny-unnamed in enterprise `_query_level`. Records three owner decisions of 2026-10-23: `EDITION` is the only enterprise fact (`resolve_profile` ignores `KIROCREW_PROFILE`, the SSO-marker probe and entry points), the policy URL is a build-time constant `KIROCREW_POLICY_URL` cannot change, and no local source can widen a control, with a built-in baseline policy holding the minimal sets. Open: the governance service API. | | [rfc-pptx-maker-server-driven-engine.md](rfc-pptx-maker-server-driven-engine.md) | `draft` | Nothing; the document lands first because following SDPM v0.10 replaces the app's four agents with one and gates deck chats on an engine update, and it also moves where "Start a deck" opens, and the First Principles lane reads that decision off the base branch. Verified at `ad5b1e670` and re-read at `557c8f8e64`: `backend/engine_source.py` pins `ENGINE_TAG = "v0.3.8"`, `agents/` ships `pptx-maker-{spec,vibe,style,composer}.json` loading prompts by `file://` from `mcp-local/acp-agent-prompts/`, `prompts/spec-studio.md` overrides the engine prompt, and `PptxMakerPage.tsx` `startChat` navigates to `/chat`. The implementation is [#15394](https://github.com/kirodotdev/KiroCrew/pull/15394), a draft until this document is accepted | | [rfc-gated-pr-poll-loops.md](rfc-gated-pr-poll-loops.md) | `implemented` | Accepted 2026-10-21 by maintainer Joe Guo (@iamwhatever); shipped in [#22442](https://github.com/kirodotdev/KiroCrew/pull/22442), verified at `098bc0a174` with that change applied. The prepare-pr recipe (`SKILL.md` Phase 3 step 5) and the babysit comment-aware example arm their pull-request poll loop gated with `judge=False`, so an unchanged pull request costs no turn and each change to its reading wakes the loop once; `_monitor_tick_is_quiet` in `autonudge_service/gate.py` is the judge-less path that makes it so, and `test/test_prepare_pr_gated_poll_loop.py` drives both recipes through it. `docs/system-specs/modules/learn-cron-dashboard.md` owns the shipped behaviour, and prepare-pr's `references/rationale.md` carries the reasons. Related: [rfc-wake-judge.md](rfc-wake-judge.md) § 8 PR F. Tracks [#22418](https://github.com/kirodotdev/KiroCrew/issues/22418). | diff --git a/docs/request-for-change/rfc-git-branch-switcher.md b/docs/request-for-change/rfc-git-branch-switcher.md new file mode 100644 index 00000000000..a6f21c1d738 --- /dev/null +++ b/docs/request-for-change/rfc-git-branch-switcher.md @@ -0,0 +1,376 @@ +--- +title: Git branch switcher -- switch the project's checked-out branch from the dashboard, refused while an agent works in the repository +status: draft +author: inariku, with Kiro Crew +created: 2026-10-24 +last-audited: 2026-10-25 +audited-at: 1a7190c711 +doc-pr: 23725 +implementation-prs: [23102] +tracking-issues: [22670] +supersedes: [] +superseded-by: [] +--- + +# RFC: Git branch switcher + +- Status: draft. Nothing is built on main. A working implementation of every + phase below exists as one draft pull request, + [#23102](https://github.com/kirodotdev/KiroCrew/pull/23102), which takes over + [#17628](https://github.com/kirodotdev/KiroCrew/pull/17628). That pull request + is held in draft until this document is accepted, and will be split along the + phases in section 6. +- Tracking issue: [#22670](https://github.com/kirodotdev/KiroCrew/issues/22670) + (`tier:T3`, `parked`). +- Why an RFC: the change adds the dashboard's second route that rewrites a + repository's working tree, adds a rule that refuses a user action while any + agent works in the same repository, and records agent prompts in flight at the + ACP transport. Those are hard to reverse once other code relies on them, and + the First Principles lane reads the decision from the base branch. +- Measured at `1a7190c711`. Every symbol cited below is on that commit unless + it names #23102. + +## 1. Summary + +Let the dashboard switch the project's checked-out branch from a searchable +picker, opened from the branch name in the Git panel header and from the branch +segment of the project chip under the chat box. The switch runs `git switch` in +the project's repository, relies on git's own refusal to overwrite uncommitted +work, and is refused while any agent is working in that repository, naming what +it is waiting for. Nothing is ended, killed or stashed to make a switch possible. + +## 2. Motivation + +### 2.1 What exists today + +- The branch is shown, never changed. `GET /api/project/git` + (`api_project_git` in `src/kiro_crew/dashboard/file_api/project_dirs.py`) + reads `.git/HEAD` directly to label the branch; `GET /api/project/git/status` + and `GET /api/project/git/log` (`api_project_git_status` and + `api_project_git_log` in `src/kiro_crew/dashboard/file_api/git_panel.py`) are + read-only. No route under `src/` and no component under `website/src/` + switches a branch: `git grep` for `git/switch`, `git/branches` and + `repo_checkout_guard` returns nothing. +- Clicking the branch segment of the composer chip copies the name + (`CopyBranchButton`, rendered by `website/src/components/ChatInput.tsx`). +- The one route that rewrites a repository today is `POST /api/worktree/create` + (`api_worktree_create` in `src/kiro_crew/dashboard/handlers/worktree.py`), + which serializes per repository on `_repo_lock` and runs git through the + strict-sandbox helper `_run_git`, both in the same module. +- Nothing records which agent work is running in which repository. A chat turn + starts at `_turn_open` (`src/kiro_crew/dashboard/chat_turn/turn_entry.py`) + and claims its session in `_turn_admit` (`chat_turn/turn_admission.py`); a + sub-agent runs from `_run_inner_impl` + (`src/kiro_crew/subagent_manager/run_turn.py`); workflow steps open their own + sessions outside chat slots (`src/kiro_crew/workflows/agent_exec.py`, + `agent_pool.py`); and the ACP request writers (`_send_request` in + `acp/client_transport.py`; `send_request`, `send_request_for_answer` and + `_send_and_await` in `acp/runtime_wire.py`) do not know the repository a + request will act on. + +### 2.2 The problem + +The issue's case: an agent works in a separate worktree and opens a pull +request; the user later checks that branch out in the main checkout to deploy +it; reopening the repository in Kiro Crew, it is still on that branch, and the +user does not want the agent to see it. The dashboard offers no way back, so +the user leaves for a terminal or asks the agent to run `git switch`, which +spends a turn on a one-word command. + +The hard part is not running `git switch`. It is that a checkout rewrites files +under every session working in that repository. A switch that lands while an +agent is mid-turn means the agent read files on one branch and writes them back +on another. The picker can see its own chat's turn, but the same repository can +be worked on at the same time by another chat, a Slack thread linked to an idle +chat, a sub-agent, a workflow step, a cron run, or a turn the user cancelled +whose backend is still running tools. + +## 3. Goals + +- G1. Switch to a local branch, check out a remote-only branch as a tracking + branch, or create a branch at the current commit, from one picker. +- G2. Never lose work: uncommitted and untracked changes either carry over (git's + default) or the switch is refused with the files in the way named. Ignored + local files the target branch tracks are protected the same way. +- G3. Never switch under a running agent. Refuse while any agent work may still + change files in the repository, and say what to wait for. +- G4. End nothing to make a switch possible. A refusal is the whole answer; the + user decides whether to stop the work. +- G5. The project boundary `POST /api/worktree/create` already draws, plus an + owner-only switch, strict-sandbox git with hooks and fsmonitor off, every + string redacted, and every switch in the SEL. + +## 4. Non-goals + +- fetch, pull, push, rebase, merge, delete or rename a branch. +- Switching a project folder that is a subfolder of its repository (the switch + would rewrite files outside the project the user chose). +- Repositories whose config declares a checkout filter driver (`git lfs` and + similar). The picker lists them and says why switching is off. +- Stashing, auto-committing or discarding changes on the user's behalf. +- Remote instances and peers. +- Changing what the agent does; an agent can still run `git switch` itself. + +## 5. Design + +### 5.1 Where the switcher lives + +- The branch name in the Git panel header opens the picker. +- The branch segment of the composer's project chip opens the same picker + upward. While this chat's response is running the segment keeps today's copy + behaviour and its tooltip says to stop the response with the message box's + stop button; a switch would be refused at that moment anyway. + +The picker is searchable. It lists local branches newest first, then +remote-tracking branches that have no local branch of the same short name, and +offers "create branch" when the query is a valid new name. Each list is capped at +200 rows and says so when cut. Every row carries the commit subject, author and +date; local rows carry ahead/behind counts. + +### 5.2 Routes + +`GET /api/project/git/branches?path=` lists. `POST /api/project/git/switch` +with `{path, branch, create?, track?}` switches. Both live in a new +`dashboard/handlers/git_branches.py` and reuse the worktree route's boundary: +`path` must name or sit inside a live chat slot's project, and so must the git +toplevel it resolves to (`repo_root_outside_project`, 403). Listing takes the +dashboard-caller gate; switching is owner-only, as `/api/file-write` is. + +The switch runs `git switch --no-recurse-submodules --no-overwrite-ignore` with +`--no-guess` for an existing branch, `--no-track -c` for a new one and +`-c --track refs/remotes/` for a remote one. Names are validated +before git sees them (no leading `-`, no `@{`, no whitespace; an existing name +must resolve under `refs/heads/`). Already-on-branch is a 200 no-op. + +Failures are coded so the picker can render localized copy: +`git_switch_dirty` (with up to five redacted `files` and a `files_more` count), +`git_switch_session_busy` (with `busy`, section 5.3), `git_switch_filter_refused`, +`git_branch_exists`, `git_branch_in_worktree`, `git_operation_in_progress`, +`git_branch_not_found`, `invalid_branch`, and `git_switch_failed` carrying git's +redacted first line. git runs with `LC_ALL=C` because the codes are read from +git's English diagnostics. + +### 5.3 The busy rule + +A switch is refused with 409 `git_switch_session_busy` while any of these may +change files in the repository, matched on the repository root, a folder inside +it, or a folder containing it. + +A folder inside the root that belongs to another checkout does not match. A +linked worktree created under the repository (`.worktrees/x`), a nested +repository or a submodule has its own working tree, which `git switch +--no-recurse-submodules` in the root does not rewrite. A folder is in another +checkout when, walking up from it toward the root, a `.git` file or directory +appears before the root is reached; the check reads the file system and runs no +git. The same match decides which work waits on a checkout's reservation. #23102 +matches on the path alone (`_shares_tree` in +`dashboard/handlers/git_branches.py`, `_covering_root` in +`repo_checkout_guard.py`), so a session in a worktree nested under the +repository refuses a switch it cannot be affected by; Phase 3 changes that. + +The sources are: + +1. a dashboard chat turn, matched on every folder the turn works in: the + project at its entry, the project its session was allocated for and the + folder that session reports. A project picked mid-turn moves `project` while + the session keeps its folder, so `project` alone is not enough; +2. a turn a linked channel (a Slack thread on a chat) runs on an idle chat's + session; +3. a running sub-agent, through its own working folder, its ancestors' or its + root chat's project; +4. a workflow step's session; +5. any prompt in flight to an agent process working in the repository, recorded + at the ACP transport from before the request is written until the backend + answers it or the runtime confirms the process tree exited (section 5.4). + +A cron, heartbeat or monitor-wake turn, or an app's turn, has no rule of its +own: it runs on a chat slot, a channel session or a sub-agent, so it is caught +by whichever of rules 1 to 3 applies, and in every case by rule 5. +Work whose folder is not known yet counts as busy in every repository. A turn +is "unplaced" from the moment it starts allocating its session until the session +reports its folder, because the allocator may place it in a member's workspace +or a resumed conversation's stored folder that no `project` value names. + +The refusal carries `busy`: the title of the chat or the label of the workflow +run that holds the repository, redacted and clipped to 120 characters, empty for +a sub-agent, a prompt in flight or unplaced work. The picker shows it ("Not +switched: “Fix the login redirect” is still running in this repository. Wait +for it to finish, then switch."). + +Busy is checked under the repository lock, and the switch also takes a +reservation in a new registry, `kiro_crew/repo_checkout_guard.py` (below the +dashboard, so the engines that register with it import no dashboard code), +before the check awaits anything. Work that starts during a checkout waits: +`_turn_open` waits at its first await, the turn waits again before it claims a +session, sub-agents wait in a `pre_run_gate` the dashboard installs, and ACP +writers wait before reserving a request id. The reservation is released by the +checkout worker's own completion, so a cancelled HTTP request cannot drop it +while git is still rewriting files. + +That wait is bounded by git, not by a timer of its own. Every git call the +switch makes goes through `worktree._run_git`, which kills the call after +`_GIT_TIMEOUT` (120 s) of wall-clock time, and the switch makes a fixed number +of calls (the toplevel probe, the filter-driver check, the name and HEAD checks +and the `git switch` itself). A git that hangs therefore ends in a timeout, the +worker completes, and the reservation is released; the switch answers with an +error and the SEL records it. While anything waits on a reservation it logs a +warning every 120 s naming what waits. A timer on the wait itself is rejected: +releasing the reservation while git may still be writing is the failure the +reservation exists to prevent. Whether that rule also binds the busy records +of section 5.4 is open question 3. + +### 5.4 Prompts in flight + +Rules 1 to 4 see work through the structures that start it. Rule 5 exists for +what they cannot see: a turn the user cancelled whose backend keeps running +tools, a channel turn with no dashboard slot, and a workflow step after its +caller gave up. Each request that starts agent work (`session/prompt`, a kiro +slash command, a steer, a KAS checkpoint restore) is recorded by +`AcpClient._send_request` and `AcpRuntime`'s three request writers, against the +session's folder. The frame readers end it when its response arrives, including +a response whose `result` is JSON `null`. A teardown that confirms the process +tree exited ends that process's prompts; one that cannot (a root it could not +prove dead, descendants left for the orphan sweep) hands the guard a probe of +the leftovers, which the busy check runs off the loop until they are gone. A +root that exits on its own (an OOM kill) gets the same treatment once the exit +is confirmed. + +If the writer is cancelled before any byte reached stdin, the registration is +removed. Once `stdin.write` accepted the frame it stays, because the backend may +still consume it. + +### 5.5 Security + +- Owner-only switch, project allow-list on both `path` and the resolved + toplevel, as for `POST /api/worktree/create`. +- git runs through `worktree._run_git`: strict sandbox, argv list, + `core.hooksPath=/dev/null`, `core.fsmonitor=false`, scrubbed environment. +- A repository whose config declares a `filter.*.{clean,smudge,process}` driver + is refused, as the Git panel's status route already refuses it (the probe in + `git_panel.py` mirrors `worktree.py`'s `_checkout_filter`). + `--no-recurse-submodules` keeps a submodule's own drivers out of reach. +- Every string in both responses is redacted. A branch name the redactor alters + is listed as not switchable, since it could never be echoed back. The handler + is registered as a redaction sink in `security_posture_sinks_delivery.py`, + which makes the implementation a sensitive-path change. +- Every switch, refused or not, is written to the SEL, including one whose HTTP + request was cancelled while git ran. + +## 6. Migration plan + +Each phase is one pull request and can be abandoned without the next. + +**Phase 1: list branches (read-only).** `GET /api/project/git/branches`, a +read-only branch list opened from the Git panel header (each row shows its +subject, author, date and ahead/behind; clicking a row does nothing yet), and +the redaction-sink row. No new way to change anything. +Exit: the route lists local and remote branches with redaction and caps; a +redactor-altered name is `switchable: false`; a filter-driver repository lists +with `switchBlocked: "filter"`; a path outside every live chat slot's project +is refused with 403 and audited. + +**Phase 2: the work registry, with nothing reading it.** The registry in +`kiro_crew/repo_checkout_guard.py`, the chat-turn, sub-agent, linked-channel and +workflow-step registrations, and prompt tracking at the ACP transport. No user +behaviour changes; the cost is bookkeeping on every turn and request. +Exit: tests show a cancelled turn whose backend has not answered stays +recorded; a `result: null` response ends its prompt; a root that dies on its own +ends its prompts once confirmed; a lingering descendant keeps the record until +it exits, across a respawn of the same runtime; workflow tests keep the +expectations they have on main (no session is ended); prompt tracking is +tested for every backend in `BASELINE_SELECTABLE_BACKENDS` +(`src/kiro_crew/agent_sdk/backends.py`), parametrized over that set so a +backend added later is covered without a new test, since every harness writes +through the same ACP transport; and the bookkeeping adds at most 1 ms at p99 to +an ACP request write and to a response read, measured with and without it on +the same host and recorded in the pull request. + +**Phase 3: switch from the Git panel.** `POST /api/project/git/switch` and the +busy rule reading Phase 2's registry, the reservation and the waits, SEL +records, localized refusal copy. +Exit: each busy source in section 5.3 refuses with the right `busy` text; a +session working in a linked worktree under the repository neither refuses the +switch nor waits on it; a switch blocks a turn that starts during the checkout +until git exits; a cancelled +switch request still releases the reservation and writes its SEL record; dirty, +filter and invalid-name cases return their codes and change nothing. + +**Phase 4: the composer chip.** The chip's branch segment opens the picker; +while the response runs it keeps copying and says how to stop it. +Exit: chip tests for both states; screenshots for the UX lane. + +## 7. Backward compatibility + +The routes and the registry are additive. The one visible change to existing +behaviour is the chip's branch segment, which opens the picker instead of +copying when the chat is idle (Phase 4). Phase 2 adds work to every ACP request +write and response; its exit criterion bounds that cost. + +## 8. Alternatives considered + +- **End the sessions working in the repository, then switch.** The design in + #17628 ended a workflow step's session when the step failed or was cancelled, + so the repository would read as idle. That loses the conversation of anyone + who never uses the switcher, and the First Principles review blocked it. + Refusing loses nothing and leaves the decision with the user. +- **Warn instead of refuse.** A warning the user clicks through still rewrites + files under a running agent. The refusal is cheap to wait out; the damage is + not cheap to undo. +- **Stash automatically.** Moves the user's work somewhere they did not ask for, + and the stash is shared by every worktree of the repository. +- **Tell the user to ask the agent.** Today's answer. It works, costs a turn, + and races the same way if another session is working in the repository. +- **Open a worktree per branch instead.** Already offered by the follow-up card + (`POST /api/worktree/create`). It answers "work on another branch", not the + issue's "put this checkout back on main". +- **Only check the chat's own turn.** The picker already disables itself while + its own response runs. That misses every other source in section 5.3, and + those are the ones a user cannot see from the chat they are in. + +## 9. Open questions + +1. Scope of the first release. The issue asks for local branches only; + #23102 also offers remote-tracking checkout and create. Should Phases 1 and 3 + ship local-only and add the other two later? +2. Unplaced work counts as busy in every repository. That is safe but can refuse + a switch in an unrelated repository while a session is still starting. Is a + refusal that names "a session that has not reported its folder yet" enough, + or should unplaced work be bounded by a timeout? +3. **Decide before acceptance:** on the Windows client path there is no pid to + probe after a teardown that could not confirm the tree exited, so as + implemented in #23102 that process's prompts stay recorded, and switching in + that repository stays refused, until the gateway restarts. Options: + (a) keep that, and say in the refusal that a gateway restart clears it; + (b) hold the record for a bounded time (for example 10 minutes) after the + unconfirmed teardown, then drop it and write an SEL record, accepting that a + descendant still writing after that window is not seen; + (c) keep refusing, but let the owner switch anyway from the refusal, with an + explicit confirmation and an SEL record, for records left by an unconfirmed + teardown only. + This question also settles a conflict in the document. Section 5.3 rejects a + timer on the reservation's wait, because releasing while git may still write + is what the reservation prevents; option (b) ends a busy record on elapsed + time while a descendant may still write. Choosing (b) makes it a stated + exception to that rule; choosing (a) or (c) extends the rule to every busy + record. (a) costs a refusal the user cannot end without a restart, (c) costs + an override path the owner must judge. The document takes no position; the + choice is the maintainers' at acceptance. +4. Should branches checked out in another worktree be marked disabled in the + list, as the issue suggests, instead of being refused on click with + `git_branch_in_worktree`? +5. How much of the busy rule ships. Three sizes: + (a) all five rules, as proposed; + (b) rules 1 to 4, without prompt tracking at the ACP transport, accepting + the gap of a cancelled turn whose backend keeps running tools; + (c) rules 1 and 3 only, close to an editor's branch picker: refuse while a + chat turn or a sub-agent works in the repository, and let a switch through + under a linked-channel turn or a workflow step. + (b) drops the ACP part of Phase 2 and with it the per-request cost and the + per-backend tests; (c) also drops the linked-channel and workflow-step + registrations, roughly halving Phase 2. Each smaller size lets through the + work the larger one names, and the user cannot see that work from the chat + they switch from. +6. Should the busy rule be the shared guard for any future route that changes a + repository (for example `POST /api/worktree/create`), and if so should a + maintainer own `kiro_crew/repo_checkout_guard.py` as a module of its own + rather than as part of this feature?