05c450147c
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>
25 lines
1.5 KiB
Go
25 lines
1.5 KiB
Go
// 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
|