PR-B (commit 9691726) land 후 main track 이관 saturated:
- PR 18 (H3-queue 본 이관): callable dispatch 가 Python 잔존이라 deque
본체 이관에 PyO3 callback registry 필요. 부분 이관(dedup state만)은
critical section FFI cost 대비 가성비 낮음.
- PR 19 (디코더 이관): 현 _parse_*_outcome 은 이미 Rust JSON 받아
dataclass wrap 만 함. 완전 이관에 C tagged union 또는 PyO3 필요.
둘 다 잔존 쟁점 #8 (PyO3 ADR) 결정에 의존 — 본 plan scope 밖으로
이동. 다음 가시 가치 슬라이스는 Track H2 (commands.py 파일 분할).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final piece of the H1 file_open chain — the Python wrapper +
``open_remote_file_into_local_cache`` thin Rust call (commit 4c8dcde).
After PR 14.5d:
- file_open transaction fully owned by Rust (broker.request + guard +
atomic_write all in one call).
- Python only validates workspace root + maps outcome dict to typed
result.
- 11 transport_cache_mirror tests migrated to mock at the new boundary
(``_rust_file_open_transaction`` instead of internals).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PYTHON_THINNING_PLAN §5 PR 15. 코드 변경 없음.
실측 결과 ``sublime/sessions/commands.py:6562-6688`` 의 auto-reconnect는:
- 스레드가 *아니라* Sublime scheduler chain (``_set_timeout(fire,
delay_s * 1000)``).
- backoff state machine + max_attempts + pending tracking 모두
module-globals + UI thread 호출.
- ``bridge.request_broken_pipe`` trace event를 listener로 받아 backoff
scheduling.
따라서 plan v1.1의 "auto-reconnect thread → broker driven" 표현은 stale.
실제 단독 분리는:
1. ``_AUTO_RECONNECT_*`` state를 Rust supervisor로 → PR 16 worker queue
이관과 강결합 (``_CONNECT_GENERATION`` token 직렬화 invariant).
2. broker-side health probing → broker.rs 개수 thread 추가 + Python
listener disconnect callback.
(2)만 단독 진행 가능하나, (1)이 따라오지 않으면 reconnect SM이 두 곳에
나뉘어 boundary M1 (single source of truth) 위반. boundary-keeper 4-team
토론 시 ``_CONNECT_GENERATION`` 결합성 발견 — PR 16과 합쳐 한 PR로 land
해야 거버넌스 통과.
따라서 PR 15는 별도 코드 변경 없이 PR 16 본체 슬라이스에 흡수. plan 표
정정.
다음: PR 15.5 (PR-A integration tests) 는 PR 16 *전* land 가능한
``Rust 측`` 통합 테스트인데 ``sessions_orchestrator`` crate 신설을
전제. 따라서 PR 16 본체와 한 PR로 묶음.
본 세션 final 상태:
- PR 0~14.5 (16 commit) 완료.
- PR 13b.2-.4, PR 16 (PR-A 본체 ~600 LOC + 테스트 + Lint #2 활성화)은
후속 세션 작업 — 사이즈 + 회귀 표면이 단일 세션에 안전 land 불가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
진행 현황:
- 8ac7225 PR 13b.1 cancel flag map skeleton
PR 13b 4-way 분할의 첫 슬라이스 land. 나머지 셋(handler abort polling /
deadline propagation / priority+back-pressure) 은 사이즈가 크고 회귀
표면이 넓어 후속 세션 작업으로 인계.
PR 13b.2-.4 완료 후 PR 14 → 16 일직선 진행 가능.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two threads landing together because they share the
``Sessions.sublime-settings`` header comment edits.
Track D residue cleanup
-----------------------
v0.6.7 dropped the in-Sublime agent integration (Track D, 2026-04-27)
but left install-flow leftovers behind — now removed:
* ``BUILTIN_MANAGED_REMOTE_EXTENSION_CATALOG`` drops the three
``kind="agent"`` rows (``tmux``, ``claude-code``, ``codex-cli``) and
the ``kind="jupyter"`` row (``jupyterlab``, superseded by
``marimo_hosting``). Twelve ``_BUILTIN_BASH_*`` install/remove/probe
blocks deleted. ``managed_remote_extension_catalog.py`` shrinks
358 → 182 lines.
* ``rust/crates/local_bridge/src/agent_remote_payload.rs`` (279 lines)
+ ``parse-agent-editor-envelope`` CLI subcommand removed — used
only by the deleted ``agent_proposal_watcher``; verified zero live
callers.
* Tests: drop ``test_catalog_contains_jupyter_extension_entry`` and
``test_catalog_contains_agent_extension_entries``; ``debugpy``
``kind="debugger"`` test stays. ``test_settings_model.py`` builtin
id assertions trimmed to four entries.
* Comments: ``frozen-experimental`` docstring + matching
``Sessions.sublime-settings`` block deleted; ``commands.py``
``_managed_extension_project_client_keys_for_spec`` example
jupyter → debugger; Open-Remote-Terminal docstring drops the "no
tmux session multiplexing" framing; ``marimo_hosting.py`` drops
dead ``tmux``-children + ``jupyter_hosting.py`` postmortem
references.
* Planning: ``AGENT_TMUX_LAYOUT.md`` and ``V0_6_5_REPRO.md`` deleted
(both reference deleted features); ``BACKLOG.md`` Track D entry,
``REVIEW_v0_6_4_DISTRIBUTION_PLAN.md`` Stage 4 obsolete +
follow-up cleanup section, ``PYTHON_RUST_BOUNDARY.md``
agent_remote_payload row, ``README.md`` Track D bullet — all
updated to reflect the 2026-04-30 residue removal.
No backward-compat shim. ``debugpy`` ``kind="debugger"`` row
untouched.
LSP-style project-level override for the on-save pipeline
---------------------------------------------------------
The original Sessions design wired toolchain settings with the same
package → user → ``.sublime-project`` precedence Sublime LSP uses,
and ``merge_sessions_lsp_into_project_data`` already follows that
for the ``settings.LSP`` row writer. The on-save toggle path
(``_effective_sessions_settings_for_remote_python`` →
``load_sessions_settings_from_sublime``) skipped the project layer,
so per-workspace toggling required editing global user settings.
Fix: ``_effective_sessions_settings_for_remote_python`` accepts an
optional ``window`` and overlays
``window.project_data().get("settings", {})`` on top of the user
merge for ``sessions_remote_python_auto_diagnostics_on_save``,
``sessions_remote_python_auto_diagnostics_on_open``, and
``sessions_remote_python_tool_pipeline``. New
``_project_settings_block_for_window`` helper tolerates missing
``project_data`` callable / ``None`` payloads / non-mapping values.
Bool keys reject non-bool values silently (fall through to user);
pipeline runs through ``normalize_remote_python_tool_pipeline``.
All five callers in ``commands_python_pipeline.py`` now pass
``window``; the two listeners (``on_post_save``,
``on_activated_async``) reorder window-resolution before the toggle
check so the project block is consultable when the listener fires.
Six new regression tests in ``test_commands.py`` pin
project-overrides-user / user-wins-when-absent / pipeline-override /
wrong-type-rejected / null-project_data-safe / no-window-legacy.
``Sessions.sublime-settings`` header comment now documents the
precedence chain inline so users discover the
``.sublime-project`` ``"settings"`` block path without code-diving.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring the post-v0.7.23 audit + redesign of Track G's `.git` sync into
the planning tree as a tracked document. The plan was authored as a
working draft from a code audit + external-tool methodology survey
(Git refspecs, VS Code/Zed remote-dev, Jujutsu's op log, Syncthing
conflict copies); committing it makes the rationale and the phased
delivery (A0 verification → A1 op log → A2 `git bundle` → A3
conflict UI) reviewable alongside the code that will eventually
implement it.
The originally co-authored Track T (Terminus pane survival) section
has been removed from this plan; that fix already shipped in commit
0e2fdd9 (`fix(sublime/terminal): pin stdio to /dev/tty +
auto_close=False`).
Wire it in:
- README planning index links the new file alongside the existing
PYTHON_RUST_BOUNDARY / VSCODE_REMOTE_TRANSPORT_MODEL / DEEP-RESEARCH
documents.
- BACKLOG Track G section's v1 scope paragraph points to the plan,
so contributors landing v1 work see the architecture before
touching the wipe-and-replace path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 2026-04 distribution review flagged that the codebase shape is
closer to "Python calls Rust a lot" than "Rust owns the hot paths":
``commands.py`` (7379 LOC), ``ssh_file_transport.py`` (2240),
``_rust_ffi.py`` (1337) still carry runtime ownership Python should
not. Track H captures the ownership-migration plan as three concrete
sub-tracks driven from the existing PYTHON_RUST_BOUNDARY waves:
- H1: ``open_remote_file_into_local_cache()`` → Rust runtime API
(single biggest single-file ROI; ssh_file_transport target < 1500 LOC).
- H2: ``commands.py`` service split + module-global state reduction
(commands target < 4000 LOC; first PR extracts the save service).
- H3: background queue / mirror queue / open-file watch / auto-reconnect
→ Rust broker (auto-reconnect thread first; queues in follow-up PRs).
Each sub-track lists its first-PR scope, conflict surface,
done-when, regression test set, and risk + mitigation. Recommended
PR order H1 → H2-save → H3-reconnect → H2-connect → H3-queue → … is
in the dependency graph.
This is plan-only; no implementation lives in this commit. The
implementation PRs come later, off this BACKLOG entry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track G v0 final piece: when the user switches branches in Sublime
Merge against the local mirror, the remote working tree follows.
New ``sublime/sessions/git_branch_proxy.py``:
- ``install_post_checkout_hook(local_dot_git)`` writes a tiny
``sh`` script at ``<.git>/hooks/post-checkout`` that drops a
``SESSIONS_PENDING_CHECKOUT`` JSON marker on every local
checkout. Idempotent re-write detection (don't re-flush identical
bytes — Sublime's "file changed on disk" reload would otherwise
fire on every refresh). Marks the file +x on POSIX; harmless on
Windows where git ignores ``core.fileMode``.
- ``read_pending_checkout`` / ``clear_pending_checkout`` —
defensive parser. A truncated marker (hook crashed mid-write) is
treated as "nothing to do" rather than raising.
- ``apply_pending_checkout(host_alias, repo)`` proxies the marker:
runs ``git checkout <new_head>`` on the remote via
``exec/once``. On success the marker is cleared. On stock git
refusal (dirty working tree, unknown ref, timeout) the marker is
kept so the user resolves the remote-side state and re-fires
``Sessions: Refresh Git State`` to retry. **G6 lives here**: the
refusal path surfaces ``stderr`` verbatim so Sublime's status bar
shows the same "Your local changes would be overwritten…" message
git emitted, with no auto-stash. Path-spec checkouts
(``branch_flag != "1"``) are silently dropped — the hook fires on
``git checkout -- some/file`` too but those don't move HEAD.
``Sessions: Refresh Git State`` flow now does, per repo:
1. Discover (G1) → fetch ``.git`` (G2)
2. Install the post-checkout hook (G4 install step)
3. Drain any pending checkout marker (G4 apply step):
- If branch switch refused, surface stderr in status bar +
``failed`` summary entry, skip materialise so we don't
re-classify against a HEAD that didn't actually move.
4. Materialise (G3): skip-worktree on clean tracked + content
pull on dirty.
Two new trace events: ``git.hook_install_failed`` (rare; only on
filesystem permission issues) and ``git.checkout_proxy`` carrying
``proxied / ok / new_head / error_detail`` for post-mortems.
15 new unit tests cover hook write + idempotence + overwrite of
unrelated existing hook, marker round-trip, JSON tolerance,
no-marker no-op, path-spec skip, branch-switch happy path, dirty
refusal (G6), remote timeout, empty new_head sanity, and a real
hook-script smoke test that invokes the installed ``sh`` directly
on POSIX. Full suite 1216 pass.
Track G v0 is now feature-complete: read-only history (v0.7.9) +
staging / commit (v0.7.11) + branch switching (this commit). v1
work (automatic reconcile, refs/ diff fast-path, multi-repo,
submodules, LFS, untracked-not-ignored lazy fetch) is tracked at
the BACKLOG section heading.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The broker + lsp-stdio relay are now cross-platform. Pre-W1 both
pieces were ``#[cfg(unix)]``-gated despite ``interprocess`` 2.x
already supporting Windows Named Pipes via the same ``GenericFilePath``
resolver — the gates were only there because the original v0.5.x
implementation used ``UnixListener`` / ``UnixStream`` directly. This
patch lifts the gates and swaps the remaining raw Unix calls to the
cross-platform interprocess equivalents.
Concrete changes:
- ``persistent.rs``: ``PersistentBroker`` / ``BrokerAttachRequest``
/ ``BrokerAttachResponse`` / ``handle_broker_client`` /
``lsp_response_body_to_framed_string`` are now unconditional.
``persistent_broker_endpoint_path`` splits on ``cfg``: Unix returns
``$TMPDIR/sessions-local-bridge-<host>-<pid>.sock`` (unchanged
behaviour); Windows returns ``\\.\pipe\sessions-local-bridge-<host>
-<pid>``, which is the only form ``GenericFilePath`` accepts on
Windows. The ``fs::remove_file`` and ``fs::set_permissions(0o600)``
calls stay ``cfg(unix)`` since named pipes are reaped by the OS
and don't take POSIX modes.
- ``lsp_stdio.rs``: drop the ``cfg(unix)`` gates from the relay loop
+ the ``run_lsp_stdio`` client, and replace ``UnixStream::connect``
with ``IpcStream::connect`` resolved via ``GenericFilePath``. The
``cfg(not(unix))`` "not supported" stub is gone.
- ``lsp_project_wiring.py``: docstring updated to note that the
empty-broker_socket case on Windows now means "broker failed to
start" (rare; e.g. AV blocking the pipe), not "feature not
implemented". The v0.7.6 ``managed_lsp_enabled`` gate then flips
``enabled: True`` on the next handshake once ``broker_socket``
populates.
- New regression test ``endpoint_path_uses_named_pipe_namespace_on_
windows`` (gated ``cfg(windows)``) pins the path shape; the
``lsp_response_body_*`` tests are no longer ``cfg(unix)`` since
the function isn't.
Verification: full Linux test suite + Windows cross-compile
(`x86_64-pc-windows-gnu`) build clean; 1165 sublime tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
BACKLOG audit found Track A's two items both already landed in
earlier releases:
- A1 (interpreter folder browser): shipped v0.5.7 as
``python_interpreter_browser.py`` + the "Browse remote
filesystem..." quick-panel row. The "autocompletion as you type"
piece is genuinely separate work and lives under W4.
- A2 (status-bar indicator): shipped v0.6.2 as ``Python: <venv>
(<X.Y.Z>)`` with version probe + cache + syntax gating. M2 had
the same done-when; both folded together.
Track A is now strikethrough'd / closed. Active queue narrows to
G (git/SCM) + M3 (extension install latency + auto-format race) +
W1/W4 (Windows parity). Track E stays as reference-only.
No code change needed; just BACKLOG hygiene. v0.7.8 release skipped
since there's nothing to ship — next user-visible version is
whatever lands first from G / M3 / W.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v0.7.5 split mirror-sync from the generic 45 s bridge timeout but
left file/read (30 s), file/stat (30 s), and the helper handshake
(60 s) hard-coded. Slow tunnels (AWS SSM, mobile tether) can hit any
of those budgets the same way mirror-sync did, so make all three
overridable from settings:
- ``sessions_file_read_timeout_s`` (default 30)
- ``sessions_file_stat_timeout_s`` (default 30)
- ``sessions_helper_handshake_timeout_s`` (default 60)
New ``_settings_timeout_s(key, fallback)`` helper folds the
``load_settings → get → float → clamp >=1 s`` boilerplate that
``_mirror_sync_timeout_s`` had into one place; each per-method
helper is a one-line wrapper. ``execute_remote_read_file`` and
``execute_remote_stat_file`` keep their existing ``timeout_s`` kwarg
for callers that want an explicit budget; ``timeout_s=None`` (the
new default) reads the setting at call time. ``open_session``'s
60 s hand-shake budget swaps the literal for the helper.
``file/watch`` is intentionally not a setting: its timeout already
varies per-request (``request.timeout_ms / 1000 + 5 s`` slack),
which is the right knob for a long-poll. The 120 s Rust-side
request ceiling stays as an architectural cap.
Tests: three new cases on the helper trio (defaults / override /
clamp / garbage-fallback). Full sublime suite 1165 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 4e81804 (2026-04-27) removed terminal_link_click.py and the
whole Terminus integration as part of the pivot to an external OS
terminal — that retires M1 (Terminus hover, shipped v0.6.10) and
makes M4 (Multiple Terminus panes / plain close) moot since the OS
terminal owns the lifecycle now. Update both entries with the
"[dropped 2026-04-27]" / retired tags so future readers don't
re-propose the same items.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User direction 2026-04-27: agents will run in an external terminal
outside Sublime, so the in-Sublime agent-tmux work (Track D) is
dropped wholesale — the v0.6.0–v0.6.7 code stays in the repo as
historical layer but no further sub-tracks ship. Terminus's role is
narrowed to "lightweight execution", which retires Track C (hover
activation + session persist) and the same-shaped Windows mirrors
W2/W3. Track A re-scoped to interpreter UX only — A3 (extension
status label rename) and A4 (.sublime-project pollution) drop; A3's
caching idea is absorbed into M3. Track B drops entirely: B1 merges
into M3, B2 (Cargo.toml hydrate-on-demand) deferred until Track G's
materialisation controller exposes a similar plumbing point. M2 and
M6 closed (M2 shipped in v0.6.2; M6 not blocking — moved to README
territory).
Live queue after this pass: G (next big feature), A1+A2 (interpreter
UX polish), W1 (Windows LSP stdio via PersistentBroker), W4 (folder
browser auto-descend), M3 (extension probe latency + format race),
M4 remainder (plain-close palette wiring), M5 remainder (file/watch
/file/read/helper-launch per-method timeouts). Track E retained for
visibility only.
Diff is mostly deletions: 354 lines down to 84 insertions / 270
deletions. Strikethrough headers + "[dropped]" tags kept as gravestones
so future readers don't re-propose the same items.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Capture the v0.7.5 diagnosis + fix in the two planning docs that
tracked the open repro. M5 status moved to "[mostly shipped]" —
mirror-sync timeout split + auto-refresh backoff + depth default
landed; file/watch / file/read / helper-launch per-method timeouts
still pending but not blocking. V0_6_5_REPRO §B1 records the
2026-04-27 capture that ruled out OOM and channel-buffer hypotheses
and tagged the actual root cause: deep walk at depth 12 over slow
tunnels genuinely runs 45-50 s, just exceeding the generic 45 s
request timeout. Original capture recipe kept in place for future
timeout-shaped repros.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the design converged in conversation: keep .git real and
fully synced both ways, leave clean tracked files as stubs with
git update-index --skip-worktree, materialise dirty + non-ignored
untracked files on demand. Branch switch flows through a local
post-checkout hook that proxies to remote and re-runs the
materialisation controller; refuses (no auto-stash) when remote-side
dirty files would be overwritten.
Six sub-tracks (G1 discovery, G2 .git sync, G3 materialisation, G4
post-checkout proxy, G5 dirty-set freshness, G6 branch-switch refusal
UX), v0 / v1 scope split, risk register (.git desync, skip-worktree
edge cases on reset/merge/rebase, big-repo initial pull cost), and a
3-agent parallel plan. Out of scope is explicitly called out:
GitLens-style inline blame (Sublime UI primitives don't reach there)
and TUI integration (covered by users picking lazygit instead).
Reading-order prose at the top now mentions Tracks D + G as the two
major feature tracks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 2026-04-26 v0.6.12 test pass surfaced that local sidebar deletes
intentionally do NOT propagate to the remote — Sessions is a read-mostly
mirror to avoid silent remote-data loss. The asymmetry is correct policy
but undiscoverable without an explicit knob. This commit ships the knob.
* New WindowCommand SessionsDeleteRemoteFileCommand wired into both
the palette ("Sessions: Delete Remote File") and the Side Bar
context menu. Resolution priority: explicit ``remote_file`` arg →
sidebar ``paths`` (reverse-mapped via RemoteToLocalCacheMapper) →
active view's file_name (also reverse-mapped). Refuses with a clear
status message when the target lives outside the workspace cache
mirror so a stray ``foo.py`` from /etc/hostname can never reach the
bridge.
* Confirmation via ``ok_cancel_dialog``; the dialog text shows the
fully-resolved absolute remote path so the user can audit before
acting. Cancel emits ``Sessions: remote delete cancelled.``
* Bridge call: ``execute_remote_exec_once(host, ("rm","-f","--",path))``
via the persistent bridge's ``exec/once`` channel — no new SSH spawn,
no new helper RPC method. ``rm -f`` swallows ENOENT so a
remote-already-gone state still completes the local-side cleanup
(sidecar + cache copy + open views). Non-zero rm exit (permission
denied / readonly FS / path-is-directory) keeps BOTH sides intact
with a ``Sessions warning: remote delete of <path> failed (rm exit
N): <stderr>`` status message so the user can investigate.
* Trace events for end-to-end auditability:
``file.delete.remote_begin`` / ``file.delete.remote_done`` /
``file.delete.remote_failed`` / ``file.delete.remote_transport_error``.
* Five regression tests covering: happy path drops cache + sidecar,
user cancel preserves both sides, non-zero rm refuses local cleanup,
outside-cache refusal, sidebar context-menu paths argument resolves
via reverse-mapper.
* planning/TEST_CHECKLIST.md gains §D (new findings from the v0.6.12
test pass) and §E (verification checklist for the five fixes that
shipped in 76bdf5b plus this Delete feature). §D records the open
policy questions (delete propagation, "Refresh Remote Worktree"
rename, sub-second remote-create vs Save-As race, Terminus URL
intercept) that need design / data before they can land as code
changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical data-loss guard plus four UX fixes from the 2026-04-26
working session, all backed by regression tests:
- Data-loss guard: brand-new files saved into the cache mirror with
no metadata sidecar are no longer destroyed by the REMOTE_NOT_FOUND
branch (both _apply_hydrate_result and SessionsOpenRemoteFileCommand
now consult _has_remote_metadata_sidecar and preserve local-only
content).
- Auto-reconnect with backoff (1s->2s->5s->10s->30s, cap 12 attempts):
subscribe to bridge.rust.collector_error / helper_stdout_eof /
handshake_recv_timeout and revive the bridge automatically, but
keep v0.6.11's silent cold-start contract.
- Window reuse on connect / reconnect: existing Sessions windows
swap project_data in place via set_project_data instead of
spawning a new window through open_project_or_workspace.
- New file/folder first-time push: helper Missing precondition path
now runs fs::create_dir_all(parent), so "new folder + new file
inside" lands without a separate mkdir step. Python skips the
conflict evaluator's BASELINE_UNKNOWN refusal for first-time
creates and conservatively refuses blind overwrite of an unfetched
remote.
- Jupyter timeout 15s -> 60s with SESSIONS_JUPYTER_STARTUP_TIMEOUT_S
override; timeout error message now includes the cat rc + ssh
stderr so "last log snippet: ''" is replaced with actionable
diagnostics.
- tmux list-sessions diagnostics: stop swallowing every non-zero SSH
exit into an empty list; warn with stderr tail (excluding the
benign "no server running" path).
- trace_event time-field consistency: commands.py now emits both
"ts" and "time" fields, matching ssh_file_transport's shape.
- Right-click expand diagnostic trace (expand.invoked /
sidebar_resolved / quick_panel_deferred) so the wrong-path bug
from v0.6.11 testing can be diagnosed from the trace log alone.
planning/TEST_CHECKLIST.md is rewritten as a slim v0.6.12 doc
focused on these ship verifications + diagnostic capture for the
still-open issues (#3 LSP race, #6/#7 Terminus URL handling,
#8/#9 tmux empty-list, #12 agent tmux TTY).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring the version header, "What's new" preamble, §0 prerequisites,
§8 release verification assets, and §9 known-limitations header
forward to v0.6.11. §8.1 example tag advanced to v0.6.12 to match.
New scenarios:
- §1.5 (NEW, v0.6.11): no auto-spawn on cold start / disconnect.
Two flavours — (a) Sublime restart with restored ``.py`` view
focus, (b) mid-session bridge kill (``Broken pipe`` repro). In
both cases the trace log must NOT contain
``bridge.helper_editor_download_*`` /
``bridge.helper_ssh_push_*`` / ``bridge.session_spawn`` /
``bridge.rust.handshake_ok`` until the user runs
``Sessions: Reconnect Current Workspace`` explicitly. Status bar
still paints the cached interpreter or ``(…)`` placeholder so
the slot is not blank.
- §1.6 (NEW, v0.6.11): ``sessions_remote_python_auto_diagnostics_on_open``
opt-in still respects the gate. Even with the flag enabled,
view focus must not auto-spawn — the same
``_workspace_runtime_connected`` check guards the diagnostics
pipeline listener.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch release shipping the fix from 7ca1dbc.
User report (2026-04-26 debug-trace.log): after a Sublime restart of a
restored Sessions project window, the bridge was being spawned without
any explicit reconnect command. Trace evidence at 12:59:42–13:46:32:
- 12:59:42 mirror-sync ``Broken pipe`` → bridge dead.
- 13:00:21 ``sessions.probe_python_version`` enqueued, helper download
begins.
- 13:46:29 same probe re-enqueued; helper SSH push, ``session_spawn``,
``handshake_ok``, then ``lsp.managed_server_restart`` × 3
for LSP-pyright / LSP-ruff / rust-analyzer.
The probe path was triggered by
``SessionsPythonInterpreterStatusListener.on_activated_async``: every
time a restored ``.py`` view came into focus, the listener saw a cold
version cache and scheduled ``_probe_active_python_version_task``,
which calls ``execute_remote_exec_once`` →
``_persistent_bridge_for_host(allow_spawn=True)`` and revives SSH
unconditionally. The other on-activated / on-load callbacks (sidebar
placeholder hydrate, LSP workspace activation tracer, active-remote
view revalidate) already gate this with
``_workspace_runtime_connected``; this listener (and its sibling
``SessionsRemotePythonPipelineListener.on_activated_async``) was the
straggler.
The fix in 7ca1dbc adds the same gate to both. After a restart the
status bar still paints the cached interpreter or the ``(…)``
placeholder so the slot is not blank, but no SSH is touched until the
user runs ``Sessions: Reconnect Current Workspace`` explicitly. The
next view activation after reconnect fires the probe and the version
fills in.
CI (``.gitea/workflows/upload-session-helper-gitea.yml``, ``tags: v*``
trigger) imports the GPG signing subkey from secrets and publishes
the signed bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The doc was last revised for v0.6.4. Bring the version header,
"What's new" preamble, and §0 prerequisites forward to v0.6.10.
New scenarios:
- §3.2 hover URL: add v0.6.5 (``0.0.0.0`` canonicalization +
trailing ``/`` so Cmd+click no longer lands on ``about:blank-``)
and v0.6.10 (ANSI/VT100 escape strip + relative-path resolution
against the workspace mirror via ``RemoteToLocalCacheMapper``;
structured ``terminal_link.hover.*`` / ``terminal_link.click``
logs at every decision point).
- §3.4: marker bumped to v0.6.2 / v0.6.5 — v0.6.5 finally registered
``Sessions: New Remote Terminal Pane`` /
``Kill Remote Terminal`` in ``sublime/plugin.py``; the v0.6.2
``.sublime-commands`` rows alone weren't enough.
- §3.5 (NEW, v0.6.6): ``Sessions: Attach to Tmux Session`` palette
command — read-only attach to any remote tmux session
(Sessions-owned or foreign); foreign sessions never enter the
per-host caches so existing Open / New / Kill flows stay scoped.
- §3.6 (NEW, v0.6.10): ``sessions_terminal_close_default`` setting
documented — internal API ships, palette wiring + on-pane-close
listener still pending; flag has no visible effect yet.
- §3.7 (NEW, v0.6.5): ``sessions_show_dev_commands`` gate hides
developer-only palette entries (first gated:
``Sessions: Preview Remote Agent Payload``).
- §7.1: marker bumped to v0.6.2 / v0.6.5 — v0.6.5 closed the
remaining ``open terminal failed: not a terminal`` holes
(``ssh -T`` + ``</dev/null``).
§8 release verification points at v0.6.10 assets. Tag-signature
expectation split per release: ≤ v0.6.9 are subkey-signed and
``git tag -v`` should show "Good signature"; ≥ v0.6.10 may be
plain annotated tags because CI signs the artifacts via the
dedicated subkey on tag push (see Fix-→tag-→release memory).
§9 known limitations: D7 Phase 1 / Phase 2 marked **abandoned**
(not deferred) — the supporting modules
(``agent_proposal_watcher`` / ``agent_change_badge``) and their
tests were deleted in v0.6.7 along with the chat→tmux pivot away
from diff-centric review. New entries cover the v0.6.10 deferred
palette wiring for close-terminal and the macOS hover box-vs-underline
theme caveat.
(v0.6.7 / v0.6.8 / v0.6.9 are pure clean-code refactors / dead
code retirement — no behavior change to test, mentioned only in
the "What's new" preamble.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Terminal hover (M1, shipped):
- ``terminal_link_click`` now strips ANSI/VT100 escapes at
``classify_terminal_token`` entry so abspath / URL detection works
against ANSI-coloured ``ls`` output (allocation-free fast path when
no ``\x1b`` is present).
- New ``_RELPATH_PATTERN`` + ``_resolve_relpath_in_cache(view, token)``
resolve relative tokens against the workspace mirror via
``RemoteToLocalCacheMapper`` — only marked clickable when the local
cache file actually exists; directory / ``..`` traversal /
no-context cases all safely fall through.
- Click handler routes ``relpath`` outcomes through
``_handle_local_path`` directly (no bridge round-trip needed).
- Structured ``terminal_link.hover.*`` / ``terminal_link.click`` logs
at every decision point now name ``matched_kind`` / ``matched_text``
/ ``resolved_target`` / ``action`` / ``outcome`` /
``source=hover_cache|reclassify`` so the next macOS Cmd+click
"paint OK / click silent" repro can be diagnosed from logs alone.
Terminal close (M4, partial):
- ``terminal_tmux_session.close_terminal_session(host, name, *, kind)``
unifies the three close paths via ``TerminalCloseOutcome``:
``"plain"`` = ``tmux kill-session`` (non-persistent default-on-pane
close), ``"kill"`` = same SSH effect but explicit user action
(palette command path), ``"detach"`` = current default (no SSH
call, session persists).
- New ``sessions_terminal_close_default`` setting accepts
``"detach"`` (default, current behavior) or ``"plain"``;
``"kill"`` deliberately excluded from default policy
(regression-guarded).
- ``kill_terminal_session()`` retained for backward compat —
``close_terminal_session("plain"|"kill")`` delegates so
session-name validation + argv shape stay in one place.
- Palette wiring (new ``Sessions: Close Remote Terminal (don't
persist)`` command) + on-pane-close listener that reads the new
setting are deferred to a follow-up commit; the internal API in
this release is feature-complete and tested but not yet
user-reachable from the palette.
Diagnostic instrumentation (no behavior change):
- ``jupyter_hosting.build_notebook_url`` logs ``local_port`` +
``notebook_path`` at entry.
- ``commands_python_pipeline._open_remote_jupyter_in_browser`` logs
the constructed URL right after ``build_notebook_url``, again at
``finish()`` entry, again immediately before ``webbrowser.open``,
and ``.exception(...)`` inside the previously-silent
``except Exception: pass`` branch — pinpoints which step swallows
the open in the slow-link "queue.done elapsed_ms=27748 but no
browser tab" repro.
- ``ssh_file_transport._execute_rust_bridge_request_persistent``
extracts ``payload_bytes`` + ``params.max_traversal_depth`` from
the request payload and threads both into the
``bridge.request_timeout`` trace event, so the mirror-sync
deep-traversal hang
(``stall_phase=awaiting_response_dispatch`` after 45s) reports the
depth + payload size that hit the timeout.
- ``sessions_native::broker::dispatch_response_line`` gains an
env-gated (``SESSIONS_BROKER_DISPATCH_DEBUG``) stderr trail
covering enter / parse-OK / parse-FAILED / no-id-drop /
id-and-slot-presence — zero noise unless explicitly enabled,
drained through the existing ``STDERR_TAIL_CAPACITY=100`` ring so
``_rust_ffi.stderr_tail(host_alias)`` surfaces the trace
post-repro.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PoC validation step for the Windows W1 PersistentBroker port. Swap
the broker server side from std's ``UnixListener`` / ``UnixStream``
to ``interprocess`` 2.4.2's cross-platform ``LocalSocketListener``
(``IpcListener``) / ``Stream`` (``IpcStream``) abstraction. Behind
the abstraction the Unix path is byte-for-byte unchanged at the OS
level — same ``AF_UNIX`` socket file at
``/tmp/sessions-local-bridge-<host>-<pid>.sock`` with the same
``chmod 0600`` permissions hardening (kept inline as a
``#[cfg(unix)]`` block); on Windows the same code opens a Named Pipe
at ``\\.\pipe\sessions-local-bridge-<host>-<pid>``.
Why this matters: the maintainer flagged (2026-04-25) Windows as the
likely largest user fraction, making BACKLOG W1 (PersistentBroker
for Windows, currently a no-op blocker on the Sessions-managed LSP
attach path) a real product gap. ssh-mux
(https://git.teahaven.kr/Rust-related/ssh-mux) ships a battle-tested
named-pipe IPC implementation we can crib hardening patterns from.
This commit lands the foundation: cross-platform listener, ungated
``PersistentBroker`` struct, ``IpcStream`` parameter types in
``handle_broker_client`` + ``broker_lsp_relay_loop``. The
``run_lsp_stdio`` client side stays on ``UnixStream::connect`` for
this PoC scope, and the broker call site at ``main.rs:240`` stays
``#[cfg(unix)]``-gated so Windows builds still report an empty
``broker_socket`` — no behavior change on Windows yet.
Verified on macOS / Linux:
- ``cargo test --workspace`` green (all 19 test binaries).
- ``cargo clippy --all-targets -- -D warnings`` green.
- ``pytest sublime/tests/`` green (1432 tests).
- ``cargo build --release`` size delta < 50 KB.
planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- §2.6 Windows W1 status: ``[plan]`` → ``[plan-MVP, foundation
laid]`` with concrete next steps:
1. Ungate broker call site in ``main.rs:240``.
2. Cross-platform ``run_lsp_stdio`` client
(``UnixStream::connect`` → ``IpcStream::connect``).
3. Verify ``\\.\pipe\...`` survives Sublime project-file JSON
round-trip.
4. Windows test pass: pyright LSP attach reaches handshake.
- "Open questions" — Windows W1 entry resolved; MVP estimate
3-4 days, optional hardening (anti-squatting + DACL via
ssh-mux patterns) ~1 week.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps workspace 0.6.6 → 0.6.7. Bundles the dead-diff retirement
(commit e666e91) into a release tag.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Maintainer signoff (2026-04-25): the chat→tmux pivot abandoned the
diff-centric review direction. The diff primitives that survived the
pivot have no live caller in the agent flow and were carrying test
weight (56 tests across 3 files) for an undefined product surface.
git history preserves them for anyone who wants to revive the diff
direction later.
Removed
- ``sublime/sessions/agent_proposal_watcher.py`` (290 LOC) — pure-Python
unified diff parser; was meant to tail ``tmux pipe-pane`` output.
- ``sublime/sessions/agent_change_badge.py`` (248 LOC,
``AgentChangeBadgeRenderer``) — post-apply phantom badge renderer.
- ``sublime/tests/test_agent_proposal_watcher.py`` (20 tests).
- ``sublime/tests/test_agent_proposal_watcher_adversarial.py`` (10).
- ``sublime/tests/test_agent_change_badge.py`` (26).
Updated
- ``sublime/sessions/agent_tmux.py``: docstring no longer points at the
retired companion module; new line documents the historical context
of the chat→tmux pivot so future readers know why the broker is
agent-agnostic.
- ``planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md``: "Code to consider
retiring" → "Code retired (post-direction-correction)" with the
test-health gate numbers post-deletion. Closed two of the three
open questions (rm signoff and Linux-only sublime-package decision —
defer the latter, no Linux-only release asset for now).
Test-health gate stays green: 1432 sublime tests pass; adversarial 190
(floor 184), real-subprocess 55 (floor 53), contract-fixture 27 (floor
27), mock-only:high-value 0.95 (cap 0.98).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps workspace 0.6.5 → 0.6.6. Bundles the foreign-tmux attach feature
(commit 358d674) into a release tag plus the plan direction
correction that cleared the way for it.
CI on tag v0.6.6 will produce the signed bundle via the dedicated
signing subkey (master never touches the runner) and upload assets
to the release page + musl session_helper to the generic registry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The maintainer flagged two divergences between the external review's
prioritization and the actual product direction (2026-04-25):
1. #29 diff-centric review/apply was abandoned when the agent UI
pivoted from chat-with-diff to tmux session passthrough — agents
run in a tmux pane, edit remote files directly, and Sessions's
job is multi-session lifecycle (spawn/switch/kill), not diff
orchestration. The diff primitives that survived the pivot —
`agent_proposal_watcher` (290 LOC), `agent_change_badge`
(248 LOC) — are dead code from the abandoned design.
2. The persistent-terminal flow always opens `sessions-term-<host>`,
so a user with their own `tmux new-session -A -s work` on the
remote can't reach it via the palette. The "single Sessions-owned
tmux session per host" model is too narrow.
Plus environment constraints: cross-platform CI runners aren't
available; code-signing budget (~$600/yr) is out of scope.
Code: Sessions: Attach to Tmux Session
- New `list_all_remote_tmux_sessions` in `terminal_tmux_session.py`,
sibling of `list_terminal_sessions` but without the SESSION_NAME_PREFIX
filter — returns Sessions-owned sessions alongside foreign ones.
- New `SessionsAttachRemoteTmuxCommand` in `commands.py` + companion
`_attach_remote_tmux_session` helper that opens a Terminus pane via
`ssh -tt <alias> tmux attach-session -t <name>`. Read-only attach
semantics: foreign sessions never enter the Sessions-owned per-host
/ per-session view caches (so kill / new-pane flows can never
reach into a foreign session by accident).
- Quick panel rows distinguish `Sessions-owned` vs `foreign` tmux
sessions in the description column so the user knows what they're
attaching to.
- Existing `Sessions: Open Remote Terminal` / `New Remote Terminal
Pane` / `Kill Remote Terminal` stay scoped to `sessions-term-*`
unchanged.
- Wired through `plugin.py` import + `__all__`; entrypoint smoke +
runtime-import smoke updated; new palette entry in
`Sessions.sublime-commands`.
Tests
- 6 new tests for `list_all_remote_tmux_sessions` (no-filter,
empty-server, missing-tmux, timeout, oserror, blank-line stripping).
- 5 new tests for the attach command (palette listing including
foreign sessions, terminus_open argv shape, foreign-session does
NOT register in the Sessions-owned caches, empty-list status hint,
tmux-missing status hint).
- Entrypoint + runtime-import smoke tests updated for the new export.
- 1488 sublime tests pass (was 1477).
Plan: planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- New "Direction correction (post-review, 2026-04-25)" section
documents the chat→tmux pivot + the dead diff modules + the
blocked environment constraints.
- §2.1 was "#29 diff-centric MVP, highest priority" → replaced with
"Tmux session discovery + attach to foreign sessions" (this
commit's feature). #29 moves to "Items DEPRIORITIZED / dropped".
- §1.4 cross-platform smoke + signing → `[blocked-by-environment]`
with documented "feasible without runners/certs" guidance.
- §1.1 updated to reflect Linux-only first iteration is feasible
now; full matrix waits on §1.4.
- New "Code to consider retiring" section catalogues
`agent_proposal_watcher.py` + `agent_change_badge.py` + their
tests as removal candidates pending maintainer signoff.
- "Open questions" surfaces three decision points: retire-or-archive
the dead diff modules, ship Linux-only sublime-package now or
hold, and Windows W1 priority given the macOS-primary workflow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The earlier rewrite labelled the immediate-CI-step sub-item as
`[partial]` even though no CI step has actually been added; the
script exists in scripts/ but isn't called from any workflow.
Downgrade to `[plan]` and add the third missing piece (sign script
needs to learn --extra-asset so SHA256SUMS covers the .sublime-package).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User restored the original review.md (it had been lost mid-session).
Rewrites the plan with all three review layers fully captured, and
fixes a stale README claim the review surfaced.
planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- Rewritten end-to-end. Three layers:
- Layer 1 — five distribution must-haves (sublime-package +
binaries, safe-by-default, experimental gating, cross-platform
smoke + signing, release discipline).
- Layer 2 — feature priority before adding breadth: #29
diff-centric review MVP, #32 large-file streaming, sync-mode
product feature, probe TTL cache + hash-based self-write
suppression, .sublime-project LSP command leakage, Windows W1
PersistentBroker, slow-link timeout/backoff.
- Layer 3 — Python ↔ Rust ownership migration (review's strongest
architectural critique: "Rust로 많이 옮겼다"가 아니라 "Rust를
많이 호출한다"). Stops helper-level FFI growth; lays out four
ownership stages (broker, materialization, envelope, agent
state) + non-LOC success metrics + concrete migration order.
- Each item has [done @ commit] / [partial] / [plan] /
[needs-input] status, acceptance criteria, file/code pointers.
- "Items DEPRIORITIZED" section: more agent types, more palette
commands, big LSP redesign, wrapper-level migration that doesn't
move ownership. Pause-check list before starting new work.
- "Already shipped from this batch" cross-links the four batch-3
fixes from 7793879 + the two immediate fixes from 280d105 + this
README correction.
- "Open questions" surfaces three decision points needing maintainer
input.
README.md
- Remote LSP track (#34/#35/#36/#37) all closed: move from "Open
milestones" / "next implementation" to "Completed milestones".
Phase 9 (#10, #29 diff-centric, #32 large-file streaming) now the
sole open milestone, with explicit issue annotations.
- P0.5 stabilization is closed; collapse from "Done / In progress"
bullets into a single "closed" line.
- Cross-link the new distribution plan from the execution-order line
so contributors find the prioritization reasoning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps workspace 0.6.4 → 0.6.5. Bundles the four batch-3 fixes from the
v0.6.4 macOS test pass (commits 7793879, 280d105) into a release tag
plus a focused repro checklist for the next test pass.
Includes
- 7793879 fix(0.6.5): macOS batch-3 — agent tmux, palette, hover URL,
status doc
- 280d105 chore: distribution-readiness review — plan + immediate fixes
planning/V0_6_5_REPRO.md (new)
- Narrow checklist (vs the full TEST_CHECKLIST.md): four "verify the
fix landed" steps for the batch-3 items + four "capture diag"
steps for the still-open issues (mirror-sync deep hang at
awaiting_response_dispatch, hover absolute path open silent,
Jupyter open silent launch). Includes the exact log fragments to
paste back so the next debug round starts with concrete signal.
CI on tag v0.6.5 will produce the signed bundle via the dedicated
signing subkey (master never touches the runner) and upload assets to
the release page + musl session_helper to the generic registry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>