Skip to content

Track which worktrees/branches a session is actually working in #804

Open

Description

@kyleseaman

What

Track which git worktrees (and therefore which branches) a chat session is
actually working in, and surface them — instead of only the single branch of the
session's project directory.

Follow-up to #673 / PR #760, which added <folder> · <branch> to the active
project button. That label is correct for its scope but structurally cannot cover
a multi-worktree session.

Why

A chat slot has exactly one branch-bearing field — self.project: str in
src/kiro_crew/dashboard/state.py. There is no session→worktrees association
anywhere in state: no list of worktrees used, no set of branches touched. So the
chip renders one project directory's HEAD and nothing else.

For KiroCrew's own development workflow that is the common case, not an edge
case. The kirocrew-worktree-dev skill makes one-worktree-per-feature a hard
rule, and this checkout currently has 78 worktrees. A session that starts in the
main clone and then creates ../kirocrew-wt-<feature> to do the actual work
leaves the chip reading main — a branch that is none of the ones being
worked on, displayed with the same authority as a correct one. A session that
touches several worktrees in one sitting shows whichever single directory happens
to be the project dir.

Two adjacent facts make this worse rather than self-correcting:

  • set_project cannot fix it from inside the session. It is one of the six
    tools behind the strict session-identity gate (_strict_identity_diagnosis in
    src/kiro_crew/mcp_core.py), so on a default install where
    mcp_gateway.enabled is false it refuses. See Six session-identity tools are dead on a default install (monitor_start, ask_question, suggest_followup, ...) #755. The agent therefore cannot
    retarget the slot to the worktree it just created.
  • Dev Fleet does not answer this question. It enumerates worktrees with their
    branches, but scoped to MAIN_REPO via git worktree list — every worktree of
    the repo, not the ones a given session used. It answers "what worktrees exist",
    never "what is this session working on".

Possible approaches (not a decision)

  1. Track the agent's working directory per turn. Most faithful to "what is
    this session working on", but needs the runtime to report cwd per turn, which
    it does not today.
  2. Record worktrees created during the session. Cheaper and covers the
    dominant KiroCrew pattern (git worktree add mid-session), but misses a
    session that merely cds into a pre-existing worktree.
  3. Multi-value the project dir. Let a slot carry several project directories
    and render N branches. Largest blast radius — the single project field is
    read in many places (file picker, skills resolution, @-mentions, knowledge
    scoping), so this is a data-model change, not a label change.

Whatever the mechanism, the UI question is separate: the composer chip has room
for roughly one <folder> · <branch> pair before truncation bites, so N branches
probably belong in the session's side panel or the sidebar row rather than the
chip.

Explicitly out of scope for #760

PR #760 keeps the project-directory scope as its contract, which is what #673
asked for. This issue is the real multi-worktree gap and needs its own design —
new state, not a rendering tweak.

Not verified

Whether the ACP runtime can cheaply expose the agent's cwd per turn (approach 1)
has not been investigated; it is listed as a candidate, not a known-feasible
path.

