Skip to content

Conversation

@kzndotsh
Copy link
Contributor

Pull Request

Description

Provide a clear summary of your changes and reference any related issues. Include the motivation behind these changes and list any new dependencies if applicable.

If your PR is related to an issue, please include the issue number below:

Related Issue: Closes #

Type of Change:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring
  • Test improvements

Guidelines

  • My code follows the style guidelines of this project (formatted with Ruff)

  • I have performed a self-review of my own code

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation if needed

  • My changes generate no new warnings

  • I have tested this change

  • Any dependent changes have been merged and published in downstream modules

  • I have added all appropriate labels to this PR

  • I have followed all of these guidelines.

How Has This Been Tested? (if applicable)

Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable)

Screenshots (if applicable)

Please add screenshots to help explain your changes.

Additional Information

Please add any other information that is important to this PR.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Sorry @kzndotsh, your pull request is larger than the review limit of 150000 diff characters

@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2025

Important

Review skipped

Too many files!

10 files out of 160 files are above the max files limit of 150.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Large refactor and documentation update: adds contributor guidelines, reorganizes and expands tests, tightens typing (TYPE_CHECKING), introduces constants and exit-code handling, consolidates TLDR command handling and cache lifecycle, reorders logging/startup configuration, and adjusts logging/telemetry and a few public signatures. Mostly non-functional and tests.

Changes

