Skip to content

feat(orchestrator): add skills field to agent YAML template and CreateAgentRequest API#1228

Open
geoffjay wants to merge 2 commits into
epic-1209from
issue-1210
Open

feat(orchestrator): add skills field to agent YAML template and CreateAgentRequest API#1228
geoffjay wants to merge 2 commits into
epic-1209from
issue-1210

Conversation

@geoffjay
Copy link
Copy Markdown
Owner

Adds the skills field throughout the agent lifecycle so operators can declare which skills each agent receives.

Changes:

  • AgentConfig and CreateAgentRequest gain a skills: Vec<String> field (serde default empty)
  • New DB migration m20260513_000017 adds skills TEXT NOT NULL DEFAULT '[]' column
  • AgentTemplate in apply.rs gains a SkillsField enum supporting skills: all (expand to all discovered skills) or skills: [name1, name2] (validated against discovered skills)
  • schemas/agent.yml updated with oneOf skills property
  • main.rs binary gains mod skills so crate::skills::discover_all_skills resolves in API handler

Closes #1210

geoffjay added 2 commits May 13, 2026 09:36
…eAgentRequest API

- Add skills Vec<String> to AgentConfig and CreateAgentRequest in types.rs
- Add skills TEXT column to agents entity and migration m20260513_000017
- Store/retrieve skills in storage.rs (JSON-serialized Vec<String>)
- Add SkillsField enum to apply.rs with 'all' expansion and name validation
- Add skills to AgentTemplate with serde(default)
- Update schemas/agent.yml with oneOf skills property
- Wire skills from CreateAgentRequest into AgentConfig in api.rs
- Add mod skills to main.rs binary so crate::skills resolves

Closes #1210
@geoffjay geoffjay added the review-agent Used to invoke a review by an agent tracking this label label May 13, 2026
Copy link
Copy Markdown
Owner Author

@geoffjay geoffjay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat(orchestrator): add skills field to agent YAML template and CreateAgentRequest API

Stack Position

Branch issue-1210 is stacked on epic-1209, not main. The parent branch epic-1209 contains the issue-1209 (Skill model) work plus a fix(skills): address review feedback commit.

Parent status: PR #1226 has been re-submitted for review (review-agent label is set, needs-rework has been removed). I verified the two blocking issues from that review are both fixed on epic-1209:

  • discover_all_skills() now has a global sort ✓
  • Per-entry read_dir errors now use let Ok(entry) = entry else { continue }

This PR is approvable in isolation but cannot merge until #1226 is re-reviewed and cleared. The conductor should treat it as blocked until the parent chain is clean.


SkillsField Enum: Correct

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum SkillsField {
    All(String),      // matches any string — validated at apply time
    Named(Vec<String>), // matches any JSON array
}

#[serde(untagged)] is the right choice here — serde tries All(String) first for string values and Named(Vec<String>) for array values, which correctly handles skills: all vs skills: [a, b] vs skills: []. The Default impl returning Named(vec![]) matches the schema's default: []. ✓


Skills Resolution in apply_agent: Correct

All four cases are handled cleanly:

Input Outcome
skills: all Expands to all discovered skill names
skills: "typo" Fails with actionable error: use 'all' or a list
skills: [] / omitted Assigns no skills (fast path, no discovery call)
skills: [a, b] Validates each name against discovered skills; fails with missing names and a hint to run agent skill list

The guard SkillsField::Named(names) if names.is_empty() => vec![] correctly avoids calling discover_all_skills() when no skills are needed. ✓


Migration: Idempotent and Consistent

let stmt = "ALTER TABLE agents ADD COLUMN skills TEXT NOT NULL DEFAULT '[]'";
if let Err(e) = db.execute_unprepared(stmt).await {
    if !e.to_string().contains("duplicate column name") {
        return Err(e);
    }
}

Follows the same idempotency pattern as other migrations in this codebase (m20260417_000016 etc.). The SQLite "duplicate column name" string check is the established convention here. ✓

The empty down() with an explanatory comment about SQLite < 3.35.0 is the right approach — consistent with other migrations that can't safely DROP COLUMN on older SQLite.


Storage / Entity Integration: Consistent

skills is stored as JSON text ("[]" default) in both the entity model and the AgentConfig, following the exact same pattern as rooms. Serialization uses unwrap_or_else(|_| "[]".to_string()) and deserialization uses unwrap_or_default() — matching every other JSON-in-text column in storage.rs. ✓


Schema: Correct oneOf

oneOf:
  - type: array
    items: {type: string}
  - type: string
    enum: [all]
default: []

"all" matches only the string variant. ["a", "b"] and [] match only the array variant. No overlap, no ambiguity. ✓


Non-Blocking: No Tests for SkillsField

There are no unit tests for SkillsField YAML/JSON deserialization or for the apply_agent skills resolution logic. The #[serde(untagged)] behavior (particularly the fallthrough for unexpected strings reaching SkillsField::All and being caught at apply time) would be valuable to cover. These are tracked in #1214 (the capstone test issue for the epic), so the omission is intentional — just noting it for when #1214 is dispatched.


Non-Blocking: discover_all_skills() Called Per-Agent at Apply Time

When skills: all or skills: [named] is used, discover_all_skills() runs once per agent in apply_agent. If an agent YAML defines 20 agents, this means up to 20 filesystem scans. For the typical small .agentd/skills/ directory this is negligible, but it's worth noting as a future optimization candidate (scan once, pass results to all agents).


Summary

The code is correct and well-integrated throughout the call chain (apply.rstypes.rsapi.rsstorage.rs → migration). The parent epic-1209 branch has confirmed fixes for both blocking issues from the #1226 review. Two non-blocking notes: tests deferred to #1214 (expected), and per-agent skill discovery at apply time is a minor inefficiency.

Holding merge-queue pending re-review and approval of #1226. Once that clears, this can follow immediately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-queue Approved by reviewer, queued for merge by conductor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add skills field to agent YAML template and CreateAgentRequest API

1 participant