Skip to content

Commit 9caf29c

Browse files
committed
fix: scan top-down for effective base in all resolvers
Change base-finding to scan from highest priority downward to find the nearest replace layer, then compose only layers above it. Prevents evaluation of irrelevant lower layers (e.g. a wrap without placeholder below a higher-priority replace) across Python, bash, and PowerShell.
1 parent 4ba13ae commit 9caf29c

3 files changed

Lines changed: 26 additions & 33 deletions

File tree

scripts/bash/common.sh

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -537,19 +537,13 @@ except Exception:
537537
return 0
538538
fi
539539

540-
# Compose bottom-up: start from the effective base.
541-
# Find the highest-priority replace layer that sits below composing layers.
542-
# Skip non-replace layers below any replace (they have no base to compose onto).
543-
local content=""
544-
local has_base=false
540+
# Find the effective base: scan from highest priority (index 0) downward
541+
# to find the nearest replace layer. Only compose layers above that base.
545542
local base_idx=-1
546543
local i
547-
for (( i=count-1; i>=0; i-- )); do
548-
local strat="${layer_strategies[$i]}"
549-
if [ "$strat" = "replace" ]; then
544+
for (( i=0; i<count; i++ )); do
545+
if [ "${layer_strategies[$i]}" = "replace" ]; then
550546
base_idx=$i
551-
elif [ $base_idx -ge 0 ]; then
552-
# Found a non-replace above a replace — this is where composition starts
553547
break
554548
fi
555549
done
@@ -558,7 +552,8 @@ except Exception:
558552
return 1 # no base layer found
559553
fi
560554

561-
# Read the base content; start composing from the layer above the base
555+
# Read the base content; compose layers above the base (higher priority)
556+
local content
562557
content=$(cat "${layer_paths[$base_idx]}"; printf x)
563558
content="${content%x}"
564559

scripts/powershell/common.ps1

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -532,15 +532,13 @@ except Exception:
532532
return (Get-Content $layerPaths[0] -Raw)
533533
}
534534

535-
# Find the effective base: highest-priority replace below composing layers.
536-
# Skip non-replace layers below any replace (they have no base to compose onto).
535+
# Find the effective base: scan from highest priority (index 0) downward
536+
# to find the nearest replace layer. Only compose layers above that base.
537537
$baseIdx = -1
538-
for ($i = $layerPaths.Count - 1; $i -ge 0; $i--) {
539-
$strat = $layerStrategies[$i]
540-
if ($strat -eq 'replace') {
538+
for ($i = 0; $i -lt $layerPaths.Count; $i++) {
539+
if ($layerStrategies[$i] -eq 'replace') {
541540
$baseIdx = $i
542-
} elseif ($baseIdx -ge 0) {
543-
break # non-replace above a replace — composition starts here
541+
break
544542
}
545543
}
546544
if ($baseIdx -lt 0) { return $null }

src/specify_cli/presets.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,24 +2920,24 @@ def resolve_content(
29202920
# layers is ordered highest-priority first. We process in reverse.
29212921
reversed_layers = list(reversed(layers))
29222922

2923-
# Find the base content: scan bottom-up for the highest-priority
2924-
# "replace" layer that can serve as a base. Non-replace layers below
2925-
# the base are irrelevant (they have nothing to compose onto).
2926-
content = None
2927-
start_idx = 0
2928-
for i, layer in enumerate(reversed_layers):
2923+
# Find the effective base: scan from highest priority (layers[0]) downward
2924+
# to find the nearest replace layer. Only compose layers above that base.
2925+
# layers is highest-priority first; reversed_layers is lowest first.
2926+
base_layer_idx = None # index in layers[] (highest-priority first)
2927+
for idx, layer in enumerate(layers):
29292928
if layer["strategy"] == "replace":
2930-
content = layer["path"].read_text(encoding="utf-8")
2931-
start_idx = i + 1
2932-
elif content is not None:
2933-
# Found a non-replace layer above a replace — stop scanning.
2934-
# The replace layer(s) form the base; composition starts here.
2929+
base_layer_idx = idx
29352930
break
2936-
# Non-replace layers below any replace are skipped (no base yet)
29372931

2938-
# If no base content found, there's nothing to compose onto
2939-
if content is None:
2940-
return None
2932+
if base_layer_idx is None:
2933+
return None # no replace base found
2934+
2935+
# Convert to reversed_layers index
2936+
base_reversed_idx = len(layers) - 1 - base_layer_idx
2937+
content = layers[base_layer_idx]["path"].read_text(encoding="utf-8")
2938+
# Compose only the layers above the base (higher priority = lower index in layers,
2939+
# higher index in reversed_layers). Process bottom-up from base+1.
2940+
start_idx = base_reversed_idx + 1
29412941

29422942
# For command composition, strip frontmatter from each layer to avoid
29432943
# leaking YAML metadata into the composed body. The highest-priority

0 commit comments

Comments
 (0)