Skip to content

Commit 8ddd743

Browse files
committed
feat: align client surfaces with coverage join and always-on adoption metrics
- remove typing_coverage/docstring_coverage toggles from CLI, pyproject config, runtime plumbing, and docs - always collect adoption facts in metrics mode and keep unified metrics baselines writing adoption snapshots - surface Coverage Join facts in the VS Code overview and gate the optional coverage help topic by server version - harden the VS Code Coverage Join path against null metrics payloads and add formatter regression coverage - sync Claude Desktop and Codex plugin guidance with canonical coverage/adoption/API MCP surfaces - refresh HTML quality/clones/dead-code/dependencies presentation with shared stat-card summaries - update repo pyproject and contract/docs/tests to match the simplified metrics model
1 parent b88cc11 commit 8ddd743

42 files changed

Lines changed: 774 additions & 226 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ across MCP/HTML/clients; tightens MCP launcher/runtime behavior.
2828
- Surface effective analysis profile in report meta, MCP summary/triage, and HTML subtitle.
2929
- Add `health_scope`, `focus`, `new_by_source_kind` to MCP summary/triage.
3030
- Make baseline mismatch explicit (python tags + no-valid-baseline signal).
31+
- Surface `Coverage Join` facts and the optional `coverage` MCP help topic in
32+
the VS Code extension when the connected server supports them.
3133
- Prefer workspace-local launchers over `PATH` (Poetry fallback).
3234
- Add `workspace_root` to force project `.venv` selection.
3335

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<a href="https://github.com/orenlab/codeclone/actions/workflows/tests.yml"><img src="https://github.com/orenlab/codeclone/actions/workflows/tests.yml/badge.svg?branch=main&style=flat-square" alt="Tests"></a>
1717
<a href="https://github.com/orenlab/codeclone/actions/workflows/benchmark.yml"><img src="https://github.com/orenlab/codeclone/actions/workflows/benchmark.yml/badge.svg?style=flat-square" alt="Benchmark"></a>
1818
<a href="https://pypi.org/project/codeclone/"><img src="https://img.shields.io/pypi/pyversions/codeclone.svg?style=flat-square" alt="Python"></a>
19-
<a href="https://github.com/orenlab/codeclone"><img src="https://img.shields.io/badge/codeclone-87%20(B)-green" alt="codeclone 89 (B)"></a>
19+
<a href="https://github.com/orenlab/codeclone"><img src="https://img.shields.io/badge/codeclone-89%20(B)-green" alt="codeclone 89 (B)"></a>
2020
<a href="#license"><img src="https://img.shields.io/badge/license-MPL--2.0-brightgreen?style=flat-square" alt="License"></a>
2121
</p>
2222

@@ -50,7 +50,8 @@ Live sample report:
5050
- **Reports** — interactive HTML, deterministic JSON/TXT plus Markdown and SARIF projections from one canonical report
5151
- **MCP server** — optional read-only surface for AI agents and IDEs, designed as a budget-aware guided control
5252
surface for agentic development
53-
- **VS Code extension** — preview native client for CodeClone MCP with triage-first structural review
53+
- **VS Code extension** — preview native client for CodeClone MCP with triage-first structural review, factual
54+
`Coverage Join` overview support, and bounded in-IDE help topics
5455
- **Native client surfaces** — preview Claude Desktop bundle and Codex plugin over the same canonical MCP contract
5556
- **CI-first** — deterministic output, stable ordering, exit code contract, pre-commit support
5657
- **Fast** — incremental caching, parallel processing, warm-run optimization, and reproducible benchmark coverage

