Problem
adopt.sh's only "already scaffolded" guard is a filesystem heuristic:
if [[ -f justfile && -d scripts/ci ]]; then
die "this repo already looks scaffolded (justfile + scripts/ci exist)"
fi
(adopt.sh:70-72)
This produces both false positives and false negatives:
- False positive (hit live): a repo with an old, hand-rolled
justfile + scripts/ci that has nothing to do with this template trips the guard. The only workaround is renaming those two paths out of the way before re-running - which works, but throws away the one signal the script had, and turns the run into a much bigger manual merge than the usual one-file .forge-suggested case (the renamed-away originals now have to be reconciled against everything the template copies in, not diffed file-by-file).
- False negative (not yet hit, but easy to construct): delete/rename
justfile after a real adopt, or adopt into a repo that never had a justfile to begin with post-copy, and the guard no longer detects a completed adoption at all - a second adopt.sh run would happily start over.
Neither case is about whether the rust-forge machinery was actually applied here - the guard is inferring that from paths that anyone (including this template) can create for unrelated reasons.
Existing pattern to reuse
The template already solves exactly this problem for the other direction (just init, forging a new repo from the template): docs/forge/FORGE-STAMP.toml + docs/forge/TEMPLATE_VERSION are a real birth-certificate pair, checked by scripts/ci/check_forge_stamp.sh as a drift gate (forged-from, template-version, project).
adopt.sh doesn't write either file today - FORGE-STAMP.toml/TEMPLATE_VERSION aren't in its MACHINERY copy list at all, only docs/forge/ADOPTING.md is.
Proposed fix
- Have
adopt.sh write docs/forge/FORGE-STAMP.toml (+ TEMPLATE_VERSION) as part of its Phase 6 commit, same shape as the init-time stamp, plus a field distinguishing provenance, e.g. origin = "adopted" vs origin = "init" (useful context for anyone reading the stamp later, and for template-update tooling that diffs against a known baseline).
- Change the precondition at
adopt.sh:70-72 to check for that stamp file instead of (or in addition to, as a secondary heuristic) justfile/scripts/ci presence. A repo with a stamp has genuinely been adopted; a repo with a justfile merely has a justfile.
- Optional belt-and-suspenders, since local file state can be deleted/reset independently of history: also check whether
adopt/rust-forge-scaffolding was ever created and merged (git log --all --grep for the chore(adopt): rust-forge scaffolding trial commit message, or a merged-PR check via gh if a remote is configured) before concluding "never adopted." Lower priority than (1)+(2) - the stamp file alone fixes the reported false-positive/false-negative cases.
Non-goals
This doesn't change the trial-branch-and-commit mechanics (just adopt-status / just adopt-undo), which are already reversible and working correctly. It's scoped to the one precondition check.
Filed after a live adoption run hit the justfile+scripts/ci false positive (worked around by renaming the two paths) and separately hit a real bug in adopt.sh's Phase 5 wiring step (catastrophic-backtracking regex, fixed in #47). Not blocking - the workaround is fine for a one-off adoption - but worth fixing so the next adopter doesn't have to rediscover the same workaround.
🤖 Filed with Claude Code
Problem
adopt.sh's only "already scaffolded" guard is a filesystem heuristic:(
adopt.sh:70-72)This produces both false positives and false negatives:
justfile+scripts/cithat has nothing to do with this template trips the guard. The only workaround is renaming those two paths out of the way before re-running - which works, but throws away the one signal the script had, and turns the run into a much bigger manual merge than the usual one-file.forge-suggestedcase (the renamed-away originals now have to be reconciled against everything the template copies in, not diffed file-by-file).justfileafter a real adopt, or adopt into a repo that never had ajustfileto begin with post-copy, and the guard no longer detects a completed adoption at all - a secondadopt.shrun would happily start over.Neither case is about whether the rust-forge machinery was actually applied here - the guard is inferring that from paths that anyone (including this template) can create for unrelated reasons.
Existing pattern to reuse
The template already solves exactly this problem for the other direction (
just init, forging a new repo from the template):docs/forge/FORGE-STAMP.toml+docs/forge/TEMPLATE_VERSIONare a real birth-certificate pair, checked byscripts/ci/check_forge_stamp.shas a drift gate (forged-from,template-version,project).adopt.shdoesn't write either file today -FORGE-STAMP.toml/TEMPLATE_VERSIONaren't in itsMACHINERYcopy list at all, onlydocs/forge/ADOPTING.mdis.Proposed fix
adopt.shwritedocs/forge/FORGE-STAMP.toml(+TEMPLATE_VERSION) as part of its Phase 6 commit, same shape as the init-time stamp, plus a field distinguishing provenance, e.g.origin = "adopted"vsorigin = "init"(useful context for anyone reading the stamp later, and for template-update tooling that diffs against a known baseline).adopt.sh:70-72to check for that stamp file instead of (or in addition to, as a secondary heuristic)justfile/scripts/cipresence. A repo with a stamp has genuinely been adopted; a repo with ajustfilemerely has a justfile.adopt/rust-forge-scaffoldingwas ever created and merged (git log --all --grepfor thechore(adopt): rust-forge scaffolding trialcommit message, or a merged-PR check viaghif a remote is configured) before concluding "never adopted." Lower priority than (1)+(2) - the stamp file alone fixes the reported false-positive/false-negative cases.Non-goals
This doesn't change the trial-branch-and-commit mechanics (
just adopt-status/just adopt-undo), which are already reversible and working correctly. It's scoped to the one precondition check.Filed after a live adoption run hit the
justfile+scripts/cifalse positive (worked around by renaming the two paths) and separately hit a real bug inadopt.sh's Phase 5 wiring step (catastrophic-backtracking regex, fixed in #47). Not blocking - the workaround is fine for a one-off adoption - but worth fixing so the next adopter doesn't have to rediscover the same workaround.🤖 Filed with Claude Code