Records the failure shape behind #3126, from the person who caused it.
## What is new about it
The three inert conversions this program had catalogued — #3051, #3062,
#3068 — all called `resolveTaskWorkflowIrSync` **at the call site**,
where the sync resolver is visible in the diff. That is what the
existing entries describe, and it is why they read as avoidable.
Mine was not that. #3114 converted a `triage.ts` arm to
`disposeLanes.wip`, reusing a value `resolvePlannerLanes` had produced a
few lines above. It merged, and `main` went red: `triage.ts: 7 -> 8`.
My reasoning at the time, verbatim from the PR body:
> `resolvePlannerLanes` already called immediately above — no new
resolution/await.
That sentence checks the **cost** question and skips the **correctness**
one. I confirmed I was not adding an `await` to a synchronous listener —
the usual blocker, and a real one — and never asked what kind of
resolver had produced the local I was reusing.
**Reusing an already-resolved value reads as strictly safer than
resolving.** No new work, no new await, no new failure mode. That
intuition is correct about cost and silent about correctness, and the
sync-ness sits one hop away inside the helper, where a call-site
reviewer does not see it.
So the check is not *"am I calling a sync resolver here?"* but **"what
produced every lane value I am about to compare against,
transitively?"** A local is not evidence; the resolver behind it is.
#3122 widened the gate to follow wrappers for precisely this reason —
and I walked through the door it was widened to cover, during the same
phase I was adding it.
## Two corollaries recorded with it
1. **A gate that catches the defect but does not block is a report.**
`check-inert-sync-lanes` fired correctly and the PR merged anyway,
because it is not in the blocking set. #3127 fixes that, and I would
prioritise it over any individual conversion — this is the second time
this phase a correct non-blocking signal was ignored.
2. **`triage.ts`'s remaining 7 are not backlog.** The revert takes it to
7, and those seven are the same shape: they need the emitter-side /
async-threading work tracked in #3082, not another conversion pass.
`--claims` (#3124) now marks sync-resolver files as inert-risk and keeps
them out of the start-here list for exactly this reason.
## Scope
Docs only — one section appended to the existing learnings file, placed
with the other numbered shapes and before "The rule that produced every
fix above". No code, no gate, no changeset (internal docs).
The revert itself is #3126, which I confirmed on a clean detached
`origin/main` checkout rather than on a branch; I did not open a
competing fix.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>