codeclone/_cli_args.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -214,18 +214,6 @@ def build_parser(version: str) -> _ArgumentParser:
214214
flag="--ci",
215215
help_text=ui.HELP_CI,
216216
)
217-
_add_bool_optional_argument(
218-
baselines_ci_group,
219-
flag="--typing-coverage",
220-
help_text=ui.HELP_TYPING_COVERAGE,
221-
default=True,
222-
)
223-
_add_bool_optional_argument(
224-
baselines_ci_group,
225-
flag="--docstring-coverage",
226-
help_text=ui.HELP_DOCSTRING_COVERAGE,
227-
default=True,
228-
)
229217
_add_bool_optional_argument(
230218
baselines_ci_group,
231219
flag="--api-surface",

codeclone/_cli_baselines.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ class _BaselineArgs(Protocol):
6262
fail_on_typing_regression: bool
6363
fail_on_docstring_regression: bool
6464
fail_on_api_break: bool
65-
typing_coverage: bool
66-
docstring_coverage: bool
6765
api_surface: bool
6866
ci: bool
6967

@@ -388,7 +386,7 @@ def _update_metrics_baseline_if_requested(
388386
new_metrics_baseline = MetricsBaseline.from_project_metrics(
389387
project_metrics=project_metrics,
390388
path=metrics_baseline_path,
391-
include_adoption=args.typing_coverage or args.docstring_coverage,
389+
include_adoption=True,
392390
include_api_surface=args.api_surface,
393391
)
394392
try:

codeclone/_cli_config.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ class _ConfigKeySpec:
5656
"fail_dead_code": _ConfigKeySpec(bool),
5757
"fail_health": _ConfigKeySpec(int),
5858
"fail_on_new_metrics": _ConfigKeySpec(bool),
59-
"typing_coverage": _ConfigKeySpec(bool),
60-
"docstring_coverage": _ConfigKeySpec(bool),
6159
"api_surface": _ConfigKeySpec(bool),
6260
"coverage_xml": _ConfigKeySpec(str, allow_none=True),
6361
"fail_on_typing_regression": _ConfigKeySpec(bool),

codeclone/_cli_runtime.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ class _RuntimeArgs(Protocol):
4242
min_typing_coverage: int
4343
min_docstring_coverage: int
4444
coverage_min: int
45-
typing_coverage: bool
46-
docstring_coverage: bool
4745
api_surface: bool
4846
update_metrics_baseline: bool
4947
skip_metrics: bool
@@ -181,10 +179,7 @@ def metrics_computed(args: _RuntimeArgs) -> tuple[str, ...]:
181179
computed.append("dependencies")
182180
if not args.skip_dead_code:
183181
computed.append("dead_code")
184-
if bool(getattr(args, "typing_coverage", True)) or bool(
185-
getattr(args, "docstring_coverage", True)
186-
):
187-
computed.append("coverage_adoption")
182+
computed.append("coverage_adoption")
188183
if bool(getattr(args, "api_surface", False)):
189184
computed.append("api_surface")
190185
if bool(getattr(args, "coverage_xml", None)):

codeclone/_html_css.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,8 @@
668668
/* Fact-list: compact label ··· value rows inside overview-summary-item cards. */
669669
.overview-fact-list{display:flex;flex-direction:column;gap:var(--sp-2)}
670670
.overview-fact-row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
671-
font-size:.8rem;padding-bottom:6px}
672-
.overview-fact-row:last-child{padding-bottom:0}
671+
font-size:.8rem;padding-bottom:6px;border-bottom:1px solid var(--border)}
672+
.overview-fact-row:last-child{padding-bottom:0;border-bottom:none}
673673
.overview-fact-label{color:var(--text-muted)}
674674
.overview-fact-value{display:inline-flex;align-items:baseline;gap:6px;
675675
color:var(--text-primary);font-weight:600;font-variant-numeric:tabular-nums;text-align:right}

codeclone/_html_report/_sections/_clones.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from typing import TYPE_CHECKING, Literal
1313

1414
from ... import _coerce
15-
from ..._html_badges import _source_kind_badge_html
15+
from ..._html_badges import _micro_badges, _source_kind_badge_html, _stat_card
1616
from ..._html_data_attrs import _build_data_attrs
1717
from ..._html_escape import _escape_html
1818
from ..._html_filters import CLONE_TYPE_OPTIONS, SPREAD_OPTIONS, _render_select
@@ -27,6 +27,7 @@
2727
from ...report.json_contract import clone_group_id
2828
from ...report.suggestions import classify_clone_type
2929
from .._components import Tone, insight_block
30+
from .._glossary import glossary_tip
3031
from .._icons import ICONS
3132
from .._tables import render_rows_table
3233
from .._tabs import render_split_tabs
@@ -740,12 +741,66 @@ def render_clones_panel(ctx: ReportContext) -> tuple[str, bool, int, int]:
740741
"from active review."
741742
)
742743
clones_tone: Tone = "warn" if ctx.clone_groups_total > 0 else "ok"
744+
745+
# Stat cards
746+
avg_per_group = (
747+
f"{ctx.clone_instances_total / max(1, ctx.clone_groups_total):.1f}"
748+
if ctx.clone_groups_total > 0
749+
else "0"
750+
)
751+
high_spread = sum(
752+
1
753+
for gs in (ctx.func_sorted, ctx.block_sorted, ctx.segment_sorted)
754+
for _, items in gs
755+
if len({str(it.get("filepath", "")) for it in items}) > 1
756+
)
757+
clone_cards = [
758+
_stat_card(
759+
"Clone groups",
760+
ctx.clone_groups_total,
761+
detail=_micro_badges(
762+
("functions", len(ctx.func_sorted)),
763+
("blocks", len(ctx.block_sorted)),
764+
("segments", len(ctx.segment_sorted)),
765+
),
766+
value_tone="warn" if ctx.clone_groups_total > 0 else "good",
767+
glossary_tip_fn=glossary_tip,
768+
),
769+
_stat_card(
770+
"Instances",
771+
ctx.clone_instances_total,
772+
detail=_micro_badges(("avg/group", avg_per_group)),
773+
value_tone="warn" if ctx.clone_instances_total > 0 else "good",
774+
glossary_tip_fn=glossary_tip,
775+
),
776+
]
777+
if novelty_enabled:
778+
clone_cards.append(
779+
_stat_card(
780+
"New groups",
781+
total_new,
782+
detail=_micro_badges(("known", total_known)),
783+
value_tone="bad" if total_new > 0 else "good",
784+
glossary_tip_fn=glossary_tip,
785+
),
786+
)
787+
clone_cards.append(
788+
_stat_card(
789+
"High spread",
790+
high_spread,
791+
value_tone="warn" if high_spread > 0 else "muted",
792+
glossary_tip_fn=glossary_tip,
793+
),
794+
)
795+
clone_cards_html = f'<div class="stat-cards">{"".join(clone_cards)}</div>'
796+
743797
panel = (
744798
insight_block(
745799
question="Where is duplication concentrated right now?",
746800
answer=clones_answer,
747801
tone=clones_tone,
748802
)
803+
+ clone_cards_html
749804
+ panel
750805
)
751806

0 commit comments

Comments
 (0)