Skip to content

Fix/remote bridge diagnostics - #34

Closed
abeljim wants to merge 10 commits into
abeljim8am:mainfrom
WooliSoft:fix/remote-bridge-diagnostics
Closed

Fix/remote bridge diagnostics#34
abeljim wants to merge 10 commits into
abeljim8am:mainfrom
WooliSoft:fix/remote-bridge-diagnostics

Conversation

@abeljim

@abeljim abeljim commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

abeljim8am and others added 10 commits July 28, 2026 12:07
A fresh install currently behaves exactly like upstream Zellij — the sidebar,
selector and agent status are all opt-in through hand-authored KDL. Records the
gaps and a five-phase plan for closing them, with the locked decisions: bare
flock opens the selector, Super s is the selector key, no filesystem
auto-discovery, and no dependency on any external config manager.
Everything that distinguishes Flock — the sidebar dock, the project selector,
agent status — was opt-in through hand-authored KDL, so `flock` with no config
was byte-for-byte upstream `zellij`. Phase 0 of the out-of-the-box plan:

- default_layout ships as "flock" instead of being unset, so the sidebar is
  docked on startup.
- Super s opens the project selector. Super is swallowed by some terminals
  before Flock sees it; documented, with the rebind.
- flock-selector and flock-sidebar are registered as plugin aliases, and the
  bundled layouts now reference those aliases instead of restating args. Folder
  args are therefore stated once in config.kdl and reach the layouts, the
  keybinding, and each project session's sidebar alike.
- Sessions created from the selector default to the `flock` layout. They
  previously got Zellij's `default`, leaving a project opened *through* Flock
  with no sidebar and no way back to the selector.

The alias indirection removes an existing bug class by construction rather than
by discipline: a keybinding whose arg set disagreed with the layout's used to
miss the running selector and launch a second one (see the subset-match test in
plugins/plugin_map.rs). With one source of truth the sets cannot disagree.

Because the alias is resolved late and silently — an unresolved alias just loads
no plugin and renders an empty dock — both bundled layouts get a test asserting
they resolve against the shipped default config.

BREAKING: a custom ~/.config/flock/layouts/default.kdl is no longer loaded on
startup, since resolution now looks for the name "flock". Set
default_layout "default" to restore it, or rename the file to flock.kdl. Pinned
by tests in both directions.
Shipping `default_layout "flock"` was the wrong lever. Setting that option
skips the `layouts/default.kdl` lookup entirely, so anyone who had written their
own default layout silently stopped getting it — a breaking change bought for no
benefit.

Make the flock layout a startup *fallback* instead. `default_layout` goes back
to unset, and the fallback applies only when the caller named no layout at all
*and* the user has no `default.kdl` of their own:

- user has layouts/default.kdl  -> their layout (no dock)
- user has none                 -> built-in flock layout (sidebar docked)
- default_layout "default"      -> plain upstream Zellij chrome

The rule lives in two places that must agree — LayoutInfo::from_config, which
decides what the session records it started from, and the loader in layout.rs,
which reads the KDL. Both key off data::FALLBACK_BUILTIN_LAYOUT and check the
same extensionless-then-.kdl candidates, so they cannot disagree about whether
a user file exists. Tests cover all three rows above.

Found by checking the built binary rather than trusting the unit tests: a fresh
config dir with a default.kdl still came up with the dock. Two traps worth
recording, both now noted in the plan doc — the client re-derives LayoutInfo
from config_options.layout_dir (unset here) rather than the config-dir-derived
path, and on a config dir with no config.kdl the first-run setup wizard both
overrides the layout and writes a config.kdl into that directory, which
pollutes any fixture you point it at.
One place to tell Flock where your projects are and which remote providers to
offer, instead of restating folder args on every plugin that needs them:

    flock {
        root_dirs "~/src" "~/work"
        individual_dirs "~/dotfiles"
        devcontainers true
        ssh true
    }

The values are merged in *underneath* the flock-selector and flock-sidebar
aliases, so the selector, the Super s keybinding and the sidebar in every session
derive from one source, while anything stated on a plugin or in a layout still
wins and can opt out.

