v0.7.37 left CI at 79.94% — the runner is consistently ~0.6p below the workstation due to subprocess paths in ssh_file_transport.py that race differently in CI. Add 14 deterministic tests in two adjacent modules to lift the floor with margin. * lsp_save_preferences (90% → 100%): _settings_getter fallbacks (no settings method / store.get not callable), _as_enabled_flag int/float/unknown branches, list / blank / non-string filter paths in lsp_code_actions_on_save_kinds, plus the unsupported-shape early-return. * connect_progress (81% → ~89%): _hide_panel_if_progress three branches (active panel matches → hide_panel runs, user switched panels → no-op, window without active_panel → no-op), ConnectProgressPanel.failure terminal line, and ConnectProgressPanel.success terminal line. Local: 1,352 tests pass at 80.67%. The +0.67-point margin should land CI at ~80.08% and clear the gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sessions
Sessions is a Sublime Text package and Rust helper toolkit for remote workspaces over SSH.
Current focus:
- Completed milestones: Phase 0–6.2 (all closed), Phase 7 - Stability Hardening (closed), Phase 8 - Rust Transport Expansion (closed), Remote LSP integration track (#34, #35, #36, #37 — all closed;
local_bridge lsp-stdio, persistent broker attach IPC,session_helper lsp_stdiosupervision, URI rewrite + save barrier, host-scoped install with workspace-scoped env/config). Seeplanning/GITEA_ISSUES.md. - Open milestones: Phase 9 - Quality Gates & Scale (#10, #32 large-file streaming). #29 (diff-centric review) was reframed in the 2026-04-25 distribution review and is no longer the next feature — see
planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.mdandplanning/SHIPPED.md. Track D (in-Sublime agent integration) was dropped 2026-04-27 and the residualtmux/claude-code/codex-cli/jupyterlabcatalog entries were excised on 2026-04-30 — seeplanning/BACKLOG.mdandplanning/SHIPPED.md. - Execution order (2026-04, Rust-first): P0.5 stabilization → crate consolidation → artifact publish + manifest/checksum → #24 Rust runtime ownership → #32 large-file → Track G v1 (multi-repo, refs/ fast-path, line-staging polish). #29 diff-centric review/apply is deprioritized, not on this order. Normative detail:
planning/GITEA_ISSUES.md(execution priority and schedule), migration waves:planning/PYTHON_RUST_BOUNDARY.md. Distribution-readiness + ownership-migration plan:planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md. - P0.5 stabilization (2026-04, closed): persistent bridge, download-only helper, reconnect, mirror ignore patterns, save conflict UI, wire contract test coverage (bridge stdout fixtures, binary smoke test, ABI smoke test), stability hardening (prune symlink/permission edges, multi-window dedup, refresh race prevention), remote file auto-reload via periodic stat → revert, LSP-ready on-demand fetch via external path mapper +
on_window_commandinterceptor. - SSH config driven workspace selection
- session-bound helper over SSH stdio
- local cache with local-host-independent workspace identity
- formatter and linter execution in the remote environment (baseline + #30 pipeline on save)
long-term evolution toward a multi-session agent window— dropped 2026-04-27, residue removed 2026-04-30: the v0.6.0–v0.6.7 in-Sublime agent code (agent_tmux,agent_window_layout,agent_switcher_view, agent palette commands) was deleted in v0.6.7; thetmux/claude-code/codex-clicatalog entries and the paralleljupyterlab(kind="jupyter") entry were excised on 2026-04-30. Agents now run in an external terminal that the user manages outside Sublime;marimoreplaces in-tree Jupyter hosting. Seeplanning/BACKLOG.mdTrack D andplanning/SHIPPED.md.
Repository layout
sublime/: Sublime package code and Python testsrust/: Rust workspace for bridge/helper/shared cratesdocs/: human-facing project documentation (when present)planning/: roadmap, Gitea issue bootstrap, transport model, Python/Rust boundary, deep-research notes
Planning index (browse on Gitea)
| Document | Role |
|---|---|
planning/GITEA_ISSUES.md |
Milestones, parent issue text, execution priority |
planning/PYTHON_RUST_BOUNDARY.md |
Python vs Rust split, Rust-first migration waves 0–5 |
planning/VSCODE_REMOTE_TRANSPORT_MODEL.md |
Envelope + logical channels (VS Code–aligned) |
planning/REMOTE_DEV_MVP_LSP.md |
Phase 6.2 LSP / tool transport choices |
planning/DEEP-RESEARCH-REPORT.md |
External audit + priority reconciliation (end) |
planning/TRACK_G_V1_BIDIRECTIONAL_SYNC.md |
Track G v1 plan: bidirectional .git sync redesign (op-log + ref snapshot + git bundle, replaces tar-wipe) |
Installing In Sublime Text
sublime/ is the actual Sublime package root for this repository.
- Development install: link or copy
sublime/toPackages/Sessions - Current example:
Packages/Sessions -> /path/to/sessions/sublime - Unsupported layout: linking the repository root directly
The repository root remains a development workspace for Python tooling, Rust crates,
docs, and planning files. Sublime does not discover the nested sublime/
directory automatically when the whole repository is linked as one package.
Building A Release Archive
Build a distributable archive from the sublime/ package root with:
uv run python scripts/build_sublime_package.py
The script writes dist/Sessions.sublime-package and excludes development-only
artifacts such as tests/ and __pycache__/.
To build the current platform's Rust bridge/helper in release mode and bundle
them automatically, run:
uv run python scripts/build_sublime_package.py --bundle-built-rust-binaries
To ship pre-built Rust binaries (or any other release-only files), pass an
explicit layout rather than relying on discovery inside sublime/:
- Repeat
--bundle-file SOURCE=PATH_IN_PACKAGEso eachPATH_IN_PACKAGEis the zip entry path (POSIX separators, no..). - Or point
--bundle-manifestat a JSON file: a list of objects withsourceandpath_in_packagestrings. Relativesourcepaths resolve from the manifest directory, which keeps CI artifacts and the install tree layout in one place.
Example manifest:
[
{
"source": "artifacts/darwin-aarch64/local_bridge",
"path_in_package": "sessions/bin/local-bridge/darwin-aarch64/local_bridge"
},
{
"source": "artifacts/linux-x86_64/session_helper",
"path_in_package": "sessions/bin/remote-helper/linux-x86_64/session_helper"
}
]
Rust Binary Status
Current product policy (local vs remote):
- Remote host: Linux only. The remote
session_helperis fetched by the editor (not the remote):local_bridgedownloads the matching binary from the Gitea generic registry into the editor cache, then pushes it to the remote over the existing SSH session. Nocurl/wgetruns on the remote, and no remotecargo build. Binary is keyed by Linux platform tag + workspace semver fromrust/Cargo.toml. - Local machine (editor side): Linux, macOS, and Windows are supported for
running Sublime + this package. For day-to-day development, treat
local_bridgeas built on that machine (cargo build -p local_bridge, see Development). The plugin discoversrust/target/debug/local_bridge(or.exeon Windows) when no shipped binary is present undersessions/bin/....
Planned packaging (later): pre-built local_bridge per local OS/arch inside
release archives; until then, local build is the supported path for macOS
and Windows editors.
Current behavior:
- Persistent bridge session:
local_bridge --persistentruns as a long-lived process per host, communicating withsession_helperover a single SSH stdio session. Python sends NDJSON request envelopes and receives async responses via a background reader thread. Each request gets a unique monotonicenvelope_idto prevent response mis-routing under concurrency. - Editor-cache helper resolution:
session_helperis downloaded by the editor host (local_bridge) from the Gitea generic registry into the editor cache, then pushed to the remote over the existing SSH session —curl/wgetnever run on the remote. Identified by workspace semver + Linux platform tag, the remote-side cache lives at$HOME/.cache/sessions/helpers/<revision>/session_helper. If the editor-side download fails, the connection fails explicitly (nocargo buildfallback). - Required handshake fields:
Handshake.remote_homeandHandshake.archare required (noOption, no fallback). The bridge merges helper ensure + launch into a single SSH command to avoid double authentication. - Async multiplexer:
local_bridgeacts as an async multiplexer with a background mirror thread performing BFS viasession_helper. Python sends commands and manages async I/O; file opens are never blocked by mirror progress. - Reconnect:
SessionsReconnectCurrentWorkspaceCommandruns in a background thread withssh_prompt_callback, explicitly resets the bridge, and fails fast on handshake timeout (kills the bridge process immediately instead of returning a broken session). - After you open a remote file into the workspace cache, a normal editor save
(
Cmd+S/Ctrl+S) writes the local cache file and then pushes those bytes to the remote path automatically;Sessions: Save Remote Fileremains available as an explicit palette action.
Planned end-user install behavior (when releases ship all local binaries):
- the local
local_bridgebinary will ship inside the releasedSessionspackage for each supported local platform/architecture - the remote
session_helperbinary is downloaded on demand from the Gitea generic registry by the remote host itself (curl/wget) - the uploaded helper path is versioned as
$HOME/.cache/sessions/helpers/<revision>/session_helper - the bridge rejects helper handshakes whose reported helper version does not match the bridge version, so mixed bridge/helper bundles fail fast
- end users should not need a repository checkout or Cargo installed
Windows: running Sessions on the editor host
Typical flow when your PC is Windows and the SSH server is Linux:
- Sublime Text — Install a current build (ST4). This package is loaded from
Packages/Sessionspointing at this repo’ssublime/tree (symlink or copy; see Installing In Sublime Text above). - SSH client — Use Windows’ optional OpenSSH Client or Git for Windows so
sshis onPATH. Put hosts in%USERPROFILE%\.ssh\config(same idea as~/.ssh/configon Unix). - Rust toolchain — Install rustup for Windows. From
the repository root (the directory that contains
rust/andsublime/), run:cargo build --manifest-path rust/Cargo.toml -p local_bridgeThat producesrust\target\debug\local_bridge.exe, which the package picks up when no shipped binary exists undersessions/bin/local-bridge/windows-*/. - Gitea / registry (if required) — If helper downloads need auth or a
non-default host, set the
sessions_gitea_*keys in Preferences → Settings for Sessions (see comments insublime/Sessions.sublime-settings). The Linux remote still performs the download; these settings only affect URL and headers resolved on the editor side. - Use the package — In Sublime, run the Sessions connect flow, pick your
Hostfrom SSH config, then open a remote folder. The first connection builds or reuses the persistentlocal_bridgesession; the remote runssession_helperafter fetching the Linux artifact.
If bridge commands fail with “local_bridge binary not found”, confirm the Cargo
debug output path exists and that you opened a workspace whose sublime/ parent
is the repo root (so rust/target/debug resolves correctly).
Troubleshooting: duplicate “Sessions:” commands in the palette
The package ships a single Sessions.sublime-commands file; if every Sessions
entry appears twice, Sublime is almost certainly loading two package trees
that both contribute commands (merged resources).
Typical causes:
- Unpacked + packaged copy — e.g.
Packages/Sessions/(dev link) andInstalled Packages/Sessions.sublime-packagefrom Package Control. Remove one: disable the package in Package Control, or delete the shipped archive, or setignored_packagesso only your dev link is active. - Wrong link target — install only
sublime/asPackages/Sessions, not the repository root (see Installing In Sublime Text). A mis-linked tree can lead to confusing layouts; fix the symlink/copy so there is exactly one package root. - Case-insensitive volume duplicates — on Windows or default macOS disks, avoid having two folder names that only differ in case both pointing at the same code.
cargo build --workspace does not register Sublime commands by itself; rebuilding
Rust only changes binaries. If duplicates appeared right after a package change,
reload the editor (Developer: Reload from Disk / restart) once duplicates are
resolved so stale command tables are cleared.
The shipped Sessions.sublime-commands intentionally lists
sessions_run_remote_python_tool twice with different args (lint vs format);
that is not a duplicate installation.
Troubleshooting: Remote LSP install vs save-time ruff/pyright
Install Remote LSP Server (and the status panel) use the persistent bridge’s
exec/once path to run install/remove scripts and probe_argv on the remote
host. That checks that tools such as ruff --version exist in the remote
environment.
Save-time diagnostics for mirrored .py files are controlled separately by
sessions_remote_python_auto_diagnostics_on_save and
sessions_remote_python_tool_pipeline in Sessions.sublime-settings. That
pipeline still needs an active bridge session. If you see
Rust bridge closed the persistent session (or similar), reconnect before
expecting on-save lint to run.
Enable sessions_debug_trace_enabled for <Sublime cache>/Sessions/logs/debug-trace.log
(correlates Python bridge.* lines with Rust bridge.rust_* when
SESSIONS_BRIDGE_DIAG_LOG is set). After this package version, remote LSP
install/remove/probe also emit [Sessions LSP] lines to the console (stderr).
Remote workspace window title
Generated .sublime-project files include a name field such as
app [SSH: prod] (remote root basename plus SSH host alias) so the Sublime
window title is easier to recognize when several projects are open.
Output panels and remote diagnostics
Sessions output panels (LSP status, tool output) scroll to the end after open so
the latest lines stay visible. Remote Python diagnostics from Sessions use
add_regions on the mirrored buffer; if underlines from another package (for
example Sublime LSP) look stale until a window reload, that is usually editor
refresh ordering rather than the remote ruff run being skipped.
Per-project LSP settings are owned by the LSP package you use, not by Sessions:
configure them in .sublime-project / project-specific user settings as
documented for that package (for example client enablement, server paths, and
syntax scopes). Sessions writes/updates sessions_workspace_key and workspace
display name, while preserving existing project settings (including settings.LSP)
on rematerialize/reconnect. sessions_remote_code_servers in
Sessions.sublime-settings is for Sessions transport wiring, not Sublime LSP UI.
Troubleshooting: git status noise (GitSavvy)
The Sessions sidebar mirror adds your workspace cache folder (under Sublime’s
cache path) to the project. That directory is a plain file tree, not a Git
repository. GitSavvy (and similar packages) may still run git status for the
current working directory and log errors such as:
fatal: not a git repository (or any of the parent directories): .git
with a stack trace under GitSavvy.sublime-package/.../status_bar.py or
git_mixins/status.py. That comes from GitSavvy, not from Sessions’ own
code.
Mitigations:
- Turn off GitSavvy’s status-bar polling (simplest): in
Preferences → Package Settings → GitSavvy → Settings, set
"git_status_in_status_bar": false. Branch/dirty text disappears globally, but GitSavvy’s palette commands still work when you are inside a real repo. - Project-only override: if GitSavvy exposes project settings for your build,
add the same key under the
.sublime-projectsettingsblock for Sessions-only windows. - Unrelated console line
top level value must be an array: usually means some JSON (oftenfoldersin a.sublime-project) is not shaped the way Sublime expects. If it appears right after editing the project file, validate that"folders"is a JSON array of objects.
Sessions does not ship GitSavvy; avoiding non-repo git status spam is a
third-party / user-settings concern.
Development
The expected workflow is:
- add or refine a skeleton
- add or update focused tests
- implement the smallest coherent behavior
- run the relevant checks
- commit only after pre-commit passes