Skip to content

feat(factory): tmux terminals + pane/viewing-in on Factory Floor (extension half of #731)#734

Merged
muqsitnawaz merged 3 commits into
mainfrom
feat/factory-tmux
Jul 7, 2026
Merged

feat(factory): tmux terminals + pane/viewing-in on Factory Floor (extension half of #731)#734
muqsitnawaz merged 3 commits into
mainfrom
feat/factory-tmux

Conversation

@muqsitnawaz

Copy link
Copy Markdown
Contributor

What

Extension half of the tmux terminal standardization (companion to CLI #731). Makes the Factory Floor feed workable by giving every agent terminal a stable, external address (a tmux pane) and surfacing where each agent is being viewed.

Same-cwd agents were previously indistinguishable and VS Code terminal tabs had no external addressing — the root cause behind the feed bugs (#145 host mis-attribution, #147 drifting startedAt, #149 status-bar-wrong-session). tmux panes give a uniform, resumable, trackable identity.

Changes

  • tmux by default (agents.terminalMode: auto | tmux | native, replaces the old boolean agents.enableTmux). auto runs agents inside tmux on macOS/Linux for uniform addressing/resume/tracking, falling back to a native VS Code terminal on Windows or when tmux is missing. — terminalMode.ts (pure, unit-tested), extension.ts
  • Publish tmux pane + editor-tab index per agent terminal so the CLI can render "viewing in Codium tab N". — tabIndex.ts (pure resolveTabIndex, unit-tested), tmux.ts, foreman.registry.ts
  • Surface pane + "viewing in" on Factory Floor cards.remoteSessions.ts, FeedItem.tsx, floorAdapter.ts, floorModel.ts

Testing

  • tsc -p ./ clean (0 errors)
  • New pure-helper tests green: tabIndex.test.ts + remoteSessions.test.ts — 55/55; terminalMode.test.ts present and green
  • Full factory suite: 1042 pass, 4 fail — the 4 failures are pre-existing live-LLM/network/timing integration tests (generateCommitMessageWithClaude, draftDispatchPrompt, resolveAlias, SessionWatcher 5s timeout), none touching any file in this diff

Notes

muqsitnawaz and others added 3 commits July 7, 2026 04:31
…ive)

Replace the default-off boolean agents.enableTmux with an enum
agents.terminalMode: 'auto' (default) runs agents inside tmux when
isTmuxAvailable() (macOS/Linux), else native; 'tmux' forces tmux and
warns+falls-back when unavailable; 'native' never uses tmux. The
decision is a pure resolver (src/core/terminalMode.ts) so extension.ts
never re-derives it inline. openSingleAgent, the H/V split handlers, the
enable/disableTmux commands, and updateContextKeys all read the setting.
createTmuxTerminal and the native path are reused unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
snapshotOwnTerminals now enriches each published LiveTerminal with
tmuxSession, tmuxPane (from a new async getTmuxInfo getter in tmux.ts
that lazily reads the %pane off the shared socket), and tabIndex (the
1-based editor-tab position resolved by a pure src/core/tabIndex helper
matching the terminal name against its TabInputTerminal tab label). These
three fields are the data contract the CLI reads to render 'Codium tab N'
and to address panes uniquely. tmuxPane + tabIndex join the republish
hash so a tab move re-publishes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

# Conflicts:
#	apps/factory/src/core/tabIndex.test.ts
#	apps/factory/src/core/tabIndex.ts
@muqsitnawaz muqsitnawaz merged commit 6f72a0f into main Jul 7, 2026
4 checks passed
@muqsitnawaz muqsitnawaz deleted the feat/factory-tmux branch July 7, 2026 11:51
@prix-cloud

prix-cloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

Code Reviewer

Verdict: Ready to merge (with one minor fix recommended)

Build: tsc -p ./ — 0 errors
Tests: 1007 pass, 12 fail across 83 files — all failures are pre-existing and environment-specific (no API keys, agents CLI not installed, gray-matter/marked packages absent in sandbox). None of the 12 failures touch any file in this diff.

Confirmed PR-specific test targets:

  • tabIndex.test.ts + terminalMode.test.ts + remoteSessions.test.ts60/60 pass
  • floorAdapter.test.ts + floorModel.test.ts102/102 pass

What works well

The terminalMode.ts / tabIndex.ts split into pure helpers with full unit coverage is the right call — both modules are easy to test in isolation and the logic is correct. The readPaneId fallback chain in tmux.ts handles the "tmux is not on the extension host's PATH" case cleanly, and the early-return if (pane) return pane; return undefined prevents falling through to incorrect candidates when tmux runs but reports no pane. Hash stability in foreman.registry.ts correctly includes tmuxPane/tabIndex so the registry reflects pane changes. The floorAdapter || undefined coercions keep empty strings out of the UI.


Issues that need attention

1 — floorAdapter.test.ts:309 — test fixture missing required tmuxPane field

RemoteSessionLike.tmuxPane was added as a required (string, not string?) field in floorAdapter.ts:92, but the existing base fixture in the test file wasn't updated:

// floorAdapter.test.ts:309
const base: RemoteSessionLike = {
  host: 'this-mac', sessionId: 's1', ..., replyMuxSocket: '',
  // ← tmuxPane: string is missing; this is a type error
}

This slips past tsc -p ./ because tsconfig.json:14-15 covers only src/**/* and explicitly excludes test files — the entire ui/ tree is outside its scope. Bun runs the test fine at runtime (JavaScript doesn't enforce types) and nothing asserts on pane in the base-derived cases, so tests pass. But if UI type-checking is ever enabled, or if a future test spreads base and asserts r.pane is absent, this will surprise someone.

Fix (two options):

  • Add tmuxPane: '' to the base fixture (consistent with how replyRail: '', replyMuxTarget: '', etc. are initialized)
  • OR make tmuxPane?: string optional in RemoteSessionLike (consistent with viewingIn?: string) — empty string is already handled by r.tmuxPane || undefined in toFloorAgentFromRemote

Option 2 is slightly cleaner since viewingIn already uses ? and tmuxPane has the same semantics (absent for non-tmux sessions).


2 — extension.ts:3877tmuxEnabled context key is true in 'auto' mode even when tmux is unavailable

// extension.ts:3877
const tmuxEnabled = normalizeTerminalMode(config.get('terminalMode')) !== 'native';

With the new default of 'auto', this evaluates to true on every platform — including Windows and systems without tmux. The package.json:691-696 gates that use this context key control visibility of the "Enable/Disable Tmux" menu commands. The comment above reads 'auto'/'tmux' show it (tmux is active) but 'auto' does not mean tmux is active — it means "use tmux if available."

On a Windows install or a machine without tmux, users see the "Disable Tmux" command even though tmux was never used. Clicking it just sets the mode to 'native', so nothing breaks — but it implies tmux is running when it isn't.

This is a minor UX issue, not a correctness bug. A tighter version would call isTmuxAvailable() before setting the context key, but that adds async overhead to every activation. Alternatively, the comment could be updated to say show for 'auto'/'tmux' (tmux may be active) to reflect the true semantics. Raising it so you can decide whether it's worth a follow-up.


Things to verify manually

The pane-ID read (readPaneId in tmux.ts:507-524) is best-effort and fire-and-forget behind getTmuxInfo. The path that exercises the new tmuxSession/tmuxPane/tabIndex fields end-to-end (agent launch → registry snapshot → CLI reads the registry → "viewing in Codium tab N" surfaces in the status bar) requires a live VS Code + tmux session — worth a smoke test with the compiled .vsix before shipping the paired CLI #731.


Reviewed by Code Reviewer — ran tsc -p ./ and bun test on branch feat/factory-tmux.

Instruction files read: /workspace/task-5e8da1e4/CLAUDE.md (repo root), /workspace/task-5e8da1e4/apps/factory/AGENTS.md, /workspace/task-5e8da1e4/apps/factory/ui/CLAUDE.md

muqsitnawaz added a commit that referenced this pull request Jul 7, 2026
…parity sync)

Rebuilt on current main (post apps/factory tmux merge #734), reconciling the feed
model so both the tmux pane/viewing-in fields and the parity-sync fields coexist.

- Fan-out remote-session enrichment: correct per-device attribution (machine),
  worktree slug + live preview + structured ticket + real branch surfaced on cards.
- resolveStartedAtMs caches start time by PID (no more Date.now() drift per republish).
- Single @shared feed model with a MISSING_EXPORT build-time drift guard.
- Merge reconciliation: FloorAgent/RemoteSession carry both viewingIn/tmuxPane (#734)
  and preview/pr/worktree/machine (this PR); normalizeRecentSession defaults the
  live-only tmux fields to '' for idle/historical rows.
- Add apps/factory/CHANGELOG.md so scripts/release.sh preflight passes (was missing;
  every release attempt would exit 1 on the CHANGELOG grep).

Supersedes #732 (which was based on the pre-#734 layout and conflicted).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant