diff --git a/PLAN.md b/PLAN.md index 8972691..3a78160 100644 --- a/PLAN.md +++ b/PLAN.md @@ -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 current `hemx-build` frontier now adversarially proves all class/form/event/slot/atom/handle extraction, field/parameter metadata, invalid-name, collision, deduplication, and keyed-upgrade behavior through `AppBuilder::run`; resource extraction is mutation-clean, leaving only internal diagnostic/code-generation survivors before the full package gate. 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 current `hemx-build` frontier now mutation-proves resource extraction plus canonical generated Rust/global exports/symbol manifests across keyed/component/collision/deduplication branches; the full 1,368-mutant package gate was runnable but exceeded 30 minutes after exposing 187 remaining survivors, led by `AppBuilder::run`, collection/error propagation, stylesheet parsing, and template-context diagnostics. 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 diff --git a/hemx-build/src/lib.rs b/hemx-build/src/lib.rs index c43c252..7da79af 100644 --- a/hemx-build/src/lib.rs +++ b/hemx-build/src/lib.rs @@ -548,7 +548,7 @@ impl Resources { let root_indent = usize::default(); if global_exports { - self.push_component_refs(&mut out, root_indent); + self.push_component_refs(&mut out); self.push_resource_modules(&mut out, &component_names, None, root_indent); } self.push_lowering_api(&mut out, None, root_indent); @@ -567,19 +567,15 @@ impl Resources { out } - fn push_component_refs(&self, out: &mut String, indent: usize) { - let pad = " ".repeat(indent); - let inner = " ".repeat(indent + 1); - out.push_str(&format!( - "{pad}#[allow(non_upper_case_globals)]\n{pad}pub mod components {{\n" - )); + fn push_component_refs(&self, out: &mut String) { + out.push_str("#[allow(non_upper_case_globals)]\npub mod components {\n"); for component in self.component_names() { out.push_str(&format!( - "{inner}pub const {component}: ::hemx::ComponentRef = ::hemx::ComponentRef::new({});\n", + " pub const {component}: ::hemx::ComponentRef = ::hemx::ComponentRef::new({});\n", rust_str(&component) )); } - out.push_str(&format!("{pad}}}\n")); + out.push_str("}\n"); } fn push_resource_modules( @@ -752,14 +748,8 @@ impl Resources { .values() .filter(|res| component_matches(res, component)) { - push_root_export( - out, - &pad, - "targets", - &mut root_exports, - &res.ident, - "target", - ); + root_exports.insert(res.ident.clone()); + out.push_str(&format!("{pad}pub use self::targets::{};\n", res.ident)); } out.push('\n'); @@ -2453,6 +2443,87 @@ mod tests { assert!(generated_with_globals.contains("pub mod advanced {")); assert!(generated_with_globals.contains("pub mod slots {")); + let mut row_slot = resource("row", "row", 21); + row_slot.keyed = true; + resources.slots.insert("row".into(), row_slot); + resources + .handles + .insert("edit".into(), resource("edit", "row", 22)); + resources + .atoms + .insert("selection".into(), resource("selection", "row", 23)); + resources + .slots + .insert("child".into(), resource("child", "page", 24)); + resources + .handles + .insert("child_handle".into(), resource("child_handle", "child", 25)); + resources + .slots + .insert("shared".into(), resource("shared", "page", 26)); + resources + .handles + .insert("shared".into(), resource("shared", "page", 27)); + resources.forms.insert( + "shared".into(), + FormResource { + resource: resource("shared", "page", 28), + controls: Vec::new(), + }, + ); + for (symbol, ident, component, token) in [ + ("a::card", "card", "page", "card"), + ("b::card", "card", "row", "card"), + ("z::last", "last", "page", "last"), + ] { + resources.classes.insert( + symbol.into(), + ClassToken { + symbol: symbol.into(), + ident: ident.into(), + component: component.into(), + token: token.into(), + }, + ); + } + for (component, name) in [("page", "click"), ("row", "click"), ("z", "submit")] { + resources.events.insert( + format!("{component}::{name}"), + EventToken { + symbol: format!("{component}::{name}"), + ident: name.into(), + component: component.into(), + name: name.into(), + }, + ); + } + resources + .handle_forms + .insert("save".into(), "profile".into()); + resources + .handle_params + .entry("save".into()) + .or_default() + .insert("item_id".into()); + let canonical_generated = resources.generated_rs(false); + let canonical_globals = resources.generated_rs(true); + let canonical_syms = resources.syms(); + assert_eq!( + stable_id("generated-rs", &canonical_generated), + 3_748_714_336, + "canonical generated Rust changed" + ); + assert_eq!( + stable_id("generated-rs-global", &canonical_globals), + 2_783_766_475, + "canonical global-export Rust changed" + ); + assert_eq!( + stable_id("generated-syms", &canonical_syms), + 984_222_700, + "canonical symbol manifest changed" + ); + assert_eq!(Resources::default().client_bootstrap().unwrap(), ""); resources .client_handlers