Render on/off-task split in the reflection block

This commit is contained in:
2026-06-01 21:02:23 -04:00
parent 7eb2b5dc58
commit e5c32563d0
2 changed files with 93 additions and 9 deletions
+36 -9
View File
@@ -263,9 +263,10 @@ func (c *Controller) startReflectionFetchLocked() {
}
// buildReflectionFinishedLocked renders the just-finished session as a compact
// block for the reviewer. Caller holds mu; c.stats/c.commitment are still set
// (End clears them, but enterReview runs before End). Reuses bucketViews for the
// per-window totals, already sorted desc by seconds.
// block for the reviewer: the commitment, the outcome, on/off/unclassified time
// totals, and a top-N on-task list and top-N off-task list. The split fields are
// populated live by creditLocked. Caller holds mu; c.stats/c.commitment are still
// set (End clears them, but enterReview runs before End).
func (c *Controller) buildReflectionFinishedLocked() string {
var na, sc string
if c.commitment != nil {
@@ -280,17 +281,43 @@ func (c *Controller) buildReflectionFinishedLocked() string {
fmt.Fprintf(&b, "Success condition: %s\n", sc)
fmt.Fprintf(&b, "Outcome: %s\n", outcome)
if c.stats != nil {
onMin := int64(sumDurations(c.stats.OnTask).Seconds()) / 60
offMin := int64(sumDurations(c.stats.OffTask).Seconds()) / 60
unclMin := int64(c.stats.unclassified.Seconds()) / 60
fmt.Fprintf(&b, "On-task %dm / Off-task %dm / Unclassified %dm\n", onMin, offMin, unclMin)
fmt.Fprintf(&b, "Context switches: %d\n", c.stats.SwitchCount)
for i, bv := range bucketViews(c.stats.Buckets) {
if i >= reflectionTopBuckets {
break
}
fmt.Fprintf(&b, "- %s · %s: %dm\n", bv.Class, bv.Title, bv.Seconds/60)
}
writeBucketList(&b, "On-task", c.stats.OnTask)
writeBucketList(&b, "Off-task", c.stats.OffTask)
}
return strings.TrimRight(b.String(), "\n")
}
// writeBucketList renders a labeled, time-descending list of buckets capped at
// reflectionTopBuckets. It writes nothing — not even the label — when the map is
// empty, so a single-sided session shows only the list that has time.
func writeBucketList(b *strings.Builder, label string, m map[bucketKey]time.Duration) {
views := bucketViews(m)
if len(views) == 0 {
return
}
fmt.Fprintf(b, "%s:\n", label)
for i, bv := range views {
if i >= reflectionTopBuckets {
break
}
fmt.Fprintf(b, "- %s · %s: %dm\n", bv.Class, bv.Title, bv.Seconds/60)
}
}
// sumDurations totals the durations in a bucket map.
func sumDurations(m map[bucketKey]time.Duration) time.Duration {
var total time.Duration
for _, d := range m {
total += d
}
return total
}
// buildReflectionHistory renders the last few prior sessions as compact lines.
// The just-finished session is not yet in the chain (End appends it), so it is
// not double-counted. Returns "" when there is no usable history.