Config names read better than the plugin args they translate to —
`devcontainers true` rather than `devcontainers_enabled "true"`, and a real list
rather than a `;`-joined string. Both spellings are accepted for lists and flags
so a value copied out of a layout works, and an unrecognized key is a parse error
rather than a silent skip, since a typo would otherwise leave the selector
mysteriously empty with nothing to point at.

Two design points worth the reviewer's attention:

The projection is computed on demand by
Config::plugin_aliases_with_flock_defaults() and deliberately *not* folded into
Config.plugins. The stored aliases are what gets serialized back to disk — the
configuration plugin writes the whole config, and the first-run wizard triggers
it — so baking the values in would be a one-way door: the next write copies them
into each alias body, where they outrank `flock { }` itself, and editing the
block silently stops having any effect. A test pins both halves of that.

FlockConfig therefore also needs to_kdl, or the section is dropped by that same
write-back. Tested by round-tripping through Config::to_string.

The plan called for injecting at plugin-load time in zellij-server so a layout
naming zellij:flock-selector directly would also pick the section up. Projecting
onto the aliases accepts that gap — narrow now that the bundled layouts and the
README all use the alias form, and documented in the README and CHANGELOG — for
one seam in zellij-utils that is fully unit-testable.

Verified against the built binary as well as in unit tests: with only a
`flock { }` block in config.kdl, dump-layout shows root_dirs, individual_dirs and
devcontainers_enabled resolved onto the sidebar inside the bundled layout, which
never mentions them.
…pick`

Bare `flock` opened a fresh shell in a randomly-named session — the same thing
plain zellij does. It now opens the project selector: pick a project and it starts
or switches to that project's session. `flock pick` (alias `p`) does it on
demand, which matters when the default is turned off.

The picker runs in a single fixed session named flock-selector, so reaching for it
repeatedly lands in the same session rather than accumulating throwaway ones.

Implemented by rewriting the request into the `attach --create` form and letting
that branch run, rather than adding a parallel create path. That branch already
resolves live / dead-but-resurrectable / absent correctly. The alternative was a
trap: a hand-rolled create calls assert_session_ne, which *exits the process*
when the name exists as a dead session, so bare `flock` would have begun failing
outright the first time a stale picker snapshot existed.

Anything that names what it wants is left alone — `--session`, `--layout`,
`attach`, any other subcommand, `session_name` in the config, and reconnects
after a detach. `flock options ...` counts as bare, since it only sets overrides
on an otherwise ordinary startup. `flock { selector_on_startup false }` restores
the old behavior wholesale and still leaves `flock pick` available; that key is
consumed by the CLI, so it is deliberately excluded from the plugin projection.

attach_to_session needed no handling: it is only consulted inside the
`session_name` branch, which selector mode already declines to touch. Confirmed
by reading that branch rather than assuming.

The bundled flock-selector layout is now the picker as the session's only pane.
It floated over a shell with `tail -f /dev/null` underneath as a keepalive,
guarding against an interactive shell exiting during its own startup and tearing
the session down before the plugin finished loading. A plugin pane cannot exit,
so both the race and the sacrificial process are gone — and a tiled picker pane
is what the author's own daily layout already used. Tests pin that no pane in
that layout is a shell, and that the layout's session_name still matches the
constant the CLI creates the session under.

The first-run setup wizard is deliberately not special-cased; it floats over the
picker on a brand-new install, which becomes a coherent funnel once Phase 3 makes
the empty project list actionable.

Verified against the built binary in a pty for every branch: bare flock creates
the picker session (session metadata confirms one tiled pane running
flock-selector, with root_dirs arriving from the flock { } section), attaches
without error when it is already live, recovers it from a killed state rather
than aborting, declines to open it under selector_on_startup false, and opens it
anyway for an explicit `flock pick`.
Flock deliberately does not go hunting for projects on its own, so on a fresh
install the empty project list is the first screen a new user sees. It said
" no project folders configured" and stopped there — a status line reporting
itself, with no way to act on it.

It is now the setup instructions:

     no project folders configured
     add them to your config.kdl:
         flock {
             root_dirs "~/src" "~/work"
         }
     then reopen the selector

The configured-but-empty state got the same treatment. " no projects found"
alone gives no way to spot the common misconfiguration, so it now names it:
root_dirs is scanned one level deep, meaning a root pointed at a project rather
than at the folder containing it finds nothing, and individual_dirs is the option
for a folder that is itself a project.

Both blocks fall back to a single line that still names the fix when the pane is
too short for the full form — a truncated block would trail off mid-snippet, which
is worse than a compact line. A search with no matches stays a plain status line;
mid-search is not the moment for setup instructions.

The planned in-app "add a project folder" action is deliberately not here. The
only way a plugin can persist to config.kdl is reconfigure, which rewrites the
whole file from the serialized config — it backs the old one up and prepends a
pointer to the backup, but comments and formatting do not survive in the live
file. It also needs a new Reconfigure permission, a one-time dialog for every
existing user, and for a declaratively-managed config (nix, dotfiles, chezmoi) it
would replace the symlink and break the manager. Paying that to save one paste, on
a file the user owns and that we document with comments, is the wrong trade.
Recorded in the plan doc as revisitable only if the flock { } block can be patched
surgically.

Verified by replaying the pty output of a real session onto a grid, so the block
was checked as rendered rather than as asserted: each row is separately
cursor-positioned, so the multi-line text is not contiguous in the byte stream and
a naive substring check on the whole phrase misses it.

The regenerated flock-selector.wasm is included because release builds embed
assets/plugins rather than target/; the other fourteen plugin assets also rebuilt
(different toolchain than whoever last committed them) and were reverted, per
AGENTS.md.
`setup --check` listed the directories Flock searches but not what it resolved,
so the questions that actually go wrong needed a live session and a dump-layout to
answer: which layout won, whether the flock { } section reached the plugins, and
whether bare flock will open the selector. It now reports:

    [STARTUP LAYOUT]: built-in "flock"
    [SELECTOR ON STARTUP]: yes
    [PROJECT ROOT DIRS]: "~/src", "~/work"
    [PROJECT INDIVIDUAL DIRS]: none
    [REMOTE PROVIDERS]: devcontainers, ssh
    [FLOCK PLUGIN ALIASES]: flock-selector, flock-sidebar

[STARTUP LAYOUT] distinguishes a built-in from one of the user's own layout files
by path, so "why am I not getting my default.kdl" is answerable without starting
a session — the exact thing that made verifying the layout-fallback work awkward.

Configuration that silently does nothing is the recurring failure mode in this
area, so the report is deliberately loud rather than terse in three cases: no
project folders configured (with the consequence and the fix), the selector opted
out of startup (naming `flock pick` as the way back), and a missing flock plugin
alias, which makes the whole flock { } section inert for that plugin.

That last case cannot be reached by editing config.kdl — alias merging can
override an entry but never remove one — but it is reachable through the public
plugin_aliases_with_flock_defaults, so it is reported rather than left to be
discovered, and unit-tested rather than left as dead code.

Completes the out-of-the-box plan: the README rewrite and the documented
flock { } block in default.kdl landed in the earlier phases, verified here rather
than assumed.
- pin a selector session layout to the layout dir only when the file is
  really there, so a bare built-in name like "flock" survives to the new
  session and resolves to the bundled asset instead of silently falling
  back to the plain default layout on fresh installs
- drop `pick`'s `p` alias, which collided with `plugin`'s pre-existing one
  and broke `flock p -- <url>`
- answer instead of panicking when bare `flock` runs inside the picker
  session itself
- make `setup --check` read folders, providers and session layout from the
  merged alias projection the selector actually receives, not the
  `flock { }` section alone
- correct the empty-state hint ("start flock again", not "reopen the
  selector") and truncate overflowing rows to the pane width instead of
  wrapping into the row below

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@abeljim

abeljim commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by mega PR #35

@abeljim abeljim closed this Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants