fix(xtask): map mutation shards correctly

Translate one-based public shard numbers to mutest's zero-based API, retain requested labels in output paths, and grant compiler-probe tests a 120-second per-mutant floor. Corrected hemx-build shard 1/8 now passes.

req: test/022

req: test/023
This commit is contained in:
slhx agent
2026-07-17 09:07:50 +02:00
parent fdae13b2b6
commit 5586f87f43
4 changed files with 14 additions and 10 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file
- Add only durable style, ownership, gotchas, and at most a few stable commands agents should actually run.
- Prefer links or pointers to canonical sources over copied lists.
- Avoid project trees, architecture maps, generated inventories, current file sizes, issue lists, TODO inventories, and other snapshots that will rot.
- Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- mutation [PACKAGE] [SHARD/TOTAL]`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate refs`. Use the xtask runner for full verification so jobs are capped from local CPU and memory and commands resolve the workspace independently of the caller's directory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. Keep fast crate tests, focused browser smoke, and full xtask authority distinct; the full path should stay within a documented 10 minute local timeout or use every deterministic native mutation shard under the same wrapper. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014 req: test/015 req: test/016 req: test/022 req: test/023
- Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- mutation [PACKAGE] [SHARD/TOTAL]`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate refs`. Use the xtask runner for full verification so jobs are capped from local CPU and memory and commands resolve the workspace independently of the caller's directory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. Keep fast crate tests, focused browser smoke, and full xtask authority distinct; mutation shards use one-based `1..=TOTAL` numbering and allow the wrapper's 120-second floor for repo-owned compiler probes. The full path should stay within a documented 10 minute local timeout or use every deterministic mutation shard under the same wrapper. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014 req: test/015 req: test/016 req: test/022 req: test/023
- Example behavior tests should prefer `hemx_test` generated-resource assertion methods over raw slot constants, raw effect/payload matching, or boolean predicates wrapped in opaque `assert!`; failures should include the expectation and actual effects, while rendered target/handle assertions should name the generated resource. Keep browser selector helpers as test adapters only, not authoring APIs. Process-backed tests use the RAII `TestProcess` harness rather than duplicating readiness loops and child cleanup. req: test/008 req: test/009 req: test/010 req: test/017 req: test/018 req: test/019
- Run the workout product exemplar with `cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`; set `HEMX_WORKOUT_ADDR=127.0.0.1:3030` if the default port is busy. Its durable visual direction and recovery expectations live in `examples/workout/DESIGN.md`. req: examples/008
- Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013
+1 -1
View File
@@ -21,7 +21,7 @@
- [ ] **State:** In progress — the package-native capped xtask entry point is reachable, rejects unknown packages, propagates mutest failure, and mutation-tests `hemx-axum`, `hemx-core`, `hemx-js`, and the full `hemx-test` package cleanly; full package closure remains.
- **User value:** maintainers can run one bounded repository command and trust that meaningful Rust logic across every mutation-applicable library is either killed or explicitly justified.
- **Build:** add a capped `hemx-xtask` mutation command that invokes `/opt/repositories/mutest`/`mutest` through package-native test targets rather than the broken workspace-wide example path; enumerate only current mutation-applicable library/proc-macro packages; finish adversarial tests or simplify code until every survivor is classified; keep equivalent, invariant-only, and infrastructure-inapplicable classifications inspectable and minimal; document the exact local release command in the existing readiness surface.
- **Blocked by:** none; broad survivors currently remain in `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The mutation entry point now accepts validated native `SHARD/TOTAL` operands with shard-specific output directories while preserving the unsharded gate. `hemx-build` shards `1/4` and `2/4` passed through xtask with 325 mutants each (`1/4`: 280 caught, 45 unviable; `2/4`: 286 caught, 39 unviable); shards `3/4` and `4/4` remain before the package gate is complete. The complete 470-mutant `hemx-axum` package gate now passes with 262 caught and 208 unviable after public page/form/multipart/registry/response/runtime proofs and narrow classification of infallible header parsing and streamed multipart unwrap-equivalent mutants.
- **Blocked by:** none; broad survivors currently remain in `hemx-build`, `hemx-derive`, and `hemx-lsp` outside already-clean focused contracts. The mutation entry point now accepts validated native `SHARD/TOTAL` operands with shard-specific output directories while preserving the unsharded gate. The shard wrapper now correctly maps one-based public numbering to mutest's zero-based native API and grants repo-owned compiler-probe tests a 120-second per-mutant floor instead of reporting false timeouts. Corrected `hemx-build` shard `1/8` passed with 163 mutants (87 caught, 76 unviable); shards `2/8` through `8/8` remain. The complete 470-mutant `hemx-axum` package gate now passes with 262 caught and 208 unviable after public page/form/multipart/registry/response/runtime proofs and narrow classification of infallible header parsing and streamed multipart unwrap-equivalent mutants.
- **Proof:** the new xtask mutation command exits zero within its documented bound, covers each applicable package, emits no unexplained missed mutant, and a deliberate adjacent mutation makes it fail. `cargo run -p hemx-xtask -- test` remains green. req: test/020 req: test/021
## 3. Elect and enforce the release license policy
+2 -2
View File
@@ -704,9 +704,9 @@ what a valid business email is. [north_star]
021 Unexplained missed mutants must block release; equivalent, invariant-only, and infrastructure-inapplicable mutants must be explicitly classified. [north_star]
022 `cargo run -p hemx-xtask -- mutation [PACKAGE] [SHARD/TOTAL]` must run capped exhaustive package-native mutation tests and fail on unexplained survivors. [north_star]
022 `cargo run -p hemx-xtask -- mutation [PACKAGE] [SHARD/TOTAL]` must run capped exhaustive package-native mutation tests with enough per-mutant time for repo-owned compiler probes, and fail on unexplained survivors. [north_star]
023 A supplied mutation `SHARD/TOTAL` must select a deterministic native shard and reject invalid bounds; omission preserves the full-package gate. [north_star]
023 A supplied mutation `SHARD/TOTAL` uses one-based `1..=TOTAL` numbering, maps to the deterministic native shard, and rejects invalid bounds; omission preserves the full-package gate. [north_star]
---
+10 -6
View File
@@ -1304,7 +1304,8 @@ fn mutation_shard(shard: Option<&str>) -> Result<Option<String>, String> {
"invalid mutation shard `{shard}`; require TOTAL >= 2 and 1 <= SHARD <= TOTAL"
));
}
Ok(Some(format!("{index}/{total}")))
// mutest numbers shards from zero; xtask presents the conventional 1..=TOTAL surface.
Ok(Some(format!("{}/{total}", index - 1)))
}
fn run_mutation_plan(package: Option<&str>, shard: Option<&str>) -> ExitCode {
@@ -1315,7 +1316,8 @@ fn run_mutation_plan(package: Option<&str>, shard: Option<&str>) -> ExitCode {
return ExitCode::from(2);
}
};
let shard = match mutation_shard(shard) {
let requested_shard = shard.map(str::to_owned);
let native_shard = match mutation_shard(shard) {
Ok(shard) => shard,
Err(error) => {
eprintln!("{error}");
@@ -1337,7 +1339,7 @@ fn run_mutation_plan(package: Option<&str>, shard: Option<&str>) -> ExitCode {
for package in packages {
eprintln!("\n==> mutation: {package}");
let output = shard.as_ref().map_or_else(
let output = requested_shard.as_ref().map_or_else(
|| output_root.join(package),
|shard| {
output_root
@@ -1361,11 +1363,13 @@ fn run_mutation_plan(package: Option<&str>, shard: Option<&str>) -> ExitCode {
"--annotations",
"none",
"--no-times",
"--minimum-test-timeout",
"120",
"--exhaustive",
"-o",
])
.arg(output);
if let Some(shard) = &shard {
if let Some(shard) = &native_shard {
command.args(["--shard", shard]);
}
let status = command.status();
@@ -1628,8 +1632,8 @@ mod tests {
assert!(error.contains("hemx-core"));
assert_eq!(mutation_shard(None).unwrap(), None);
assert_eq!(mutation_shard(Some("1/4")).unwrap().as_deref(), Some("1/4"));
assert_eq!(mutation_shard(Some("4/4")).unwrap().as_deref(), Some("4/4"));
assert_eq!(mutation_shard(Some("1/4")).unwrap().as_deref(), Some("0/4"));
assert_eq!(mutation_shard(Some("4/4")).unwrap().as_deref(), Some("3/4"));
for invalid in ["1", "a/4", "1/a", "0/4", "5/4", "1/1"] {
assert!(
mutation_shard(Some(invalid)).unwrap_err().contains(invalid),