Activity

  1. added
    enhancementNew feature or request
    follow-upDeferred work split out of a merged PR
    area: dashboardDashboard UI and its backend handlers
    on Jul 30, 2026
  2. added
    needs-investigationTriage: requires deep analysis before a fix
    and removed
    needs-triageAwaiting automated triage
    on Aug 11, 2026
  3. NicholasRBowers commented on Aug 11, 2026

    @NicholasRBowers
    Contributor

    🤖 Kiro Crew Auto-Pipeline [operator: NicholasRBowers]: The grounding here is right: a chat slot has exactly one branch-bearing field and there is no session-to-worktree association anywhere in state, so the displayed branch can be none of the ones actually being worked in.

    What is genuinely open is not the cause but the mechanism: how membership gets detected (observed tool calls and their working directories, explicit registration, or a scan of the checkout's worktrees), how stale entries are retired, and how the chip presents a session that spans several worktrees without asserting one of them with false authority. Those are design choices with a persistence cost, so this needs a human decision before implementation rather than an automated fix.

  4. added
    needs-humanTriage: requires a human or product decision
    and removed
    needs-investigationTriage: requires deep analysis before a fix
    on Aug 24, 2026
  5. dwu96 commented on Aug 24, 2026

    @dwu96
    Contributor

    🤖 Kiro Crew Auto-Pipeline [operator: dwu96#8c08bee0]: Re-triage — moving to needs-human. The core claim survives: a slot still has exactly one branch-bearing field (self.project: str = \"\" at src/kiro_crew/dashboard/state.py:2747), with no session→worktrees association anywhere. One supporting claim does not survive and should be re-checked before anyone acts on the body: _strict_identity_diagnosis no longer exists in src/kiro_crew/mcp_core.py, so the set_project gate described here has since moved or been removed. Routing to needs-human because the ask is a new tracking surface whose approach is explicitly uncommitted in the issue itself (how worktree use is detected, and whether the chip shows a set or a primary) — that is a product/design decision, not analysis debt.

  6. pm25coder commented on Aug 25, 2026

    @pm25coder

    Verified against current main, plus a mechanism proposal that reuses plumbing that already exists.

    Premise check (current line numbers). self.project: str = "" is at src/kiro_crew/dashboard/state.py:3139 (the :2747 citation is stale), serialized as "project": self.project in the slot wire format at state.py:4876. The strict gate moved but survives: _strict_identity_diagnosis is now _resolve_session_key_strict (src/kiro_crew/mcp_core.py:619) with a diagnostic twin strict_identity_diagnosis (:679), and set_project still fail-closes without a strict identity on the MCP path (mcp_core.py:637-638 lists it explicitly). One nuance for the issue body: the dashboard REST write api_chat_slot_project (src/kiro_crew/dashboard/chat_handlers.py:4465-4504) is gated only by _deny_cross_app_slot_access (:4472, defined :4562) plus is_sensitive_path (:4487) — dashboard-user requests always pass (:4571-4573). So "the agent cannot retarget the slot" holds for the MCP tool, but there is a second, identity-ungated write path to the same slot.project; worth confirming the intended boundary.

    Approach 1 — the "Not verified" item is now answerable: the ACP runtime's cwd is spawn-time only, not per-turn. providers/acp.py:436-443 exposes cwd as the underlying client's _work_dir ("the real workspace path", fixed at session create/resume — acp.py:631 and :832), threaded into the provider context by workflows/agent_exec.py:54/:83. There is no per-turn cwd report today, so approach 1 is a runtime/agent-side change (the agent's tool layer reporting cwd per call), not dashboard plumbing.

    Approach 2 already has a precedent in this repo. src/kiro_crew/git_coord.py:43-45 creates a worktree per scheduled task and stores run.worktree_path on the Project object; finalize removes it (:110-114). A chat slot could carry the same shape: a worktrees: list[str] (path + last-seen round), populated when the session's tool path intercepts git worktree add.

    Stale-entry retirement is nearly free with approach 2. Membership = observed ∧ still-existing: diff the recorded set against git worktree list --porcelain, which Dev Fleet already parses (dev_fleet/server.py:1832+), and drop paths that vanished (the task lifecycle removes them via git_coord.finalize). No TTL heuristic needed — the worktree list is the ground truth. The .git-as-file handling in _resolve_primary_checkout (server.py:125-128) is exactly the resolution needed to map a recorded path back to a worktree object (primary vs linked).

    Approach 3's blast radius is smaller than feared if done additively. The wire format already emits a single "project" (:4876); adding a sibling "worktrees": [...] while the chip keeps reading project as the primary is a wire-format addition, not a change to every project consumer — the file picker / skills resolution / @-mentions keep reading the unchanged primary.

    Net: the cheapest faithful mechanism is 2 (record created worktrees) + porcelain-diff retirement, reusing Dev Fleet's existing enumeration; 1 needs a runtime change and can be layered on later as a stricter source.

  7. bolichen97 commented on Sep 25, 2026

    @bolichen97
    Contributor

    Answers to the questions raised in this thread, checked against current main. The design decision itself (how to track several worktrees and where to show them) is not addressed here and stays open.

    1. set_project is no longer identity-gated. This corrects the body and the latest comment.
    Since b756211 (#844, one of the two fixes for #755, with afb1c5f / #907 as the second), set_project has been a stateless directive.

    What this means for this issue:

    • The tool needs no strict session identity and no mcp_gateway.enabled. In a dashboard chat turn that a person started, the agent can point its slot at a worktree it just created. The change takes effect on the next message, which cold-starts in the new directory.
    • It is refused for turns no person started (cron, sub-agents, task runners, app-driven turns, loop wakes). It is also refused for channel turns that hold no dashboard slot (L266-L290).
    • Two texts still list set_project as a strict-identity tool, and both are stale: the _resolve_session_key_strict docstring (mcp_core.py#L846) and the macOS/Windows kirocrew doctor note (cli_doctor.py#L2169-L2182). The tool does not run the check they describe.
    • No bundled skill calls set_project: there is no match under src/kiro_crew/builtin_skills/. kirocrew-worktree-dev runs git worktree add and then cd. After that, the slot keeps its original project unless the agent calls set_project itself.

    This only helps with a single worktree. slot.project is still one string (state.py#L2673), sent to the frontend as a single "project" key (slot_projection.py#L288). A session that spans several worktrees still has no representation.

    2. Approach 1, the "Not verified" item: the ACP working directory is set once per session, not per turn. The previous comment reached the same conclusion; here are current citations.

    3. The REST write to slot.project is the dashboard's own project picker, not an agent path.

    • The composer's project picker calls POST /api/chat/slots/{slot}/project (ChatPage.tsx#L3117-L3126, client.ts#L3983-L3984).
    • Every dashboard request needs a valid token, and loopback is not exempt (token_auth.py#L2329-L2333).
    • The route limits app tokens to slots and sessions they own. It then runs realpath, sensitive-path and data-home-overlap checks (chat_handlers.py#L10096-L10155).
    • The handler's comment calls it the one remaining HTTP writer of slot.project, next to the in-turn set_project directive (L10156-L10165). Session identity is not the relevant control on this route, because the caller is the dashboard user rather than an agent turn.

    Related existing behaviour: the follow-up card's "Start in new worktree" action creates a worktree and opens a new session scoped to it (ChatPage.tsx#L3355-L3385). That gives one session per worktree. It does not track worktrees inside one session.

    Still open, for a maintainer to decide:

    • how the session-to-worktree link is recorded,
    • how stale entries are removed,
    • where several branches are shown.

    Related but separate requests: #13488 (move the single project dir automatically), #12432 and #1607 (worktree pickers).

    Triage pass against main @ c66f95b.

  8. added
    tier:T2Small additive feature; existing behaviour unchanged
    on Oct 3, 2026
  9. KiroCrewCaptain commented on Oct 8, 2026

    @KiroCrewCaptain
    Collaborator

    Thanks for the report. This issue has been triaged by Crew Captain and will be assigned to a maintainer, who will follow up here.

  10. dodgeblaster commented on Oct 21, 2026

    @dodgeblaster
    Contributor

    Re-verified against current main (0b89e0c6f7). The gap is unchanged; posting refreshed citations because state.py was split since the last triage (the earlier state.py:3139 / slot_projection.py:288 line numbers are now stale).

    The cause still holds — a slot has exactly one branch-bearing field, and there is no session→worktree association anywhere in state.

    • The field moved out of the monolithic state.py into a state part: self.project = "" now lives at src/kiro_crew/dashboard/state_parts/slot_session.py:217, still a single string.
    • It is still projected to the frontend as a single key: "project": slot.project at src/kiro_crew/dashboard/slot_projection.py:409.
    • No worktrees list, no branches_touched set, and no per-turn cwd record exists on the slot or anywhere session-scoped (grep of state_parts/ for worktree/branches returns only unrelated code-comment uses; the worktrees: list[dict] hits in apps/builtins/dev_fleet/fleet_state.py:1129 and repository.py:1654 are Dev Fleet's repo-scoped git worktree list enumeration, not session membership).

    No open PR addresses this. The branch-related open PRs (e.g. #17628 Git-panel/composer branch switcher, #21995 remote Git panel, #21260 Kiro Cloud session environments) are about switching or viewing a branch, not recording which worktrees a session has worked in.

    What remains open is unchanged from @bolichen97's 2026-09-25 triage — a design/product decision, not analysis debt:

    • how the session→worktree link is recorded (observe tool-call cwd per turn — not available today, ACP cwd is spawn-time only; record worktrees created mid-session via the tool path; or scan the checkout),
    • how stale entries are retired (porcelain-diff against git worktree list is nearly free, per the earlier comment),
    • where several branches are surfaced (the composer chip fits ~one <folder> · <branch> pair, so N branches likely belong in the side panel / sidebar row).

    Leaving open for a maintainer to commit the design. No code change this pass.

    Triage refresh against main @ 0b89e0c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: dashboardDashboard UI and its backend handlersenhancementNew feature or requestfollow-upDeferred work split out of a merged PRneeds-humanTriage: requires a human or product decisionquestionFurther information is requestedtier:T2Small additive feature; existing behaviour unchanged

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions