225 lines
13 KiB
Markdown
225 lines
13 KiB
Markdown
# Architecture Policy
|
|
|
|
**Base Policy Version:** 1.3
|
|
|
|
**Last Updated**: 2026-09-29
|
|
|
|
**Upstream Source:** [Standard Repository Policies](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/architecture.md)
|
|
|
|
## Purpose And Ownership
|
|
|
|
The adopted policy defines the application's purpose and the architectural
|
|
constraints contributors must follow. It owns system boundaries, responsibility
|
|
and dependency rules, established invariants, and explicit non-goals.
|
|
|
|
Describe the concrete package inventory and implementation mechanics in internal
|
|
documentation. Link to canonical public contracts for their exact behavior and
|
|
to ADRs for decision rationale.
|
|
|
|
ConfigKit is an opinionated Go library for consistent YAML configuration loading
|
|
and resolution across consuming applications. The [component overview](../internal/overview.md)
|
|
owns implementation inventory, the [configuration contract](../config.md) owns
|
|
implemented behavior, and the [deferred scope](../roadmap/deferred.md) preserves
|
|
deferred scope.
|
|
|
|
## Adopted Constraints And Implemented Structure
|
|
|
|
Architectural constraints govern current and future development. Record
|
|
implemented structure accurately, proposals and implementation status in the
|
|
roadmap, and significant decisions and rationale in ADRs. Describe a target
|
|
structure as implemented when its changes land.
|
|
|
|
Constraints must reflect deliberately selected requirements and decisions.
|
|
Do not promote incidental implementation choices into architectural invariants.
|
|
|
|
## Success Criteria
|
|
|
|
ConfigKit prioritizes a coherent configuration model: consistency and
|
|
predictability take precedence over flexibility for accommodating individual
|
|
downstream conventions. Consumers should learn one set of rules, not different
|
|
combination or presence semantics depending on the source format or consuming
|
|
application. Distinct operations such as strict composition and intentional
|
|
overlay express distinct purposes within that model.
|
|
|
|
Consumers should be able to load once and receive a complete, validated
|
|
configuration with inspectable provenance for its final values, including
|
|
defaults and recorded application-derived values. Configuration and metadata
|
|
together must distinguish explicit zero, false, empty, and null input from
|
|
omission; ordinary Go values alone need not encode those distinctions.
|
|
Application-owned resolution belongs within the recorded loading lifecycle, so
|
|
consumers need no subsequent generic merging, defaulting, or provenance
|
|
reconstruction. This does not transfer domain execution or runtime resource
|
|
management into ConfigKit.
|
|
|
|
1. **Reduce application infrastructure:** Can applications delete substantial
|
|
configuration infrastructure while retaining clear ownership of their domain
|
|
rules?
|
|
2. **Unify configuration conventions:** Do consumers adopt one consistent
|
|
configuration model, with only the flexibility needed to express genuine
|
|
application requirements?
|
|
|
|
Both criteria must hold. Minimize consumer loading and processing code subject
|
|
to the consistency, presence, and provenance principles above, rather than
|
|
trading those principles for consumer-specific shortcuts. ConfigKit should not
|
|
preserve divergent consumer conventions merely for compatibility, nor impose
|
|
restrictions that prevent useful
|
|
domain information from being expressed. Prefer a canonical representation using
|
|
existing primitives before adding another mechanism. Extend the model when a
|
|
concrete requirement cannot be accommodated cleanly, not simply because a
|
|
consumer already implements a different convention.
|
|
|
|
Use both criteria to evaluate public API changes and consumer adoption. Shared
|
|
loading, presence tracking, source attribution, and diagnostic plumbing should
|
|
move out of applications; application types, defaults, normalization, and semantic
|
|
validation should remain recognizable application code. Replacing that plumbing
|
|
with equally repetitive callback bookkeeping does not meet the first criterion.
|
|
Offering alternate parsing, precedence, or composition conventions through escape
|
|
hatches does not meet the second.
|
|
|
|
Consumer migrations are expected and may intentionally change configuration
|
|
syntax. Existing consumer code supplies implementation foundations and evidence
|
|
of needs; it does not define ConfigKit's compatibility obligations. Once ConfigKit
|
|
publishes its own contracts, changes to those contracts remain governed by the
|
|
[release policy](release.md).
|
|
|
|
Require representative simple and complex consumer examples to substantiate API
|
|
designs. Proposed extensions must identify the domain requirement, explain why
|
|
existing primitives cannot accommodate it cleanly, and show how the extension
|
|
preserves a coherent model. Application-specific interpretation and validation
|
|
remain appropriate. Reuse Narratio's applicable infrastructure under these
|
|
boundaries, and keep simple consumers independent of optional composition
|
|
features. The [consumer adoption guide](../consumers/README.md) demonstrates the
|
|
implemented surface against representative simple and complex shapes.
|
|
|
|
## Responsibility Boundaries
|
|
|
|
ConfigKit owns configuration input processing and the coordination of decoding,
|
|
resolution, validation, and inspection. Applications own their configuration
|
|
types, field meanings, defaults, normalization, semantic validation, and schema
|
|
version policy. Applications also own main-file search conventions, CLI wiring,
|
|
remote acquisition, runtime initialization, and resource checks.
|
|
|
|
Library code must not depend on consuming applications or initialize their
|
|
runtime resources. It must not perform implicit network access, discover
|
|
application-specific paths, or change process-global state during ordinary `Load`.
|
|
The explicit secrets-directory bootstrap is a narrow, opt-in exception: applications
|
|
invoke it during startup, and it reports writes, skips and partial failures. It
|
|
must never run implicitly as part of configuration loading.
|
|
Remote data can enter through application-supplied input without introducing a
|
|
network provider into the library.
|
|
|
|
## Loading Lifecycle
|
|
|
|
Configuration loading is a synchronous, complete-on-return operation. A successful
|
|
load provides the final configuration and inspection metadata after the configured
|
|
processing and validation phases complete. A failed load reports diagnostics and
|
|
returns no usable partial result. The normal application pattern is one load near
|
|
startup; applications may explicitly initiate additional loads.
|
|
|
|
ConfigKit releases its library-owned loading handles before returning and remains
|
|
inactive until the application explicitly invokes it again. It performs no
|
|
background watching, refresh, or mutation of published results. Caller-owned
|
|
resources and application callback effects remain the caller's responsibility.
|
|
The [configuration contract](../config.md#completion-and-resource-lifetime) owns
|
|
the precise completion and ownership behavior. The proposed decision record is
|
|
[ADR 0008](../adr/0008-complete-on-return-loading.md).
|
|
|
|
## Public API And Dependency Direction
|
|
|
|
Keep one small, coherent public surface for the supported lifecycle. Simple
|
|
single-document consumers must not need import, overlay, or provider machinery.
|
|
Do not expose parser-specific nodes, errors, or extension hooks through the
|
|
public API. Parser integration belongs behind a private boundary, as recorded
|
|
in [ADR 0002](../adr/0002-own-yaml-semantics.md).
|
|
|
|
The root `configkit` package is the sole supported consumer import path. Public
|
|
named types stay root-owned and may hold private delegated implementation state;
|
|
their exported signatures, fields, and method sets must not expose internal
|
|
types or acquire internal methods through anonymous embedding. Root production
|
|
code may depend inward on a cohesive engine and its supporting internal packages,
|
|
but internal production packages must not import the root. Explicit forwarding
|
|
and local conversion are acceptable where they preserve type identity,
|
|
ownership, callback lifetime, and inspection behavior. The accepted root-wrapper
|
|
boundary and its migration rationale are recorded in
|
|
[ADR 0006](../adr/0006-public-facade.md); the implemented layout is described in
|
|
the [component overview](../internal/overview.md).
|
|
|
|
Separate input access, document processing, composition, decoding, metadata, and
|
|
rendering responsibilities without prescribing an interface or package for each.
|
|
Dependencies must preserve those boundaries; package splits and abstractions
|
|
must earn their complexity. Do not impose a full application or hexagonal
|
|
framework on this library.
|
|
|
|
## Configuration Integrity
|
|
|
|
Within a configuration tier, independent contributions use strict composition:
|
|
shared mappings combine recursively, while overlapping non-mapping definitions
|
|
are conflicts, including equal values. Between precedence tiers, intentional
|
|
overlay combines mappings recursively, retains omitted lower-priority children,
|
|
and replaces other present values according to the existing overlay rules.
|
|
Tier precedence is explicit; traversal order within a tier establishes stable
|
|
reporting, not permission to override. The distinction and its rationale are
|
|
recorded in [ADR 0001](../adr/0001-separate-composition-and-overlay.md).
|
|
|
|
The main document and its imports form a strictly composed tier. Explicit
|
|
underlay groups compose strictly within each group, then combine recursively
|
|
from lowest to highest beneath the main tier. Source acquisition kind does not
|
|
determine its precedence role. Input-specific constraints, including CLI path
|
|
rules and opt-in version ownership, remain part of the configuration contract. Consistent combination
|
|
semantics do not require identical acquisition or lifetime behavior from every
|
|
source kind.
|
|
|
|
ConfigKit must define supported YAML semantics explicitly rather than inherit
|
|
parser defaults. Processing order and diagnostics must be deterministic for the
|
|
same inputs and options. Invalid input must not produce a successful usable
|
|
configuration. Do not run application hooks on invalid typed configuration.
|
|
|
|
User-controlled precedence is application defaults, ordered underlays, composed
|
|
main input, selected overlay, then explicitly supplied CLI overrides.
|
|
Applications own flag parsing; ConfigKit owns overlay semantics and provenance.
|
|
Domain resolution follows these
|
|
inputs and must remain recorded, not become a hidden override channel.
|
|
|
|
Preserve node-based composition until the effective input is ready for typed
|
|
decoding. Preserve presence and source information through processing. Ordinary Go zero
|
|
values must not be used to infer whether input was absent. Final configuration,
|
|
metadata, and effective inspection must agree, including application resolution
|
|
changes; see [ADR 0003](../adr/0003-preserve-presence-and-resolution-history.md).
|
|
Safe historical values and an explanation renderer are part of the implemented contract,
|
|
including superseded/deleted paths and effective source attribution. Inspection
|
|
must agree with the final typed snapshot and apply historical sensitivity rules.
|
|
See [ADR 0005](../adr/0005-explicit-overrides-and-safe-explanation.md).
|
|
Detailed syntax and operation tables belong to the
|
|
[current configuration contract](../config.md).
|
|
|
|
## Ownership And Confidentiality
|
|
|
|
Returned configuration and metadata must not share mutable state across loads.
|
|
ConfigKit must not mutate a result after returning it. Ordinary exported Go
|
|
values are not language-enforced immutable objects; document caller ownership
|
|
and the lifetime of captured metadata without promising otherwise.
|
|
|
|
Secret values enter configuration through typed environment-name references;
|
|
literal credentials are not a supported configuration convention. Only an explicit
|
|
accessor exposes a resolved credential to application code. Credential bytes must
|
|
never enter composition trees, history, or inspection. General environment-variable
|
|
configuration overlays are an explicit non-goal. Bootstrap must preserve already
|
|
set variables, including empty ones, and report blocked files without contents.
|
|
See [ADR 0004](../adr/0004-secret-references-and-explicit-bootstrap.md).
|
|
|
|
Secrets must be considered throughout error handling and inspection. Library
|
|
renderers must apply secret classification, and parser errors must not be
|
|
forwarded blindly when they can include raw input. Do not log configuration
|
|
implicitly. Applications remain responsible for classifying their secrets and
|
|
for safe messages supplied by their hooks.
|
|
|
|
## Scope Boundaries
|
|
|
|
Do not introduce automatic directory discovery, network providers, arbitrary
|
|
merge strategies, additional configuration languages, or application runtime
|
|
management without concrete consumer evidence and an explicit design decision.
|
|
The [deferred scope](../roadmap/deferred.md#explicit-non-goals-and-deferred-scope)
|
|
owns the deferred-feature inventory. Significant boundary
|
|
changes require an ADR and corresponding policy/contract updates; implementation
|
|
convenience alone is not a reason to broaden the public API.
|