Cohort / File(s) Summary
Contributor guidelines
\.cursor/commands/deslop.md
New contributor/workflow and coding-standards document added.
Tests reorg & additions
tests/... tests/core/*, tests/database/*, tests/services/*, tests/shared/*, tests/modules/*, tests/plugins/*, tests/ui/*
Reorganized unit/integration -> domain-based layout; many new focused test modules, removed legacy aggregated tests, added package init files and test formatting/fixture updates.
Fixtures & test utilities
tests/fixtures/*, tests/conftest.py
Renamed test_data_fixtures.pydata_fixtures.py; added utils.py, sentry_fixtures.py; updated exports and pytest_plugins; added mock factory helpers and typed fixture returns.
CI / config / docs
pyproject.toml, .github/workflows/tests.yml, CHANGELOG.md, AGENTS.md, docs/...
Broadened pytest markers, enabled strict/importlib addopts, CI now runs tests/, updated changelog/docs and added contributor guidance.
Scripts: constants & typing
scripts/... (e.g., scripts/config/validate.py, scripts/dev/*, scripts/docs/lint.py, scripts/db/*, scripts/tux/start.py)
Added module constants (DB_URL_DISPLAY_LENGTH, YAML_FRONTMATTER_PARTS, ERROR_EXIT_CODE_THRESHOLD, ARCHIVE_DOWNLOAD_TIMEOUT_SECONDS, etc.), AsyncSession/type hints, explicit exit codes, narrowed exception catches, adjusted cleaning scope and other doc/typing tweaks.
Startup / run / exit flow
src/tux/main.py, src/tux/core/app.py, src/tux/core/base_cog.py, scripts/tux/start.py
run() drops debug param; logging configured earlier (CLI and fallback in app.start); TuxApp.start uses _get_exit_code; shutdown now returns None; CLI uses explicit exit codes and sys.exit.
Setup & logging refactor
src/tux/core/setup/*.py, src/tux/core/setup/orchestrator.py
Removed _log_step scaffolding, migrated to loguru logger calls, use TuxSetupError for setup failures, added migration timeout handling and task monitor startup.
Core typing & controllers
src/tux/database/controllers/*, src/tux/core/decorators.py, src/tux/core/task_monitor.py, src/tux/core/__init__.py
Added TYPE_CHECKING guards, tightened type annotations (PermissionSystem, AsyncSession, Tux, DatabaseService), re-exported DEFAULT_RANKS, added some constructor/signature annotations.
Prefix manager & cache
src/tux/core/prefix_manager.py
set_prefix now fire-and-forget persists; startup resilient cache loading; added get_cache_stats() method.
TLDR command consolidation
src/tux/modules/tools/tldr.py
Unified slash/prefix handlers into _handle_tldr_command, added cache task handle and lifecycle (cog_load/cog_unload), unified fetch/cache/update/pagination and messaging across Interaction and Context.
Service wrappers & API handling
src/tux/services/wrappers/* (godbolt, wandbox, tldr)
Standardized response handling to consistent return types and raised TuxAPI* errors, expanded TypedDict payloads, added time/embed constants, removed Optional-return paths in several wrappers.
Error handler & extractors
src/tux/services/handlers/error/*
Removed config param from _send_error_response; extractor functions now accept **_kwargs to ignore extraneous context; minor formatter comments.
Constants & exports
src/tux/shared/constants.py
Added and exported SLOW_COG_LOAD_THRESHOLD in __all__.
Logging subsystem
src/tux/core/logging.py, many services/modules
Added VALID_LOG_LEVELS and validation helper; expanded INTERCEPTED_LIBRARIES and THIRD_PARTY_LOG_LEVELS; normalized level handling; many log-level adjustments (trace/debug/info/success).
Minor behavioral/stylistic tweaks
src/tux/modules/*, src/tux/help/navigation.py, src/tux/ui/views/config/callbacks.py, src/tux/modules/utility/*
Replaced explicit len(...) checks with truthiness, prefer keyword args in unload_if_missing_config calls, add divide-by-zero guards, refine interaction-data extraction, use enumerate, small logging/string-format changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User (Interaction / Prefix)
  participant Tldr as Tldr Cog
  participant Cache as TLDR Cache
  participant Archive as Archive/Fetcher

  Note over User,Tldr: Command trigger (slash or prefix)
  User->>Tldr: invoke _handle_tldr_command(command, platform, language, flags)
  Tldr->>Cache: check cache for command (async)
  alt cache hit
    Cache-->>Tldr: cached pages
  else cache miss or stale
    Tldr->>Archive: fetch archive (with timeout)
    Archive-->>Tldr: archive content or error
    Tldr->>Cache: schedule cache update (fire-and-forget)
  end
  alt command found
    Tldr->>User: send embed(s) via interaction response or context.send
  else not found
    Tldr->>User: send ephemeral error or message
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • refactor: scripts #1108 — Overlaps on scripts/CLI refactors and typing/constant changes in scripts/* (e.g., scripts/tux/start.py, scripts/db/*), indicating potential overlap in script-level changes.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is completely unfilled—it only contains the repository's PR template with no actual content describing the changes, related issues, testing approach, or completed checklist items. Provide a detailed description of the changes made, reference any related issues, specify the type of change, and confirm completion of the provided guidelines and testing.
Title check ❓ Inconclusive The title 'cleanup: tests and slop' is vague and uses non-descriptive language ('slop'). While it refers to cleanup work, it lacks specificity about what is being cleaned up. Replace 'slop' with specific terminology describing the actual changes (e.g., 'refactor', 'optimize', 'reorganize') to make the title more meaningful and professional.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 96.10% which is sufficient. The required threshold is 80.00%.

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

@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

📚 Documentation Preview

Type URL Version Message
Production https://tux.atl.dev - -
Preview https://530c2cb2-tux-docs.allthingslinux.workers.dev 530c2cb2-5bc3-4318-aadd-af6a0cbd002d Preview: tux@c72de2355ab6bfabc4b0b5ba0eef16dd694bbc21 on 1124/merge by kzndotsh (run 217)

@sentry
Copy link

sentry bot commented Dec 26, 2025

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
421 1 420 69
View the top 1 failed test(s) by shortest run time
tests/database/test_database_migrations.py::TestSchemaErrorHandlingThroughService::test_operations_on_disconnected_service
Stack Traces | 0.008s run time
.venv/lib/python3.13.../sqlalchemy/engine/base.py:143: in __init__
    self._dbapi_connection = engine.raw_connection()
                             ^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/base.py:3309: in raw_connection
    return self.pool.connect()
           ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:447: in connect
    return _ConnectionFairy._checkout(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:1264: in _checkout
    fairy = _ConnectionRecord.checkout(pool)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:711: in checkout
    rec = pool._do_get()
          ^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/impl.py:177: in _do_get
    with util.safe_reraise():
         ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/langhelpers.py:224: in __exit__
    raise exc_value.with_traceback(exc_tb)
.venv/lib/python3.13.../sqlalchemy/pool/impl.py:175: in _do_get
    return self._create_connection()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:388: in _create_connection
    return _ConnectionRecord(self)
           ^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:673: in __init__
    self.__connect()
.venv/lib/python3.13.../sqlalchemy/pool/base.py:899: in __connect
    with util.safe_reraise():
         ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/langhelpers.py:224: in __exit__
    raise exc_value.with_traceback(exc_tb)
.venv/lib/python3.13.../sqlalchemy/pool/base.py:895: in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/create.py:661: in connect
    return dialect.connect(*cargs, **cparams)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/default.py:630: in connect
    return self.loaded_dbapi.connect(*cargs, **cparams)  # type: ignore[no-any-return]  # NOQA: E501
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../dialects/postgresql/psycopg.py:812: in connect
    await_only(creator_fn(*arg, **kw))
.venv/lib/python3.13.../sqlalchemy/util/_concurrency_py3k.py:132: in await_only
    return current.parent.switch(awaitable)  # type: ignore[no-any-return,attr-defined] # noqa: E501
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/_concurrency_py3k.py:196: in greenlet_spawn
    value = await result
            ^^^^^^^^^^^^
.venv/lib/python3.13....../site-packages/psycopg/connection_async.py:145: in connect
    raise type(last_ex)("\n".join(lines)).with_traceback(None)
E   psycopg.OperationalError: connection failed: connection to server at "127.0.0.1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?
E   Multiple connection attempts failed. All failures were:
E   - host: 'localhost', port: 5432, hostaddr: '::1': connection failed: connection to server at "::1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?
E   - host: 'localhost', port: 5432, hostaddr: '127.0.0.1': connection failed: connection to server at "127.0.0.1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?

The above exception was the direct cause of the following exception:
tests/database/test_database_migrations.py:310: in test_operations_on_disconnected_service
    await guild_controller.create_guild(guild_id=TEST_GUILD_ID)
.../database/controllers/guild.py:67: in create_guild
    return await self.create(id=guild_id)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.../controllers/base/base_controller.py:141: in create
    return await self._crud.create(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.../controllers/base/crud.py:43: in create
    await session.commit()
.venv/lib/python3.13.../ext/asyncio/session.py:1000: in commit
    await greenlet_spawn(self.sync_session.commit)
.venv/lib/python3.13.../sqlalchemy/util/_concurrency_py3k.py:201: in greenlet_spawn
    result = context.throw(*sys.exc_info())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:2030: in commit
    trans.commit(_to_root=True)
<string>:2: in commit
    ???
.venv/lib/python3.13.../sqlalchemy/orm/state_changes.py:137: in _go
    ret_value = fn(self, *arg, **kw)
                ^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:1311: in commit
    self._prepare_impl()
<string>:2: in _prepare_impl
    ???
.venv/lib/python3.13.../sqlalchemy/orm/state_changes.py:137: in _go
    ret_value = fn(self, *arg, **kw)
                ^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:1286: in _prepare_impl
    self.session.flush()
.venv/lib/python3.13.../sqlalchemy/orm/session.py:4331: in flush
    self._flush(objects)
.venv/lib/python3.13.../sqlalchemy/orm/session.py:4466: in _flush
    with util.safe_reraise():
         ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/langhelpers.py:224: in __exit__
    raise exc_value.with_traceback(exc_tb)
.venv/lib/python3.13.../sqlalchemy/orm/session.py:4427: in _flush
    flush_context.execute()
.venv/lib/python3.13.../sqlalchemy/orm/unitofwork.py:466: in execute
    rec.execute(self)
.venv/lib/python3.13.../sqlalchemy/orm/unitofwork.py:642: in execute
    util.preloaded.orm_persistence.save_obj(
.venv/lib/python3.13.../sqlalchemy/orm/persistence.py:68: in save_obj
    ) in _organize_states_for_save(base_mapper, states, uowtransaction):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/persistence.py:223: in _organize_states_for_save
    for state, dict_, mapper, connection in _connections_for_states(
.venv/lib/python3.13.../sqlalchemy/orm/persistence.py:1759: in _connections_for_states
    connection = uowtransaction.transaction.connection(base_mapper)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<string>:2: in connection
    ???
.venv/lib/python3.13.../sqlalchemy/orm/state_changes.py:137: in _go
    ret_value = fn(self, *arg, **kw)
                ^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:1037: in connection
    return self._connection_for_bind(bind, execution_options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<string>:2: in _connection_for_bind
    ???
.venv/lib/python3.13.../sqlalchemy/orm/state_changes.py:137: in _go
    ret_value = fn(self, *arg, **kw)
                ^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:1173: in _connection_for_bind
    conn = self._parent._connection_for_bind(
<string>:2: in _connection_for_bind
    ???
.venv/lib/python3.13.../sqlalchemy/orm/state_changes.py:137: in _go
    ret_value = fn(self, *arg, **kw)
                ^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/orm/session.py:1187: in _connection_for_bind
    conn = bind.connect()
           ^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/base.py:3285: in connect
    return self._connection_cls(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/base.py:145: in __init__
    Connection._handle_dbapi_exception_noconnection(
.venv/lib/python3.13.../sqlalchemy/engine/base.py:2448: in _handle_dbapi_exception_noconnection
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
.venv/lib/python3.13.../sqlalchemy/engine/base.py:143: in __init__
    self._dbapi_connection = engine.raw_connection()
                             ^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/base.py:3309: in raw_connection
    return self.pool.connect()
           ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:447: in connect
    return _ConnectionFairy._checkout(self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:1264: in _checkout
    fairy = _ConnectionRecord.checkout(pool)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:711: in checkout
    rec = pool._do_get()
          ^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/impl.py:177: in _do_get
    with util.safe_reraise():
         ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/langhelpers.py:224: in __exit__
    raise exc_value.with_traceback(exc_tb)
.venv/lib/python3.13.../sqlalchemy/pool/impl.py:175: in _do_get
    return self._create_connection()
           ^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:388: in _create_connection
    return _ConnectionRecord(self)
           ^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/pool/base.py:673: in __init__
    self.__connect()
.venv/lib/python3.13.../sqlalchemy/pool/base.py:899: in __connect
    with util.safe_reraise():
         ^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/langhelpers.py:224: in __exit__
    raise exc_value.with_traceback(exc_tb)
.venv/lib/python3.13.../sqlalchemy/pool/base.py:895: in __connect
    self.dbapi_connection = connection = pool._invoke_creator(self)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/create.py:661: in connect
    return dialect.connect(*cargs, **cparams)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/engine/default.py:630: in connect
    return self.loaded_dbapi.connect(*cargs, **cparams)  # type: ignore[no-any-return]  # NOQA: E501
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../dialects/postgresql/psycopg.py:812: in connect
    await_only(creator_fn(*arg, **kw))
.venv/lib/python3.13.../sqlalchemy/util/_concurrency_py3k.py:132: in await_only
    return current.parent.switch(awaitable)  # type: ignore[no-any-return,attr-defined] # noqa: E501
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.13.../sqlalchemy/util/_concurrency_py3k.py:196: in greenlet_spawn
    value = await result
            ^^^^^^^^^^^^
.venv/lib/python3.13....../site-packages/psycopg/connection_async.py:145: in connect
    raise type(last_ex)("\n".join(lines)).with_traceback(None)
E   sqlalchemy.exc.OperationalError: (psycopg.OperationalError) connection failed: connection to server at "127.0.0.1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?
E   Multiple connection attempts failed. All failures were:
E   - host: 'localhost', port: 5432, hostaddr: '::1': connection failed: connection to server at "::1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?
E   - host: 'localhost', port: 5432, hostaddr: '127.0.0.1': connection failed: connection to server at "127.0.0.1", port 5432 failed: Connection refused
E   	Is the server running on that host and accepting TCP/IP connections?
E   (Background on this error at: https://sqlalche..../e/20/e3q8)

To view more test analytics, go to the [Prevent Tests Dashboard](https://All Things Linux.sentry.io/prevent/tests/?preventPeriod=30d&integratedOrgName=allthingslinux&repository=tux&branch=cleanup)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/tux/core/base_cog.py (1)

222-252: Docstring example is inconsistent with the new keyword-only signature.

The docstring example on lines 247-250 still shows positional argument style, but the signature now requires keyword arguments. This will cause confusion for developers referencing the docstring.

🔎 Proposed fix
         Examples
         --------
         >>> def __init__(self, bot: Tux):
         ...     super().__init__(bot)
         ...     if self.unload_if_missing_config(
-        ...         not CONFIG.GITHUB_TOKEN, "GITHUB_TOKEN"
+        ...         condition=not CONFIG.GITHUB_TOKEN,
+        ...         config_name="GITHUB_TOKEN",
         ...     ):
         ...         return  # Exit early, cog will be partially loaded
         ...     self.github_client = GitHubClient()
tests/fixtures/sentry_fixtures.py (1)

104-126: Function definition line is completely missing.

Line 104-105 has a comment and docstring, but there is no function signature (no def mock_discord_context(): line). The @pytest.fixture decorator is also missing. The function body starts at line 106 without a function definition, making this code syntactically invalid.

tests/core/test_dynamic_permission_system.py (1)

188-200: Fix the docstring mismatch.

Line 196 defines the docstring as """Represent a special command.""", but line 200 asserts it equals "This is my special command.". This test will fail due to the mismatch.

🔎 Proposed fix
         @requires_command_permission()
         async def my_special_command(ctx: commands.Context[Tux]) -> None:
-            """Represent a special command."""
+            """This is my special command."""
 
         # Should preserve function metadata
         assert my_special_command.__name__ == "my_special_command"
         assert my_special_command.__doc__ == "This is my special command."
tests/database/test_database_migrations.py (1)

298-312: Remove misleading comment and narrow exception handling.

Two issues:

  1. Line 302: The comment # sourcery skip: use-contextlib-suppress is misleading since the code does use contextlib.suppress now (line 310). Remove this comment.

  2. Line 310: Using suppress(Exception) is too broad. Please specify the expected exception types that should be suppressed (e.g., suppress(RuntimeError, ConnectionError)).

🔎 Proposed fix
-    # sourcery skip: use-contextlib-suppress
     """Test behavior when trying to use disconnected service."""
     # Service starts disconnected
     assert disconnected_async_db_service.is_connected() is False
 
     guild_controller = GuildController(disconnected_async_db_service)
 
     # Operations should fail gracefully when not connected
-    with suppress(Exception):
+    with suppress(RuntimeError, ConnectionError):
         await guild_controller.create_guild(guild_id=TEST_GUILD_ID)
         # If we get here, the service should handle disconnection gracefully
tests/conftest.py (1)

83-109: Simplify confusing control flow with redundant returns.

The function has confusing control flow with:

  • Unnecessary process = None initialization (line 95) since it's immediately assigned
  • An else clause that returns True (line 107)
  • Another return True after the try-except (line 109)

This makes the logic harder to follow.

🔎 Proposed simplification
 def _terminate_pglite_process(pid: int) -> bool:
-    """Terminate a pglite process gracefully, then forcefully if needed.
+    """Terminate a pglite process gracefully, then forcefully if needed.
 
     Parameters
     ----------
     pid : int
         Process ID to terminate.
 
     Returns
     -------
     bool
         True if the process was successfully terminated.
     """
-    process = None
     try:
         process = psutil.Process(pid)
         process.terminate()
         process.wait(timeout=2.0)
+        return True
     except psutil.TimeoutExpired:
-        if process is not None:
-            process.kill()
-            process.wait(timeout=1.0)
+        process.kill()
+        process.wait(timeout=1.0)
+        return True
     except psutil.NoSuchProcess:
         return False
-    else:
-        return True
-
-    return True
src/tux/database/controllers/case.py (1)

438-478: Remove duplicate Parameters section in docstring.

The docstring contains two Parameters sections (lines 446-452 and lines 458-462) documenting the same parameters. This duplication should be removed for clarity.

🔎 Proposed fix
         """
         Mark a tempban case as processed after the user has been unbanned.
 
         Parameters
         ----------
         case_id : int
             The case ID to mark as expired.
         guild_id : int | None, optional
             The guild ID (currently unused, kept for API consistency).
 
         This sets case_processed=True to indicate the expiration has been handled.
         The case_status remains True (the case is still valid, just completed).
         The case_expires_at field remains unchanged as a historical record.
 
-        Parameters
-        ----------
-        case_id : int
-            The ID of the case to mark as processed
-        guild_id : int | None
-            Deprecated parameter kept for backward compatibility (unused)
-
         Returns
         -------
         bool
             True if the case was updated, False if not found
         """
🧹 Nitpick comments (39)
scripts/dev/docstring_coverage.py (1)

15-17: Consider adding an explicit type annotation to the constant.

While the constant effectively replaces the magic number and improves maintainability, adding an explicit type annotation would enhance clarity and align with the codebase's type-safety practices.

🔎 Proposed enhancement
 # Constants
-ERROR_EXIT_CODE_THRESHOLD = 100
+ERROR_EXIT_CODE_THRESHOLD: int = 100
scripts/docs/lint.py (1)

23-25: Nice refactoring to extract the magic number.

The introduction of YAML_FRONTMATTER_PARTS improves maintainability by making the frontmatter validation check more explicit.

Optional refinements for clarity

Consider these minor improvements:

  1. Add a type annotation (as per coding guidelines):
-YAML_FRONTMATTER_PARTS = 3
+YAML_FRONTMATTER_PARTS: int = 3
  1. Clarify the constant's purpose with a comment to document its relationship to the maxsplit=2 at line 52:
 # Constants
-YAML_FRONTMATTER_PARTS = 3
+# Minimum parts expected after splitting by "---" with maxsplit=2:
+# ["", frontmatter_content, document_content]
+YAML_FRONTMATTER_PARTS: int = 3
  1. Consider a more descriptive name like MIN_FRONTMATTER_SPLIT_PARTS to clarify that this represents the expected split result length, not the number of frontmatter sections.

Also applies to: 53-53

src/tux/modules/utility/poll.py (1)

135-136: Consider using start=1 for consistency.

For consistency with the refactored loop at line 152, consider updating this line to use enumerate(options_list, start=1) as well.

🔎 Proposed refactor for consistency
-        description = "\n".join(
-            [f"{num + 1}\u20e3 {option}" for num, option in enumerate(options_list)],
-        )
+        description = "\n".join(
+            [f"{num}\u20e3 {option}" for num, option in enumerate(options_list, start=1)],
+        )
scripts/db/queries.py (1)

54-55: Type annotations improved with AsyncSession.

The parameter type has been correctly tightened from Any to AsyncSession. The return type changed from list[tuple[Any, Any, str, str]] to Any, which is less specific but acceptable.

💡 Optional: Consider a more specific return type

For slightly better type safety, you could specify the return type more precisely:

 async def _get_long_queries(
     session: AsyncSession,
-) -> Any:
+) -> list[Any]:
     result = await session.execute(text(LONG_RUNNING_QUERIES_SQL))
     return result.fetchall()

This reflects that fetchall() returns a list of Row objects.

scripts/db/health.py (1)

26-26: LGTM!

Explicit -> None return type annotation improves type safety.

💡 Optional: Consider NoReturn for functions that always raise

Since _fail() always raises an exception and never returns normally, you could use NoReturn for even more precise typing:

+from typing import NoReturn
+
-def _fail() -> None:
+def _fail() -> NoReturn:
     """Raise Exit(1) to satisfy Ruff's TRY301 rule."""
     raise Exit(1)

This helps type checkers understand that code after calling _fail() is unreachable.

scripts/db/tables.py (1)

40-40: Type annotations improved with AsyncSession.

The parameter type has been correctly tightened to AsyncSession. The Any return type is acceptable for fetchall() results.

💡 Optional: Consider a more specific return type

For slightly better type safety, you could specify the return type more precisely:

 async def _get_tables(session: AsyncSession) -> Any:
+) -> list[Any]:
     result = await session.execute(
         text("""

This reflects that fetchall() returns a list of Row objects.

scripts/config/validate.py (1)

19-20: LGTM! Good refactoring to extract the magic number.

The constant follows naming conventions and improves maintainability by providing a single source of truth for the display truncation length.

Optional: Add explicit type hint for clarity
 # Constants
-DB_URL_DISPLAY_LENGTH = 50
+DB_URL_DISPLAY_LENGTH: int = 50

This makes the type explicit, though it's not strictly necessary for constants.

tests/services/test_service_wrappers.py (1)

166-209: Consider testing the public interface instead of private methods.

The integration tests directly invoke service._execute(), which is a private method. While this works for testing internal integration, it couples the tests to implementation details. If the services expose a public method for code execution, prefer testing through that interface for better maintainability.

tests/database/test_migration_url_conversion.py (1)

61-70: Consider adding pass or ... to skipped test bodies for clarity.

While valid Python (the docstring is a statement), skipped tests typically include pass or ... to explicitly indicate the function body is intentionally empty. This improves readability and makes the intent clearer.

🔎 Suggested addition
     @pytest.mark.skip(reason="Requires mocking CONFIG.database_url")
     def test_offline_mode_converts_url(self):
         """Test that offline mode converts async URLs."""
         # This would require mocking CONFIG.database_url
         # For now, we test the conversion logic directly
+        ...
 
     @pytest.mark.skip(reason="Requires mocking CONFIG.database_url")
     def test_online_mode_converts_url(self):
         """Test that online mode converts async URLs."""
         # This would require mocking CONFIG.database_url and database connection
         # For now, we test the conversion logic directly
+        ...
src/tux/services/wrappers/tldr.py (1)

24-25: Consider making these constants environment-configurable for consistency.

The existing configuration constants (lines 21-23) follow a pattern of reading from environment variables with defaults. For consistency and operational flexibility, consider making these new constants configurable:

🔎 Optional refactor for environment-configurable constants
-ARCHIVE_DOWNLOAD_TIMEOUT_SECONDS: int = 30
-DISCORD_EMBED_MAX_LENGTH: int = 4000
+ARCHIVE_DOWNLOAD_TIMEOUT_SECONDS: int = int(os.getenv("TLDR_ARCHIVE_TIMEOUT", "30"))
+DISCORD_EMBED_MAX_LENGTH: int = int(os.getenv("DISCORD_EMBED_MAX_LENGTH", "4000"))
src/tux/services/wrappers/wandbox.py (1)

61-67: Redundant raise_for_status() call.

The http_client.post() method already calls response.raise_for_status() internally before returning (see src/tux/services/http_client.py lines 218-219). The call at line 67 is redundant and can be removed.

🔎 Proposed fix
     try:
         response = await http_client.post(
             url,
             json=payload,
             headers=headers,
             timeout=15.0,
         )
-        response.raise_for_status()
     except httpx.ReadTimeout as e:
src/tux/services/wrappers/godbolt.py (4)

88-107: Redundant raise_for_status() call in sendresponse.

The http_client.get() method already calls response.raise_for_status() internally before returning (see src/tux/services/http_client.py lines 191-192). Line 90 is redundant.

🔎 Proposed fix
     try:
         response = await http_client.get(url, timeout=15.0)
-        response.raise_for_status()
     except httpx.ReadTimeout as e:

241-260: Redundant raise_for_status() call in getoutput.

Same issue as sendresponsehttp_client.post() already calls raise_for_status() internally. Line 243 is redundant.

🔎 Proposed fix
     try:
         response = await http_client.post(url_comp, json=payload, timeout=15.0)
-        response.raise_for_status()
     except httpx.ReadTimeout as e:

323-342: Redundant raise_for_status() call in generateasm.

Same pattern—http_client.post() already handles status checking. Line 325 is redundant.

🔎 Proposed fix
     try:
         response = await http_client.post(url_comp, json=payload, timeout=15.0)
-        response.raise_for_status()
     except httpx.ReadTimeout as e:

181-260: Consider extracting common POST logic to reduce duplication.

getoutput() and generateasm() share nearly identical structure—payload construction, POST request, and error handling. The only differences are skipAsm (True vs False) and execute filter (True vs False). Consider extracting a helper function.

🔎 Proposed refactor
async def _compile_request(
    code: str,
    lang: str,
    compileroptions: str | None,
    skip_asm: bool,
    execute: bool,
) -> str:
    """Internal helper for Godbolt compile requests."""
    url_comp = f"{url}/api/compiler/{lang}/compile"
    copt = compileroptions if compileroptions is not None else ""

    payload: Payload = {
        "source": code,
        "options": {
            "userArguments": copt,
            "compilerOptions": {"skipAsm": skip_asm, "executorRequest": False},
            "filters": {
                "binary": False,
                "binaryObject": False,
                "commentOnly": True,
                "demangle": True,
                "directives": True,
                "execute": execute,
                "intel": True,
                "labels": True,
                "libraryCode": True,
                "trim": True,
                "debugCalls": True,
            },
            "tools": [],
            "libraries": [],
        },
        "lang": lang,
        "allowStoreCodeDebug": True,
    }

    try:
        response = await http_client.post(url_comp, json=payload, timeout=15.0)
    except httpx.ReadTimeout as e:
        raise TuxAPIConnectionError(service_name="Godbolt", original_error=e) from e
    except httpx.RequestError as e:
        raise TuxAPIConnectionError(service_name="Godbolt", original_error=e) from e
    except httpx.HTTPStatusError as e:
        if e.response.status_code == HTTP_NOT_FOUND:
            raise TuxAPIResourceNotFoundError(
                service_name="Godbolt",
                resource_identifier=lang,
            ) from e
        raise TuxAPIRequestError(
            service_name="Godbolt",
            status_code=e.response.status_code,
            reason=e.response.text,
        ) from e
    else:
        return response.text


async def getoutput(code: str, lang: str, compileroptions: str | None = None) -> str:
    """..."""
    return await _compile_request(code, lang, compileroptions, skip_asm=True, execute=True)


async def generateasm(code: str, lang: str, compileroptions: str | None = None) -> str:
    """..."""
    return await _compile_request(code, lang, compileroptions, skip_asm=False, execute=False)

Also applies to: 263-342

scripts/dev/clean.py (1)

23-38: Consider NumPy-style docstrings for consistency.

The function logic and error handling are solid. However, per coding guidelines, NumPy docstrings should be used for documenting functions. Consider expanding the docstring to document parameters and return values.

Example:

def _remove_item(item: Path, project_root: Path) -> tuple[int, int]:
    """
    Remove a file or directory and return statistics.

    Parameters
    ----------
    item : Path
        The file or directory to remove.
    project_root : Path
        The project root path for relative path display.

    Returns
    -------
    tuple[int, int]
        A tuple of (count, size) where count is 1 if removed, 0 otherwise,
        and size is the bytes freed.
    """

As per coding guidelines, NumPy docstrings should be used for documenting Python functions and classes.

tests/modules/test_moderation_service_integration.py (1)

39-106: Optional: Consider adding return type hints to test fixtures.

While the fixtures have good docstrings and appropriate parameter types, adding return type hints would improve type safety and align with the coding guideline to "include complete type hints in all public APIs."

Example for mock_db_service:

def mock_db_service(self) -> MagicMock:
    """Create a mock database service."""
    ...

This is a minor enhancement and not critical for test code.

src/tux/modules/tools/tldr.py (3)

73-80: Consider structured error handling instead of string matching.

The error detection relies on checking if result_msg.startswith("Failed"), which is fragile. If TldrClient.update_tldr_cache changes its error message format, this check will fail silently.

🔎 Suggested improvement

Consider having TldrClient.update_tldr_cache raise exceptions for errors instead of returning error messages in strings, or return a structured result (e.g., a dataclass or tuple with success status). For example:

# If TldrClient can be modified to raise exceptions:
try:
    await asyncio.to_thread(
        TldrClient.update_tldr_cache,
        lang_code,
    )
    logger.debug(f"Cache update for '{lang_code}': success")
except TldrUpdateError as e:
    logger.error(f"Cache update for '{lang_code}': {e}")

Alternatively, if the return format must remain, consider documenting this contract explicitly or using a more robust check.


167-179: Add parameter documentation for consistency.

The platform_autocomplete method is missing parameter documentation for interaction and current, while the other autocomplete methods (command_autocomplete and language_autocomplete) include them. Adding these would improve consistency and completeness.

🔎 Proposed documentation enhancement
     async def platform_autocomplete(
         self,
         interaction: discord.Interaction,
         current: str,
     ) -> list[app_commands.Choice[str]]:
         """
         Autocomplete for the platform parameter.
 
+        Parameters
+        ----------
+        interaction : discord.Interaction
+            The interaction triggering autocomplete.
+        current : str
+            Current input value.
+
         Returns
         -------
         list[app_commands.Choice[str]]
             List of platform choices for autocomplete.
         """

253-255: Simplify redundant boolean conversions.

The explicit bool() conversions are unnecessary since the parameters are already typed as bool | None and have default values in the function signature. The special handling for show_long on line 254 is also redundant because the parameter defaults to True, so it will only be None if explicitly passed.

🔎 Simplified version
         await self._handle_tldr_command(
             source=interaction,
             command_name=command,
             platform=platform,
             language=language,
-            show_short=bool(show_short),
-            show_long=bool(show_long) if show_long is not None else True,
-            show_both=bool(show_both),
+            show_short=show_short or False,
+            show_long=show_long if show_long is not None else True,
+            show_both=show_both or False,
         )

Or even simpler, rely on the defaults in _handle_tldr_command:

         await self._handle_tldr_command(
             source=interaction,
             command_name=command,
             platform=platform,
             language=language,
-            show_short=bool(show_short),
-            show_long=bool(show_long) if show_long is not None else True,
-            show_both=bool(show_both),
+            show_short=show_short or False,
+            show_long=True if show_long is None else show_long,
+            show_both=show_both or False,
         )
src/tux/modules/info/info.py (1)

765-823: Consider using the new helper in paginated_embed for consistency.

The paginated_embed method still directly calls EmbedCreator.create_embed instead of using the new _create_info_embed helper. While this works fine, using the helper would provide more consistency across the module.

🔎 Proposed refactor
-        embed: discord.Embed = EmbedCreator.create_embed(
-            embed_type=EmbedType.INFO,
+        embed: discord.Embed = self._create_info_embed(
             title=title,
-            custom_color=discord.Color.blurple(),
         )
src/tux/services/handlers/error/cog.py (1)

161-179: Consider removing unused parameter.

The _config parameter (renamed from config with a leading underscore) is never used in the function body. The leading underscore convention indicates an intentionally unused parameter, but if it's truly not needed, consider removing it from the signature to simplify the interface.

🔎 Proposed refactor to remove unused parameter
 async def _send_error_response(
     self,
     source: commands.Context[Tux] | discord.Interaction,
     embed: discord.Embed,
-    _config: ErrorHandlerConfig,
 ) -> None:
     """Send error response to user."""

And update the call site at line 99:

-        await self._send_error_response(source, embed, config)
+        await self._send_error_response(source, embed)
tests/fixtures/database_fixtures.py (1)

32-33: Consider more specific exception handling in cleanup blocks.

The bare except Exception: pass blocks silence all exceptions during cleanup. While acceptable for test teardown, consider logging failures or catching more specific exceptions (e.g., RuntimeError, OSError) to avoid masking unexpected issues.

🔎 Suggested refinement
     finally:
         try:
             manager.stop()
-        except Exception:
-            pass
+        except (RuntimeError, OSError) as e:
+            logger.warning(f"Failed to stop PGlite manager: {e}")

Apply similar pattern at lines 51-52.

Also applies to: 51-52

src/tux/database/controllers/guild_config.py (1)

10-21: Clarify the TYPE_CHECKING import pattern.

The current pattern imports DatabaseService in both the TYPE_CHECKING and non-TYPE_CHECKING branches, which is redundant. If DatabaseService is needed at runtime (lines 18-21), there's no need to also import it under TYPE_CHECKING (lines 15-16).

Recommendation: Choose one approach:

  • If DatabaseService is needed at runtime, import it normally without TYPE_CHECKING guards
  • If it's only needed for type hints, import it only under TYPE_CHECKING and use string literals for forward references where needed

This pattern appears in multiple controller files (guild_config.py, starboard.py, levels.py, guild.py, snippet.py) and could be simplified consistently across the codebase.

🔎 Simplified import pattern
-from typing import TYPE_CHECKING, Any
+from typing import Any

+from tux.database.service import DatabaseService
 from tux.database.controllers.base import BaseController
 from tux.database.models import GuildConfig
-
-if TYPE_CHECKING:
-    from tux.database.service import DatabaseService
-
-if not TYPE_CHECKING:
-    from tux.database.service import DatabaseService

Or, if avoiding circular imports is the goal, use TYPE_CHECKING only:

 from typing import TYPE_CHECKING, Any

 from tux.database.controllers.base import BaseController
 from tux.database.models import GuildConfig

 if TYPE_CHECKING:
     from tux.database.service import DatabaseService
-
-if not TYPE_CHECKING:
-    from tux.database.service import DatabaseService
tests/core/test_permission_bypass.py (1)

3-3: Consider removing unused AsyncMock import.

The AsyncMock import appears to be unused in this file. All mocks in the test use MagicMock or AsyncMock from patched locations.

🔎 Proposed fix
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import MagicMock, patch
tests/services/test_error_extractors_arguments.py (1)

29-38: Potential duplicate test - verify test scenarios are distinct.

The test implementation for test_extract_missing_argument_details_with_string is identical to test_extract_missing_argument_details_with_param (lines 18-27), only changing the param name from "user" to "member". The docstring indicates these should test different scenarios ("with string format" vs regular param object), but both create a MagicMock param object with a name attribute.

If these tests are meant to verify different input formats, please update the implementation to reflect those differences. Otherwise, consider removing the duplicate.

tests/database/test_database_model_performance.py (1)

59-95: Consider using strict=True in the zip call.

Since both guilds and configs are created in the same loop with the same range, they are guaranteed to have the same length. Using strict=True would make this expectation explicit and improve code safety.

🔎 Proposed change
-            for guild, config in zip(guilds, configs, strict=False):
+            for guild, config in zip(guilds, configs, strict=True):
tests/database/test_database_model_relationships.py (2)

53-74: Consider using a more specific regex pattern for the exception match.

The current regex r".*" matches any exception. While the subsequent assertion checks for "foreign key" or "constraint" in the error message, specifying a more precise regex (e.g., r".*(foreign key|constraint).*") would make the test expectation clearer and fail faster if the wrong exception is raised.

🔎 Proposed change
-            with pytest.raises(Exception, match=r".*") as exc_info:
+            with pytest.raises(Exception, match=r".*(foreign key|constraint).*") as exc_info:
                 await session.commit()
 
-            # Check that it's a constraint violation
-            error_msg = str(exc_info.value).lower()
-            assert "foreign key" in error_msg or "constraint" in error_msg

76-98: Consider using a more specific regex pattern for the exception match.

Similar to the foreign key test, using r".*(unique|constraint).*" would make the test expectation more explicit and provide faster feedback if the wrong exception type is raised.

🔎 Proposed change
-            with pytest.raises(Exception, match=r".*") as exc_info:
+            with pytest.raises(Exception, match=r".*(unique|constraint).*") as exc_info:
                 await session.commit()
 
-            # Check that it's a constraint violation
-            error_msg = str(exc_info.value).lower()
-            assert "unique" in error_msg or "constraint" in error_msg
tests/fixtures/data_fixtures.py (1)

16-20: Consider using a more specific return type.

The fixture correctly creates and returns a sample guild. While Any is acceptable, consider using Guild as the return type for better type checking.

🔎 Optional improvement
+from tux.database.models.models import Guild
+
 @pytest.fixture(scope="function")
-async def sample_guild(guild_controller: GuildController) -> Any:
+async def sample_guild(guild_controller: GuildController) -> Guild:
     """Sample guild for testing."""
tests/modules/test_moderation_critical_issues.py (3)

343-344: Duplicate comment can be removed.

Line 344 duplicates the comment on line 343. Consider removing the redundant comment.

             with patch.object(
                 moderation_coordinator,
                 "_send_response_embed",
                 new_callable=AsyncMock,
             ):
                 # Condition checks are now handled via decorators at command level
-                # Condition checks are handled at command level

581-604: Test relies on timing and has weak assertions.

The asyncio.sleep(0.1) introduces flakiness. Additionally, the assertion only checks if at least one action was called, which doesn't definitively verify the race condition handling. Consider:

  1. Using a synchronization primitive (e.g., asyncio.Event) instead of sleep
  2. Adding more specific assertions about the expected behavior

The comment on lines 592-597 acknowledges this uncertainty, which is reasonable for a race condition test, but the sleep should be avoided if possible.


846-883: Mock classes could benefit from type hints.

Per coding guidelines, consider adding type hints to the mock class parameters for consistency with the rest of the codebase.

🔎 Suggested improvements
 class MockMember:
     """Mock Discord Member for testing."""
 
-    def __init__(self, user_id: int = 555666777):
+    def __init__(self, user_id: int = 555666777) -> None:
         self.id = user_id
         self.name = "TestUser"
         self.top_role = MockRole(position=5)
         self.display_avatar = MockAvatar()


 class MockBotMember:
     """Mock bot member with permissions."""
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.guild_permissions = MockPermissions()


 class MockPermissions:
     """Mock guild permissions."""
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.ban_members = True
         self.kick_members = True
         self.moderate_members = True


 class MockRole:
     """Mock Discord Role."""
 
-    def __init__(self, position: int = 5):
+    def __init__(self, position: int = 5) -> None:
         self.position = position


 class MockAvatar:
     """Mock Discord Avatar."""
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.url = "https://example.com/avatar.png"
src/tux/database/controllers/afk.py (1)

16-20: Redundant conditional import pattern.

The if TYPE_CHECKING / if not TYPE_CHECKING pattern is redundant. At runtime, TYPE_CHECKING is always False, so the second block always executes. The first block only affects type checkers. This can be simplified:

 if TYPE_CHECKING:
     from tux.database.service import DatabaseService

-if not TYPE_CHECKING:
-    from tux.database.service import DatabaseService
+else:
+    from tux.database.service import DatabaseService

Or simply use a single unconditional import if DatabaseService is always needed at runtime:

-if TYPE_CHECKING:
-    from tux.database.service import DatabaseService
-
-if not TYPE_CHECKING:
-    from tux.database.service import DatabaseService
+from tux.database.service import DatabaseService

The current pattern achieves the same result but is confusing to read.

tests/fixtures/utils.py (1)

7-7: Add type hints for **overrides parameters.

Per coding guidelines, all public APIs should include complete type hints. The factory functions are missing type annotations for the **overrides parameters.

🔎 Suggested fix
-def create_mock_guild(**overrides) -> dict[str, Any]:
+def create_mock_guild(**overrides: Any) -> dict[str, Any]:

Apply the same pattern to create_mock_user, create_mock_channel, and create_mock_interaction.

tests/database/test_database_data_integrity.py (1)

79-84: Consider catching a more specific exception type.

The broad except Exception will catch any error, not just the expected unique constraint violation. Consider catching sqlalchemy.exc.IntegrityError specifically to ensure the test validates the correct behavior.

🔎 Suggested improvement
+from sqlalchemy.exc import IntegrityError
+
 # Now try to add duplicate in a new transaction
 # Note: This intentionally creates an identity key conflict to test constraint behavior
 # The SAWarning is expected and indicates the test is working correctly
 try:
     guild2 = Guild(id=TEST_GUILD_ID, case_count=1)  # Same ID - should fail
     session.add(guild2)
     await session.commit()  # This should fail due to unique constraint
-except Exception:
+except IntegrityError:
     await session.rollback()  # Rollback the failed transaction
src/tux/core/setup/database_setup.py (1)

95-95: Use asyncio.get_running_loop() instead of asyncio.get_event_loop().

Since _upgrade_head_if_needed() is an async method, calling asyncio.get_event_loop() is deprecated in Python 3.10+. Use asyncio.get_running_loop() to retrieve the currently running event loop from within the coroutine.

-        loop = asyncio.get_event_loop()
+        loop = asyncio.get_running_loop()
src/tux/core/prefix_manager.py (2)

121-122: Consider monitoring fire-and-forget persistence task failures.

The fire-and-forget pattern for _persist_prefix means failures are only logged, with no higher-level notification or metrics. If persistence consistently fails (e.g., transient DB issues), the cache and database may diverge silently.

Consider:

  • Adding error metrics/counters for persistence failures
  • Implementing a retry queue for failed persistence operations
  • Or at minimum, ensure logging is monitored/alerted

</invoke_end -->


223-228: Document the 1000-guild limit for cache loading.

The limit=1000 parameter caps the initial cache load to 1000 guilds. For bots in more than 1000 guilds, some prefixes won't be cached at startup (they'll be lazy-loaded on first use). Consider:

  • Documenting this limitation in the method docstring
  • Or implementing pagination to load all guilds
  • Or increasing the limit if the bot is expected to be in many guilds

</invoke_end -->

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/tux/database/controllers/snippet.py (1)

412-414: Incorrect SQL NULL comparison - use SQLAlchemy's is_not(None) instead.

Using Python's is not None performs an identity check at the Python level, not a SQL IS NOT NULL comparison. This will always evaluate to True because Snippet.alias is a column attribute, causing the filter to have no effect.

🔎 Proposed fix
     async def get_all_aliases(self, guild_id: int) -> list[Snippet]:
         ...
         return await self.find_all(
-            filters=(Snippet.alias is not None) & (Snippet.guild_id == guild_id),
+            filters=(Snippet.alias.is_not(None)) & (Snippet.guild_id == guild_id),
         )
tests/core/test_dynamic_permission_system.py (1)

194-200: Test assertion will fail - docstring mismatch.

The function's docstring on line 196 is """Execute my special command.""" but the assertion on line 200 expects "This is my special command.". This test will fail.

🔎 Proposed fix - align the docstring with the assertion
         @requires_command_permission()
         async def my_special_command(ctx: commands.Context[Tux]) -> None:
-            """Execute my special command."""
+            """This is my special command."""

         # Should preserve function metadata
         assert my_special_command.__name__ == "my_special_command"
         assert my_special_command.__doc__ == "This is my special command."
src/tux/database/controllers/case.py (1)

277-297: Function signature doesn't match implementation.

The get_recent_cases function accepts a hours parameter but currently ignores it and returns all cases. This creates a misleading API where callers may expect time-based filtering that doesn't occur.

Consider one of these approaches:

  • Remove the hours parameter until filtering is implemented
  • Raise NotImplementedError if hours != 24 (the default)
  • Add a deprecation warning documenting the limitation

Would you like me to generate a fix that either removes the parameter or adds a NotImplementedError guard?

♻️ Duplicate comments (4)
tests/fixtures/database_fixtures.py (2)

30-33: Exception handling improved as requested.

The teardown exception is now properly logged, addressing the past review comment. This implementation correctly preserves test resilience while maintaining visibility of failures.


47-53: Cleanup exception handling improved.

The addition of explanatory comments and exception logging addresses the past review comment. The implementation correctly documents that cleanup failures should not break tests while maintaining observability.

tests/conftest.py (1)

137-187: Type hints added, but consider removing duplication.

The type hints properly address the past review comment. However, these four functions duplicate identical implementations in tests/fixtures/utils.py (lines 6-99), which have more complete NumPy-style docstrings.

Consider importing these functions from tests.fixtures.utils instead of duplicating them here, or removing them from utils.py if conftest.py is the intended location.

🔎 Proposed fix to eliminate duplication
+from tests.fixtures.utils import (
+    create_mock_channel,
+    create_mock_guild,
+    create_mock_interaction,
+    create_mock_user,
+)
-
-# Test utility functions (inspired by organizex patterns)
-def create_mock_guild(**overrides: Any) -> dict[str, Any]:
-    """Create mock Discord guild data for testing."""
-    default_data = {
-        "id": 123456789012345678,
-        "name": "Test Guild",
-        "member_count": 100,
-        "owner_id": 987654321098765432,
-    }
-    default_data.update(overrides)
-    return default_data
-
-
-def create_mock_user(**overrides: Any) -> dict[str, Any]:
-    """Create mock Discord user data for testing."""
-    default_data = {
-        "id": 987654321098765432,
-        "name": "testuser",
-        "discriminator": "1234",
-        "display_name": "Test User",
-        "bot": False,
-        "mention": "<@987654321098765432>",
-    }
-    default_data.update(overrides)
-    return default_data
-
-
-def create_mock_channel(**overrides: Any) -> dict[str, Any]:
-    """Create mock Discord channel data for testing."""
-    default_data = {
-        "id": 876543210987654321,
-        "name": "test-channel",
-        "mention": "<#876543210987654321>",
-        "type": "text",
-    }
-    default_data.update(overrides)
-    return default_data
-
-
-def create_mock_interaction(**overrides: Any) -> dict[str, Any]:
-    """Create mock Discord interaction data for testing."""
-    default_data = {
-        "user": create_mock_user(),
-        "guild": create_mock_guild(),
-        "guild_id": 123456789012345678,
-        "channel": create_mock_channel(),
-        "channel_id": 876543210987654321,
-        "command": {"qualified_name": "test_command"},
-    }
-    default_data.update(overrides)
-    return default_data
tests/database/test_database_service.py (1)

137-141: Exception pattern properly narrowed.

The updated regex pattern r".*(duplicate key|unique constraint|integrity)" addresses the past review comment by matching specific integrity error messages while remaining flexible across database backends.

🧹 Nitpick comments (11)
scripts/dev/clean.py (1)

44-46: Consider simplifying the single-directory loop.

Since only the scripts directory is processed now, the loop could be simplified by directly using the path instead of iterating over a single-item list.

🔎 Optional simplification
-    for dir_path in [
-        project_root / "scripts",
-    ]:
+    dir_path = project_root / "scripts"
+    if True:  # Keep the same indentation structure

Alternatively, you could keep the list structure if you anticipate adding more directories in the future.

tests/shared/test_config_generation.py (3)

84-91: Strengthen parsed TOML assertions.

The TOML parsing on lines 88-91 addresses the validation concern from the past review, but the test could be more robust. Lines 84-86 still use loose "or" fallbacks, and the parsed dict isn't checked for expected keys.

🔎 Proposed fix to validate parsed structure
-    # Verify output contains commented field names and descriptions
-    # Built-in generator uses different format - check for descriptions and commented defaults
-    assert "Enable debug mode" in toml_output or "debug" in toml_output.lower()
-    assert "Server port" in toml_output or "port" in toml_output.lower()
-    assert "Application name" in toml_output or "name" in toml_output.lower()
-
     # Verify it's valid TOML by actually parsing it
     parsed = tomllib.loads(toml_output)
     assert isinstance(parsed, dict)
     assert len(parsed) > 0
+    
+    # Verify expected keys are present in parsed structure
+    assert "debug" in parsed
+    assert "port" in parsed
+    assert "name" in parsed

59-63: Extract repeated PSESettings initialization to a fixture.

The same PSESettings initialization pattern with warning suppression is repeated identically across all four tests. This violates DRY principles and makes maintenance harder.

🔎 Proposed fixture to eliminate duplication

Add a pytest fixture at the module level:

import pytest

@pytest.fixture
def pse_settings() -> PSESettings:
    """Create PSESettings with warning suppression."""
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", message=".*pyproject_toml_table_header.*")
        return PSESettings(
            root_dir=Path.cwd(),
            project_dir=Path.cwd(),
            respect_exclude=True,
        )

Then update each test to use the fixture:

-    # Generate TOML (suppress PSESettings warning about pyproject_toml_table_header)
-    with warnings.catch_warnings():
-        warnings.filterwarnings("ignore", message=".*pyproject_toml_table_header.*")
-        pse_settings = PSESettings(
-            root_dir=Path.cwd(),
-            project_dir=Path.cwd(),
-            respect_exclude=True,
-        )
+def test_generate_toml_format(pse_settings: PSESettings) -> None:

Apply the same pattern to all four test functions.

Also applies to: 121-125, 166-170, 226-230


252-259: Consider simplifying the nested section search.

The next() expression with a nested generator is correct but could be more readable with intermediate steps or a simpler approach.

🔎 Alternative approach for clarity
-    nested_section = next(
-        (
-            value
-            for _key, value in parsed.items()
-            if isinstance(value, dict) and "host" in value
-        ),
-        None,
-    )
+    # Find the nested section containing database config
+    nested_section = None
+    for value in parsed.values():
+        if isinstance(value, dict) and "host" in value:
+            nested_section = value
+            break
src/tux/services/handlers/error/cog.py (1)

99-99: LGTM! Good refactoring to remove unused parameter.

The config parameter was correctly removed from _send_error_response since it was never used in the method body. All config-dependent logic (whether to send the embed and how to format it) appropriately happens in _handle_error before calling this method. This simplifies the method signature and clarifies its single responsibility: sending the pre-formatted embed to the user.

Also applies to: 161-165

src/tux/modules/tools/tldr.py (2)

174-192: Add parameter documentation for consistency.

The command_autocomplete method now includes parameter documentation (lines 133-139), but platform_autocomplete and language_autocomplete lack similar documentation. For consistency and adherence to NumPy docstring style, consider documenting the interaction and current parameters in these methods.

🔎 Suggested documentation additions

For platform_autocomplete:

     async def platform_autocomplete(
         self,
         interaction: discord.Interaction,
         current: str,
     ) -> list[app_commands.Choice[str]]:
         """
         Autocomplete for the platform parameter.
 
+        Parameters
+        ----------
+        interaction : discord.Interaction
+            The interaction triggering autocomplete.
+        current : str
+            Current input value.
+
         Returns
         -------
         list[app_commands.Choice[str]]
             List of platform choices for autocomplete.
         """

Similar additions should be made for language_autocomplete.

Also applies to: 194-227


260-262: Consider simplifying boolean parameter handling.

The boolean casting for show_short and show_both is straightforward, but show_long uses a more complex expression:

show_long=bool(show_long) if show_long is not None else True

Since the parameter already has a default value of True (line 251), you could simplify to:

show_long=show_long if show_long is not None else True

This preserves the same behavior while being slightly more direct. The bool() cast is defensive but may be unnecessary given Discord's type coercion.

tests/database/test_database_migrations.py (2)

103-112: Consider using a more specific exception type.

Using pytest.raises(Exception, match=r".*") catches any exception. While this works, using more specific exception types (e.g., IntegrityError from SQLAlchemy) would make the test more precise and less likely to pass on unrelated errors.

🔎 Suggested improvement
+from sqlalchemy.exc import IntegrityError
+
 # Test 1: Create config without guild (should raise IntegrityError)
-with pytest.raises(Exception, match=r".*") as exc_info:
+with pytest.raises(IntegrityError):
     await guild_config_controller.get_or_create_config(
         guild_id=999999999999999999,  # Non-existent guild
         prefix="!",
     )
-# Should fail due to foreign key constraint violation
-assert (
-    "foreign key" in str(exc_info.value).lower()
-    or "constraint" in str(exc_info.value).lower()
-)

309-311: Using suppress hides whether an exception was actually raised.

While suppress is cleaner, it makes it impossible to verify that the expected exception type was actually raised. If the code changes to silently succeed (a bug), this test would still pass.

🔎 Suggested improvement
-        with suppress(RuntimeError, ConnectionError):
-            await guild_controller.create_guild(guild_id=TEST_GUILD_ID)
-            # If we get here, the service should handle disconnection gracefully
+        with pytest.raises((RuntimeError, ConnectionError)):
+            await guild_controller.create_guild(guild_id=TEST_GUILD_ID)

Or if graceful handling (no exception) is also acceptable:

-        with suppress(RuntimeError, ConnectionError):
-            await guild_controller.create_guild(guild_id=TEST_GUILD_ID)
-            # If we get here, the service should handle disconnection gracefully
+        try:
+            await guild_controller.create_guild(guild_id=TEST_GUILD_ID)
+            # Graceful handling - no exception raised
+        except (RuntimeError, ConnectionError):
+            # Expected behavior for disconnected service
+            pass
src/tux/core/prefix_manager.py (2)

121-122: Minor inconsistency with past review suggestion.

The past review comment recommended removing the assignment entirely and using just asyncio.create_task(self._persist_prefix(guild_id, prefix)) without any variable binding. The current implementation uses _ = with a noqa comment, which achieves a similar effect but doesn't fully match the suggested fix.

Apply the originally suggested fix
-        # Fire-and-forget task to persist to database
-        _ = asyncio.create_task(self._persist_prefix(guild_id, prefix))  # noqa: RUF006
+        # Fire-and-forget: persist to database asynchronously
+        asyncio.create_task(self._persist_prefix(guild_id, prefix))

156-166: Consider simplifying the try/except/else pattern.

The current implementation uses else after except, which is valid but creates an indirect flow. Since prefix is only used in the success path, you could return it directly from the try block for clarity.

Simpler alternative
-            prefix = guild_config.prefix
-            self._prefix_cache[guild_id] = prefix
+            prefix = guild_config.prefix
+            self._prefix_cache[guild_id] = prefix
+            return prefix

         except Exception as e:
             logger.warning(
                 f"Failed to load prefix for guild {guild_id}: {type(e).__name__}",
             )
             return self._default_prefix
-        else:
-            return prefix
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27992f2 and 873c0f8.

📒 Files selected for processing (31)
  • docs/content/developer/best-practices/debugging.md
  • scripts/dev/clean.py
  • scripts/tux/start.py
  • src/tux/core/base_cog.py
  • src/tux/core/prefix_manager.py
  • src/tux/database/controllers/__init__.py
  • src/tux/database/controllers/afk.py
  • src/tux/database/controllers/case.py
  • src/tux/database/controllers/guild.py
  • src/tux/database/controllers/guild_config.py
  • src/tux/database/controllers/levels.py
  • src/tux/database/controllers/reminder.py
  • src/tux/database/controllers/snippet.py
  • src/tux/database/controllers/starboard.py
  • src/tux/modules/tools/tldr.py
  • src/tux/services/handlers/error/cog.py
  • tests/conftest.py
  • tests/core/test_dynamic_permission_system.py
  • tests/database/test_database_data_integrity.py
  • tests/database/test_database_migrations.py
  • tests/database/test_database_model_creation.py
  • tests/database/test_database_model_performance.py
  • tests/database/test_database_model_queries.py
  • tests/database/test_database_model_relationships.py
  • tests/database/test_database_service.py
  • tests/fixtures/data_fixtures.py
  • tests/fixtures/database_fixtures.py
  • tests/fixtures/sentry_fixtures.py
  • tests/services/test_error_extractors.py
  • tests/services/test_error_extractors_integration.py
  • tests/shared/test_config_generation.py
🚧 Files skipped from review as they are similar to previous changes (12)
  • tests/database/test_database_model_performance.py
  • docs/content/developer/best-practices/debugging.md
  • src/tux/database/controllers/levels.py
  • tests/fixtures/data_fixtures.py
  • tests/database/test_database_model_queries.py
  • scripts/tux/start.py
  • src/tux/database/controllers/starboard.py
  • src/tux/database/controllers/guild.py
  • tests/services/test_error_extractors_integration.py
  • tests/database/test_database_model_creation.py
  • src/tux/database/controllers/guild_config.py
  • tests/database/test_database_model_relationships.py
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with the form Type | None instead of Optional[Type] in Python
Use NumPy docstrings for documenting Python functions and classes
Prefer absolute imports, but relative imports are allowed within the same module in Python
Use import grouping in Python: stdlib → third-party → local imports
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of the Python file unless absolutely necessary
Use async/await for I/O operations and asynchronous code in services and database operations
Use Pydantic for data validation in Python models and services
Log errors with context and handle Discord rate limits appropriately
Do not include secrets in code; use environment variables for configuration
Validate all user inputs in Python code
Implement proper permission checks in Discord commands and services
Keep Python files to a maximum of 1600 lines
Use descriptive filenames for Python modules
Include complete type hints in all public APIs
Include docstrings for public API functions and classes

Files:

  • tests/services/test_error_extractors.py
  • src/tux/database/controllers/snippet.py
  • src/tux/services/handlers/error/cog.py
  • src/tux/core/base_cog.py
  • src/tux/database/controllers/afk.py
  • tests/database/test_database_data_integrity.py
  • src/tux/database/controllers/case.py
  • tests/shared/test_config_generation.py
  • scripts/dev/clean.py
  • src/tux/database/controllers/reminder.py
  • src/tux/core/prefix_manager.py
  • src/tux/database/controllers/__init__.py
  • tests/database/test_database_migrations.py
  • tests/core/test_dynamic_permission_system.py
  • tests/fixtures/database_fixtures.py
  • tests/conftest.py
  • tests/fixtures/sentry_fixtures.py
  • src/tux/modules/tools/tldr.py
  • tests/database/test_database_service.py
**/services/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/services/**/*.py: Apply transactions for multi-step database operations in Python services
Use custom exceptions for business logic error handling in services
Implement dependency injection in service classes
Keep services stateless where possible
Cache frequently accessed data to optimize performance
Optimize database queries for performance

