Repository navigation
feat(play): replace Google Play store listing images through Hands - #810
Merged
stamp-build[bot] merged 2 commits intoOct 27, 2026
Merged
Conversation
stamp-build
Bot
force-pushed
the
agent/codex-android-devops/play-store-images
branch
3 times, most recently
from
October 27, 2026 18:33
ce8b070 to
6d8fd24
Compare
Stacked on the store listing text change. The same push endpoint now also accepts multipart/form-data (a `request` JSON part plus one file part per image) and applies edits.images in the same Play edit, after the text. - worker: Google's full imageType enum (appImageTypeUnspecified is "not supported yet"); Play's documented format, pixel-size, count and 15,728,640-byte limits checked before any Play call; 24 MiB per push; receipt records each image's sha256, size, type, dimensions and Play id; an adapter that does not acknowledge images fails closed. - play-adapter: edits.images.list/deleteall/upload (with aiGeneratedState); a set Play already holds byte-for-byte is skipped; upload sha256 must match; committed sets are read back. - cli: push --images <dir> accepts <dir>/<device>/<lang>/NN.png (designer export: phone, tablet-7, tablet-10, feature-graphic, ...) and <dir>/<lang>/<imageType>/NN.png, explicit folder-language table, default/. - tests use two real designer exports as fixtures; docs updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng closed Review follow-up for store listing images. Whether Google's Image.sha256 is a digest of the uploaded bytes is undocumented, so it no longer fails a push. - play-adapter: each uploaded image records sha256 (Hands' digest of the bytes sent), provider_sha256 (Google's, null when absent or not a digest) and sha256_match true|false|null. play_image_integrity_mismatch is removed. Commit readback fails only on an image-count mismatch and records sha256 agreement. Hands' own declared-vs-uploaded sha256 check stays fail-closed. - worker: image changes are narrowed to shape-checked fields; mismatches are listed as image_warnings (provider_sha256_mismatch) in the response and receipt. - cli: prints image warnings. Docs updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stamp-build
Bot
force-pushed
the
agent/codex-android-devops/play-store-images
branch
from
October 27, 2026 18:40
6d8fd24 to
12b9c90
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Screenshots and graphics are the other half of the store listing. The designer's export for Raft (phone, 7-inch and 10-inch tablet, feature graphic; en / zh-Hans / ja) should go to Play in the same edit as the text, and be checked against Play's rules before anything is sent.
What
Transport: the same
POST …/distributions/play/store-listingalso acceptsmultipart/form-data. It carries arequestJSON part and one file part per image. The JSON is the #809 body plusimages:[{language, imageType, images:[{part, sha256, aiGeneratedState?}]}].I chose multipart over staging in R2 and streaming. Store images are small (51 files, 9.4 MB) and don't need to be kept in Hands, so there is nothing to stage, seal or clean up. The cost is a cap of 24 MiB of images per push; split larger sets with
--locales.Worker (validates before any Play edit is opened)
imageTypeaccepts Google's full 9-value enum. Implemented: phone, 7-inch, 10-inch, TV and Wear screenshots, icon, featureGraphic, tvBanner.appImageTypeUnspecifiedreturnsimage_type_not_supported, never "invalid".aiGeneratedStateand the Play image id.play_images_not_acknowledgedand a receipt.Play adapter
edits.images.list,deleteallandupload(withaiGeneratedState).image_language_has_no_listing.Image.sha256is recorded, not enforced: each image getssha256(ours),provider_sha256andsha256_match: true|false|null; a mismatch is animage_warningsentry, not a failure. Committed sets are read back from a fresh edit (count must match). Our own declared-vs-uploaded sha256 check stays fail-closed.CLI:
hands store-listing push <app> [--listing …] --images <dir> [--ai-generated-state …] [--commit]<dir>/<device>/<lang>/NN.png, with devices phone, tablet-7, tablet-10, feature-graphic, icon, tv, wear and tv-banner;<dir>/<lang>/<imageType>/NN.png.default/folder applies to every target language that has no set of its own.Sources
imageTypeandaiGeneratedStateenums, upload max size.Unknowns
Image.sha256is the hash of the uploaded bytes isn't documented (fastlane supply relies on it).sha256_matchin the receipt will show it on the first real upload.Deploy
Follow #809's order (
docs/google-play-operations.md); no migration beyond #809's. A new worker with an old adapter fails closed on any push with images.Tests
aiGeneratedStatequery;feedback_closure_reason_migration, also fails on main. Fixtures are two real designer exports: a 1080×1920 phone screenshot and the 1024×500 feature graphic. Covers:default/, the language table, and the multipart request shape.docs/public/cli-reference.md(rules table),docs/google-play-operations.md(transport choice).🤖 Generated with Claude Code