Parent: #96
Confirmed failure
v0.6.2 displayed an active timer from startedAt and froze it at endedAt, so a completed review showed its duration.
Current ReviewMonitor instead maps a completed ReviewRunRecord.endedAt to .relative(to: endedAt) and renders Text(date, style: .relative). The current test suite explicitly expects this age-since-completion behavior. Persisted rows without a live run fall back again to generic chat activity time and also lose review status/symbol.
Broken invariant
Review timing is lifecycle state owned by the review run. A completed review's visible time is endedAt - startedAt, not wall-clock age since endedAt and not generic thread recency.
Acceptance criteria
- Running reviews show elapsed time from their review start.
- Completed, failed, and cancelled reviews show a stable review duration based on lifecycle start/end.
- The displayed value does not change as wall-clock time advances after completion.
- Replay/restart either reconstructs the authoritative lifecycle timing or explicitly omits unavailable timing; it must not relabel thread age as review duration.
- Review status, symbol, target title, and timing come from one semantic review projection.
- Tests use an injected/fixed clock and cover live completion, persisted replay, cancellation, failure, missing lifecycle metadata, and wall-clock advance.
- No formatter-only patch or fallback to
activityDate is used to mask missing ownership.
Regression evidence
3799f6b first discarded runtime review timestamps from the row path.
8a820eb completed the generic chat migration.
0ff385f explicitly introduced .relative(to: endedAt) and its current test expectation.
- v0.6.2
ReviewMonitorJobRowView.TimerLabelView used a paused timer interval from start to end.
Parent: #96
Confirmed failure
v0.6.2 displayed an active timer from
startedAtand froze it atendedAt, so a completed review showed its duration.Current ReviewMonitor instead maps a completed
ReviewRunRecord.endedAtto.relative(to: endedAt)and rendersText(date, style: .relative). The current test suite explicitly expects this age-since-completion behavior. Persisted rows without a live run fall back again to generic chat activity time and also lose review status/symbol.Broken invariant
Review timing is lifecycle state owned by the review run. A completed review's visible time is
endedAt - startedAt, not wall-clock age sinceendedAtand not generic thread recency.Acceptance criteria
activityDateis used to mask missing ownership.Regression evidence
3799f6bfirst discarded runtime review timestamps from the row path.8a820ebcompleted the generic chat migration.0ff385fexplicitly introduced.relative(to: endedAt)and its current test expectation.ReviewMonitorJobRowView.TimerLabelViewused a paused timer interval from start to end.