Core skills are independent tools, not a set to finish. Help no longer
reports uninstalled skills as missing, and the packager no longer
requires every skill id in the manifest body.
A fresh npx install has no _bmad, so the render command fails with
script-not-found and the skill dead-ended at HALT. Name that one state
and its remedy: run the installed bmad skill's setup, then retry the
render once. Any other failure still halts.
pre-commit ran lint-staged and npm test but never checked their exit
codes, so a failing suite still produced a commit. set -e makes any
failing step abort.
The model already knows who it is talking to and what language fits the
conversation; answers frozen at install time only mislead. Setup asks no
questions of its own now — the only questions come from installed module
manifests. user_name, communication_language, user_skill_level, and
document_output_language leave the config templates, setup.py, the setup
reference, and every live skill's prose. Classic-installer surface
(module.yaml, tools/installer, v6-shims, bmad-help) is unchanged; the
test suite keeps negative assertions that the keys stay out of team
config.
The packaged tree publishes to bmad-code-org/bmad-skills, whose default
branch moves only on release. BMAD-METHOD itself never carries a skills/
tree, so the old value could never resolve.
The branch parked the npx setup payload in bmad-help and later moved it
to the bmad skill, leaving SKILL.md routing setup and update to files
that no longer exist. bmad-help is legacy-installer surface: this branch
leaves it byte-identical to main and the packager already excludes it.
Add two deterministic runtime modes to the bmad skill. Update inspects
every installed manifest copy, compares each against its declared source,
and reports state per module without writing anything. Doctor repairs an
existing _bmad: it asks only newly declared questions, refreshes the
shared scripts as an exact copy, and makes each selected module's script
tree match its declaration, choosing the unique highest orderable release
when copies disagree and blocking the module otherwise.
Add the canonical bmad entrypoint, exclude bmad-help from flattened
output, and retain transitional CSV packaging. Move the setup tests,
allow the canonical root ID in validation, and normalize the existing
install-only custom path for strict reference checks.
A second bmad-help setup now fills new team-config and module-yaml
keys, replaces the baked catalog, and repairs a wrong or stale
scripts path. Existing custom/ and *.user.toml stay untouched.
First-run setup copies authored team and user templates instead of
walking module.yaml, and reads the three user answers from a sidecar
so the skill route does not put them on the shell.
Read package steps top-down, assemble bmad-help.csv through the
stdlib csv module so quoted descriptions stay intact, and assert
bad, empty, or short catalogs fail without touching dest.
Walk core+bmm skill sources into a skills/<canonical-id>/ tree and
fatten only dest bmad-help with the shared Python, module.yaml defaults,
and a baked core+bmm help catalog.
Build now ends with a one or two sentence summary and a one-line offer
of next steps: create a PR, use bmad-walkthrough, or make another
change. The open_spec default is empty, so no editor opens unless
customized, and Build no longer appends a Suggested Review Order to the
spec; bmad-walkthrough generates a trail in conversation when the spec
has none.
Rename the in-session route to oneshot to match the step name, and
rewrite step-oneshot in plain English. Guard sprint-status updates at
the call sites so untracked work skips the sub-step, and shorten the
sync instruction while keeping its edge cases. Resume in-progress
oneshot specs on the oneshot route instead of dispatch, which expects a
Code Map and Tasks the oneshot spec does not have.
Drop the negative code -r renderer assertions, which only pinned the
config default; the sentinel override test still covers open_spec
substitution.
Rework the walkthrough doc to open with the comprehension order a
reader should follow and why a raw diff fails at it, frame "when to
use" around understanding a change and deciding whether to ship, move
the human-versus-agentic review note into an admonition, and drop the
Review Trail section.
Replace the flat "find at least ten issues" with a floor the hunter
computes from the content it already reads: N = min(floor(sqrt(kB) + 1),
10) over the staged diff's size (the oneshot layer computes it from the
changed content's size), stating the arithmetic in one line so the floor
is auditable. The flat ten over-asks small diffs — a 2 kB change owes 3
findings, not 10 — and the cap keeps today's demand on large diffs,
where the constant already binds.
Benchmarked as an isolated-hunter A/B on a three-commit size ladder
(1.65/10.1/30.4 kB, 3 runs per arm per commit): the scaled floor
self-computes correctly in 9/9 runs, cuts hunter volume ~40% and cost
16-31% at the two lower tiers, keeps every Minor-or-worse finding at
those tiers (union recall loss is Nit-tier only), and matches the flat
quota at the cap.
The adversarial lens in the core bmad-review skill keeps its flat floor;
it reviews arbitrary content where a byte-size denominator is untested.
The catalog had drifted from the code it describes. Corrections, each
verified against src/ and src/scripts/:
- REF-01 cited a `config.yaml` config source. The central config is
`_bmad/config.toml` plus its user/custom overlays (config_utils.py).
- REF-01's double-curly exception was stated for all `{{...}}`, which
contradicted TPL-01. The two forms differ: plain `{{name}}` is an
artifact placeholder that survives rendering, while dotted `{{.key}}`
and `{{config.key}}` are compile-time substitutions consumed by
render_skill.py. Split the exception by form.
- PATH-05 told authors to replace cross-skill file paths with
`skill:skill-name`, while REF-03 forbade that prefix. Prose uses
invoke language; `skill:` is the customize.toml value form. Recorded
the split as a REF-03 exception.
- WF-03 governed workflow.md frontmatter variables. Neither of the two
workflow.md files carries frontmatter, and no WF-01/WF-02 exist.
Removed.
- SKILL-04's stated regex rejected the bare name `bmad`, which
validate_skills.py accepts. Matched it to the implementation.
- Replaced dead example paths (`core/data`, `bmm/data`,
`core/workflows`) and `{installed_path}` examples already covered by
PATH-02.
Drop STEP-02 and STEP-03: step goal sections and next-step references
are structure, and the validator no longer takes a position on it.
STEP-04 and STEP-05 stay, being execution safety rather than shape.
Add a Token Forms table and a Skill Layouts section so the token
grammar and the three coexisting step layouts are stated once, and
extend REF-01 to cover `{workflow.key}`, `{agent.key}`, and
`[[bmad-snapshot:]]`. Compile-time tokens are predicated on the skill
actually invoking render_skill.py, which only bmad-build and
bmad-build-auto do.
Details the catalog now states precisely: render_skill.py substitutes
`{workflow.key}` but not `{agent.key}`, whose only source is
resolve_customization.py output; a `[[bmad-snapshot:]]` target must be
a Markdown file other than SKILL.md, which _load_sources excludes;
workflow.md is the renderer's entry point, not a property of any
layout; config keys are declared in module.yaml, which exists in a
checkout, rather than the installed config.toml, which does not; and
`{project-root}` and `{skill-root}` are named as sources so REF-01
does not flag the two most common tokens in the corpus. SKILL-04
claims no character limit, since NAME_REGEX does not bound length and
a passing deterministic rule is skipped by the inference pass.
Drop the Skill Spec Cheatsheet. Its path-resolution notes duplicated
PATH-01 and the rest (loading levels, 500-line tip, discovery
keywords) was read on every run and used by no rule; the intro now
links the specification instead. The How to Use steps no longer
restate the first-pass skip rule, and backslash-escaped backticks
inside code spans are replaced with double-backtick spans so they
render.
Verified across all 50 skills: every `{workflow.*}` and `{agent.*}`
resolves in its own customize.toml, every snapshot target exists, and
no unrendered skill uses a compile-time token.
The verification-gap layer's evidence rules already make it read the
tests and run the searches behind every gap claim, and triage then
re-derived that same evidence from a colder position. Split by claim
type instead: a gap finding is self-certified — the block must stand on
its own evidence and now carries a required disposition (patch naming
the test to add, or defer with why) in place of the optional suggested
test shape — while a claim that code misbehaves is a defect and goes
under Other findings for standard triage, however the layer found it.
Triage in bmad-build, bmad-build-auto, and bmad-code-review takes a gap
finding as pre-verified: skip verification, render the verdict from the
filed evidence, weigh the filed disposition when routing. Other
findings from that layer are verified like everything else.
In A/B forks of a warmed triage session, self-certification held layer
cost flat and cut parent triage from 109K to 77K real tokens with no
recall loss against the judged finding set.
The patch route told the orchestrator to describe each fix to the
step-03 implementation subagent in its own words. Composed fresh every
run, that message drifts: it grows into a lecture, and nothing stops
the subagent from re-running the full verification it was taught in
step-03 — which step-04 then runs again itself.
Now the message is fixed text in the step file, filled in with the
findings. It tells the patcher to make the smallest change that does
the job, run only the tests covering the files it edits, and return —
full verification stays on the orchestrator's side.
Two more sentences on the orchestrator: re-engage means continuing the
same subagent by the name or id its launch returned — a fresh launch is
not re-engagement; and after patching, rewrite the diff file to match
the patched tree, so nothing later in the pass reads a stale diff.
Adapted from #2805 by pbean.
Co-authored-by: pbean <paulbeanjr@gmail.com>
* feat(build): carry forward triage verdicts on re-review
A second look at the same work — a loopback after failed layers, a
follow-up pass on a done spec — makes the review layers re-report the
same findings, and triage pays full price to re-verify each one, then
re-defers items already in the ledger.
Now triage checks each finding against the existing Review Triage Log
first. Same location, same claim, and the code there still reads as the
row describes: the finding keeps the recorded verdict and route, its row
is written again marked carried — no re-verification, no second patch,
no duplicate defer. Anything else is verified as before, and a code
change at the cited spot voids the match.
Applies to bmad-build and bmad-build-auto, the flows where re-review is
part of the loop. Standalone bmad-code-review is left out: it usually
runs without a spec, where nothing is persisted between runs, so there
is nothing to carry.
Adapted from #2805 by pbean.
Co-authored-by: pbean <paulbeanjr@gmail.com>
* chore(code-review): curlies only where a value expands
The step files mixed two spellings of the same variable: {spec_file}
and spec_file, with no rule for which appears where. Convention now:
curly braces mean "substitute the value here" (paths in commands,
printed values); a bare name refers to the variable itself
(assignments, clears, set/unset and equality tests). Also drop the
decorative quotes around mode literals: full and no-spec are values,
not strings in any language.
No behavior change intended.
---------
Co-authored-by: pbean <paulbeanjr@gmail.com>
Re-running the skill against a done spec starts a follow-up review, but
step-04 grades its recommendation by the first-pass rule, so a pass that
patched two mediums recommends yet another pass and the loop keeps
scheduling reviews of work that has converged.
Step-01 now sets followup_pass when it routes a done spec, and step-04
splits the rule: a first pass recommends a follow-up for a patched high
or two patched mediums, a follow-up pass only for a patched high — and
any true recommendation must name the specific unverified risk, or it
is false. Patch volume is never grounds.
Adapted from #2805 (2bafb3fb) by pbean, minus that branch's
carry-forward machinery.
Co-authored-by: pbean <paulbeanjr@gmail.com>
Replaces tools/validate-skills.js with tools/validate_skills.py, taken from the
flat-tree work on feat/npx-skills-distribution rather than written afresh, so
the two do not diverge again. Discovery is repointed from that branch's flat
skills/ tree to src/. Behaviour is otherwise unchanged: same rule IDs, same
JSON shape, same --strict and --json flags, and the tests come across as
unittest cases run through uv, matching how the other Python tests are wired.
The three step-structure rules go away with it. STEP-01 (filename format),
STEP-06 (step frontmatter) and STEP-07 (step count) all key off a literal
steps/ directory that only two of fifty skills have — everywhere else step
files sit at the skill root — so they validated almost nothing. The catalog's
first-pass list drops from 13 rules to 10 to match, and their rule sections
are removed.
CI is unaffected: quality.yaml already provisions uv and invokes the npm
script rather than the file.
* chore(review): swap Augment for Greptile and repair CodeRabbit config
Augment is no longer in use, so its guidelines file goes away in favour of
greptile.json. Rather than paraphrasing the repo's conventions, both bots are
now pointed at the same source files: AGENTS.md and tools/skill-validator.md,
plus docs/_STYLE_GUIDE.md scoped to docs.
Four CodeRabbit path instructions had gone stale and matched no tracked files:
src/**/skills/**, src/**/workflows/**, src/**/tasks/** and src/**/*.agent.yaml.
The skill-validator catalog was consequently applied to nothing. Retarget them
at src/*-skills/** and src/bmm-skills/agents/**, and drop the two describing
layouts the repo no longer has. Three further fixes: `walkthrough: false` is
not a schema v2 key, so use collapse_walkthrough; the media filters were
root-only globs and left docs/images under review; and the validation-report
filter named .md where those files are .html.
AGENTS.md gains the prompt-length and testing conventions, which were until
now written down nowhere in the repo. CLAUDE.md is unignored and imports it.
* chore(review): align catalog scoping and narrow the ignore lists
Greptile's global instructions named tools/skill-validator.md and
docs/_STYLE_GUIDE.md, but customContext.files supplies those only for skill and
documentation paths. Reviewing anything else, Greptile was told to apply
catalogs it had not been given — an invitation to invent rules from the
filenames. The global clause now names only AGENTS.md, which is loaded on every
review; the two scoped catalogs keep their scopes and their own descriptions.
website/** was excluding 18 hand-written source files along with the assets —
the Astro components and pages, both rehype plugins, the locale and site-url
helpers, the i18n JSON. Exclude website/public/** and website/diagrams/**
instead, so generated and editor-authored artifacts stay out while the source
is reviewed.
tools/tests/fixtures/** joins test/fixtures/** ahead of the validator port that
moves the skill fixtures there. Those fixtures encode defects deliberately, so
any review comment on them is wrong by construction.
Today every all-maybe-false entry routes to defer, so the deferred-work
ledger accumulates low-stakes hypotheticals that later sweeps read back
as work. Dropping the bucket wholesale would go too far the other way:
maybe-false means triage could not decide, and the claims that would be
serious if true are exactly the ones worth a durable record.
So gate the route on what the claim would be if true: medium or high
defers, with that severity marked unverified and what would settle it
as the evidence; low rejects, keeping the same note in the triage log.
Applied at the four triage sites (bmad-build step-04 and oneshot,
bmad-build-auto step-04, bmad-code-review step-03), the build-auto
deferred-list severity comment, and autonomous-development-loops.md.
Private vulnerability reporting is now enabled on the repo, so the
advisory link in this policy resolves. Adds security@bmadcode.com as a
second channel, replaces the unmet response SLA with best-effort
language, and adds a Security Model section so scope decisions rest on a
stated trust boundary rather than case-by-case argument.
Claude-Session: https://claude.ai/code/session_014nrWo411353cGjRBpMLpfV
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(customization): resolve the project root from the working directory
resolve_customization.py inferred the project root by walking up from the
skill's installed directory. For a skill installed under the user's home,
that walk reaches ~ — and when a user-level install has put a ~/_bmad
there, the resolver treats home as the project, finds no override, and
returns shipped defaults. The real project's _bmad/custom/ file is never
opened, with no error and no warning.
Whether an override takes effect therefore depended on where its skill
happened to be installed, which the override's author cannot see from the
override. Every harness installs global skills under ~/<tool>/skills, so
this hits all of them identically.
Resolve from an ordered candidate list instead: the working directory
first (the project is where the user works, not where the skill lives),
then the script's own install path (skills invoke it as
{project-root}/_bmad/scripts/..., so its grandparent is a root the caller
already resolved), then the skill directory as a last resort.
Rank _bmad/ above .git at every depth while walking. A submodule or
nested repo carries .git without being the BMad project, so treating the
two as equal stopped the walk short of the root owning _bmad/custom/ —
the same silent failure, reachable by project-installed skills too.
Break the silence: when the chosen root has no override for the skill but
a rejected candidate does, write a note to stderr naming both roots.
stdout stays pure JSON.
Pass --project-root from all 44 skill and doc invocation sites, matching
resolve_config.py, which already requires it. That asymmetry was the root
cause; with the flag passed there is nothing left to infer.
Fixes#2796
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8Hyqmp2giAVgEAnB49zEQ
* fix(customization): pass --project-root from the party and forge wrappers
resolve_party.py and resolve_personas.py shell out to the customization
resolver with --skill only, though both already hold the project root and
both pass it to resolve_config.py two functions earlier. The resolver
therefore had to infer a root, and since these wrappers capture stderr
and discard it, the note about a masked override went nowhere.
Under the old skill-directory-first inference this happened to land on
the right root for a project-installed skill; under working-directory
-first it takes the ambient root instead, which is wrong whenever the
party runs from a nested project or an unrelated worktree. Passing the
flag removes the inference for both paths rather than trading one wrong
guess for another.
Cover each wrapper with a test that captures the resolver command and
asserts the flag carries the project root it was given.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8Hyqmp2giAVgEAnB49zEQ
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two path-analysis bullets, added to all four copies of the prompt
(bmad-build, bmad-build-auto, bmad-code-review, and the bmad-review
lens):
- Handle lifetime: a re-check of something the code already held exists
because an intervening call can invalidate it — identify that call,
what it does to the thing held, and what is silently skipped when the
re-check fails. Targets use-after-invalidation bugs the hunter's
branch walk slides past because the guarded exit reads as handled.
- Call vs declaration: for each call site the diff adds or changes,
tests included, check the call against the callee's declaration.
Gives arity/type mismatches an owning layer; previously no layer was
chartered to notice a call that cannot compile.