fix(skills): reconcile team-template skill names so role bindings actually bind
ci / gates (push) Successful in 5s
ci / rust (push) Failing after 10s
ci / frontend (push) Failing after 19s
ci / e2e (push) Skipped
ci / publish (push) Skipped

Every skill reference in every team template was failing to resolve. The
TOMLs used snake_case slugs (`write_rust`, `index_selection`) while the
authored skills under `skills/**/*.md` declare kebab-case names
(`write-rust-current-edition`, `postgres-index-selection`), so
`get_by_name` missed on all of them: 128 skipped bindings across 51
distinct names, and no mission agent received any of its template's
skills.

The mirror-image half was equally invisible: ten authored skills —
including `int-xx-marker-protocol`, whose own `when_to_use` says "pin on
every coding role" — were referenced by no role at all, so nothing could
ever load them.

- Rename the 14 references that have authored skills behind them, and
  dedupe the two that now collapse onto the commit-protocol skill.
- Attach all ten orphaned skills to the roles their `when_to_use` names.
  All 23 authored skills now reach at least one role.
- Aggregate the loader's per-name logging into one line per template.
  The old per-name spam is why this went unnoticed; a bound/unresolved
  count is noticeable. References with no authored skill are kept and
  listed — they record intent for skills not yet written.
- Two regression tests: no authored skill may be orphaned, and every
  authored skill must be referenced by its exact name.

Also clears the two standing clippy warnings: group
`mint_team_from_template`'s eight positional args into `TeamMint`, and
make `provider_alias_for` branch on `is_exact_provider_match` so the
helper is live code and the two can't disagree about what counts as an
exact family match.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-31 19:47:18 -07:00
co-authored by Claude Opus 5
parent 95bd65540c
commit 2eb0880fc0
14 changed files with 214 additions and 77 deletions
+21 -14
View File
@@ -29,14 +29,15 @@ pub fn claw_alias(claw_id: Uuid) -> String {
pub fn provider_alias_for(model: &str) -> &'static str {
let m = model.trim().to_ascii_lowercase();
// Prefix families first (covers claude-sonnet-5, claude-opus-4-8,
// claude-haiku-4-5-*, etc.) then explicit aliases.
if m.starts_with("claude") {
return "anthropic.default";
}
if m.starts_with("gemini") {
return "gemini.default";
}
if m.starts_with("llama") || m.starts_with("groq") {
// claude-haiku-4-5-*, etc.) then explicit aliases. `is_exact_provider_match`
// decides what "its own family" means, so the two can't drift apart.
if is_exact_provider_match(&m) {
if m.starts_with("claude") {
return "anthropic.default";
}
if m.starts_with("gemini") {
return "gemini.default";
}
return "groq.default";
}
match m.as_str() {
@@ -69,12 +70,13 @@ pub fn provider_alias_for(model: &str) -> &'static str {
}
}
/// Whether `provider_alias_for` resolved this model to its own family, or
/// substituted a different one.
/// Whether `provider_alias_for` resolves this model to its own family, or
/// substitutes a different one.
///
/// Callers that surface a model choice to a user can use this to say so rather
/// than letting the substitution be discovered on an invoice. Kept alongside
/// `provider_alias_for` so the two can't disagree about what counts as a match.
/// `provider_alias_for` branches on this, so it is the single definition of
/// "its own family". Also public for callers that surface a model choice to a
/// user, so a substitution can be said out loud rather than discovered on an
/// invoice.
pub fn is_exact_provider_match(model: &str) -> bool {
let m = model.trim().to_ascii_lowercase();
m.starts_with("claude")
@@ -350,7 +352,12 @@ mod tests {
"{m} resolves to anthropic.default by substitution, not by family"
);
}
for m in ["claude-sonnet-5", "gemini-2.5-flash", "groq-llama", "llama3"] {
for m in [
"claude-sonnet-5",
"gemini-2.5-flash",
"groq-llama",
"llama3",
] {
assert!(
super::is_exact_provider_match(m),
"{m} should resolve to its own family"