Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions openspace/skills/default-utf8-encoding/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: default-utf8-encoding
description: Baseline file encoding policy. Always use UTF-8 for created or edited text files unless the project explicitly requires another encoding.
---

# UTF-8 Baseline

This is a mandatory baseline skill for local development.

## Rules

1. Default encoding for all newly created or edited text files is UTF-8.
2. Preserve existing non-UTF-8 encoding only when the target file is already using it and changing encoding may break runtime behavior.
3. When uncertain about file encoding, prefer safe read/modify/write steps that keep file content valid and avoid mojibake.
4. For JSON, Markdown, YAML, Python, TypeScript, and shell scripts, treat UTF-8 as the standard unless the repository explicitly states otherwise.

## Output Hygiene

1. Avoid introducing garbled characters caused by encoding mismatch.
2. Keep line endings and formatting consistent with repository conventions.
16 changes: 16 additions & 0 deletions openspace/tool_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

logger = Logger.get_logger(__name__)

MANDATORY_SKILL_NAME = "default-utf8-encoding"


@dataclass
class OpenSpaceConfig:
Expand Down Expand Up @@ -704,6 +706,20 @@ async def _select_and_inject_skills(
"selected": [],
}

# Always inject built-in UTF-8 baseline guidance when available.
forced_skill_ids: List[str] = []
forced_meta = self._skill_registry.get_skill_by_name(MANDATORY_SKILL_NAME)
if forced_meta:
already_selected = {s.skill_id for s in selected}
if forced_meta.skill_id not in already_selected:
selected.insert(0, forced_meta)
forced_skill_ids.append(forced_meta.skill_id)
logger.debug(f"Forced baseline skill: {forced_meta.skill_id}")

if selection_record is not None and forced_skill_ids:
selection_record["forced_skills"] = forced_skill_ids
selection_record["selected"] = [s.skill_id for s in selected]

# Record skill selection to metadata.json
if self._recording_manager and selection_record:
# Add model info to the record
Expand Down
129 changes: 129 additions & 0 deletions tests/test_utf8_baseline_skill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import importlib
import sys
import types
import unittest


def _install_tool_layer_stubs():
agents_mod = types.ModuleType("openspace.agents")
agents_mod.GroundingAgent = type("GroundingAgent", (), {})

llm_mod = types.ModuleType("openspace.llm")
llm_mod.LLMClient = type("LLMClient", (), {"__init__": lambda self, *a, **k: None})

grounding_client_mod = types.ModuleType("openspace.grounding.core.grounding_client")
grounding_client_mod.GroundingClient = type("GroundingClient", (), {})

config_mod = types.ModuleType("openspace.config")
config_mod.get_config = lambda: None
config_mod.load_config = lambda *args, **kwargs: None

config_loader_mod = types.ModuleType("openspace.config.loader")
config_loader_mod.get_agent_config = lambda *args, **kwargs: None

recording_mod = types.ModuleType("openspace.recording")
recording_mod.RecordingManager = type("RecordingManager", (), {})

skill_engine_mod = types.ModuleType("openspace.skill_engine")
skill_engine_mod.SkillRegistry = type("SkillRegistry", (), {})
skill_engine_mod.ExecutionAnalyzer = type("ExecutionAnalyzer", (), {})
skill_engine_mod.SkillStore = type("SkillStore", (), {})

evolver_mod = types.ModuleType("openspace.skill_engine.evolver")
evolver_mod.SkillEvolver = type("SkillEvolver", (), {})

logging_mod = types.ModuleType("openspace.utils.logging")

class Logger:
@staticmethod
def get_logger(name):
return types.SimpleNamespace(
info=lambda *a, **k: None,
debug=lambda *a, **k: None,
warning=lambda *a, **k: None,
error=lambda *a, **k: None,
)

logging_mod.Logger = Logger

stubs = {
"openspace.agents": agents_mod,
"openspace.llm": llm_mod,
"openspace.grounding.core.grounding_client": grounding_client_mod,
"openspace.config": config_mod,
"openspace.config.loader": config_loader_mod,
"openspace.recording": recording_mod,
"openspace.skill_engine": skill_engine_mod,
"openspace.skill_engine.evolver": evolver_mod,
"openspace.utils.logging": logging_mod,
}

originals = {name: sys.modules.get(name) for name in stubs}
sys.modules.update(stubs)
return originals


class UTF8BaselineSkillTests(unittest.IsolatedAsyncioTestCase):
@classmethod
def setUpClass(cls):
cls._originals = _install_tool_layer_stubs()
sys.modules.pop("openspace.tool_layer", None)
cls.tool_layer = importlib.import_module("openspace.tool_layer")

@classmethod
def tearDownClass(cls):
sys.modules.pop("openspace.tool_layer", None)
for name, module in cls._originals.items():
if module is None:
sys.modules.pop(name, None)
else:
sys.modules[name] = module

async def test_select_and_inject_skills_always_includes_utf8_baseline(self):
config = self.tool_layer.OpenSpaceConfig()
openspace = self.tool_layer.OpenSpace(config)

forced_meta = types.SimpleNamespace(
skill_id="default-utf8-encoding__imp_abc12345",
name="default-utf8-encoding",
description="baseline",
)

class FakeRegistry:
async def select_skills_with_llm(self, task, llm_client, max_skills, skill_quality):
return [], {"method": "llm", "selected": []}

def get_skill_by_name(self, name):
if name == "default-utf8-encoding":
return forced_meta
return None

def build_context_injection(self, skills, backends=None):
return "|".join(s.skill_id for s in skills)

injected = {}

class FakeAgent:
backend_scope = ["shell"]

def set_skill_context(self, context_text, skill_ids):
injected["context_text"] = context_text
injected["skill_ids"] = list(skill_ids)

def clear_skill_context(self):
injected["cleared"] = True

openspace._skill_registry = FakeRegistry()
openspace._grounding_agent = FakeAgent()
openspace._recording_manager = None
openspace._skill_store = None
openspace._get_skill_selection_llm = lambda: object()

selected = await openspace._select_and_inject_skills("any task")

self.assertTrue(selected)
self.assertIn(forced_meta.skill_id, injected["skill_ids"])


if __name__ == "__main__":
unittest.main()