Skip to content

feat(update): toggle auto-update via /update auto, /settings, and info - #133

Merged
elkaix merged 2 commits into
mainfrom
feat/auto-update-toggle
Jun 13, 2026
Merged

feat(update): toggle auto-update via /update auto, /settings, and info#133
elkaix merged 2 commits into
mainfrom
feat/auto-update-toggle

Conversation

@elkaix

@elkaix elkaix commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes the gap surfaced while releasing 0.43.0: silent startup auto-updates (shipped in #130) could only be turned off by editing config or setting an env var — there was no in-app control. This adds three surfaces, all effective-state aware.

What's added

  • /update auto on|off — toggle silent startup auto-updates; /update auto (no arg) reports the effective state. Persists to the auto_update config field; no reload (auto-update is consulted only at startup, so the running config is mirrored in place).
  • /settings row — the interactive panel now exposes Auto-update. When an external override forces it off, the row is read-only and shows the reason, so the panel never offers a no-op toggle.
  • pythinker info — reports auto-update: <effective> (config auto_update=<...>[; <override>]), in both text and --json.

All three resolve the effective state via the precedence in auto_update_enabled: the PYTHINKER_CLI_NO_AUTO_UPDATE kill-switch and source-checkout detection outrank the config field and are surfaced as the reason.

Design notes

  • Extracted the pure policy resolver (auto_update_enabled, auto_update_override_reason, and the two primitives) into a new shell-free update_policy module. pythinker info imports from there, so the lightweight CLI no longer pulls the shell stack (aiohttp/console). ui/shell/update.py re-exports the names for back-compat.
  • Added create=False to get_share_dir() / get_config_file() so the read-only info path resolves the config path without materializing ~/.pythinker as a side effect (default create=True preserves all existing behavior). Guarded by a regression test.

Tests

  • /update auto persist/no-op/usage/no-config-file/override-status (test_update_auto_slash.py)
  • /settings selector: live toggle, read-only under override, apply path (test_settings_selector.py)
  • auto_update_override_reason precedence (test_silent_auto_update.py)
  • pythinker info line formatting + no share-dir side effect (test_info.py)
  • Repointed the one pre-existing auto_update_enabled precedence test to the new module.

make check-pythinker-code green (ruff + pyright); focused + regression suites pass (312+ tests).

Notes

Targets main post-0.43.0, so this ships in the next release (0.44.0). ## Unreleased CHANGELOG entry added; docs updated (slash-commands.md).

Summary by CodeRabbit

  • New Features
    • Added /update auto [on|off] to toggle automatic background updates on or off.
    • /update auto displays the current auto-update status and any active overrides.
    • Interactive settings panel now includes an auto-update toggle.
    • pythinker info reports auto-update configuration and override status.

Add an in-app way to turn silent startup auto-updates on or off:
- `/update auto on|off` (and `/update auto` reports the effective state)
- an effective-state-aware row in the interactive `/settings` panel
- auto-update status in `pythinker info`

All surfaces show the effective state: an external override
(PYTHINKER_CLI_NO_AUTO_UPDATE or a source checkout) is surfaced as the
reason and renders the /settings row read-only, so the toggle is never a
silent no-op.

Extract the pure policy resolver into a shell-free `update_policy` module so
`pythinker info` reports status without importing the shell stack, and add
`create=False` to get_share_dir/get_config_file so the read-only info path no
longer materializes ~/.pythinker as a side effect.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@elkaix, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 41 minutes and 59 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a7222b2b-f6f3-40b1-ac74-1c3ed753e4a2

📥 Commits

Reviewing files that changed from the base of the PR and between e81ff3b and 92ef912.

📒 Files selected for processing (9)
  • src/pythinker_code/cli/info.py
  • src/pythinker_code/ui/shell/__init__.py
  • src/pythinker_code/ui/shell/selectors/settings.py
  • src/pythinker_code/ui/shell/slash.py
  • src/pythinker_code/ui/shell/update.py
  • src/pythinker_code/update_policy.py
  • tests/ui_and_conv/test_settings_selector.py
  • tests/ui_and_conv/test_shell_update.py
  • tests/ui_and_conv/test_update_auto_slash.py
📝 Walkthrough

Walkthrough

This PR adds a complete auto-update toggle system: a new policy module establishes environment/config/source-checkout precedence, the shell update logic is refactored to use that canonical module, and three user-facing interfaces (CLI /update auto, settings panel, and pythinker info) integrate the toggle with override detection. Read-only config access prevents side effects during diagnostics.

Changes

Auto-update toggle feature

Layer / File(s) Summary
Policy foundation: auto-update precedence and override detection
src/pythinker_code/update_policy.py, tests/ui_and_conv/test_silent_auto_update.py
New module defines auto_update_disabled(), is_running_from_source_checkout(), auto_update_enabled(config) with env → config → source precedence, and auto_update_override_reason() for override reason detection. Tests verify precedence and reason reporting across disabled-by-env, disabled-by-source, and unblocked states.
Config/share read-only access infrastructure
src/pythinker_code/share.py, src/pythinker_code/config.py
get_share_dir(*, create: bool = True) and get_config_file(*, create: bool = True) now support read-only path resolution without materializing directories, enabling diagnostics to check config state without side effects.
Shell update module refactoring
src/pythinker_code/ui/shell/update.py, tests/ui_and_conv/test_shell_update.py
Removes duplicate policy logic and re-exports auto_update_enabled, auto_update_override_reason from update_policy module; test monkeypatching redirected to canonical policy module location.
Info command auto-update diagnostics
src/pythinker_code/cli/info.py, tests/cli/test_info.py
pythinker info reports effective state, stored config value, and override reason via InfoData fields and human-readable line output; _auto_update_info() uses read-only config access to avoid creating directories during safe diagnostics.
Settings UI auto-update row with override detection
src/pythinker_code/ui/shell/selectors/settings.py, tests/ui_and_conv/test_settings_selector.py
Settings panel adds toggleable auto_update row; when override is active, row becomes read-only and displays override reason; setting changes persist to config. Test helper _item() fetches settings by id; tests verify toggleable vs. read-only behavior and config updates.
**Slash command /update auto [on off]**
src/pythinker_code/ui/shell/slash.py, tests/ui_and_conv/test_update_auto_slash.py
Release notes
CHANGELOG.md
Document the new auto-update toggle feature, override reason reporting, and disabled interaction when externally overridden.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Pythoughts-labs/pythinker-code#130: Previous PR established the config field auto_update and initial integration points; this PR adds the policy module, command handler, and complete UI/diagnostic wiring on top.

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.17% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commits format (feat(update): description), is directly related to the main changeset (adding auto-update toggle across three surfaces), and clearly summarizes the feature.
Description check ✅ Passed Description includes all required sections: Summary, What's added, Design notes, Tests, and Notes. Related issue is referenced. Checklist is present but items are unchecked; however, the author's detailed explanations indicate work was done.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/auto-update-toggle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/pythinker_code/ui/shell/update.py Fixed
Comment thread src/pythinker_code/ui/shell/update.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pythinker_code/cli/info.py`:
- Around line 42-43: The broad except in _auto_update_info currently swallows
all exceptions; change it to only catch expected errors (e.g.,
FileNotFoundError, json.JSONDecodeError, KeyError, or any domain-specific
Policy/Config error your code raises) and add a logged diagnostic (use the
module logger or logging.getLogger(__name__) and call logger.exception or
logger.error with the exception) before returning the unknowns; leave the
fallback return (None, None, None) but do not suppress unexpected
exceptions—either let them propagate or re-raise after logging if they are truly
unexpected.

In `@src/pythinker_code/update_policy.py`:
- Around line 36-41: The is_running_from_source_checkout() block currently
swallows all exceptions when importing pythinker_code; instead, limit the except
to the expected failure modes (e.g., ImportError, AttributeError,
FileNotFoundError, TypeError) and log the failure before returning False so
callers know why detection failed: update the try/except around the import and
Path resolution in is_running_from_source_checkout() to catch those specific
exceptions, call logging.getLogger(__name__).debug()/error(...) with the
exception details and context (including the exception message and that
import/path detection failed), and only return False for those caught cases
while allowing truly unexpected exceptions to propagate. Ensure you reference
the same symbols (is_running_from_source_checkout, pythinker_code, package_path)
when making the change.

In `@tests/ui_and_conv/test_update_auto_slash.py`:
- Around line 56-66: The test currently mocks internal helpers (load_config,
save_config, shell_slash.console.print) but should instead exercise observable
behavior: create a real temp config file containing config_for_save at
config_path (don’t mock load_config/save_config), call _run_update(app, "auto
on"), then reopen/read the persisted config file and assert its auto_update is
True, assert the in-memory/runtime config (app or returned config) reflects
auto_update True, and assert the user-facing message was printed by
capturing/patching shell_slash.console.print only for output verification; apply
the same replacement strategy to the other test blocks mentioned (lines 83-90,
102-124, 136-143).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: df5f8107-52d6-4448-8a71-0c8526ef7b60

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef84e4 and e81ff3b.

⛔ Files ignored due to path filters (1)
  • docs/en/reference/slash-commands.md is excluded by !docs/**
📒 Files selected for processing (13)
  • CHANGELOG.md
  • src/pythinker_code/cli/info.py
  • src/pythinker_code/config.py
  • src/pythinker_code/share.py
  • src/pythinker_code/ui/shell/selectors/settings.py
  • src/pythinker_code/ui/shell/slash.py
  • src/pythinker_code/ui/shell/update.py
  • src/pythinker_code/update_policy.py
  • tests/cli/test_info.py
  • tests/ui_and_conv/test_settings_selector.py
  • tests/ui_and_conv/test_shell_update.py
  • tests/ui_and_conv/test_silent_auto_update.py
  • tests/ui_and_conv/test_update_auto_slash.py

Comment thread src/pythinker_code/cli/info.py Outdated
Comment thread src/pythinker_code/update_policy.py
Comment thread tests/ui_and_conv/test_update_auto_slash.py Outdated
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

… excepts

- Repoint consumers (shell __init__, /update, /settings) and tests to import
  auto_update_enabled / auto_update_override_reason directly from the canonical
  `update_policy` module, and drop the unused re-export shims from
  ui/shell/update.py (resolves "unused import" findings).
- Narrow the broad `except Exception` in `info._auto_update_info` to
  (OSError, ValueError, ImportError) and log the degraded path instead of
  silently swallowing (C03); ConfigError/pydantic errors are ValueError.
- Narrow `update_policy.is_running_from_source_checkout` to
  (ImportError, AttributeError, OSError).
- Strengthen the /update auto persist tests to assert real on-disk persistence
  via load_config rather than mock call-coupling.
@elkaix
elkaix merged commit 7e476c1 into main Jun 13, 2026
36 checks passed
@elkaix
elkaix deleted the feat/auto-update-toggle branch June 13, 2026 22:27
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.

1 participant