cuke-dedup 0.1.1 → 0.2.0

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,98 @@
3
3
  All notable changes to CukeDedup are documented in this file. The project follows
4
4
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## [0.2.0] - 2026-09-10
7
+
8
+ ### Changed
9
+
10
+ - Reorganized the user documentation around a concise quick-start README and focused reference
11
+ guides for configuration, discovery, rules, reports, CI adoption, and safety limits.
12
+ - **Breaking (reports and baselines):** JSON and JSONL reports now use schema version `2`, and
13
+ semantic baselines use version `2`. Large exact-equivalence groups are represented by bounded
14
+ cluster evidence instead of many pair records. Existing version `1` baselines are rejected for
15
+ normal comparison but can be replaced directly with `--update-baseline`; no manual deletion is
16
+ required.
17
+ - **Breaking (CLI):** analysis that cannot cover the complete corpus now warns and exits on
18
+ finding severity instead of returning operational exit code `2`. Candidate-comparison
19
+ truncation, resolution resource limits, and registration imports that cannot be resolved
20
+ statically are all reported as incomplete coverage rather than analyzer failure. Set
21
+ `failOnIncomplete: true`, pass `--fail-on-incomplete`, or set the Action's
22
+ `fail-on-incomplete` input to restore exit code `2`.
23
+ - **Breaking (SARIF):** an incomplete run now reports `executionSuccessful: false` so code-scanning
24
+ consumers can distinguish partial coverage from a complete scan.
25
+ - **Breaking (Rust API):** `AnalysisOutcome::operational_errors` is replaced by
26
+ `AnalysisOutcome::incomplete`. Bounded work that could not finish is a completeness signal, not
27
+ an operational failure; `analyze` still refuses an incomplete result.
28
+ - **Breaking (Rust API):** report renderers and `write_reports` now accept a shared
29
+ `ReportContext` instead of separate result, root, threshold, and metrics arguments. Construct it
30
+ with `ReportContext::new` or `ReportContext::with_metrics`; the redundant
31
+ `*_with_threshold` and `write_reports_with_metrics` wrappers were removed.
32
+ - **Breaking (Rust API):** public result, diagnostic, reporter, and extensible enum types are now
33
+ `#[non_exhaustive]`. Use `AnalysisResult::new`, `ExecutionMetrics::new`, existing constructors,
34
+ and wildcard enum match arms so future fields and variants do not require another breaking
35
+ release.
36
+
37
+ ### Added
38
+
39
+ - An `overlapping-matcher` rule that reports two matchers accepting the same step text without
40
+ needing a feature corpus to prove it.
41
+ - `registrations` project configuration plus inference for local wrappers that forward their own
42
+ leading parameters to a known registration.
43
+ - `parameterTypes` project configuration, which restores exact usage and ambiguity analysis for
44
+ matchers that use project-defined Cucumber Expression parameter types.
45
+ - `failOnIncomplete` project configuration, the `--fail-on-incomplete` flag, and the Action's
46
+ `fail-on-incomplete` input.
47
+ - A `corpus.incomplete` flag in machine reports and a matching HTML alert when a registration
48
+ import could not be resolved statically.
49
+ - Legacy Cucumber.js and Cypress Cucumber registration entrypoints, plus Playwright-BDD
50
+ class-method decorator registrations.
51
+ - A `corpus.featureFilesWithoutSteps` census field for converted Gherkin Markdown inputs that
52
+ yielded no concrete steps.
53
+
54
+ ### Security
55
+
56
+ - Repository-controlled configuration can lower analysis candidate budgets but can no longer
57
+ raise their immutable memory and work ceilings.
58
+ - npm publishing now uses short-lived OIDC trusted-publisher credentials exclusively; the release
59
+ workflow no longer reads a long-lived `NPM_TOKEN` secret and fails closed on unsupported npm
60
+ clients.
61
+ - A second fuzz target covers configuration discovery, project module metadata, and baseline
62
+ parsing, the untrusted inputs that are read outside the source parsers.
63
+ - Release jobs are bound to the immutable workflow commit instead of accepting a free-form checkout
64
+ ref, and the GitHub Action no longer derives outbound download URLs from runtime file contents.
65
+
66
+ ### Performance
67
+
68
+ - Feature-step usage analysis evaluates every matcher in one pass with a shared regular-expression
69
+ set and matches each distinct step text once, roughly halving usage-dominated analysis time.
70
+
71
+ ### Fixed
72
+
73
+ - Repository-owned CodeQL analysis excludes the intentionally malformed JavaScript parser fixture
74
+ from extraction while preserving extended security queries for every supported project language.
75
+ - Analyzing a single package whose imports resolve outside it no longer fails the run; the
76
+ unresolved specifier is reported as a source-localized warning naming its cause.
77
+ - Registration modules the filesystem refuses to read remain operational failures, matching how
78
+ unreadable definition sources are already treated.
79
+ - Semantic baselines are no longer updated from an incomplete corpus.
80
+ - Wrapper inference no longer promotes deferred callbacks, nested declarations, generators, async
81
+ functions, conditional calls, or multi-statement helpers into file-wide registration aliases.
82
+ - Ambiguity evidence uses linear membership storage, and static overlap work shares the configured
83
+ candidate budget and caps retained findings instead of allocating an unbounded quadratic pair
84
+ or finding set.
85
+ - Oversized combined matcher indexes are split into bounded regular-expression sets instead of
86
+ silently restoring the definitions-by-steps fallback loop.
87
+ - npm release validation now covers `package-lock.json`, including the root package, platform
88
+ workspaces, and optional dependency versions.
89
+ - Gherkin Markdown that converts to an empty feature document now marks the corpus incomplete and
90
+ disables `unused-definition` instead of producing false non-use findings.
91
+ - Non-authoritative Cucumber Expression fallbacks and JavaScript regular expressions using the
92
+ Unicode-sets `v` flag can no longer produce false unused or ambiguity conclusions.
93
+ - Playwright-BDD factory aliases now require trusted ESM or CommonJS import evidence, and local
94
+ registration barrels preserve imported framework bindings across recoverable resolver errors.
95
+ - Static overlap witness scans are bounded before index evaluation, handler behavior signatures
96
+ normalize renamed receivers, and SARIF preserves pre-truncated cluster metadata.
97
+
6
98
  ## [0.1.1] - 2026-09-08
