Files
owlry/docs/RESTRUCTURE-V2.md
vikingowl a7af0e5d46 feat(v2): defer bookmarks provider (D22) — drop rusqlite dep
The bookmarks provider opened Firefox's places.sqlite directly via
rusqlite, which pulled libsqlite3-sys with its bundled feature to
compile sqlite3.c statically. In clean Arch chroots the bundled feature
kept slipping out of the resolved feature graph and the chroot build
failed at link time with every sqlite3_* symbol undefined. Inline
features = ["bundled"] on the optional dep didn't help; neither did
the explicit "rusqlite/bundled" feature wiring (commit 7569b2d).

Rather than fight Cargo's resolver further, defer the bookmarks
provider alongside the widgets per the same pattern (D20 -> D22).
Returns in a later 2.x release with a pure-Rust reader:
- Chromium's Bookmarks file is already JSON.
- Firefox exposes JSON backups under ~/.mozilla/firefox/<profile>/
  bookmarkbackups/<dated>.jsonlz4 — needs lz4 + JSON parse, no SQLite.

Removed:
- crates/owlry/src/providers/bookmarks.rs
- bookmarks module + registration in providers/mod.rs
- bookmarks feature + rusqlite dep in Cargo.toml
- ProvidersConfig.bookmarks field (existing user configs that still
  have 'bookmarks = true' silently ignore it — serde default behavior)
- :bm prefix from README's search-prefix table
- bookmarks acknowledgement in README + ROADMAP roadmap section

Cargo.lock loses rusqlite, libsqlite3-sys, fallible-iterator,
fallible-streaming-iterator, foldhash, hashlink, rsqlite-vfs,
sqlite-wasm-rs (-80 lines).

PKGBUILD: owlry-plugin-bookmarks stays in replaces= (any user who has
it installed still needs a clean upgrade), but moved from the 'folded'
comment block to the 'deferred (D20+)' block.

Docs:
- README: bookmarks removed from optional-providers list, feature
  table, search-prefix table, config example. Added to upcoming
  roadmap section alongside widgets.
- ROADMAP: 'Bookmarks return' subsection added next to 'Widget
  providers return'.
- CLAUDE.md: provider tree updated.
- docs/RESTRUCTURE-V2.md: new decision D22 with chroot-build
  rationale; task #6 plugin count 8 -> 7 -> 6.

245 tests pass with --features full (was 252; the 7 bookmarks unit
tests went with the module). Build verified locally; chroot build
should now succeed since libsqlite3-sys is no longer in the graph
at all.
2026-05-13 03:13:16 +02:00

28 KiB
Raw Permalink Blame History

Owlry v2 Restructure Plan

Status: in progress on branch v2 (cut from main @ 1caa050) Target version: owlry 2.0.0 Scope: repository collapse, C-ABI removal, runtime consolidation, CLI cleanup, single AUR package Originating issue: #5 uuctl launcher not working — closed by deletion of the C-ABI loader that caused it


0. Decisions log

All design decisions agreed before work started. These are load-bearing — do not silently change them.

