Shared-backlog infrastructure, not a single-file conversion. This is the
blocker I hit on three separate census files and flagged twice; landing
it once beats working around it five more times.
## The problem
`resolveWorkflowIrForTask` returns the default coding IR in two cases
that are **not** the same as knowing which workflow governs a task:
- the selection read threw;
- the store reported no selection at all — the synchronous PostgreSQL
path does exactly this, deliberately.
Callers cannot distinguish either from a genuine selection.
**For lifecycle-column work that difference decides correctness.**
Post-merge the default coding lineage declares `todo` as its single
Planning column and **no `triage`**. So a call site converting a `column
=== "triage"` guard to trait resolution silently stops firing for
`builtin:legacy-coding` cards whenever the store cannot name the
workflow — it is handed the default's vocabulary with no signal that it
was a guess.
## Why this is the census blocker, with receipts
Every conversion I have landed has hit it and worked around it the same
way:
| Site | Workaround forced |
|---|---|
| `usage-limit-detector.ts` (#2572) | narrowed to intake, three separate
corrections |
| `mission-feature-sync.ts` (#2609) | legacy ids unioned, then
position-ordered to stop over-claiming |
| `live-agent-count.ts` (#2604) | not converted at all — left as a
documented finding |
That is why the count stalls around a dozen rather than converging on
zero: the honest conversion is unavailable, so each site keeps the
literal "just in case". With provenance a caller can finally say what it
means — **trust the resolved columns when the workflow was selected;
fall back to legacy compat only when it was guessed.**
## What lands
`resolveWorkflowIrForTaskWithProvenance` returning `{ ir, source:
"selection" | "default", workflowId? }`. **Additive by construction:**
`resolveWorkflowIrForTask` delegates to it and drops the provenance, so
the two answers cannot drift and no existing caller changes behaviour.
## Red-green
Mislabelling the no-selection guess as a selection fails its test (`1
failed | 5 passed`).
One test deliberately pins the underlying *fact* rather than assuming it
— the default guess really does lack `triage` and does have `todo`. If
that lineage ever regains the column the hazard changes, and the callers
relying on provenance should be revisited; this is what will tell them.
Another asserts `resolveWorkflowIrForTask` returns exactly the
provenance form's IR across all three paths, so the delegation cannot
silently diverge.
## Not done here
I have **not** converted any call site onto it. Each one is a behaviour
decision for its owner — `comments-ops.ts`, `task-creation.ts`,
`archive-planning.ts`, plus revisiting the three above — and bundling
them would make this unrevertable. The enabler is the shared part.
## Verification
- 6 new tests green; `pnpm test:gate` green (132 / 10 / 482 / 71); `pnpm
lint` clean; `tsc --noEmit` clean
- Additive API on a private package (`@fusion/core`), no behaviour
change, so no changeset
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>