--- title: "project-bootstrap@1.3.0 — idempotent CLAUDE.md merge on upgrade" type: concept updated: 2026-04-30 --- # project-bootstrap@1.3.0 — idempotent CLAUDE.md merge on upgrade _2026-04-30._ ## Problem Step 5 of `project-bootstrap` (≤1.2.0) treated `CLAUDE.md` as binary: - **File missing** → write template. - **File exists** → ask "append the whole template, or leave as is?" Both branches fail the upgrade case. Append-the-whole-template duplicates trigger lines; leave-as-is leaves the file behind whenever the canonical template gains new lines. Concretely: the v1.2.0 release added `check across all projects` (→ `using-projects-meta`) to the template — but every project bootstrapped before v1.2.0 silently kept its old CLAUDE.md and never got the new trigger when re-run on the upgrade path. Same gap for `we're on Windows` (→ `active-platform`). ## Decision Step 5 in v1.3.0 splits along the existing fork: - **Init (file missing)** — unchanged. Write the template, substitute the platform line on non-Windows hosts. - **Upgrade (file exists)** — read existing → diff against template → confirm → append-only-missing. Diff rules: - Trigger lines (everything except the platform line): present iff any existing line, after `trim` + `tolower`, contains the template line's text. Substring match, not equality — tolerates user rewording or trailing punctuation. Avoids false-positive duplicates while preserving canonical wording for missing ones. - Platform line: present iff any existing line matches `we're on (windows|linux|macos)` case-insensitively. If the user pinned a different platform on purpose, leave it. Only append the host-appropriate platform line when none of the three is present. After diff: if zero missing lines → no-op. Otherwise show the diff to the user, wait for explicit confirm, then append (don't rewrite — file order preserved). Re-runs become no-ops once canon is reached. ## Why merge, not overwrite User-edited CLAUDE.md often carries project-specific lines beyond the template (custom triggers, comments, ordering preferences). Overwrite would clobber them; equality-match would miss substring rephrasings. Substring + append-only is the minimum-violence reconcile that keeps canon current without trampling user intent. The confirmation gate stays — append is still a write. ## Composition with `[bootstrap-recommend-projects-meta]` `[bootstrap-recommend-projects-meta]` (⚪ Ready, Step 5.7 future work) covers the **machine-level** dependency: detect missing `projects-meta-mcp` MCP server and recommend `setup-projects-meta`. This task covers the **project-level** trigger merge. They compose cleanly: - 5.7 makes the trigger functional on the host. - 1.3.0 ensures the trigger is actually present in the project's CLAUDE.md. Either alone is partial; together they close the loop for the `check across all projects` line specifically and for any future canonical trigger generally. ## Pattern: idempotent reconcile of canonical config Same pattern other setup-skills can borrow when they own a canonical file shape on disk: read existing → diff against canon → confirm → append-only-missing. Avoids the binary "create from template / leave alone" trap that's good for greenfield and bad for upgrade.