Skip to content

Post-merge review fixes for #40: unreachable German strings, weakened tests, tier-collapse doc drift - #41

Merged
kipp-ing merged 2 commits into
mainfrom
review-pr40-followups
Jul 25, 2026
Merged

kipp-ing merged 2 commits into
mainfrom
review-pr40-followups

Conversation

@kipp-ing

Copy link
Copy Markdown
Owner

Post-merge review of PR #40 (German localization + Supporter-Unlock tier collapse), and the fixes for everything it found.

The review ran as a 6-dimension fan-out — tier-collapse semantics, catalog wiring, format/plural integrity, Siri phrase localization, test integrity, and repo policy — with every candidate finding routed through an adversarial verifier before it was acted on. 20 candidates, 14 confirmed, 6 refuted, 11 distinct defects after dedup (three dimensions independently found the same plural).

Fixed — localization that never resolved at runtime

  • PhotoAlbumPickerView — the limited-access pool row rendered the model's hardcoded English SourceCollection.title, so the "Ausgewählte Fotos" entry added in German localization + Siri phrases (300), on the 1100 Supporter-Unlock tier collapse #40 only ever appeared when pool enumeration failed. The row now shows and persists the localized name. Row identity moves off the display label onto the sentinel collection ID, which also fixes a latent bug: identity was the label, so a rename would have made an already-added pool look un-added.
  • AlbumPickerView.subtitle — the photo count was concatenated into a plain String, leaving the 1 photo / %lld photos German plurals unreachable.
  • PurchaseViewModel.failureMessage — a bare English String with no catalog entry, rendered verbatim under an already-translated title. Now resolves against .module (a package's strings do not live in the app bundle); German added.

Fixed — tests that had stopped testing

  • refreshIgnoresTipsAndRevokedTransactions asserted against a revoked and a live transaction for the same product, so it passed whether or not isRevoked was honoured — while still claiming @covers FR-1100-12. Split into a revoked-only case (grants nothing) and a live-alongside-revoked case (still grants).
  • FrameIntentGlueTests compared String(localized:) against English literals; those strings gained German in German localization + Siri phrases (300), on the 1100 Supporter-Unlock tier collapse #40, so a de_DE run would go red. Locale pinned to English, keeping the assertions about the contract wording rather than the runner's language.

Fixed — copy and docs

  • The iOS money pledge still said "the unlocks" (plural) post-collapse, with the German translated from the stale plural. Now word-for-word the tvOS string, so both platforms share one catalog entry.
  • Four code comments asserted an English-only policy retired on 2026-07-23, each sitting directly above code that localizes.
  • docs/manual-verification.md — the T042 ASC checklist, the one remaining 1100 task, still asked for "Family Sharing ON for the three non-consumable unlocks" and "Buy Automation". The sibling quickstart.md §5 had the identical sentence corrected by German localization + Siri phrases (300), on the 1100 Supporter-Unlock tier collapse #40; the two are back in step as T041 requires.
  • docs/spec-overview.md, CLAUDE.md, docs/where-the-money-goes.md (the declared canonical source of the string this PR changes), specs/300-slideshow/spec.md (amendment banner + FR-300-30 + US7 + Roadmap), specs/800-app-intents/spec.md, and contracts/uitest-seams.md.
  • docs/handover-release-prep.md marked historical — everything in its "Deferred" list has since shipped.

Refuted under adversarial check

Worth recording, because these were the high-stakes hypotheses:

  • No claw-back. A stale two-tier snapshot genuinely fails to decode, but nil → EntitlementSet.none is the specified contract, tested at EntitlementSnapshotCacheTests.swift:166, and no unlock.pro transaction can exist — nothing has shipped publicly, and FR-1100-13 binds from first public release.
  • Dropped legacy product ids — same reasoning.
  • --uitest-entitlements reachable in a Release build — refuted on five independent checks; the production path only diverges under the launch argument, and the seam writes to a throwaway defaults suite.

Known gaps, now documented rather than papered over

ProductID.uiSlug is genuinely untested — no XCUITest references unlock.price.supporter or unlock.buy.supporter. The contract now says so plainly instead of requiring an assertion that does not exist.

Gates

https://claude.ai/code/session_01XWbnBdWdcjnCH5smD6X1Ai

kipp-ing added 2 commits July 25, 2026 22:39
…test

Post-merge review of PR #40 (German localization + Supporter-Unlock tier
collapse). Every item below was found by a reviewer and independently
reproduced by a second agent before being fixed.

