`TaskCard.tsx` 1 → 0. **Census 13 → 12**, baseline re-recorded
in-commit.
## The defect
`getInReviewCompletionMs` gated on `task.column === "done"`, so on a
board whose completion lane is renamed, a finished card rendered its
execution time **without the completion half** — the `Completed <when>`
part of the indicator's `title` / `aria-label` never appeared.
Nobody reported it because the card does not look broken. It looks like
a card whose completion time was never recorded.
## The recorded blocker had expired, and I trusted it twice
The note on that helper read:
> Module-scope, takes only a `Task`, and has no flags to consult.
Converting it means either threading resolved flags through a pure
duration helper or resolving a workflow inside it.
True when written (2026-07-30). False within a day, and the evidence is
in the same file:
- `taskColumnFlags` is a **prop of this component**, destructured and
already consumed by `isWipColumnRole` / `isReviewColumnRole`.
- The **sibling duration helpers were threaded for exactly this
purpose** — `getTotalAgentActiveMs` carries the note *"THREADED SO THE
CONVERSION IS NOT INERT"*.
- This helper has **one caller**, inside the component, where the flags
are in scope.
The threading the note called prohibitive was already done; only this
helper was left behind. I read that note twice this week and took it at
face value both times — and what finally prompted the check was main
landing `taskRevert 2 → 0 — **the recorded blocker named the wrong
variable**` (#3129), someone else finding the same class of decay in a
note I had also accepted.
This program's own learnings say a deferral's stated blocker is a claim
that ages like any measurement. I had applied every other entry in that
document this week except that one.
## A dependency-array bug the conversion would have introduced
The memo now reads `taskColumnFlags`, so it joins the dependency array.
Flags arrive **asynchronously** — the board resolves workflows after
first paint — so a card rendered before they load and re-rendered after
would otherwise keep the pre-flag answer, since none of the memo's other
inputs changed. This repo has **no `react-hooks/exhaustive-deps` rule**,
so nothing would have flagged the omission.
## Two wrong probes before a correct one, both caught by controls and
mutation
Recording these because the fix was right from the start and my
instruments were not:
1. **`textContent` matched nothing.** The completion time lands in
`title`/`aria-label`, never in visible text. The **control failed too**
— the signature of a broken probe rather than a broken fix.
2. **`innerHTML` on the whole card matched always.** The lifecycle-dates
footer renders its own `Completed <date>` line, and *that* path already
resolves the complete lane correctly. The probe was reading a different,
already-converted feature. **Mutation exposed it: reverting the fix left
all six green.**
The final assertion queries `.card-time-indicator` and reads its
`title`, which is the only form that can tell the two apart.
## Verification
| | result |
|---|---|
| suite | **6 passed** |
| mutation (restore `=== "done"`) | **1 failed \| 5 passed** — the
renamed case only, control still green |
| dashboard `tsc -p tsconfig.app.json` | **0 errors** |
| census `--strict` | exit 0, baseline re-recorded in-commit |
Flags stay optional with the legacy id as fallback
(`isCompleteColumnRole`), so any caller without resolved flags behaves
exactly as before.
## Note on `check-fnxc-future-dates`
It fails on this branch, but **not because of it** — `scheduler.ts` and
one PG test carry future stamps on `main` itself. #3139 fixes that. None
of my files appear in the report.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>