config

package
v0.33.0 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package config loads and validates the per-project .awf/ configuration: a skeleton config.yaml plus per-target sidecar YAMLs and convention parts.

Index

Constants

View Source
const DirName = ".awf"

DirName is the config-tree directory name at the project root.

View Source
const DocsDir = "docs"

DocsDir is the fixed root for awf-managed documentation.

Variables

This section is empty.

Functions

func ConfigPath added in v0.6.0

func ConfigPath(root string) string

ConfigPath returns the skeleton config.yaml path for a project root.

func HasMapping added in v0.30.0

func HasMapping(src []byte, key string) (bool, error)

HasMapping reports whether src carries a top-level key whose value is a mapping node, so a caller can distinguish an absent block from an empty one without parsing config.yaml itself (ADR-0026 keeps that knowledge here). A migration needs it because RemoveMappingKey drops a parent it empties: only the caller that knew a block was present can tell a deliberate absence from one its own removals just created. A key present with a non-mapping value reports false, matching every editor above that declines a foreign shape.

func HasValue added in v0.30.0

func HasValue(src []byte, key string) (bool, error)

HasValue reports whether src carries a top-level key holding a non-empty scalar, without parsing config.yaml itself (ADR-0026 keeps that knowledge here). It is the flat-scalar sibling of HasMapping, which answers the narrower question of whether a present key holds a mapping.

Emptiness rather than presence is deliberate: the callers are the schema migration that seeds a required key and the port-forward that must reproduce it, and both must treat `key:` with a null or empty value the same way they treat an absent key. A present-but-empty key is not a value to preserve; it is the same failed validation an absent one produces.

func IsSingletonKind

func IsSingletonKind(kind string) bool

IsSingletonKind reports whether kind is an always-on singleton whose sidecar lives at <root>/<kind>.yaml and whose parts live under <root>/parts/<kind>/ (ADR-0021, ADR-0043).

func LockPath added in v0.6.0

func LockPath(root string) string

LockPath returns the awf.lock path for a project root.

func MarshalSkeleton

func MarshalSkeleton(s Skeleton) ([]byte, error)

MarshalSkeleton renders a fresh config.yaml from s in the canonical awf format (two-space block style). It is the construction half of internal/config's ownership of config.yaml serialization (ADR-0026).

func MoveMappingKeyToBool added in v0.31.0

func MoveMappingKeyToBool(src []byte, fromKey, child, toKey string, value bool) ([]byte, error)

MoveMappingKeyToBool removes child from one top-level mapping and writes the same child as a boolean under another mapping in one YAML-node edit. Comments owned by a removed child, and by a source mapping that becomes empty, move to the replacement nodes rather than disappearing with the retired null value.

func RemoveArrayMember added in v0.32.0

func RemoveArrayMember(src []byte, key, name string) ([]byte, bool, error)

RemoveArrayMember removes every occurrence of name from the sequence under key. It reports whether it removed an item, leaves an absent key or member byte-identical, and rejects a present non-sequence value. Schema migrations use it when an obsolete catalog selection must disappear before strict catalog validation.

func RemoveKey added in v0.2.0

func RemoveKey(src []byte, key string) ([]byte, error)

RemoveKey deletes the top-level mapping entry under key from a config.yaml source via a yaml.Node round-trip that preserves comments and every untouched key (ADR-0026). Removing an absent key is a no-op (returns src unchanged), so a schema migration can re-run safely.

func RemoveMappingKey added in v0.18.0

func RemoveMappingKey(src []byte, key, child string) ([]byte, error)

RemoveMappingKey removes child from the mapping at top-level key, preserving comments and every untouched key via the same yaml.Node round-trip as the other editors (ADR-0026). When the removal empties the parent mapping, the parent key goes too, so a retired setting leaves no vestigial block behind. An absent parent, a non-mapping parent, or an absent child is a no-op (returns src unchanged), so a schema migration can re-run safely.

func RootDir added in v0.6.0

func RootDir(root string) string

RootDir returns the config-tree directory for a project root (<root>/.awf).

func SeedVarKey added in v0.14.0

func SeedVarKey(src []byte, name string) ([]byte, error)

