IW-423: Configured terminal workspace manager for the dashboard #444
No reviewers
Labels
No labels
agent-ready
bug
contract
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
needs-input
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/iw-cli!444
Loading…
Reference in a new issue
No description provided.
Delete branch "IW-423"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes IW-423.
Summary
The dashboard is a long-lived daemon with no invoking terminal, so it could not use
iw's existing rule for picking a terminal workspace manager — and worse, it inheritsHERDR_ENVat spawn time from whatever terminal started it, a value that can be daysold and describe a pane that no longer exists. Its create-worktree button therefore
always drove tmux, whatever the user actually runs.
This branch gives the pure selection rule a second input — a server-scoped configured
default — and gives the dashboard its own binder that supplies that input safely, while
the CLI keeps resolving purely from the environment.
Changes
Selection rule and config (
core/)ServerConfiggains an optionalworkspaceBackendfield, read only by the dashboard.TerminalWorkspaceBackend.selectbecomes a two-input pure function(env, configuredName)with the decided precedence: recognised
IW_WORKSPACE_BACKEND>HERDR_ENV=1>configured default > tmux. A typo in either source is an error, not a silent fallback,
and a source is parsed only when the precedence reaches it.
WorkspaceBackendis extracted to its own file with a pureresolve; itslazy val selectedpassesNone, so the CLI ignores the setting entirely.Constants.ServerConfigKeys.WorkspaceBackendnames the config key, so the errormessage and the field cannot drift apart silently.
Dashboard binder and de-tmuxing sweep (
dashboard/)ServerWorkspaceresolves the backend for the daemon, filtering its environment downto
IW_WORKSPACE_BACKENDonly —HERDR_ENVnever reaches the rule from a server process.TmuxAdapter.createSessiononto the sameselect/opsForcomposition the CLI uses.WorktreeCreationError.TmuxErrorbecomesTerminalWorkspaceError;WorktreeCreationResult'stmuxSessionName/tmuxAttachCommandbecomeworkspaceName/attachCommand; attach and failure hints render fromops.attachHintand
ops.unavailableHintinstead of hand-written tmux strings.file are unaffected.
Docs
docs/server-config.mddocuments the new key and states plainly that it steers thedashboard only, not
iw start/open/rm.docs/api/ServerTypes.md,docs/api/Constants.mdandllms.txtrefreshed for thechanged modules.
Accepted limitation
A worktree created from the dashboard as a herdr workspace, then opened later by
iw openfrom a plain terminal, gets a second workspace in tmux. This follows from decision 3 (the
setting is server-scoped) and is documented; the workaround is
IW_WORKSPACE_BACKEND=herdrin the shell rc.
Testing
./mill core.test— 186 pass./mill dashboard.test— 241 pass (whole unit tier)./iw ./test e2e— 188 BATS tests pass, 0 failurestmuxgrep acrossdashboard/jvm/— the sweepis verified, not asserted.
Both phase PRs (#434, #435) passed code review with 0 critical findings. One warning found
in Phase 2 review was fixed: an inline
onclickhandler inCreationSuccessViewthat thisphase's change made reachable with attacker-influenced text, replaced by a
data-copy-textattribute.
Lessons triaged
20 entries triaged before this PR; 0 pending. Filed as iw-cli issues #436-#443 and kanon
notes #183, #184. Two promotions landed in this branch (
WorkspaceBackendTestguardcomment; the config-key constant in Phase 2), one was rejected, one retained, one deleted.
Issue #441 is a security finding raised by the Phase 2 review — the dashboard creation
handler anchors filesystem paths on a client-supplied
projectPathwith no allow-listcheck. The behaviour predates this branch, which only changed the callee of one of the
three operations sharing that anchor.
Release notes
project-management/issues/IW-423/release-notes.md(Czech). Note:VERSIONis not bumpedby this branch — the change is additive, so the version line was left out of the notes
until the release number is decided.
Review
Start with
project-management/issues/IW-423/review-packet.md— it names the entry points,the precedence and component diagrams, and the renamed-identifier table for the sweep.
Issue: IW-423 Phase: 1 - Selection rule + config field This commit marks the checkpoint for phase 1 context generation. Use wf-implement IW-423 to return to this point. 🤖 Generated with Claude CodeIssue: IW-423 - --checkpoint takes phase:sha; the command text's phase:context_sha=sha double-writes the key - review-state update preserves git_sha rather than refreshing it 🤖 Generated with Claude CodeIssue: IW-423 Phase: 1 - Selection rule + config field A source is parsed only when the precedence reaches it. Decided by Michal on 2026-08-23; the deciding cell is pinned by the test table. 🤖 Generated with Claude CodeSelection rule + config field Artifacts: - phase-01-tasks.md 🤖 Generated with Claude CodeIssue: IW-423 Phase: 2 - Dashboard binder + de-tmuxing sweep This commit marks the checkpoint for phase 2 context generation. Use wf-implement IW-423 to return to this point. 🤖 Generated with Claude CodeDecided with Michal: the dashboard binder calls WorkspaceBackend.resolve and nothing else from the shared rule, so resolve gains the production caller phase-01-context.md promised it. The binder needed the backend by name for one suggestion string ("check that tmux is running"). Rather than carry a name across three layers - or change resolve's signature one PR after it merged - the sentence moves onto the port: TerminalWorkspaceOps gains unavailableHint, a fourth hint beside attachHint, focusHint and leaveHint, where each implementation writes its own text. That also produces better copy: herdr is a server, not a multiplexer. Context changes: - ServerWorkspace holds trustedEnvironment, workspaceFor, configuredBackend, forRequest. backendFor, opsFor and backendName are gone; forRequest returns TerminalWorkspaceOps rather than an Either. - toUserFriendly takes (error, issueId, attachHint, unavailableHint). - "The two core changes" documents unavailableHint and its four implementations alongside the existing Constants key change. - Testing strategy, files list, acceptance criteria and estimate note follow. Rejected alternatives are recorded in the settled-decision section: accepting a resolve with no caller, backend-neutral wording, and returning (backend, ops). 🤖 Generated with Claude CodeDashboard binder + de-tmuxing sweep 127 tagged tasks across nine groups, ordered for TDD: setup, the two core changes, the pure renames, the binder, the wiring, the docs, the generated-docs refresh, then the verification gate. One ordering correction the context's "Approach" does not make, recorded in a "Compile constraints" block rather than applied silently: the three signature additions (WorktreeCreationService's attachHint, toUserFriendly's two parameters) cannot compile before the binder exists, because CaskServer has no honest source for the values it must pass. Group 3 therefore holds the pure renames only, which keeps the sweep separable from the binder as the context intends. Also settled in this commit: - The test package for ServerWorkspaceTest.scala is iw.dashboard.test. The directory has 7 packages across 65 files, so there was no convention to follow, only one to pick. - Fixed a defect in the context's "Testing Strategy" friendly-helper snippet: its last two arguments were swapped against the declared signature and would not have compiled, and its unavailableHint fixture was a bare name where the call site renders a clause. Artifacts: - phase-02-tasks.md 🤖 Generated with Claude Code