# Decision Rationale
D1 Collapse to 1 AUR package: owlry 14 PKGBUILDs maintained in lockstep was the root cost of every API bump. Native plugin distribution was theoretical (no third-party shipped any).
D2 Drop the C-ABI plugin system entirely Issue #5's root cause: strict api_version != API_VERSION rejection. Compiling providers in eliminates the failure mode.
D3 Drop Rune runtime Only one user plugin (hyprshutdown) uses Rune; will be ported to Lua. 118 MB dylib for one plugin is not earned.
D4 Use Lua for config + plugins via mlua (Lua 5.4, bundled) — Phase 3+ Single language for both configuration and user extension. LuaJIT perf is irrelevant for a config language.
D5 Configs-as-plugins model owlry.provider {} in init.lua registers a runtime provider treated identically to a built-in. No separate ~/.config/owlry/plugins/ dir.
D6 Daemon as subcommand with -d short alias: owlry daemon or owlry -d Single binary; owlryd no longer exists. systemd unit invokes owlry -d.
D7 -m auto as explicit alias for the implicit no-flag default Today's accept_all behavior — all enabled providers active, tabs cycle. Pinned with an integration test so refactors can't regress it.
D8 Hard cut on TOML config in a later phase Migrator command owlry migrate-config reads existing TOML + script plugins, emits init.lua. One-shot; TOML reader deleted in the same release that ships Lua.
D9 AUR ships --features full Defaults in Cargo.toml are minimal; AUR PKGBUILD enables everything. End-user picks at runtime via config, not at install time.
D10 Runtime activation is config-driven Compiled-in ≠ enabled. Config (TOML now, Lua later) decides which providers run.
D11 Drop config_editor provider (1127 LOC) An in-launcher settings editor is replaced by editing init.lua + owlry config validate.
D12 Drop scripts provider Replaced by owlry.provider {} in Lua config (Phase 3+).
D13 Rename syspower "sys" collides mentally with "systemd". :sys kept as alias.
D14 Keep submenu protocol Used by systemd provider for service actions. Becomes a method on the Provider trait.
D15 Rename systemd unit owlryd.{service,socket}owlry.{service,socket} Consistent with single-binary collapse. PKGBUILD ships new names; old units cleaned up via replaces=() upgrade path.
D16 Submenu protocol redesign deferred to Phase 5 Today's SUBMENU:type:data string encoding works. Typed IPC variant is hygiene, not blocking.
D17 Keep the daemon Cold-start cost (XDG scan, frecency load, provider warm-up) is the real reason the daemon exists. Single-process collapse remains an option to revisit post-2.0 if profiling proves it's free.
D18 TOML reader: hard cut at 3.0.0 One migration moment. 2.x stays TOML-only; 3.0 ships Lua-only with owlry migrate-config shipping in 2.x as preview.
D19 Lua sandbox (Phase 3): filesystem read + process spawn allowed; no network by default Launcher needs to read configs and spawn commands. Network access (HTTP, sockets) requires explicit config opt-in.
D20 Widget providers (weather, media, pomodoro) on hold UI positioning unresolved. Excluded from Phase 1 conversion entirely — not pulled in, not compiled in. Revisit as a dedicated workstream after 2.0 ships.
D21 Hot-reload of init.lua on save (Phase 3) notify crate already a dep. Watch ~/.config/owlry/init.lua and any required files; re-run config eval on change.
D22 Bookmarks provider deferred too The rusqlite dep (for Firefox places.sqlite) caused libsqlite3-sys/bundled to fall out of the resolved feature graph in clean Arch chroots — every chroot build linked against system sqlite or failed with sqlite3_* undefined-symbol errors. Rather than fight Cargo's resolver, defer the bookmarks provider alongside the widgets. Returns when we wire a pure-Rust path (Firefox JSON backups under bookmarkbackups/; Chromium's Bookmarks is already JSON).

1. Target shape (post-Phase 2 — what 2.0.0 ships as)

Repo layout

owlry/
├── Cargo.toml                 -- workspace with single member
├── crates/owlry/
│   ├── Cargo.toml             -- one binary, features per provider
│   └── src/
│       ├── main.rs            -- subcommand router
│       ├── cli.rs             -- clap defs
│       ├── server.rs          -- daemon mode (was owlry-core/src/server.rs)
│       ├── client.rs          -- IPC client to daemon (UI mode)
│       ├── backend.rs         -- abstracts daemon vs local
│       ├── ipc.rs             -- Request/Response types
│       ├── filter.rs          -- ProviderFilter
│       ├── paths.rs           -- XDG paths
│       ├── notify.rs          -- desktop notifications
│       ├── theme.rs           -- CSS loading
│       ├── config/            -- config loader (TOML in Phase 1, Lua in Phase 3)
│       ├── data/              -- FrecencyStore
│       ├── providers/         -- ALL providers, each gated by feature
│       │   ├── mod.rs         -- ProviderManager, Provider traits, ProviderType
│       │   ├── application.rs (feature: app)
│       │   ├── command.rs     (feature: cmd)
│       │   ├── calculator.rs  (feature: calc)
│       │   ├── converter/     (feature: conv)
│       │   ├── power.rs       (feature: power, was sys)
│       │   ├── dmenu.rs       (feature: dmenu, moved from owlry/src/providers/)
│       │   ├── bookmarks.rs   (feature: bookmarks)
│       │   ├── clipboard.rs   (feature: clipboard)
│       │   ├── emoji.rs       (feature: emoji)
│       │   ├── ssh.rs         (feature: ssh)
│       │   ├── systemd.rs     (feature: systemd, type_id "uuctl")
│       │   ├── websearch.rs   (feature: websearch)
│       │   └── filesearch.rs  (feature: filesearch)
│       │   -- weather, media, pomodoro: deferred (D20)
│       └── ui/                -- GTK4 client
├── data/
│   ├── config.example.toml    -- shipped defaults (Phase 1)
│   └── default-config.lua     -- shipped defaults (Phase 3+)
├── systemd/
│   ├── owlryd.service         -- renamed to owlry.service in Phase 2; ExecStart=/usr/bin/owlry -d
│   └── owlryd.socket          -- renamed to owlry.socket
└── aur/owlry/PKGBUILD         -- the only AUR package

