| name | signal-over-noise |
| description | Good investigations depend on good signals. Use this skill to improve the quality of observability in a Python codebase — surfacing failures that are currently hidden, and removing noise that buries the signals you need. Errors swallowed silently, stack traces discarded, log output that can't be queried, credentials leaking into logs, INFO-level chatter drowning out what matters: all of these slow down debugging and erode trust in your observability stack. Standardising log levels means DEBUG stays out of production, INFO marks genuine lifecycle events, and ERROR only fires when something actually failed — so when you filter to WARNING or above during an incident, you see exactly what you need and nothing you don't. Run this before an incident review, before onboarding a service, or any time investigations feel harder than they should.
|
| argument-hint | [--mode surface|tune] [--severity critical|high|medium|all] [path] |
| mandatory_triggers | ["/signal-over-noise","audit observability","surface swallowed exceptions","tune logging noise"] |
| optional_triggers | ["find hidden errors","clean up log levels","improve signal to noise","remove logging anti-patterns"] |
| owner | connector-platform-team |
| last_updated | 2026-05-12 |
| staleness_days | 90 |
| inputs | [{"target_path":"first positional arg — directory to audit (default: current working directory)"},{"mode":"surface | tune (default: surface)"},{"severity":"critical | high | medium | all (default: all)"}] |
| outputs | ["remediation plan presented in plan mode for user review","edits applied to caught/silent exception sites (surface mode) or logging call sites (tune mode), left uncommitted","TODO comments at sites that require manual review","optional pyproject.toml ruff rule additions (tune mode, if approved)"] |
| gates | ["No edits applied before the user approves the remediation plan.","Each phase runs pre-commit on changed files before handing back to the user.","All changes left uncommitted — the user reviews and commits."] |
Skill: /signal-over-noise
A two-phase Python observability audit that improves the ratio of meaningful signal to noise
in your error handling and logging. Each phase analyzes the codebase, presents a remediation
plan for your review, and — after approval — executes the fixes.
--mode surface (default): uncovers suppressed exceptions that make failures invisible
--mode tune: fixes logging anti-patterns that pollute or obscure your log stream
Invocation
/signal-over-noise # surface audit, current directory
/signal-over-noise packages/app-framework/src # surface audit, specific path
/signal-over-noise --mode surface # same as default
/signal-over-noise --mode tune # logging quality audit
/signal-over-noise --mode tune --severity high # HIGH + CRITICAL findings only
/signal-over-noise --mode surface packages/foo/src # surface audit, specific path
Argument parsing:
- First positional token (not a flag): target path. Default:
.
--mode surface|tune: which phase to run. Default: surface
--severity <level>: filter findings; levels: critical, high, medium, all. Default: all
How each mode works
Both modes follow the same two-stage structure:
-
Analysis — grep the codebase, read context, classify findings, build a remediation plan.
Enter plan mode and present the plan for user review. The user can iterate on the plan.
-
Execution — after the user approves the plan, apply the agreed fixes. Run pre-commit on
changed files. Leave all changes uncommitted for the user to review before committing.
Mode: surface
Finds every pattern where exceptions are caught and silently discarded, converting failures
into invisible state.
Detection is now delegated to the conformance suite (E-series). The E001–E018 checks
run deterministically via uv run atlan-application-sdk-conformance detect --repo . --series E
and produce SARIF output with one finding per rule violation. This skill retains its value
for the remediation / fix-authoring pass (Stage 2) — the suite deliberately does not
apply fixes. For a quick detection-only scan, prefer the suite runner; use this skill when
you want a plan-mode review and automated fix application.
Pattern catalogue, severity criteria, fix templates, and linting rules are in:
.claude/skills/signal-over-noise/references/error-recovery-patterns.md
Stage 1 — Analysis
Step 1.1 — Setup
- Read
.claude/skills/signal-over-noise/references/error-recovery-patterns.md in full.
- Read
.claude/skills/signal-over-noise/references/typed-error-prescription.md in full.
- Resolve
TARGET_PATH from arguments (default: current directory). Confirm it exists.
- Set
SEVERITY_FILTER from --severity (default: all).
Step 1.2 — Pattern Detection
Run all 13 grep searches in parallel against TARGET_PATH (*.py files only):
| # | Pattern ID | Grep |
|---|
| 1 | P1 | except\s*:\s*pass |
| 2 | P2 | except\s+\w.*:\s*pass |
| 3 | P3 | contextlib\.suppress\( |
| 4 | P4 | except\s+(Exception|BaseException)\s*: |
| 5 | P5 | logger\.(warning|error|critical)\( inside except blocks — collect hits for Step 1.3 |
| 6 | P6 | ^\s*except\s*: |
| 7 | P7 | except block with return and no log — collect hits for Step 1.3 |
| 8 | P8 | except\s+ImportError |
| 9 | P9 | except block with only an assignment — collect hits for Step 1.3 |
| 10 | P10 | return_exceptions=True |
| 11 | P11 | class\s+\w+.*logging\.Filter or def filter\(self inside a Filter subclass |
| 12 | P12 | raise\s+(ValueError|RuntimeError|Exception|TypeError|NotImplementedError|OSError|KeyError|LookupError)\b |
| 13 | P13 | raise\s+(ClientError|ApiError|OrchestratorError|WorkflowError|IOError|CommonError|DocGenError|ActivityError|AtlanError)\b |
For P5, P7, P9, P11, P12, P13: raw grep over-matches. Collect file:line hits and read context in Step 1.3.
P13 context note: IOError is also a Python builtin alias for OSError — confirm the hit imports from application_sdk.common.error_codes before classifying as legacy. Bare AtlanError raises (no constant) require litmus-test classification (§3 of typed-error-prescription.md).
Collect all hits as a flat list: (file, line_number, pattern_id, raw_snippet).
Step 1.3 — Context Reading and Classification
For each hit, read 10–15 lines of surrounding context.
Classify each finding:
- Severity: CRITICAL, HIGH, MEDIUM, or LOW (from §Severity Classification in reference)
- Legitimacy:
genuine-bug or acceptable (from §Legitimacy in reference)
- Category:
silent-swallow, missing-traceback, overly-broad-catch,
error-to-return-value, suppress-operational, optional-import,
asyncio-unexamined, untyped-raise, legacy-raise
- Typed-error prescription: for every
genuine-bug finding whose fix involves
a raise (FT-1b, FT-3b, FT-5b, FT-8, FT-9), look up the prescribed leaf and
code from references/typed-error-prescription.md. Record as Prescribed: on
the remediation entry so the user sees the target class in the plan.
Apply SEVERITY_FILTER: discard findings below the threshold before proceeding.
P12 classification notes:
- Inside
__post_init__, stdlib dataclass validators, or Pydantic @field_validator / @validator methods: check for a comment
explaining the stdlib-interop need. If present → acceptable. If absent →
flag as genuine-bug (MEDIUM — add the comment).
- Inside an
@task-decorated activity body or @activity.defn-decorated function → escalate to CRITICAL.
P13: confirmed hits are genuine-bug (HIGH). After context reading (see over-matches note above), use the §5 migration table from
typed-error-prescription.md for the Prescribed: field. When no matching legacy constant is present, use §3 litmus tests to select a leaf directly.
Step 1.4 — Build Remediation Plan
For each genuine-bug finding, produce a remediation entry. Include Prescribed:
whenever the fix involves raising an exception:
File: application_sdk/clients/sql.py:352
Severity: HIGH
Category: untyped-raise
Pattern: P12
Current:
raise ValueError("Engine is not initialized. Call load() first.")
Prescribed:
EngineNotInitializedError(InternalError) → code=INTERNAL_ENGINE_NOT_INITIALIZED
Subclass bakes message + component + invariant. Place in a sibling
module (e.g. _<area>_errors.py); see typed-error-prescription.md §4.
Fix (FT-8):
raise EngineNotInitializedError()
Auto-fixable: no (leaf + subclass choice needs context — manual review)
Priority: P1
For existing-pattern (P1–P11) findings whose fix re-raises, also include
Prescribed: with the leaf selected from typed-error-prescription.md §4.
Priority mapping:
- P0 — CRITICAL: fix immediately, block merges
- P1 — HIGH: fix in current sprint
- P2 — MEDIUM/LOW: fix when touching the file
Step 1.5 — Enter Plan Mode
Call EnterPlanMode.
Present the Surface Report as the plan:
# Surface Report — <TARGET_PATH>
## Executive Summary
- Files scanned: N
- Total findings: N (N genuine bugs, N acceptable patterns)
- By severity: CRITICAL: N | HIGH: N | MEDIUM: N | LOW: N
- By category: silent-swallow: N | missing-traceback: N | ...
- Auto-fixable: N | Needs manual review: N
## Findings
| # | File | Line | Severity | Category | Pattern | Auto-fixable | Notes |
|---|------|------|----------|----------|---------|--------------|-------|
## Remediation Plan
### P0 — Fix Immediately (CRITICAL)
[entries]
### P1 — Fix This Sprint (HIGH)
[entries]
### P2 — Fix When Touching (MEDIUM/LOW)
[entries]
## Acceptable Patterns (no action required)
| File | Line | Pattern | Justification |
|------|------|---------|---------------|
Call ExitPlanMode to request user approval. Wait for approval before proceeding to Stage 2.
Stage 2 — Execution (after plan approval)
Apply the remediation plan that was approved. Only fix what was listed in the approved plan.
Auto-fixable patterns — apply directly:
- FT-1a: Add logging to silent-swallow (log-and-continue, best-effort paths)
- FT-2: Add
exc_info=True to existing log call in except block
- FT-3a: Add logging before error-to-return-value (when caller treats default as valid)
- FT-4: Add exception inspection after
asyncio.gather(..., return_exceptions=True)
- FT-5a: Replace broad
contextlib.suppress(Exception) with logged try/except (best-effort)
Manual review required — leave TODO comments:
- FT-1b (log + typed re-raise): leaf choice is contextual
- FT-3b (convert swallow to typed raise): default for error-to-return-value when caller trusts result
- FT-5b (typed re-raise for non-best-effort suppress): leaf choice is contextual
- FT-6 (narrow broad catch):
# TODO(signal-over-noise): [P4] narrow this catch
- FT-7 (Filter exception safety):
# TODO(signal-over-noise): [P11] wrap filter body in try/except
- FT-8 (convert untyped builtin raise to typed
AppError): leaf from typed-error-prescription.md §4. Define a subclass for each distinct failure mode in a sibling errors module (SDK: application_sdk/<area>/errors.py or _<area>_errors.py; app: app/failures.py). Bake message and evidence-field defaults when they're stable across raise sites; for one-off sites, the subclass may just override code and let the raise site pass message.
- FT-9 (convert legacy
AtlanError raise to typed AppError): leaf from typed-error-prescription.md §5. Same subclassing rule applies when the same error recurs.
TODO comment format for swallow/logging findings:
TODO comment format for untyped/legacy raise findings — include the prescribed leaf:
After applying fixes:
Run pre-commit on every file that was changed:
uv run pre-commit run --files <file1> <file2> ...
If pre-commit is not available, skip this step (do not fail).
Leave all changes uncommitted — the user reviews before committing.
Finish
Report a brief summary: N files changed, N auto-fixes applied, N TODO comments added.
Then recommend the next step:
Next: Run /signal-over-noise --mode tune to audit logging quality (f-strings, missing exc_info, log levels, credential leaks, and more).
Mode: tune
Finds logging anti-patterns that pollute your log stream, obscure structured fields, or create
security risks.
Detection is now delegated to the conformance suite (L-series). The L001–L021 checks
run deterministically via uv run atlan-application-sdk-conformance detect --repo . --series L
and produce SARIF output with one finding per rule violation. This skill retains its value
for the remediation / fix-authoring pass (Stage 2) — the suite deliberately does not
apply fixes. For a quick detection-only scan, prefer the suite runner; use this skill when
you want a plan-mode review and automated fix application.
Skill ↔ Suite ID crosswalk
The suite renumbers the skill's L-ids. When reading SARIF output and mapping back to the
fix templates below, use this table:
| Skill ID | Suite ID | Rule name |
|---|
| L1 | L001 | FStringInLogMessage |
| L2 | L002 | InconsistentLoggerFactory |
| L3 | L003 | ExtraKwargInNonStdlibCall |
| L4 | L004 | MissingExcInfo |
| L5 | L005 | PrintInProductionCode |
| L6 | L006 | InfoInLoop |
| L7 | L007 | CriticalCall |
| L8 | L008 | UnguardedExpensiveDebug |
| L9 | L009 | WarnThenRaise |
| L10 | L010 | CredentialInLog |
| L11 | L011 | StringConcatInLogMessage |
| L13 | L012 | StdlibExtraReservedKeyCollision |
| L14 | L013 | StdlibArbitraryKwargs |
| L15 | L014 | StructlogEventKwargOverwrite |
| L16 | L015 | DictConfigDisableExistingLoggers |
| L17 | L016 | BasicConfigNoopAfterFirstCall |
| L18 | L017 | LoggerExceptionUsage |
| L24 | L018 | KwargsInApplicationLogCalls |
| L19 | L019 | DiscardedBindResult |
| L23 | L020 | DeprecatedLoggingWarn |
| L12, L20, L21, L22 | — | not ported (%-style, propagate=False, loguru.remove, loguru kwargs) |
Pattern catalogue, severity criteria, fix templates, and linting rules are in:
.claude/skills/signal-over-noise/references/logging-patterns.md — detectable patterns (L1–L23)
.claude/skills/signal-over-noise/references/logging-level-guidelines.md — level philosophy and framework awareness notes
Stage 1 — Analysis
Step 2.1 — Setup and Discovery
- Read both reference files listed above in full.
- Resolve
TARGET_PATH from arguments (default: current directory). Confirm it exists.
- Set
SEVERITY_FILTER from --severity (default: all).
Discover the project's logging framework — do not assume any specific one:
Grep for common logger factory patterns and count occurrences:
structlog\.get_logger\(
logging\.getLogger\(
loguru (import or usage)
get_logger\( (custom wrapper)
The factory with the highest count is CANONICAL_FACTORY.
Check pyproject.toml and requirements.txt / uv.lock for logging framework dependencies:
structlog in deps → LOGGER_FRAMEWORK = structlog
loguru in deps → LOGGER_FRAMEWORK = loguru
- Neither →
LOGGER_FRAMEWORK = stdlib
Determine SUPPORTS_KWARGS:
- structlog or loguru:
SUPPORTS_KWARGS = true
- stdlib:
SUPPORTS_KWARGS = false
Determine FRAMEWORK_SPECIFIC_PATTERNS — which additional patterns (L13–L23) to run:
- stdlib: L13, L14, L16, L17, L20, L23
- structlog: L15, L19
- loguru: L19, L21, L22
- All frameworks: L18
Discover logger variable names — grep for assignments:
logger\s*=\s*
log\s*=\s*
_logger\s*=\s*
_log\s*=\s*
Collect distinct prefixes (e.g. logger, log, _logger). Build LOGGER_VAR_PATTERN by
joining them: (logger|log|_logger|_log)\. — use this in all Step 2.2 greps.
Step 2.2 — Pattern Detection
Run the universal patterns (L1–L12) plus FRAMEWORK_SPECIFIC_PATTERNS (L13–L23) in parallel
against TARGET_PATH. Use LOGGER_VAR_PATTERN in place of hardcoded logger\. in each pattern.
Universal patterns (all frameworks):
| Pattern | Grep | Type |
|---|
| L1 f-string | LOGGER_VAR_PATTERN\.\w+\(f["'] | mechanical |
| L2 inconsistent factory | Files not using CANONICAL_FACTORY | mechanical |
L3 extra={} | extra=\{ near a logger call | heuristic |
| L4 missing exc_info | multiline: except + log call without exc_info | heuristic |
| L5 print() | ^\s*print\( (exclude test files, CLI scripts) | mechanical |
| L6 INFO in loop | LOGGER_VAR_PATTERN\.info\( inside for/while | heuristic |
| L7 critical() | LOGGER_VAR_PATTERN\.critical\( | mechanical |
| L8 expensive debug | LOGGER_VAR_PATTERN\.debug\(.*(?:json\.dumps|repr|\.to_dict|\.model_dump) | heuristic |
| L9 warn-then-raise | multiline: log call within 3 lines before raise | heuristic |
| L10 credential in log | LOGGER_VAR_PATTERN\.\w+\(.*(?:password|secret|token|api_key|credential|bearer|private_key) (case-insensitive) | heuristic |
| L11 concatenation | LOGGER_VAR_PATTERN\.\w+\(["'].*["']\s*\+ | mechanical |
| L12 %-style (non-stdlib only) | LOGGER_VAR_PATTERN\.\w+\(["'].*%[sdfr] — skip if LOGGER_FRAMEWORK == stdlib | heuristic |
| L18 exception() outside except | LOGGER_VAR_PATTERN\.exception\( | heuristic |
| L24 kwargs in application log calls | LOGGER_VAR_PATTERN\.\w+\([^)]*,\s*\w+= (non-stdlib only) — collect hits for Step 2.3 | heuristic |
Framework-specific patterns (run only for the indicated framework):
| Pattern | Grep | Framework | Type |
|---|
| L13 extra reserved key | extra=\{ — check keys against 22 reserved list | stdlib | heuristic |
| L14 arbitrary kwargs | LOGGER_VAR_PATTERN\.\w+\([^)]*,\s*\w+= | stdlib | heuristic |
| L15 event= kwarg | LOGGER_VAR_PATTERN\.\w+\(.*event= | structlog | mechanical |
| L16 dictConfig disable_existing | dictConfig\( | stdlib | heuristic |
| L17 basicConfig no-op | basicConfig\( (collect all across codebase) | stdlib | heuristic |
| L19 bind() discarded | ^\s+\w+\.bind\( as bare statement | structlog/loguru | heuristic |
| L20 propagate=False | propagate\s*=\s*False | stdlib | heuristic |
| L21 logger.remove() | logger\.remove\(\s*\) (no args) | loguru | mechanical |
| L22 loguru kwargs as format | LOGGER_VAR_PATTERN\.\w+\("[^"]*"[^)]*,\s*\w+= | loguru | heuristic |
| L23 warn() deprecated | \.warn\( | stdlib | mechanical |
For heuristic patterns: collect file:line hits for Step 2.3 context reading.
For mechanical patterns (L1, L2, L5, L7, L11, L15, L21, L23): record directly as findings.
Step 2.3 — Context Reading and Classification
For each heuristic hit, read 10–15 lines of surrounding context.
Classify each hit:
- Severity: apply the severity from the pattern catalogue, adjusted by context
- Legitimacy: genuine finding or acceptable pattern (see §Legitimacy in
logging-patterns.md)
Universal framework adjustments:
- L3: if
LOGGER_FRAMEWORK == stdlib → ACCEPTABLE. If structlog/loguru → MEDIUM. Fix direction: %-style message body, not "unwrap to kwargs".
- L12: if
LOGGER_FRAMEWORK == stdlib → ACCEPTABLE. If the log call goes through CANONICAL_FACTORY (e.g. get_logger()) AND that factory has a %-style bridge (look for _format_printf_args or similar in the adapter) → ACCEPTABLE. If the log call uses a direct loguru import (from loguru import logger / loguru.logger) even when a bridge adapter exists → HIGH (the bridge is bypassed; positional args are silently dropped because loguru uses str.format() not %). If vanilla loguru (no bridge) → HIGH. If vanilla structlog → MEDIUM.
- L10: read context —
token_name=name is acceptable; token=value is CRITICAL.
- L6: if loop is clearly bounded to ≤10 items → ACCEPTABLE.
- L18: read 5 lines before the
.exception() call — if inside an except block → ACCEPTABLE.
- L24: skip
exc_info=True/False. Skip files that define the logging factory/adapter (the file containing get_logger or the logger adapter class). Flag all other kwargs as MEDIUM.
Framework-specific classification rules:
- L13: Read the
extra={} dict literal. Flag CRITICAL for each key that matches any of the 22 reserved LogRecord attributes. Keys not in the reserved list → ACCEPTABLE.
- L14: Read the log call kwargs. Flag CRITICAL for any kwarg not in
{exc_info, extra, stack_info, stacklevel}. Do NOT flag structlog or loguru projects.
- L16: Check the config dict passed to
dictConfig(). If "disable_existing_loggers" is absent or True → HIGH. If False → ACCEPTABLE.
- L17: Collect all
basicConfig() calls across the codebase. If more than one → flag the second+ as HIGH. Single call in if __name__ == "__main__": → ACCEPTABLE.
- L19: Check if the
.bind() call is a bare statement (result not assigned) → HIGH. If assigned (x = logger.bind(...)) → ACCEPTABLE.
- L20: Read 10 lines around
propagate = False. Check if addHandler() is called on the same logger. If no handler → HIGH. If handler present → ACCEPTABLE.
- L22: Read the message string. If any kwarg name has no matching
{kwarg_name} placeholder in the message → MEDIUM. If all kwargs have placeholders → ACCEPTABLE (used for formatting).
Apply SEVERITY_FILTER: drop findings below the threshold.
Step 2.4 — Build Remediation Plan
For each genuine finding, produce an entry:
- Location:
file:line
- Severity: CRITICAL / HIGH / MEDIUM / LOW
- Category: L1–L23 code + name
- Current code: offending lines
- Fix template: reference to FT-L1 through FT-L23 from
logging-patterns.md
- Auto-fixable: yes/no
Group into priority tiers:
- P0 — Fix Immediately: CRITICAL — L10 (credential leak), L13 (reserved key crash), L14 (stdlib kwargs crash)
- P1 — Fix This Sprint: HIGH — L1, L2, L4, L15, L16, L17, L19, L20, L21
- P2 — Fix When Touching File: MEDIUM/LOW — L3, L5, L6, L7, L8, L9, L11, L12, L18, L22, L23, L24
Also compute linting recommendations: read pyproject.toml for existing ruff or flake8 rule
selections. List any missing rules from §Linting Rules in logging-patterns.md:
- G001, G003, G004, T201, LOG009 — recommend if not present
- G002 — do NOT recommend (%-style is the preferred formatting; G002 would produce false positives)
- LOG015 — recommend if stdlib and ruff version supports it
Include the minimal pyproject.toml diff and suggested lint-logging Makefile target in the plan.
Step 2.5 — Enter Plan Mode
Call EnterPlanMode.
Present the Tune Report as the plan:
# Tune Report — <TARGET_PATH>
## Project Logging Profile
- Framework: <LOGGER_FRAMEWORK>
- Canonical factory: <CANONICAL_FACTORY>
- Supports kwargs: <yes/no>
- Logger variable names found: logger, log, ...
## Executive Summary
- Files scanned: N
- Total hits: N (N genuine findings, N acceptable patterns)
- By severity: CRITICAL: N | HIGH: N | MEDIUM: N | LOW: N
- Auto-fixable: N | Needs manual review: N
## Logger Consistency
- Files using canonical factory: N
- Files using non-canonical factory: N (list)
## Findings
| # | File | Line | Severity | Pattern | Auto-fixable | Notes |
|---|------|------|----------|---------|--------------|-------|
## Remediation Plan
### P0 — Fix Immediately (CRITICAL)
[L10, L13, L14 findings with exact lines and fix template]
### P1 — Fix This Sprint (HIGH)
[L1, L2, L4, L15, L16, L17, L19, L20, L21 findings]
### P2 — Fix When Touching File (MEDIUM/LOW)
[L3, L5, L6, L7, L8, L9, L11, L12, L18, L22, L23 findings]
## Linting Recommendations
[pyproject.toml diff]
[Makefile target suggestion]
## Acceptable Patterns (no action required)
| File | Line | Pattern | Justification |
|------|------|---------|---------------|
Call ExitPlanMode to request user approval. Wait for approval before proceeding to Stage 2.
Stage 2 — Execution (after plan approval)
Apply the remediation plan that was approved. Only fix what was listed in the approved plan.
Auto-fixable patterns — apply directly:
- FT-L1: f-string → rewrite as %-style message body (embed context in message, not kwargs)
- FT-L2: Import swap to canonical factory
- FT-L4: Add
exc_info=True to logger.warning( / logger.error( in except blocks
- FT-L5: Simple
print("...") → logger.info("...") using %-style for any embedded values
- FT-L7:
.critical( → .error(
- FT-L11: String concatenation → %-style message body
- FT-L14: Wrap stdlib log call kwargs in
extra={} (stdlib only)
- FT-L16: Add
"disable_existing_loggers": False to dictConfig call
- FT-L19: Capture
bind() return value — logger.bind(...) → logger = logger.bind(...)
- FT-L22: loguru kwargs → %-style message body
- FT-L23:
.warn( → .warning(
- FT-L24: Application kwargs → %-style message body (keep
exc_info=True)
Do not auto-fix L10 (credential leak) — always flag for human review only.
Manual review required — leave TODO comments:
- FT-L3, FT-L6, FT-L8, FT-L9, FT-L10, FT-L12, FT-L13, FT-L15, FT-L17, FT-L18, FT-L20, FT-L21
TODO comment format:
Apply linting config changes (if approved in the plan):
Update pyproject.toml with the recommended ruff rules.
After applying fixes:
Run pre-commit on every file that was changed:
uv run pre-commit run --files <file1> <file2> ...
If pre-commit is not available, skip this step (do not fail).
Leave all changes uncommitted — the user reviews before committing.
Finish
Report a brief summary: N files changed, N auto-fixes applied, N TODO comments added.
Key Design Notes
-
Plan mode is the fix gate. The user approves the remediation plan before any files are
touched. The plan shows exact file:line locations, current code, and proposed fixes.
-
Two invocations, not one. Surface and Tune are separate --mode values. This keeps each
plan focused and reviewable. Run Surface first; its execution recommends Tune as the next step.
-
Framework-agnostic: Tune's Step 2.1 discovers the logging framework and canonical factory
from the codebase itself. Nothing is hardcoded.
-
kwargs are a universal anti-pattern in application code: Always embed context in the
message body via %-style. Framework context (Temporal workflow_id, run_id, activity_type,
task_queue, attempt, etc.) is auto-injected by the logging adapter — never pass it manually.
All other application kwargs land in an unindexed JSON blob in the observability platform,
making them invisible to log stream scanning. The only accepted kwarg is exc_info=True.
L24 detects kwargs in application log calls (non-stdlib, outside the adapter file itself).
L3 and L12 are still framework-dependent for classification: extra={} is correct in
stdlib and wrong in structlog/loguru (use %-style body instead). %-style is the universal
preferred format; in vanilla loguru (without a custom adapter bridge) %-style args are
silently dropped — flag as HIGH. In stdlib and projects with a custom %-bridge adapter,
%-style is already correct.
-
L13 and L14 are crash-level stdlib patterns: extra={} key collisions with reserved
LogRecord attributes raise KeyError that propagates to the caller. Treat as P0.
-
L10 is security-critical: Never auto-fix credential patterns. Always flag for human review.
-
Phase ordering matters: Run Surface before Tune. A silent-swallow in an except block (P1/P2)
overlaps with a missing-exc_info finding (L4). Surface fixes the structural problem; Tune then
verifies the logging quality of the fix. Avoid double-reporting the same line.
-
Typed-error prescription is mandatory. Every fix that re-raises must name an AppError
leaf from application_sdk.errors. The skill never recommends raising a bare builtin or a
legacy AtlanError. The leaf catalogue (§2), litmus tests (§3), SDK-context cookbook (§4),
and exhaustive legacy-constant migration table (§5) all live in
references/typed-error-prescription.md. P12 (untyped builtin raises) and P13 (legacy
AtlanError raises) fold the BLDX-1261 audit into surface mode — every finding gets a
Prescribed: entry naming the target leaf and suggested code string.