fix(#5408): deduplicate skills by basename during base composition - #5409
Conversation
When a child harness declares a skill whose directory basename matches a base skill, the merge logic now replaces the base entry instead of concatenating both. This mirrors the existing mergeHostFiles override-by-dest pattern and prevents duplicateDestinationNameError from rejecting legitimate skill overrides at bootstrap time. The fix adds a mergeSkills helper used in three merge paths: mergeBaseIntoChild (compose.go), mergeForgeConfigInto (compose.go), and mergeForgeConfig (forge.go). The duplicateDestinationNameError guard in claude.go is retained as a safety net for genuinely accidental collisions between two unrelated child skills. Closes #5408
|
🤖 Finished Review · ✅ Success · Started 11:30 AM UTC · Completed 11:43 AM UTC |
Site previewPreview: https://805996e8-site.fullsend-ai.workers.dev Commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Nevermind, I can't test this in my repository until we do get #5357 merged. Either way looks good to me. |
ReviewFindingsLow
Previous runReviewFindingsMedium
Low
Previous run (2)ReviewFindingsMedium
Low
Previous run (3)ReviewFindingsHigh
Medium
Low
Labels: PR modifies harness composition logic (internal/harness/) to fix skill deduplication during base: composition |
|
/fs-fix fix and resolve all the review issues that are actionable, both inline and in the sticky comment. |
|
🤖 Finished Fix · ✅ Success · Started 11:48 AM UTC · Completed 11:54 AM UTC |
- ADR 0045: update skills merge description in forge inheritance table (line 190), base→child merge rules (line 362), complexity note (line 612), and nil-vs-empty section (line 660) to describe dedup-by-basename instead of concatenation - bring-your-own-agent.md: separate skills from plugins/providers in merge rules table (line 278), update example comment (line 369), and "What you can configure" prose (line 423) - compose.go mergeSkills: update baseIndex when appending new child entries, matching mergeHostFiles' pattern for consistent dedup of duplicate child basenames - compose_test.go: add test case for duplicate child basename dedup Addresses review feedback on #5409
🔧 Fix agent — iteration 1 (human-triggered)Addressed all 4 review findings: updated stale concatenation references in ADR 0045 (4 locations) and bring-your-own-agent.md (3 locations) to describe dedup-by-basename semantics; fixed mergeSkills to update baseIndex on child append matching mergeHostFiles pattern; added test for duplicate child basename edge case. Fixed (4):
Tests: passed Updated by fullsend fix agent |
E2E tests did not runE2E tests run automatically for org/repo members and collaborators on pull requests. For other contributors, a maintainer must add the See E2E testing guide for details. |
|
/fs-review |
|
🤖 Finished Review · ✅ Success · Started 12:24 PM UTC · Completed 12:40 PM UTC |
Superseded by updated review
|
/fs-fix fix and resolve all the review issues that are actionable, both inline and in the sticky comment. |
|
🤖 Finished Fix · ✅ Success · Started 12:43 PM UTC · Completed 12:52 PM UTC |
Add diffSkills function that compares skill paths by filepath.Base instead of exact string, matching mergeSkills override-by-basename semantics. DiffHarness and diffForgeConfig now use diffSkills for skills instead of diffStringSlice, preventing spurious "child removes items from base" warnings when a child overrides a base skill by basename (e.g. fullsend migrate). Update stale "skills concatenate" references in plan docs (phase1.md, phase2.md), ADR 0064 (minor annotations on Accepted ADR), and cli-internals.md to describe merge-with-deduplication-by- basename semantics. Addresses review feedback on #5409
🔧 Fix agent — iteration 2 (human-triggered)Addressed all 7 review findings. Added diffSkills function for basename-aware skill diffing (fixes logic-error in DiffHarness/diffForgeConfig that would break fullsend migrate with skill overrides). Updated 5 stale doc references from concatenation to merge-with-deduplication-by-basename semantics. Fixed (7):
Tests: passed Updated by fullsend fix agent |
|
/fs-review |
|
🤖 Finished Review · ✅ Success · Started 1:05 PM UTC · Completed 1:22 PM UTC |
|
/fs-fix fix and resolve all the review issues that are actionable, both inline and in the sticky comment. |
|
🤖 Finished Fix · ✅ Success · Started 1:39 PM UTC · Completed 1:45 PM UTC |
…geSkills limitation - ADR 0070 line 91: replaced misleading cross-reference to skills merge pattern in ADR 0045. Profiles/providers use concatenation with id/name dedup, not the basename-aware dedup that skills now use. - compose.go mergeSkills: added code comment documenting the known limitation when base slice contains duplicate basenames. Addresses review feedback on #5409
🔧 Fix agent — iteration 3 (human-triggered)Addressed both review findings from the latest review cycle. Fixed misleading cross-reference in ADR 0070 that referenced skills merge pattern (now outdated since skills use basename-aware dedup). Documented the mergeSkills duplicate-basename limitation as a code comment. Fixed (2):
Tests: passed Updated by fullsend fix agent |
|
/fs-review |
|
🤖 Finished Review · ✅ Success · Started 1:47 PM UTC · Completed 2:01 PM UTC |
Summary
Fixes skill override via
base:composition by deduplicating skills by basename during merge. Previously, a child harness declaring a same-named skill as a built-in produced two entries in the merged skills list, which triggeredduplicateDestinationNameErrorat bootstrap time.Related Issue
Fixes #5408
Changes
mergeSkillshelper incompose.gothat deduplicates skill paths byfilepath.Base, mirroringmergeHostFiles' override-by-dest pattern (child replaces base entry in-place)mergeBaseIntoChild(compose.go) to usemergeSkillsinstead of concatenationmergeForgeConfigInto(compose.go) to usemergeSkillsinstead of concatenationmergeForgeConfig(forge.go) to usemergeSkillsinstead of appendduplicateDestinationNameErrorinclaude.gois unchanged — it remains a safety net for collisions between two unrelated child skillsTesting
TestMergeSkills(6 sub-cases covering nil, no-overlap, override, full-override)TestLoadWithBase_ChildSkillOverridesBaseByBasename,TestLoadWithBase_ChildSkillOverride_PreservesOrderTestResolveForge_SkillsOverrideByBasenameTestLoadWithBase_LocalBase_SkillsConcat,TestResolveForge_SkillsConcat,TestClaudeRuntime_Bootstrap_DuplicateSkillNames_FailsLoudlygo test ./internal/harness/...passesgo vetpassesChecklist
!for breaking changes)Closes #5408
Post-script verification
agent/5408-skill-override-dedup)90e75879bf2e67bfab34aa22fca6f125445a5ea8..HEAD)