[Copilot speaking]
Review findings for pull request #113
This issue contains the approved findings from the review of #113 at source commit 3edf357. The report continues in comments because the complete rendered Markdown exceeds GitHub's per-body limit.
Title: Align the design comment-directive spelling with the supported syntax
[Copilot speaking]
The design says the attribute accepts exactly the text accepted by an equivalent // gamma: comment directive. The supported and documented comment form is instead an attribute-shaped comment such as // #[gamma::test_timeout_multiplier(...)]; the shorthand-looking // gamma: form is not the directive syntax. Because this paragraph defines the user-visible accepted syntax and emphasizes character-for-character equivalence, the backticked spelling reads as a contractual example rather than an informal channel name.
Location: crates/cargo-gamma-attrs-impl/docs/DESIGN.md:21-24
Reproducible reasoning: The public attribute documentation's “Relationship to the comment form” section consistently shows // #[gamma::...]. The cross-channel agreement test also constructs // #[gamma::test_timeout_multiplier({arguments})] and describes the two channels as differing only by the leading comment characters. Separately, the suppression regression tests explicitly treat shorthand without the attribute brackets as unsupported. Reading those sources together shows that // gamma: does not identify a valid literal directive form, while this design paragraph presents it in code formatting immediately before claiming exact textual equivalence.
Consequence: A reader can copy or preserve the shorthand-looking form and believe Gamma will apply a timeout override, while the tool ignores or rejects that text instead of applying the documented contract.
Recommended action: Make the design wording and accepted syntax agree. If // gamma: is only a label for the comment channel, replace the code-form spelling with “comment-form directive” or show a supported representative such as // #[gamma::test_timeout_multiplier(...)]. If the shorthand is intended as literal syntax, implement it in the comment parser and cover it in the cross-channel agreement tests before claiming exact equivalence.
References:
- crates/cargo-gamma-attrs/src/lib.rs, “Relationship to the comment form”
- crates/cargo-gamma-lib/tests/agreement.rs,
the_two_channels_agree_on_timeout_multiplier_arguments
Impacted locations:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:21-24
Reviewed in: #113
Title: Document or separate the workspace-wide lint policy change
[Copilot speaking]
The pull request is titled and described as an end-to-end hardening of cargo-gamma, but Cargo.toml also changes the lint policy inherited across the entire workspace: it enables empty_structs_with_brackets and too_long_first_doc_paragraph, and globally allows literal_string_with_formatting_args. This repository-wide policy change is not identified in the pull request description.
Location: Cargo.toml:211-219
Reproducible reasoning: The new entries are under [workspace.lints], so their scope is every workspace member that inherits the workspace lint table rather than only the cargo-gamma crates. The pull request description lists mutation discovery, process containment, runtime census, reporting, CLI diagnostics, and contract coverage, but does not mention changing static-verification policy for unrelated crates. Because both the title and description frame the work around cargo-gamma, a reviewer has no basis to tell whether this workspace-wide change is an intentional part of the mission or unrelated work included accidentally.
Consequence: Maintainers can merge the change believing they are reviewing only cargo-gamma hardening while also changing which constructs produce warnings or are permitted by default in future work across unrelated workspace crates.
Recommended action: If the lint policy change is intentional, add it to the pull request's motivation and substantive-change summary and explain why it belongs with the cargo-gamma work. If it is independent, move it to a separate pull request; that provides isolated policy review at the cost of repeating the small amount of validation associated with these manifest entries.
Impacted locations:
Reviewed in: #113
Title: Define the attribute vocabulary before stating parser boundaries
[Copilot speaking]
The added boundary rules introduce the specialized terms "stated value", "comment directive", and "positional timeout multiplier", then refer to selectors and the reason and tag arguments, without defining that vocabulary or directing the reader to its definitions before use. The sibling public README has dedicated "The vocabulary" and "Stating a value" sections, but this design document does not establish that relationship.
Location: crates/cargo-gamma-attrs-impl/docs/DESIGN.md:17-28
Reproducible reasoning: The Purpose section only explains that this crate parses and validates inert attributes. A reader then reaches the new rules and must already know that a stated value is the expression supplied through #[gamma::value(...)], that the // gamma: form is a source-comment equivalent of an attribute directive, and that a timeout multiplier is one argument accepted alongside selector, reason, and tag arguments. Those are domain-specific concepts rather than ordinary Rust terminology. Their definitions elsewhere in the repository confirm that they require explanation, but the design document neither defines them nor links to those definitions before making contractual claims about their ordering and rejection behavior. A linear reading therefore cannot recover the precise meaning of the new rules from this document.
Consequence: A maintainer can misread "stated value" as any literal supplied to an attribute, or infer that a "positional timeout multiplier" has position-dependent semantics despite the following sentence denying positional meaning. That ambiguity makes future parser, diagnostic, and documentation changes more likely to use the terms inconsistently.
Recommended action: Add a short terminology paragraph before the boundary rules, or an explicit preceding link to the public vocabulary sections, defining the stated-value attribute, comment directive, timeout multiplier, selectors, and named reason and tag arguments. Then express the boundary rules with those established terms.
References:
- crates/cargo-gamma-attrs/README.md, "The vocabulary"
- crates/cargo-gamma-attrs/README.md, "Stating a value"
Impacted locations:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:17-28
Reviewed in: #113
Title: Align empty-body eligibility with the documented semantic rationale
[Copilot speaking]
The new empty-body documentation and diagnostic claim that substituting any stated expression for an empty function body produces an identical program. That is false for valid unit-compatible expressions with observable behavior, such as panic!() or a side-effecting call returning (). The implementation rejects these annotations because collection excludes empty-body sites before consulting the stated value, so the user-facing rationale is stronger than the behavior the code establishes.
Location: crates/cargo-gamma-engine/src/ops/collect/stated.rs:54-56
Reproducible reasoning: An empty body evaluates to (), but Rust also accepts expressions that return () after observable effects and diverging expressions that coerce to the function's return type. Replacing {} with either kind can change behavior. The collector's early exclusion of an empty body is therefore the reproducible reason no stated-value mutant is produced; behavioral equivalence of every accepted expression is not. The incorrect semantic claim is repeated across implementation documentation, diagnostics, and test rationale.
Consequence: Users can be told that an observable mutant is inherently identical to the original program, while maintainers may preserve the exclusion for a false semantic reason and overlook that it is an eligibility policy or a possible future extension point.
Recommended action: Choose one contract and make collection, attribute validation, public documentation, diagnostics, and tests agree. If empty bodies remain ineligible, describe that as an eligibility rule applied before the stated expression is read, without claiming that every unit-compatible or diverging expression is behaviorally identical. If those expressions should be eligible, change collection accordingly and cover side-effecting and diverging examples. Preserve a semantic-equivalence claim only if the accepted syntax actually enforces it.
References:
- crates/cargo-gamma-attrs-impl/docs/DESIGN.md
- crates/cargo-gamma-attrs/src/lib.rs
- crates/cargo-gamma/docs/MUTATORS.md, "Stating the value yourself"
- crates/cargo-gamma-engine/src/ops/collect/collector.rs:482-513
Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:70-75
crates/cargo-gamma-attrs-impl/src/implementation.rs:344-347
crates/cargo-gamma-engine/src/ops/collect/stated.rs:54-56
crates/cargo-gamma-engine/src/ops/collect/stated.rs:150-154
crates/cargo-gamma-engine/src/ops/collect/stated.rs:457-459
Reviewed in: #113
Title: Validate timeout-multiplier arguments without materializing and rebuilding the whole list
[Copilot speaking]
The new timeout-multiplier path clones the attribute stream, splits every top-level argument into an allocated Vec<Vec<TokenTree>>, and then clones every non-positional argument into a second TokenStream. validate_shape immediately materializes that rebuilt stream into another token vector. This duplicates the token storage before validation even begins, although only one argument needs to be classified at a time.
Location: crates/cargo-gamma-attrs-impl/src/implementation.rs:598
Reproducible reasoning: arguments_of allocates an outer vector and a separate vector for each nonempty comma-delimited argument. validate_timeout_multiplier retains all of those vectors while it creates rest by cloning the non-positional tokens and synthesizing comma tokens. It then passes rest to validate_shape, whose existing random-access parser collects the stream into its own vector. The design contract requires top-level splitting and per-argument classification, but it does not require retaining all segments simultaneously or reconstructing their original stream. Processing each completed top-level segment as it is encountered, while carrying the stated/strict-reading state between segments, preserves the accepted syntax and duplicate detection while avoiding the new outer collection and reconstruction allocations. This path runs inside rustc for every gamma, timeout_multiplier, and test_timeout_multiplier macro expansion, so the added work scales with annotated items.
Consequence: A crate with many timeout-annotated tests performs multiple avoidable heap allocations and token clones for every macro expansion, increasing compile-time allocation pressure and latency in rustc.
Recommended action: Fold top-level splitting, positional classification, and shape validation into a streaming pass. Reuse one segment buffer (or bounded-lookahead cursor), carry multiplier-seen state across segments, and validate non-positional segments directly instead of retaining Vec<Vec<TokenTree>> and rebuilding rest. Preserve the syntax contract documented in the crate design while eliminating the duplicate token materialization.
References:
- crates/cargo-gamma-attrs-impl/docs/DESIGN.md, Boundaries
Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:540-643
Reviewed in: #113
Title: Centralize newly referenced items in module-level imports
[Copilot speaking]
New code across the reviewed modules introduces dependencies inside declarations and function bodies by spelling qualified paths inline, placing use declarations inside functions or below module items, importing production siblings through super::, or bypassing a crate’s public composition path. These references do not require path-based disambiguation and can be represented consistently in each file or module import block.
Location: crates/cargo-gamma-attrs-impl/src/implementation.rs:633
Reproducible reasoning: The affected instances share one maintainability issue: newly referenced types, traits, functions, macros, constants, and modules were not incorporated into the owning module’s import surface. Module-level imports make dependencies visible in one place and avoid coupling production modules to nesting depth. Where an item is re-exported by the crate that owns the composition boundary, importing through that public crate path also keeps callers independent of the underlying dependency. Existing cfg gates and aliases can remain on the imports where platform availability or name collisions require them.
Consequence: Dependencies are scattered through signatures, declarations, and function bodies instead of being visible at module boundaries. This makes the affected modules harder to scan and creates many separate path spellings to update when items move or imports are reorganized.
Recommended action: Move the affected references into the owning file or module import block and use the imported names at call sites. In production modules, prefer crate-root paths over super::; across crates, prefer the composing crate’s public re-export over its private dependency path. Preserve necessary cfg attributes and aliases, and keep qualified call-site paths only where they genuinely disambiguate names.
Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:633
crates/cargo-gamma-attrs/tests/consumer.rs:75
crates/cargo-gamma-attrs/tests/diagnostics.rs:63-111
crates/cargo-gamma-engine/src/error.rs:255
crates/cargo-gamma-engine/src/ops/collect/collector.rs:1334
crates/cargo-gamma-engine/src/ops/collect/defaults.rs:261-280
crates/cargo-gamma-engine/src/ops/collect/defaults.rs:968-980
crates/cargo-gamma-engine/src/ops/collect/tests.rs:115
crates/cargo-gamma-engine/src/ops/collect/tests.rs:2777
crates/cargo-gamma-lib/src/ci/sarif.rs:187-195
crates/cargo-gamma-engine/src/text.rs:212
crates/cargo-gamma-lib/src/commands/list.rs:44-45
crates/cargo-gamma-lib/src/commands/run.rs:800
crates/cargo-gamma-lib/src/config.rs:1033-1051
crates/cargo-gamma-lib/src/config.rs:1098-1105
crates/cargo-gamma-lib/src/config.rs:1140-1146
crates/cargo-gamma-lib/src/config.rs:1195-1200
crates/cargo-gamma-lib/src/config.rs:1275
crates/cargo-gamma-lib/src/discover/record.rs:501-505
crates/cargo-gamma-lib/src/error.rs:136-142
crates/cargo-gamma-lib/src/exec/census.rs:990-1001
crates/cargo-gamma-lib/src/exec/census.rs:1524
crates/cargo-gamma-lib/src/exec/measure.rs:493
crates/cargo-gamma-lib/src/exec/sweep.rs:119
crates/cargo-gamma-lib/src/exec/sweep.rs:1531-1571
crates/cargo-gamma-lib/src/exec/workspace.rs:1119
crates/cargo-gamma-lib/src/exec/workspace.rs:1277
crates/cargo-gamma-lib/src/exec/workspace.rs:1762
crates/cargo-gamma-lib/src/exec/workspace.rs:1769
crates/cargo-gamma-lib/src/exec/workspace.rs:1805
crates/cargo-gamma-lib/src/exec/workspace.rs:1832
crates/cargo-gamma-lib/src/merge/union.rs:447
crates/cargo-gamma-lib/tests/agreement.rs:239
crates/cargo-gamma-lib/tests/agreement.rs:308-311
crates/cargo-gamma-lib/tests/session.rs:389
crates/cargo-gamma-lib/tests/session.rs:413
crates/cargo-gamma-lib/tests/units.rs:143-146
crates/cargo-gamma-process/src/testing.rs:99
crates/cargo-gamma-rt/src/runtime.rs:675-701
crates/cargo-gamma-rt/src/runtime.rs:1030-1055
crates/cargo-gamma-rt/src/runtime.rs:2968-3035
crates/cargo-gamma-unsafe/src/cgroup.rs:83
crates/cargo-gamma-unsafe/src/cgroup.rs:108
crates/cargo-gamma-unsafe/src/cgroup.rs:1604
crates/cargo-gamma-unsafe/src/job.rs:798
crates/cargo-gamma-unsafe/src/job.rs:817-821
crates/cargo-gamma-unsafe/src/native_faults.rs:126
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:12-13
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:576
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:606
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:642
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:673
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:41
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:44
crates/cargo-gamma-lib/src/exec/sync.rs:22
crates/cargo-gamma-process/src/process_tree.rs:18
crates/cargo-gamma-process/src/process_tree.rs:68
crates/cargo-gamma-process/src/process_tree.rs:1058
crates/cargo-gamma-process/src/process_tree.rs:2069
crates/cargo-gamma-process/src/process_tree.rs:2410
crates/cargo-gamma-process/src/process_tree.rs:2526
crates/cargo-gamma-rt/src/runtime.rs:2835-2882
Reviewed in: #113
Title: Document the value rejection as a contract, not a guard implementation
[Copilot speaking]
The newly expanded public rustdoc correctly states that #[gamma::value] is rejected on const fn and empty bodies, but it explains that rule through the collector's reachability and the current run-time guard insertion mechanism. Those details belong to the attribute implementation; the user-facing contract is the accepted placement and diagnostic behavior.
Location: crates/cargo-gamma-attrs/src/lib.rs:435-439
Reproducible reasoning: Lines 435-439 describe collection reaching a value and a mutant being spliced behind a run-time guard. crates/cargo-gamma-attrs/docs/DESIGN.md defines this crate as the user-facing attribute namespace and identifies supported syntax and diagnostics as its public contract, while crates/cargo-gamma-attrs-impl/docs/DESIGN.md owns parsing and validation. The rejection can remain stable if collection or instrumentation is later reorganized, so publishing the current mechanism in the macro API documentation turns an implementation choice into an apparent promise.
Consequence: A future instrumentation or collection refactor can preserve the rejection contract while making this rustdoc false, and downstream readers may incorrectly rely on guard placement or collector behavior as stable API behavior.
Recommended action: Keep the public rustdoc focused on the requirement that value is rejected on declarations, const fn, and empty bodies. Move the collector and run-time guard explanation to the implementation crate's design documentation or an implementation comment.
References:
- crates/cargo-gamma-attrs/docs/DESIGN.md
- crates/cargo-gamma-attrs-impl/docs/DESIGN.md
Impacted locations:
crates/cargo-gamma-attrs/src/lib.rs:435-439
Reviewed in: #113
Title: Describe the diagnostic tests as fragment checks rather than exact-output contracts
[Copilot speaking]
The new diagnostics test module says it pins each macro's exact diagnostic, but every test only checks that stderr contains the macro prefix and one message fragment. It does not pin the complete diagnostic, spans, severity, formatting, or additional emitted errors.
Location: crates/cargo-gamma-attrs/tests/diagnostics.rs:5-14
Reproducible reasoning: The module documentation at lines 5-14 claims exactness and then explains that validation is performed through substrings. The helper returns the complete stderr, but the assertions at lines 131-192 use only contains. Therefore many changes to the actual diagnostic output can pass while the documentation tells maintainers that the exact output is protected.
Consequence: Maintainers can rely on a stronger test contract than exists and overlook regressions in diagnostic shape because the suite remains green despite the documented exact-output guarantee.
Recommended action: Rename and document these as checks for diagnostic identity and the essential reason. If byte-for-byte diagnostic stability is genuinely required, use a normalized golden comparison instead, recognizing that exact compiler stderr is more brittle across supported Rust versions.
Impacted locations:
crates/cargo-gamma-attrs/tests/diagnostics.rs:5-14
crates/cargo-gamma-attrs/tests/diagnostics.rs:131-192
Reviewed in: #113
Title: Make timeout-multiplier example reasons match the demonstrated code
[Copilot speaking]
The two newly added successful examples attach operational reasons that their functions do not demonstrate. digest accepts an arbitrary byte slice and computes a sum, but its reason says it hashes a megabyte. accumulate performs an ordinary conversion and u64 addition, but its reason says widening arithmetic is slow. Because these examples teach how to use auditable reason metadata, the reasons should remain verifiable from the scenario they describe.
Location: crates/cargo-gamma-attrs/src/lib.rs:564-573
Reproducible reasoning: Reading the example bodies reproduces the mismatch directly: digest has no input-size constraint and invokes no hash operation, while accumulate only folds bytes into a u64 total. The examples therefore demonstrate the attribute syntax with explanations unrelated to the shown workload. The same syntax can be taught without this shortcut by choosing scenario names, bodies, and reasons that describe one another accurately.
Consequence: Consumers copying the examples may treat reason as placeholder prose rather than durable justification for a timeout override, leaving future reviewers unable to determine why a multiplier is needed or whether it is still appropriate.
Recommended action: Revise these examples so each function and reason describe a coherent, realistic workload while preserving the positional-order and trailing-comma forms being demonstrated. For example, either show an operation and input contract that genuinely justify the multiplier, or use reasons that accurately describe the simple accumulation bodies.
Impacted locations:
crates/cargo-gamma-attrs/src/lib.rs:564-573
Reviewed in: #113
Title: Select the proc-macro artifact without filesystem timestamps
[Copilot speaking]
gamma_artifact scans the target and host dependency directories for hashed dynamic libraries whose filenames have a gamma prefix, sorts the candidates by filesystem modification time, and silently passes the newest candidate to rustc. A Cargo target directory can retain matching artifacts from other source revisions, feature combinations, compiler versions, checkouts, or concurrent Cargo activity, so the newest file is not necessarily the artifact Cargo selected for this test run.
Location: crates/cargo-gamma-attrs/tests/diagnostics.rs:80-82
Reproducible reasoning: Cargo artifact hashes distinguish builds, but this lookup does not recover the hash of the dependency used by the current integration test. Cargo can reuse an already-valid current artifact without updating its modification time, while an unrelated artifact can have a later timestamp. A file's modification time records when it was written, not whether it belongs to the running test binary's build graph. Sorting by metadata.modified() and taking the last candidate therefore makes fixture compilation depend on filesystem time and target-directory history rather than the reviewed source, without documenting an invariant that makes multiple candidates safe.
Consequence: After switching branches or build configurations that share a target directory, the diagnostic tests can compile malformed fixtures against stale or unrelated proc-macro code. This can hide a regression in the current implementation or cause branch-history-dependent failures while leaving maintainers without explicit assumptions for recognizing or safely changing the selection behavior.
Recommended action: Arrange for Cargo to identify or load the exact proc-macro artifact for the current build, such as by compiling fixtures through a dedicated Cargo or trybuild-style crate with a path dependency, or by consuming Cargo's structured build output to obtain the artifact path. Cache that deterministic path for the test process instead of rescanning and ranking target contents by time. If the recency heuristic must remain, document the assumptions that make it valid and reject ambiguous candidate sets rather than silently selecting one.
References:
- crates/cargo-gamma-lib/tests/instrumented_compiles.rs (the
attrs_crate rationale explains why selecting a Cargo proc-macro artifact from a direct rustc invocation is fragile)
Impacted locations:
crates/cargo-gamma-attrs/tests/diagnostics.rs:34-82
crates/cargo-gamma-attrs/tests/diagnostics.rs:34-80
Reviewed in: #113
Title: Exclude the new integration tests from the published crate
[Copilot speaking]
The change adds consumer.rs and diagnostics.rs as development-only integration tests, but cargo-gamma-attrs/Cargo.toml has no include or exclude package-file policy. Cargo therefore includes these tracked test sources in the crate archive even though downstream users do not compile or need them.
Location: crates/cargo-gamma-attrs/tests/consumer.rs
Reproducible reasoning: Both added files live under crates/cargo-gamma-attrs/tests/ and exist solely to validate this repository's proc-macro implementation. Cargo's default packaging file selection includes tracked test sources unless the manifest narrows the package contents. The package manifest currently declares neither an allowlist nor an exclusion for tests/**, so this diff directly expands the published archive with development-only source.
Consequence: Every release of cargo-gamma-attrs would distribute the external-consumer fixtures and the compiler-spawning diagnostic harness to users, increasing the package payload and exposing test infrastructure as published content without any runtime or documentation benefit.
Recommended action: Add an explicit package include list containing only the files required to compile and document the proc-macro crate, or at minimum exclude tests/**. An allowlist prevents future development artifacts from entering the archive, at the cost of updating it when a genuinely required package file is added.
References:
- crates/cargo-gamma-attrs/Cargo.toml
Impacted locations:
crates/cargo-gamma-attrs/tests/consumer.rs:1-85
crates/cargo-gamma-attrs/tests/diagnostics.rs:1-193
Reviewed in: #113
Title: Condense overlong Rust documentation summaries
[Copilot speaking]
New and rewritten Rust item and module documentation across the affected crates and tests uses opening paragraphs whose rendered visible text exceeds the 80-character summary limit. Wrapping the source across multiple documentation-comment lines does not create a paragraph break in rustdoc, so the affected comments still render as overlong summaries.
Location: crates/cargo-gamma-engine/src/cfg.rs:160-161
Reproducible reasoning: Every source finding identifies the same root cause: detailed or multi-clause prose remains in the uninterrupted first documentation paragraph, making its rendered summary longer than the required limit. The findings differ only in the affected items and files. A blank documentation line is required to separate a concise summary from the additional explanation.
Consequence: The generated documentation wraps these opening summaries instead of presenting the concise, consistently scannable first line required by the Rust documentation convention.
Recommended action: Rewrite each affected first paragraph to at most 80 visible rendered characters. Where the removed detail remains useful, retain it in a separate following paragraph after a blank documentation line, measuring rendered text rather than Markdown source width.
Impacted locations:
crates/cargo-gamma-engine/src/cfg.rs:160-161
crates/cargo-gamma-engine/src/model/identity.rs:145-152
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:279-280
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:298-299
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:306-307
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:327-328
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:432-433
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:98-99
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:117-118
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:125-126
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:145-146
crates/cargo-gamma-engine/src/parse/comment.rs:33
crates/cargo-gamma-engine/src/schema.rs:246
crates/cargo-gamma-engine/src/text.rs:128-129
crates/cargo-gamma-engine/src/text.rs:152
crates/cargo-gamma-lib/src/ci/sarif.rs:24-25
crates/cargo-gamma-lib/src/ci/sarif.rs:200
crates/cargo-gamma-lib/src/discover/survey.rs:1030
crates/cargo-gamma-lib/src/discover/survey.rs:1039
crates/cargo-gamma-lib/src/exec/copy.rs:62
crates/cargo-gamma-lib/src/exec/copy.rs:123
crates/cargo-gamma-lib/src/exec/sweep.rs:827
crates/cargo-gamma-lib/src/exec/workspace.rs:743
crates/cargo-gamma-lib/src/parse.rs:9
crates/cargo-gamma-lib/src/report/progress.rs:357
crates/cargo-gamma-lib/tests/agreement.rs:6-7
crates/cargo-gamma-lib/tests/agreement.rs:143-144
crates/cargo-gamma-lib/tests/agreement.rs:182-183
crates/cargo-gamma-lib/tests/agreement.rs:249-250
crates/cargo-gamma-lib/tests/agreement.rs:293-294
crates/cargo-gamma-lib/tests/agreement.rs:305-306
crates/cargo-gamma-lib/tests/agreement.rs:316-319
crates/cargo-gamma-lib/tests/docs.rs:269
crates/cargo-gamma-lib/tests/docs.rs:582
crates/cargo-gamma-lib/tests/docs.rs:596
crates/cargo-gamma-lib/tests/docs.rs:720
crates/cargo-gamma-lib/tests/sarif_contract.rs:36-37
crates/cargo-gamma-lib/tests/sarif_contract.rs:79
crates/cargo-gamma-process/src/process_tree.rs:38
crates/cargo-gamma-process/src/process_tree.rs:223
crates/cargo-gamma-process/src/process_tree.rs:279
crates/cargo-gamma-process/src/process_tree.rs:289
crates/cargo-gamma-rt/src/runtime.rs:585-613
crates/cargo-gamma-rt/src/runtime.rs:670-718
crates/cargo-gamma-unsafe/src/cgroup.rs:84
crates/cargo-gamma-unsafe/src/cgroup.rs:109
crates/cargo-gamma-unsafe/src/cgroup.rs:262
crates/cargo-gamma-unsafe/src/cgroup.rs:276
crates/cargo-gamma-unsafe/src/cgroup.rs:563
crates/cargo-gamma-unsafe/src/cgroup.rs:592
crates/cargo-gamma-unsafe/src/cgroup.rs:778
crates/cargo-gamma-unsafe/src/cgroup.rs:899
crates/cargo-gamma-unsafe/src/cgroup.rs:1982
crates/cargo-gamma-unsafe/src/job.rs:152-164
crates/cargo-gamma-unsafe/src/platform_error.rs:73-75
crates/cargo-gamma/src/real_host.rs:47-53
crates/cargo-gamma/tests/binary.rs:22
crates/cargo-gamma/tests/binary.rs:30
crates/cargo-gamma/tests/binary.rs:111
crates/cargo-gamma-rt/src/runtime.rs:259-271
crates/cargo-gamma-rt/src/runtime.rs:297-316
crates/cargo-gamma-rt/src/runtime.rs:368-400
Reviewed in: #113
Title: Keep all Error implementation blocks together
[Copilot speaking]
The new Parts type is placed between Error's inherent implementation and its Display, StdError, and From<io::Error> implementations. This splits the blocks belonging to the primary error type with a different type definition.
Location: crates/cargo-gamma-engine/src/error.rs:95-110
Reproducible reasoning: The code-layout guidance requires different blocks associated with the same type to stay together. In this file, impl Error ends before Parts, but the subsequent impl Display for Error, impl StdError for Error, and impl From<io::Error> for Error remain below Parts. A reader following Error therefore has to cross the unrelated Parts definition before reaching the rest of Error's behavior.
Consequence: Future maintenance of the engine error can overlook one of its separated trait implementations, and readers cannot inspect the complete behavior of the primary type as one contiguous unit.
Recommended action: Move Parts below the Display, StdError, and From<io::Error> implementations so every block associated with Error is contiguous, while keeping the Parts definition and its own future implementations together.
Impacted locations:
crates/cargo-gamma-engine/src/error.rs:95-136
Reviewed in: #113
Title: Define the discovery pre-pass terminology before using it
[Copilot speaking]
The new design boundary introduces several implementation-specific terms—“pre-pass”, “stated-value audit”, “numeric/import indexes”, and “fused entry point”—without defining them or identifying the fused API. The preceding Purpose and Boundaries text describes mutation discovery generally, but it does not give a reader enough context to distinguish these passes or understand how they relate.
Location: crates/cargo-gamma-engine/docs/DESIGN.md:23-30
Reproducible reasoning: A reader encounters this vocabulary for the first time in lines 23–30. The paragraph then contrasts the unnamed “fused entry point” with check_stated, but neither entry point is introduced and the role of the indexes is not explained. Understanding the boundary therefore requires finding and reverse-engineering implementation modules outside the design document, contrary to the requirement that specialized terms be defined before use.
Consequence: A reader can miss that the configuration-aware combined operation and the standalone stated-value checker intentionally inspect different regions, or may mistake internal pass names for previously established design concepts.
Recommended action: Introduce these concepts in plain language before this boundary, or rewrite the boundary to define each pass as it is named. Identify and link the combined API if “fused entry point” means check_stated_and_collect_with, and briefly state what evidence the numeric/import indexes provide.
Impacted locations:
crates/cargo-gamma-engine/docs/DESIGN.md:23-30
Reviewed in: #113
Title: Describe MutantId compatibility at the actual serialization boundary
[Copilot speaking]
The new rustdoc attributes artifact compatibility to two different mechanisms more broadly than the implementation supports. The type documentation says Serde transparency means every plan, record, and report written by an earlier version still reads unchanged, although transparency only preserves this field's string-shaped wire representation and cannot guarantee compatibility of the surrounding artifact schema. The MutantId::new documentation then says accepting arbitrary text prevents future-version records from failing to read, but derived transparent deserialization constructs the wrapped CompactString directly and does not call new.
Location: crates/cargo-gamma-engine/src/model/identity.rs:31-38
Reproducible reasoning: The type derives Deserialize with #[serde(transparent)], so Serde deserializes the single CompactString field without routing the value through the inherent constructor. Consequently, validation added or omitted in MutantId::new has no effect on reading persisted records. Transparency also establishes only that MutantId itself has the same serialized representation as its inner string; successful deserialization of a complete plan, record, or report still depends on every other field and schema rule in that artifact. The current wording therefore promises artifact-wide backward compatibility and explains constructor behavior using a path the implementation does not take.
Consequence: A maintainer can rely on the comment as evidence that arbitrary older or future artifacts are supported, or preserve the constructor's permissive behavior to protect deserialization, even though neither conclusion follows from this code. That can turn later schema or validation work into an accidental compatibility promise and obscure where persisted-input compatibility is actually enforced.
Recommended action: Narrow the type documentation to the contractual fact that MutantId remains serialized as its underlying string. Document new only as constructing an opaque identity from already rendered text and state the actual reason it accepts arbitrary text, without tying that choice to Serde record reads. Put any whole-artifact backward- or forward-compatibility guarantee on the artifact formats that implement and test it.
Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:23-38
Reviewed in: #113
Title: Describe the identity contract without repeating its numeric version
[Copilot speaking]
The documentation for MUTANT_ID_VERSION names the newly assigned numeric version immediately above the constant that stores the same value. The useful part of the comment is the normalization change and its compatibility effect; repeating the numeric literal creates a second copy that must be kept synchronized.
Location: crates/cargo-gamma-engine/src/model/identity.rs:185-191
Reproducible reasoning: The constant declaration is the authoritative version value. Its new documentation says Version 5 while the declaration says MUTANT_ID_VERSION: u32 = 5. If a later identity change increments only the constant or only the prose, readers receive conflicting information. The surrounding explanation can remain independently useful by describing the current normalization contract and, if historical compatibility needs to be retained, referring to the preceding contract without restating the current literal.
Consequence: A future version increment can leave the comment claiming a different emitted identity version than the value serialized into campaign artifacts, making compatibility investigations harder.
Recommended action: Rewrite the newly added paragraph to explain the current self-type normalization and compatibility boundary without spelling out the constant's numeric value. Keep the declaration as the single source of truth for that value.
Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:185-191
Reviewed in: #113
Title: Document the trait implementation metadata on MutantDefinition
[Copilot speaking]
The new public trait_impl field on MutantDefinition has no field documentation, even though its representation is intentionally narrower than a full trait path and it exists for a specific policy boundary. The corresponding Candidate field explains that it stores the terminal trait name separately from item_path so policy can select implementations without parsing the identity, but that contract is absent where the metadata becomes part of the exported mutation definition.
Location: crates/cargo-gamma-engine/src/model/mutant_definition.rs:22
Reproducible reasoning: MutantDefinition is publicly re-exported from the engine model, and trait_impl: Option<Arc<str>> alone does not reveal whether the text is qualified, normalized, or intended for display, identity, or selection. The collector records only the last path segment, then the definition carries that value across the engine boundary. Without a local explanation, a maintainer or consumer must trace the collector and candidate types to reproduce the field's meaning and may reasonably infer the wrong representation.
Consequence: A consumer can treat trait_impl as a fully qualified trait path or fold it into identity logic, causing qualified traits with the same terminal name to be interpreted differently from the documented selection policy.
Recommended action: Add field documentation to MutantDefinition::trait_impl stating that it is the optional terminal name of the enclosing implemented trait and explaining that it is kept separately for trait-implementation selection policy rather than as a full path or identity component.
References:
- crates/cargo-gamma-engine/src/ops/collect/candidate.rs
Impacted locations:
crates/cargo-gamma-engine/src/model/mutant_definition.rs:22
crates/cargo-gamma-engine/src/ops/collect/candidate.rs:34-40
Reviewed in: #113
Title: Inline the new public trivial forwarders and accessors
[Copilot speaking]
New public APIs across the cargo-gamma crates add methods and trait implementations whose complete bodies only return a field, expose an underlying scalar, compare wrapped text, or forward to one existing operation. These include the supported MutantId and SiteIndex accessors and conversions, engine error and source-position accessors, control-encoding wrappers, the RunRecord iterator adapter, the Unix effective-user wrapper, and PlatformError accessors. They lack #[inline] even though downstream crates call them across crate boundaries.
Location: crates/cargo-gamma-engine/src/model/identity.rs:43-54
Reproducible reasoning: For a trivial public wrapper, #[inline] makes the body available to a downstream optimizer without requiring link-time optimization; it is an optimization opportunity rather than a guarantee that every call disappears. Each affected body has no independent algorithm or error path, so exposing it does not duplicate substantial code. This consolidation excludes test-only or representation-probing APIs that should instead be removed or narrowed rather than optimized.
Consequence: Ordinary non-LTO downstream builds may retain avoidable calls at small abstraction boundaries used while scanning source, comparing identities, rendering output, iterating records, or classifying platform failures.
Recommended action: Add #[inline] to the listed production-supported trivial public methods and trait implementations. Do not use #[inline(always)], and do not annotate an API that is being removed or narrowed for visibility reasons; the recommendation applies only to the supported cross-crate forwarders that remain.
Impacted locations:
crates/cargo-gamma-engine/src/error.rs:68-70
crates/cargo-gamma-engine/src/model/identity.rs:43-140
crates/cargo-gamma-engine/src/model/identity.rs:164-179
crates/cargo-gamma-engine/src/parse/source_file.rs:161-191
crates/cargo-gamma-engine/src/schema.rs:205-215
crates/cargo-gamma-engine/src/text.rs:44-46
crates/cargo-gamma-engine/src/text.rs:56-58
crates/cargo-gamma-lib/src/discover/record.rs:528-530
crates/cargo-gamma-unsafe/src/identity.rs:16-20
crates/cargo-gamma-unsafe/src/platform_error.rs:53-62
Reviewed in: #113
Title: Version mutation reports before changing the mutant-ID scheme
[Copilot speaking]
MUTANT_ID_VERSION advances to version 5 because implementation scopes now produce different IDs, but cargo-gamma reports do not record the identity scheme that produced their mutant IDs. The report reader and merger accept reports across cargo-gamma versions and union them by the opaque ID text alone. SARIF explicitly versions the fingerprint key, so that output recognizes the namespace change, while the report/merge path has no equivalent transition marker.
Location: crates/cargo-gamma-engine/src/model/identity.rs:187-194
Reproducible reasoning: For an implementation affected by the new scope normalization, the same logical mutant has one ID in a version-4 report and another in a version-5 report. merge keys verdicts by mutant.id and can withdraw an absent old ID only when an unsharded report supplies a complete current population. Its documented shard behavior deliberately keeps every ID admissible when only shards are available. Because neither Report nor RunInfo carries MUTANT_ID_VERSION, and the merge does not use framework.version to establish identity compatibility, a rotation containing pre-upgrade and post-upgrade shards cannot determine that the two IDs represent one mutant. Both survive as distinct entries even when they refer to the same source construct.
Consequence: A normal sharded CI rotation spanning the upgrade can count an affected implementation mutant twice, retain its stale version-4 verdict beside its version-5 result, and distort the merged score until a complete version-5 population happens to withdraw the legacy ID.
Recommended action: Record the mutant-ID scheme version in cargo-gamma report metadata and define an explicit merge policy for mixed schemes. For example, treat reports without the field as the legacy scheme and refuse or isolate mixed-version shard merges unless a complete current population can retire the legacy IDs. Document the upgrade effect so users know whether they need a complete run or must start a new report rotation.
References:
- crates/cargo-gamma/docs/DESIGN.md — Identity and knowledge across campaigns
Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:187-194
crates/cargo-gamma-lib/src/elements/report.rs:44-81
crates/cargo-gamma-lib/src/merge/union.rs:15-20
crates/cargo-gamma-lib/src/merge/union.rs:72-81
Reviewed in: #113
Title: Call item_path an item path rather than a human-readable identity
[Copilot speaking]
The new trait_impl field documentation first names item_path and then calls the same value a "human-readable identity." In this module, identity already has the distinct, explicitly defined meaning represented by MutantId, while the surrounding APIs consistently call this value an item path.
Location: crates/cargo-gamma-engine/src/ops/collect/candidate.rs:38-39
Reproducible reasoning: The documentation says trait_impl avoids parsing a human-readable identity, but the value it is contrasted with is specifically item_path. A reader therefore has to decide whether "identity" means the item path, the hashed MutantId, or another representation. Reusing the established term "item path" removes that ambiguity and preserves the distinction between the descriptive scope text and the content-addressed mutant identity.
Consequence: A maintainer can incorrectly infer that trait-implementation selection would otherwise parse MutantId, or introduce more code and documentation using two terms for the same item-path concept.
Recommended action: Replace "human-readable identity" with "item path" (or refer directly to item_path) so the documentation uses the established term and leaves "identity" for MutantId.
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/candidate.rs:36-40
Reviewed in: #113
Title: Preserve token boundaries when normalizing implementation self types
[Copilot speaking]
impl_scope now passes an arbitrary implementation self type through compact_path, which removes every whitespace character and comment without preserving lexical token boundaries. That normalization is safe for ordinary paths separated by punctuation, but it is not injective for the full Rust Type syntax accepted as an implementation self type.
Location: crates/cargo-gamma-engine/src/ops/collect/collector.rs:466
Reproducible reasoning: For example, dyn Marker is a trait-object type while dynMarker can be a distinct nominal type identifier. Both normalize to the same text, dynMarker. Consequently, implementations such as impl Subject for dyn Marker and impl Subject for dynMarker receive the same scope string when their method names match. into_definitions then disambiguates identical site keys with a source-order occurrence index, so reordering those implementation blocks can transfer stable mutant identifiers between different self types. This contradicts the new identity design's purpose of representing the complete self type and eliminating source-order dependence.
Consequence: Cached verdicts, shard assignment, configured expectations, or report fingerprints can become associated with the wrong implementation after otherwise behavior-preserving implementation reordering.
Recommended action: Normalize the self type with a token-aware representation that removes trivia while retaining separators wherever removing trivia would merge adjacent Rust tokens. Preserve the current formatting-insensitivity, and add a regression that gives dyn Marker and a nominal dynMarker identical method bodies and verifies their identities remain distinct and stable when the implementation blocks are reordered.
References:
- crates/cargo-gamma-engine/docs/DESIGN.md
- crates/cargo-gamma-engine/src/model/identity.rs
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:466-467
crates/cargo-gamma-engine/src/ops/collect/collector.rs:1194-1229
Reviewed in: #113
Title: Use the established “terminal identifier” term for trait matching
[Copilot speaking]
The new trait_impl field documentation calls the stored value the trait's “terminal name”, while the user-facing configuration contract consistently defines the same value as the implemented trait's “terminal identifier”. Both refer to the final written path segment, such as Debug in core::fmt::Debug, so the two terms do not identify different concepts.
Location: crates/cargo-gamma-engine/src/ops/collect/collector.rs:93-94
Reproducible reasoning: The configuration documentation introduces “terminal identifier” as the term for the value matched by trait-impl rules and explains its qualification and alias behavior. The new collector comment describes the value populated from path.segments.last() as a “terminal name”. Because the implementation stores exactly the documented terminal identifier and does not define a separate meaning for “name”, using a synonym here makes readers determine whether the wording signals a semantic distinction.
Consequence: Maintainers tracing trait-exclusion behavior from configuration into candidate collection can incorrectly suspect that trait_impl uses a different normalization rule from the documented terminal-identifier contract.
Recommended action: Change the field comment to call the value the “terminal identifier of the enclosing implemented trait”, matching the established configuration terminology.
References:
- crates/cargo-gamma/docs/CONFIG.md, [[exclude-mutants]] contract
- crates/cargo-gamma/README.md, trait implementation exclusions
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:93-94
Reviewed in: #113
Title: Describe the prefilter without duplicating its literal
[Copilot speaking]
The new prefilter comment repeats the exact leak string used by both contains calls. The rationale is useful, but spelling the implementation value again in prose creates two representations of the same decision that must remain synchronized.
Location: crates/cargo-gamma-engine/src/ops/collect/collector/noop.rs:57-62
Reproducible reasoning: Lines 57-61 explain that matching depends on a literal identifier and name that identifier explicitly; line 62 independently embeds the same spelling twice in executable code. A future change to the recognized method name or to a shared identifier can update the code while leaving the prose behind, so the comment no longer reliably communicates the decision it is intended to justify.
Consequence: A maintainer can read a stale method name in the rationale after changing the prefilter, obscuring which identifier the optimization is actually required to detect.
Recommended action: Keep the correctness and performance rationale, but refer to the required terminal method identifier checked by path_ends_with instead of repeating its spelling. Alternatively, introduce one suitably named constant and have the code refer to that single source of truth.
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector/noop.rs:57-62
Reviewed in: #113
Title: Borrow the configuration set in the fused pre-pass
[Copilot speaking]
The fused phase-one pass makes two owned copies of the build configuration for every source file: Walk::new(selection, cfg) clones it into Walk, and PhaseOne clones it again for the shared traversal gate. CfgSet owns three hash sets containing strings and string pairs, while both visitors only read it.
Location: crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:66-67
Reproducible reasoning: traversal::collect invokes phase_one::run once per parsed source file. At lines 66-67, constructing PhaseOne calls Walk::new and also explicitly clones cfg; Walk::new performs its clone at indexes.rs:479. Cloning a populated CfgSet allocates new hash-table storage and copies its owned names and key/value strings. This happens even when the selected mutators consult no numeric or import indexes, because Walk::new clones the set before the visitor can determine that those indexes are disabled. The configuration reference passed to run remains valid for the entire synchronous traversal, so neither owned copy is required.
Consequence: On a workspace with many source files, discovery performs repeated allocations and copies proportional to the number of active configuration names and feature pairs for every file. This can offset part of the allocation and traversal reduction that the fused pre-pass is intended to provide, including for selections that do not use the indexes at all.
Recommended action: Give Walk and PhaseOne a lifetime and store the existing &CfgSet in both visitors instead of cloning it. This adds lifetime parameters to these internal visitor types but preserves the current behavior while removing the per-file hash-set and string copies.
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:66-67
crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:464-479
Reviewed in: #113
Title: Avoid re-interning the trait name for every candidate
[Copilot speaking]
Each candidate inside a trait implementation already carries an Arc<str> cloned from the collector's implementation-level trait_impl. into_definitions discards that sharing boundary and calls Interner::text again for every candidate. That call performs a hash-table lookup and another reference-counted clone for every mutant, and the first occurrence also allocates a replacement Arc<str> plus the interner's owned key.
Location: crates/cargo-gamma-engine/src/ops/collect/definitions.rs:83
Reproducible reasoning: The collector creates one trait-name Arc<str> when entering an impl, clones that same pointer into every emitted Candidate, and restores the previous value when leaving the impl. In into_definitions, line 83 borrows each candidate's already-shared pointer, probes Interner::texts through Interner::text, creates or clones a second pointer, and then drops the candidate's pointer. The repository's own interner documentation says a run can contain hundreds of thousands of mutations, so this converts implementation-level work into per-mutant hashing and atomic reference-count churn. Cross-implementation deduplication only needs work once per distinct incoming trait value or implementation scope, not once per candidate.
Consequence: A source file with large trait implementations pays an avoidable hash lookup and extra Arc clone/drop cycle for every generated mutant, increasing discovery CPU cost on the same high-cardinality path that the surrounding sharing logic is intended to optimize.
Recommended action: Preserve the existing candidate-owned Arc<str> when constructing MutantDefinition, or intern/cache trait names at implementation scope before candidates are emitted and then move the candidate pointer into the definition. If collapsing identical trait names across separate implementations is important, retain that deduplication while ensuring the hash lookup happens once per distinct implementation-level pointer rather than once per mutant.
References:
- crates/cargo-gamma-engine/src/model/interner.rs
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/definitions.rs:83
Reviewed in: #113
Title: Keep the Audit implementation blocks adjacent
[Copilot speaking]
The new inert_reason free function is placed between Audit's inherent implementation and its Visit implementation. This separates two blocks that together define the behavior of the same type, even though the layout guideline requires associated blocks for a type to remain together.
Location: crates/cargo-gamma-engine/src/ops/collect/stated.rs:221-229
Reproducible reasoning: impl Audit ends at line 212, the newly added inert_reason function occupies lines 214-229, and impl Visit for Audit begins at line 235. Reading or searching through Audit therefore reaches an intervening free function before the remainder of the type's implementation. Moving the helper before the Audit declaration or after the Visit implementation preserves the exact dependency and behavior while restoring one contiguous type-oriented block.
Consequence: A maintainer inspecting Audit can overlook its visitor behavior or must jump across unrelated item boundaries to understand the complete type, making this diagnostic traversal harder to navigate and maintain.
Recommended action: Move inert_reason outside the sequence of Audit implementation blocks, for example immediately after the Visit implementation, so the inherent and trait implementations remain adjacent.
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/stated.rs:148-235
Reviewed in: #113
[Copilot speaking]
Review findings for pull request #113
This issue contains the approved findings from the review of #113 at source commit 3edf357. The report continues in comments because the complete rendered Markdown exceeds GitHub's per-body limit.
Title: Align the design comment-directive spelling with the supported syntax
[Copilot speaking]
The design says the attribute accepts exactly the text accepted by an equivalent
// gamma:comment directive. The supported and documented comment form is instead an attribute-shaped comment such as// #[gamma::test_timeout_multiplier(...)]; the shorthand-looking// gamma:form is not the directive syntax. Because this paragraph defines the user-visible accepted syntax and emphasizes character-for-character equivalence, the backticked spelling reads as a contractual example rather than an informal channel name.Location:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:21-24Reproducible reasoning: The public attribute documentation's “Relationship to the comment form” section consistently shows
// #[gamma::...]. The cross-channel agreement test also constructs// #[gamma::test_timeout_multiplier({arguments})]and describes the two channels as differing only by the leading comment characters. Separately, the suppression regression tests explicitly treat shorthand without the attribute brackets as unsupported. Reading those sources together shows that// gamma:does not identify a valid literal directive form, while this design paragraph presents it in code formatting immediately before claiming exact textual equivalence.Consequence: A reader can copy or preserve the shorthand-looking form and believe Gamma will apply a timeout override, while the tool ignores or rejects that text instead of applying the documented contract.
Recommended action: Make the design wording and accepted syntax agree. If
// gamma:is only a label for the comment channel, replace the code-form spelling with “comment-form directive” or show a supported representative such as// #[gamma::test_timeout_multiplier(...)]. If the shorthand is intended as literal syntax, implement it in the comment parser and cover it in the cross-channel agreement tests before claiming exact equivalence.References:
the_two_channels_agree_on_timeout_multiplier_argumentsImpacted locations:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:21-24Reviewed in: #113
Title: Document or separate the workspace-wide lint policy change
[Copilot speaking]
The pull request is titled and described as an end-to-end hardening of cargo-gamma, but
Cargo.tomlalso changes the lint policy inherited across the entire workspace: it enablesempty_structs_with_bracketsandtoo_long_first_doc_paragraph, and globally allowsliteral_string_with_formatting_args. This repository-wide policy change is not identified in the pull request description.Location:
Cargo.toml:211-219Reproducible reasoning: The new entries are under
[workspace.lints], so their scope is every workspace member that inherits the workspace lint table rather than only the cargo-gamma crates. The pull request description lists mutation discovery, process containment, runtime census, reporting, CLI diagnostics, and contract coverage, but does not mention changing static-verification policy for unrelated crates. Because both the title and description frame the work around cargo-gamma, a reviewer has no basis to tell whether this workspace-wide change is an intentional part of the mission or unrelated work included accidentally.Consequence: Maintainers can merge the change believing they are reviewing only cargo-gamma hardening while also changing which constructs produce warnings or are permitted by default in future work across unrelated workspace crates.
Recommended action: If the lint policy change is intentional, add it to the pull request's motivation and substantive-change summary and explain why it belongs with the cargo-gamma work. If it is independent, move it to a separate pull request; that provides isolated policy review at the cost of repeating the small amount of validation associated with these manifest entries.
Impacted locations:
Cargo.toml:211-219Reviewed in: #113
Title: Define the attribute vocabulary before stating parser boundaries
[Copilot speaking]
The added boundary rules introduce the specialized terms "stated value", "comment directive", and "positional timeout multiplier", then refer to selectors and the
reasonandtagarguments, without defining that vocabulary or directing the reader to its definitions before use. The sibling public README has dedicated "The vocabulary" and "Stating a value" sections, but this design document does not establish that relationship.Location:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:17-28Reproducible reasoning: The Purpose section only explains that this crate parses and validates inert attributes. A reader then reaches the new rules and must already know that a stated value is the expression supplied through
#[gamma::value(...)], that the// gamma:form is a source-comment equivalent of an attribute directive, and that a timeout multiplier is one argument accepted alongside selector, reason, and tag arguments. Those are domain-specific concepts rather than ordinary Rust terminology. Their definitions elsewhere in the repository confirm that they require explanation, but the design document neither defines them nor links to those definitions before making contractual claims about their ordering and rejection behavior. A linear reading therefore cannot recover the precise meaning of the new rules from this document.Consequence: A maintainer can misread "stated value" as any literal supplied to an attribute, or infer that a "positional timeout multiplier" has position-dependent semantics despite the following sentence denying positional meaning. That ambiguity makes future parser, diagnostic, and documentation changes more likely to use the terms inconsistently.
Recommended action: Add a short terminology paragraph before the boundary rules, or an explicit preceding link to the public vocabulary sections, defining the stated-value attribute, comment directive, timeout multiplier, selectors, and named
reasonandtagarguments. Then express the boundary rules with those established terms.References:
Impacted locations:
crates/cargo-gamma-attrs-impl/docs/DESIGN.md:17-28Reviewed in: #113
Title: Align empty-body eligibility with the documented semantic rationale
[Copilot speaking]
The new empty-body documentation and diagnostic claim that substituting any stated expression for an empty function body produces an identical program. That is false for valid unit-compatible expressions with observable behavior, such as
panic!()or a side-effecting call returning(). The implementation rejects these annotations because collection excludes empty-body sites before consulting the stated value, so the user-facing rationale is stronger than the behavior the code establishes.Location:
crates/cargo-gamma-engine/src/ops/collect/stated.rs:54-56Reproducible reasoning: An empty body evaluates to
(), but Rust also accepts expressions that return()after observable effects and diverging expressions that coerce to the function's return type. Replacing{}with either kind can change behavior. The collector's early exclusion of an empty body is therefore the reproducible reason no stated-value mutant is produced; behavioral equivalence of every accepted expression is not. The incorrect semantic claim is repeated across implementation documentation, diagnostics, and test rationale.Consequence: Users can be told that an observable mutant is inherently identical to the original program, while maintainers may preserve the exclusion for a false semantic reason and overlook that it is an eligibility policy or a possible future extension point.
Recommended action: Choose one contract and make collection, attribute validation, public documentation, diagnostics, and tests agree. If empty bodies remain ineligible, describe that as an eligibility rule applied before the stated expression is read, without claiming that every unit-compatible or diverging expression is behaviorally identical. If those expressions should be eligible, change collection accordingly and cover side-effecting and diverging examples. Preserve a semantic-equivalence claim only if the accepted syntax actually enforces it.
References:
Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:70-75crates/cargo-gamma-attrs-impl/src/implementation.rs:344-347crates/cargo-gamma-engine/src/ops/collect/stated.rs:54-56crates/cargo-gamma-engine/src/ops/collect/stated.rs:150-154crates/cargo-gamma-engine/src/ops/collect/stated.rs:457-459Reviewed in: #113
Title: Validate timeout-multiplier arguments without materializing and rebuilding the whole list
[Copilot speaking]
The new timeout-multiplier path clones the attribute stream, splits every top-level argument into an allocated
Vec<Vec<TokenTree>>, and then clones every non-positional argument into a secondTokenStream.validate_shapeimmediately materializes that rebuilt stream into another token vector. This duplicates the token storage before validation even begins, although only one argument needs to be classified at a time.Location:
crates/cargo-gamma-attrs-impl/src/implementation.rs:598Reproducible reasoning:
arguments_ofallocates an outer vector and a separate vector for each nonempty comma-delimited argument.validate_timeout_multiplierretains all of those vectors while it createsrestby cloning the non-positional tokens and synthesizing comma tokens. It then passesresttovalidate_shape, whose existing random-access parser collects the stream into its own vector. The design contract requires top-level splitting and per-argument classification, but it does not require retaining all segments simultaneously or reconstructing their original stream. Processing each completed top-level segment as it is encountered, while carrying thestated/strict-reading state between segments, preserves the accepted syntax and duplicate detection while avoiding the new outer collection and reconstruction allocations. This path runs inside rustc for everygamma,timeout_multiplier, andtest_timeout_multipliermacro expansion, so the added work scales with annotated items.Consequence: A crate with many timeout-annotated tests performs multiple avoidable heap allocations and token clones for every macro expansion, increasing compile-time allocation pressure and latency in rustc.
Recommended action: Fold top-level splitting, positional classification, and shape validation into a streaming pass. Reuse one segment buffer (or bounded-lookahead cursor), carry multiplier-seen state across segments, and validate non-positional segments directly instead of retaining
Vec<Vec<TokenTree>>and rebuildingrest. Preserve the syntax contract documented in the crate design while eliminating the duplicate token materialization.References:
Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:540-643Reviewed in: #113
Title: Centralize newly referenced items in module-level imports
[Copilot speaking]
New code across the reviewed modules introduces dependencies inside declarations and function bodies by spelling qualified paths inline, placing
usedeclarations inside functions or below module items, importing production siblings throughsuper::, or bypassing a crate’s public composition path. These references do not require path-based disambiguation and can be represented consistently in each file or module import block.Location:
crates/cargo-gamma-attrs-impl/src/implementation.rs:633Reproducible reasoning: The affected instances share one maintainability issue: newly referenced types, traits, functions, macros, constants, and modules were not incorporated into the owning module’s import surface. Module-level imports make dependencies visible in one place and avoid coupling production modules to nesting depth. Where an item is re-exported by the crate that owns the composition boundary, importing through that public crate path also keeps callers independent of the underlying dependency. Existing
cfggates and aliases can remain on the imports where platform availability or name collisions require them.Consequence: Dependencies are scattered through signatures, declarations, and function bodies instead of being visible at module boundaries. This makes the affected modules harder to scan and creates many separate path spellings to update when items move or imports are reorganized.
Recommended action: Move the affected references into the owning file or module import block and use the imported names at call sites. In production modules, prefer crate-root paths over
super::; across crates, prefer the composing crate’s public re-export over its private dependency path. Preserve necessarycfgattributes and aliases, and keep qualified call-site paths only where they genuinely disambiguate names.Impacted locations:
crates/cargo-gamma-attrs-impl/src/implementation.rs:633crates/cargo-gamma-attrs/tests/consumer.rs:75crates/cargo-gamma-attrs/tests/diagnostics.rs:63-111crates/cargo-gamma-engine/src/error.rs:255crates/cargo-gamma-engine/src/ops/collect/collector.rs:1334crates/cargo-gamma-engine/src/ops/collect/defaults.rs:261-280crates/cargo-gamma-engine/src/ops/collect/defaults.rs:968-980crates/cargo-gamma-engine/src/ops/collect/tests.rs:115crates/cargo-gamma-engine/src/ops/collect/tests.rs:2777crates/cargo-gamma-lib/src/ci/sarif.rs:187-195crates/cargo-gamma-engine/src/text.rs:212crates/cargo-gamma-lib/src/commands/list.rs:44-45crates/cargo-gamma-lib/src/commands/run.rs:800crates/cargo-gamma-lib/src/config.rs:1033-1051crates/cargo-gamma-lib/src/config.rs:1098-1105crates/cargo-gamma-lib/src/config.rs:1140-1146crates/cargo-gamma-lib/src/config.rs:1195-1200crates/cargo-gamma-lib/src/config.rs:1275crates/cargo-gamma-lib/src/discover/record.rs:501-505crates/cargo-gamma-lib/src/error.rs:136-142crates/cargo-gamma-lib/src/exec/census.rs:990-1001crates/cargo-gamma-lib/src/exec/census.rs:1524crates/cargo-gamma-lib/src/exec/measure.rs:493crates/cargo-gamma-lib/src/exec/sweep.rs:119crates/cargo-gamma-lib/src/exec/sweep.rs:1531-1571crates/cargo-gamma-lib/src/exec/workspace.rs:1119crates/cargo-gamma-lib/src/exec/workspace.rs:1277crates/cargo-gamma-lib/src/exec/workspace.rs:1762crates/cargo-gamma-lib/src/exec/workspace.rs:1769crates/cargo-gamma-lib/src/exec/workspace.rs:1805crates/cargo-gamma-lib/src/exec/workspace.rs:1832crates/cargo-gamma-lib/src/merge/union.rs:447crates/cargo-gamma-lib/tests/agreement.rs:239crates/cargo-gamma-lib/tests/agreement.rs:308-311crates/cargo-gamma-lib/tests/session.rs:389crates/cargo-gamma-lib/tests/session.rs:413crates/cargo-gamma-lib/tests/units.rs:143-146crates/cargo-gamma-process/src/testing.rs:99crates/cargo-gamma-rt/src/runtime.rs:675-701crates/cargo-gamma-rt/src/runtime.rs:1030-1055crates/cargo-gamma-rt/src/runtime.rs:2968-3035crates/cargo-gamma-unsafe/src/cgroup.rs:83crates/cargo-gamma-unsafe/src/cgroup.rs:108crates/cargo-gamma-unsafe/src/cgroup.rs:1604crates/cargo-gamma-unsafe/src/job.rs:798crates/cargo-gamma-unsafe/src/job.rs:817-821crates/cargo-gamma-unsafe/src/native_faults.rs:126crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:12-13crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:576crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:606crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:642crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:673crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:41crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:44crates/cargo-gamma-lib/src/exec/sync.rs:22crates/cargo-gamma-process/src/process_tree.rs:18crates/cargo-gamma-process/src/process_tree.rs:68crates/cargo-gamma-process/src/process_tree.rs:1058crates/cargo-gamma-process/src/process_tree.rs:2069crates/cargo-gamma-process/src/process_tree.rs:2410crates/cargo-gamma-process/src/process_tree.rs:2526crates/cargo-gamma-rt/src/runtime.rs:2835-2882Reviewed in: #113
Title: Document the
valuerejection as a contract, not a guard implementation[Copilot speaking]
The newly expanded public rustdoc correctly states that
#[gamma::value]is rejected onconst fnand empty bodies, but it explains that rule through the collector's reachability and the current run-time guard insertion mechanism. Those details belong to the attribute implementation; the user-facing contract is the accepted placement and diagnostic behavior.Location:
crates/cargo-gamma-attrs/src/lib.rs:435-439Reproducible reasoning: Lines 435-439 describe
collectionreaching a value and a mutant being spliced behind a run-time guard.crates/cargo-gamma-attrs/docs/DESIGN.mddefines this crate as the user-facing attribute namespace and identifies supported syntax and diagnostics as its public contract, whilecrates/cargo-gamma-attrs-impl/docs/DESIGN.mdowns parsing and validation. The rejection can remain stable if collection or instrumentation is later reorganized, so publishing the current mechanism in the macro API documentation turns an implementation choice into an apparent promise.Consequence: A future instrumentation or collection refactor can preserve the rejection contract while making this rustdoc false, and downstream readers may incorrectly rely on guard placement or collector behavior as stable API behavior.
Recommended action: Keep the public rustdoc focused on the requirement that
valueis rejected on declarations,const fn, and empty bodies. Move the collector and run-time guard explanation to the implementation crate's design documentation or an implementation comment.References:
Impacted locations:
crates/cargo-gamma-attrs/src/lib.rs:435-439Reviewed in: #113
Title: Describe the diagnostic tests as fragment checks rather than exact-output contracts
[Copilot speaking]
The new diagnostics test module says it pins each macro's exact diagnostic, but every test only checks that stderr contains the macro prefix and one message fragment. It does not pin the complete diagnostic, spans, severity, formatting, or additional emitted errors.
Location:
crates/cargo-gamma-attrs/tests/diagnostics.rs:5-14Reproducible reasoning: The module documentation at lines 5-14 claims exactness and then explains that validation is performed through substrings. The helper returns the complete stderr, but the assertions at lines 131-192 use only
contains. Therefore many changes to the actual diagnostic output can pass while the documentation tells maintainers that the exact output is protected.Consequence: Maintainers can rely on a stronger test contract than exists and overlook regressions in diagnostic shape because the suite remains green despite the documented exact-output guarantee.
Recommended action: Rename and document these as checks for diagnostic identity and the essential reason. If byte-for-byte diagnostic stability is genuinely required, use a normalized golden comparison instead, recognizing that exact compiler stderr is more brittle across supported Rust versions.
Impacted locations:
crates/cargo-gamma-attrs/tests/diagnostics.rs:5-14crates/cargo-gamma-attrs/tests/diagnostics.rs:131-192Reviewed in: #113
Title: Make timeout-multiplier example reasons match the demonstrated code
[Copilot speaking]
The two newly added successful examples attach operational reasons that their functions do not demonstrate.
digestaccepts an arbitrary byte slice and computes a sum, but its reason says it hashes a megabyte.accumulateperforms an ordinary conversion andu64addition, but its reason says widening arithmetic is slow. Because these examples teach how to use auditablereasonmetadata, the reasons should remain verifiable from the scenario they describe.Location:
crates/cargo-gamma-attrs/src/lib.rs:564-573Reproducible reasoning: Reading the example bodies reproduces the mismatch directly:
digesthas no input-size constraint and invokes no hash operation, whileaccumulateonly folds bytes into au64total. The examples therefore demonstrate the attribute syntax with explanations unrelated to the shown workload. The same syntax can be taught without this shortcut by choosing scenario names, bodies, and reasons that describe one another accurately.Consequence: Consumers copying the examples may treat
reasonas placeholder prose rather than durable justification for a timeout override, leaving future reviewers unable to determine why a multiplier is needed or whether it is still appropriate.Recommended action: Revise these examples so each function and reason describe a coherent, realistic workload while preserving the positional-order and trailing-comma forms being demonstrated. For example, either show an operation and input contract that genuinely justify the multiplier, or use reasons that accurately describe the simple accumulation bodies.
Impacted locations:
crates/cargo-gamma-attrs/src/lib.rs:564-573Reviewed in: #113
Title: Select the proc-macro artifact without filesystem timestamps
[Copilot speaking]
gamma_artifactscans the target and host dependency directories for hashed dynamic libraries whose filenames have agammaprefix, sorts the candidates by filesystem modification time, and silently passes the newest candidate torustc. A Cargo target directory can retain matching artifacts from other source revisions, feature combinations, compiler versions, checkouts, or concurrent Cargo activity, so the newest file is not necessarily the artifact Cargo selected for this test run.Location:
crates/cargo-gamma-attrs/tests/diagnostics.rs:80-82Reproducible reasoning: Cargo artifact hashes distinguish builds, but this lookup does not recover the hash of the dependency used by the current integration test. Cargo can reuse an already-valid current artifact without updating its modification time, while an unrelated artifact can have a later timestamp. A file's modification time records when it was written, not whether it belongs to the running test binary's build graph. Sorting by
metadata.modified()and taking the last candidate therefore makes fixture compilation depend on filesystem time and target-directory history rather than the reviewed source, without documenting an invariant that makes multiple candidates safe.Consequence: After switching branches or build configurations that share a target directory, the diagnostic tests can compile malformed fixtures against stale or unrelated proc-macro code. This can hide a regression in the current implementation or cause branch-history-dependent failures while leaving maintainers without explicit assumptions for recognizing or safely changing the selection behavior.
Recommended action: Arrange for Cargo to identify or load the exact proc-macro artifact for the current build, such as by compiling fixtures through a dedicated Cargo or trybuild-style crate with a path dependency, or by consuming Cargo's structured build output to obtain the artifact path. Cache that deterministic path for the test process instead of rescanning and ranking target contents by time. If the recency heuristic must remain, document the assumptions that make it valid and reject ambiguous candidate sets rather than silently selecting one.
References:
attrs_craterationale explains why selecting a Cargo proc-macro artifact from a direct rustc invocation is fragile)Impacted locations:
crates/cargo-gamma-attrs/tests/diagnostics.rs:34-82crates/cargo-gamma-attrs/tests/diagnostics.rs:34-80Reviewed in: #113
Title: Exclude the new integration tests from the published crate
[Copilot speaking]
The change adds
consumer.rsanddiagnostics.rsas development-only integration tests, butcargo-gamma-attrs/Cargo.tomlhas noincludeorexcludepackage-file policy. Cargo therefore includes these tracked test sources in the crate archive even though downstream users do not compile or need them.Location:
crates/cargo-gamma-attrs/tests/consumer.rsReproducible reasoning: Both added files live under
crates/cargo-gamma-attrs/tests/and exist solely to validate this repository's proc-macro implementation. Cargo's default packaging file selection includes tracked test sources unless the manifest narrows the package contents. The package manifest currently declares neither an allowlist nor an exclusion fortests/**, so this diff directly expands the published archive with development-only source.Consequence: Every release of
cargo-gamma-attrswould distribute the external-consumer fixtures and the compiler-spawning diagnostic harness to users, increasing the package payload and exposing test infrastructure as published content without any runtime or documentation benefit.Recommended action: Add an explicit package
includelist containing only the files required to compile and document the proc-macro crate, or at minimum excludetests/**. An allowlist prevents future development artifacts from entering the archive, at the cost of updating it when a genuinely required package file is added.References:
Impacted locations:
crates/cargo-gamma-attrs/tests/consumer.rs:1-85crates/cargo-gamma-attrs/tests/diagnostics.rs:1-193Reviewed in: #113
Title: Condense overlong Rust documentation summaries
[Copilot speaking]
New and rewritten Rust item and module documentation across the affected crates and tests uses opening paragraphs whose rendered visible text exceeds the 80-character summary limit. Wrapping the source across multiple documentation-comment lines does not create a paragraph break in rustdoc, so the affected comments still render as overlong summaries.
Location:
crates/cargo-gamma-engine/src/cfg.rs:160-161Reproducible reasoning: Every source finding identifies the same root cause: detailed or multi-clause prose remains in the uninterrupted first documentation paragraph, making its rendered summary longer than the required limit. The findings differ only in the affected items and files. A blank documentation line is required to separate a concise summary from the additional explanation.
Consequence: The generated documentation wraps these opening summaries instead of presenting the concise, consistently scannable first line required by the Rust documentation convention.
Recommended action: Rewrite each affected first paragraph to at most 80 visible rendered characters. Where the removed detail remains useful, retain it in a separate following paragraph after a blank documentation line, measuring rendered text rather than Markdown source width.
Impacted locations:
crates/cargo-gamma-engine/src/cfg.rs:160-161crates/cargo-gamma-engine/src/model/identity.rs:145-152crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:279-280crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:298-299crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:306-307crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:327-328crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:432-433crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:98-99crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:117-118crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:125-126crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:145-146crates/cargo-gamma-engine/src/parse/comment.rs:33crates/cargo-gamma-engine/src/schema.rs:246crates/cargo-gamma-engine/src/text.rs:128-129crates/cargo-gamma-engine/src/text.rs:152crates/cargo-gamma-lib/src/ci/sarif.rs:24-25crates/cargo-gamma-lib/src/ci/sarif.rs:200crates/cargo-gamma-lib/src/discover/survey.rs:1030crates/cargo-gamma-lib/src/discover/survey.rs:1039crates/cargo-gamma-lib/src/exec/copy.rs:62crates/cargo-gamma-lib/src/exec/copy.rs:123crates/cargo-gamma-lib/src/exec/sweep.rs:827crates/cargo-gamma-lib/src/exec/workspace.rs:743crates/cargo-gamma-lib/src/parse.rs:9crates/cargo-gamma-lib/src/report/progress.rs:357crates/cargo-gamma-lib/tests/agreement.rs:6-7crates/cargo-gamma-lib/tests/agreement.rs:143-144crates/cargo-gamma-lib/tests/agreement.rs:182-183crates/cargo-gamma-lib/tests/agreement.rs:249-250crates/cargo-gamma-lib/tests/agreement.rs:293-294crates/cargo-gamma-lib/tests/agreement.rs:305-306crates/cargo-gamma-lib/tests/agreement.rs:316-319crates/cargo-gamma-lib/tests/docs.rs:269crates/cargo-gamma-lib/tests/docs.rs:582crates/cargo-gamma-lib/tests/docs.rs:596crates/cargo-gamma-lib/tests/docs.rs:720crates/cargo-gamma-lib/tests/sarif_contract.rs:36-37crates/cargo-gamma-lib/tests/sarif_contract.rs:79crates/cargo-gamma-process/src/process_tree.rs:38crates/cargo-gamma-process/src/process_tree.rs:223crates/cargo-gamma-process/src/process_tree.rs:279crates/cargo-gamma-process/src/process_tree.rs:289crates/cargo-gamma-rt/src/runtime.rs:585-613crates/cargo-gamma-rt/src/runtime.rs:670-718crates/cargo-gamma-unsafe/src/cgroup.rs:84crates/cargo-gamma-unsafe/src/cgroup.rs:109crates/cargo-gamma-unsafe/src/cgroup.rs:262crates/cargo-gamma-unsafe/src/cgroup.rs:276crates/cargo-gamma-unsafe/src/cgroup.rs:563crates/cargo-gamma-unsafe/src/cgroup.rs:592crates/cargo-gamma-unsafe/src/cgroup.rs:778crates/cargo-gamma-unsafe/src/cgroup.rs:899crates/cargo-gamma-unsafe/src/cgroup.rs:1982crates/cargo-gamma-unsafe/src/job.rs:152-164crates/cargo-gamma-unsafe/src/platform_error.rs:73-75crates/cargo-gamma/src/real_host.rs:47-53crates/cargo-gamma/tests/binary.rs:22crates/cargo-gamma/tests/binary.rs:30crates/cargo-gamma/tests/binary.rs:111crates/cargo-gamma-rt/src/runtime.rs:259-271crates/cargo-gamma-rt/src/runtime.rs:297-316crates/cargo-gamma-rt/src/runtime.rs:368-400Reviewed in: #113
Title: Keep all
Errorimplementation blocks together[Copilot speaking]
The new
Partstype is placed betweenError's inherent implementation and itsDisplay,StdError, andFrom<io::Error>implementations. This splits the blocks belonging to the primary error type with a different type definition.Location:
crates/cargo-gamma-engine/src/error.rs:95-110Reproducible reasoning: The code-layout guidance requires different blocks associated with the same type to stay together. In this file,
impl Errorends beforeParts, but the subsequentimpl Display for Error,impl StdError for Error, andimpl From<io::Error> for Errorremain belowParts. A reader followingErrortherefore has to cross the unrelatedPartsdefinition before reaching the rest ofError's behavior.Consequence: Future maintenance of the engine error can overlook one of its separated trait implementations, and readers cannot inspect the complete behavior of the primary type as one contiguous unit.
Recommended action: Move
Partsbelow theDisplay,StdError, andFrom<io::Error>implementations so every block associated withErroris contiguous, while keeping thePartsdefinition and its own future implementations together.Impacted locations:
crates/cargo-gamma-engine/src/error.rs:95-136Reviewed in: #113
Title: Define the discovery pre-pass terminology before using it
[Copilot speaking]
The new design boundary introduces several implementation-specific terms—“pre-pass”, “stated-value audit”, “numeric/import indexes”, and “fused entry point”—without defining them or identifying the fused API. The preceding Purpose and Boundaries text describes mutation discovery generally, but it does not give a reader enough context to distinguish these passes or understand how they relate.
Location:
crates/cargo-gamma-engine/docs/DESIGN.md:23-30Reproducible reasoning: A reader encounters this vocabulary for the first time in lines 23–30. The paragraph then contrasts the unnamed “fused entry point” with
check_stated, but neither entry point is introduced and the role of the indexes is not explained. Understanding the boundary therefore requires finding and reverse-engineering implementation modules outside the design document, contrary to the requirement that specialized terms be defined before use.Consequence: A reader can miss that the configuration-aware combined operation and the standalone stated-value checker intentionally inspect different regions, or may mistake internal pass names for previously established design concepts.
Recommended action: Introduce these concepts in plain language before this boundary, or rewrite the boundary to define each pass as it is named. Identify and link the combined API if “fused entry point” means
check_stated_and_collect_with, and briefly state what evidence the numeric/import indexes provide.Impacted locations:
crates/cargo-gamma-engine/docs/DESIGN.md:23-30Reviewed in: #113
Title: Describe MutantId compatibility at the actual serialization boundary
[Copilot speaking]
The new rustdoc attributes artifact compatibility to two different mechanisms more broadly than the implementation supports. The type documentation says Serde transparency means every plan, record, and report written by an earlier version still reads unchanged, although transparency only preserves this field's string-shaped wire representation and cannot guarantee compatibility of the surrounding artifact schema. The
MutantId::newdocumentation then says accepting arbitrary text prevents future-version records from failing to read, but derived transparent deserialization constructs the wrappedCompactStringdirectly and does not callnew.Location:
crates/cargo-gamma-engine/src/model/identity.rs:31-38Reproducible reasoning: The type derives
Deserializewith#[serde(transparent)], so Serde deserializes the singleCompactStringfield without routing the value through the inherent constructor. Consequently, validation added or omitted inMutantId::newhas no effect on reading persisted records. Transparency also establishes only thatMutantIditself has the same serialized representation as its inner string; successful deserialization of a complete plan, record, or report still depends on every other field and schema rule in that artifact. The current wording therefore promises artifact-wide backward compatibility and explains constructor behavior using a path the implementation does not take.Consequence: A maintainer can rely on the comment as evidence that arbitrary older or future artifacts are supported, or preserve the constructor's permissive behavior to protect deserialization, even though neither conclusion follows from this code. That can turn later schema or validation work into an accidental compatibility promise and obscure where persisted-input compatibility is actually enforced.
Recommended action: Narrow the type documentation to the contractual fact that
MutantIdremains serialized as its underlying string. Documentnewonly as constructing an opaque identity from already rendered text and state the actual reason it accepts arbitrary text, without tying that choice to Serde record reads. Put any whole-artifact backward- or forward-compatibility guarantee on the artifact formats that implement and test it.Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:23-38Reviewed in: #113
Title: Describe the identity contract without repeating its numeric version
[Copilot speaking]
The documentation for
MUTANT_ID_VERSIONnames the newly assigned numeric version immediately above the constant that stores the same value. The useful part of the comment is the normalization change and its compatibility effect; repeating the numeric literal creates a second copy that must be kept synchronized.Location:
crates/cargo-gamma-engine/src/model/identity.rs:185-191Reproducible reasoning: The constant declaration is the authoritative version value. Its new documentation says
Version 5while the declaration saysMUTANT_ID_VERSION: u32 = 5. If a later identity change increments only the constant or only the prose, readers receive conflicting information. The surrounding explanation can remain independently useful by describing the current normalization contract and, if historical compatibility needs to be retained, referring to the preceding contract without restating the current literal.Consequence: A future version increment can leave the comment claiming a different emitted identity version than the value serialized into campaign artifacts, making compatibility investigations harder.
Recommended action: Rewrite the newly added paragraph to explain the current self-type normalization and compatibility boundary without spelling out the constant's numeric value. Keep the declaration as the single source of truth for that value.
Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:185-191Reviewed in: #113
Title: Document the trait implementation metadata on MutantDefinition
[Copilot speaking]
The new public
trait_implfield onMutantDefinitionhas no field documentation, even though its representation is intentionally narrower than a full trait path and it exists for a specific policy boundary. The correspondingCandidatefield explains that it stores the terminal trait name separately fromitem_pathso policy can select implementations without parsing the identity, but that contract is absent where the metadata becomes part of the exported mutation definition.Location:
crates/cargo-gamma-engine/src/model/mutant_definition.rs:22Reproducible reasoning:
MutantDefinitionis publicly re-exported from the engine model, andtrait_impl: Option<Arc<str>>alone does not reveal whether the text is qualified, normalized, or intended for display, identity, or selection. The collector records only the last path segment, then the definition carries that value across the engine boundary. Without a local explanation, a maintainer or consumer must trace the collector and candidate types to reproduce the field's meaning and may reasonably infer the wrong representation.Consequence: A consumer can treat
trait_implas a fully qualified trait path or fold it into identity logic, causing qualified traits with the same terminal name to be interpreted differently from the documented selection policy.Recommended action: Add field documentation to
MutantDefinition::trait_implstating that it is the optional terminal name of the enclosing implemented trait and explaining that it is kept separately for trait-implementation selection policy rather than as a full path or identity component.References:
Impacted locations:
crates/cargo-gamma-engine/src/model/mutant_definition.rs:22crates/cargo-gamma-engine/src/ops/collect/candidate.rs:34-40Reviewed in: #113
Title: Inline the new public trivial forwarders and accessors
[Copilot speaking]
New public APIs across the cargo-gamma crates add methods and trait implementations whose complete bodies only return a field, expose an underlying scalar, compare wrapped text, or forward to one existing operation. These include the supported
MutantIdandSiteIndexaccessors and conversions, engine error and source-position accessors, control-encoding wrappers, theRunRecorditerator adapter, the Unix effective-user wrapper, andPlatformErroraccessors. They lack#[inline]even though downstream crates call them across crate boundaries.Location:
crates/cargo-gamma-engine/src/model/identity.rs:43-54Reproducible reasoning: For a trivial public wrapper,
#[inline]makes the body available to a downstream optimizer without requiring link-time optimization; it is an optimization opportunity rather than a guarantee that every call disappears. Each affected body has no independent algorithm or error path, so exposing it does not duplicate substantial code. This consolidation excludes test-only or representation-probing APIs that should instead be removed or narrowed rather than optimized.Consequence: Ordinary non-LTO downstream builds may retain avoidable calls at small abstraction boundaries used while scanning source, comparing identities, rendering output, iterating records, or classifying platform failures.
Recommended action: Add
#[inline]to the listed production-supported trivial public methods and trait implementations. Do not use#[inline(always)], and do not annotate an API that is being removed or narrowed for visibility reasons; the recommendation applies only to the supported cross-crate forwarders that remain.Impacted locations:
crates/cargo-gamma-engine/src/error.rs:68-70crates/cargo-gamma-engine/src/model/identity.rs:43-140crates/cargo-gamma-engine/src/model/identity.rs:164-179crates/cargo-gamma-engine/src/parse/source_file.rs:161-191crates/cargo-gamma-engine/src/schema.rs:205-215crates/cargo-gamma-engine/src/text.rs:44-46crates/cargo-gamma-engine/src/text.rs:56-58crates/cargo-gamma-lib/src/discover/record.rs:528-530crates/cargo-gamma-unsafe/src/identity.rs:16-20crates/cargo-gamma-unsafe/src/platform_error.rs:53-62Reviewed in: #113
Title: Version mutation reports before changing the mutant-ID scheme
[Copilot speaking]
MUTANT_ID_VERSIONadvances to version 5 because implementation scopes now produce different IDs, but cargo-gamma reports do not record the identity scheme that produced their mutant IDs. The report reader and merger accept reports across cargo-gamma versions and union them by the opaque ID text alone. SARIF explicitly versions the fingerprint key, so that output recognizes the namespace change, while the report/merge path has no equivalent transition marker.Location:
crates/cargo-gamma-engine/src/model/identity.rs:187-194Reproducible reasoning: For an implementation affected by the new scope normalization, the same logical mutant has one ID in a version-4 report and another in a version-5 report.
mergekeys verdicts bymutant.idand can withdraw an absent old ID only when an unsharded report supplies a complete current population. Its documented shard behavior deliberately keeps every ID admissible when only shards are available. Because neitherReportnorRunInfocarriesMUTANT_ID_VERSION, and the merge does not useframework.versionto establish identity compatibility, a rotation containing pre-upgrade and post-upgrade shards cannot determine that the two IDs represent one mutant. Both survive as distinct entries even when they refer to the same source construct.Consequence: A normal sharded CI rotation spanning the upgrade can count an affected implementation mutant twice, retain its stale version-4 verdict beside its version-5 result, and distort the merged score until a complete version-5 population happens to withdraw the legacy ID.
Recommended action: Record the mutant-ID scheme version in cargo-gamma report metadata and define an explicit merge policy for mixed schemes. For example, treat reports without the field as the legacy scheme and refuse or isolate mixed-version shard merges unless a complete current population can retire the legacy IDs. Document the upgrade effect so users know whether they need a complete run or must start a new report rotation.
References:
Impacted locations:
crates/cargo-gamma-engine/src/model/identity.rs:187-194crates/cargo-gamma-lib/src/elements/report.rs:44-81crates/cargo-gamma-lib/src/merge/union.rs:15-20crates/cargo-gamma-lib/src/merge/union.rs:72-81Reviewed in: #113
Title: Call
item_pathan item path rather than a human-readable identity[Copilot speaking]
The new
trait_implfield documentation first namesitem_pathand then calls the same value a "human-readable identity." In this module, identity already has the distinct, explicitly defined meaning represented byMutantId, while the surrounding APIs consistently call this value an item path.Location:
crates/cargo-gamma-engine/src/ops/collect/candidate.rs:38-39Reproducible reasoning: The documentation says
trait_implavoids parsing a human-readable identity, but the value it is contrasted with is specificallyitem_path. A reader therefore has to decide whether "identity" means the item path, the hashedMutantId, or another representation. Reusing the established term "item path" removes that ambiguity and preserves the distinction between the descriptive scope text and the content-addressed mutant identity.Consequence: A maintainer can incorrectly infer that trait-implementation selection would otherwise parse
MutantId, or introduce more code and documentation using two terms for the same item-path concept.Recommended action: Replace "human-readable identity" with "item path" (or refer directly to
item_path) so the documentation uses the established term and leaves "identity" forMutantId.Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/candidate.rs:36-40Reviewed in: #113
Title: Preserve token boundaries when normalizing implementation self types
[Copilot speaking]
impl_scopenow passes an arbitrary implementation self type throughcompact_path, which removes every whitespace character and comment without preserving lexical token boundaries. That normalization is safe for ordinary paths separated by punctuation, but it is not injective for the full RustTypesyntax accepted as an implementation self type.Location:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:466Reproducible reasoning: For example,
dyn Markeris a trait-object type whiledynMarkercan be a distinct nominal type identifier. Both normalize to the same text,dynMarker. Consequently, implementations such asimpl Subject for dyn Markerandimpl Subject for dynMarkerreceive the same scope string when their method names match.into_definitionsthen disambiguates identical site keys with a source-order occurrence index, so reordering those implementation blocks can transfer stable mutant identifiers between different self types. This contradicts the new identity design's purpose of representing the complete self type and eliminating source-order dependence.Consequence: Cached verdicts, shard assignment, configured expectations, or report fingerprints can become associated with the wrong implementation after otherwise behavior-preserving implementation reordering.
Recommended action: Normalize the self type with a token-aware representation that removes trivia while retaining separators wherever removing trivia would merge adjacent Rust tokens. Preserve the current formatting-insensitivity, and add a regression that gives
dyn Markerand a nominaldynMarkeridentical method bodies and verifies their identities remain distinct and stable when the implementation blocks are reordered.References:
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:466-467crates/cargo-gamma-engine/src/ops/collect/collector.rs:1194-1229Reviewed in: #113
Title: Use the established “terminal identifier” term for trait matching
[Copilot speaking]
The new
trait_implfield documentation calls the stored value the trait's “terminal name”, while the user-facing configuration contract consistently defines the same value as the implemented trait's “terminal identifier”. Both refer to the final written path segment, such asDebugincore::fmt::Debug, so the two terms do not identify different concepts.Location:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:93-94Reproducible reasoning: The configuration documentation introduces “terminal identifier” as the term for the value matched by
trait-implrules and explains its qualification and alias behavior. The new collector comment describes the value populated frompath.segments.last()as a “terminal name”. Because the implementation stores exactly the documented terminal identifier and does not define a separate meaning for “name”, using a synonym here makes readers determine whether the wording signals a semantic distinction.Consequence: Maintainers tracing trait-exclusion behavior from configuration into candidate collection can incorrectly suspect that
trait_impluses a different normalization rule from the documented terminal-identifier contract.Recommended action: Change the field comment to call the value the “terminal identifier of the enclosing implemented trait”, matching the established configuration terminology.
References:
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector.rs:93-94Reviewed in: #113
Title: Describe the prefilter without duplicating its literal
[Copilot speaking]
The new prefilter comment repeats the exact
leakstring used by bothcontainscalls. The rationale is useful, but spelling the implementation value again in prose creates two representations of the same decision that must remain synchronized.Location:
crates/cargo-gamma-engine/src/ops/collect/collector/noop.rs:57-62Reproducible reasoning: Lines 57-61 explain that matching depends on a literal identifier and name that identifier explicitly; line 62 independently embeds the same spelling twice in executable code. A future change to the recognized method name or to a shared identifier can update the code while leaving the prose behind, so the comment no longer reliably communicates the decision it is intended to justify.
Consequence: A maintainer can read a stale method name in the rationale after changing the prefilter, obscuring which identifier the optimization is actually required to detect.
Recommended action: Keep the correctness and performance rationale, but refer to the required terminal method identifier checked by
path_ends_withinstead of repeating its spelling. Alternatively, introduce one suitably named constant and have the code refer to that single source of truth.Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector/noop.rs:57-62Reviewed in: #113
Title: Borrow the configuration set in the fused pre-pass
[Copilot speaking]
The fused phase-one pass makes two owned copies of the build configuration for every source file:
Walk::new(selection, cfg)clones it intoWalk, andPhaseOneclones it again for the shared traversal gate.CfgSetowns three hash sets containing strings and string pairs, while both visitors only read it.Location:
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:66-67Reproducible reasoning:
traversal::collectinvokesphase_one::runonce per parsed source file. At lines 66-67, constructingPhaseOnecallsWalk::newand also explicitly clonescfg;Walk::newperforms its clone atindexes.rs:479. Cloning a populatedCfgSetallocates new hash-table storage and copies its owned names and key/value strings. This happens even when the selected mutators consult no numeric or import indexes, becauseWalk::newclones the set before the visitor can determine that those indexes are disabled. The configuration reference passed torunremains valid for the entire synchronous traversal, so neither owned copy is required.Consequence: On a workspace with many source files, discovery performs repeated allocations and copies proportional to the number of active configuration names and feature pairs for every file. This can offset part of the allocation and traversal reduction that the fused pre-pass is intended to provide, including for selections that do not use the indexes at all.
Recommended action: Give
WalkandPhaseOnea lifetime and store the existing&CfgSetin both visitors instead of cloning it. This adds lifetime parameters to these internal visitor types but preserves the current behavior while removing the per-file hash-set and string copies.Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/collector/phase_one.rs:66-67crates/cargo-gamma-engine/src/ops/collect/collector/indexes.rs:464-479Reviewed in: #113
Title: Avoid re-interning the trait name for every candidate
[Copilot speaking]
Each candidate inside a trait implementation already carries an
Arc<str>cloned from the collector's implementation-leveltrait_impl.into_definitionsdiscards that sharing boundary and callsInterner::textagain for every candidate. That call performs a hash-table lookup and another reference-counted clone for every mutant, and the first occurrence also allocates a replacementArc<str>plus the interner's owned key.Location:
crates/cargo-gamma-engine/src/ops/collect/definitions.rs:83Reproducible reasoning: The collector creates one trait-name
Arc<str>when entering animpl, clones that same pointer into every emittedCandidate, and restores the previous value when leaving theimpl. Ininto_definitions, line 83 borrows each candidate's already-shared pointer, probesInterner::textsthroughInterner::text, creates or clones a second pointer, and then drops the candidate's pointer. The repository's own interner documentation says a run can contain hundreds of thousands of mutations, so this converts implementation-level work into per-mutant hashing and atomic reference-count churn. Cross-implementation deduplication only needs work once per distinct incoming trait value or implementation scope, not once per candidate.Consequence: A source file with large trait implementations pays an avoidable hash lookup and extra
Arcclone/drop cycle for every generated mutant, increasing discovery CPU cost on the same high-cardinality path that the surrounding sharing logic is intended to optimize.Recommended action: Preserve the existing candidate-owned
Arc<str>when constructingMutantDefinition, or intern/cache trait names at implementation scope before candidates are emitted and then move the candidate pointer into the definition. If collapsing identical trait names across separate implementations is important, retain that deduplication while ensuring the hash lookup happens once per distinct implementation-level pointer rather than once per mutant.References:
Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/definitions.rs:83Reviewed in: #113
Title: Keep the Audit implementation blocks adjacent
[Copilot speaking]
The new
inert_reasonfree function is placed betweenAudit's inherent implementation and itsVisitimplementation. This separates two blocks that together define the behavior of the same type, even though the layout guideline requires associated blocks for a type to remain together.Location:
crates/cargo-gamma-engine/src/ops/collect/stated.rs:221-229Reproducible reasoning:
impl Auditends at line 212, the newly addedinert_reasonfunction occupies lines 214-229, andimpl Visit for Auditbegins at line 235. Reading or searching throughAudittherefore reaches an intervening free function before the remainder of the type's implementation. Moving the helper before theAuditdeclaration or after theVisitimplementation preserves the exact dependency and behavior while restoring one contiguous type-oriented block.Consequence: A maintainer inspecting
Auditcan overlook its visitor behavior or must jump across unrelated item boundaries to understand the complete type, making this diagnostic traversal harder to navigate and maintain.Recommended action: Move
inert_reasonoutside the sequence ofAuditimplementation blocks, for example immediately after theVisitimplementation, so the inherent and trait implementations remain adjacent.Impacted locations:
crates/cargo-gamma-engine/src/ops/collect/stated.rs:148-235Reviewed in: #113