[0aa8c331] chore(conventions): fix or waive real lint suppressions in roboco/ (#725) (#734)

Co-authored-by: roboco-app[bot] <302741806+roboco-app[bot]@users.noreply.github.com>
Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
This commit is contained in:
roboco-app[bot]
2026-07-30 07:04:46 +00:00
committed by GitHub
co-authored by roboco-app[bot] <302741806+roboco-app[bot]@users.noreply.github.com> Backend Developer 1
parent e1f5e0950e
commit 9f07183b01
7 changed files with 43 additions and 11 deletions
+19
View File
@@ -107,3 +107,22 @@ waivers:
not a production component — the same pattern already used by the
pre-existing use-agents.test.tsx / use-observability.test.tsx hook
tests colocated under hooks/__tests__/.
- path: roboco/api/routes/prompter_live.py
rule: no_lint_suppressions
reason: >-
preview_live_batch's `session_id` path parameter is unused in the
function body (the endpoint is a pure precompute) but must stay in the
signature under that exact name for FastAPI to bind the
`/live/{session_id}/preview-batch` path — renaming or dropping it
breaks routing, so the ARG001 suppression is a permanent framework
constraint, not silenced debt.
- path: roboco/foundation/policy/lifecycle.py
rule: no_lint_suppressions
reason: >-
The module-load-time `_run_all_lifecycle_validators` import sits below
other module code deliberately: its validators live in
`roboco.foundation._validate_lifecycle`, a sibling of
`foundation/_validate.py`, and `roboco.foundation.__init__` eagerly
imports `_validate` — importing it at the top of this file would
create a real import cycle. The E402 suppression documents a genuine
circular-import constraint, not silenced debt.
+1 -2
View File
@@ -8,6 +8,7 @@ for production and colored console output for development.
import logging
import logging.handlers
import os
import re as _re
import sys
from collections.abc import Mapping, MutableMapping
from pathlib import Path
@@ -39,8 +40,6 @@ def add_app_context(
# `ghs_`/`gho_`) plus bearer tokens and generic "key = <value>" shapes. The
# regex is intentionally loose on length so it catches tokens regardless of
# future format tweaks.
import re as _re # noqa: E402 — module-local alias only
_SECRET_PATTERNS: list[_re.Pattern[str]] = [
_re.compile(r"(github_pat_[A-Za-z0-9_]{20,})"),
_re.compile(r"(ghp_[A-Za-z0-9_]{20,})"),
+4 -1
View File
@@ -54,7 +54,10 @@ class JournalEntry(TimestampMixin):
# Sentiment/mood tracking (for growth analysis)
sentiment: str | None = Field(
default=None,
description="Sentiment indicator (positive, neutral, negative, frustrated, confident, etc.)", # noqa: E501
description=(
"Sentiment indicator (positive, neutral, negative, frustrated, "
"confident, etc.)"
),
)
# Visibility
+2 -1
View File
@@ -10940,12 +10940,13 @@ Start by:
from roboco.utils.converters import require_uuid
# Compute human-friendly duration
_MINUTES_PER_HOUR = 60
duration_desc = "unknown duration"
try:
activated_at = datetime.fromisoformat(activated_at_str)
elapsed = datetime.now(UTC) - activated_at
total_minutes = int(elapsed.total_seconds() / 60)
if total_minutes < 60: # noqa: PLR2004
if total_minutes < _MINUTES_PER_HOUR:
duration_desc = f"{total_minutes} minute(s)"
else:
duration_desc = f"{total_minutes // 60}h {total_minutes % 60}m"
+7 -2
View File
@@ -198,7 +198,7 @@ class KanbanService(BaseService):
return KanbanBoard(
id=f"{board_type.value}-{team.value if team else 'all'}",
title=f"{team.value.title() if team else 'All'} {board_type.value.title()} Board", # noqa: E501
title=self._board_title(team, board_type),
board_type=board_type,
team=team,
columns=column_list,
@@ -207,6 +207,11 @@ class KanbanService(BaseService):
last_updated=datetime.now(UTC),
)
def _board_title(self, team: Team | None, board_type: KanbanBoardType) -> str:
"""Shared board title: '<Team|All> <BoardType> Board'."""
team_label = team.value.title() if team else "All"
return f"{team_label} {board_type.value.title()} Board"
def _get_swimlane_key(self, task: TaskTable, swimlane_by: str) -> str:
"""Get the swimlane key for a task."""
if swimlane_by == "priority":
@@ -319,7 +324,7 @@ class KanbanService(BaseService):
return KanbanBoard(
id=f"{board_type.value}-{team.value if team else 'all'}-swimlane",
title=f"{team.value.title() if team else 'All'} {board_type.value.title()} Board", # noqa: E501
title=self._board_title(team, board_type),
board_type=board_type,
team=team,
swimlanes=swimlanes,
+6 -4
View File
@@ -28,6 +28,7 @@ from sqlalchemy import select
if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
from roboco.foundation.identity import PM_ROLES
from roboco.foundation.identity import Role as _FoundationRole
from roboco.foundation.policy.communications import NOTIFY_SENDER_ROLES
from roboco.models import AgentRole, Team
@@ -41,6 +42,11 @@ from roboco.models.permissions import (
)
from roboco.services.base import SingletonService
# PM_ROLES is canonical in foundation.identity; re-exported here for
# backwards compatibility with existing `from roboco.services.permissions
# import PM_ROLES` consumers.
__all__ = ["PM_ROLES"]
# =============================================================================
# NOTIFICATION PERMISSIONS (derived from foundation.NOTIFY_SENDER_ROLES)
# =============================================================================
@@ -264,10 +270,6 @@ async def has_privileged_access(db: "AsyncSession", agent_id: UUID) -> bool:
return role in PRIVILEGED_ROLES if role else False
# PM_ROLES is canonical in foundation.identity. Re-export for backwards
# compatibility; new consumers import from foundation directly.
from roboco.foundation.identity import PM_ROLES # noqa: F401, E402
MANAGEMENT_ROLES = frozenset(
{AgentRole.CEO, AgentRole.PRODUCT_OWNER, AgentRole.CELL_PM, AgentRole.MAIN_PM}
)
+4 -1
View File
@@ -157,9 +157,12 @@ class TgCockpitService(BaseService):
tokens_by_day,
models_by_day,
) = await self._session_metrics_by_day(days)
_MIN_DAYS_FOR_PRIOR_COMPARISON = 2
series = [round(cost_by_day.get(d, 0.0), 4) for d in days]
today_cost = series[-1] if series else 0.0
prior_cost = series[-2] if len(series) >= 2 else 0.0 # noqa: PLR2004
prior_cost = (
series[-2] if len(series) >= _MIN_DAYS_FOR_PRIOR_COMPARISON else 0.0
)
today_tokens = tokens_by_day.get(days[-1], 0) if days else 0
today_models = models_by_day.get(days[-1], set()) if days else set()
# $0 with real tokens spent, on a subscription-billed-but-