package hostbox import ( "context" "errors" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/registry" "sync" "time" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostwrite" ) var ErrChanged = errors.New("hostbox: selected authority changed") var ErrUnsupported = errors.New("hostbox: unresolved lifecycle contract") type Selection struct { HomeRegion, FleetHomeRegion string Host, Action, Box, Region string Captured hostguard.Captured Kind, Op, Phase, Generation string Session uint64 // installation marks the work Reconciler.installWithWork selected for // one installation (Installation). Only this package sets it. installation bool } // Installation reports whether the selection is the work an installation // selected for itself (Reconciler.installWithWork, the path to the remote // route registration and the local row write), not ordinary work on an // installed host. A policy may admit what only an installation can need, // such as a registry row the installation has not written yet (harness G10, // 2026-10-21); no caller outside this package can set it. func (s Selection) Installation() bool { return s.installation } // installationWork marks w, a selection SelectAt made, as installation // work. Any other work is returned unmarked, so it is refused whatever an // installation-only admission would allow. func installationWork(w hostwrite.Work) hostwrite.Work { selected, ok := w.(selectedWork) if !ok { return w } selected.selection.installation = true return selected } type WorkSource struct { Timer func(context.Context, hostwrite.Work, registry.Timer, string, bool) (*registry.Timer, error) FleetHomeRegion string HomeRegion string Prepare func(context.Context, hostclaim.Observed) error Guard hostguard.Guard Box, Region string Now func() time.Time // Policy checks the action's role/phase and the selected config/release. Policy func(context.Context, *hostclaim.Claim, Selection, hostwrite.Identity) error // Local checks quarantine, execution deadlines and failed cleanup under flock. Local func(context.Context, *hostclaim.Claim, Selection) error // Continue reports whether a selection last checked at one op phase may // keep working at another (hostclaim.SessionObserves). Nil keeps main's rule: // any phase change ends the selection. Only the moves C1 integration sets // it, and only with CARLOS_HOST_CLAIM and CARLOS_MOVES_PHASE1 both on. Continue func(kind, from, to string) bool } type selectedWork struct { source WorkSource selection Selection // checked is the op phase of the last claim a With check accepted. The // selection's own Phase stays the selected one for Policy and Local. checked *checkedPhase } type checkedPhase struct { mu sync.Mutex phase string } func (s WorkSource) Select(ctx context.Context, host, action string) (hostwrite.Work, error) { if ctx == nil || s.Box == "" || s.Region == "" || action == "" || s.Guard.Claims == nil || s.Policy == nil || s.Local == nil || s.Now == nil { return nil, ErrUnsupported } captured, err := s.Guard.Capture(host) if err != nil { return nil, err } obs, err := s.Guard.Claims.Read(ctx, host) if err != nil { return nil, err } if obs.Absent || obs.Host != host || obs.Claim == nil || obs.Claim.Holder == nil || obs.Claim.Host != host { return nil, ErrChanged } if s.Prepare != nil { if err := s.Prepare(ctx, obs); err != nil { return nil, err } } c := obs.Claim if c.Holder.Incarnation != captured.Incarnation || c.Holder.Stamp != captured.Stamp || captured.Stamp == 0 { return nil, ErrChanged } w := selectedWork{source: s, selection: Selection{HomeRegion: s.HomeRegion, FleetHomeRegion: s.FleetHomeRegion, Host: host, Action: action, Box: s.Box, Region: s.Region, Captured: captured, Kind: c.Op.Kind, Op: c.Op.ID, Phase: c.Op.Phase, Generation: c.Holder.Generation, Session: c.Holder.Session}, checked: &checkedPhase{phase: c.Op.Phase}} return w, nil } func (w selectedWork) With(ctx context.Context, identity hostwrite.Identity, fn func() error) error { s, selected := w.source, w.selection return s.Guard.With(ctx, selected.Host, selected.Captured, func(c *hostclaim.Claim) error { if c.Op.Kind != selected.Kind || c.Op.ID != selected.Op || !s.continues(selected.Kind, w.checked.get(), c.Op.Phase) || c.Holder.Stamp != selected.Captured.Stamp || c.Holder.Session != selected.Session || c.Holder.Generation != selected.Generation { return ErrChanged } switch selected.Action { case "install", "restore", "spawn", "seed", "writer", "probe": if until := park.QuietUntil(c.QuietAfter, s.HomeRegion, s.Region); !until.IsZero() && !s.Now().After(until) { return ErrChanged } } if err := s.Local(ctx, c, selected); err != nil { return err } if err := s.Policy(ctx, c, selected, identity); err != nil { return err } return w.checked.advance(func(from string) bool { return s.continues(selected.Kind, from, c.Op.Phase) }, c.Op.Phase) }, fn) } func (p *checkedPhase) get() string { p.mu.Lock() defer p.mu.Unlock() return p.phase } // advance records phase as checked if it still continues from the last // checked phase. A concurrent check that already moved on refuses this one. func (p *checkedPhase) advance(continues func(from string) bool, phase string) error { p.mu.Lock() defer p.mu.Unlock() if !continues(p.phase) { return ErrChanged } p.phase = phase return nil } // continues binds the selection to op id, kind, session, stamp and generation. // Its phase may only take a forward step the configured table allows from the // last phase a check accepted, so a skipped or backward step ends it. func (s WorkSource) continues(kind, from, to string) bool { if s.Continue == nil { return from == to } return s.Continue(kind, from, to) } // Serving permits ordinary host writes only. Operations need their own policy. func Serving(c *hostclaim.Claim, selected Selection) error { if c.Holder == nil || c.Op.Kind != hostclaim.KindServing || c.Op.ID != "" || c.Op.Phase != "" || c.Op.Exec != selected.Box { return ErrChanged } for _, box := range c.Lost { if box == selected.Box { return ErrChanged } } return nil } // SelectAt captures an already chosen installation without relabelling it. func (s WorkSource) SelectAt(ctx context.Context, host, action string, captured hostguard.Captured) (hostwrite.Work, error) { work, err := s.Select(ctx, host, action) if err != nil { return nil, err } selected := work.(selectedWork) if selected.selection.Captured != captured { return nil, ErrChanged } return work, nil }