dc2d4ec425
executus CI / test (push) Failing after 1m6s
Completes the P4 battery set (squashed onto main from phase-4c-batteries). - checkpoint/: run.Checkpointer durable-resume (CheckpointStore + throttled handle + Memory). - schedule/: generic cron Runner (Tick/Loop; no cron grammar of its own). - critic/: two-tier timeout watchdog (run.Critic) + Escalator policy seam + ExtendOnce default. Includes the verified gadfly #6 fixes (ExtendOnce per-run, Kill-sticky, watch panic-recovery; checkpoint throttle-after-success; schedule Next-before-Run + nil-guard + Loop recovery). P4 battery set complete: audit, budget, persona, skill, checkpoint, schedule, critic — each nil-safe, each with a default, each core-import-clean. Executor wiring for Critic/Checkpointer remains a P2 follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
1.4 KiB
Go
56 lines
1.4 KiB
Go
package checkpoint
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
)
|
|
|
|
// Memory is a zero-dependency in-process CheckpointStore. NOTE: an in-memory
|
|
// checkpoint store does NOT survive the process restart it exists to recover
|
|
// from — it is the test/light-host default and makes ListInterrupted meaningful
|
|
// only within a single process lifetime. A host that wants real
|
|
// crash-recovery wires a durable CheckpointStore (mort's durable-job table).
|
|
type Memory struct {
|
|
mu sync.RWMutex
|
|
cps map[string]RunCheckpoint // by run id
|
|
}
|
|
|
|
// NewMemory returns an empty in-memory CheckpointStore.
|
|
func NewMemory() *Memory { return &Memory{cps: map[string]RunCheckpoint{}} }
|
|
|
|
var _ CheckpointStore = (*Memory)(nil)
|
|
|
|
func (m *Memory) Save(_ context.Context, cp RunCheckpoint) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.cps[cp.Meta.RunID] = cp
|
|
return nil
|
|
}
|
|
|
|
func (m *Memory) Load(_ context.Context, runID string) (*RunCheckpoint, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
cp, ok := m.cps[runID]
|
|
if !ok {
|
|
return nil, nil // no checkpoint (not an error — the run finished cleanly or never started)
|
|
}
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *Memory) Delete(_ context.Context, runID string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
delete(m.cps, runID)
|
|
return nil
|
|
}
|
|
|
|
func (m *Memory) ListInterrupted(_ context.Context) ([]RunCheckpoint, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
out := make([]RunCheckpoint, 0, len(m.cps))
|
|
for _, cp := range m.cps {
|
|
out = append(out, cp)
|
|
}
|
|
return out, nil
|
|
}
|