fix(profile): seed bundled skills for fresh WebUI-created profiles (#2305) - #2314
WanderWang wants to merge 1 commit into
Conversation
…esquena#2305) When creating a new profile via the Web UI without cloning from an existing profile, the resulting profile had 0 skills because api/profiles.py::create_profile_api() never called seed_profile_skills(). This is inconsistent with the CLI behaviour where hermes profile create automatically seeds bundled skills. - Add seed_profile_skills() call in create_profile_api() when clone_from is None (fresh profile). - Skip seeding for cloned profiles since create_profile() already copies skills from the source. - Wrap in try/except so a seed failure is non-fatal (empty skills/ is recoverable, a missing profile is not). - Handle ImportError for Docker/standalone fallback paths where hermes_cli is unavailable. Refs nesquena#2305, nesquena#749
SummaryReading the diff against Code reference
if clone_from is None:
try:
from hermes_cli.profiles import seed_profile_skills
seed_profile_skills(profile_path, quiet=True)
except ImportError:
logger.debug("hermes_cli unavailable; skipping skill seed for %s", name)
except Exception as e:
logger.warning("seed_profile_skills failed for new profile %s: %s", name, e)This is functionally fine — Concern — test isolation
monkeypatch.delitem(sys.modules, "api.profiles", raising=False)
import importlib
profiles_mod = importlib.import_module("api.profiles")Deleting The static-source tests in Suggested fix: rather than reimporting, use Minor
VerdictCode change is correct. Test file works for the explicit cases but the module reload is a footgun that should be removed before merge. CHANGELOG entry would be welcome. Note that PR #2315 is a parallel implementation of the same fix from another contributor; the maintainer will need to pick one. |
|
Thanks for the contribution — really appreciate the attention to detail on the test coverage and the careful import-error handling. We received two PRs for #2305 on essentially the same timeline:
After comparing the two implementations they're nearly identical in shape — both call
Both implementations were genuinely solid — this came down to small tiebreaker details. Closing this PR as superseded by #2315, but the contribution is appreciated and the analysis was on point. If you have follow-up ideas around profile creation / skill seeding (e.g. interactive prompts in the WebUI to choose which bundled skills get seeded, or making the bundled-skills list configurable), those would be welcome new PRs. Thanks again. — maintainer |
…, add CHANGELOG Address review feedback from @nesquena-hermes on PR nesquena#2314: 1. Remove api.profiles module reload from sys.modules — inject mock hermes_cli.profiles via sys.modules before calling create_profile_api() instead. The from hermes_cli.profiles import seed_profile_skills inside create_profile_api is evaluated at call time, so the mock is resolved without reloading api.profiles and without creating stale module state. 2. Drop TestProfileCreateSkillSeedingStatic (3 source-string tests). They verified string presence in api/profiles.py but could not catch behavioural regressions; the 3 behavioural tests already cover the same surface. 3. Add CHANGELOG.md entry under [Unreleased] → Fixed for user-visible behaviour change. Refs nesquena#2305, nesquena#749
|
@nesquena-hermes Thanks for the detailed review — all three points addressed in the latest push (
Let me know if anything else needs adjustment! |
Problem
When creating a new profile via the Web UI without checking "Clone from active profile", the resulting profile has 0 skills (empty
skills/directory). This is inconsistent with the CLI behavior wherehermes profile create fooautomatically seeds bundled skills.Root Cause
api/profiles.py::create_profile_api()never calledseed_profile_skills(). The CLI entrypoint (hermes_cli/main.py) and the web server route (hermes_cli/web_server.py) both call it, but the WebUI'screate_profile_api()bypassed this.Fix
seed_profile_skills()call increate_profile_api()whenclone_from is None(fresh profile).create_profile()already copies skills from the source.try/exceptso a seed failure is non-fatal — an emptyskills/directory is recoverable, a missing profile is not.ImportErrorfor Docker/standalone fallback paths wherehermes_cliis unavailable.Tests
Added
tests/test_issue2305_seed_profile_skills.pywith 6 tests:clone_from=None) callsseed_profile_skillsseed_profile_skills4-6. Static analysis verifying the seed call exists, is conditional on
clone_from is None, and has proper exception handlingVerification
All 6 tests pass.
Refs #2305, #749