Add RecentSessions reader over the audit chain
Exposes the last n session summaries (oldest-first) for the reviewer to read as recent-history context. Missing/empty chain or n<=0 yields nil. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,25 @@ func readSummaries(path string) ([]SessionSummary, error) {
|
|||||||
return out, sc.Err()
|
return out, sc.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentSessions returns up to n most-recent summaries from the audit chain in
|
||||||
|
// oldest-first order. A missing/empty chain or n <= 0 yields (nil, nil).
|
||||||
|
func RecentSessions(path string, n int) ([]SessionSummary, error) {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
all, err := readSummaries(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(all) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if len(all) > n {
|
||||||
|
all = all[len(all)-n:]
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
||||||
|
|
||||||
// AppendSession links s onto the chain (genesis prev_hash = 64 zeros), assigns
|
// AppendSession links s onto the chain (genesis prev_hash = 64 zeros), assigns
|
||||||
// the next contiguous Seq, computes its Hash, and appends one JSON line with an
|
// the next contiguous Seq, computes its Hash, and appends one JSON line with an
|
||||||
// fsync.
|
// fsync.
|
||||||
|
|||||||
@@ -75,3 +75,45 @@ func TestTamperDetected(t *testing.T) {
|
|||||||
t.Fatalf("tamper on line 2 should be reported, got %v", err)
|
t.Fatalf("tamper on line 2 should be reported, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRecentSessionsReturnsLastNOldestFirst(t *testing.T) {
|
||||||
|
path := auditFixture(t)
|
||||||
|
for _, id := range []string{"a", "b", "c"} {
|
||||||
|
if err := AppendSession(path, sampleSummary("session-"+id)); err != nil {
|
||||||
|
t.Fatalf("append %s: %v", id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
got, err := RecentSessions(path, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("recent: %v", err)
|
||||||
|
}
|
||||||
|
if len(got) != 2 {
|
||||||
|
t.Fatalf("want 2 summaries, got %d", len(got))
|
||||||
|
}
|
||||||
|
if got[0].SessionID != "session-b" || got[1].SessionID != "session-c" {
|
||||||
|
t.Fatalf("want [b c] oldest-first, got [%s %s]", got[0].SessionID, got[1].SessionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecentSessionsFewerThanN(t *testing.T) {
|
||||||
|
path := auditFixture(t)
|
||||||
|
_ = AppendSession(path, sampleSummary("session-a"))
|
||||||
|
got, err := RecentSessions(path, 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("recent: %v", err)
|
||||||
|
}
|
||||||
|
if len(got) != 1 || got[0].SessionID != "session-a" {
|
||||||
|
t.Fatalf("want [a], got %+v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecentSessionsMissingOrZero(t *testing.T) {
|
||||||
|
if got, err := RecentSessions(auditFixture(t), 5); err != nil || got != nil {
|
||||||
|
t.Fatalf("missing chain: want (nil,nil), got (%+v,%v)", got, err)
|
||||||
|
}
|
||||||
|
path := auditFixture(t)
|
||||||
|
_ = AppendSession(path, sampleSummary("session-a"))
|
||||||
|
if got, err := RecentSessions(path, 0); err != nil || got != nil {
|
||||||
|
t.Fatalf("n=0: want (nil,nil), got (%+v,%v)", got, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user