| name | l-lessons-client-bundling |
| description | Project lessons learned for client bundling (zfb-build bundler, bundle.exclude, shadow staging, esbuild resolution) and for running autonomous review→fix loops on this area. Read PROACTIVELY before planning or implementing work touching crates/zfb-build/src/bundler.rs, bundle.exclude semantics, or any fully-autonomous epic run — contains traps, root causes, and "watch for next time" notes from previous attempts. |
Lessons: client bundling
2026-07-11 — bundle.exclude vs esbuild resolution: non-converging autonomous fix loop
What we set out to do
Epic #1497 (client bundling primitives: bundle-mode loaders/defines, raw imports, module workers, bundle.exclude) via a fully autonomous Codex x-wt-teams run with -m -a (auto-fix findings, auto-merge, no human checkpoint).
Approach we tried first
Two compounding choices:
- Code: make
bundle.exclude airtight by predicting in Rust what esbuild would resolve — progressively reimplementing esbuild's resolver (extension probing, index files, package.json main/imports, node_modules walk, symlink semantics, Go filepath quirks), version-pinned to esbuild 0.25.12.
- Process: review→fix→re-review rounds with the same agent family on both sides, no round budget, no convergence criterion, no same-seam detection, no human escalation.
Why it went wrong (root cause)
Two unbounded loops multiplied. A correctness criterion of "matches what esbuild would do" has no finish line: every fix expands the parity surface, so every review round finds a genuinely new, empirically-valid divergence — the reviewer is right every time, and that is exactly why it never converges. The harness then had no brake: 6 escalating fix commits in 3h39m (+366 → +372 → +284 → +125 → +953 → +1986/−354 lines, all in ), two merges both labeled "final", then a round-8 worktree with +5,202 more uncommitted lines. One helper () was rewritten 5 times in ~3h and then deleted; another function lived 21 minutes.