executor parameter kind
Part of design: #1432 — per-component-executor-binding
Design document: https://github.com/JoshuaRowePhantom/Phantom.Workspaces/blob/design/docs/design/per-component-executor-binding.md
Scope
Add an executor parameter kind to the manifest parameter model and its documentation, plus value recording/substitution. The parameter is how a user, at launch, picks which executor a parameter-strategy executor resource resolves to. It offers a choice among two selectable option kinds, and records a disambiguated selection in a dedicated typed parameter-selections map (string → JsonElement) — NOT as a JSON-encoded string in the string→string parameter-values map, which stays reserved for ${param} text templating. Make parameter kind read from the manifest parameter kind field rather than being inferred purely by name (see Contradictions). (NEW parameter kind.)
Files
the AgentManifest parameter model / substitutor (Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.cs and the parameter property model); Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md.
Detailed design
Requirement 5 — executor launch parameter. A new manifest parameter kind:"executor" lets the user pick, at launch, which executor a parameter-strategy executor resource resolves to. The parameter offers a choice among two selectable option kinds:
- a trust-profile entity ("choose by trust policy") — resolves to that trust profile's
DefaultExecutionTarget connection-descriptor;
- a user-computer-profile entity — choosing one synthesizes an implicit trust profile (an in-memory
TrustProfileDefinition whose DefaultExecutionTarget = {"type":"user-computer-profile","entity-id":<chosen uuid>} and whose HostingWorkspacesClientInstances = [<chosen uuid>]); no trust-profile entity needs to be pre-authored or persisted.
Both paths converge on the same thing: a trust profile (explicit or implicit) whose DefaultExecutionTarget is the connection-descriptor used as the executor binding. This unifies executor selection under trust profiles, which is exactly where DefaultExecutionTarget already lives (Phantom.Workspaces.Llm.Core/Trust/TrustProfile.cs:105,143; TrustProfileDefinition.DefaultExecutionTarget; ExecutionTargetResolver.Resolve(TrustProfile?) returns DefaultExecutionTarget or {"type":"local"}).
Disambiguated recorded selection — a typed entry in parameter-selections (M7)
The chosen selection is recorded in the session's dedicated typed parameter-selections map (string parameter name → JsonElement selection), identifying BOTH the kind and the id as a small JSON object — {"trust-profile":"<name-or-id>"} or {"user-computer-profile":"<entity-id>"} — kept lossless through PhantomAgentSchema round-trip.
Decoupling contract (M7 — separate text templating from structured selection). parameter-values is IReadOnlyDictionary<string,string> — a string→string map — in AgentSessionEntityFactory (Phantom.Workspaces.Data.Core/AgentSessionEntityFactory.cs:46, serialized at :77-85) and in the substitutor (AgentDefinitionParameterSubstitutor.Substitute(AgentManifest, IReadOnlyDictionary<string,string>?), AgentDefinitionParameterSubstitutor.cs:15-17,133-140). That map exists for ${param} text templating; an executor selection is not a text substitution, so it does NOT go there. Instead the executor parameter's disambiguated selection is recorded in a dedicated typed parameter-selections map (string → JsonElement) — a sibling root key of parameter-values and executor-bindings on the persisted agent-session entity (AgentSessionEntityFactory.CreateEntityData, Data.Core/AgentSessionEntityFactory.cs:39-93). The resolver (#1436) reads the JsonElement selection directly — no JSON-string parsing. Do NOT widen parameter-values to string→object and do NOT store a JSON-encoded string in it; parameter-values stays string→string for templating, avoiding a breaking change across its ~20 call sites. Text/directory parameters continue to use parameter-values exactly as today.
Modified files (applicable):
Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json — document an executor parameter kind.
Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.cs + the parameter property model — an executor parameter is a structured selection recorded in the typed parameter-selections map, NOT a ${}-substitutable string; parameter-values text substitution is unchanged for text/directory params.
Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md — document parameters + executor kind + executor resources + executor-bindings round-trip, INCLUDING that the executor selection lives in the typed parameter-selections map (string→JsonElement), a sibling of the string→string parameter-values.
Contradictions with the initial brief (verified against the codebase):
agent-manifest.json parameters is untyped. The schema declares parameters as type:object, additionalProperties:true (Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json:36-40) — it is not a typed properties[] array. The {name,kind,description,required,default} shape lives in the AgentManifest model and in agent-options-parameters.md, not in the JSON schema. Adding the executor kind is primarily a model + documentation change (Commit 2).
- Launchpad infers parameter kind by NAME, not a
kind field. AgentManifestLaunchpadViewModel.DetermineParameterKind returns Directory only for the exact name working-directory (AgentManifestLaunchpadViewModel.cs:291-296); AgentManifestParameterKind has only Text and Directory (AgentManifestParameterKind.cs). Commit 2/8 must switch the picker to honour the manifest parameter kind field and add an Executor kind (not UserComputerProfile).
Data flow (applicable): The Launchpad records the disambiguated executor selection as a typed JsonElement entry in parameter-selections. ExecutorResourceResolver.Resolve later reads that selection (M7) and routes the parameter strategy through the selected/implicit trust profile, returning that profile's DefaultExecutionTarget descriptor. Text/directory parameters still flow through the string→string parameter-values.
Tests
AgentManifestExecutorResourceTests (Phantom.Workspaces.Llm.Core.Tests)
Load_ExecutorParameter_Recognised
- a substitutor test for the new kind, covering both disambiguated value shapes (
{"trust-profile":...} and {"user-computer-profile":...}).
ExecutorParameterSelection_RecordedInParameterSelections — (M7) the recorded selection is a typed JsonElement entry in the parameter-selections map (both {"trust-profile":…} and {"user-computer-profile":…} shapes), NOT a JSON-encoded string in parameter-values, and survives PhantomAgentSchema round-trip; the string→string parameter-values type and text-substitution behaviour are unchanged.
Testing strategy — Schema and model round-trip: A manifest carrying an executor parameter loads through PhantomAgentSchema and re-serialises losslessly (including the disambiguated selection recorded in the typed parameter-selections map), remaining compliant with the AgentSchema source-scan guard test. → AgentManifestExecutorResourceTests.
Considered / Background
An earlier framing made this a user-computer-profile-only parameter kind (kind:"user-computer-profile") that let the user pick a remote machine and recorded a bare profile entity-id. This was generalised to the executor parameter above so an executor can also be chosen by trust policy (a trust-profile entity) — with a user-computer-profile selection now handled as an implicit trust profile rather than a special case. The disambiguated recorded value replaces the bare entity-id so the two option kinds never collide.
Storing the disambiguated selection inside parameter-values — whether as a JSON-encoded string or by widening the map to string→object — was considered but rejected (M7): it conflates structured selection with ${param} text templating, and widening the string→string dictionary (and the session-entity writer / substitutor) to string→object is a breaking type change rippling through ~20 call sites (incl. Mongo/Web persistence DTOs). Decoupling into a dedicated typed parameter-selections map keeps parameter-values string-only with no blast radius. The general typed-value model is filed separately as #1444 (non-blocking); the executor feature does not depend on it, and #1444 would later let parameter-selections fold into a unified typed parameter-values.
Dependencies
None.
Consumed by: #1436 — ExecutorResourceResolver reads this typed executor selection from parameter-selections (M7) for the parameter strategy.
Consumed by: #1437 — the session writer persists this selection in the typed parameter-selections root key (M7), a sibling of parameter-values and executor-bindings.
executorparameter kindPart of design: #1432 — per-component-executor-binding
Design document: https://github.com/JoshuaRowePhantom/Phantom.Workspaces/blob/design/docs/design/per-component-executor-binding.md
Scope
Add an
executorparameter kind to the manifest parameter model and its documentation, plus value recording/substitution. The parameter is how a user, at launch, picks which executor aparameter-strategy executor resource resolves to. It offers a choice among two selectable option kinds, and records a disambiguated selection in a dedicated typedparameter-selectionsmap (string → JsonElement) — NOT as a JSON-encoded string in thestring→stringparameter-valuesmap, which stays reserved for${param}text templating. Make parameter kind read from the manifest parameterkindfield rather than being inferred purely by name (see Contradictions). (NEW parameter kind.)Files
the
AgentManifestparameter model / substitutor (Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.csand the parameter property model);Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md.Detailed design
Requirement 5 —
executorlaunch parameter. A new manifest parameterkind:"executor"lets the user pick, at launch, which executor aparameter-strategy executor resource resolves to. The parameter offers a choice among two selectable option kinds:DefaultExecutionTargetconnection-descriptor;TrustProfileDefinitionwhoseDefaultExecutionTarget = {"type":"user-computer-profile","entity-id":<chosen uuid>}and whoseHostingWorkspacesClientInstances = [<chosen uuid>]); no trust-profile entity needs to be pre-authored or persisted.Both paths converge on the same thing: a trust profile (explicit or implicit) whose
DefaultExecutionTargetis the connection-descriptor used as the executor binding. This unifies executor selection under trust profiles, which is exactly whereDefaultExecutionTargetalready lives (Phantom.Workspaces.Llm.Core/Trust/TrustProfile.cs:105,143;TrustProfileDefinition.DefaultExecutionTarget;ExecutionTargetResolver.Resolve(TrustProfile?)returnsDefaultExecutionTargetor{"type":"local"}).Disambiguated recorded selection — a typed entry in
parameter-selections(M7)The chosen selection is recorded in the session's dedicated typed
parameter-selectionsmap (stringparameter name →JsonElementselection), identifying BOTH the kind and the id as a small JSON object —{"trust-profile":"<name-or-id>"}or{"user-computer-profile":"<entity-id>"}— kept lossless throughPhantomAgentSchemaround-trip.Decoupling contract (M7 — separate text templating from structured selection).
parameter-valuesisIReadOnlyDictionary<string,string>— a string→string map — inAgentSessionEntityFactory(Phantom.Workspaces.Data.Core/AgentSessionEntityFactory.cs:46, serialized at:77-85) and in the substitutor (AgentDefinitionParameterSubstitutor.Substitute(AgentManifest, IReadOnlyDictionary<string,string>?),AgentDefinitionParameterSubstitutor.cs:15-17,133-140). That map exists for${param}text templating; an executor selection is not a text substitution, so it does NOT go there. Instead theexecutorparameter's disambiguated selection is recorded in a dedicated typedparameter-selectionsmap (string → JsonElement) — a sibling root key ofparameter-valuesandexecutor-bindingson the persisted agent-session entity (AgentSessionEntityFactory.CreateEntityData,Data.Core/AgentSessionEntityFactory.cs:39-93). The resolver (#1436) reads theJsonElementselection directly — no JSON-string parsing. Do NOT widenparameter-valuestostring→objectand do NOT store a JSON-encoded string in it;parameter-valuesstaysstring→stringfor templating, avoiding a breaking change across its ~20 call sites. Text/directory parameters continue to useparameter-valuesexactly as today.Modified files (applicable):
Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json— document anexecutorparameter kind.Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.cs+ the parameter property model — anexecutorparameter is a structured selection recorded in the typedparameter-selectionsmap, NOT a${}-substitutable string;parameter-valuestext substitution is unchanged for text/directory params.Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md— document parameters +executorkind +executorresources +executor-bindingsround-trip, INCLUDING that theexecutorselection lives in the typedparameter-selectionsmap (string→JsonElement), a sibling of thestring→stringparameter-values.Contradictions with the initial brief (verified against the codebase):
agent-manifest.jsonparametersis untyped. The schema declaresparametersastype:object, additionalProperties:true(Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json:36-40) — it is not a typedproperties[]array. The{name,kind,description,required,default}shape lives in theAgentManifestmodel and inagent-options-parameters.md, not in the JSON schema. Adding theexecutorkind is primarily a model + documentation change (Commit 2).kindfield.AgentManifestLaunchpadViewModel.DetermineParameterKindreturnsDirectoryonly for the exact nameworking-directory(AgentManifestLaunchpadViewModel.cs:291-296);AgentManifestParameterKindhas onlyTextandDirectory(AgentManifestParameterKind.cs). Commit 2/8 must switch the picker to honour the manifest parameterkindfield and add anExecutorkind (notUserComputerProfile).Data flow (applicable): The Launchpad records the disambiguated
executorselection as a typedJsonElemententry inparameter-selections.ExecutorResourceResolver.Resolvelater reads that selection (M7) and routes theparameterstrategy through the selected/implicit trust profile, returning that profile'sDefaultExecutionTargetdescriptor. Text/directory parameters still flow through thestring→stringparameter-values.Tests
AgentManifestExecutorResourceTests(Phantom.Workspaces.Llm.Core.Tests)Load_ExecutorParameter_Recognised{"trust-profile":...}and{"user-computer-profile":...}).ExecutorParameterSelection_RecordedInParameterSelections— (M7) the recorded selection is a typedJsonElemententry in theparameter-selectionsmap (both{"trust-profile":…}and{"user-computer-profile":…}shapes), NOT a JSON-encoded string inparameter-values, and survivesPhantomAgentSchemaround-trip; thestring→stringparameter-valuestype and text-substitution behaviour are unchanged.Testing strategy — Schema and model round-trip: A manifest carrying an
executorparameter loads throughPhantomAgentSchemaand re-serialises losslessly (including the disambiguated selection recorded in the typedparameter-selectionsmap), remaining compliant with the AgentSchema source-scan guard test. →AgentManifestExecutorResourceTests.Considered / Background
An earlier framing made this a
user-computer-profile-only parameter kind (kind:"user-computer-profile") that let the user pick a remote machine and recorded a bare profile entity-id. This was generalised to theexecutorparameter above so an executor can also be chosen by trust policy (a trust-profile entity) — with a user-computer-profile selection now handled as an implicit trust profile rather than a special case. The disambiguated recorded value replaces the bare entity-id so the two option kinds never collide.Storing the disambiguated selection inside
parameter-values— whether as a JSON-encoded string or by widening the map tostring→object— was considered but rejected (M7): it conflates structured selection with${param}text templating, and widening thestring→stringdictionary (and the session-entity writer / substitutor) tostring→objectis a breaking type change rippling through ~20 call sites (incl. Mongo/Web persistence DTOs). Decoupling into a dedicated typedparameter-selectionsmap keepsparameter-valuesstring-only with no blast radius. The general typed-value model is filed separately as #1444 (non-blocking); the executor feature does not depend on it, and #1444 would later letparameter-selectionsfold into a unified typedparameter-values.Dependencies
None.
Consumed by: #1436 —
ExecutorResourceResolverreads this typedexecutorselection fromparameter-selections(M7) for theparameterstrategy.Consumed by: #1437 — the session writer persists this selection in the typed
parameter-selectionsroot key (M7), a sibling ofparameter-valuesandexecutor-bindings.