Deleted entirely

Path Reason
crates/owlry-core/ Folded into crates/owlry/
crates/owlry-plugin-api/ C-ABI gone
crates/owlry-lua/ mlua compiled in via Phase 3
crates/owlry-rune/ Rune dropped (D3)
crates/owlry-core/src/plugins/ (entire dir) C-ABI loader, runtime loader, manifest, registry, watcher, error
crates/owlry-core/src/providers/native_provider.rs C-ABI bridge
crates/owlry-core/src/providers/lua_provider.rs Old Lua bridge
crates/owlry-core/src/providers/config_editor.rs D11
crates/owlry/src/plugin_commands.rs (1296 LOC) No plugin CLI
aur/owlry-core/, aur/owlry-lua/, aur/owlry-rune/ Folded into aur/owlry/
aur/owlry-plugin-* (none yet in this repo) n/a
aur/owlry-meta-* (essentials, widgets, tools, full) Redundant
owlry-plugins/ sibling repo Archived on somegit.dev

2. CLI shape (post-Phase 1)

owlry                              launch UI, auto mode (default)
owlry -m auto                      launch UI, auto mode (explicit alias)
owlry -m <mode>                    launch UI in single-provider mode
owlry --profile <name>             launch UI with a named profile

owlry daemon                       run daemon (alias: owlry -d)
owlry dmenu [-p <prompt>]          dmenu mode (was: owlry -m dmenu)
owlry doctor                       diagnostics: providers, config, socket, runtime
owlry providers [<name>]           list providers (or show one)
owlry config validate              check config for errors
owlry config show                  print resolved effective config
owlry migrate-config               TOML → init.lua (Phase 3+)

Removed CLI surface

The entire owlry plugin ... subcommand tree (~1296 LOC in plugin_commands.rs) is deleted: list, search, info, install, remove, update, enable, disable, create, validate, runtimes, run, commands. There is no plugin installation to manage — features are compiled in.


3. Feature names + descriptions

Final naming for cargo features and corresponding provider IDs:

Feature CLI mode / prefix What it does
app :app XDG desktop applications
cmd :cmd Executables on $PATH
calc :calc / = Calculator (math expressions, e.g. = 2+2)
conv :conv Unit & currency converter (e.g. 5 ft to m)
power :power (:sys alias) Shutdown, reboot, logout, suspend, lock — was sys
dmenu (subcommand) Pipe-based selection — stdin in, selection out
bookmarks :bm Browser bookmarks (Firefox, Chromium)
clipboard :clip Clipboard history via cliphist/wl-clipboard
emoji :emoji Emoji picker (writes via wl-clipboard)
ssh :ssh SSH hosts from ~/.ssh/config
systemd :systemd (:uuctl alias) systemd user units (type_id stays "uuctl" for config compat)
websearch :web / ? Web search (DDG, Google, custom engines)
filesearch :file / / File search via fd or mlocate
weather Deferred (D20) — not in 2.0.0
media Deferred (D20) — not in 2.0.0
pomodoro Deferred (D20) — not in 2.0.0

Cargo feature groups

[features]
default = ["app", "cmd", "calc", "conv", "power", "dmenu"]
full = [
    "app", "cmd", "calc", "conv", "power", "dmenu",
    "bookmarks", "clipboard", "emoji", "ssh", "systemd",
    "websearch", "filesearch",
    # widgets (weather, media, pomodoro): deferred (D20)
]
dev-logging = []

AUR PKGBUILD builds with --features full.


4. Runtime config (Phase 1: TOML, Phase 3+: Lua)

