feat(testrun): route WaitForAttribute in the Test-Run sandbox (deferred #14)

Closes deferred-work register item #14 with full fidelity. The Central UI Test-Run
sandbox previously threw ScriptSandboxException for Attributes.WaitAsync/WaitForAsync;
now the value-equality forms route to the bound deployed instance over the same
cross-site path inbound Route.To().WaitForAttribute() uses.

- Commons: additive trailing RouteToWaitForAttributeRequest.RequireGoodQuality
  (default false; message-evolution safe) so quality-gated waits route too.
- SiteRuntime: DeploymentManagerActor's routed-wait handler now threads
  request.RequireGoodQuality into the InstanceActor WaitForAttributeRequest
  (previously hard-coded to default).
- CentralUI sandbox: ISandboxInstanceGateway.WaitForAttributeAsync +
  SandboxInstanceGateway impl (via CommunicationService.RouteToWaitForAttributeAsync);
  SandboxAttributeAccessor value-equality WaitAsync/WaitForAsync route through it
  (codec-encoded target, scope-resolved name). Predicate overloads stay unsupported
  (an in-process lambda can't be routed) and throw a clearer, dedicated message.
- Tests: sandbox accessor routing + predicate/unbound rejection (CentralUI);
  site-handler RequireGoodQuality threading (SiteRuntime). Register + the historical
  waitfor-deferred plan doc updated.

Full slnx build 0/0; CentralUI 59, SiteRuntime routed-wait 4, InboundAPI wait 32 green.

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 09:29:38 -04:00
parent 4cd21b342b
commit 22136d36d9
8 changed files with 216 additions and 31 deletions
@@ -87,9 +87,10 @@ public record RouteToSetAttributesResponse(
/// <summary>
/// Request to block until a remote instance attribute reaches a target value
/// (spec §6 — <c>Route.To("inst").WaitForAttribute(name, targetValue, timeout)</c>).
/// Value-equality ONLY across the wire: <see cref="TargetValueEncoded"/> carries the
/// canonical <c>AttributeValueCodec</c>-encoded target; there is no predicate and no
/// quality flag in the comparison. The site evaluates equality and either matches or
/// Value-equality across the wire: <see cref="TargetValueEncoded"/> carries the
/// canonical <c>AttributeValueCodec</c>-encoded target; there is no predicate (a
/// cross-process lambda cannot be routed). <see cref="RequireGoodQuality"/> optionally
/// gates the match on Good quality. The site evaluates the match and either matches or
/// times out.
/// </summary>
/// <param name="ParentExecutionId">
@@ -99,6 +100,12 @@ public record RouteToSetAttributesResponse(
/// so the inbound→site execution-tree link survives the wait path. Additive trailing
/// member — null for the Central UI sandbox path or for callers built before the field existed.
/// </param>
/// <param name="RequireGoodQuality">
/// Quality-gated ("Good"-only) mode (spec §4.2): when true, a value reaching the target at
/// Bad/Uncertain quality is NOT a match — the site holds the wait until the value satisfies
/// the target at Good quality (or times out). Additive trailing member; defaults to
/// <see langword="false"/> (quality-agnostic) so older callers and the wire default are unchanged.
/// </param>
public record RouteToWaitForAttributeRequest(
string CorrelationId,
string InstanceUniqueName,
@@ -106,7 +113,8 @@ public record RouteToWaitForAttributeRequest(
string? TargetValueEncoded,
TimeSpan Timeout,
DateTimeOffset Timestamp,
Guid? ParentExecutionId = null);
Guid? ParentExecutionId = null,
bool RequireGoodQuality = false);
/// <summary>
/// Response from a remote attribute wait. <see cref="Success"/>/<see cref="ErrorMessage"/>