fix(agent): gate skill-index demotion behind the opt-in focus mode (#44387)
The coding posture's names-only demotion of non-coding skill categories (#44342) applied under the default auto mode, silently changing the skill index for every user in a git repo. Index changes must be opt-in: demotion now only fires under agent.coding_context=focus, alongside the toolset collapse. auto/on leave the skill index untouched; focus semantics are unchanged (demoted, never hidden; deny-list keeps coding-adjacent and custom categories at full entries).
This commit is contained in:
parent
c7bfc938d5
commit
4d6a133a9f
4 changed files with 52 additions and 33 deletions
|
|
@ -38,12 +38,13 @@ session (deferred), the same contract as ``/skills install`` vs ``--now``.
|
|||
|
||||
Activation (config ``agent.coding_context``):
|
||||
|
||||
* ``auto`` (default) — posture (brief + snapshot + names-only demotion of
|
||||
non-coding skill categories) on an interactive coding surface sitting in
|
||||
a code workspace (git repo or recognised project root). Prompt-only;
|
||||
toolsets untouched, no skill is ever hidden.
|
||||
* ``auto`` (default) — posture (brief + snapshot) on an interactive coding
|
||||
surface sitting in a code workspace (git repo or recognised project root).
|
||||
Prompt-only; toolsets and the skill index untouched.
|
||||
* ``focus`` — like ``auto``, but additionally collapses the toolset to the
|
||||
``coding`` set + enabled MCP servers. Explicit opt-in for a lean schema.
|
||||
``coding`` set + enabled MCP servers and demotes non-coding skill
|
||||
categories to names-only in the prompt's skill index (no skill is ever
|
||||
hidden). Explicit opt-in for a lean schema.
|
||||
* ``on`` — force the posture anywhere (incl. non-workspaces). Prompt-only.
|
||||
* ``off`` — disable entirely.
|
||||
"""
|
||||
|
|
@ -214,8 +215,8 @@ class ContextProfile:
|
|||
(extension seam; not yet consumed by the router).
|
||||
``memory_policy``— memory namespace/weighting hint (extension seam).
|
||||
``compact_skill_categories`` — skill categories DEMOTED to names-only in
|
||||
the system-prompt skill index while this posture is
|
||||
active. Never hidden: every skill name stays visible
|
||||
the system-prompt skill index under the opt-in ``focus``
|
||||
mode. Never hidden: every skill name stays visible
|
||||
(so memory-anchored recall keeps working) — only the
|
||||
descriptions are dropped to cut index noise. Deny-list
|
||||
semantics so unknown/custom categories keep full
|
||||
|
|
@ -231,7 +232,7 @@ class ContextProfile:
|
|||
|
||||
|
||||
# Skill categories that are clearly not part of a coding workflow. Demoted to
|
||||
# names-only in the prompt's skill index while the coding posture is active
|
||||
# names-only in the prompt's skill index under the opt-in ``focus`` mode only
|
||||
# (deny-list — anything not listed here, incl. custom user categories, keeps
|
||||
# full entries). Coding-adjacent categories (devops, github, mcp,
|
||||
# data-science, diagramming, research, security, …) are intentionally absent.
|
||||
|
|
@ -438,15 +439,22 @@ class RuntimeMode:
|
|||
def compact_skill_categories(self) -> frozenset[str]:
|
||||
"""Skill categories to demote to names-only in the prompt's skill index.
|
||||
|
||||
Demoted — never hidden. An earlier revision fully pruned these
|
||||
categories from the index, which caused silent capability loss in a
|
||||
real workflow: agent-created skills are the model's accumulated
|
||||
project memory (server-ops runbooks, learned pitfalls, …), and models
|
||||
do not reliably reach for ``skills_list`` to rediscover what the
|
||||
index stopped showing them. Names-only keeps every skill loadable on
|
||||
recall while still cutting the description noise from the index.
|
||||
Gated on the opt-in ``focus`` mode, like the toolset collapse: the
|
||||
default posture leaves the skill index untouched. Users who didn't ask
|
||||
for a lean prompt keep full entries for every category — index changes
|
||||
under ``auto`` proved too surprising in practice, even names-only ones
|
||||
(a demoted description is information the model no longer weighs when
|
||||
deciding what to load).
|
||||
|
||||
Demoted — never hidden — even under ``focus``. An earlier revision
|
||||
fully pruned these categories from the index, which caused silent
|
||||
capability loss in a real workflow: agent-created skills are the
|
||||
model's accumulated project memory (server-ops runbooks, learned
|
||||
pitfalls, …), and models do not reliably reach for ``skills_list`` to
|
||||
rediscover what the index stopped showing them. Names-only keeps every
|
||||
skill loadable on recall while still cutting the description noise.
|
||||
"""
|
||||
if not self.is_coding:
|
||||
if not self.is_coding or self.config_mode != "focus":
|
||||
return frozenset()
|
||||
return frozenset(self.profile.compact_skill_categories)
|
||||
|
||||
|
|
@ -534,9 +542,11 @@ def coding_compact_skill_categories(
|
|||
) -> frozenset[str]:
|
||||
"""Skill categories the active posture demotes to names-only in the index.
|
||||
|
||||
Empty outside the coding posture. Demoted — never hidden: every skill
|
||||
name stays in the index and remains loadable via ``skill_view`` /
|
||||
``skills_list``; only descriptions are dropped.
|
||||
Empty outside the coding posture and outside the opt-in ``focus`` mode —
|
||||
the default posture never touches the skill index. Under ``focus``,
|
||||
demoted — never hidden: every skill name stays in the index and remains
|
||||
loadable via ``skill_view`` / ``skills_list``; only descriptions are
|
||||
dropped.
|
||||
"""
|
||||
return resolve_runtime_mode(
|
||||
platform=platform, cwd=cwd, config=config
|
||||
|
|
|
|||
|
|
@ -191,9 +191,10 @@ def build_system_prompt_parts(agent: Any, system_message: Optional[str] = None)
|
|||
)
|
||||
if toolset
|
||||
}
|
||||
# Coding posture demotes non-coding skill categories to names-only in
|
||||
# the index (never hidden — skill_view/skills_list reach everything,
|
||||
# and every name stays visible for memory-anchored recall).
|
||||
# Focus mode (opt-in) demotes non-coding skill categories to
|
||||
# names-only in the index (never hidden — skill_view/skills_list
|
||||
# reach everything, and every name stays visible for recall). The
|
||||
# default coding posture leaves the index untouched.
|
||||
_compact_cats = frozenset()
|
||||
try:
|
||||
from agent.coding_context import coding_compact_skill_categories
|
||||
|
|
|
|||
|
|
@ -877,7 +877,9 @@ DEFAULT_CONFIG = {
|
|||
# Toolsets are never touched; messaging platforms
|
||||
# unaffected.
|
||||
# "focus" — auto + collapse the toolset to the lean coding
|
||||
# set (+ enabled MCP servers). Explicit opt-in.
|
||||
# set (+ enabled MCP servers) + demote non-coding
|
||||
# skill categories to names-only in the prompt's
|
||||
# skill index. Explicit opt-in.
|
||||
# "on" — force the prompt posture everywhere.
|
||||
# "off" — disable entirely.
|
||||
"coding_context": "auto",
|
||||
|
|
|
|||
|
|
@ -368,21 +368,27 @@ class TestProfiles:
|
|||
assert cc.GENERAL_PROFILE.toolset is None
|
||||
assert cc.GENERAL_PROFILE.guidance == ""
|
||||
|
||||
def test_skill_demotion_scoped_to_coding_posture(self, tmp_path):
|
||||
# Coding posture demotes clearly-non-coding categories to names-only
|
||||
# in the index (never hides them — agent-created skills are the
|
||||
# model's project memory and must stay recallable by name).
|
||||
# Coding-adjacent categories keep full entries (deny-list semantics).
|
||||
def test_skill_demotion_gated_on_focus(self, tmp_path):
|
||||
# Names-only demotion is opt-in via focus mode — the default (auto)
|
||||
# and forced (on) postures leave the skill index untouched. Under
|
||||
# focus, clearly-non-coding categories are demoted (never hidden) and
|
||||
# coding-adjacent ones keep full entries (deny-list semantics).
|
||||
_git_init(tmp_path)
|
||||
for raw in ("auto", "on", "focus"):
|
||||
for raw in ("auto", "on"):
|
||||
mode = cc.resolve_runtime_mode(
|
||||
platform="cli", cwd=tmp_path, config={"agent": {"coding_context": raw}}
|
||||
)
|
||||
assert mode.is_coding is True
|
||||
compact = mode.compact_skill_categories()
|
||||
assert "social-media" in compact and "smart-home" in compact
|
||||
for kept in ("github", "devops", "software-development", "data-science"):
|
||||
assert kept not in compact
|
||||
assert mode.compact_skill_categories() == frozenset()
|
||||
focus = cc.resolve_runtime_mode(
|
||||
platform="cli", cwd=tmp_path,
|
||||
config={"agent": {"coding_context": "focus"}},
|
||||
)
|
||||
assert focus.is_coding is True
|
||||
compact = focus.compact_skill_categories()
|
||||
assert "social-media" in compact and "smart-home" in compact
|
||||
for kept in ("github", "devops", "software-development", "data-science"):
|
||||
assert kept not in compact
|
||||
# General posture demotes nothing.
|
||||
general = cc.resolve_runtime_mode(platform="telegram", cwd=tmp_path, config={})
|
||||
assert general.compact_skill_categories() == frozenset()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue