Telegram-iOS/docs/superpowers/plans/2026-04-26-postbox-wave-106-import-drop-sweep.md
isaac 5a3a4f5536 docs: plan for postbox wave 106 — speculative import Postbox drop sweep round 2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 22:21:14 +04:00

17 KiB
Raw Blame History

Wave 106: Speculative import Postbox Drop Sweep (round 2) 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: Drop import Postbox from any consumer-module Swift file in submodules/ whose remaining content no longer references a Postbox-only symbol. Wave 106 of the Postbox → TelegramEngine refactor — round 2 of the wave-93 speculative-drop sweep.

Architecture: Procedural sweep with build-feedback loop. (1) inventory candidates → (2) pre-flight regex pre-restore → (3) drop imports en masse → (4) build with --continueOnError → (5) restore failures → iterate → (6) final clean build → (7) optional BUILD-dep sweep → (8) single atomic commit. No code semantic changes — only import and BUILD deps lines.

Tech Stack: Swift, Bazel via Make.py, grep/sed for inventory, no unit tests. Verification is the full-project debug-sim-arm64 build.

Iteration budget: 2-5 build cycles (wave-93 precedent: 2 iter — drop, restore, clean).

Note on TDD: No unit tests in this project. Each task verifies via Bazel build + diff inspection. Build feedback IS the test.

Spec: docs/superpowers/specs/2026-04-26-postbox-wave-106-import-drop-sweep-design.md.


File Structure

Artifact Role
/tmp/wave106-candidates.txt All consumer files currently import Postbox
/tmp/wave106-skiplist.txt Files that match preemptive-restore regex (keep import)
/tmp/wave106-droplist.txt candidates skiplist; files to edit
/tmp/wave106-build-iterN.log Per-iteration build log
/tmp/wave106-restore-iterN.txt Files needing restore after iter N
/tmp/wave106-final-droplist.txt Net dropped files after all iterations
submodules/**/*.swift Edited files (single-line Edit each)
submodules/**/BUILD (Optional Step 7) packages with no remaining Postbox imports

Task 1: Pre-flight WIP check

File: none (read-only).

  • Step 1: Verify clean working tree (modulo known-persistent state)

Run:

git status --short

Expected output:

 m build-system/bazel-rules/sourcekit-bazel-bsp
?? build-system/tulsi/
?? submodules/TgVoip/
?? third-party/libx264/

If output contains anything else (modified M files, other untracked dirs), HALT — there is unrelated WIP that would get tangled with the wave commit. Resolve before proceeding.

  • Step 2: Confirm we are on master

Run:

git branch --show-current

Expected: master. If not, stop and ask.


Task 2: Inventory candidate files

File: none (read-only).

  • Step 1: Build the candidate list

Run:

grep -rl "^import Postbox" submodules --include="*.swift" \
  | grep -v "^submodules/Postbox/" \
  | grep -v "^submodules/TelegramCore/" \
  | grep -v "^submodules/TelegramApi/" \
  | sort -u > /tmp/wave106-candidates.txt
wc -l /tmp/wave106-candidates.txt

Expected: between ~700 and ~1200 files (wave-93-era was ~1200; waves 94-105 may have peeled some).

  • Step 2: Sanity-check the exclusion filters worked

Run:

grep -E "^submodules/(Postbox|TelegramCore|TelegramApi)/" /tmp/wave106-candidates.txt | head -5

Expected: empty output (no excluded paths leaked through).


Task 3: Build the skip-list via preemptive regex

File: none (read-only).

  • Step 1: Run the combined skip-regex against candidates

The skip-regex is the union of three tiers from the spec. Run:

grep -El "\bPostbox\b|\bMediaBox\b|\bMediaResource\b|\bMediaResourceData\b|\bMediaResourceId\b|\bPostboxCoding\b|\bPostboxDecoder\b|\bPostboxEncoder\b|\bMemoryBuffer\b|\bTempBoxFile\b|\bValueBoxKey\b|\bPostboxView\b|\bcombinedView\b|\bPeerId\b|\bMessageId\b|\bMediaId\b|\bMessageIndex\b|\bMessageAndThreadId\b|\bPeerNameIndex\b|\bStoryId\b|\bItemCollectionId\b|\bFetchResourceSourceType\b|\bFetchResourceError\b|\bPeer\b|\bMessage\b|\bMedia\b" \
  $(cat /tmp/wave106-candidates.txt) \
  | sort -u > /tmp/wave106-skiplist.txt
