Skip to content

adopt.sh: replace the justfile+scripts/ci heuristic with a real idempotency stamp #48

Description

@githubrobbi

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

  1. 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).
  2. 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.
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions