Catalog the public API for version one
This commit is contained in:
793
docs/roadmap/review.md
Normal file
793
docs/roadmap/review.md
Normal file
@@ -0,0 +1,793 @@
|
||||
# Public API Catalog And v1.0 Review
|
||||
|
||||
Reviewed revision: `fb71421956ba6b431bdc8745dcf7ab02607dac3a`.
|
||||
Status: discussion draft; no API changes are selected or implemented by this review.
|
||||
|
||||
## Scope And Method
|
||||
|
||||
The supported consumer surface is the root package
|
||||
`gitea.maximumdirect.net/eric/ConfigKit`, package name `configkit`.
|
||||
[ADR 0006](../adr/0006-public-facade.md) owns root type identity and the private
|
||||
implementation boundary. Exported engine names are not consumer API.
|
||||
|
||||
This catalog includes every exported root type, function, method, struct field,
|
||||
and constant in the current build: **372 entries** comprising **54 types,
|
||||
31 functions, 36 methods, 116 fields, and 135 constants**. There are no exported
|
||||
package variables, named interfaces, type aliases, or anonymously embedded public
|
||||
fields. Of the constants, 104 are diagnostic codes: the symbol count does not
|
||||
mean that consumers must learn 372 independent operations.
|
||||
|
||||
Inventory was extracted from the root non-test Go declarations, cross-checked
|
||||
against the package's build-selected files and `go doc -all .`, and reviewed
|
||||
against declaration comments, the [configuration contract](../config.md),
|
||||
[consumer examples](../consumers/README.md), the
|
||||
[architecture policy](../policy/architecture.md), and relevant accepted ADRs.
|
||||
This is an API-design review, not another correctness or performance audit.
|
||||
|
||||
Each catalog row has an assessment addressing clarity, ergonomics, and/or
|
||||
consolidation. **Retain** means no revision is currently warranted, not a new
|
||||
invariant or a promise to preserve private mechanics. **Discuss Dn** identifies
|
||||
a possible pre-v1 naming/removal/signature decision below; **An** is an optional
|
||||
additive idea. Struct declarations show their kind without private fields; all
|
||||
exported fields are separate rows. Callable signatures include generic constraints
|
||||
and receiver types. Constants include their current types and values.
|
||||
|
||||
Go declaration comments and the configuration contract remain the canonical
|
||||
behavior owners. This roadmap is a revision-pinned review record, not a second
|
||||
permanent API reference. Nothing here creates new release gates or consumer
|
||||
requirements. Under the [release policy](../policy/release.md#versioning-and-compatibility),
|
||||
v1 establishes compatibility expectations; it does not prevent later compatible
|
||||
additions. The highest-value decisions now are potential renames/removals that
|
||||
would otherwise require a later incompatible release.
|
||||
|
||||
## Overall Assessment
|
||||
|
||||
The facade has a coherent core: explicit source selection, one complete load,
|
||||
application-owned rules, separate presence/provenance, and safe frozen inspection.
|
||||
Its private implementation can evolve without changing public type ownership.
|
||||
Most of the API should remain. The useful cleanup opportunities are concentrated
|
||||
in vocabulary and small redundant state, not a wholesale loading framework.
|
||||
|
||||
Prioritize D1–D4: these names can encourage an incorrect interpretation of the
|
||||
state a consumer receives. D5–D7 are useful additional naming/signature discussions;
|
||||
D8 is a small surface-hygiene question. A1–A2 can wait unless a concrete consumer
|
||||
need makes them useful now. These are recommendations for discussion, not approved
|
||||
changes or a declaration that v1 must wait for every suggestion.
|
||||
|
||||
## Pre-v1 Discussion Candidates
|
||||
|
||||
### D1. Name the fixed resolution view as entry state
|
||||
|
||||
Affected: `Resolution.InputInfo`; the documentation of `Resolution` and
|
||||
`FieldInfo`.
|
||||
|
||||
The lookup includes retained typed defaults and remains fixed while the resolver
|
||||
changes the working configuration. “Input” can therefore suggest either raw
|
||||
authored values or the mutable current value, neither of which is its complete
|
||||
meaning.
|
||||
|
||||
Recommendation: consider renaming the method to `EntryInfo` and consistently
|
||||
calling this the resolution-entry view. Keep its return type and silent
|
||||
unknown/shape-mismatch result. Keep `IsExplicit` as the convenient surviving-input
|
||||
question with its existing path-validation behavior.
|
||||
|
||||
Alternative: retain `InputInfo`, whose comment already explains the contract,
|
||||
and improve the nearby example. That avoids consumer migration and is defensible
|
||||
if the terminology is already well understood. Do not expose the growing history
|
||||
model through the callback or collapse entry and final metadata to solve a naming
|
||||
problem.
|
||||
|
||||
### D2. Distinguish sensitivity classification from secret references
|
||||
|
||||
Affected: `Definition.Secret`; related terminology around `Secret` and
|
||||
`ValueSnapshot.Redacted`.
|
||||
|
||||
The function marks arbitrary semantic paths sensitive, including ordinary strings
|
||||
and containers. It neither creates nor resolves the typed environment references
|
||||
represented by `Secret`.
|
||||
|
||||
Recommendation: consider `Definition.Sensitive func(Path) bool`. This makes a
|
||||
definition such as `Sensitive: func(path Path) bool { ... }` describe its purpose
|
||||
without implying credential lookup. Preserve the existing tag/predicate union,
|
||||
historical masking, and reference semantics.
|
||||
|
||||
Alternative: keep `Secret` on the field because it aligns with the existing
|
||||
`configkit:"secret"` tag and the established documentation. The current name is
|
||||
not functionally wrong. No tag rename, new classification policy, credential
|
||||
provider, or declassification option follows from this discussion.
|
||||
|
||||
### D3. Make version-policy and version-evidence names literal
|
||||
|
||||
Affected: `SourceVersionModeDefault`, `SourceVersionCheck`, and
|
||||
`SourceInfo.VersionCheck`.
|
||||
|
||||
“Default” describes an implementation/history relationship rather than the
|
||||
selected main-only behavior. A “Check” record can exist for an absent declaration
|
||||
without proving that its checker ran or accepted a value.
|
||||
|
||||
Recommendation: consider `SourceVersionModeMainOnly` for the existing zero mode,
|
||||
`SourceVersionEvidence` for the record, and `SourceInfo.VersionEvidence` for the
|
||||
field. Keep all existing mode values, invocation/absence rules, source-local
|
||||
coordinates, consumption, and redaction behavior. In particular, an if-present
|
||||
mode still has the documented main-absence checker behavior; naming cleanup
|
||||
must not silently change it.
|
||||
|
||||
Alternative: retain the names and make their precise meanings prominent in
|
||||
declaration comments and examples. “Default” is familiar for zero enums, and
|
||||
existing consumers may already understand the evidence qualification. Do not add
|
||||
an “accepted” boolean unless ConfigKit actually chooses to retain that fact under
|
||||
a separately agreed contract.
|
||||
|
||||
### D4. Describe the empty-import-selection subset and remove redundant state
|
||||
|
||||
Affected: `ImportSelection`, `SourceInfo.ImportSelections`,
|
||||
`ImportSelection.Outcome`, `ImportSelectionOutcome`, and `ImportSelectionNone`.
|
||||
|
||||
These records describe optional declarations that selected no source. They are
|
||||
not a complete selection ledger. Their outcome has exactly one supported value,
|
||||
`"none"`; successful acquisition is represented elsewhere by source records.
|
||||
|
||||
Recommendation: consider `EmptyImportSelection` and
|
||||
`SourceInfo.EmptyImportSelections`, retaining path, location, and selector.
|
||||
With that explicit scope, remove the constant-only outcome field/type/constant.
|
||||
This reduces the public model without losing information that these records
|
||||
currently communicate.
|
||||
|
||||
Alternative: retain the broad names and explicit outcome. It leaves room for
|
||||
compatible additional outcomes and avoids migration, but consumers must learn
|
||||
that the collection currently reports empty selections only. Future flexibility
|
||||
alone should not imply an approved complete event-history feature.
|
||||
|
||||
### D5. Describe destination fallback evidence more precisely
|
||||
|
||||
Affected: `SourceInfo.DestinationUnresolved`.
|
||||
|
||||
The destination can be a useful nearest resolved enclosing container. The flag
|
||||
does not mean every aspect of placement is unresolved, and a source's own item
|
||||
container can be an exact destination without publishing its item index here.
|
||||
|
||||
Recommendation: consider `DestinationIsFallback` with the existing meaning.
|
||||
Retain `Destination` and `PlacementMode`; another placement object/state machine
|
||||
is not needed just to clarify this boolean.
|
||||
|
||||
Alternative: retain `DestinationUnresolved` and its detailed comment. It is
|
||||
already specific enough for informed consumers. Neither option adds exact item
|
||||
coordinates, identity tracking, or extra source-location guarantees.
|
||||
|
||||
### D6. Separate manual recording intents from inspection-only operation kinds
|
||||
|
||||
Affected: `Resolution.Record` and its use of `ChangeKind`.
|
||||
|
||||
Four named operation values are exported, but only derived-default and
|
||||
normalization are valid manual recording intents. Decoding and inheritance
|
||||
describe library processing. A caller can choose an apparently suitable exported
|
||||
constant only to receive a load-time invalid-kind diagnostic.
|
||||
|
||||
Recommendation for discussion: consider replacing manual `Record` with
|
||||
`RecordDerivedDefault(path Path, reason string, dependencies ...Path)` and
|
||||
`RecordNormalization(path Path, reason string, dependencies ...Path)` methods.
|
||||
Keep the generic assignment helpers `Default` and `Normalize`, which do different
|
||||
work, and keep all inspection `ChangeKind` values. The explicit methods remove a
|
||||
misleading input choice without changing the two accepted intents.
|
||||
|
||||
Alternative: retain the single method and prominently document its accepted
|
||||
subset. A separate input-only kind type is another viable design, but adds a
|
||||
second enum and conversion vocabulary; it is less attractive unless consumers
|
||||
need to select recording intent dynamically. Do not change net-change auditing
|
||||
or impose new recording obligations through this cleanup.
|
||||
|
||||
### D7. Make environment mutation evident in the bootstrap function name
|
||||
|
||||
Affected: `LoadSecretsDir`.
|
||||
|
||||
Unlike ordinary `Load`, this explicit startup operation writes previously unset
|
||||
process environment variables and can report partial application failure.
|
||||
|
||||
Recommendation: consider `BootstrapSecretsDir`. It follows the repository's
|
||||
existing bootstrap terminology and makes the distinction from configuration
|
||||
loading easier to notice. Retain its report-plus-error return and all current
|
||||
preflight, skip, partial-failure, and ownership behavior.
|
||||
|
||||
Alternative: retain `LoadSecretsDir`, whose comment clearly states the side
|
||||
effects. Avoid a broad new bootstrap options framework or merging this operation
|
||||
into ordinary loading. The purpose is naming clarity, not stronger filesystem
|
||||
or process-transaction guarantees.
|
||||
|
||||
### D8. Resolve the intent of the unproduced options diagnostic code
|
||||
|
||||
Affected: `CodeOptionsUnavailable`.
|
||||
|
||||
The scoped implementation search found its root and engine declarations but no
|
||||
named production producer. That is a reason to ask about its intended role, not
|
||||
evidence that an exported symbol has no consumers.
|
||||
|
||||
Recommendation: establish whether there is an intentional supported/reserved case;
|
||||
remove the constant before v1 if there is none. Do not invent a new failure merely
|
||||
to give it a producer.
|
||||
|
||||
Alternative: retain it as deliberately reserved classification if there is a
|
||||
concrete reason. Document that intent rather than automatically deleting it.
|
||||
The other diagnostic families should not be collapsed just to reduce the count.
|
||||
|
||||
## Optional Additive Ideas
|
||||
|
||||
### A1. Direct source lookup on successful results
|
||||
|
||||
`Resolution.Source(id)` and both `SourceContext(id)` methods accept a source ID,
|
||||
while a final result offers only `Sources()` for source records. A consumer
|
||||
following many origin locations must scan or index that slice itself.
|
||||
|
||||
Consider `Result.Source(id string) (SourceInfo, bool)` with the same detached,
|
||||
passive lookup meaning as the resolution query. Prefer it over a general source
|
||||
registry abstraction if representative consumers repeatedly need this lookup.
|
||||
Keeping only the ordered slice is a viable smaller API for consumers that
|
||||
enumerate once. This is an addition that can follow v1; it need not block the
|
||||
existing API freeze.
|
||||
|
||||
### A2. Path comparison without losing segment meaning
|
||||
|
||||
`Path.String()` is a JSON Pointer spelling, not a complete semantic identity:
|
||||
an explicit numeric mapping key and an index can share it. The public type is
|
||||
opaque and currently non-comparable, so consumers cannot directly compare its
|
||||
segment choices without relying on reflection or private representation.
|
||||
|
||||
If consumers need path comparison, consider an explicitly documented comparison
|
||||
operation. Decide whether it means exact segment representation (including
|
||||
parsed-versus-explicit choice and invalid state), or resolution against a
|
||||
document; those are different operations. Prefer a small exact-representation
|
||||
operation over exposing mutable segments or adding another pattern language.
|
||||
Do not label pointer-string equality semantic equality.
|
||||
|
||||
Alternatively keep Path solely as an opaque query/building value and clarify
|
||||
the limitation in examples. No concrete need currently justifies a public
|
||||
segment-inspection framework. A comparison method can be added compatibly later.
|
||||
|
||||
## Consolidation Assessment
|
||||
|
||||
| Area | Assessment |
|
||||
| --- | --- |
|
||||
| C1: ordinary versus layered loading | Keep two entry points. Ordinary options combine one input's choices with load-wide rules; layered Input owns the former and LayeredOptions the latter. Reusing one options type would introduce ignored/competing fields. Anonymous embedding also makes keyed composite literals less convenient. Existing Definition, ImportDefaults, Input, and Override sharing is useful consolidation already. |
|
||||
| C2: filesystem vocabulary | Keep Filesystem (borrowed identity), FilesystemContext (input roots), and ResourceContext (passive returned access) distinct. FS means different concrete types because the roles differ. Clarify with paired host/native examples, rather than introducing one universal context or changing ownership. |
|
||||
| C3: history versus explanation | Keep Change and ValueChange, Metadata.History and Result.Explain distinct. Metadata has composite/previous attribution; explanation has safe values and decoding/net replay. Their shared private evidence is the correct consolidation boundary. |
|
||||
| C4: semantic path versus sequence selector | Keep Path and SequenceSelector distinct. One describes concrete/contextual queries, the other limited mapping-key wildcard registration. Unifying them would broaden grammar and validation for little current benefit. |
|
||||
| Errors and renderers | Keep LoadError and SecretsError separate because bootstrap can accompany partial external effects. Keep structured diagnostics plus safe rendering. SecretsReport.WriteTo follows io.WriterTo; other renderers need not return byte counts merely for symmetry. |
|
||||
| Presence records | Keep FieldInfo, Version and ValueSnapshot's purpose-specific booleans. A generic optional-value/tri-state framework would add vocabulary without simplifying the application configuration structs. |
|
||||
| Result ownership | Keep caller-owned Config and frozen inspection. A copying accessor would not make arbitrary T language-enforced immutable and would add work; emphasize the established snapshot distinction instead. |
|
||||
|
||||
## Related Consumer Integration Contracts
|
||||
|
||||
The Go symbols are not the entire compatibility commitment. These established
|
||||
integration surfaces also matter to v1, but are defined by their canonical
|
||||
contracts rather than counted as additional exported Go declarations:
|
||||
|
||||
| Surface | Owner and assessment |
|
||||
| --- | --- |
|
||||
| Configuration structs, YAML field tags, standard text codecs, defaults and typed decoding | [Schema binding](../config.md#application-schema-binding), [defaults](../config.md#defaults-and-typed-decoding), and [projection](../config.md#typed-projection-and-ownership). Retain ordinary application types and supported standard codec integration; do not add parser-specific public hooks. |
|
||||
| YAML declarations, composition, imports, overlays and sibling inheritance | [Configuration contract](../config.md) and accepted ADRs linked there. Retain the established coherent language; this review does not propose reserved-key or merge-policy changes. |
|
||||
| Presence, provenance, recorded resolution and validation | [Metadata](../config.md#layered-metadata), [resolution](../config.md#recorded-application-resolution), and [validation](../config.md#semantic-validation). Retain the distinctions that remove consumer bookkeeping; naming discussions do not change these semantics. |
|
||||
| Secret references, classification and explicit environment bootstrap | [Secret references](../config.md#typed-secret-references), [classification](../config.md#secret-classification-and-effective-rendering), and [bootstrap](../config.md#explicit-secrets-directory-bootstrap). D2/D7 concern API vocabulary, not a new credential or security model. |
|
||||
| One-shot completion, resource ownership, safe rendering and comparison | [Completion](../config.md#completion-and-resource-lifetime), [source contexts](../config.md#source-resource-contexts), [explanation](../config.md#historical-value-explanation), and [diffs](../config.md#effective-configuration-diffs). Retain established lifetimes and safe snapshot boundaries rather than adding watching, lazy work or a required result Close operation. |
|
||||
|
||||
## Complete Catalog
|
||||
|
||||
All rows below assess the current declarations, not the proposed replacement
|
||||
names. No proposed symbol is counted in the 372-entry inventory.
|
||||
|
||||
### Loading and application policy
|
||||
|
||||
Declarations: [layered.go](../../layered.go), [lifecycle.go](../../lifecycle.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Input` | `type Input struct` | Retain a source plus placement/import/access settings; zero Source is invalid, unlike explicit NoSource. |
|
||||
| `Input.Source` | `Source` | Retain the opaque source union; no provider interface or parser node exposure. |
|
||||
| `Input.Destination` | `Path` | Retain the same root-relative placement meaning as ordinary options. |
|
||||
| `Input.PlacementMode` | `PlacementMode` | Retain the same explicit placement enum as ordinary options. |
|
||||
| `Input.Imports` | `bool` | Retain per-input traversal choice; it differs from load-wide declaration defaults. |
|
||||
| `Input.Filesystem` | `*FilesystemContext` | Retain per-input access context, not a global context for unrelated namespaces. |
|
||||
| `Underlay` | `type Underlay struct` | Retain a named strict tier; flattening all inputs would lose the peer-versus-precedence boundary. |
|
||||
| `Underlay.Inputs` | `[]Input` | Retain ordered inputs in one strict group; an empty group remains a no-op. |
|
||||
| `LayeredOptions` | `type LayeredOptions[T any] struct` | Retain separate layered entry-point options; input-level settings already belong to Input (C1). |
|
||||
| `LayeredOptions.Definition` | `Definition[T]` | Retain the same Definition type as ordinary loading. |
|
||||
| `LayeredOptions.SourceVersionMode` | `SourceVersionMode` | Discuss D3's mode name; retain one rule across the load rather than per-source policy switches. |
|
||||
| `LayeredOptions.ImportDefaults` | `ImportDefaults` | Retain the shared type and one load-wide default policy. |
|
||||
| `LayeredOptions.Underlays` | `[]Underlay` | Retain ordered low-to-high strict groups; no separate precedence numbers are needed. |
|
||||
| `LayeredOptions.Overlay` | `string` | Retain the same selected-overlay concept as ordinary loading. |
|
||||
| `LayeredOptions.OverlayFilesystem` | `*FilesystemContext` | Retain its explicit overlay-only purpose; merging it into main input context would change behavior (C1/C2). |
|
||||
| `LayeredOptions.Overrides` | `[]Override` | Retain shared Override records and the same CLI semantics. |
|
||||
| `LoadLayered` | `func LoadLayered[T any](main Input, supplied ...LayeredOptions[T]) (*Result[T], error)` | Retain explicit tiers and independent main input; do not make composition strategy a switch. See consolidation assessment C1. |
|
||||
| `Definition` | `type Definition[T any] struct` | Retain reusable application rules separate from per-load selections; zero disables hooks and uses Go-zero defaults. |
|
||||
| `Definition.Defaults` | `func() T` | Retain a fresh-value factory; zero-value configuration and shared mutable defaults are different concerns. |
|
||||
| `Definition.Resolve` | `func(*T, *Resolution) error` | Retain audited mutation callback and error return; do not move application normalization outside loading. |
|
||||
| `Definition.Validate` | `func(*T, *Validation)` | Retain read-only audited validation despite pointer parameter; a value parameter would not make maps/pointers immutable. |
|
||||
| `Definition.Version` | `*VersionRule` | Retain optional application version policy; nil disables the rule. Do not confuse it with the ConfigKit release version. |
|
||||
| `Definition.Secret` | `func(Path) bool` | Discuss D2: rename the classification predicate to Sensitive, distinguishing it from environment-backed Secret references. |
|
||||
| `Definition.SequenceIdentities` | `[]SequenceIdentity` | Retain authoritative identity registrations; no new per-consumer merge strategy is needed. |
|
||||
| `PlacementMode` | `type PlacementMode uint8` | Retain small explicit enum; zero is mapping placement, not automatic inference. |
|
||||
| `SourceVersionMode` | `type SourceVersionMode uint8` | Discuss D3: the zero behavior deserves a descriptive MainOnly name rather than a historical Default label. |
|
||||
| `ImportDefaults` | `type ImportDefaults struct` | Retain independent tri-state defaults; zero leaves library defaults in effect. Do not replace pointers with false-as-unspecified. |
|
||||
| `ImportDefaults.Required` | `*bool` | Retain *bool: nil is unspecified, false is an explicit optional selection. |
|
||||
| `ImportDefaults.Follow` | `*bool` | Retain *bool: explicit false must override the true library default. |
|
||||
| `ImportDefaults.Hidden` | `*bool` | Retain *bool: explicit false must override the true library default. |
|
||||
| `ImportDefaults.Recursive` | `*bool` | Retain *bool: distinguishes declaration defaulting from an explicit choice. |
|
||||
| `ImportDefaults.Pattern` | `[]string` | Retain nil-versus-empty selection meaning; a new pattern language is unwarranted. |
|
||||
| `Options` | `type Options[T any] struct` | Retain ordinary-load options with input-specific settings; do not consolidate by anonymously embedding another public options struct (C1). |
|
||||
| `Options.Definition` | `Definition[T]` | Retain the shared Definition type; avoids duplicating callback configuration. |
|
||||
| `Options.Destination` | `Path` | Retain literal mapping placement independent of filesystem paths; root is the zero Path. |
|
||||
| `Options.PlacementMode` | `PlacementMode` | Retain explicit mapping/item choice; avoid inferring it from destination spelling. |
|
||||
| `Options.Imports` | `bool` | Retain explicit opt-in; import defaults alone must not enable traversal. |
|
||||
| `Options.ImportDefaults` | `ImportDefaults` | Retain one shared load-wide default type, not duplicate boolean fields here. |
|
||||
| `Options.Overlay` | `string` | Retain one selected overlay name; empty selects none. |
|
||||
| `Options.Filesystem` | `*FilesystemContext` | Retain context for ordinary source/import/overlay handling; distinguish from the layered overlay-only context (C1/C2). |
|
||||
| `Options.Overrides` | `[]Override` | Retain explicit CLI assignments; do not generalize these into another arbitrary source tier. |
|
||||
| `Options.SourceVersionMode` | `SourceVersionMode` | Discuss D3's zero-mode name; retain load-wide version policy here. |
|
||||
| `FilesystemContext` | `type FilesystemContext struct` | Retain input access scope, distinct from returned ResourceContext. Consider documentation terminology rather than merging lifetimes (C2). |
|
||||
| `FilesystemContext.BaseDir` | `string` | Retain host-absolute versus namespace-relative coordinates; clarify with paired examples (C2). |
|
||||
| `FilesystemContext.Root` | `string` | Retain explicit scope separate from the source-relative base; do not silently strengthen filesystem security policy. |
|
||||
| `FilesystemContext.FS` | `*Filesystem` | Retain *Filesystem identity, not raw fs.FS: nil selects host context and namespace identity matters (C2). |
|
||||
| `Override` | `type Override struct` | Retain parser-neutral explicit CLI input, distinct from defaults and ordinary sources. |
|
||||
| `Override.Path` | `Path` | Retain application-root semantic coordinates. |
|
||||
| `Override.Value` | `any` | Retain built-in neutral values; accepting arbitrary typed objects would add another conversion contract. |
|
||||
| `Override.Flag` | `string` | Retain safe display attribution, not the raw CLI argument or value. |
|
||||
| `Load` | `func Load[T any](source Source, supplied ...Options[T]) (*Result[T], error)` | Retain the source-only call and one typed options record; the optional form avoids a mandatory empty options value or request builder. |
|
||||
|
||||
### Sources and filesystem namespaces
|
||||
|
||||
Declarations: [resource_context.go](../../resource_context.go), [source.go](../../source.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `ResourceContext` | `type ResourceContext struct` | Retain passive post-acquisition resource access distinct from input scope; zero has no filesystem (C2). |
|
||||
| `ResourceContext.FS` | `fs.FS` | Retain borrowed fs.FS. Callers close files they open; a successful load itself needs no Close. |
|
||||
| `ResourceContext.BaseDir` | `string` | Retain slash-relative source base; do not replace it with a host path that excludes embedded filesystems. |
|
||||
| `Source` | `type Source struct` | Retain opaque acquisition descriptions and deferred validation; zero is invalid and NoSource is deliberate absence. |
|
||||
| `Filesystem` | `type Filesystem struct` | Retain a reusable borrowed fs.FS namespace with identity; zero is not a valid namespace. |
|
||||
| `NewFilesystem` | `func NewFilesystem(label string, filesystem fs.FS) Filesystem` | Retain label plus borrowed fs.FS, returning a namespace whose argument failures surface during load. |
|
||||
| `Filesystem.File` | `func (f Filesystem) File(name string) Source` | Retain native namespace file selection as a method; replacing it with a raw fs.FS Source overload would lose identity/context. |
|
||||
| `File` | `func File(path string) Source` | Retain the short reusable host-file constructor. |
|
||||
| `Bytes` | `func Bytes(label string, data []byte) Source` | Retain immediate byte ownership snapshot and a safe label; delayed constructor diagnostics match other sources. |
|
||||
| `Reader` | `func Reader(label string, input io.Reader) Source` | Retain explicit single-use borrowed-reader semantics; do not add automatic Close or hidden buffering for reuse. |
|
||||
| `Mapping` | `func Mapping(label string, values map[string]any) Source` | Retain immediate neutral-map snapshot; nil root is invalid and empty mapping is meaningful. |
|
||||
| `NoSource` | `func NoSource() Source` | Retain explicit absence separate from zero Source and an empty document. |
|
||||
|
||||
### Explicit main-file discovery
|
||||
|
||||
Declarations: [discovery.go](../../discovery.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Search` | `type Search struct` | Retain immutable explicit host-search description; zero contributes no candidates. No independent discovery/execution stage is needed. |
|
||||
| `FilenameGroup` | `type FilenameGroup struct` | Retain a small opaque description; zero contributes no names. It is not a general glob matcher. |
|
||||
| `SearchOptions` | `type SearchOptions struct` | Retain whole-search absence policy separate from per-directory filename choices. |
|
||||
| `SearchOptions.Required` | `bool` | Retain optional-by-default search exhaustion; selected-file errors remain terminal. |
|
||||
| `DirectorySearchOptions` | `type DirectorySearchOptions struct` | Retain the filename-group option record; preserve nil/default versus empty/no-candidates distinction. |
|
||||
| `DirectorySearchOptions.Filenames` | `[]FilenameGroup` | Retain ordered groups; no candidate list or candidate-attempt history is necessary. |
|
||||
| `NamedFilenames` | `func NamedFilenames() FilenameGroup` | Retain application-named YAML filename preset; it keeps common consumer setup short. |
|
||||
| `GenericFilenames` | `func GenericFilenames() FilenameGroup` | Retain generic YAML filename preset; separate selection avoids implicit convention changes. |
|
||||
| `CustomFilenames` | `func CustomFilenames(names ...string) FilenameGroup` | Retain explicit filename components and order, not a second glob/template mechanism. |
|
||||
| `SearchDirectory` | `func SearchDirectory(directory string, supplied ...DirectorySearchOptions) Search` | Retain directory search plus optional filenames; argument problems remain load-time diagnostics. |
|
||||
| `Paths` | `func Paths(paths ...string) Search` | Retain literal host-candidate description. Document that this is discovery, not semantic Path construction. |
|
||||
| `Searches` | `func Searches(searches ...Search) Search` | Retain ordered concatenation; it expresses one search, not repeated loads or fallback after parse failure. |
|
||||
| `UnixSystemDirectories` | `func UnixSystemDirectories() Search` | Retain explicit platform preset; do not turn its presence into implicit cross-platform guarantees. |
|
||||
| `UserConfigDirectory` | `func UserConfigDirectory() Search` | Retain explicit OS user-config location preset. |
|
||||
| `HomeDirectory` | `func HomeDirectory() Search` | Retain explicit home-root search preset, distinct from user-config location. |
|
||||
| `WorkingDirectory` | `func WorkingDirectory() Search` | Retain explicit working-directory preset; do not activate it automatically. |
|
||||
| `SearchConfig` | `func SearchConfig(application string, search Search, supplied ...SearchOptions) Source` | Retain deferred source selection inside loading. Optional exhaustion and selected-file failure are intentionally different. |
|
||||
|
||||
### Paths and sequence identity registration
|
||||
|
||||
Declarations: [path.go](../../path.go), [sequence_identity.go](../../sequence_identity.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Path` | `type Path struct` | Discuss A2: preserve opaque semantic paths; consider exact segment comparison only if consumers need it. Zero is root; current type is not comparable. |
|
||||
| `Keys` | `func Keys(keys ...string) Path` | Retain concise literal-key construction and zero-argument root; do not interpret numeric keys as indexes. |
|
||||
| `ParsePath` | `func ParsePath(pointer string) (Path, error)` | Retain contextual numeric tokens and an error return; pointer spelling alone is not full semantic identity (A2). |
|
||||
| `Path.Key` | `func (p Path) Key(key string) Path` | Retain immutable literal-key append, including empty and numeric keys. |
|
||||
| `Path.Index` | `func (p Path) Index(index int) Path` | Retain explicit sequence indexing; negative indexes produce an invalid path rejected by configuration operations. |
|
||||
| `Path.String` | `func (p Path) String() string` | Retain canonical JSON Pointer formatting, but explicitly avoid calling it a complete semantic identity (A2). |
|
||||
| `SequenceSelector` | `type SequenceSelector []SequenceSelectorSegment` | Retain the named slice of literal/wildcard mapping-key segments; zero selects root. Do not merge it with general Path syntax (C4). |
|
||||
| `SequenceSelectorSegment` | `type SequenceSelectorSegment struct` | Retain the opaque segment; zero is the literal empty key, not a wildcard. |
|
||||
| `SequenceLiteralKey` | `func SequenceLiteralKey(key string) SequenceSelectorSegment` | Retain explicit literal construction, including '*' as a real key. |
|
||||
| `SequenceAnyMappingKey` | `func SequenceAnyMappingKey() SequenceSelectorSegment` | Retain an explicit wildcard constructor; textual '*' must not acquire a second meaning. |
|
||||
| `SequenceIdentity` | `type SequenceIdentity struct` | Retain immutable registration value, not a pluggable merge strategy. Zero lacks required identity fields and is rejected during load. |
|
||||
| `SequenceIdentityAt` | `func SequenceIdentityAt(path Path, fields ...string) SequenceIdentity` | Retain the exact-path convenience; it avoids building a selector for ordinary static destinations. |
|
||||
| `SequenceIdentityMatching` | `func SequenceIdentityMatching(selector SequenceSelector, fields ...string) SequenceIdentity` | Retain wildcard mapping destinations for repeated namespaces; constructor copies inputs. |
|
||||
|
||||
### Resolution, validation, and application schema versions
|
||||
|
||||
Declarations: [lifecycle.go](../../lifecycle.go), [resource_context.go](../../resource_context.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Resolution` | `type Resolution struct` | Retain a library-issued callback capability; nil/zero/closed contexts are invalid and panic. Do not add a general resolver service interface. |
|
||||
| `VersionRule` | `type VersionRule struct` | Retain one application-owned version path/checker. Zero Path means root; a non-nil rule needs a checker. |
|
||||
| `VersionRule.Path` | `Path` | Retain mode-defined effective/source-local coordinates; clarify the distinction in examples, not separate version systems. |
|
||||
| `VersionRule.Check` | `func(Version, *Validation) error` | Retain Version plus diagnostic context and error return; application acceptance remains application-owned. |
|
||||
| `Version` | `type Version struct` | Retain the compact absent/null/text record rather than imposing a semver or numeric version type. |
|
||||
| `Version.Present` | `bool` | Retain: omission cannot be inferred from Text being empty. |
|
||||
| `Version.Null` | `bool` | Retain: an explicit null is different from absence and an empty string. |
|
||||
| `Version.Text` | `string` | Retain exact accepted spelling; do not expose YAML scalar kinds when string/integer equivalence is intentional. |
|
||||
| `Validation` | `type Validation struct` | Retain the callback-only diagnostic capability; no public constructor or reusable context is needed. |
|
||||
| `Resolution.IsExplicit` | `func (r *Resolution) IsExplicit(path Path) bool` | Retain surviving-input presence semantics, including inherited input; document its schema-path diagnostics versus a lookup miss. |
|
||||
| `Resolution.InputInfo` | `func (r *Resolution) InputInfo(path Path) (FieldInfo, bool)` | Discuss D1: EntryInfo more accurately names the fixed pre-resolution view, including defaults. |
|
||||
| `Resolution.Source` | `func (r *Resolution) Source(id string) (SourceInfo, bool)` | Retain direct acquired-source lookup by ID; optional Result parity is A1. |
|
||||
| `Resolution.Record` | `func (r *Resolution) Record(path Path, kind ChangeKind, reason string, dependencies ...Path)` | Discuss D6: its ChangeKind argument admits inspection-only constants that loading rejects; consider intent-specific record methods. |
|
||||
| `Resolution.Error` | `func (r *Resolution) Error(path Path, message string, related ...Path)` | Retain safe diagnostic collection; do not expose raw application errors automatically. |
|
||||
| `Resolution.Warning` | `func (r *Resolution) Warning(path Path, message string, related ...Path)` | Retain warning/error symmetry and related semantic paths. |
|
||||
| `Default` | `func Default[V any](resolution *Resolution, path Path, target *V, value V, reason string, dependencies ...Path)` | Retain the generic typed assignment helper; explicit zero/false/null must suppress absence-based assignment. |
|
||||
| `Normalize` | `func Normalize[V any](resolution *Resolution, path Path, target *V, value V, reason string, dependencies ...Path)` | Retain unconditional typed assignment plus audit intent; not redundant with absence-based Default. |
|
||||
| `Validation.Error` | `func (v *Validation) Error(path Path, message string, related ...Path)` | Retain value-free errors collected at semantic paths, distinct from unwrapping arbitrary callback errors. |
|
||||
| `Validation.Warning` | `func (v *Validation) Warning(path Path, message string, related ...Path)` | Retain matching warning API; no generic severity switch is necessary. |
|
||||
| `Resolution.SourceContext` | `func (r *Resolution) SourceContext(id string) (ResourceContext, bool)` | Retain passive source-context lookup during the callback, with no I/O. |
|
||||
|
||||
### Results, presence, and source/provenance evidence
|
||||
|
||||
Declarations: [inspection.go](../../inspection.go), [lifecycle.go](../../lifecycle.go), [metadata.go](../../metadata.go), [records.go](../../records.go), [resource_context.go](../../resource_context.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Result.Explain` | `func (r *Result[T]) Explain(path Path) (Explanation, bool)` | Retain structured safe history plus found boolean; query semantics differ from Metadata.History (C3). |
|
||||
| `Result.WriteEffective` | `func (r *Result[T]) WriteEffective(output io.Writer) error` | Retain frozen redacted YAML output and error-only writer convention; nil/zero snapshot or nil writer is an error. |
|
||||
| `Result.WriteExplanation` | `func (r *Result[T]) WriteExplanation(output io.Writer, path Path) error` | Retain human-readable output. Clarify that unknown paths return an error without output; this is not a new behavior. |
|
||||
| `Result` | `type Result[T any] struct` | Retain the successful configuration plus frozen inspection boundary; zero/nil has no usable loaded snapshot. |
|
||||
| `Result.Config` | `T` | Retain caller-owned mutable T. Clarify that later mutation does not update inspection; replacing it with a cloning getter adds cost without immutability. |
|
||||
| `Result.Metadata` | `func (r *Result[T]) Metadata() Metadata` | Retain detached effective/history query access; do not translate Explain output into metadata. |
|
||||
| `Result.Sources` | `func (r *Result[T]) Sources() []SourceInfo` | Retain ordered detached acquisition records; optional direct Source lookup is A1. |
|
||||
| `Result.Diagnostics` | `func (r *Result[T]) Diagnostics() []Diagnostic` | Retain successful-load nonfatal diagnostics, parallel to structured load failures. |
|
||||
| `Metadata` | `type Metadata struct` | Retain opaque detached queries; zero contains no known paths. |
|
||||
| `Metadata.Lookup` | `func (m Metadata) Lookup(path Path) (FieldInfo, bool)` | Retain effective FieldInfo plus found boolean; this handles absence without optional wrappers in application structs. |
|
||||
| `Metadata.IsExplicit` | `func (m Metadata) IsExplicit(path Path) bool` | Retain ergonomic convenience over Lookup; inherited input is explicit and defaults/resolution do not invent presence. |
|
||||
| `Metadata.History` | `func (m Metadata) History(path Path) []Change` | Retain attribution-only history, including removed paths; it intentionally differs from value replay (C3). |
|
||||
| `Metadata.HistoricalPaths` | `func (m Metadata) HistoricalPaths() []Path` | Retain canonical-pointer enumeration, including removed/default/control paths; no extra per-shape enumeration API is justified. |
|
||||
| `Layer` | `type Layer string` | Retain string-valued provenance categories; base/main terminology merits documentation clarity, not an automatic wire-value rename. |
|
||||
| `SourceKind` | `type SourceKind string` | Retain acquisition kind independently of precedence layer. |
|
||||
| `ChangeKind` | `type ChangeKind string` | Discuss D6's recording input; retain all inspection operations and the empty/default definition state. |
|
||||
| `Location` | `type Location struct` | Retain source ID plus optional line/column coordinates; zero coordinates mean unavailable/synthetic. |
|
||||
| `Location.SourceID` | `string` | Retain a load-local acquired-source key, not a host filename. |
|
||||
| `Location.Line` | `int` | Retain line coordinate; zero means unavailable. |
|
||||
| `Location.Column` | `int` | Retain column coordinate; zero means unavailable. |
|
||||
| `SourceInfo` | `type SourceInfo struct` | Retain structured acquisition and placement evidence; simplify misleading evidence names in D3–D5, not source identity. |
|
||||
| `SourceInfo.ID` | `string` | Retain load-local ID independent of physical path or label. |
|
||||
| `SourceInfo.Label` | `string` | Retain safe display label separate from path and stable identity. |
|
||||
| `SourceInfo.Path` | `string` | Retain host-canonical/native-relative location where present; do not imply every source has a path. |
|
||||
| `SourceInfo.Destination` | `Path` | Retain effective mapping/sequence-container placement; it is not always an item's exact value path. |
|
||||
| `SourceInfo.PlacementMode` | `PlacementMode` | Retain explicit placement evidence, independent of acquisition kind. |
|
||||
| `SourceInfo.DestinationUnresolved` | `bool` | Discuss D5: DestinationIsFallback would describe the enclosing-container evidence more precisely. |
|
||||
| `SourceInfo.Layer` | `Layer` | Retain provenance tier independent of acquisition order and source kind. |
|
||||
| `SourceInfo.Kind` | `SourceKind` | Retain acquisition category rather than inferring it from path syntax. |
|
||||
| `SourceInfo.UnderlayIndex` | `int` | Retain one-based original tier position, zero otherwise; do not renumber around empty tiers. |
|
||||
| `SourceInfo.ImportedFrom` | `*Location` | Retain optional declaring location rather than a second parent-source object graph. |
|
||||
| `SourceInfo.VersionCheck` | `*SourceVersionCheck` | Discuss D3: VersionEvidence avoids implying that the checker ran or accepted anything. |
|
||||
| `SourceInfo.ImportSelections` | `[]ImportSelection` | Discuss D4: name the empty-selection-only scope explicitly. |
|
||||
| `ImportSelector` | `type ImportSelector string` | Retain path/directory/glob selector classification; do not combine it with source acquisition kind. |
|
||||
| `ImportSelectionOutcome` | `type ImportSelectionOutcome string` | Discuss D4: this public enum has only one supported outcome and may be redundant in an explicitly empty-selection record. |
|
||||
| `ImportSelection` | `type ImportSelection struct` | Discuss D4: EmptyImportSelection would accurately name the currently reported subset. |
|
||||
| `ImportSelection.Path` | `Path` | Retain declaration-local path if the record is renamed; not the selected source's destination. |
|
||||
| `ImportSelection.Location` | `Location` | Retain declaration mapping/source coordinates. |
|
||||
| `ImportSelection.Selector` | `ImportSelector` | Retain the authored selector kind; empty path, directory, and glob choices differ. |
|
||||
| `ImportSelection.Outcome` | `ImportSelectionOutcome` | Discuss D4: remove the constant-only none field if empty-only scope is made explicit. |
|
||||
| `SourceVersionCheck` | `type SourceVersionCheck struct` | Discuss D3: SourceVersionEvidence better describes observed declaration state without implying validation success. |
|
||||
| `SourceVersionCheck.Path` | `Path` | Retain requested source-local version path, including absent evidence. |
|
||||
| `SourceVersionCheck.Version` | `Version` | Retain absent/null/text observation, not an acceptance result. |
|
||||
| `SourceVersionCheck.Location` | `*Location` | Retain optional actual/nearest authored location. |
|
||||
| `SourceVersionCheck.Redacted` | `bool` | Retain explicit withheld-text indication; absence cannot be inferred from empty Text. |
|
||||
| `InheritanceStep` | `type InheritanceStep struct` | Retain explicit transfer/control provenance; it adds no acquired source or precedence layer. |
|
||||
| `InheritanceStep.From` | `Path` | Retain contributor semantic path. |
|
||||
| `InheritanceStep.To` | `Path` | Retain destination semantic path. |
|
||||
| `InheritanceStep.Reference` | `Path` | Retain controlling extends path, distinct from the inherited value path. |
|
||||
| `InheritanceStep.Locations` | `[]Location` | Retain directive coordinates rather than mislabeling them as value coordinates. |
|
||||
| `Origin` | `type Origin struct` | Retain composite locations, precedence, operation and transfer trace; no generic untyped metadata bag. |
|
||||
| `Origin.Layer` | `Layer` | Retain provenance classification; inheritance does not invent another layer. |
|
||||
| `Origin.Locations` | `[]Location` | Retain ordered composite origins; one SourceID field would lose strict composition evidence. |
|
||||
| `Origin.Operation` | `ChangeKind` | Retain inspection ChangeKind, including decoding/inheritance (D6 concerns recording input only). |
|
||||
| `Origin.Inheritance` | `[]InheritanceStep` | Retain concrete derivation steps separately from acquired source locations. |
|
||||
| `FieldInfo` | `type FieldInfo struct` | Retain compact effective presence/null/origin record. |
|
||||
| `FieldInfo.Explicit` | `bool` | Retain input presence independent of the final Go zero value. |
|
||||
| `FieldInfo.Null` | `bool` | Retain explicit null independently of missing lookup and scalar zero. |
|
||||
| `FieldInfo.Origin` | `Origin` | Retain final effective attribution. |
|
||||
| `Change` | `type Change struct` | Retain attribution-only history and Previous origins; not redundant with ValueChange (C3). |
|
||||
| `Change.Path` | `Path` | Retain reported operation path, which may enclose the queried affected path. |
|
||||
| `Change.Origin` | `Origin` | Retain this definition/operation's attribution. |
|
||||
| `Change.Previous` | `[]Origin` | Retain preceding selected origins after shape filtering. |
|
||||
| `Change.Reasons` | `[]string` | Retain ordered recorded reasons without embedding configuration values. |
|
||||
| `Change.Dependencies` | `[]Path` | Retain original recorded dependency paths independently of replay grouping. |
|
||||
| `Result.SourceContext` | `func (r *Result[T]) SourceContext(id string) (ResourceContext, bool)` | Retain passive lookup by origin source ID; do not open or cache resource files implicitly. |
|
||||
|
||||
### Safe value inspection and comparison
|
||||
|
||||
Declarations: [diff.go](../../diff.go), [inspection.go](../../inspection.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Difference` | `type Difference struct` | Retain a separate changed-region record; effective comparison is not a provenance/history diff. |
|
||||
| `Difference.Path` | `Path` | Retain canonical changed-region coordinates. |
|
||||
| `Difference.Left` | `ValueSnapshot` | Retain the safe left snapshot under combined classification, not independently rendered context. |
|
||||
| `Difference.Right` | `ValueSnapshot` | Retain left/right symmetry; before/after belongs to chronological history. |
|
||||
| `Difference.HiddenChanged` | `bool` | Retain actual concealed-change indication distinct from Redacted. |
|
||||
| `Diff` | `func Diff[T any](left, right *Result[T]) ([]Difference, error)` | Retain same-type frozen-result comparison; nil/zero results are errors and credential contents are not compared. |
|
||||
| `ValueSnapshot` | `type ValueSnapshot struct` | Retain detached Present/Redacted/YAML state, shared by explanation and diff without exposing parser nodes. |
|
||||
| `ValueSnapshot.Present` | `bool` | Retain absence/deletion versus explicit null; YAML text alone is insufficient. |
|
||||
| `ValueSnapshot.Redacted` | `bool` | Retain a structured mask flag; consumers must not interpret marker text. |
|
||||
| `ValueSnapshot.YAML` | `string` | Retain deterministic safe text, not a reloadable or mutable node promise. |
|
||||
| `ValueChange` | `type ValueChange struct` | Retain value-bearing replay record distinct from metadata Change; do not force both projections into one record (C3). |
|
||||
| `ValueChange.Before` | `ValueSnapshot` | Retain the earlier safe value with explicit presence. |
|
||||
| `ValueChange.After` | `ValueSnapshot` | Retain chronological before/after symmetry. |
|
||||
| `ValueChange.Origin` | `Origin` | Retain attribution from shared evidence, without requiring identical metadata/explanation event lists. |
|
||||
| `ValueChange.Reasons` | `[]string` | Retain ordered application intent; consumers remain responsible for safe prose. |
|
||||
| `ValueChange.Dependencies` | `[]Path` | Retain recorded dependencies and their semantic paths. |
|
||||
| `SecretReferenceInfo` | `type SecretReferenceInfo struct` | Retain explicit resolved-reference annotation; it is different from redaction classification. |
|
||||
| `SecretReferenceInfo.Name` | `string` | Retain public environment name only; do not add credential content. |
|
||||
| `Explanation` | `type Explanation struct` | Retain effective state plus safe chronological values; not a second mutable configuration object. |
|
||||
| `Explanation.Path` | `Path` | Retain the queried semantic path. |
|
||||
| `Explanation.Effective` | `ValueSnapshot` | Retain final safe value even for a path now absent. |
|
||||
| `Explanation.Origin` | `Origin` | Retain effective provenance separate from each historical event's origin. |
|
||||
| `Explanation.History` | `[]ValueChange` | Retain ValueChange replay, not a converted Metadata.History list (C3). |
|
||||
| `Explanation.SecretReference` | `*SecretReferenceInfo` | Retain optional resolved-reference name; a classified path can withhold this annotation. |
|
||||
|
||||
### Typed credentials and explicit bootstrap
|
||||
|
||||
Declarations: [secret.go](../../secret.go), [secrets.go](../../secrets.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `Secret` | `type Secret struct` | Retain opaque environment reference and explicit credential access. Zero is unresolved/unspecified; a surviving non-pointer Secret is required. |
|
||||
| `SecretEnv` | `func SecretEnv(name string) Secret` | Retain an explicit reference constructor; no plain-string credential constructor. |
|
||||
| `Secret.EnvName` | `func (s Secret) EnvName() string` | Retain public reference access separate from credential access. |
|
||||
| `Secret.Value` | `func (s Secret) Value() string` | Retain deliberate credential accessor; empty means unresolved. Do not combine it with formatting. |
|
||||
| `Secret.IsResolved` | `func (s Secret) IsResolved() bool` | Retain cheap explicit resolution-state query despite nonempty-value validation. |
|
||||
| `Secret.Format` | `func (s Secret) Format(state fmt.State, _ rune)` | Retain fmt.Formatter safety for all formatting verbs; do not replace it with only String. |
|
||||
| `Secret.MarshalJSON` | `func (s Secret) MarshalJSON() ([]byte, error)` | Retain name-only json.Marshaler behavior, not a credential serialization API. |
|
||||
| `SecretFileStatus` | `type SecretFileStatus string` | Retain distinct loaded/skipped/not-applied/failed states; partial bootstrap needs more than success/failure. |
|
||||
| `SecretFileResult` | `type SecretFileResult struct` | Retain value-free per-entry bootstrap outcome, distinct from load sources. |
|
||||
| `SecretFileResult.Name` | `string` | Retain entry/environment name, never content. |
|
||||
| `SecretFileResult.Path` | `string` | Retain safe source-file attribution. |
|
||||
| `SecretFileResult.Status` | `SecretFileStatus` | Retain structured status, not interpretation of report text. |
|
||||
| `SecretFileResult.ExistingEmpty` | `bool` | Retain empty-existing winner evidence for skipped entries; zero does not mean that an entry was loaded. |
|
||||
| `SecretsReport` | `type SecretsReport struct` | Retain detached bootstrap outcomes; zero/nil has no entries. A report may accompany an error. |
|
||||
| `SecretsReport.Entries` | `func (r *SecretsReport) Entries() []SecretFileResult` | Retain copied filename-ordered outcomes. |
|
||||
| `SecretsReport.WriteTo` | `func (r *SecretsReport) WriteTo(output io.Writer) (int64, error)` | Retain io.WriterTo-compatible count/error convention; do not force other renderers to return counts just for symmetry. |
|
||||
| `SecretsError` | `type SecretsError struct` | Retain separate bootstrap failure type: reports partial external effects, unlike a failed configuration load. |
|
||||
| `SecretsError.Error` | `func (e *SecretsError) Error() string` | Retain safe value-free summary and errors.As support through its type. |
|
||||
| `SecretsError.Diagnostics` | `func (e *SecretsError) Diagnostics() []Diagnostic` | Retain detached bootstrap diagnostics; no automatic raw filesystem/environment error unwrapping. |
|
||||
| `LoadSecretsDir` | `func LoadSecretsDir(directory string) (*SecretsReport, error)` | Discuss D7: BootstrapSecretsDir makes process-environment mutation clearer than another Load name. |
|
||||
|
||||
### Diagnostics and failed loads
|
||||
|
||||
Declarations: [diagnostic.go](../../diagnostic.go), [records.go](../../records.go).
|
||||
|
||||
| Entry | Declaration/type | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `LoadError` | `type LoadError struct` | Retain a concrete inspectable load error and errors.As workflow; zero/nil renders a safe generic failure. |
|
||||
| `LoadError.Error` | `func (e *LoadError) Error() string` | Retain value-free summary, not detailed raw parser/application errors. |
|
||||
| `LoadError.Diagnostics` | `func (e *LoadError) Diagnostics() []Diagnostic` | Retain detached structured diagnostics; do not add Unwrap that defeats safe error boundaries. |
|
||||
| `LoadError.Sources` | `func (e *LoadError) Sources() []SourceInfo` | Retain acquired-source evidence available before failure. |
|
||||
| `WriteDiagnostics` | `func WriteDiagnostics(output io.Writer, diagnostics []Diagnostic) error` | Retain writer-first safe deterministic rendering; do not promise stable prose beyond established contracts. |
|
||||
| `Severity` | `type Severity string` | Retain the two diagnostic severities; no arbitrary numeric priority surface. |
|
||||
| `Code` | `type Code string` | Retain detailed stable diagnostic codes; collapsing categories would lose useful consumer handling. |
|
||||
| `Diagnostic` | `type Diagnostic struct` | Retain a common safe problem record across loading, hooks, and bootstrap. |
|
||||
| `Diagnostic.Code` | `Code` | Retain stable machine classification rather than matching prose. |
|
||||
| `Diagnostic.Severity` | `Severity` | Retain explicit severity, independent of diagnostic code spelling. |
|
||||
| `Diagnostic.Path` | `Path` | Retain semantic configuration path, not a filesystem location. |
|
||||
| `Diagnostic.Message` | `string` | Retain human-readable safe context; no automatic secret inference or blanket prose-stability guarantee. |
|
||||
| `Diagnostic.Primary` | `*Location` | Retain optional primary source position; synthetic reports may lack one. |
|
||||
| `Diagnostic.Related` | `[]Location` | Retain ordered related locations; do not homogenize semantically meaningful composition claimant order. |
|
||||
|
||||
### Constants and their values
|
||||
|
||||
Each constant is separately listed. Retaining a diagnostic code preserves a
|
||||
machine-readable failure distinction; consumers should not infer new guarantees
|
||||
from a name or assume that the current set can never grow. D8 notes the one
|
||||
unproduced named code identified for discussion. This catalog does not prescribe
|
||||
new diagnostics or merge existing failure categories.
|
||||
|
||||
#### PlacementMode constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `PlacementModeMapping` | `PlacementMode = 0` | Retain explicit mapping default. |
|
||||
| `PlacementModeSequenceItem` | `PlacementMode = 1` | Retain explicit record placement. |
|
||||
|
||||
#### SourceVersionMode constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `SourceVersionModeDefault` | `SourceVersionMode = 0` | Discuss D3: MainOnly names the existing zero behavior more precisely. |
|
||||
| `SourceVersionModeIfPresent` | `SourceVersionMode = 1` | Retain; keep the documented main-absence checker behavior. |
|
||||
| `SourceVersionModeRequired` | `SourceVersionMode = 2` | Retain participating-document requirement and existing NoSource behavior. |
|
||||
|
||||
#### Layer constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `LayerDefault` | `Layer = "default"` | Retain distinct provenance layer. |
|
||||
| `LayerBase` | `Layer = "base"` | Retain established base/main attribution; no automatic wire-value rename. |
|
||||
| `LayerUnderlay` | `Layer = "underlay"` | Retain distinct provenance layer. |
|
||||
| `LayerOverlay` | `Layer = "overlay"` | Retain distinct provenance layer. |
|
||||
| `LayerCLI` | `Layer = "cli"` | Retain distinct provenance layer. |
|
||||
| `LayerResolution` | `Layer = "resolution"` | Retain distinct provenance layer. |
|
||||
|
||||
#### SourceKind constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `SourceKindFile` | `SourceKind = "file"` | Retain distinct acquisition kind, independent of layer. |
|
||||
| `SourceKindFilesystem` | `SourceKind = "filesystem"` | Retain distinct acquisition kind, independent of layer. |
|
||||
| `SourceKindBytes` | `SourceKind = "bytes"` | Retain distinct acquisition kind, independent of layer. |
|
||||
| `SourceKindReader` | `SourceKind = "reader"` | Retain distinct acquisition kind, independent of layer. |
|
||||
| `SourceKindMapping` | `SourceKind = "mapping"` | Retain distinct acquisition kind, independent of layer. |
|
||||
| `SourceKindCLI` | `SourceKind = "cli"` | Retain distinct acquisition kind, independent of layer. |
|
||||
|
||||
#### ChangeKind constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `ChangeDecoding` | `ChangeKind = "decoding"` | Retain inspection value; D6 addresses its misleading availability as a Record argument. |
|
||||
| `ChangeInheritance` | `ChangeKind = "inheritance"` | Retain inspection value; D6 addresses its misleading availability as a Record argument. |
|
||||
| `ChangeDerivedDefault` | `ChangeKind = "derived_default"` | Retain recorded/inspected intent; discuss the Record input shape in D6. |
|
||||
| `ChangeNormalization` | `ChangeKind = "normalization"` | Retain recorded/inspected intent; discuss the Record input shape in D6. |
|
||||
|
||||
#### Severity constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `SeverityError` | `Severity = "error"` | Retain explicit diagnostic severity. |
|
||||
| `SeverityWarning` | `Severity = "warning"` | Retain explicit diagnostic severity. |
|
||||
|
||||
#### Code constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `CodeSourceInvalid` | `Code = "source.invalid"` | Retain distinct source / invalid classification. |
|
||||
| `CodeSourceMissing` | `Code = "source.missing"` | Retain distinct source / missing classification. |
|
||||
| `CodeSourcePermission` | `Code = "source.permission"` | Retain distinct source / permission classification. |
|
||||
| `CodeSourceRead` | `Code = "source.read"` | Retain distinct source / read classification. |
|
||||
| `CodeSourceNotRegular` | `Code = "source.not_regular"` | Retain distinct source / not regular classification. |
|
||||
| `CodeSourceLimitExceeded` | `Code = "source.limit_exceeded"` | Retain distinct source / limit exceeded classification. |
|
||||
| `CodeSourceReaderUsed` | `Code = "source.reader_used"` | Retain distinct source / reader used classification. |
|
||||
| `CodeYAMLInvalid` | `Code = "yaml.invalid"` | Retain distinct yaml / invalid classification. |
|
||||
| `CodeYAMLEmpty` | `Code = "yaml.empty"` | Retain distinct yaml / empty classification. |
|
||||
| `CodeYAMLMultipleDocuments` | `Code = "yaml.multiple_documents"` | Retain distinct yaml / multiple documents classification. |
|
||||
| `CodeYAMLRootType` | `Code = "yaml.root_type"` | Retain distinct yaml / root type classification. |
|
||||
| `CodeYAMLDuplicateKey` | `Code = "yaml.duplicate_key"` | Retain distinct yaml / duplicate key classification. |
|
||||
| `CodeYAMLTag` | `Code = "yaml.tag"` | Retain distinct yaml / tag classification. |
|
||||
| `CodeYAMLAnchor` | `Code = "yaml.anchor"` | Retain distinct yaml / anchor classification. |
|
||||
| `CodeYAMLAlias` | `Code = "yaml.alias"` | Retain distinct yaml / alias classification. |
|
||||
| `CodeYAMLMergeKey` | `Code = "yaml.merge_key"` | Retain distinct yaml / merge key classification. |
|
||||
| `CodeYAMLScalar` | `Code = "yaml.scalar"` | Retain distinct yaml / scalar classification. |
|
||||
| `CodeYAMLDepthLimit` | `Code = "yaml.depth_limit"` | Retain distinct yaml / depth limit classification. |
|
||||
| `CodeYAMLNodeLimit` | `Code = "yaml.node_limit"` | Retain distinct yaml / node limit classification. |
|
||||
| `CodeSchemaRoot` | `Code = "schema.root"` | Retain distinct schema / root classification. |
|
||||
| `CodeSchemaUnsupportedType` | `Code = "schema.unsupported_type"` | Retain distinct schema / unsupported type classification. |
|
||||
| `CodeSchemaAmbiguousField` | `Code = "schema.ambiguous_field"` | Retain distinct schema / ambiguous field classification. |
|
||||
| `CodeSchemaTag` | `Code = "schema.tag"` | Retain distinct schema / tag classification. |
|
||||
| `CodeSchemaTextCodec` | `Code = "schema.text_codec"` | Retain distinct schema / text codec classification. |
|
||||
| `CodeSchemaInline` | `Code = "schema.inline"` | Retain distinct schema / inline classification. |
|
||||
| `CodeSchemaCycle` | `Code = "schema.cycle"` | Retain distinct schema / cycle classification. |
|
||||
| `CodeSchemaProjectionLimit` | `Code = "schema.projection_limit"` | Retain distinct schema / projection limit classification. |
|
||||
| `CodeSchemaCodec` | `Code = "schema.codec"` | Retain distinct schema / codec classification. |
|
||||
| `CodeSchemaNonFinite` | `Code = "schema.non_finite"` | Retain distinct schema / non finite classification. |
|
||||
| `CodeSchemaDynamicType` | `Code = "schema.dynamic_type"` | Retain distinct schema / dynamic type classification. |
|
||||
| `CodeDecodeType` | `Code = "decode.type"` | Retain distinct decode / type classification. |
|
||||
| `CodeDecodeNull` | `Code = "decode.null"` | Retain distinct decode / null classification. |
|
||||
| `CodeDecodeRange` | `Code = "decode.range"` | Retain distinct decode / range classification. |
|
||||
| `CodeDecodeDuration` | `Code = "decode.duration"` | Retain distinct decode / duration classification. |
|
||||
| `CodeDecodeText` | `Code = "decode.text"` | Retain distinct decode / text classification. |
|
||||
| `CodeDecodeUnknownField` | `Code = "decode.unknown_field"` | Retain distinct decode / unknown field classification. |
|
||||
| `CodeDecodeArrayLength` | `Code = "decode.array_length"` | Retain distinct decode / array length classification. |
|
||||
| `CodeOptionsInvalid` | `Code = "options.invalid"` | Retain distinct options / invalid classification. |
|
||||
| `CodeOptionsUnavailable` | `Code = "options.unavailable"` | Discuss D8: confirm intentional reservation or remove the unproduced classification. |
|
||||
| `CodeImportDisabled` | `Code = "import.disabled"` | Retain distinct import / disabled classification. |
|
||||
| `CodeImportInvalid` | `Code = "import.invalid"` | Retain distinct import / invalid classification. |
|
||||
| `CodeImportMissing` | `Code = "import.missing"` | Retain distinct import / missing classification. |
|
||||
| `CodeImportPermission` | `Code = "import.permission"` | Retain distinct import / permission classification. |
|
||||
| `CodeImportRead` | `Code = "import.read"` | Retain distinct import / read classification. |
|
||||
| `CodeImportOutsideRoot` | `Code = "import.outside_root"` | Retain distinct import / outside root classification. |
|
||||
| `CodeImportCycle` | `Code = "import.cycle"` | Retain distinct import / cycle classification. |
|
||||
| `CodeImportRepeated` | `Code = "import.repeated"` | Retain distinct import / repeated classification. |
|
||||
| `CodeImportLimitExceeded` | `Code = "import.limit_exceeded"` | Retain distinct import / limit exceeded classification. |
|
||||
| `CodeImportVersion` | `Code = "import.version"` | Retain distinct import / version classification. |
|
||||
| `CodeSequenceInvalid` | `Code = "sequence.invalid"` | Retain distinct sequence / invalid classification. |
|
||||
| `CodeSequenceConflict` | `Code = "sequence.conflict"` | Retain distinct sequence / conflict classification. |
|
||||
| `CodeUnderlayVersion` | `Code = "underlay.version"` | Retain distinct underlay / version classification. |
|
||||
| `CodeOverlayInvalid` | `Code = "overlay.invalid"` | Retain distinct overlay / invalid classification. |
|
||||
| `CodeOverlayMissing` | `Code = "overlay.missing"` | Retain distinct overlay / missing classification. |
|
||||
| `CodeOverlayOutsideRoot` | `Code = "overlay.outside_root"` | Retain distinct overlay / outside root classification. |
|
||||
| `CodeOverlayRepeated` | `Code = "overlay.repeated"` | Retain distinct overlay / repeated classification. |
|
||||
| `CodeOverlayDirective` | `Code = "overlay.directive"` | Retain distinct overlay / directive classification. |
|
||||
| `CodeOverlayVersion` | `Code = "overlay.version"` | Retain distinct overlay / version classification. |
|
||||
| `CodeOverlayLimitExceeded` | `Code = "overlay.limit_exceeded"` | Retain distinct overlay / limit exceeded classification. |
|
||||
| `CodeComposeInvalid` | `Code = "compose.invalid"` | Retain distinct compose / invalid classification. |
|
||||
| `CodeComposeConflict` | `Code = "compose.conflict"` | Retain distinct compose / conflict classification. |
|
||||
| `CodeVersionType` | `Code = "version.type"` | Retain distinct version / type classification. |
|
||||
| `CodeVersionError` | `Code = "version.error"` | Retain distinct version / error classification. |
|
||||
| `CodeVersionWarning` | `Code = "version.warning"` | Retain distinct version / warning classification. |
|
||||
| `CodeVersionHookFailed` | `Code = "version.hook_failed"` | Retain distinct version / hook failed classification. |
|
||||
| `CodeResolveError` | `Code = "resolve.error"` | Retain distinct resolve / error classification. |
|
||||
| `CodeResolveWarning` | `Code = "resolve.warning"` | Retain distinct resolve / warning classification. |
|
||||
| `CodeResolveHookFailed` | `Code = "resolve.hook_failed"` | Retain distinct resolve / hook failed classification. |
|
||||
| `CodeResolveInvalidPath` | `Code = "resolve.invalid_path"` | Retain distinct resolve / invalid path classification. |
|
||||
| `CodeResolveInvalidKind` | `Code = "resolve.invalid_kind"` | Retain distinct resolve / invalid kind classification. |
|
||||
| `CodeResolveInvalidRecord` | `Code = "resolve.invalid_record"` | Retain distinct resolve / invalid record classification. |
|
||||
| `CodeResolveInvalidTarget` | `Code = "resolve.invalid_target"` | Retain distinct resolve / invalid target classification. |
|
||||
| `CodeResolveInvalidValue` | `Code = "resolve.invalid_value"` | Retain distinct resolve / invalid value classification. |
|
||||
| `CodeResolveUnrecordedChange` | `Code = "resolve.unrecorded_change"` | Retain distinct resolve / unrecorded change classification. |
|
||||
| `CodeValidateError` | `Code = "validate.error"` | Retain distinct validate / error classification. |
|
||||
| `CodeValidateWarning` | `Code = "validate.warning"` | Retain distinct validate / warning classification. |
|
||||
| `CodeValidateInvalidPath` | `Code = "validate.invalid_path"` | Retain distinct validate / invalid path classification. |
|
||||
| `CodeValidateMutation` | `Code = "validate.mutation"` | Retain distinct validate / mutation classification. |
|
||||
| `CodeSecretsInvalidPath` | `Code = "secrets.invalid_path"` | Retain distinct secrets / invalid path classification. |
|
||||
| `CodeSecretsMissing` | `Code = "secrets.missing"` | Retain distinct secrets / missing classification. |
|
||||
| `CodeSecretsPermission` | `Code = "secrets.permission"` | Retain distinct secrets / permission classification. |
|
||||
| `CodeSecretsRead` | `Code = "secrets.read"` | Retain distinct secrets / read classification. |
|
||||
| `CodeSecretsNotRegular` | `Code = "secrets.not_regular"` | Retain distinct secrets / not regular classification. |
|
||||
| `CodeSecretsInvalidName` | `Code = "secrets.invalid_name"` | Retain distinct secrets / invalid name classification. |
|
||||
| `CodeSecretsLimitExceeded` | `Code = "secrets.limit_exceeded"` | Retain distinct secrets / limit exceeded classification. |
|
||||
| `CodeSecretsInvalidValue` | `Code = "secrets.invalid_value"` | Retain distinct secrets / invalid value classification. |
|
||||
| `CodeSecretsApplyFailed` | `Code = "secrets.apply_failed"` | Retain distinct secrets / apply failed classification. |
|
||||
| `CodeSecretRequired` | `Code = "secret.required"` | Retain distinct secret / required classification. |
|
||||
| `CodeSecretInvalidName` | `Code = "secret.invalid_name"` | Retain distinct secret / invalid name classification. |
|
||||
| `CodeSecretUnset` | `Code = "secret.unset"` | Retain distinct secret / unset classification. |
|
||||
| `CodeSecretEmpty` | `Code = "secret.empty"` | Retain distinct secret / empty classification. |
|
||||
| `CodeOverrideInvalidPath` | `Code = "override.invalid_path"` | Retain distinct override / invalid path classification. |
|
||||
| `CodeOverrideInvalidLabel` | `Code = "override.invalid_label"` | Retain distinct override / invalid label classification. |
|
||||
| `CodeOverrideInvalidValue` | `Code = "override.invalid_value"` | Retain distinct override / invalid value classification. |
|
||||
| `CodeOverrideDuplicate` | `Code = "override.duplicate"` | Retain distinct override / duplicate classification. |
|
||||
| `CodeOverrideOverlap` | `Code = "override.overlap"` | Retain distinct override / overlap classification. |
|
||||
| `CodeOverrideDirective` | `Code = "override.directive"` | Retain distinct override / directive classification. |
|
||||
| `CodeOverrideVersion` | `Code = "override.version"` | Retain distinct override / version classification. |
|
||||
| `CodeOverrideLimitExceeded` | `Code = "override.limit_exceeded"` | Retain distinct override / limit exceeded classification. |
|
||||
| `CodeExplainLimitExceeded` | `Code = "explain.limit_exceeded"` | Retain distinct explain / limit exceeded classification. |
|
||||
| `CodeInheritanceInvalid` | `Code = "inheritance.invalid"` | Retain distinct inheritance / invalid classification. |
|
||||
| `CodeInheritanceMissing` | `Code = "inheritance.missing"` | Retain distinct inheritance / missing classification. |
|
||||
| `CodeInheritanceCycle` | `Code = "inheritance.cycle"` | Retain distinct inheritance / cycle classification. |
|
||||
| `CodeInheritanceVersion` | `Code = "inheritance.version"` | Retain distinct inheritance / version classification. |
|
||||
|
||||
#### ImportSelector constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `ImportSelectorPath` | `ImportSelector = "path"` | Retain distinct authored selector kind. |
|
||||
| `ImportSelectorDirectory` | `ImportSelector = "dir"` | Retain distinct authored selector kind. |
|
||||
| `ImportSelectorGlob` | `ImportSelector = "glob"` | Retain distinct authored selector kind. |
|
||||
|
||||
#### ImportSelectionOutcome constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `ImportSelectionNone` | `ImportSelectionOutcome = "none"` | Discuss D4: redundant if the record explicitly describes empty selections only. |
|
||||
|
||||
#### SecretFileStatus constants
|
||||
|
||||
| Entry | Type and value | Assessment |
|
||||
| --- | --- | --- |
|
||||
| `SecretFileLoaded` | `SecretFileStatus = "loaded"` | Retain successful application outcome. |
|
||||
| `SecretFileSkippedExisting` | `SecretFileStatus = "skipped_existing"` | Retain existing-environment winner, including empty winners. |
|
||||
| `SecretFileNotApplied` | `SecretFileStatus = "not_applied"` | Retain planned but unapplied outcome, distinct from a failed entry. |
|
||||
| `SecretFileFailed` | `SecretFileStatus = "failed"` | Retain failed-entry outcome, distinct from unapplied siblings. |
|
||||
|
||||
## Validation And Next Discussion
|
||||
|
||||
The catalog has one row for each of the 372 exported declarations/members
|
||||
found by the syntax inventory. The callable/type/field signatures were read from
|
||||
the declarations rather than reconstructed from the graph. The graph supported
|
||||
navigation and reference checks; its inventory alone was not treated as proof of
|
||||
completeness. Implicit interfaces include error for `*LoadError` and
|
||||
`*SecretsError`, fmt.Stringer for Path, fmt.Formatter and json.Marshaler for Secret,
|
||||
and io.WriterTo for `*SecretsReport`; there are no separate exported interface types.
|
||||
|
||||
Useful existing behavioral owners include the public facade boundary and
|
||||
compatibility tests, callback-context guards, consumer examples, and metadata,
|
||||
secret, explanation, source-version, discovery, and sequence tests linked by the
|
||||
[development guide](../development.md#task-to-test-map). This documentation-only
|
||||
review adds no tests or production changes. Validation completed: the syntax
|
||||
comparison found exactly one catalog row for all 372 entries, with no missing
|
||||
entries. The local documentation-link and public-facade boundary tests passed,
|
||||
as did the whitespace check.
|
||||
|
||||
Next, decide which D1–D8 candidates merit changes. Prepare a scoped roadmap/plan
|
||||
only for accepted decisions and update the declaration comments, contract owners,
|
||||
examples and consumers together during that implementation. Preserve the current
|
||||
loading, presence, composition, callback-lifetime, redaction and ownership contracts
|
||||
unless a separately explicit decision changes one. No release or tagging action
|
||||
is authorized by this review.
|
||||
Reference in New Issue
Block a user