Squash of 63 commits spanning waves 46-93 (plus interspersed docs commits) of the gradual Postbox->TelegramEngine consumer-side migration. Scope: 139 files changed, 2123 insertions(+), 452 deletions(-). ## Themes by wave-block **Waves 46-58 — Peer field migrations + facade additions** Foundational EnginePeer convenience init additions (PeerReference, RenderedPeer, SelectivePrivacyPeer). Multiple `peer: Peer` field migrations across PeerInfo, ChatList, and SettingsUI components. **Waves 59-73 — peer field cascade + EnginePeer wrap drops** Series of single- to two-file peer-field migrations; consumer-side wrap removal (`EnginePeer(peer)` -> direct EnginePeer use); `as? TelegramUser` cast conversion to `case let .user(...)` enum match. Wave 64: RenderedPeer convenience init. Wave 68: SelectivePrivacyPeer convenience init. **Waves 74-83 — controller-Node bridge cleanup + small migrations** Wave-71 shadow-pattern cleanup at controller->Node bridges. Migrations of ChatRecentActionsController.peer (74), PeerInfoMember (75), MentionChatInputPanelItem (76), PassportUI SecureIdAuthController (77), AccountWithInfo + ShareController (78), peerInputActivitiesPromise (79), InactiveChannel (80), BlockedPeers (81), openHashtag resolveSignal (82), NotificationExceptionsList (83). **Waves 84-90 — TelegramEngine.Resources facade migrations** Per-method Shape-A/B sweeps converting `<ctx>.account.postbox.mediaBox.X(...)` to `<ctx>.engine.resources.X(...)`. Wave 90 was a single-commit big sweep: 40 fetchedMediaResource sites in 25 files migrated to engine.resources.fetch facade in one atomic pass with first-pass-clean build. Methods covered: storeResourceData, completedResourcePath, cancelInteractiveResourceFetch, resourceRangesStatus, resourceStatus, fetch (fetchedMediaResource). **Waves 91-92 — additional type migrations** Wave 91: ItemListWebsiteItem.peer + RecentSessionsController enum-case payload + openWebSession callback Peer? -> EnginePeer?. Wave 92: ChatListController StateHolder.EntryContext status type MediaResourceStatus -> EngineMediaResource.FetchStatus. **Wave 93 — speculative `import Postbox` drop sweep** Drop import from 7 wave-touched files where it became unused; restore in 5 files where bare PeerId/Message/MediaId/StoryId references escaped the pre-flight regex. Includes one MediaId(...) -> EngineMedia.Id(...) swap in InAppPurchaseManager to unlock its import drop. ## Build state Final state at squash: clean Telegram/Telegram build at debug_sim_arm64. ## Persistent-state notes - Pre-existing WIP unchanged across the squashed range: - build-system/bazel-rules/sourcekit-bazel-bsp submodule marker - Untracked: build-system/tulsi/, submodules/TgVoip/, third-party/libx264/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 KiB
Wave 47 — PeerInfoHeaderNode.peer stored field Peer? → EnginePeer?
Date: 2026-04-25
Predecessor: Wave 46 (commit 5ca99da5a7) — PeerInfo avatar chain.
Shape: Single-file stored-field type migration. No external API change (field is private).
Target
submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift, stored field private var peer: Peer? at line 92.
Pre-flight inventory
grep -n "self\.peer\b" PeerInfoHeaderNode.swift returns exactly 3 references:
| Line | Code | Site type | Action |
|---|---|---|---|
| 426 | if let peer = self.peer, peer.profileImageRepresentations.isEmpty && gallery { |
Read | None — profileImageRepresentations is forwarded by EnginePeer (see submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift:485). Compiles unchanged. |
| 521 | self.peer = peer?._asPeer() |
Assignment | Drop the bridge → self.peer = peer. The peer parameter is already EnginePeer? after wave 45. |
| 2049–2054 | guard let self, let peer = self.peer, ... followed by peer: EnginePeer(peer), |
Read | Drop the wrap at line 2054 → peer: peer,. |
External access check: grep -rn "headerNode\.peer\b" submodules/ Telegram/ returns empty. The field is private; only same-file siblings touch it.
EnginePeer forwarding (re-confirmed at plan time):
profileImageRepresentations— forwarded (Peer.swift:485). ✓EnginePeer(peer)(PHN:2054) — acceptsEnginePeerdirectly when the local is alreadyEnginePeer; drop the constructor.
Field-declaration change is the only "type" change needed. The 3 callers' adjustments are mechanical bridge drops.
Edit list
- Line 92:
private var peer: Peer?→private var peer: EnginePeer? - Line 521:
self.peer = peer?._asPeer()→self.peer = peer - Line 2054:
peer: EnginePeer(peer),→peer: peer,
Total: 3 edits in 1 file.
Out of scope
PeerInfoData.swift:355,487— different classes'self.peerassignments (different types). Audit confirms these areRenderedChannelParticipant.peerand similar — already migrated in earlier waves or owned by other types.PeerInfoAvatarTransformContainerNode.peer(line 223) — alreadyEnginePeer?after wave 46.
Build & verify
source ~/.zshrc 2>/dev/null; \
python3 build-system/Make/Make.py --overrideXcodeVersion \
--cacheDir ~/telegram-bazel-cache build \
--configurationPath build-system/appstore-configuration.json \
--gitCodesigningRepository git@gitlab.com:peter-iakovlev/fastlanematch.git \
--gitCodesigningType development --gitCodesigningUseCurrent \
--buildNumber=1 --configuration=debug_sim_arm64
Expected: 1-iteration first-pass-clean. Only PeerInfoScreen + TelegramUI recompile.
Commit
Postbox -> TelegramEngine wave 47. Body lists the 3-edit summary and notes -3 internal bridges.
Outcome capture
Append a Wave 47 entry to docs/superpowers/postbox-refactor-log.md and update memory file project_postbox_refactor_next_wave.md.