Wire skills into agent runs: org-scoped, published-only, org-preferred resolution

ISkillCatalog.GetByKeysAsync now takes the org id and resolves each key within that org's namespace
only — the org's own published skill, else a shared builtin (null org), never another org's. Org-owned
is preferred over the builtin; only Published (golden-tested) skills are injected; the resolved
skill@version is recorded in the prompt heading and run trace. AgentRunExecutor threads
context.OrganizationId. SeatsPage now loads the org library (builtins + authored + installed), dedupes
to one entry per key, and flags drafts (won't run until published).

Verified: ArchitectureTests 8/8, IntegrationTests 48/48 (new SkillRunScopingTests: a run assembles the
org's own skill over the builtin of the same key, and another org's same-key skill never leaks in),
client build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-13 13:35:53 +03:30
parent cca7c68da3
commit 2ebe2808be
6 changed files with 216 additions and 9 deletions
@@ -35,7 +35,7 @@ internal static class PromptAssembler
builder.AppendLine("# Skills");
foreach (var skill in ordered)
{
builder.AppendLine("## " + skill.Name).AppendLine(skill.Body).AppendLine();
builder.AppendLine("## " + skill.Name + " (v" + skill.Version + ")").AppendLine(skill.Body).AppendLine();
}
if (context.Docs.Count > 0)
@@ -69,7 +69,7 @@ internal static class PromptAssembler
{
agent = context.AgentName,
autonomy = context.Autonomy.ToString(),
skills = ordered.Select(s => s.Key).ToArray(),
skills = ordered.Select(s => s.Key + "@" + s.Version).ToArray(),
docs = context.Docs,
memories = memories.Count,
apiConfigId = context.ApiConfigId,