feat: planner quiet-mode skips edge rounds while work runs and goals hold

Every worker write-back fired a full planning round even when the open-goal set had not changed; on a live three-worker task that was 7 rounds in 45 minutes. Edge-triggered rounds are now dropped while active intents exist and the open-goal fingerprint recorded at the end of the last round is unchanged; heartbeat rounds, goal-set changes and frontier drains still plan immediately. Fails open on store errors.
This commit is contained in:
BaaRai
2026-10-12 18:36:44 +02:00
parent ff2e0bf338
commit f57cf659b5
3 changed files with 135 additions and 0 deletions
+22
View File
@@ -1192,6 +1192,28 @@ WHERE exploration_id=$1 AND kind='goal' AND state='open')`, s.expID).Scan(&exist
return exists, err
}
// OpenGoalFingerprint returns a stable fingerprint of the open-goal set: the
// sorted goal ids joined by commas, "" when none. Planner quiet-mode (#79) uses
// it to tell "the goal set changed since the last planning round" apart from
// "nothing the plan depends on changed".
func (s *ExplorationStore) OpenGoalFingerprint() (string, error) {
rows, err := s.db.Query(`SELECT id FROM exploration_nodes
WHERE exploration_id=$1 AND kind='goal' AND state='open' ORDER BY id`, s.expID)
if err != nil {
return "", err
}
defer rows.Close()
var ids []string
for rows.Next() {
var id int64
if err := rows.Scan(&id); err != nil {
return "", err
}
ids = append(ids, strconv.FormatInt(id, 10))
}
return strings.Join(ids, ","), rows.Err()
}
// ClaimIntent atomically moves an open intent to running. Returns true if claimed.
func (s *ExplorationStore) ClaimIntent(id int64, owner string) (bool, error) {
res, err := s.db.Exec(`UPDATE exploration_nodes SET state='running', owner=$1
+37
View File
@@ -99,6 +99,10 @@ type Engine struct {
started sync.Map // taskID -> bool, so Run is idempotent per task
lastAct sync.Map // taskID -> int64 unix, last planner/worker activity (heartbeat)
llmCalls sync.Map // taskID -> *int64, actual planner/worker/main-agent LLM calls
// quiet-mode (#79): open-goal fingerprint recorded at the end of the last
// completed planning round, per task. Edge-triggered rounds are skipped
// while it is unchanged and work is still in flight.
goalFP sync.Map // taskID -> string (sorted open-goal ids joined)
paused sync.Map // taskID -> bool, user-paused (planner + workers idle but loops alive)
deleting sync.Map // taskID -> bool, delete barrier (no new task-owned writes)
dropCnt sync.Map // taskID -> *int64, running count of dropped (unpersistable) activity records
@@ -797,6 +801,22 @@ func resetPlannerTimer(timer *time.Timer, d time.Duration) {
timer.Reset(d)
}
// quietSkipEdge reports whether an edge-triggered planning round should be
// dropped (#79): the open-goal set is unchanged since the last completed round
// AND active work exists (open or running intents). Fails open (false) on any
// store error: when in doubt, plan, preserving the pre-quiet-mode behavior.
func (e *Engine) quietSkipEdge(t *Task) bool {
fp, err := t.Store.OpenGoalFingerprint()
if err != nil {
return false
}
if last, ok := e.goalFP.Load(t.ID); !ok || last != fp {
return false
}
active, err := t.Store.HasActiveIntent()
return err == nil && active
}
func (e *Engine) plannerLoop(ctx context.Context, t *Task) {
interval := plannerHeartbeatInterval(t)
// Arming the heartbeat timer at loop entry = counting from the task start: even a
@@ -856,6 +876,17 @@ func (e *Engine) plannerLoop(ctx context.Context, t *Task) {
}
return // the goalless branch never enters planner.Plan
}
// quiet-mode (#79): an edge round is redundant while work is in flight
// and the open-goal set is unchanged since the last completed round —
// there is no new decision for the plan to make. Drain the triggers so
// they do not accumulate and let the heartbeat supervise; the next edge
// after a goal opens or closes, or once the frontier drains and all
// workers are idle, plans for real.
if src == "edge" && e.quietSkipEdge(t) {
t.drainTriggers()
log.Printf("[planner] task %s quiet: work in flight, goals unchanged, edge round skipped", t.ID)
return
}
if !e.beginTaskOperation(t.ID) {
return
}
@@ -895,6 +926,12 @@ func (e *Engine) plannerLoop(ctx context.Context, t *Task) {
default:
log.Printf("[planner] task %s planning finished", t.ID)
}
// Record the post-round open-goal fingerprint for quiet-mode (#79):
// goals opened or closed by this round change it, so a later edge can
// tell "nothing changed" apart from "the plan input changed".
if fp, err := t.Store.OpenGoalFingerprint(); err == nil {
e.goalFP.Store(t.ID, fp)
}
e.touch(t.ID)
}
+76
View File
@@ -0,0 +1,76 @@
package server
import (
"testing"
"github.com/Autumn-27/artex/db"
)
// Planner quiet-mode (#79): an edge round is dropped only when BOTH hold — the
// open-goal fingerprint is unchanged since the last completed round AND active
// work exists (open or running intents). Every other combination plans.
func TestPlannerQuietSkipEdge(t *testing.T) {
m, err := NewManager(t.TempDir(), "")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = m.Close() })
if m.pg == nil {
t.Skip("no postgres for this run")
}
created, err := m.CreateTaskWithOptions("quiet-mode probe", "probe goal", db.TaskCreateOptions{})
if err != nil {
t.Fatal(err)
}
tt, ok := m.Task(created.ID)
if !ok {
t.Fatal("task not found after creation")
}
store := tt.Store
e := NewEngine(m)
gid, err := store.AddGoal(map[string]any{"summary": "g1"}, "seed")
if err != nil {
t.Fatal(err)
}
iid, err := store.AddIntent(map[string]any{"summary": "i1"}, 0, nil, "seed")
if err != nil {
t.Fatal(err)
}
fp, err := store.OpenGoalFingerprint()
if err != nil {
t.Fatal(err)
}
if fp == "" {
t.Fatal("fingerprint must not be empty with an open goal")
}
e.goalFP.Store(tt.ID, fp)
if !e.quietSkipEdge(tt) {
t.Fatal("unchanged goals + active intent must skip the edge round")
}
// a goal closes -> the fingerprint changes -> plan
if err := store.SetNodeState(gid, "met"); err != nil {
t.Fatal(err)
}
if e.quietSkipEdge(tt) {
t.Fatal("a closed goal must allow planning")
}
// goal back open, intent drained -> no active work -> plan
if err := store.SetNodeState(gid, "open"); err != nil {
t.Fatal(err)
}
fp2, err := store.OpenGoalFingerprint()
if err != nil {
t.Fatal(err)
}
e.goalFP.Store(tt.ID, fp2)
if err := store.SetIntentState(iid, "done"); err != nil {
t.Fatal(err)
}
if e.quietSkipEdge(tt) {
t.Fatal("no active intent must allow planning")
}
}