Tasks: US7 — Agent Skills Replace Prompt Tools¶
Input: Design documents from /docs/product-specs/023-choose-your-backend/
Prerequisites: plan.md (required), spec.md (required), research.md (R7), data-model.md (§8)
Tests: TDD approach — write tests FIRST, verify they FAIL, then implement.
Dependencies: US1 (ADK backend) and US2 (AF backend) must be complete before backend adapter tasks.
Organization: Tasks are grouped by phase to enable incremental delivery.
Format: [ID] [P?] [US7] Description¶
- [P]: Can run in parallel (different files, no dependencies)
- [US7]: All tasks belong to User Story 7
Path Conventions¶
- Single project:
src/holodeck/,tests/at repository root
Phase 2: Foundational — Remove PromptTool from ToolUnion¶
Purpose: Clean removal of the unimplemented PromptTool before introducing SkillTool.
CRITICAL: SkillTool implementation (Phase 3) cannot begin until PromptTool is fully removed.
- T701 [P] [US7] Remove
PromptToolclass fromsrc/holodeck/models/tool.py— delete the entirePromptTool(BaseModel)class definition (currently at line ~555) - T702 [P] [US7] Remove
PromptToolfromToolUniondiscriminated union insrc/holodeck/models/tool.py— remove theAnnotated[PromptTool, Tag("prompt")]variant from the union type (currently at line ~888) - T703 [P] [US7] Remove
PromptToolexport fromsrc/holodeck/models/__init__.py— remove from__all__and import list - T704 [US7] Remove any
PromptTool/TestPromptTooltests fromtests/unit/models/test_tool_models.py— delete existing prompt tool test cases to prevent import errors - T705 [US7] Run
make testto verify PromptTool removal causes no regressions (any remaining references to PromptTool should surface as import errors)
Checkpoint: PromptTool fully removed — ToolUnion clean for SkillTool addition.
Phase 3: US7 — SkillTool Model, Validation, Adapters, Tests¶
3a: SkillTool Pydantic Model¶
Purpose: Define the SkillTool model with all validation rules from data-model.md §8.
Tests (TDD — write FIRST, verify they FAIL)¶
- T706 [P] [US7] Write test
test_inline_skill_validintests/unit/models/test_tool_models.py— create SkillTool withname="sentiment-analyzer",type="skill",description="Analyze sentiment",instructions="Analyze the given text", assert model validates successfully - T707 [P] [US7] Write test
test_file_based_skill_validintests/unit/models/test_tool_models.py— create SkillTool withname="research-assistant",type="skill",path="./skills/research-assistant/",description="Research tool", assert model validates successfully - T708 [P] [US7] Write test
test_skill_rejects_both_instructions_and_pathintests/unit/models/test_tool_models.py— provide bothinstructionsandpath, assertValidationErrorraised - T709 [P] [US7] Write test
test_skill_rejects_neither_instructions_nor_pathintests/unit/models/test_tool_models.py— omit bothinstructionsandpath, assertValidationErrorraised - T710 [P] [US7] Write test
test_inline_skill_requires_descriptionintests/unit/models/test_tool_models.py— create inline skill (withinstructions) but nodescription, assertValidationErrorraised - T711 [P] [US7] Write test
test_file_based_skill_description_optionalintests/unit/models/test_tool_models.py— create file-based skill (withpath) but nodescription, assert model validates successfully (description falls back to SKILL.md frontmatter at runtime) - T712 [P] [US7] Write test
test_skill_name_pattern_validintests/unit/models/test_tool_models.py— test valid names:"my-skill","a","skill-123","multi-word-skill-name", assert all validate - T713 [P] [US7] Write test
test_skill_name_pattern_invalidintests/unit/models/test_tool_models.py— test invalid names:"MySkill"(uppercase),"my_skill"(underscore),"-leading"(leading hyphen),"trailing-"(trailing hyphen),"double--hyphen"(consecutive hyphens),""(empty), assertValidationErrorfor each - T714 [P] [US7] Write test
test_skill_name_max_lengthintests/unit/models/test_tool_models.py— test name with 65 characters, assertValidationError; test name with 64 characters, assert valid - T715 [P] [US7] Write test
test_skill_description_max_lengthintests/unit/models/test_tool_models.py— test description with 1025 characters, assertValidationError; test description with 1024 characters, assert valid - T716 [P] [US7] Write test
test_skill_with_allowed_toolsintests/unit/models/test_tool_models.py— create SkillTool withallowed_tools=["knowledge_base", "web_search"], assert model validates andallowed_toolsis stored correctly - T717 [P] [US7] Write test
test_skill_in_tool_unionintests/unit/models/test_tool_models.py— create a tool dict withtype: "skill"and valid fields, parse throughToolUniondiscriminated union, assert result isSkillToolinstance
Implementation¶
- T718 [US7] Implement
SkillTool(BaseModel)class insrc/holodeck/models/tool.pywith fields:name(str, 1-64 chars, pattern^[a-z0-9]+(-[a-z0-9]+)*$),description(str|None, max 1024 chars),type(Literal["skill"]),instructions(str|None),path(str|None),allowed_tools(list[str]|None). Add@model_validator(mode="after")enforcing: (1) exactly one ofinstructions/pathset, (2)descriptionrequired wheninstructionsset - T719 [US7] Add
SkillTooltoToolUniondiscriminated union insrc/holodeck/models/tool.py— addAnnotated[SkillTool, Tag("skill")]variant - T720 [US7] Export
SkillToolfromsrc/holodeck/models/__init__.py— add to__all__and import list - T721 [US7] Run tests T706–T717 and verify they PASS after implementation
Checkpoint: SkillTool model complete with all validation rules.
3b: allowed_tools Cross-Validation Against Parent Agent¶
Purpose: Validate that allowed_tools references exist in the parent agent's tools list at config time.
Tests (TDD — write FIRST, verify they FAIL)¶
- T722 [P] [US7] Write test
test_allowed_tools_valid_referencesintests/unit/models/test_tool_models.py— create an Agent with tools[knowledge_base (vectorstore), sentiment-analyzer (skill, allowed_tools=["knowledge_base"])], assert config validates successfully - T723 [P] [US7] Write test
test_allowed_tools_invalid_referenceintests/unit/models/test_tool_models.py— create an Agent with tools[sentiment-analyzer (skill, allowed_tools=["nonexistent_tool"])], assertValidationErrorraised with clear error message mentioning the nonexistent tool name - T724 [P] [US7] Write test
test_allowed_tools_none_is_validintests/unit/models/test_tool_models.py— create a skill withallowed_tools=None, assert model validates (skill has no tool access)
Implementation¶
- T725 [US7] Add
@model_validator(mode="after")toAgentinsrc/holodeck/models/agent.pythat iterates overtools, findsSkillToolinstances, and validates eachallowed_toolsentry exists as anamein the agent's other tools. RaiseValidationErrorwith message:"Skill '{skill.name}' references unknown tool '{tool_name}' in allowed_tools. Available tools: {available}" - T726 [US7] Run tests T722–T724 and verify they PASS
Checkpoint: Cross-validation ensures skill tool references are valid at config time.
3c: SK Backend Rejection for SkillTool¶
Purpose: SK backend raises a clear error when skill tools are configured (SK is excluded from skill support).
Tests (TDD — write FIRST, verify they FAIL)¶
- T727 [P] [US7] Write test
test_sk_backend_rejects_skill_toolintests/unit/lib/backends/test_selector.py— create an Agent withbackend: semantic_kerneland a skill tool configured, attempt backend selection/initialization, assert error raised explaining skills are not supported on SK backend
Implementation¶
- T728 [US7] Add skill tool validation to
BackendSelectororSKBackend.initialize()insrc/holodeck/lib/backends/selector.py(orsk_backend.py) — check if any tool in the agent's tools list is aSkillTool; if so and the resolved backend issemantic_kernel, raiseBackendInitErrorwith message:"Skill tools (type: skill) are not supported on the Semantic Kernel backend. Use google_adk, agent_framework, or claude backend instead." - T729 [US7] Run test T727 and verify it PASSES
Checkpoint: SK backend clearly rejects skill tools.
3d: Backend Skill Adapters (Depends on US1, US2)¶
Purpose: Each supported backend adapts SkillTool to its native sub-agent/skill runtime.
BLOCKED: These tasks require US1 (ADK backend) and US2 (AF backend) to be complete.
Tests (TDD — write FIRST, verify they FAIL)¶
- T730 [P] [US7] Write test
test_adk_skill_adapter_inlineintests/unit/lib/backends/test_adk_tool_adapters.py— create an inline SkillTool, adapt via ADK tool adapter, assert a sub-agent is created with the skill's instructions using the parent's model, and the skill'sallowed_toolsare filtered from the parent's tool set - T731 [P] [US7] Write test
test_adk_skill_adapter_file_basedintests/unit/lib/backends/test_adk_tool_adapters.py— create a file-based SkillTool withpath, adapt via ADK tool adapter, assert skill directory path is passed to ADK's native skill runtime - T732 [P] [US7] Write test
test_af_skill_adapter_inlineintests/unit/lib/backends/test_af_tool_adapters.py— create an inline SkillTool, adapt via AF tool adapter, assert a sub-agent is created with the skill's instructions using the parent's model, andallowed_toolsare filtered - T733 [P] [US7] Write test
test_af_skill_adapter_file_basedintests/unit/lib/backends/test_af_tool_adapters.py— create a file-based SkillTool withpath, adapt via AF tool adapter, assert skill directory path is passed to AF's native skill runtime - T734 [P] [US7] Write test
test_claude_skill_adapter_inlineintests/unit/lib/backends/test_claude_backend.py(or dedicated test file) — create an inline SkillTool, adapt via Claude tool adapter insrc/holodeck/lib/backends/tool_adapters.py, assert sub-agent created using Claude SDK's native sub-agent system with the skill's instructions and filtered tools - T735 [P] [US7] Write test
test_claude_skill_adapter_file_basedintests/unit/lib/backends/test_claude_backend.py(or dedicated test file) — create a file-based SkillTool withpath, adapt via Claude tool adapter, assert skill directory is passed to Claude SDK's native sub-agent system
Implementation¶
- T736 [US7] Add SkillTool adapter to
src/holodeck/lib/backends/adk_tool_adapters.py— handle inline skills (create ADK sub-agent with skill instructions and filtered parent tools) and file-based skills (pass skill directory to ADK's native skill runtime). Skill inherits parent's model config - T737 [US7] Add SkillTool adapter to
src/holodeck/lib/backends/af_tool_adapters.py— handle inline skills (create AF sub-agent via AF's delegation system with skill instructions and filtered parent tools) and file-based skills (pass skill directory to AF's native skill runtime). Skill inherits parent's model config - T738 [US7] Add SkillTool adapter to
src/holodeck/lib/backends/tool_adapters.py— handle inline skills (create Claude SDK sub-agent with skill instructions and filtered parent tools) and file-based skills (pass skill directory to Claude SDK's native sub-agent system). Skill inherits parent's model config - T739 [US7] Run tests T730–T735 and verify they PASS
Checkpoint: All three supported backends can adapt skill tools to their native sub-agent runtimes.
3e: SKILL.md Frontmatter Parsing (Config-Time Validation)¶
Purpose: For file-based skills, parse SKILL.md YAML frontmatter to validate required fields and provide fallback description.
Tests (TDD — write FIRST, verify they FAIL)¶
- T740 [P] [US7] Write test
test_skill_md_frontmatter_validintests/unit/models/test_tool_models.py— create a tmp skill directory with a valid SKILL.md containing frontmatter (name,description), create a file-based SkillTool with that path, assert frontmatter is parseable anddescriptionfallback works - T741 [P] [US7] Write test
test_skill_md_frontmatter_missing_nameintests/unit/models/test_tool_models.py— SKILL.md missingnamein frontmatter, assert validation error - T742 [P] [US7] Write test
test_skill_md_missing_fileintests/unit/models/test_tool_models.py—pathpoints to a directory with no SKILL.md, assert validation error - T743 [P] [US7] Write test
test_file_based_skill_fallback_descriptionintests/unit/models/test_tool_models.py— file-based skill with nodescriptionin YAML but SKILL.md frontmatter hasdescription: "From frontmatter", assert the skill resolves to the frontmatter description
Implementation¶
- T744 [US7] Add SKILL.md frontmatter parsing utility — either as a private method on
SkillToolor a standalone function insrc/holodeck/models/tool.py. Uses PyYAML (existing dependency) to parse YAML frontmatter from SKILL.md. Extractnameanddescriptionfields. Validate thatnameis present. Providedescriptionfallback for file-based skills when not set in YAML - T745 [US7] Run tests T740–T743 and verify they PASS
Checkpoint: File-based skills have config-time validation and description fallback from SKILL.md frontmatter.
Phase 4: Polish — Documentation and Examples¶
Purpose: Update documentation and examples to reflect SkillTool replacing PromptTool.
- T746 [P] [US7] Update
docs/examples/with_tools.yaml— replace the prompt tool example with a skill tool example showing both inline and file-based forms - T747 [US7] Run
make formatto format all modified files with Black + Ruff - T748 [US7] Run
make lintand fix any Ruff + Bandit violations - T749 [US7] Run
make type-checkand fix any MyPy errors (ensure SkillTool type annotations are correct) - T750 [US7] Run full test suite
make testto verify no regressions
Checkpoint: US7 complete — PromptTool replaced by SkillTool across model, validation, all backends, and docs.
Dependencies & Execution Order¶
Phase Dependencies¶
- Phase 2 (Foundational): No dependencies — can start immediately
- Phase 3a (SkillTool Model): Depends on Phase 2 completion (PromptTool removed)
- Phase 3b (Cross-Validation): Depends on Phase 3a (SkillTool model exists)
- Phase 3c (SK Rejection): Depends on Phase 3a (SkillTool model exists). Can run in parallel with 3b.
- Phase 3d (Backend Adapters): Depends on Phase 3a + US1 (ADK backend) + US2 (AF backend). BLOCKED until both are complete.
- Phase 3e (SKILL.md Parsing): Depends on Phase 3a. Can run in parallel with 3b, 3c, 3d.
- Phase 4 (Polish): Depends on all Phase 3 sub-phases being complete
External Dependencies¶
- US1 (ADK Backend): Required for T730, T731, T736 (ADK skill adapter)
- US2 (AF Backend): Required for T732, T733, T737 (AF skill adapter)
- Claude backend skill adapter (T734, T735, T738) has no external dependency — Claude backend already exists
Parallel Opportunities¶
- T701, T702, T703 can run in parallel (different files)
- T706–T717 can run in parallel (same file, independent test functions)
- T722, T723, T724 can run in parallel (same file, independent test functions)
- T730–T735 can run in parallel (different files, independent test functions)
- T740–T743 can run in parallel (same file, independent test functions)
- Phase 3b, 3c, 3e can run in parallel after 3a completes
Key Files Modified¶
| File | Phase | Changes |
|---|---|---|
src/holodeck/models/tool.py |
2, 3a | Remove PromptTool; add SkillTool class + ToolUnion variant |
src/holodeck/models/__init__.py |
2, 3a | Remove PromptTool export; add SkillTool export |
src/holodeck/models/agent.py |
3b | Add @model_validator for allowed_tools cross-validation |
src/holodeck/lib/backends/selector.py or sk_backend.py |
3c | Reject skill tools on SK backend |
src/holodeck/lib/backends/adk_tool_adapters.py |
3d | Add SkillTool → ADK sub-agent adapter |
src/holodeck/lib/backends/af_tool_adapters.py |
3d | Add SkillTool → AF sub-agent adapter |
src/holodeck/lib/backends/tool_adapters.py |
3d | Add SkillTool → Claude sub-agent adapter |
tests/unit/models/test_tool_models.py |
2, 3a, 3b, 3e | Remove PromptTool tests; add SkillTool tests |
tests/unit/lib/backends/test_selector.py |
3c | SK rejection test |
tests/unit/lib/backends/test_adk_tool_adapters.py |
3d | ADK skill adapter tests |
tests/unit/lib/backends/test_af_tool_adapters.py |
3d | AF skill adapter tests |
tests/unit/lib/backends/test_claude_backend.py |
3d | Claude skill adapter tests |
docs/examples/with_tools.yaml |
4 | Replace prompt tool example with skill example |
Notes¶
- [P] tasks = different files, no dependencies
- [US7] label maps all tasks to User Story 7 for traceability
- TDD: Verify tests fail before implementing
- Commit after each task or logical group
- Stop at any checkpoint to validate phase independently
- PromptTool was never implemented — removal is safe with no runtime impact
- SKILL.md frontmatter parsing uses PyYAML (existing dependency) — no new deps needed
- Each backend handles SKILL.md content natively via its own skill runtime; HoloDeck only validates frontmatter at config time
- No model override on skills — they inherit the parent agent's model entirely
- SK is excluded from skill support (planned for deprecation)