P4c: remaining batteries — checkpoint + schedule + critic
executus CI / test (push) Failing after 1m6s
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>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user