v0.90.0 — guest RAM resize (R-24) + fast-tick-until-convergence (R-28)
MinAgent coupling: felhom-controller v0.143.0 gates its guest-memory-resize UI on this agent (FeatureGuestMemoryResize, MinAgent 0.90.0). R-24 guest RAM resize (internal/localapi/guestmemory.go): self-scoped GET/POST /guest/memory. Agent enforces every bound FRESH per request (min 2048, max host_total-2048, shrink floor max(2048, usage+512)); applies via PVE SetConfig — live cgroup apply, no reboot (Phase-0 proven on the nested demo box). Verify-after-apply re-reads maxmem before claiming success. New narrow MemoryOps seam (GuestAPI untouched); Options.Memory nil -> 503. Memory only. R-28 fast-tick (internal/fasttick): while any desired-state item is unapplied - including the pre-tunnel window a hub poke can't reach - pulse the shared out-of-band trigger every 30s, self-disarm on convergence. Four cached sources (desired-gen==0, reconcile Planned-Pending>0, pbsdr waiting_secret only, wgtunnel desired-not-operational); LOUD pbsdr states + pending_signature excluded. Seams: reconcile.Engine.LastResult() + wgtunnel.Manager.TunnelConvergence() (cached, no per-tick exec). Guests-0/0: hypothesis REFUTED live (9201 IS a pool member; 0/0 was the pre-provision window; PoolAddVMID re-assert already covers restore-over-existing). No code change; the fast-tick mitigates the window. Tests + red-proofs (i floor guard, ii max guard, iii always-pulse) all restored green.
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -42,6 +43,31 @@ type Engine struct {
|
||||
stateDir string
|
||||
|
||||
opSeq uint64 // atomic; makes each op id unique per attempt
|
||||
|
||||
// lastRes records the most recent successful Reconcile Result (v0.90.0, R-28 fast-tick source).
|
||||
// The fast-tick reads it to decide convergence: actionable drift is Planned − Pending > 0 (a
|
||||
// destructive pending_signature refusal is EXPECTED state, not drift to hammer on). lastOK is
|
||||
// false until the first successful pass.
|
||||
lastMu sync.Mutex
|
||||
lastRes Result
|
||||
lastOK bool
|
||||
}
|
||||
|
||||
// LastResult returns the most recent successful Reconcile Result and whether one has been recorded
|
||||
// (false until the first successful pass). Read by the fast-tick convergence source; safe for
|
||||
// concurrent use.
|
||||
func (e *Engine) LastResult() (Result, bool) {
|
||||
e.lastMu.Lock()
|
||||
defer e.lastMu.Unlock()
|
||||
return e.lastRes, e.lastOK
|
||||
}
|
||||
|
||||
// recordResult stores the latest successful pass Result (called from reconcileOnce).
|
||||
func (e *Engine) recordResult(res Result) {
|
||||
e.lastMu.Lock()
|
||||
e.lastRes = res
|
||||
e.lastOK = true
|
||||
e.lastMu.Unlock()
|
||||
}
|
||||
|
||||
// EngineOptions configures a new Engine. Norm defaults to DefaultNormalizers, Logger
|
||||
@@ -293,6 +319,7 @@ func (e *Engine) reconcileOnce(ctx context.Context) {
|
||||
e.logger.Error("reconcile: pass failed", "err", err)
|
||||
return
|
||||
}
|
||||
e.recordResult(res) // v0.90.0: fast-tick convergence source
|
||||
if res.Planned > 0 {
|
||||
e.logger.Info("reconcile: pass complete",
|
||||
"planned", res.Planned, "executed", res.Executed, "failed", res.Failed, "pending", res.Pending)
|
||||
|
||||
Reference in New Issue
Block a user