Files:

  • tests/services/test_error_extractors.py
  • src/tux/services/handlers/error/cog.py
**/database/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/database/**/*.py: Use SQLModel for ORM operations to ensure type safety with Pydantic integration
Implement model-level validation in SQLModel database models

Files:

  • src/tux/database/controllers/snippet.py
  • src/tux/database/controllers/afk.py
  • tests/database/test_database_data_integrity.py
  • src/tux/database/controllers/case.py
  • src/tux/database/controllers/reminder.py
  • src/tux/database/controllers/__init__.py
  • tests/database/test_database_migrations.py
  • tests/database/test_database_service.py
**/modules/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/modules/**/*.py: Use hybrid commands (slash + traditional) for Discord bot command modules
Implement role-based permissions for Discord commands
Implement cooldowns and rate limiting for Discord commands

Files:

  • src/tux/modules/tools/tldr.py
🧠 Learnings (6)
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/database/**/*.py : Use SQLModel for ORM operations to ensure type safety with Pydantic integration

Applied to files:

  • src/tux/database/controllers/snippet.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/database/**/*.py : Implement model-level validation in SQLModel database models

Applied to files:

  • tests/database/test_database_data_integrity.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/*.py : Implement proper permission checks in Discord commands and services

Applied to files:

  • tests/core/test_dynamic_permission_system.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/modules/**/*.py : Implement role-based permissions for Discord commands

