feat: rig-managed tmux focus-events + shell env vars (COLORTERM=truecolor) - #205
Open
alex-mextner wants to merge 2 commits into
Open
feat: rig-managed tmux focus-events + shell env vars (COLORTERM=truecolor)#205alex-mextner wants to merge 2 commits into
alex-mextner wants to merge 2 commits into
Conversation
Closes #204 Alex wants tmux's `focus-events` option on so terminal focus in/out events reach programs running inside panes (needed by some editors/ tools) -- provisioned through rig, not a hand-edited dotfile. Adds `tmux.focus_events.enabled` (default true) following the exact established pattern for every other modeled tmux boolean: a Block in config_schema.py (+ regenerated schema/rig.schema.json), a manual validator entry in config.py's _TMUX_TOP_KEYS/_TMUX_SUBKEYS, a TmuxPlan field + build_tmux() kwarg in tmux.py, an explicit on/off render line (never omit-when-false, so the generated tail overrides a preserved inline value from a migrated conf -- same contract as @continuum-restore/@continuum-boot/@resurrect-capture-pane-contents), and pass-through in plan.py's provision_tmux options + runner.py's tmux_plan_from_action. Placed first in the generated file, before the plugin declarations -- a plain terminal-capability toggle independent of the resurrect/continuum/tpm ordering guarantee documented on render_rig_conf. Docs: docs/config-schema.md gets the yaml example + table row. Tests: validate accept/reject (typo guard), render default-on, render explicit-off, the pre-upgrade Action-replay default (mirrors the existing pane_titles coverage), and an end-to-end test through the FULL plan.py -> runner.py pass-through chain for the disabled case (the other tests either call tmux.build_tmux directly, bypassing that chain, or only assert the default, so a broken pass-through would have rendered 'on' regardless and slipped past every other test undetected). Full suite (`uv run pytest -q tests/`): 2317 passed, 18 skipped. Reviewed with `review diff --staged` (Fable, then Opus after a small follow-up edit); findings addressed (missing end-to-end disabled-path test; docs/docstring framing inconsistency on line placement being "load-bearing" vs "readability only" -- resolved to the latter, matching the actual code; verified TmuxPlan's only constructor is build_tmux, so the new required field has no other call site to break). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZGonUbk2sDjqYaaH5zRrM
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Closes #206 Follow-up filed: #207 (GLOBAL-labeled blocks have no repo-layer enforcement) Alex wants COLORTERM=truecolor set machine-wide for richer terminal colors, rolled out through rig -- not a hand-edited .zshenv/.zshrc. No existing rig mechanism managed generic shell env vars (verified: no source-file line in .zshrc/.zprofile, no schema block, no module), so this adds one, mirroring riglib.tmux's "own a generated file, splice one import line" idiom. New module riglib/shell_env.py (pure, stdlib-only): ShellEnvPlan + build_shell_env resolve an `env:` config block; render_env_file() writes one `export KEY=value` per var (keys validated as POSIX shell identifiers -- both at config.validate time and, defense in depth, again at render time, since keys are interpolated UNQUOTED; values are shlex-quoted); desired_rc_text() is a POSITION-TOLERANT splice -- deliberately NOT end-anchored like tmux's import line (tmux's ordering guarantee is a real hazard from continuum; a plain exported var has none), so a user-relocated import line is left exactly where they put it, while a coexisting STALE copy (an old generated_dir) is still always dropped. import_line() is shlex.quote'd (not hand-placed quotes -- an adversarial generated_dir, reachable per review from a repo's own committed rig.yaml, could otherwise break out of a hand-quoted string). Wiring: config_schema.py/config.py (validation, including var-key identifier + newline/empty-path rejection), plan.py (_build_env + the shared env_options_from_config used by both the planner and the disabled-scan below), actions/runner.py (_do_provision_env -- both artifacts go through fsutil.write_file for consistent on_conflict/ backup/error handling), drift.py (_check_env sharing apply's exact predicate so status can never disagree with apply, plus check_disabled_env -- a still-installed leftover after `enabled: false`, checking BOTH the generated file and a lingering rc_path source line independently), areas.py/layers.py (status-summary registration), cli.py (wires the disabled-scan). Default rc_path is ~/.zshenv: zsh's only startup file sourced unconditionally in every mode (login or not, interactive or not), so a var reaches even `zsh -l -c '...'` / mosh / SSH non-interactive shells -- confirmed against Alex's own ~/.zshenv, which already carries a Homebrew-PATH comment making the same point. Docs: new docs/config-schema.md#env section (position-tolerance, teardown limitations, trust-model note). Tests: tests/test_shell_env.py (82 tests) -- validation, pure rendering, CRLF/trailing-blank-line preservation, position-tolerance, stale-line handling (including stale-plus-current coexistence), the full plan->apply->drift round trip, shell-injection cases (bad keys, adversarial values, an adversarial generated_dir), on_conflict variance, non-UTF-8 file handling, and the disabled-block orphan scan (both directions). Full suite: `uv run pytest -q tests/` -- 2386 passed, 18 skipped (pre-existing, unrelated codex-update probe-timing flakiness under load, confirmed present on unmodified main too). Reviewed with `review diff --staged` across 11 rounds (Fable/Opus, codex hit its usage limit early and sat out most rounds) as the design evolved -- shell-injection hardening (unquoted var keys, quoted values, the import-line path itself), the apply/status predicate-sharing fix, position-tolerance (a design change from an initial end-anchored version, after review found it silently reverted a user's deliberate line placement forever), byte-preserving CRLF/trailing-blank handling, error-handling parity between the generated file and rc_path, path-resolution duplication cleanup (env_options_from_config shared by plan.py and cli.py; a single canonical ENV_VAR_KEY_PATTERN in shell_env.py, imported by config.py instead of duplicated), and newline/empty-path validation. Final round: "I could not find a blocking correctness bug... the shipped logic is sound." Remaining known limitations are documented inline and in docs/config-schema.md (the GLOBAL-vs-repo-layer trust question is deliberately deferred to #207, shared by env's siblings tmux/ gitignore/spotlight, not something #206 could fix in isolation without redesigning the whole layer-provenance model). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PZGonUbk2sDjqYaaH5zRrM
alex-mextner
force-pushed
the
feat/tmux-focus-events
branch
2 times, most recently
from
August 7, 2026 08:37
a362425 to
1f6afe9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related, independently-committed additions to rig's declarative config, both requested by Alex to replace hand-edited dotfiles with rig-managed provisioning:
1.
tmux.focus_events(Closes #204) — models tmux'sfocus-eventsoption as a proper rig-managed boolean, following the exact established pattern of every other modeled tmux boolean.render_rig_conf()emits an explicitset -g focus-events on/off(never omitted), placed first in the generated file since it carries no ordering dependency on the resurrect/continuum/tpm machinery.2.
env:block — shell environment variables (Closes #206) — a brand-new capability: rig owns a generated~/.config/rig/env/rig.env.sh(oneexport KEY=valueper configured var) and ensures asourceline is present inrc_path(default~/.zshenv— the only zsh startup file sourced unconditionally in every mode, including non-interactive shells). Mirrorsriglib.tmux's "generated file + spliced import line" idiom, but deliberately position-tolerant (not end-anchored like tmux) since a plain exported var has none of tmux's continuum-ordering hazard. Shell-injection hardening throughout: var keys validated as POSIX identifiers (both at config-validate time and again at render time), valuesshlex.quoted, the import line itselfshlex.quoted rather than hand-placed quotes.Follow-up filed: #207 — a pre-existing, system-wide characteristic (shared by
env/tmux/gitignore/spotlight) that a repo's own committedrig.yamlcan declare a GLOBAL-labeled block with no layer enforcement. Confirmed not a regression from this PR; deferred as an architecture decision, not something either of these two features could fix in isolation.Test plan
uv run pytest -q tests/(full suite) — 2386 passed, 18 skipped (pre-existing codex-update probe-timing flakiness under load, confirmed present on unmodifiedmaintoo — unrelated to this PR)uv run ruff checkon every touched file — clean (one pre-existing, unrelatedF401inplan.py, confirmed present onmainbefore this PR)bash tests/smoke.sh --fast— OKuv run python -m riglib schema --write— regeneratedschema/rig.schema.json, committedreview diff --staged— multiple rounds across both commits (Fable/Opus; codex hit its usage limit early). Theenvfeature alone went through 11 review rounds as the design evolved (shell-injection hardening, apply/status predicate-sharing, a position-tolerance redesign after review caught an end-anchored version silently reverting user-relocated lines forever, byte-preserving CRLF/trailing-blank handling, path-resolution duplication cleanup). Final round: "I could not find a blocking correctness bug... the shipped logic is sound."rig apply commit --only tmux/--only env(surgical, scoped — not a fullrig apply) — verifiedtmux show-options -g focus-eventsreportsonin a live session, andzsh -l -c 'echo $COLORTERM'printstruecolor🤖 Generated with Claude Code
https://claude.ai/code/session_01PZGonUbk2sDjqYaaH5zRrM