Intent Surface — Seam 8, Phases 2-5 (complete)¶
Status: kickoff slice (Phases 2-3) shipped on
feat/au-intent-surface; the CPD (Phase 1,feat/au-cpd) and this doc's §7 remaining work (Phases 4-5 — CPD-backed ranking, the calibrated-outcomes learning loop, resolution caching, the intent-surface skill content (now folded intograph-runtime-and-governance, since moved to graph-os's own skill pack; see the note below), and the A/B selection-accuracy harness) shipped onfeat/au-seam8-complete(NOT merged/pushed — awaiting review). Parent plan:plans/program-design-2026-07-11-epistemic-tool-routing.md. Concepts:CONCEPT:AU-ECO.mcp.intent-surface-condensed-collapse(the surface collapse) /CONCEPT:AU-ECO.mcp.intent-surface-tool-lifecycle(the load→use→unload lifecycle) /CONCEPT:AU-ECO.mcp.intent-surface-cpd-ranking(CPD-backed resolver ranking) /CONCEPT:AU-ECO.mcp.intent-surface-outcome-learning(the calibrated-outcomes learning loop) /CONCEPT:AU-ECO.mcp.intent-surface-resolution-cache(bounded resolution caching) /CONCEPT:AU-ECO.mcp.intent-surface-selection-accuracy(the A/B measurement harness).Naming note (post-collapse,
feature/authority-convergence). This doc's §5 table and thekg-*skill names it references throughout are historical: the ~73 granularkg-*skills it describes (one per wrapped verb/tool) were folded into 13 broad domain skills (agent_utilities/skills/graph-*/agent-utilities-*) so the skill layer matches this same doc's own condensed-surface philosophy one level up. Those 7graph-*domain skills have since moved to graph-os's own skill pack (graph_os/skills/graph-*, taken over in graph-os commit 704ce45) and are removed from agent-utilities, which retains only the 4agent-utilities-*skills. Every verb named below is still reachable exactly as documented; find its current skill home via graph-os'sgraph-runtime-and-governanceskill's "Coverage governance" section or the mapping in §5.Retirement note (current). The retired tool-mode profile switch described in §2-§3 below (
condensed/verbose/both/intent) has since been retired. There is now exactly one MCP tool contract, unconditionally: graph-os serves only the intent tools (ask,find,write,act,manage,why, plus the two MCP Apps launchers), each takingaction+params(+ optional natural-languageintent) — not thehints_jsonshape this doc describes below. The ~95 granular action-routed tools this doc calls the "condensed" surface still register fully on a private backing FastMCP server and dispatch through the same_execute_tool/REGISTERED_TOOLScore; they are simply never listed, reached instead viaact(action="<tool>.<op>")orfind(action="describe"). There is no gating tag, no per-deployment mode, and noload_tools/unload_toolsescape hatch on graph-os itself (that session-visibility mechanism lives on in the standalonemcp-multiplexergateway for fleet tools). Seeagent_utilities/mcp/intent_contract.pyandagent_utilities/mcp/graphos_surface.pyfor the current, authoritative contract; the rest of this document is retained as the historical design record of how the collapse was built.
1. Problem¶
graph-os's condensed (default) MCP surface is already ~95 tools. Past a point, more tools lowers LLM tool-selection accuracy — worst for Haiku / local / small-context models. The granular tools (and every REST route) must stay exactly as capable as today; only the LLM-facing tool list needed a smaller, additional front door.
2. What this ships (historical — see the retirement note above for the current contract)¶
flowchart TD
subgraph Intent["graph-os — the one tool contract (always on)"]
V["6 intent verbs\nask · find · write · act · manage · why"]
R["Resolver\n(intent_tools.resolve_intent)"]
G["Granular backing tools\n(~95 operations, registered but never\nlisted in tools/list)"]
V --> R
R -->|"ranked capability + why"| G
end
X["_execute_tool core\n(REGISTERED_TOOLS)"]
REST["REST gateway\n(ACTION_TOOL_ROUTES)"]
G --> X
X --> REST
style Intent fill:#efe,stroke:#8a8
(Historical note: the kickoff design below shipped this as an opt-in the retired intent tool mode
profile alongside a default condensed mode and a load_tools/unload_tools escape hatch on
graph-os itself. Both the mode switch and the local gate were later retired — the diagram above
reflects the current, unconditional contract; see the retirement note at the top of this
document.)
- Six intent-verb tools (
agent_utilities/mcp/tools/intent_tools.py) —ask(NL/UQL read),find(capability discovery),write,act,manage,why. Each takesaction: str(an operation id"<tool>.<op>", or"describe") +params: dict(structured args) + optionalintent: str(natural language, routed whenactionis empty) +execute: bool. Small, fixed schema in either case of how multiple granular tools exist behind it. (The kickoff design below originally shaped this asintent+hints_json; the condensedaction/paramsshape superseded it — see the retirement note above.) - The resolver (
resolve_intent/dispatch_intent) — ranks everyREGISTERED_TOOLSentry tagged for that verb (TOOL_VERBS) with a dependency-free lexical scorer (token overlap + a name-coverage tie-breaker). Focused action facades publishLiteral[...]action contracts into the generated manifest; an exact multi-word action phrase such ascode contextcontributes route evidence, while an explicit unpinnedactionfirst narrows candidates to its current owning tools and scores only that selected operation. The resolver then picks the top candidate and dispatches it through the same_execute_toolcore every condensed tool uses, and returns{"result", "routing", "executed"}—routingcarries the chosen tool/action, matched terms, alternatives considered, and a plain-English "why".askalso falls back tonl_query(the engine's own NL planner) when the winning candidate needs structured args the caller didn't provide. - (Retired) the profile switch — the kickoff design gave the retired tool-mode switch a 4th value,
intent, alongsidecondensed/verbose/both(mcp/verbose_tools.py). Inintentmode the condensed tools still registered fully (REST +_execute_tool+REGISTERED_TOOLS— nothing lost) but were also taggedGATED_TAG(+ the mode-independentGRANULAR_TAG) and held back from a session's default tool list, whilecondensed(the default) stayed unaffected. The whole mode switch has since been retired: graph-os now serves only the intent contract, unconditionally, with no per-deployment toggle. - (Retired) the escape hatch, both directions — the fleet
load_tools/unload_toolsmeta-tools (mcp/multiplexer.py) manage session-visibility for external fleet tools (MCPMultiplexer._exposed); the kickoff design extended the SAME mechanism to graph-os's own gated tools (_local_gated) soload_tools(tools=["graph_query"])can reveal an exact granular tool with no mounting needed, andunload_toolsretract it again. graph-os no longer gates its own tools this way — the granular backing tools are simply always reachable viaact/find(action="describe"), with no local-gate state to load/unload. Theload_tools/unload_toolsmechanism itself lives on, unchanged, for the standalonemcp-multiplexergateway's fleet tools. - (Retired) responsible tool usage — the load→use→unload lifecycle
(
CONCEPT:AU-ECO.mcp.intent-surface-tool-lifecycle).load_tools(..., auto_unload=true)marked a tool for automatic retraction the moment it was next called, and themanageintent verb exposed the same lifecycle core directly. With graph-os's own gating retired, this lifecycle no longer applies to graph-os's granular tools — reclaiming context there is now moot, since they were never individually listed in the first place. It remains the live mechanism for fleet tools mounted throughmcp-multiplexer.
3. Resolver design — CPD-backed, with a graceful pre-CPD fallback¶
resolve_intent/dispatch_intent now rank each capability against its OWN generated
Capability Power Descriptor (docs/capabilities-power.json — one_line/examples/does[]
action names + intent_verbs, unioned with the hand-curated TOOL_VERBS entry so switching to
the CPD only ever ADDS routing surface, never narrows it) when one exists for that tool. A tool
absent from the CPD set (a brand-new tool ahead of the next gen_capability_power.py --write, or
the CPD JSON missing entirely — a lean/headless install with no docs/) falls back
per-capability to the original local candidate table (REGISTERED_TOOLS docstrings +
TOOL_VERBS + _graphos_action_manifest) — never an error, never a silent gap.
CapabilityCandidate/resolve_intent/dispatch_intent's shape is unchanged, exactly as this
kickoff's original design intended; routing.capability_source reports which substrate actually
produced the winning ranking for that dispatch.
On top of the CPD-backed ranking, two more seams landed in the same slice
(agent_utilities/mcp/tools/intent_tools.py):
- Calibrated-outcomes learning loop (
CONCEPT:AU-ECO.mcp.intent-surface-outcome-learning). Everydispatch_intentcall feeds its success/failure back intoOutcomeRouter(agent_utilities/orchestration/outcome_router.py, itself a thin wrapper overCapabilityIndex.record_outcome/reward_of— the SAME durable-bandit mechanismReasonerRouter/variant_pool.evolve_profilealready share, not a second learner), keyedverb:tool.resolve_intentblends each candidate's reward EMA into its lexical score, so a capability that keeps failing under a verb sinks in the ranking and one that keeps succeeding rises — real calibrated-outcomes routing, distinct from the CPD JSON's own (always-empty at static-generation time)calibrated_outcomesfield. - Resolution caching (
CONCEPT:AU-ECO.mcp.intent-surface-resolution-cache). Ranked (non-pinned) resolutions are served from a small bounded in-process LRU keyed by(verb, normalized intent, hints, top_k)plus two monotonic counters — the candidate-table generation (bumps when the CPD/tool surface rebuilds) and the reward epoch (bumps on every recorded outcome) — so a repeated intent is served from cache until the routing policy it was ranked under actually changes.
4. Kickoff-slice scope note (historical) — all closed by §7's follow-up work¶
The original kickoff slice (Phases 2-3) deliberately shipped without a per-capability CPD, an
outcome-learning loop, a dedicated intent-surface skill, or an A/B measurement — each was a named
follow-up (§7). All four landed together on feat/au-seam8-complete:
- Per-capability CPD —
feat/au-cpd(Phase 1) shippeddocs/capabilities-power.json; the resolver now ranks against it (§3). - Calibrated-outcomes / bandit learning loop —
resolve_intent/dispatch_intentnow record and blend in a learned reward EMA viaOutcomeRouter(§3). - Dedicated skill — originally
agent_utilities/skills/kg-intent/SKILL.md(tier: meta), now folded intograph-runtime-and-governance's "Manage tool visibility responsibly" workflow step (that skill has since moved to graph-os's own skill pack and is removed from agent-utilities), which documents the resolver/dispatcher mechanism directly;ask/find/write/act/manage/whyremain inskill_coverage.INTENTIONALLY_UNSKILLED(correctly — a meta concern never claims verb coverage), with the comment there pointing at that section. - A/B selection-accuracy measurement —
scripts/measure_intent_routing_accuracy.py+agent_utilities/knowledge_graph/retrieval/intent_selection_accuracy.py(originally a 21-case hand-labelled corpus across all five dispatching verbs, now 25 — see D-ORC-19 in §7) + thetests/unit/test_intent_selection_accuracy.pyregression tripwire. Measured 2026-07-11: top-1 76.19%, top-3 85.71% against the original 21-case corpus (a live run of the real resolver, not a fabricated number) — see §7.
5. Skill sweep — preserving every kg-* skill under the condensed intent surface¶
Scope: agent-utilities' own skills only (agent_utilities/skills/**), not the 785-skill
fleet. Principle: zero feature lost — every kg-* skill still documents its exact
granular tool(s) unchanged; each one now ALSO explains how to reach that tool when
the retired intent tool mode is active.
What changed, uniformly, in all 53 verb-wrapping kg-*/kg-modality-* skills (tier core
or modality — computed from skill_coverage.discover_skills(), the SAME machinery the
MCP⇄REST⇄skill parity gate uses): one standardized note inserted right after the # kg-<name>
heading (frontmatter, ## Invoke, and every other section untouched).
Post-collapse note: the "Skill" column below names the former
kg-*slug each row documented at the time this note shipped. Onfeature/authority-convergenceall 53 were folded into 13 broad domain skills (agent_utilities/skills/graph-*/agent-utilities-*) — the tool/verb mapping is still exactly accurate, butagent_utilities/skills/kg-<name>/no longer exists on disk. The 7graph-*domain skills have since moved to graph-os's own skill pack and are removed from agent-utilities, which retains only the 4agent-utilities-*skills. Cross-reference graph-os'sgraph-runtime-and-governanceskill's "Coverage governance" section (orpython -m agent_utilities.mcp.skill_coverage) for which broad skill documents a given tool today.Condensed intent-surface note (Seam 8). Under the small/cheap-LLM profile (the retired
intenttool mode),<tool(s)>is/are held back from the default tool list (nothing removed — REST +_execute_toolstill reach it/them exactly as documented below). Two ways to use this skill unchanged: (1)load_tools(tools=["<tool>"])once per session (as below), then proceed exactly as documented; or (2) call the<verb>intent verb with the same natural-language request — the resolver routes to<tool>for the operator and returns the result plus a routing justification. The retired defaultcondensedtool mode was completely unaffected.
| Skill | Wrapped tool(s) | Verb |
|---|---|---|
kg-analyze |
graph_analyze |
ask |
kg-ask |
graph_ask, ask_data |
ask |
kg-broker |
graph_broker |
act |
kg-bus |
graph_bus |
act |
kg-code |
graph_code, graph_code_nav |
ask |
kg-configure |
graph_configure |
manage |
kg-context |
graph_context |
ask |
kg-document-tree |
graph_document_tree |
ask |
kg-etl |
graph_etl |
write |
kg-evaluate |
graph_evaluate |
why |
kg-explain |
graph_explain |
why |
kg-extract-concepts |
concept_registry |
find |
kg-feedback |
graph_feedback |
why |
kg-feeds |
graph_feeds |
act |
kg-fork |
graph_fork |
act |
kg-gis |
graph_gis |
ask |
kg-goals |
graph_goals, spec_ticket |
act |
kg-hydrate |
graph_hydrate |
manage |
kg-ingest |
graph_ingest, source_sync, source_drain, source_connector, document_process |
write |
kg-kvcache |
graph_kvcache |
manage |
kg-loops |
graph_loops |
act |
kg-memory |
graph_memory |
write |
kg-message |
graph_message |
act |
kg-modality-analytics |
engine_analytics, engine_datascience, engine_graphlearn, graph_learn |
ask |
kg-modality-blob |
engine_blob |
write |
kg-modality-channels |
engine_channels, engine_broker |
act |
kg-modality-finance |
engine_finance |
ask |
kg-modality-ledger |
engine_ledger |
write |
kg-modality-mining |
engine_mining, graph_mine, graph_mine_deep |
ask |
kg-modality-nodes-edges |
engine_nodes, engine_edges, engine_graph, engine_lifecycle |
write |
kg-modality-streaming |
engine_streaming |
act |
kg-modality-timeseries |
engine_timeseries |
write |
kg-modality-txn |
engine_txn |
act |
kg-observe |
graph_observe |
why |
kg-ontology |
graph_ontology, ontology_property_types, ontology_value_types, ontology_interface, ontology_sampling_profile, ontology_function, ontology_derive, ontology_link_materialize, ontology_leanix_sync, object_edits, object_index, object_permissioning, object_set |
write |
kg-ops-causal |
graph_ops_causal |
why |
kg-orchestrate |
graph_orchestrate |
act |
kg-persist-report |
research_artifact |
ask |
kg-promql |
graph_promql |
ask |
kg-query |
graph_query, nl_query |
ask |
kg-reach |
graph_reach |
act |
kg-research |
graph_research |
ask |
kg-runvcs |
graph_runvcs |
act |
kg-sandbox |
graph_sandbox |
act |
kg-schedules |
graph_schedules |
manage |
kg-search |
graph_search, graph_search_synthesis, graph_federated_search |
ask |
kg-secret |
graph_secret |
manage |
kg-sessions |
graph_sessions, ingest_sessions, usage_query |
manage |
kg-share |
graph_share |
write |
kg-table |
graph_table |
ask |
kg-traces |
graph_traces |
ask |
kg-write |
graph_write |
write |
kg-writeback |
graph_writeback |
write |
act seeds its natural-language text into graph_orchestrate.task. With no
agent_name, GraphOS resolves an ingested skill/workflow in the KG and delegates the
whole execution to local vLLM; authenticated child MCP schemas remain internal. The
non-read preview/plan_ref contract still governs the dispatch, and the result returns
only bounded output, opaque provenance, tool names/statuses, and any approval request.
For pinned orchestration intents, the agent and server hint aliases normalize to
agent_name; do not provide an alias and agent_name with different values. Every
other hint must be a documented parameter of the selected tool and is rejected before
a preview is stored.
Not touched (correctly exempt — tier: meta/surface, not verb wrappers): the former
kg-capability-builder (folded into agent-utilities-development), kg-coverage-doctor and
kg-mux-extend/kg-mux-use (folded into graph-runtime-and-governance), kg-delegate
(folded into graph-orchestration-and-automation) — the latter two skills have since moved
to graph-os's own skill pack and are removed from agent-utilities — plus, from the separate
agent-webui package (not part of this collapse), kg-webui-admin, kg-webui-dashboards,
kg-webui-extraction, kg-webui-graphviz, kg-webui-ontology-operator, kg-webui-swe.
Higher-level docs also updated (mention tool names/the retired tool-mode switch in prose, not a
per-capability wrapper):
- agent_utilities/skills/skill_graphs/agent-utilities/tools/SKILL.md — the platform's own tool
reference gained a top-of-file note pointing at this doc.
- agent_utilities/skills/skill_graphs/agent-utilities/SKILL.md — the one-line tool list now
notes the intent profile alternative.
- the graph-os repo's graph_os/skills/graphos-genesis/SKILL.md — the env-var canon section
(the retired tool-mode switch enum) now lists intent as a 4th valid value with a one-line explanation, so
a genesis-provisioned deployment's drift-guard/docs stay accurate. (No code change needed —
check_env_var_drift.py only checks for the KEY's presence, not an enum of values.)
Checked NOT needed: agent-utilities-self-evolution, agent-utilities-deployment,
agent-utilities-source-integration, autonomous-contribution skills reference graph-os tool
names only as illustrative examples of existing behavior (e.g. graph_write + graph_query in
a smoke test) that remains equally true under any retired tool mode — nothing in them assumes a
specific tool-visibility default, so no edit was needed to keep them accurate. (The latter two
have since moved to graph-os's own skill pack and are removed from agent-utilities.)
6. Tests¶
tests/unit/test_intent_surface.py— the resolver +dispatch_intent(hermetic: a fakeREGISTERED_TOOLSentry, no live engine): the required end-to-end proof (askresolves + dispatches via_execute_tool+ returns the justification), the NL-planner fallback, the explicit-tool-hint pin, a dispatch failure reported as structurederror(not a crash), and thatgraph_query— the toolgraph-query-and-explanationdocuments (now graph-os's own skill) — still resolves underask(no feature lost).tests/unit/test_intent_surface_build_server.py— builds the REAL graph-os server (bootstrap=False, no live engine) under the retiredintenttool mode: verbs + REST twins register, the granular surface (graph_query,graph_write,nl_query, …) stays fully registered, and the defaultcondensedmode is unaffected (regression guard).tests/test_intent_surface_gating.py— the local-gate + lifecycle mechanism against a real FastMCP server +Client: a gated tool is hidden by default,load_toolsreveals it (and it is then actually callable),unload_toolsretracts it by exact name / whole server / toolset tag,auto_unloadretracts it automatically right after its next call (andload_toolsbrings it straight back), and themanageverb's lifecycle shortcut reaches the same core.tests/unit/mcp/test_verbose_tools.py::test_surface_intent_mode_gates_condensed_tools—intentmode inregister_tool_surfacetags condensed toolsGATED_TAG/GRANULAR_TAGandgated_tool_names()surfaces them, without registering a verbose 1:1 surface.tests/unit/test_gateway_mcp_parity.py(existing, unmodified contract) stays green: every intent verb gets a REST twin via the SAME genericACTION_TOOL_ROUTESmechanismnl_query/ask_dataalready use, mounted by the SAME generic include_mount_rest_routes— no bespoke REST wiring was needed.tests/conftest.py's_isolate_registered_toolsfixture was extended to ALSO snapshot/restoreACTION_TOOL_ROUTES(previously onlyREGISTERED_TOOLS) — a test that builds the server under the retiredintenttool mode was otherwise the first thing in the whole suite to add a conditionally-registeredACTION_TOOL_ROUTESentry, which leaked into later tests as a false "phantom route" (every existing dynamic entry, e.g.nl_query, is added unconditionally on every build, so this gap never surfaced before).
§7 follow-up tests (added on feat/au-seam8-complete):
tests/unit/test_intent_surface.py::test_resolver_ranks_against_the_generated_cpd_when_availableand::test_dispatch_reports_cpd_capability_source_for_a_cpd_backed_tool— a real CPD-backed tool (graph_query) is ranked using its CPD text anddispatch_intentreports the CPD ascapability_source.::test_outcome_learning_biases_a_later_resolution— two lexically-tied fake tools; a recorded success on one flips a LATER unpinned resolution's top pick in its favor.::test_resolution_cache_hits_repeat_intent_misses_a_different_one— the same(verb, intent)hits the bounded cache (identical ranking, no new entry); a different intent is a fresh miss.tests/unit/test_intent_selection_accuracy.py— the A/B corpus is bounded (15-30 cases) and covers all five dispatching verbs; live-measured accuracy stays above a floor set with headroom below the measured baseline (top-1 76.19% / top-3 85.71%).tests/conftest.pygained_isolate_intent_outcome_learning— resets the sharedOutcomeRouter+ resolution cache between EVERY test in the suite (not justtest_intent_surface.py's own local fixture), so a dispatch outcome recorded against a REAL tool (e.g.graph_query) in one test file can never bias ranking in an unrelated, later test.
7. Program phases 4-5 — CLOSED on feat/au-seam8-complete¶
All five items below shipped together; each is cross-referenced to where it landed.
- Merge
feat/au-cpdand swap the resolver onto it — DONE.feat/au-cpd(Phase 1) had already merged tomainahead of this slice;_build_candidates/resolve_intentinagent_utilities/mcp/tools/intent_tools.pynow rank each capability against its own CPD (one_line/examples/does[]/intent_verbs, unioned withTOOL_VERBS) with a graceful per-capability fallback to the pre-CPD lexical table (§3).CapabilityCandidate/resolve_intent/dispatch_intent's shape is unchanged. - Calibrated-outcomes learning loop — DONE (
CONCEPT:AU-ECO.mcp.intent-surface-outcome-learning).dispatch_intentrecords every execution's success/failure intoOutcomeRouter(reusingCapabilityIndex.record_outcome/reward_of— no second learner);resolve_intentblends the learned reward EMA into its lexical score. Proven bytests/unit/test_intent_surface.py::test_outcome_learning_biases_a_later_resolution— two lexically-tied fake tools, one recorded success, the later unpinned resolution flips to prefer it. - Cache resolution — DONE (
CONCEPT:AU-ECO.mcp.intent-surface-resolution-cache). A bounded in-process LRU (_RESOLUTION_CACHE, 256 entries) keyed by(verb, normalized intent, hints, top_k, candidate-table generation, reward epoch)— the last two counters make it a policy-version-aware cache: a CPD/tool-surface rebuild or a freshly recorded outcome invalidates exactly the entries that can have used it, without a manual flush. A bare in-process cache was used rather than the Seam 6 KV-cache seam (that seam caches LLM KV blocks keyed on prompt-prefix reuse; this cache holds rankedCapabilityCandidatelists keyed on normalized intent — a different cache shape, better served by its own small LRU than by overloading the LLM KV-cache layer). Proven bytest_resolution_cache_hits_repeat_intent_misses_a_different_one. - Feed dispatch outcomes back into calibrated routing — DONE, folded into item 2. The
checked-in CPD JSON's own
calibrated_outcomesfield stays empty at static-generation time (no live engine reachable whengen_capability_power.py --writeruns) — the REAL calibrated-outcomes signal for live routing is the in-processOutcomeRouterreward EMA the resolver blends in, which is exactly what a bandit needs (fast, updated per-call) and is exposed per-dispatch asrouting.calibrated_outcome_reward. - A dedicated
tier: metaskill for the intent surface — DONE: originallyagent_utilities/skills/kg-intent/SKILL.md, now graph-os's owngraph-runtime-and-governanceskill's "Manage tool visibility responsibly" step — the six verbs, the CPD/learning/caching mechanism, the load→use→unload lifecycle, and when to usecondensedvs.intentmode.
A/B selection-accuracy measurement (design doc §4 phase 4) also shipped in this slice:
scripts/measure_intent_routing_accuracy.py (CLI) +
agent_utilities/knowledge_graph/retrieval/intent_selection_accuracy.py (originally the 21-case
corpus + measurement function) + tests/unit/test_intent_selection_accuracy.py (CI regression
tripwire). Measured against the real, CPD-backed resolver: top-1 76.19% (16/21), top-3
85.71% (18/21) — a live run, not a fabricated number (see the module docstring for method
and why "naming the tool directly" is the 100%-by-definition baseline this is traded against, not
a competing measurement). A model-in-the-loop task-success soak (vs. a small/cheap model, per the
original phase-4 wording) remains a natural follow-up once a model-eval harness is wired to this
corpus — this slice measures resolver accuracy alone, which is the mechanism Seam 8 itself owns.
D-ORC-19 / D-INT-4 (2026-08-01) — the 21-case corpus above measured resolver accuracy in
general but contained exactly ONE delegation case, and it only passed because its wording
contained the target tool's own name-token ("orchestrate"). No case named an ingested SKILL, the
actual operator delegation workflow, so the floor stayed green while unpinned delegation ("act:
Delegate to the servicenow-incident-management skill: ...") routed to an unrelated tool 100% of
the time — the skill's own name tokenized to servicenow/incident/management, which out-scored
graph_orchestrate's single skill doc-token hit (graph_incident 0.3383 vs graph_orchestrate
0.1111). Fixed by (1) adding four skill-naming delegation cases (none containing "orchestrate") to
the corpus, growing it to 25, and (2) teaching the resolver a structural
skill-delegation shape: any CPD that declares a skill_name input parameter (today only
graph_orchestrate — derived from typed_io.input_params, not the tool's name, so a future
delegation façade qualifies automatically) gets a flat +0.5 score bonus whenever the intent text
matches a hyphenated-slug-adjacent-to-the-word-"skill" pattern (_SKILL_DELEGATION_RE) —
independent of what domain words make up the slug, so naming any skill no longer fights the
router. Re-measured on the 25-case corpus at the same commit: top-1 76.00% (19/25), top-3 84.00%
(21/25), with all 5 delegation cases (the original plus the 4 new ones) now top-1 hits; the same
corpus against the pre-fix resolver measured top-1 64.00% (16/25), top-3 80.00% (20/25). Unpinned
plain-language skill delegation is therefore now correctly routed; pinning
(hints_json={"tool": "graph_orchestrate"}) remains supported and unaffected.