SeedVarKey adds `name: ""` under the top-level vars: mapping when the key is absent, creating the mapping if needed, via the same comment-preserving yaml.Node round-trip as SetArrayMember (ADR-0026). A present key - set, empty, or null - is left untouched and src is returned unchanged: presence is the open-to-do signal and absence the deliberate decline (ADR-0087), so seeding must never overwrite either state.

func SetArrayMember

func SetArrayMember(src []byte, key, name string, add bool) ([]byte, error)

SetArrayMember adds or removes name in the sequence under key in a config.yaml source, via a yaml.Node round-trip that preserves comments and every untouched key (ADR-0026). The edited sequence is normalized to block style, so a flow-style input (`key: [a, b]`) is accepted. Adding a member already present is a no-op; removing a member absent from the key (or a key absent on remove) errors. touches-state: config/configuration:config-mutation-roundtrip - yaml.Node add/remove round-trip; proof in edit_test.go

func SetMappingInteger added in v0.22.0

func SetMappingInteger(src []byte, key, child string, value int) ([]byte, error)

SetMappingInteger sets child to an integer under a top-level mapping, creating the mapping when absent while preserving comments and unrelated keys.

func SetMappingScalar added in v0.5.0

func SetMappingScalar(src []byte, key, child string, value bool) ([]byte, error)

SetMappingScalar sets child to a bool value under a top-level mapping at key in a config.yaml source, creating the key's mapping (and the child) if absent, via a yaml.Node round-trip that preserves comments and every untouched key (ADR-0026). It is the nested-scalar analog of SetArray (which writes a sequence): the bootstrap enable entry is `bootstrap.enabled: <bool>`, not an enable array, so it needs a mapping-scalar writer rather than SetArrayMember. An existing scalar under key/child is overwritten.

func SetMappingString added in v0.30.0

func SetMappingString(src []byte, key, child, value string) ([]byte, error)

SetMappingString replaces a present string value at child under a top-level mapping at key, via the same yaml.Node round-trip as the other editors so comments and every untouched key survive (ADR-0026). It is the string sibling of SetMappingScalar (bool) and SetMappingInteger (int), added for the retired-command value migration (ADR-0159 Decision 8): SeedVarKey writes a string only into an *absent* key, so until now nothing rewrote a present one. The scalar's style is preserved, so a quoted value stays quoted and the diff carries only the value change.

Every shape it does not own is a no-op returning src unchanged: an absent key, a non-mapping parent, an absent child, and a child that is not a plain scalar (an alias node or a nested structure). Each sibling handles a foreign shape differently: SetMappingInteger errors on a non-mapping parent, SetMappingScalar coerces one by replacing the node, and this editor declines. It declines because it exists for the ADR-0159 value migration, whose contract is that any value it does not recognize is left untouched. An alias is the case that matters: `proseGateCmd: *cmd` decodes to a Go string while its node is an alias, so erroring here would abort awf upgrade over a value it was never asked to rewrite and strand the tree below the current schema generation. Being total also keeps a re-run safe.

func SetString added in v0.30.0

func SetString(src []byte, key, value string) ([]byte, error)

SetString sets the top-level scalar entry under key to value, creating the key if it is absent and replacing whatever is there otherwise, via a yaml.Node round-trip that preserves comments and every untouched key (ADR-0026). The required-explicit integrationBranch key has no in-code default, so its schema migration must materialize a visible scalar line rather than append to or seed a nested block (ADR-0202 Decision 6).

func ValidateArtifactName added in v0.10.0

func ValidateArtifactName(kind, name string) error

ValidateArtifactName reports whether a flat artifact name uses the catalog's lowercase kebab-case grammar. The charset is frontmatter-safe: it excludes path separators, awf's reserved "_" namespace, and punctuation that would break a generated skill's YAML frontmatter. Migration also uses it to recognize the historical flat skill and agent sidecar surface.

func ValidateDomainName

func ValidateDomainName(name string) error

ValidateDomainName reports whether name is a usable domain key: non-empty and free of path separators or "..".

func ValidatePathGlobs added in v0.33.0

func ValidatePathGlobs(globs []string) error

ValidatePathGlobs rejects empty, duplicate, or malformed anchored path globs.

Types

type AuditConfig

