From 24b16571ea3614161b784ad796e156611c313d89 Mon Sep 17 00:00:00 2001 From: Oliver Le Date: Thu, 16 Apr 2026 21:01:21 -0700 Subject: [PATCH] chore: fix 66 ruff violations (unblock SDK CI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mix of auto-fixes (ruff --fix) and manual touch-ups. No behavior change. Auto-fixes (57 lines across 20 files): - UP017: datetime.timezone.utc → datetime.UTC - I001: unsorted imports - F401: unused imports - UP037: quoted annotations unwrapped - UP045: Optional[X] → X | None - RUF100: unused noqa - F541: f-strings missing placeholders - TC005: empty type-checking blocks Manual (9): - cli.py: collapse nested HTTPS-guard if; 2× chmod try/except → contextlib.suppress - correction_detector.py: remove unused correction_text local - collaborative_filter.py: zip(strict=True) for vec_a/vec_b cosine - clustering.py: for _category (unused loop key in .items()) - rule_pipeline.py: collapse graduation nested-if pairs into single conditions - loop_intelligence.py: 2× try/except/pass → contextlib.suppress Unblocks SDK CI so PR #98 (RetainOrchestrator) and PR #99 (hook daemon) can land through normal CI rather than admin bypass. Co-Authored-By: Gradata --- src/gradata/_core.py | 1 - src/gradata/_events.py | 2 +- src/gradata/cli.py | 28 +++++------ src/gradata/correction_detector.py | 5 +- .../bandits/collaborative_filter.py | 6 +-- src/gradata/enhancements/carl.py | 2 +- src/gradata/enhancements/clustering.py | 2 +- .../graduation/agent_graduation.py | 21 ++++---- .../enhancements/graduation/judgment_decay.py | 5 +- src/gradata/enhancements/meta_rules.py | 2 +- .../enhancements/profiling/tone_profile.py | 5 +- src/gradata/enhancements/rule_pipeline.py | 39 ++++++++++----- .../enhancements/scoring/brain_scores.py | 7 +-- .../enhancements/scoring/calibration.py | 3 +- .../scoring/correction_tracking.py | 5 -- .../enhancements/scoring/gate_calibration.py | 2 +- .../enhancements/scoring/loop_intelligence.py | 49 +++++++++---------- .../enhancements/scoring/memory_extraction.py | 6 +-- src/gradata/enhancements/scoring/reports.py | 4 +- src/gradata/enhancements/self_improvement.py | 3 -- src/gradata/hooks/self_review.py | 2 +- 21 files changed, 95 insertions(+), 104 deletions(-) diff --git a/src/gradata/_core.py b/src/gradata/_core.py index 5c28f8e7..c890c7c5 100644 --- a/src/gradata/_core.py +++ b/src/gradata/_core.py @@ -989,7 +989,6 @@ def _cloud_sync_session( try: import hashlib import os - from pathlib import Path # 1. Resolve cloud credentials: ~/.gradata/config.toml or env var diff --git a/src/gradata/_events.py b/src/gradata/_events.py index 6437865c..d8bc4adf 100644 --- a/src/gradata/_events.py +++ b/src/gradata/_events.py @@ -414,7 +414,7 @@ class RetainOrchestrator: position on the next call. """ - def __init__(self, brain_dir: "str | Path") -> None: + def __init__(self, brain_dir: str | Path) -> None: from pathlib import Path as _Path self.brain_dir = _Path(brain_dir) self.events_path = self.brain_dir / "events.jsonl" diff --git a/src/gradata/cli.py b/src/gradata/cli.py index 6d9db36c..35a9bee2 100644 --- a/src/gradata/cli.py +++ b/src/gradata/cli.py @@ -20,6 +20,7 @@ from __future__ import annotations import argparse +import contextlib import json import logging import sys @@ -516,7 +517,7 @@ def _sanitize_toml_value(val: str) -> str: return val.replace("\n", "").replace("\r", "").replace("[", "").replace("]", "").replace('"', "").replace("\\", "").strip() -def _check_config_permissions(config_path: "Path") -> None: +def _check_config_permissions(config_path: Path) -> None: """Finding 4: warn if config file is world-readable (Unix only).""" import os import stat @@ -538,17 +539,18 @@ def cmd_login(args): import os import time import webbrowser + from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen - from urllib.error import URLError, HTTPError api_url = os.environ.get("GRADATA_API_URL", "https://api.gradata.ai/api/v1") # Finding 5: reject non-HTTPS API URLs (allow localhost for development) - if not api_url.startswith("https://"): - if not (api_url.startswith("http://localhost") or api_url.startswith("http://127.0.0.1")): - print(f"Error: GRADATA_API_URL must use HTTPS (got: {api_url})") - print(" HTTP is only allowed for localhost/127.0.0.1 during development.") - sys.exit(1) + if not api_url.startswith("https://") and not ( + api_url.startswith("http://localhost") or api_url.startswith("http://127.0.0.1") + ): + print(f"Error: GRADATA_API_URL must use HTTPS (got: {api_url})") + print(" HTTP is only allowed for localhost/127.0.0.1 during development.") + sys.exit(1) # Step 1: Request a device code try: @@ -614,10 +616,9 @@ def cmd_login(args): config_path.parent.mkdir(parents=True, exist_ok=True) # Finding 4: restrict directory permissions (Unix) - try: + # Windows — os.chmod has limited effect, suppress the error + with contextlib.suppress(OSError, AttributeError): os.chmod(config_path.parent, 0o700) - except (OSError, AttributeError): - pass # Windows — os.chmod has limited effect # Finding 12: sanitize values before writing TOML safe_key = _sanitize_toml_value(api_key) @@ -634,16 +635,15 @@ def cmd_login(args): ) # Finding 4: restrict file permissions (Unix) - try: + # Windows — os.chmod has limited effect, suppress the error + with contextlib.suppress(OSError, AttributeError): os.chmod(config_path, 0o600) - except (OSError, AttributeError): - pass # Windows — os.chmod has limited effect # Also set in the environment for the current process (so # subsequent brain operations in the same shell can auto-sync) os.environ["GRADATA_API_KEY"] = api_key - print(f" Connected! Your brain will sync to app.gradata.ai") + print(" Connected! Your brain will sync to app.gradata.ai") print(f" Config saved to {config_path}") # Finding 4: check permissions on startup diff --git a/src/gradata/correction_detector.py b/src/gradata/correction_detector.py index c15fe6ed..dc6ea690 100644 --- a/src/gradata/correction_detector.py +++ b/src/gradata/correction_detector.py @@ -24,7 +24,7 @@ from __future__ import annotations import re -from dataclasses import dataclass, field +from dataclasses import dataclass from enum import StrEnum from typing import Any @@ -149,7 +149,7 @@ def to_dict(self) -> dict[str, Any]: } @classmethod - def from_dict(cls, data: dict[str, Any]) -> "StructuredCorrection": + def from_dict(cls, data: dict[str, Any]) -> StructuredCorrection: """Deserialize from a plain dict.""" return cls( what_wrong=data.get("what_wrong", ""), @@ -416,7 +416,6 @@ def extract_structured_correction( # one/both of draft/final are empty. Nothing to extract. return None - correction_text = final or context full_text = " ".join(filter(None, [context, final])) what_wrong = _extract_what_wrong(draft, final) diff --git a/src/gradata/enhancements/bandits/collaborative_filter.py b/src/gradata/enhancements/bandits/collaborative_filter.py index ce645919..42d0207c 100644 --- a/src/gradata/enhancements/bandits/collaborative_filter.py +++ b/src/gradata/enhancements/bandits/collaborative_filter.py @@ -34,7 +34,7 @@ import hashlib import math -from dataclasses import dataclass, field +from dataclasses import dataclass @dataclass @@ -61,7 +61,7 @@ class BrainFingerprint: category_distribution: dict[str, int] # category -> rule count @classmethod - def from_lessons(cls, lessons: list, domain: str = "", total_sessions: int = 0) -> "BrainFingerprint": + def from_lessons(cls, lessons: list, domain: str = "", total_sessions: int = 0) -> BrainFingerprint: """Build a fingerprint from a list of Lesson objects.""" rules = [] @@ -122,7 +122,7 @@ def compute_brain_similarity(a: BrainFingerprint, b: BrainFingerprint) -> float: vec_b = [b.category_distribution.get(c, 0) for c in sorted(all_cats)] # Cosine similarity - dot = sum(x * y for x, y in zip(vec_a, vec_b)) + dot = sum(x * y for x, y in zip(vec_a, vec_b, strict=True)) mag_a = math.sqrt(sum(x * x for x in vec_a)) mag_b = math.sqrt(sum(x * x for x in vec_b)) diff --git a/src/gradata/enhancements/carl.py b/src/gradata/enhancements/carl.py index 303a54c4..44081d7f 100644 --- a/src/gradata/enhancements/carl.py +++ b/src/gradata/enhancements/carl.py @@ -1,6 +1,6 @@ """Backward-compatibility shim — all code moved to behavioral_engine.py.""" -from gradata.enhancements.behavioral_engine import ( # noqa: F401 +from gradata.enhancements.behavioral_engine import ( BehavioralContract, ConstraintViolation, ContractRegistry, diff --git a/src/gradata/enhancements/clustering.py b/src/gradata/enhancements/clustering.py index 7794a5d3..01f0361f 100644 --- a/src/gradata/enhancements/clustering.py +++ b/src/gradata/enhancements/clustering.py @@ -156,7 +156,7 @@ def promote_instinct_clusters( by_category[lesson.category].append(lesson) promoted: list[str] = [] - for category, members in by_category.items(): + for _category, members in by_category.items(): if len(members) < min_cluster_size: continue diff --git a/src/gradata/enhancements/graduation/agent_graduation.py b/src/gradata/enhancements/graduation/agent_graduation.py index e195eb4b..270be1fa 100644 --- a/src/gradata/enhancements/graduation/agent_graduation.py +++ b/src/gradata/enhancements/graduation/agent_graduation.py @@ -48,26 +48,25 @@ import json import re from dataclasses import dataclass, field -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path # Import graduation constants from self_improvement (same research-backed values) from gradata.enhancements.self_improvement import ( - SURVIVAL_BONUS, ACCEPTANCE_BONUS, + MIN_APPLICATIONS_FOR_PATTERN, + MIN_APPLICATIONS_FOR_RULE, MISFIRE_PENALTY, PATTERN_THRESHOLD, RULE_THRESHOLD, - MIN_APPLICATIONS_FOR_PATTERN, - MIN_APPLICATIONS_FOR_RULE, - LessonState, + SURVIVAL_BONUS, Lesson, - parse_lessons, + LessonState, format_lessons, get_maturity_phase, + parse_lessons, ) - # --------------------------------------------------------------------------- # Approval Gate Thresholds # --------------------------------------------------------------------------- @@ -250,7 +249,7 @@ def compile_deterministic_rule(lesson: Lesson) -> DeterministicRule | None: def _now() -> str: - return datetime.now(timezone.utc).isoformat() + return datetime.now(UTC).isoformat() # --------------------------------------------------------------------------- @@ -450,7 +449,7 @@ def _extract_agent_lesson( category = "AGENT_" + profile.agent_type.upper() if edit_category: category = edit_category.upper() - today = datetime.now(timezone.utc).strftime("%Y-%m-%d") + today = datetime.now(UTC).strftime("%Y-%m-%d") # Check for duplicate (same edit pattern already captured) edit_lower = edits.lower()[:100] @@ -773,7 +772,7 @@ def compute_quality_scores(self) -> dict: "best_agent": best, } - def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list["DeterministicRule"]: + def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list[DeterministicRule]: """Get RULE-tier lessons compiled into enforceable guard logic. Only RULE-tier lessons with an enforceable pattern are returned. @@ -820,7 +819,7 @@ def get_deterministic_rules(self, agent_type: str, task_type: str = "") -> list[ return rules - def enforce_rules(self, agent_type: str, output: str, task_type: str = "") -> "EnforcementResult": + def enforce_rules(self, agent_type: str, output: str, task_type: str = "") -> EnforcementResult: """Apply all deterministic RULE-tier guards to agent output. Returns an EnforcementResult with pass/fail and details of any violations. diff --git a/src/gradata/enhancements/graduation/judgment_decay.py b/src/gradata/enhancements/graduation/judgment_decay.py index 0e25c626..213aef60 100644 --- a/src/gradata/enhancements/graduation/judgment_decay.py +++ b/src/gradata/enhancements/graduation/judgment_decay.py @@ -35,12 +35,11 @@ from typing import Any from gradata.enhancements.self_improvement import ( - LessonState, - Lesson, PATTERN_THRESHOLD, + Lesson, + LessonState, ) - # Decay constants DECAY_PER_IDLE_SESSION = 0.02 CONFIDENCE_FLOOR = 0.10 diff --git a/src/gradata/enhancements/meta_rules.py b/src/gradata/enhancements/meta_rules.py index 03da4d64..c9756259 100644 --- a/src/gradata/enhancements/meta_rules.py +++ b/src/gradata/enhancements/meta_rules.py @@ -681,7 +681,7 @@ def _try_llm_principle(rules: list[Lesson], category: str) -> str | None: api_base=b, model=os.environ.get("GRADATA_LLM_MODEL", "gpt-4o-mini"), ) - except Exception as exc: # noqa: BLE001 -- degrade to deterministic + except Exception as exc: _log.debug("OpenAI-compat synthesis failed for %s: %s", category, exc) return None diff --git a/src/gradata/enhancements/profiling/tone_profile.py b/src/gradata/enhancements/profiling/tone_profile.py index 1af356c6..58cbf153 100644 --- a/src/gradata/enhancements/profiling/tone_profile.py +++ b/src/gradata/enhancements/profiling/tone_profile.py @@ -33,7 +33,6 @@ from dataclasses import dataclass, field from typing import Any - # --------------------------------------------------------------------------- # Tone Feature Extraction # --------------------------------------------------------------------------- @@ -118,7 +117,7 @@ def to_dict(self) -> dict[str, Any]: } @classmethod - def from_dict(cls, d: dict[str, Any]) -> "ToneFeatures": + def from_dict(cls, d: dict[str, Any]) -> ToneFeatures: """Deserialize from dict.""" return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__}) @@ -250,7 +249,7 @@ def to_dict(self) -> dict[str, Any]: } @classmethod - def from_dict(cls, d: dict[str, Any]) -> "ToneProfile": + def from_dict(cls, d: dict[str, Any]) -> ToneProfile: features = ToneFeatures.from_dict(d.get("features", {})) return cls( task_type=d["task_type"], diff --git a/src/gradata/enhancements/rule_pipeline.py b/src/gradata/enhancements/rule_pipeline.py index c804de1a..6b232590 100644 --- a/src/gradata/enhancements/rule_pipeline.py +++ b/src/gradata/enhancements/rule_pipeline.py @@ -187,7 +187,7 @@ def _generate_skill_file( return skill_path -def review_generated_skill(skill_path: "Path") -> dict: +def review_generated_skill(skill_path: Path) -> dict: """Review a generated skill file for quality issues. Returns dict with: @@ -370,8 +370,9 @@ def run_rule_pipeline( ) if already_exists: continue - from gradata._types import Lesson as _Lesson from datetime import date as _date + + from gradata._types import Lesson as _Lesson candidate = _Lesson( date=_date.today().isoformat(), state=LessonState.INSTINCT, @@ -404,18 +405,26 @@ def run_rule_pipeline( # ── Phase 2: Atomic writes ──────────────────────────────────────────────── # Graduate rules, update confidence, create meta-rules. for lesson in all_lessons: - if lesson.state.name == "INSTINCT" and lesson.confidence >= PATTERN_THRESHOLD: - if lesson.fire_count >= MIN_APPLICATIONS_FOR_PATTERN: - lesson.state = LessonState.PATTERN - result.graduated.append(f"{lesson.category}:{lesson.description[:30]}") - elif lesson.state.name == "PATTERN" and lesson.confidence >= RULE_THRESHOLD: - if lesson.fire_count >= MIN_APPLICATIONS_FOR_RULE: - lesson.state = LessonState.RULE - result.graduated.append(f"{lesson.category}:{lesson.description[:30]}") + if ( + lesson.state.name == "INSTINCT" + and lesson.confidence >= PATTERN_THRESHOLD + and lesson.fire_count >= MIN_APPLICATIONS_FOR_PATTERN + ): + lesson.state = LessonState.PATTERN + result.graduated.append(f"{lesson.category}:{lesson.description[:30]}") + elif ( + lesson.state.name == "PATTERN" + and lesson.confidence >= RULE_THRESHOLD + and lesson.fire_count >= MIN_APPLICATIONS_FOR_RULE + ): + lesson.state = LessonState.RULE + result.graduated.append(f"{lesson.category}:{lesson.description[:30]}") # Synthesize meta-rules from graduated rules try: - from gradata.enhancements.meta_rules import synthesize_meta_rules_agentic # type: ignore[import] + from gradata.enhancements.meta_rules import ( + synthesize_meta_rules_agentic, # type: ignore[import] + ) from gradata.enhancements.meta_rules_storage import ( # type: ignore[import] load_meta_rules, save_meta_rules, @@ -470,7 +479,9 @@ def run_rule_pipeline( # Disposition updates from this session's corrections try: - from gradata.enhancements.behavioral_engine import DispositionTracker # type: ignore[import] + from gradata.enhancements.behavioral_engine import ( + DispositionTracker, # type: ignore[import] + ) tracker = DispositionTracker() disp_path = lessons_path.parent / "disposition.json" @@ -613,7 +624,9 @@ def build_knowledge_graph(lessons_path: Path, db_path: Path) -> dict: # Cross-domain candidates try: - from gradata.enhancements.meta_rules import detect_cross_domain_candidates # type: ignore[import] + from gradata.enhancements.meta_rules import ( + detect_cross_domain_candidates, # type: ignore[import] + ) graph["cross_domain"] = detect_cross_domain_candidates(lessons) except (ImportError, Exception): pass diff --git a/src/gradata/enhancements/scoring/brain_scores.py b/src/gradata/enhancements/scoring/brain_scores.py index 45e35bb2..c1746995 100644 --- a/src/gradata/enhancements/scoring/brain_scores.py +++ b/src/gradata/enhancements/scoring/brain_scores.py @@ -21,11 +21,6 @@ import sqlite3 from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - pass - # --------------------------------------------------------------------------- # Data model @@ -233,7 +228,7 @@ def compute_brain_scores( """ # Primary path: delegate to the authoritative implementation try: - import gradata._events as _events # noqa: PLC0415 + import gradata._events as _events _fn = getattr(_events, "compute_brain_scores", None) if _fn is None: diff --git a/src/gradata/enhancements/scoring/calibration.py b/src/gradata/enhancements/scoring/calibration.py index 3cbb23af..3ba28bc6 100644 --- a/src/gradata/enhancements/scoring/calibration.py +++ b/src/gradata/enhancements/scoring/calibration.py @@ -28,8 +28,7 @@ from __future__ import annotations -import math -from dataclasses import dataclass, field +from dataclasses import dataclass @dataclass diff --git a/src/gradata/enhancements/scoring/correction_tracking.py b/src/gradata/enhancements/scoring/correction_tracking.py index e9a45805..30342411 100644 --- a/src/gradata/enhancements/scoring/correction_tracking.py +++ b/src/gradata/enhancements/scoring/correction_tracking.py @@ -15,11 +15,6 @@ import sqlite3 from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - pass - # --------------------------------------------------------------------------- # Data model diff --git a/src/gradata/enhancements/scoring/gate_calibration.py b/src/gradata/enhancements/scoring/gate_calibration.py index ed7e46ad..df090e7d 100644 --- a/src/gradata/enhancements/scoring/gate_calibration.py +++ b/src/gradata/enhancements/scoring/gate_calibration.py @@ -31,7 +31,7 @@ from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass @dataclass diff --git a/src/gradata/enhancements/scoring/loop_intelligence.py b/src/gradata/enhancements/scoring/loop_intelligence.py index 0fddb6e1..1ed2d16d 100644 --- a/src/gradata/enhancements/scoring/loop_intelligence.py +++ b/src/gradata/enhancements/scoring/loop_intelligence.py @@ -16,16 +16,16 @@ from __future__ import annotations +import contextlib import json import sqlite3 from collections import defaultdict -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path -from typing import Any, Optional +from typing import Any from gradata._events import emit as _emit_event_fn - # ═══════════════════════════════════════════════════════════════════ # Registries (domain-agnostic defaults, override via register_*) # ═══════════════════════════════════════════════════════════════════ @@ -126,10 +126,8 @@ def _init_tables(conn: sqlite3.Connection) -> None: # Migration: add columns if missing from older schema for table in ("activity_log", "prep_outcomes"): for col in ("session", "timestamp"): - try: + with contextlib.suppress(sqlite3.OperationalError): conn.execute(f"ALTER TABLE {table} ADD COLUMN {col} TEXT DEFAULT NULL") - except sqlite3.OperationalError: - pass def log_activity( @@ -140,13 +138,13 @@ def log_activity( detail: str = "", prep_level: int = 0, source: str = "claude_assisted", - session: Optional[int] = None, - date: Optional[str] = None, + session: int | None = None, + date: str | None = None, emit_event: bool = True, ) -> dict[str, Any]: """Log an activity (email, call, meeting, deal change).""" - today = date or datetime.now(timezone.utc).strftime("%Y-%m-%d") - now = datetime.now(timezone.utc).isoformat() + today = date or datetime.now(UTC).strftime("%Y-%m-%d") + now = datetime.now(UTC).isoformat() conn = _get_db(db_path) _init_tables(conn) @@ -162,7 +160,8 @@ def log_activity( conn.close() if emit_event: - try: + # Never break the logging path on emit failure + with contextlib.suppress(Exception): _emit_event_fn( "DELTA_TAG", "loop_intelligence", { @@ -180,8 +179,6 @@ def log_activity( ] if t], session=session, ) - except Exception: - pass # Never break the logging path return { "id": row_id, @@ -194,12 +191,12 @@ def log_prep( prospect: str, prep_type: str, prep_level: int = 0, - session: Optional[int] = None, - date: Optional[str] = None, + session: int | None = None, + date: str | None = None, ) -> dict[str, Any]: """Log prep work for a prospect (before outcome is known).""" - today = date or datetime.now(timezone.utc).strftime("%Y-%m-%d") - now = datetime.now(timezone.utc).isoformat() + today = date or datetime.now(UTC).strftime("%Y-%m-%d") + now = datetime.now(UTC).isoformat() conn = _get_db(db_path) _init_tables(conn) @@ -226,10 +223,10 @@ def log_outcome( prep_type: str, outcome: str, days: int = 0, - date: Optional[str] = None, + date: str | None = None, ) -> dict[str, Any]: """Link an outcome to the most recent unresolved prep for this prospect+type.""" - today = date or datetime.now(timezone.utc).strftime("%Y-%m-%d") + today = date or datetime.now(UTC).strftime("%Y-%m-%d") conn = _get_db(db_path) _init_tables(conn) @@ -266,7 +263,7 @@ def log_outcome( """INSERT INTO prep_outcomes (date, prospect, prep_type, prep_level, outcome, days_to_outcome, claude_assisted, timestamp) VALUES (?, ?, ?, 0, ?, ?, 1, ?)""", - (today, prospect, prep_type, outcome, days, datetime.now(timezone.utc).isoformat()), + (today, prospect, prep_type, outcome, days, datetime.now(UTC).isoformat()), ) conn.commit() conn.close() @@ -282,10 +279,10 @@ def detect_manual( gmail_sent: int = 0, crm_updates: int = 0, session_logged: int = 0, - date: Optional[str] = None, + date: str | None = None, ) -> dict[str, Any]: """Detect manual activities by comparing external counts vs session-logged counts.""" - today = date or datetime.now(timezone.utc).strftime("%Y-%m-%d") + today = date or datetime.now(UTC).strftime("%Y-%m-%d") conn = _get_db(db_path) _init_tables(conn) @@ -328,7 +325,7 @@ def get_activity_stats(db_path: str | Path, days: int = 30) -> dict[str, Any]: conn = _get_db(db_path) _init_tables(conn) - cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d") + cutoff = (datetime.now(UTC) - timedelta(days=days)).strftime("%Y-%m-%d") by_source = { r["source"]: r["c"] @@ -391,7 +388,7 @@ def get_activity_stats(db_path: str | Path, days: int = 30) -> dict[str, Any]: def query_tagged_interactions( db_path: str | Path, - session: Optional[int] = None, + session: int | None = None, exclude_sources: tuple[str, ...] = ("instantly",), ) -> list[dict[str, str]]: """Query DELTA_TAG events with structured tags for pattern analysis.""" @@ -440,7 +437,7 @@ def query_tagged_interactions( def aggregate_by_key( interactions: list[dict[str, str]], key: str, - positive_outcomes: Optional[set[str]] = None, + positive_outcomes: set[str] | None = None, ) -> dict[str, dict[str, Any]]: """Aggregate interactions by a key field. Returns {value: {sent, replies, rate, confidence}}.""" pos = positive_outcomes or _POSITIVE_OUTCOMES @@ -536,7 +533,7 @@ def update_markdown_table( def update_patterns_file( db_path: str | Path, patterns_file: str | Path, - session: Optional[int] = None, + session: int | None = None, dry_run: bool = False, ) -> dict[str, Any]: """Main entry: update a patterns markdown file from tagged events.""" diff --git a/src/gradata/enhancements/scoring/memory_extraction.py b/src/gradata/enhancements/scoring/memory_extraction.py index a2bc354c..3aaa366e 100644 --- a/src/gradata/enhancements/scoring/memory_extraction.py +++ b/src/gradata/enhancements/scoring/memory_extraction.py @@ -35,7 +35,7 @@ import re from dataclasses import dataclass, field -from datetime import datetime, timezone +from datetime import UTC, datetime @dataclass @@ -110,7 +110,7 @@ def extract(self, messages: list[dict]) -> list[ExtractedFact]: List of ExtractedFact objects. """ facts: list[ExtractedFact] = [] - now = datetime.now(timezone.utc).isoformat() + now = datetime.now(UTC).isoformat() for msg in messages: role = msg.get("role", "user") @@ -211,7 +211,7 @@ def reconcile( op="invalidate", fact=candidate, target_id=match.get("id"), - reason=f"Superseded by newer information", + reason="Superseded by newer information", supersedes=match.get("id"), )) actions.append(ReconcileAction( diff --git a/src/gradata/enhancements/scoring/reports.py b/src/gradata/enhancements/scoring/reports.py index da500c68..d9805808 100644 --- a/src/gradata/enhancements/scoring/reports.py +++ b/src/gradata/enhancements/scoring/reports.py @@ -17,7 +17,7 @@ import json import sqlite3 from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -143,7 +143,7 @@ def generate_health_report(db_path: Path) -> HealthReport: first_draft_acceptance=fda, rules_active=rules_count, lessons_active=lessons_count, - timestamp=datetime.now(timezone.utc).isoformat(), + timestamp=datetime.now(UTC).isoformat(), issues=issues, ) diff --git a/src/gradata/enhancements/self_improvement.py b/src/gradata/enhancements/self_improvement.py index 13a93e8a..9c3316b8 100644 --- a/src/gradata/enhancements/self_improvement.py +++ b/src/gradata/enhancements/self_improvement.py @@ -16,9 +16,6 @@ import logging import re -from dataclasses import dataclass, field -from enum import StrEnum -from pathlib import Path from gradata._types import ( CorrectionType, diff --git a/src/gradata/hooks/self_review.py b/src/gradata/hooks/self_review.py index 8c5a09b5..a3d490c4 100644 --- a/src/gradata/hooks/self_review.py +++ b/src/gradata/hooks/self_review.py @@ -35,8 +35,8 @@ def _load_mandatory_rules(brain_dir: str) -> list[dict]: """Load RULE-tier mandatory lessons, cached by file mtime.""" try: - from gradata.enhancements.self_improvement import parse_lessons from gradata._types import LessonState + from gradata.enhancements.self_improvement import parse_lessons lessons_path = Path(brain_dir) / "lessons.md" if not lessons_path.is_file():