From 79b4d1e167f268b3a7b7671d46290f59ca7e3623 Mon Sep 17 00:00:00 2001 From: slhx agent Date: Fri, 12 Jun 2026 16:15:09 +0200 Subject: [PATCH] fix(xtask): give mobile starters app-owned commands Make app new --mobile write hemx-app with HEMX_APP_* release settings, app-scoped release output, and docs that name Workout only as the concrete starter flow instead of leaking Workout command identity. req: ceremony/006 --- docs/recipes/mobile-release.md | 4 ++-- hemx-xtask/src/main.rs | 36 ++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/docs/recipes/mobile-release.md b/docs/recipes/mobile-release.md index 8d024e2..f4a9ae5 100644 --- a/docs/recipes/mobile-release.md +++ b/docs/recipes/mobile-release.md @@ -14,8 +14,8 @@ this path outside the repository: cargo run -p hemx-xtask -- app new --mobile PATH ``` -The created app includes the same `hemx-workout mobile-release` and -`hemx-workout mobile-verify` commands, plus `MOBILE_STARTER.md` naming the host, +The created app includes app-owned `hemx-app mobile-release` and +`hemx-app mobile-verify` commands, plus `MOBILE_STARTER.md` naming the host, recovery, and release-kit boundary. req: ceremony/006 ## When to use this path diff --git a/hemx-xtask/src/main.rs b/hemx-xtask/src/main.rs index 3c6ead9..3d2b29b 100644 --- a/hemx-xtask/src/main.rs +++ b/hemx-xtask/src/main.rs @@ -91,7 +91,7 @@ fn run_app_new(operands: &[String]) -> ExitCode { ); if mobile { println!( - "next\tHEMX_WORKOUT_ORIGIN=https://app.example.com {}/hemx-workout mobile-release", + "next\tHEMX_APP_ORIGIN=https://app.example.com {}/hemx-app mobile-release", destination.display() ); } @@ -217,9 +217,31 @@ fn create_app_scaffold(destination: &Path) -> std::io::Result<()> { fn create_mobile_app_scaffold(destination: &Path) -> std::io::Result<()> { // req: ceremony/006 req: examples/006 create_workout_app(destination)?; + fs::rename( + destination.join("hemx-workout"), + destination.join("hemx-app"), + )?; + for (from, to) in [ + ("hemx_workout_app", "hemx_app"), + ("hemx-workout-app", "hemx-app"), + ("workout-app", "hemx-app"), + ("./hemx-workout", "./hemx-app"), + ("hemx-workout", "hemx-app"), + ("HEMX_WORKOUT_", "HEMX_APP_"), + ("target/hemx-mobile/workout", "target/hemx-mobile/app"), + ("com.hemx.workout", "com.hemx.app"), + ("hemx Workout Copilot", "hemx App"), + ( + "# Workout mobile external blockers", + "# hemx app mobile external blockers", + ), + ("workout-mobile", "app-mobile"), + ] { + replace_in_tree(destination, from, to)?; + } fs::write( destination.join("MOBILE_STARTER.md"), - "# Mobile hemx starter\n\nThis starter is the phone-first Workout path exposed through `app new --mobile`: it has a real page/form/keyed partial/notice flow, typed host haptics/share calls returning through Rust handlers, app-owned command/event/projection recovery truth, and the same `hemx-workout mobile-release` / `hemx-workout mobile-verify` release-kit commands.\n\nIt deliberately does not add a hemx mobile framework, client store, signing secret owner, or store submission bot. req: ceremony/006 req: examples/006\n", + "# Mobile hemx starter\n\nThis starter is the phone-first path exposed through `app new --mobile`: it has a real page/form/keyed partial/notice flow, typed host haptics/share calls returning through Rust handlers, app-owned command/event/projection recovery truth, and app-owned `hemx-app mobile-release` / `hemx-app mobile-verify` release-kit commands.\n\nThe starter uses the Workout product flow as a concrete first app, but its command surface, crate name, binary name, release output, and environment variables are owned by the created app. It deliberately does not add a hemx mobile framework, client store, signing secret owner, or store submission bot. req: ceremony/006 req: examples/006\n", )?; Ok(()) } @@ -1250,17 +1272,21 @@ mod tests { create_mobile_app_scaffold(&destination).expect("create mobile app scaffold"); let mobile_readme = fs::read_to_string(destination.join("MOBILE_STARTER.md")).expect("mobile docs"); - let command = fs::read_to_string(destination.join("hemx-workout")).expect("command"); + let command = fs::read_to_string(destination.join("hemx-app")).expect("command"); let lib_rs = fs::read_to_string(destination.join("src/lib.rs")).expect("lib rs"); let template = fs::read_to_string(destination.join("templates/workout.heml")).expect("template"); - assert!(mobile_readme.contains("phone-first Workout path")); + assert!(mobile_readme.contains("phone-first path")); assert!(mobile_readme.contains("typed host haptics/share calls")); assert!(mobile_readme.contains("command/event/projection recovery truth")); + assert!(mobile_readme.contains("app-owned `hemx-app mobile-release`")); assert!(mobile_readme.contains("does not add a hemx mobile framework")); assert!(command.contains("mobile-release")); assert!(command.contains("mobile-verify")); + assert!(command.contains("HEMX_APP_ORIGIN")); + assert!(command.contains("target/hemx-mobile/app")); + assert!(!command.contains("HEMX_WORKOUT_")); assert!(command.contains("app-owned command/event/projection records")); assert!(command.contains("external_blockers")); assert!(lib_rs.contains("HostCall::Share")); @@ -1269,6 +1295,8 @@ mod tests { assert!(template.contains("