The herdr sidebar names a worktree by its key, and a rename sticks #476

Closed
opened 2026-08-28 08:04:20 +00:00 by mph · 0 comments
Owner

Problem

iw start labels a herdr workspace <project>-<key> — iw-cli-IW-463,
iw-cli-dashboard-control-plane. The label does not fit the herdr sidebar, so
the worktree is hard to find. A worktree opened in herdr's own UI carries the
short name (ACA-569, MEDECA-430), which reads much better in the same list.

The label is also renamed by hand in herdr, to make a worktree findable. Today
that rename is unsafe in principle: HerdrLookup.resolve falls back to the
label when it has no path candidate.

Outcome

  • The herdr sidebar shows a worktree by its key alone: IW-463,
    dashboard-control-plane.
  • A rename in herdr sticks. iw never reads a label back, so no operation can
    be confused by a renamed workspace.

Design

Split the one name the port carries into two values:

  • name — the addressing name, <project>-<key>. Unchanged. It is the
    tmux session name, and LiveHerdrOps.candidatePath derives the sibling
    checkout path from it.
  • label — a display name, key.value. Written once at creation, never
    read back.

The checkout path becomes the sole identity under herdr. candidatePath
already computes a deterministic sibling of the repo root; only its
.filter(os.exists) stops it from answering before iw start creates the
directory. Removing that filter makes the path route serve every call,
including the iw start collision check — which is the one case the label
route exists for today.

Changes

  • WorktreePath — add workspaceLabel: String = key.value.
  • TerminalWorkspaceOps.create(name, label, workDir). LiveTmuxOps discards
    the label; LiveHerdrOps sends it as worktree open --label.
    UnavailableWorkspaceOps and FakeCommandEnv follow.
  • Call sites: Start.scala:256, Open.scala:119.
  • LiveHerdrOps.candidatePath — drop .filter(os.exists).
  • Open.scala:99 — replace
    env.workspace.isInside && env.workspace.currentName.contains(sessionName)
    with env.workspace.isCurrent(sessionName), which compares workspace ids
    under herdr and session names under tmux. Without this, iw open inside the
    target worktree stops printing "Already in session" and re-focuses itself.
  • Tests: OpenHarnessTest, StartHarnessTest, HerdrArgvTest,
    HerdrLookupTest. Docs: docs/api/WorktreeTypes.md, then rebuild the
    published skills.

BATS is unaffected — every suite forces IW_WORKSPACE_BACKEND=tmux.

Decisions to take during the work

  1. Delete the label route in HerdrLookup? After the candidatePath
    change, resolve's label fallback and has's label test are unreachable,
    and byLabel has no caller. Leaving them is dead code that invites a wrong
    answer later; removing them is a rewrite of a reviewed unit. Needs Michal's
    call before the code is touched.

  2. currentName on the port. After the Open.scala change it has no
    production caller. Keep or drop.

  3. Effort slugs lose the project. dashboard-control-plane alone does not
    say which repository. herdr nests a worktree under its parent workspace, so
    this may already be answered in the sidebar. Confirm by eye before closing.

Notes

  • Existing workspaces keep their long labels until they are closed and
    reopened. They still resolve, by path.
  • Nothing in the dashboard reads a herdr label: HerdrAgentReader and
    AgentSessionService key an agent on its working directory.
  • herdr worktree open --label is optional, so the flag could be dropped
    instead and herdr would derive a name from the branch. Rejected: it puts the
    naming in herdr's hands and leaves us no way to change it.
## Problem `iw start` labels a herdr workspace `<project>-<key>` — `iw-cli-IW-463`, `iw-cli-dashboard-control-plane`. The label does not fit the herdr sidebar, so the worktree is hard to find. A worktree opened in herdr's own UI carries the short name (`ACA-569`, `MEDECA-430`), which reads much better in the same list. The label is also renamed by hand in herdr, to make a worktree findable. Today that rename is unsafe in principle: `HerdrLookup.resolve` falls back to the label when it has no path candidate. ## Outcome - The herdr sidebar shows a worktree by its key alone: `IW-463`, `dashboard-control-plane`. - A rename in herdr sticks. iw never reads a label back, so no operation can be confused by a renamed workspace. ## Design Split the one `name` the port carries into two values: - **`name`** — the addressing name, `<project>-<key>`. Unchanged. It is the tmux session name, and `LiveHerdrOps.candidatePath` derives the sibling checkout path from it. - **`label`** — a display name, `key.value`. Written once at creation, never read back. The checkout path becomes the sole identity under herdr. `candidatePath` already computes a deterministic sibling of the repo root; only its `.filter(os.exists)` stops it from answering before `iw start` creates the directory. Removing that filter makes the path route serve every call, including the `iw start` collision check — which is the one case the label route exists for today. ## Changes - `WorktreePath` — add `workspaceLabel: String = key.value`. - `TerminalWorkspaceOps.create(name, label, workDir)`. `LiveTmuxOps` discards the label; `LiveHerdrOps` sends it as `worktree open --label`. `UnavailableWorkspaceOps` and `FakeCommandEnv` follow. - Call sites: `Start.scala:256`, `Open.scala:119`. - `LiveHerdrOps.candidatePath` — drop `.filter(os.exists)`. - `Open.scala:99` — replace `env.workspace.isInside && env.workspace.currentName.contains(sessionName)` with `env.workspace.isCurrent(sessionName)`, which compares workspace ids under herdr and session names under tmux. Without this, `iw open` inside the target worktree stops printing "Already in session" and re-focuses itself. - Tests: `OpenHarnessTest`, `StartHarnessTest`, `HerdrArgvTest`, `HerdrLookupTest`. Docs: `docs/api/WorktreeTypes.md`, then rebuild the published skills. BATS is unaffected — every suite forces `IW_WORKSPACE_BACKEND=tmux`. ## Decisions to take during the work 1. **Delete the label route in `HerdrLookup`?** After the `candidatePath` change, `resolve`'s label fallback and `has`'s label test are unreachable, and `byLabel` has no caller. Leaving them is dead code that invites a wrong answer later; removing them is a rewrite of a reviewed unit. Needs Michal's call before the code is touched. 2. **`currentName` on the port.** After the `Open.scala` change it has no production caller. Keep or drop. 3. **Effort slugs lose the project.** `dashboard-control-plane` alone does not say which repository. herdr nests a worktree under its parent workspace, so this may already be answered in the sidebar. Confirm by eye before closing. ## Notes - Existing workspaces keep their long labels until they are closed and reopened. They still resolve, by path. - Nothing in the dashboard reads a herdr label: `HerdrAgentReader` and `AgentSessionService` key an agent on its working directory. - `herdr worktree open --label` is optional, so the flag could be dropped instead and herdr would derive a name from the branch. Rejected: it puts the naming in herdr's hands and leaves us no way to change it.
mph closed this issue 2026-09-01 20:00:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
iterative-works/iw-cli#476
No description provided.