Skip to content

fix(manifest): a configured source_dirs must survive a manifest discovery failure, and an in-repo includeBuild must be judged by its path - #724

Merged
0xLeif merged 3 commits into
mainfrom
0xleif/fix-723-landing
Aug 27, 2026
Merged

fix(manifest): a configured source_dirs must survive a manifest discovery failure, and an in-repo includeBuild must be judged by its path#724
0xLeif merged 3 commits into
mainfrom
0xleif/fix-723-landing

Conversation

@0xLeif

@0xLeif 0xLeif commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Closes #723. 6.0 release blocker — a real adopter cannot use any v6 candidate at all.

The report, confirmed across the whole line

Every SpecSync 6 candidate from rc.1 through rc.7 fails coverage on our valid includeBuild("vendor/podo-shared"), even with source_dirs = ["app/src/main/java"] explicitly configured. view and change new still work, but check --strict and coverage exit 1, so v6 cannot gate CI here. There is no working v6 RC to pin; v5.2.0 works and reports 24/29 files covered (82.76%).

Reproduced locally before fixing:

check --strict exit=1
coverage       exit=1
error: Coverage inconclusive: Cannot parse Gradle settings manifest
       settings.gradle.kts: Unsupported Gradle workspace mutator includeBuild

Two defects; the second is the one that matters

A — includeBuild was rejected on the token prefix, never the path. src/manifest.rs decided on token.starts_with("include") and never looked at the argument, so an in-repo composite build was refused identically to one escaping the repository. Every fixture in the guarding test used "../outside", so the ordinary case was never covered.

Now judged by argument: a literal path beneath the root parses and contributes no module; escapes, interpolation, dynamic expressions, multiple arguments and trailing config blocks keep failing closed, and the error now names the argument.

B — a discovery failure overrode an explicitly configured source_dirs and aborted coverage. This is why setting it did not help them. Other call sites degrade (config.rs uses unwrap_or_else, validator.rs:430 falls back to a scan); coverage propagated with ?.

Discovery exists to infer what the user did not state. When the user has stated it, a failure to infer must not veto it. It is now a notice rendered beside the coverage figures — deliberately non-gating, since gating would put the adopter back where they started, and unlike a shrunken denominator a notice cannot inflate a percentage.

B is the more valuable fix: it removes the whole class. No unreadable manifest, in any ecosystem, can override an explicit declaration again.

Verified against the reported case, not just unit tests

Same fixture, this build:

coverage       exit=0   File coverage: 1/1 (100%)   LOC coverage: 2/2 (100%)
check --strict exit=1   -> 13 warnings, ALL scaffold placeholders

Zero Gradle/manifest/mutator hits remain in either output. The remaining exit 1 is --strict treating unfilled-scaffold warnings as errors — correct behaviour on a fixture whose specs were never written, not the reported bug.

Tests, and which fail against main

All four fail on current main, verified by disabling both fixes in place and re-running so each failed with its own message rather than a shared compile error:

  • gradle_settings_accept_an_in_repo_include_build
  • gradle_settings_still_refuse_an_escaping_or_dynamic_include_buildhonest label: its refusals pass unfixed too; what fails is the diagnostic, which previously said the same thing about an escape and a valid build
  • configured_source_dirs_survive_a_manifest_that_cannot_be_parsed
  • unsafe_gradle_discovery_degrades_over_stated_source_dirs_without_escaping

The precedence test asserts both halves in one test — stated source_dirs completes with a notice; the same tree with the flag off is still fatal — so it cannot pass against a change that merely stopped failing. Its manifest is malformed rather than unsupported, so fixing A cannot make it vacuous.

Two things found on the way

Six existing integration tests were asserting the wrong case. Every gradle_*_is_inconclusive_for_coverage_gating_commands fixture built on setup_minimal_project, which states sourceDirs, so all six were exercising the configured path while using the exit code as a proxy for safety. Each now clears it and still asserts the fail-closed contract on an inferred list, and the new integration test checks the safety properties directly — no byte outside the root disclosed, nothing generated from rejected discovery.

A first-cut bug in the fix itself: judging includeBuild by argument left its position unjudged, so if (x) { includeBuild("vendor/s") } was refused on one line and accepted across three. Settled by accepting both — a composite build contributes no module whether or not its branch runs — deliberately asymmetric with include, which stays refused when conditional.

cargo clippy -- -D warnings clean, change check exit 0, change audit --strict clean, 2389 unit + 406 integration.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DgYAvsQM6P9fKxDotnDuzP

0xLeif and others added 3 commits August 27, 2026 08:54
…-manifest-discovery-failure-and-an-in-repo-includebuild-must-be-judged-by-its
…fest-discovery-failure-and-an-in-repo-includebuild-must-be-judged-by-its verification
… fix

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgYAvsQM6P9fKxDotnDuzP
@0xLeif
0xLeif requested a review from a team as a code owner August 27, 2026 15:11
@0xLeif
0xLeif requested review from 0xGaspar, Kyntrin and tofu-ux and removed request for a team August 27, 2026 15:11
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@0xLeif
0xLeif merged commit fe55a21 into main Aug 27, 2026
21 checks passed
@0xLeif
0xLeif deleted the 0xleif/fix-723-landing branch August 27, 2026 15:19
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.

includeBuild is rejected by token prefix, so a valid in-repo Gradle composite build blocks check and coverage entirely

1 participant