Post-merge review fixes for #40: unreachable German strings, weakened tests, tier-collapse doc drift - #41
Merged
Conversation
…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
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
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.
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 EnglishSourceCollection.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 plainString, leaving the1 photo/%lld photosGerman plurals unreachable.PurchaseViewModel.failureMessage— a bare EnglishStringwith 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
refreshIgnoresTipsAndRevokedTransactionsasserted against a revoked and a live transaction for the same product, so it passed whether or notisRevokedwas honoured — while still claiming@covers FR-1100-12. Split into a revoked-only case (grants nothing) and a live-alongside-revoked case (still grants).FrameIntentGlueTestscomparedString(localized:)against English literals; those strings gained German in German localization + Siri phrases (300), on the 1100 Supporter-Unlock tier collapse #40, so ade_DErun would go red. Locale pinned to English, keeping the assertions about the contract wording rather than the runner's language.Fixed — copy and docs
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 siblingquickstart.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, andcontracts/uitest-seams.md.docs/handover-release-prep.mdmarked historical — everything in its "Deferred" list has since shipped.Refuted under adversarial check
Worth recording, because these were the high-stakes hypotheses:
nil → EntitlementSet.noneis the specified contract, tested atEntitlementSnapshotCacheTests.swift:166, and nounlock.protransaction can exist — nothing has shipped publicly, and FR-1100-13 binds from first public release.--uitest-entitlementsreachable 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.uiSlugis genuinely untested — no XCUITest referencesunlock.price.supporterorunlock.buy.supporter. The contract now says so plainly instead of requiring an assertion that does not exist.Gates
BrokerSetupUITestsand ShareSheetIncomingUITests: testIncomingLinkPrefillsSetupWhenUnconfigured is order-dependent — passes alone, fails in the full suite #22ShareSheetIncomingUITestsnow pass; theCLAUDE.mdnote calling them open failures was itself stale and has been correctedhttps://claude.ai/code/session_01XWbnBdWdcjnCH5smD6X1Ai