Skylos treats a "good coding practice" as enforceable only when it meets at least one of these tests:
- It is backed by a language, security, quality, or framework standard.
- It is enforced by a mainstream tool that the ecosystem already accepts.
- It protects a concrete quality attribute: security, reliability, maintainability, performance efficiency, or API correctness.
- It is an explicit repository policy, such as requiring type-checking, linting, pre-commit hooks, or a Skylos gate.
Rules that are only taste preferences should stay out of the default quality set. They can still be project policy, but they should be configured separately and rolled out as advisory checks first.
Every built-in rule in skylos/rules/catalog.py declares a finding_type:
defect for concrete correctness patterns, review for risks requiring context,
and advice for maintainability or tooling conventions. Severity describes impact
and remains separate. These labels do not certify a production incident.
All findings in the selected scan scope remain in JSON. Terminal reports list
Defect and Review individually and group Advice by rule, with counts and affected
files. Use --expand-advice to show every Advice location and message. MCP and
LLM replies summarize Advice by default and provide access to the full records.
Display grouping and expansion do not change the assessment or configured gate
limits. Assign a type explicitly when adding any rule.
The source scan answers four separate questions: Risk, Maintainability, Dead code, Gate. It does not blend clean categories into an overall A–F grade.
- Risk: preserve the highest unresolved severity among Defect and Review findings outside the dead-code candidate lists. Include correctness findings from quality rules. Adding lines, clean files, or unrelated languages cannot dilute the risk. A Review finding remains a candidate risk, not a proved incident.
- Maintainability: retain Advice counts and rule groups. Where a complete inventory exists, report affected units against the units measured for that rule and language. Measure threshold excess separately: complexity 11 against limit 10 is 1.1× the limit; 45 is 4.5×. Count a unit once within a rule when measuring prevalence, while retaining every finding record.
- Dead code: report candidates, confidence and evidence for review. A high confidence score does not authorize deletion or establish runtime safety.
- Gate: evaluate the existing limits independently of the readouts. Advice remains eligible for enforcement unless an existing rule or scan-scope policy explicitly excludes it. Grouping is not an exemption.
Disabled checks are not assessed. Failed or unavailable checks make coverage incomplete; known findings stay visible. Missing or incomplete unit inventories produce counts with an unavailable denominator rather than an invented ratio. No numeric health score or maintainability letter is assigned until its bands have been calibrated against manually reviewed repositories.
Keep Type, severity, confidence and coverage distinct in every reporting surface. Preserve complete records in JSON and expose them from compact agent replies. Apply ordinary display filters after assessment; disclose its original scope. A changed analysis scope must not reuse a full-project assessment. See scan assessment.
| Practice Area | Source / Stipulation | Skylos Coverage | CI / Tool Coverage |
|---|---|---|---|
| Python style and lint hygiene | PEP 8, Ruff linter | SKY-R102, existing Python quality rules |
Advisory ruff check . |
| Python type clarity | PEP 484, mypy | SKY-T101, SKY-T102, SKY-R101 |
Advisory mypy skylos |
| TypeScript type safety | TypeScript strict mode | SKY-T103, SKY-T104, SKY-T105, SKY-R105 |
Advisory npm run compile / npm run build |
| Rust idioms and correctness | Clippy, Rustfmt style edition | Repo policy visibility through CI | Advisory cargo fmt --check, cargo clippy |
| API authorization practice | OWASP API Security Top 10 2023, CWE-862 |
SKY-F102, security/danger rules |
Diff-aware Skylos advisory scan |
| API response contracts | FastAPI response models / return types | SKY-F101 |
Diff-aware Skylos advisory scan |
| Dependency-injected guards | FastAPI dependencies and security | SKY-F102 |
Diff-aware Skylos advisory scan |
| Maintainability and complexity | ISO/IEC 5055:2021, ISO/IEC 25010:2011, CWE complexity/coding-standard classes | SKY-Q*, SKY-C*, SKY-L* |
Diff-aware Skylos advisory scan |
| Secure coding conventions | CWE-710, OWASP API Security Top 10 | SKY-L*, SKY-F102, SKY-R* |
Diff-aware Skylos advisory scan |
| Repository enforcement policy | Repo-local pyproject.toml, .pre-commit-config.yaml, CI workflow policy |
SKY-R101 through SKY-R105 |
Advisory quality workflow and Skylos advisory gate |
| Rule | ID | What It Catches |
|---|---|---|
| Complexity | ||
| Cyclomatic complexity | SKY-Q301 | Too many branches/loops (default: >10) |
| Deep nesting | SKY-Q302 | Too many nested levels (default: >3) |
| Duplicate branch logic | SKY-Q305 | Duplicate conditions or duplicate branch bodies |
| Cognitive complexity | SKY-Q306 | Nested/control-flow complexity that is hard to reason about |
| Structure | ||
| Too many arguments | SKY-C303 | Functions with >5 args |
| Function too long | SKY-C304 | Functions >50 lines |
| Clone group | SKY-C401 | Duplicated implementation fragments |
| Logic | ||
| Mutable default | SKY-L001 | def foo(x=[]) - causes state leaks |
| Bare except | SKY-L002 | except: swallows SystemExit |
| Dangerous comparison | SKY-L003 | x == None instead of x is None |
| Anti-pattern try block | SKY-L004 | Nested try, or try wrapping too much logic |
| Unused exception variable | SKY-L005 | except ValueError as e: where e is never used |
| Inconsistent return | SKY-L006 | Mixed value returns and bare/None returns |
| Empty error handler | SKY-L007 | Silent Python handlers or empty TS/JS catch blocks |
| Missing resource cleanup | SKY-L008 | open() without a context manager or close path |
| Debug leftover | SKY-L009 | print() / breakpoint() in production code |
| Security TODO | SKY-L010 | Comments that defer auth/security fixes |
| Disabled security control | SKY-L011 | Calls/settings that disable TLS, CSRF, or validation |
| Insecure random | SKY-L013 | Security-sensitive use of weak random sources |
| Hardcoded credential | SKY-L014 | Credential-looking constants in source |
| Undefined config | SKY-L016 | Environment/config references with no declared default |
| Error disclosure | SKY-L017 | Exception details returned from handlers |
| Broad file permissions | SKY-L020 | World-writable or overly broad file modes |
| Stale mock | SKY-L024 | Tests mocking symbols that no longer exist |
| Unfinished code or placeholder default | SKY-L026 | Incomplete Python bodies/defaults or TS/JS functions that only throw "Not implemented" |
| Duplicate string literal | SKY-L027 | Repeated long literals that should be named constants |
| Too many returns | SKY-L028 | Functions with excessive exit paths |
| Boolean trap | SKY-L029 | Public APIs with unclear boolean flags |
| Broad exception | SKY-L030 | Broad Exception handlers with trivial bodies |
| Missing network timeout | SKY-L031 | HTTP calls without explicit timeout |
| Repeated mutable alias | SKY-L034 | Sequence multiplication can reuse mutable elements across repetitions instead of creating independent values |
| Blanket ESLint disable | SKY-L035 | A leading bare /* eslint-disable */ disables every rule for the file |
| Type Checking | ||
| Untyped public parameters | SKY-T101 | Public functions in typed modules missing parameter annotations |
| Missing public return type | SKY-T102 | Public functions in typed modules missing return annotations |
| Suspicious chained assertion | SKY-T103 | A cast reaches a precise type through any, unknown, object, or {} |
| TypeScript compiler suppression | SKY-T104 | Effective @ts-ignore or file-wide @ts-nocheck directives |
| Unvalidated JSON assertion | SKY-T105 | A direct JSON.parse() or proven fetch Response.json() result is asserted without validation |
| Unsafe exported API type | SKY-T106 | Exported TS APIs expose exact any, Record<string, any>, or an any-valued index signature |
| Framework Practices | ||
| FastAPI response contract | SKY-F101 | Route lacks response_model, response_class, or return annotation |
| Mutating route auth guard | SKY-F102 | POST/PUT/PATCH/DELETE route lacks obvious auth/dependency guard |
| Performance | ||
| Memory load | SKY-P401 | .read() / .readlines() loads entire file |
| Pandas no chunk | SKY-P402 | read_csv() without chunksize |
| Nested loop | SKY-P403 | O(N²) complexity |
| Unbounded eager ORM query | SKY-P404 | SQLAlchemy-style .all() without limit or pagination |
| Await in loop | SKY-Q402 | Awaiting serially inside loops where batching may be intended |
| Async / Class Design | ||
| Blocking call in async code | SKY-Q401 | Synchronous I/O or blocking calls in async handlers |
| Lock order inversion | SKY-Q403 | Nested locks acquired in inconsistent order |
| Thread shared state mutation | SKY-Q404 | Thread target mutates module state without an obvious lock |
| Async Promise executor | SKY-Q405 | new Promise(async ...) ignores the executor's async result; an async-generator executor never runs |
Async forEach callback |
SKY-Q406 | Built-in Array.forEach does not await an async callback |
Discarded async map result |
SKY-Q407 | An Array.map(async ...) result is thrown away without consuming its promises |
| God class | SKY-Q501 | Classes with too many methods or attributes |
| God file | SKY-Q502 | Files with too many code lines or too many responsibilities |
| Coupling | SKY-Q701 | Classes coupled to too many other classes |
| Low cohesion | SKY-Q702 | Classes whose methods do not share state or responsibilities |
| AI Defects | ||
| Test assertion weakening | SKY-A101 | Specific or exception assertion replaced with a broad truthiness/null check, skip, or xfail |
| High-risk change without tests | SKY-A102 | Auth, billing, validation, or similar high-risk code changed with no test file changed |
| CI permission expansion | SKY-A103 | GitHub Actions diff adds write permissions or privileged workflow triggers |
| Public CLI surface drift | SKY-A104 | Public CLI flag removed from argparse, Click, or Typer surface |
| Phantom reference | SKY-L012 | Calls to undefined/hallucinated security helpers |
| Phantom decorator | SKY-L023 | Security decorators that are not defined or imported |
| Hallucinated dependency | SKY-D222 | Imported package does not exist in the package registry |
| API signature hallucination | SKY-D224 | Real package called with an invented API or keyword |
| Dependency version hallucination | SKY-D225 | Manifest pins a package version that does not exist |
| Architecture | ||
| High instability | SKY-Q801 | Module dependency instability is above policy |
| High main-sequence distance | SKY-Q802 | Module architecture is far from abstractness/instability balance |
| Architecture zone warning | SKY-Q803 | Module falls into Zone of Pain or Zone of Uselessness |
| Dependency inversion violation | SKY-Q804 | Concrete module depends on another concrete module where abstraction is expected |
| Repository Policy | ||
| Python type-check policy | SKY-R101 | No mypy or pyright policy is configured |
| Python lint policy | SKY-R102 | No Ruff policy is configured |
| Skylos gate policy | SKY-R103 | No [tool.skylos.gate] policy is configured |
| Pre-commit policy | SKY-R104 | No pre-commit config is present |
| TypeScript type-check policy | SKY-R105 | TS package lacks an npm script that runs tsc |
| Unused dependency | SKY-U005 | Declared dependency is not imported by reachable code |
| Unreachable | ||
| Unreachable Code | SKY-UC001 | if False: or else after always-true |
| Unreachable statement | SKY-UC002 | Statement after return/throw/break/continue |
| Empty | ||
| Empty File | SKY-E002 | Empty File |