Three orthogonal axes:

  1. Compiled in — Cargo features at build time. AUR ships full.
  2. Enabled — Config decides which providers actually run.
  3. Selected-m / --profile / Tab key narrows the active set at use time.

Phase 1 (TOML, kept temporarily)

[general]
tabs = ["app", "cmd", "calc", "conv", "power"]   # what shows in auto mode

[providers]
applications = true
commands = true
calculator = true
converter = true
power = true            # was `system`
bookmarks = false       # opt-in
clipboard = false
# ...etc

Phase 3+ (Lua, target)

local owlry = require("owlry")

owlry.set {
  theme = "apex-neon",
  width = 850, height = 650,
  tabs = { "app", "cmd", "power" },
}

owlry.providers { "app", "cmd", "calc", "conv", "power", "bookmarks", "systemd" }

owlry.provider {
  id = "hs", prefix = ":hs", tab_label = "Shutdown",
  items = function(_q)
    return {
      { name = "Lock",     command = "hyprlock" },
      { name = "Shutdown", command = "systemctl poweroff" },
      { name = "Reboot",   command = "systemctl reboot" },
    }
  end,
}

5. Phased plan

Phase 1 — Repo collapse (current)

Goal: single workspace member, single binary, TOML config still works, C-ABI gone, all built-in providers compiled in behind features.

