The hub bound every route to references/help.md and was told to treat no
manifest key but `module` as routing, so a module shipped from another
repository was visible and unroutable. Discovery now reads each sound
manifest's free-form `knowledge` text and follows it, for the module the
question concerns, to the document it names.
help.md stops being privileged: it opens with the discovery model and carries
its method/toolbox content as a labelled section — which is simply what those
two modules' manifests point at.
The knowledge value named `reference/help.md`, but the bmad skill has no
reference/ directory — the file is references/help.md. Nothing ever resolved
the value, so the dangling pointer reached 30 manifests and the release
stamper's constant unnoticed.
parse_packaged_manifest required `knowledge` to equal one hardcoded literal
naming the bmad skill's own help document, so no second module could ship a
manifest that parses. The value is then discarded — it is not a field on
ParsedManifest and nothing reads it — so the check bought nothing.
The cost was disproportionate. discover_installed_copies parses every sibling
manifest before doing any work, so one unacceptable value aborted `bmad update`,
`setup`, and `doctor` for the whole project, including modules whose own
manifests were fine.
This is also what the format spec asks for: the packager rejects unknown or
malformed keys, while `bmad` ignores what it does not recognize, so a manifest
from a newer format cannot break an older hub. stamp_release.py keeps its
equality check — it is the packager, and it only ever runs against this repo's
manifests.
`knowledge` stays required and non-empty. CIS points it at its published
llms.txt, the same remote-docs grounding help.md already names for BMM.
help.md now names https://docs.bmad-method.org/llms.txt as the place to
look when it and the installed skills cannot answer a BMad question; the
index also names the source repository as the final authority. The hub
skill's constraints direct that fetch before conceding a limitation,
restoring the remote-docs grounding the CSV-era bmad-help had via its
_meta row.
setup and doctor rewrite _bmad wholesale via replace_dir, so running
them through a symlinked _bmad crashed mid-flight with a raw
IsADirectoryError from the backup rename. Reads through the symlink
are the point of symlinking and stay untouched; only the write paths
now fail fast, naming the link target and the --project-root to use
against the real installation. The doctor flow rejects in its
missing-_bmad pre-flight so --list-config-questions --doctor fails
before interrogating the user.
The seven standalone support skills that help.md lists as belonging to
no path and no stage — brainstorming, forge-idea, deep-recon,
advanced-elicitation, review, party-mode, customize — now carry
module = "toolbox"; the other 22 skills carry module = "method". The
module key names only the distribution grouping; the _bmad/bmm runtime
config path skills read is staged by setup.py independently of it.
The Claude marketplace and the Codex plugin lid leave this repo: both
plugin ecosystems are built by bmad-code-org/bmad-plugins from the
stamped manifests bmad-skills main serves, sharing one skills tree per
plugin. The stamper therefore stamps and validates only the manifests
(exact key set, known module, the one known update source, per-module
byte identity), and the runbook gains a rebuild-the-plugins step.
Two marketplace entries share source "./" and list their skills
explicitly, replacing the single plugin whose skills tree was a symlink
to skills/ — with per-plugin subsets the symlink bought nothing. The
plugins/bmad-method lid is deleted. bmad-tools has no skills yet, and an
empty skills list makes Claude Code fall back to scanning skills/, so
its source points at the empty plugins/tools directory instead.
The stamper now validates the whole distribution shape before writing:
every manifest carries exactly module, version, and update_source, with
a known module and the one known source; the marketplace has exactly
one entry per module; and each entry's skills list equals, as a set,
the skills whose manifest carries that module — no orphans, no
double-shipping, no dangling paths (a dangling path is load-bearing:
Claude Code silently falls back to shipping every skill for one). The
manifest byte-identity check is now per module, since manifests
legitimately differ in the module line.
Verified live: bmad-bmm installs with 29 skills, bmad-tools with 0,
and a scratch stamp of the real tree passes.
Run the suite with pytest-xdist (-n auto): 17.7s to 5.9s wall measured
locally. Quiet output when green (-q), short tracebacks and a summary
of non-passing outcomes when not (-ra --tb=short). Write nothing to
disk: -p no:cacheprovider stops .pytest_cache and
PYTHONDONTWRITEBYTECODE=1 stops __pycache__; a warm single-directory
bytecode cache measured no faster than cold, so the cache bought
nothing.
Parallelism exposed a race in test_recon_kit.py: two test classes
shared the fixture path tests/_report.md and deleted it after use, so
xdist workers could unlink it under each other. All four file-fixture
tests there now write into per-test temporary directories.
The stamper globbed for manifests, so a skill directory without
module-manifest.toml was silently skipped and would ship unstamped
with no version or update_source. Enumerate skill directories and
fail naming the offender before anything is written.
The tools/bmm split will put a second plugin into the Claude marketplace,
so the stamper no longer hardcodes plugins[0] and the bmad-method path.
It now stamps the version of every entry in the marketplace plugins
array, discovers plugins/*/.claude-plugin/plugin.json by glob, and
requires the marketplace entry count to match the plugin manifest count.
Each textual rewrite is proven against the parsed JSON: the result must
equal the original tree with only the intended version nodes changed,
and a "version" key anywhere else in a stamped file fails the run
before anything is written.
The version's single source of truth is skills/*/module-manifest.toml.
Plugin metadata (Claude marketplace and plugin.json, Codex plugin.json)
is no longer stamped — whatever builds those artifacts reads the version
from skills/bmad/module-manifest.toml. The textual single-"version"-key
rewrite would have broken the moment the Claude marketplace gained a
second plugin, and those lids are likely to leave this repo anyway.
Tests updated: JSON-stamping cases replaced with a regression test that
plugin metadata is left byte-identical; runbook expectation adjusted.
Make the bmad-skills mirror's main branch release-only: dev now mirrors
this branch unstamped, and main is always current dev plus one stamp
commit cut by following tools/release.md.
- tools/stamp_release.py: stdlib-only stamper that validates an
orderable SemVer argument (rejects -dev, which setup.py cannot
order), rewrites the version line in every skills/*/module-manifest.toml
textually to keep the manifests byte-identical, and sets the version in
.claude-plugin/marketplace.json, plugins/bmad-method/.claude-plugin/
plugin.json, and .codex-plugin/plugin.json without reformatting them.
Nothing is written unless every file validates; after writing it
re-reads everything and fails naming the offending path if the
manifests diverge or any file carries a different version.
- tools/release.md: self-contained runbook — clean detached checkout of
origin/dev, human-chosen version that differs from what main serves,
stamp, review, commit, force-with-lease push of HEAD:main.
- tools/tests/test_stamp_release.py: unit tests covering the happy path,
invalid and -dev versions, missing version line/key, divergent
manifests, idempotent re-stamp, and formatting preservation.
- TEMP-RELEASE-PROCESS.md: the mirror is the testbed for both the npx
install/update flow and the release process that feeds it. Records the
dev/main branch model, the 0.0.0-next.N throwaway versions to stamp
there, and a note to replace the file with the real release
instructions before this branch merges to main.
- AGENTS.md: replace the test-mirror section with a pointer to that file.
- .npmignore: exclude the new tool files and the temporary runbook from
the npm tarball.
Codex plugin add copies the plugin directory without dereferencing
symlinks, so the nested plugin shipped no skills. Root the Codex
plugin at the repo so the cache copy carries the real skills tree.
Point both marketplaces at plugins/bmad-method. That folder holds the
host manifests and a skills/ symlink into the repo skills tree so an
install copies the plugin directory and Claude dereferences the payload.
Track the Codex catalog at .agents/plugins/marketplace.json.
The Node suite still installed skills under `_bmad/bmm/`. Cover the current
host-skill layout from render_skill.py itself, and keep a few internals
tests for publish paths that are awkward to hit through a full skill.
Drop test/test-template-sync.js and its hook in test:sprint-planning.
The retrospective fixture comment now says to sync the vendored
template by hand when the source changes.
All intra-skill references are now spelled from the skill root.
checkpoint-preview's step files move into steps/ (matching code-review)
and generate-trail.md into references/ so the whole chain falls under
the file-refs checker; 305 refs verified, both validators clean.
Replace tools/validate-skills.js with a stdlib Python 3.11 port and rewire
validate:skills and test:skills onto it. Observable behavior is preserved,
including the JS frontmatter quirks; in-skill file walks are sorted.
The checker's V4/V5 grammar (exec attrs, invoke-task, step metadata,
Load directives, quoted dot-paths, {_bmad} shorthand) matches nothing
in the flat tree; it verified only four script filenames. It now
resolves backticked slash-paths against the containing file's directory
and the skill root, flagging a missing file only when the path's first
directory exists — paths without one are prose, so the current tree
stays at zero false positives (276 refs verified, up from 135). Files
sitting directly under skills/ are reported as stray.
The checker still scanned src/, which the flattening removed, so it
crashed on any fresh checkout and failed the quality gate. It now scans
skills/; the obsolete core-skills/bmm-skills module mapping is replaced
by _bmad/scripts/ -> skills/bmad/scripts/, with the install-only and
install-generated skip lists unchanged.
It forwarded to bmad-project-context, which help.md routes to directly.
Docs mentions are historical migration notes and stand as written;
removals.txt has no consumer since the JS installer was deleted.
The former core skills ship in the same flat unit but belong to no
path or stage; help.md never mentioned them, so the router could not
recommend them.
setup.py requires a version field and every shipped TOML manifest
lacked one, so all 30 failed parse_packaged_manifest. 6.11.0-next is
valid semver and orders below the eventual 6.11.0 release, so dev-tree
installs report newer-available once main ships.
Move the duplicated BMM routing guide into the bmad hub skill and replace
every module-manifest.md with a two-key TOML file parsed by stdlib tomllib.
Drop PyYAML from setup.py and from test:npx-skills.
Stdlib zipfile replaces the zip-CLI dependency; behavior and output
are otherwise unchanged. Verified live on both the success path (all
6 bundles packaged) and the missing-directory refusal path.
Move core and BMM skills to skills/<id>/ so npx skills can install
the repo as-is. Copy the BMM module manifest into every skill, move
the shared Python runtime into skills/bmad/scripts/, drop src/, and
retarget repo tests at the new locations.
Port tools/validate-file-refs.js to tools/validate_file_refs.py (PEP 723,
pyyaml) preserving detection, resolution, CLI, and CI-annotation behavior.
CSV extraction is dropped: .csv leaves the scan set, and the CSV test and
fixtures are deleted rather than ported. The npm dependencies block (yaml,
csv-parse) goes away with its sole consumer; validate:refs and test:refs
now run via uv, and test:refs joins the quality chain. A new stdlib
unittest suite covers extraction, mapping, skip lists, UNRESOLVED vs
BROKEN, leak detection, exit codes, and the GitHub Actions output path.
The port also hardens what the JS left fragile: multi-document YAML is
scanned in full, non-UTF-8 bytes are replaced instead of crashing the
run, and directory symlinks are not traversed.