Problem
adopt.sh's never-clobber guarantee operates at file-path granularity: if the destination file already exists, the template's version is written alongside as <name>.forge-suggested instead of overwriting it (adopt.sh:110-128, copy_path()). That's the right rule for file content collisions.
It does nothing for just recipe-name collisions across different files. just recipes from every imported .just file share one flat namespace; two files defining a recipe with the same name breaks just entirely (not just that recipe - the whole tool), regardless of which files they live in.
Observed live
During an adoption into an existing repo, the pre-existing project already had a hand-rolled just/dev.just defining an audit: recipe. adopt.sh copies just/analysis.just from the template (grep -n '^audit' just/*.just in this repo confirms just/analysis.just:6:audit:) - a new file, so the file-existence check sees no collision and copies it clean. Result: two files, two audit: recipes, just refuses to run at all until one is renamed.
The workaround (rename the project's own recipe, e.g. to audit-legacy, keep it rather than delete it) is reasonable and consistent with adopt.sh's "never erase" contract - but the adoptee has to discover the breakage themselves (by running just and having it fail outright) rather than adopt.sh surfacing it as a .forge-suggested-style warning during the run.
Proposed fix
After Phase 2 (copying the machinery) or Phase 5 (wiring), diff the set of top-level recipe names across all just/*.just files (existing + newly copied) and flag any name that now appears in more than one file - same "surface, don't block" posture as the rest of adopt.sh (see the SUGGESTED list / forge-adopt-fallbacks.txt pattern already used for other manual-fixup cases). A simple approach: grep -hoE '^[a-zA-Z_-][a-zA-Z0-9_-]*:' just/*.just (recipe headers, no args) before vs. after the copy, or lean on just --list / just --summary actually failing and capturing that as the signal.
Lower priority than the two active bugs fixed in #47 (ReDoS, slug-hyphen breakage) - this is "the adoptee finds out via a confusing just error" rather than "the adopt run itself hangs or produces invalid code" - but worth closing since collisions are entirely plausible whenever a repo already has ad hoc just tooling (exactly the profile adopt.sh targets).
Filed alongside #48 (idempotency-stamp) and #47 (adopt.sh bug fixes) from the same live adoption run.
🤖 Filed with Claude Code
Problem
adopt.sh's never-clobber guarantee operates at file-path granularity: if the destination file already exists, the template's version is written alongside as<name>.forge-suggestedinstead of overwriting it (adopt.sh:110-128,copy_path()). That's the right rule for file content collisions.It does nothing for
justrecipe-name collisions across different files.justrecipes from every imported.justfile share one flat namespace; two files defining a recipe with the same name breaksjustentirely (not just that recipe - the whole tool), regardless of which files they live in.Observed live
During an adoption into an existing repo, the pre-existing project already had a hand-rolled
just/dev.justdefining anaudit:recipe.adopt.shcopiesjust/analysis.justfrom the template (grep -n '^audit' just/*.justin this repo confirmsjust/analysis.just:6:audit:) - a new file, so the file-existence check sees no collision and copies it clean. Result: two files, twoaudit:recipes,justrefuses to run at all until one is renamed.The workaround (rename the project's own recipe, e.g. to
audit-legacy, keep it rather than delete it) is reasonable and consistent with adopt.sh's "never erase" contract - but the adoptee has to discover the breakage themselves (by runningjustand having it fail outright) rather than adopt.sh surfacing it as a.forge-suggested-style warning during the run.Proposed fix
After Phase 2 (copying the machinery) or Phase 5 (wiring), diff the set of top-level recipe names across all
just/*.justfiles (existing + newly copied) and flag any name that now appears in more than one file - same "surface, don't block" posture as the rest of adopt.sh (see theSUGGESTEDlist /forge-adopt-fallbacks.txtpattern already used for other manual-fixup cases). A simple approach:grep -hoE '^[a-zA-Z_-][a-zA-Z0-9_-]*:' just/*.just(recipe headers, no args) before vs. after the copy, or lean onjust --list/just --summaryactually failing and capturing that as the signal.Lower priority than the two active bugs fixed in #47 (ReDoS, slug-hyphen breakage) - this is "the adoptee finds out via a confusing
justerror" rather than "the adopt run itself hangs or produces invalid code" - but worth closing since collisions are entirely plausible whenever a repo already has ad hocjusttooling (exactly the profileadopt.shtargets).Filed alongside #48 (idempotency-stamp) and #47 (adopt.sh bug fixes) from the same live adoption run.
🤖 Filed with Claude Code