yog 0.0.1

yog: a balls-oriented session manager for lernie loops (egui frontend)
Documentation
yog-0.0.1 has been yanked.

yog

yog is the desktop manager for lernie loops: an egui/eframe desktop window that browses every lernie workspace, surfaces what needs attention, and drives the loop lifecycle — start work from nothing, a path, or a ball, message or stop agents, assign and close balls — over the lernie and bl command-line tools.

It is its own package (mudbungie/yog), deliberately outside the lernie and balls workspaces: both ship as composable components and yog composes on top of them. It takes no Cargo dependency on either today — it spawns the lernie and bl binaries and reads the workspaces' git repos directly. The intended direction is a fuller GUI with direct library dependencies on both; lernie and balls take neither back.

Architecture

docs/DESIGN.md is the authority — the state inventory, the attention model, the write paths, the module map, and the line budgets live there, and it is kept in sync with the code. This section is only the map.

yog is a stateless renderer: every frame is a pure function of on-disk state at that tick, so two instances against the same repos converge with no coordination. The only durable, yog-owned state is ui.json (pins, collapse overrides, attention watermarks — DESIGN §4.1) and the ops.jsonl action log (§4.2); everything else is derived from disk. The code is split the house way: pure view-model modules with no egui — the per-tick git_tree, the nav roster, attention, projects/binding, start, ui_state, the inspector VMs (transcript, steps_view, jsonview, inboxview, budgets) composed by the tested inspector tab dispatch, and config_edit — fronted by thin egui glue in shell (coverage-excluded), with the keyboard bindings a pure keymap table.

Visually, the window wears the congeries palette (src/theme, DESIGN §11): Yog-Sothoth is a congeries of iridescent globes, and the UI is exactly that — lore-named sphere-hues against a violet-black void (hydra green for liveness, spectral blue for in-flight, brazen bronze for pending/warn, ichor for errors, gate violet for yog itself), with every colour authored once in the theme module and each driven tool keyed to its own hue.

The window is three information altitudes (DESIGN §11): the attention strip and roster (does anything need me? what's running?), the focused workspace (its agent descent tree with per-agent state badges, the four refs/lernie/* marks — conflicted, budget-exhausted, abandoned, notify — budgets, streaming tails, and tool pulses), and a per-agent tabbed inspector. Two composite flows sit on top: the start flow (DESIGN §3.4) — prompts land as new roots in the focused workspace (a fresh world bootstraps one automatically; creating more is a deliberate act, walling off spheres like clients or corporate vs. personal) and may carry a path or a ball payload (bl create + bl claim --as <workspace-name>), through an editable goal composer whose Send fires lernie prompt detached — and the config editors stage-and-validate brazen config.toml, lernie global config, and per-workspace config branches (DESIGN §9). Replays (<lernie-data>/replays/*) render through the same view, read-only.

Running

yog                                # browse every workspace, attention-sorted
yog --workspace /path/to/workspace # start focused on one workspace

yog enumerates every lernie workspace (named workspaces under $XDG_DATA_HOME/yog/workspaces/, foreign workspaces and replays under the lernie data root) and opens on the first one that needs attention. Balls bind to workspaces by claimant: every claim a workspace makes is stamped --as its minted name, so assignment is late-mutable metadata, not a location (DESIGN §3.2). The optional --workspace <path> flag overrides that startup focus. There is no --repo flag — the whole roster is the view.

From a source checkout, make run does the same via cargo run; pass WS=<path> to preset the initial focus.

egui is winit-based; its only runtime deps are the X11/Wayland libs already present on any Linux desktop. No apt install step is required.

The window drives the lernie and bl binaries over PATH (or LERNIE_BINARY / BL_BINARY); install lernie from its repo (make install there).

The world

yog composes its own nested world — a substrate environment under $XDG_DATA_HOME/yog/world that overrides LERNIE_HOME and XDG_STATE_HOME and hands it to every child it spawns, so yog's bl/lernie state never collides with your ambient tools (brazen's config, credentials, and model cache all stay shared — one host bz, one config, its secrets and its regenerable cache). The world is the authority in DESIGN §16; one rm -rf $XDG_DATA_HOME/yog erases all of it and leaves your ambient substrates untouched.

