# Consumer Adoption Guide ConfigKit removes parser setup, strict decoding, presence bookkeeping, import and selected-overlay traversal, precedence, provenance, secret lookup, and safe inspection from applications. Consumers still own their configuration structs, defaults, semantic validation, schema-version policy, CLI parsing, runtime matching and conversion, resource checks, remote acquisition, and service initialization. The maintained [`examples/consumers`](../../examples/consumers) package demonstrates sanitized shapes using only ConfigKit and the Go standard library: | Shape | Demonstrated fit | Application code deliberately retained | | --- | --- | --- | | Narratio-like | Fresh defaults, explicit partial imports, artifact maps, recorded inherited defaults, normalization, and source ownership | Artifact meaning and the separate `model_profile` domain field | | Notarius-like | Canonical object bindings, a string-backed schema version accepting string or integer YAML, strict dynamic options, and `int64` dynamic integers | Catalog selection, allowed option keys, range-checked conversion to runtime `int` | | WeatherReporter-like | Defaults-only loading, report maps, canonical module objects, typed option unions, explicit CLI records, and typed secret startup | Report/module compatibility, rejection of explicitly empty domain lists, CLI parsing, and startup ordering | | Seriatim-like | Independent strict ordered speaker and autocorrect rule files | First-match and ordered replacement algorithms; no central application configuration file | | Bundled file | Simple native `fs.FS` source through ordinary `Load` | Selection and lifetime of the application-owned embedded filesystem | | Layered catalog | Embedded defaults with an import, a mapping underlay, an independent host main file, explicit false/zero input, and source-relative resource access | Domain validation and opening/interpreting the template resource | | Mounted collections | Entry-declared relative destinations, nested metadata/prompt/profile imports, local inheritance, and a version-only higher-tier override | Collection IDs, profile meaning, and opening/interpreting prompt resources | | Named presets | One-load sibling inheritance, explicit false, nested destination resolution, and source/derivation inspection | Choosing and executing a preset | | Effective configuration diff | Same-type frozen effective comparisons for two selected files or base-versus-overlay impact | Source/options selection and consumer-owned safe presentation | | Shared source-version checks | One `Definition.Version` policy for accepted declarations across participating source documents, with optional or required declarations and sanitized evidence | Choosing which declarations the application accepts; final assembled schema and domain validation | | Sequence catalogs | Declared collection operations, record inheritance, typed CLI and resolution, inspection, exact and wildcard application registrations | Catalog meaning and final domain validation | The YAML under [`testdata`](../../examples/consumers/testdata) is loaded by the executable examples and ordinary tests. It contains no private application data, credentials, network calls, or dependency on a sibling checkout. The Notarius-shaped fixtures deliberately load both `version: 4` and `version: "4"` into the same string-backed application field. Its version checker still owns the supported identifier; fractional numeric input and unsupported integer identifiers remain negative examples. ## Happy path An application defines ordinary types and passes one explicit source. Optional features appear only when the application needs them: ```go result, err := configkit.Load[Config](configkit.File(path), configkit.Options[Config]{ Definition: rules, Imports: useDeclaredImports, Overlay: selectedConfigOverlay, Overrides: explicitlySetFlags, }) if err != nil { return err } return start(result.Config) ``` Simple consumers use neither imports nor selected overlays. A complex consumer can opt into both without redefining their syntax. ConfigKit overlays are configuration overlays; they do not select or replace an application's LLM, model, pipeline, report, or execution profile concepts. ## Explicit main-file discovery Applications choose the search conventions and their precedence, then pass the resulting `SearchConfig` source to the same `Load` or `LoadLayered` entry point. For example, the maintained sequence catalog puts a caller-selected file before a custom directory fallback while keeping its underlay and overlay choices independent. See [`sequence_catalog.go`](../../examples/consumers/sequence_catalog.go) and its [behavior tests](../../examples/consumers/sequence_catalog_test.go), plus the [working-directory executable example](../../examples/consumers/discovery_test.go). Together, the root-owned [discovery API](../../discovery.go) and [configuration discovery contract](../config.md#configuration-discovery) define ordered concatenation, directory-first Cartesian expansion, optional exhaustion, and selected-file behavior. [ADR 0019](../adr/0019-explicit-configuration-discovery.md) records why discovery is explicitly selected. ## Import defaults and selection inspection `Options.ImportDefaults` and `LayeredOptions.ImportDefaults` apply one load-wide policy to enabled main, underlay, and transitive imports. Each field is resolved independently: a declaration's explicit value wins, then the application default, then the library default. `Required`, `Follow`, `Hidden`, and `Recursive` use nil pointers for unspecified values. `Pattern` applies to directory searches: nil leaves filenames unrestricted, while a non-nil empty slice selects no files. A declaration's own `pattern` replaces the application default. The defaults do not enable imports or change selected-overlay behavior. `Result.Sources`, `LoadError.Sources`, and `Resolution.Source` expose each declaring source's `ImportSelections`. These records identify the source-local declaration path, its authored location and selector, and the `none` outcome; they do not include candidate names or a guessed reason for absence. Returned records are detached snapshots. The [configuration contract](../config.md#explicit-imports) owns selector applicability and exact absence semantics. ## Shared source-version checks [`LoadVersionedService`](../../examples/consumers/source_version.go) demonstrates if-present and required modes with one `Definition.Version` rule across a mounted main input and an underlay. The example reads only sanitized `SourceInfo.VersionCheck` fields; its executable test is in [`layered_test.go`](../../examples/consumers/layered_test.go). The [configuration contract](../config.md#shared-source-version-checks) defines participation, source-local lookup and diagnostics, non-main header consumption, main-path protection, default behavior, and evidence disclosure. Accepted declarations do not replace final assembled decoding or application validation. ## Effective configuration diffs [`CompareFiles`](../../examples/consumers/diff.go) loads two caller-selected files with one application definition, handles each load error, calls `configkit.Diff`, and presents the safe records through consumer-owned `FormatDifferences`. [`CompareRolloutOverlay`](../../examples/consumers/diff.go) loads the same base once without an overlay and once with the selected overlay, so omitted lower priority values remain present. Its resolver records a derived endpoint URL after overlay application, and its nested typed secret reference is classified before the resulting records are presented. The maintained [behavior tests and executable examples](../../examples/consumers/diff_test.go) use temporary files and disposable secret values. Presenters should use `ValueSnapshot.Present` for absence, record membership for change, and `HiddenChanged` for a withheld change indicator. Do not parse the redaction marker or add separately rendered full contexts that lack combined sensitivity. The [configuration contract](../config.md#effective-configuration-diffs) owns shared equality, granularity, and disclosure semantics. ## Named sibling inheritance [`LoadPresets`](../../examples/consumers/inheritance.go) loads the maintained [`presets.yml`](../../examples/consumers/testdata/inheritance/presets.yml) in one call. `custom` inherits `enabled: false` from `base`, while its nested `settings.primary` changes the inherited `settings.secondary` result at the custom destination. The application struct has no `extends` field; ConfigKit consumes the relationship before strict typed decoding. The [executable example](../../examples/consumers/consumers_test.go) reads `Metadata.IsExplicit` for the inherited false value, then follows `Origin.Inheritance` back to the parent path while `Origin.Locations` identifies the declaring source. Applications still choose which preset to execute and validate domain meaning. The [configuration contract](../config.md#sibling-inheritance) owns exact lookup, precedence, cancellation, and migration rules. ## Native filesystem and layered input [`LoadBundled`](../../examples/consumers/layered.go) keeps the simple `Load` shape: it selects a file in an application-owned embedded filesystem and needs no underlay setup. [`LoadCatalog`](../../examples/consumers/layered.go) uses `LoadLayered` with an imported embedded-defaults group, a separate `Mapping` group, and a host main file supplied by its caller. The groups compose strictly within themselves and recursively across precedence boundaries; the main file's explicit `false` and `0` replace lower values without being mistaken for omissions. Its validator owns allowed modes and resource-name policy. [`ReadWelcome`](../../examples/consumers/layered.go) follows the effective field's origin location to a source ID, queries `Result.SourceContext`, and opens the resource relative to that context's `BaseDir`. It does not assume the main file owns a retained underlay value. A composite origin needs an application choice; ConfigKit does not guess a directory. The context query is passive, while later resource reads observe the caller-owned filesystem or a freshly opened host root and may fail independently of the completed configuration load. The [configuration contract](../config.md#source-resource-contexts) owns exact lifetime and precedence rules. [`LoadPromptBundle`](../../examples/consumers/layered.go) is the ordinary-root counterpart: its entry opts into imports through `Load`, imports metadata at the root, and places a prompt beneath `prompts` using YAML. The executable example and behavior test are in [`layered_test.go`](../../examples/consumers/layered_test.go). ## Input destinations and mounted collections Main and underlay inputs use the same destination field. An import without a `destination` inherits its declaring document's effective destination; an explicit sequence of key strings appends a relative suffix. Nested imports append to their immediate parent's effective destination, while filesystem paths remain relative to the declaring file. Destinations are application mapping paths, independent of filesystem context. The configuration contract owns the complete behavior and validation rules. The small [`Example_inputDestination`](../../examples/consumers/destinations_test.go) mounts one mapping source beneath literal collection/prompt keys and loads a second input without a destination to show that root placement remains the default. Consumers pass paths, not filenames or namespace strings for ConfigKit to interpret. [`LoadCollectionBundle`](../../examples/consumers/destinations.go) assembles an embedded collection manifest mounted beneath a literal collection path. The entry's YAML places nested metadata imports at the collection root and prompt and profile peers beneath their own keys. A higher-priority input changes only one prompt's ordinary `version` field. The main document alone supplies the application schema version. A prompt's local `extends` relationship resolves after all tiers, so `custom` inherits the final `brief` content and fields while retaining its own profile selection. The resolver follows each effective `content_file` origin to its declaring source context. This also works for `custom`'s inherited resource. The application-owned [`ReadCollectionPromptContent`](../../examples/consumers/destinations.go) demonstrates the same lookup after loading; ConfigKit does not open or interpret the resource. [`destinations_test.go`](../../examples/consumers/destinations_test.go) checks source destinations and authored coordinates, presence, inherited values, relative resources, and strict peer conflicts. The [configuration contract](../config.md#input-destinations) owns shared placement, import, source-location, and ownership behavior. ## Identified prompt sequences [`LoadPromptCatalog`](../../examples/consumers/prompt_catalog.go) shows the smaller import-focused adoption path: a registered `prompts.id` identity, directory and glob selection, a helper import that supplies an item's ID, and resource lookup through the effective source context. Its executable examples and tests are in [`prompt_catalog_test.go`](../../examples/consumers/prompt_catalog_test.go), with complete [catalog](../../examples/consumers/testdata/prompt-catalog/config.yml) and [required-file](../../examples/consumers/testdata/prompt-catalog/simple.yml) fixtures. [`LoadSequenceCatalog`](../../examples/consumers/sequence_catalog.go) integrates an underlay declaration, imported main fragments, identified-record inheritance, selected overlay `append`, `upsert`, and `replace`, a typed CLI override, and recorded application resolution. Its tests and runnable example demonstrate stable output order, whole-record upsert, provenance/history inspection, resource lookup for inherited records, and secret-safe explanation in [`sequence_catalog_test.go`](../../examples/consumers/sequence_catalog_test.go). [`LoadNamespacedMapCatalog`](../../examples/consumers/sequence_catalog.go) adds an exact root registration and a wildcard namespace registration, showing that each concrete sequence has an independent identity scope. The same tests show that a YAML identity declaration without an application registration does not create final-result identity enforcement. The examples keep prompt meaning and domain checks in the consuming application. The [configuration contract](../config.md#strict-document-composition) owns declaration, identity, operation, ownership, and final-validation semantics; [sibling inheritance](../config.md#sibling-inheritance) defines record relationships, and the [CLI contract](../config.md#explicit-cli-overrides) owns override ordering. Known module catalogs should use canonical object entries with a module identifier and typed options. A small union struct is sufficient when the available option fields are known; validation selects which fields apply to each module. Runtime- selected catalogs may retain `map[string]any`, whose integers are `int64`; applications validate keys and ranges and convert only at their runtime boundary. Neither shape needs YAML reserialization, a parser hook, a ConfigKit registry, or a generic subtree conversion API. ## Source-aware application path policy [`LoadNotariusPaths`](../../examples/consumers/consumers.go) is the maintained path-bearing example. Its application resolver uses `Resolution.InputInfo` and `Resolution.Source` before normalizing: relative CLI input uses an explicitly supplied invocation directory, retained defaults use the canonical main source, and a file-authored scalar uses its one declaring source. Absolute paths remain unchanged. Its executable test covers the matching effective YAML and explanation history, source-file ownership, and a symlink opened through an alias; canonical target-file directories, rather than the alias directory or process working directory, are the selected bases. The selected example policy leaves a relative value unchanged and emits a warning when a CLI base is absent or file provenance is missing or composite. That is an application choice, not a ConfigKit fallback: consumers can instead reject it or choose another explicit base. The example intentionally performs neither target existence checks nor confinement checks. ## Intentional migration changes The examples preserve domain meaning while adopting one configuration convention: - Explicit empty strings, lists, zeroes, false values, and nulls remain intentional. ConfigKit accepts an empty list structurally; WeatherReporter-shaped validation rejects an explicitly or programmatically supplied empty module/template list as a domain rule. Loaded and constructed values use the same validator. - Canonical module and object bindings replace string shorthand. Unknown fields and incompatible option shapes fail at their semantic paths. - Schema versions remain string-backed in application types. A configured `VersionRule` accepts string or strict decimal-integer YAML at its exact path and supplies the spelling to both the checker and a plain string destination. - Integers inside dynamic options are portable `int64` values. Runtime `int` conversion is explicit and range checked. - Strict YAML intentionally rejects duplicate keys, aliases, merge keys, unsupported tags/scalars, trailing documents, and unknown typed fields. Diagnostics expose stable classifications and safe paths, not copied parser text. - General environment-variable configuration overlays retire. Application CLI parsers translate only explicitly set configuration flags into `Override` records. Separate invocation state remains outside configuration. - Credential fields migrate to environment-name `Secret` references. Applications explicitly call `LoadSecretsDir` before `Load` when they choose file bootstrap; blocked empty variables remain blocked, and ordinary `Load` never changes the process environment. ### Selected-overlay migration To adopt the current selected-overlay API, rename `Options.Profile`, `LayerProfile`, and `CodeProfile*` references to their `Overlay` equivalents; move selected files from `profiles/.yml` to `overlays/.yml`; update any handling of the `overlay` layer and `overlay.*` diagnostic strings; then rebuild. The [configuration contract](../config.md#selected-overlays) is the canonical behavioral reference. Application-owned profile concepts, such as `model_profile`, are unchanged, and any CLI flag rename remains the application's decision. These examples are migration evidence, not completed changes to Narratio, Notarius, WeatherReporter, or Seriatim. Updating those repositories, their deployed YAML, CLI wiring, runtime catalogs, and operational documentation remains separate downstream work. ## Maintained API examples Every nontrivial initial API sketch has a compiled example or a focused behavioral owner: | Roadmap surface | Maintained owner | | --- | --- | | `Load`, `NoSource`, validation, versions, resolution, `SecretEnv`, overrides, and explanation | Executable examples in [`example_test.go`](../../example_test.go) | | Source/import/selected-overlay pipeline rules and artifact defaults | Narratio-shaped executable example and fixture | | Canonical module objects and dynamic option handling | WeatherReporter- and Notarius-shaped examples and migration tests | | `LoadSecretsDir`, bootstrap reporting, typed lookup, CLI explanation | `Example_weatherStartup` | | Native filesystem, grouped underlays, programmatic mapping, retained resource context | `Example_nativeFilesystem`, `Example_layeredCatalog`, and their focused consumer tests | | Literal input destinations, root-default compatibility, and mounted peer collections | `Example_inputDestination`, `LoadCollectionBundle`, and `destinations_test.go` | | Named sibling inheritance, presence, and derivation | `Example_inheritedPresets` and `TestPresetCatalogOneLoadAndDerivation` | | Effective configuration diffs and consumer-owned presentation | `ExampleDiff`, `ExampleCompareFiles`, and `ExampleCompareRolloutOverlay` | | Results, metadata, diagnostics, effective output, and writer failures | Focused root-package load, provenance, rendering, and explanation tests | The package-level import-boundary test covers the consumer examples too: supported usage names no YAML dependency, parser type, parser option, or parser error chain.