Skip to content

bug: legacy skills-library adoption compares a raw URL to a normalized one, so the same repo never matches and every sync alerts #2763

Description

@dolho

Summary

SkillService._adopt_legacy_clone() decides whether a legacy skills_library_url names an already-configured skills source using raw string equality, while the rest of the platform stores and produces that URL normalized. The two sides therefore never match for the same repository written two ways, and the install takes the ent#346 "already has sources" refusal branch on every sync, forever — for a repo it is already syncing.

This is the root cause behind the alert flood in #2744. That issue makes the flood idempotent (correct, and independently wanted); this one stops the branch being entered spuriously at all.

Mechanism

validate_skills_library_url is a normalizer, and the write route uses it as one:

# src/backend/routers/skills.py:649 — ASSIGNS the return
url = validate_skills_library_url(body.url)

The adoption path calls the same function and throws the return away, then compares the raw setting value:

# src/backend/services/skill_service.py:734 — return DISCARDED
validate_skills_library_url(url)
...
# src/backend/services/skill_service.py:754
existing = [s for s in db.list_skill_sources() if s.url == url]

Measured on origin/dev@2c5cfe0e3:

input validate_skills_library_url returns
github.com/abilityai/trinity-skills https://github.com/abilityai/trinity-skills
https://github.com/abilityai/trinity-skills https://github.com/abilityai/trinity-skills

And the bundled default source is seeded from a literal that never passes through the validator:

# src/backend/config.py:522
"TRINITY_DEFAULT_SKILL_SOURCE", "github.com/abilityai/trinity-skills"

So the two representations the platform itself produces — seeded (no scheme) and API-created (normalized) — cannot compare equal to a legacy key written in the other form.

Reproduction

Driving the real _adopt_legacy_clone with a stubbed db:

source row legacy key result
github.com/abilityai/trinity-skills (seeded default) https://github.com/abilityai/trinity-skills refusal branch + alert
https://github.com/abilityai/trinity-skills (via POST /api/skills/sources) github.com/abilityai/trinity-skills refusal branch + alert
identical strings identical strings adopted silently, no alert

In both alerting cases validate_skills_library_url(source_url) == validate_skills_library_url(legacy_key) — the two sides name the same repository, and the alert was never warranted.

Why the fix cannot weaken ent#346

A match returns the existing source id and creates no row — it is the no-op branch. The grant branch (count_skill_sources() == 0create_skill_source) is untouched, and a key that genuinely names a different repo still reaches the refusal exactly as ent#346 left it.

Normalizing removes false positives from the detector. It does not widen what may be granted.

Acceptance criteria

  • _adopt_legacy_clone compares repo identity, not raw strings — assign the validator's return, and normalize the stored source url too (sources exist in both forms on live installs).
  • A regression test drives the real method for both mismatch directions and asserts no alert is filed, plus a control proving a genuinely different repo still alerts.
  • The grant branch and the count_skill_sources() == 0 path are demonstrably unchanged.

Out of scope

Normalization collapses the scheme/no-scheme pair — the split the platform creates. These remain distinct after normalization and are a smaller, separate tail: …/trinity-skills.git, a trailing /, and the bare owner/repo shorthand.

Context

Found while investigating the private follow-up to ent#346. Related: #2744 (the flood this causes — assigned, in progress, complementary and independently wanted).

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions