chore: distribution-readiness review — plan + immediate fixes
External-review reading of the repo asked "ready for broad distribution?" The verdict was "strong internal alpha/beta, not yet ready for public / company-wide release" with concrete action items spanning install / packaging, platform reliability, security, and remaining performance work. Distill the actionable themes into a planning doc; land the two bits that can ship right now. planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md (new) - Numbered work items with [done] / [plan] / [needs-input] status, acceptance criteria, and cross-links to existing open issues (#32 large-file streaming, etc). - Captures: command palette tier split, default-settings safe profile, stable vs dev release channel, macOS/Windows smoke CI, platform code signing, remote-install consent flow. README + ssh_file_transport diagnostic-matrix [done] - README claimed `session_helper` was downloaded directly by the remote via curl/wget. The actual implementation has been "editor-cache download → SSH push to remote" since v0.5.x; rust/local_bridge tests explicitly assert the remote provisioning command does not contain curl/wget. Update README + the H6_remote_download diagnostic-matrix hypothesis text to match the implementation. sessions_show_dev_commands toggle [done] - New setting (default false). Gates dev / debugging palette commands behind a maintainer flag so non-maintainer users see a tighter command surface. First gated command: `Sessions: Preview Remote Agent Payload` (reads arbitrary remote command stdout, renders JSON; useful when debugging the agent envelope round-trip, distracting clutter otherwise). - 3 new tests cover the three visibility paths (default, flag-on, no-load_settings). NOTE: The original review.md was lost mid-session (rm'd in error). This plan is reconstructed from the partial content I had retained. If additional review themes were in the original, append under the "Open questions" section of the plan rather than starting a new doc. 1477 sublime tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
22
README.md
22
README.md
@@ -93,10 +93,12 @@ Example manifest:
|
||||
|
||||
**Current product policy (local vs remote):**
|
||||
|
||||
- **Remote host:** **Linux only.** The remote `session_helper` is still resolved
|
||||
the same way: the **remote** machine downloads a pre-built binary from the
|
||||
Gitea generic registry (`curl` / `wget`), keyed by Linux platform tag and
|
||||
`rust/` revision. No remote `cargo build`.
|
||||
- **Remote host:** **Linux only.** The remote `session_helper` is fetched by the
|
||||
**editor** (not the remote): `local_bridge` downloads the matching binary from
|
||||
the Gitea generic registry into the editor cache, then pushes it to the remote
|
||||
over the existing SSH session. No `curl` / `wget` runs on the remote, and no
|
||||
remote `cargo build`. Binary is keyed by Linux platform tag + workspace
|
||||
semver from `rust/Cargo.toml`.
|
||||
- **Local machine (editor side):** **Linux, macOS, and Windows** are supported for
|
||||
running Sublime + this package. For day-to-day development, treat **`local_bridge`
|
||||
as built on that machine** (`cargo build -p local_bridge`, see *Development*).
|
||||
@@ -114,11 +116,13 @@ Current behavior:
|
||||
session. Python sends NDJSON request envelopes and receives async responses via
|
||||
a background reader thread. Each request gets a unique monotonic `envelope_id`
|
||||
to prevent response mis-routing under concurrency.
|
||||
- **Download-only helper resolution:** `session_helper` is downloaded directly by
|
||||
the remote machine from the Gitea generic registry (no `cargo build` fallback,
|
||||
no local download). The binary is identified by git revision + platform tag and
|
||||
cached at `$HOME/.cache/sessions/helpers/<revision>/session_helper`. If the
|
||||
download fails, the connection fails explicitly.
|
||||
- **Editor-cache helper resolution:** `session_helper` is 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` /
|
||||
`wget` never 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 (no `cargo build` fallback).
|
||||
- **Required handshake fields:** `Handshake.remote_home` and `Handshake.arch` are
|
||||
required (no `Option`, no fallback). The bridge merges helper ensure + launch
|
||||
into a single SSH command to avoid double authentication.
|
||||
|
||||
246
planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
Normal file
246
planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# Review-driven distribution-readiness plan (v0.6.4 → v0.7+)
|
||||
|
||||
External-review reading of the repo asked: "ready for broad distribution
|
||||
yet?" Verdict was: **strong internal alpha/beta**, **not yet ready** for
|
||||
company-wide or public release. Reasons cluster into install/packaging,
|
||||
platform reliability, feature surface, security/EDR, and remaining
|
||||
performance scale items.
|
||||
|
||||
This plan distills the actionable themes into work items, splits them
|
||||
into "in this batch" vs "deferred", and records acceptance criteria so
|
||||
each item can be picked up later without re-reading the source review.
|
||||
|
||||
> The original review document was lost mid-session (rm'd in error). Items
|
||||
> here are reconstructed from the partial review I had retained. If
|
||||
> additional themes were in the original review, append them here under
|
||||
> the right section rather than starting a new doc.
|
||||
|
||||
## Status legend
|
||||
|
||||
- `[done @ <commit>]` — landed in this batch, commit ref noted.
|
||||
- `[plan]` — captured here; pick up later.
|
||||
- `[needs-input]` — needs maintainer decision before scoping.
|
||||
|
||||
---
|
||||
|
||||
## Batch landing now (v0.6.5)
|
||||
|
||||
### 1. README ↔ implementation drift on `session_helper` resolution `[done]`
|
||||
|
||||
**Issue:** README (lines ~96-99 and ~117-121) says the remote machine
|
||||
downloads `session_helper` directly from the Gitea generic registry via
|
||||
`curl`/`wget`. The actual implementation since v0.5.x downloads to the
|
||||
**editor cache** (`_ensure_session_helper_in_editor_cache`) then pushes
|
||||
via SSH (`_needs_remote_session_helper_push` /
|
||||
`_remote_session_helper_push_check_script`). Rust tests assert the
|
||||
remote provisioning command does NOT contain `curl` / `wget`
|
||||
(`local_bridge/src/lib.rs:1297-1298`). Settings comment matches the
|
||||
real flow; README does not.
|
||||
|
||||
**Acceptance:** README describes the editor-cache + SSH-push flow; the
|
||||
diagnostic-matrix `H6_remote_download` hypothesis text is updated to
|
||||
reflect "editor download → SSH push" instead of "remote curl/wget".
|
||||
|
||||
### 2. Hide developer-only `Preview Remote Agent Payload` from main palette `[done]`
|
||||
|
||||
**Issue:** Review flagged the 27-command palette as too broad for
|
||||
non-power users, and singled out `Sessions: Preview Remote Agent
|
||||
Payload` as developer-flavored — it dumps the agent invocation argv
|
||||
+ env into a scratch view, useful for debugging, distracting in the
|
||||
main palette.
|
||||
|
||||
**Acceptance:** `is_visible` returns `False` by default. New setting
|
||||
`sessions_show_dev_commands` (default `false`) flips it back on for
|
||||
maintainers. Other palette commands unaffected.
|
||||
|
||||
---
|
||||
|
||||
## Deferred — architecture / packaging tracks
|
||||
|
||||
### 3. Command palette split: core / advanced / experimental `[plan]`
|
||||
|
||||
**Theme:** 27 commands at the top level mixes "Connect Remote
|
||||
Workspace" (core flow) with "Preview Remote Agent Payload" (debug),
|
||||
"Diagnose LSP Workspace" (debug), "Register Jupyter Kernel for Active
|
||||
Python" (advanced flow). Power users like the breadth; broader users
|
||||
read it as "the product center is unclear".
|
||||
|
||||
**Proposal:** Three tiers, gated by settings:
|
||||
|
||||
- **Core** (always visible): Connect, Open Recent, Open Remote
|
||||
Folder, Open Remote Tree, Open Remote File, Reconnect, Settings,
|
||||
Open Remote Terminal, Select Python Interpreter, New Agent Session,
|
||||
Show Agent Switcher.
|
||||
- **Advanced** (visible when `sessions_show_advanced_commands: true`,
|
||||
default `true` for now, default `false` for v0.7 broad release):
|
||||
Refresh Remote Workspace, Install/Remove/Status Remote Extension,
|
||||
Open/Stop Remote Jupyter, Setup Remote Python Debugging, Register
|
||||
Jupyter Kernel, Expand Deferred Directory, Kill Agent Session,
|
||||
New Remote Terminal Pane, Kill Remote Terminal, Clear Python
|
||||
Interpreter, Open Local SSH Config.
|
||||
- **Dev** (visible when `sessions_show_dev_commands: true`, default
|
||||
`false`): Preview Remote Agent Payload, Diagnose LSP Workspace.
|
||||
|
||||
**Acceptance:** Each command's `is_visible` reads its tier's setting.
|
||||
Settings default values yield exactly the "core + advanced" set
|
||||
visible today minus the dev commands. Test: assert visibility for
|
||||
each known palette caption under the three setting-combination
|
||||
matrices.
|
||||
|
||||
### 4. Default-settings "safe profile" toggle `[plan]`
|
||||
|
||||
**Theme:** First-experience defaults are aggressive:
|
||||
`sessions_connect_auto_open_remote_folder=true`,
|
||||
`sessions_mirror_auto_refresh=true`,
|
||||
`sessions_mirror_include_files=true`. Good for power users; can be
|
||||
loud for security-sensitive orgs or huge workspaces.
|
||||
|
||||
**Proposal:** `sessions_safe_profile` boolean. When `true`, force
|
||||
`sessions_mirror_auto_refresh=false`,
|
||||
`sessions_mirror_include_files=false`,
|
||||
`sessions_connect_auto_open_remote_folder=false`,
|
||||
auto-deepen depth=1, mirror_max_entries=300, mirror_max_dir_fanout=50.
|
||||
Document in SECURITY.md as the recommended default for orgs running
|
||||
Sessions across many workstations.
|
||||
|
||||
Don't flip the master defaults yet — too disruptive. Add the toggle,
|
||||
document it, then in v0.7 consider flipping the default once the
|
||||
"broad distribution" track is cleared.
|
||||
|
||||
**Acceptance:** New setting + override layer that takes precedence
|
||||
over individual mirror caps when set. New SECURITY.md row in the
|
||||
"deployment guidance" section. Tests: load with toggle on,
|
||||
`SessionsSettings.from_loaded()` reflects the conservative caps.
|
||||
|
||||
### 5. Stable vs dev release channel `[plan]`
|
||||
|
||||
**Theme:** v0.6.0 → v0.6.4 in ~36 hours with several
|
||||
cancelled/failed CI runs along the way. Internal iteration speed is a
|
||||
feature, but external readers see a "fast-changing, still shifting"
|
||||
product. Public users want a calm channel.
|
||||
|
||||
**Proposal:**
|
||||
- Tag protocol: `v0.X.Y` continues to be the hot iteration channel
|
||||
(default for `git fetch`, what CI publishes per push).
|
||||
- New: `vX.Y-stable` rolling tags that move forward when an internal
|
||||
test pass on macOS + Windows + Linux completes against a `v0.X.Y`
|
||||
candidate. Release page links the latest stable tag separately.
|
||||
- Documentation lists the stable tag as the recommended fetch for
|
||||
non-maintainer users.
|
||||
|
||||
**Acceptance:** New scripts/`promote_stable.py` that takes a `vX.Y.Z`
|
||||
tag and force-updates `vX.Y-stable` → that commit (signed). Release
|
||||
asset cross-link in the Gitea release notes for the unstable tag
|
||||
points to the matching stable tag (or "no matching stable yet").
|
||||
SECURITY.md verification command updated to use the stable tag.
|
||||
|
||||
### 6. macOS / Windows smoke CI `[plan]`
|
||||
|
||||
**Theme:** Repository CI is single-platform (`ubuntu-latest`). Code
|
||||
explicitly targets Win/macOS (CREATE_NO_WINDOW threading, macOS
|
||||
PersistentBroker, etc.). External users can't verify the supported
|
||||
platforms actually pass CI on those platforms.
|
||||
|
||||
**Proposal:** Add two cheap smoke jobs (no full test suite — just
|
||||
"does it build + does the import smoke pass"):
|
||||
|
||||
- `cargo build --manifest-path rust/Cargo.toml -p local_bridge -p sessions_native`
|
||||
on `macos-latest` and `windows-latest`.
|
||||
- `python -m compileall -q sublime` and the runtime-import smoke test
|
||||
on the same matrix.
|
||||
|
||||
Skip the full pytest suite there (Windows runners are slow and the
|
||||
real coverage stays on Ubuntu). The smoke gate just answers "does it
|
||||
load on those platforms".
|
||||
|
||||
**Acceptance:** New `.gitea/workflows/cross-platform-smoke.yml` (or
|
||||
add jobs to the existing `ci.yml`) that runs on PR + main. Document
|
||||
the matrix in CONTRIBUTING.md. Failures block merge.
|
||||
|
||||
### 7. Platform code-signing (Apple Developer ID, Windows Authenticode) `[plan]`
|
||||
|
||||
**Theme:** SECURITY.md admits binaries are unsigned (just GPG +
|
||||
checksum). For corporate / public distribution this is insufficient —
|
||||
macOS Gatekeeper still raises "unidentified developer" warnings on a
|
||||
release-bundle binary; Windows SmartScreen does the same.
|
||||
|
||||
**Proposal:** Add per-platform signing pipelines, gated on the
|
||||
existence of org-level credentials:
|
||||
|
||||
- macOS: notarize + staple via Apple Developer ID. Requires an Apple
|
||||
Developer Program membership ($99/yr) and `xcrun notarytool` access.
|
||||
Sign `local_bridge`, `session_helper`, `libsessions_native.dylib`.
|
||||
- Windows: Authenticode sign via an EV code-signing cert (DigiCert et
|
||||
al., ~$500/yr). Sign `local_bridge.exe`, `session_helper.exe`,
|
||||
`sessions_native.dll`.
|
||||
- Both pipelines lift the credential from CI secrets at signing time;
|
||||
the credentials never enter a contributor workstation.
|
||||
|
||||
`[needs-input]`: budget approval for the certs + Apple membership.
|
||||
Without those, this stays planned but not actionable.
|
||||
|
||||
**Acceptance:** Two new CI workflow steps (one per platform) that run
|
||||
after the existing `cargo build --release` on the matrix runner.
|
||||
Outputs are signed binaries that go into the release asset bundle
|
||||
alongside the GPG signature. SECURITY.md updated to "platform
|
||||
signature + GPG signature" dual-trust verification.
|
||||
|
||||
### 8. Remote install consent flow `[plan]`
|
||||
|
||||
**Theme:** Managed remote-extension catalog includes
|
||||
`curl ... | bash` (Claude Code), `npm install -g @openai/codex`,
|
||||
`pip install --user` (Jupyter, debugpy, pyright). The product crosses
|
||||
the line from "remote code editor" to "remote tool installer" — every
|
||||
install runs commands on the user's remote workstation under their
|
||||
SSH identity. Power users want this; security/IT teams push back.
|
||||
|
||||
**Proposal:** Three-tier install gating:
|
||||
|
||||
- New setting `sessions_remote_extension_install_enabled` (default
|
||||
`true` today; switch to `false` in v0.7 broad-release default).
|
||||
- When `false`, the "Install Remote Extension" command shows a
|
||||
one-shot consent dialog naming the exact commands that will run on
|
||||
the remote, with "Run once" / "Always allow on this host" / "Never"
|
||||
buttons. "Always allow" sets a per-host flag in `workspace_state`.
|
||||
- "Never" leaves the catalog visible (so users see what's available)
|
||||
but greys out the install button and hints at the setting toggle.
|
||||
|
||||
**Acceptance:** New setting + per-host opt-in registry. Existing
|
||||
install flow gates on it. Tests: install command refused when setting
|
||||
off + host not opt'd in; install proceeds when opt'd in. SECURITY.md
|
||||
gains a "remote command surface" appendix listing every install
|
||||
command + what it touches.
|
||||
|
||||
### 9. Large-file hydrate streaming (open issue #32) `[plan]`
|
||||
|
||||
**Theme:** Current hydrate has a small-file fast path; large or
|
||||
high-latency files block the UI thread for the duration of the SSH
|
||||
fetch. Issue #32 wants progressive streaming.
|
||||
|
||||
**Proposal:** Track on existing issue; not in scope for this batch.
|
||||
Note here as "review-acknowledged".
|
||||
|
||||
**Acceptance:** Cross-link to issue #32 in SHIPPED.md once landed.
|
||||
|
||||
### 10. Diff-centric change review workflow `[plan]`
|
||||
|
||||
**Theme:** Open issue. Agent flow surfaces edits but there's no "show
|
||||
me what the agent / I changed in this session, diff-style" view.
|
||||
|
||||
**Proposal:** Out of scope here. `agent_change_badge.py` exists; the
|
||||
`file/watch` driver is the missing piece (already documented as a
|
||||
v0.7 limitation in TEST_CHECKLIST §9).
|
||||
|
||||
**Acceptance:** v0.7 follow-up.
|
||||
|
||||
---
|
||||
|
||||
## Open questions / partial review recovery
|
||||
|
||||
- The original review.md may have called out additional items the head
|
||||
preview did not capture (lost lower paragraphs). If you (Myeongseon)
|
||||
paste the original back, append themes here under section 11+ rather
|
||||
than restarting a new doc.
|
||||
- The "Phase 9 — Quality Gates & Scale" milestone referenced in the
|
||||
review presumably ties to these items 4 / 5 / 6 / 9; cross-link
|
||||
when the milestone is reopened.
|
||||
@@ -177,6 +177,14 @@
|
||||
}
|
||||
],
|
||||
|
||||
// Show developer / debugging commands in the main palette. Default ``false``
|
||||
// hides ``Sessions: Preview Remote Agent Payload`` (and any future
|
||||
// dev-flagged command). Maintainers can flip this to ``true`` in
|
||||
// Packages/User/Sessions.sublime-settings to surface them. See
|
||||
// ``planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md`` § "Command palette
|
||||
// split" for the broader core / advanced / dev tier plan.
|
||||
"sessions_show_dev_commands": false,
|
||||
|
||||
// Optional remote extension install/remove catalog (command palette install/remove/status).
|
||||
// When this list is missing, invalid, or [], defaults are merged in code (bash -lc
|
||||
// scripts: pip/ensurepip/get-pip fallbacks for Pyright/Ruff; rustup for rust-analyzer).
|
||||
|
||||
@@ -3386,6 +3386,28 @@ def _remote_extension_exec_failure_detail(
|
||||
class SessionsPreviewRemoteAgentPayloadCommand(sublime_plugin.WindowCommand):
|
||||
"""Fetch a remote agent preview envelope and render it in an output panel."""
|
||||
|
||||
def is_visible(self) -> bool:
|
||||
"""Hide from the main palette unless the dev-commands toggle is on.
|
||||
|
||||
This is a developer / debugging command — it reads an arbitrary
|
||||
remote command's stdout and renders the JSON payload in a scratch
|
||||
view. Useful when debugging the agent envelope round-trip;
|
||||
distracting clutter for non-maintainer users. Gate behind
|
||||
``sessions_show_dev_commands`` (default ``false``); maintainers
|
||||
flip the flag in their User-level Sessions.sublime-settings.
|
||||
"""
|
||||
load_settings = getattr(sublime, "load_settings", None)
|
||||
if not callable(load_settings):
|
||||
return False
|
||||
try:
|
||||
stored = load_settings("Sessions.sublime-settings")
|
||||
except Exception:
|
||||
return False
|
||||
getter = getattr(stored, "get", None)
|
||||
if not callable(getter):
|
||||
return False
|
||||
return bool(getter("sessions_show_dev_commands", False))
|
||||
|
||||
def run(
|
||||
self,
|
||||
remote_command: str = "",
|
||||
|
||||
@@ -1313,7 +1313,10 @@ def _bridge_diagnostic_hypothesis_catalog() -> list[dict[str, str]]:
|
||||
{
|
||||
"id": "H6_remote_download",
|
||||
"rust_events": "bridge.rust.ensure_remote_helper_*",
|
||||
"meaning": "Remote helper download via curl/wget; revision cache check.",
|
||||
"meaning": (
|
||||
"Editor-cache download + SSH push of session_helper; "
|
||||
"revision cache check on the remote (no curl/wget runs there)."
|
||||
),
|
||||
},
|
||||
{
|
||||
"id": "H7_python_rust_id",
|
||||
|
||||
@@ -1322,3 +1322,44 @@ def test_probe_remote_extension_installed_pyright_fallbacks_to_cli(
|
||||
assert commands._probe_remote_extension_installed(context, spec) is True
|
||||
assert _remote_extension_sh_c_contains(calls[0], "pyright-langserver")
|
||||
assert _remote_extension_sh_c_contains(calls[1], "pyright --version")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SessionsPreviewRemoteAgentPayloadCommand.is_visible — palette gating.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _install_fake_load_settings(monkeypatch, value: object) -> None:
|
||||
"""Patch ``sublime.load_settings`` so the command sees a sentinel value."""
|
||||
|
||||
class _StoredSettings:
|
||||
def get(self, key: str, default=None):
|
||||
assert key == "sessions_show_dev_commands"
|
||||
return value
|
||||
|
||||
monkeypatch.setattr(
|
||||
commands.sublime,
|
||||
"load_settings",
|
||||
lambda _name: _StoredSettings(),
|
||||
raising=False,
|
||||
)
|
||||
|
||||
|
||||
def test_preview_remote_agent_payload_hidden_by_default(monkeypatch) -> None:
|
||||
_install_fake_load_settings(monkeypatch, False)
|
||||
cmd = commands.SessionsPreviewRemoteAgentPayloadCommand(FakeWindow())
|
||||
assert cmd.is_visible() is False
|
||||
|
||||
|
||||
def test_preview_remote_agent_payload_shown_when_dev_flag_on(monkeypatch) -> None:
|
||||
_install_fake_load_settings(monkeypatch, True)
|
||||
cmd = commands.SessionsPreviewRemoteAgentPayloadCommand(FakeWindow())
|
||||
assert cmd.is_visible() is True
|
||||
|
||||
|
||||
def test_preview_remote_agent_payload_hidden_when_load_settings_unavailable(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(commands.sublime, "load_settings", None, raising=False)
|
||||
cmd = commands.SessionsPreviewRemoteAgentPayloadCommand(FakeWindow())
|
||||
assert cmd.is_visible() is False
|
||||
|
||||
Reference in New Issue
Block a user