policy

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package policy loads lane policy (ADR-006): .agents/factory/lanes.yaml, validated against the published schema, with defaults applied, a hash per lane for the decision record, CEL guards over structured facts, and placeholders filled only from labels.

Index

Constants

View Source
const DefaultNoCIAfter = 30 * time.Minute

DefaultNoCIAfter is how long a proposed commit may report no check or status at all before the item escalates, when a lane does not say.

View Source
const FactoryFile = "factory.yaml"

FactoryFile is a configuration repository's factory file, in its factory folder (ADR-006).

View Source
const LanesFile = "lanes.yaml"

LanesFile is the name of the lane policy file in a factory folder.

Variables

View Source
var FactoryDirs = []string{".agents/factory", ".ynh/ynf", ".ynm/ynf", ".ynf"}

FactoryDirs is where a repository keeps ynf's files, in lookup order (ADR-009). The first found wins; the others are shadowed and reported, never merged.

View Source
var SafeLabel = regexp.MustCompile(`^[A-Za-z0-9._/-]+(:[A-Za-z0-9._/-]+)?$`)

SafeLabel is what a label given with a prompt must match: a plain name or <prefix>:<value>.

View Source
var SafeValue = regexp.MustCompile(`^[A-Za-z0-9._/-]+$`)

SafeValue is what a substituted value must match, so ticket data never reaches a shell as anything but a plain path-like token (ADR-006).

Functions

func DefinesLane

func DefinesLane(doc []byte, name string) bool

DefinesLane reports whether a lanes.yaml document declares a lane called name. A document that does not parse declares none.

func Expand

func Expand(tmpl string, labels []string) (string, error)

Expand fills {label.<prefix>} placeholders from '<prefix>:<value>' labels.

func ExpandShape

func ExpandShape(tmpl string) string

ExpandShape fills every {label.<prefix>} placeholder with a safe dummy path segment, to check the shape of a template before any item's labels are known.

func Guard

func Guard(expr string, facts, item map[string]any) (bool, error)

Guard evaluates a CEL guard over structured facts and the item. Free text is never in facts (ADR-006), so a ticket body cannot steer a guard.

func LabelValue

func LabelValue(labels []string, prefix string) (string, bool)

LabelValue returns the value of the first '<prefix>:<value>' label.

func LaneSources

func LaneSources(config, repo []byte, lane string) (map[string]string, error)

LaneSources says where each of a lane's values came from: every leaf path in the merged lane (dotted, such as run.ynh.focus) maps to "repo" when the repository's lanes set it and "config" when only the configuration repository's do.

func MergeLanes

func MergeLanes(config, repo []byte) ([]byte, error)

MergeLanes lays a target repository's lanes over a configuration repository's, key by key: the target repository wins (ADR-006). Maps merge; anything else in the repository replaces the configuration's, lists included, so a repository can narrow a list as well as extend it. Either may be empty. The result is validated as a whole when it is loaded, so a repository may hold only overrides, such as turning a lane off with enabled: false.

func ParseDuration

func ParseDuration(s string) (time.Duration, error)

ParseDuration accepts the schema's durations, including days.

func Resolve

func Resolve(exists func(dir string) bool) (dir string, shadowed []string)

Resolve picks the factory folder from those that exist, and lists the shadowed ones.

func ValidateYAML

func ValidateYAML(schemaJSON []byte, schemaURL string, doc []byte) error

ValidateYAML checks a YAML document against a JSON schema and returns every violation, so a hand-edited file gets all its problems at once.

Types

type Budgets

type Budgets struct {
	MaxTurns  int    `yaml:"max_turns" json:"max_turns,omitempty"`
	MaxTokens int    `yaml:"max_tokens" json:"max_tokens,omitempty"`
	MaxWall   string `yaml:"max_wall" json:"max_wall,omitempty"`
}

Budgets may only tighten the harness's own.

type Command

