Record underlay implementation review findings
This commit is contained in:
119
docs/roadmap/remediation.md
Normal file
119
docs/roadmap/remediation.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Filesystem Sources and Underlays Review Findings
|
||||
|
||||
Reviewed implementation: `cbf031a`, covering the implementation commits after
|
||||
`27df838`. These findings concern the agreed source, precedence, and provenance
|
||||
contracts in the [roadmap](sources-and-layering.md) and
|
||||
[implementation plan](implementation.md). No implementation fixes are included
|
||||
in this review.
|
||||
|
||||
## 1. Preserve underlay attribution for sequence descendants (P2)
|
||||
|
||||
**Owners:** `internal/engine/load_request.go:174`,
|
||||
`internal/engine/compose.go:113`, and `internal/engine/load_result.go:39`.
|
||||
|
||||
The first nonempty underlay seeds the accumulator using a composed document.
|
||||
Composition claims intentionally stop at sequences because sequences are atomic
|
||||
for strict conflict checking. Consequently its origin index contains the sequence
|
||||
itself but not sequence elements or nested fields inside them. Relabeling that
|
||||
index as `LayerUnderlay` leaves those descendants with the decoder's default
|
||||
`LayerBase` attribution. Later overlay insertion recursively collects origins,
|
||||
so attribution also depends on whether the same input is the initial tier.
|
||||
|
||||
**Reproduction:** load `NoSource()` as main and an underlay mapping containing
|
||||
`items: [{name: first}]`. The load succeeds. `Metadata.Lookup` reports
|
||||
`LayerUnderlay` for `/items`, but `LayerBase` for `/items/0` and `/items/0/name`,
|
||||
all referring to the same underlay source ID. `Metadata.History` repeats the
|
||||
incorrect base attribution for the descendants.
|
||||
|
||||
**Required correction:** cover every surviving input node when assigning origins,
|
||||
including descendants of atomic sequence values. Keep strict-composition claims
|
||||
and sequence replacement semantics unchanged. Ensure resolver entry metadata,
|
||||
returned metadata, and explanation agree with each acquired source's role.
|
||||
|
||||
**Regression coverage:** a first-tier sequence containing nested mappings,
|
||||
including an imported contribution, retained beneath main input; compare with
|
||||
equivalent later-tier insertion. Assert source ID and underlay role for elements
|
||||
and their fields as well as the sequence container.
|
||||
|
||||
## 2. Preserve later same-role mapping contributions in history (P2)
|
||||
|
||||
**Owner:** `internal/engine/load_metadata.go:128` (`applyOverlayMetadata`).
|
||||
|
||||
The final-history reconciliation skips an effective origin when its layer and
|
||||
operation match the last recorded event. Multiple underlay tiers share
|
||||
`LayerUnderlay`, so those two fields do not establish that the recorded event
|
||||
contains the effective source contributors. The comparison cannot distinguish
|
||||
contributions from repeated tiers of the same role.
|
||||
|
||||
**Reproduction:** with no main document, supply three ordered underlays:
|
||||
|
||||
```yaml
|
||||
# Tier 1
|
||||
x: 1
|
||||
# Tier 2
|
||||
x: {}
|
||||
# Tier 3
|
||||
x: {y: 2}
|
||||
```
|
||||
|
||||
The final `/x` origin correctly includes sources 2 and 3. Its metadata history
|
||||
contains only source 1 and the replacement by source 2; source 3's contribution
|
||||
is omitted. The returned effective provenance and chronological history disagree.
|
||||
|
||||
**Required correction:** determine whether the effective provenance is already
|
||||
represented using contributing origins, not merely layer/operation equality.
|
||||
Preserve meaningful same-tier-role contributions without duplicating events
|
||||
already represented. Keep existing replacement, equal-value, and displaced-path
|
||||
history behavior.
|
||||
|
||||
**Regression coverage:** scalar-to-mapping replacement followed by a recursive
|
||||
mapping contribution from a later underlay; assert the final contributor appears
|
||||
in history and that metadata and explanation describe consistent source origins.
|
||||
|
||||
## 3. Honor standard filesystem names in resource contexts (P2)
|
||||
|
||||
**Owner:** `internal/engine/resource_context.go:38` and `:77`.
|
||||
|
||||
Both resource adapters reject any name containing a backslash, beyond
|
||||
`fs.ValidPath` validation. A backslash is a legal literal filename character in
|
||||
an `io/fs` namespace and on the Linux host filesystem. Native source acquisition
|
||||
accepts such paths, but resource-context access rejects them. This introduces an
|
||||
unrequested path restriction and applies host-style separator assumptions to a
|
||||
generic filesystem.
|
||||
|
||||
**Reproduction:** supply `fstest.MapFS` with `config.yml` and a resource named
|
||||
`asset\name`. Loading `config.yml` succeeds; the resource name passes
|
||||
`fs.ValidPath` and can be read directly from the supplied filesystem.
|
||||
The equivalent read through the returned context fails with `fs.ErrInvalid`:
|
||||
|
||||
```go
|
||||
context, ok := result.SourceContext(sourceID) // ok is true
|
||||
_, err := fs.ReadFile(context.FS, name) // rejects the valid name
|
||||
```
|
||||
|
||||
**Required correction:** use standard slash-path validation for native contexts
|
||||
and preserve literal characters accepted by the supplied filesystem. For host
|
||||
contexts, use platform-appropriate conversion while preserving the existing
|
||||
contained-root behavior; native namespace semantics must not inherit Windows
|
||||
separator restrictions. This is not a request to broaden root traversal or
|
||||
change symlink guarantees.
|
||||
|
||||
**Regression coverage:** access a literal-backslash resource through both native
|
||||
Open/ReadDir paths where applicable and a Linux host context; retain invalid-path
|
||||
and outside-root coverage. Avoid rejecting valid native names on other platforms
|
||||
merely because the corresponding host path would be invalid there.
|
||||
|
||||
## Review validation
|
||||
|
||||
- `./scripts/check.sh` passed (tests, vet, build, formatting, and whitespace).
|
||||
- `GOPROXY=off go test -race ./...` passed.
|
||||
- Broad-catalog and many-small-tier benchmarks ran with `-benchmem` and 50 ms
|
||||
samples. Allocated bytes scaled approximately with tested input sizes; these
|
||||
measurements are review evidence, not timing gates.
|
||||
- A temporary external Go program reproduced all three findings through the
|
||||
public API; no diagnostic/test code was added to the repository.
|
||||
|
||||
The intended facade, shared synchronous lifecycle, strict within-tier composition,
|
||||
recursive precedence, mapping snapshotting, and passive resource-context design
|
||||
are present. The findings above are correctness gaps within that design, not a
|
||||
recommendation to expand its scope or change its selected combination semantics.
|
||||
Reference in New Issue
Block a user