v0.4.0-rc1: slice 4 Phase A — reconcile engine (structural, runs live unfed)
New internal/reconcile package: the agent-side control core's structural half. - Per-guest serializer Queue (doc 03 §10): the single choke point all mutation sources funnel through; same-vmid serial in submit order, different vmids parallel (cond-var FIFO lanes). - Desired-state model + DesiredProvider seam; EmptyProvider is the only live source at slice 4 (no hub serving until slice 10) so the live engine computes an empty action set and performs zero mutations. - Normalization layer (FieldNormalizers): normalized desired-vs-actual so Proxmox round-trip quirks don't read as drift. normDesc promoted out of main.go to reconcile.NormDescription; selftest uses the shared helper. - Plan (pure diff): minimal benign action set (Start/Stop/SetConfig) for guests in both desired and actual; provision/destroy out of scope here. - Engine: dispatches onto the shared queue; honors the dual-mode SetConfig contract (UPID -> WaitTask; empty UPID -> synchronous success). - Durable op journal + idempotency store (mirrors authz.FileNonceStore): in-flight task ids for crash detection + AlreadyApplied dedupe across restart. - Wired into runDaemon alongside the hub loop, sharing the queue; runs cleanly with no desired state and no signers. Full module race-clean and vet-clean on the Linux build server. CHECKPOINT: Phase A only. Awaiting validation before Phase B (the reversibility gate + signed-op consuming layer, landing v0.4.0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
// Package reconcile is the agent-side control core (slice 4): the engine that
|
||||
// converges actual Proxmox state toward a desired state, the per-guest serializer
|
||||
// that all mutation sources funnel through (doc 03 §10), the field-normalization
|
||||
// layer that keeps Proxmox round-trip quirks from reading as drift, and the durable
|
||||
// operation journal + idempotency store.
|
||||
//
|
||||
// Phase A (this file set) is structural and runs LIVE but UNFED: at slice 4 there is
|
||||
// no desired-state provider (hub serving is slice 10, provisioning is slice 7), so
|
||||
// the live engine computes an empty action set and performs ZERO mutations. The
|
||||
// engine, serializer, normalizer, journal, and planner are all exercised against
|
||||
// synthetic fixtures. The first live convergence arrives when slice 10 serves desired
|
||||
// state into the DesiredProvider seam.
|
||||
//
|
||||
// Phase B (added later) layers the benign/destructive classifier and the
|
||||
// reversibility gate (doc 03 §4) plus the signed-op consuming layer over
|
||||
// internal/authz (doc 04) in front of the queue's executor — so every mutation passes
|
||||
// the gate. Phase A deliberately wires only the benign-on-existing-guest action set:
|
||||
// Start / Stop / SetConfig.
|
||||
//
|
||||
// Action set scope (slice 4): Start, Stop, SetConfig on EXISTING guests only.
|
||||
// Provisioning (restore-to-new-guest) and destroy/overwrite are out of scope here —
|
||||
// the destructive set is classified and gated in Phase B but not wired to live
|
||||
// execution, because nothing serves destructive deltas until slice 10.
|
||||
package reconcile
|
||||
Reference in New Issue
Block a user