Define discovery filename groups and search presets

This commit is contained in:
2026-10-24 01:16:42 +00:00
parent 5bb4f364b7
commit 988e49bb09

View File

@@ -1,7 +1,8 @@
# Composable Configuration Discovery Roadmap
Status: proposal with the agreed direction captured below. Preset contents and
the remaining behavioral choices are under [Open Decisions](#open-decisions).
Status: proposal with the agreed direction, filename groups, and Unix system
preset captured below. Remaining preset and behavioral choices are under
[Open Decisions](#open-decisions).
This document describes future work, not an implemented contract. A staged
implementation plan will follow once the decisions are settled.
@@ -43,13 +44,76 @@ library-provided search conventions. Consumer ownership remains unchanged.
## Agreed Direction
### Ordered Candidate Groups
### Filename Groups, Directory Searches, And Search Presets
Provide reusable descriptions of built-in search lists and custom ordered paths.
Consumers select one or more groups in the desired order. Conceptually, concatenate
the groups into one ordered candidate sequence, preserving order within each
group. This is ordered concatenation followed by first-match selection, not a
set union or a precedence merge.
Organize discovery around three composable descriptions. These are ordered lists:
their order determines precedence.
**Filename groups** provide reusable, ordered filename templates. The built-in
groups are:
| Group | Filenames, in order |
| --- | --- |
| Named | `<appname>.yml`, `<appname>.yaml` |
| Generic | `config.yml`, `config.yaml`, `main.yml`, `main.yaml` |
`<appname>` is the application slug supplied by the consumer.
**Directory searches** pair a directory with an ordered list of filename groups.
The default selection is `[Named, Generic]`, producing the six filenames above
in that order. A directory search may select different groups or reverse their
precedence, such as `[Generic, Named]`, while retaining each group's internal
order. Consumers can also supply custom filenames.
**Search presets** bundle ordered directory searches. Consumers can select a
complete preset for the simple case or compose presets, custom directory searches,
and explicit file candidates in their desired order. Explicit file candidates
are literal paths and receive no filename expansion.
### Ordered Expansion And Concatenation
The selection model is **ordered expansion and concatenation**, followed by
first-match selection:
1. Expand the selected presets into directory searches in their supplied order.
2. For each directory search, concatenate its selected filename groups in order,
substitute the application slug where needed, and join each filename to that
directory.
3. Concatenate the resulting candidate lists, preserving the positions of custom
directory searches and explicit file candidates.
4. Try candidates in order, stopping at the first selected file or an error as
described below.
Directory priority comes before filename priority: exhaust one directory's
filenames before trying the next directory. When several directories share a
filename list, their expansion is a Cartesian product traversed directory first.
Different directories may select different lists, so ordered expansion and
concatenation describes the complete model. Set union does not express its
precedence semantics.
The eventual documentation should explain this model using the applicable
mathematical nomenclature: ordered lists, ordered concatenation, and Cartesian
product where a shared filename list applies. Retain these names in relevant
implementation comments where they help readers understand the operations. The
model describes ordering without requiring eager construction of every candidate.
### Unix System Preset
The agreed Unix system preset is:
| Directory, in search order | Filename groups, in order |
| --- | --- |
| `/usr/local/etc/<appname>` | `[Named, Generic]` |
| `/etc/<appname>` | `[Named, Generic]` |
| `/etc` | `[Named]` |
This produces 14 candidates: six in each application-specific directory, then
`/etc/<appname>.yml` and `/etc/<appname>.yaml`. For example,
`/usr/local/etc/<appname>/main.yaml` precedes
`/etc/<appname>/<appname>.yml`. Selecting only `Named` for `/etc` is an ordinary
directory-search description; the selection algorithm needs no special exception.
### Illustrative Consumer API
Names below are illustrative and are not implemented Go API:
@@ -78,8 +142,9 @@ source := configkit.SearchConfig(
```
Use the resulting source with ordinary loading or as a layered main input. Custom
lists participate at their specified position; they do not silently append
unselected built-in defaults. Reordering groups changes search priority.
searches and file lists participate at their specified position; they do not
silently append unselected presets. Reordering presets or their directory
searches changes search priority.
### Selection And Failure Behavior
@@ -93,9 +158,9 @@ unselected built-in defaults. Reordering groups changes search priority.
| Entire search has no match and is required | Fail with a diagnostic explaining the unsuccessful search. |
| Caller explicitly uses `File(path)` | Retain existing exclusive-file behavior, including errors for a missing file. |
Requiredness applies to the complete search, not to each group. Permitting an
absent file does not waive application version, schema, or domain requirements;
the remaining lifecycle may still reject an incomplete configuration.
Requiredness applies to the complete search, not to each preset or directory.
Permitting an absent file does not waive application version, schema, or domain
requirements; the remaining lifecycle may still reject an incomplete configuration.
Perform discovery before parsing the chosen main file. Do not implement fallback
by repeatedly running complete loads. Defaults, application resolution, secret
@@ -142,25 +207,31 @@ An explicitly selected overlay is not another main-file fallback candidate.
The subsequent implementation plan should cover:
- Define candidate-group and search-source representations, slug validation, and
a search-wide requiredness option. Keep construction separate from acquisition.
- Implement the selected preset expansions, ordered custom paths, and first-match
selection through existing file access. Resolve each source once per consumption
before entering its parse and composition lifecycle.
- Define filename-group, directory-search, preset, and search-source representations,
slug validation, and a search-wide requiredness option. Keep construction
separate from acquisition and the simple preset selection concise.
- Implement ordered expansion and concatenation for presets, filename groups,
custom searches, and explicit file candidates, followed by first-match selection
through existing file access. Resolve each source once per consumption before
entering its parse and composition lifecycle.
- Integrate selected and absent outcomes with ordinary and layered main inputs,
placement, imports, application version checking, and overlay contexts according
to OD-3. Preserve ownership and close acquired handles on success and failure.
- Preserve real selected-source metadata and safe diagnostics without treating
absent candidates as configuration contributions.
- Exercise candidate and group ordering, custom-list composition, both filename
extensions, no-match behavior, required searches, unavailable preset bases,
errors that must stop fallback, repeated loads, and untouched explicit `File`
- Exercise preset, directory, filename-group, and filename ordering, reversed
group precedence, the Unix system preset's 14 candidates and named-only `/etc`
search, custom-list composition, both filename extensions, no-match behavior,
required searches, unavailable preset bases, errors that must stop fallback,
repeated loads, and untouched explicit `File`
behavior. Cover imports, overlays, underlays, defaults, CLI, source versions,
source contexts, and source-list/inspection evidence at their boundaries.
- Clarify the architecture policy's consumer-selected discovery boundary. Record
any warranted durable ownership decision in an ADR; keep preset filenames and
API mechanics in their canonical contract when implemented. Update consumer
examples and the configuration contract when the feature lands.
examples and the configuration contract when the feature lands. Explain the
three-part discovery model and its mathematical terminology in the eventual
documentation, with simple preset selection and custom composition examples.
This roadmap does not change implementation, policy, or ADRs by itself. Coordinate
implementation with the ongoing sequence integration audit rather than assuming
@@ -168,54 +239,48 @@ its in-progress changes are part of a fixed implementation baseline.
## Open Decisions
### OD-1 — Preset Contents And Filename Order
### OD-1 — Additional Preset Locations
**Recommendation:** begin with three explicitly selected presets. Try `.yml`
before `.yaml` at each location, completing both alternatives there before moving
to the next location:
The filename groups, default `[Named, Generic]` order, and Unix system preset
are settled above. The remaining choice is which additional directory conventions
to provide as presets.
| Preset | Candidate locations, in order |
| --- | --- |
| Unix system | `/usr/local/etc/<slug>/config.yml`, `/usr/local/etc/<slug>/config.yaml`, `/etc/<slug>/config.yml`, `/etc/<slug>/config.yaml`, `/etc/<slug>.yml`, `/etc/<slug>.yaml` |
| User configuration | `<user-config-dir>/<slug>/config.yml`, then `config.yaml` in the same directory |
| Working directory | `./<slug>.yml`, then `./<slug>.yaml` |
**Recommendation:** also provide user-configuration and working-directory presets:
| Preset | Directory | Filename groups, in order |
| --- | --- | --- |
| User configuration | `<user-config-dir>/<appname>` | `[Named, Generic]` |
| Working directory | Current working directory | `[Named, Generic]` |
For the user preset, use Go's platform-specific
[`os.UserConfigDir`](https://pkg.go.dev/os#UserConfigDir) as the base. This supplies
a conventional user location without inventing separate home-directory rules.
The system list retains the originally proposed location order. Application-named
working-directory files make the selected application's convention explicit.
No preset walks ancestor directories.
The working-directory preset searches that directory without walking ancestors.
Both reuse the agreed default filename groups.
**Alternative:** use `./config.yml` and `./config.yaml` for the working-directory
preset, or add them after the application-named files. Generic names are concise
for project-local applications, while application-named files distinguish
applications sharing a working directory. Adding both is convenient but expands
the candidate list and makes a generic file a fallback even when the consumer
intended an application-specific convention.
An additional viable user convention is a home-directory dotfile or directory,
such as `~/.<slug>.yml` or `~/.<slug>/config.yml`. It is familiar for some tools,
but would add a second user convention and ordering to explain. Prefer the
user-config-directory preset initially; custom paths can express other layouts.
**Alternative:** provide a home-directory convention, such as
`~/.<appname>/` with `[Named, Generic]`, instead of or alongside the
user-config-directory preset. It is familiar for some tools, but providing both
adds another convention for consumers to select and order. Prefer the
user-config-directory preset initially; custom searches can express other layouts.
### OD-2 — Unavailable Bases And Platform Applicability
**Recommendation:** resolve group bases as the ordered search reaches them.
If a selected group's base cannot be resolved, report an error. Treat using the
**Recommendation:** resolve directory bases as the ordered search reaches them.
If a selected directory's base cannot be resolved, report an error. Treat using the
Unix system preset on a platform where it is unsupported as an explicit
configuration error when that group is reached. Specify the supported platforms
configuration error when that preset is reached. Specify the supported platforms
in the implementation plan. Do not silently translate the Unix preset into a
different platform's convention. User and working-directory presets use their
platform-native bases.
An unavailable base is different from a candidate file being absent. Failure to
determine the user's configuration directory should not silently redirect loading
to a different group. Once an earlier candidate is selected, later groups need
to a different directory. Once an earlier candidate is selected, later searches need
not resolve their bases. Static invalid search arguments still fail ordinary
option validation before source acquisition.
**Alternative:** treat unsupported presets or unavailable bases as empty groups
**Alternative:** treat unsupported presets or unavailable bases as empty searches
and continue. This makes one preset composition usable across more environments,
but can unexpectedly select a lower-priority configuration. A selectable
skip-unavailable behavior could serve that need later; it adds a distinct policy
@@ -248,14 +313,15 @@ case could avoid changing `NoSource()`, at the cost of two different absence rul
### OD-4 — Empty Composition And Implicit Preset Selection
**Recommendation:** require explicit group selection to activate a convention.
An empty group composition has no candidates: optional search contributes no
source, and required search fails as exhausted. Empty custom lists follow the
same rule. This supports programmatic list construction and introduces no hidden
**Recommendation:** require explicit preset or search selection to activate a
convention. An empty search composition has no candidates: optional search contributes no
source, and required search fails as exhausted. Empty custom lists, including
empty filename-group selections, follow the same rule. This supports programmatic
list construction and introduces no hidden
system, user, or working-directory fallback. One explicitly selected preset
still gives the simple application a short call.
**Alternative:** let omission of groups select a documented default preset.
**Alternative:** let omission of searches select a documented default preset.
This shortens the simplest call, but omission and an intentionally empty computed
list would need distinguishable representations. It also requires choosing a
default convention across platforms and application styles. Another alternative