Two escape hatches (DESIGN §8.4) let a human — or a foreign frontend — join that world from a shell with one prefix:

eval "$(yog env)"          # drop THIS shell into the world; ambient bl/lernie/bz
                           # now operate on yog's nested state
yog exec bl list           # run one command inside the world (its exit is yog's)
yog exec --cwd /path bl close bl-1234 --as me

yog env prints the world's shell-quoted export lines; yog exec [--cwd DIR] <cmd…> layers the world env over your inherited environment, inherits stdio, and propagates the child's exit code (a terminating signal maps to 128 + signum). Both work headless — no display required.

By default the nested balls clone tracks the project's shared balls/tasks store branch, so a ball yog claims is visible to your ambient bl list and vice versa. A per-project no-marks knob (stealth bl conf task-remote none, and/or a custom task-branch) trades that coordination for invisibility — driven through bl's own config surface, never a yog config file (DESIGN §16.3).

Building and contributing

Target What it does
make build / make release Debug / release build
make test Run the test suite (parallel; see the Makefile note on spawn discipline)
make coverage Enforce the 100% coverage floor via pinned tarpaulin (see tarpaulin.toml)
make lint / make fmt Clippy -D warnings + ast-grep rules audit + cargo deny check / rustfmt
make rules-audit Run the pinned ast-grep rules (rules/) over src and verify the rules/fixtures negatives still fire
make check fmt-check + lint + coverage — the complete gate CI and the pre-commit hook mirror
make run [WS=<path>] Launch the window (optionally focused on one workspace)
make install [INSTALL_PREFIX=<p>] Release-build and drop yog into $INSTALL_PREFIX/bin (default ~/.local/bin)
make install-hooks Point core.hooksPath at .githooks — do this once per clone

Code style is governed by the Rust Bootstrap v3 standard — the flat-numbered, yog-adapted rules in AGENTS.md, machine-enforced by rules/*.yml (pinned ast-grep), the clippy manifest, and cargo-deny; read it before writing code.

Task tracking uses balls (bl); never commit directly on main — all changes land via a claimed worktree and are delivered by bl close. The pre-commit hook enforces this, a 300-line ceiling on source files, and the 100% coverage floor.

Delivery

Delivery to the upstream (mudbungie/yog) is automatic. bl close squash-merges the worktree onto main, but it seals that commit itself — outside git commit — so git's post-commit hook never fires on a close. Delivery instead rides balls' own extension seam: the scripts/bl-push-main plugin, wired into close.post, pushes main to origin right after the close lands it. A push failure only warns — it never fails the close, so a landed change is never lost to a transient network error; re-push manually with git push origin main. Balls task state travels its own path, pushed by the bl-tracker plugin; the two are independent.

Wiring the plugin into a checkout is checkout-local, not a repo edit: symlink scripts/bl-push-main into the landing's config/plugins/bin/, then bl conf append close.post bl-push-main. The .githooks/post-commit hook is retained for the rare legitimate manual commit made directly on main; run make install-hooks once per clone to arm the git hooks.

Local install (CICD)

The local half of delivery is a sibling close.post plugin, scripts/bl-install-main: when a close lands on main it recompiles that tip and make installs yog into $PATH. It builds from an ephemeral git worktree --detach main, never the root checkout — a plumbing delivery leaves the root working tree stale and possibly holding uncommitted work, so the main ref is the only trustworthy source of "what landed". The build shares the repo's target/ (CARGO_TARGET_DIR) to stay incremental, runs detached (setsid) so bl close returns at once, and logs to target/cicd-install.log. Like bl-push-main it always exits 0 — a build failure only lands in the log, never rolls back the close. Wire it the same way: symlink scripts/bl-install-main into the landing's config/plugins/bin/, then bl conf append close.post bl-install-main.

On every push to main, GitHub Actions (.github/workflows/ci.yml) runs make ci on Linux (fmt-check, clippy -D warnings, tarpaulin 100% floor) and cargo build + make test on macOS (Apple silicon).

Publishing

The crate is published to crates.io with make publish. That target runs cargo publish --dry-run unconditionally; the real upload runs only when you opt in explicitly with make publish CONFIRM=yes. Publishing a release is a deliberate human decision, never a side effect of CI or delivery.