Skip to content

fix: resolve deep merge type-override regression and optimize describe affected --upload (250x speedup)#2248

Merged
aknysh merged 3 commits intomainfrom
aknysh/test-upload-flag
Mar 25, 2026
Merged

fix: resolve deep merge type-override regression and optimize describe affected --upload (250x speedup)#2248
aknysh merged 3 commits intomainfrom
aknysh/test-upload-flag

Conversation

@aknysh
Copy link
Copy Markdown
Member

@aknysh aknysh commented Mar 25, 2026

what

Two critical fixes addressing regressions and performance issues:

1. Deep merge type-mismatch regression

  • Remove overly strict type-check guards in deepMergeNative that rejected stack configs where lists are overridden with {} (empty map), scalars, or null — while overriding a list with {} is technically a misconfiguration (the correct way is []), this pattern exists in production configs and worked with the previous mergo-based merge
  • The native merge (PR perf(merge): replace mergo pre-copy loop with reflection-free native deep merge (3.5× faster) #2201) added type-check guards that were stricter than the previous mergo-based merge. The previous merge allowed any type to override any other type at the same key. The native merge rejected some of these overrides (specifically list→map and list→scalar), breaking configs that relied on the previous behavior
  • Example of the misconfiguration that was broken: allow_ingress_from_vpc_accounts: {} overriding a list of maps — the correct way would be allow_ingress_from_vpc_accounts: [], but the {} pattern worked before and must not break in a patch release
  • This fix preserves backward compatibility to prevent regressions — a future release may add warnings for type-mismatched overrides to guide users toward correct patterns

2. describe affected --upload timeout on large infrastructures (~250x speedup)

  • --upload forces --include-dependents, which called ExecuteDescribeDependents for every affected component — each call did a full ExecuteDescribeStacks resolution from scratch with no caching
  • For large infrastructures with ~2,400 affected components, this resulted in ~2,400 full stack resolutions (~1s each = 40+ minutes, never completing)
  • Applied three incremental optimizations:
    • Cache ExecuteDescribeStacks result: called once instead of N times (40+ min → ~3.5 min)
    • Cache component lookup: extract component sections from cached stacks instead of calling ExecuteDescribeComponent per item (~3.5 min → ~1:54)
    • Pre-built reverse dependency index: build index once from stacks data, then O(1) lookup per component instead of O(stacks × components) scan (~1:54 → ~10s)

why

Deep merge regression

  • PR perf(merge): replace mergo pre-copy loop with reflection-free native deep merge (3.5× faster) #2201 (native deep merge, 3.5x faster) introduced type-mismatch guards that are too strict for real-world Atmos configurations
  • Some production configs override inherited lists with {} (empty map) instead of [] (empty list) — while this is a misconfiguration, it worked with the previous mergo-based merge and must not break in a patch release
  • Stack-processing commands (list stacks, describe stacks, etc.) fail on affected configs

describe affected --upload timeout

  • The --upload flag is used by Atmos Pro integration to upload affected stacks
  • Large infrastructures with many components across many stacks generate thousands of affected items
  • The O(N × full_stack_resolution) cost made the command unusable, blocking CI/CD pipelines

Test results

Deep merge type-override

Test Result
11 new type-override unit tests (list→map, list→scalar, list→nil, list→bool, nested, with slice flags) All pass
Minimal stack fixture (merge-type-override) with 3 override patterns atmos list stacks succeeds
All existing merge tests (updated 6 tests from error→success expectations) All pass
Merge package coverage 92.5% overall, 100% on merge_native.go functions

describe affected --upload optimization

Metric Before After
describe affected (no dependents) ~7s ~7s (unchanged)
describe affected --include-dependents 40+ min (never completes) ~10s
Payload size (with dependents) N/A ~1.2 MB
Test Result
All 30+ existing affected/dependent tests All pass
8 new dependency index tests (build, lookup, self-reference, abstract skip, multi-stack, helmfile, edge cases) All pass
Changed function coverage: all above 80% (findDependentsByScan 95.3%, findDependentsFromIndex 100%, buildDependencyIndex 81.1%) Above threshold

references

Summary by CodeRabbit

  • Bug Fixes

    • Restored deep-merge behavior so stack overrides can replace list-typed values with maps, scalars or nulls.
    • Fixed describe-affected --upload hang/timeout by caching stack resolution and using an indexed dependent lookup.
  • Documentation

    • Added detailed pages describing both regressions, root causes, and verification steps.
  • Tests

    • Added unit and end-to-end tests covering cross-type merge overrides and dependency-index dependent resolution.

…ffected --upload (~250x speedup)

Two critical fixes:

1. Native deep merge type-mismatch regression (PR #2201):
   Remove slice→map and slice→non-slice type-check guards that broke real configs
   where users override lists with {} (empty map), scalars, or null. The WithOverride
   contract means src always wins regardless of type differences. Atmos never used
   mergo's WithTypeCheck — the guards were an incorrect assumption.

2. describe affected --upload timeout on large infrastructures:
   Cache ExecuteDescribeStacks result, component lookups, and build a reverse
   dependency index — all computed once instead of per-affected-component.
   For 2,422 affected items: 40+ min → ~10s (~250x speedup).

   Three optimizations applied incrementally:
   - Cache ExecuteDescribeStacks: 40+ min → ~3.5 min
   - Cache component lookup from cached stacks: ~3.5 min → ~1:54
   - Pre-built reverse dependency index: ~1:54 → ~10s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@aknysh aknysh added the patch A minor, backward compatible change label Mar 25, 2026
@aknysh aknysh requested review from a team as code owners March 25, 2026 04:12
@github-actions github-actions bot added the size/l Large size PR label Mar 25, 2026
@aknysh aknysh self-assigned this Mar 25, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 25, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA ace6cb9.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@aknysh aknysh requested review from milldr and osterman March 25, 2026 04:22
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

📝 Walkthrough

Walkthrough

Reverts native deep-merge type-mismatch guards so stack imports can override values across types, and optimizes describe-affected by caching stack resolution and adding a reverse-dependency index to avoid repeated full-stack scans.

Changes

Cohort / File(s) Summary
Deep Merge Type Override
pkg/merge/merge_native.go, pkg/merge/merge_native_test.go, pkg/merge/merge_no_duplicate_errors_test.go, pkg/merge/merge_type_override_test.go
Removed slice↔map and slice↔non-slice guards in deepMergeNative; non-map branches now apply WithOverride semantics to replace destination with source regardless of runtime type; updated/added tests to assert successful overrides.
Describe Affected Optimization
internal/exec/describe_affected_utils_2.go, internal/exec/describe_dependents.go, internal/exec/describe_dependents_index.go, internal/exec/describe_dependents_index_test.go
Single upfront ExecuteDescribeStacks and buildDependencyIndex(stacks) added; DescribeDependentsArgs accepts cached Stacks/DepIndex; dependent lookup uses index when provided and falls back to scan; cached data propagated through recursive calls.
Integration & Validation Tests
tests/merge_type_override_test.go, internal/exec/validate_stacks_test.go
Added end-to-end integration test exercising type-changing merges; updated validation tests to expect success (no error) under type-override semantics instead of merge-type errors.
Fixtures
tests/fixtures/scenarios/merge-type-override/...
New scenario fixtures (Terraform components: eks-cluster, s3-bucket, vpc) plus catalog defaults and sandbox overlays to exercise list→map, list→scalar, and list→null override cases used by integration tests.
Docs
docs/fixes/2026-03-24-deep-merge-type-mismatch-regression.md, docs/fixes/2026-03-24-describe-affected-upload-timeout.md
Documented the deep-merge regression and chosen fix (restore override semantics) and described the describe affected --upload hang root cause and applied caching/indexing optimizations.

Sequence Diagram(s)

sequenceDiagram
    participant Client as DescribeAffected
    participant Utils as describe_affected_utils_2
    participant Deps as describe_dependents
    participant Index as describe_dependents_index
    participant Stacks as StacksResolver

    Client->>Utils: addDependentsToAffected(affected)
    Utils->>Stacks: ExecuteDescribeStacks() (single upfront call)
    Stacks-->>Utils: stacks map
    Utils->>Index: buildDependencyIndex(stacks)
    Index-->>Utils: depIndex

    loop per affected component
        Utils->>Deps: ExecuteDescribeDependents(target, Stacks=stacks, DepIndex=depIndex)
        alt DepIndex present
            Deps->>Index: findDependentsFromIndex(target)
            Index-->>Deps: dependents (index lookup)
        else Fallback
            Deps->>Stacks: scan stacks for dependents
            Stacks-->>Deps: dependents (linear scan)
        end
        Deps-->>Utils: dependents
    end

    Utils-->>Client: aggregated dependents
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • nitrocode
  • osterman
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the two main fixes: deep merge type-override regression and describe affected --upload performance optimization with quantified impact (250x speedup).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aknysh/test-upload-flag

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/merge/merge_no_duplicate_errors_test.go (1)

73-91: ⚠️ Potential issue | 🟡 Minor

Use t.Cleanup for os.Stderr restoration to avoid global-state leakage on early failure.

If this test fails/panics before the restore block runs, later tests can inherit a redirected stderr. A cleanup hook makes teardown deterministic.

Suggested fix
 	oldStderr := os.Stderr
-	r, w, _ := os.Pipe()
+	r, w, err := os.Pipe()
+	if err != nil {
+		t.Fatalf("os.Pipe failed: %v", err)
+	}
 	os.Stderr = w
+	t.Cleanup(func() {
+		_ = w.Close()
+		_ = r.Close()
+		os.Stderr = oldStderr
+	})
@@
-	w.Close()
-	os.Stderr = oldStderr
+	_ = w.Close()
 	stderrOutput := <-stderrChan
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/merge/merge_no_duplicate_errors_test.go` around lines 73 - 91, The test
currently replaces os.Stderr and restores it manually which can leak global
state if the test fails; replace the manual restore with t.Cleanup to always
restore os.Stderr and close the pipe: after creating oldStderr, r, w and
assigning os.Stderr = w, register a t.Cleanup that closes w and sets os.Stderr =
oldStderr (and optionally closes r if you create one that needs closing),
ensuring the stderr-reading goroutine still receives EOF and the stderrChan is
drained; keep the Merge(...) call and subsequent reading of stderrOutput
unchanged but remove the explicit restore/close block so cleanup runs even on
early failure.
🧹 Nitpick comments (5)
internal/exec/describe_dependents_index.go (1)

79-82: Consider debug logging on decode failures.

Silent return on line 82 is safe but may complicate debugging malformed stack configs. A log.Debug call here would help troubleshoot edge cases.

💡 Optional: Add debug logging
 	var stackComponentVars schema.Context
 	if err := mapstructure.Decode(stackComponentVarsSection, &stackComponentVars); err != nil {
+		log.Debug("Failed to decode component vars during index build", "component", stackComponentName, "stack", stackName, "error", err)
 		return
 	}

This would require adding the log import.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/describe_dependents_index.go` around lines 79 - 82, The
mapstructure.Decode call that populates stackComponentVars currently returns
silently on error; update the error path in the describe dependents logic to log
the decode failure (include the error and the offending
stackComponentVarsSection) via the package logger (add the log import) before
returning so malformed stack configs are visible; locate the
mapstructure.Decode(...) surrounding stackComponentVars and insert a
log.Debug/log.Debugf call that prints context and err prior to the return.
internal/exec/describe_dependents_index_test.go (1)

86-106: Consider adding test for enabled: false components.

This test covers metadata.type: "abstract", but isAbstractOrDisabled also skips components with enabled: false via isComponentEnabled. A separate test case would strengthen coverage.

💡 Optional: Test disabled component filtering
func TestBuildDependencyIndex_SkipsDisabledComponents(t *testing.T) {
	stacks := map[string]any{
		"dev-use1": map[string]any{
			"components": map[string]any{
				"terraform": map[string]any{
					"disabled-component": map[string]any{
						"metadata": map[string]any{"enabled": false},
						"vars":     map[string]any{"region": "us-east-1"},
						"dependencies": map[string]any{
							"components": []any{
								map[string]any{"component": "vpc"},
							},
						},
					},
				},
			},
		},
	}
	idx := buildDependencyIndex(stacks)
	assert.Empty(t, idx["vpc"], "disabled components should be skipped")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/describe_dependents_index_test.go` around lines 86 - 106, Add a
new unit test named TestBuildDependencyIndex_SkipsDisabledComponents that
mirrors TestBuildDependencyIndex_SkipsAbstractComponents but uses metadata with
"enabled": false to exercise the isComponentEnabled/isAbstractOrDisabled path;
construct a stacks map with a "disabled-component" under terraform that lists a
dependency on "vpc", call buildDependencyIndex(stacks), and assert that
idx["vpc"] is empty to ensure disabled components are skipped.
docs/fixes/2026-03-24-describe-affected-upload-timeout.md (1)

89-96: Line number references may become stale.

The table references specific line ranges (e.g., describe_affected_utils_2.go:532-594). Consider using function names or permanent links instead, as line numbers shift with future changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md` around lines 89 -
96, The table uses fragile file line ranges which will break as code moves;
replace those line-range references with stable identifiers and links: use
function names like addDependentsToAffected, addDependentsToDependents,
ExecuteDescribeDependents, UploadAffectedStacks and the Upload flag handler (or
its concrete function name) instead of line numbers, and where possible convert
each cell to a permanent permalink (commit/blob URL) or package+symbol reference
(package.Function) so reviewers can resolve the target reliably; update the
table rows to omit all numeric line ranges and use these names/permalinks
consistently.
pkg/merge/merge_type_override_test.go (1)

71-77: Consider setting wantVal for the nested case for consistency.

The special-case handling at lines 85-89 works, but setting wantVal to the expected nested structure would make the table more self-documenting.

Optional: fill in wantVal for completeness
 		{
 			name:    "nested: list inside map overridden by empty map",
 			dst:     map[string]any{"vars": map[string]any{"accounts": []any{"a"}}},
 			src:     map[string]any{"vars": map[string]any{"accounts": map[string]any{}}},
 			wantKey: "vars",
-			// After deep merge, vars.accounts should be the empty map from src.
+			wantVal: map[string]any{"accounts": map[string]any{}},
 		},

Then update the assertion:

-		if tt.wantKey == "vars" {
-			// Nested case: check vars.accounts.
-			vars, ok := tt.dst["vars"].(map[string]any)
-			require.True(t, ok)
-			assert.Equal(t, map[string]any{}, vars["accounts"])
-		} else {
-			assert.Equal(t, tt.wantVal, tt.dst[tt.wantKey])
-		}
+		assert.Equal(t, tt.wantVal, tt.dst[tt.wantKey])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/merge/merge_type_override_test.go` around lines 71 - 77, Add a wantVal
entry to the "nested: list inside map overridden by empty map" test case and set
it to the expected nested result (e.g. map[string]any{"vars":
map[string]any{"accounts": map[string]any{}}}), then update the assertion that
currently checks only wantKey to also assert equality against wantVal so the
test is self-documenting and verifies the nested value as well.
pkg/merge/merge_native.go (1)

16-25: Remove the unused isMapValue helper function.

It's not called anywhere in the codebase—the rg search confirms only its definition and documentation exist. Since the type-mismatch guards that relied on it have been removed, this function is dead code and should be cleaned up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/merge/merge_native.go` around lines 16 - 25, The helper function
isMapValue is dead code and should be removed: delete the entire isMapValue
function declaration from pkg/merge/merge_native.go (the function named
isMapValue and its body) and ensure no other code references it (rg already
shows none); run the build/tests to confirm removal didn't affect anything.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/fixtures/scenarios/merge-type-override/atmos.yaml`:
- Around line 1-13: Add an integration test that exercises the
"merge-type-override" fixture end-to-end by changing the working directory to
"./fixtures/scenarios/merge-type-override" (use t.Chdir) and invoking the
stack-composition pipeline via the command layer (call the same entry used by
integration tests that cover "source-provisioner" and "yaml-functions-in-lists"
— e.g., the CLI entry or ComposeStacks/stack composition function). The test
should run the composition, capture the composed stack output, and assert that
the type-override behavior from deepMergeNative is preserved in the final
composed result; mirror the structure and assertions from the existing
integration tests that exercise composition through the command layer.

---

Outside diff comments:
In `@pkg/merge/merge_no_duplicate_errors_test.go`:
- Around line 73-91: The test currently replaces os.Stderr and restores it
manually which can leak global state if the test fails; replace the manual
restore with t.Cleanup to always restore os.Stderr and close the pipe: after
creating oldStderr, r, w and assigning os.Stderr = w, register a t.Cleanup that
closes w and sets os.Stderr = oldStderr (and optionally closes r if you create
one that needs closing), ensuring the stderr-reading goroutine still receives
EOF and the stderrChan is drained; keep the Merge(...) call and subsequent
reading of stderrOutput unchanged but remove the explicit restore/close block so
cleanup runs even on early failure.

---

Nitpick comments:
In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md`:
- Around line 89-96: The table uses fragile file line ranges which will break as
code moves; replace those line-range references with stable identifiers and
links: use function names like addDependentsToAffected,
addDependentsToDependents, ExecuteDescribeDependents, UploadAffectedStacks and
the Upload flag handler (or its concrete function name) instead of line numbers,
and where possible convert each cell to a permanent permalink (commit/blob URL)
or package+symbol reference (package.Function) so reviewers can resolve the
target reliably; update the table rows to omit all numeric line ranges and use
these names/permalinks consistently.

In `@internal/exec/describe_dependents_index_test.go`:
- Around line 86-106: Add a new unit test named
TestBuildDependencyIndex_SkipsDisabledComponents that mirrors
TestBuildDependencyIndex_SkipsAbstractComponents but uses metadata with
"enabled": false to exercise the isComponentEnabled/isAbstractOrDisabled path;
construct a stacks map with a "disabled-component" under terraform that lists a
dependency on "vpc", call buildDependencyIndex(stacks), and assert that
idx["vpc"] is empty to ensure disabled components are skipped.

In `@internal/exec/describe_dependents_index.go`:
- Around line 79-82: The mapstructure.Decode call that populates
stackComponentVars currently returns silently on error; update the error path in
the describe dependents logic to log the decode failure (include the error and
the offending stackComponentVarsSection) via the package logger (add the log
import) before returning so malformed stack configs are visible; locate the
mapstructure.Decode(...) surrounding stackComponentVars and insert a
log.Debug/log.Debugf call that prints context and err prior to the return.

In `@pkg/merge/merge_native.go`:
- Around line 16-25: The helper function isMapValue is dead code and should be
removed: delete the entire isMapValue function declaration from
pkg/merge/merge_native.go (the function named isMapValue and its body) and
ensure no other code references it (rg already shows none); run the build/tests
to confirm removal didn't affect anything.

In `@pkg/merge/merge_type_override_test.go`:
- Around line 71-77: Add a wantVal entry to the "nested: list inside map
overridden by empty map" test case and set it to the expected nested result
(e.g. map[string]any{"vars": map[string]any{"accounts": map[string]any{}}}),
then update the assertion that currently checks only wantKey to also assert
equality against wantVal so the test is self-documenting and verifies the nested
value as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 56cd8206-f687-4f90-8616-b4278316e2dc

📥 Commits

Reviewing files that changed from the base of the PR and between 2d6917c and 72ffd69.

📒 Files selected for processing (27)
  • docs/fixes/2026-03-24-deep-merge-type-mismatch-regression.md
  • docs/fixes/2026-03-24-describe-affected-upload-timeout.md
  • internal/exec/describe_affected_utils_2.go
  • internal/exec/describe_dependents.go
  • internal/exec/describe_dependents_index.go
  • internal/exec/describe_dependents_index_test.go
  • pkg/merge/merge_native.go
  • pkg/merge/merge_native_test.go
  • pkg/merge/merge_no_duplicate_errors_test.go
  • pkg/merge/merge_type_override_test.go
  • tests/fixtures/scenarios/merge-type-override/atmos.yaml
  • tests/fixtures/scenarios/merge-type-override/components/terraform/eks-cluster/main.tf
  • tests/fixtures/scenarios/merge-type-override/components/terraform/s3-bucket/main.tf
  • tests/fixtures/scenarios/merge-type-override/components/terraform/vpc/main.tf
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/eks-defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/eks-sandbox.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/s3-defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/s3-sandbox.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/vpc-defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/catalog/vpc-sandbox.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/_defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/dev/_defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/dev/us-east-1/eks.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/sandbox/_defaults.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/sandbox/us-east-1/eks.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/sandbox/us-east-1/s3.yaml
  • tests/fixtures/scenarios/merge-type-override/stacks/orgs/acme/sandbox/us-east-1/vpc.yaml

… logging

- Remove unused isMapValue helper and reflect import (dead code after
  type-mismatch guard removal)
- Add debug logging for mapstructure decode failures in dependency index build
- Add test for enabled:false component filtering in dependency index
- Simplify merge_type_override_test nested case (set wantVal, remove special-case)
- Remove stale line-number references from fix doc
- Update validate_stacks_test: type overrides now succeed (not error)
- Add end-to-end integration test for merge-type-override fixture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
docs/fixes/2026-03-24-describe-affected-upload-timeout.md (1)

20-21: Tighten symptom wording to match observed behavior.

“Hang indefinitely” is stronger than the measured behavior (“40–50 min, then timeout/killed”). Consider aligning this sentence with the observed timeout window for accuracy.

Proposed wording
-The `--upload` flag causes the command to hang indefinitely on large infrastructures.
+The `--upload` flag causes the command to stall for 40–50 minutes on large infrastructures, often ending in timeout or manual termination.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md` around lines 20 -
21, Update the symptom sentence to match observed behavior by replacing “hang
indefinitely” with wording that reflects the measured timeout window (e.g.,
“hangs for ~40–50 minutes before timing out or being killed”); ensure the
sentence about the --upload flag states that the command hangs for that observed
duration on large infrastructures rather than claiming an indefinite hang.
internal/exec/validate_stacks_test.go (1)

153-180: Consider asserting NoError when expectedParts is empty.

When expectedParts is nil, the test logs any error but doesn't fail. This could silently pass if an unexpected error occurs. For the "type override succeeds" case, you likely want to assert success explicitly.

Proposed fix
 			// If no expected parts, just log the error if it exists
-			if err != nil {
-				t.Logf("Error occurred: %v", err)
-			}
+			assert.NoError(t, err, "Expected no error when expectedParts is nil")
 		})
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/validate_stacks_test.go` around lines 153 - 180, The test
currently only logs errors when tt.expectedParts is empty, which can hide
unexpected failures; change the branch after the comment "// If no expected
parts, just log the error if it exists" to assert no error instead—call
assert.NoError(t, err) (or require.NoError if you prefer test-fatal) for the
case where tt.expectedParts is nil/empty (this affects the table-driven test
that calls tt.setupFunc()), so unexpected errors (e.g., in the "type override
succeeds" case) will fail the test instead of being silently logged.
tests/merge_type_override_test.go (1)

106-109: Assert element contents, not just length.

Per coding guidelines, assert.Len alone can mask regressions that corrupt slice contents. Assert at least first/last element values.

Proposed fix
 		// allowed_accounts should be the original list from defaults.
 		accounts, ok := vars["allowed_accounts"].([]any)
 		require.True(t, ok, "allowed_accounts should be a list in dev (no override)")
-		assert.Len(t, accounts, 2, "allowed_accounts should have 2 entries from defaults")
+		require.Len(t, accounts, 2, "allowed_accounts should have 2 entries from defaults")
+		assert.Equal(t, "111111111111", accounts[0], "first allowed_account should match defaults")
+		assert.Equal(t, "222222222222", accounts[1], "second allowed_account should match defaults")
 	})

As per coding guidelines: "For slice-result tests, assert element contents, not just length."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/merge_type_override_test.go` around lines 106 - 109, The test currently
only checks the length of vars["allowed_accounts"] (accounts) using require.True
and assert.Len; update it to also assert the actual contents of the slice to
prevent silent regressions by checking element values (e.g., assert.Equal on
accounts[0] and accounts[1] or the first/last entries). Locate the block that
reads vars["allowed_accounts"] into accounts and replace or extend the
assert.Len with additional assertions that verify the expected string values for
the elements in accounts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md`:
- Around line 177-181: Update the table/adjacent text for the "describe
affected" benchmark to clarify scope: add a short note stating that the
`--upload` flag implicitly forces inclusion of dependents (i.e., behaves like
`--include-dependents`) so it follows the same payload/bottleneck and thus the
same timing improvements; reference the `describe affected` command and the
`--include-dependents` and `--upload` flags when adding this one-line
clarification.

---

Nitpick comments:
In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md`:
- Around line 20-21: Update the symptom sentence to match observed behavior by
replacing “hang indefinitely” with wording that reflects the measured timeout
window (e.g., “hangs for ~40–50 minutes before timing out or being killed”);
ensure the sentence about the --upload flag states that the command hangs for
that observed duration on large infrastructures rather than claiming an
indefinite hang.

In `@internal/exec/validate_stacks_test.go`:
- Around line 153-180: The test currently only logs errors when tt.expectedParts
is empty, which can hide unexpected failures; change the branch after the
comment "// If no expected parts, just log the error if it exists" to assert no
error instead—call assert.NoError(t, err) (or require.NoError if you prefer
test-fatal) for the case where tt.expectedParts is nil/empty (this affects the
table-driven test that calls tt.setupFunc()), so unexpected errors (e.g., in the
"type override succeeds" case) will fail the test instead of being silently
logged.

In `@tests/merge_type_override_test.go`:
- Around line 106-109: The test currently only checks the length of
vars["allowed_accounts"] (accounts) using require.True and assert.Len; update it
to also assert the actual contents of the slice to prevent silent regressions by
checking element values (e.g., assert.Equal on accounts[0] and accounts[1] or
the first/last entries). Locate the block that reads vars["allowed_accounts"]
into accounts and replace or extend the assert.Len with additional assertions
that verify the expected string values for the elements in accounts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1824ebff-ca67-496f-856a-51e4f19b76f8

📥 Commits

Reviewing files that changed from the base of the PR and between 72ffd69 and 85fbb9a.

📒 Files selected for processing (7)
  • docs/fixes/2026-03-24-describe-affected-upload-timeout.md
  • internal/exec/describe_dependents_index.go
  • internal/exec/describe_dependents_index_test.go
  • internal/exec/validate_stacks_test.go
  • pkg/merge/merge_native.go
  • pkg/merge/merge_type_override_test.go
  • tests/merge_type_override_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/exec/describe_dependents_index.go
  • internal/exec/describe_dependents_index_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/merge/merge_type_override_test.go
  • pkg/merge/merge_native.go

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 25, 2026

Codecov Report

❌ Patch coverage is 89.20000% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.23%. Comparing base (2d6917c) to head (ace6cb9).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/exec/describe_dependents_index.go 71.92% 9 Missing and 7 partials ⚠️
internal/exec/describe_dependents.go 94.67% 4 Missing and 5 partials ⚠️
internal/exec/describe_affected_utils_2.go 91.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2248      +/-   ##
==========================================
+ Coverage   77.18%   77.23%   +0.05%     
==========================================
  Files        1014     1015       +1     
  Lines       95886    96019     +133     
==========================================
+ Hits        74005    74159     +154     
+ Misses      17700    17671      -29     
- Partials     4181     4189       +8     
Flag Coverage Δ
unittests 77.23% <89.20%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/merge/merge_native.go 90.00% <ø> (-7.94%) ⬇️
internal/exec/describe_affected_utils_2.go 78.26% <91.66%> (+0.82%) ⬆️
internal/exec/describe_dependents.go 93.87% <94.67%> (+6.32%) ⬆️
internal/exec/describe_dependents_index.go 71.92% <71.92%> (ø)

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Fix doc: "hang indefinitely" → "stall for 40-50 minutes"
- Fix doc: add note that --upload forces IncludeDependents
- validate_stacks_test: assert NoError when expectedParts is nil
- Integration test: assert element contents, not just length

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
docs/fixes/2026-03-24-describe-affected-upload-timeout.md (2)

168-173: Tiny docs style polish: use vs. in the table header.

For US English consistency, change Speedup vs previous to Speedup vs. previous.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md` around lines 168 -
173, Update the Markdown table header cell "Speedup vs previous" to "Speedup vs.
previous" for US English consistency; locate the table header row containing the
exact string "Speedup vs previous" (the column header in the table that also
appears alongside "Step", "Time", and "Cumulative speedup") and replace that
cell text only, leaving the rest of the table and formatting intact.

29-35: Prefer symbol references over hard-coded source line numbers.

These docs cite exact line numbers in Go files (e.g., “lines 220-222”). Those drift quickly and become stale. Consider referencing function names/flow only so the fix note stays accurate longer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md` around lines 29 -
35, Replace hard-coded source line numbers in the note with symbol-based
references: instead of "lines 220-222" and "line 285-289" and "line 138",
reference the functions and call flow (e.g., the code path that sets
IncludeDependents=true and IncludeSettings=true, the addDependentsToAffected
function which iterates affected components, the ExecuteDescribeDependents
function, and the fact that ExecuteDescribeDependents calls
ExecuteDescribeStacks). Update the text to describe the flow and functions by
name and remove any explicit line numbers so the doc remains correct as code
moves.
internal/exec/validate_stacks_test.go (1)

133-149: Rename/simplify this case to match its new purpose.

This block now validates a success-path contract, but the surrounding shape (TestMergeContextErrorFormatting, expectedParts) still reads like error-formatting verification. Consider renaming and flattening it to a direct “defined contract” success test for clarity.

Based on learnings: Applies to **/*_test.go : Contract vs. legacy behavior: if a test says 'matches mergo' (or any other library), add an opt-in cross-validation test behind a build tag (e.g., //go:build compare_mergo); otherwise state 'defined contract' explicitly so it's clear the native implementation owns the behavior.

Also applies to: 176-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/exec/validate_stacks_test.go` around lines 133 - 149, Rename and
simplify the subcase currently named "type override succeeds without error"
(inside TestMergeContextErrorFormatting) into a standalone success test that
asserts ValidateStacks(atmosConfig) returns nil (e.g.,
TestValidateStacks_TypeOverrideSucceeds or TestValidateStacks_DefinedContract);
remove the error-formatting scaffolding like expectedParts and the table-driven
placement and instead call ValidateStacks directly and assert no error, and if
you want an opt-in cross-validation against mergo add a separate test guarded by
a build tag (//go:build compare_mergo) as suggested for the mergo comparison
lines referenced around 176-177.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/fixes/2026-03-24-describe-affected-upload-timeout.md`:
- Around line 168-173: Update the Markdown table header cell "Speedup vs
previous" to "Speedup vs. previous" for US English consistency; locate the table
header row containing the exact string "Speedup vs previous" (the column header
in the table that also appears alongside "Step", "Time", and "Cumulative
speedup") and replace that cell text only, leaving the rest of the table and
formatting intact.
- Around line 29-35: Replace hard-coded source line numbers in the note with
symbol-based references: instead of "lines 220-222" and "line 285-289" and "line
138", reference the functions and call flow (e.g., the code path that sets
IncludeDependents=true and IncludeSettings=true, the addDependentsToAffected
function which iterates affected components, the ExecuteDescribeDependents
function, and the fact that ExecuteDescribeDependents calls
ExecuteDescribeStacks). Update the text to describe the flow and functions by
name and remove any explicit line numbers so the doc remains correct as code
moves.

In `@internal/exec/validate_stacks_test.go`:
- Around line 133-149: Rename and simplify the subcase currently named "type
override succeeds without error" (inside TestMergeContextErrorFormatting) into a
standalone success test that asserts ValidateStacks(atmosConfig) returns nil
(e.g., TestValidateStacks_TypeOverrideSucceeds or
TestValidateStacks_DefinedContract); remove the error-formatting scaffolding
like expectedParts and the table-driven placement and instead call
ValidateStacks directly and assert no error, and if you want an opt-in
cross-validation against mergo add a separate test guarded by a build tag
(//go:build compare_mergo) as suggested for the mergo comparison lines
referenced around 176-177.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 06417084-c8a9-487e-a87f-1f7be1bc8c97

📥 Commits

Reviewing files that changed from the base of the PR and between 85fbb9a and ace6cb9.

📒 Files selected for processing (3)
  • docs/fixes/2026-03-24-describe-affected-upload-timeout.md
  • internal/exec/validate_stacks_test.go
  • tests/merge_type_override_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/merge_type_override_test.go

@aknysh aknysh merged commit f941c79 into main Mar 25, 2026
57 checks passed
@aknysh aknysh deleted the aknysh/test-upload-flag branch March 25, 2026 13:59
@github-actions
Copy link
Copy Markdown

These changes were released in v1.212.0.

@nitrocode
Copy link
Copy Markdown
Member

@aknysh thanks for catching this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants