From ca5e76baf501b19007fb53fef7533efdeb70544e Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Mon, 1 Jun 2026 21:05:07 -0400 Subject: [PATCH] Test faithful on/off-task crediting through RecordWindow Co-Authored-By: Claude Opus 4.8 --- internal/session/session_test.go | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/internal/session/session_test.go b/internal/session/session_test.go index cb67479..e0a2783 100644 --- a/internal/session/session_test.go +++ b/internal/session/session_test.go @@ -1465,3 +1465,46 @@ func TestReflectionBlockOmitsEmptyOffTaskList(t *testing.T) { t.Errorf("totals line missing or wrong, got:\n%s", got) } } + +func TestRecordWindowCreditsSplitFaithfully(t *testing.T) { + c, _ := newTestController(t) + clk := &fakeClock{now: time.Unix(1000, 0)} + c.SetClock(clk.fn()) + c.SetDriftJudge(&fakeJudge{verdict: ai.Verdict{OnTask: false, Reason: "off"}}) + + c.RecordWindow(snap("code", "main.go")) // latest window before start + startActive(t, c, []string{"code"}) // seeds code/main.go at t=1000, driftStatus idle + + clk.advance(5 * time.Minute) + c.RecordWindow(snap("code", "main.go")) // credits 5m to code/main.go while idle -> unclassified; now on-task (local match) + clk.advance(10 * time.Minute) + c.RecordWindow(snap("code", "main.go")) // credits 10m on-task + clk.advance(10 * time.Minute) + c.RecordWindow(snap("firefox", "YouTube")) // credits 10m on-task (total 20m); firefox -> judge (pending) + waitDriftStatus(t, c, "drifting") // wait for the async verdict before crediting the firefox segment + + clk.advance(8 * time.Minute) + c.RecordWindow(snap("firefox", "Reddit")) // credits 8m to firefox/YouTube while drifting -> off-task; cache keeps drifting + clk.advance(4 * time.Minute) + if err := c.Complete(); err != nil { // flush: credits 4m to firefox/Reddit while drifting -> off-task + t.Fatalf("complete: %v", err) + } + + c.mu.Lock() + got := c.buildReflectionFinishedLocked() + c.mu.Unlock() + + want := "Next action: write report\n" + + "Success condition: report drafted\n" + + "Outcome: completed\n" + + "On-task 20m / Off-task 12m / Unclassified 5m\n" + + "Context switches: 2\n" + + "On-task:\n" + + "- code · main.go: 20m\n" + + "Off-task:\n" + + "- firefox · YouTube: 8m\n" + + "- firefox · Reddit: 4m" + if got != want { + t.Errorf("faithful split mismatch:\n--- got ---\n%s\n--- want ---\n%s", got, want) + } +}