Record maintainability and complexity audit findings

This commit is contained in:
2026-10-25 16:18:07 +00:00
parent 320d0c5f43
commit a80273433e

283
docs/roadmap/complexity.md Normal file
View File

@@ -0,0 +1,283 @@
# Maintainability And Complexity Audit
Reviewed revision: `320d0c5f439a059a7081cf7532c9b5d65891f5c7`.
Audit date: 2026-10-25.
## Scope And Method
This audit examines readability, responsibility boundaries, state handoffs, and
interface duplication. It covers the root facade, engine subsystems, document
and YAML boundaries, and relevant tests, documentation, and prior refactoring
decisions. It does not propose new feature behavior or performance guarantees.
The codebase-memory graph was refreshed and its function spans, cyclomatic and
cognitive complexity estimates, parameter counts, and caller traces were used
to select code for inspection. The graph contains 907 non-test functions/methods
and 940 test-file functions/methods. Its index excludes examples, documentation,
and scripts; tracked-file inventory and direct inspection supplemented it.
Standalone `gocyclo`, `gocognit`, and `golangci-lint` were unavailable. No tool
installation, benchmarks, or empirical performance testing was needed.
The tracked production inventory, excluding examples, contains 16 root Go files,
97 engine files, one document file, and two YAML adapter files: approximately
22,612 physical lines overall. The largest production files are `compose.go`
(599 lines), `history.go` (568), `overlay.go` (564), and `discovery.go` (540).
These measurements locate responsibilities; they are not refactoring thresholds.
Representative graph measurements:
| Function | File | Function span | Cognitive estimate |
| --- | --- | ---: | ---: |
| `valueOperation.clone` | `internal/engine/clone.go` | 130 | 104 |
| `acquireInput` | `internal/engine/acquisition.go` | 202 | 99 |
| `valueBuilder.build` | `internal/engine/value.go` | 104 | 93 |
| `decodeValue` | `internal/engine/decode.go` | 115 | 85 |
| `selectGlobImportCandidates` | `internal/engine/glob.go` | 112 | 73 |
| `assembleLayeredInput` | `internal/engine/load_request.go` | 146 | 63 |
| `executeLoadRequest` | `internal/engine/load.go` | 198 | 56 |
| `mergeOverlayMappingsWithCollectionPolicy` | `internal/engine/overlay.go` | 163 | 46 |
Spans include comments and blank lines; scores are tool estimates for comparative
triage, not standardized quality grades. Source review, rather than a numerical
cutoff, determines the findings below.
## Architectural Assessment
The [implemented architecture](../internal/overview.md) remains coherent: the
root owns the supported public types and explicit conversions; the engine owns
loading and frozen inspection; the document package owns the ordered tree; and
the YAML adapter isolates the upstream parser. Ordinary and layered loads share
one normalized request and execution lifecycle. Imports and inheritance retain
their separate scopes and use the common dependency scheduler.
The recent consolidation improved import ownership and file boundaries, separated
inheritance preparation/combination/finalization, shared filesystem facts, and
clarified schema and projection responsibilities. The findings concern remaining
maintenance opportunities, not a recommendation to repeat that refactor or split
the engine into a package per subsystem.
The existing [correctness](correctness.md) and [efficiency](efficiency.md) audits
remain separate owners of their findings. No additional runtime defect was
established in this pass. Related risks are referenced rather than duplicated.
## 1. Remove The Test-Only Eager Acquisition Mode
Status: open. Priority: medium. Scope: engine acquisition and its test helpers.
**Relevant code.** `inputAcquisitionRequest.deferAssembly` in
[acquisition.go](../../internal/engine/acquisition.go), and the final branch of
`acquireImportedWithAccess` in
[import_acquisition.go](../../internal/engine/import_acquisition.go), lines
206–224. Production construction sites in `executeLoadRequest`
([load.go](../../internal/engine/load.go), lines 210–214) and
`acquireUnderlayGroupScopes`
([load_request.go](../../internal/engine/load_request.go), lines 243–246) both
set this flag to true.
**Complexity.** Caller tracing found no production caller selecting eager
composition. The false branch remains exercised by private test entry helpers,
including `testImportRequest`, `testAcquireImport`, and `testAcquireInput` in
[imports_test.go](../../internal/engine/imports_test.go). Acquisition therefore
maintains two return protocols: an acquired scope with a nil composed root, or
an already composed document. The eager branch reconstructs `assembledInput`
and reenters composition from the acquisition function, while the published
loading paths perform that phase later. Tests can consequently exercise a
different handoff from the one consumers use.
**Potential refactoring.** Make acquisition return retained scopes consistently
and remove the unused production mode. Tests needing composed values can invoke
the real composition phase explicitly through a small test-only convenience
helper. Keep acquisition-only tests acquisition-only. Preserve handle closure,
pending-item placement reservation, accounting, diagnostics, and meaningful
composition coverage. This removes a private alternative path, not support for
any public loading workflow. Do not infer that other nil-state or directive
contexts are unused without separately tracing their callers.
## 2. Clarify Acquisition And Load-Coordinator Phase Handoffs
Status: open. Priority: medium. Scope: engine, across acquisition, version,
assembly, and lifecycle files.
**Relevant code.** `acquireInput` in
[acquisition.go](../../internal/engine/acquisition.go), `executeLoadRequest` in
[load.go](../../internal/engine/load.go), and `assembleLayeredInput` in
[load_request.go](../../internal/engine/load_request.go).
**Complexity.** `acquireInput` still combines search outcome handling, parsing,
disabled-directive checks, three directive/version-processing branches,
import-versus-standalone dispatch, placement/accounting, and passive context
construction. Its version-processing section repeats source-ID extraction,
failure handling, and `mainProcessing` updates, while the layered branch must
extract directives separately and restore them after version processing.
The coordinators also manipulate several intentionally distinct representations:
parsed source, local payload, acquired scope, composed root, inherited root,
local/protected version paths, and effective version evidence. For example,
`executeLoadRequest` includes a substantial ordinary-load resource-context
fallback between assembly and inheritance, while `assembleLayeredInput` repeats
declaration gathering, tier application, and destination projection for underlays
and main. Understanding a source-version or placement change requires tracing
both those mechanics and the overall phase order through long functions.
**Potential refactoring.** Extract cohesive operations for processing one parsed
input's directives/version evidence and for placing/registering a standalone
contribution. Reuse the existing processing result and acquisition state rather
than inventing another context hierarchy. Isolate the substantial context and
structural-expansion operations from the top-level lifecycle, and consider one
helper for the repeated tier application/projection handoff. Keep phase order,
error exits, and source acquisition order readable in the coordinators.
Extraction is useful only when it gives one operation a clear owner and result;
moving blocks into helpers that still share the same mutable surrounding locals
would not solve the problem. Preserve the deliberate ordinary/layered overlay
differences, default versus enabled version-check timing, warning accumulation,
exactly-once callbacks, and load-wide versus tier-local state. This is separate
from finding 1: removing the eager branch does not simplify these active paths.
## 3. Separate Collection-Policy Preparation From Merge Evidence Application
Status: open. Priority: medium. Scope: engine precedence and inheritance.
**Relevant code.** `mergeOverlayMappingsWithCollectionPolicy` and
`applyOverlaySequenceEvidence` in
[overlay.go](../../internal/engine/overlay.go);
`resolveStrictCollectionPolicies` in
[compose.go](../../internal/engine/compose.go);
`combineInheritanceTemplate` in
[inheritance_combine.go](../../internal/engine/inheritance_combine.go); and the
policy/origin helpers in
[inheritance_collection.go](../../internal/engine/inheritance_collection.go).
**Complexity.** The overlay coordinator prepares claims and declaration groups,
discovers applicable destinations, resolves operations, configures recursive
merge callbacks, accumulates diagnostics/history, reconciles declaration-only
policies after mutation, and sorts the output. Its callbacks close over several
maps and mutate the assembly while reporting failures through an outer slice.
`applyOverlaySequenceEvidence` then mixes policy retirement, displaced origins,
replacement records, copied incoming origins, and record-policy reconstruction.
There is also concrete duplicated bookkeeping: overlay and inheritance callbacks
both translate a sequence result into replace-versus-mapped history descriptors;
overlay evidence and `applyInheritanceSequencePolicy` both rebuild record policy
order from lower/higher item projections. The shared `combineSequence` is already
the correct value-combination owner, but maintainers must separately coordinate
these sidecar translations when its result representation changes.
**Potential refactoring.** Give overlay preparation, value/evidence application,
and declaration-only finalization explicit local boundaries. Let a small
operation owner or result carry the diagnostic and evidence collections currently
captured by the large callbacks. Share the straightforward history-descriptor
construction; evaluate a common projected-record selection helper while retaining
named precedence and inheritance ownership rules.
Do not merge the distinct origin semantics or hide mode-lifetime differences
behind a generic strategy framework. Keep `combineSequence` and the recursive
mapping merge shared, and consume their result rather than recomputing values.
Coordinate this work with collection-policy findings 1–2 in the
[efficiency audit](efficiency.md) so clearer preparation does not preserve or
introduce repeated whole-table copying/scanning. Preserve replacement ordering,
pending declarations, new versus retained record owners, and stable history.
## 4. Extract Patterned Glob Entry Preparation From Traversal Orchestration
Status: open. Priority: medium. Scope: engine import discovery.
**Relevant code.** `selectGlobImportCandidates` in
[glob.go](../../internal/engine/glob.go), especially its patterned-entry branch.
The corresponding directory-search entry operation already has a named owner,
`prepareImportDirectoryEntry` in
[import_selection.go](../../internal/engine/import_selection.go).
**Complexity.** The glob function nests pattern-segment traversal, parent
candidates, and directory entries. Its innermost branch handles matching,
hidden-entry policy, lexical and access paths, containment, link inspection,
follow behavior, intermediate-directory eligibility, native/host identity
differences, canonicalization, labels, and candidate creation. Set-union and
component-advancement mechanics are interleaved with those admission decisions,
making the important lexical-versus-canonical distinction difficult to follow.
**Potential refactoring.** Extract a glob-specific entry preparation operation
returning an admitted candidate, an ordinary skip, or a diagnostic. Leave pattern
component advancement, literal segments, per-component union/sorting, and final
exclusion/requiredness visible in the selector. Reuse low-level filesystem facts
where semantics coincide; directory and glob admission need not become one
general visitor interface. Preserve inspection/error order, entry accounting,
native namespace spelling, and selected-file validation. This is a readability
change, not a proposal to change the selected discovery algorithm or add caching.
## 5. Simplify Redundant Facade-To-Engine Input Transport
Status: open. Priority: low. Scope: root facade and engine, across packages.
**Relevant code.** Root `toEngineImportDefaults` in
[conversion.go](../../conversion.go), `toEngineOptions` in
[lifecycle.go](../../lifecycle.go), and `toEngineLayeredOptions` in
[layered.go](../../layered.go); engine transport/private defaults and option
decorators in [import_spec.go](../../internal/engine/import_spec.go), lines
46–87; and placement transport helpers in
[destination.go](../../internal/engine/destination.go), lines 23–39.
**Complexity.** Import defaults have a root-owned public representation, an
engine facade-transport representation, and a second engine-private representation
with the same five fields. Conversion constructs the transport, an option
decorator converts it to private defaults, and load normalization snapshots it
again. Placement similarly requires constructing an engine record and then
passing it through a separate setter-style helper. Even a simple control change
has multiple mechanical mapping sites across the package boundary.
Caller and text-reference checks also found no uses of the standalone
`PlacementModeFromFacade` and `PlacementModeForFacade` functions beyond their
declarations. The similarly named `SourceInfoPlacementModeForFacade` is used by
root source-record conversion and is not dead code. The unused pair adds a
second apparent placement conversion route for maintainers to investigate.
Root type ownership and explicit conversion are deliberate and useful; the
opportunity is the extra engine-internal representation/construction protocol,
not eliminating the facade. The public wrappers do not need to change.
**Potential refactoring.** Remove the two unused enum bridges. Then consider one
engine representation of import-default input values and a straightforward
construction surface on the existing engine option/input records. Review inert
placement decorators as part of the same bounded cleanup. Keep helpers that
actually encode domain rules or ownership conversion, and keep
definition/registration compilation separate from transport.
Avoid adding another universal request DTO, code generator, reflective mapper,
or adapter framework to remove a few mechanical functions.
Preserve root-owned named types and signatures, invalid-enum validation,
nil-versus-empty patterns, pointer/slice snapshots, and the two public loading
entry points. Exported declarations inside Go's `internal` boundary need not
become public root aliases or bridge methods. Pursue this low-priority cleanup
only if it reduces total concepts and mapping sites, rather than relocating them.
## Areas To Retain And Review Boundaries
- The clone traversal was explicitly reviewed and retained in the completed
consolidation record at `34cbc0d:docs/roadmap/consolidation.md`. Its high score
is not new evidence for reopening that work. `valueBuilder.build`, typed
decoding, and the YAML translator also contain substantial but recognizable
type dispatch; no repository-wide visitor framework is recommended.
- The cohesive engine package and explicit root record conversions follow
[ADR 0006](../adr/0006-public-facade.md). Function/file counts alone do not
justify package extraction, public API consolidation, or generated conversions.
- The shared scheduler, mapping/sequence primitives, recently separated
inheritance phases, schema field assembly, and resolved filesystem facts remain
useful common owners. Preserve those improvements.
- `history.go` and the larger integration-test files have several substantial
routines, but file size alone did not establish another prioritized refactor.
The deferred replay, copy-handoff, caching, and contributor-index experiments
remain in [future work](future.md#3-performance-experiments).
These are opportunities for further consideration, not a staged implementation
plan or new release blockers. Favor the concrete unused-mode removal and active
acquisition/collection boundaries before lower-value transport cleanup. A later
implementation should preserve the established contracts and relevant behavioral
coverage; complexity scores, helper counts, and private call sequences should
not become test thresholds or repository guarantees.
## Validation
This audit changes only this findings document. No production code, tests,
dependencies, policies, or public contracts are changed. Validation consists of
the existing local documentation-link test and `git diff --check`; runtime and
performance measurements are not part of this review.