Skip to content

feat(config): add a flock { } configuration section - #28

Closed
abeljim8am wants to merge 1 commit into
feat/default-setup-phase-0from
feat/flock-config-section
Closed

feat(config): add a flock { } configuration section#28
abeljim8am wants to merge 1 commit into
feat/default-setup-phase-0from
feat/flock-config-section

Conversation

@abeljim8am

Copy link
Copy Markdown
Owner

Stacked on #27 (feat/default-setup-phase-0). Review that one first; this PR's diff is only the Phase 1 commit.

Why

Phase 0 made the two Flock plugins configurable in one place by registering them as plugin aliases — but that place is a plugins { } alias body, which is plumbing, not a settings surface. It also means folder args are written twice (once per alias) and expressed in the plugins' wire format: root_dirs "~/src;~/work" rather than a list, devcontainers_enabled "true" rather than a bool.

Phase 1 gives Flock a real configuration section:

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

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

Ergonomics

  • Lists take multiple entries (root_dirs "~/src" "~/work") and the ;-joined form, so a value copied out of a layout still works.
  • Flags take the KDL bool (ssh true) and the quoted string (ssh "true") for the same reason.
  • Unset means "say nothing", leaving the plugin's own default in force rather than overriding it with a zero value.
  • An unrecognized key is a parse error, not a silent skip. A typo like root_dirs_typo would otherwise leave the selector mysteriously empty with nothing to point at.

Two design points worth your attention

1. The projection is computed on demand, not stored. Config::plugin_aliases_with_flock_defaults() builds it where the server consumes the aliases; Config.plugins stays pristine.

This is the important one. The stored aliases are what gets serialized back to disk — the configuration plugin writes the whole config, and the first-run wizard triggers it (I hit exactly this in #27, where the wizard wrote a config.kdl into a test fixture). Folding the projection into Config.plugins would be a one-way door: the next write copies the values into each alias body, where they outrank flock { } itself, so editing the block would silently stop having any effect. Tests pin both halves — the stored alias stays clean, and a to_string round-trip must not leak root_dirs into the plugins block.

2. FlockConfig needs to_kdl, not just from_kdl — or that same write-back silently drops the section. Covered by a round-trip test.

Deviation from the plan

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. I projected onto the aliases instead: one seam in zellij-utils that is fully unit-testable, versus threading config into the plugin thread and keeping several matching call sites consistent — where a missed seam fails silently, which is the failure mode that already bit this area twice.

The accepted cost is that a layout referencing zellij:flock-* directly gets no flock { } values and keeps whatever args it states. That is narrow now that the bundled layouts and the README all use the alias form, and it is documented in the README, the CHANGELOG, and the method's doc comment.

Deferred to Phase 2

The key to disable selector-on-startup. That behavior does not exist yet, and shipping a config key that does nothing is worse than adding it when it works.

Verification

  • cargo xtask test — 2353 passing, 0 failures. cargo xtask format --check clean.
  • 14 new tests: multi-entry and ;-joined lists, bool and string flags, unknown-key error, arg-name translation, both plugins receiving identical config, alias-body-wins precedence, empty-section no-op, projection-not-stored, write-back round-trip plus the no-leak assertion, merge precedence in both directions, and that the shipped default ships the section commented out with both aliases present.
  • Snapshot changes are purely additive (zero removed lines) — just the new empty flock field in the config debug output.
  • Checked against the built binary: with only a flock { } block in config.kdl, action dump-layout shows root_dirs "~/src;~/work", individual_dirs "~/dotfiles" and devcontainers_enabled "true" resolved onto the sidebar inside the bundled layout, which never mentions them. Ran with a pre-seeded config.kdl so the first-run wizard could not interfere — the lesson from feat(setup): make a fresh install land in Flock, not plain Zellij #27.
  • Both README snippets were run through setup --check ([CONFIG FILE]: Well defined.) rather than shipped untested.

🤖 Generated with Claude Code

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.
@abeljim8am
abeljim8am force-pushed the feat/flock-config-section branch from 5feb830 to edfcde1 Compare August 4, 2026 15:34
@abeljim

abeljim commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

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