Applied to files:

  • tests/core/test_dynamic_permission_system.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/*.py : Include complete type hints in all public APIs

Applied to files:

  • tests/conftest.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/services/**/*.py : Apply transactions for multi-step database operations in Python services

Applied to files:

  • tests/database/test_database_service.py
🧬 Code graph analysis (13)
src/tux/database/controllers/snippet.py (2)
src/tux/database/controllers/base/base_controller.py (1)
  • BaseController (20-473)
src/tux/database/service.py (1)
  • DatabaseService (42-506)
src/tux/database/controllers/afk.py (3)
src/tux/database/models/models.py (1)
  • AFK (914-1007)
src/tux/database/controllers/base/crud.py (1)
  • update_by_id (75-93)
src/tux/database/controllers/base/query.py (1)
  • find_all (81-121)
tests/database/test_database_data_integrity.py (3)
src/tux/database/models/models.py (2)
  • Guild (36-173)
  • GuildConfig (176-325)
src/tux/database/service.py (2)
  • DatabaseService (42-506)
  • session (173-200)
tests/fixtures/database_fixtures.py (1)
  • db_service (57-70)
src/tux/database/controllers/case.py (3)
src/tux/database/models/models.py (2)
  • Case (571-718)
  • Guild (36-173)
src/tux/database/models/enums.py (1)
  • CaseType (34-50)
src/tux/database/controllers/base/query.py (1)
  • find_one (49-79)
tests/shared/test_config_generation.py (3)
typings/pydantic_settings_export/models.pyi (2)
  • FieldInfoModel (35-78)
  • SettingsInfoModel (82-99)
src/tux/shared/config/generators/json.py (2)
  • JsonGenerator (33-153)
  • JsonGeneratorSettings (26-30)
src/tux/shared/config/generators/yaml.py (2)
  • YamlGenerator (36-190)
  • YamlGeneratorSettings (26-33)
src/tux/database/controllers/reminder.py (1)
src/tux/database/service.py (1)
  • DatabaseService (42-506)
src/tux/core/prefix_manager.py (2)
src/tux/database/controllers/__init__.py (1)
  • guild_config (117-121)
src/tux/modules/config/config.py (1)
  • config (48-52)
src/tux/database/controllers/__init__.py (3)
src/tux/database/service.py (1)
  • DatabaseService (42-506)
src/tux/core/base_cog.py (1)
  • db (146-163)
src/tux/core/bot.py (1)
  • db (118-133)
tests/database/test_database_migrations.py (4)
src/tux/database/controllers/guild_config.py (3)
  • GuildConfigController (19-595)
  • get_or_create_config (44-56)
  • get_config_by_guild_id (33-42)
src/tux/database/controllers/guild.py (1)
  • GuildController (21-207)
src/tux/database/service.py (2)
  • DatabaseService (42-506)
  • is_connected (115-123)
tests/fixtures/database_fixtures.py (4)
  • db_service (57-70)
  • guild_controller (74-76)
  • guild_config_controller (80-82)
  • disconnected_async_db_service (93-98)
tests/core/test_dynamic_permission_system.py (2)
tests/core/test_restricted_commands.py (3)
  • permission_system (33-39)
  • mock_bot (20-22)
  • mock_db_coordinator (25-30)
tests/modules/test_moderation_critical_issues.py (1)
  • mock_bot (71-76)
tests/fixtures/database_fixtures.py (2)
src/tux/database/service.py (2)
  • engine (158-170)
  • DatabaseService (42-506)
tests/database/test_database_cli_commands.py (1)
  • db_service (329-334)
tests/conftest.py (1)
tests/fixtures/utils.py (4)
  • create_mock_guild (7-27)
  • create_mock_user (30-52)
  • create_mock_channel (55-75)
  • create_mock_interaction (78-100)