type Command struct {
	Argv []string `yaml:"argv" json:"argv"`
	// Image is the image the command runs in on a container executor. When the lane resolves to
	// the command runner it wins over run.image; when it resolves to ynh it is ignored.
	Image string `yaml:"image" json:"image,omitempty"`
	// ImageUser keeps the image's own user and home, as a ynh-built agent image does, instead of
	// running as ynf's user. It is for an image whose command needs its own user; a docker executor
	// only.
	ImageUser  bool   `yaml:"image_user" json:"image_user,omitempty"`
	ResultFile string `yaml:"result_file" json:"result_file,omitempty"`
}

Command is the command runner's settings.

type Defaults

type Defaults struct {
	Executor  string  `yaml:"executor" json:"executor,omitempty"`
	Attempts  int     `yaml:"attempts" json:"attempts,omitempty"`
	Retention string  `yaml:"retention" json:"retention,omitempty"`
	NoCIAfter string  `yaml:"no_ci_after" json:"no_ci_after,omitempty"`
	Egress    *Egress `yaml:"egress" json:"egress,omitempty"`
	Stop      *Stop   `yaml:"stop" json:"stop,omitempty"`
	PR        *PR     `yaml:"pr" json:"pr,omitempty"`
	Labels    *Labels `yaml:"labels" json:"labels,omitempty"`
}

Defaults are what every lane inherits unless it sets its own.

type Egress

type Egress struct {
	Allow []string `yaml:"allow" json:"allow"`
}

Egress is what a run may reach (ADR-007).

type Factory

type Factory struct {
	Version  int                       `yaml:"version" json:"version"`
	Repos    []string                  `yaml:"repos" json:"repos"`
	Trackers map[string]map[string]any `yaml:"trackers" json:"trackers,omitempty"`
	Forges   map[string]map[string]any `yaml:"forges" json:"forges,omitempty"`
}

Factory is a configuration repository's factory.yaml: enrolment, and the tracker and forge instances, which are declared only here.

func LoadFactory

func LoadFactory(doc []byte) (*Factory, error)

LoadFactory validates and reads a factory.yaml.

type File

type File struct {
	Version  int             `yaml:"version" json:"version"`
	Defaults Defaults        `yaml:"defaults" json:"defaults"`
	Lanes    map[string]Lane `yaml:"lanes" json:"lanes"`
}

File is a parsed lanes.yaml.

func Load

func Load(doc []byte) (*File, error)

Load validates lanes.yaml against the published schema and returns it with defaults applied.

func (*File) Names

func (f *File) Names() []string

Names returns lane names, sorted.

func (*File) Pins

func (f *File) Pins() map[string]Pin

Pins returns the harness each lane pins, by lane name.

type Guards

type Guards struct {
	Eligible string `yaml:"eligible" json:"eligible,omitempty"`
}

Guards are CEL expressions over structured facts.

type Intake

type Intake struct {
	GitHubSearch string `yaml:"github.search" json:"github.search,omitempty"`
	JiraSearch   string `yaml:"jira.search" json:"jira.search,omitempty"`
	Every        string `yaml:"every" json:"every,omitempty"`
}

Intake is one source of work for a lane.

func (Intake) Interval

func (i Intake) Interval() time.Duration

Interval parses Every.

type LabelChange

type LabelChange struct {
	Add    []string `yaml:"add" json:"add,omitempty"`
	Remove []string `yaml:"remove" json:"remove,omitempty"`
}

LabelChange adds and removes labels.

type Labels

type Labels struct {
	OnClaim    *LabelChange `yaml:"on_claim" json:"on_claim,omitempty"`
	OnPropose  *LabelChange `yaml:"on_propose" json:"on_propose,omitempty"`
	OnReview   *LabelChange `yaml:"on_review" json:"on_review,omitempty"`
	OnEscalate *LabelChange `yaml:"on_escalate" json:"on_escalate,omitempty"`
	OnDone     *LabelChange `yaml:"on_done" json:"on_done,omitempty"`
}

Labels are what ynf writes on the ticket as the item enters a state (ADR-003, ADR-006).

