fix(templateengine+centralui): resolve follow-ups #3 (derived-template collisions) and #7 (sandbox batch/wait surface)
#3 — CollisionDetector counted a derived template's IsInherited placeholder rows as a distinct origin from the parent members the inheritance walk re-adds, reporting a spurious "Naming collision" for every inherited row and blocking any attribute/composition add to a derived template. CollectDirectMembers now skips IsInherited rows on the direct-template and inherited-parent walks; it keeps them for the composed-module walk, where placeholders are the sole representation of a derived module's inherited members (that walk does not climb the composed template's parent chain). #7 — SandboxAttributeAccessor (Central UI Test-Run host) omitted WriteBatchAndWaitAsync / WaitAsync / WaitForAsync, so the editor false-flagged valid instance scripts with CS1061 even though `template validate` and the deploy gate accept them. Added the five overloads mirroring the runtime AttributeAccessor; they throw a labelled ScriptSandboxException if run in Test Run (the central sandbox has no device-batch / event-waiter transport). Tests: +3 CollisionDetector unit + 1 end-to-end TemplateService (derived add now succeeds); +2 ScriptAnalysisService diagnose-clean. Each new test verified to fail without its fix with the exact user-facing symptom. Full suites green (TemplateEngine.Tests 438, CentralUI.Tests 866). Docs: Component-TemplateEngine.md (inherited-placeholder collision rule), Component-ScriptAnalysis.md (third sandbox surface + its compile-clean guard), known-issues tracker #3/#7 marked resolved and the minor note promoted to #8.
This commit is contained in:
@@ -313,6 +313,59 @@ public class SandboxAttributeAccessor
|
||||
_ctx.SetAttribute(Resolve(key), value?.ToString() ?? string.Empty);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Batch-write/wait helpers. These mirror the runtime AttributeAccessor
|
||||
// (SiteRuntime/Scripts/ScopeAccessors.cs) and the deploy-gate
|
||||
// ScriptCompileSurface member-for-member so instance scripts using them COMPILE
|
||||
// in the editor and pass Test Run analysis (follow-up #7) — previously the
|
||||
// sandbox omitted them and the editor false-flagged valid scripts with CS1061.
|
||||
// Execution needs the site's DCL batch path + event-driven attribute waiter,
|
||||
// for which the central Test Run sandbox has no transport, so each throws a
|
||||
// clearly-labelled ScriptSandboxException; the same code validates/deploys/runs
|
||||
// unchanged at a site.
|
||||
|
||||
/// <summary>
|
||||
/// Sandbox stand-in for <c>AttributeAccessor.WriteBatchAndWaitAsync</c>: present
|
||||
/// for editor/compile parity, throws <see cref="ScriptSandboxException"/> when run
|
||||
/// in Test Run (no device batch-write transport here).
|
||||
/// </summary>
|
||||
public Task<bool> WriteBatchAndWaitAsync(
|
||||
IReadOnlyDictionary<string, object?> values, string flagKey, object? flagValue,
|
||||
string responseKey, object? responseValue, TimeSpan timeout)
|
||||
=> throw NotInSandbox(nameof(WriteBatchAndWaitAsync));
|
||||
|
||||
/// <summary>
|
||||
/// Sandbox stand-in for <c>AttributeAccessor.WaitAsync</c> (value-equality form);
|
||||
/// see <see cref="WriteBatchAndWaitAsync"/>.
|
||||
/// </summary>
|
||||
public Task<bool> WaitAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false)
|
||||
=> throw NotInSandbox(nameof(WaitAsync));
|
||||
|
||||
/// <summary>
|
||||
/// Sandbox stand-in for <c>AttributeAccessor.WaitAsync</c> (predicate form);
|
||||
/// see <see cref="WriteBatchAndWaitAsync"/>.
|
||||
/// </summary>
|
||||
public Task<bool> WaitAsync(string key, Func<object?, bool> predicate, TimeSpan timeout, bool requireGoodQuality = false)
|
||||
=> throw NotInSandbox(nameof(WaitAsync));
|
||||
|
||||
/// <summary>
|
||||
/// Sandbox stand-in for <c>AttributeAccessor.WaitForAsync</c> (value-equality form);
|
||||
/// see <see cref="WriteBatchAndWaitAsync"/>.
|
||||
/// </summary>
|
||||
public Task<WaitResult> WaitForAsync(string key, object? targetValue, TimeSpan timeout, bool requireGoodQuality = false)
|
||||
=> throw NotInSandbox(nameof(WaitForAsync));
|
||||
|
||||
/// <summary>
|
||||
/// Sandbox stand-in for <c>AttributeAccessor.WaitForAsync</c> (predicate form);
|
||||
/// see <see cref="WriteBatchAndWaitAsync"/>.
|
||||
/// </summary>
|
||||
public Task<WaitResult> WaitForAsync(string key, Func<object?, bool> predicate, TimeSpan timeout, bool requireGoodQuality = false)
|
||||
=> throw NotInSandbox(nameof(WaitForAsync));
|
||||
|
||||
private static ScriptSandboxException NotInSandbox(string member) =>
|
||||
new($"{member}(...) drives live device tags and the site's event-driven " +
|
||||
"attribute waiter, which aren't available in the central Test Run sandbox — " +
|
||||
"deploy to a site to exercise batch-write/wait handshakes.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user