tests/database/test_database_service.py (5)
src/tux/database/controllers/guild_config.py (1)
  • GuildConfigController (19-595)
src/tux/database/controllers/guild.py (1)
  • GuildController (21-207)
src/tux/database/models/models.py (2)
  • Guild (36-173)
  • GuildConfig (176-325)
src/tux/database/service.py (1)
  • DatabaseService (42-506)
tests/fixtures/database_fixtures.py (5)
  • db_service (57-70)
  • guild_controller (74-76)
  • guild_config_controller (80-82)
  • disconnected_async_db_service (93-98)
  • db_session (86-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run All Tests (3.13.8)
  • GitHub Check: Seer Code Review
🔇 Additional comments (40)
src/tux/core/base_cog.py (1)

222-222: Breaking API change is correctly implemented with all call-sites updated.

The keyword-only argument requirement at line 222 is properly enforced with the * separator, making the API more explicit and self-documenting. The docstring at lines 248–249 correctly demonstrates the new calling pattern. All five call-sites across the codebase (in levels.py, level.py, status_roles.py, features/levels.py, and git.py) have been updated to use keyword arguments consistently.

scripts/dev/clean.py (1)

42-42: Docstring now accurately reflects implementation scope.

The docstring has been updated to match the actual behavior of the function, which only cleans the scripts directory. This addresses the previous review concern about the mismatch between the docstring and implementation.

tests/shared/test_config_generation.py (3)

1-22: LGTM!

The module docstring and import organization follow the coding guidelines correctly (stdlib → third-party → local).


94-137: LGTM!

The YAML test appropriately validates commented output format. The assertions are specific enough for the YAML comment structure.


139-178: LGTM!

The JSON test correctly parses the output and validates the expected keys are present, which is a solid validation approach.

src/tux/services/handlers/error/cog.py (1)

70-73: LGTM! Well-justified exception handling.

The comment appropriately documents why catching broad Exception is acceptable in this module reloading context. The resilient approach ensures other modules continue loading even if one fails, and failures are properly logged.

src/tux/modules/tools/tldr.py (2)

53-58: LGTM! Resource cleanup properly implemented.

The cog_unload method correctly addresses the past review comment about cleaning up the cache task. The implementation properly cancels the task and suppresses the CancelledError, preventing resource leaks when the cog is unloaded or reloaded.


295-396: Excellent refactoring to unify command handling.

The _handle_tldr_command method effectively consolidates slash and prefix command logic into a single implementation. The source type discrimination using isinstance checks is clean and type-safe, and the response handling correctly adapts to both interaction and context sources.

tests/services/test_error_extractors.py (1)

1-14: LGTM! Previous concern has been addressed.

The file now correctly serves as a documentation index pointing to the split test files. The test classes have been moved to their respective focused modules as stated in the docstring.

src/tux/database/controllers/__init__.py (3)

11-14: LGTM! Correct use of TYPE_CHECKING guard.

The DatabaseService import is correctly guarded under TYPE_CHECKING since it's only needed for type hints. With from __future__ import annotations, the runtime doesn't need the actual import.


16-31: Exports are properly updated.

The __all__ list correctly includes all exported controllers, including the newly added CaseController and GuildController.


54-107: Clean lazy-loading pattern for controllers.

The DatabaseCoordinator correctly implements lazy initialization with proper None checks and consistent DI pattern. The explicit -> None return type on __init__ is a good addition for type completeness.

src/tux/database/controllers/snippet.py (1)

10-18: LGTM! Correct TYPE_CHECKING pattern.

The DatabaseService import is properly guarded under TYPE_CHECKING, consistent with the pattern used across other controller modules.

tests/database/test_database_migrations.py (1)

19-29: LGTM! Clean import organization.

Import reordering and additions are well-organized following the stdlib → third-party → local pattern.

src/tux/database/controllers/reminder.py (1)

11-17: LGTM! Previous concern addressed.

The redundant if not TYPE_CHECKING: block has been removed. The DatabaseService import is now correctly guarded under TYPE_CHECKING only.

tests/core/test_dynamic_permission_system.py (1)

2-25: Formatting improvements look good.

Import reordering and whitespace adjustments improve readability without changing behavior.

src/tux/database/controllers/afk.py (4)

11-17: LGTM! Correct TYPE_CHECKING pattern.

The DatabaseService import is properly guarded under TYPE_CHECKING, consistent with the pattern across the codebase.


68-78: Good use of composite primary key for updates.

The update correctly uses the composite key (member_id, guild_id) matching the AFK model's primary key definition. The fallback to existing if update returns None is a safe pattern.


204-244: Well-implemented dynamic filtering.

The find_many method cleanly handles both dictionary-style and keyword filters, building SQLAlchemy expressions dynamically. The attribute existence check with hasattr(AFK, key) provides good safety against invalid filter keys.


276-284: Correct SQLAlchemy NULL and boolean comparisons.

Good use of == False (with appropriate noqa) and .is_not(None) for proper SQL generation. The type ignore comments are acceptable for SQLAlchemy column operations.

tests/database/test_database_data_integrity.py (4)

1-12: Good test file structure.

Clean module setup with proper imports from fixtures and database models.


17-40: Basic integrity tests look good.

Tests for required fields and data types provide good foundational coverage.


42-60: NULL handling test validates defaults correctly.

Good verification that the default prefix "$" is set and optional fields are None.


62-89: Transaction rollback test is well-designed.

The test correctly validates that unique constraint violations are handled and original data is preserved after rollback. The broad except Exception is acceptable here since different database backends may raise different exception types for constraint violations.

tests/fixtures/database_fixtures.py (1)

63-64: LGTM: Private attribute access properly documented.

The noqa: SLF001 comments appropriately document intentional private attribute access needed for test fixture setup. This pattern is acceptable in test infrastructure where bypassing normal initialization is required.

src/tux/database/controllers/case.py (4)

11-24: LGTM: TYPE_CHECKING guards properly implemented.

The use of TYPE_CHECKING guards to conditionally import AsyncSession and DatabaseService is a standard pattern that reduces runtime import overhead while preserving type hints for static analysis. This aligns with the PR-wide typing improvements.


336-336: LGTM: More idiomatic boolean conversion.

The change from len(active_cases) > 0 to bool(active_cases) is more Pythonic while preserving the same behavior. Empty lists are falsy in Python, making explicit length checks unnecessary.


419-433: LGTM: Database-level sorting improves performance.

The refactor to use order_by=[Case.id.desc()] moves sorting to the database, which is more efficient than fetching all cases and sorting in Python. The assumption that higher IDs indicate newer cases is valid for auto-incrementing primary keys.


435-468: LGTM: Improved documentation.

The expanded docstring clearly documents the parameters and behavior, particularly the distinction between case_processed (expiration handled) and case_status (case validity). This improves API clarity.

tests/fixtures/sentry_fixtures.py (1)

22-191: LGTM: Well-structured test fixtures.

The new Discord and Sentry fixtures are well-implemented with:

  • Proper use of MagicMock(spec=...) for type safety
  • Appropriate AsyncMock for async methods
  • Clean fixture composition pattern (e.g., mock_discord_interaction composing user/guild/channel)
  • Organized sections with clear comments

These fixtures will enable comprehensive testing of Discord interactions and Sentry tracking.

tests/conftest.py (4)

15-20: LGTM: Fixture module renamed appropriately.

The change from test_data_fixtures to data_fixtures follows pytest conventions where fixture modules should not be prefixed with "test_" to avoid confusion with test files.


23-37: LGTM: Simplified documentation.

The updated docstring is clearer and more concise, correctly noting that test markers are defined in pyproject.toml and don't need duplication.


55-79: LGTM: More precise process detection.

The change from checking for generic "pglite" to specifically checking for "pglite_manager.js" reduces false positives and more accurately targets the actual PGlite manager process.


82-108: LGTM: Improved termination logic.

The updated implementation adds proper timeout handling with graceful termination followed by force kill if needed. Re-fetching the process object after timeout is defensive programming that handles edge cases where the process reference may become stale.

tests/database/test_database_service.py (3)

19-19: LGTM: Random import used appropriately.

The random import is used at line 302 to generate unique guild IDs for benchmark tests, preventing duplicate key conflicts. This is a good solution for test data generation.


170-173: LGTM: Consistent multi-line formatting.

The reformatted method signatures improve readability and adhere to the project's 88-character line length guideline. Trailing commas are properly included for multi-line signatures.

Also applies to: 202-207, 228-231, 268-271, 316-320, 365-370


234-234: LGTM: Formatting improvements.

The added blank lines and multi-line statement formatting improve code readability without changing functionality. The formatting is consistent with PEP 8 style guidelines.

Also applies to: 250-250, 322-322, 348-357, 386-386

src/tux/core/prefix_manager.py (3)

196-197: LGTM! Good defensive programming.

Removing the cache entry on persistence failure maintains consistency between cache and database state, preventing the cache from diverging from the source of truth.


214-243: Verify that the "fail-once" cache loading strategy aligns with operational requirements.

When database is unavailable, times out, or encounters errors during load_all_prefixes, the code sets _cache_loaded = True to prevent retry loops (lines 218, 239, 243). While this prevents infinite retries, it also means:

  1. If the database is temporarily unavailable at bot startup, the cache won't automatically reload when the database becomes available again
  2. Manual intervention (restart or invalidate_cache()) would be required to retry cache loading
  3. The bot will fall back to on-demand loading for all guilds, increasing database load

This is a reasonable design for preventing retry storms, but operators should be aware that transient startup issues require manual recovery.


227-228: The concern about config.id being a separate primary key is unfounded. The GuildConfig model explicitly documents that id is the Discord guild ID with a foreign key to the guild table, not a generated primary key. Line 228's usage of self._prefix_cache[config.id] = config.prefix is correct—config.id is semantically equivalent to guild_id, and this pattern is consistent with other cache operations in the file (e.g., lines 157, 119).

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/tux/core/prefix_manager.py (1)

222-229: Past review concern still unresolved.

The hardcoded limit=1000 (line 223) and timeout=10.0 (line 224) remain unchanged from the previous review. As noted in the earlier feedback, this can cause issues for large deployments where bots serve more than 1000 guilds, leading to incomplete cache loading without visibility or warning.

The previous review suggested:

  1. Make the limit configurable via environment variable or config
  2. Add a warning log when len(all_configs) >= 1000 to indicate truncation
  3. Consider paginated fetching for complete coverage

Please refer to the detailed suggestions in the past review comment.

🧹 Nitpick comments (2)
tests/core/test_dynamic_permission_system.py (2)

83-87: Remove unnecessary outer parentheses for cleaner code.

The outer parentheses wrapping the AsyncMock call are unnecessary and reduce readability.

🔎 Suggested simplification
-        permission_system.db.permission_assignments.get_user_permission_rank = (
-            AsyncMock(
-                return_value=0,
-            )
-        )
+        permission_system.db.permission_assignments.get_user_permission_rank = AsyncMock(
+            return_value=0,
+        )

109-113: Remove unnecessary outer parentheses for cleaner code.

Same issue as lines 83-87: the outer parentheses wrapping the AsyncMock call are unnecessary.

🔎 Suggested simplification
-        permission_system.db.permission_assignments.get_user_permission_rank = (
-            AsyncMock(
-                return_value=3,
-            )
-        )
+        permission_system.db.permission_assignments.get_user_permission_rank = AsyncMock(
+            return_value=3,
+        )
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 873c0f8 and da78893.

📒 Files selected for processing (4)
  • src/tux/core/prefix_manager.py
  • src/tux/database/controllers/snippet.py
  • src/tux/modules/utility/run.py
  • tests/core/test_dynamic_permission_system.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tux/database/controllers/snippet.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with the form Type | None instead of Optional[Type] in Python
Use NumPy docstrings for documenting Python functions and classes
Prefer absolute imports, but relative imports are allowed within the same module in Python
Use import grouping in Python: stdlib → third-party → local imports
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of the Python file unless absolutely necessary
Use async/await for I/O operations and asynchronous code in services and database operations
Use Pydantic for data validation in Python models and services
Log errors with context and handle Discord rate limits appropriately
Do not include secrets in code; use environment variables for configuration
Validate all user inputs in Python code
Implement proper permission checks in Discord commands and services
Keep Python files to a maximum of 1600 lines
Use descriptive filenames for Python modules
Include complete type hints in all public APIs
Include docstrings for public API functions and classes

Files:

  • src/tux/modules/utility/run.py
  • src/tux/core/prefix_manager.py
  • tests/core/test_dynamic_permission_system.py
**/modules/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/modules/**/*.py: Use hybrid commands (slash + traditional) for Discord bot command modules
Implement role-based permissions for Discord commands
Implement cooldowns and rate limiting for Discord commands

Files:

  • src/tux/modules/utility/run.py
🧠 Learnings (2)
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/*.py : Implement proper permission checks in Discord commands and services

Applied to files:

  • tests/core/test_dynamic_permission_system.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/modules/**/*.py : Implement role-based permissions for Discord commands

Applied to files:

  • tests/core/test_dynamic_permission_system.py
🧬 Code graph analysis (2)
src/tux/modules/utility/run.py (3)
src/tux/shared/exceptions.py (3)
  • TuxAPIConnectionError (182-197)
  • TuxAPIRequestError (200-220)
  • TuxAPIResourceNotFoundError (223-248)
src/tux/services/wrappers/wandbox.py (1)
  • getoutput (23-84)
src/tux/services/wrappers/godbolt.py (1)
  • getoutput (181-260)
src/tux/core/prefix_manager.py (1)
src/tux/database/controllers/__init__.py (1)
  • guild_config (117-121)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run All Tests (3.13.8)
  • GitHub Check: Seer Code Review
🔇 Additional comments (14)
src/tux/modules/utility/run.py (3)

20-28: LGTM!

The new exception imports are properly grouped with existing imports from tux.shared.exceptions and all three are used in the error handling blocks below.


248-268: Well-structured error handling.

The try/except correctly wraps only the external API call, keeping the scope minimal. Catching all three exception types explicitly provides good documentation of expected failure modes, even though TuxAPIResourceNotFoundError inherits from TuxAPIRequestError. The warning-level logging with compiler context is appropriate for recoverable errors.


301-331: Consistent error handling with GodboltService.

The implementation correctly mirrors the Godbolt error handling pattern. The exception handling properly wraps only the API call, logs with appropriate context, and returns None to allow the caller to handle the failure gracefully through TuxCompilationError.

tests/core/test_dynamic_permission_system.py (6)

2-2: LGTM!

Minor documentation formatting improvement.


14-25: LGTM!

Import organization follows the coding guidelines correctly (stdlib → third-party → local).


32-34: LGTM!

The simplified fixture is clear and follows common test patterns for mocking.


44-50: LGTM!

Parameter formatting enhances readability while maintaining proper type hints.


160-163: LGTM!

Argument formatting improves readability while maintaining clarity.


194-200: LGTM!

Adding a docstring improves the test by actually verifying that the decorator preserves docstring content, not just function executability. The noqa comments are appropriate for test fixture data.

src/tux/core/prefix_manager.py (5)

121-122: LGTM! Correctly addresses past review feedback.

The removal of the unused _persist_task variable binding properly implements fire-and-forget persistence while keeping the async task execution. The noqa: RUF006 suppression is appropriate here since not awaiting the task is intentional.


156-166: Well-structured refactor using try/except/else pattern.

The simplified logic correctly handles both success and failure paths. The else clause is idiomatic Python—it executes only when no exception occurs, making the success path explicit. The removal of extra existence checks improves readability.


196-197: Excellent defensive programming for cache coherency.

Removing the cache entry on persistence failure prevents the cache from diverging from the database state. This ensures that subsequent reads will attempt to reload from the database rather than serving stale, unpersisted data.


268-282: Well-designed monitoring API addition.

The get_cache_stats method provides useful observability for debugging and monitoring cache health. The implementation correctly follows the NumPy docstring format and type hint guidelines. Casting _cache_loaded to int (line 281) maintains a consistent return type.


218-243: Clarify misleading docstring and improve failure logging for cache initialization.

The docstring claims the method is "Idempotent and safe to call multiple times," but setting _cache_loaded = True on failure (lines 218, 239, 243) means subsequent calls will return immediately without retrying (lines 207-208). If the database is transiently unavailable at startup, the cache remains permanently empty—manual intervention via invalidate_cache() + retry is required for recovery.

Since load_all_prefixes() is called exactly once during bot initialization (prefix_setup.py:36), the current "fail fast" behavior is reasonable for avoiding startup blocking. However, this design choice should be explicitly documented. Update the docstring to clarify that failures prevent automatic retries, and consider logging at ERROR level (not just WARNING) when the database is unavailable or timeouts occur, so operators recognize this as a critical startup condition requiring manual recovery.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/services/test_error_extractors_integration.py (3)

1-9: Scope mismatch: file tests only one extractor, not Discord.py errors.

The module docstring states "Tests for extractors with actual Discord.py/httpx error objects" and the filename is plural ("test_error_extractors_integration.py"), but the file only imports and tests a single extractor function (extract_httpx_status_details) with httpx errors—no Discord.py errors are tested.

Consider either:

  1. Adding tests for other extractor functions and Discord.py errors to match the broader scope implied by the name
  2. Renaming the file to test_httpx_status_extractor_integration.py and updating the docstring to reflect the narrower scope

15-27: Previous review concern has been addressed.

The test now calls extract_httpx_status_details(error) on line 26 and validates its behavior, addressing the previous review's concern that extractors weren't being tested.

Optional: Remove redundant attribute assertions.

Lines 21-22 verify that the error object has expected attributes, but these checks are somewhat redundant. If the httpx error objects didn't have these attributes, the construction on lines 17-18 would have already failed. Consider removing these assertions to keep tests focused on extractor behavior.


43-62: Test validates extractor with HTTPStatusError correctly.

The test properly constructs a real httpx.HTTPStatusError and validates that extract_httpx_status_details extracts the expected status code, URL, and response text.

Strengthen URL assertion for more precise validation.

Line 60 uses a substring check ("api.example.com" in str(context["url"])), which is less precise than checking the full URL. Since the extractor returns the complete URL as a string, consider an exact match:

🔎 Suggested improvement
-        assert "api.example.com" in str(context["url"])
+        assert context["url"] == "https://api.example.com/not-found"

Note: The CodeQL warning about "Incomplete URL substring sanitization" is a false positive in this test context—the assertion is validating extracted data, not performing security sanitization.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da78893 and c9f3428.

📒 Files selected for processing (1)
  • tests/services/test_error_extractors_integration.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with the form Type | None instead of Optional[Type] in Python
Use NumPy docstrings for documenting Python functions and classes
Prefer absolute imports, but relative imports are allowed within the same module in Python
Use import grouping in Python: stdlib → third-party → local imports
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of the Python file unless absolutely necessary
Use async/await for I/O operations and asynchronous code in services and database operations
Use Pydantic for data validation in Python models and services
Log errors with context and handle Discord rate limits appropriately
Do not include secrets in code; use environment variables for configuration
Validate all user inputs in Python code
Implement proper permission checks in Discord commands and services
Keep Python files to a maximum of 1600 lines
Use descriptive filenames for Python modules
Include complete type hints in all public APIs
Include docstrings for public API functions and classes

Files:

  • tests/services/test_error_extractors_integration.py
**/services/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/services/**/*.py: Apply transactions for multi-step database operations in Python services
Use custom exceptions for business logic error handling in services
Implement dependency injection in service classes
Keep services stateless where possible
Cache frequently accessed data to optimize performance
Optimize database queries for performance

Files:

  • tests/services/test_error_extractors_integration.py
🧬 Code graph analysis (1)
tests/services/test_error_extractors_integration.py (1)
src/tux/services/handlers/error/extractors.py (1)
  • extract_httpx_status_details (248-282)
🪛 GitHub Check: CodeQL
tests/services/test_error_extractors_integration.py

[failure] 60-60: Incomplete URL substring sanitization
The string api.example.com may be at an arbitrary position in the sanitized URL.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run All Tests (3.13.8)
🔇 Additional comments (1)
tests/services/test_error_extractors_integration.py (1)

29-41: Test logic is correct.

The test properly validates that extract_httpx_status_details handles ConnectError gracefully by returning an empty dict when no response attribute is present.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
scripts/utils/validate_paths.py (1)

222-236: Remove CalledProcessError from exception handling (dead code).

The exception handler catches subprocess.CalledProcessError, but this exception will never be raised because check=False is set on line 225. CalledProcessError is only raised when check=True and the subprocess returns a non-zero exit code. Since the code manually checks result.returncode in the else block (line 238), catching this exception is misleading dead code.

🔎 Proposed fix to remove CalledProcessError
     except (
         subprocess.TimeoutExpired,
-        subprocess.CalledProcessError,
         FileNotFoundError,
     ) as e:
src/tux/database/controllers/case.py (1)

277-297: Consider renaming or implementing time-based filtering.

The function name get_recent_cases and the hours parameter suggest time-based filtering, but the implementation returns all cases for the guild without any time filtering. This could confuse callers who expect the hours parameter to have an effect.

Consider one of these approaches:

  • Rename to get_cases_by_guild (a method already exists at line 249-262 with similar behavior)
  • Implement the time-based filtering when a created_at field is available
  • Remove the unused hours parameter until filtering is implemented
Suggested refactor
-async def get_recent_cases(self, guild_id: int, hours: int = 24) -> list[Case]:
+async def get_recent_cases(self, guild_id: int) -> list[Case]:
     """
     Get cases created within the last N hours.
     
     Parameters
     ----------
     guild_id : int
         The guild ID to filter cases by.
-    hours : int, optional
-        Number of hours to look back (default is 24). Currently unused as
-        created_at filtering is not yet implemented.
-
+    
+    Notes
+    -----
+    Time-based filtering is not yet implemented as the Case model lacks
+    a created_at field. This currently returns all cases for the guild.
+    
     Returns
     -------
     list[Case]
         List of recent cases.
     """
-    # For now, just get all cases in the guild since we don't have a created_at field
-    # TODO: Implement created_at filtering when Case model has timestamp field
-    _ = hours  # Mark as intentionally unused until filtering is implemented
     return await self.find_all(filters=Case.guild_id == guild_id)
🧹 Nitpick comments (5)
src/tux/modules/snippets/create_snippet.py (1)

135-135: Good change to logger.success, but consider the alias creation log for consistency.

The change from logger.info to logger.success aligns well with the PR's objective to elevate successful operations. However, line 121 still uses logger.info for alias creation, which is also a successful operation. For consistency, consider updating line 121 to use logger.success as well.

🔎 Suggested consistency fix for line 121
logger.success(
    f"{ctx.author} created snippet '{name}' as an alias to '{content}'.",
)
src/tux/services/hot_reload/watcher.py (1)

88-89: Consider adjusting remaining verbose logs for consistency.

The logging level adjustments you made at lines 95, 204, and 238 are good. For consistency, you might consider similar adjustments to other verbose logs in the on_modified method:

  • Lines 88-89: WATCHDOG EVENT (appears very frequently)
  • Lines 98, 101, 105, 108: File filtering and change detection steps
  • Lines 117-118: Extension determination and callback notification

These could potentially be debug-level as well, since they're detailed processing steps similar to line 95.

Also applies to: 98-108, 117-118

scripts/docs/lint.py (1)

23-24: Optional: Consider a more descriptive constant name.

While YAML_FRONTMATTER_PARTS is acceptable, a name like MIN_YAML_FRONTMATTER_PARTS or EXPECTED_YAML_FRONTMATTER_PARTS would more clearly convey that this is a minimum threshold for the split operation rather than just a count.

🔎 Proposed naming improvement
-YAML_FRONTMATTER_PARTS = 3
+MIN_YAML_FRONTMATTER_PARTS = 3

And update the usage:

-                    if len(parts) >= YAML_FRONTMATTER_PARTS:
+                    if len(parts) >= MIN_YAML_FRONTMATTER_PARTS:
src/tux/core/bot.py (1)

288-313: Consider reducing repetitive inline comments.

The comment "# Boolean is the value being set, not a flag (Sentry API design)" appears multiple times. Consider adding a single module-level or method-level docstring explaining this pattern once, rather than repeating it at every set_data call.

🔎 Suggestion

Add a note in the method docstring or a module-level comment explaining the Sentry set_data API pattern, then remove the repetitive inline comments:

     async def shutdown(self) -> None:
         """
         Gracefully shut down the bot and clean up all resources.

         Performs shutdown in three phases: cancel startup task, clean up
         background tasks, and close Discord, database, and HTTP connections.
         This method is idempotent - calling it multiple times is safe. All
         phases are traced with Sentry for monitoring shutdown performance.
+
+        Note: All `transaction.set_data(key, bool)` calls set the boolean value
+        as tracing data, not as a flag parameter (Sentry API design).
         """
src/tux/core/logging.py (1)

141-143: Consider adding VALID_LOG_LEVELS to __all__ exports.

VALID_LOG_LEVELS is used in tests (tests/core/test_logging.py imports it), making it part of the public API. Consider adding it to __all__ for explicit export.

🔎 Proposed fix
 __all__ = [
     "configure_logging",
     "configure_testing_logging",
     "StructuredLogger",
     "verify_logging_interception",
+    "VALID_LOG_LEVELS",
 ]
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9f3428 and 05fa9d2.

📒 Files selected for processing (33)
  • .vscode/settings.json
  • docs/content/developer/best-practices/logging.md
  • docs/content/developer/concepts/core/app.md
  • docs/content/developer/concepts/core/logging.md
  • docs/content/developer/concepts/core/main.md
  • scripts/dev/lint_fix.py
  • scripts/docs/lint.py
  • scripts/tux/start.py
  • scripts/utils/validate_paths.py
  • src/tux/core/app.py
  • src/tux/core/base_cog.py
  • src/tux/core/bot.py
  • src/tux/core/logging.py
  • src/tux/core/permission_system.py
  • src/tux/core/setup/base.py
  • src/tux/database/controllers/base/bulk.py
  • src/tux/database/controllers/case.py
  • src/tux/database/service.py
  • src/tux/database/utils.py
  • src/tux/help/data.py
  • src/tux/modules/snippets/create_snippet.py
  • src/tux/modules/tools/wolfram.py
  • src/tux/plugins/atl/git.py
  • src/tux/services/emoji_manager.py
  • src/tux/services/handlers/activity.py
  • src/tux/services/hot_reload/cog.py
  • src/tux/services/hot_reload/file_utils.py
  • src/tux/services/hot_reload/service.py
  • src/tux/services/hot_reload/watcher.py
  • src/tux/services/moderation/moderation_coordinator.py
  • src/tux/ui/views/config/dashboard.py
  • src/tux/ui/views/config/ranks.py
  • tests/core/test_logging.py
✅ Files skipped from review due to trivial changes (1)
  • src/tux/services/hot_reload/service.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tux/plugins/atl/git.py
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with the form Type | None instead of Optional[Type] in Python
Use NumPy docstrings for documenting Python functions and classes
Prefer absolute imports, but relative imports are allowed within the same module in Python
Use import grouping in Python: stdlib → third-party → local imports
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of the Python file unless absolutely necessary
Use async/await for I/O operations and asynchronous code in services and database operations
Use Pydantic for data validation in Python models and services
Log errors with context and handle Discord rate limits appropriately
Do not include secrets in code; use environment variables for configuration
Validate all user inputs in Python code
Implement proper permission checks in Discord commands and services
Keep Python files to a maximum of 1600 lines
Use descriptive filenames for Python modules
Include complete type hints in all public APIs
Include docstrings for public API functions and classes

Files:

  • scripts/dev/lint_fix.py
  • src/tux/database/utils.py
  • src/tux/core/permission_system.py
  • src/tux/services/hot_reload/cog.py
  • src/tux/ui/views/config/dashboard.py
  • scripts/tux/start.py
  • src/tux/ui/views/config/ranks.py
  • src/tux/core/bot.py
  • tests/core/test_logging.py
  • src/tux/modules/snippets/create_snippet.py
  • src/tux/core/base_cog.py
  • src/tux/modules/tools/wolfram.py
  • src/tux/help/data.py
  • scripts/utils/validate_paths.py
  • scripts/docs/lint.py
  • src/tux/services/hot_reload/file_utils.py
  • src/tux/services/handlers/activity.py
  • src/tux/core/setup/base.py
  • src/tux/services/emoji_manager.py
  • src/tux/core/logging.py
  • src/tux/database/controllers/case.py
  • src/tux/database/controllers/base/bulk.py
  • src/tux/database/service.py
  • src/tux/core/app.py
  • src/tux/services/moderation/moderation_coordinator.py
  • src/tux/services/hot_reload/watcher.py
**/database/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/database/**/*.py: Use SQLModel for ORM operations to ensure type safety with Pydantic integration
Implement model-level validation in SQLModel database models

Files:

  • src/tux/database/utils.py
  • src/tux/database/controllers/case.py
  • src/tux/database/controllers/base/bulk.py
  • src/tux/database/service.py
**/services/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/services/**/*.py: Apply transactions for multi-step database operations in Python services
Use custom exceptions for business logic error handling in services
Implement dependency injection in service classes
Keep services stateless where possible
Cache frequently accessed data to optimize performance
Optimize database queries for performance

Files:

  • src/tux/services/hot_reload/cog.py
  • src/tux/services/hot_reload/file_utils.py
  • src/tux/services/handlers/activity.py
  • src/tux/services/emoji_manager.py
  • src/tux/services/moderation/moderation_coordinator.py
  • src/tux/services/hot_reload/watcher.py
**/ui/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Use rich embeds for Discord message formatting in UI module

Files:

  • src/tux/ui/views/config/dashboard.py
  • src/tux/ui/views/config/ranks.py
**/modules/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/modules/**/*.py: Use hybrid commands (slash + traditional) for Discord bot command modules
Implement role-based permissions for Discord commands
Implement cooldowns and rate limiting for Discord commands

Files:

  • src/tux/modules/snippets/create_snippet.py
  • src/tux/modules/tools/wolfram.py
🧠 Learnings (2)
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/*.py : Include complete type hints in all public APIs

Applied to files:

  • scripts/tux/start.py
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/modules/**/*.py : Use hybrid commands (slash + traditional) for Discord bot command modules

Applied to files:

  • src/tux/core/logging.py
🧬 Code graph analysis (9)
scripts/tux/start.py (3)
src/tux/core/app.py (2)
  • start (124-177)
  • run (79-101)
src/tux/core/logging.py (1)
  • configure_logging (164-212)
src/tux/main.py (1)
  • run (6-9)
src/tux/ui/views/config/ranks.py (4)
src/tux/core/base_cog.py (1)
  • db (152-169)
src/tux/core/bot.py (1)
  • db (118-133)
src/tux/database/controllers/__init__.py (1)
  • permission_ranks (173-177)
src/tux/database/controllers/permissions.py (1)
  • get_permission_ranks_by_guild (85-100)
src/tux/core/bot.py (1)
src/tux/services/sentry/tracing.py (4)
  • start_transaction (451-494)
  • transaction (277-341)
  • set_data (64-73)
  • span (344-406)
tests/core/test_logging.py (1)
src/tux/core/logging.py (11)
  • StructuredLogger (545-641)
  • _configure_third_party_logging (432-512)
  • _determine_log_level (248-281)
  • _format_record (336-377)
  • _get_relative_file_path (380-404)
  • _validate_log_level (221-245)
  • verify_logging_interception (515-542)
  • emit (451-499)
  • performance (554-577)
  • database (580-603)
  • api_call (606-641)
src/tux/modules/snippets/create_snippet.py (1)
src/tux/modules/features/influxdblogger.py (1)
  • logger (109-179)
src/tux/core/base_cog.py (1)
src/tux/shared/functions.py (1)
  • generate_usage (276-347)
src/tux/core/setup/base.py (2)
src/tux/services/sentry/tracing.py (3)
  • start_span (413-447)
  • span (344-406)
  • set_data (64-73)
src/tux/core/setup/orchestrator.py (1)
  • setup (46-82)
src/tux/database/controllers/case.py (3)
src/tux/database/service.py (1)
  • session (173-200)
src/tux/database/controllers/__init__.py (1)
  • case (145-149)
src/tux/database/controllers/base/query.py (1)
  • find_one (49-79)
src/tux/core/app.py (3)
src/tux/services/sentry/utils.py (1)
  • capture_exception_safe (23-49)
src/tux/core/logging.py (1)
  • configure_logging (164-212)
src/tux/core/bot.py (1)
  • shutdown (275-315)
🪛 LanguageTool
docs/content/developer/best-practices/logging.md

[grammar] ~199-~199: Use a hyphen to join words.
Context: ... more verbose output. This enables DEBUG level logging without modifying your `.e...

(QB_NEW_EN_HYPHEN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run All Tests (3.13.8)
🔇 Additional comments (43)
src/tux/services/emoji_manager.py (3)

193-195: LGTM! Appropriate logging level for successful initialization.

The transition from logger.info to logger.success correctly reflects that the emoji cache initialization completed successfully. This improves observability by clearly distinguishing successful operations from informational messages.


288-288: LGTM! Appropriate logging level for successful emoji creation.

The transition to logger.success appropriately signals successful emoji creation, making it easier to filter and monitor successful operations in logs.


448-448: LGTM! Appropriate logging level for successful emoji deletion.

The use of logger.success for successful deletion operations is consistent with the other changes in this file and improves the semantic clarity of the logging system.

src/tux/services/hot_reload/watcher.py (3)

95-95: LGTM! Appropriate logging level adjustment.

Changing this from info to debug is correct—file processing details are useful for debugging but too granular for operational logs.


204-204: LGTM! Appropriate logging level adjustment.

Changing this diagnostic information to debug level is correct—the current working directory is useful for troubleshooting but not needed at info level.


238-238: LGTM! Good use of success-level logging.

Using logger.success for startup confirmation is appropriate and provides clear operational visibility.

src/tux/help/data.py (2)

125-128: Logging level change to trace is appropriate.

The change from logger.debug to logger.trace intentionally reduces verbosity for these rare error conditions in the help system's permission checks. The safe default (hiding commands on error) provides appropriate defensive behavior.

Based on the AI summary and PR objectives, this aligns with project-wide logging granularity refinements.


222-225: Logging level change to trace is appropriate.

Consistent with the earlier exception handler, this change reduces log verbosity while maintaining the safe default behavior of hiding commands when permission checks fail. The pattern is consistent and aligns with the project's logging refinements.

Based on the AI summary and PR objectives, this aligns with project-wide logging granularity refinements.

scripts/docs/lint.py (3)

23-24: LGTM: Good refactor to introduce a named constant.

Replacing the magic number 3 with YAML_FRONTMATTER_PARTS improves maintainability and makes the intent clearer.


53-53: LGTM: Correct usage of the constant.

The constant correctly replaces the literal check, preserving the behavior while improving readability.


64-64: Excellent: Narrowed exception handling to specific types.

Replacing the broad except Exception with except (UnicodeDecodeError, OSError) is a significant improvement. This change:

  • Covers the relevant exceptions from Path.read_text() (encoding and I/O errors)
  • Improves debugging by allowing unexpected exceptions to surface
  • Aligns with the PR objectives of tightening error handling
scripts/dev/lint_fix.py (1)

7-7: LGTM! Import is necessary and correctly placed.

The subprocess import is required for the specific exception handling and follows the import grouping guidelines.

src/tux/core/base_cog.py (5)

89-93: LGTM!

The switch to logger.exception() without embedding the exception object is correct—loguru's exception() method automatically captures the full traceback from sys.exc_info(), making the code cleaner while preserving diagnostic information.


134-149: LGTM!

Using logger.trace for these edge-case failures is appropriate—it enables debugging when needed without polluting normal log output. The fallback to command.qualified_name ensures graceful degradation.


209-214: LGTM!

Consistent with the other exception handling improvements—using logger.exception for automatic traceback capture while returning the default value gracefully.


297-298: LGTM!

Using logger.exception for the background unload task is appropriate—it captures the full traceback while allowing the task to complete gracefully without crashing.


228-258: All callers of unload_if_missing_config correctly use keyword arguments. The breaking change to keyword-only arguments has been properly implemented across five call sites.

.vscode/settings.json (1)

77-79: Configuration is valid. The .markdownlint.yaml file exists at the workspace root, and the markdownlint.config with extends property is a supported format in the current markdownlint VSCode extension.

src/tux/modules/tools/wolfram.py (1)

32-32: LGTM! Logging level standardization.

The change from logger.info to logger.success aligns with the project-wide logging standardization. The SUCCESS custom logging level is properly configured in src/tux/core/logging.py via the LEVEL_COLORS dictionary and _configure_level_colors() function, providing consistent visual distinction (bold green) for successful initialization events across the codebase.

src/tux/database/controllers/base/bulk.py (1)

50-52: LGTM! Logging improvement for bulk creation.

Elevating the successful bulk creation log to logger.success improves observability by clearly marking successful operations. This aligns with the broader logging enhancements across the codebase.

src/tux/services/hot_reload/cog.py (1)

18-18: LGTM! Appropriate logging level adjustment.

Moving the hot reload cog load message to logger.trace is appropriate for this level of diagnostic detail, reducing noise at higher log levels.

src/tux/database/utils.py (1)

82-82: LGTM! Appropriate trace-level logging for fallback paths.

Downgrading these exception logs to logger.trace is appropriate since these represent expected fallback scenarios rather than errors. This reduces log noise while maintaining diagnostic capability.

Also applies to: 119-119

src/tux/database/service.py (1)

98-98: LGTM! Success-level logging for database connection.

Upgrading the successful database connection log to logger.success appropriately marks this important milestone and improves visibility of successful system initialization.

src/tux/services/handlers/activity.py (1)

174-174: LGTM! Reduced logging noise for activity rotation.

Moving the activity rotation log to logger.trace is appropriate given the high frequency of these messages. This prevents log clutter while maintaining diagnostic capability when needed.

src/tux/services/hot_reload/file_utils.py (1)

59-86: LGTM! Appropriate trace-level logging for extension discovery.

Moving the detailed extension detection logs to logger.trace is appropriate for this level of internal diagnostic detail. These checks occur frequently during hot reload operations, and trace-level logging reduces noise while maintaining full diagnostic capability.

src/tux/services/moderation/moderation_coordinator.py (1)

133-268: LGTM! Comprehensive logging granularity improvements.

The logging level adjustments throughout the moderation coordinator improve observability:

  • logger.trace for detailed flow tracking (DM timing, action execution, response preparation)
  • logger.success for completed operations (action execution, case creation, workflow completion)

These changes provide better log signal-to-noise ratio while maintaining full diagnostic capability at trace level.

src/tux/core/permission_system.py (1)

141-198: LGTM! Clear logging hierarchy for guild initialization.

The logging level adjustments in initialize_guild create a clear hierarchy:

  • logger.debug for function entry (line 141)
  • logger.trace for detailed rank checks and creation steps (lines 144, 150-152, 166, 180)
  • logger.success for successful completion (lines 196-198)

This provides better observability while reducing noise at higher log levels, aligning with the broader logging improvements across the codebase.

scripts/tux/start.py (2)

18-20: LGTM! Type annotations added to public constants.

The constants now have explicit type annotations as previously requested.


28-50: Clean startup flow with proper logging initialization.

The logging configuration is correctly prioritized before other operations. The exit code handling using symbolic constants (SUCCESS_EXIT_CODE, USER_SHUTDOWN_EXIT_CODE) improves readability.

src/tux/core/bot.py (2)

161-166: Good use of logger.success for setup completion.

Using logger.success appropriately signals a significant milestone in the bot lifecycle. The Sentry tag for setup completion aids in monitoring.


336-383: Comprehensive connection cleanup telemetry.

Good observability pattern: each connection type (Discord, DB, HTTP) has success and error span data with error details captured on failure.

tests/core/test_logging.py (4)

1-51: Well-structured test module with good isolation.

The _reset_logging_state context manager ensures proper test isolation by resetting the logging state and removing handlers after each test.


54-172: Comprehensive log level validation and determination tests.

Good coverage of the priority order (explicit > LOG_LEVEL > DEBUG > default) and validation edge cases including case normalization and invalid levels.


284-322: Good fallback testing for InterceptHandler.

Tests verify the exception fallback path when logger.patch fails, ensuring robustness of the interception mechanism.


568-636: Comprehensive StructuredLogger tests.

Good coverage of all three methods (performance, database, api_call) with various parameter combinations including optional duration and status fields.

src/tux/core/logging.py (4)

38-62: Good expansion of intercepted libraries.

Adding discord.app_commands, discord.ui, h2, pydantic, pydantic_settings, typer, and reactionmenu improves log routing coverage for commonly used libraries.


84-98: Helpful documentation for SQLAlchemy pool logging levels.

The detailed comments explaining what each pool operation log type represents (checkout, pre-ping, reset, etc.) provide valuable context for debugging connection pool issues.


221-281: Well-designed log level validation with clear priority.

The validation function properly normalizes case and provides a helpful error message with all valid options. The priority order (explicit > config.LOG_LEVEL > config.DEBUG > default) is clearly implemented.


459-474: Robust level mapping with fallback chain.

The explicit mapping dictionary plus fallback chain (mapping → loguru lookup → numeric) ensures reliable level translation from standard library to loguru.

src/tux/core/app.py (4)

98-99: Good use of logger.exception for capturing stack traces.

Changing from logger.error to logger.exception ensures the full stack trace is captured, which is valuable for debugging startup failures.


107-107: Added return type annotation.

Good addition of the -> None return type annotation to _handle_signal, aligning with the coding guidelines for complete type hints.


137-145: Critical: Logging configured before Sentry initialization.

The comment clearly explains why this order matters: Sentry uses the logger, so logging must be configured first. The fallback comment notes this is defensive since scripts/tux/start.py normally handles this.


211-224: Clean separation of shutdown and exit code logic.

Changing shutdown() to return None and introducing _get_exit_code() improves separation of concerns. The shutdown method now focuses solely on cleanup, while exit code determination is isolated to a dedicated method.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
scripts/utils/validate_paths.py (1)

165-169: Good fix for encoding error handling.

The addition of UnicodeDecodeError correctly addresses the past review comment and ensures that encoding issues when reading settings.json are properly caught and reported.

tests/core/test_logging.py (1)

196-212: Internal loguru API usage remains in duplicate configuration test.

This test still accesses logger._core.handlers which is an unstable internal API. While testing duplicate prevention is valid, consider verifying behavior through side effects instead:

  1. Capture logs to verify only one "Logging configured" message appears
  2. Use the public API pattern shown in test_configure_logging_removes_default_handler (lines 184-193)
🔎 Suggested refactor using public API
     @pytest.mark.unit
     def test_configure_logging_prevents_duplicate_configuration(self) -> None:
         """Test that configure_logging prevents duplicate configuration."""
         with _reset_logging_state():
+            # Capture log output to verify behavior
+            log_capture = io.StringIO()
             configure_logging(level="DEBUG")
-            first_handlers = len(logger._core.handlers)
-            first_handler_level = logger._core.handlers[
-                next(iter(logger._core.handlers.keys()))
-            ].levelno
+            handler_id = logger.add(log_capture, level="DEBUG")
 
-            # Second call should be ignored (duplicate prevention)
-            configure_logging(level="INFO")
-            assert len(logger._core.handlers) == first_handlers
-            # Level should still be DEBUG, not INFO (proves second call was ignored)
-            second_handler_level = logger._core.handlers[
-                next(iter(logger._core.handlers.keys()))
-            ].levelno
-            assert second_handler_level == first_handler_level
+            try:
+                # Second call should be ignored (duplicate prevention)
+                configure_logging(level="INFO")
+                
+                # Verify DEBUG messages are still captured (proves level wasn't changed to INFO)
+                logger.debug("Debug test message")
+                output = log_capture.getvalue()
+                assert "Debug test message" in output
+            finally:
+                logger.remove(handler_id)
🧹 Nitpick comments (6)
scripts/utils/validate_paths.py (1)

230-234: Remove CalledProcessError from exception handling.

subprocess.CalledProcessError is only raised when check=True is passed to subprocess.run. Since Line 225 uses check=False, this exception will never be raised and should be removed from the tuple.

🔎 Proposed fix
-    except (
-        subprocess.TimeoutExpired,
-        subprocess.CalledProcessError,
-        FileNotFoundError,
-    ) as e:
+    except (subprocess.TimeoutExpired, FileNotFoundError) as e:
         print_warning(f"Could not test pytest discovery: {e}")
         return False
src/tux/database/controllers/case.py (1)

277-296: Function name is misleading and duplicates get_all_cases.

After removing the hours parameter, get_recent_cases now returns all cases for a guild—identical to get_all_cases at lines 407-416. The name no longer reflects the behavior.

Consider either:

  1. Renaming this to something accurate (e.g., keeping only get_all_cases)
  2. Deprecating this method and pointing callers to get_all_cases
  3. Implementing actual time-based filtering (add created_at to the Case model)
tests/shared/test_config_generation.py (2)

85-98: Consider validating parsed TOML content when keys are present.

The string assertions at lines 85-87 use "or" fallbacks, and the subsequent TOML parsing validates syntax but doesn't verify structure. When the parsed dict contains keys, you could verify expected fields exist in the parsed data rather than searching raw strings.

🔎 Proposed enhancement to validate parsed content
     # Verify output contains commented field names and descriptions
     # Built-in generator uses different format - check for descriptions and commented defaults
     assert "Enable debug mode" in toml_output or "debug" in toml_output.lower()
     assert "Server port" in toml_output or "port" in toml_output.lower()
     assert "Application name" in toml_output or "name" in toml_output.lower()

     # Verify it's valid TOML by actually parsing it
     # Note: The generator may produce TOML with only comments and no key-value pairs
-    # So we just verify it's valid TOML, not that it has content
     try:
         parsed = tomllib.loads(toml_output)
         assert isinstance(parsed, dict)
-        # If parsed is empty, that's okay - it just means the generator produced comments-only TOML
+        # If parsed has keys, verify expected fields are present
+        if parsed:
+            # When keys exist, validate structure instead of raw string checks
+            expected_keys = {"debug", "port", "name"}
+            # At least some expected keys should be present if TOML has content
+            assert any(key in parsed for key in expected_keys), (
+                f"Expected at least one of {expected_keys} in parsed TOML, got: {parsed}"
+            )
     except tomllib.TOMLDecodeError:
         # If parsing fails, the TOML is invalid - that's a problem
         pytest.fail(f"Generated TOML is invalid: {toml_output}")

58-64: Consider extracting PSESettings creation to a pytest fixture.

The PSESettings initialization with warning suppression is duplicated across all four test functions. Extracting this to a fixture would reduce duplication and make the setup more maintainable.

🔎 Proposed fixture to reduce duplication

Add this fixture near the top of the file:

@pytest.fixture
def pse_settings() -> PSESettings:
    """Create PSESettings for tests, suppressing pyproject_toml_table_header warning."""
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", message=".*pyproject_toml_table_header.*")
        return PSESettings(
            root_dir=Path.cwd(),
            project_dir=Path.cwd(),
            respect_exclude=True,
        )

Then use it in each test:

-    # Generate TOML (suppress PSESettings warning about pyproject_toml_table_header)
-    with warnings.catch_warnings():
-        warnings.filterwarnings("ignore", message=".*pyproject_toml_table_header.*")
-        pse_settings = PSESettings(
-            root_dir=Path.cwd(),
-            project_dir=Path.cwd(),
-            respect_exclude=True,
-        )
-    generator = TomlGenerator(
+def test_generate_toml_format(pse_settings: PSESettings) -> None:
+    # ...
+    generator = TomlGenerator(
         pse_settings,

Also applies to: 126-132, 171-177, 231-237

tests/core/test_logging.py (2)

225-227: Consider abstracting internal API access into a test helper.

Multiple tests access logger._core.handlers[...].levelno for handler level verification. If internal API access is necessary for these assertions, isolate it in a single helper function to minimize maintenance burden if loguru internals change:

def _get_first_handler_level() -> int:
    """Get level of first handler (internal API, for testing only)."""
    handler_key = next(iter(logger._core.handlers.keys()))
    return logger._core.handlers[handler_key].levelno

Alternatively, verify behavior through log capture as shown in other tests.

Also applies to: 240-242, 251-253, 451-458


474-514: Internal _filter API access could be replaced with behavior-based testing.

Accessing handler._filter directly tests implementation rather than behavior. Consider verifying the filter's effect through actual log output:

🔎 Suggested behavior-based approach
@pytest.mark.unit
def test_safe_message_filter_escapes_curly_braces(self) -> None:
    """Test that messages with curly braces are logged without format errors."""
    with _reset_logging_state():
        configure_logging(level="DEBUG")
        
        log_capture = io.StringIO()
        logger.add(log_capture, level="DEBUG", format="{message}")
        
        # This would cause KeyError without proper escaping
        logger.info("Test {key} value")
        
        output = log_capture.getvalue()
        # Verify message logged successfully (braces escaped internally)
        assert "Test" in output and "value" in output
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05fa9d2 and 1ce1e93.

📒 Files selected for processing (11)
  • scripts/dev/lint_fix.py
  • scripts/utils/validate_paths.py
  • src/tux/core/logging.py
  • src/tux/database/controllers/case.py
  • tests/core/test_logging.py
  • tests/database/test_database_model_relationships.py
  • tests/services/test_error_extractors_integration.py
  • tests/services/test_error_handler.py
  • tests/services/test_service_wrappers.py
  • tests/shared/test_config_generation.py
  • tests/shared/test_config_loaders_basic.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/services/test_error_handler.py
  • tests/shared/test_config_loaders_basic.py
  • tests/database/test_database_model_relationships.py
  • tests/services/test_error_extractors_integration.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use strict type hints with the form Type | None instead of Optional[Type] in Python
Use NumPy docstrings for documenting Python functions and classes
Prefer absolute imports, but relative imports are allowed within the same module in Python
Use import grouping in Python: stdlib → third-party → local imports
Use 88 character line length for Python code
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of the Python file unless absolutely necessary
Use async/await for I/O operations and asynchronous code in services and database operations
Use Pydantic for data validation in Python models and services
Log errors with context and handle Discord rate limits appropriately
Do not include secrets in code; use environment variables for configuration
Validate all user inputs in Python code
Implement proper permission checks in Discord commands and services
Keep Python files to a maximum of 1600 lines
Use descriptive filenames for Python modules
Include complete type hints in all public APIs
Include docstrings for public API functions and classes

Files:

  • tests/core/test_logging.py
  • src/tux/core/logging.py
  • scripts/utils/validate_paths.py
  • scripts/dev/lint_fix.py
  • tests/services/test_service_wrappers.py
  • src/tux/database/controllers/case.py
  • tests/shared/test_config_generation.py
**/services/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/services/**/*.py: Apply transactions for multi-step database operations in Python services
Use custom exceptions for business logic error handling in services
Implement dependency injection in service classes
Keep services stateless where possible
Cache frequently accessed data to optimize performance
Optimize database queries for performance

Files:

  • tests/services/test_service_wrappers.py
**/database/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/database/**/*.py: Use SQLModel for ORM operations to ensure type safety with Pydantic integration
Implement model-level validation in SQLModel database models

Files:

  • src/tux/database/controllers/case.py
🧠 Learnings (1)
📚 Learning: 2025-12-21T20:54:45.840Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T20:54:45.840Z
Learning: Applies to **/modules/**/*.py : Use hybrid commands (slash + traditional) for Discord bot command modules

Applied to files:

  • src/tux/core/logging.py
🧬 Code graph analysis (4)
tests/core/test_logging.py (1)
src/tux/core/logging.py (13)
  • StructuredLogger (546-642)
  • _configure_third_party_logging (433-513)
  • _determine_log_level (249-282)
  • _format_record (337-378)
  • _get_relative_file_path (381-405)
  • _validate_log_level (222-246)
  • configure_logging (165-213)
  • configure_testing_logging (155-162)
  • verify_logging_interception (516-543)
  • emit (452-500)
  • performance (555-578)
  • database (581-604)
  • api_call (607-642)
tests/services/test_service_wrappers.py (1)
src/tux/modules/utility/run.py (2)
  • GodboltService (222-268)
  • WandboxService (271-331)
src/tux/database/controllers/case.py (2)
src/tux/database/models/models.py (2)
  • Case (571-718)
  • Guild (36-173)
src/tux/database/controllers/base/query.py (1)
  • find_one (49-79)
tests/shared/test_config_generation.py (3)
typings/pydantic_settings_export/models.pyi (2)
  • FieldInfoModel (35-78)
  • SettingsInfoModel (82-99)
src/tux/shared/config/generators/json.py (2)
  • JsonGenerator (33-153)
  • JsonGeneratorSettings (26-30)
src/tux/shared/config/generators/yaml.py (2)
  • YamlGenerator (36-190)
  • YamlGeneratorSettings (26-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run All Tests (3.13.8)
  • GitHub Check: Seer Code Review
🔇 Additional comments (27)
scripts/dev/lint_fix.py (2)

7-7: LGTM! Import correctly added to support enhanced exception handling.

The subprocess import is necessary for catching subprocess.CalledProcessError and follows the coding guidelines for import grouping (stdlib imports grouped together).


22-30: Excellent implementation of the suggested exception handling!

The exception handling now properly addresses the concerns raised in the previous review:

  • Line 25 narrows the exception from a broad catch to specifically handle subprocess.CalledProcessError for command execution failures.
  • Lines 28-30 add handling for system-level exceptions (FileNotFoundError, PermissionError, OSError) that can occur when the executable is missing or inaccessible.
  • The error message includes the exception details for better debugging.

This prevents the script from crashing with unhandled exceptions and provides clear, actionable error messages to users.

src/tux/database/controllers/case.py (6)

21-24: LGTM!

Using TYPE_CHECKING guards for AsyncSession and DatabaseService is a good practice to prevent runtime imports while preserving type hints for static analysis.


182-189: LGTM!

Good use of loguru's granular log levels: trace for detailed debugging data and success for confirming completed operations.


335-335: LGTM!

Using bool(active_cases) is more Pythonic and idiomatic than comparing length.


427-432: LGTM! Good performance improvement.

Offloading sorting to the database with order_by=[Case.id.desc()] is more efficient than fetching all matching cases and sorting in Python. The assumption that higher ID equals newer case is valid for auto-increment primary keys.


445-451: LGTM!

The extended docstring clearly explains the semantics of case_processed vs case_status and the preservation of case_expires_at for historical reference.


480-511: LGTM!

Good log level separation: trace for routine checks with no results, info when expired cases are found (actionable), and debug for per-case details. This reduces log noise during normal operation while keeping important events visible.

tests/services/test_service_wrappers.py (3)

6-11: LGTM: Import reorganization improves test efficiency.

Moving these imports to module level reduces per-test overhead and makes dependencies more explicit. The import grouping correctly follows the stdlib → third-party → local pattern as per coding guidelines.


190-194: LGTM: Formatting improvement for readability.

The multi-line formatting with trailing commas improves readability and follows Python conventions.


207-209: This test correctly reflects the designed API contract. The run() method's return type is documented as str | None, with the docstring explicitly stating "None if execution failed." The sole caller at line 557 already properly handles the None case with a check at line 559. The implementations intentionally catch exceptions, log warnings with error context, and return None — this is not a breaking change but the documented, intended behavior.

Likely an incorrect or invalid review comment.

tests/shared/test_config_generation.py (4)

1-22: LGTM!

Imports are well-organized following the coding guidelines: standard library → third-party → local imports, with proper grouping.


101-144: LGTM!

The YAML generation test properly verifies that field descriptions are included as comments and values are commented out as expected.


146-185: LGTM!

The JSON generation test correctly validates both parsing and structure by checking for expected keys in the parsed output.


187-271: LGTM!

The nested settings test properly validates that child settings are correctly represented in the generated TOML and can be parsed and accessed.

tests/core/test_logging.py (6)

41-51: Well-structured test isolation with context manager.

The _reset_logging_state() context manager properly saves/restores state and cleans up handlers in the finally block. This ensures test isolation even when tests fail.


54-84: Good test coverage for log level validation.

Tests comprehensively cover valid levels, invalid levels, and case normalization with clear test names and assertions.


86-172: Thorough testing of log level determination priority.

Tests correctly verify the priority order (explicit > config.LOG_LEVEL > config.DEBUG > default "INFO") and validation error propagation.


256-398: Comprehensive InterceptHandler testing with proper edge cases.

Good coverage of:

  • Level mapping for all standard levels
  • Exception fallback when patching fails
  • Unknown/custom level fallback to numeric

The use of standard library logging.getLogger().handlers[0] is appropriate here (not internal loguru API).


400-422: LGTM!

Tests correctly verify third-party library interception configuration using the standard library logging API.


516-640: Good coverage of formatting and structured logging utilities.

Tests properly verify:

  • _format_record exception fallback behavior
  • _get_relative_file_path fallback when "src" not in path
  • verify_logging_interception output format
  • All StructuredLogger methods with various parameter combinations
src/tux/core/logging.py (6)

24-64: Good additions to intercepted libraries list.

The new libraries (discord.app_commands, discord.ui, h2, pydantic, pydantic_settings, typer, reactionmenu) are appropriate for a Discord bot using these frameworks. Comments clearly explain each library's purpose.


84-104: Excellent documentation of SQLAlchemy pool logging behavior.

The detailed comments explaining pool operations (checkout, pre-ping, __connect, return/finalize, reset) will help future maintainers understand why these are set to WARNING and when to change them for debugging.


142-144: LGTM!

Using a set for VALID_LOG_LEVELS provides O(1) membership testing, and includes all standard loguru levels.


222-247: Well-implemented validation function.

Clear docstring, proper type hints, and helpful error message that lists valid options. Using sorted() on the error message ensures deterministic output.


249-283: Correct validation integration in log level determination.

Validation is properly applied to user-provided values (explicit level and config.LOG_LEVEL) while hardcoded returns ("DEBUG", "INFO") bypass validation since they're known-valid constants.


460-476: Explicit level mapping adds robustness.

While standard library level names typically match loguru's, the explicit mapping dictionary provides:

  1. Clear documentation of expected mappings
  2. Safety against potential naming differences
  3. Easy extensibility if custom mappings are needed

The numeric fallback on line 475 correctly handles custom/unknown levels.

- Removed the debug flag from the run function in main.py, streamlining the bot's startup process.
- Updated the start command in start.py to directly call the run function without a separate _run_bot function.
- Introduced a new method _get_exit_code in TuxApp to determine the exit code based on shutdown type, improving clarity in shutdown logic.
- Enhanced the shutdown method documentation in bot.py for better understanding of the shutdown process.
…istency

- Removed the custom `_log_step` method from various setup services.
- Replaced instances of custom logging with `loguru` logger for improved logging consistency and clarity.
- Enhanced logging messages across setup processes, including success and error handling for better monitoring.
…ance monitoring

- Added SLOW_COG_LOAD_THRESHOLD constant to track slow-loading cogs.
- Refactored comments for clarity and removed redundant code to streamline the cog loading process.
- Improved logging for cog loading metrics, including success and failure rates.
- Enhanced error handling and telemetry integration for better monitoring of cog loading performance.
- Updated the import statement to include the PermissionSystem class for better clarity.
- Enhanced error message variable naming for improved readability.
- Simplified guild and user ID retrieval logic to reduce redundancy.
- Updated type annotations for the _get_user_rank_from_interaction function to specify PermissionSystem.
…che handling

- Removed redundant comments and simplified logic for prefix retrieval and persistence.
- Enhanced error handling to ensure consistent fallback to default prefixes.
- Improved cache invalidation logic to maintain consistency during prefix updates.
- Optimized loading of all prefixes with clearer structure and reduced complexity.
- Updated type annotations for the bot parameter in the TaskMonitor class to specify the Tux type for better clarity and type safety.
- Improved error message variable naming for consistency and readability in the task monitoring error handling logic.
…lities

- Updated the AFK update logic to utilize a composite primary key for better accuracy.
- Improved the find_many method to accept a 'where' parameter for flexible filtering.
- Enhanced the expired AFK retrieval method to accurately return temporary AFK entries based on expiration criteria.
…formatting

- Removed commented-out server and roles commands for cleaner code.
- Replaced direct embed creation with a dedicated method for consistency.
- Enhanced boolean value representation in embeds for improved readability.
- Updated footer text handling in embed creation for better clarity.
- Improved type annotations for better clarity and type safety in the Tldr class.
- Streamlined cache initialization and update logic for efficiency.
- Consolidated command handling for both slash and prefix commands into a single method.
- Enhanced logging messages for cache operations and command responses.
- Removed redundant comments and improved code readability.
…ling

- Enhanced type annotations for functions to improve clarity and type safety.
- Simplified response handling in the sendresponse function, ensuring consistent error raising for connection issues and HTTP errors.
- Updated docstrings for better documentation of parameters and return types.
- Removed redundant comments and improved code readability throughout the module.
- Removed the unused NoneType import to streamline the code.
- Updated the type annotation for the 'until' parameter in the add_afk function for better clarity and type safety.
…ance monitoring

- Introduced SLOW_COG_LOAD_THRESHOLD to track slow-loading cogs, enhancing performance monitoring capabilities.
- Updated the __all__ list to include the new constant for better accessibility.
…omain-based organization

- Move tests from tests/unit/ and tests/integration/ to domain-specific directories
- New structure: tests/core/, tests/database/, tests/services/, tests/shared/, tests/modules/, tests/help/, tests/plugins/
- Add comprehensive database model tests (creation, queries, relationships, serialization, performance, timestamps)
- Add error extractor tests (arguments, flags, HTTP, permissions, roles, utilities, integration)
- Add config loader tests (basic, environment variable handling, generation)
- Add version system tests (module functions, version objects, system integration)
- Reorganize test fixtures with improved utilities and validation functions
- Remove deprecated test directories and fixture files
…ements

- Update fixtures.md with new fixture structure and organization
- Update debugging.md and versioning.md with current test paths
- Add CHANGELOG entries for test reorganization in [Unreleased] section
- Update pytest command to run all tests from tests/ directory
- Remove references to old tests/unit/ and tests/integration/ directories
- Ensure all tests are discovered regardless of directory structure
- Comment out legacy test directory patterns in pyproject.toml with explanatory notes
- Update AGENTS.md project structure to reflect new test directories
- Remove old test directory references from cleanup script
- Update test file docstrings with correct paths
- Use TYPE_CHECKING for conditional imports to reduce runtime overhead
- Alphabetize __all__ exports for better maintainability
- Improve import organization and structure
- Add comments explaining Sentry boolean value usage (not flags)
- Improve error extractor documentation and structure
- Update imports and error handling across modules
- Improve script error handling and validation
- Minor code quality improvements
- Add tests for handling TimeoutException and ConnectError, ensuring the extractor returns an empty dict for these cases.
- Introduce a test for HTTPStatusError, verifying that the extractor correctly retrieves status code, URL, and response text from the error object.
- Replace generic exception handling with specific logging for better traceability.
- Update logger calls to use logger.exception for improved error context.
- Add debug logs for fallback scenarios in command usage generation.
- Replace generic exception handling with specific subprocess.CalledProcessError for better error context.
- Ensure that linting errors are reported accurately, enhancing the robustness of the linting script.
- Update exception handling in the linting script to catch only UnicodeDecodeError and OSError.
- Enhance clarity of error messages when files cannot be read, improving the robustness of the linting process.
- Update exception handling in validate_paths.py to catch specific JSONDecodeError and subprocess-related exceptions.
- Enhance error messages for better clarity and robustness in path validation processes.
- Change markdownlint configuration to use the "extends" property for better compatibility with workspace settings.
- Ensure proper integration of the markdownlint configuration file in the VSCode settings.
- Update logging initialization to ensure it occurs before Sentry setup, providing a defensive fallback in TuxApp.
- Improve log level handling with explicit validation and normalization for better robustness.
- Refactor logger calls to use appropriate log levels, enhancing clarity and consistency across the application.
- Add comprehensive unit tests for the logging system, covering configuration, level determination, and third-party library interception.
- Add specific exception handling for FileNotFoundError, PermissionError, and OSError to improve robustness.
- Provide clearer error messages when the linting tool fails to execute, enhancing user feedback during the linting process.
…arsing

- Update exception handling in validate_paths.py to catch both JSONDecodeError and UnicodeDecodeError.
- Improve error messaging for better clarity when parsing settings.json fails.
- Introduce VALID_LOG_LEVELS to the list of exported members in the logging module for improved log level validation and accessibility.
…tion

- Remove unused hours parameter from get_recent_cases method to streamline functionality.
- Update method documentation to clarify that it retrieves all cases for a guild, noting that time-based filtering is not yet implemented due to the absence of a created_at field in the Case model.
- Update tests to verify logging handler behavior using the public API by capturing emitted log messages.
- Ensure that the test checks for the presence of the expected log message and its log level in the output.
- Modify the assertion in the test for error extractors to check for the exact URL format instead of a substring match, ensuring more precise validation of the context returned during error handling.
…er assertions

- Update the test for model relationships to ensure proper cascade deletion by using the delete statement and refreshing the session.
- Simplify error handler tests by removing unnecessary configuration parameter from the _send_error_response method calls.
- Enhance TOML generation tests to validate that generated TOML is correctly parsed, even if it contains only comments.
- Refactor invalid file loader tests to clarify warning emission during initialization and ensure they return empty dictionaries for invalid formats.
…cess errors

- Eliminate the handling of subprocess.CalledProcessError in check_pytest_config function to streamline error management.
- Maintain focus on relevant exceptions to improve clarity in error reporting during pytest configuration checks.
@kzndotsh kzndotsh merged commit 2d247ce into main Dec 28, 2025
27 of 28 checks passed
@kzndotsh kzndotsh deleted the cleanup branch December 28, 2025 16:20
@sentry
Copy link

sentry bot commented Dec 30, 2025

Issues attributed to commits in this pull request

This pull request was merged and Sentry observed the following issues:

This was referenced Dec 30, 2025
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