wc -l /tmp/wave106-skiplist.txt

Expected: most of the candidate list (likely 600-1100 files matched) — \bPeer\b, \bMessage\b, \bMedia\b are deliberately broad and catch many false positives. False positives are SAFE — they just mean fewer drops, not bad drops.

  • Step 2: Compute the drop-list

Run:

comm -23 /tmp/wave106-candidates.txt /tmp/wave106-skiplist.txt > /tmp/wave106-droplist.txt
wc -l /tmp/wave106-droplist.txt
head -20 /tmp/wave106-droplist.txt

Expected: 5-50 files in the drop-list (wave 93 had 12). If 0, the regex is over-matching — halt and revisit. If >100, the regex is under-matching — halt, expand patterns, re-run.

  • Step 3: Spot-verify 3 random drop candidates

Run for each of 3 files from the head of the drop-list:

head -3 /tmp/wave106-droplist.txt | while read f; do
  echo "=== $f ==="
  grep -nE "Postbox|MediaBox|MediaResource|PeerId|MessageId|MediaId|MessageIndex" "$f" | head -5
done

Expected: Only import Postbox line appears. If any other Postbox-token appears, the file should have been skipped — add the missing pattern to the regex in Step 1, redo Steps 1-2, and re-spot-check.


Task 4: Drop import Postbox from drop-list files

Files: every path listed in /tmp/wave106-droplist.txt.

  • Step 1: Read each drop-list file's import block to locate the exact import Postbox line

For each file in the drop-list, the line is import Postbox (exact match, no whitespace variations expected). Use a single-purpose sed to remove it from all drop-list files:

while read f; do
  sed -i '' '/^import Postbox$/d' "$f"
done < /tmp/wave106-droplist.txt

The sed -i '' syntax is BSD/macOS specific — required on Darwin.

  • Step 2: Verify the imports were removed

Run:

grep -lE "^import Postbox$" $(cat /tmp/wave106-droplist.txt) | wc -l

Expected: 0 (no file in the drop-list still contains import Postbox).

  • Step 3: Verify no other lines were touched

Run:

git diff --stat | tail -5
git diff --shortstat

Expected: same number of files modified as drop-list size. Each file should show -1 insertion (or -1 deletion). If any file shows multiple deletions, something went wrong — git checkout -- $(cat /tmp/wave106-droplist.txt) and investigate.


Task 5: Build iteration 1 — capture failures

File: none (build only).

  • Step 1: Run the build with --continueOnError

Run:

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 \
 --continueOnError 2>&1 | tee /tmp/wave106-build-iter1.log

Expected: Build completes (with errors). Wall-clock 30-260s depending on cache state.

  • Step 2: Extract failing files

Run:

grep -E ":[0-9]+:[0-9]+: error:" /tmp/wave106-build-iter1.log \
  | awk -F: '{print $1}' \
  | sort -u > /tmp/wave106-restore-iter1.txt
wc -l /tmp/wave106-restore-iter1.txt
cat /tmp/wave106-restore-iter1.txt

Expected: a subset of the drop-list. Wave 93 saw 5 of 12 needing restore. If the count > 50% of drop-list, the regex is missing a major pattern — HALT, analyze the failure cluster, add the missing pattern to Task 3 Step 1, restart from Task 4.

  • Step 3: Verify no errors in TelegramCore/Postbox/TelegramApi

Run:

grep -E "^submodules/(TelegramCore|Postbox|TelegramApi)/" /tmp/wave106-restore-iter1.txt

Expected: empty. If non-empty: HALT immediately, git checkout -- submodules/, and revert the wave — scope drift indicates the candidate filter or sed pattern is wrong.


Task 6: Restore failing files (iter 1)

Files: every path in /tmp/wave106-restore-iter1.txt.

  • Step 1: Re-add import Postbox to each failing file

