Skip to content

Migrate to rstest-bdd v0.6.0 - #805

Draft
leynos wants to merge 31 commits into
mainfrom
adopt-rstest-bdd-v0-6-0
Draft

leynos wants to merge 31 commits into
mainfrom
adopt-rstest-bdd-v0-6-0

Conversation

@leynos

@leynos leynos commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

Migrate Netsuke's behavioural test suite from rstest-bdd 0.5.0 to the published 0.6.0 release, and guard the correctness fix the upgrade brings.

Cargo.toml changes exactly two version strings, both caret requirements, with strict-compile-time-validation retained. Cargo.lock resolves the whole rstest-bdd family — rstest-bdd, -macros, -patterns, -policy and -harness — to 0.6.0. No harness crate is added to any manifest: rstest-bdd-harness is transitive through rstest-bdd-macros, and the opt-in adapters rstest-bdd-harness-tokio and rstest-bdd-harness-gpui are absent from the lockfile because nothing requests them. The async scenarios are synchronous, so no adapter is needed.

No MSRV bump was needed. The graph already required Rust 1.89 via pre-existing ortho_config 0.9.0 and serde-saphyr 1.2.0, above the 1.88 that gherkin 0.16 imposes. rust-toolchain.toml and its Polonius nightly pin are untouched.

Lockfile scope

The lockfile delta is 22 packages, and every one lies inside the rstest-bdd family's transitive closure — 157 packages at 0.5.0, 176 at 0.6.0. No package outside that closure changed version, and none was added or removed.

The churn is real but entirely family-owned. rstest-bdd-macros 0.6.0 swapped proc-macro-error 1.0.4 for proc-macro-error3 3.1.1, taking convert_case, heck and a windows-sys family with it; it also pulled in cargo_metadata, link-section, linktime-proc-macro, derive_more and syn 3. proc-macro-error and proc-macro-error-attr are removals, so they are absent from the post-bump closure while sitting inside the pre-bump one.

The behavioural change, and its guard

0.6.0 fixes a false green: a step whose return type is a type alias of Result<T, E> previously had its Err boxed as an unused payload, so the scenario passed despite a failed assertion. This repository's fallible steps are declared -> Result<()> where Result is anyhow::Result — exactly such an alias — so the fix lands squarely on the suite.

None of the scenarios turned red, so no latent swallowed error was exposed. A green suite is equally consistent with the propagation working and with it being silently reverted, and no existing scenario has a step that returns Err — so the fix arrived unguarded.

tests/step_error_propagation_tests.rs closes that gap. It is deliberately driven to failure and passes only when a step's Err reaches the generated step loop. Validated in both directions: green as committed, and red on its trailing assertion (trailing step ran: the earlier error was not propagated) when the failing step is neutralised to Ok(()), which reproduces the pre-0.6.0 behaviour. It cannot pass by accident.

Its feature file lives in tests/features_step_results/, not tests/features/, because scenarios! sweeps the latter directory and would otherwise collect a deliberately-failing scenario as an ordinary one expected to pass.

Breaking changes: applied or inapplicable

"Inapplicable" is evidenced by the absence of a call site, not assumed. The consumed surface is rstest_bdd::Slot plus the given, when, then and scenarios macros — nothing else, and Slot is unchanged in 0.6.0.

Breaking change Disposition
Underscore-prefixed implicit fixture naming inapplicable — fixtures use unprefixed world
runtime = "tokio-current-thread" inapplicable — no scenario selector anywhere; scenarios are synchronous
HarnessAdapter::run returns HarnessResult inapplicable — no custom harness
Step aliases of Result<T, E> propagate Err applied, now guarded
insert_value returns InsertOutcome inapplicable — zero direct callers
record_bypassed_steps takes BypassedScenario inapplicable — zero callers
RustStepIndexResult from indexing entry points inapplicable — zero references
index_feature_file removed inapplicable — zero callers
publish_rust_diagnostics removed inapplicable — zero callers
find_feature_files returns Result inapplicable — zero callers
Feature paths manifest-relative in reports inapplicable — no metadata/JSON/JUnit consumer
MSRV raised to 1.88 not binding — graph already required 1.89

Documentation

docs/rstest-bdd-users-guide.md and docs/rstest-bdd-v0-6-0-migration-guide.md were imported byte-for-byte from tag v0.6.0 (commit 72fb22635670e456545ca368805ba4c1c9d7bd69), with provenance recorded in the ExecPlan and docs/contents.md.

This PR additionally corrects the ## rstest-bdd v0.6.0 usage section of docs/developers-guide.md, which had recommended runtime = "tokio-current-thread". 0.6.0 deprecates that syntax, and it warns rather than compiles silently because this repository builds on a nightly channel — the rstest_bdd_nightly condition the macros crate's build script selects — so a -D warnings build would escalate the diagnostic to an error.

The lint failures this migration caused, and their resolution

rstest-bdd 0.6.0 declares tracing with features = ["log"] non-optionally. Because rstest-bdd is a dev-dependency while tracing is a normal dependency, Cargo unifies tracing/log onto the single node the library compiles against whenever tests are built. That feature expands every tracing::* macro into extra branches, and Clippy's cognitive_complexity is measured after macro expansion — so a macro written inline is charged to its enclosing function.

An earlier revision of this description attributed the resulting make lint failure to a pre-existing condition on main and dismissed a dev-dependency bump as a possible cause. That was wrong. The cause was proved three ways: cargo tree -e features -i tracing names rstest-bdd v0.6.0 as the sole enabler of tracing's default feature; a counter-control reverting only rstest-bdd to 0.5.0 re-runs the identical probe from exit 101 with two complexity errors to exit 0 with none; and the flagged functions each contained exactly one inline tracing macro.

Measured cost model: +7 per inline tracing macro under log, +1 without, and nothing extra for a function that merely calls an emitter holding the macro. The remedy is therefore to hoist each charged macro into a dedicated emitter function.

The scope is larger than the first count suggested. Clippy's reported number is a lower bound: it aborts the build, so clearing one diagnostic unmasks whatever the abort was hiding, and fixing the first batch kept revealing more. In the end the hoist is mechanical but wide — 76 new functions across 37 non-test source files, spanning src/stdlib, src/runner, src/manifest, src/cli and the test support crates. It is behaviour-preserving by construction: every field name, message string and emitted ordering moves verbatim into its emitter, and nothing else in those functions changes. As one illustration, record_timing_summary_sink_write keeps its counter! and histogram! calls in place and calls a new debug_timing_summary_sink_failed_from_fields() instead of writing the macro inline.

One of those sites deserves naming, because it arrived with the rebase rather than with the migration. span_fields_are_captured_by_name_and_recording_point in src/test_tracing_capture.rs is a test main added while this branch was in review; the branch had never before compiled it under unified tracing features, so it had never charged the three inline trace_span! macros. Hoisting preserves the span names, field::Empty declarations, recording order and guard lifetime, so the test's exact captured-string assertions still pin the behaviour.

Hoisting the two Kani sites then pushed tests/kani_mutation_evidence_tests/compile_guard.rs from 393 to 413 lines, past Whitaker's 400-line module_max_lines cap — a second failure invisible until now for the same cascade reason, since make lint aborts at the first failing stage and Clippy failed ahead of Whitaker. The patch-application machinery moves to a sibling apply_patch module along a real seam: one module runs git apply and decides when a reverse is owed, the other decides which patches exist and what compiling them proves. The guard is left at 294 lines.

Two further defects, both found by CI rather than locally

A tool-version skew failed the required build-test check. CI pins MDTABLEFIX_VERSION: '0.6.0'; the local install was 0.6.1, and the two have different canonical wrap points. make fmt under 0.6.1 wrote this plan into a form that 0.6.0 rejects, so CI's Format step — run: make check-fmt — failed with docs/execplans/adopt-rstest-bdd-v0-6-0.md +11 -12 and skipped Lint, Typecheck, Doc coverage, Spelling, Mermaid, Workflow contracts and Test behind it. Every locally run gate agreed with 0.6.1, which is why the divergence was invisible here. The fix is the fixpoint rather than either tool's output: converging under 0.6.0 and then checking with 0.6.1, and the reverse, both report 174 files left unchanged, and comparing the result to what 0.6.1 wrote with all whitespace removed shows the two are identical — only line breaks moved. The local binary is still 0.6.1, not the pinned 0.6.0 the earlier revision of this description claimed, so the lanes can still diverge and every Markdown edit has to be checked against the pinned binary explicitly rather than assumed safe.