func (*Labels) For

func (l *Labels) For(state string) *LabelChange

For is the change for an item entering state, or nil.

type Lane

type Lane struct {
	Name string `yaml:"-" json:"name"`
	// ID is the lane's own id for telemetry, when it declares one; otherwise the id is where the
	// lane is defined plus its name (ADR-006).
	ID        string              `yaml:"id" json:"id,omitempty"`
	Kind      string              `yaml:"kind" json:"kind"`
	Enabled   *bool               `yaml:"enabled" json:"enabled,omitempty"`
	Intake    []Intake            `yaml:"intake" json:"intake"`
	Guards    Guards              `yaml:"guards" json:"guards"`
	Run       Run                 `yaml:"run" json:"run"`
	When      map[string]Reaction `yaml:"when" json:"when"`
	PR        PR                  `yaml:"pr" json:"pr"`
	Stop      Stop                `yaml:"stop" json:"stop"`
	Executor  string              `yaml:"executor" json:"executor,omitempty"`
	Attempts  int                 `yaml:"attempts" json:"attempts,omitempty"`
	Retention string              `yaml:"retention" json:"retention,omitempty"`
	// NoCIAfter is how long a proposed head commit may have no check or status at all before the
	// item reacts (when: no_ci). Empty means DefaultNoCIAfter; 0s switches it off.
	NoCIAfter string  `yaml:"no_ci_after" json:"no_ci_after,omitempty"`
	Labels    *Labels `yaml:"labels" json:"labels,omitempty"`
}

Lane is one lane, with defaults applied after Load.

func (Lane) CheckLabels

func (l Lane) CheckLabels(labels []string) error

CheckLabels reports whether labels fill every {label.<prefix>} placeholder the lane's runs use, with safe values: its command's arguments and its ynh sensor scopes. Work the lane cannot run is refused before it starts, rather than failing in its first run.

func (Lane) Hash

func (l Lane) Hash() string

Hash is the SHA-256 of the lane's normalised form, recorded with every decision (ADR-006).

func (Lane) NoCIWait

func (l Lane) NoCIWait() time.Duration

NoCIWait is the lane's no_ci_after as a duration; zero means the reaction is off.

func (Lane) On

func (l Lane) On() bool

On reports whether the lane is enabled.

type PR

type PR struct {
	AllowedPaths   []string `yaml:"allowed_paths" json:"allowed_paths,omitempty"`
	ProtectedPaths []string `yaml:"protected_paths" json:"protected_paths,omitempty"`
	Draft          *bool    `yaml:"draft" json:"draft,omitempty"`
}

PR is how changes are proposed.

func (PR) IsDraft

func (p PR) IsDraft() bool

IsDraft reports whether pull requests open as drafts (the default).

type Pin

type Pin struct {
	Repo string `json:"repo"`
	Ref  string `json:"ref"`
	// URL is the repository as the clone tools are given it, set only when it differs from Repo:
	// a bare host and path is cloned over https.
	URL string `json:"url,omitempty"`
}

Pin is a harness pinned from a repository: the repository and the tag or commit to take from it. A lane writes it as run.ynh.harness: <repository>@<tag-or-commit>, such as github.com/org/harness@v1.2.0 or file:///srv/harness.git@3f9c2e1. ynh takes the same two things as `ynh install <repository> --ref <tag-or-commit>`.

func ParsePin

func ParsePin(harness string) (p Pin, ok bool, err error)

ParsePin reads a lane's harness value. ok is false when it is not a pin: a folder in the repository, or the id of an installed harness. A value shaped like a pin that cannot be used as one (no tag or commit, or one a flag could be mistaken for) is an error, so a typo is refused rather than run as an installed id.

func (Pin) CloneURL

func (p Pin) CloneURL() string

CloneURL is the repository as the clone tools are given it. A URL with a scheme and an scp-like address are used as written; a bare host and path, such as github.com/org/repo, is https://github.com/org/repo, because the clone tools read the bare form as a folder.

