fix(skills_sync): honor .no-bundled-skills marker for DESCRIPTION.md copy (finish PR #15) - #22
Conversation
…copy (finish PR #15) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
|
Warning Review limit reached
More reviews will be available in 58 minutes and 1 second. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔎 Lint report:
|
|
auto-review: approved, awaiting human merge + kanban_approve. Rules checked:
|
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
…copy (finish PR #15) (#22) PR #15 added the .no-bundled-skills marker check at every documented caller of sync_skills(), but the DESCRIPTION.md copy loop inside sync_skills() ran unconditionally — so when a profile-bootstrap path that *had* the marker check still ended up calling sync_skills() directly (e.g. seed_profile_skills() racing the marker write, or a caller path PR #15 didn't cover), the SKILL.md copy was correctly skipped but the category DESCRIPTION.md files still landed in the profile skills dir. Result: opted-out profiles (braintrusteng, bt-optimizer) accumulated useless byte-identical DESCRIPTION.md stubs under */skills/<category>/ that duplicate the global versions at ~/.hermes/skills/<category>/. agent/prompt_builder.py reads category descriptions from the external (global) dir already (line 1162), so the profile stubs are dead bytes. Fix is defense-in-depth: sync_skills() itself now checks the marker at HERMES_HOME (SKILLS_DIR.parent) and short-circuits to a zero-effect return with skipped_opt_out=True before either loop runs. Regression tests cover both paths (marker present skips everything; marker absent proceeds normally). Also fixes test_nonexistent_bundled_dir to patch SKILLS_DIR to a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test. Refs PR #15 (t_00313e4b), kanban t_ad76c02b
What
Defense-in-depth check inside
sync_skills()for the.no-bundled-skillsopt-out marker. When present atHERMES_HOME(resolved asSKILLS_DIR.parent),sync_skills()short-circuits withskipped_opt_out=Truebefore either the SKILL.md or DESCRIPTION.md copy loops run.Why
PR #15 added marker checks at the documented callers (gateway startup, the update command,
seed_profile_skills) but the DESCRIPTION.mdshutil.copy2loop attools/skills_sync.py:325ran unconditionally insidesync_skills()itself. As a result, opted-out profiles (e.g.braintrusteng,bt-optimizer) still accumulated byte-identical DESCRIPTION.md stubs under<profile>/skills/<category>/that duplicate~/.hermes/skills/<category>/DESCRIPTION.md.agent/prompt_builder.py:1162reads category descriptions from the external (global) dir already, so the profile stubs were dead bytes.braintrustOrchprofile (no stubs) has worked correctly throughout — proof the profile-side DESCRIPTION.md files are unnecessary.Cleanup
Removed existing stubs from both
braintrustengandbt-optimizerskills dirs (14 files total, all verified byte-identical to global before deletion). braintrusteng's now-empty top-level category dirs were also removed; bt-optimizer keeps its category dirs (they hold real custom skills).Tests
TestNoBundledSkillsMarkerclass with two cases: marker-present skips both copies, marker-absent proceeds normally.test_nonexistent_bundled_dirto patchSKILLS_DIRto a clean tmp path so the developer's live opt-out marker doesn't short-circuit the test.pytest tests/tools/test_skills_sync.py -q→ 47 passed.Refs: PR #15 (t_00313e4b), kanban t_ad76c02b