type AuditConfig struct {
	AllowedScopes []ScopeSpec `yaml:"allowedScopes"`
}

AuditConfig carries the repository-specific Conventional Commits scope vocabulary for `awf audit` (ADR-0017). Every audit rule and threshold is fixed in internal/audit; a nil *AuditConfig or an empty AllowedScopes accepts any scope.

type BootstrapConfig added in v0.5.0

type BootstrapConfig struct {
	Enabled bool `yaml:"enabled"`
}

BootstrapConfig configures the rendered .awf/bootstrap.sh singleton (ADR-0040, relocated by ADR-0047). A nil *BootstrapConfig (key absent) and Enabled false both mean "do not render"; only Enabled true renders the artifact - a nested enable entry rather than a top-level scalar bool (the Alternatives table rejected the bare bool).

type CommitPolicyConfig added in v0.30.0

type CommitPolicyConfig struct {
	GrandfatheredThrough string                 `yaml:"grandfatheredThrough"`
	AllowedIdentities    []CommitPolicyIdentity `yaml:"allowedIdentities"`
	RequireSignedCommits bool                   `yaml:"requireSignedCommits"`
	AllowedSigners       []CommitPolicySigner   `yaml:"allowedSigners"`
	// contains filtered or unexported fields
}

CommitPolicyConfig is an optional exact-commit provenance policy. Repository resolution and verification belong to later operation owners; this package validates only the authored structural contract.

func (*CommitPolicyConfig) UnmarshalYAML added in v0.30.0

func (c *CommitPolicyConfig) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML retains optional-list presence while preserving strict nested field validation for the commitPolicy mapping and its records.

type CommitPolicyIdentity added in v0.30.0

type CommitPolicyIdentity struct {
	Name  string `yaml:"name"`
	Email string `yaml:"email"`
}

CommitPolicyIdentity is one exact author/committer name and email pair.

type CommitPolicySigner added in v0.30.0

type CommitPolicySigner struct {
	Principal string `yaml:"principal"`
	Key       string `yaml:"key"`
}

CommitPolicySigner is one SSH signing principal and public key pair.

type Config

type Config struct {
	Prefix string `yaml:"prefix"`
	// IntegrationBranch names the branch effort work integrates into. It is
	// required-explicit and carries no in-code default (the Prefix precedent,
	// not the DocsDir one): the schema migration writes `integrationBranch:
	// main` visibly so no adopter silently inherits a branch name it never
	// chose (ADR-0202 Decision 6, keeping ADR-0127's silent-default removal).
	IntegrationBranch string              `yaml:"integrationBranch"`
	Vars              map[string]any      `yaml:"vars"`
	Domains           []string            `yaml:"domains"`
	Tags              map[string]string   `yaml:"tags"`
	ContextIgnore     []string            `yaml:"contextIgnore"`
	CurrentState      *CurrentStateConfig `yaml:"currentState"`
	Audit             *AuditConfig        `yaml:"audit"`
	Bootstrap         *BootstrapConfig    `yaml:"bootstrap"`
	ProseGate         *ProseGateConfig    `yaml:"proseGate"`
	MemoryCite        *MemoryCiteConfig   `yaml:"memoryCite"`
	CommitPolicy      *CommitPolicyConfig `yaml:"commitPolicy"`
	// contains filtered or unexported fields
}

Config is the skeleton config.yaml: repository facts and render shaping.

func Load

func Load(awfDir string) (*Config, error)

Load reads <awfDir>/config.yaml with the strict decoder and records awfDir as the sidecar/part resolution root.

func Parse added in v0.18.0

func Parse(awfDir string, b []byte) (*Config, error)

Parse strictly decodes config.yaml bytes, records awfDir as the sidecar/part resolution root, and applies defaults.

func ParseTree added in v0.22.0

func ParseTree(awfDir string, b []byte, read TreeReader) (*Config, error)

ParseTree decodes config bytes and injects the selected config-tree reader.

func (*Config) HasSidecar added in v0.10.0

func (c *Config) HasSidecar(kind, name string) (bool, error)

HasSidecar reports whether a declaring sidecar file exists for an artifact - the presence signal that marks a non-catalog name as an intentional local artifact rather than a typo (ADR-0068).

