feat(siteruntime): thread ParentExecutionId into the routed script's ScriptRuntimeContext

This commit is contained in:
Joseph Doherty
2026-05-21 17:35:49 -04:00
parent dc2c73b07d
commit 6af2607a50
8 changed files with 288 additions and 25 deletions

View File

@@ -735,9 +735,13 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
{
if (_instanceActors.TryGetValue(request.InstanceUniqueName, out var instanceActor))
{
// Convert to ScriptCallRequest and Ask the Instance Actor
// Convert to ScriptCallRequest and Ask the Instance Actor.
// Audit Log #23 (ParentExecutionId): carry the inbound request's
// ExecutionId down as ParentExecutionId so the routed script
// execution can record its spawner.
var scriptCall = new ScriptCallRequest(
request.ScriptName, request.Parameters, 0, request.CorrelationId);
request.ScriptName, request.Parameters, 0, request.CorrelationId,
ParentExecutionId: request.ParentExecutionId);
var sender = Sender;
instanceActor.Ask<ScriptCallResult>(scriptCall, TimeSpan.FromSeconds(30))
.ContinueWith(t =>

View File

@@ -320,7 +320,10 @@ public class InstanceActor : ReceiveActor
{
if (_scriptActors.TryGetValue(request.ScriptName, out var scriptActor))
{
// Forward the request to the Script Actor, preserving the original sender
// Forward the request to the Script Actor, preserving the original
// sender. The whole record is forwarded unchanged, so any
// ParentExecutionId (Audit Log #23) set by an inbound-API-routed
// call is carried through to the Script Actor verbatim.
scriptActor.Forward(request);
}
else

View File

@@ -184,7 +184,13 @@ public class ScriptActor : ReceiveActor, IWithTimers
return;
}
SpawnExecution(request.Parameters, request.CurrentCallDepth, Sender, request.CorrelationId);
// Audit Log #23 (ParentExecutionId): carry any inbound-routed
// ParentExecutionId through to the ScriptExecutionActor so the routed
// script's ScriptRuntimeContext can record its spawner. Null for normal
// (tag-change / timer) runs and nested Script.Call invocations.
SpawnExecution(
request.Parameters, request.CurrentCallDepth, Sender, request.CorrelationId,
request.ParentExecutionId);
}
/// <summary>
@@ -379,7 +385,8 @@ public class ScriptActor : ReceiveActor, IWithTimers
IReadOnlyDictionary<string, object?>? parameters,
int callDepth,
IActorRef replyTo,
string correlationId)
string correlationId,
Guid? parentExecutionId = null)
{
var executionId = $"{_scriptName}-exec-{_executionCounter++}";
@@ -401,7 +408,10 @@ public class ScriptActor : ReceiveActor, IWithTimers
_logger,
_scope,
_healthCollector,
_serviceProvider));
_serviceProvider,
// Audit Log #23 (ParentExecutionId): null for trigger-driven runs;
// an inbound-API-routed call supplies the inbound request's id.
parentExecutionId));
Context.ActorOf(props, executionId);
}

View File

@@ -43,7 +43,11 @@ public class ScriptExecutionActor : ReceiveActor
ILogger logger,
Commons.Types.Scripts.ScriptScope scope,
ISiteHealthCollector? healthCollector = null,
IServiceProvider? serviceProvider = null)
IServiceProvider? serviceProvider = null,
// Audit Log #23 (ParentExecutionId): the spawning execution's
// ExecutionId for an inbound-API-routed call. Null for normal
// (tag-change / timer) runs and nested Script.Call invocations.
Guid? parentExecutionId = null)
{
// Immediately begin execution
var self = Self;
@@ -52,7 +56,8 @@ public class ScriptExecutionActor : ReceiveActor
ExecuteScript(
scriptName, instanceName, compiledScript, parameters, callDepth,
instanceActor, sharedScriptLibrary, options, replyTo, correlationId,
self, parent, logger, scope, healthCollector, serviceProvider);
self, parent, logger, scope, healthCollector, serviceProvider,
parentExecutionId);
}
private static void ExecuteScript(
@@ -71,7 +76,8 @@ public class ScriptExecutionActor : ReceiveActor
ILogger logger,
Commons.Types.Scripts.ScriptScope scope,
ISiteHealthCollector? healthCollector,
IServiceProvider? serviceProvider)
IServiceProvider? serviceProvider,
Guid? parentExecutionId)
{
var timeout = TimeSpan.FromSeconds(options.ScriptExecutionTimeoutSeconds);
@@ -164,7 +170,12 @@ public class ScriptExecutionActor : ReceiveActor
// emission. Best-effort: null degrades the helpers to a
// no-emission path; the S&F handoff and TrackedOperationId
// return are unaffected.
cachedForwarder: cachedForwarder);
cachedForwarder: cachedForwarder,
// Audit Log #23 (ParentExecutionId): the spawning execution's
// id for an inbound-API-routed call. The routed script still
// mints its own fresh ExecutionId — this records the spawner.
// Null for normal (tag-change / timer) runs.
parentExecutionId: parentExecutionId);
var globals = new ScriptGlobals
{