8.4 KiB
8.4 KiB
Quality Assessment -- 2026-04-11
Version: 0.1.0 Assessed by: Claude Sonnet 4.6 Previous assessment: QA-2026-04-11_0528.md
Inventory
| Metric | Value |
|---|---|
| Source files | 72 |
| Source lines | 10,325 |
| Test files | 30 |
| Test lines | 2,736 |
| Test:source ratio | 0.26 |
| Direct dependencies | 10 core + 1 optional + 4 dev |
Raw Metrics
Test Suite
240 passed, 7 warnings in 9.26s
- Tests collected: 240
- Tests passed: 240
- Tests failed: 0
- Test duration: 9.26s
Type Safety (mypy --strict)
Found 49 errors in 20 files (checked 72 source files)
- Total errors: 49
- Files with errors: 20 / 72 (72% clean)
- Top error categories:
[type-arg]17 — missing generic parameters ondict,list[attr-defined]9 — attribute access on loosely typed objects[no-any-return]5 — returningAnyfrom typed functions[var-annotated]3[import-untyped]3[assignment]3[valid-type]2[return-value]2[no-untyped-call]2[unused-ignore]1[no-untyped-def]1[comparison-overlap]1
Lint (ruff)
Found 89 errors.
[*] 73 fixable with the `--fix` option (9 hidden fixes can be enabled with the `--unsafe-fixes` option).
- Total violations: 89
- Auto-fixable: 73 (82%)
- Top violation rules:
F40161 — unused importsI0019 — unsorted importsF6018 — duplicate dictionary keysE5015 — line too longF8413 — unused variablesP0352 — string concatenation in f-stringF5411 — f-string without placeholder
Complexity
- File size: min=1 / median=133 / mean=143 / max=693
- Files >300 lines: 6 / 72
- High-complexity files (branch density >15):
80 src/impakt/io/mme.py (693 lines) -- ISO 13499 parser, justified
44 src/impakt/web/components/criteria.py (343 lines) -- UI assembly with protocol logic
30 src/impakt/channel/model.py (456 lines) -- core data model, multiple classes
27 src/impakt/web/state.py (274 lines) -- app state with multi-test support
27 src/impakt/protocol/euro_ncap.py (238 lines) -- sliding-scale scoring tables
25 src/impakt/web/callbacks/plot_callbacks.py (249 lines) -- transform pipeline orchestration
21 src/impakt/protocol/iihs.py (180 lines) -- G/A/M/P rating logic
20 src/impakt/plot/engine.py (257 lines) -- Plotly rendering with corridors
19 src/impakt/script/cli.py (140 lines) -- CLI arg parsing
17 src/impakt/web/components/channel_grid.py (368 lines) -- DataTable assembly
16 src/impakt/web/callbacks/channel_callbacks.py (195 lines) -- selection/filter callbacks
Documentation
- Docstring coverage: 414 / 454 definitions (91%)
- Modules with
__all__: 6 / 11 public modules- channel: YES
- criteria: YES
- io: NO
- plot: YES
- plugin: NO
- protocol: YES
- report: NO
- script: NO
- template: NO
- transform: YES
- web: YES
- README: 1,266 lines with 20 Mermaid diagram references
- Architectural diagrams: yes
Security
- eval/exec (sandboxed): 1 —
math_expr.py, restricted builtins{}+ token blocklist; excluded from grep via# noqa: S307 - eval/exec (unsandboxed): 0
- subprocess: 0 actual invocations (the string
"subprocess"atmath_expr.py:70is a forbidden-token blocklist entry, not a real call) - Hardcoded secrets: 0
- Bare except: 0
Maintainability
- TODO: 0
- FIXME: 0
- HACK: 0
- Logging calls: 48
- try/except blocks: 52
- Bare excepts: 0
- Internal imports (coupling): 190
Scorecard
| # | Dimension | Weight | Score | Weighted | Justification |
|---|---|---|---|---|---|
| 1 | Test Health | 20% | 8.0/10 | 1.60 | 240/240 pass. test:source ratio 0.26 (within 0.2–0.5 band). Integration tests with real datasets present. No coverage % configured. |
| 2 | Type Safety | 15% | 6.5/10 | 0.975 | mypy strict enabled. 49 errors in 20 files, concentrated in web layer. Mostly cosmetic (type-arg 17). Interpolated between 6 (<50 errors) and 8 (<10 errors). |
| 3 | Lint Hygiene | 10% | 6.0/10 | 0.60 | 89 violations (82% auto-fixable). Dominated by unused imports (F401=61). 8 duplicate dict keys (F601) need manual fix. Rubric: <100, mostly auto-fixable = 6. |
| 4 | Architecture | 15% | 9.0/10 | 1.35 | Clean 4-layer design (data→transform→protocol→web). Plugin system present. No layer violations found. 6/11 modules export __all__. Docked 1 point for 5 missing __all__. |
| 5 | Documentation | 10% | 9.0/10 | 0.90 | 91% docstring coverage (>90%). README with 20 Mermaid diagrams. No generated API reference docs, so not a full 10. |
| 6 | Complexity | 10% | 7.0/10 | 0.70 | Median 133 (<150). 6 files >300 lines. mme.py at 693/80 complexity is the outlier — justified as a format parser. Interpolated between 8 (≤3 files >300) and 6 (≤10 files >300). |
| 7 | Security | 10% | 8.5/10 | 0.85 | Single eval sandboxed with {"__builtins__": {}} + 16-item token blocklist. No subprocess, no secrets, no bare excepts. Interpolated between 9 (fully sandboxed) and 7 (partially sandboxed). |
| 8 | Maintainability | 10% | 8.5/10 | 0.85 | Zero debt markers. Zero bare excepts. 48 logging calls across codebase. Modern tooling (uv, hatchling, ruff, mypy). Between 10 (perfect) and 8 (<5 markers). |
Composite Score: 78.3 / 100
Grade: B
Calculation: (8.0×0.20 + 6.5×0.15 + 6.0×0.10 + 9.0×0.15 + 9.0×0.10 + 7.0×0.10 + 8.5×0.10 + 8.5×0.10) × 10 = (1.60 + 0.975 + 0.60 + 1.35 + 0.90 + 0.70 + 0.85 + 0.85) × 10 = 7.825 × 10 = 78.3
Delta from Previous Assessment
| Dimension | Previous | Current | Change |
|---|---|---|---|
| Test Health | 8.0 | 8.0 | 0.0 |
| Type Safety | 6.5 | 6.5 | 0.0 |
| Lint Hygiene | 6.0 | 6.0 | 0.0 |
| Architecture | 9.0 | 9.0 | 0.0 |
| Documentation | 9.0 | 9.0 | 0.0 |
| Complexity | 7.0 | 7.0 | 0.0 |
| Security | 8.5 | 8.5 | 0.0 |
| Maintainability | 8.5 | 8.5 | 0.0 |
| Composite | 78.3 | 78.3 | 0.0 |
Top Improvements Since Last Assessment
No code changes detected since QA-2026-04-11_0528 — all raw metrics are identical.
Recommended Actions (Priority Order)
| # | Action | Effort | Impact | Dimensions Affected |
|---|---|---|---|---|
| 1 | Run uv run ruff check --fix src/ to clear 73 auto-fixable violations |
1 min | +2.0 lint → 8.0 | Lint Hygiene |
| 2 | Manually fix 8 duplicate dict keys (F601) in channel/lookup.py and remaining non-auto-fixable lint violations |
15 min | +1.0 lint → 9.0+ | Lint Hygiene |
| 3 | Add --cov --cov-report=term-missing to pytest config; target ≥80% branch coverage |
30 min | +1.0 test → 9.0 | Test Health |
| 4 | Resolve 17 [type-arg] mypy errors (add dict[str, X] / list[X] generics, primarily in web layer) |
1 hr | +1.0 type → 7.5 | Type Safety |
| 5 | Add __all__ to io, plugin, report, script, template modules |
30 min | +0.5 arch → 9.5 | Architecture |
Projected composite after actions 1–5: ~85 (B+)
Notes
- No code changes detected between this assessment and QA-2026-04-11_0528. All 72 source files and 30 test files are unchanged, yielding identical metrics and scores for the third consecutive assessment.
- Architecture is qualitative. Import graph inspected: no layer violations found. The
webmodule sits at the top of the dependency tree;io/transform/protocollayers do not import fromweborplot. - Security eval in
math_expr.pyis sandboxed via empty__builtins__dict and a 16-entry token blocklist (includingimport,exec,eval,subprocess,os.,sys.,__). The# noqa: S307comment excludes it from thegrep -v '# noqa'scan. An AST-based evaluator would be safer but is lower priority given existing mitigations. - Subprocess grep hit is a confirmed false positive: the string
"subprocess"appears only as a forbidden-token blocklist entry atmath_expr.py:70, not as an actual invocation. - Complexity scoring for
mme.pyremains lenient: ISO 13499 format parsers inherently carry high branch density. Consider extracting sub-parsers if it grows beyond ~800 lines. - The three assessments today (0459, 0528, 0619) are identical because no source code was modified between runs. The recommended actions above remain the highest-value next steps.