7
99
 
8
100
  ### Changed
@@ -28,5 +120,6 @@ Initial public release.
28
120
  - Native Cargo and npm distributions for eight supported targets.
29
121
  - A checksum-verified GitHub Action and an agent-oriented CukeDedup skill.
30
122
 
123
+ [0.2.0]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.1.1...v0.2.0
31
124
  [0.1.1]: https://github.com/figueiredoluiz/cuke-dedup/compare/v0.1.0...v0.1.1
32
125
  [0.1.0]: https://github.com/figueiredoluiz/cuke-dedup/releases/tag/v0.1.0
package/README.md CHANGED
@@ -3,41 +3,54 @@
3
3
  [![CI](https://github.com/figueiredoluiz/cuke-dedup/actions/workflows/ci.yml/badge.svg)](https://github.com/figueiredoluiz/cuke-dedup/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
 
6
- CukeDedup finds duplicate, near-duplicate, ambiguous, reusable, and unused Cucumber/Gherkin step definitions. It analyzes source code statically: project configuration and test code are never executed.
6
+ CukeDedup is a fast static analyzer for duplicate, ambiguous, reusable, and unused
7
+ Cucumber/Gherkin step definitions. It reads project configuration and test code without executing
8
+ either one.
7
9
 
8
- Current support includes:
10
+ It supports:
9
11
 
10
- - JavaScript/JSX and TypeScript/TSX step definitions, including common module variants.
11
- - Cucumber.js, Playwright BDD, and Cypress Cucumber workflows.
12
+ - JavaScript, JSX, TypeScript, and TSX step definitions.
13
+ - Cucumber.js, Playwright-BDD, and Cypress Cucumber projects.
12
14
  - Classic `.feature` files and Gherkin Markdown `.feature.md` files.
13
15
  - Terminal, JSON, JSON Lines, HTML, and SARIF reports.
14
- - Threshold, baseline, changed-file, suppression, and ignore-file workflows.
16
+ - Thresholds, baselines, changed-file analysis, suppressions, and ignore files.
15
17
 
16
18
  ## Install
17
19
 
18
- Install the native CLI through Cargo:
20
+ With Cargo:
19
21
 
20
22
  ```sh
21
23
  cargo install cuke-dedup
22
24
  ```
23
25
 
24
- Or install the native launcher in a JavaScript project:
26
+ Or in a JavaScript project:
25
27
 
26
28
  ```sh
27
29
  npm install --save-dev cuke-dedup
28
30
  npx cuke-dedup .
29
31
  ```
30
32
 
31
- To build from source, clone the repository with Rust 1.90 or newer and run
32
- `cargo build --release --locked`.
33
+ Building from source requires Rust 1.90 or newer:
33
34
 
34
- ## Usage
35
+ ```sh
36
+ cargo build --release --locked
37
+ ```
35
38
 
36
- The path-only and explicit `check` forms are equivalent when both include a path:
39
+ ## Quick start
40
+
41
+ Run CukeDedup from the root of a test project:
37
42
 
38
43
  ```sh
39
44
  cuke-dedup .
40
- cuke-dedup check .
45
+ ```
46
+
47
+ No configuration is required. CukeDedup respects `.gitignore` and `.cuke-dedupignore`, skips
48
+ common generated directories, discovers conventional Gherkin and JavaScript/TypeScript files, and
49
+ recognizes common `Given`, `When`, `Then`, and `defineStep` registrations and aliases.
50
+
51
+ Useful commands:
52
+
53
+ ```sh
41
54
  cuke-dedup . --threshold 5
42
55
  cuke-dedup . --print-config
43
56
  cuke-dedup . --reporters terminal,json,html,sarif
@@ -45,228 +58,74 @@ cuke-dedup . --reporters jsonl
45
58
  cuke-dedup . --output reports/cuke-dedup
46
59
  ```
47
60
 
48
- `check` is reserved as the explicit subcommand and requires a path. To analyze a directory literally named `check`, use `cuke-dedup ./check`.
49
-
50
- With no configuration, CukeDedup respects `.gitignore` and `.cuke-dedupignore`, excludes common generated directories, discovers classic `.feature` files and Gherkin Markdown `.feature.md` files, and inspects conventional JavaScript/TypeScript step registrations such as `Given`, `When`, `Then`, and `defineStep`, including common aliases.
51
-
52
- ## Configuration
61
+ `cuke-dedup check .` is equivalent to `cuke-dedup .`. The explicit `check` form requires a path;
62
+ use `cuke-dedup ./check` to analyze a directory literally named `check`.
53
63
 
54
- ### Precedence and config files
64
+ ## Framework support
55
65
 
56
- Configuration precedence is:
66
+ | Workflow | Recognized registrations |
67
+ | --- | --- |
68
+ | Cucumber.js | `@cucumber/cucumber` and legacy `cucumber`; ESM, CJS, aliases, namespaces, and static local re-exports. |
69
+ | Playwright-BDD | `createBdd()` registrations and `playwright-bdd/decorators` class-method decorators. |
70
+ | Cypress Cucumber | `@badeball/cypress-cucumber-preprocessor` and legacy `cypress-cucumber-preprocessor/steps`. |
57
71
 
58
- ```text
59
- CLI flags > explicit --config > auto-discovered CukeDedup config > detected framework configuration > built-in defaults
60
- ```
72
+ Package entrypoints are matched exactly. Plain Playwright projects are supported when their
73
+ Gherkin bindings use Cucumber.js or Playwright-BDD.
61
74
 
62
- CukeDedup accepts `--config/-c <file>`, auto-discovers one configuration source, and lets CLI values replace matching config values. Automatic discovery stops at the first valid source in this order:
75
+ See [Discovery and frameworks](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/discovery-and-frameworks.md)
76
+ for configuration detection, monorepo boundaries, module resolution, and incomplete-corpus
77
+ behavior.
63
78
 
64
- ```text
65
- .cuke-dedup.json
66
- .config/cuke-dedup.json
67
- .config/.cuke-dedup.json
68
- cuke-dedup.config.json (legacy compatibility)
69
- package.json#cukeDedup
70
- ```
79
+ ## Configuration
71
80
 
72
- An explicit `--config` path bypasses automatic discovery and is a fatal configuration error when it cannot be read or parsed. Relative config, output, and baseline paths are resolved from the analyzed root. An invalid auto-discovered file emits a warning and falls through to the next source. JavaScript projects may use the `cukeDedup` key in `package.json`; other repositories should prefer `.cuke-dedup.json`.
81
+ CukeDedup reads an explicit `--config` file, an auto-discovered CukeDedup configuration, or
82
+ supported framework configuration. CLI flags always have the highest precedence.
73
83
 
74
- ### Example configuration
84
+ A minimal `.cuke-dedup.json` might be:
75
85
 
76
86
  ```json
77
87
  {
78
88
  "definitions": ["features/steps/**/*.ts"],
79
89
  "features": ["features/**/*.{feature,feature.md}"],
80
90
  "exclude": ["dist/**"],
81
- "excludeDefaults": true,
82
- "includeHidden": false,
83
91
  "threshold": 5,
84
- "requireFeatures": true,
85
- "noMetrics": false,
86
- "reporters": ["terminal", "json", "html", "sarif"],
87
- "output": "reports/cuke-dedup",
88
- "rules": {
89
- "duplicate-matcher": "error",
90
- "duplicate-handler": "error",
91
- "near-duplicate-step": "warning",
92
- "unused-definition": "off"
93
- },
94
- "suppressions": [
95
- {
96
- "rule": "duplicate-handler",
97
- "path": "features/steps/legacy.ts",
98
- "matcher": "the legacy flow is complete",
99
- "reason": "Kept distinct while the legacy flow is retired"
100
- }
101
- ]
92
+ "reporters": ["terminal", "html"]
102
93
  }
103
94
  ```
104
95
 
105
- ### Suppressions
106
-
107
- Every suppression must include a non-empty `reason` and select at least a `path` or `matcher`:
108
-
109
- - When both selectors are present, both must match.
110
- - For a pair finding, a configured `path` must contain every involved definition.
111
- - A matcher selector must select at least one involved definition.
112
-
113
- These rules prevent a directory-scoped exception from hiding a conflict that crosses into maintained code.
114
-
115
- One definition can instead carry an auditable source-local suppression. The directive must be immediately above the registration, select one rule, and include a reason:
116
-
117
- ```ts
118
- // cuke-dedup:ignore duplicate-handler -- retained for an external compatibility contract
119
- Given("the legacy flow completes", legacyHandler);
120
- ```
121
-
122
- Malformed directives are operational errors rather than silently ignored comments. A directive suppresses findings involving its attached definition only.
123
-
124
- ### Framework discovery and corpus boundaries
125
-
126
- When `features` is not explicitly configured, CukeDedup statically reads literal paths from the project's BDD setup: Playwright-BDD's `defineBddConfig`, Cypress `e2e.specPattern` when the Badeball Cucumber preprocessor is installed, and Cucumber.js configuration. It never executes project configuration. Cucumber directories include both `.feature` and `.feature.md`; Playwright-BDD directories follow that framework's `.feature` default. Explicit file or glob paths are preserved, so a project can select names such as `*.spec`. Dynamic paths produce a warning and can be made deterministic by setting `features` in CukeDedup's own configuration.
127
-
128
- Analysis is intentionally scoped to the supplied root: every discovered definition and feature under that root belongs to one comparison corpus. In a monorepo whose packages use independent step registries, run CukeDedup once per package (for example, `cuke-dedup packages/accounts`) instead of treating the monorepo root as one suite. Framework detection selects the appropriate registration patterns but does not silently split the corpus because mixed-framework projects can deliberately share definitions.
129
-
130
- `.feature.md` selects the Gherkin Markdown parser. Other extensions selected by a custom or framework glob are treated as classic Gherkin. This prevents ordinary Markdown files from being scanned while still allowing conventions such as `.spec`.
131
-
132
- ### Exclusions and ignore files
133
-
134
- Configured `exclude` patterns replace custom patterns from lower-precedence layers, while built-in generated-directory exclusions remain independently enabled. An exclusion may name one file, one directory, or a glob.
135
-
136
- - Set `excludeDefaults` to `false` to analyze an explicitly selected workspace under `node_modules`, `target`, or another protected directory.
137
- - Set `includeHidden` to `true` to descend into dot-directories.
138
- - Definitions, features, excludes, reporters, and suppressions replace the corresponding lower-precedence value.
139
- - Scalar values and individual rule severities merge by precedence and rule name.
140
-
141
- Configured `definitions`, `features`, `exclude`, and suppression-path values use `globset` syntax against root-relative paths normalized with `/`. `*` and `?` may cross `/`; use `**` when a recursive directory boundary should be obvious to readers. Character classes such as `[ab]`, brace alternatives such as `{js,ts}`, and backslash escaping are supported. These configuration globs are distinct from `.gitignore` and `.cuke-dedupignore`, which use directory-scoped gitignore semantics and support negation.
142
-
143
- Repositories may also place a `.cuke-dedupignore` file at the analyzed root or in any descendant directory. It uses gitignore syntax: blank lines and `#` comments are ignored, `/` anchors a rule to the ignore file's directory, a trailing `/` selects directories, and `!` negates an earlier matching rule. Nested files apply only to their directory subtree. These rules are applied in addition to `.gitignore`, built-in exclusions, and configured `exclude` patterns. `--no-default-excludes` disables only the built-in generated-directory list; it does not disable either ignore file. Configured `exclude` patterns remain hard exclusions and cannot be negated from `.cuke-dedupignore`.
144
-
145
- Example `.cuke-dedupignore`:
96
+ Use `.cuke-dedupignore` for repository-specific exclusions:
146
97
 
147
98
  ```gitignore
148
- # Generated feature sources
149
99
  features/generated/*
150
100
  **/*.generated.ts
151
-
152
- # Keep one reviewed generated definition
153
101
  !features/generated/reviewed.generated.ts
154
102
  ```
155
103
 
156
- ### CLI overrides and discovery diagnostics
157
-
158
- Equivalent discovery and rule overrides are available from the CLI:
159
-
160
- ```sh
161
- cuke-dedup . \
162
- --config .cuke-dedup.json \
163
- --definitions 'features/steps/**/*.ts' \
164
- --features 'features/**/*.feature' \
165
- --exclude 'generated/legacy.ts' \
166
- --exclude 'vendor' \
167
- --exclude 'dist/**/*.ts' \
168
- --threshold 5 \
169
- --rule duplicate-matcher=warning
170
- ```
171
-
172
- `--exclude` may be repeated or receive comma-separated patterns. Patterns are resolved relative to the analyzed root. The CLI equivalents for the discovery escape hatches are `--no-default-excludes` and `--include-hidden`. Explicit definition globs do not implicitly weaken either safety default.
173
-
174
- Use `--explain-discovery` to print the effective pattern origin, selected parser, matching pattern, and definition inputs without changing report output. Unmatched feature patterns are warnings. If definitions exist but no feature files match, CukeDedup warns and disables `unused-definition` findings rather than presenting an incomplete corpus as proof that every definition is unused. Set `requireFeatures: true` or pass `--require-features` to make that condition an operational failure (exit code `2`).
175
-
176
- Malformed discovered JavaScript or TypeScript remains a fail-closed operational error because partial extraction could make a duplication gate pass incorrectly. Fix the syntax, use a `.tsx` extension for JSX-bearing TypeScript, or narrow `definitions` to the actual step-definition sources.
177
-
178
- Use `--print-config` to serialize the fully merged and validated configuration as JSON and exit without discovery. The output includes the selected CukeDedup configuration source, framework-derived feature-pattern origin, CLI overrides, effective default exclusions, rule severities, and configuration warnings.
179
-
180
- ## Rules
181
-
182
- | Rule | Default | Meaning |
183
- | --- | --- | --- |
184
- | `duplicate-matcher` | Error | Definitions use the same effective matcher. |
185
- | `normalized-matcher` | Error | Matchers become equivalent after normalization. |
186
- | `ambiguous-step` | Error | A concrete feature step matches multiple definitions. |
187
- | `duplicate-handler` | Error | Different matchers have the same alpha-normalized handler. |
188
- | `near-duplicate-step` | Warning | Matcher wording is close and handler structure agrees. |
189
- | `parameterization-candidate` | Warning | Handler structures differ primarily in literal values. |
190
- | `unused-definition` | Warning | No discovered feature step uses the definition. |
191
-
192
- Set a rule to `off`, `warning`, or `error` in configuration. The CLI also accepts `warn` as an alias for `warning`.
193
-
194
- Exact handler equivalence is always reported as `duplicate-handler` when matchers are not equivalent. If the matcher wording is also close, the same retained pair may carry the advisory `near-duplicate-step` finding as additional evidence.
195
-
196
- ## Duplication threshold
197
-
198
- `threshold` controls the percentage of discovered definitions that may participate in active, error-level duplication findings. It accepts a value from `0` through `100` and defaults to `0`.
199
-
200
- ```sh
201
- cuke-dedup . --threshold 5
202
- ```
203
-
204
- The numerator contains unique definition locations involved in `duplicate-matcher`, `normalized-matcher`, `duplicate-handler`, and any `near-duplicate-step` or `parameterization-candidate` rule configured as an error. A definition is counted once even when it participates in several pairs. Suppressed and baselined findings do not count. The denominator is every discovered definition, including definitions without findings.
104
+ See the [configuration reference](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/configuration.md)
105
+ for precedence, every setting, glob semantics, suppressions, and discovery diagnostics.
205
106
 
206
- The threshold passes when the unrounded percentage is less than or equal to the configured value. Active `ambiguous-step` and `unused-definition` errors remain independently fatal because they are correctness and usage policies rather than duplication tolerance. Empty projects have a `0%` duplication rate.
107
+ ## Findings and reports
207
108
 
208
- ## Reports
109
+ CukeDedup detects eight classes of duplication, ambiguity, reuse, and unused definitions. Exact
110
+ collisions default to errors; heuristic findings default to warnings. See the
111
+ [rule reference](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/rules.md) for the
112
+ meaning, default severity, and remediation guidance for every rule.
209
113
 
210
- Choose reporters according to how the result will be consumed:
211
-
212
- | Reporter | Destination | Best for |
213
- | --- | --- | --- |
214
- | `terminal` | stdout | Interactive local use and concise CI logs. |
215
- | `json` | File | Structured integration data. |
216
- | `jsonl` | stdout | Streaming shell and agent workflows. |
217
- | `html` | File | Human review with search, filters, and theme switching. |
218
- | `sarif` | File | Code-scanning platforms such as GitHub. |
219
-
220
- The `jsonl` reporter emits one compact object per finding followed by a final summary object. Because `terminal` and `jsonl` both own stdout, they cannot be selected together. JSONL can be combined with file reporters without contaminating the stream.
221
-
222
- JSON, HTML, and SARIF reports default to `reports/cuke-dedup/` and can be redirected with `--output`. Relative output paths are resolved from the analyzed root, not the shell's current directory. File-only reporter runs print the generated report paths to stdout.
223
-
224
- ### JSON and HTML
225
-
226
- The JSON report uses schema version `1`. It includes relative source spans, severity, suppressions, similarity scores, suggested actions, structured matcher/handler evidence, threshold calculations, input counts, and execution metrics. The self-contained HTML report presents the same result with search, severity and rule filters, a light/dark theme switch, matcher differences, and side-by-side handler snippets.
227
-
228
- Set `noMetrics: true`, pass `--no-metrics`, or use the Action's `no-metrics: true` input to omit timing data when byte-reproducible artifacts matter.
229
-
230
- ### JSON Lines
231
-
232
- JSONL schema version `1` is intended for streaming agent and shell consumption:
233
-
234
- ```sh
235
- cuke-dedup . --reporters jsonl \
236
- | jq -c 'select(.type == "finding" and .active)'
237
- ```
238
-
239
- Each finding record is self-contained and carries a location-independent semantic fingerprint, active/suppressed state, threshold contribution, relative source spans, suggested action, and structured evidence. Free-text fields are capped at 2,000 Unicode characters; `truncatedFields` names every shortened field.
240
-
241
- The final `type: "summary"` record declares `recordCount` and `truncated: false`, and includes aggregate counts, threshold outcome, and execution metrics. Operational warnings and errors remain on stderr, so stdout can be parsed incrementally.
242
-
243
- ### SARIF, safety, and scale
244
-
245
- SARIF 2.1.0 contains active findings, portable relative Unicode-aware locations, stable partial fingerprints, severity, similarity properties, suggested actions, and the threshold outcome for GitHub code scanning or another compatible consumer. Handler snippets are bounded before being embedded. HTML-visible text is escaped, bidirectional and invisible-format controls—including Unicode Tag characters—are removed, and embedded JSON characters that could close a script element are encoded.
246
-
247
- Terminal, JSON, HTML, and SARIF output retain at most 10,000 findings, prioritizing active errors before warnings and clearly reporting truncation. Their summaries still describe the complete analysis. JSONL remains the uncapped finding stream.
248
-
249
- ## Agent Skill
250
-
251
- CukeDedup ships a portable Agent Skill that teaches coding assistants how to run the JSONL reporter, interpret its records, choose safe step-definition remediations, and verify the result without weakening the configured quality gate.
252
-
253
- Install it from a local checkout:
254
-
255
- ```sh
256
- npx skills add ./skills --skill cuke-dedup
257
- ```
258
-
259
- Install it directly from GitHub:
260
-
261
- ```sh
262
- npx skills add figueiredoluiz/cuke-dedup --skill cuke-dedup
263
- ```
114
+ | Reporter | Best for |
115
+ | --- | --- |
116
+ | `terminal` | Interactive use and concise CI logs. |
117
+ | `json` | Structured integrations and complete result metadata. |
118
+ | `jsonl` | Streaming shell and agent workflows. |
119
+ | `html` | Searchable human review with light and dark themes. |
120
+ | `sarif` | GitHub code scanning and compatible platforms. |
264
121
 
265
- The installer can target supported coding agents or install globally; consult `npx skills add --help` for the available agent and scope flags. The skill is delivered from [`skills/cuke-dedup`](https://github.com/figueiredoluiz/cuke-dedup/blob/main/skills/cuke-dedup/SKILL.md), independently of the Cargo and npm packages. Invoke it explicitly as `$cuke-dedup` where supported, or ask the agent to analyze and safely fix duplicate Cucumber step definitions.
122
+ JSON, HTML, and SARIF files default to `reports/cuke-dedup/`. See
123
+ [Reports](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/reports.md) for schemas,
124
+ destinations, reproducibility, truncation signals, and reporter-specific behavior.
266
125
 
267
126
  ## GitHub Action
268
127
 
269
- CukeDedup can run as a native, checksum- and provenance-verified GitHub Action without compiling Rust in the consumer repository:
128
+ The Action downloads a native binary and verifies its checksum and build provenance:
270
129
 
271
130
  ```yaml
272
131
  permissions:
@@ -277,101 +136,81 @@ steps:
277
136
  with:
278
137
  fetch-depth: 0
279
138
  persist-credentials: false
280
- - uses: figueiredoluiz/cuke-dedup@v0.1.1
281
- id: cuke-dedup
139
+ - uses: figueiredoluiz/cuke-dedup@v0.2.0
282
140
  with:
283
141
  path: .
284
142
  threshold: 5
285
- config: .cuke-dedup.json
286
- exclude: |
287
- generated/**
288
- fixtures/vendor/**
289
143
  reporters: terminal,json,html,sarif
290
- changed-since: ${{ github.event.pull_request.base.sha }}
291
- no-metrics: true
292
- - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
293
- if: always()
294
- with:
295
- name: cuke-dedup-reports
296
- path: reports/cuke-dedup/
297
144
  ```
298
145
 
299
- The Action adds JSON internally when needed so its outputs are always available: `exit-code`, `duplicate-rate`, `duplicate-definitions`, `total-definitions`, `json-report`, `html-report`, and `sarif-report`. Omitted threshold, reporter, and output inputs retain the resolved project configuration; explicitly supplied Action inputs override it. Set `reporters: jsonl` when an agent-oriented workflow should receive the stream in the step log; the Action also creates its internal JSON report for outputs. Its optional `baseline` and `fail-on-new` inputs expose the semantic new-finding gate to pull-request workflows. Inputs are passed directly to the native process as an argument array. The `version` input selects one exact compatible release. Downloads fail closed when the archive, adjacent SHA-256 checksum, provenance bundle, or exact archive contents are missing or invalid. Provenance verification uses the GitHub CLI available on GitHub-hosted runners; self-hosted runners must provide `gh` on `PATH`.
146
+ For changed-file checks, baselines, report artifacts, outputs, and hardened pinning, see
147
+ [CI and baselines](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/ci-and-baselines.md).
300
148
 
301
- For security-sensitive workflows, pin CukeDedup to the release tag's complete commit SHA. Exit codes retain the CLI contract; use `continue-on-error` only when a later workflow step intentionally evaluates the `exit-code` output.
302
-
303
- ## Incremental CI adoption
304
-
305
- Only report findings involving files changed from a Git revision while still comparing those files against the complete definition corpus. Paths remain correct when analyzing a repository subdirectory, and untracked files are included:
149
+ ## Exit codes
306
150
 
307
- ```sh
308
- cuke-dedup . --changed-since origin/main
309
- ```
151
+ | Code | Meaning |
152
+ | --- | --- |
153
+ | `0` | The configured quality gate passed. |
154
+ | `1` | The duplication threshold, an error rule, or the new-finding allowance failed. |
155
+ | `2` | Discovery, parsing, configuration, or another operational error prevented a valid run. |
310
156
 
311
- Create or refresh a compact semantic baseline from the complete current analysis:
157
+ Warnings alone do not produce exit code `1`.
312
158
 
313
- ```sh
314
- cuke-dedup . \
315
- --baseline .cuke-dedup-baseline.json \
316
- --update-baseline
317
- ```
159
+ ## Agent Skill
318
160
 
319
- Subsequent runs suppress the recorded multiplicity of each semantic finding. File renames and unrelated line shifts do not make a finding new, while adding another occurrence beyond the recorded count does:
161
+ CukeDedup includes an Agent Skill for safely interpreting and remediating JSONL findings:
320
162
 
321
163
  ```sh
322
- cuke-dedup . --baseline .cuke-dedup-baseline.json --fail-on-new
323
- cuke-dedup . --baseline .cuke-dedup-baseline.json --fail-on-new 3
164
+ npx skills add figueiredoluiz/cuke-dedup --skill cuke-dedup
324
165
  ```
325
166
 
326
- `--fail-on-new` defaults to zero when no count is supplied. `--update-baseline` and `--fail-on-new` require `--baseline`; they cannot be combined. Baseline updates also reject `--changed-since`, preventing a partial scan from erasing accepted findings outside the changed-file set. The sorted, versioned baseline records one semantic fingerprint per line with a multiplicity count for reviewable diffs.
167
+ From a local checkout, use `npx skills add ./skills --skill cuke-dedup`.
327
168
 
328
- Changed-file mode still analyzes the complete discovered corpus so a changed definition can be compared with unchanged definitions. It filters the reported findings to those touching a changed file, while summary definition counts and the duplication-threshold denominator remain the complete corpus.
169
+ The installer can target supported coding agents or install globally. Run
170
+ `npx skills add --help` for agent and scope options.
329
171
 
330
- An empty changed-file set emits a warning so an ignored target cannot look indistinguishable from a clean incremental run. Parse errors in unchanged files do not fail changed-file mode.
172
+ Invoke it as `$cuke-dedup` where supported, or ask the agent to analyze and safely fix duplicate
173
+ Cucumber step definitions. The skill lives in
174
+ [`skills/cuke-dedup`](https://github.com/figueiredoluiz/cuke-dedup/blob/main/skills/cuke-dedup/SKILL.md)
175
+ and is distributed independently from the Cargo and npm packages.
331
176
 
332
- ## Exit codes
177
+ ## Documentation
333
178
 
334
- | Code | Meaning |
335
- | --- | --- |
336
- | `0` | The duplication rate is within the threshold and no independent error rule failed. |
337
- | `1` | The duplication threshold was exceeded, an active non-duplication error rule failed, or the configured new-finding allowance was exceeded. |
338
- | `2` | Discovery, parsing, configuration, or another operational failure. |
339
-
340
- Warnings do not produce exit code `1`.
179
+ - [Configuration](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/configuration.md)
180
+ - [Discovery and frameworks](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/discovery-and-frameworks.md)
181
+ - [Rules and duplication threshold](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/rules.md)
182
+ - [Reports](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/reports.md)
183
+ - [CI and baselines](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/ci-and-baselines.md)
184
+ - [Safety and limitations](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/safety-and-limitations.md)
341
185
 
342
- ## Compatibility and limitations
186
+ ## Compatibility
343
187
 
344
- - The minimum supported Rust version is 1.90. Node.js 20 and 24 are tested for the npm launcher.
345
- - Source adapters currently cover JavaScript and TypeScript, including JSX and common module variants. Unsupported languages are rejected rather than guessed.
346
- - CukeDedup uses static analysis and never executes framework configuration or test code. Dynamic configuration may require explicit `features` or `definitions` patterns.
347
- - Named handlers declared in the same source file are resolved to their bodies. Imported or unresolved handler references remain available for matcher and usage rules but are not compared by identifier text.
348
- - Malformed JavaScript or TypeScript fails closed. Unsupported regular-expression constructs emit a warning; malformed matcher escapes are operational errors.
349
- - One analysis root is one comparison corpus. Run independent monorepo packages separately when their step registries are unrelated.
350
- - Exact matcher and handler equivalence groups produce a linear spanning set of findings. Fuzzy structural comparisons are bounded at two million candidates and fail closed with exit code `2`; split independent suites or narrow the root if that limit is reached.
351
- - Before version 1.0, configuration and machine-report schemas may evolve between minor releases. Schema changes will be explicit and versioned.
188
+ - Rust 1.90 or newer is required. The npm launcher is tested on Node.js 20 and 24.
189
+ - Definition extraction currently supports JavaScript and TypeScript, including JSX and common
190
+ module variants.
191
+ - Static analysis cannot safely resolve every dynamic configuration, matcher, wrapper, or imported
192
+ handler. CukeDedup reports incomplete analysis instead of treating missing evidence as clean.
193
+ - Before version 1.0, configuration and machine-report schemas may change between minor releases.
352
194
 
353
- CukeDedup is an independent project. It is not affiliated with or endorsed by the Cucumber project or its maintainers.
195
+ See [Safety and limitations](https://github.com/figueiredoluiz/cuke-dedup/blob/main/docs/safety-and-limitations.md)
196
+ for the complete compatibility contract and resource limits.
354
197
 
355
- ## Support
198
+ ## Contributing and support
356
199
 
357
- Use the repository issue templates for reproducible bugs and focused feature requests. Include a minimal sanitized fixture and remove application-specific names, credentials, and source code. Report vulnerabilities privately according to [SECURITY.md](SECURITY.md).
200
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development commands, corpus guidance, and pull-request
201
+ expectations. Use the issue templates for reproducible bugs and focused feature requests. Report
202
+ vulnerabilities privately according to [SECURITY.md](SECURITY.md). Bug reports should include a
203
+ minimal sanitized fixture with application-specific names, credentials, and source removed.
358
204
 
359
- ## Development
205
+ CukeDedup is an independent project. It is not affiliated with or endorsed by the Cucumber project
206
+ or its maintainers.
360
207
 
361
- The minimum supported Rust version is 1.90. The repository pins and tests that toolchain and also runs its quality checks on current stable Rust.
208
+ ## Releases
362
209
 
363
- Enable the tracked pre-commit hook once per clone and run the same compliance gate directly when needed:
364
-
365
- ```sh
366
- git config core.hooksPath .githooks
367
- scripts/check/check.sh
368
- ```
369
-
370
- See [CONTRIBUTING.md](CONTRIBUTING.md) for prerequisites, focused commands, corpus and benchmark guidance, pull-request expectations, and the project decision process. Public Rust items carry rustdoc, and CI treats missing API documentation as an error.
371
-
372
- ## Releases and verification
373
-
374
- Release notes and native archives are published on [GitHub Releases](https://github.com/figueiredoluiz/cuke-dedup/releases). Each archive has an adjacent SHA-256 checksum, a keyless Sigstore bundle, and GitHub build provenance. Verify provenance with:
210
+ Release archives include SHA-256 checksums, keyless Sigstore bundles, and GitHub build provenance.
211
+ See [GitHub Releases](https://github.com/figueiredoluiz/cuke-dedup/releases) and
212
+ [CHANGELOG.md](CHANGELOG.md) for published versions and release notes. Runtime dependency licenses
213
+ are listed in [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md).
375
214
 
376
215
  ```sh
377
216
  gh attestation verify <archive> \
@@ -379,8 +218,6 @@ gh attestation verify <archive> \
379
218
  --signer-workflow figueiredoluiz/cuke-dedup/.github/workflows/release.yml
380
219
  ```
381
220
 
382
- Cargo, npm, the Git tag, and the GitHub release use the same version. See [CHANGELOG.md](CHANGELOG.md) for release history and [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md) for the runtime dependency license inventory.
383
-
384
221
  ## License
385
222
 
386
223
  [MIT](LICENSE)