Per-release upgrade notes. Newest first. Each section is self-contained; skip to the version you are migrating from.
Who this affects: installs on post-#2285 BMAD, where the canonical config is
_bmad/config.toml (resolved by _bmad/scripts/resolve_config.py across four
layers) and _bmad/config.yaml is legacy. Before this change PULSE read/wrote
only the yaml, so it never saw config.toml nor the custom/config.toml
overrides — a split-brain where the values diverged and PULSE stayed on the
legacy file.
What changed:
- Consumers resolve toml-first. Every workflow/agent now runs
resolve_config.py --key modules.pulse, readspulse_*from the resolved toml, falls back per key to the legacypulse:section ofconfig.yaml, and uses themodule.yamldefault only when neither has the key. - Setup writes toml.
merge-config.pywrites the module section to_bmad/custom/config.tomlunder[modules.pulse](the layer that wins over installer defaults), preserving that file's comments and other sections. It no longer writes thepulse:section intoconfig.yamland strips a stale one on run.
⚠ Caveat crítico (pareamento obrigatório). The [modules.pulse] in
config.toml on a post-#2285 install carries the install defaults
(pulse_dev_categories = "standard_4", pulse_estimation_method = "hours"),
which do not match a team using a custom taxonomy (e.g.
frontend, backend, fullstack, mobile, security) or bcp. If PULSE goes
toml-first without the team's real values pinned in custom/config.toml,
it silently regresses to the install defaults and loses the custom taxonomy.
How to migrate (re-run setup): re-running bmad-pulse-setup is the
migration. It reads the current effective values (resolved toml, then the legacy
pulse: in config.yaml) and offers them as the prompt defaults; accepting the
defaults pins the team's answers into custom/config.toml [modules.pulse] and
strips the legacy yaml section. Verify pulse_dev_categories and
pulse_estimation_method in custom/config.toml after migrating.
This applies to every PULSE upgrade, not a specific version.
The upstream BMAD installer performs an additive deploy when PULSE is
already installed in a project: brand-new files are copied, but
pre-existing files (module.yaml, SKILL.md, …) are not overwritten
and renamed/removed skills are not pruned. Re-running
npx bmad-method install over an existing install therefore leaves a
mixed state frozen at the old version — new files present, old files
stale, orphan folders lingering (issue #36).
bmad-pulse-setup now self-heals this. Its first step,
reconcile-skills.py, force-syncs every PULSE skill directory from the
authoritative source (the freshly-fetched BMAD custom-module cache) and
prunes orphaned renamed folders. It is idempotent (no-op when already in
sync) and non-fatal on fresh installs.
# 1. Fetch the new PULSE version (refreshes the BMAD custom-module cache)
npx bmad-method install --custom-source https://github.com/nidelson/bmad-module-pulse
# 2. Re-run setup — reconcile converges the deployed tree automatically
/bmad-pulse-setupIf the deployed bmad-pulse-setup skill itself was stale, the first run
reconciles it in place; the JSON notice will ask you to run
/bmad-pulse-setup once more so the current version executes. Re-run it
once and you are converged.
Production tip — pin a released tag. Installing with the bare
--custom-source tracks main (version: main in the manifest), so
every re-run pulls whatever has since landed there. For deterministic,
team-reproducible installs, pin an explicit tag and bump it deliberately:
npx bmad-method install \
--custom-source https://github.com/nidelson/bmad-module-pulse \
--version v0.4.5Reconcile still runs on every /bmad-pulse-setup and converges the
deployed tree to whatever the cache holds — pinning just makes what the
cache holds deterministic.
Manual reconcile (e.g. for a bug report) — --dry-run previews without
writing:
python3 .claude/skills/bmad-pulse-setup/scripts/reconcile-skills.py \
--project-root . --dry-runQuem isto afeta: quem usa a seção de previsão do dashboard com BCP. Breaking numa seção: a antiga Previsão de Capacidade (extrapolação por leverage) foi substituída por Previsão de Projeto (BCP × h/BCP ± IC 90%).
A v0.8 vira a engine honesta pra frente: dado o backlog pontuado restante, prevê quantas horas falta — com intervalo de confiança de 90% — pra times que faturam por hora.
- Previsão de Projeto (substitui Previsão de Capacidade). PULSE enumera (read-only) as stories pontuadas não-iniciadas (com
bcp.total, sem entrada empulse_metrics) → BCP restante por categoria, e prevêhoras = BCP × h/BCP(geométrico, calibrado da v0.5) com IC de 90% (escala a banda de confiança de k=1 ~68% pra k=1.645 ~90%). O total soma os limites por categoria (faixa conservadora, declarada). Categorias finas (n<3) usam baseline pooled e marcam baixa confiança. Backlog vazio → sem seção. - Digest (
digest.md). Um resumo conciso (previsibilidade + forecast + coortes em risco) é gerado junto do dashboard. Entrega thin: PULSE não chama Slack/Linear direto — você apontaon_completepra um comando que posta odigest.md(ex.: webhook Slack viacurl). Credenciais ficam no seu ambiente. - Quebra por desenvolvedor/agente: adiada — PULSE não captura identidade hoje (só
dev_count); fica pra um marco futuro com decisão de privacidade à parte. A v0.8 quebra por categoria (que já existe).
Nada no disco além do upgrade normal. O forecast usa dados que PULSE já lê + os story files pontuados (read-only). Se o backlog não for enumerável, informe um total manual em pulse_forecast_remaining_bcp. Pra digests, configure on_complete.
⚠ Breaking: a seção
🔮 Previsão de Capacidadedeixou de existir; quem parseava ela (ou dependia da extrapolação por leverage) deve migrar pra🔮 Previsão de Projeto. As demais seções não mudaram de formato.
Quem isto afeta: quem usa /bmad-pulse-track-start e o dashboard. Aditivo — nada existente muda de formato; a v0.7 só acrescenta um aviso e uma seção.
A v0.5/v0.6 são retrospectivas (você vê o drift depois que a story fechou). A v0.7 leva o sinal pro momento do compromisso: ao iniciar uma story, PULSE avisa se a coorte dela vem errando a estimativa — antes de virar promessa. PULSE segue passivo: o alerta informa, nunca dirige.
- Alerta no track-start. Ao registrar o start, PULSE computa o drift recente da coorte da story (
category+ segmento de tamanho quando há BCP) e, se as últimas 5 erraram > 25% na mediana (mínimo 3 stories), mostra uma linha não-bloqueante:⚠ Heads up: stories like backend/story were off +N% (median) over the last 5 — re-estimate before committing?. O start é registrado normalmente; PULSE nunca alteraestimated_hours— reestimar é decisão sua. - Watch-list no dashboard. Nova seção
🚦 Estimation drift watchlista as coortes que estão estimando mal agora (mediana > 25%, ≥3 stories), ordenadas por drift, com tendência. Coortes saudáveis são omitidas; tudo saudável → "No cohorts drifting — estimates are tracking".
Nada. Aditivo, sem migração de dados. Próximo /bmad-pulse-track-start e /bmad-pulse-dashboard já trazem o aviso e a seção. Os thresholds (K=5, T=25%) são inline nesta versão.
ℹ️ Não-breaking. Nenhum formato existente foi reordenado ou removido; só houve acréscimos. O alerta é advisory e nunca toca a sua estimativa.
Quem isto afeta: todos os usuários do dashboard e do track-done; mais profundamente quem usa pulse_estimation_method=bcp. Nada na coleta de dados muda — a virada é em como os números são enquadrados.
A v0.6 age sobre a engine honesta da v0.5: troca a métrica-herói de alavancagem → previsibilidade/acurácia. Um ~1.0x estável é saudável; um multiplicador alto sinaliza estimativa inflada, não velocidade. PULSE segue passivo e zero-write-coupled ao bmad-module-bcp.
- Métrica-herói invertida. O
General Statisticsdo dashboard agora lidera comPredictability(mediana do erro de estimativa por story|actual−estimated|/estimated, menor = melhor, com seta de tendência) e demoveAvg AI Leveragepra uma linha de contextoAI Leverage (vs PLAN) — context, not a target. - Sinal de convergência. Nova seção "Baseline convergence" mostra se o
h/BCPestá estabilizando (mediana de|drift|da 1ª vs 2ª metade + banda estreitando). Precisa de ≥4 stories BCP. - Detecção de regime. PULSE passa a ler
estimated_hours_basis(read-only) pra rotular cada multiplicador pela base (vs PLAN (bcp),vs PLAN (hours), …), com fallback propulse_estimation_method. Nunca escreve o campo, nunca deriva horas dele. - Celebração invertida. O
track-done(etrack-backfill) param de premiar leverage alto (🔥 Exceptional!aposentado). O gatilho agora é acurácia:🎯 On-planquando a estimativa erra ≤15%;⚠ Off-plan — review the estimate basisquando erra ≥50%. Ospulse_leverage_threshold_*ficam nomodule.yamlpor back-compat, mas não disparam mais celebração.
Nada no disco além do upgrade normal (/bmad-pulse-setup). A mudança é nos workflows; o próximo /bmad-pulse-dashboard e /bmad-pulse-track-done já renderizam o novo enquadramento. Sem migração de dados.
⚠ Breaking para parsers e para quem dependia do troféu de leverage. O
General Statisticsfoi reordenado, colunas de leverage ganharam "(vs PLAN)" e a celebração do track-done mudou de gatilho. Se você scrapeia o dashboard ou automatiza em cima do "🔥 Exceptional", atualize. O número de leverage continua visível, só sem troféu.
Who this affects: only projects using pulse_estimation_method=bcp. If you
do not use BCP, the dashboard is unchanged — skip this section.
v0.5.0 reworks how the dashboard summarizes per-category h/BCP so the number
is honest rather than flattering. Nothing about tracking, frontmatter, or the
BCP module boundary changes — PULSE stays passive and zero-coupled to
bmad-module-bcp (it still only reads bcp.*, never writes a baseline). The
shift is entirely in how the BCP Productivity section is computed and
rendered.
- Geometric mean, not arithmetic. Per-category h/BCP baselines are now the
geometric mean of the per-story ratios (
exp(mean(ln(ratio)))). h/BCP is a multiplicative ratio, so the arithmetic mean was biased high and fragile to a single outlier. Baselines recomputed from the same data will shift — this is the intended correction, not a regression. - Micro vs story-size segmentation. Stories are split at the observed
median BCP total (
microbelow,storyat/above) and each segment gets its own baseline, so a 1-point fix no longer pollutes a 40-point story's number. A pooledallrow remains for continuity. The split is data-driven — PULSE makes no assumption about your BCP point scale and there is no new config key. - Confidence band, not a point. Each baseline now shows a typical range
[low–high](geometric standard deviation, ~68%) plus the sample sizen. Ranges appear only atn >= 3; thinner samples show the bare point. - Leverage reframed (anti-Goodhart). A new dashboard note states that
leverage (
estimated_hours / actual_hours) collapses to ~1.0x once the estimate basis is calibrated — so a high multiplier signals an uncalibrated estimate, not velocity, and the durable signal is predictability (h/BCP drift converging on zero). The hero-metric inversion itself lands in v0.6; v0.5 only locks the invariant.
Nothing on disk beyond a normal upgrade (/bmad-pulse-setup). The change is
in the dashboard workflow; the next /bmad-pulse-dashboard run renders the new
shape. There is no data migration — historical pulse_metrics are re-read and
re-summarized in place.
⚠ Breaking only for tooling that parses the dashboard markdown. The BCP Productivity table gained
Segmentandncolumns and the h/BCP cell now carries a[low–high]range. If you scrape the dashboard, update your parser. The human-readable dashboard and all non-BCP sections are unaffected.
If bmad-pulse-track-start was not firing automatically at the start of
your stories (issue #47), the cause was the auto-tracking trigger living in
the persistent_facts array of _bmad/custom/bmad-dev-story.toml.
persistent_facts is passive context the agent merely carries — it did not
deterministically execute. The fix moves the trigger to
activation_steps_append (an executed activation step) and decouples it from
the sprint-status in-progress field.
The fix ships in the PULSE source, but existing installs keep the old
override file: bmad-pulse-setup aborts rather than overwrite a file you
may have customized. To pick up the fix, regenerate it:
# 1. Pull the new PULSE version
npx bmad-method install --custom-source https://github.com/nidelson/bmad-module-pulse
# 2. Remove the stale override (or back it up if you customized it)
rm _bmad/custom/bmad-dev-story.toml
# 3. Re-run setup — it re-emits the override with the activation-step trigger
/bmad-pulse-setupAlternatively, re-run setup and let it overwrite via --force (passed through
to inject_customize.py). bmad-code-review.toml (track-done) is unchanged —
only the bmad-dev-story override needs regeneration.
v0.4.5 consolidates the Levi agent into a single canonical skill folder
and aligns it with the bmad ecosystem convention (bmad-agent-{module-code}).
This closes a duplication that emerged on BMAD v6.6.0 installs, where the
core installer auto-provisioned bmad-agent-pulse/ alongside the legacy
bmad-pulse-agent-levi/ shipped by previous PULSE versions, producing two
divergent entry points for the same agent.
| Before (v0.4.4) | After (v0.4.5) |
|---|---|
skills/bmad-pulse-agent-levi/ in PULSE source |
skills/bmad-agent-pulse/ in PULSE source |
levi.agent.yaml + bmad-skill-manifest.yaml sidecars |
Persona inlined in SKILL.md, sidecars dropped |
.claude/skills/bmad-pulse-agent-levi/ AND .claude/skills/bmad-agent-pulse/ after install (duplicate) |
.claude/skills/bmad-agent-pulse/ only (single canonical) |
| Persona text in PT-BR (in some installs) | Persona text in EN at source; runtime language follows communication_language in _bmad/config.yaml |
The slash command for talking to Levi remains the same:
/bmad-agent-pulse
If you previously typed /bmad-pulse-agent-levi, that command no longer
exists after the upgrade. Switch to /bmad-agent-pulse.
# 1. Pull the new PULSE version
npx bmad-method install --custom-source https://github.com/nidelson/bmad-module-pulse
# 2. Re-run setup — the cleanup step removes the legacy folder automatically
/bmad-pulse-setupbmad-pulse-setup now invokes a new cleanup step that removes
.claude/skills/bmad-pulse-agent-levi/ when the canonical
.claude/skills/bmad-agent-pulse/ is present. The step is idempotent and
safety-checked: it refuses to delete the legacy folder if the canonical
replacement is missing, so a partial install can never strand you
without a Levi agent.
If you prefer to clean up manually:
# Verify the canonical folder exists first
ls .claude/skills/bmad-agent-pulse/SKILL.md
# Then remove the legacy folder
rm -rf .claude/skills/bmad-pulse-agent-levi/The shipped persona text is now EN to match the public distribution.
Runtime language continues to flow from communication_language in
_bmad/config.yaml — set it to Português do Brasil (or any other
language) and Levi will greet you and reason in that language.
If your project previously contained hand-edited PT-BR text inside
.claude/skills/bmad-agent-pulse/SKILL.md, the upgrade will overwrite
it with the new EN source. Move any customizations into
_bmad/custom/bmad-agent-pulse.toml (forthcoming in #31) so they
survive future PULSE upgrades.
If /bmad-pulse-setup fails to clean up the legacy folder, run the
cleanup mode standalone and attach the JSON output to a bug report:
python3 .claude/skills/bmad-pulse-setup/scripts/cleanup-legacy.py \
--remove-legacy-agent \
--project-root .PULSE v0.4.0 ships two breaking changes in a single release:
- Drops support for BMAD <6.4.0 — auto-tracking is now wired through
BMAD v6.4.0's
customize.tomlframework instead ofworkflow.mdmarkers. After upgrading, PULSE auto-tracking will survive future BMAD core upgrades transparently. - Skill folders and slash commands renamed from
pulse-*tobmad-pulse-*to align with the most recent BMAD ecosystem reference pattern. Re-running/bmad-pulse-setupis sufficient — oldpulse-*folders can be deleted manually after the upgrade.
This is a one-time migration.
| Before (v0.3.x) | After (v0.4.0) |
|---|---|
/pulse-setup |
/bmad-pulse-setup |
/pulse-track-start |
/bmad-pulse-track-start |
/pulse-track-done |
/bmad-pulse-track-done |
/pulse-dashboard |
/bmad-pulse-dashboard |
# 1. Make sure BMAD core is on v6.4.0 or higher
npx bmad-method install
# 2. Upgrade PULSE
npx bmad-method install --custom-source https://github.com/nidelson/bmad-module-pulse
# 3. Re-run bmad-pulse-setup
/bmad-pulse-setup
# 4. Append the printed snippet to your .gitignore (manual)
# 5. Commit the new files
git add _bmad/custom/bmad-dev-story.toml _bmad/custom/bmad-code-review.toml .gitignore
git commit -m "chore(pulse): migrate to v0.4.0 customize.toml integration"| Before (v0.3.x) | After (v0.4.0) |
|---|---|
<!-- PULSE:auto-inject --> markers in .claude/skills/bmad-dev-story/workflow.md |
_bmad/custom/bmad-dev-story.toml (track-start auto-trigger) |
Track-done injected at end of bmad-dev-story workflow |
_bmad/custom/bmad-code-review.toml (track-done in on_complete) |
| Auto-tracking broken silently on every BMAD ≥6.4.0 install | Auto-tracking survives BMAD core upgrades |
- Capability check — verifies BMAD ≥6.4.0 is installed; aborts with a clear message otherwise.
- Legacy cleanup — removes any
<!-- PULSE:auto-inject -->blocks fromworkflow.mdif it still exists. - Emits two override files —
_bmad/custom/bmad-dev-story.tomland_bmad/custom/bmad-code-review.toml. - Prints
.gitignoresnippet — read-only; you copy-paste it manually.
If _bmad/custom/bmad-dev-story.toml or _bmad/custom/bmad-code-review.toml
already exists in your project (e.g. you customized it), bmad-pulse-setup
aborts with a message naming the file. Choose one:
- Keep your version — do nothing, ignore the abort, your file is untouched.
- Restore PULSE defaults — re-run with
--force(passed through to the inject script). This overwrites your customization. - Merge manually — diff against the template at
node_modules/.../bmad-module-pulse/skills/bmad-pulse-setup/assets/customize-templates/<skill>.toml, apply the PULSE bits to your file, then move on.
The conflict policy guarantees byte-stability: without --force, the file
on disk has identical sha256 before and after the failed run.
In v0.3.x, track-done was injected at the end of bmad-dev-story. But
bmad-dev-story ends with story status review, not done. Recording
completion at that point produced premature metrics (review cycles not
yet measured). v0.4.0 places track-done on bmad-code-review.on_complete,
which fires after the review is approved and sprint status is synced —
the correct moment.
Open an issue at https://github.com/nidelson/bmad-module-pulse/issues with:
- BMAD version (
cat _bmad/_config/files-manifest.csv | head -1) - Output of
python3 .claude/skills/bmad-pulse-setup/scripts/detect_bmad_capability.py --project-root . - Contents of
_bmad/custom/(if any)