05 — The M9 Hard Gate Miss

Failure mode: Rung 6 — the Eigen-based MLP with a pybind11 Python binding — is not shipped by end of Month 9. This is the roadmap’s designated hard gate: the first project that proves “I can write applied ML in C++ and integrate it with Python.” If this doesn’t ship by M9, the M10–M13 capstones cannot land on schedule.

Probability: 30%

Why 30% and not lower: this is the hardest technical rung. Eigen has its own API dialect. Template errors are baroque. pybind11 has its own gotchas around GIL, buffer protocols, and object lifetime. And by M8–M9, sprint fatigue has accumulated across ~9 months of consistent work — the Valley of Despair (M4–M6) is behind, but the “am I ever going to finish this?” cognitive tax is real.

Why It Happens

  • Eigen learning curve. Templates + expression templates + lazy evaluation. First 2 weeks feel like fighting the library, not building the MLP.

  • pybind11 subtlety. Making numpy arrays and Eigen matrices share memory without copies requires understanding Eigen Ref<> + numpy buffer protocol. Getting it wrong = mysterious segfaults across the FFI boundary.

  • Debugging FFI segfaults is brutal. GDB/LLDB across a Python↔C++ boundary is a different skill. First segfault can eat a whole weekend.

  • Scope creep. “While I’m at it, let me add a CUDA backend” — kills the sprint. Or “let me publish it to PyPI properly” — kills two.

  • M8–M9 sprint fatigue. Even with good hygiene, 8 months in, motivation flags. The middle-late miles.

Early Warning Signals

  1. P5.1 (Eigen MLP core) stalled >2 weeks in M8. Not “slow” — actually stalled: no commits, no design notes, no benchmarks.

  2. Lab notebook entries for M8 dominated by frustration keywords — “stuck”, “confused”, “no idea why”, without accompanying “resolved by X” a week later.

  3. First pybind11 attempt not started by M8 W3 (with 5 weeks left to M9 end).

  4. Sprint retros in M8 have “next sprint I’ll figure it out” 3 times in a row.

  5. Reading MLP theory YouTube instead of writing MLP code — a specific relapse into tutorial hell in the ML domain.

Mitigation — Scope Cuts, In Order

The moment a signal fires, apply scope cuts in this order (each one lighter than the next):

  1. Drop the CUDA backend. Not in scope for rung 6. Move to rung 7 or M11.

  2. Drop the PyPI publish. GitHub-only. pip install git+https://... is fine for demo purposes. Real PyPI package can be M11.

  3. Reduce to a 2-layer MLP. Not 4 layers, not deep. Just: input → hidden → output. ReLU + softmax. Trained on MNIST. That’s enough to prove the FFI works.

  4. Drop the benchmarks vs PyTorch. Just show it works and produces same-order accuracy on MNIST. Benchmarks are a follow-up post.

  5. Drop the “clean API” polish. Ship the ugly version. If .fit(X, y) and .predict(X) work, it’s shipped. numpy arrays in, numpy arrays out.

The floor: the FFI works, MNIST trains, accuracy is at least 90%. That is rung 6. Anything above that floor is bonus.

The Reframe

Rung 6 is not about a good MLP. It is about proving:

  • You can write nontrivial C++ that uses Eigen for linear algebra.

  • You can build a Python↔C++ FFI that doesn’t leak or crash.

  • You can produce a working end-to-end applied ML artifact in C++.

An ugly, slow, 2-layer MLP that trains MNIST from Python and returns predictions accomplishes all three. It is not a portfolio piece by itself, but it unlocks the portfolio pieces in M10–M13 (MiniServe capstone builds on this exact pattern).

Do not confuse rung 6 with “the impressive project.” Rung 6 is the pipe. The impressive projects flow through it.

Escalation Trigger

If rung 6 is not shipped by end of M9 with all 5 scope cuts applied:

  1. 2-week pause. No C++, no ML. Re-read ../13_discipline/07_motivation_sustainment.md twice. Sleep. Walk. Talk to family. Do not touch the roadmap.

  2. Roadmap reset. On return, run the reset protocol in 09_summary_and_reset_protocol.md.

  3. 6-month reduced-scope plan focused only on ML integration. Drop or defer P3 (concurrency deep-dive), P4 (systems), and both capstones. The reduced plan is: rung 6 (Eigen MLP) → rung 7 (proper pybind11 + benchmarks) → rung 8 (one small end-to-end applied inference tool) → study conversion. That is a 6-month arc from the pause point.

  4. The pitch changes. The 13-month “applied C++ + ML” pitch becomes a 15–18 month “applied C++ with a focused ML integration story.” Still lands. Still strong. Just longer, and with less breadth.

Ship-Recovery Protocol (if M9 blows past)

If rung 6 is 1–2 weeks late, don’t reset. Ship it late — declare M10 sprint 1 as “M9 catch-up” and adjust M10 targets down. That is a slip, not a reset. Slips of 1–2 weeks on hard gates are normal and don’t need dramatic response.

Reset only when the slip is 4+ weeks or when the underlying signal is “I don’t know how to make this work and I’ve been stuck for a month.”

What Success Looks Like at End of M9

  • mlp-cpp repo public on GitHub, 1 tag (v0.1.0), README with install + example.

  • import mlp_cpp; mlp_cpp.MLP(...).fit(X, y).predict(X) works from Python.

  • MNIST demo notebook committed, showing ≥ 90% accuracy.

  • One blog post: “Building a small MLP in C++ with Eigen + pybind11 — what I learned about the FFI.”

  • Lab notebook has an entry: “I have shipped my first applied-ML C++ artifact.” Take a walk. This is a big rung.


Nav: ← 04_the_zoho_creep.md · → 06_the_capstone_scope_creep.md