Skip to content

fix(setup): never delete live core BMAD config/manifest (#64)#73

Merged
pbean merged 5 commits into
mainfrom
fix/64-setup-preserve-core-config
Jul 9, 2026
Merged

fix(setup): never delete live core BMAD config/manifest (#64)#73
pbean merged 5 commits into
mainfrom
fix/64-setup-preserve-core-config

Conversation

@pbean

@pbean pbean commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #64. Running /bmad-loop-setup in a multi-module BMAD v6 project deleted live, manifest-tracked BMAD infrastructure — _bmad/core/config.yaml, per-module config, and the entire _bmad/_config/ manifest directory — breaking future npx bmad-method install upgrades and violating the README's "your standard BMAD install is never modified" guarantee.

Four destructive paths were confirmed in source (the reporter named three; validation found the fourth):

Path What it destroyed
cleanup-legacy.pydirs_to_remove = [module_code, "core"] + also_remove shutil.rmtree of _bmad/core/ (and _config/ via --also-remove)
SKILL.md cleanup command passed --also-remove _config → manifest dir
merge-config.pycleanup_legacy_configs() unlinked _bmad/core/config.yaml
merge-help-csv.pycleanup_legacy_csvs() unlinked _bmad/core/module-help.csv

The --skills-dir "safety check" only guarded dirs containing a SKILL.md; config-only dirs bypassed it and were removed while the JSON still reported skills_verified: true.

Root cause

The scripts are a fork of the bmad-code-org/bmad-builder setup-skill-template, which assumes the installer stages redundant skill payloads + config under _bmad/<module>/ and core/. On BMAD v6 that's false: per-module config.yaml is live, skills live at .claude/skills/, and _bmad/_config/ is the live installer manifest. Same root cause as upstream bmad-code-org/bmad-builder#96 (whose maintainer plans to remove the scaffolder — which won't fix bmad-loop's already-shipped fork, so this fixes it here).

Fix

Adopt #96's version-agnostic verified-redundant-payload guard. A directory is removed only when:

  • it contains ≥1 SKILL.md, and
  • it carries no live config/manifest files (config.yaml, module-help.csv, installer manifests, or the _config/ dir), and
  • with --skills-dir, every skill in it is verified installed.

Live config dirs (core/, per-module, _config/) are protected and reported in a new directories_protected field. The merge scripts still read legacy config as fallback defaults but no longer delete it. No version check needed — the decision is driven entirely by directory contents, so it safely no-ops on v6 while still cleaning genuine pre-v6 payload dirs.

SKILL.md drops --also-remove _config and its prose is updated accordingly.

Behavior note

On a bmad-auto→bmad-loop rename, a stale _bmad/bauto/config.yaml (if it holds only config) is now preserved rather than deleted — harmless residue, documented in SKILL.md as a manual-cleanup option. This is the correct trade-off given the "never delete live config" invariant.

Tests

New tests/test_setup_cleanup_scripts.py runs the real scripts end-to-end and asserts:

  • the documented invocation is a no-op on a v6 tree; core//_config//per-module config survive
  • explicit --module-code core --also-remove _config still leaves them intact
  • a genuine redundant skill-payload dir is removed
  • a payload dir whose skill is missing from --skills-dir errors (exit 1) instead of deleting
  • the merge scripts preserve core/config.yaml and core/module-help.csv

Full suite: 1425 passed, 1 skipped. trunk check clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed bmad-loop-setup cleanup to preserve live BMAD core configuration and installer/manifest metadata during upgrades, including when targeting legacy cleanup options.
    • Updated config/help merge behavior to preserve legacy YAML and legacy module-help.csv (merged outputs still take precedence).
    • Cleanup now removes only verified redundant skill payload directories (via SKILL.md), reports directories_protected, and safely handles missing installed-skill metadata; legacy bmad-auto cleanup is now conditional.
  • Tests
    • Added regression tests for v6-like layouts, protected/live preservation, safe no-op behavior, correct deletion/protection reporting, and merge no-delete guarantees.

bmad-loop-setup's cleanup scripts hardcoded `core` in their delete lists
(and the SKILL passed `--also-remove _config`), so running /bmad-loop-setup
in a multi-module BMAD v6 project deleted _bmad/core/config.yaml, per-module
config, and the entire _bmad/_config/ manifest — breaking future
`npx bmad-method install` upgrades and violating the "standard BMAD install
is never modified" guarantee.

Cleanup now removes a directory only when it is a verified-redundant skill
payload: it contains a SKILL.md, carries no live config/manifest files, and
(with --skills-dir) its skills are verified installed. Live config dirs
(core/, per-module, _config/) are protected and reported under a new
`directories_protected` field. The merge scripts still read legacy config as
fallback defaults but no longer delete it (on v6 those files are live and
manifest-tracked). Version-agnostic — driven by directory contents, no
version check.

Adds tests/test_setup_cleanup_scripts.py exercising the real scripts.
Same root cause as upstream bmad-code-org/bmad-builder#96.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a6dd6314-2ce4-44ac-b537-f518a1f58f0d

📥 Commits

Reviewing files that changed from the base of the PR and between 9d4973a and adc4d52.

📒 Files selected for processing (2)
  • src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md
  • src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md
  • src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py

Walkthrough

The setup cleanup flow now preserves live BMAD v6 config and manifest state. Cleanup only removes verified redundant payloads, merge scripts keep legacy inputs read-only, and the docs, changelog, and tests were updated to match.

Changes

Cleanup and merge preservation fix

Layer / File(s) Summary
Cleanup classification logic
src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py
Adds config/manifest-aware directory classification, removes the old verification-only flow, and reports protected directories in the JSON result.
Merge scripts preserve legacy files
src/bmad_loop/data/skills/bmad-loop-setup/scripts/merge-config.py, src/bmad_loop/data/skills/bmad-loop-setup/scripts/merge-help-csv.py
Both scripts keep legacy config and CSV files, return empty deletion lists, and update their docs/help text to reflect read-only legacy handling.
Setup docs and changelog updates
src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md, CHANGELOG.md
Setup instructions and the changelog now describe preservation of live config, removal only of verified-redundant payloads, and the fixed deletion bug.
Regression tests for cleanup and merge scripts
tests/test_setup_cleanup_scripts.py
Adds subprocess-based tests for v6-like cleanup behavior, protected state preservation, verified payload deletion, missing-skill failures, and merge-script preservation of legacy files.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Poem

A rabbit hopped where configs grew,
"Keep live things safe, that’s the rule I knew!"
The manifest sparkled, the payloads went light,
Redundant bits vanished, but v6 stayed right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Clear and specific; it accurately summarizes the main fix of preventing deletion of live core BMAD config/manifest.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/64-setup-preserve-core-config

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@augmentcode

augmentcode Bot commented Jul 6, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Prevents bmad-loop-setup from deleting live, manifest-tracked BMAD v6 infrastructure under _bmad/ (core config, per-module config, and the installer manifest).

Changes:

  • Reworks cleanup-legacy.py to remove only verified redundant skill-payload directories (requires SKILL.md, no config/manifest markers, and optional installed-skill verification).
  • Adds explicit reporting for protected directories via directories_protected, and improves directories_not_found reporting for TOCTOU/nested removals.
  • Disables legacy file deletion in merge-config.py and merge-help-csv.py while retaining legacy reads as fallback defaults.
  • Updates SKILL.md guidance to reflect the new safety model and removes the documented --also-remove _config invocation.
  • Adds end-to-end regression tests covering v6 no-op behavior, protected core/_config, payload removal, and missing-skill error handling.

Technical Notes: The cleanup decision is content-based (no version checks) and aims to preserve shared BMAD state while still cleaning genuine pre-v6 staged payloads.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py Outdated
cleanup-legacy.py discarded cleanup_directories()'s not_found return, so a
removable dir that vanished between classify_dirs() and removal (TOCTOU, or a
nested --also-remove target removed with its parent earlier in the loop) fell
out of every JSON list — directories_removed, directories_protected, and
directories_not_found — while safety_checks.skills_verified still printed true,
misreporting what happened.

Merge cleanup_directories()'s not_found into the classify-time not_found before
building the result. The two are disjoint (cleanup only processes `removable`,
which excludes classify's not_found), so plain concatenation is a strict
superset that cannot change existing behavior.

Adds a deterministic regression test that removes a nested --also-remove target
whose parent is deleted first, asserting the child surfaces in
directories_not_found. Addresses augmentcode[bot] review on PR #73.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pbean

pbean commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

augment review

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Everything else is left untouched. On a modern BMAD v6 install the per-module and
core directories hold only live config (no staged SKILL.md), and _bmad/_config/ is
the live installer manifest — so this script is a safe no-op there and never deletes
shared BMAD state. 'core' and '_config' are never removed. The removal decision is

@augmentcode augmentcode Bot Jul 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module docstring states that 'core' is never removed, but the implementation only hard-codes protection for _config by name (and otherwise relies on marker files). If core/ ever lacks those markers but contains SKILL.md payload, it looks eligible for removal, so the wording may be stronger than the actual guarantee.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — the docstring promised more than the code enforced. Fixed in 9d4973a by making the code match the docs: core is now protected by name exactly like _config (checked in both classify_dirs and is_config_bearing, reported as "live BMAD dir (protected by name)" in directories_protected). The cost is nil in practice — any real legacy core/ also carries config.yaml and was already marker-protected; the hypothetical marker-less core payload now becomes harmless preserved residue, the same trade-off SKILL.md already documents for the bauto rename case. Added test_cleanup_protects_core_by_name_even_as_pure_payload so the docstring guarantee is tested, not just asserted.

missing = []
if path.name == "_config":
return True
for marker in _CONFIG_MARKERS:

@augmentcode augmentcode Bot Jul 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_config_bearing() only checks for marker files directly under the candidate directory, while find_skill_dirs() scans recursively; if config/manifest markers can exist deeper in the tree, a directory could be misclassified as removable. Consider whether this guard should also be recursive (or otherwise more conservative) to avoid any chance of deleting live state.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid catch — a candidate with a SKILL.md plus nested live config (e.g. sub/config.yaml) was classified removable and rmtree would have taken the nested state with it. Fixed in 9d4973a: is_config_bearing() now scans the whole candidate tree (marker files and nested _config/ dirs).

One deliberate nuance: the scan is payload-aware rather than blindly recursive. Staged skill payloads legitimately ship marker-named files — bmad-loop-setup itself ships assets/module-help.csv — so a blind recursive scan would false-protect genuine payloads and permanently no-op the documented bauto rename cleanup. A marker only protects when it is not inside a SKILL.md-bearing subtree (strictly below the candidate root, so top-level markers always protect, preserving existing behavior). Regression tests cover both directions: test_cleanup_protects_dir_with_nested_live_config (nested live config → protected) and test_cleanup_removes_payload_whose_skill_ships_marker_named_assets (marker-named skill assets → still removed).

pbean added 2 commits July 8, 2026 17:14
Resolve CHANGELOG.md conflict: keep main's Unreleased ### Added block
followed by this branch's ### Fixed entry.
…#73 review)

Two review fixes to the cleanup-legacy guard:

- 'core' is now protected by name like '_config', so the docstring's
  "'core' and '_config' are never removed" is literally enforced; a
  marker-less core/ holding a SKILL.md payload is preserved (harmless
  residue) instead of removed.
- is_config_bearing() now scans the whole candidate tree for
  config/manifest markers instead of only the top level, so nested live
  state (e.g. sub/config.yaml) protects the dir. The scan is
  payload-aware: marker-named files inside a SKILL.md-bearing subtree
  are skill assets (bmad-loop-setup itself ships assets/module-help.csv),
  not live state — a blind recursive scan would have false-protected
  genuine payloads and no-opped the documented bauto rename cleanup.

Adds three regression tests covering name-protected core, nested live
config, and a payload whose skill ships marker-named assets.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md (1)

184-196: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the active CLI’s skills tree here. This hardcodes .claude/skills, but codex/gemini installs live under .agents/skills/, so the installed-skill check can point at the wrong tree on non-Claude setups.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md` around lines 184 - 196,
Update the cleanup confirmation in SKILL.md to use the active CLI’s skills
directory instead of hardcoding .claude/skills, since the installed-skill
verification in cleanup-legacy.py must target .agents/skills/ for codex/gemini
installs. Locate the cleanup examples and the explanatory text around
cleanup-legacy.py, and make the --skills-dir guidance resolve from the current
CLI environment or explicitly mention the correct tree per CLI so the
verification step checks the right installed skills path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md`:
- Around line 184-196: Update the cleanup confirmation in SKILL.md to use the
active CLI’s skills directory instead of hardcoding .claude/skills, since the
installed-skill verification in cleanup-legacy.py must target .agents/skills/
for codex/gemini installs. Locate the cleanup examples and the explanatory text
around cleanup-legacy.py, and make the --skills-dir guidance resolve from the
current CLI environment or explicitly mention the correct tree per CLI so the
verification step checks the right installed skills path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26355e2e-dba0-4fc0-a4b2-cce615b06a00

📥 Commits

Reviewing files that changed from the base of the PR and between bb0ff97 and 9d4973a.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/bmad_loop/data/skills/bmad-loop-setup/SKILL.md
  • src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py
  • tests/test_setup_cleanup_scripts.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/bmad_loop/data/skills/bmad-loop-setup/scripts/cleanup-legacy.py

@pbean

pbean commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

augment review

@augmentcode

augmentcode Bot commented Jul 9, 2026

Copy link
Copy Markdown

Looks like you're out of credits for this review. Head to your account settings to top up and keep reviews running. https://app.augmentcode.com/account/subscription

…eview)

SKILL.md hardcoded .claude/skills as the cleanup verification tree, but
codex/gemini/copilot/antigravity installs lay skills at .agents/skills/.
On a non-Claude-only setup with a genuine legacy payload the documented
command failed verification (fail-safe exit 1, nothing deleted) and
wedged setup with a spurious missing-skills error.

Guidance now names the per-CLI trees and notes any registered CLI's
tree verifies (init installs the bundle into every selected tree).
Script docstring and --skills-dir help aligned. Docs/help only — no
behavior change.
@pbean

pbean commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai Valid — fixed in adc4d52. The --skills-dir guidance in SKILL.md's cleanup section now names the per-CLI trees (.claude/skills for claude, .agents/skills for codex/gemini/copilot/antigravity) and notes that any registered CLI's tree verifies correctly, since init lays the bundled skills into every selected tree. The script docstring and --skills-dir --help text were aligned to match.

Scope notes: the failure mode was already fail-safe — a wrong tree makes verification exit 1 without deleting anything — so this was a wedged-setup UX bug, not a data-loss path; the hardcode also predates this PR (this PR's diff only dropped --also-remove _config from that line), but since the section was rewritten here it was cheap to fix in place. I kept the fix docs-only rather than teaching cleanup-legacy.py to accept multiple --skills-dir trees: the verification set is always the bmad-loop-* bundle, which init installs into every registered tree, so one correctly-chosen tree is sufficient.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cd /tmp
gh api repos/bmad-code-org/bmad-loop/commits/adc4d52 --jq '.commit.message' 2>&1 | head -20

Length of output: 884


@pbean Confirmed — commit adc4d52 addresses this cleanly. The updated SKILL.md guidance now correctly documents the per-CLI skill tree locations (.claude/skills for Claude, .agents/skills for codex/gemini/copilot/antigravity), and the note that any single registered CLI's tree verifies successfully (since init installs the bundle into every selected tree) is a good clarification for users running mixed/non-Claude setups.

Agreed on scope: since the original hardcode was already fail-safe (wrong tree → exit 1, no deletion) and predates this PR, a docs/help-only fix is the right level of effort here rather than expanding cleanup-legacy.py to accept multiple --skills-dir trees. Thanks for the quick turnaround.

(‐^ᴥ^‐)

@pbean pbean merged commit a495333 into main Jul 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bmad-loop-setup deletes core BMAD config/manifest (_bmad/core/config.yaml, _bmad/_config/) in multi-module projects

1 participant