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
- func ConfigPath(root string) string
- func HasMapping(src []byte, key string) (bool, error)
- func HasValue(src []byte, key string) (bool, error)
- func IsSingletonKind(kind string) bool
- func LockPath(root string) string
- func MarshalSkeleton(s Skeleton) ([]byte, error)
- func RemoveKey(src []byte, key string) ([]byte, error)
- func RemoveMappingKey(src []byte, key, child string) ([]byte, error)
- func RootDir(root string) string
- func SeedVarKey(src []byte, name string) ([]byte, error)
- func SetArray(src []byte, key string, values []string) ([]byte, error)
- func SetArrayMember(src []byte, key, name string, add bool) ([]byte, error)
- func SetMappingInteger(src []byte, key, child string, value int) ([]byte, error)
- func SetMappingScalar(src []byte, key, child string, value bool) ([]byte, error)
- func SetMappingString(src []byte, key, child, value string) ([]byte, error)
- func SetString(src []byte, key, value string) ([]byte, error)
- func ValidateArtifactName(kind, name string) error
- func ValidateDocName(name string) error
- func ValidateDomainName(name string) error
- type AuditConfig
- type BootstrapConfig
- type CatalogTrim
- type CommitPolicyConfig
- type CommitPolicyIdentity
- type CommitPolicySigner
- type Config
- func (c *Config) HasSidecar(kind, name string) (bool, error)
- func (c *Config) PartPath(kind, artifact, section string) string
- func (c *Config) ReadPart(kind, artifact, section string) ([]byte, bool, error)
- func (c *Config) ReadPartPath(full string) ([]byte, error)
- func (c *Config) ReadSidecar(rel string) ([]byte, bool)
- func (c *Config) Sidecar(kind, name string) (Sidecar, error)
- func (c *Config) Source() []byte
- func (c *Config) Validate() error
- type CurrentStateConfig
- type CurrentStateSource
- type GlobRewrite
- type HooksConfig
- type MemoryCiteConfig
- type MemoryExemption
- type ProseExemption
- type ProseGateConfig
- type RunnerConfig
- type ScopeSpec
- type SectionOverride
- type Sidecar
- type Skeleton
- type SkeletonAudit
- type TreeReader
Constants ¶
const DirName = ".awf"
DirName is the config-tree directory name at the project root.
Variables ¶
This section is empty.
Functions ¶
func ConfigPath ¶ added in v0.6.0
ConfigPath returns the skeleton config.yaml path for a project root.
func HasMapping ¶ added in v0.30.0
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
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 ¶
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 MarshalSkeleton ¶
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 RemoveKey ¶ added in v0.2.0
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
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
RootDir returns the config-tree directory for a project root (<root>/.awf).
func SeedVarKey ¶ added in v0.14.0
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 SetArray ¶ added in v0.4.0
SetArray sets the sequence under key to exactly values, creating the key if it is absent and replacing it otherwise, via a yaml.Node round-trip that preserves comments and every untouched key (ADR-0026). Used where the whole list is computed rather than edited member-by-member - the targets array carries a Load default, so an absent on-disk key must be materialized as the full resolved list, not appended to (ADR-0037).
func SetArrayMember ¶
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
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
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
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
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). It is the top-level string sibling of SetArray (sequence): 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
ValidateArtifactName reports whether name is usable as a local skill/agent name (ADR-0068): non-empty lowercase kebab-case (letters, digits, hyphens). The charset is frontmatter-safe - it excludes the path separators and ".." the invariant requires, awf's reserved "_" namespace, and the colon/space/quote characters that would otherwise interpolate into the base template's name: line and break its YAML frontmatter. It mirrors every catalog artifact's naming. touches-state: config/validation:local-name-validated - local skill/agent name charset validation; proof in config_test.go
func ValidateDocName ¶ added in v0.15.0
ValidateDocName validates a path-aware local doc name (ADR-0091): one or more lowercase-kebab segments joined by "/", rejecting a path escape, an empty or leading/trailing segment, a ".md" suffix, and any segment (e.g. the reserved "_base" stem) carrying a non-kebab character. Skill/agent names stay flat. touches-state: config/validation:local-doc-name-path-validated - path-aware local doc name validation; proof in docname_test.go
func ValidateDomainName ¶
ValidateDomainName reports whether name is a usable domain key: non-empty and free of path separators or "..". Shared by Validate and the `awf enable domain` path so a freeform domain name is rejected the same way in both.
Types ¶
type AuditConfig ¶
type AuditConfig struct {
AllowedTypes []string `yaml:"allowedTypes"`
AllowedScopes []ScopeSpec `yaml:"allowedScopes"`
SubjectMaxLength *int `yaml:"subjectMaxLength"`
DependencyManifests []string `yaml:"dependencyManifests"`
DiffThreshold *int `yaml:"diffThreshold"`
DomainDocStaleness *bool `yaml:"domainDocStaleness"`
DomainCodeStaleness *bool `yaml:"domainCodeStaleness"`
UndocumentedDomain *bool `yaml:"undocumentedDomain"`
PlainPunctuation *bool `yaml:"plainPunctuation"`
UncommittedChanges *bool `yaml:"uncommittedChanges"`
}
AuditConfig tunes `awf audit` (ADR-0017). A nil *AuditConfig means all defaults; within it, a nil slice means "use the default", an explicit empty slice means "accept any / disabled" per field. Resolution and defaults live in internal/audit (audit.Resolve), which owns the audit domain semantics.
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 CatalogTrim ¶
CatalogTrim optionally overrides which catalog skills/docs a scaffolded config enables (ADR-0029 catalog trim). A nil *CatalogTrim - or a nil dimension within it - means "no selection: keep the curated-core default"; a non-nil dimension is the verbatim, fully-deselectable enable set (an empty slice deselects all).
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
CommitPolicyIdentity is one exact author/committer name and email pair.
type CommitPolicySigner ¶ added in v0.30.0
CommitPolicySigner is one SSH signing principal and public key pair.
type Config ¶
type Config struct {
Prefix string `yaml:"prefix"`
DocsDir string `yaml:"docsDir"`
// 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"`
Skills []string `yaml:"skills"`
Agents []string `yaml:"agents"`
Docs []string `yaml:"docs"`
Domains []string `yaml:"domains"`
Tags map[string]string `yaml:"tags"`
ContextIgnore []string `yaml:"contextIgnore"`
Targets []string `yaml:"targets"`
CurrentState *CurrentStateConfig `yaml:"currentState"`
Audit *AuditConfig `yaml:"audit"`
Bootstrap *BootstrapConfig `yaml:"bootstrap"`
Hooks *HooksConfig `yaml:"hooks"`
Runner *RunnerConfig `yaml:"runner"`
ProseGate *ProseGateConfig `yaml:"proseGate"`
MemoryCite *MemoryCiteConfig `yaml:"memoryCite"`
CommitPolicy *CommitPolicyConfig `yaml:"commitPolicy"`
// contains filtered or unexported fields
}
Config is the skeleton config.yaml: global fields plus flat enable arrays. Presence of a name in Skills/Agents/Docs enables that artifact; per-artifact data/sections/local live in sidecars, not here. Targets is the adapter-runtime enable array (default ["claude"]); adapter artifacts render once per entry.
func Load ¶
Load reads <awfDir>/config.yaml with the strict decoder, records awfDir as the sidecar/part resolution root, and defaults DocsDir.
func Parse ¶ added in v0.18.0
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
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) ReadPart ¶ added in v0.22.0
ReadPart returns selected-universe convention-part bytes.
func (*Config) ReadPartPath ¶ added in v0.22.0
ReadPartPath reads a consumed absolute part path through the selected reader.
func (*Config) ReadSidecar ¶ added in v0.22.0
ReadSidecar returns selected-universe sidecar bytes by config-relative path.
func (*Config) Sidecar ¶
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).
type CurrentStateConfig ¶ added in v0.18.0
type CurrentStateConfig struct {
Sources []CurrentStateSource `yaml:"sources"`
TestGlobs []string `yaml:"testGlobs"`
MaxTopicsPerPath *int `yaml:"maxTopicsPerPath"`
}
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) EffectiveMaxTopicsPerPath ¶ added in v0.18.0
func (c *CurrentStateConfig) EffectiveMaxTopicsPerPath() int
EffectiveMaxTopicsPerPath returns the configured fan-out budget, defaulting to eight without materializing that default into the decoded config.
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
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 HooksConfig ¶ added in v0.6.0
type HooksConfig struct {
Enabled bool `yaml:"enabled"`
}
HooksConfig configures the rendered .awf/hooks/ payload singleton (ADR-0048): three inert git-hook payload scripts adopters wire into hook setups they own. BootstrapConfig semantics: a nil *HooksConfig (key absent) and Enabled false both mean "do not render"; only Enabled true renders the payloads. The key reuses the name the schema-4 drop-hooks migration stripped (ADR-0032); the legacy array shape never reaches this struct - gated commands migrate first, ungated ones fail loudly on the strict parser's type error.
type MemoryCiteConfig ¶ added in v0.30.0
type MemoryCiteConfig struct {
Enabled bool `yaml:"enabled"`
Exemptions []MemoryExemption `yaml:"exemptions"`
}
MemoryCiteConfig configures `awf check repo memory` (ADR-0158): a scan of the staged decision-record directories, and of the commit-message body, for a citation of a specific working-memory file. ProseGateConfig semantics: a nil *MemoryCiteConfig (key absent) and Enabled false both mean "the scan does not run". The default is off because the scan blocks a commit, and a corpus that has never been swept would fail it on the day it lands.
type MemoryExemption ¶ added in v0.30.0
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 {
Enabled bool `yaml:"enabled"`
Exemptions []ProseExemption `yaml:"exemptions"`
}
ProseGateConfig configures `awf check repo prose` (ADR-0119): a presence-level scan of every tracked text file for the seven banned typographic punctuation substitutes. BootstrapConfig semantics: a nil *ProseGateConfig (key absent) and Enabled false both mean "the command exits zero without scanning". The default is off because the scan blocks a commit, and a tree that has never been swept would fail it on the day it lands.
type RunnerConfig ¶ added in v0.18.0
type RunnerConfig struct {
Enabled bool `yaml:"enabled"`
}
RunnerConfig configures the rendered runner singleton (ADR-0156): a pure, fully awf-owned wrapper `awf` at the repo root that resolves one awf invocation (vars.awfInvokeCmd, else bootstrap-pinned, else PATH awf) and execs it with all arguments forwarded verbatim. Like the bootstrap/hooks toggles, a nil *RunnerConfig (key absent) and Enabled false both mean "do not render"; only Enabled true renders the wrapper. Default-on by seeding: `awf init` scaffolds the key true and the schema-18 enable-runner migration seeds an absent key to enabled on `awf upgrade` (an explicit false is respected); at render time an absent key still renders nothing.
type ScopeSpec ¶ added in v0.8.0
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).
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"`
Sections map[string]SectionOverride `yaml:"sections"`
Local bool `yaml:"local"`
// 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, per-section overrides, and the local flag. 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"`
Skills []string `yaml:"skills"`
Agents []string `yaml:"agents"`
Docs []string `yaml:"docs"`
Audit *SkeletonAudit `yaml:"audit,omitempty"`
Bootstrap *BootstrapConfig `yaml:"bootstrap,omitempty"`
Hooks *HooksConfig `yaml:"hooks,omitempty"`
Runner *RunnerConfig `yaml:"runner,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.