@@ -107,8 +107,6 @@ def test_build_system_prompt_with_skills():
107107 ]
108108 prompt = build_system_prompt (skills )
109109 assert "Pebble watch" in prompt
110- assert "speech-to-text" in prompt
111- assert "phonetically similar" in prompt
112110 assert "- add-note: Save a note." in prompt
113111 assert "- add-event: Create a calendar event." in prompt
114112 assert "/skills/personal/add-note.md" in prompt
@@ -138,13 +136,6 @@ def test_prompt_no_longer_caps_to_one_skill():
138136 assert "as many as the command needs" in p
139137
140138
141- def test_prompt_mentions_kb_default ():
142- from clayde .webhook .skills import build_system_prompt
143- p = build_system_prompt ([])
144- assert "/home/clayde/knowledge_base" in p
145- assert "Syncthing" in p
146-
147-
148139def test_prompt_contains_json_contract ():
149140 from clayde .webhook .skills import build_system_prompt
150141 p = build_system_prompt ([])
@@ -160,16 +151,6 @@ def test_prompt_when_no_skills_still_invites_judgement():
160151 assert "judgement" in p .lower () or "judgment" in p .lower ()
161152
162153
163- def test_prompt_mentions_kb_structure_disambiguation ():
164- from clayde .webhook .skills import build_system_prompt
165- p = build_system_prompt ([])
166- # Tells Claude to inspect KB layout and prefer phonetic neighbours
167- # that match real folders ("after people and tree" → "add a people entry").
168- assert "ls /home/clayde/knowledge_base" in p
169- assert "phonetic" in p .lower ()
170- assert "people" in p
171-
172-
173154def test_discovers_builtin_alongside_host (tmp_path ):
174155 from clayde .webhook .skills import discover_skills
175156 # Simulate the in-container layout: /skills/builtin + /skills/personal.
@@ -184,3 +165,36 @@ def test_discovers_builtin_alongside_host(tmp_path):
184165 skills = discover_skills (tmp_path )
185166 names = {s .name for s in skills }
186167 assert names == {"ping" , "add-note" }
168+
169+
170+ def test_discover_personal_overrides_builtin (tmp_path , caplog ):
171+ """Non-builtin skills (personal/shared) win over builtin on name collision."""
172+ from clayde .webhook .skills import discover_skills
173+ (tmp_path / "builtin" ).mkdir ()
174+ (tmp_path / "personal" ).mkdir ()
175+ (tmp_path / "builtin" / "voice-command.md" ).write_text (
176+ "---\n name: voice-command\n description: Builtin version.\n ---\n \n Builtin body.\n "
177+ )
178+ (tmp_path / "personal" / "voice-command.md" ).write_text (
179+ "---\n name: voice-command\n description: Personal override.\n ---\n \n Custom body.\n "
180+ )
181+ with caplog .at_level ("WARNING" , logger = "clayde.webhook" ):
182+ skills = discover_skills (tmp_path )
183+ assert len (skills ) == 1
184+ assert skills [0 ].description == "Personal override."
185+ assert any ("Duplicate skill name" in r .getMessage () for r in caplog .records )
186+
187+
188+ def test_voice_command_builtin_skill_exists ():
189+ """The shipped voice-command builtin skill has the expected frontmatter."""
190+ from clayde .webhook import skills as skills_mod
191+ import importlib .resources
192+ builtin_dir = Path (skills_mod .__file__ ).parent .parent / "skills_builtin"
193+ vc_path = builtin_dir / "voice-command.md"
194+ assert vc_path .exists (), "voice-command.md missing from skills_builtin/"
195+ skill = _parse_skill (vc_path )
196+ assert skill .name == "voice-command"
197+ # Behavioral content belongs in the skill, not in the system prompt.
198+ body = vc_path .read_text ()
199+ assert "speech-to-text" in body or "voice" in body .lower ()
200+ assert "/home/clayde/knowledge_base" in body
0 commit comments