cov-loupe 3.0.0 → 4.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +230 -0
- data/CLAUDE.md +5 -0
- data/CODE_OF_CONDUCT.md +62 -0
- data/CONTRIBUTING.md +102 -0
- data/GEMINI.md +5 -0
- data/README.md +154 -51
- data/RELEASE_NOTES.md +452 -0
- data/dev/images/cov-loupe-icon-lores.png +0 -0
- data/dev/images/cov-loupe-icon-square.png +0 -0
- data/dev/images/cov-loupe-icon.png +0 -0
- data/dev/images/cov-loupe-logo.png +0 -0
- data/dev/prompts/README.md +74 -0
- data/dev/prompts/archive/architectural-review-and-actions-prompt.md +53 -0
- data/dev/prompts/archive/investigate-and-report-issues-prompt.md +33 -0
- data/dev/prompts/archive/produce-action-items-prompt.md +25 -0
- data/dev/prompts/guidelines/ai-code-evaluator-guidelines.md +337 -0
- data/dev/prompts/improve/refactor-test-suite.md +18 -0
- data/dev/prompts/improve/simplify-code-logic.md +133 -0
- data/dev/prompts/improve/update-documentation.md +21 -0
- data/dev/prompts/review/comprehensive-codebase-review.md +176 -0
- data/dev/prompts/review/identify-action-items.md +143 -0
- data/dev/prompts/review/verify-code-changes.md +54 -0
- data/dev/prompts/validate/create-screencast-outline.md +234 -0
- data/dev/prompts/validate/test-documentation-examples.md +180 -0
- data/docs/QUICKSTART.md +63 -0
- data/docs/assets/images/cov-loupe-logo-lores.png +0 -0
- data/docs/assets/images/cov-loupe-logo.png +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/assets/stylesheets/branding.css +16 -0
- data/docs/assets/stylesheets/extra.css +15 -0
- data/docs/code_of_conduct.md +1 -0
- data/docs/contributing.md +1 -0
- data/docs/dev/ARCHITECTURE.md +56 -11
- data/docs/dev/DEVELOPMENT.md +116 -12
- data/docs/dev/FUTURE_ENHANCEMENTS.md +14 -0
- data/docs/dev/README.md +3 -2
- data/docs/dev/RELEASING.md +2 -0
- data/docs/dev/arch-decisions/README.md +10 -7
- data/docs/dev/arch-decisions/application-architecture.md +259 -0
- data/docs/dev/arch-decisions/coverage-data-quality.md +193 -0
- data/docs/dev/arch-decisions/output-character-mode.md +217 -0
- data/docs/dev/arch-decisions/path-resolution.md +90 -0
- data/docs/dev/arch-decisions/{004-x-arch-decision.md → policy-validation.md} +32 -28
- data/docs/dev/arch-decisions/{005-x-arch-decision.md → simplecov-integration.md} +47 -44
- data/docs/dev/presentations/cov-loupe-presentation.md +15 -13
- data/docs/examples/mcp-inputs.md +3 -0
- data/docs/examples/prompts.md +3 -0
- data/docs/examples/success_predicates.md +3 -0
- data/docs/fixtures/demo_project/.resultset.json +170 -0
- data/docs/fixtures/demo_project/README.md +6 -0
- data/docs/fixtures/demo_project/app/controllers/admin/audit_logs_controller.rb +19 -0
- data/docs/fixtures/demo_project/app/controllers/orders_controller.rb +26 -0
- data/docs/fixtures/demo_project/app/models/order.rb +20 -0
- data/docs/fixtures/demo_project/app/models/user.rb +19 -0
- data/docs/fixtures/demo_project/lib/api/client.rb +22 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/cleanup_job.rb +16 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/report_job.rb +17 -0
- data/docs/fixtures/demo_project/lib/payments/processor.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/refund_service.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/reporting/exporter.rb +16 -0
- data/docs/index.md +1 -0
- data/docs/license.md +3 -0
- data/docs/release_notes.md +3 -0
- data/docs/user/ADVANCED_USAGE.md +208 -115
- data/docs/user/CLI_FALLBACK_FOR_LLMS.md +2 -0
- data/docs/user/CLI_USAGE.md +276 -101
- data/docs/user/ERROR_HANDLING.md +4 -4
- data/docs/user/EXAMPLES.md +121 -128
- data/docs/user/INSTALLATION.md +9 -28
- data/docs/user/LIBRARY_API.md +227 -122
- data/docs/user/MCP_INTEGRATION.md +114 -203
- data/docs/user/README.md +5 -1
- data/docs/user/TROUBLESHOOTING.md +49 -27
- data/docs/user/installing-a-prelease-version-of-covloupe.md +43 -0
- data/docs/user/{V2-BREAKING-CHANGES.md → migrations/MIGRATING_TO_V2.md} +62 -72
- data/docs/user/migrations/MIGRATING_TO_V3.md +72 -0
- data/docs/user/migrations/MIGRATING_TO_V4.md +591 -0
- data/docs/user/migrations/README.md +22 -0
- data/docs/user/prompts/README.md +9 -0
- data/docs/user/prompts/non-web-coverage-analysis-prompt.md +103 -0
- data/docs/user/prompts/rails-coverage-analysis-prompt.md +94 -0
- data/docs/user/prompts/use-cli-not-mcp-prompt.md +53 -0
- data/examples/cli_demo.sh +77 -0
- data/examples/filter_and_table_demo-output.md +114 -0
- data/examples/filter_and_table_demo.rb +174 -0
- data/examples/fixtures/demo_project/coverage/.resultset.json +10 -0
- data/examples/mcp-inputs/README.md +66 -0
- data/examples/mcp-inputs/coverage_detailed.json +1 -0
- data/examples/mcp-inputs/coverage_raw.json +1 -0
- data/examples/mcp-inputs/coverage_summary.json +1 -0
- data/examples/mcp-inputs/list.json +1 -0
- data/examples/mcp-inputs/uncovered_lines.json +1 -0
- data/examples/prompts/README.md +27 -0
- data/examples/prompts/custom_resultset.txt +2 -0
- data/examples/prompts/detailed_with_source.txt +2 -0
- data/examples/prompts/list_lowest.txt +2 -0
- data/examples/prompts/summary.txt +2 -0
- data/examples/prompts/uncovered.txt +2 -0
- data/examples/success_predicates/README.md +198 -0
- data/examples/success_predicates/all_files_above_threshold_predicate.rb +21 -0
- data/examples/success_predicates/directory_specific_thresholds_predicate.rb +30 -0
- data/examples/success_predicates/project_coverage_minimum_predicate.rb +6 -0
- data/lib/cov_loupe/base_tool.rb +229 -20
- data/lib/cov_loupe/cli.rb +132 -23
- data/lib/cov_loupe/commands/base_command.rb +25 -6
- data/lib/cov_loupe/commands/command_factory.rb +0 -1
- data/lib/cov_loupe/commands/detailed_command.rb +10 -5
- data/lib/cov_loupe/commands/list_command.rb +2 -1
- data/lib/cov_loupe/commands/raw_command.rb +7 -5
- data/lib/cov_loupe/commands/summary_command.rb +12 -7
- data/lib/cov_loupe/commands/totals_command.rb +74 -10
- data/lib/cov_loupe/commands/uncovered_command.rb +7 -5
- data/lib/cov_loupe/commands/validate_command.rb +11 -3
- data/lib/cov_loupe/commands/version_command.rb +6 -4
- data/lib/cov_loupe/{app_config.rb → config/app_config.rb} +13 -5
- data/lib/cov_loupe/config/app_context.rb +43 -0
- data/lib/cov_loupe/config/boolean_type.rb +91 -0
- data/lib/cov_loupe/config/logger.rb +92 -0
- data/lib/cov_loupe/{option_normalizers.rb → config/option_normalizers.rb} +55 -24
- data/lib/cov_loupe/{option_parser_builder.rb → config/option_parser_builder.rb} +46 -24
- data/lib/cov_loupe/coverage/coverage_calculator.rb +53 -0
- data/lib/cov_loupe/coverage/coverage_reporter.rb +63 -0
- data/lib/cov_loupe/coverage/coverage_table_formatter.rb +133 -0
- data/lib/cov_loupe/{error_handler.rb → errors/error_handler.rb} +21 -33
- data/lib/cov_loupe/{errors.rb → errors/errors.rb} +48 -71
- data/lib/cov_loupe/formatters/formatters.rb +75 -0
- data/lib/cov_loupe/formatters/source_formatter.rb +18 -7
- data/lib/cov_loupe/formatters/table_formatter.rb +80 -0
- data/lib/cov_loupe/loaders/all.rb +15 -0
- data/lib/cov_loupe/loaders/all_cli.rb +10 -0
- data/lib/cov_loupe/loaders/all_mcp.rb +23 -0
- data/lib/cov_loupe/loaders/resultset_loader.rb +147 -0
- data/lib/cov_loupe/mcp_server.rb +3 -2
- data/lib/cov_loupe/model/model.rb +520 -0
- data/lib/cov_loupe/model/model_data.rb +13 -0
- data/lib/cov_loupe/model/model_data_cache.rb +116 -0
- data/lib/cov_loupe/option_parsers/env_options_parser.rb +17 -6
- data/lib/cov_loupe/option_parsers/error_helper.rb +16 -10
- data/lib/cov_loupe/output_chars.rb +192 -0
- data/lib/cov_loupe/paths/glob_utils.rb +100 -0
- data/lib/cov_loupe/{path_relativizer.rb → paths/path_relativizer.rb} +5 -13
- data/lib/cov_loupe/paths/path_utils.rb +265 -0
- data/lib/cov_loupe/paths/volume_case_sensitivity.rb +173 -0
- data/lib/cov_loupe/presenters/base_coverage_presenter.rb +9 -13
- data/lib/cov_loupe/presenters/coverage_payload_presenter.rb +21 -0
- data/lib/cov_loupe/presenters/payload_caching.rb +23 -0
- data/lib/cov_loupe/presenters/project_coverage_presenter.rb +73 -21
- data/lib/cov_loupe/presenters/project_totals_presenter.rb +16 -10
- data/lib/cov_loupe/repositories/coverage_repository.rb +149 -0
- data/lib/cov_loupe/resolvers/coverage_line_resolver.rb +90 -76
- data/lib/cov_loupe/resolvers/{resolver_factory.rb → resolver_helpers.rb} +6 -5
- data/lib/cov_loupe/resolvers/resultset_path_resolver.rb +40 -12
- data/lib/cov_loupe/scripts/command_execution.rb +113 -0
- data/lib/cov_loupe/scripts/latest_ci_status.rb +97 -0
- data/lib/cov_loupe/scripts/pre_release_check.rb +164 -0
- data/lib/cov_loupe/scripts/setup_doc_server.rb +23 -0
- data/lib/cov_loupe/scripts/start_doc_server.rb +24 -0
- data/lib/cov_loupe/staleness/stale_status.rb +23 -0
- data/lib/cov_loupe/staleness/staleness_checker.rb +328 -0
- data/lib/cov_loupe/staleness/staleness_message_formatter.rb +91 -0
- data/lib/cov_loupe/tools/coverage_detailed_tool.rb +14 -15
- data/lib/cov_loupe/tools/coverage_raw_tool.rb +14 -14
- data/lib/cov_loupe/tools/coverage_summary_tool.rb +16 -16
- data/lib/cov_loupe/tools/coverage_table_tool.rb +139 -21
- data/lib/cov_loupe/tools/coverage_totals_tool.rb +31 -13
- data/lib/cov_loupe/tools/help_tool.rb +16 -20
- data/lib/cov_loupe/tools/list_tool.rb +65 -0
- data/lib/cov_loupe/tools/uncovered_lines_tool.rb +14 -14
- data/lib/cov_loupe/tools/validate_tool.rb +18 -24
- data/lib/cov_loupe/tools/version_tool.rb +8 -3
- data/lib/cov_loupe/version.rb +1 -1
- data/lib/cov_loupe.rb +83 -55
- metadata +184 -154
- data/docs/dev/BRANCH_ONLY_COVERAGE.md +0 -158
- data/docs/dev/arch-decisions/001-x-arch-decision.md +0 -95
- data/docs/dev/arch-decisions/002-x-arch-decision.md +0 -159
- data/docs/dev/arch-decisions/003-x-arch-decision.md +0 -165
- data/lib/cov_loupe/app_context.rb +0 -26
- data/lib/cov_loupe/constants.rb +0 -22
- data/lib/cov_loupe/coverage_reporter.rb +0 -31
- data/lib/cov_loupe/formatters.rb +0 -51
- data/lib/cov_loupe/mode_detector.rb +0 -56
- data/lib/cov_loupe/model.rb +0 -339
- data/lib/cov_loupe/presenters/coverage_detailed_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_raw_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_summary_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_uncovered_presenter.rb +0 -14
- data/lib/cov_loupe/resultset_loader.rb +0 -131
- data/lib/cov_loupe/staleness_checker.rb +0 -247
- data/lib/cov_loupe/table_formatter.rb +0 -64
- data/lib/cov_loupe/tools/all_files_coverage_tool.rb +0 -51
- data/lib/cov_loupe/util.rb +0 -88
- data/spec/MCP_INTEGRATION_TESTS_README.md +0 -111
- data/spec/TIMESTAMPS.md +0 -48
- data/spec/all_files_coverage_tool_spec.rb +0 -53
- data/spec/app_config_spec.rb +0 -142
- data/spec/base_tool_spec.rb +0 -62
- data/spec/cli/show_default_report_spec.rb +0 -33
- data/spec/cli_enumerated_options_spec.rb +0 -90
- data/spec/cli_error_spec.rb +0 -184
- data/spec/cli_format_spec.rb +0 -123
- data/spec/cli_json_options_spec.rb +0 -50
- data/spec/cli_source_spec.rb +0 -44
- data/spec/cli_spec.rb +0 -192
- data/spec/cli_table_spec.rb +0 -28
- data/spec/cli_usage_spec.rb +0 -42
- data/spec/commands/base_command_spec.rb +0 -107
- data/spec/commands/command_factory_spec.rb +0 -76
- data/spec/commands/detailed_command_spec.rb +0 -34
- data/spec/commands/list_command_spec.rb +0 -28
- data/spec/commands/raw_command_spec.rb +0 -69
- data/spec/commands/summary_command_spec.rb +0 -34
- data/spec/commands/totals_command_spec.rb +0 -34
- data/spec/commands/uncovered_command_spec.rb +0 -55
- data/spec/commands/validate_command_spec.rb +0 -213
- data/spec/commands/version_command_spec.rb +0 -38
- data/spec/constants_spec.rb +0 -61
- data/spec/cov_loupe/formatters/source_formatter_spec.rb +0 -267
- data/spec/cov_loupe/formatters_spec.rb +0 -76
- data/spec/cov_loupe/presenters/base_coverage_presenter_spec.rb +0 -79
- data/spec/cov_loupe_model_spec.rb +0 -454
- data/spec/cov_loupe_module_spec.rb +0 -37
- data/spec/cov_loupe_opts_spec.rb +0 -185
- data/spec/coverage_reporter_spec.rb +0 -102
- data/spec/coverage_table_tool_spec.rb +0 -59
- data/spec/coverage_totals_tool_spec.rb +0 -37
- data/spec/error_handler_spec.rb +0 -197
- data/spec/error_mode_spec.rb +0 -139
- data/spec/errors_edge_cases_spec.rb +0 -312
- data/spec/errors_stale_spec.rb +0 -83
- data/spec/file_based_mcp_tools_spec.rb +0 -99
- data/spec/help_tool_spec.rb +0 -26
- data/spec/integration_spec.rb +0 -789
- data/spec/logging_fallback_spec.rb +0 -128
- data/spec/mcp_logging_spec.rb +0 -44
- data/spec/mcp_server_integration_spec.rb +0 -23
- data/spec/mcp_server_spec.rb +0 -106
- data/spec/mode_detector_spec.rb +0 -153
- data/spec/model_error_handling_spec.rb +0 -269
- data/spec/model_staleness_spec.rb +0 -79
- data/spec/option_normalizers_spec.rb +0 -203
- data/spec/option_parsers/env_options_parser_spec.rb +0 -221
- data/spec/option_parsers/error_helper_spec.rb +0 -222
- data/spec/path_relativizer_spec.rb +0 -98
- data/spec/presenters/coverage_detailed_presenter_spec.rb +0 -19
- data/spec/presenters/coverage_raw_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_summary_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_uncovered_presenter_spec.rb +0 -16
- data/spec/presenters/project_coverage_presenter_spec.rb +0 -87
- data/spec/presenters/project_totals_presenter_spec.rb +0 -144
- data/spec/resolvers/coverage_line_resolver_spec.rb +0 -282
- data/spec/resolvers/resolver_factory_spec.rb +0 -61
- data/spec/resolvers/resultset_path_resolver_spec.rb +0 -60
- data/spec/resultset_loader_spec.rb +0 -167
- data/spec/shared_examples/README.md +0 -115
- data/spec/shared_examples/coverage_presenter_examples.rb +0 -66
- data/spec/shared_examples/file_based_mcp_tools.rb +0 -179
- data/spec/shared_examples/formatted_command_examples.rb +0 -64
- data/spec/shared_examples/mcp_tool_text_json_response.rb +0 -16
- data/spec/spec_helper.rb +0 -127
- data/spec/staleness_checker_spec.rb +0 -374
- data/spec/staleness_more_spec.rb +0 -42
- data/spec/support/cli_helpers.rb +0 -22
- data/spec/support/control_flow_helpers.rb +0 -20
- data/spec/support/fake_mcp.rb +0 -40
- data/spec/support/io_helpers.rb +0 -29
- data/spec/support/mcp_helpers.rb +0 -35
- data/spec/support/mcp_runner.rb +0 -66
- data/spec/support/mocking_helpers.rb +0 -30
- data/spec/table_format_spec.rb +0 -70
- data/spec/tools/validate_tool_spec.rb +0 -132
- data/spec/tools_error_handling_spec.rb +0 -130
- data/spec/util_spec.rb +0 -154
- data/spec/version_spec.rb +0 -123
- data/spec/version_tool_spec.rb +0 -141
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/bar.rb +0 -0
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/foo.rb +0 -0
- /data/lib/cov_loupe/{config_parser.rb → config/config_parser.rb} +0 -0
- /data/lib/cov_loupe/{predicate_evaluator.rb → config/predicate_evaluator.rb} +0 -0
- /data/lib/cov_loupe/{error_handler_factory.rb → errors/error_handler_factory.rb} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Path Resolution Strategy
|
|
2
|
+
|
|
3
|
+
[Back to ADR Index](README.md)
|
|
4
|
+
|
|
5
|
+
## Cross-OS Coverage Data Support
|
|
6
|
+
|
|
7
|
+
### Status
|
|
8
|
+
**Accepted** (2025-12-26)
|
|
9
|
+
|
|
10
|
+
### Context
|
|
11
|
+
|
|
12
|
+
SimpleCov's `.resultset.json` files contain file paths as keys in the coverage data hash. These paths are typically absolute paths from the machine where tests were run. Different operating systems use different path separators:
|
|
13
|
+
- Unix/Linux/macOS: `/`
|
|
14
|
+
- Windows: `\` (backslash)
|
|
15
|
+
|
|
16
|
+
Early versions of cov-loupe included path normalization logic that converted backslashes to forward slashes, enabling cross-platform path matching. This was motivated by a theoretical use case: analyzing a `.resultset.json` file generated on one OS (e.g., Windows) on a different OS (e.g., Linux/macOS).
|
|
17
|
+
|
|
18
|
+
Upon closer examination, this cross-OS scenario is unrealistic for several reasons:
|
|
19
|
+
|
|
20
|
+
1. **CI/CD workflows** – While coverage files might be generated in CI (often Linux), developers typically either:
|
|
21
|
+
- Re-run tests locally to generate fresh coverage
|
|
22
|
+
- View coverage reports rendered by SimpleCov's HTML formatter
|
|
23
|
+
- Use hosted coverage services (Codecov, Coveralls)
|
|
24
|
+
|
|
25
|
+
2. **Docker development** – When tests run in containers, volume mounting ensures paths already match the host environment
|
|
26
|
+
|
|
27
|
+
3. **Path structure differences** – Cross-OS paths differ in more ways than just separators (drive letters, root paths, etc.), making simple separator normalization insufficient
|
|
28
|
+
|
|
29
|
+
4. **Same-machine path variations** – The legitimate use case is handling different working directories or relative vs absolute paths on the *same* machine, not across different OSes
|
|
30
|
+
|
|
31
|
+
### Decision
|
|
32
|
+
|
|
33
|
+
**Do not support analyzing `.resultset.json` files across different operating systems.**
|
|
34
|
+
|
|
35
|
+
Specifically:
|
|
36
|
+
- Normalize backslashes to forward slashes on Windows only
|
|
37
|
+
- Apply case-insensitive path matching on Windows (filesystem is case-insensitive)
|
|
38
|
+
- Apply case-sensitive path matching on Unix (filesystem is case-sensitive)
|
|
39
|
+
- Keep path resolution focused on same-OS scenarios:
|
|
40
|
+
- Exact absolute path matching
|
|
41
|
+
- Relative path matching (stripping project root)
|
|
42
|
+
- Trust that paths in coverage data use the native separator for the OS where cov-loupe runs
|
|
43
|
+
|
|
44
|
+
### Consequences
|
|
45
|
+
|
|
46
|
+
**Benefits:**
|
|
47
|
+
- **Simpler code** – No need for path separator normalization logic
|
|
48
|
+
- **Clearer semantics** – Path matching behavior is more predictable
|
|
49
|
+
- **Fewer edge cases** – No ambiguity around mixed separator styles
|
|
50
|
+
- **Honest API** – We don't promise cross-OS compatibility we can't fully deliver
|
|
51
|
+
|
|
52
|
+
**Trade-offs:**
|
|
53
|
+
- Users cannot analyze Windows `.resultset.json` files on Linux/macOS or vice versa
|
|
54
|
+
- This is acceptable because this scenario is impractical anyway
|
|
55
|
+
- Users who encounter this should re-run tests in their current environment
|
|
56
|
+
|
|
57
|
+
**No impact on:**
|
|
58
|
+
- Same-OS path resolution (absolute, relative)
|
|
59
|
+
- Normal development workflows
|
|
60
|
+
- CI/CD integration
|
|
61
|
+
- Docker/container-based development
|
|
62
|
+
|
|
63
|
+
### Implementation
|
|
64
|
+
|
|
65
|
+
Path normalization is centralized in the `PathUtils` module (`lib/cov_loupe/path_utils.rb`). It handles:
|
|
66
|
+
- Normalizing path separators (backslashes to forward slashes on Windows)
|
|
67
|
+
- Case normalization for case-insensitive volumes (autodetected or explicit)
|
|
68
|
+
- Path cleaning
|
|
69
|
+
|
|
70
|
+
`CoverageLineResolver` delegates all path normalization to `PathUtils.normalize`, avoiding scattered platform checks and keeping the resolver logic focused on lookup strategies.
|
|
71
|
+
|
|
72
|
+
#### Path Comparison Strategy
|
|
73
|
+
|
|
74
|
+
Path comparison uses a **normalize-for-comparison-only** approach rather than normalize-and-store:
|
|
75
|
+
|
|
76
|
+
- **Original paths are preserved** - `PathUtils.expand` preserves the original case to avoid corrupting displayed file paths
|
|
77
|
+
- **Normalization happens at comparison time** - The `normalized_start_with?` helper method normalizes both paths internally for comparison but doesn't modify the originals
|
|
78
|
+
- **Boundary checking** - Prevents false matches where a path starts with a prefix string but isn't actually within that directory (e.g., `/home/user/project` should not match `/home/user/project-backup/file.rb`)
|
|
79
|
+
|
|
80
|
+
This approach ensures:
|
|
81
|
+
1. User-visible paths maintain original casing for better UX
|
|
82
|
+
2. Path matching works correctly on case-insensitive volumes (Windows, most macOS)
|
|
83
|
+
3. Mixed path separators (forward slash vs backslash) are handled transparently
|
|
84
|
+
4. Directory boundary checking prevents incorrect prefix matches
|
|
85
|
+
|
|
86
|
+
### References
|
|
87
|
+
|
|
88
|
+
- Implementation: `lib/cov_loupe/resolvers/coverage_line_resolver.rb` (delegates to `PathUtils`)
|
|
89
|
+
- Central Logic: `lib/cov_loupe/path_utils.rb`
|
|
90
|
+
- Related tests removed: `spec/resolvers/coverage_line_resolver_spec.rb` (cross-OS separator normalization context)
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Policy Validation
|
|
2
2
|
|
|
3
|
-
[Back to main README](../../
|
|
3
|
+
[Back to main README](../../index.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This document describes how cov-loupe allows users to define and enforce custom coverage policies through success predicates.
|
|
6
|
+
|
|
7
|
+
## Ruby `instance_eval` for Success Predicates
|
|
8
|
+
|
|
9
|
+
### Status
|
|
6
10
|
|
|
7
11
|
Accepted
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
### Context
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
cov-loupe needed a mechanism for users to define custom coverage policies beyond simple percentage thresholds. Different projects have different requirements:
|
|
12
16
|
|
|
13
17
|
- Some want all files above 80%, others allow a few files below threshold
|
|
14
18
|
- Some need different thresholds for different directories (e.g., 90% for API code, 60% for legacy)
|
|
@@ -22,7 +26,7 @@ We considered several approaches:
|
|
|
22
26
|
3. **Ruby file evaluation**: Load and execute arbitrary Ruby code that returns a callable predicate
|
|
23
27
|
4. **Sandboxed DSL**: Use a restricted Ruby environment (e.g., `$SAFE` levels, isolated VMs)
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
#### Key Requirements
|
|
26
30
|
|
|
27
31
|
- Flexibility: Support arbitrarily complex coverage policies
|
|
28
32
|
- Simplicity: Easy for users to write and understand
|
|
@@ -30,7 +34,7 @@ We considered several approaches:
|
|
|
30
34
|
- CI/CD integration: Clear exit codes (0 = pass, 1 = fail, 2 = error)
|
|
31
35
|
- Access to coverage data: Predicates need access to the full `CoverageModel` API
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
#### Why Not a Custom DSL?
|
|
34
38
|
|
|
35
39
|
A custom DSL would be:
|
|
36
40
|
- Limited in expressiveness (hard to predict all future use cases)
|
|
@@ -38,7 +42,7 @@ A custom DSL would be:
|
|
|
38
42
|
- More maintenance burden (parsing, validation, documentation)
|
|
39
43
|
- Still vulnerable to injection if it allowed any dynamic computation
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
#### Why Not Sandboxing?
|
|
42
46
|
|
|
43
47
|
Ruby's sandboxing options are limited:
|
|
44
48
|
- `$SAFE` levels were deprecated and removed in Ruby 2.7+
|
|
@@ -46,13 +50,13 @@ Ruby's sandboxing options are limited:
|
|
|
46
50
|
- Any Turing-complete sandbox can be escaped given enough effort
|
|
47
51
|
- True security requires not executing untrusted code at all
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
### Decision
|
|
50
54
|
|
|
51
55
|
We chose to **evaluate Ruby files using `instance_eval`** with prominent security warnings rather than attempting to create a false sense of security through incomplete sandboxing.
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
#### Implementation
|
|
54
58
|
|
|
55
|
-
The implementation is in `lib/cov_loupe/cli.rb:
|
|
59
|
+
The implementation is in `lib/cov_loupe/cli.rb` (`CoverageCLI#load_success_predicate`):
|
|
56
60
|
|
|
57
61
|
```ruby
|
|
58
62
|
def load_success_predicate(path)
|
|
@@ -97,11 +101,11 @@ rescue => e
|
|
|
97
101
|
end
|
|
98
102
|
```
|
|
99
103
|
|
|
100
|
-
|
|
104
|
+
#### Security Model: Treat as Executable Code
|
|
101
105
|
|
|
102
106
|
Rather than pretending to sandbox untrusted code, we treat success predicates **exactly like any other Ruby code in the project**:
|
|
103
107
|
|
|
104
|
-
1. **Prominent warnings** in documentation (examples/success_predicates/README.md
|
|
108
|
+
1. **Prominent warnings** in documentation (highlighted near the top of `examples/success_predicates/README.md`):
|
|
105
109
|
```
|
|
106
110
|
⚠️ SECURITY WARNING
|
|
107
111
|
|
|
@@ -116,14 +120,14 @@ Rather than pretending to sandbox untrusted code, we treat success predicates **
|
|
|
116
120
|
3. **CI/CD best practices**: Same permissions model as running tests themselves
|
|
117
121
|
4. **Example predicates**: Well-documented examples showing safe patterns
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
#### Predicate API
|
|
120
124
|
|
|
121
125
|
Success predicates must be callable (lambda, proc, or object with `#call` method):
|
|
122
126
|
|
|
123
127
|
**Lambda example:**
|
|
124
128
|
```ruby
|
|
125
129
|
->(model) do
|
|
126
|
-
model.
|
|
130
|
+
model.list.all? { |f| f['percentage'] >= 80 }
|
|
127
131
|
end
|
|
128
132
|
```
|
|
129
133
|
|
|
@@ -133,7 +137,7 @@ end
|
|
|
133
137
|
|
|
134
138
|
class CoveragePolicy
|
|
135
139
|
def call(model)
|
|
136
|
-
api_files = model.
|
|
140
|
+
api_files = model.list['files'].select { |f| f['file'].start_with?('lib/api/') }
|
|
137
141
|
api_files.all? { |f| f['percentage'] >= 90 }
|
|
138
142
|
end
|
|
139
143
|
end
|
|
@@ -142,14 +146,14 @@ AllFilesAboveThreshold.new
|
|
|
142
146
|
```
|
|
143
147
|
|
|
144
148
|
The predicate receives a full `CoverageModel` instance with access to:
|
|
145
|
-
- `
|
|
149
|
+
- `list(tracked_globs:, sort_order:)` - All file coverage data
|
|
146
150
|
- `summary_for(path)` - Coverage summary for a specific file
|
|
147
151
|
- `uncovered_for(path)` - Uncovered lines for a specific file
|
|
148
152
|
- `detailed_for(path)` - Per-line coverage data
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
### Consequences
|
|
151
155
|
|
|
152
|
-
|
|
156
|
+
#### Positive
|
|
153
157
|
|
|
154
158
|
1. **Maximum flexibility**: Users can express arbitrarily complex coverage policies using full Ruby
|
|
155
159
|
2. **Familiar tooling**: Users can debug predicates with standard Ruby tools (pry, byebug, etc.)
|
|
@@ -158,20 +162,20 @@ The predicate receives a full `CoverageModel` instance with access to:
|
|
|
158
162
|
5. **Composability**: Users can require other libraries, define helper methods, etc.
|
|
159
163
|
6. **Excellent examples**: We provide 5+ well-documented example predicates
|
|
160
164
|
|
|
161
|
-
|
|
165
|
+
#### Negative
|
|
162
166
|
|
|
163
167
|
1. **Security responsibility**: Users must understand the security implications
|
|
164
168
|
2. **Potential misuse**: Users might mistakenly trust untrusted predicate files
|
|
165
169
|
3. **No isolation**: Buggy predicates can access/modify anything in the system
|
|
166
170
|
4. **Documentation burden**: Must clearly communicate security model
|
|
167
171
|
|
|
168
|
-
|
|
172
|
+
#### Trade-offs
|
|
169
173
|
|
|
170
174
|
- **Versus custom DSL**: More powerful and debuggable, but requires user awareness of security
|
|
171
175
|
- **Versus plugin architecture**: Simpler (no gem dependencies, no protocol to learn), but same security profile
|
|
172
176
|
- **Versus incomplete sandboxing**: Honest about capabilities rather than security theater
|
|
173
177
|
|
|
174
|
-
|
|
178
|
+
#### Threat Model
|
|
175
179
|
|
|
176
180
|
This approach is **appropriate** when:
|
|
177
181
|
- Predicate files are stored in version control with code review
|
|
@@ -183,7 +187,7 @@ This approach is **inappropriate** when:
|
|
|
183
187
|
- Allowing users to upload predicates via web interface
|
|
184
188
|
- Running in a multi-tenant environment without isolation
|
|
185
189
|
|
|
186
|
-
|
|
190
|
+
#### Future Considerations
|
|
187
191
|
|
|
188
192
|
If demand arises for truly untrusted predicate execution, alternatives include:
|
|
189
193
|
|
|
@@ -193,11 +197,11 @@ If demand arises for truly untrusted predicate execution, alternatives include:
|
|
|
193
197
|
|
|
194
198
|
However, for the primary use case (CI/CD policy enforcement), the current approach is simpler and more flexible than these alternatives.
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
### References
|
|
197
201
|
|
|
198
|
-
- Implementation: `lib/cov_loupe/cli.rb
|
|
199
|
-
- Security warnings: `examples/success_predicates/README.md
|
|
202
|
+
- Implementation: `lib/cov_loupe/cli.rb` (`CoverageCLI#load_success_predicate` and `#run_success_predicate`)
|
|
203
|
+
- Security warnings: `examples/success_predicates/README.md`
|
|
200
204
|
- Example predicates: `examples/success_predicates/*.rb`
|
|
201
205
|
- CoverageModel API: `lib/cov_loupe/model.rb`
|
|
202
|
-
- CLI config: `lib/cov_loupe/cli_config.rb
|
|
203
|
-
- Option parsing: `lib/cov_loupe/option_parser_builder.rb` (
|
|
206
|
+
- CLI config: `lib/cov_loupe/cli_config.rb` (`success_predicate` field)
|
|
207
|
+
- Option parsing: `lib/cov_loupe/option_parser_builder.rb` (`--success-predicate` flag)
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SimpleCov Integration
|
|
2
2
|
|
|
3
|
-
[Back to main README](../../
|
|
3
|
+
[Back to main README](../../index.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This document describes how cov-loupe integrates with SimpleCov and manages its dependency on the SimpleCov gem.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## SimpleCov Runtime Dependency
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Status
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Replaced** – cov-loupe now requires SimpleCov at runtime so that multi-suite resultsets can be merged using SimpleCov's combine helpers.
|
|
12
12
|
|
|
13
|
-
###
|
|
13
|
+
### Original Context
|
|
14
|
+
|
|
15
|
+
cov-loupe provides tooling for inspecting SimpleCov coverage reports. When designing the gem, we had to decide whether to depend on SimpleCov as a runtime dependency.
|
|
16
|
+
|
|
17
|
+
#### Alternative Approaches
|
|
14
18
|
|
|
15
19
|
1. **Runtime dependency on SimpleCov**: Use SimpleCov's API to read and process coverage data
|
|
16
20
|
2. **Development-only dependency**: Read SimpleCov's `.resultset.json` files directly without requiring SimpleCov at runtime
|
|
17
21
|
3. **Support multiple coverage formats**: Parse coverage data from multiple tools (SimpleCov, Coverage, etc.)
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
#### Key Considerations
|
|
20
24
|
|
|
21
25
|
**Dependency weight**: SimpleCov itself has dependencies:
|
|
22
26
|
- `docile` (~> 1.1)
|
|
@@ -25,7 +29,7 @@ SimpleCov MCP provides tooling for inspecting SimpleCov coverage reports. When d
|
|
|
25
29
|
|
|
26
30
|
**Use case separation**:
|
|
27
31
|
- SimpleCov is needed when **running tests** to collect coverage
|
|
28
|
-
-
|
|
32
|
+
- cov-loupe is needed when **inspecting coverage** after tests complete
|
|
29
33
|
- These are temporally separated activities
|
|
30
34
|
|
|
31
35
|
**Deployment contexts**:
|
|
@@ -38,33 +42,32 @@ SimpleCov MCP provides tooling for inspecting SimpleCov coverage reports. When d
|
|
|
38
42
|
- The format is simple JSON with predictable structure
|
|
39
43
|
- Breaking changes would affect all SimpleCov users, so the format is unlikely to change
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
### Original Decision
|
|
42
46
|
|
|
43
|
-
We chose to **make SimpleCov a development dependency only** and read `.resultset.json` files directly using Ruby's standard library JSON parser.
|
|
47
|
+
We initially chose to **make SimpleCov a development dependency only** and read `.resultset.json` files directly using Ruby's standard library JSON parser.
|
|
44
48
|
|
|
45
|
-
###
|
|
49
|
+
### Revision: SimpleCov as Runtime Dependency
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
cov-loupe now depends on SimpleCov at runtime for the following reasons:
|
|
52
|
+
|
|
53
|
+
1. **Multi-suite merging**: Projects using multiple test suites (e.g., RSpec + Minitest) produce separate coverage results that must be merged using SimpleCov's `SimpleCov::ResultMerger.merge_results`
|
|
54
|
+
2. **Consistent calculations**: SimpleCov's coverage percentage algorithms handle edge cases that are difficult to replicate correctly
|
|
55
|
+
3. **Format compatibility**: Changes to SimpleCov's internal data structures are automatically handled by using its API
|
|
56
|
+
|
|
57
|
+
### Current Implementation
|
|
58
|
+
|
|
59
|
+
cov-loupe currently depends on `amazing_print`, `mcp`, and `simplecov` at runtime.
|
|
53
60
|
|
|
54
|
-
Coverage data is read directly from JSON files
|
|
61
|
+
Coverage data is read directly from JSON files by `CovLoupe::CoverageModel#load_coverage_data`:
|
|
55
62
|
```ruby
|
|
56
|
-
rs =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
raise "No test suite with coverage data found in resultset file: #{rs}" unless data
|
|
62
|
-
cov = data['coverage'] or raise "No 'coverage' key found in resultset file: #{rs}"
|
|
63
|
-
@cov = cov.transform_keys { |k| File.absolute_path(k, @root) }
|
|
64
|
-
@cov_timestamp = (data['timestamp'] || data['created_at'] || 0).to_i
|
|
63
|
+
rs = Resolvers::ResolverHelpers.find_resultset(@root, resultset: resultset)
|
|
64
|
+
loaded = ResultsetLoader.load(resultset_path: rs)
|
|
65
|
+
coverage_map = loaded.coverage_map or raise(CoverageDataError, "No 'coverage' key found in resultset file: #{rs}")
|
|
66
|
+
@cov = coverage_map.transform_keys { |k| File.absolute_path(k, @root) }
|
|
67
|
+
@cov_timestamp = loaded.timestamp
|
|
65
68
|
```
|
|
66
69
|
|
|
67
|
-
Coverage calculations use simple algorithms (
|
|
70
|
+
Coverage calculations use simple algorithms in `CovLoupe::CoverageCalculator` (`summary`, `uncovered`, `detailed`):
|
|
68
71
|
```ruby
|
|
69
72
|
def summary(arr)
|
|
70
73
|
total = 0
|
|
@@ -121,9 +124,9 @@ Where:
|
|
|
121
124
|
|
|
122
125
|
### Resultset Discovery
|
|
123
126
|
|
|
124
|
-
We implement flexible discovery of `.resultset.json` files
|
|
127
|
+
We implement flexible discovery of `.resultset.json` files via `Resolvers::ResultsetPathResolver::DEFAULT_CANDIDATES`:
|
|
125
128
|
```ruby
|
|
126
|
-
|
|
129
|
+
DEFAULT_CANDIDATES = [
|
|
127
130
|
'.resultset.json',
|
|
128
131
|
'coverage/.resultset.json',
|
|
129
132
|
'tmp/.resultset.json'
|
|
@@ -132,9 +135,9 @@ RESULTSET_CANDIDATES = [
|
|
|
132
135
|
|
|
133
136
|
This supports common SimpleCov configurations without requiring SimpleCov to be loaded.
|
|
134
137
|
|
|
135
|
-
|
|
138
|
+
### Consequences
|
|
136
139
|
|
|
137
|
-
|
|
140
|
+
#### Positive (Original Development-Only Approach)
|
|
138
141
|
|
|
139
142
|
1. **Lightweight installation**: No transitive dependencies beyond `mcp` gem
|
|
140
143
|
2. **Deployment flexibility**: Can analyze coverage in environments without test dependencies
|
|
@@ -143,7 +146,7 @@ This supports common SimpleCov configurations without requiring SimpleCov to be
|
|
|
143
146
|
5. **CI/CD optimization**: Analysis jobs don't need full test suite dependencies
|
|
144
147
|
6. **Production-safe**: Can be deployed to production environments if needed (e.g., for monitoring)
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
#### Negative (Original Development-Only Approach)
|
|
147
150
|
|
|
148
151
|
1. **Format dependency**: Tightly coupled to SimpleCov's JSON format
|
|
149
152
|
2. **Breaking changes risk**: If SimpleCov changes `.resultset.json` structure, we must adapt
|
|
@@ -151,18 +154,18 @@ This supports common SimpleCov configurations without requiring SimpleCov to be
|
|
|
151
154
|
4. **Duplicate logic**: Coverage percentage calculations reimplemented (though simple)
|
|
152
155
|
5. **Maintenance**: Must track SimpleCov format changes manually
|
|
153
156
|
|
|
154
|
-
|
|
157
|
+
#### Trade-offs (Current Runtime Dependency Approach)
|
|
155
158
|
|
|
156
|
-
- **Versus
|
|
159
|
+
- **Versus development-only dependency**: Heavier installation footprint, but better multi-suite support and format compatibility
|
|
157
160
|
- **Versus multi-format support**: Simpler implementation but locked to SimpleCov ecosystem
|
|
158
|
-
- **Versus
|
|
161
|
+
- **Versus custom merging logic**: More reliable but requires SimpleCov at runtime
|
|
159
162
|
|
|
160
163
|
### Risk Mitigation
|
|
161
164
|
|
|
162
165
|
1. **Format stability**: SimpleCov has maintained `.resultset.json` compatibility for years
|
|
163
166
|
2. **Simple format**: JSON structure is straightforward and unlikely to change dramatically
|
|
164
167
|
3. **Development dependency**: We still use SimpleCov in our own tests, so format changes would be detected immediately
|
|
165
|
-
4. **Documentation**:
|
|
168
|
+
4. **Documentation**: AGENTS.md documents the format dependency explicitly
|
|
166
169
|
5. **Error handling**: Robust error messages when format doesn't match expectations
|
|
167
170
|
|
|
168
171
|
### Format Evolution Strategy
|
|
@@ -176,14 +179,14 @@ If SimpleCov's format changes:
|
|
|
176
179
|
|
|
177
180
|
- Only supports SimpleCov (not Coverage gem, other tools)
|
|
178
181
|
- Assumes standard `.resultset.json` locations
|
|
179
|
-
-
|
|
182
|
+
- Multi-suite merging requires SimpleCov runtime dependency
|
|
180
183
|
- No support for branch coverage (SimpleCov feature not widely used yet)
|
|
181
184
|
|
|
182
|
-
|
|
185
|
+
### References
|
|
183
186
|
|
|
184
|
-
- Gemspec dependencies: `cov-loupe.gemspec
|
|
185
|
-
- JSON parsing: `lib/cov_loupe/
|
|
186
|
-
- Coverage calculations: `lib/cov_loupe/
|
|
187
|
-
- Resultset discovery: `lib/cov_loupe/
|
|
187
|
+
- Gemspec dependencies: `cov-loupe.gemspec` (`spec.add_dependency` entries)
|
|
188
|
+
- JSON parsing: `lib/cov_loupe/resultset_loader.rb` (`ResultsetLoader.load`)
|
|
189
|
+
- Coverage calculations: `lib/cov_loupe/coverage_calculator.rb` (`CoverageCalculator.summary`, `.uncovered`, `.detailed`)
|
|
190
|
+
- Resultset discovery: `lib/cov_loupe/resolvers/resultset_path_resolver.rb` (`ResultsetPathResolver::DEFAULT_CANDIDATES`)
|
|
188
191
|
- SimpleCov format documentation: https://github.com/simplecov-ruby/simplecov
|
|
189
192
|
- Development usage: Uses SimpleCov in `spec/spec_helper.rb` to test itself
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[Back to main README](../../
|
|
1
|
+
[Back to main README](../../index.md)
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
marp: true
|
|
@@ -9,7 +9,7 @@ backgroundColor: #fff
|
|
|
9
9
|
color: #333
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
#
|
|
12
|
+
# CovLoupe
|
|
13
13
|
### MCP Server, CLI, and Library for SimpleCov Ruby Test Coverage
|
|
14
14
|
|
|
15
15
|
- Keith Bennett
|
|
@@ -17,7 +17,7 @@ color: #333
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
## What is
|
|
20
|
+
## What is cov-loupe?
|
|
21
21
|
|
|
22
22
|
A **three-in-one** gem that makes SimpleCov coverage data accessible to:
|
|
23
23
|
|
|
@@ -59,7 +59,7 @@ This code base requires a Ruby version >= 3.2.0, because this is required by the
|
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
# Test the MCP server manually
|
|
62
|
-
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/cov_loupe/model.rb"}}}' | cov-loupe
|
|
62
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/cov_loupe/model.rb"}}}' | cov-loupe -m mcp
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
**What AI agents can do:**
|
|
@@ -73,12 +73,13 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_s
|
|
|
73
73
|
|
|
74
74
|
| Tool | Purpose | Example CLI Command |
|
|
75
75
|
|---------------------------|----------------------------|------------------------------------------------------|
|
|
76
|
-
| `
|
|
76
|
+
| `list_tool` | Project-wide coverage data | `cov-loupe list` |
|
|
77
77
|
| `coverage_detailed_tool` | Per-line hit counts | `cov-loupe detailed lib/cov_loupe/model.rb` |
|
|
78
78
|
| `coverage_raw_tool` | Raw SimpleCov lines array | `cov-loupe raw lib/cov_loupe/model.rb` |
|
|
79
79
|
| `coverage_summary_tool` | Get coverage % for a file | `cov-loupe summary lib/cov_loupe/model.rb` |
|
|
80
80
|
| `coverage_table_tool` | Formatted coverage table | `cov-loupe list` |
|
|
81
81
|
| `coverage_totals_tool` | Aggregated line totals | `cov-loupe totals` |
|
|
82
|
+
| `validate_tool` | Validate coverage policy | `cov-loupe validate coverage_policy.rb` |
|
|
82
83
|
| `help_tool` | Tool usage guidance | `cov-loupe --help` |
|
|
83
84
|
| `uncovered_lines_tool` | Find missing test coverage | `cov-loupe uncovered lib/cov_loupe/cli.rb` |
|
|
84
85
|
| `version_tool` | Display version info | `cov-loupe version` |
|
|
@@ -89,14 +90,14 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_s
|
|
|
89
90
|
###
|
|
90
91
|
|
|
91
92
|
```bash
|
|
92
|
-
# Show all files,
|
|
93
|
+
# Show all files, best coverage first
|
|
93
94
|
cov-loupe
|
|
94
95
|
|
|
95
96
|
# Focus on a specific file
|
|
96
97
|
cov-loupe summary lib/cov_loupe/cli.rb
|
|
97
98
|
|
|
98
99
|
# Find untested lines with source context
|
|
99
|
-
cov-loupe uncovered lib/cov_loupe/cli.rb --source=uncovered --
|
|
100
|
+
cov-loupe uncovered lib/cov_loupe/cli.rb --source=uncovered --context-lines 3
|
|
100
101
|
|
|
101
102
|
# JSON for scripts
|
|
102
103
|
cov-loupe -fJ | jq '.files[] | select(.percentage < 80)'
|
|
@@ -124,7 +125,7 @@ cov-loupe --resultset coverage-all/
|
|
|
124
125
|
cov-loupe --sort-order d
|
|
125
126
|
|
|
126
127
|
# Staleness checking (file newer than coverage?)
|
|
127
|
-
cov-loupe --
|
|
128
|
+
cov-loupe --raise-on-stale true
|
|
128
129
|
|
|
129
130
|
# Track new files missing from coverage
|
|
130
131
|
cov-loupe --tracked-globs "lib/**/tools/*.rb"
|
|
@@ -141,7 +142,7 @@ require 'cov_loupe'
|
|
|
141
142
|
model = CovLoupe::CoverageModel.new
|
|
142
143
|
|
|
143
144
|
# Get project overview
|
|
144
|
-
files = model.
|
|
145
|
+
files = model.list
|
|
145
146
|
puts "Lowest coverage: #{files.first['percentage']}%"
|
|
146
147
|
|
|
147
148
|
# Focus on specific concerns
|
|
@@ -162,7 +163,7 @@ puts "Missing lines: #{uncovered['uncovered'].inspect}"
|
|
|
162
163
|
```ruby
|
|
163
164
|
require 'cov_loupe'
|
|
164
165
|
|
|
165
|
-
files = CovLoupe::CoverageModel.new.
|
|
166
|
+
files = CovLoupe::CoverageModel.new.list
|
|
166
167
|
critical, other = files.partition { |f| f['file'].include?('/lib/critical/') }
|
|
167
168
|
|
|
168
169
|
fails = critical.select { |f| f['percentage'] < 100.0 } +
|
|
@@ -192,18 +193,19 @@ lib/cov_loupe
|
|
|
192
193
|
├── model.rb
|
|
193
194
|
├── path_relativizer.rb
|
|
194
195
|
├── staleness_checker.rb
|
|
196
|
+
├── version.rb
|
|
195
197
|
├── tools
|
|
196
|
-
│ ├──
|
|
198
|
+
│ ├── list_tool.rb
|
|
197
199
|
│ ├── coverage_detailed_tool.rb
|
|
198
200
|
│ ├── coverage_raw_tool.rb
|
|
199
201
|
│ ├── coverage_summary_tool.rb
|
|
200
202
|
│ ├── coverage_table_tool.rb
|
|
201
203
|
│ ├── coverage_totals_tool.rb
|
|
204
|
+
│ ├── validate_tool.rb
|
|
202
205
|
│ ├── help_tool.rb
|
|
203
206
|
│ ├── uncovered_lines_tool.rb
|
|
204
207
|
│ └── version_tool.rb
|
|
205
|
-
|
|
206
|
-
└── version.rb
|
|
208
|
+
└── util.rb
|
|
207
209
|
```
|
|
208
210
|
|
|
209
211
|
**Clean separation:** CLI ↔ Model ↔ MCP Tools
|