Commit Graph
2793 Commits
Author SHA1 Message Date
Codeman maintainer 497711a05e docs: Pane B is a TerminalTile (reconnect, exactly-once input, focus)
CLAUDE.md, architecture-invariants#split-pane-sessions and the split-pane
spec described Pane B as having no reconnect, seq-less input and xterm's
own Ctrl+V. Updated for TerminalTile (terminal-tile.js, load order 7.4):
reconnect and stop codes, the input-socket map and which input is kept
out of the persisted queue, image paste, the geometry rules, and
_focusedPane() with its Ctrl+W exception. The tile-grid spec now records
PR 1 as built (no key handler factory; scheduleLoad and scrollback move
to PR 2 with their first user).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:46:33 +02:00
Codeman maintainer fe9b209f67 feat(split): terminal shortcuts, voice and paste follow the focused pane
With the split open, every app-level terminal action resolved against
Pane A: Ctrl+L typed into Pane B cleared Pane A's display while xterm
sent the ^L into Pane B's PTY, and Ctrl+Shift+R restored Pane A's size.

_focusedPane() now answers with the terminal focused LAST (a mic or
header click moves DOM focus but not the user's pane): Pane B claims it
from its own textarea's focus, the primary terminal's focus gives it
back, and a destroyed pane never holds it. Ctrl+L, Ctrl+Shift+R, voice
dictation and image paste act on the focused pane. Ctrl+W deliberately
still closes the active session: it kills with no confirm, so moving it
is an owner decision (docs/tile-grid-plan.md, decision 5).

Also destroys every tile after each terminal-tile-input test: a real
reconnect timer from one test opened a socket in a later one and flaked
under full-suite load.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:45:16 +02:00
Codeman maintainer fbd69e62aa feat(split): clickable file paths and image paste in Pane B
- Pane B registers the primary pane's file-path/URL link provider on
  its own terminal, so a path an agent prints there opens the file
  preview or log viewer for Pane B's session (it was plain text).
- Ctrl+V/Cmd+V in Pane B goes through the primary pane's paste trap,
  aimed at Pane B: a pasted image uploads to Pane B's session and its
  path is typed there; text keeps its bracketed-paste markers. xterm's
  default handled text only.

Tests pin both the tile wiring and the targeted link provider itself
(registered on the target terminal, opening against the target's
session at click time, the primary's tap-path provider untouched).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:20:25 +02:00
Codeman maintainer a54ad81684 fix(split): Pane B and its PTY never disagree about size (#464)
- Font size, family and weight changes refit Pane B AND tell its PTY.
  They used to reflow the xterm only, leaving the CLI wrapping at the old
  column count, the garbled-redraw class #464 fixed for the primary pane.
- The resize frame reports the size the xterm actually holds, with no
  40x10 floor (the divider's 20% clamp leaves about 28 columns), skips
  an unchanged size, and is always re-sent on a fresh socket so it
  re-registers as a desktop viewer.
- The server's {t:'zc'} geometry report is handled: a different column
  count is adopted, rows stay local, using the primary pane's own
  reconcilePtyGeometry verdict.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:07:40 +02:00
Codeman maintainer 0ec17633ba feat(split): Pane B reconnects after a drop instead of staying dead
A Codeman restart (every deploy) or a network blip used to leave Pane B
dead, with a marker asking the user to close and reopen the split.
TerminalTile now reconnects:

- A transient close reconnects on the primary pane's backoff ladder
  (CodemanWsReconnect) plus jitter; the attempt count resets only on a
  successful open. The redelivery sweep's forced close (1005) counts as
  transient.
- On reopen the closed state is cleared before the buffer refresh that
  closes the output gap, so no stale marker lands under a healthy pane.
- 4003/4004/4009/4010 stop the pane for good and report once through a
  new onExit(code) callback; the marker says why.
- Sockets are replaced race-free: the old one is detached before a new
  one opens, and every handler ignores events from a socket that is no
  longer current. destroy() cancels a pending reconnect.
- reconnectNow() lets an owner skip the backoff.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:54:57 +02:00
Codeman maintainer 5e3dbf2057 feat(split): Pane B input goes through the exactly-once queue
TerminalTile used to send every xterm onData chunk as a bare {t:'i'}
frame: no seq, no ACK, silently dropped while its socket was down, and
typing never acknowledged the session's idle alert. Keystrokes and
pastes now go through app._sendInputAsync over the tile's own socket,
registered in the input-socket map while open (HTTP fallback while
not), so they are ACKed, persisted until delivered, redelivered after a
drop, and the ACK clears the idle alert.

What xterm generates on its own stays out of that persisted queue: a
query reply (DA/CPR/OSC) is dropped, as the primary pane drops it, and a
focus or mouse report goes out once via _sendInputEphemeral. The tile's
socket carries the tab identity with a :tile suffix so it can never
evict the primary pane's socket.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:41:10 +02:00
Codeman maintainer d1bbb4cc26 refactor(split): move the pane class into terminal-tile.js as TerminalTile
Pure move and rename, no behavior change. The split pane's second
terminal (SplitTerminalPane) moves out of terminal-split.js into its own
terminal-tile.js (load order 7.4) as TerminalTile, so the tile grid can
reuse it. terminal-split.js keeps the split orchestration (picker,
divider, auto-collapse) and constructs a TerminalTile for Pane B.

Tests follow the class: split-pane-terminal-unit becomes
terminal-tile-unit, and the Shift+Enter guard and the two browser suites
read terminal-tile.js / window.TerminalTile. The browser suites match
master (one pre-existing environmental failure in both).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:28:08 +02:00
Codeman maintainer e2f56dc077 fix(input): image paste and dictation land in the session they started in
Both read activeSessionId at the END of an async gap, so switching tabs
in between sent the input to the wrong session:

- An image upload inserted its paths with sendInput(), which re-reads
  activeSessionId after the uploads finish. It now inserts into the
  session the batch was uploaded to, through the same durable queue.
- Voice dictation read the target when the transcript arrived and again
  when the send button or the compose overlay's Send was pressed. The
  target is now captured in start() (via _focusedPane()), the local-echo
  overlay is only used when that target is the active session, and a
  target that closed meanwhile gets a toast instead of a 404.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:09:51 +02:00
Codeman maintainer ead3d34411 refactor(terminal): seams for a second terminal pane (input socket map, targeted links, copy, paste)
No behavior change. Prepares the split pane's second terminal (and later
grid tiles) to share what today only the primary terminal has:

- _inputSocketFor/_registerInputSocket/_unregisterInputSocket: the
  exactly-once input queue, its ACK handling and the redelivery sweep now
  deliver over any registered socket bound to a session, not only
  this._ws. ACKs are routed by the receiving socket's session; silence is
  judged per socket; a stale handle cannot unregister its replacement.
- registerFilePathLinkProvider, cleanedTerminalSelection,
  copyTerminalSelection and _handleImagePaste take an optional target
  terminal and session (defaults: the primary pane).
- _focusedPane() is the one place to ask which pane the keyboard is in
  (primary only, for now); _forEachTile() replaces the _splitPane special
  cases in the font, family, weight, skin and resize paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 08:55:05 +02:00
Codeman maintainer 310f20b288 feat(header): rings for CPU and MEM in the Compact style (#426)
Compact now draws every reading the same way, ring then label then
value: CPU and MEM get an accent ring (red past 80%, like their value)
in place of the sparklines, the plan windows keep their green, yellow
or red rings, the dot separators go, and both pills share one label and
one value style. The sparkline markup, history and CSS are removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 08:44:20 +02:00
Codeman maintainer f1537a7887 docs: tile grid design spec (two-PR plan: TerminalTile foundation, then the grid)
Plans a grid of up to nine live sessions side by side. All tiles are
equal TerminalTiles, the main terminal is parked while the grid is
open, and activeSessionId follows the focused tile. The split pane stays
and shares the tile class. PR 1 builds the seams and TerminalTile, so
the split's second pane gains reconnect, exactly-once input, links,
image paste and focus-following shortcuts. PR 2 adds the grid.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 08:35:14 +02:00
Codeman maintainer 55cafc282f feat(tabs): no label on the idle row (#426)
Idle is the default state of a tab, so naming it only adds noise. The
idle group is now quiet: its heading element stays (it anchors the row's
order band and, as the first row, holds its place beside the brand) but
draws no label or count. Needs you, Waiting and Working are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 06:29:40 +02:00
Codeman maintainer bb5fd5ee97 fix(tabs): state rows start under the brand, labels left-aligned (#426)
In the desktop header strip grouped by state, the brand leaves the flow
and sits over the strip's top-left corner, so every row after the first
starts at the left edge under "Codeman" instead of leaving that corner
empty. Labels are left-aligned in the measured column; the first row's
heading takes its natural width beside the brand (--tab-triage-brand,
kept by a ResizeObserver so no render pass reads layout for it).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 06:25:26 +02:00
Codeman maintainer bebf0db792 fix(header,tabs): tile-shaped header buttons beside the clustered stats, a bolder active ledger cell, one ledger row height (#426)
Header icon buttons next to the Tiles take the tile box (36px, border,
fill, 18px glyph) and turn into round chips beside the Compact pills;
the gear's quarter turn moves onto the glyph. The ledger's active cell
gets an inset second pixel and an accent bar (the skin's 1px !important
border was too quiet in a grid of look-alike cells), and every ledger
cell stretches to a 30px minimum so rows stay one height.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 04:08:02 +02:00
Codeman maintainer e3dfbf6591 feat(tabs): Tab Layout setting with by case (A) and ledger (B), reversible state order, cleaner tiles (#426)
Tab Grouping becomes Tab Layout (tabArrangement: 'state' | 'case' |
'ledger' | 'classic', default 'state'), the first row of App Settings,
Appearance, Tabs, so the old and the new strip are one choice apart.

- By case (option A): each case's tabs sit in one .tab-cluster box in
  first-appearance order, labelled with the case and its count, coloured
  by a stable hash into the session palette. Membership is
  _mobileOverviewCaseFor(), the home screens' own match. Inside a box with
  company a generated w75-api-gateway reads w75; the -<case> stays in the
  DOM in a .tab-name-case span only .tabs-clusters hides. The incremental
  render path rebuilds only when the cluster structure key changes. The
  rail and the sidebar get a labelled section per case; phones dissolve
  the boxes into the chip row. Drag stays inside one box.
- Ledger (option B): CSS only on .tabs-ledger, desktop header strip: an
  auto-fill column grid of equal cells in mono type with a 3px status
  bar. Its markup is identical to classic's.
- State Order (tabStateOrder: 'urgent-first' | 'urgent-last'): flips the
  by-state groups so needs you can be the bottom row.
- By state: the label column is measured to the widest label on screen
  and the labels are right-aligned in it, instead of a fixed 92px gutter
  that left short labels far from their tabs.
- Tiles: a three-row grid (label, value, bar) with pixel line-heights in
  the bundled JetBrains Mono, 36px like the header. The bar used to lie
  over a fixed 28px tile, and a taller system mono (SF Mono) pushed the
  value into it. The WS tile's grid moved onto an inner .connection-tile
  span because JS writes the indicator's display inline. Compact uses the
  same font and a matched WS size.

Tests: test/tab-clusters.test.ts (new), plus the rename and the reversed
order in test/tab-triage.test.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 04:08:02 +02:00
Codeman maintainer 9b0d305223 feat(tabs,header): group tabs by state and add header stats styles (#426)
Two directions from Discussion #426, each a per-device setting and the
new default.

Tab Grouping (tabGrouping: 'state' | 'none', default 'state', option C):
tabs are grouped needs you (red, plus failed sessions), waiting (yellow),
working and idle (also ended, exited panes and web tabs), most urgent on
top. The desktop header strip draws a row per group with its label and
count in a left gutter; the flat vertical rail and the sidebar draw a
section per group; tablets keep their scrolling row with inline dividers;
phones keep the chip row in group order without headings. Classification
is the home screens' own (_mobileOverviewState/_mobileOverviewExit), the
fold into four groups is pure in CodemanTabTriage (constants.js). It is
flex `order` plus aria-hidden heading/break elements reconciled in place
after both render paths, never a DOM reorder, so Alt+N, the keyboard walk
and drag keep reading tab order; a drop is refused across groups. Named
groups in the vertical rail take precedence.

Header Stats Style (headerStatsStyle: 'classic' | 'compact' | 'tiles',
default 'tiles', option G): tiles give WS, CPU, MEM and each plan window
a label-over-value tile with a bar underneath; compact is one WS/CPU/MEM
pill with sparklines plus a plan-ring pill; classic is the header as
before. Desktop only (classic below 768px and in solo windows). The
clustered styles move #connectionIndicator into #headerSystemStats and
WS stays out of a hidden System Stats pill. The extra parts are always
rendered and hidden by default in CSS, so classic is unchanged.

Tests: test/tab-triage.test.ts, test/header-stats-style.test.ts; three
source pins in test/tab-rail-order.test.ts follow renamed lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 04:08:01 +02:00
DevvynandClaude Sonnet 5.5 4c2fdd5f5a test: make opencode-resize and split-pane browser tests environment-proof
- opencode-resize: record WebSocket resize frames as well as POST /resize,
  seed the needsRefresh test with real PTY output, skip the OpenCode close
  modal test when opencode is not installed
- split-pane: send the marker with useMux:false (plain prompts otherwise go
  through tmux send-keys, which test mode does not have) and retry past
  Codeman's own post-create clear

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-06 09:20:55 +08:00
DevvynandClaude Sonnet 5.5 ab7e89873f ci: nightly browser suite; fix stale session-id extraction in browser tests
WIP: the suite still has failures on a clean master that are not fixed here.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-06 09:20:55 +08:00
Codeman maintainer ac94f339ac chore: version packages (1.35.0)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
codeman@1.35.0
2026-10-06 03:01:26 +02:00
Codeman maintainer 88f5a43a9f fix(cases): bounded path probe landing fixes (#516)
- hooks-config: a probe the bulk cap refused gets ONE bounded re-probe past the
  cap (probeBeforeTouching), and whatever is still unknown is skipped. The
  per-spawn hook and statusLine helpers used to fall back to an unbounded
  lstat/readFile there, which on a dead workspace never settled and could take
  the last threadpool workers (and hang the boot hook sweep). New test: cap
  engaged, stat/lstat/readFile hanging on two more paths; both helpers return.
- describeUnknownPath()/unknownPathReason(): POST /api/sessions, quick-start and
  GET /api/cases/:name now say a folder was not checked (other mounts are still
  not answering) instead of blaming a healthy folder at the stall ceiling.
  errorCodes unchanged.
- #535 x #516: Create in a custom folder probes the parent through the bounded
  probe before realpath/stat/lstat/readdir touch it; an unknown parent is 422
  OPERATION_FAILED (UNREACHABLE) within the probe timeout. New test.
- Docs: MAX_STALLED default is 2 (follows UV_THREADPOOL_SIZE), CaseInfo
  .unreachable covers a refused probe, the boot sweep skips an unanswering
  workspace, a CLAUDE.md gotcha for bounded probes, verbs.md documents the 422
  (plugin mirror synced), api-reference documents the custom-folder 422.
- Tests: the launcher case-lookup describe is no longer nested in the Grok
  block, and the cap-below-ceiling test no longer depends on an inherited
  UV_THREADPOOL_SIZE / CODEMAN_PATH_PROBE_MAX_STALLED.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 20:00:30 +02:00
Codeman maintainer aca23aa404 chore: changeset wording, the in-flight rename repaint is #526's own 2026-10-05 19:53:10 +02:00
Codeman maintainer f16f294576 chore: add #516 to the landing changeset 2026-10-05 19:53:03 +02:00
Codeman maintainer 737a2527d6 fix(tabs): report an edit dropped behind an in-flight save, respect the group cap (#525 landing)
- createEditCoordinator's finally block rebases the edits queued during a write; one that the write's 409 made inapplicable was dropped with no toast. It is now reported once, like the main loop and adoptExternal do (found by the PR bot's re-review; regression test fails without it).
- At the 32-group server cap the row and group menus no longer offer a new group, which could only fail with an untranslated 'group limit reached'. MAX_GROUPS is exported from tab-layout-browser.js.
- CLAUDE.md names the pagehide keepalive as the one deliberate exception to 'never PUT the layout outside the coordinator'.
- The Dashboard wiki page describes tab groups in the vertical rail row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer 6f88e40b77 chore: changeset for the #525, #526, #534, #535, #536, #537 landing
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer 2c38e77f8a fix(git-status): landing fixes (#537)
- A cached list of repositories below a folder is re-checked against the
  Docker case workspaces as they are now, so a repository linked as a Docker
  workspace within the 30 s list cache is no longer inspected.
- A diff past runGit's 8 MB output bound is cut short from git's partial
  output instead of failing with a 500.
- The browser test waits for its slow route handler on unroute
  (unrouteAll behavior 'wait'), so a late route.continue() cannot fail the run.
- "Upstream is gone" now reads "Upstream not on remote", true for a branch
  that was never pushed as well as one deleted on the remote; docs mirrored.
- The diff route checks the repository against the workspace's own cached
  repository list (findWorkspaceRepo) and refreshes only that repository,
  instead of a fresh status of every repository in the folder.
- CLAUDE.md: a Key Patterns entry for the git read surface and its rules.
- The enclosing repository is identified with one cached rev-parse before
  any full status, so an unrelated repository above the workspace costs one
  process and its failure no longer hides the repositories below.
- Wiki: the bottom-bar indicator moves out of the header-controls table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer cf26853390 fix(doctor): Diagnostics landing fixes (#536)
- The doctor now judges candidates like the run mode's resolver: the PATH
  hit, then each search dir, each one version-checked on its own and
  skipped on a mismatch (a wrong `pi`/`grok` on the PATH no longer hides
  the real one in a search dir). A search-dir candidate must be an
  absolute path to an executable regular file, so a relative dir or a
  file without the x bit reads as missing, as it does in the Run menu.
  `isExecutableRegularFile` is exported from cli-executable-resolver.ts
  and reused rather than copied.
- Every doctor probe passes killSignal: 'SIGKILL'; a --version that
  ignores SIGTERM held the probe for its full runtime (15 s vs 5 s
  measured with a TERM-trapping script).
- README no longer claims parity with the Run menu or nvm prefixes.
- The Diagnostics panel marks a missing optional tool with ○, a missing
  required one with ✗, as the terminal doctor does.
- expandSearchDir names its twin, expandHome() in cli-resolver.ts.
- test/doctor-cli-json.test.ts is hermetic: temp HOME, a PATH of only
  `which` and `node`, and a clis.json that drops the registry's absolute
  search dirs, so it never runs the machine's installed agent CLIs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer 192a5994e0 fix(cases): custom-folder create landing fixes (#535)
- Route test hygiene: each test works in its own mkdtemp folder, every
  deletion goes through safeRmHomeTree, and the suite refuses to start
  outside test/setup.ts's temp HOME, so a raw `npx vitest` can no longer
  delete a real ~/projects or the live linked-cases registry.
- Path policy: the symlink-resolved target is also judged against the
  resolved home, data dir and system roots (home reached through a link,
  macOS /etc -> /private/etc); test expectations are realpath-safe.
- Refuse a target equal to or inside the caller's or the shared cases
  directory, pointing at plain Create New (it would list twice, and
  deleting the local copy removes files).
- The registry re-read comment no longer claims to prevent the
  lost-update race; documented as narrowing it, like /api/cases/link.
- UI: the success toast names the folder the server created, the
  "under ~/codeman-cases" blurb and name hint change while a custom
  folder is ticked, a "/" parent previews and sends /<name> instead of
  an empty path, and the new labels have zh-CN entries.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer 566365e127 test(tabs): static CI guard that no rail or sidebar clamp out-ranks the rename unclamp (#534 landing)
The behavioural check for the detailed-rail rename clamp (#534, #526) lives in test/inline-rename.test.ts, a browser suite the CI gate does not run. This pins the cascade from styles.css itself, from computed selector specificity and source order, so a later clamp rule cannot silently out-rank the shared unclamp again. Mutation-checked: deleting the detailed-rail twin fails exactly that case.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 19:51:59 +02:00
Codeman maintainer ff94637718 Merge pull request #516 from aakhter/pr/bounded-path-probe
fix(cases): bound path probes for linked workspaces and session creation, so an unreachable mount cannot freeze the server

# Conflicts:
#	src/web/routes/case-routes.ts
2026-10-05 19:51:43 +02:00
Codeman maintainer 2063d15c20 Merge pull request #537 from opticon454/feat/git-status-indicator
feat(ui): git status indicator in the bottom bar, with a panel of uncommitted and unpushed work

# Conflicts:
#	config/test-suites.ts
#	docs/api-reference.md
2026-10-05 19:51:43 +02:00
Codeman maintainer fed3a0897a Merge pull request #536 from opticon454/feat/doctor-in-settings
feat(settings): codeman doctor in Settings → System → Diagnostics

# Conflicts:
#	config/test-suites.ts
2026-10-05 19:51:42 +02:00
Codeman maintainer d9c760609f Merge pull request #535 from opticon454/feat/case-custom-path
feat(cases): create a new case in a custom folder
2026-10-05 19:51:41 +02:00
Codeman maintainer 74e8015783 Merge pull request #534 from opticon454/fix/rail-rename-unclamp
fix(rail): keep the inline rename editor unclamped in the detailed tab rail

# Conflicts:
#	src/web/public/styles.css
#	test/inline-rename.test.ts
2026-10-05 19:51:41 +02:00
Codeman maintainer fea5626efc Merge pull request #526 from aakhter/pr/grouped-rail-rename-fixes
fix(tabs): grouped rail interaction fixes for inline rename
2026-10-05 19:51:40 +02:00
Codeman maintainer bd109d3b16 Merge pull request #525 from aakhter/pr/grouped-rail-edit
feat(tabs): edit groups in the vertical rail
2026-10-05 19:51:40 +02:00
Aamer Akhter 9fa44109b8 fix(cases): keep deleted workspaces deleted, cap pastCap, scope stalls to network mounts
- applyWorkspaceHooks: an "unknown" probe that is not near a stalled path
  (refused by the stall cap, or an unexpected stat error) no longer reads as
  "go ahead". It checks existence with pathExistsForWrite first, so a deleted
  workspace is not recreated by the mkdir -p in ensureCodemanHooks.
- pastCap gets a hard ceiling, PATH_PROBE_STALL_CEILING = UV_THREADPOOL_SIZE
  (default 4) minus one, so explicit requests against several dead paths can
  never take the last libuv worker. The bulk cap now defaults to one below the
  ceiling (2 with the default pool), leaving a slot for an explicit request.
- A stall widens to its mount only for network and FUSE filesystem types read
  from /proc/self/mounts; on a local mount (a path typed under a local /home
  that reaches a NAS through a symlink) it narrows to the stalled path.
- GET /api/cases/:name probes CLAUDE.md with pastCap, like the folder probe.
- Comment in config/path-probe.ts describes the mount-scoped stall.
2026-10-05 09:51:27 -04:00
Aamer Akhter 3e768e1b5b fix(tabs): show the in-flight name when an unchanged rename is confirmed 2026-10-05 09:47:08 -04:00
Aamer Akhter 92f51fa619 fix(tabs): inline rename review fixes
Reopening the editor over a rename still in flight filled it from the name
the server had not replaced yet, so dismissing it (blur commits) queued the
old name behind the new one and undid the rename. The queue now records the
newest queued name per session (_inlineRenamePending, cleared with the queue
entry), and a reopened editor takes its prefix, input and "unchanged"
comparison from it. An untouched confirm sends nothing more.

A failed write only toasted while its editor was still current. The queue
reports the failure itself now, and the editor only puts its label back.

One rejected task blocked every later rename of that session until reload.
Each task now chains from a settled predecessor, the local apply after a
successful PUT is guarded, and the queue entry is cleaned up on either
outcome.

The rail and sidebar editor's 4rem floor moves from a stylesheet
`!important` into the inline min-width startInlineRename already writes per
layout (0 in the header strip, 4rem in the rail and sidebar).

Tests: the reopened-editor case now expects only "First" to be sent; new
cases cover a 500 answered after the editor is gone and a throw in
updateSubagentParentNames; the long-prefix check runs in the sidebar and
detailed sidebar too and asserts the inline floor; the header strip editor
keeps min-width 0.
2026-10-05 09:45:24 -04:00
Aamer Akhter 06aba94ef1 fix(tabs): grouped rail interaction fixes for inline rename
Two problems with renaming a tab in the vertical rail, both easier to hit now
that the grouped rail has its own inline editor beside the session one.

Writes. A committed rename PUT its name and only applied the answer if the
same editor was still open when it came back. Reopening the editor before the
PUT answered (F2 or right-click again, or starting a group rename, which
cancels the session editor) threw the confirmed name away, so the tab kept
showing the old name until an SSE frame happened to repaint it. Two quick
renames also raced as two concurrent PUTs. Inline renames now go through a
per-session queue: one PUT at a time in the order they were made, the
confirmed name applied to app.sessions whatever happened to the editor, and
the "already that name" check made when the write runs rather than when Enter
is pressed, so confirming the name still on screen over a write in flight is
a real write.

Layout. The editor (a flex row) could not shrink below the input's intrinsic
width, so a long w<n>-<case> prefix pushed the label past its row: the prefix
slid out of view in the detailed rows and the input was clipped mid-word in
the compact rail. The label now has min-width 0, the prefix gives way first
(down to 2rem, with an ellipsis), the input keeps 4rem, and in the compact
rail the row's adornments step aside while the name is edited. The detailed
rows' three-line clamp also outranked the shared unclamp rule, which is what
the existing "unclamped editor" browser test caught; it is restated there.

Header strip, sidebar and flat-rail markup are unchanged.

Tests (test/inline-rename.test.ts, browser suite): the unclamp check runs for
simple and detailed rows; a write-ordering describe covers ordering, a
reopened editor cancelled over a confirmed write, a re-sent unchanged name and
a group rename taking over; a long-prefix describe drives real rows from a
live session in simple, detailed and compact rails.
2026-10-05 09:45:24 -04:00
Aamer Akhter ea80c5f471 docs(tabs): correct the tab-layout constructor comment 2026-10-05 09:45:06 -04:00
DevvynandClaude Sonnet 5.5 0c4bb5169f fix(git-status): address #537 review (docker workspaces, gone upstream, in-flight reset, docs)
- never inspect a repository at or inside a Docker case workspace (walk-up, scan, diff route): git would run its clean filters on the host
- a branch whose upstream was deleted and pruned reports upstreamGone and falls back to commits on no remote, instead of green
- turning the setting off during a poll releases the in-flight flag
- log.showSignature=false; reword the docs: clean filters still run
- CLAUDE.md frontend load order, changeset names git-diff
- discovery reads a bounded, sorted directory listing; leading-dash paths allowed; diff 500 redacts credentials
- keyboard focus survives the poll re-render; panel stays on screen on narrow viewports; aria-expanded visible on light skins

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 11:49:38 +08:00
DevvynandClaude Sonnet 5.5 b2423c90ce test(git-status): real-git rename and conflict diffs, tree setting round-trip
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:57:30 +08:00
DevvynandClaude Sonnet 5.5 4152ee1015 test(doctor): minimal-PATH searchDirs regression and the non-admin gate
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:56:10 +08:00
DevvynandClaude Sonnet 5.5 90dfa328a8 docs(cases): README entry for creating a case in a custom folder
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:50:47 +08:00
DevvynandClaude Sonnet 5.5 294ce0a667 docs(doctor): README entry for Settings → System → Diagnostics
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:50:24 +08:00
DevvynandClaude Sonnet 5.5 58b52fceff docs(git-status): document the diff view, folder grouping and collapsed repositories
README, Working With Files (new Git changes section), Settings Reference, The Dashboard and the changeset.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:49:58 +08:00
DevvynandClaude Sonnet 5.5 4fc75d494f feat(git-status): repositories start collapsed when several are listed
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:46:35 +08:00
DevvynandClaude Sonnet 5.5 948c7c54dd feat(git-status): group changed files under collapsible folders (setting, default on)
The Git window shows each group's files under their folders, collapsed until clicked, with single-child folder chains merged and open folders surviving the refresh. App Settings → Bottom bar → 'Git status: group files by folder' (per device) switches back to the flat list.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:41:03 +08:00
DevvynandClaude Sonnet 5.5 cd9218c23e feat(git-status): click a file in the Git panel to see its diff
Rows open an in-panel diff (staged, not staged, untracked as additions, deleted as removals) via GET /api/sessions/:id/git-diff, with Back and Open file. The route matches repo and path against the current status, runs git diff read-only (--no-ext-diff --no-textconv), and caps output at 400 KB.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:14:07 +08:00
DevvynandClaude Sonnet 5.5 db9a39405b fix(doctor): resolve CLIs via searchDirs, single-flight runs, admin-gate the group (#536 review)
- doctor probes each CLI's discovery.searchDirs when which misses and runs --version on the resolved path, so a service with a minimal PATH no longer reports installed CLIs as missing
- GET /api/doctor shares one in-flight run per category
- Diagnostics group hidden from non-admins in multi-user mode (_applyDoctorAdminGate)
- 500 uses INTERNAL_ERROR; a killed child reports 'timed out after 30 s'
- browser test blocks service workers so page.route() is reliable
- wiki: Diagnostics sentence

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS
2026-10-05 09:11:10 +08:00