Localization that never resolved at runtime:
- PhotoAlbumPickerView: the limited-access pool row rendered the model's
  hardcoded English `SourceCollection.title`, so the "Ausgewählte Fotos"
  entry added in #40 only appeared when pool enumeration *failed*. The row
  now shows and persists the localized name. Identity moves off the label
  onto the sentinel collection ID, so neither a rename nor a language switch
  can make an added pool look un-added.
- AlbumPickerView.subtitle: the photo count was concatenated into a plain
  String, leaving the `1 photo` / `%lld photos` German plurals unreachable.
- PurchaseViewModel.failureMessage: bare English String with no catalog
  entry, rendered verbatim under an already-translated title. Now resolves
  against `.module` (a package's strings do not live in the app bundle);
  German added to the PurchaseKit catalog.

Tests that had stopped testing:
- EntitlementStoreTests.refreshIgnoresTipsAndRevokedTransactions asserted
  against a revoked *and* a live transaction for the same product, so it
  passed whether or not `isRevoked` was honoured while still claiming
  @Covers FR-1100-12. Split into a revoked-only case (grants nothing) and a
  live-alongside-revoked case (still grants), pinning both directions.
- FrameIntentGlueTests compared `String(localized:)` against English
  literals; those strings gained German in #40, so a de_DE run would go red.
  The locale is now pinned to English, keeping the assertions about the
  contract wording rather than the runner's language.

Copy:
- The iOS money pledge still said "the unlocks" (plural) after the collapse,
  and the German was translated from the stale plural. Now word-for-word the
  tvOS string, so both platforms share one catalog entry.
- Four comments asserted an English-only policy that was retired 2026-07-23,
  each directly above code that localizes. Replaced with the real reason the
  string is or is not translated.

Gates: PurchaseKit 106/106 host green; full iOS suite 163 passed / 0 failed /
5 skipped on iPad Pro 11-inch (M4).

Claude-Session: https://claude.ai/code/session_01XWbnBdWdcjnCH5smD6X1Ai
PR #40 collapsed Pro/Automation into one Supporter Unlock and shipped German,
but left the surrounding documentation describing the old world.

Tier collapse:
- docs/manual-verification.md — the T042 ASC checklist, the one remaining 1100
  task, still asked for "Family Sharing ON for the three non-consumable
  unlocks" and "Buy Automation". Neither exists; the sibling quickstart.md §5
  had the same sentence corrected by #40. Now back in step, as T041 requires.
- docs/spec-overview.md — the map file CLAUDE.md tells agents to start from
  still described two tiers plus a bundle.
- CLAUDE.md — same drift in the active-feature paragraph and the T033 note.
- docs/where-the-money-goes.md — the declared canonical English source of the
  in-app transparency statement was still plural, which would have re-created
  the drift the shipped string just lost.
- specs/1100-purchase-gate/contracts/uitest-seams.md — binding assertion 3
  required `unlock.price.supporter` / `unlock.buy.supporter` to be asserted,
  but no XCUITest references either identifier. It now describes what the
  tests actually assert, and says plainly that `ProductID.uiSlug` is untested.

Shipped German:
- specs/300-slideshow/spec.md — the only spec covering localization still
  declared German deferred and the app English-rendering. Amendment banner
  added in the house style; FR-300-30, the US7 narrative, and the Roadmap
  entry corrected.
- specs/800-app-intents/spec.md — intent titles/phrases are no longer
  English-only; the phrases ship localized via AppShortcuts.xcstrings.
- docs/handover-release-prep.md — marked historical: everything in its
  "Deferred" list (800, 900, the 320 disk cache, the 510 clock overlay,
  German) has since shipped.

Also corrects the test-count claims in CLAUDE.md to the 2026-07-25 measured
gate (106 host, 163/0/5 iOS) and records that #21/#22 were fixed in PR #36 —
the note calling them open failures was itself stale.

Claude-Session: https://claude.ai/code/session_01XWbnBdWdcjnCH5smD6X1Ai
@kipp-ing
kipp-ing merged commit 2d2a4fd into main Jul 25, 2026
1 check passed
@kipp-ing
kipp-ing deleted the review-pr40-followups branch July 25, 2026 22:07
kipp-ing added a commit that referenced this pull request Sep 13, 2026
Post-merge review fixes for #40: unreachable German strings, weakened tests, tier-collapse doc drift
kipp-ing added a commit that referenced this pull request Sep 13, 2026
Post-merge review fixes for #40: unreachable German strings, weakened tests, tier-collapse doc drift
kipp-ing added a commit that referenced this pull request Sep 26, 2026
Post-merge review fixes for #40: unreachable German strings, weakened tests, tier-collapse doc drift
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