Step 5 was binary on upgrade (append whole template / leave alone), so projects bootstrapped before v1.2.0 silently missed new canonical triggers (`check across all projects`, `we're on Windows`) on re-run. Now upgrade reads existing CLAUDE.md, substring-diffs vs template, preserves a deliberately-pinned platform line, and appends only missing lines after explicit confirm. Re-runs are no-ops. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.2 KiB
bootstrap-claude-md-merge
Goal
project-bootstrap upgrade mode currently treats CLAUDE.md as binary: either append
the entire template (causing duplicate trigger lines) or leave it untouched. Result:
projects bootstrapped before v1.2.0 silently miss new canonical triggers like
check across all projects (→ using-projects-meta) and we're on Windows
(→ active-platform) when re-run on the upgrade path. Fix: turn Step 5 into an
idempotent merge — read existing CLAUDE.md, diff against the template, append only
missing canonical lines after user confirm. Re-runs become no-ops.
Key files
skills/project-bootstrap/SKILL.md:220-249— Step 5 (the broken binary choice)skills/project-bootstrap/assets/CLAUDE.md.template— canonical trigger setskills/project-bootstrap/README.md— public surface, mention idempotencydist/project-bootstrap.skill— rebuild after install~/.claude/skills/project-bootstrap/— reinstall target
Decisions log
- 2026-04-30: Diff strategy — substring match per template line against existing file (trim + lowercase). A trigger is "present" if any existing line contains the template trigger text. Avoids false-positive duplicates when user has rephrased a trigger but preserves canonical wording for missing ones. Rejected: full line-equality match (too brittle — user comments / case differences would re-add).
- 2026-04-30: Confirmation gate stays — show user the diff (N lines to append) before touching the file. Idempotent run with no missing lines = silent no-op.
- 2026-04-30: Version bump 1.2.0 → 1.3.0. Behavior change visible to users on every upgrade — minor bump fits semver intent.
Open questions
- Should the platform line (
we're on Windows) be substituted to match the host during merge, the same way init does it? Decision: yes — if existing file has nowe're on (Windows|Linux|macOS)line, append the host-appropriate one. If it has any of the three, leave it (user may have intentionally pinned a different platform).
Completed steps
- open task
- patch Step 5 in SKILL.md (read → diff → confirm → append) — split Init / Upgrade-merge
- bump version 1.2.0 → 1.3.0 in SKILL.md frontmatter
- update
skills/project-bootstrap/README.mdCLAUDE.mdrow to note idempotent merge - update root
README.md+README.ru.mdupgrade-mode paragraph - reinstall + rebuild dist (
bash scripts/install.sh project-bootstrap && bash scripts/build.sh project-bootstrap); verifiedversion: 1.3.0on disk + Step 5 split - wiki: concept page
.wiki/concepts/bootstrap-claude-md-merge.md, index entry, log entry - commit
- STATUS.md: 🔴 → 🟢
- decision on platform line — preserve if user pinned non-host one (case-insensitive
we're on (windows|linux|macos)match)
Notes
Existing related task [bootstrap-recommend-projects-meta] (Step 5.7) is machine-level
detection (recommend setup-projects-meta if MCP server not registered). This task is
project-level merge of the trigger line into the project's CLAUDE.md. They compose
cleanly: 5.7 makes the trigger functional on the host, this task ensures the trigger
is actually present in the file.