Skip to content

App Ad asset editing, billing, change history, and an identifier guard - #7

Merged
icoolqin merged 1 commit into
mainfrom
feat/app-ad-assets-billing-changes
Aug 5, 2026
Merged

App Ad asset editing, billing, change history, and an identifier guard#7
icoolqin merged 1 commit into
mainfrom
feat/app-ad-assets-billing-changes

Conversation

@icoolqin

@icoolqin icoolqin commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • ads assets / ads set-assets — read an App Ad's real assets from app_ad.* and edit them in place (add/remove/set), instead of rebuilding the ad group per creative change. ad_group_ad_asset_view retains historical associations and is not used as the source of truth.
  • billing show — funding, net remaining balance, and spend runway. account_budget reports the net amount; --tax-rate prints a gross-equivalent for reconciling with the web UI.
  • changes listchange_event history with the bounded date window and LIMIT the resource requires.
  • Report presets: assets, network, daily-campaign.
  • scripts/check_identifiers.py + .pre-commit-config.yaml — blocks real account identifiers (IDs, balances, emails, live ad copy) from entering this public repo. detect-secrets only recognizes credentials, not business identifiers, so it misses these entirely.

Test plan

  • ruff check . / ruff format --check .
  • pytest (67 tests, 65% coverage, threshold 55%)
  • detect-secrets-hook against tracked files
  • scripts/check_identifiers.py against tracked files and full git history
  • Live smoke test against a real Google Ads account (read-only + --validate-only; no writes)

🤖 Generated with Claude Code

…r guard

## Commands

- `ads assets` reads an App Ad's real asset list from `app_ad.*` with slot fill
  and per-orientation coverage. `ad_group_ad_asset_view` retains historical
  associations and can report more assets than the ad carries, so it is not used
  as the source of truth.
- `ads set-assets` edits App Ad assets in place. Those fields are whole-field
  replacements, so an `update_mask` on `app_ad.images` silently drops anything
  omitted; the command reads current state and applies an add/remove delta on
  top. Caps, duplicates, removing an absent asset, and stripping every visual
  asset are rejected before the API call. This removes the need to rebuild an ad
  group per creative change, which left undeletable ads behind.
- `billing show` reports funding, remaining balance, and runway. account_budget
  returns the NET spendable amount, so a prepay top-up shown as a gross figure
  in the UI yields a shorter runway than expected; `--tax-rate` prints a
  gross-equivalent column to reconcile the two.
- `changes list` surfaces change_event history, supplying the bounded date
  window and LIMIT the resource requires.
- Report presets: assets, network, daily-campaign.

Video orientation is inferred from asset names because the API does not expose
video aspect ratio; explicit ratio tokens take priority over words like
"portrait", which creative names often use for the subject rather than the frame.

Not available through the API and left to the web UI: promotional account
credits, SKAdNetwork reports, and Google's per-orientation Ad Strength breakdown
for App ads (asset_group.asset_coverage is Performance Max only).

## Identifier guard

Building the above put real account IDs, an account balance, live ad copy, and
internal creative naming into tests and docs. detect-secrets reported clean
throughout, because it recognizes credentials, not business identifiers — a real
customer ID is just a ten-digit number to it.

CI is also the wrong layer. It runs on push, and this repository is public: by
the time CI objects the value is already published, and git history keeps it
after any later fix. The gate has to run before the commit.

scripts/check_identifiers.py pairs two rules, wired into pre-commit:

- Denylist: anything in .private-values (gitignored) fails. Precise, but only
  catches values someone remembered to write down.
- Allowlist: every 8+ digit number, grouped payments-shaped ID, and email must
  appear in .identifier-allowlist.txt. This is the rule that catches values
  nobody knew to list, and while wiring it up it flagged a real account_budget
  ID still sitting in tests after a manual sweep for known IDs.

Numeric separators are stripped before matching so a balance cannot hide behind
underscores. The guard also rejected the first draft of its own test file, which
had used real IDs as fixtures; sample values are now assembled from fragments so
no literal identifier exists in a tracked file.

Adding an allowlist line is deliberately a reviewable act: that line is where a
reviewer asks whether the value is real. CI keeps the check as a backstop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@icoolqin
icoolqin merged commit 03f3f2e into main Aug 5, 2026
4 checks passed
@icoolqin
icoolqin deleted the feat/app-ad-assets-billing-changes branch August 5, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant