feat(workout): surface store submission blockers

Record Google Play and App Store Connect submission targets as external mobile blockers, and make verification require those blockers to be acknowledged in the release kit.

req: examples/006
This commit is contained in:
slhx agent
2026-06-12 12:57:13 +02:00
parent 815392b232
commit f13e21868c
4 changed files with 22 additions and 8 deletions
+1 -1
View File
@@ -810,7 +810,7 @@ what a valid business email is.
005 Canonical examples must not contain user-authored browser JavaScript. They may load the shared hemx runtime (`/hemx.js`) and may use declarative `data-hemx-*` attributes; inline `<script>`, `on*=` event handlers, and `javascript:` URLs are forbidden outside opaque leaf-widget examples.
### req: examples/006
006 The Workout exemplar must have one boring command surface for local development, product tests, production server build, Android/iOS mobile release metadata, and mobile verification. The mobile commands must make app identity, version, production origin, runtime asset policy, cache/offline state policy, environment/secrets boundary, rollback expectation, and external store-signing blockers explicit without adding a broad `hemx-mobile` framework.
006 The Workout exemplar must have one boring command surface for local development, product tests, production server build, Android/iOS mobile release metadata, and mobile verification. The mobile commands must make app identity, version, production origin, runtime asset policy, cache/offline state policy, environment/secrets boundary, rollback expectation, and external store-signing/submission blockers explicit without adding a broad `hemx-mobile` framework.
---
+5 -4
View File
@@ -75,10 +75,11 @@ The command writes release-ready metadata, not store-signed binaries. That is th
honest boundary: producing `.aab`/`.apk` and `.ipa` files requires vendor SDKs,
signing credentials, and store accounts on the release machine.
Android blockers are reported when the Android SDK/JDK/signing key are not
visible. iOS blockers are reported when Xcode or the Apple signing team is not
visible. These blockers are copied into `BLOCKERS.md` so the release kit can be
reviewed without guessing what is still external. req: examples/006
Android blockers are reported when the Android SDK/JDK/signing key or Play
Console submission target are not visible. iOS blockers are reported when Xcode,
the Apple signing team, or the App Store Connect submission team are not visible.
These blockers are copied into `BLOCKERS.md` so the release kit can be reviewed
without guessing what is still external. req: examples/006
## What this does not add
+3 -2
View File
@@ -87,8 +87,9 @@ state, and host capability boundaries fail closed with the mobile artifacts. The
kit records app identity, version, production origin, runtime asset policy,
cache/offline state policy, secrets/signing boundaries, rollback expectations,
Android TWA metadata, iOS WebView metadata, and any external blocker such as
missing Android SDK, Xcode, or store signing credentials. See
`docs/recipes/mobile-release.md`. req: examples/006
missing Android SDK, Xcode, store signing credentials, or Play/App Store
submission account targets. See `docs/recipes/mobile-release.md`. req:
examples/006
## Boundaries proven
+13 -1
View File
@@ -333,6 +333,9 @@ fn mobile_external_blockers(config: &WorkoutMobileConfig) -> Vec<String> {
if env::var_os("HEMX_WORKOUT_ANDROID_KEYSTORE").is_none() {
blockers.push("Android signing key not configured: set HEMX_WORKOUT_ANDROID_KEYSTORE for store-ready signing".into());
}
if env::var_os("HEMX_WORKOUT_GOOGLE_PLAY_TRACK").is_none() {
blockers.push("Google Play submission target not configured: set HEMX_WORKOUT_GOOGLE_PLAY_TRACK after choosing the Play Console track outside this repo".into());
}
if !has_command("xcodebuild") {
blockers.push(
"Xcode command line tools not found: iOS archive/export requires xcodebuild on macOS"
@@ -342,6 +345,9 @@ fn mobile_external_blockers(config: &WorkoutMobileConfig) -> Vec<String> {
if env::var_os("HEMX_WORKOUT_IOS_TEAM_ID").is_none() {
blockers.push("iOS signing team not configured: set HEMX_WORKOUT_IOS_TEAM_ID for App Store/TestFlight export".into());
}
if env::var_os("HEMX_WORKOUT_APP_STORE_CONNECT_TEAM").is_none() {
blockers.push("App Store Connect submission team not configured: set HEMX_WORKOUT_APP_STORE_CONNECT_TEAM after choosing the Apple account outside this repo".into());
}
blockers
}
@@ -573,7 +579,7 @@ fn workout_mobile_blockers_md(blockers: &[String]) -> String {
fn android_release_readme(config: &WorkoutMobileConfig) -> String {
format!(
"# Workout Android release\n\nUse `twa-release.json` as the Android shell authority for `{}`. Build the hemx server with the same release and serve `{}/` over HTTPS. Android SDK, Java, and signing credentials are external inputs; this repository does not own them.\n",
"# Workout Android release\n\nUse `twa-release.json` as the Android shell authority for `{}`. Build the hemx server with the same release and serve `{}/` over HTTPS. Android SDK, Java, signing credentials, Play Console account state, and submission track are external inputs; this repository does not own them.\n",
config.android_package, config.origin
)
}
@@ -1055,6 +1061,12 @@ mod tests {
assert!(failures
.iter()
.any(|failure| failure.contains("BLOCKERS.md") && failure.contains("Android")));
assert!(failures
.iter()
.any(|failure| failure.contains("Google Play")));
assert!(failures
.iter()
.any(|failure| failure.contains("App Store Connect")));
let _ = fs::remove_dir_all(&config.out_dir);
}