Subtasks (tracked in TaskList):

  1. Inventory: map Provider trait + plugin shapes
  2. Workspace skeleton: collapse to single crate — move owlry-core/src/* into owlry/src/, absorb deps, remove other crates from workspace
  3. Delete C-ABI plugin systemplugins/ dir, native_provider.rs, lua_provider.rs, owlry-plugin-api crate; strip ProviderManager::new_with_config() of plugin loading
  4. Delete Rune + Lua runtime cratescrates/owlry-rune/, crates/owlry-lua/
  5. Delete config_editor + scripts providers
  6. Convert 6 plugins to native Provider impls — clipboard, emoji, ssh, systemd, websearch, filesearch. Pull source from owlry-plugins/crates/owlry-plugin-*, convert extern "C" vtable → impl Provider / impl DynamicProvider. Per-plugin mechanical work. Widgets (weather, media, pomodoro) excluded per D20; scripts excluded per D12; bookmarks excluded per D22.
  7. Wire cargo features per provider#[cfg(feature = "...")] gating; default / full feature groups
  8. Rename syspower — file, type_id (in CLI mode mapping table), :sys kept as alias, config key providers.systemproviders.power (with TOML migration shim that reads the old name)
  9. CLI restructure — new clap shape (subcommands daemon, dmenu, doctor, providers, config, migrate-config); drop entire plugin subcommand tree; daemon mode via owlry -d / owlry daemon
  10. Auto-mode integration testtests/auto_mode.rs asserts the no-flag default still surfaces results from all enabled providers and tabs match general.tabs
  11. Phase 1 final build + smokecargo check --workspace, cargo build --release, cargo test, runtime socket smoke

Phase 2 — AUR republish as 2.0.0

Goal: ship the collapsed binary to users. Issue #5 closes by virtue of the C-ABI being gone.

Steps:

  • Update aur/owlry/PKGBUILD:
    • pkgver=2.0.0
    • replaces=('owlry-core' 'owlry-lua' 'owlry-rune' 'owlry-plugin-bookmarks' 'owlry-plugin-clipboard' 'owlry-plugin-emoji' 'owlry-plugin-filesearch' 'owlry-plugin-media' 'owlry-plugin-pomodoro' 'owlry-plugin-scripts' 'owlry-plugin-ssh' 'owlry-plugin-systemd' 'owlry-plugin-weather' 'owlry-plugin-websearch' 'owlry-meta-essentials' 'owlry-meta-widgets' 'owlry-meta-tools' 'owlry-meta-full')
    • conflicts=(<same list>)
    • provides=(<same list>)
    • build() uses cargo build --release --features full
    • package() installs single binary, single systemd service+socket, default config
  • Delete the 14 other PKGBUILDs under aur/
  • git tag owlry-v2.0.0
  • Push, build, aur-publish
  • Archive owlry-plugins sibling repo on somegit.dev (read-only, history preserved)
  • Comment on issue #5: "fixed by 2.0 rewrite; the C-ABI plugin loader that produced this class of bug has been removed"

Phase 3 — Lua config core (later release: 2.1.0 or 3.0.0)

Goal: replace TOML with Lua-driven config. Hyprland-style configs-as-code.

Steps:

  • Add mlua = { version = "0.11", features = ["lua54", "vendored", "send", "serialize"] } dependency
  • Build the owlry.* Lua API surface:
    • owlry.set { ... } — global settings (theme, dimensions, tabs)
    • owlry.providers { ... } — enable list (subset of compiled-in features)
    • owlry.provider { id, prefix, tab_label, items, ... } — register a runtime provider
    • owlry.bind(key, action) — keybinding overrides
    • owlry.theme(name | { ... }) — theme selection or inline definition
  • Resolve config: ~/.config/owlry/init.lua if present; else fall back to shipped data/default-config.lua
  • owlry migrate-config subcommand: read existing config.toml (+ ~/.config/owlry/plugins/* if any) and emit equivalent init.lua
  • Wire owlry config validate and owlry config show to surface Lua errors and resolved state
  • Decision pending: hard cut TOML in 3.0.0 vs. dual-read in 2.1.0 → cut in 3.0.0

Phase 4 — Configs-as-plugins (same release as Phase 3)

Goal: Lua-registered providers are first-class. Drop the script plugin discovery dir.

Steps:

  • owlry.provider {} creates a LuaProvider that wraps the supplied Lua closures and implements the Provider trait
  • Remove the ~/.config/owlry/plugins/ filesystem discovery entirely
  • The shipped default-config.lua showcases at least one inline provider as documentation

Phase 5 — Hygiene

Goal: clean up debt that the collapse uncovered.

Steps:

  • Update CLAUDE.md — binary name (owlry, not owlry-core/owlryd), workspace layout, drop "Phase 5 pending" wording
  • Update README.md — new CLI, new install instructions, drop AUR package list
  • Refresh ROADMAP.md
  • Split files >1000 LOC:
    • providers/mod.rs — separate ProviderType / LaunchItem / ItemSource from ProviderManager
    • ui/main_window.rs — input handling, render, signal wiring as separate modules
    • providers/converter/units.rs — move data tables to a units.toml resource
  • Fix double-daemon spawn: rely on socket activation only; add flock guard in main.rs if needed
  • Make sure systemd unit name is owlry.service (or owlryd.service?) — decide before AUR ship

6. Provider-by-provider conversion notes (Phase 1.6)

Mechanical pattern for each plugin: drop extern "C" exports, PluginItemLaunchItem, opaque ProviderHandle → struct field on a Provider impl.

provider_kind-driven categorization (current state)

Plugin Kind Submenu? Per-keystroke? Notes
bookmarks DEFER D22 — rusqlite/bundled fragile in chroot; returns with a pure-Rust reader
clipboard Static yes (?) no Uses cliphist, wl-clipboard
emoji Static no no Bundled emoji data; writes via wl-clipboard
ssh Static no no Parses ~/.ssh/config
systemd Static yes no type_id "uuctl"; SUBMENU: protocol for service actions
scripts DELETE Replaced by Lua config (D12)
websearch Dynamic no yes per-keystroke; ? trigger
filesearch Dynamic no yes per-keystroke; / trigger; fd or mlocate
weather DEFER D20 — UI positioning unresolved
media DEFER D20 — UI positioning unresolved
pomodoro DEFER D20 — UI positioning unresolved

Submenu mechanism on Provider trait

Add to pub trait Provider:

/// Returns submenu actions for the given encoded data string, or empty if none.
/// Called when a user selects an item whose command field starts with "SUBMENU:".
fn submenu_actions(&self, _data: &str) -> Vec<LaunchItem> { Vec::new() }

/// Handle a plugin-defined action command (e.g. "POMODORO:start").
/// Returns true if handled.
fn execute_action(&self, _command: &str) -> bool { false }

/// Optional UI hints — replace today's hardcoded match table.
fn prefix(&self) -> Option<&str> { None }
fn icon(&self) -> Option<&str> { None }
fn position(&self) -> ProviderPosition { ProviderPosition::Normal }
fn priority(&self) -> u32 { 0 }

ProviderManager::query_submenu_actions and execute_plugin_action route to these trait methods instead of the C-ABI NativeProvider.


7. Breaking changes / migration impact

What users lose at 2.0.0

  • 14 separate AUR packages → 1 (paru handles via replaces/conflicts)
  • Ability to install a subset of plugins via AUR (still possible via cargo install --no-default-features --features ...)
  • Rune scripting (the user's hyprshutdown plugin breaks; will be ported to Lua in Phase 3, or compiled into a small custom build)
  • Dynamic plugin loading at runtime (was never used by anyone outside Owlibou)
  • ~/.config/owlry/plugins/ directory (Phase 4 — but already orphaned at 2.0.0 since Rune is gone)

What stays unchanged for users

  • Launcher behavior, keybindings, themes, search prefixes
  • Mode flags: -m app, -m systemd, -m uuctl (alias), -m power (was -m sys)
  • TOML config compatibility through 2.x — until Phase 3 hard-cut to Lua
  • Socket protocol (mostly — Request::PluginList is dropped; Submenu and PluginAction stay)
  • systemd integration (unit file invokes owlry -d instead of owlryd)

replaces=() array for aur/owlry/PKGBUILD (2.0.0)

owlry-core
owlry-lua
owlry-rune
owlry-plugin-bookmarks
owlry-plugin-clipboard
owlry-plugin-emoji
owlry-plugin-filesearch
owlry-plugin-media
owlry-plugin-pomodoro
owlry-plugin-scripts
owlry-plugin-ssh
owlry-plugin-systemd
owlry-plugin-weather
owlry-plugin-websearch
owlry-meta-essentials
owlry-meta-widgets
owlry-meta-tools
owlry-meta-full

8. Open questions — RESOLVED

All section-8 questions from the original plan have been answered. Captured as D15D21 in section 0.

Original question Resolution Recorded as
systemd unit name Rename to owlry.{service,socket} D15
Submenu protocol redesign Deferred to Phase 5 D16
Drop daemon entirely? No — cold-start cost justifies it D17
TOML reader removal timing Hard cut at 3.0.0 D18
Lua sandbox model fs read + process spawn allowed; no network by default D19
Widget positioning On hold; widgets excluded from Phase 1 D20
Hot-reload init.lua Yes (Phase 3) D21

Newly open (post-resolution)

Question Defer to Notes
Widget rework: keep the "Widget position" UI concept, redesign, or drop entirely? Post-2.0 workstream Pulled out of Phase 1 scope per D20. Decide before re-introducing weather/media/pomodoro.
owlry migrate-config shipped in 2.x as preview, or only at 3.0.0? Phase 3 planning Lean toward shipping in 2.1.0 as a preview so users can test their migrations before TOML is gone.

9. Build / verify checklist (Phase 1 acceptance)

Phase 1 is "done" when all of these pass:

  • cargo check --workspace (no warnings on default features)
  • cargo check --workspace --all-features (no warnings)
  • cargo build --release --features full
  • cargo test --workspace --all-features
  • cargo clippy --workspace --all-features -- -D warnings
  • cargo fmt --all --check
  • Workspace contains only crates/owlry member
  • crates/owlry-core/, crates/owlry-plugin-api/, crates/owlry-lua/, crates/owlry-rune/ no longer exist
  • No occurrences of owlry_plugin_api, mlua, libloading, Rune, liblua.so, librune.so outside historical docs
  • target/release/owlry runs:
    • owlry --help shows new subcommand structure
    • owlry doctor reports providers + config + socket status
    • owlry -d starts daemon; socket listens at $XDG_RUNTIME_DIR/owlry/owlry.sock
    • owlry -m systemd shows systemd units (Issue #5 fixed locally)
    • owlry (no flags) shows auto mode with results from all enabled providers, tabs match general.tabs
  • Integration test tests/auto_mode.rs passes
  • No daemon double-spawn (verify pgrep -af owlry shows one process when launched normally)

10. Working notes (mutable)

This section captures in-progress state. Update freely as work proceeds.

  • Branch: v2, cut from main @ 1caa050 on 2026-05-13
  • Checkpoints landed:
    • 163e68a — plan doc
    • 2fc976b — D15D21 resolutions
    • ae4a903 — C-ABI demolition: tasks #3/#4/#5 done in one commit
    • 1d20754 — TDD characterization pass (+36 tests)
    • 0a4a090 — Workspace collapse: owlry-core merged into owlry (task #2)
    • eb8a65f — systemd provider converted (issue #5 functional fix in v2)
    • cb2ea59 — Remaining 6 plugins converted in parallel: bookmarks, clipboard, emoji, filesearch, ssh, websearch (tasks #6 + #7)
    • d1c3270 + 1ba0a97 — sys → power rename (task #8)
    • 27e2683 — CLI subcommand restructure + doctor/providers/config/migrate-config (task #9)
    • e9f310d — auto-mode integration test (task #10)
    • c48efaa — cargo fmt pass
    • 0376abd — OWLRY_SOCKET env override (smoke-test enabler)

Phase 1 status: COMPLETE — 11/11 tasks done. Ready for Phase 2 (AUR republish as 2.0.0).

Phase 1 acceptance results:

  • cargo check --workspace (no default features) — clean
  • cargo check --workspace --features full — clean
  • cargo build --release --features full — 1m 19s
  • cargo test --workspace --features full252 tests pass (225 unit + 5 auto-mode + 14 ipc + 8 server)
  • cargo clippy --workspace --features full — only 10 style warnings (sort_by_key suggestions, all benign)
  • cargo fmt --all --check — clean after c48efaa
  • Workspace contains only crates/owlry member
  • crates/owlry-core/, crates/owlry-plugin-api/, crates/owlry-lua/, crates/owlry-rune/ removed
  • No occurrences of owlry_plugin_api, mlua, libloading, liblua.so, librune.so
  • target/release/owlry --help shows new subcommand structure
  • owlry -d starts daemon, socket binds at $XDG_RUNTIME_DIR/owlry/owlry.sock
  • owlry doctor reports config + socket + 8 provider IDs
  • owlry providers lists all registered providers; owlry providers uuctl shows the systemd provider details (id, prefix, icon, etc.)
  • Issue #5 functionally fixed: systemd provider compiled in AND verified live — owlry providers uuctl dbus returns dbus-broker + at-spi-dbus-bus from the running daemon's real systemctl --user list-units invocation. 43 user units loaded at refresh time.
  • Every provider entry point smoke-tested with proper modes parameter: 13/13 produced expected output (app→Firefox, cmd→git*, power→Shutdown, bookmarks→GitHub*, clipboard→50 entries, emoji→heart*, ssh→aur.archlinux.org, uuctl→dbus-broker, calc 2+2→4, conv 5 ft to m→1.524 m, websearch→Search URL, filesearch→file matches).
  • ⚠️ Daemon double-spawn: pre-existing PIDs 3042 + 3278 still around (not Phase 1 work; addressed in Phase 5 hygiene)

Known follow-ups (deferred, not blocking 2.0.0 ship):

  • Dynamic providers (calc, conv, filesearch, websearch) don't appear in available_providers() — pre-existing behavior matched the old impl. Worth surfacing in doctor later.
  • 10 clippy sort_by_key style warnings — Phase 5 cleanup.
  • refresh_widgets() no-op stub in backend.rs — remove when widgets are revisited (D20 work).
  • Tasks done: #1 inventory, #3 delete C-ABI, #4 delete Rune+Lua crates, #5 delete config_editor (scripts never lived in this repo)
  • Tasks remaining (Phase 1): #2 workspace collapse, #6 convert 8 plugins, #7 cargo features, #8 sys→power rename, #9 CLI subcommands, #10 auto-mode test, #11 final build+smoke
  • Stray processes from inventory phase:
    • PIDs 3042, 3278 — pre-existing owlryd (double-spawn bug; will resolve via Phase 5)
    • PID 594897 — test daemon from inventory probe; harness denied kill; resolves at next reboot or user kill
  • Hyprland exec-once for owlryd: suspected source of double-spawn; verify and remove during Phase 5
  • plugin-api-v1.0.1 git tag: still has API_VERSION = 3. Moot now — C-ABI deleted.
  • Known clippy nits (Phase 5 cleanup): ~8 sort_bysort_by_key suggestions in providers/mod.rs and currency provider. Style only, not blocking.
  • refresh_widgets() stub in backend.rs: retained as a no-op since the UI still calls it. Delete when widgets are revisited (post-2.0 per D20).
  • system provider rename to power (D13): pending Task #8. Internal type_id and config key still system. Test names still use sys_plugin.