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 ConvertInvariantsToCurrentState(src []byte) ([]byte, 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 SetMappingScalar(src []byte, key, child string, value bool) ([]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 Config
- type CurrentStateConfig
- type CurrentStateSource
- type GlobRewrite
- type HooksConfig
- type InvariantConfig
- type InvariantSource
- type ProseExemption
- type ProseGateConfig
- type RunnerConfig
- type ScopeSpec
- type SectionOverride
- type Sidecar
- type Skeleton
- type SkeletonAudit
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 ConvertInvariantsToCurrentState ¶ added in v0.18.0
ConvertInvariantsToCurrentState plans the bridge preparation conversion while retaining the yaml.Node representation of every copied field and all unrelated comments and key order. It never weakens an explicit legacy opt-out.
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-invariant: config-mutation-roundtrip - yaml.Node add/remove round-trip; proof in edit_test.go
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 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-invariant: 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-invariant: 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 Config ¶
type Config struct {
Prefix string `yaml:"prefix"`
DocsDir string `yaml:"docsDir"`
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"`
Invariants *InvariantConfig `yaml:"invariants"`
CurrentState *CurrentStateConfig `yaml:"currentState"`
Audit *AuditConfig `yaml:"audit"`
Bootstrap *BootstrapConfig `yaml:"bootstrap"`
Hooks *HooksConfig `yaml:"hooks"`
Runner *RunnerConfig `yaml:"runner"`
ProseGate *ProseGateConfig `yaml:"proseGate"`
// 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 (*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) 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"`
TopicCoverage string `yaml:"topicCoverage"`
TopicFanout string `yaml:"topicFanout"`
MaxTopicsPerPath *int `yaml:"maxTopicsPerPath"`
// contains filtered or unexported fields
}
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 retains severity presence while preserving 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 InvariantConfig ¶
type InvariantConfig struct {
Disabled bool `yaml:"disabled"`
Sources []InvariantSource `yaml:"sources"`
TestGlobs []string `yaml:"testGlobs"`
}
InvariantConfig configures language-agnostic invariant backing. A nil *InvariantConfig (key absent) means "unchecked"; Disabled is the explicit opt-out; a non-empty Sources enables enforcement.
TestGlobs scopes the proof `invariant:` marker to test files (ADR-0105): when non-empty, a proof marker backs a slug only in a file matching one of these anchored globs; when empty or absent, backing falls back to source-glob scope (the pre-ADR-0105 semantics). TestGlobs is an inert optional field within the current schema - an absent value degrades to the fallback, so it needs no schema-generation bump.
type InvariantSource ¶
type InvariantSource struct {
Globs []string `yaml:"globs"`
Marker string `yaml:"marker"`
Close string `yaml:"close"`
}
InvariantSource pairs anchored path globs (ADR-0077; matched against a file's slash-separated repo-relative path) with the literal comment marker that prefixes a backing `invariant: <slug>` tag. Close is the optional literal close token for block-comment markers (`-->`, `*/`): when non-empty, one trailing token (plus surrounding whitespace) is stripped from a matched marker line before tag parsing (ADR-0121). Additive and optional - empty means no stripping - so no schema-generation bump.
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 prose-gate` (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 command-runner singleton `x` (ADR-0101): a co-owned file (ADR-0100) whose awf-verb dispatch awf owns and whose project verbs live in in-place-editable sections the adopter fills. Like the bootstrap/hooks toggles, a nil *RunnerConfig (key absent) and Enabled false both mean "do not render"; only Enabled true renders the runner. Additive and default-off - no schema-generation migration, and adopters opt in explicitly.
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-invariant: 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"`
Vars map[string]string `yaml:"vars"`
Skills []string `yaml:"skills"`
Agents []string `yaml:"agents"`
Docs []string `yaml:"docs"`
Audit *SkeletonAudit `yaml:"audit,omitempty"`
Invariants *InvariantConfig `yaml:"invariants,omitempty"`
Bootstrap *BootstrapConfig `yaml:"bootstrap,omitempty"`
Hooks *HooksConfig `yaml:"hooks,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.