detached 5bcc0f8

perf(state): skip redundant persistence writes

commit
5bcc0f82d6584a7c7a56a642388ab2c86fc39471
author
brice · · 2026-08-18 15:03 UTC
parent
6fc319c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +219 -14
M packages/agent/subagents/resident_journal.go +48 -14
@@ -2,6 +2,7 @@ package subagents
22
33 import (
44 "bufio"
5+ "bytes"
56 "encoding/json"
67 "errors"
78 "fmt"
@@ -163,10 +164,16 @@ func (j *ResidentJournal) RecordAgentEvent(event core.AgentEvent) error {
163164 j.mu.Unlock()
164165 return errors.New("resident journal: closed")
165166 }
167+ if record.Type == residentRecordUsage && j.usageRecordAlreadyDurable(record) {
168+ j.mu.Unlock()
169+ j.publishAgentEvent(event)
170+ return nil
171+ }
166172 err := j.appendSync(record)
167173 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {
168174 j.usageMu.Lock()
169175 j.usage = usageEvent.Cumulative
176+ j.usagePersisted = true
170177 if current := usageEvent.Usage.PromptTokens(); current > 0 {
171178 j.contextUsed = current
172179 }
@@ -224,19 +231,20 @@ type ResidentResult struct {
224231 // ResidentJournal serializes durable child records. It is intentionally a
225232 // narrow storage primitive; the manager owns scheduling and provider work.
226233 type ResidentJournal struct {
227- mu sync.Mutex
228- eventMu sync.RWMutex
229- eventObserver func(core.AgentEvent)
230- summaryMu sync.RWMutex
231- latestSummary string
232- usageMu sync.RWMutex
233- usage provider.Usage
234- contextUsed int
235- contextMax int
236- subscription bool
237- dir string
238- file *os.File
239- lease *residentLease
234+ mu sync.Mutex
235+ eventMu sync.RWMutex
236+ eventObserver func(core.AgentEvent)
237+ summaryMu sync.RWMutex
238+ latestSummary string
239+ usageMu sync.RWMutex
240+ usage provider.Usage
241+ usagePersisted bool
242+ contextUsed int
243+ contextMax int
244+ subscription bool
245+ dir string
246+ file *os.File
247+ lease *residentLease
240248 }
241249
242250 // ResidentUsageSnapshot is the bounded usage projection shared by durable and
@@ -275,6 +283,19 @@ func (j *ResidentJournal) usageSnapshot() ResidentUsageSnapshot {
275283 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}
276284 }
277285
286+func (j *ResidentJournal) usageRecordAlreadyDurable(record residentRecord) bool {
287+ if record.Usage == nil {
288+ return false
289+ }
290+ j.usageMu.RLock()
291+ defer j.usageMu.RUnlock()
292+ return j.usagePersisted &&
293+ j.usage == *record.Usage &&
294+ j.contextUsed == record.ContextUsed &&
295+ j.contextMax == record.ContextMax &&
296+ j.subscription == record.Subscription
297+}
298+
278299 // SetEventObserver publishes in-memory agent events without creating another
279300 // durable transcript. Finalized events are observed only after journal append.
280301 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {
@@ -1074,6 +1095,10 @@ func writeResidentProjection(dir, name string, value any) error {
10741095 if err != nil {
10751096 return err
10761097 }
1098+ path := filepath.Join(dir, name)
1099+ if residentProjectionCurrent(path, data) {
1100+ return nil
1101+ }
10771102 tmp, err := os.CreateTemp(dir, ".metadata-*")
10781103 if err != nil {
10791104 return err
@@ -1094,7 +1119,7 @@ func writeResidentProjection(dir, name string, value any) error {
10941119 if err != nil {
10951120 return fmt.Errorf("resident metadata write: %w", err)
10961121 }
1097- if err := os.Rename(tmpName, filepath.Join(dir, name)); err != nil {
1122+ if err := os.Rename(tmpName, path); err != nil {
10981123 return fmt.Errorf("resident projection rename: %w", err)
10991124 }
11001125 if err := syncDirectory(dir); err != nil {
@@ -1102,3 +1127,12 @@ func writeResidentProjection(dir, name string, value any) error {
11021127 }
11031128 return nil
11041129 }
1130+
1131+func residentProjectionCurrent(path string, data []byte) bool {
1132+ current, err := os.ReadFile(path)
1133+ if err != nil || !bytes.Equal(current, data) {
1134+ return false
1135+ }
1136+ info, err := os.Stat(path)
1137+ return err == nil && info.Mode().Perm() == 0o600
1138+}
@@ -2,6 +2,7 @@ package subagents
2 2
3 import (3 import (
4 "bufio"4 "bufio"
5+ "bytes"
5 "encoding/json"6 "encoding/json"
6 "errors"7 "errors"
7 "fmt"8 "fmt"
@@ -163,10 +164,16 @@ func (j *ResidentJournal) RecordAgentEvent(event core.AgentEvent) error {
163 j.mu.Unlock()164 j.mu.Unlock()
164 return errors.New("resident journal: closed")165 return errors.New("resident journal: closed")
165 }166 }
167+ if record.Type == residentRecordUsage && j.usageRecordAlreadyDurable(record) {
168+ j.mu.Unlock()
169+ j.publishAgentEvent(event)
170+ return nil
171+ }
166 err := j.appendSync(record)172 err := j.appendSync(record)
167 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {173 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {
168 j.usageMu.Lock()174 j.usageMu.Lock()
169 j.usage = usageEvent.Cumulative175 j.usage = usageEvent.Cumulative
176+ j.usagePersisted = true
170 if current := usageEvent.Usage.PromptTokens(); current > 0 {177 if current := usageEvent.Usage.PromptTokens(); current > 0 {
171 j.contextUsed = current178 j.contextUsed = current
172 }179 }
@@ -224,19 +231,20 @@ type ResidentResult struct {
224 // ResidentJournal serializes durable child records. It is intentionally a231 // ResidentJournal serializes durable child records. It is intentionally a
225 // narrow storage primitive; the manager owns scheduling and provider work.232 // narrow storage primitive; the manager owns scheduling and provider work.
226 type ResidentJournal struct {233 type ResidentJournal struct {
227- mu sync.Mutex234+ mu sync.Mutex
228- eventMu sync.RWMutex235+ eventMu sync.RWMutex
229- eventObserver func(core.AgentEvent)236+ eventObserver func(core.AgentEvent)
230- summaryMu sync.RWMutex237+ summaryMu sync.RWMutex
231- latestSummary string238+ latestSummary string
232- usageMu sync.RWMutex239+ usageMu sync.RWMutex
233- usage provider.Usage240+ usage provider.Usage
234- contextUsed int241+ usagePersisted bool
235- contextMax int242+ contextUsed int
236- subscription bool243+ contextMax int
237- dir string244+ subscription bool
238- file *os.File245+ dir string
239- lease *residentLease246+ file *os.File
247+ lease *residentLease
240 }248 }
241 249
242 // ResidentUsageSnapshot is the bounded usage projection shared by durable and250 // ResidentUsageSnapshot is the bounded usage projection shared by durable and
@@ -275,6 +283,19 @@ func (j *ResidentJournal) usageSnapshot() ResidentUsageSnapshot {
275 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}283 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}
276 }284 }
277 285
286+func (j *ResidentJournal) usageRecordAlreadyDurable(record residentRecord) bool {
287+ if record.Usage == nil {
288+ return false
289+ }
290+ j.usageMu.RLock()
291+ defer j.usageMu.RUnlock()
292+ return j.usagePersisted &&
293+ j.usage == *record.Usage &&
294+ j.contextUsed == record.ContextUsed &&
295+ j.contextMax == record.ContextMax &&
296+ j.subscription == record.Subscription
297+}
298+
278 // SetEventObserver publishes in-memory agent events without creating another299 // SetEventObserver publishes in-memory agent events without creating another
279 // durable transcript. Finalized events are observed only after journal append.300 // durable transcript. Finalized events are observed only after journal append.
280 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {301 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {
@@ -1074,6 +1095,10 @@ func writeResidentProjection(dir, name string, value any) error {
1074 if err != nil {1095 if err != nil {
1075 return err1096 return err
1076 }1097 }
1098+ path := filepath.Join(dir, name)
1099+ if residentProjectionCurrent(path, data) {
1100+ return nil
1101+ }
1077 tmp, err := os.CreateTemp(dir, ".metadata-*")1102 tmp, err := os.CreateTemp(dir, ".metadata-*")
1078 if err != nil {1103 if err != nil {
1079 return err1104 return err
@@ -1094,7 +1119,7 @@ func writeResidentProjection(dir, name string, value any) error {
1094 if err != nil {1119 if err != nil {
1095 return fmt.Errorf("resident metadata write: %w", err)1120 return fmt.Errorf("resident metadata write: %w", err)
1096 }1121 }
1097- if err := os.Rename(tmpName, filepath.Join(dir, name)); err != nil {1122+ if err := os.Rename(tmpName, path); err != nil {
1098 return fmt.Errorf("resident projection rename: %w", err)1123 return fmt.Errorf("resident projection rename: %w", err)
1099 }1124 }
1100 if err := syncDirectory(dir); err != nil {1125 if err := syncDirectory(dir); err != nil {
@@ -1102,3 +1127,12 @@ func writeResidentProjection(dir, name string, value any) error {
1102 }1127 }
1103 return nil1128 return nil
1104 }1129 }
1130+
1131+func residentProjectionCurrent(path string, data []byte) bool {
1132+ current, err := os.ReadFile(path)
1133+ if err != nil || !bytes.Equal(current, data) {
1134+ return false
1135+ }
1136+ info, err := os.Stat(path)
1137+ return err == nil && info.Mode().Perm() == 0o600
1138+}
M packages/agent/subagents/resident_journal_test.go +102 -0
@@ -93,6 +93,36 @@ func TestResidentJournalReconcilesUsageMetadata(t *testing.T) {
9393 }
9494 }
9595
96+func TestResidentJournalSkipsDuplicateUsageRecord(t *testing.T) {
97+ root := t.TempDir()
98+ journal, err := OpenResidentJournal(root, "duplicate-usage")
99+ if err != nil {
100+ t.Fatal(err)
101+ }
102+ t.Cleanup(func() { _ = journal.Close() })
103+ spec := ResidentChildSpec{ID: "duplicate-usage", SessionID: "usage-session", Provider: "openai", Model: "gpt-test"}
104+ if err := journal.Accept(spec, "task"); err != nil {
105+ t.Fatal(err)
106+ }
107+ journal.ConfigureUsage(128_000, false)
108+ usage := provider.Usage{InputTokens: 10_000, OutputTokens: 500}
109+ event := core.EvUsage{Usage: usage, Cumulative: usage}
110+ if err := journal.RecordAgentEvent(event); err != nil {
111+ t.Fatal(err)
112+ }
113+ if err := journal.RecordAgentEvent(event); err != nil {
114+ t.Fatal(err)
115+ }
116+
117+ records, err := ReadResidentJournal(filepath.Join(journal.Dir(), residentTranscriptName))
118+ if err != nil {
119+ t.Fatal(err)
120+ }
121+ if got := len(records); got != 2 {
122+ t.Fatalf("journal records = %d, want accepted record plus one usage record", got)
123+ }
124+}
125+
96126 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {
97127 root := t.TempDir()
98128 journal, err := OpenResidentJournal(root, "cache-child")
@@ -146,6 +176,78 @@ func TestResidentJournalProjectsUsageEmittedAfterInterruption(t *testing.T) {
146176 }
147177 }
148178
179+func TestResidentProjectionSkipsIdenticalRewrite(t *testing.T) {
180+ dir := t.TempDir()
181+ metadata := ResidentMetadata{
182+ Version: residentJournalVersion,
183+ ID: "projection-child",
184+ SessionID: "projection-session",
185+ State: ResidentIdle,
186+ UpdatedAt: time.Unix(1_700_000_000, 0).UTC(),
187+ }
188+ if err := writeResidentMetadata(dir, metadata); err != nil {
189+ t.Fatal(err)
190+ }
191+ path := filepath.Join(dir, residentMetadataName)
192+ oldTime := time.Unix(1_600_000_000, 0)
193+ if err := os.Chtimes(path, oldTime, oldTime); err != nil {
194+ t.Fatal(err)
195+ }
196+
197+ if err := writeResidentMetadata(dir, metadata); err != nil {
198+ t.Fatal(err)
199+ }
200+ info, err := os.Stat(path)
201+ if err != nil {
202+ t.Fatal(err)
203+ }
204+ if !info.ModTime().Equal(oldTime) {
205+ t.Fatalf("identical projection rewrite changed mtime to %v", info.ModTime())
206+ }
207+
208+ metadata.State = ResidentStopped
209+ if err := writeResidentMetadata(dir, metadata); err != nil {
210+ t.Fatal(err)
211+ }
212+ info, err = os.Stat(path)
213+ if err != nil {
214+ t.Fatal(err)
215+ }
216+ if info.ModTime().Equal(oldTime) {
217+ t.Fatal("changed projection was not rewritten")
218+ }
219+}
220+
221+func TestResidentProjectionRepairsPermissionsForIdenticalData(t *testing.T) {
222+ if runtime.GOOS == "windows" {
223+ t.Skip("POSIX permission bits are not enforced on Windows")
224+ }
225+ dir := t.TempDir()
226+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
227+ data, err := json.Marshal(metadata)
228+ if err != nil {
229+ t.Fatal(err)
230+ }
231+ path := filepath.Join(dir, residentMetadataName)
232+ if err := os.WriteFile(path, data, 0o644); err != nil {
233+ t.Fatal(err)
234+ }
235+ if err := os.Chmod(path, 0o644); err != nil {
236+ t.Fatal(err)
237+ }
238+
239+ if err := writeResidentMetadata(dir, metadata); err != nil {
240+ t.Fatal(err)
241+ }
242+ info, err := os.Stat(path)
243+ if err != nil {
244+ t.Fatal(err)
245+ }
246+ if got := info.Mode().Perm(); got != 0o600 {
247+ t.Fatalf("projection permissions = %o, want 600", got)
248+ }
249+}
250+
149251 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {
150252 for _, childID := range []string{".", "..", "nested/child"} {
151253 t.Run(childID, func(t *testing.T) {
@@ -93,6 +93,36 @@ func TestResidentJournalReconcilesUsageMetadata(t *testing.T) {
93 }93 }
94 }94 }
95 95
96+func TestResidentJournalSkipsDuplicateUsageRecord(t *testing.T) {
97+ root := t.TempDir()
98+ journal, err := OpenResidentJournal(root, "duplicate-usage")
99+ if err != nil {
100+ t.Fatal(err)
101+ }
102+ t.Cleanup(func() { _ = journal.Close() })
103+ spec := ResidentChildSpec{ID: "duplicate-usage", SessionID: "usage-session", Provider: "openai", Model: "gpt-test"}
104+ if err := journal.Accept(spec, "task"); err != nil {
105+ t.Fatal(err)
106+ }
107+ journal.ConfigureUsage(128_000, false)
108+ usage := provider.Usage{InputTokens: 10_000, OutputTokens: 500}
109+ event := core.EvUsage{Usage: usage, Cumulative: usage}
110+ if err := journal.RecordAgentEvent(event); err != nil {
111+ t.Fatal(err)
112+ }
113+ if err := journal.RecordAgentEvent(event); err != nil {
114+ t.Fatal(err)
115+ }
116+
117+ records, err := ReadResidentJournal(filepath.Join(journal.Dir(), residentTranscriptName))
118+ if err != nil {
119+ t.Fatal(err)
120+ }
121+ if got := len(records); got != 2 {
122+ t.Fatalf("journal records = %d, want accepted record plus one usage record", got)
123+ }
124+}
125+
96 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {126 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {
97 root := t.TempDir()127 root := t.TempDir()
98 journal, err := OpenResidentJournal(root, "cache-child")128 journal, err := OpenResidentJournal(root, "cache-child")
@@ -146,6 +176,78 @@ func TestResidentJournalProjectsUsageEmittedAfterInterruption(t *testing.T) {
146 }176 }
147 }177 }
148 178
179+func TestResidentProjectionSkipsIdenticalRewrite(t *testing.T) {
180+ dir := t.TempDir()
181+ metadata := ResidentMetadata{
182+ Version: residentJournalVersion,
183+ ID: "projection-child",
184+ SessionID: "projection-session",
185+ State: ResidentIdle,
186+ UpdatedAt: time.Unix(1_700_000_000, 0).UTC(),
187+ }
188+ if err := writeResidentMetadata(dir, metadata); err != nil {
189+ t.Fatal(err)
190+ }
191+ path := filepath.Join(dir, residentMetadataName)
192+ oldTime := time.Unix(1_600_000_000, 0)
193+ if err := os.Chtimes(path, oldTime, oldTime); err != nil {
194+ t.Fatal(err)
195+ }
196+
197+ if err := writeResidentMetadata(dir, metadata); err != nil {
198+ t.Fatal(err)
199+ }
200+ info, err := os.Stat(path)
201+ if err != nil {
202+ t.Fatal(err)
203+ }
204+ if !info.ModTime().Equal(oldTime) {
205+ t.Fatalf("identical projection rewrite changed mtime to %v", info.ModTime())
206+ }
207+
208+ metadata.State = ResidentStopped
209+ if err := writeResidentMetadata(dir, metadata); err != nil {
210+ t.Fatal(err)
211+ }
212+ info, err = os.Stat(path)
213+ if err != nil {
214+ t.Fatal(err)
215+ }
216+ if info.ModTime().Equal(oldTime) {
217+ t.Fatal("changed projection was not rewritten")
218+ }
219+}
220+
221+func TestResidentProjectionRepairsPermissionsForIdenticalData(t *testing.T) {
222+ if runtime.GOOS == "windows" {
223+ t.Skip("POSIX permission bits are not enforced on Windows")
224+ }
225+ dir := t.TempDir()
226+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
227+ data, err := json.Marshal(metadata)
228+ if err != nil {
229+ t.Fatal(err)
230+ }
231+ path := filepath.Join(dir, residentMetadataName)
232+ if err := os.WriteFile(path, data, 0o644); err != nil {
233+ t.Fatal(err)
234+ }
235+ if err := os.Chmod(path, 0o644); err != nil {
236+ t.Fatal(err)
237+ }
238+
239+ if err := writeResidentMetadata(dir, metadata); err != nil {
240+ t.Fatal(err)
241+ }
242+ info, err := os.Stat(path)
243+ if err != nil {
244+ t.Fatal(err)
245+ }
246+ if got := info.Mode().Perm(); got != 0o600 {
247+ t.Fatalf("projection permissions = %o, want 600", got)
248+ }
249+}
250+
149 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {251 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {
150 for _, childID := range []string{".", "..", "nested/child"} {252 for _, childID := range []string{".", "..", "nested/child"} {
151 t.Run(childID, func(t *testing.T) {253 t.Run(childID, func(t *testing.T) {
M packages/core/session.go +13 -0
@@ -49,6 +49,9 @@ type Session struct {
4949
5050 retryMu sync.Mutex
5151 pendingRetryLifecycle []RetryLifecycleRecord
52+ lastWriteWasUsage bool
53+ lastUsage provider.Usage
54+ lastCumulativeUsage provider.Usage
5255 }
5356
5457 // GoalStatus is the persisted lifecycle state of an autonomous session goal.
@@ -1628,9 +1631,18 @@ func (s *Session) writeUsageCheckpointLocked(row sessionLine) error {
16281631 if len(s.pendingRetryLifecycle) > 0 {
16291632 row.RetryLifecycle = append([]RetryLifecycleRecord(nil), s.pendingRetryLifecycle...)
16301633 }
1634+ if row.Type == "usage" && len(row.RetryLifecycle) == 0 && row.Usage != nil && row.Cumulative != nil &&
1635+ s.lastWriteWasUsage && *row.Usage == s.lastUsage && *row.Cumulative == s.lastCumulativeUsage {
1636+ return nil
1637+ }
16311638 if err := s.writeLine(row); err != nil {
16321639 return err
16331640 }
1641+ if row.Type == "usage" && row.Usage != nil && row.Cumulative != nil {
1642+ s.lastWriteWasUsage = true
1643+ s.lastUsage = *row.Usage
1644+ s.lastCumulativeUsage = *row.Cumulative
1645+ }
16341646 s.pendingRetryLifecycle = nil
16351647 return nil
16361648 }
@@ -1732,6 +1744,7 @@ func (s *Session) Close() error {
17321744 }
17331745
17341746 func (s *Session) writeLine(row sessionLine) error {
1747+ s.lastWriteWasUsage = false
17351748 b, err := json.Marshal(row)
17361749 if err != nil {
17371750 return err
@@ -49,6 +49,9 @@ type Session struct {
49 49
50 retryMu sync.Mutex50 retryMu sync.Mutex
51 pendingRetryLifecycle []RetryLifecycleRecord51 pendingRetryLifecycle []RetryLifecycleRecord
52+ lastWriteWasUsage bool
53+ lastUsage provider.Usage
54+ lastCumulativeUsage provider.Usage
52 }55 }
53 56
54 // GoalStatus is the persisted lifecycle state of an autonomous session goal.57 // GoalStatus is the persisted lifecycle state of an autonomous session goal.
@@ -1628,9 +1631,18 @@ func (s *Session) writeUsageCheckpointLocked(row sessionLine) error {
1628 if len(s.pendingRetryLifecycle) > 0 {1631 if len(s.pendingRetryLifecycle) > 0 {
1629 row.RetryLifecycle = append([]RetryLifecycleRecord(nil), s.pendingRetryLifecycle...)1632 row.RetryLifecycle = append([]RetryLifecycleRecord(nil), s.pendingRetryLifecycle...)
1630 }1633 }
1634+ if row.Type == "usage" && len(row.RetryLifecycle) == 0 && row.Usage != nil && row.Cumulative != nil &&
1635+ s.lastWriteWasUsage && *row.Usage == s.lastUsage && *row.Cumulative == s.lastCumulativeUsage {
1636+ return nil
1637+ }
1631 if err := s.writeLine(row); err != nil {1638 if err := s.writeLine(row); err != nil {
1632 return err1639 return err
1633 }1640 }
1641+ if row.Type == "usage" && row.Usage != nil && row.Cumulative != nil {
1642+ s.lastWriteWasUsage = true
1643+ s.lastUsage = *row.Usage
1644+ s.lastCumulativeUsage = *row.Cumulative
1645+ }
1634 s.pendingRetryLifecycle = nil1646 s.pendingRetryLifecycle = nil
1635 return nil1647 return nil
1636 }1648 }
@@ -1732,6 +1744,7 @@ func (s *Session) Close() error {
1732 }1744 }
1733 1745
1734 func (s *Session) writeLine(row sessionLine) error {1746 func (s *Session) writeLine(row sessionLine) error {
1747+ s.lastWriteWasUsage = false
1735 b, err := json.Marshal(row)1748 b, err := json.Marshal(row)
1736 if err != nil {1749 if err != nil {
1737 return err1750 return err
M packages/core/session_usage_test.go +56 -0
@@ -46,6 +46,62 @@ func TestSessionUsageDetailDeltasMeasuredCacheUsage(t *testing.T) {
4646 }
4747 }
4848
49+func TestSessionAppendUsageSkipsConsecutiveDuplicateCheckpoint(t *testing.T) {
50+ sess := newUsageTestSession(t)
51+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "prompt"}}}); err != nil {
52+ t.Fatal(err)
53+ }
54+ usage := provider.Usage{InputTokens: 120, OutputTokens: 8, CostUSD: 0.25}
55+ if err := sess.AppendUsage(usage, usage); err != nil {
56+ t.Fatal(err)
57+ }
58+ if err := sess.AppendUsage(usage, usage); err != nil {
59+ t.Fatal(err)
60+ }
61+ if err := sess.Close(); err != nil {
62+ t.Fatal(err)
63+ }
64+
65+ snapshot, err := ReadSessionSnapshot(sess.Path)
66+ if err != nil {
67+ t.Fatal(err)
68+ }
69+ if got := len(snapshot.UsageCheckpoints); got != 1 {
70+ t.Fatalf("usage checkpoints = %d, want 1 for consecutive duplicate usage", got)
71+ }
72+}
73+
74+func TestSessionAppendUsagePreservesCheckpointAfterMessage(t *testing.T) {
75+ sess := newUsageTestSession(t)
76+ usage := provider.Usage{InputTokens: 120, OutputTokens: 8}
77+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "first"}}}); err != nil {
78+ t.Fatal(err)
79+ }
80+ if err := sess.AppendUsage(usage, usage); err != nil {
81+ t.Fatal(err)
82+ }
83+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleAssistant, Content: []provider.Content{provider.TextBlock{Text: "second"}}}); err != nil {
84+ t.Fatal(err)
85+ }
86+ if err := sess.AppendUsage(usage, usage); err != nil {
87+ t.Fatal(err)
88+ }
89+ if err := sess.Close(); err != nil {
90+ t.Fatal(err)
91+ }
92+
93+ snapshot, err := ReadSessionSnapshot(sess.Path)
94+ if err != nil {
95+ t.Fatal(err)
96+ }
97+ if got := len(snapshot.UsageCheckpoints); got != 2 {
98+ t.Fatalf("usage checkpoints = %d, want 2 across transcript changes", got)
99+ }
100+ if got := snapshot.UsageCheckpoints[1].MessageCount; got != 2 {
101+ t.Fatalf("second checkpoint message count = %d, want 2", got)
102+ }
103+}
104+
49105 func TestSessionUsageDetailResetsContextAfterCompaction(t *testing.T) {
50106 t.Run("compaction is latest row", func(t *testing.T) {
51107 sess := newUsageTestSession(t)
@@ -46,6 +46,62 @@ func TestSessionUsageDetailDeltasMeasuredCacheUsage(t *testing.T) {
46 }46 }
47 }47 }
48 48
49+func TestSessionAppendUsageSkipsConsecutiveDuplicateCheckpoint(t *testing.T) {
50+ sess := newUsageTestSession(t)
51+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "prompt"}}}); err != nil {
52+ t.Fatal(err)
53+ }
54+ usage := provider.Usage{InputTokens: 120, OutputTokens: 8, CostUSD: 0.25}
55+ if err := sess.AppendUsage(usage, usage); err != nil {
56+ t.Fatal(err)
57+ }
58+ if err := sess.AppendUsage(usage, usage); err != nil {
59+ t.Fatal(err)
60+ }
61+ if err := sess.Close(); err != nil {
62+ t.Fatal(err)
63+ }
64+
65+ snapshot, err := ReadSessionSnapshot(sess.Path)
66+ if err != nil {
67+ t.Fatal(err)
68+ }
69+ if got := len(snapshot.UsageCheckpoints); got != 1 {
70+ t.Fatalf("usage checkpoints = %d, want 1 for consecutive duplicate usage", got)
71+ }
72+}
73+
74+func TestSessionAppendUsagePreservesCheckpointAfterMessage(t *testing.T) {
75+ sess := newUsageTestSession(t)
76+ usage := provider.Usage{InputTokens: 120, OutputTokens: 8}
77+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleUser, Content: []provider.Content{provider.TextBlock{Text: "first"}}}); err != nil {
78+ t.Fatal(err)
79+ }
80+ if err := sess.AppendUsage(usage, usage); err != nil {
81+ t.Fatal(err)
82+ }
83+ if err := sess.AppendMessage(provider.Message{Role: provider.RoleAssistant, Content: []provider.Content{provider.TextBlock{Text: "second"}}}); err != nil {
84+ t.Fatal(err)
85+ }
86+ if err := sess.AppendUsage(usage, usage); err != nil {
87+ t.Fatal(err)
88+ }
89+ if err := sess.Close(); err != nil {
90+ t.Fatal(err)
91+ }
92+
93+ snapshot, err := ReadSessionSnapshot(sess.Path)
94+ if err != nil {
95+ t.Fatal(err)
96+ }
97+ if got := len(snapshot.UsageCheckpoints); got != 2 {
98+ t.Fatalf("usage checkpoints = %d, want 2 across transcript changes", got)
99+ }
100+ if got := snapshot.UsageCheckpoints[1].MessageCount; got != 2 {
101+ t.Fatalf("second checkpoint message count = %d, want 2", got)
102+ }
103+}
104+
49 func TestSessionUsageDetailResetsContextAfterCompaction(t *testing.T) {105 func TestSessionUsageDetailResetsContextAfterCompaction(t *testing.T) {
50 t.Run("compaction is latest row", func(t *testing.T) {106 t.Run("compaction is latest row", func(t *testing.T) {
51 sess := newUsageTestSession(t)107 sess := newUsageTestSession(t)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close