From 448161e448d69346150f1f9c0671afecde969321 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Jul 2026 21:09:20 +0000
Subject: [PATCH 1/2] Initial plan
From fefffeaa6ad38a206f71119433b45d01c070356c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Jul 2026 21:22:12 +0000
Subject: [PATCH 2/2] Fix: propagate autoentity resolution counts to child
configs during validation
When using `data-source-files`, the metadata provider stores autoentity
resolution counts only on the root (merged) config. ValidateRootConfig
now copies those counts to each child config before per-child validation,
so child configs with only autoentities correctly pass the entity-presence
check instead of failing with "No entities found".
Adds two regression tests:
- TestChildWithDataSourceAndAutoentitiesResolvingEntitiesIsValid
- TestRootAndChildBothWithAutoentitiesResolvingEntitiesIsValid
---
src/Cli.Tests/ValidateConfigTests.cs | 66 +++++++++++++++++++
.../Configurations/RuntimeConfigValidator.cs | 12 ++++
2 files changed, 78 insertions(+)
diff --git a/src/Cli.Tests/ValidateConfigTests.cs b/src/Cli.Tests/ValidateConfigTests.cs
index fe985f1a34..6e3f0fb1d5 100644
--- a/src/Cli.Tests/ValidateConfigTests.cs
+++ b/src/Cli.Tests/ValidateConfigTests.cs
@@ -883,6 +883,72 @@ public void TestChildWithEntitiesAndAutoentitiesResolvingZeroLogsNamedWarning()
VerifyAutoentityZeroDiscoveredWarning(loggerMock, expectedFileNameInMessage: "child-db.json");
}
+ ///
+ /// Child config with only autoentities that resolve to >0 entities is valid.
+ /// Simulates the production code path where the metadata provider stores resolution
+ /// counts on the root (merged) config rather than on the child config directly.
+ /// This is the regression test for the bug where child-config autoentities caused
+ /// "No entities found" even when they were expanded successfully.
+ /// Covers child truth-table row C5 (DS=1, E=0, AE=1, resolved>0, counts on root).
+ ///
+ [TestMethod]
+ public void TestChildWithDataSourceAndAutoentitiesResolvingEntitiesIsValid()
+ {
+ // Child has no explicit entities; only autoentities.
+ RuntimeConfig childConfig = BuildTestConfig(
+ hasDataSource: true,
+ entities: new(),
+ autoentities: new() { { "ae1", BuildSimpleAutoentity() } });
+ childConfig.IsChildConfig = true;
+
+ RuntimeConfig rootConfig = BuildTestConfig(
+ hasDataSource: false, entities: new(),
+ dataSourceFiles: new DataSourceFiles(new[] { "child-db.json" }));
+ rootConfig.ChildConfigs.Add(("child-db.json", childConfig));
+
+ // Simulate production: metadata provider stores counts in the root config,
+ // NOT directly on the child config.
+ rootConfig.AutoentityResolutionCounts["ae1"] = 3;
+
+ RuntimeConfigValidator validator = BuildValidator(rootConfig);
+ validator.ValidateDataSourceAndEntityPresence(rootConfig);
+
+ Assert.AreEqual(0, validator.ConfigValidationExceptions.Count,
+ "Child config with autoentities that resolved entities should pass validation.");
+ }
+
+ ///
+ /// Both root and child configs have autoentities that resolve to >0 entities.
+ /// Resolution counts for both are stored only on the root config (as the metadata
+ /// provider does at runtime). Validation must pass for both configs.
+ ///
+ [TestMethod]
+ public void TestRootAndChildBothWithAutoentitiesResolvingEntitiesIsValid()
+ {
+ RuntimeConfig childConfig = BuildTestConfig(
+ hasDataSource: true,
+ entities: new(),
+ autoentities: new() { { "child-ae", BuildSimpleAutoentity() } });
+ childConfig.IsChildConfig = true;
+
+ RuntimeConfig rootConfig = BuildTestConfig(
+ hasDataSource: true,
+ entities: new(),
+ dataSourceFiles: new DataSourceFiles(new[] { "child-db.json" }),
+ autoentities: new() { { "root-ae", BuildSimpleAutoentity() } });
+ rootConfig.ChildConfigs.Add(("child-db.json", childConfig));
+
+ // Simulate production: metadata provider stores ALL counts in the root config.
+ rootConfig.AutoentityResolutionCounts["root-ae"] = 2;
+ rootConfig.AutoentityResolutionCounts["child-ae"] = 4;
+
+ RuntimeConfigValidator validator = BuildValidator(rootConfig);
+ validator.ValidateDataSourceAndEntityPresence(rootConfig);
+
+ Assert.AreEqual(0, validator.ConfigValidationExceptions.Count,
+ "Root and child configs both with autoentities that resolved entities should pass validation.");
+ }
+
///
/// Helper: verifies that the autoentity-discovered-zero warning was logged at least once,
/// optionally also checking that the formatted message contains a child config file name.
diff --git a/src/Core/Configurations/RuntimeConfigValidator.cs b/src/Core/Configurations/RuntimeConfigValidator.cs
index 1c9f8b9ecd..c8a6c6e2c9 100644
--- a/src/Core/Configurations/RuntimeConfigValidator.cs
+++ b/src/Core/Configurations/RuntimeConfigValidator.cs
@@ -739,6 +739,18 @@ private void ValidateRootConfig(RuntimeConfig runtimeConfig)
// Validate each child config independently.
foreach ((string fileName, RuntimeConfig childConfig) in runtimeConfig.ChildConfigs)
{
+ // The metadata provider stores autoentity resolution counts on the root (merged)
+ // config. Copy those counts to the child config so per-child validation can find
+ // them. Only copy if the child hasn't already been populated (e.g. in unit tests).
+ foreach (KeyValuePair ae in childConfig.Autoentities)
+ {
+ if (!childConfig.AutoentityResolutionCounts.ContainsKey(ae.Key)
+ && runtimeConfig.AutoentityResolutionCounts.TryGetValue(ae.Key, out int count))
+ {
+ childConfig.AutoentityResolutionCounts[ae.Key] = count;
+ }
+ }
+
ValidateNonRootConfig(childConfig, configName: fileName);
}
}