Use awk uniformly (BSD sed -i '' 'i\' line-continuation is fragile inside shell loops). Insert import Postbox immediately before import TelegramCore if present, else immediately after the first existing import line:

while read f; do
  awk '
    BEGIN { added = 0 }
    !added && /^import TelegramCore$/ { print "import Postbox"; print; added = 1; next }
    { print }
    END {
      if (!added) {
        # fallback path was not used — try post-first-import injection
        # (this END block is a no-op; awk cannot re-emit lines after END)
      }
    }
  ' "$f" > "$f.tmp"
  if ! grep -q "^import Postbox$" "$f.tmp"; then
    # no TelegramCore anchor found — fall back to "after first import"
    awk '
      BEGIN { added = 0 }
      !added && /^import / { print; print "import Postbox"; added = 1; next }
      { print }
    ' "$f" > "$f.tmp"
  fi
  mv "$f.tmp" "$f"
done < /tmp/wave106-restore-iter1.txt
  • Step 2: Verify restorations

Run:

grep -L "^import Postbox$" $(cat /tmp/wave106-restore-iter1.txt)

Expected: empty (every file in the restore list now contains import Postbox again).

  • Step 3: Update the working drop-list

Run:

comm -23 /tmp/wave106-droplist.txt /tmp/wave106-restore-iter1.txt > /tmp/wave106-final-droplist.txt
wc -l /tmp/wave106-final-droplist.txt

This is the current "successfully dropped" set.


Task 7: Build iteration 2 — verify clean (or iterate further)

File: none (build only).

  • Step 1: Re-run the build with --continueOnError

Run:

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 \
 --continueOnError 2>&1 | tee /tmp/wave106-build-iter2.log
  • Step 2: Extract any new failures

Run:

grep -E ":[0-9]+:[0-9]+: error:" /tmp/wave106-build-iter2.log \
  | awk -F: '{print $1}' \
  | sort -u > /tmp/wave106-restore-iter2.txt
wc -l /tmp/wave106-restore-iter2.txt
  • Step 3: If non-empty, repeat Task 6 with iter2.txt and run Task 7 again as iter3.

Stop when:

  • restore-iterN.txt is empty → proceed to Task 8.
  • N == 5 → HALT (diminishing returns); commit what is green via Task 9.

Each repeat: substitute iter1iter2iter3 etc. throughout. Update the final-droplist after each restore: comm -23 /tmp/wave106-final-droplist.txt /tmp/wave106-restore-iterN.txt > /tmp/wave106-final-droplist.txt.new && mv /tmp/wave106-final-droplist.txt.new /tmp/wave106-final-droplist.txt.


Task 8: Final clean build (no --continueOnError)

File: none (build only).

  • Step 1: Run a clean build to confirm no inter-module ordering issue was masked

Run:

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 2>&1 | tail -30

Expected: build success, no error: lines in the tail. If failure: an inter-module visibility issue exists that --continueOnError masked. Restore the final-droplist file(s) implicated by the error, repeat Task 7 / Task 8.


Task 9 (optional): BUILD-dep sweep

Files: various submodules/*/BUILD files.

This step removes //submodules/Postbox from any Bazel package whose Swift sources no longer contain import Postbox. Skip this task if iteration time is constrained — the import drops alone are the core wave value; deps trim is housekeeping.

  • Step 1: Find packages whose BUILD still depends on //submodules/Postbox but whose Sources/**/*.swift no longer imports it

Run:

for build in $(find submodules -name BUILD -not -path "submodules/Postbox/*"); do
  pkg_dir=$(dirname "$build")
  if grep -q "//submodules/Postbox" "$build" 2>/dev/null; then
    if ! grep -rq "^import Postbox$" "$pkg_dir" --include="*.swift" 2>/dev/null; then
      echo "$build"
    fi
  fi
done > /tmp/wave106-build-deps-candidates.txt
wc -l /tmp/wave106-build-deps-candidates.txt
cat /tmp/wave106-build-deps-candidates.txt

Expected: 0-5 candidates. If 0, skip to Task 10.

  • Step 2: For each candidate BUILD, locate the //submodules/Postbox line and remove it via Edit

Note that BUILD files often list deps as either "//submodules/Postbox" (string) or via aliases. Use Read to inspect each, then Edit to drop just the dep line. The exact string pattern varies — typically "//submodules/Postbox", on its own line within a deps = [ ... ] block.

For each candidate file, Read the lines around the match, then Edit to remove the line preserving the surrounding bracket structure.

  • Step 3: Re-run the clean build (no --continueOnError) to confirm no dep was load-bearing

Run the same command as Task 8 Step 1.

If failure: a transitive dep was being satisfied through Postbox. Restore the dep line(s) implicated by the error and re-run.


Task 10: Commit the wave

File: git.

  • Step 1: Inspect final diff statistics

Run:

git diff --stat
git diff --shortstat

Expected: N file modifications, all -1 line changes (just import Postbox lines), possibly plus a small number of BUILD diffs from Task 9.

  • Step 2: Confirm only allowed paths are touched

Run:

git diff --name-only | grep -vE "^submodules/" | head -5
git diff --name-only | grep -E "^submodules/(Postbox|TelegramCore|TelegramApi)/" | head -5

Both expected: empty. If either has output: HALT — rogue changes exist; investigate before committing.

  • Step 3: Stage only the modified Swift and BUILD files

Run:

git add $(git diff --name-only)
git status --short

Expected: all changes staged with M. Untracked dirs (build-system/tulsi/, submodules/TgVoip/, third-party/libx264/) and the m submodule marker remain untouched.

  • Step 4: Commit with the wave message

Substitute <N> with the count from /tmp/wave106-final-droplist.txt, <M> with the total restored across iterations, and <K> with the BUILD deps removed (0 if Task 9 skipped).

N=$(wc -l < /tmp/wave106-final-droplist.txt | tr -d ' ')
M=$(cat /tmp/wave106-restore-iter*.txt 2>/dev/null | sort -u | wc -l | tr -d ' ')
K=$(git diff --cached --name-only | grep -c BUILD)

git commit -m "$(cat <<EOF
Postbox -> TelegramEngine wave 106 (import drop sweep round 2)

Speculative drop of \`import Postbox\` in $N files where the last
Postbox-typed symbol reference was peeled off by waves 94-105.
Methodology: pattern-based pre-flight skip + drop + build-feedback
restore loop (wave-93-validated recipe). $M files restored after build.
$K BUILD deps removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
)"
git status --short

Expected: clean commit, working tree returns to the known-persistent untracked-only state.


Task 11: Update memory file with wave outcome

File: /Users/isaac/.claude/projects/-Users-isaac-build-telegram-telegram-ios/memory/project_postbox_refactor_next_wave.md.

  • Step 1: Read the current memory file to find the recent-commits section

Read the top section listing wave commits.

  • Step 2: Insert a wave-106 line below the wave-105 entry

Append (substituting the actual commit hash from git log -1 --format=%H | head -c 10):

- `<HASH>` — wave 106: speculative `import Postbox` drop sweep round 2. <N> files dropped, <M> restored after build feedback. Methodology re-run of wave 93 (`72de7c4fd5`) with expanded pre-flight regex (added bare-name escapes `\bPeer\b`/`\bMessage\b`/`\bMedia\b` per wave-93 lesson). <K> BUILD deps removed. <iter-count> build cycles.

Update the memory file's description: frontmatter to reflect wave 106 as the latest.


Halt-and-revert recipe (if anything goes seriously wrong)

If at any point the build fails in TelegramCore/Postbox/TelegramApi, or iteration count exceeds 5 with non-trivial residue, or scope drifts beyond the spec:

git checkout -- submodules/
git status --short  # should match the pre-flight expected output

The wave is fully reversible until Task 10 commits.


Plan Self-Review Notes

  • Spec coverage: Tasks 1-11 map 1:1 to the 8-step procedure in the spec plus pre-flight (Task 1) and post-commit memory update (Task 11). Halt conditions appear in Tasks 5/7/9 and the final halt-and-revert recipe.
  • Placeholder scan: No TBDs/TODOs. All <N>/<M>/<K>/<HASH> are explicitly substituted via shell expansion in Step 4 of Task 10.
  • Type/method consistency: Single-purpose tasks operating on filesystem and grep — no method-name drift risk.
  • Iteration shape: Tasks 5-7 form the iteration loop; Task 8 is the validation gate; Task 9 is optional housekeeping; Task 10 commits.