func (*Config) PartPath

func (c *Config) PartPath(kind, artifact, section string) string

PartPath returns the convention part path for a section of an artifact.

func (*Config) ReadPart added in v0.22.0

func (c *Config) ReadPart(kind, artifact, section string) ([]byte, bool, error)

ReadPart returns selected-universe convention-part bytes.

func (*Config) ReadPartPath added in v0.22.0

func (c *Config) ReadPartPath(full string) ([]byte, error)

ReadPartPath reads a consumed absolute part path through the selected reader.

func (*Config) ReadSidecar added in v0.22.0

func (c *Config) ReadSidecar(rel string) ([]byte, bool)

ReadSidecar returns selected-universe sidecar bytes by config-relative path.

func (*Config) Sidecar

func (c *Config) Sidecar(kind, name string) (Sidecar, error)

Sidecar reads <root>/<kind>/<name>.yaml; agents-doc lives at <root>/agents-doc.yaml. A missing file yields a zero Sidecar (publication-safe: empty data/sections).

func (*Config) Source added in v0.10.0

func (c *Config) Source() []byte

Source returns the exact config.yaml bytes Load read. A byte-level editor (SetArrayMember, SetArray, SetMappingScalar) reuses these instead of re-reading the file, which after a successful Load could only fail on a race.

func (*Config) Validate

func (c *Config) Validate() error

type CurrentStateConfig added in v0.18.0

type CurrentStateConfig struct {
	Sources   []CurrentStateSource `yaml:"sources"`
	TestGlobs []string             `yaml:"testGlobs"`
}

CurrentStateConfig configures bridge-preparation validation for canonical current-state topics. It is deliberately separate from the legacy invariant authority, which remains active throughout the bridge tranche.

func (*CurrentStateConfig) UnmarshalYAML added in v0.18.0

func (c *CurrentStateConfig) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML preserves strict nested field validation for the custom-decoded current-state mapping.

type CurrentStateSource added in v0.18.0

type CurrentStateSource struct {
	Globs  []string `yaml:"globs"`
	Marker string   `yaml:"marker"`
	Close  string   `yaml:"close"`
	// contains filtered or unexported fields
}

CurrentStateSource describes one marker-bearing source family. closeSet distinguishes an omitted close token from an explicitly empty one.

func (*CurrentStateSource) UnmarshalYAML added in v0.18.0

func (s *CurrentStateSource) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML retains close-token presence while preserving strict nested field validation for the custom-decoded source mapping.

type GlobRewrite added in v0.12.0

type GlobRewrite struct {
	Key  string
	From string
}

A GlobRewrite records one no-slash glob scalar AnchorNoSlashGlobs anchored: Key is the config location, From the original pattern (rewritten to `**/<From>`).

func AnchorNoSlashGlobs added in v0.11.0

func AnchorNoSlashGlobs(src []byte) ([]byte, []GlobRewrite, error)

AnchorNoSlashGlobs rewrites every no-slash glob scalar under invariants.sources[].globs and audit.dependencyManifests to `**/<pattern>`, preserving comments and untouched keys (ADR-0026) and reporting the rewrites performed. Slashed patterns are left alone, so the rewrite is idempotent; absent keys are a no-op. It is the nested-sequence editor the schema-7 anchored-globs migration (ADR-0077) consumes - the sequence analog of SetMappingScalar.

type MemoryCiteConfig added in v0.30.0

type MemoryCiteConfig struct {
	Exemptions []MemoryExemption `yaml:"exemptions"`
}

MemoryCiteConfig configures exemptions for `awf check repo memory` (ADR-0158), which always scans the staged decision-record directories and every cleaned commit-message body for a citation of a specific working-memory file. A nil *MemoryCiteConfig means no paths are exempt.

type MemoryExemption added in v0.30.0

type MemoryExemption struct {
	Path  string `yaml:"path"`
	Count *int   `yaml:"count"`
}

MemoryExemption permits citations in one path. A nil Count permits any number of them; a non-nil Count pins the expected number, so an added citation in an exempt file still fails.

type ProseExemption added in v0.18.0

type ProseExemption struct {
	Path      string `yaml:"path"`
	Codepoint string `yaml:"codepoint"`
	Count     *int   `yaml:"count"`
}

