cli: reinstate felis ssh <dest>, drop the all-mirror verbs and the --to-* destination flags #277
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#277
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Reinstate
felis ssh <dest>as the verb that re-points the current window at a remote daemon, makewindow retargetthe local-only form with a positional destination, and remove the all-mirror verbsand the
--to-*destination flags. Nothing is tagged (git tagis empty, no Forgejo release), so thesurface is redesigned in place rather than deprecated.
Decision
window retargetacts on the frontend attachment the command runs in(
session-lifecycle.md"Attached: one or more same-user clients subscribe to grid updates and sendinput"). The session never moves; the window re-attaches elsewhere. That reading stands.
felis ssh <dest>is the SSH form of that verb. "ssh somewhere" is the phrase a user reachesfor, and its everyday meaning is "point this terminal at that host", not "open a new window there".
The rejected-alias record (
docs/explanation/architecture/control-surfaces.md:546-552) issuperseded. Its two objections do not survive the new shape: the clap objection existed only because
the alias shared
window retarget's parser (--ssh-argcould notrequirea destination onespelling supplied positionally); a separate subcommand with a required positional destination makes
--ssh-argunconditional. The "one destination, three spellings" objection disappears because--to-hostis removed: the SSH destination has exactly one spelling.sessions switch-allandsessions retarget-allare removed. They are the pre-B-9 broadcastkept under a new name when the per-window default landed (
97a7a755: "The broadcast keeps itsbehavior under …"). No consumer or scenario was ever recorded for moving every mirror at once, and the
cross-carrier form does not even preserve what it claims to move: with a
Createlanding eachwindow dials independently and lands on its own fresh session, so the mirrors come apart on arrival.
Principle 1, second Test: "if no real consumer needs it yet, the terminal does not implement it."
The one scenario the history names, "a script sweeping several sessions onto another host"
(
f513f60d), is the rationale of a test pin, not a consumer; such a sweep is a loop overwindow retarget --from <id>per session. The bridge'ssessions.switchparameterall(
cli_bridge.rs:723,:1203-1229,docs/reference/cli.md:563,schemas/felis-bridge-v1.schema.json)is the same capability under another spelling and goes with the verbs.
The removal earns no decision record in the docs: doc-cascade §3 "Default to no record" applies, and the
why lives in the commit body and the
CHANGELOG.mdRemoved entry. No Revisit if line is written; §3permits one, the maintainer chose not to record one. Decided 2026-09-14.
--to-host/--to-socketare removed. Theto-prefix existed only to keep the verb'sdestination from reading as the root
--host/--socket(control-surfaces.md:554). With the SSHdestination positional on
felis ssh, the local destination becomes positional onwindow retargetand the prefix has nothing left to disambiguate.
Resulting grammar
<dest>is the OpenSSH destination, verbatim (user@host,ssh://…, a config alias). Required.<socket-path>is a local daemon socket. Absent means the default local daemon: the way home afterfelis ssh, spelled as the absence of a choice (the existing "no--localflag" rule stays).--ssh-arglives onfelis sshonly; norequires, no post-parse refusal.felis --host a ssh b) stays a usage error; that rule is unchanged.felis ssh devbox(re-point this window) andfelis --host devbox(open a new window there) aredifferent verbs for different operations; the reference and the how-to must show them side by side.
Wire
SwitchScope.all(felis.proto:1356) loses its producers (CLI verbs and the bridge'sall) and goes withthem: a wire variant nothing can reach is not a capability. Decided 2026-09-14:
OpsSwitch.scopeis malformed and is refused at decode (convert/ops.rs:204-214today mapsabsence to
All). Absence must never initiate movement, andDefaultwould silently turn anunversioned request into a one-window move. Add the decode-rejection test beside the other hand-built
wire rejections in
convert/.reserved, never recycled (docs/reference/ipc.mdrule on retired numbers).just proto, thenjust proto-compatwith abase: <merge-base sha>line and its why incrates/felis-protocol/proto/BREAKING.md. Not a minor bump.docs/reference/ipc.md:1539,preface.rsMINOR_LEDGER) is rewritten todescribe the current design:
SwitchScopeisDefault/Attachment(id); a peer that sends no scope isrefused. The
check_scope_minorgates in the CLI and bridge follow.session_task.rsand its tests go with it.Cascade
Code (
extend-ipc):crates/felis-cli/src/cli_bridge.rs: drop theallparameter from bothsessions.switchparse paths andthe allowed-parameter table;
cli_schema.rsSessionsSwitchParams.all; the"all": truefixture incrates/felis-cli/tests/schema_fixtures.rs; bridge tests;just schemaregeneratesschemas/felis-bridge-v1.schema.json;docs/reference/cli.md:563follows.NoInputOwnerdiagnostics (cli_sessions.rs:1583-1586,cli_bridge.rs:1746-1748) stop recommendingthe
-allform /allparameter and name--attachment/attachmentalone; pin the new text in tests.check_scope_minorincrates/felis-client-core/src/connector.rs:199-210andSWITCH_SCOPE_FIELDSincrates/felis-protocol/src/minor.rs:191-197both treatAllas the minor-0representation; with
Allgone every scope requires minor 2, and both gates plus their tests change.crates/felis-cli/src/main.rs,cli_sessions.rs: add theSshsubcommand, dropRetargetAll,SwitchAll,RetargetCarrierFlags(to_host,to_socket), make thewindow retargetdestinationpositional;
RETARGET_ALL_VERBand the shared-grammar testretarget_all_speaks_the_window_retarget_grammargo.cli_completions.rs(fish/zsh/bash tokens forretarget-all,switch-all,--to-host),cli_doctor.rs, the help snapshotevery_visible_help_page_is_snapshotted.snap,tests.rs,crates/felis-cli/tests/cli_sessions.rs.crates/felis-daemon/src/serve/session_task.rs: the all-mirror push path and its tests(
all_mirror_retarget_push_reaches_only_window_subscribersand siblings).crates/felis-protocol/proto/felis.proto(SwitchScopeAll,reserved 3),messages.rs,convert/ops.rs, regeneratedfelis.v1.rs,proto/BREAKING.md,preface.rsledger;docs/reference/ipc.md(minor-2 row,SwitchScopeshape) follows.Docs (
doc-cascade):docs/explanation/architecture/control-surfaces.md: rewrite thewindownamespace section(
:540-575,:625-634) to describe the current design only, with thefelis sshrationale and noreference to the alias, the
-allverbs, or theto-prefix. History is not doc content (§4).docs/reference/cli.md(:92,:200-201,:236-237,:263-264,:432-452,:478,:492,:508),docs/reference/control-surfaces.md,docs/reference/ipc.md,docs/reference/keybindings.md.docs/how-to/attach-over-ssh.md,docs/how-to/drive-a-session-beside-its-window.md.skills/felis/SKILL.md(product-shipped; the retarget verbs move from "surfaces this skill does notcover" or stay there, but the spelling it names must be the new one),
README.mdif it names any ofthe verbs.
CHANGELOG.mdunder[Unreleased]: Addedfelis ssh <dest>; Changedwindow retarget [<socket-path>];Removed
sessions switch-all,sessions retarget-all,--to-host,--to-socket. The dated[0.1.0]section is left as written even though the tag is not pushed.
retarget-all,switch-all,to-host,to_host,to-socket,to_socket,felis ssh,all-mirror,SwitchScopeAll,SwitchScope::All,-alland"all"incrates/felis-cli/src,across
docs/,skills/,crates/,README.md.Acceptance
felis ssh devboxre-points the invoking window at devbox's daemon;felis window retargetbrings it back.felis sshwith no destination is a clap usage error (exit 2);felis window retarget --to-host x,felis sessions retarget-all,felis sessions switch-allare unknown to clap.docs/,skills/, orREADME.mdnamesretarget-all,switch-all,--to-host,--to-socket, or the bridgeallparameter;control-surfaces.mddescribes the two retarget verbs as theyare and carries no rejection record or Revisit if for the removed forms.
OpsSwitchwith noscopeis refused as malformed, pinned by a decode test;just proto-compatpasseswith the
BREAKING.mdacknowledgment.just checkpasses; the help snapshot is regenerated, not patched.Related: #275 (the split-daemon bug that surfaced this discussion; independent of it).