@@ -7,6 +7,7 @@ import (
77 "fmt"
88 "log/slog"
99 "strings"
10+ "sync"
1011
1112 "github.com/charmbracelet/crush/internal/db"
1213 "github.com/charmbracelet/crush/internal/event"
@@ -84,6 +85,12 @@ type service struct {
8485 *pubsub.Broker[Session]
8586 db *sql.DB
8687 q *db.Queries
88+
89+ // Estimated usage stays in memory so fetch-modify-save paths (e.g.,
90+ // updating todos or parent-session cost) do not rebuild a session from
91+ // SQLite and incorrectly clear the UI "~" marker.
92+ estimatedUsageMu sync.RWMutex
93+ estimatedUsage map[string]bool
8794}
8895
8996func (s *service) Create(ctx context.Context, title string) (Session, error) {
@@ -155,6 +162,7 @@ func (s *service) Delete(ctx context.Context, id string) error {
155162 }
156163
157164 session := s.fromDBItem(dbSession)
165+ s.clearEstimatedUsageState(dbSession.ID)
158166 s.Publish(pubsub.DeletedEvent, session)
159167 event.SessionDeleted()
160168 return nil
@@ -165,7 +173,9 @@ func (s *service) Get(ctx context.Context, id string) (Session, error) {
165173 if err != nil {
166174 return Session{}, err
167175 }
168− return s.fromDBItem(dbSession), nil
176+ session := s.fromDBItem(dbSession)
177+ s.applyEstimatedUsageState(&session)
178+ return session, nil
169179}
170180
171181func (s *service) GetLast(ctx context.Context) (Session, error) {
@@ -173,7 +183,9 @@ func (s *service) GetLast(ctx context.Context) (Session, error) {
173183 if err != nil {
174184 return Session{}, err
175185 }
176− return s.fromDBItem(dbSession), nil
186+ session := s.fromDBItem(dbSession)
187+ s.applyEstimatedUsageState(&session)
188+ return session, nil
177189}
178190
179191func (s *service) Save(ctx context.Context, session Session) (Session, error) {
@@ -201,6 +213,7 @@ func (s *service) Save(ctx context.Context, session Session) (Session, error) {
201213 return Session{}, err
202214 }
203215 estimatedUsage := session.EstimatedUsage
216+ s.setEstimatedUsageState(session.ID, estimatedUsage)
204217 session = s.fromDBItem(dbSession)
205218 session.EstimatedUsage = estimatedUsage
206219 s.Publish(pubsub.UpdatedEvent, session)
@@ -236,11 +249,34 @@ func (s *service) List(ctx context.Context) ([]Session, error) {
236249 sessions := make([]Session, len(dbSessions))
237250 for i, dbSession := range dbSessions {
238251 sessions[i] = s.fromDBItem(dbSession)
252+ s.applyEstimatedUsageState(&sessions[i])
239253 }
240254 return sessions, nil
241255}
242256
243−func (s service) fromDBItem(item db.Session) Session {
257+func (s *service) applyEstimatedUsageState(session *Session) {
258+ s.estimatedUsageMu.RLock()
259+ session.EstimatedUsage = s.estimatedUsage[session.ID]
260+ s.estimatedUsageMu.RUnlock()
261+}
262+
263+func (s *service) setEstimatedUsageState(sessionID string, estimatedUsage bool) {
264+ s.estimatedUsageMu.Lock()
265+ defer s.estimatedUsageMu.Unlock()
266+ if estimatedUsage {
267+ s.estimatedUsage[sessionID] = true
268+ return
269+ }
270+ delete(s.estimatedUsage, sessionID)
271+}
272+
273+func (s *service) clearEstimatedUsageState(sessionID string) {
274+ s.estimatedUsageMu.Lock()
275+ delete(s.estimatedUsage, sessionID)
276+ s.estimatedUsageMu.Unlock()
277+}
278+
279+func (s *service) fromDBItem(item db.Session) Session {
244280 todos, err := unmarshalTodos(item.Todos.String)
245281 if err != nil {
246282 slog.Error("Failed to unmarshal todos", "session_id", item.ID, "error", err)
@@ -285,9 +321,10 @@ func unmarshalTodos(data string) ([]Todo, error) {
285321func NewService(q *db.Queries, conn *sql.DB) Service {
286322 broker := pubsub.NewBroker[Session]()
287323 return &service{
288− Broker: broker,
289− db: conn,
290− q: q,
324+ Broker: broker,
325+ db: conn,
326+ q: q,
327+ estimatedUsage: make(map[string]bool),
291328 }
292329}
293330