AIManage has two unrelated “skill” mechanisms: tenant-persisted handler metadata and host-local Markdown prompt files. Neither is connected to SendMessage/StreamMessage. No reflection executor or Semantic Kernel tool registration consumes them, so they are not callable business tools today.
1. Two skill catalogs
| Dimension | persisted AISkill | FileSkillDefinition |
|---|---|---|
| scope | TenantId | process/deployment |
| storage | database | AI_SKILLS_PATH or AI/Skills/*.md |
| fields | type, method, schema, enabled | name/category/description/body |
| API | register/list/delete | list/reload |
| authorization | Mediator ai/skill | authentication only |
| execution | none | none |
| reload | next query | watcher, 500ms debounce |
2. Persistent metadata
POST /api/ai/skillsContent-Type: application/json
{ "name": "CreateTicket", "description": "Create a support ticket", "category": "Support", "handlerType": "Acme.Support.CreateTicketHandler, Acme.Support", "handlerMethod": "ExecuteAsync", "parameterSchemaJson": "{\"type\":\"object\",\"required\":[\"title\"]}"}The handler checks only nonblank Name and HandlerType. It does not validate loadability, method signature, JSON Schema, target authorization, argument safety, or result type. Source contains no primary Activator, GetMethod, or Invoke execution path, and the Semantic Kernel adapter does not register these entries.
3. File skills
---name: ContractReviewcategory: LegalOpsdescription: Identify performance risks in a contract---
Only report risks, evidence, and questions requiring human review.The scanner uses simple regular expressions, not a full YAML parser. Empty bodies fall back to description; duplicate names overwrite; ordering is unspecified. It scans at Start and watches an existing directory. If the directory is absent at startup, the watcher is not established, and later directory creation is not reliably discovered without a path that restarts watching.
List/reload bypass Mediator. Any authenticated user can see file metadata and trigger a process-wide rescan. Restrict them to skill/operations administrators and treat file parsing, size, duplicates, and event loss as production inputs.
4. Semantic Kernel is not automatically an Agent
The adapter performs text chat and option conversion. It exposes Kernel for advanced consumers, but the primary path adds no plugin, function, tool approval, or audit.
A safe tool loop requires a server allowlist; per-invocation tenant/user/resource authorization; schema and size validation; minimal arguments/results; explicit confirmation and idempotency for writes; actor/target/policy/result audit; recursion/concurrency/token/time/cost limits; and no automatic replay of side effects.
5. RAG port
Application registers UnavailableRagSearchPort. Infrastructure replaces it only when AIManage:Provider is configured and required connector services exist.
The adapter narrows connector DTOs, which is a sound boundary. Some GetSystemPrompt, IndexDocument, and collection calls do not pass cancellation through. Collection idempotency depends on exception-message text such as “already exists”, a brittle SDK boundary.
6. Agent port
IAgentServiceAdapter is also fail closed by default. The production bridge wraps connector session chat and raw streaming. Comments identify Guidance as a possible consumer, not a completed switch. Stream does not automatically persist history.
Session isolation is currently a caller responsibility. A structured tenant-owned key and tenant-bound store predicate are stronger than a string convention such as tenant|user|route.
7. RAG security
Define tenant/data-domain collection isolation, server-enforced metadata filters, per-result source authorization, delete/permission-change propagation, embedding-provider data policy, and untrusted-context delimiters. Vector search does not replace authorization.
// Build the collection and mandatory filter from trusted server context.var scope = await ragScope.ResolveAsync(currentUser, resourceIds, cancellationToken);var results = await rag.HybridSearchAsync( scope.Collection, query, Math.Min(topK, 20), scope.RequiredFilter, cancellationToken);
// Reauthorize every returned source before content reaches the model.8. Honest maturity table
| Capability | Current state | Safe claim |
|---|---|---|
| tenant skill catalog | persisted | metadata management |
| file prompt catalog | process scanner | prompt-file discovery/reload |
| tool execution | absent | none |
| Semantic Kernel chat | wired | text-chat adapter |
| RAG bridge | conditional | explicit consumers may call it |
| automatic chat RAG | absent | none |
| Agent bridge | conditional | explicit consumers may call it |
| multi-agent orchestration | absent | none |
Existing tests verify adapter forwarding and fail-closed composition. Add file-route authorization, missing directory/event loss/duplicate names, malicious or huge files, schema validation, per-tool authorization, cross-tenant RAG, deletion propagation, prompt injection, cancellation, typed connector errors, and end-to-end Chat/RAG/Tool tests.
9. Extension decision guide
Use a persisted skill entry only when the platform has a governed executor and tenant release lifecycle. Use a file prompt only for deployment-owned prompt material whose process-wide scope is intentional. Use RAG only when source authorization and index lifecycle are defined. Use Agent sessions only when a structured tenant-owned session key, budget, and recovery contract exist.
Do not use HandlerType strings as a plugin marketplace, file scanning as tenant configuration, a client-supplied metadata filter as authorization, or an Agent session ID convention as the sole isolation boundary.
10. Operations and troubleshooting
| Symptom | Inspect | Current caveat |
|---|---|---|
| new skill file not detected | directory existed at Start and watcher state | later directory creation is unreliable |
| registered skill never runs | chat/tool wiring | no executor exists today |
| RAG resolves as unavailable | provider config and connector DI | fail-closed is intentional |
| deleted source still retrieved | index lifecycle consumer | module does not supply universal deletion sync |
| cross-session history appears | session-key construction and connector store | caller owns isolation today |
# Confirm that catalog surfaces have not silently become executable tools.rg -n "HandlerAssemblyQualifiedType|FileSkillDefinition|KernelFunction|Activator.CreateInstance" \ src/Platform/AIManage -g '*.cs'