feat(infra): tracing recovery, browser cleanup, CLI fixes, UI workspace split (#709)

- Tracing: recover stale running traces/spans on startup (PG + SQLite)
- Browser: Chrome orphan cleanup via launcher PID, timeouts, Leakless
- Claude CLI: WaitDelay 5s + context-cancel early exit
- Agent loop: safety-net defer to finalize orphan root traces
- UI: split workspace sharing into separate Memory and KG toggles
- Minor: for-range idiom, min() builtin
This commit is contained in:
viettranx committed 2026-04-05 21:32:59 +07:00
1 parent 7d7b716074
commit 41e6c8f5cc
15 files changed
+208 -45

No files matched your search

+21
View File
@@ -101,6 +101,7 @@ func (c *Collector) SetExporter(exp SpanExporter) {
func (c *Collector) Start() {
c.wg.Add(1)
go c.flushLoop()
go c.recoverStaleTraces() // fix orphan traces from previous crash
slog.Info("tracing collector started")
}
@@ -224,6 +225,26 @@ func (c *Collector) flushLoop() {
}
}
// recoverStaleTraces marks "running" traces older than 30 min as "error".
// Called once on startup to fix orphans left by a previous crash or stuck goroutine.
func (c *Collector) recoverStaleTraces() {
const staleThreshold = 30 * time.Minute
cutoff := time.Now().UTC().Add(-staleThreshold)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
recovered, err := c.store.RecoverStaleRunningTraces(ctx, cutoff)
if err != nil {
slog.Warn("tracing: failed to recover stale running traces", "error", err)
return
}
if recovered > 0 {
slog.Info("tracing: recovered stale running traces on startup",
"count", recovered, "older_than", cutoff.Format(time.RFC3339))
}
}
// pruneOldTraces deletes traces and spans older than traceRetention.
func (c *Collector) pruneOldTraces() {
cutoff := time.Now().UTC().Add(-traceRetention)