feat(project-bootstrap): v1.3.0 — idempotent CLAUDE.md merge on upgrade
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>
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
# Task Board
|
||||
_Updated: 2026-04-30_
|
||||
|
||||
## 🟢 [bootstrap-claude-md-merge] — project-bootstrap upgrade: idempotent merge of canonical CLAUDE.md triggers
|
||||
**Status:** done
|
||||
**Where I stopped:** Step 5 in `skills/project-bootstrap/SKILL.md` split into Init / Upgrade-merge — upgrade reads existing CLAUDE.md, substring-diffs vs template (case-insensitive trim), platform line preserved if user pinned a non-host platform, asks confirm with diff, appends only missing canonical lines; version bump 1.2.0 → 1.3.0; skill row in `skills/project-bootstrap/README.md` updated to note idempotent merge; root `README.md` + `README.ru.md` upgrade-mode paragraph extended; reinstalled to `~/.claude/skills/project-bootstrap/`, `dist/project-bootstrap.skill` rebuilt; concept page `.wiki/concepts/bootstrap-claude-md-merge.md` written; index + log entries added
|
||||
**Next action:** (none — kept until merged)
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Canonical layout. One block per task. Per-task deep context lives in
|
||||
.tasks/<task-slug>.md (created by using-tasks when a task becomes active
|
||||
|
||||
53
.tasks/bootstrap-claude-md-merge.md
Normal file
53
.tasks/bootstrap-claude-md-merge.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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 set
|
||||
- `skills/project-bootstrap/README.md` — public surface, mention idempotency
|
||||
- `dist/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 no
|
||||
`we'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
|
||||
- [x] open task
|
||||
- [x] patch Step 5 in SKILL.md (read → diff → confirm → append) — split Init / Upgrade-merge
|
||||
- [x] bump version 1.2.0 → 1.3.0 in SKILL.md frontmatter
|
||||
- [x] update `skills/project-bootstrap/README.md` `CLAUDE.md` row to note idempotent merge
|
||||
- [x] update root `README.md` + `README.ru.md` upgrade-mode paragraph
|
||||
- [x] reinstall + rebuild dist (`bash scripts/install.sh project-bootstrap && bash scripts/build.sh project-bootstrap`); verified `version: 1.3.0` on disk + Step 5 split
|
||||
- [x] wiki: concept page `.wiki/concepts/bootstrap-claude-md-merge.md`, index entry, log entry
|
||||
- [x] commit
|
||||
- [x] STATUS.md: 🔴 → 🟢
|
||||
- [x] 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.
|
||||
49
.wiki/concepts/bootstrap-claude-md-merge.md
Normal file
49
.wiki/concepts/bootstrap-claude-md-merge.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
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.
|
||||
@@ -13,6 +13,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever
|
||||
## Concepts
|
||||
|
||||
- [active-platform-decision.md](concepts/active-platform-decision.md) — why `active-platform` is a skill (not a memory entry); why default = Windows; how it's wired into `project-bootstrap`
|
||||
- [bootstrap-claude-md-merge.md](concepts/bootstrap-claude-md-merge.md) — project-bootstrap@1.3.0 — Step 5 upgrade path becomes idempotent merge (read → diff vs template → confirm → append missing); fixes silent gap where pre-1.2.0 projects never picked up new canonical triggers (`check across all projects`, `we're on Windows`)
|
||||
- [bootstrap-manifest.md](concepts/bootstrap-manifest.md) — record of which `project-bootstrap` / `setup-wiki` / `setup-tasks` versions initialized this project's `.wiki/` and `.tasks/` layout (overwritten on re-bootstrap; history in git)
|
||||
- [build-notes.md](concepts/build-notes.md) — why `build.ps1` exists alongside `build.sh`; PS 5.1 backslash-in-zip gotcha; how to extract a `.skill`
|
||||
- [install-portability.md](concepts/install-portability.md) — `install.sh` / `build.sh` rewritten to drop `mapfile` (bash 4+) and `find -printf` (GNU only) so stock macOS (bash 3.2 + BSD find) works
|
||||
|
||||
@@ -36,3 +36,4 @@ Parseable: `grep "^## \[" .wiki/log.md | tail -20`.
|
||||
## [2026-04-30] refactor | project-bootstrap re-run on this repo (upgrade mode) — setup-wiki noop, setup-tasks noop, CLAUDE.md unchanged (matches template), bootstrap-manifest.md written: project-bootstrap@1.1.0 / setup-wiki@1.0.0 / setup-tasks@1.0.0
|
||||
## [2026-04-30] decision | project-bootstrap@1.2.0 — CLAUDE.md template gains `check across all projects` (verbatim trigger from using-projects-meta description); installs auto-load cross-project tasks + shared-wiki access in every bootstrapped repo; no Step 5.7 dependency-check mirror — Prerequisites pointer in using-projects-meta is self-correcting; local CLAUDE.md, both READMEs, dist/.skill, projects-meta-skills concept page synced
|
||||
## [2026-04-30] decision | Step 5.7 mirror of Step 5.6 (projects-meta-mcp dependency detector / `setup-projects-meta` recommendation) accepted as future work; tracked as ⚪ Ready task `[bootstrap-recommend-projects-meta]`; deferred until first observed fresh-machine miss so detector signal is informed by real failure mode; concept page `projects-meta-skills.md` updated to reflect new stance
|
||||
## [2026-04-30] decision | project-bootstrap@1.3.0 — Step 5 upgrade path turned idempotent: read existing CLAUDE.md → substring-diff vs template → confirm → append-only-missing; closes silent gap where pre-1.2.0 projects never picked up new canonical triggers (`check across all projects`, `we're on Windows`); platform line preserved if user pinned a non-host one; concept page `bootstrap-claude-md-merge.md` written; README CLAUDE.md row updated to note idempotent merge
|
||||
|
||||
@@ -58,7 +58,10 @@ project's folder and it will, in one pass:
|
||||
|
||||
Two modes, picked automatically: **init** for an empty folder, **upgrade**
|
||||
for an existing project (the skill only fills the gaps and never overwrites
|
||||
without explicit confirmation).
|
||||
without explicit confirmation). On upgrade, `CLAUDE.md` is merged
|
||||
idempotently — only canonical trigger lines that aren't already present are
|
||||
appended after explicit confirm, so re-running `project-bootstrap` after a
|
||||
template change picks up the new triggers without duplicating the old ones.
|
||||
|
||||
### Edit a skill
|
||||
|
||||
|
||||
@@ -44,7 +44,11 @@ bash scripts/install.sh caveman wiki-maintainer
|
||||
|
||||
Два режима, выбирается автоматически: **init** для пустой папки и **upgrade**
|
||||
для существующего проекта (скилл только дозаполняет пробелы и ничего не
|
||||
перезаписывает без явного подтверждения).
|
||||
перезаписывает без явного подтверждения). В режиме upgrade `CLAUDE.md`
|
||||
сливается идемпотентно — только канонические триггер-строки, которых ещё
|
||||
нет в файле, дописываются после явного подтверждения, поэтому повторный
|
||||
запуск `project-bootstrap` после обновления шаблона подтягивает новые
|
||||
триггеры, не дублируя старые.
|
||||
|
||||
### Отредактировать скилл
|
||||
|
||||
|
||||
BIN
dist/project-bootstrap.skill
vendored
BIN
dist/project-bootstrap.skill
vendored
Binary file not shown.
@@ -44,7 +44,7 @@ projects bootstrapped at different times debuggable.
|
||||
| `README.md` | minimal stub | Skipped if file exists. |
|
||||
| `.wiki/` | delegated to `setup-wiki` | Karpathy LLM Wiki layout — `CLAUDE.md`, `index.md`, `log.md`, `overview.md`, `raw/`, `entities/`, `concepts/`, `packages/`, `sources/`. |
|
||||
| `.tasks/` | delegated to `setup-tasks` | Canonical board — `STATUS.md` plus per-task `<task-slug>.md` files. |
|
||||
| `CLAUDE.md` | `assets/CLAUDE.md.template` | Skill triggers (`use superpowers`, `use project wiki`, etc.). On non-Windows hosts, swap the `we're on Windows` line for `we're on Linux` / `we're on macOS`. |
|
||||
| `CLAUDE.md` | `assets/CLAUDE.md.template` | Skill triggers (`use superpowers`, `use project wiki`, etc.). On non-Windows hosts, swap the `we're on Windows` line for `we're on Linux` / `we're on macOS`. On upgrade, the template is treated as a canonical set and merged idempotently — only missing trigger lines are appended after user confirm. Re-runs are no-ops. |
|
||||
| `.wiki/concepts/bootstrap-manifest.md` | generated | Records which skill versions initialized the project, so cross-project layout drift is debuggable. |
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: project-bootstrap
|
||||
version: 1.2.0
|
||||
version: 1.3.0
|
||||
description: >
|
||||
Initializes or upgrades a project in the current folder: git, .gitignore, README.md,
|
||||
.wiki/ using Karpathy's method, .tasks/ for task tracking, CLAUDE.md with skill triggers.
|
||||
@@ -219,12 +219,41 @@ If `setup-tasks` is not installed, **stop** and tell the user — same rule as S
|
||||
|
||||
## Step 5 — CLAUDE.md
|
||||
|
||||
If `CLAUDE.md` already exists — show its contents and ask:
|
||||
"CLAUDE.md already exists. Append skill triggers to the end, or leave as is?"
|
||||
Two paths, picked by file presence:
|
||||
|
||||
If it does not exist — create from template `assets/CLAUDE.md.template`.
|
||||
### Init (file does not exist)
|
||||
|
||||
Create `CLAUDE.md` from `assets/CLAUDE.md.template`. Substitute the platform line
|
||||
on non-Windows hosts (`we're on Linux` / `we're on macOS` instead of
|
||||
`we're on Windows`).
|
||||
|
||||
### Upgrade (file exists) — idempotent merge
|
||||
|
||||
Treat the template as the canonical trigger set and reconcile the existing file
|
||||
against it. Re-runs are no-ops once the file is in canon.
|
||||
|
||||
1. Read the existing `CLAUDE.md`.
|
||||
2. For each non-empty, non-comment line in the template, decide whether it's
|
||||
already present:
|
||||
- **Trigger lines** (everything except the platform line) — present iff any
|
||||
existing line, after `trim` + `tolower`, contains the template line's
|
||||
trigger text. Substring match, not equality — tolerates user rewording or
|
||||
trailing punctuation.
|
||||
- **Platform line** (`we're on Windows`) — 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 alone**. Only append
|
||||
the host-appropriate platform line when none of the three is present.
|
||||
3. Collect missing lines. If none → print `CLAUDE.md already canon — no changes`
|
||||
and skip to Step 5.5.
|
||||
4. Show the user the diff (N lines, exact text to append) and ask one question:
|
||||
"Append these N missing canonical triggers to the end of CLAUDE.md?" Wait
|
||||
for explicit confirmation before writing.
|
||||
5. On confirm: append a single newline (if the file doesn't end with one) and
|
||||
then the missing lines, one per line. Don't rewrite the file — only append.
|
||||
Don't reorder existing lines. Don't dedupe within the existing file.
|
||||
|
||||
Template contents (`assets/CLAUDE.md.template` — source of truth):
|
||||
|
||||
Template contents:
|
||||
```markdown
|
||||
# CLAUDE.md
|
||||
# Agent instructions. Each line is a trigger for an installed skill.
|
||||
|
||||
Reference in New Issue
Block a user