detached e791659

Merge pull request #145 from bnema/perf/state-persistence

perf(state): skip redundant persistence writes
commit
e7916598e6ebf3a82e69431ffcc6ae14d1d378cb
author
bnema · · 2026-08-18 15:49 UTC
committer
GitHub · · 2026-08-18 15:49 UTC
merge of
bb1e1e8 + d79d4ec · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
6 files · +362 -14
M packages/agent/subagents/resident_journal.go +70 -14
@@ -163,15 +163,38 @@ func (j *ResidentJournal) RecordAgentEvent(event core.AgentEvent) error {
163163 j.mu.Unlock()
164164 return errors.New("resident journal: closed")
165165 }
166+ if record.Type == residentRecordUsage {
167+ durable, metadataPersisted := j.usageRecordPersistence(record)
168+ if durable {
169+ var err error
170+ if !metadataPersisted {
171+ err = j.projectUsageMetadata()
172+ if err == nil {
173+ j.markUsageMetadataPersisted(record)
174+ }
175+ }
176+ j.mu.Unlock()
177+ if err != nil {
178+ return err
179+ }
180+ j.publishAgentEvent(event)
181+ return nil
182+ }
183+ }
166184 err := j.appendSync(record)
167185 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {
168186 j.usageMu.Lock()
169187 j.usage = usageEvent.Cumulative
188+ j.usageRecordPersisted = true
189+ j.usageMetadataPersisted = false
170190 if current := usageEvent.Usage.PromptTokens(); current > 0 {
171191 j.contextUsed = current
172192 }
173193 j.usageMu.Unlock()
174194 err = j.projectUsageMetadata()
195+ if err == nil {
196+ j.markUsageMetadataPersisted(record)
197+ }
175198 }
176199 j.mu.Unlock()
177200 if err == nil {
@@ -224,19 +247,21 @@ type ResidentResult struct {
224247 // ResidentJournal serializes durable child records. It is intentionally a
225248 // narrow storage primitive; the manager owns scheduling and provider work.
226249 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
250+ mu sync.Mutex
251+ eventMu sync.RWMutex
252+ eventObserver func(core.AgentEvent)
253+ summaryMu sync.RWMutex
254+ latestSummary string
255+ usageMu sync.RWMutex
256+ usage provider.Usage
257+ usageRecordPersisted bool
258+ usageMetadataPersisted bool
259+ contextUsed int
260+ contextMax int
261+ subscription bool
262+ dir string
263+ file *os.File
264+ lease *residentLease
240265 }
241266
242267 // ResidentUsageSnapshot is the bounded usage projection shared by durable and
@@ -275,6 +300,33 @@ func (j *ResidentJournal) usageSnapshot() ResidentUsageSnapshot {
275300 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}
276301 }
277302
303+func (j *ResidentJournal) usageRecordPersistence(record residentRecord) (durable, metadataPersisted bool) {
304+ if record.Usage == nil {
305+ return false, false
306+ }
307+ j.usageMu.RLock()
308+ defer j.usageMu.RUnlock()
309+ matches := j.usage == *record.Usage &&
310+ j.contextUsed == record.ContextUsed &&
311+ j.contextMax == record.ContextMax &&
312+ j.subscription == record.Subscription
313+ return j.usageRecordPersisted && matches, j.usageMetadataPersisted && matches
314+}
315+
316+func (j *ResidentJournal) markUsageMetadataPersisted(record residentRecord) {
317+ if record.Usage == nil {
318+ return
319+ }
320+ j.usageMu.Lock()
321+ if j.usage == *record.Usage &&
322+ j.contextUsed == record.ContextUsed &&
323+ j.contextMax == record.ContextMax &&
324+ j.subscription == record.Subscription {
325+ j.usageMetadataPersisted = true
326+ }
327+ j.usageMu.Unlock()
328+}
329+
278330 // SetEventObserver publishes in-memory agent events without creating another
279331 // durable transcript. Finalized events are observed only after journal append.
280332 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {
@@ -1074,6 +1126,10 @@ func writeResidentProjection(dir, name string, value any) error {
10741126 if err != nil {
10751127 return err
10761128 }
1129+ path := filepath.Join(dir, name)
1130+ if residentProjectionCurrent(path, data) {
1131+ return nil
1132+ }
10771133 tmp, err := os.CreateTemp(dir, ".metadata-*")
10781134 if err != nil {
10791135 return err
@@ -1094,7 +1150,7 @@ func writeResidentProjection(dir, name string, value any) error {
10941150 if err != nil {
10951151 return fmt.Errorf("resident metadata write: %w", err)
10961152 }
1097- if err := os.Rename(tmpName, filepath.Join(dir, name)); err != nil {
1153+ if err := os.Rename(tmpName, path); err != nil {
10981154 return fmt.Errorf("resident projection rename: %w", err)
10991155 }
11001156 if err := syncDirectory(dir); err != nil {
@@ -163,15 +163,38 @@ func (j *ResidentJournal) RecordAgentEvent(event core.AgentEvent) error {
163 j.mu.Unlock()163 j.mu.Unlock()
164 return errors.New("resident journal: closed")164 return errors.New("resident journal: closed")
165 }165 }
166+ if record.Type == residentRecordUsage {
167+ durable, metadataPersisted := j.usageRecordPersistence(record)
168+ if durable {
169+ var err error
170+ if !metadataPersisted {
171+ err = j.projectUsageMetadata()
172+ if err == nil {
173+ j.markUsageMetadataPersisted(record)
174+ }
175+ }
176+ j.mu.Unlock()
177+ if err != nil {
178+ return err
179+ }
180+ j.publishAgentEvent(event)
181+ return nil
182+ }
183+ }
166 err := j.appendSync(record)184 err := j.appendSync(record)
167 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {185 if usageEvent, ok := event.(core.EvUsage); err == nil && ok {
168 j.usageMu.Lock()186 j.usageMu.Lock()
169 j.usage = usageEvent.Cumulative187 j.usage = usageEvent.Cumulative
188+ j.usageRecordPersisted = true
189+ j.usageMetadataPersisted = false
170 if current := usageEvent.Usage.PromptTokens(); current > 0 {190 if current := usageEvent.Usage.PromptTokens(); current > 0 {
171 j.contextUsed = current191 j.contextUsed = current
172 }192 }
173 j.usageMu.Unlock()193 j.usageMu.Unlock()
174 err = j.projectUsageMetadata()194 err = j.projectUsageMetadata()
195+ if err == nil {
196+ j.markUsageMetadataPersisted(record)
197+ }
175 }198 }
176 j.mu.Unlock()199 j.mu.Unlock()
177 if err == nil {200 if err == nil {
@@ -224,19 +247,21 @@ type ResidentResult struct {
224 // ResidentJournal serializes durable child records. It is intentionally a247 // ResidentJournal serializes durable child records. It is intentionally a
225 // narrow storage primitive; the manager owns scheduling and provider work.248 // narrow storage primitive; the manager owns scheduling and provider work.
226 type ResidentJournal struct {249 type ResidentJournal struct {
227- mu sync.Mutex250+ mu sync.Mutex
228- eventMu sync.RWMutex251+ eventMu sync.RWMutex
229- eventObserver func(core.AgentEvent)252+ eventObserver func(core.AgentEvent)
230- summaryMu sync.RWMutex253+ summaryMu sync.RWMutex
231- latestSummary string254+ latestSummary string
232- usageMu sync.RWMutex255+ usageMu sync.RWMutex
233- usage provider.Usage256+ usage provider.Usage
234- contextUsed int257+ usageRecordPersisted bool
235- contextMax int258+ usageMetadataPersisted bool
236- subscription bool259+ contextUsed int
237- dir string260+ contextMax int
238- file *os.File261+ subscription bool
239- lease *residentLease262+ dir string
263+ file *os.File
264+ lease *residentLease
240 }265 }
241 266
242 // ResidentUsageSnapshot is the bounded usage projection shared by durable and267 // ResidentUsageSnapshot is the bounded usage projection shared by durable and
@@ -275,6 +300,33 @@ func (j *ResidentJournal) usageSnapshot() ResidentUsageSnapshot {
275 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}300 return ResidentUsageSnapshot{Usage: j.usage, ContextUsed: j.contextUsed, ContextMax: j.contextMax, Subscription: j.subscription}
276 }301 }
277 302
303+func (j *ResidentJournal) usageRecordPersistence(record residentRecord) (durable, metadataPersisted bool) {
304+ if record.Usage == nil {
305+ return false, false
306+ }
307+ j.usageMu.RLock()
308+ defer j.usageMu.RUnlock()
309+ matches := j.usage == *record.Usage &&
310+ j.contextUsed == record.ContextUsed &&
311+ j.contextMax == record.ContextMax &&
312+ j.subscription == record.Subscription
313+ return j.usageRecordPersisted && matches, j.usageMetadataPersisted && matches
314+}
315+
316+func (j *ResidentJournal) markUsageMetadataPersisted(record residentRecord) {
317+ if record.Usage == nil {
318+ return
319+ }
320+ j.usageMu.Lock()
321+ if j.usage == *record.Usage &&
322+ j.contextUsed == record.ContextUsed &&
323+ j.contextMax == record.ContextMax &&
324+ j.subscription == record.Subscription {
325+ j.usageMetadataPersisted = true
326+ }
327+ j.usageMu.Unlock()
328+}
329+
278 // SetEventObserver publishes in-memory agent events without creating another330 // SetEventObserver publishes in-memory agent events without creating another
279 // durable transcript. Finalized events are observed only after journal append.331 // durable transcript. Finalized events are observed only after journal append.
280 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {332 func (j *ResidentJournal) SetEventObserver(observer func(core.AgentEvent)) {
@@ -1074,6 +1126,10 @@ func writeResidentProjection(dir, name string, value any) error {
1074 if err != nil {1126 if err != nil {
1075 return err1127 return err
1076 }1128 }
1129+ path := filepath.Join(dir, name)
1130+ if residentProjectionCurrent(path, data) {
1131+ return nil
1132+ }
1077 tmp, err := os.CreateTemp(dir, ".metadata-*")1133 tmp, err := os.CreateTemp(dir, ".metadata-*")
1078 if err != nil {1134 if err != nil {
1079 return err1135 return err
@@ -1094,7 +1150,7 @@ func writeResidentProjection(dir, name string, value any) error {
1094 if err != nil {1150 if err != nil {
1095 return fmt.Errorf("resident metadata write: %w", err)1151 return fmt.Errorf("resident metadata write: %w", err)
1096 }1152 }
1097- if err := os.Rename(tmpName, filepath.Join(dir, name)); err != nil {1153+ if err := os.Rename(tmpName, path); err != nil {
1098 return fmt.Errorf("resident projection rename: %w", err)1154 return fmt.Errorf("resident projection rename: %w", err)
1099 }1155 }
1100 if err := syncDirectory(dir); err != nil {1156 if err := syncDirectory(dir); err != nil {
M packages/agent/subagents/resident_journal_test.go +185 -0
@@ -93,6 +93,85 @@ 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+
126+func TestResidentJournalDuplicateUsageRetriesFailedMetadataProjection(t *testing.T) {
127+ root := t.TempDir()
128+ journal, err := OpenResidentJournal(root, "retry-usage-projection")
129+ if err != nil {
130+ t.Fatal(err)
131+ }
132+ t.Cleanup(func() { _ = journal.Close() })
133+ spec := ResidentChildSpec{ID: "retry-usage-projection", SessionID: "usage-session", Provider: "openai", Model: "gpt-test"}
134+ if err := journal.Accept(spec, "task"); err != nil {
135+ t.Fatal(err)
136+ }
137+ journal.ConfigureUsage(128_000, false)
138+ metadataPath := filepath.Join(journal.Dir(), residentMetadataName)
139+ originalMetadata, err := os.ReadFile(metadataPath)
140+ if err != nil {
141+ t.Fatal(err)
142+ }
143+ if err := os.WriteFile(metadataPath, []byte("not-json"), 0o600); err != nil {
144+ t.Fatal(err)
145+ }
146+
147+ usage := provider.Usage{InputTokens: 10_000, OutputTokens: 500}
148+ event := core.EvUsage{Usage: usage, Cumulative: usage}
149+ if err := journal.RecordAgentEvent(event); err == nil {
150+ t.Fatal("first usage projection succeeded with malformed metadata")
151+ }
152+ if err := os.WriteFile(metadataPath, originalMetadata, 0o600); err != nil {
153+ t.Fatal(err)
154+ }
155+ if err := journal.RecordAgentEvent(event); err != nil {
156+ t.Fatalf("retry identical usage: %v", err)
157+ }
158+
159+ records, err := ReadResidentJournal(filepath.Join(journal.Dir(), residentTranscriptName))
160+ if err != nil {
161+ t.Fatal(err)
162+ }
163+ if got := len(records); got != 2 {
164+ t.Fatalf("journal records = %d, want accepted record plus one usage record", got)
165+ }
166+ metadata, err := ReadResidentMetadata(metadataPath)
167+ if err != nil {
168+ t.Fatal(err)
169+ }
170+ if metadata.Usage != usage || metadata.ContextUsed != usage.PromptTokens() {
171+ t.Fatalf("metadata usage = %+v, context = %d; want %+v, %d", metadata.Usage, metadata.ContextUsed, usage, usage.PromptTokens())
172+ }
173+}
174+
96175 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {
97176 root := t.TempDir()
98177 journal, err := OpenResidentJournal(root, "cache-child")
@@ -146,6 +225,112 @@ func TestResidentJournalProjectsUsageEmittedAfterInterruption(t *testing.T) {
146225 }
147226 }
148227
228+func TestResidentProjectionSkipsIdenticalRewrite(t *testing.T) {
229+ if runtime.GOOS == "windows" {
230+ t.Skip("no-follow projection reads are unavailable on Windows")
231+ }
232+ dir := t.TempDir()
233+ metadata := ResidentMetadata{
234+ Version: residentJournalVersion,
235+ ID: "projection-child",
236+ SessionID: "projection-session",
237+ State: ResidentIdle,
238+ UpdatedAt: time.Unix(1_700_000_000, 0).UTC(),
239+ }
240+ if err := writeResidentMetadata(dir, metadata); err != nil {
241+ t.Fatal(err)
242+ }
243+ path := filepath.Join(dir, residentMetadataName)
244+ oldTime := time.Unix(1_600_000_000, 0)
245+ if err := os.Chtimes(path, oldTime, oldTime); err != nil {
246+ t.Fatal(err)
247+ }
248+
249+ if err := writeResidentMetadata(dir, metadata); err != nil {
250+ t.Fatal(err)
251+ }
252+ info, err := os.Stat(path)
253+ if err != nil {
254+ t.Fatal(err)
255+ }
256+ if !info.ModTime().Equal(oldTime) {
257+ t.Fatalf("identical projection rewrite changed mtime to %v", info.ModTime())
258+ }
259+
260+ metadata.State = ResidentStopped
261+ if err := writeResidentMetadata(dir, metadata); err != nil {
262+ t.Fatal(err)
263+ }
264+ info, err = os.Stat(path)
265+ if err != nil {
266+ t.Fatal(err)
267+ }
268+ if info.ModTime().Equal(oldTime) {
269+ t.Fatal("changed projection was not rewritten")
270+ }
271+}
272+
273+func TestResidentProjectionReplacesSymlinkInsteadOfFollowingIt(t *testing.T) {
274+ if runtime.GOOS == "windows" {
275+ t.Skip("symlink creation may require elevated privileges on Windows")
276+ }
277+ dir := t.TempDir()
278+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
279+ data, err := json.Marshal(metadata)
280+ if err != nil {
281+ t.Fatal(err)
282+ }
283+ target := filepath.Join(dir, "target.json")
284+ if err := os.WriteFile(target, data, 0o600); err != nil {
285+ t.Fatal(err)
286+ }
287+ path := filepath.Join(dir, residentMetadataName)
288+ if err := os.Symlink(target, path); err != nil {
289+ t.Fatal(err)
290+ }
291+
292+ if err := writeResidentMetadata(dir, metadata); err != nil {
293+ t.Fatal(err)
294+ }
295+ info, err := os.Lstat(path)
296+ if err != nil {
297+ t.Fatal(err)
298+ }
299+ if !info.Mode().IsRegular() {
300+ t.Fatalf("projection mode = %v, want regular file", info.Mode())
301+ }
302+}
303+
304+func TestResidentProjectionRepairsPermissionsForIdenticalData(t *testing.T) {
305+ if runtime.GOOS == "windows" {
306+ t.Skip("POSIX permission bits are not enforced on Windows")
307+ }
308+ dir := t.TempDir()
309+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
310+ data, err := json.Marshal(metadata)
311+ if err != nil {
312+ t.Fatal(err)
313+ }
314+ path := filepath.Join(dir, residentMetadataName)
315+ if err := os.WriteFile(path, data, 0o644); err != nil {
316+ t.Fatal(err)
317+ }
318+ if err := os.Chmod(path, 0o644); err != nil {
319+ t.Fatal(err)
320+ }
321+
322+ if err := writeResidentMetadata(dir, metadata); err != nil {
323+ t.Fatal(err)
324+ }
325+ info, err := os.Stat(path)
326+ if err != nil {
327+ t.Fatal(err)
328+ }
329+ if got := info.Mode().Perm(); got != 0o600 {
330+ t.Fatalf("projection permissions = %o, want 600", got)
331+ }
332+}
333+
149334 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {
150335 for _, childID := range []string{".", "..", "nested/child"} {
151336 t.Run(childID, func(t *testing.T) {
@@ -93,6 +93,85 @@ 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+
126+func TestResidentJournalDuplicateUsageRetriesFailedMetadataProjection(t *testing.T) {
127+ root := t.TempDir()
128+ journal, err := OpenResidentJournal(root, "retry-usage-projection")
129+ if err != nil {
130+ t.Fatal(err)
131+ }
132+ t.Cleanup(func() { _ = journal.Close() })
133+ spec := ResidentChildSpec{ID: "retry-usage-projection", SessionID: "usage-session", Provider: "openai", Model: "gpt-test"}
134+ if err := journal.Accept(spec, "task"); err != nil {
135+ t.Fatal(err)
136+ }
137+ journal.ConfigureUsage(128_000, false)
138+ metadataPath := filepath.Join(journal.Dir(), residentMetadataName)
139+ originalMetadata, err := os.ReadFile(metadataPath)
140+ if err != nil {
141+ t.Fatal(err)
142+ }
143+ if err := os.WriteFile(metadataPath, []byte("not-json"), 0o600); err != nil {
144+ t.Fatal(err)
145+ }
146+
147+ usage := provider.Usage{InputTokens: 10_000, OutputTokens: 500}
148+ event := core.EvUsage{Usage: usage, Cumulative: usage}
149+ if err := journal.RecordAgentEvent(event); err == nil {
150+ t.Fatal("first usage projection succeeded with malformed metadata")
151+ }
152+ if err := os.WriteFile(metadataPath, originalMetadata, 0o600); err != nil {
153+ t.Fatal(err)
154+ }
155+ if err := journal.RecordAgentEvent(event); err != nil {
156+ t.Fatalf("retry identical usage: %v", err)
157+ }
158+
159+ records, err := ReadResidentJournal(filepath.Join(journal.Dir(), residentTranscriptName))
160+ if err != nil {
161+ t.Fatal(err)
162+ }
163+ if got := len(records); got != 2 {
164+ t.Fatalf("journal records = %d, want accepted record plus one usage record", got)
165+ }
166+ metadata, err := ReadResidentMetadata(metadataPath)
167+ if err != nil {
168+ t.Fatal(err)
169+ }
170+ if metadata.Usage != usage || metadata.ContextUsed != usage.PromptTokens() {
171+ t.Fatalf("metadata usage = %+v, context = %d; want %+v, %d", metadata.Usage, metadata.ContextUsed, usage, usage.PromptTokens())
172+ }
173+}
174+
96 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {175 func TestReconcileResidentJournalRetainsRootCacheID(t *testing.T) {
97 root := t.TempDir()176 root := t.TempDir()
98 journal, err := OpenResidentJournal(root, "cache-child")177 journal, err := OpenResidentJournal(root, "cache-child")
@@ -146,6 +225,112 @@ func TestResidentJournalProjectsUsageEmittedAfterInterruption(t *testing.T) {
146 }225 }
147 }226 }
148 227
228+func TestResidentProjectionSkipsIdenticalRewrite(t *testing.T) {
229+ if runtime.GOOS == "windows" {
230+ t.Skip("no-follow projection reads are unavailable on Windows")
231+ }
232+ dir := t.TempDir()
233+ metadata := ResidentMetadata{
234+ Version: residentJournalVersion,
235+ ID: "projection-child",
236+ SessionID: "projection-session",
237+ State: ResidentIdle,
238+ UpdatedAt: time.Unix(1_700_000_000, 0).UTC(),
239+ }
240+ if err := writeResidentMetadata(dir, metadata); err != nil {
241+ t.Fatal(err)
242+ }
243+ path := filepath.Join(dir, residentMetadataName)
244+ oldTime := time.Unix(1_600_000_000, 0)
245+ if err := os.Chtimes(path, oldTime, oldTime); err != nil {
246+ t.Fatal(err)
247+ }
248+
249+ if err := writeResidentMetadata(dir, metadata); err != nil {
250+ t.Fatal(err)
251+ }
252+ info, err := os.Stat(path)
253+ if err != nil {
254+ t.Fatal(err)
255+ }
256+ if !info.ModTime().Equal(oldTime) {
257+ t.Fatalf("identical projection rewrite changed mtime to %v", info.ModTime())
258+ }
259+
260+ metadata.State = ResidentStopped
261+ if err := writeResidentMetadata(dir, metadata); err != nil {
262+ t.Fatal(err)
263+ }
264+ info, err = os.Stat(path)
265+ if err != nil {
266+ t.Fatal(err)
267+ }
268+ if info.ModTime().Equal(oldTime) {
269+ t.Fatal("changed projection was not rewritten")
270+ }
271+}
272+
273+func TestResidentProjectionReplacesSymlinkInsteadOfFollowingIt(t *testing.T) {
274+ if runtime.GOOS == "windows" {
275+ t.Skip("symlink creation may require elevated privileges on Windows")
276+ }
277+ dir := t.TempDir()
278+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
279+ data, err := json.Marshal(metadata)
280+ if err != nil {
281+ t.Fatal(err)
282+ }
283+ target := filepath.Join(dir, "target.json")
284+ if err := os.WriteFile(target, data, 0o600); err != nil {
285+ t.Fatal(err)
286+ }
287+ path := filepath.Join(dir, residentMetadataName)
288+ if err := os.Symlink(target, path); err != nil {
289+ t.Fatal(err)
290+ }
291+
292+ if err := writeResidentMetadata(dir, metadata); err != nil {
293+ t.Fatal(err)
294+ }
295+ info, err := os.Lstat(path)
296+ if err != nil {
297+ t.Fatal(err)
298+ }
299+ if !info.Mode().IsRegular() {
300+ t.Fatalf("projection mode = %v, want regular file", info.Mode())
301+ }
302+}
303+
304+func TestResidentProjectionRepairsPermissionsForIdenticalData(t *testing.T) {
305+ if runtime.GOOS == "windows" {
306+ t.Skip("POSIX permission bits are not enforced on Windows")
307+ }
308+ dir := t.TempDir()
309+ metadata := ResidentMetadata{Version: residentJournalVersion, ID: "projection-child", SessionID: "projection-session", State: ResidentIdle}
310+ data, err := json.Marshal(metadata)
311+ if err != nil {
312+ t.Fatal(err)
313+ }
314+ path := filepath.Join(dir, residentMetadataName)
315+ if err := os.WriteFile(path, data, 0o644); err != nil {
316+ t.Fatal(err)
317+ }
318+ if err := os.Chmod(path, 0o644); err != nil {
319+ t.Fatal(err)
320+ }
321+
322+ if err := writeResidentMetadata(dir, metadata); err != nil {
323+ t.Fatal(err)
324+ }
325+ info, err := os.Stat(path)
326+ if err != nil {
327+ t.Fatal(err)
328+ }
329+ if got := info.Mode().Perm(); got != 0o600 {
330+ t.Fatalf("projection permissions = %o, want 600", got)
331+ }
332+}
333+
149 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {334 func TestOpenResidentJournalRejectsDotChildIDs(t *testing.T) {
150 for _, childID := range []string{".", "..", "nested/child"} {335 for _, childID := range []string{".", "..", "nested/child"} {
151 t.Run(childID, func(t *testing.T) {336 t.Run(childID, func(t *testing.T) {
A packages/agent/subagents/resident_projection_unix.go +31 -0
@@ -0,0 +1,31 @@
1+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
2+
3+package subagents
4+
5+import (
6+ "bytes"
7+ "io"
8+ "os"
9+
10+ "golang.org/x/sys/unix"
11+)
12+
13+func residentProjectionCurrent(path string, data []byte) bool {
14+ fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0)
15+ if err != nil {
16+ return false
17+ }
18+ file := os.NewFile(uintptr(fd), path)
19+ if file == nil {
20+ _ = unix.Close(fd)
21+ return false
22+ }
23+ defer file.Close()
24+
25+ info, err := file.Stat()
26+ if err != nil || !info.Mode().IsRegular() || info.Mode().Perm() != 0o600 {
27+ return false
28+ }
29+ current, err := io.ReadAll(file)
30+ return err == nil && bytes.Equal(current, data)
31+}
@@ -0,0 +1,31 @@
1+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
2+
3+package subagents
4+
5+import (
6+ "bytes"
7+ "io"
8+ "os"
9+
10+ "golang.org/x/sys/unix"
11+)
12+
13+func residentProjectionCurrent(path string, data []byte) bool {
14+ fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NOFOLLOW, 0)
15+ if err != nil {
16+ return false
17+ }
18+ file := os.NewFile(uintptr(fd), path)
19+ if file == nil {
20+ _ = unix.Close(fd)
21+ return false
22+ }
23+ defer file.Close()
24+
25+ info, err := file.Stat()
26+ if err != nil || !info.Mode().IsRegular() || info.Mode().Perm() != 0o600 {
27+ return false
28+ }
29+ current, err := io.ReadAll(file)
30+ return err == nil && bytes.Equal(current, data)
31+}
A packages/agent/subagents/resident_projection_windows.go +7 -0
@@ -0,0 +1,7 @@
1+package subagents
2+
3+// Windows does not expose a no-follow regular-file read through os. Disable the
4+// no-rewrite fast path so atomic replacement remains the only projection path.
5+func residentProjectionCurrent(string, []byte) bool {
6+ return false
7+}
@@ -0,0 +1,7 @@
1+package subagents
2+
3+// Windows does not expose a no-follow regular-file read through os. Disable the
4+// no-rewrite fast path so atomic replacement remains the only projection path.
5+func residentProjectionCurrent(string, []byte) bool {
6+ return false
7+}
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