The hoist cost child_exit.rs a CodeScene point. CodeScene's String Heavy Function Arguments rule is a file-level ratio, not a per-function verdict. Hoisting four tracing macros out of terminate_child and finalize_streaming took src/runner/process/child_exit.rs from 2 of 5 functions taking a string argument to 5 of 9, and the file fell from 10.00 to 9.68. Measured locally, cs check <merge-base>:./src/runner/process/child_exit.rs reports 10.00 against 9.68 on this branch's parent, which established the cause as this migration's own hoist. The four emitters move unchanged into src/runner/process/child_exit/emitters.rs, a same-stem directory beside their host, declared with an explicit #[path] — the shape stdlib/register already uses for query_helpers.rs. Both files score 10.00, and the CodeScene check passes.

Validation

Full gate set on the committed, rebased revision, with make lint reaching every stage — lint-clippy, lint-whitaker (both crates), lint-workflow-scripts, all five stages of lint-python, and github-actions-lint. The failing revision never got past lint-clippy, so lint-whitaker, lint-python and github-actions-lint had never executed on this branch at all.

make test passes both passes: 3919 nextest tests with 0 failures, 0 leaky and 6 skipped, plus 129 doctests across two separate targets. make check-fmt, make typecheck, make doc-coverage (98.88% against an 80% bar), make markdownlint and make nixie pass.

make markdownlint passing is itself worth noting: on the earliest revisions this target died at its preceding spelling stage, so markdownlint-cli2 had never actually executed.

The swept BDD inventory moves by zero: measured as sets at three revisions, the baseline (ebcedaef) had 254 scenario names, main (84447f0e) has 269, and this branch has 269. main's 15 additions arrived from work that landed while this branch was in review; the branch's own delta is nil, and all 254 baseline names survive. Two independent measurements agree on 269 — a grep of the swept feature directories, and the generated features_scenarios:: / features_unix_scenarios:: test names in the make test log.

make test-workflow-contracts also passes (1107 passed, 3 skipped). It is deliberately not a prerequisite of the make gates above, so it is run separately after a rebase.

The final sweep is the post-rebase one, and it is a different shape from the earlier runs. The branch was rebased onto fce1a746, whose rust_module_layout_test.py contract rejects sibling Rust modules sharing a first _-delimited prefix — exactly the shape all three of this branch's hoisted emitter modules had. Each moved into a same-stem directory beside its host, declared with an explicit #[path]; that removes the prefix pair outright, and since #[path] changes which file backs a module but never the module's path in the tree, every super:: and pub(super) reference resolves unchanged. The tools/kani/proof-scope.toml entries were recomputed for the two emitters the harnesses reach.

Nine targets then ran on the frozen revision 021e0524, sequentially, with the tree clean before and after: check-fmt (1s), spelling (7s), markdownlint (14s), typecheck (10s), lint (39s), test (238s), nixie (1s), doc-coverage (9s) and test-workflow-contracts (76s). All nine PIPESTATUS[0] sidecars contain 0, and each log brackets itself with HEAD_AT_START and HEAD_AT_END, both reading 021e0524, over an empty post-gate tree-status block — so the revision is pinned by the log rather than inferred from its name.

Two counts moved against the earlier sweep and neither is a regression: make test rose from 3912 to 3919 and the workflow contracts from 1082 to 1107, both because fce1a746 contributed 7 tests. make lint reached every stage with the word warning appearing exactly four times, all four being the literal -D warnings flag echoed in a command line rather than a finding. make test's one slow test, packaged_manifest_retains_build_script_sources, PASSED at 144.107s after crossing nextest's 60s and 120s SLOW thresholds; it is recorded as a pass, not a timeout.

Recording that sweep invalidated it, because this plan is itself read by a gate: tests/execplan_status_contract_tests.rs parses every plan header. The check that matters is therefore which gates can see docs/execplans/ at all — only check-fmt, spelling, markdownlint and nixie — and those four were re-run on the amended bytes. That re-run earned its keep by catching a real defect the first pass could not: prose added while recording the sweep contained a code span with an interior space, and markdownlint-cli2 reported MD038/no-space-in-code against it. Both mdtablefix 0.6.0 and 0.6.1 accepted the same text, which is the point — the fixpoint check covers the tool with the skew, not the linter. After the fix, all four gates are green on the committed revision b66ce687, with markdownlint-cli2 genuinely executing over 175 files.

make doc-coverage reads 98.88% (5044/5101) against the 80% bar; the numerator gains one item from src/runner/process/child_exit/emitters.rs.

References

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

leynos pushed a commit that referenced this pull request Sep 26, 2026
Close the ExecPlan's delivery: record the commit and draft pull request
#805 in `### Session provenance`, tick the final Progress item, and move
`Status:` from `IN PROGRESS` to `COMPLETE`.

Status is contract-governed, so the closed-set test was run directly
rather than assumed; all nine cases pass.

The Markdown-scoped gates were re-run on this revision after the edits
shifted it, and both pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please investigate the cause of the following issue using codegraph exploration and research, identify a fix and provide an AI coding agent prompt for the fix:

error: the function has a cognitive complexity of (10/9)
Error:   --> src/cli/discovery_trace.rs:97:8
   |