func (Pin) Resolved

func (p Pin) Resolved() string

Resolved is CloneURL and the ref: the pin as it is used, which differs from String only for a bare host and path.

func (Pin) String

func (p Pin) String() string

type Reaction

type Reaction struct {
	Action     string `json:"action,omitempty"`
	Retry      int    `json:"retry,omitempty"`
	Then       string `json:"then,omitempty"`
	ResumeWith string `json:"resume_with,omitempty"`
	Max        int    `json:"max,omitempty"`
}

Reaction is what to do for an outcome: an action, a retry then an action, or a resume.

func (*Reaction) UnmarshalYAML

func (r *Reaction) UnmarshalYAML(n *yaml.Node) error

UnmarshalYAML accepts the schema's three shapes.

type Run

type Run struct {
	Runner   string   `yaml:"runner" json:"runner,omitempty"`
	Executor string   `yaml:"executor" json:"executor,omitempty"`
	Image    string   `yaml:"image" json:"image,omitempty"`
	Egress   *Egress  `yaml:"egress" json:"egress,omitempty"`
	Env      []string `yaml:"env" json:"env,omitempty"`
	Ynh      *Ynh     `yaml:"ynh" json:"ynh,omitempty"`
	Command  *Command `yaml:"command" json:"command,omitempty"`
}

Run is how a lane's runs are made.

func (Run) ImageFor

func (r Run) ImageFor(runner string) string

ImageFor is the image a run of the named runner uses: the command's own image when the command runner runs and names one, else run.image.

type Stop

type Stop struct {
	YieldFloor        float64 `yaml:"yield_floor" json:"yield_floor,omitempty"`
	ReviewTimeCeiling string  `yaml:"review_time_ceiling" json:"review_time_ceiling,omitempty"`
	EscapedDefects    int     `yaml:"escaped_defects" json:"escaped_defects,omitempty"`
	MaxOpenProposals  int     `yaml:"max_open_proposals" json:"max_open_proposals,omitempty"`
	MinSample         int     `yaml:"min_sample" json:"min_sample,omitempty"`
}

Stop holds stop conditions (ADR-010).

type Ynh

type Ynh struct {
	Harness string `yaml:"harness" json:"harness"`
	Vendor  string `yaml:"vendor" json:"vendor,omitempty"`
	Base    string `yaml:"base" json:"base,omitempty"`
	Focus   string `yaml:"focus" json:"focus,omitempty"`
	Profile string `yaml:"profile" json:"profile,omitempty"`
	Sandbox string `yaml:"sandbox" json:"sandbox,omitempty"`
	// Model is ynh's --model: the model the agent runs on, as the vendor names it. Empty is the
	// vendor's default. The schema limits it to a plain name, so it never reaches argv as a flag.
	Model string `yaml:"model" json:"model,omitempty"`
	// Effort is ynh's --effort (low, medium or high): the reasoning effort the agent is asked to
	// run at, which ynh maps to each vendor's own setting and refuses where a vendor has none.
	// Empty is the harness's agent.effort, else the vendor's default. It needs ynh 0.10.0 or later.
	Effort string `yaml:"effort" json:"effort,omitempty"`
	// AutoApprove is ynh's --auto-approve (edits or all): the worker runs without approval prompts.
	// ynf passes it only to a contained run (ADR-007).
	AutoApprove string            `yaml:"auto_approve" json:"auto_approve,omitempty"`
	Budgets     *Budgets          `yaml:"budgets" json:"budgets,omitempty"`
	SensorScope map[string]string `yaml:"sensor_scope" json:"sensor_scope,omitempty"`
	// TelemetryRelay sets YNH_TELEMETRY_RELAY=1 for the lane's runs, so ynh agent run starts ynr
	// relay beside the vendor CLI and the vendor's telemetry reaches the run's spool folder.
	TelemetryRelay bool `yaml:"telemetry_relay" json:"telemetry_relay,omitempty"`
}

Ynh is the ynh runner's settings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL