diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index d2db5a4..299348f 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -156,3 +156,73 @@ deterministic root-open failure or a permission fixture that verifies the platform actually denies the open. The check concerns root access during loading. Later resource operations retain their existing filesystem semantics. [Layered loading contract](../config.md#layered-loading) + +## Algorithm audit + +The audit covers the implementation from `ae0c94d` through `18af2d2`. +It analyzes the work and storage implied by the code. The planning decisions +on precedence, selection, import traversal, and context ownership remain the +basis for the analysis. + +| Stage | Work and storage reviewed | +| --- | --- | +| 1 | Facade conversion and binding preflight visit each input once, with work proportional to the option data copied or validated. The existing diagnostic sort orders failures after collection. | +| 2 | Binding and namespace anchor selection use a fixed number of linear passes. The temporary context table and per-input resolved copies are avoidable. See Finding 7. | +| 3 | Declaration preparation visits each declaration once, but resolving a shared pattern default copies its entries for each inheriting directory declaration. See Finding 6. | +| 4 | The no-base path prepares declarations and scans requiredness before assembling one payload. The pattern copies contribute a multiplicative cost even though no traversal runs. Root assembly duplication is already tracked in Finding 2. | +| 5 | Context selection considers at most three choices, and the shared overlay loader makes one linear pass over acquired sources for duplicate detection. The native path correctness issue remains in Finding 1. | +| 6 | The example uses one layered load and the existing declaration, import, and tier assembly paths. It adds no separate scan for companion files. | +| 7 | Source budgeting and evidence reuse the existing load state. The focused suite passed for the new loading paths and their import, overlay, and sequence-item dependencies. | +| 8 | The documented single discovery and deferred selection match the call path reviewed above. The identified improvements concern private storage and work, with no change to the selected algorithms or public behavior. | + +## Finding 6 — Skipped directory imports copy shared patterns per declaration + +**Priority P3.** With no base, `acquireContextlessInput` calls +`prepareImportDeclarations`, which holds one full resolved policy per +declaration. Each directory declaration that inherits the load-wide +`ImportDefaults.Pattern` allocates a separate copy of the pattern slice. +The no-base path reads the requiredness from those policies and never uses +the copied patterns. +[No-base preparation](../../internal/engine/contextless_imports.go#L22) +· [Policy slice](../../internal/engine/import_acquisition.go#L245) +· [Default pattern copy](../../internal/engine/import_spec.go#L122) + +For **D** directory declarations that inherit **P** patterns, this adds +Θ(D × P) string-entry copies in time and peak temporary storage for a +single input. It becomes quadratic when both counts grow together. The +pattern text itself is shared. The work is separate from validating the +default patterns once and from matching filenames on the bound path. + +Keep the load-owned default pattern snapshot and borrow its slice read-only +when resolving an inherited pattern. The selectors already read these +entries without modifying them. This reduces pattern storage and copying +to Θ(D + P) for the policy headers and shared entries. A separate option +is to share the context-independent validation and have the no-base path +retain only the policy fields it actually uses. Either approach should +preserve explicit empty pattern overrides, validation of skipped +declarations, and existing diagnostic sites. +[Default snapshot](../../internal/engine/import_spec.go#L72) +· [Filename filtering](../../internal/engine/import_selection.go#L230) + +## Finding 7 — Each derived companion keeps a copy of the same tier context + +**Priority P3.** `acquireUnderlayGroupScopes` allocates a context pointer +slot for every input, even in tiers with no bindings. Each derived input +then gets an addressable copy of the same main context with the same +cleared main path and receiving-tier host token. The separate copies +carry no input-specific values. +[Context preparation](../../internal/engine/load_request.go#L276) + +For a tier with **U** inputs and **C** derived bindings, this adds Θ(U) +temporary pointer slots and Θ(C) context values. The passes themselves +are linear. Build at most one read-only derived context per tier and +select it during the existing ordered anchor and acquisition passes. +Use the private input's selected concrete context for fallbacks. This +reduces the extra context bookkeeping to O(1), excluding the existing +input list and native namespace map. Preserve per-input validation and +handle ownership, as well as first-input namespace anchors. Share the +context description without caching filesystem observations. + +Complexity was derived from the loops and copies above. Focused Go 1.27.3 +tests passed for the companion workflow, layered loading, overlays, +import policy and selection, and sequence-item placement.