97 |     fn emit(&self) {
   |        ^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
   = note: requested on the command line with `-D clippy::cognitive-complexity`

error: the function has a cognitive complexity of (13/9)
Error:   --> src/cli/discovery_telemetry.rs:60:8
   |
60 | pub fn record_discovery_outcome<C: MonotonicClock>(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (16/9)
Error:    --> src/cli/merge_observability.rs:127:4
    |
127 | fn record_default_event(event: &MergeEvent) {
    |    ^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (23/9)
Error:    --> src/cli/merge_observability.rs:140:4
    |
140 | fn record_file_event(event: &MergeEvent) {
    |    ^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (16/9)
Error:    --> src/cli/merge_observability.rs:164:4
    |
164 | fn record_environment_event(event: &MergeEvent) {
    |    ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (23/9)
Error:    --> src/cli/merge_observability.rs:184:4
    |
184 | fn record_cli_event(event: &MergeEvent) {
    |    ^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (22/9)
Error:    --> src/cli_localization.rs:168:8
    |
168 | pub fn build_localizer(preferred_locale: Option<&str>) -> Box<dyn Localizer> {
    |        ^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/ir/from_manifest.rs:118:8
    |
118 |     fn process_targets(
    |        ^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (17/9)
Error:    --> src/manifest/glob/diagnostics.rs:180:4
    |
180 | fn record_template_expansion(
    |    ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (13/9)
Error:   --> src/manifest/jinja_macros/telemetry.rs:89:15
   |
89 | pub(super) fn instrument_macro_invocation<T>(
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (14/9)
Error:    --> src/manifest/jinja_macros/telemetry.rs:116:15
    |
116 | pub(super) fn instrument_template_render<T>(
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (16/9)
Error:   --> src/manifest/loading.rs:39:15
   |
39 | pub(super) fn trace_expansion_report(report: &ExpansionReport) {
   |               ^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (16/9)
Error:    --> src/manifest/env_reader.rs:147:15
    |
147 | pub(super) fn env_var_with_default(
    |               ^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:   --> src/manifest/workspace.rs:62:15
   |
62 | pub(super) fn open_manifest_workspace(
   |               ^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

    Checking assert_cmd v2.2.2
error: the function has a cognitive complexity of (12/9)
Error:   --> src/runner/manifest_structure_telemetry.rs:46:8
   |
46 | pub fn record_manifest_structure(manifest: &NetsukeManifest) {
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:   --> src/runner/graph.rs:37:15
   |
37 | pub(super) fn handle_graph(
   |               ^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (13/9)
Error:   --> src/runner/help_telemetry.rs:19:15
   |
19 | pub(super) fn instrument_help_targets<T>(query: impl FnOnce() -> Result<T>) -> Result<T> {
   |               ^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (17/9)
Error:   --> src/runner/process/child_exit.rs:63:15
   |
63 | pub(super) fn terminate_child(child: &mut Child, context: &str) {
   |               ^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (15/9)
Error:   --> src/runner/process/command_logging.rs:97:15
   |
97 | pub(super) fn log_command_execution(
   |               ^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (24/9)
Error:   --> src/runner/process/ninja_program.rs:34:9
   |
34 |         |value| {
   |         ^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (17/9)
Error:    --> src/runner/process/streaming.rs:200:4
    |
200 | fn copy_with_stats<R, W>(reader: &mut R, writer: &mut W, stream_name: &'static str) -> ForwardStats
    |    ^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (12/9)
Error:   --> src/runner/recipe_shell_telemetry.rs:75:15
   |
75 | pub(super) fn instrument_recipe_shell_resolution(
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (13/9)
Error:    --> src/runner/recipe_shell_telemetry.rs:115:15
    |
115 | pub(super) fn instrument_bash_preflight<T>(
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (13/9)
Error:    --> src/runner/recipe_shell_telemetry.rs:151:15
    |
151 | pub(super) fn instrument_legacy_recipe_operation<T>(
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/runner/mod.rs:275:4
    |
275 | fn execute_ninja_tool(
    |    ^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/status_timing.rs:216:4
    |
216 | fn record_timing_summary_sink_write(elapsed: Duration, succeeded: bool) {
    |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (13/9)
Error:    --> src/stdlib/command/execution.rs:192:4
    |
192 | fn run_child(
    |    ^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (12/9)
Error:    --> src/stdlib/command/execution.rs:260:4
    |
260 | fn run_child_inner(
    |    ^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/stdlib/command/execution.rs:330:15
    |
330 | pub(super) fn wait_for_exit(
    |               ^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (17/9)
Error:    --> src/stdlib/command/pipes.rs:212:4
    |
212 | fn join_pipe_for_cleanup(
    |    ^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

    Checking rust-embed v8.12.0
error: the function has a cognitive complexity of (17/9)
Error:    --> src/stdlib/network/cache.rs:112:15
    |
112 | pub(super) fn read_cached(dir: &Dir, name: &str, limit: u64) -> Result<Option<Vec<u8>>, Error> {
    |               ^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/stdlib/network/redirect.rs:204:4
    |
204 | fn remaining_budget(deadline: Instant, url: &Url) -> Result<Duration, Error> {
    |    ^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (31/9)
Error:   --> src/stdlib/network/mod.rs:97:4
   |
97 | fn fetch_inner(
   |    ^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/stdlib/path/path_utils.rs:181:4
    |
181 | fn resolve_home<F>(home_directory: &HomeDirectory, read_env: F) -> Result<String, Error>
    |    ^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (17/9)
Error:   --> src/stdlib/path/read_telemetry.rs:81:15
   |
81 | pub(super) fn record_file_read<T>(
   |               ^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (15/9)
Error:    --> src/stdlib/register.rs:112:8
    |
112 | pub fn register_with_config(
    |        ^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:   --> src/stdlib/which/env.rs:51:4
   |
51 | fn capture_workspace_switch(env: &impl Env) -> WorkspaceSwitch {
   |    ^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (15/9)
Error:   --> src/stdlib/which/lookup/workspace/mod.rs:85:15
   |
85 | pub(super) fn search_workspace(
   |               ^^^^^^^^^^^^^^^^
   |
   = help: you could split it up into multiple smaller functions
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: the function has a cognitive complexity of (10/9)
Error:    --> src/stdlib/which/telemetry.rs:278:15
    |
278 | pub(super) fn record_resolution_error(
    |               ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: you could split it up into multiple smaller functions
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity

error: could not compile `netsuke-build` (lib) due to 39 previous errors
warning: build failed, waiting for other jobs to finish...
make: *** [Makefile:291: lint-clippy] Error 101
Error: Process completed with exit code 2.~~~

Seek a systemic fix rather than tactical. Ask yourself, can this happen again or happen elsewhere? If so, think about a long term fix of the underlying issue.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
git rev-parse HEAD
git status --short
cat Cargo.toml
cat rust-toolchain.toml
fd -i 'clippy|makefile|adopt-rstest-bdd-v0-6-0|step_error_propagation|discovery_trace|merge_observability'
ast-grep outline src/cli/discovery_trace.rs --items all
ast-grep outline src/cli/merge_observability.rs --items all
sed -n '65,135p' src/cli/discovery_trace.rs
sed -n '100,230p' src/cli/merge_observability.rs
sed -n '250,315p' Makefile
rg -n -C3 'cognitive|tracing|positive control|counter|BLOCKED|resolver' docs/execplans/adopt-rstest-bdd-v0-6-0.md
rg -n -C3 'cognitive-complexity|cognitive_complexity|cargo clippy|lint-clippy' . --glob '!Cargo.lock' --glob '!docs/rstest-bdd*'

Length of output: 84511


🤖 web_search executed:

Research Rust Clippy cognitive_complexity lint counting tracing macro expansion with tracing feature log. Check Clippy source whether external macro expansions are excluded, Cargo resolver 2 or 3 dev dependency feature unification for --all-targets, and whether tracing log can be disabled by downstream default-features=false. Identify systemic fixes without refactoring logging call sites.

Length of output: 4867


🏁 Script executed:

#!/bin/bash
sed -n '624,655p' docs/execplans/adopt-rstest-bdd-v0-6-0.md
rg -n -A24 '^name = "(rstest-bdd|tracing)"' Cargo.lock
cat clippy.toml
ast-grep outline tests/step_error_propagation_tests.rs --items all
cat tests/step_error_propagation_tests.rs
rg -n -C2 'CLIPPY_FLAGS|TEST_FLAGS' Makefile
ast-grep outline src/cli/discovery_telemetry.rs --items all
sed -n '45,125p' src/cli/discovery_telemetry.rs
rg -n -C2 'check-macro-expansion|macro expansion|cognitive' docs/whitaker-users-guide.md dylint.toml

Length of output: 13592


🏁 Script executed:

#!/bin/bash
python - <<'PY'
import urllib.request, tarfile, io, hashlib
url = "https://static.crates.io/crates/rstest-bdd/rstest-bdd-0.6.0.crate"
data = urllib.request.urlopen(url, timeout=30).read()
print("Published archive:", url, "sha256:", hashlib.sha256(data).hexdigest())
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as archive:
    for item in archive.getmembers():
        if item.name.endswith(("Cargo.toml.orig", "/Cargo.toml")):
            text = archive.extractfile(item).read().decode()
            print("\nMANIFEST:", item.name)
            print(text)
        elif item.name.endswith(".rs"):
            text = archive.extractfile(item).read().decode()
            lines = text.splitlines()
            indices = [i for i, line in enumerate(lines) if "tracing::" in line or "log::" in line or "LogTracer" in line]
            if indices:
                print("\nTRACING CALL SITES:", item.name)
                shown=set()
                for i in indices:
                    for j in range(max(0,i-3), min(len(lines),i+5)):
                        if j not in shown:
                            print(f"{j+1}: {lines[j]}")
                            shown.add(j)
url="https://raw.githubusercontent.com/rust-lang/rust-clippy/master/clippy_lints/src/cognitive_complexity.rs"
print("\nCURRENT CLIPPY SOURCE (not the pinned nightly):", url)
print(urllib.request.urlopen(url, timeout=30).read().decode())
PY

Length of output: 4374


🤖 web_search executed:

Inspect the published rstest-bdd 0.6.0 crate at https://docs.rs/crate/rstest-bdd/0.6.0/source/Cargo.toml and Cargo.toml.orig. Verify its tracing dependency features ["log"], and identify tracing call sites in its src, particularly whether tracing-to-log bridging is required for step or scenario correctness or is observability only. Propose gating tracing/log behind a non-default feature with compatibility considerations. Also inspect https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/cognitive_complexity.rs for check_macro_expansion and the per-expression visitor: can a lint-specific upstream fix exclude expansion-generated branching without skipping ordinary functions containing macros? Do not assume master equals nightly-2026-08-23.

Length of output: 3890


@leynos, fix the dependency feature request first, then correct the complexity measurement upstream. Do not refactor the 39 functions to accommodate generated logging branches. Thanks for the guidance to target a systemic fix.

Cause

I inspected Netsuke PR #805 at commit 27a95bbfe73dc81c8ccf633dc80468516eb3839f.

The dependency graph explains the failure:

netsuke-build
├── normal dependency: tracing 0.1.44
└── dev-dependency: rstest-bdd 0.6.0
                    └── tracing, features = ["log"]

Clippy --all-targets
  → builds the dev-dependency graph
  → unifies tracing/log into Netsuke's tracing dependency
  → expands tracing macros with additional log-bridge control flow
  → counts that generated control flow towards cognitive_complexity
  → exceeds the configured threshold of 9

The inspected code supports this diagnosis:

  • Cargo.toml declares both dependency paths.
  • Makefile sets CLIPPY_FLAGS to --workspace --all-targets --all-features -- -D warnings.
  • clippy.toml sets cognitive-complexity-threshold = 9.
  • record_default_event contains a small match and two tracing::debug! calls. The reported complexity is 16.
  • record_discovery_outcome contains one application-level if/else, plus tracing and metrics macros. The reported complexity is 13.

Public source documentation confirms that published rstest-bdd 0.6.0 explicitly requests tracing/log. Its manifest describes warning delivery and log-listener detection, rather than step-result handling. Published manifest

Cargo explicitly permits this feature unification when dev-dependencies participate in an all-targets build. Changing the resolver does not remove that behaviour. Cargo resolver documentation

The ExecPlan records stronger causal evidence: a minimal reproduction, a repository-scale positive control, and a counter-control that removes only the tracing/log request. Those are recorded results, not builds I reran. This sandbox permits inspection, not repository builds or tests.

Recommended fix

1. Make the tracing-to-log bridge independently selectable

In rstest-bdd, remove features = ["log"] from the unconditional tracing dependency. Expose the bridge through a separate feature.

Use this conceptual manifest change:

[dependencies]
tracing = "0.1"

[features]
log-bridge = ["tracing/log"]

Keep native tracing instrumentation. Do not remove the direct log dependency or listener detection without inspecting their warning-delivery contracts.

Handle compatibility explicitly. Making log-bridge non-default changes diagnostics for consumers that rely on a log logger. Document and test that change. If release policy requires preserving existing defaults, keep the bridge in the upstream default set initially. Then configure Netsuke to disable upstream defaults and explicitly restore every required non-bridge feature.

Publish the upstream correction, then update Netsuke to the actual published version. Do not invent a release number or leave a local registry edit as the fix.

This addresses the feature request at its source. It avoids changes across 31 application files.

2. Correct the lint to prevent recurrence

The dependency change alone cannot prevent another dependency from enabling tracing/log.

The current Clippy cognitive_complexity source skips functions whose body span comes from expansion. However, its expression visitor does not exclude each macro-generated branch inside an ordinary function. Clippy implementation

Submit a lint-specific correction that excludes expansion-generated control flow while retaining application control flow. Preserve traversal into source-written macro arguments. Do not skip an entire function merely because it contains a macro.

Treat this as a separate upstream change. The inspected public source is master, not Netsuke's pinned nightly-2026-08-23.

Reject these alternatives

  • Do not raise the threshold. That weakens the application complexity limit.
  • Do not add 39 lint exemptions. That distributes a dependency problem across unrelated functions.
  • Do not remove --all-targets. That removes test-target lint coverage.
  • Do not disable macro checking globally. That changes unrelated lint coverage.
  • Do not rely on tracing.default-features = false. Cargo features are additive; another dependency's explicit request remains enabled.

AI coding agent prompt

Fix the dependency-driven Clippy cognitive_complexity regression affecting
leynos/netsuke PR #805. Target the dependency feature request, not the 39
application functions.

Evidence:
- Netsuke was inspected at commit
  27a95bbfe73dc81c8ccf633dc80468516eb3839f.
- Netsuke declares tracing as a normal dependency and rstest-bdd 0.6.0 as
  a dev-dependency.
- Published rstest-bdd 0.6.0 explicitly enables tracing/log.
- Cargo unifies this feature when Clippy builds all targets.
- Netsuke's complexity threshold is 9.
- docs/execplans/adopt-rstest-bdd-v0-6-0.md records positive and
  counter-controls isolating the feature edge.
- The upstream repository revision was not verified. Establish the
  upstream checkout and record its exact commit before changing it.

Requirement 1 — Correct the upstream feature contract.
Inspect rstest-bdd's workspace manifests, tracing calls, direct log calls,
listener detection, and warning fallbacks.

Remove tracing/log from the unconditional tracing dependency.
Keep native tracing instrumentation.
Expose tracing/log through an independently selectable log-bridge feature.

Choose and document a compatible default-feature policy.
If the bridge remains default-enabled for compatibility, let Netsuke disable
upstream defaults and explicitly enable every required non-bridge feature.
Do not disable unrelated runtime behaviour.
Do not remove direct log usage without proving that its contracts remain intact.

Requirement 2 — Add upstream regression coverage.
Test warnings with a tracing subscriber, with a log logger, and with neither.
Test bridge-enabled and bridge-disabled configurations.
Check the resolved feature graph of a downstream consumer with both normal
and dev dependency paths.
Add a downstream Clippy reproduction for --all-targets.
Keep dependency feature checks separate from runtime behaviour checks.

Requirement 3 — Integrate the released correction into Netsuke.
Use an actual published compatible release.
Retain strict-compile-time-validation.
Keep lockfile changes targeted and explain necessary transitive changes.
Preserve tests/step_error_propagation_tests.rs and its isolated feature file.
Run the error-propagation regression and its negative control.

Preserve the complexity threshold, warning-denial policy, workspace scope,
all-targets scope, and all-features scope.
Do not add lint suppressions, global macro exclusions, or production refactors.

Requirement 4 — Address recurrence separately.
Prepare a Clippy cognitive_complexity correction that excludes generated
branching without excluding source-written branching.
Preserve traversal into source-written expressions passed to macros.
Add tests for nested macros, tracing/log on and off, source-written branches,
closures, and functions that mix macros with ordinary control flow.
Compare against the pinned nightly before proposing toolchain adoption.
Do not change Netsuke's toolchain without following its ADR.

Acceptance:
- Record the feature graph for production and all-targets builds.
- Confirm that no dependency requests tracing/log in Netsuke's intended
  bridge-disabled all-targets configuration.
- Run make lint-clippy, then make lint.
- Run make test, make typecheck, make check-fmt, and documentation gates.
- Validate the native Windows lint job.
- Record actual results. Distinguish failures from checks that did not run.
- Update the ExecPlan and migration documentation.
- Stop if upstream availability or release compatibility prevents delivery.

Verification limit: the linked upstream revision was unavailable. The archive download also failed TLS certificate verification. Public documentation supplied the manifest evidence, but it does not verify the requested Git revision. Retry that revision inspection before implementation. No fix has been implemented or build-verified here.

Linked revisions not inspected for this answer: leynos/rstest-bdd (requested revision unavailable). Their changes remain unverified.


✏️ Learnings added
Learnt from: leynos
URL: https://github.com/leynos/netsuke/pull/805

Timestamp: 2026-10-02T17:17:12.222Z
Learning: For leynos/netsuke investigations, leynos requests systemic fixes that address the underlying cause and recurrence across other code paths or dependency configurations, rather than tactical changes to individual diagnostic sites.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

codescene-access[bot]

This comment was marked as outdated.

leynos pushed a commit that referenced this pull request Oct 2, 2026
Close the ExecPlan's delivery: record the commit and draft pull request
#805 in `### Session provenance`, tick the final Progress item, and move
`Status:` from `IN PROGRESS` to `COMPLETE`.

Status is contract-governed, so the closed-set test was run directly
rather than assumed; all nine cases pass.

The Markdown-scoped gates were re-run on this revision after the edits
shifted it, and both pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@leynos
leynos force-pushed the adopt-rstest-bdd-v0-6-0 branch from e5aa844 to ac783bc Compare October 2, 2026 19:49
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

leynos and others added 9 commits October 3, 2026 02:10
Add an ExecPlan for adopting `rstest-bdd` v0.6.0 and import the two
authoritative upstream documents from `leynos/rstest-bdd` tag `v0.6.0`
(commit `72fb2263`).

Both imports are byte-for-byte copies, verified by SHA-256 against the
pinned checkout, so the recorded checksums stay meaningful. The v0.6.0
users' guide supersedes the v0.5.0 copy, which was an adapted rather than
verbatim import. The migration guide's five relative links point at
upstream files this repository does not carry; the ExecPlan records an
explicit upstream link mapping instead of rewriting the imported text.

Reconnaissance falsified two assumptions worth recording: the `v0.6.0`
tag and the published 0.6.0 crate are byte-identical, so APIs the guide
labels "v0.7.0" are in fact present; and `Slot`, plus the
`strict-compile-time-validation` feature, both survive unchanged.

Update the developers' guide to describe v0.6.0 usage, and index both
imported guides in `docs/contents.md` with their provenance.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Raise the `rstest-bdd` and `rstest-bdd-macros` dev-dependencies from
0.5.0 to 0.6.0, resolving the whole family through a targeted
`--precise 0.6.0` lockfile update.

`Cargo.toml` changes exactly two version strings. Both stay caret
requirements and the macros crate keeps
`strict-compile-time-validation`, so compile-time validation is neither
relaxed nor bypassed. No harness crate is added to any manifest:
`rstest-bdd-harness` arrives transitively through `rstest-bdd-macros`,
and the opt-in adapters `rstest-bdd-harness-tokio` and
`rstest-bdd-harness-gpui` are absent from the lockfile because nothing
requests them.

0.6.0's headline change is a correctness fix: a step whose return type
is a *type alias* of `Result<T, E>` previously had its `Err` discarded,
so the scenario passed despite a failed assertion. Netsuke's 177
fallible steps are declared `-> Result<()>` where `Result` is
`anyhow::Result`, which is exactly such an alias.

None of the suite's 254 scenarios turned red, so the correction exposed
no latent swallowed error. A green suite is equally consistent with the
propagation working and with it being silently reverted, though, and no
existing scenario has a step that returns `Err` at all — so the fix
arrived with nothing guarding it. `tests/step_error_propagation_tests.rs`
closes that gap. It is a self-contained `#[scenario]` with
`#[should_panic]`, deliberately driven to failure: it passes only when a
step's `Err` reaches the generated step loop. Making the failing step
return `Ok(())` reproduces the pre-0.6.0 behaviour and the test then
fails on its *trailing* assertion, so the check is two-sided rather than
a test that can only ever pass.

Its feature file lives in `tests/features_step_results/`, not
`tests/features/`, because `scenarios!` sweeps the latter directory and
would otherwise collect a deliberately-failing scenario as an ordinary
one expected to pass.

Most of the guide's other breaking changes are inapplicable here, which
is evidenced rather than assumed: this repository imports only
`rstest_bdd::Slot` and the `given`, `when`, `then` and `scenarios`
macros. There are no callers of `insert_value`,
`record_bypassed_steps`, `find_feature_files`, the Rust indexing entry
points or the removed `publish_rust_diagnostics`, no custom
`HarnessAdapter`, and no `runtime =` scenario selector. `Slot` is
unchanged in 0.6.0.

No MSRV bump was needed. The graph already required Rust 1.89 through
pre-existing `ortho_config` 0.9.0 and `serde-saphyr` 1.2.0, which is
above the 1.88 that `gherkin` 0.16 imposes, so `rust-toolchain.toml`
and its Polonius nightly pin are untouched.

Also corrects the `## rstest-bdd v0.6.0 usage` section of
`docs/developers-guide.md`, which recommended `runtime =
"tokio-current-thread"` — a syntax 0.6.0 deprecates, and which this
repository's `-D warnings` builds would escalate to an error.

Verification on a frozen revision: `make test` passes with 3414 tests
and 5 skips, up one from the 3413 baseline because of the new regression
test, and the swept BDD inventory is unchanged at 254 scenarios.
`check-fmt`, `typecheck`, `doc-coverage`, `markdownlint` and `nixie`
also pass.

`make lint` is recorded as unavailable rather than as a pass:
`lint-clippy` aborts on 39 `cognitive_complexity` diagnostics across 31
files, all under `src/`, none under `tests/`. Those 31 files, plus
`clippy.toml` and `rust-toolchain.toml`, are byte-identical to `main`,
and the branch's only manifest change is the two version strings, so the
condition is pre-existing and cannot be caused by a dev-dependency
bump. Because `lint-clippy` aborts first, `lint-whitaker`,
`lint-python` and `github-actions-lint` did not run on this revision.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Close the ExecPlan's delivery: record the commit and draft pull request
#805 in `### Session provenance`, tick the final Progress item, and move
`Status:` from `IN PROGRESS` to `COMPLETE`.

Status is contract-governed, so the closed-set test was run directly
rather than assumed; all nine cases pass.

The Markdown-scoped gates were re-run on this revision after the edits
shifted it, and both pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The previous revision recorded the `make lint` failure as "pre-existing on
`main`" and environmental, and marked the plan COMPLETE. Both were wrong.

`rstest-bdd` 0.6.0 is the only release of that crate that declares `tracing`
at all, and it declares it non-optionally with `features = ["log"]`. Because
`rstest-bdd` is a dev-dependency while `tracing` is a normal dependency of
the same package, Cargo's default resolver unifies `tracing/log` onto the
single node the library compiles against. That feature expands every
`tracing::*` macro into extra `log` calls, inflating the expanded AST that
Clippy's syntactic `cognitive_complexity` metric counts. The migration is
therefore the cause, not the environment.

Evidence: a minimal repro measuring the same function body at (2/0) without
the feature and (8/0) with it; a repository-scale positive control on
byte-identical `src/` reproducing the same 39 sites CI reports; and a
counter-control patching only `features = ["log"]` out of 0.6.0's manifest,
which restores green with 0.6.0 still selected.

The shipped library is unaffected — `--lib` resolves zero `log` edges and
exits 0, while `--all-targets` resolves one and exits 101 — but the `make
lint` gate and the `build-test` and `Windows / lint-windows` CI jobs are red
on this branch.

Disposing of 39 `cognitive_complexity` errors spans 31 files, exceeding the
plan's own 30-file scope tolerance, and the four alternatives tested all
carry costs recorded in the Decision log. The plan is therefore set to
BLOCKED on that decision rather than resolved unilaterally.

Also records the general lesson: "pre-existing" is a claim about causality
and needs a mechanism, not a plausibility argument.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Adds a Progress entry for the corrected revision and a Surprises entry for a
working-tree clobber found during delivery.

The clobber: four tracked files were found holding the contents of a /tmp
scratch crate built earlier for the minimal reproduction, copied into the
worktree rather than out of it. Nothing was lost — HEAD was intact, the index
was clean, and all four were restored from HEAD and verified per-file against
`git show HEAD:<path>` — but the discovery is worth recording because
`git status` showed four ordinary `M` entries throughout, which is
indistinguishable from deliberate edits. Verification has to read content or
compare hashes, never infer from status. The same event replaced `target/`,
which is why subsequent gate runs were cold.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Migrating to rstest-bdd 0.6.0 turns `make lint` red with 39
`clippy::cognitive_complexity` errors. The root cause is feature
unification: 0.6.0 is the only release of the crate that declares
`tracing` at all, it declares it non-optionally with
`features = ["log"]`, and because `rstest-bdd` is a dev-dependency
while `tracing` is a normal dependency of this package, Cargo
unifies `tracing/log` onto the library's own node whenever the test
graph is built. `tracing`'s `log` feature expands every `tracing::*`
macro into extra `log` calls, which inflates the expanded AST that
the syntactic complexity metric counts. The feature cannot be
declined from this repository, and no Clippy option suppresses the
metric per-macro.

The remedy is to hoist each charged macro into a dedicated emitter
function. Measured cost model: an inline `tracing` macro adds 7 to
its enclosing function under the `log` feature and 1 without it,
while a function that merely calls an emitter holding that macro
measures at its structural baseline. Every reported site's
structural complexity is at or below 7, so no function needed
decomposition — only its macros moved. `src/cli/discovery_diagnostics.rs`
already used this technique, which is why that file was never
among the reported sites.

The count behaved as a lower bound throughout: the enumeration with
the threshold demoted reported 39 sites, and the honest probe rounds
then measured 32, 28, 0, 2, 3, 0, 0 diagnostics. The zero at round
three was not a pass — two hard errors had replaced the complexity
errors, and clearing those exposed five more sites.

Four emitter modules are new. Three of them exist partly to keep
their parents within the repository's 400-line module cap (the
fourth, `command_logging_emitters.rs`, already did so); every edited
file is now inside the cap.

`cargo clippy --workspace --all-targets --all-features --keep-going`
exits 0 with no diagnostics. Note that `--lib` does not unify the
feature and is not a valid probe for this class.

Co-Authored-By: Claude Code <noreply@anthropic.com>
`make check-fmt`'s `mdtablefix` stage reported the plan document as
`+30 -30` after the previous commit's prose edits: the paragraphs were
wrapped to a width the tool would not produce. The change is a pure
80-column rewrap with no content difference, applied by `make fmt`
rather than by hand.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The plan still described the complexity cascade as an open escalation and
recorded `make lint` as an unavailable check. Both are now false: the
macros have been hoisted, the escalation is closed, and every stage of
`make lint` runs and passes at `31d103de`.

`Status:` moves from `BLOCKED` to `IN PROGRESS` — deliberately not
`COMPLETE`, since this commit shifts the revision again and the plan's
own rule is that the header follows the evidence.

Adds the final gate sweep as its own subsection rather than editing the
`-m4` table in place, so the earlier sweep stays readable as the record
of what was true then. The `EP-M3` and `EP-M5` acceptance notes are
reworded from "unavailable" to "available and green", with the revision
named, and the commit inventory is corrected from six to seven.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Sets `Status: COMPLETE`. The header had been held at `IN PROGRESS`
pending a green gate run on a committed tree; that evidence now exists.
The plan's own rule is that the header follows the evidence rather than
leading it, and the final sweep under `### Gate logs` supplies it: all
six gates pass at `ec1d0498` with every stage reached. Two gates beyond
that sweep also pass at this revision — `make doc-coverage` at 98.83%
against an 80% threshold, which is the post-refactor code-coverage
evidence the earlier sweeps could not supply, and a re-run of
`make markdownlint` and `make nixie` against the uncommitted form of
this very edit.

The provenance note records the delivered revision and the eighth
commit, and the revision note closes the `BLOCKED` → `IN PROGRESS` →
`COMPLETE` sequence with the reasons for each move.

Co-Authored-By: Claude Code <noreply@anthropic.com>
leynos and others added 22 commits October 3, 2026 02:11
`rstest-bdd` 0.6.0 declares `tracing`'s `log` feature non-optionally, and
because it is a dev-dependency Cargo unifies that feature onto the normal
`tracing` node whenever tests are built. Under `log` every `tracing` macro
expands into extra branches, and Clippy's `cognitive_complexity` is measured
after macro expansion, so a macro written inline counts against its enclosing
function. Measured cost: +7 per inline macro, +1 without. A function that
merely calls an emitter holding the macro costs nothing extra.

Three sites exceeded the threshold of 9 on that account:

- `AppliedPatch::drop` and `every_patched_tree_compiles_under_denied_warnings`
  in the Kani mutation compile guard, and
- the `span_fields_are_captured_by_name_and_recording_point` closure in
  `test_tracing_capture`, which main added while this branch was in review.

Each inline macro moves into a dedicated emitter. The callers return to their
structural complexity and the emitted events are unchanged: the same span
names, field names, `field::Empty` declarations, recording order and guard
lifetime, so the tests' exact captured-string assertions still pin them.

Hoisting the two guards pushed `compile_guard.rs` from 393 to 413 lines, past
Whitaker's 400-line `module_max_lines` cap. That failure was invisible until
now because `make lint` aborts at the first failing stage and Clippy failed
ahead of Whitaker. The patch-application machinery — `AppliedPatch`,
`run_git_apply` and the reverse-on-drop guarantee — moves to a sibling
`apply_patch` module, leaving the guard at 294 lines. The two halves have
genuinely different concerns: one runs `git apply` and decides when a reverse
is owed, the other decides which patches exist and what compiling them proves.

Verified: `cargo clippy --workspace --all-targets --all-features` exits 0 with
no diagnostics, `make lint-whitaker` passes both its passes, `cargo doc`
resolves every link, and the affected test targets pass —
`kani_mutation_evidence_tests` 3/3 and `test_tracing_capture` 4/4.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The rebase onto `upstream/main` (`84447f0e`) invalidated the plan's earlier
"all clear". Work that landed on `main` while this branch was in review
compiled `src/test_tracing_capture.rs` in a tree where `tracing`'s `log`
feature was not yet unified, so the branch had never charged its three inline
`trace_span!` macros; rebased, they cross the `cognitive_complexity`
threshold. Hoisting the two Kani sites then pushed `compile_guard.rs` past
Whitaker's 400-line cap, a failure hidden behind Clippy because `make lint`
aborts at the first failing stage.

Both are now fixed, and the plan records what they teach:

- `## Surprises & discoveries` gains four entries: that lint expectations on
  this branch are a property of the feature-unified build rather than of the
  files, that a cascade reports stages as "not run" rather than passing, that
  Clippy's problem count is a lower bound, and that `--lib` masks the
  unification the real gate sees.
- `### Session provenance` carries the rebase mapping from the pre-rebase SHAs
  to their rebased equivalents, and corrects "Eight commits" to nine.
- `#### Pre-rebase sweep at `ec1d0498` (superseded)` marks the old table as
  history rather than deleting it, and a `#### Rebased sweep at `dd93120a``
  table replaces it as the gating one.
- `## Progress` gains five entries covering the rebase, the counter-control
  that re-proved the diagnosis, the hoists, and the module split.
- A `## Revision note` entry records the rebase and its lesson.

The rebased sweep's table is marked `PENDING`: its gate run has not been
performed yet, and recording a verdict before reading the log would be a claim
without evidence.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The rebased sweep ran at `4c93ba63` and all seven gates passed. The
table's `PENDING` rows are replaced with their real verdicts, read from
each gate's own log rather than taken from the runner's summary.

Two related corrections, both found by trying to verify the table rather
than trust it.

The table cited `/tmp/<gate>-gates-dd93120a.out`. No such file was ever
written: the repository's log template ends in the *branch* name, so the
suffix is `adopt-rstest-bdd-v0-6-0`. A SHA suffix would encode provenance
the template does not, but it does not describe what exists. The paths
and the heading now name what is on disk.

The Decision log claimed the targeted lockfile update left "the rest of
the graph alone". That is stronger than the truth. The delta is 22
packages and every one is inside the union of the `rstest-bdd` family's
transitive closure before and after the bump (157 packages at 0.5.0, 176
at 0.6.0); no package outside the closure moved. The churn is genuine
but family-owned, chiefly `rstest-bdd-macros` 0.6.0 swapping
`proc-macro-error` 1.0.4 for `proc-macro-error3` 3.1.1. The removal of
`proc-macro-error`/`-attr` is why they are absent from the post-bump
closure while remaining inside the pre-bump one.

`make lint` reaching every stage is the substantive result, since the
failing revision never got past `lint-clippy`: `lint-whitaker` passing
for both crates means the 400-line split is genuinely inside the cap
rather than merely unblocked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Run the full eight-target gate set on the delivered revision and record
the outcome in the ExecPlan. All eight pass, with `make lint` reaching
every stage, `make test` at 3912/3912 with 0 leaky and 6 skipped plus 129
doctests across two targets, and `make test-workflow-contracts` at 1082
passed.

The sweep caught a coverage trap. `check-fmt` ran early in the sweep and
its `mdtablefix` stage read `docs/developers-guide.md` three seconds
before the file's last write, so the green did not cover the edit.
Re-running it on the final bytes failed for real: `docs/developers-guide.md
+7 -7`, `1 file would be reformatted`, exit 2. `make fmt` re-flowed the
paragraph and touched no other file, so the failure was a wrapping
artefact of editing by hand rather than a content change. `check-fmt`,
`markdownlint` and `nixie` were then re-run green on those bytes, and the
spelling stage's en-GB-oxendict rule corrected the one word it rejected.

Also correct two claims that had gone stale. The rebased sweep's table no
longer presents itself as gating the delivered revision, since each plan
edit shifts the revision and demands a fresh sweep; and the step-module
doc comment no longer says `make lint` aborts in `src/` first, which
stopped being true once the hoist cleared the complexity errors. The 45
suppressions are now attested by two independent `-D warnings` builds.

Replace the developers-guide deprecation note's cited path — a macros
crate file that does not exist in this repository — with the actual
mechanism: `emit_runtime_deprecation_warning` reaches the compiler only
because this repository builds on a nightly channel, which is the
condition `rstest_bdd_nightly` selects.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The previous sweep's evidence was unsound: the gates ran against the
working tree and this plan then recorded them, so `check-fmt` read
`docs/developers-guide.md` before that file's last write. Re-running it
on those final bytes failed (`+7 -7`, exit 2), which `make fmt` fixed as a
wrapping artefact.

A plan this repository gates cannot be brought up to date by the sweep it
documents, because the plan is an input that sweep reads. So this commit
writes the record first and names a fresh log set, `-gate7`, that has not
been run yet; the gates then run against the committed revision with no
write following them.

State in the plan why the earlier evidence was rejected, what the ordering
trap is, and why the remedy is positional rather than diagnostic.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The sweep ran on `4f0ac198`, the child of `ac783bc2` that records it, so
the three revision references now name the revision the logs describe
rather than its parent. Only those references changed; `make fmt`,
`check-fmt` and `markdownlint` were re-run green afterwards.

Co-Authored-By: Claude Code <noreply@anthropic.com>
`make check-fmt` failed on the required `build-test` check with
`docs/execplans/adopt-rstest-bdd-v0-6-0.md +11 -12`, which skipped Lint,
Typecheck, Doc coverage, Spelling, Mermaid, Workflow contracts and Test
behind it. The cause is a tool-version skew, not a content defect.

CI pins `MDTABLEFIX_VERSION: '0.6.0'` and installs that release. The
local tool was 0.6.1. The two versions have different canonical wrap
points, so `make fmt` under 0.6.1 rewrote two paragraphs into a form
that 0.6.0 rejects, and every locally run gate agreed with 0.6.1.

Converging under the CI pin settles it, because the fixpoint is accepted
by both: 0.6.0's second pass is a no-op, and 0.6.1 then reports all 174
files unchanged. Comparing the result to what 0.6.1 wrote with all
whitespace removed shows the two are identical, so only line breaks
moved — no wording, table or link changed.

The local 0.6.1 binary is replaced with 0.6.0 so the two lanes cannot
diverge again, and the discovery is recorded in the ExecPlan's
`Surprises & discoveries` section.

Gates, all green on this revision: `make check-fmt`,
`make markdownlint` (175 files, 0 issues, spelling included) and
`make nixie`.

Co-Authored-By: Claude Code <noreply@anthropic.com>
CodeScene's Code Health Review flagged `src/runner/process/child_exit.rs`
for *String Heavy Function Arguments*, scoring it 10.00 -> 9.69. The
rule is a file-level ratio, not a per-function verdict: hoisting the four
tracing macros out of `terminate_child` and `finalize_streaming` took the
file from 2 of 5 functions taking a string to 5 of 9, crossing the
threshold.

Measured locally with the `cs` CLI, which reproduces the CI figure:
`cs check <merge-base>:./src/runner/process/child_exit.rs` reports 10.00,
and the same command on this branch's parent reports 9.68 with the
warning. That established the cause was the migration's own hoist rather
than a pre-existing condition.

The remedy follows the repository's existing convention. `command_logging`,
`stdlib/register` and `stdlib/path` already keep their bounded emitters in
`*_emitters.rs` siblings for exactly this reason, and all three score
10.00. The four emitters move to `child_exit_emitters.rs` unchanged; the
module doc records why, including that the rule reads as a ratio.

`cs check` now scores both files 10.00. Behaviour is preserved by
construction: all four format strings are byte-identical to `main`'s and
only their location changed.

Gates: `cargo clippy --workspace --all-targets --all-features -- -D
warnings` clean, and 131 tests pass across the process, streaming and
exit-status selections.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The plan is a gated input: `tests/execplan_status_contract_tests.rs`
parses every plan header. Writing a sweep's results into the plan
therefore invalidates the sweep, so this section names the `-gate9` log
set and leaves the status column blank for the run that follows to fill.
That is the same order-inverting remedy the `#### Final sweep on the
delivered revision` section records, reapplied after three later commits
invalidated that sweep's coverage.

Two defects are described: the mdtablefix tool-version skew that failed
the required `build-test` check, and the CodeScene string-heavy ratio in
`child_exit.rs`.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The `-gate9` sweep ran on the committed revision `94835c96` with a clean
tree both before and after, and all eight targets exited 0. The status
column, left blank in the pre-written record, is filled from the logs
alone; each target's `.exit` sidecar holds its `PIPESTATUS[0]`.

Figures: 179 Python files formatted and 174 Markdown files unchanged;
`make lint` reaching all 11 stage invocations, with Pylint at 10.00/10
and interrogate at 100%; doc-comment coverage 4942/4999 = 98.86%; 175
Markdown files with 0 issues; all Mermaid diagrams validated; 3912/3912
nextest tests with 6 skipped and 129 doctests across two `Doc-tests`
binaries; and 1082 workflow-contract tests with 3 skipped.

Doc-comment coverage moves from 4941/4998 to 4942/4999 because
`child_exit_emitters.rs` adds one documented item; the percentage is
unchanged.

The three Markdown-sensitive targets were then re-run on the post-edit
bytes under a `-gate10` suffix, since this commit edits a gated document
after the sweep. All three pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Both defects were found by CI rather than locally, so CI is what settles
them. The `build-test` job passed on `8caf62b0` in 21m32s with `Format`
success — the step that failed before — and the nine steps CI had
previously skipped behind that failure all ran and passed: Lint Markdown,
Install Whitaker, Lint, Typecheck, Doc coverage, Spelling, Validate Mermaid
diagrams, Workflow contract tests and Release-admission runtime tests.

All four checks in the `main-required-checks` ruleset pass: `build-test`,
`kani-smoke`, `netsukefile` and `release / metadata`. No check on the head
is failing or pending, `CodeScene Code Health Review (main)` now passes
having previously failed on `child_exit.rs`, and the pull request reports
`mergeStateStatus: CLEAN` where it had reported `BLOCKED`.

The three Markdown-sensitive targets were re-run on these bytes under a
`-gate11` suffix; all three pass.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A requested rebase onto the pull request's target branch re-established
the replay boundary before rewriting anything, and the boundary already
held: origin/main was still 84447f0, a direct ancestor of the branch
head, so git replayed nothing.

Three independent sources agree on the target SHA -- git ls-remote over
SSH, the GitHub API's refs/heads/main, and the pull request's own
baseRefOid -- and git rev-list --count origin/main ^HEAD returns zero.
Every identity therefore survives: HEAD is unchanged at 4bd2aa8, the
range-diff maps all twenty commits with '=', and the branch diff against
the target has the same SHA-256 before and after.

The four make gates were re-run on those bytes rather than cited from
the earlier evidence, and all pass: 3912 nextest tests with 6 skipped,
and the three doctest binaries.

The entry also records that the estate's stored main ref in the bare
repository (1e60fb1) is stale and is not the target; only the fetched
remote ref is.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The second-rebase note described the test gate as reaching "the three
doctest binaries". Both runs of `make test` on this branch print exactly
two `Doc-tests` headers — `Doc-tests netsuke` and `Doc-tests test_support`
— and the plan already states the correct figure, "129 across two
`Doc-tests` binaries", forty lines earlier.

The error came from reading the subagent's report, which counted the
`running 2 tests` sub-block inside the netsuke run as a third target. The
count is settled by counting `Doc-tests ` headers, which is measurable
from the log rather than inferred from its shape.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The no-op rebase is now delivered: the branch was pushed over the
command-scoped SSH transport with a lease bound to the pre-rebase head
(4bd2aa8) rather than one refreshed at push time, so a peer session's
concurrent commit would have refused the push instead of being silently
overwritten. The pull request stays a draft.

The same entry is corrected: it said the test gate reached "the three
doctest binaries". Both runs print two `Doc-tests` headers, and the plan
already stated the right figure forty lines earlier. The count is now
taken from the log's `Doc-tests ` headers rather than restated from a
report about it, and the correction's own gates were re-run for the same
reason the rebase's were: a gate verdict belongs to the bytes it read.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The first gate run on 8ac67a2 failed make lint at lint-python with
"Failed to resolve `--with` requirement / Git operation failed". The
cause was not the code: the harness injects GIT_CONFIG_* pairs whose
url.<lody-github::...>.insteadOf rewrite is applied before any helper is
consulted, so uv's fetch of the pinned df12-python-lints dependency was
routed to the Lody remote helper, which fails closed while its broker is
starved. Isolated with two ls-remote calls against the same public repo:
fatal under the ambient config, 4cf41736 with it stripped. A third run
through a wrapper that removes exactly those variables reached every
stage and exited zero on the same bytes; nothing in the repository was
changed to appease it.

Also records the evidence-handling error made while fixing it: a manual
re-run wrote to the path the gate runner owned and destroyed its failure
log. The failure survives as an attributed note, and the final evidence
set is one single-author run with provenance inside each log.

Co-Authored-By: Claude Code <noreply@anthropic.com>
CI caught this rather than the local run: `make spelling` is not one of
the four commit gates, so the word passed every command run this
session while failing the build-test job's Spelling step, which skipped
the six steps behind it. The gate wants en-GB-oxendict -ize.

Fixed in its own module, probed for liveness: re-introducing the word
makes `make spelling` fail again at the same line, so the gate is
reading this file rather than skipping it. The four directed gates plus
`markdownlint`, `nixie` and `doc-coverage` all pass on the corrected
bytes.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…g gap

The plan's closing paragraph named the `-gate-pushrecord2-` run as the
final evidence set. That run gated `8ac67a28`; the delivered revision
`b9bdc38a` was gated afterwards under `-gate-spellfix-`. The paragraph now
names the delivered sweep, records that seven of its eight targets passed
and that `make test` timed out on the load-sensitive packaging cold
build, and cites CI's `success` on the same SHA as the settling evidence
for that timeout.

It also records a spelling failure that only CI could see. `fc656f72`
introduced `canonicalises`; CI's required `build-test` check failed at its
`Spelling` step and skipped the five steps behind it, while the
four-target local sweep was green because none of those four targets
reaches `make spelling`. The word was detectable locally — `typos` flags
it under the repository's own config — but no gate in the sweep read it.
The remedy recorded is to run the stop hook's five targets alongside the
four named, since `markdownlint` is the Makefile path to the spelling
gate.

Documentation only; no behaviour, API, test, or dependency change.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The pull request reported CONFLICTING/DIRTY, which suppresses every
workflow and explains why 8bcba86 had no CI. Re-reading the boundary
showed the local origin/main was stale at 48d4a59 while the remote
read 6b01bb6, so the earlier merge-tree check had been answering a
stale question. Fetched ref, ls-remote and the pull request's own
baseRefOid then agreed on fce1a74.

Replaying 26 commits onto it produced two conflicts, both three-way
collisions between a rename and an edit inside the renamed file. Each
keeps this branch's hoisted tracing emitters and takes main's relocated
module paths. The range-diff maps 25 commits with "=" and only the
hoisting commit with "!", which is exactly the commit whose edits had
to land inside main's renamed files.

Neither side touched a dependency manifest between the old merge base
and the new one, so no lock-file rebuild was needed; agreement between
Cargo.lock and the manifests was checked with cargo metadata --locked
--offline rather than assumed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Both were caught by checking the evidence after writing it.

The 408 files and 4147 insertions belong to the range 84447f0..fce1a74,
not to the commit fce1a74, which is 393 files, 1804 insertions and 1459
deletions. Both figures are right; only the attribution was wrong.

More seriously, the entry claimed the pull request's baseRefOid
corroborated fce1a74 as the boundary. It does not: it reads 84447f0,
the base the pull request was opened against, which GitHub had not
refreshed after rebasing. It is cached drift rather than a live target,
and the boundary rests on the fetched ref and ls-remote, which both read
fce1a74. The correction records the mistake rather than quietly
dropping the claim.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The file has five telemetry::OUTCOME_* references in total, including
the one the conflict resolved; the entry said there were five "other"
ones. Four lie outside the conflict block and all four already read
telemetry::, which is the fact the resolution rests on.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Commit fce1a74 on main added tests/workflow_contracts/rust_module_layout_test.py,
which rejects sibling Rust modules sharing a first `_`-delimited prefix unless the
group is recorded in DISPARATE_PREFIX_EXCEPTIONS with an exact sibling set and a
rationale. This branch's three hoisted emitter modules are exactly that shape, so
all three tripped the new contract:
  child_exit.rs + child_exit_emitters.rs
  path_utils.rs + path_emitters.rs
  register.rs   + register_emitters.rs
The same commit rewrote tools/kani/proof-scope.toml, whose contract recomputes the
Kani harness closure from the source; the two stdlib emitters are reachable from
the harnesses and so had to be named there.

Move each emitter into a same-stem directory beside its host —
child_exit/emitters.rs, path_utils/emitters.rs, register/emitters.rs — declared
with an explicit `#[path]`, the shape register/ already uses for
query_helpers.rs. That removes the shared prefix outright, so no exception entry
is needed, and it is the only shape that satisfies both contracts: `foo.rs`
beside `foo/mod.rs` still forms a `foo` prefix pair, while a plain `mod bar;`
beside a non-`mod.rs` parent trips clippy::self_named_module_files, which
Cargo.toml denies. A `#[path]` changes which file backs a module, never the
module's path in the tree, so every `super::` and `pub(super)` reference resolves
unchanged.

Also fix the one spelling failure from the same gate sweep: `reorganisation` in
the ExecPlan, which typos.toml maps to -z-, and a paragraph wrap that left a
`# 811` at column 0 where Markdown reads it as an H1. The spelling failure had
been masking that: markdownlint's recipe depends on spelling, so markdownlint
never ran.

Document the forced-split rule and the three new support modules in
docs/repository-layout.md, and record the diagnosis in the ExecPlan.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The rebase onto fce1a74 changed the revision without changing the gate set,
and the emitter relocation was its only source change. The nine gates were run
on the frozen revision 021e052: check-fmt, spelling, markdownlint, typecheck,
lint, test, nixie, doc-coverage and test-workflow-contracts. All nine exited 0,
and the logs pin the revision with matching HEAD_AT_START and HEAD_AT_END
fields plus an empty post-gate tree-status block.

Two figures moved against the superseded -gate9 sweep and neither is a
regression: nextest rose 3912 to 3919 and the workflow contracts 1082 to 1107,
both because fce1a74 contributed 7 tests; doc-coverage rose to 98.88%
(5044/5101). The slow packaging test is recorded as a pass at 144.107s rather
than a timeout, because that is what the log shows.

The sweep read bytes that did not yet carry this record, so the section states
which gates can read docs/execplans/ at all and narrows the staleness problem to
four Markdown-reading targets, whose -confirm log set is named before that run.
Local mdtablefix is 0.6.1 while CI pins 0.6.0, so the pinned binary was
installed into an isolated root and both versions were confirmed to accept the
amended text.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@leynos
leynos force-pushed the adopt-rstest-bdd-v0-6-0 branch from 8bcba86 to b66ce68 Compare October 3, 2026 01:13

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • netsuke/src/stdlib/register.rs is usually changed with: netsuke/src/stdlib/config/mod.rs

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant