Wave 103 (original): ChatRecentActionsControllerNode.peer Peer -> EnginePeer migration ABANDONED after pre-flight discovered a 75-site ADD-bridge cascade through chatRecentActionsHistoryPreparedTransition into Message.peers SimpleDictionary<PeerId, Peer> store sites. Lessons captured in docs/superpowers/postbox-refactor-log.md "Wave 103 ABANDONED" section + ~/.claude/projects/-Users-isaac-build-telegram -telegram-ios/memory/feedback_wave71_shadow_risk.md (4-layer pre-flight checklist for stored-Peer-field migrations). Wave 103 (retry, 92230b0691): drained 5 accountManager.mediaBox .storeResourceData Shape-A sites against the wave-94 facade. 2 files / 3 Edit calls (1 single + 2 replace_all) / 1-iter / 29.5s build. Closes the storeResourceData accountManager-side drain entirely. Wave 104 (08fc3f721e): drained 3 of 8 accountManager.mediaBox.resourceData Shape-A sites against the wave-32/wave-94 AccountManagerResources.data (resource:) facade + 3 consumer-side .complete -> .isComplete renames (EngineMediaResource.ResourceData field rename). 1 file / 6 Edit calls / 1-iter / 11.7s build. 5 of 8 candidates deferred behind Postbox-typed -function-parameter barriers (fetchCachedScaled*Representation cascade, combineLatest typed-tuple coupling). Established the "Postbox-typed -function-parameter barrier registry" pattern. Wave 105 (0c76724409): DeviceContactInfoSubject enum 3 case Peer? payloads + 2 callback signatures + 1 computed property migrate to EnginePeer?. 5 files / 17 edits / 1-iter / 203s build (foundational AccountContext touch). Net wrap delta -8 (10 drops, 2 ADD bridges, 1 downcast->case-let). First wave-71-shadow-style migration after the wave-103 abandonment forced a discipline reset; first-pass-clean via thorough pre-flight inventory (~15 min). Net session progress: -16 wraps across 4 wave attempts plus durable scaffolding (feedback memory, barrier registry, 4-layer pre-flight checklist). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
Wave 104: accountManager.mediaBox.resourceData drain (3 clean sites) Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Drain 3 of 8 accountManager.mediaBox.resourceData(...) Shape-A sites against the existing wave-32 / wave-94 AccountManagerResources.data(resource:) facade. Wave 104 of the Postbox → TelegramEngine refactor.
Architecture: Wave-shape-G drain with a documented consumer field rename. Single-file consumer migration in submodules/WallpaperResources/Sources/WallpaperResources.swift. 3 call rewrites + 3 consumer-side .complete → .isComplete renames, 6 Edit calls total. The remaining 5 of the original 8 resourceData candidates are deferred (2 cross a MediaResourceData flow-out cascade, 3 are coupled to postbox-side via combineLatest typed tuples).
Tech Stack: Swift, Bazel via Make.py, no unit tests. Verification is the full-project debug-sim-arm64 build.
Iteration budget: 1 (target first-pass-clean given verified pre-flight inventory).
Note on TDD: This project has no unit tests. Each task writes the edits, then verifies via Bazel build + residue grep.
File Structure
| File | Role | Changes |
|---|---|---|
submodules/WallpaperResources/Sources/WallpaperResources.swift |
Wallpaper resource pipeline | 3 call rewrites + 3 consumer renames |
No public-API ripple — leaf-consumer migration against an existing facade.
Task 1: WallpaperResources.swift — call rewrites (3 edits)
Files:
- Modify:
submodules/WallpaperResources/Sources/WallpaperResources.swift
Edits in this task: 3.
- Step 1: Migrate the call at line 957 (
reference.resourceargument)
Find:
let maybeFetched = accountManager.mediaBox.resourceData(reference.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
Replace with:
let maybeFetched = accountManager.resources.data(resource: EngineMediaResource(reference.resource), attemptSynchronously: synchronousLoad)
Note: waitUntilFetchStatus: false is omitted because the facade default is false. The site explicitly passed false, so behavior is preserved.
- Step 2: Migrate the call at line 1164 (
fileReference.media.resourceargument)
Find:
let maybeFetched = accountManager.mediaBox.resourceData(fileReference.media.resource, option: .complete(waitUntilFetchStatus: false), attemptSynchronously: synchronousLoad)
Replace with:
let maybeFetched = accountManager.resources.data(resource: EngineMediaResource(fileReference.media.resource), attemptSynchronously: synchronousLoad)
Same waitUntilFetchStatus: false omission rationale.
- Step 3: Migrate the call at line 1264 (
file.file.resourceargument, no option)
Find:
return accountManager.mediaBox.resourceData(file.file.resource)
Replace with:
return accountManager.resources.data(resource: EngineMediaResource(file.file.resource))
The original used the underlying MediaBox.resourceData(_ resource:) overload's defaults — facade defaults match exactly (pathExtension: nil, waitUntilFetchStatus: false, attemptSynchronously: false).
Task 2: WallpaperResources.swift — consumer-side .complete → .isComplete renames (3 edits)
Files:
- Modify:
submodules/WallpaperResources/Sources/WallpaperResources.swift
Edits in this task: 3.
EngineMediaResource.ResourceData exposes .isComplete (renamed from MediaResourceData.complete). All three migrated call sites have a single consumer-side .complete access on the migrated result that needs renaming.
- Step 1: Rename
maybeData.completeat line 961 (consumer of site 957)
Find:
if maybeData.complete {
Replace with:
if maybeData.isComplete {
The leading whitespace (8 spaces) must match exactly.
- Step 2: Rename
maybeData.completeat line 1168 (consumer of site 1164)
Find:
if maybeData.complete && isSupportedTheme {
Replace with:
if maybeData.isComplete && isSupportedTheme {
The leading whitespace (16 spaces) must match exactly.
- Step 3: Rename
data.completeat line 1266 (consumer of site 1264)
Find:
if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
Replace with:
if data.isComplete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
The leading whitespace (36 spaces) must match exactly.
The data.path access on the same line is unchanged — both MediaResourceData.path and EngineMediaResource.ResourceData.path are String.
Sites NOT touched (deferred)
For the implementer's awareness — these .complete accesses on UNRELATED bindings stay raw and are NOT to be renamed:
WallpaperResources.swift:968—return data.complete ? try? Data(contentsOf: URL(fileURLWithPath: data.path)) : nil— thisdatais bound fromaccount.postbox.mediaBox.resourceData(...)(postbox-side, not migrated). STAYS.complete.- Other
.completeaccesses elsewhere in the file that aren't on the 3 migrated bindings — STAY.
The 3 renames target only the 3 specific lines listed in Task 2 steps 1-3. Do NOT use replace_all=true for renames — bindings differ per scope.
Task 3: Full-project Bazel build
Files: none (verification only).
- Step 1: Run the build
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: clean build (bazel build complete / INFO: Build completed successfully). No --continueOnError. Build cost projection: ~30-60s (consumer-only, foundational module rebuild fan-out).
- Step 2: If build fails, triage iteration
Common failure modes:
EngineMediaResourceconstructor not found — verifyimport TelegramCoreat the top of WallpaperResources.swift (it should already be there). If missing, add it.- Type mismatch on
resource:parameter — would suggest the argument expression isn'tMediaResource-typed. STOP and check the actual type at the failing site. - Type mismatch on
.isCompleterename — if the closure parameter binding is somehow inferred wrong (e.g., Swift inferred the OLDMediaResourceDatatype because the call rewrite didn't take effect), the rename will fail. Re-read the diff and verify the call rewrite landed. data.pathtype mismatch — should not happen; both types exposepath: String. If it does, STOP and re-read.
If errors land outside WallpaperResources.swift: STOP and report BLOCKED. The wave is supposed to be self-contained.
Iteration budget: 2.
Task 4: Post-edit residue grep
Files: none (verification only).
- Step 1: Verify the 3 migrated call sites are gone
Run:
grep -nE "accountManager\.mediaBox\.resourceData\(" submodules/WallpaperResources/Sources/WallpaperResources.swift
Expected: exactly 3 lines remaining (L33, L59, L401 — the deferred combineLatest sites). The migrated lines (originally 957, 1164, 1264) should NOT appear.
- Step 2: Verify the 3 renames are applied
Run:
grep -nE "maybeData\.complete\b" submodules/WallpaperResources/Sources/WallpaperResources.swift
Expected: empty output. Both maybeData.complete accesses (originally L961, L1168) should be gone.
grep -nE "if data\.complete," submodules/WallpaperResources/Sources/WallpaperResources.swift
Expected: no line at L1266 (the migrated site). Other data.complete accesses on postbox-side bindings (e.g., L968) may remain — those are out of scope.
Task 5: Commit the wave
Files: none (git only).
- Step 1: Stage the 1 modified file
git add submodules/WallpaperResources/Sources/WallpaperResources.swift
- Step 2: Confirm staging is clean
git status --short | grep -v "^??"
Expected: only the 1 staged file (line starting with M ). The line m build-system/bazel-rules/sourcekit-bazel-bsp is pre-existing WIP and should NOT appear in the staged list.
- Step 3: Commit
git commit -m "$(cat <<'EOF'
Postbox -> TelegramEngine wave 104
Drain 3 accountManager.mediaBox.resourceData(...) Shape-A sites against
the existing wave-32 / wave-94 AccountManagerResources.data(resource:)
facade. Sites: WallpaperResources:957 (reference.resource), :1164
(fileReference.media.resource), :1264 (file.file.resource).
Migration: accountManager.mediaBox.resourceData(X, option: .complete(
waitUntilFetchStatus: false)[, attemptSynchronously: Y]) -> accountManager
.resources.data(resource: EngineMediaResource(X)[, attemptSynchronously:
Y]). Plus 3 consumer-side .complete -> .isComplete renames at L961,
L1168, L1266 to match EngineMediaResource.ResourceData field name.
3 sites / 1 file / 6 Edit calls. Consumer-only build.
Deferred: 2 sites in FetchCachedRepresentations.swift (482, 490) flow
data: MediaResourceData into fetchCachedScaled*Representation cascade;
3 sites in WallpaperResources (33, 59, 401) coupled to postbox-side via
combineLatest typed tuples.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
- Step 4: Verify commit
git log --oneline -1
Expected: shows the wave 104 commit as HEAD.
Task 6: Update outcome log + memory
Files:
-
Modify:
docs/superpowers/postbox-refactor-log.md -
Modify:
~/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/project_postbox_refactor_next_wave.md -
Modify:
~/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/MEMORY.md -
Step 1: Append wave 104 outcome to refactor log
Append a "Wave 104 outcome" entry to docs/superpowers/postbox-refactor-log.md matching the format of "Wave 103 (retry) outcome". Include:
-
Commit hash (from Task 5 step 4).
-
Iteration count (1 if first-pass-clean; 2 if Task 3 step 2 fired).
-
Bazel build duration (from Task 3 step 1 output).
-
Net-delta accounting: −3 raw
mediaBox.Xaccesses, +3 facade calls, +3EngineMediaResource(...)wraps, +3 consumer field renames. -
Wave-shape note: G drain with documented consumer field rename. The pre-flight identified a
MediaResourceData-typed-function-parameter barrier (fetchCachedScaled*Representationfamily) that forced 2 sites into the deferred bucket — illustrates the wave-71-shadow lesson applied to result-type cascades, not just peer migrations. -
Step 2: Update next-wave memory
Edit project_postbox_refactor_next_wave.md:
-
Add wave 104 outcome line into the recent-waves section.
-
Update accountManager-side facade drain status table:
resourceDatacount drops from 8 → 5 (3 drained, 5 deferred). -
Add a new section (or extend an existing one) documenting the "Postbox-typed-function-parameter barrier" pattern, with
Message.peers: SimpleDictionary<PeerId, Peer>(wave-103 lesson) and nowfetchCachedScaled*Representation(resourceData: MediaResourceData)as the two known instances. -
Step 3: Update MEMORY.md index
Edit ~/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/MEMORY.md:
-
Update the
[Postbox refactor next wave]line to mention wave 104 landed. -
Step 4: Commit the doc update
git add docs/superpowers/postbox-refactor-log.md
git commit -m "$(cat <<'EOF'
docs: log wave 104 outcome
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
(Memory file updates are not committed — they live outside the repo.)
Net delta projection
| Category | Count | Sites |
|---|---|---|
Raw mediaBox.X access drops |
−3 | WR:957, 1164, 1264 |
| Facade calls added | +3 | same sites, migrated form |
EngineMediaResource(...) wraps |
+3 | canonical engine-side, not Postbox bridges |
| Consumer field renames | +3 | WR:961 (maybeData.complete → .isComplete), WR:1168 (same), WR:1266 (data.complete → .isComplete) |
import Postbox drops |
0 | WallpaperResources retains import for unrelated symbols |
Total commit footprint: 6 line edits in 1 file, plus a docs commit for the outcome log.