15. Metrics and Quality Gates¶
Purpose¶
Define metrics mode selection, metrics-baseline behavior, and gating semantics.
Public surface¶
- Metrics mode wiring:
codeclone/cli.py:_configure_metrics_mode - Main orchestration and exit routing:
codeclone/cli.py:_main_impl - Gate evaluation:
codeclone/pipeline.py:metric_gate_reasons,codeclone/pipeline.py:gate - Metrics baseline persistence/diff:
codeclone/metrics_baseline.py:MetricsBaseline
Data model¶
Metrics gate inputs:
- threshold gates:
--fail-complexity,--fail-coupling,--fail-cohesion,--fail-health - boolean structural gates:
--fail-cycles,--fail-dead-code - delta gate:
--fail-on-new-metrics - baseline update:
--update-metrics-baseline
Modes:
analysis_mode=full: metrics computed and suggestions enabledanalysis_mode=clones_only: metrics skipped- Health score is a weighted blend: clones 25%, complexity 20%, cohesion 15%, coupling 10%, dead code 10%, dependencies 10%, coverage 10%.
- Clone dimension uses a piecewise density curve with breakpoints at 0.05 (score 90), 0.20 (score 50), 0.50 (score 0). Below 5% density the penalty is mild; 5–20% is steep; above 20% is aggressive.
- Grade bands: A ≥90, B ≥75, C ≥60, D ≥40, F <40.
Refs:
codeclone/cli.py:_metrics_flags_requestedcodeclone/cli.py:_metrics_computedcodeclone/_cli_meta.py:_build_report_metacodeclone/metrics/health.py:compute_healthcodeclone/contracts.py:HEALTH_WEIGHTS
Contracts¶
--skip-metricsis incompatible with metrics gating/update flags and is a contract error.- If metrics are not explicitly requested and no metrics baseline exists,
runtime auto-enables clone-only mode (
skip_metrics=true). - In clone-only mode:
skip_dead_code=true,skip_dependencies=true. --fail-dead-codeforces dead-code analysis on.--fail-cyclesforces dependency analysis on.--update-baselinein full mode implies metrics-baseline update in the same run.- If metrics baseline path equals clone baseline path and clone baseline file is
missing,
--update-metrics-baselineescalates to--update-baselineso embedded metrics can be written safely. --fail-on-new-metricsrequires trusted metrics baseline unless baseline is being updated in the same run.- In CI mode, if metrics baseline was loaded and trusted, runtime enables
fail_on_new_metrics=true.
Refs:
codeclone/cli.py:_configure_metrics_modecodeclone/cli.py:_main_implcodeclone/metrics_baseline.py:MetricsBaseline.verify_compatibility
Invariants (MUST)¶
- Metrics diff is computed only when: metrics were computed and metrics baseline is trusted.
- Metric gate reasons are emitted in deterministic order: threshold checks -> cycles/dead/health -> NEW-vs-baseline diffs.
- Metric gate reasons are namespaced as
metric:*in gate output.
Refs:
codeclone/pipeline.py:metric_gate_reasonscodeclone/pipeline.py:gate
Failure modes¶
| Condition | Behavior |
|---|---|
--skip-metrics with metrics flags |
Contract error, exit 2 |
--fail-on-new-metrics without trusted baseline |
Contract error, exit 2 |
--update-metrics-baseline when metrics were not computed |
Contract error, exit 2 |
| Threshold breach or NEW-vs-baseline metric regressions | Gating failure, exit 3 |
Determinism / canonicalization¶
- Metrics baseline snapshot fields are canonicalized and sorted where set-like.
- Metrics payload hash uses canonical JSON and constant-time comparison.
- Gate reason generation order is fixed by code path order.
Refs:
codeclone/metrics_baseline.py:snapshot_from_project_metricscodeclone/metrics_baseline.py:_compute_payload_sha256codeclone/metrics_baseline.py:MetricsBaseline.verify_integrity
Locked by tests¶
tests/test_cli_unit.py::test_configure_metrics_mode_rejects_skip_metrics_with_metrics_flagstests/test_cli_unit.py::test_main_impl_rejects_update_metrics_baseline_when_metrics_skippedtests/test_cli_unit.py::test_main_impl_fail_on_new_metrics_requires_existing_baselinetests/test_cli_unit.py::test_main_impl_ci_enables_fail_on_new_metrics_when_metrics_baseline_loadedtests/test_pipeline_metrics.py::test_metric_gate_reasons_collects_all_enabled_reasonstests/test_pipeline_metrics.py::test_metric_gate_reasons_partial_new_metrics_pathstests/test_metrics_baseline.py::test_metrics_baseline_embedded_clone_payload_and_schema_resolution
Non-guarantees¶
- Absolute threshold defaults are not frozen by this chapter.
- Metrics scoring internals, per-dimension weighting, and the exact clone density curve may evolve if exit semantics and contract statuses stay stable.