ProseExemption exempts one codepoint in one path. Codepoint is spelled "U+2014", never the character itself: config.yaml is a tracked file the scan reads, so a typed glyph here would be a finding against the file that configures the exemptions. A nil Count permits any number of occurrences; a non-nil Count pins the expected number, so an added occurrence in an exempt file still fails.

type ProseGateConfig added in v0.18.0

type ProseGateConfig struct {
	Exemptions []ProseExemption `yaml:"exemptions"`
}

ProseGateConfig configures exemptions for `awf check repo prose` (ADR-0119), which always scans every tracked text file for the seven banned typographic punctuation substitutes. A nil *ProseGateConfig means no paths or codepoints are exempt.

type ScopeSpec added in v0.8.0

type ScopeSpec struct {
	Name    string `yaml:"name"`
	Meaning string `yaml:"meaning"`
}

ScopeSpec is one allowed commit scope: a name and an optional human meaning. In config a scope is written either as a bare string (name only) or a {name, meaning} mapping (ADR-0056).

func AuditScopes added in v0.32.0

func AuditScopes(a *AuditConfig) []ScopeSpec

AuditScopes returns the configured scope vocabulary, if the audit block exists.

func (*ScopeSpec) UnmarshalYAML added in v0.8.0

func (s *ScopeSpec) UnmarshalYAML(n *yaml.Node) error

UnmarshalYAML accepts either a scalar node (the bare-string form → empty meaning) or a strict mapping node. invariant: scope-config-dual-form

type SectionOverride

type SectionOverride struct {
	Drop bool `yaml:"drop"`
}

SectionOverride is a sidecar's per-section override. Body replacement is by convention part only; the field set is deliberately just Drop. touches-state: config/configuration:no-replacewith - SectionOverride field set omits replaceWith; proof in config_test.go

type Sidecar

type Sidecar struct {
	Data         map[string]any             `yaml:"data"`
	DataDefaults map[string]bool            `yaml:"dataDefaults"`
	Sections     map[string]SectionOverride `yaml:"sections"`
	// Paths declares a domain's file territory as anchored path globs
	// (ADR-0077); read only from domain sidecars, inert on other kinds.
	Paths []string `yaml:"paths"`
}

Sidecar holds a single target's non-prose configuration: structured render data and per-section overrides. It lives at <awfDir>/<kind>/<name>.yaml (agents-doc: <awfDir>/agents-doc.yaml). An absent sidecar is the zero Sidecar (publication-safe: empty data/sections).

type Skeleton

type Skeleton struct {
	Prefix string `yaml:"prefix"`
	// IntegrationBranch is written explicitly because the key is required and
	// carries no in-code default (ADR-0202 Decision 6): a scaffold omitting it
	// would emit a config that fails its own validation on the next open.
	IntegrationBranch string            `yaml:"integrationBranch"`
	Vars              map[string]string `yaml:"vars"`
	Audit             *SkeletonAudit    `yaml:"audit,omitempty"`
	Bootstrap         *BootstrapConfig  `yaml:"bootstrap,omitempty"`
}

Skeleton is the input to MarshalSkeleton: the fields a freshly-scaffolded .awf/config.yaml carries. Vars is typed map[string]string (not map[string]any) so a nil/null var value is unrepresentable - the scaffold seeds each var with an empty string, which marshals as `x: ""`. A nil interface would marshal as `x: null` and decode back to a nil value that renders as "<no value>", tripping the publication-safe check (ADR-0026 Decision 3).

type SkeletonAudit added in v0.6.0

type SkeletonAudit struct {
	AllowedScopes []string `yaml:"allowedScopes"`
}

SkeletonAudit is the audit block a scaffold can seed (ADR-0051): only allowedScopes - the one audit field init collects. Deliberately not *AuditConfig, whose zero-value fields would serialize as explicit settings.

type TreeReader added in v0.22.0

type TreeReader interface {
	ReadFile(path string) ([]byte, bool)
	Paths(prefix string) []string
}

TreeReader supplies canonical config-tree-relative bytes without exposing a filesystem. Implementations return defensive copies.

Jump to

Keyboard shortcuts

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