E2e driven iteration
Skill veegee82/loss-driven-development/skills/e2e-driven-iteration
Use at the start of every inner-loop iteration where the goal is to fix a bug / close a failing test / reach a green E2E. Forbids editing code without first running the E2E to capture a fresh loss signal, and forbids declaring "done" without the E2E passing. Makes the cycle "E2E → loss → 5-why-by-layer → fix → E2E" the only admissible rhythm.From its SKILL.md
npx -y skills add veegee82/loss-driven-development --skill e2e-driven-iterationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.0 KB, as published. Nobody here has run it
E2E-Driven-Iteration — inner loop (∂L/∂code), the forward-pass cadence
The Metaphor
The lab scientist with a hypothesis. Before any intervention: measure the baseline. After any intervention: measure again. Between the two measurements: exactly one change. "I know what this reagent does" without running the control experiment is not science — it is storytelling. The E2E run is the measurement device. Skipping it turns every edit into a story and leaves the loss unexamined.
Overview
In Gradient Descent for Agents, the E2E run is the forward pass. Every iteration of the inner loop has one: it produces the loss number this iteration descends against. This skill enforces the rhythm measure → diagnose → fix → measure inside loop-driven-engineering. The existing dach-skill says "run E2E when warranted." That's too soft. Under time pressure, "warranted" silently becomes "never" and the iteration degrades into blind-patching based on stale assumptions.
Core principle: when you are in a fix-loop (trying to close a failing signal), every iteration begins and ends with an E2E run of the failing case. The E2E is the forward pass; its output is the loss; the delta between iterations is the gradient signal that tells you whether the last edit helped or regressed.
No E2E at the start → no loss measurement → your edit is speculative. No E2E at the end → you don't know if you converged or just stopped.
See ../../docs/ldd/convergence.md §5 for how this skill implements the loss trace across the test pyramid.
When to Use
Invoke at the start of every inner-loop iteration whose purpose is:
- Fixing a failing test
- Closing a rejected gate
- Resolving a production incident
- Debugging an unexpected behavior
- Converging on any goal where "is it fixed yet?" is the question
Do not use for: new-feature work where there's no failing E2E yet (write the E2E first, then enter this loop), pure refactors (no behavioral E2E to run), documentation-only changes.
The Six-Step Iteration
k-th iteration:
1. Run E2E → loss_k = measured rubric score / failing items
2. Compare → Δloss_k = loss_{k-1} − loss_k (was the last edit a step or a random walk?)
3. Diagnose → invoke root-cause-by-layer, name layer 4/5
4. Fix → one focused edit, smallest coherent unit, at the named layer
5. Run E2E again → loss_{k+1} becomes loss_k of iteration k+1
6. Emit trace → python -m ldd_trace append ... (see using-ldd §"Persisted trace")
Do not skip step 1 — "I know what broke, I'll just fix it" is the anti-pattern. Until you measured the current state, you don't know if the last change helped, hurt, or was a no-op.
Do not skip step 2 — if Δloss_k ≤ 0, the previous edit did not help. You are in oscillation or a local minimum. Invoke loss-backprop-lens before proposing another edit.
Do not skip step 5 — without the post-edit run, "fixed" is a claim, not a measurement.
Do not skip step 6 — the user needs to see the loss descend live, per-iteration. v0.5.1 makes this a hard step, not an optional summary at task end. Running python -m ldd_trace append ... appends to .ldd/trace.log AND prints the full trace block (sparkline + chart + per-iteration line) — one command satisfies both mandates. If the tool is unavailable, render the trace block manually per using-ldd/SKILL.md §"Loss visualization".
Red Flags — STOP, the loop is degrading
- "I'll fix it then run the E2E at the end" → no loss measurement per step; you won't notice regressions
- "The E2E is slow, I'll skip it this iteration" → you lost the gradient
- "The last edit probably worked, don't need to re-run" → you don't know
- "E2E keeps failing the same way, let me try something else" →
Δlossis not going down; step size wrong (invokeloss-backprop-lens) - "I'll run the unit test instead, same signal" → only if the unit test covers the same failure surface; otherwise you're measuring a different loss
- "Let me batch 3 fixes and run once" → conflates 3 gradient steps; if one regressed, you can't attribute
- "I'll emit the trace block at the end of the whole task, the user doesn't need it per iteration" → NO. The user CANNOT see convergence without per-iteration emission; a final-block-only cadence gives them a single data point instead of a trajectory. Per-iteration trace is step 6, not optional.
E2E selection — cheap before expensive
If the failure reproduces at a cheaper pyramid tier (unit, integration), iterate there first. Only escalate to full E2E when the cheap tier can't reproduce.
| Tier | Use as iteration gate when |
|---|---|
| Unit | The failure is localized to one function / class / module with no external deps |
| Integration | The failure crosses module boundaries but is deterministic with test doubles |
| E2E (fake externals) | The failure involves orchestration but external APIs can be mocked |
| E2E (real externals) | The failure involves real LLM / DB / network non-determinism |
Running full E2E for a typo catchable at unit tier is a pyramid violation; it wastes budget and slows the gradient signal. Running only unit tests for a cross-boundary contract bug misses the real loss surface.
Budget interaction
This skill runs inside loop-driven-engineering's K_MAX = 5. The E2E-per-iteration cost sets a floor on how fast iterations can happen; if one E2E takes 30 minutes, your total budget is 2.5 hours. Plan accordingly:
- Before entering the loop, estimate E2E cost and multiply by 5 — that's your budget ceiling
- If estimate > available time, either cheapen the E2E (subset of scenarios, fake externals) OR accept you'll hit K_MAX and escalate
- At K_MAX: escalation per
loop-driven-engineering's §Escalation — with the added requirement of attaching theloss_ktrace across all iterations
How to Apply — checklist
- Define the failing E2E. Exact command, expected state, measured state. Save the output of iteration 0 as
loss_0. If.ldd/trace.logexists in the project, first runpython -m ldd_trace status --project .to see prior iterations. - Enter the loop. At the start of each iteration: run E2E, capture
loss_k, compare toloss_{k-1}. - Interpret the delta. Negative → progress, continue. Zero → no-op, rethink. Positive → regression, revert immediately and re-diagnose.
- Diagnose via
root-cause-by-layer. Calibrate vialoss-backprop-lens. - Edit smallest coherent unit.
- Rerun E2E. That's
loss_{k+1}. - Emit trace block. Run
python -m ldd_trace append --project . --loop inner --auto-k --skill <skill> --action "<what changed>" --loss-norm <v> --raw <n>/<max>. The tool appends to.ldd/trace.logAND prints the full trace block — show the output to the user. - Close only when E2E is fully green AND the regularizers (contracts, docs) are honored — hand off to
docs-as-definition-of-done. Runpython -m ldd_trace close ...to record terminal status.
Common Rationalizations
| Excuse | Reality |
|---|---|
| "The E2E is slow, every iteration is expensive" | Yes. That's why K_MAX = 5 and you must think, not guess. Slow E2E is itself a gradient — it tells you to invest in a faster test surface. |
| "I'll skip the start-of-iteration run to save time" | You saved 5 minutes and lost the ability to tell if your last edit helped. Net loss. |
| "The loss barely moved, close enough" | "Close enough" is a claim, not a measurement. Either the rubric is too lax (outer-loop issue → method-evolution) or you're stopping before convergence. |
| "E2E passed once on try 2, ship" | You owe a reproducibility-first check. One pass is one sample. |
| "I'll fix all three issues in one iteration, E2E at end" | You cannot attribute which edit helped and which regressed. Run per edit. |
Relation to other skills
reproducibility-firstruns before this skill enters the loop — you must have a reproducible signal to begin.root-cause-by-layerruns at step 3 of each iteration — diagnosis.loss-backprop-lensruns whenΔloss ≤ 0to recalibrate step size.loop-driven-engineeringis the parent — this skill is what it dispatches during a fix-loop.docs-as-definition-of-doneruns at close — when the E2E is green, the loop isn't closed until docs are synced.
Real-World Cue
If your iteration log for a bug fix doesn't show a loss_k trace — one number per iteration, visibly decreasing toward zero — you were not doing E2E-driven iteration. You were hoping.