Documentation
¶
Overview ¶
Package apply applies validated MIDAS control-plane documents.
The apply flow proceeds in two distinct phases:
Planning — validate the bundle, inspect persisted state, and resolve cross-document referential integrity to construct an ApplyPlan describing the intended action (create, unchanged, conflict, or invalid) for each document. No persistence occurs during planning.
Execution — walk the ApplyPlan in dependency order and persist each resource according to its planned action. If any entry is invalid, the entire bundle is rejected without persisting anything.
Guarantees ¶
- Validation happens before any resources are applied.
- Invalid bundles produce validation errors only; no resources are persisted.
- Conflict entries are not persisted; they surface in the ApplyResult.
- Valid, non-conflicting bundles produce created results.
- Mixed-validity bundles are rejected as a whole.
- Cross-document references are verified before execution begins.
- Execution respects dependency order: Surface → Agent → Profile → Grant.
Resource planning semantics ¶
Surface: apply always creates a new governed version entering the governance pipeline at review status. Unchanged detection is not supported: the versioning model intends a new version record on every valid submission. A conflict is detected when the latest persisted version is already in review status, because applying again before the pending review is resolved would create an ambiguous governance state.
Agent: agents are identified by ID and are immutable once created in the apply path. A conflict is detected when an agent with the same ID already exists. Unchanged is not supported: the document schema does not carry enough field parity with the domain model to prove equality without a full field comparison.
Profile: profiles are identified by ID and are immutable once created in the apply path. A conflict is detected when a profile with the same ID already exists. Unchanged is not supported for the same reason as Agent. The profile's spec.surface_id must resolve to a surface that either exists in persisted state or is being created in the same bundle.
Grant: grants are identified by ID and are immutable once created in the apply path. A conflict is detected when a grant with the same ID already exists. Unchanged is not supported for the same reason as Agent. The grant's spec.agent_id and spec.profile_id must each resolve to resources that either exist in persisted state or are being created in the same bundle.
Bundle-level referential integrity ¶
After resource-local planning, the planner verifies that all cross-document references are satisfiable. A reference is satisfied when the referenced ID exists in persisted state (confirmed via repository) or when a non-invalid, non-conflict create entry for that (kind, id) pair is present in the same bundle. Entries whose references cannot be resolved are marked invalid, causing the entire bundle to be rejected.
Typed errors ¶
Domain conditions are signalled via typed sentinel errors (ErrInvalidBundle, ErrValidationFailed, ErrDuplicateResource, ErrResourceConflict, ErrReferentialIntegrity, etc.). Callers should use errors.Is to test for specific conditions. The original cause is always preserved in the error chain via fmt.Errorf wrapping.
Index ¶
- Variables
- func PlanResultFromPlan(plan ApplyPlan) types.PlanResult
- type AgentRepository
- type ApplyAction
- type ApplyPlan
- type ApplyPlanEntry
- type DecisionSource
- type GrantRepository
- type ProfileRepository
- type RepositorySet
- type Service
- func (s *Service) Apply(ctx context.Context, docs []parser.ParsedDocument, actor string) types.ApplyResult
- func (s *Service) ApplyBundle(ctx context.Context, yamlBytes []byte, actor string) (*types.ApplyResult, error)
- func (s *Service) Plan(ctx context.Context, docs []parser.ParsedDocument) ApplyPlan
- func (s *Service) PlanBundle(ctx context.Context, yamlBytes []byte) (*ApplyPlan, error)
- type SurfaceRepository
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidBundle is returned when a bundle cannot be parsed or is // structurally malformed before validation begins. ErrInvalidBundle = errors.New("invalid bundle") // ErrValidationFailed is returned when one or more documents in the bundle // fail semantic validation. Validation errors are also available via the // ApplyResult; this sentinel signals that apply was halted by validation. ErrValidationFailed = errors.New("validation failed") // ErrDuplicateResource is returned when a bundle contains more than one // document with the same kind and ID. ErrDuplicateResource = errors.New("duplicate resource") // ErrResourceConflict is returned when a resource cannot be applied because // a conflicting version already exists in the store. ErrResourceConflict = errors.New("resource conflict") // ErrReferentialIntegrity is returned when a document references a resource // that does not exist (e.g. a Grant pointing to a missing Profile). ErrReferentialIntegrity = errors.New("referential integrity violation") // ErrUnsupportedUpdate is returned when an apply operation attempts a // change that is not permitted on the target resource type. ErrUnsupportedUpdate = errors.New("unsupported update") // ErrVersionConflict is returned when the submitted document version does // not match the stored version. ErrVersionConflict = errors.New("version conflict") // ErrNotFound is returned by repository lookups that produced no result. ErrNotFound = errors.New("not found") )
Typed sentinel errors for control-plane apply operations.
Callers should use errors.Is to test for specific conditions. All errors preserve the original cause via fmt.Errorf wrapping so the error chain remains intact.
Functions ¶
func PlanResultFromPlan ¶
func PlanResultFromPlan(plan ApplyPlan) types.PlanResult
PlanResultFromPlan converts an ApplyPlan into a types.PlanResult suitable for serialisation in a dry-run HTTP response. The two types are kept separate so that the apply package does not export the HTTP wire format and the types package does not depend on the apply package.
Types ¶
type AgentRepository ¶
type ApplyAction ¶
type ApplyAction string
ApplyAction describes the intended outcome for a single resource in a plan.
const ( // ApplyActionCreate indicates the resource does not yet exist in persisted // state in its target form and will be created by the executor. ApplyActionCreate ApplyAction = "create" // ApplyActionUnchanged indicates the resource is already represented in // persisted state such that applying it would produce no effective change. // The executor skips persistence for unchanged entries. // // Surface documents are never planned as unchanged: the governance model // creates a new versioned record on every valid submission. Unchanged is // available for other resource types when repository-backed inspection // supports it. ApplyActionUnchanged ApplyAction = "unchanged" // ApplyActionConflict indicates the resource collides with persisted state // in a way that apply cannot silently resolve. The executor records a // conflict result without persisting the resource. // // For surfaces, a conflict is raised when the latest persisted version is // already in review status, because applying again before the pending // governance review is resolved would create an ambiguous state. ApplyActionConflict ApplyAction = "conflict" // ApplyActionInvalid indicates the resource failed validation. Invalid // entries cause the entire bundle to be rejected; no resources are persisted. ApplyActionInvalid ApplyAction = "invalid" )
type ApplyPlan ¶
type ApplyPlan struct {
Entries []ApplyPlanEntry
}
ApplyPlan holds the full set of planned actions for a bundle before execution.
func (ApplyPlan) HasConflict ¶
HasConflict returns true if any entry has a conflict action.
func (ApplyPlan) HasInvalid ¶
HasInvalid returns true if any entry has an invalid action.
type ApplyPlanEntry ¶
type ApplyPlanEntry struct {
// Kind is the document kind (Surface, Agent, Profile, Grant).
Kind string
// ID is the document metadata.id.
ID string
// Action is the intended operation.
Action ApplyAction
// DocumentIndex is the 1-based position in the original bundle.
DocumentIndex int
// ValidationErrors holds structured validation errors for this entry.
// Only populated when Action is ApplyActionInvalid.
ValidationErrors []types.ValidationError
// Message provides additional human-readable context, populated for
// conflict and invalid entries, and for profile version-create entries.
Message string
// DecisionSource records how the planner arrived at the action. This
// field is informational and intended for dry-run callers that need to
// understand the rationale for each planned action.
DecisionSource DecisionSource
// NewVersion is the version number that the executor will assign when
// persisting this entry. It is only meaningful for Profile entries with
// Action == ApplyActionCreate; it is zero for all other resource kinds.
//
// Version 1 means the profile is being created for the first time.
// Version > 1 means a new version is being appended to an existing
// profile lineage.
NewVersion int
// Doc is the underlying parsed document, available for the executor phase.
Doc parser.ParsedDocument
}
ApplyPlanEntry describes the planned action for a single document.
type DecisionSource ¶
type DecisionSource string
DecisionSource records how the planner resolved the action for an entry. It tells callers whether the decision came from a persisted-state lookup, same-bundle dependency resolution, or a local validation failure.
const ( // DecisionSourcePersistedState indicates the action was determined by // inspecting persisted state via a repository lookup. DecisionSourcePersistedState DecisionSource = "persisted_state" // DecisionSourceBundleDependency indicates the action was determined by // resolving a reference against another entry in the same bundle rather // than persisted state. DecisionSourceBundleDependency DecisionSource = "bundle_dependency" // DecisionSourceValidation indicates the action was determined by // structural or referential-integrity validation failure without any // repository lookup being relevant to the outcome. DecisionSourceValidation DecisionSource = "validation" )
type GrantRepository ¶
type ProfileRepository ¶
type RepositorySet ¶
type RepositorySet struct {
Surfaces SurfaceRepository
Agents AgentRepository
Profiles ProfileRepository
Grants GrantRepository
ControlAudit controlaudit.Repository
}
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service coordinates control-plane apply operations.
func NewService ¶
func NewService() *Service
NewService constructs a new apply service with no repositories configured. In this mode, apply performs validation and records all valid resources as created without persisting to any backing store.
func NewServiceWithRepo ¶
func NewServiceWithRepo(surfaceRepo surface.SurfaceRepository) *Service
NewServiceWithRepo constructs a new apply service with a surface repository. Agent, Profile, and Grant documents are recorded as created without persisting.
func NewServiceWithRepos ¶
func NewServiceWithRepos(repos RepositorySet) *Service
NewServiceWithRepos constructs an apply service with the full repository set. Each repository enables repository-backed planning and execution for its resource kind. Nil repository fields fall back to validation-only behaviour for that kind. If ControlAudit is nil, audit events are silently skipped.
func (*Service) Apply ¶
func (s *Service) Apply(ctx context.Context, docs []parser.ParsedDocument, actor string) types.ApplyResult
Apply validates a parsed bundle and applies it.
Apply builds the plan via Plan and then executes it. No planning logic is duplicated here.
Behavior:
- if validation fails, return validation errors only; no resources are persisted
- if a surface repository is configured, the planner inspects persisted state to determine whether each Surface document should be created or is a conflict
- if agent, profile, or grant repositories are configured, the planner inspects persisted state to determine whether each document should be created or is a conflict
- conflict entries are not persisted; they are reported in the result
- if no repositories are configured, all valid resources are recorded as created without persistence (validation-only mode)
actor identifies who initiated the apply. It is recorded in control-plane audit entries for each successfully persisted resource. Use ApplyBundle when parsing a raw YAML bundle; actor is extracted from the X-MIDAS-ACTOR request header.
func (*Service) ApplyBundle ¶
func (s *Service) ApplyBundle(ctx context.Context, yamlBytes []byte, actor string) (*types.ApplyResult, error)
ApplyBundle parses a raw YAML bundle and applies it through the validation and apply pipeline.
actor identifies who initiated the apply (e.g. from the X-MIDAS-ACTOR header). If empty, "system" is used as a fallback actor in audit entries.
Behavior:
- parse failures return an error wrapping ErrInvalidBundle
- successfully parsed bundles always return an ApplyResult
- validation failures are represented inside ApplyResult, not as an error
func (*Service) Plan ¶
Plan validates a parsed bundle and returns the ApplyPlan that describes the intended action for each document. No persistence occurs.
The returned plan is the same one Apply would execute. Callers can inspect every entry's Action, Message, DecisionSource, and ValidationErrors to understand what would happen before committing to a write.
func (*Service) PlanBundle ¶
PlanBundle parses a raw YAML bundle and returns the ApplyPlan without persisting anything.
Behavior:
- parse failures return an error wrapping ErrInvalidBundle
- successfully parsed bundles always return an ApplyPlan (never nil)
- validation failures are represented inside the ApplyPlan entries, not as an error