---
name: skill-retrieval
title: Semantic Skill Retrieval (Voyager-inspired)
description: Engine substrate for task-aware re-ordering + truncation of the skill set injected into subagent prompts
triggers: []
category: _meta
status: active
priority: 0
author: system
---

# Semantic skill retrieval

This is an **engine substrate** — it's never injected into an agent's prompt.
`Orchestrator._build_subagent_prompt` reads `config/retrieval.yaml` at
runtime and, when enabled, re-orders (and optionally truncates) the skill
bodies it injects into a subagent's system prompt by semantic relevance
to the dispatched task.

Inspired by Voyager (Wang et al. 2023). Pattern only — no code imported.

## What this skill owns

- **Enable flag**: `enabled: false` by default. When off, `_build_subagent_prompt`
  keeps its legacy behavior (inject every assigned skill in `agent_def.skills`
  order). Flip to `true` to turn on task-aware ordering.
- **top_k**: maximum number of skills to inject when enabled. `0` means
  "no truncation, just re-order" — still a win for small-LLM context use
  because the most relevant skills land first in the prompt. Positive N
  truncates to the top N after ranking.
- **min_score**: cosine-similarity floor. Hits below this are dropped
  regardless of top_k. Defaults to `0.0` (no floor) so low-relevance
  skills still appear below high-relevance ones.
- **bypass_skill_scope**: when `Orchestrator.dispatch(skill_scope=...)` is
  set (invoke_skill pathway), the dispatch is ALREADY narrowed to one
  specific skill context. Re-ordering the prompt by semantic similarity
  to the task is redundant — the caller decided the skill. Defaults to
  `true` so `skill_scope` dispatches bypass this filter entirely.

## Safety invariants (non-negotiable)

1. **Tool-grants are never filtered.** `Orchestrator.get_allowed_tools()`
   is called independently of prompt-building and unions `tools_granted`
   from EVERY skill in `agent_def.skills`. Filtering the prompt does NOT
   narrow the tool set.
2. **Fail-open at every boundary.** Missing embedder, missing fastembed,
   query-embed failure, empty result set — all fall through to the legacy
   path (inject all assigned skills in their declared order). A broken
   retrieval layer NEVER breaks subagent dispatch.
3. **Decision log unaffected.** `context_skills` in the decision record
   still contains the full `agent_def.skills` list. Semantic filtering is
   a PROMPT-layer concern, not an audit-trail concern. Future reviewers
   can see which skills were assigned even when the prompt only showed some.
4. **`skill_scope` always wins.** When invoke_skill narrowed the dispatch
   to one skill context, the semantic filter bypasses — the caller's
   explicit narrowing is respected.

## What this skill does NOT own

- Which tools the subagent can call — that's `get_allowed_tools()` +
  `skill_scope` narrowing in `_dispatch_with_tools`.
- Which skills are ASSIGNED to an agent — that's `agent_def.skills`
  from the agent definition YAML.
- Whether semantic retrieval EXISTS — that's `SkillLoader.set_embedder`
  wired in main.py. If the embedder isn't wired, this skill's flags do nothing.

## Auditability

Every dispatch where the filter actually changes the injected skill list
appends a `skill_retrieval` entry to `execution.supervisor_log` (or the
dispatch-level decision details) with `{agent, task_preview,
assigned_count, injected_count, top_scores}`. No-op paths
(disabled, no task, skill_scope set, embedder missing) log nothing.
