@luanpdd/kit-mcp 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +648 -648
  3. package/kit/COMANDOS.md +138 -138
  4. package/kit/README.md +52 -52
  5. package/kit/agents/advisor-researcher.md +106 -106
  6. package/kit/agents/assumptions-analyzer.md +107 -107
  7. package/kit/agents/codebase-mapper.md +768 -768
  8. package/kit/agents/debugger.md +772 -772
  9. package/kit/agents/example-reviewer.md +21 -21
  10. package/kit/agents/executor.md +523 -523
  11. package/kit/agents/integration-checker.md +200 -200
  12. package/kit/agents/nyquist-auditor.md +178 -178
  13. package/kit/agents/phase-researcher.md +696 -696
  14. package/kit/agents/plan-checker.md +272 -272
  15. package/kit/agents/planner.md +891 -891
  16. package/kit/agents/project-researcher.md +652 -652
  17. package/kit/agents/research-synthesizer.md +245 -245
  18. package/kit/agents/roadmapper.md +677 -677
  19. package/kit/agents/ui-auditor.md +437 -437
  20. package/kit/agents/ui-checker.md +302 -302
  21. package/kit/agents/ui-researcher.md +355 -355
  22. package/kit/agents/user-profiler.md +175 -175
  23. package/kit/agents/verifier.md +728 -728
  24. package/kit/commands/adicionar-backlog.md +75 -75
  25. package/kit/commands/adicionar-fase.md +42 -42
  26. package/kit/commands/adicionar-tarefa.md +45 -45
  27. package/kit/commands/adicionar-testes.md +41 -41
  28. package/kit/commands/ajuda.md +21 -21
  29. package/kit/commands/atualizar.md +37 -37
  30. package/kit/commands/auditar-marco.md +179 -179
  31. package/kit/commands/auditar-uat.md +23 -23
  32. package/kit/commands/autonomo.md +40 -40
  33. package/kit/commands/branch-pr.md +24 -24
  34. package/kit/commands/burn-rate-status.md +338 -70
  35. package/kit/commands/concluir-marco.md +247 -247
  36. package/kit/commands/configuracoes.md +36 -36
  37. package/kit/commands/definir-perfil.md +10 -10
  38. package/kit/commands/depurar.md +190 -190
  39. package/kit/commands/discutir-fase.md +131 -131
  40. package/kit/commands/entrar-discord.md +17 -17
  41. package/kit/commands/estatisticas.md +18 -18
  42. package/kit/commands/example-greeting.md +33 -33
  43. package/kit/commands/executar-fase.md +58 -58
  44. package/kit/commands/expresso.md +56 -56
  45. package/kit/commands/fase-ui.md +34 -34
  46. package/kit/commands/fazer.md +57 -57
  47. package/kit/commands/fio.md +125 -125
  48. package/kit/commands/fluxos-trabalho.md +64 -64
  49. package/kit/commands/forense.md +176 -176
  50. package/kit/commands/gerenciador.md +38 -38
  51. package/kit/commands/inserir-fase.md +31 -31
  52. package/kit/commands/limpeza.md +17 -17
  53. package/kit/commands/listar-hipoteses-fase.md +45 -45
  54. package/kit/commands/listar-workspaces.md +18 -18
  55. package/kit/commands/mapear-codebase.md +70 -70
  56. package/kit/commands/nota.md +33 -33
  57. package/kit/commands/novo-marco.md +43 -43
  58. package/kit/commands/novo-projeto.md +41 -41
  59. package/kit/commands/novo-workspace.md +43 -43
  60. package/kit/commands/pausar-trabalho.md +37 -37
  61. package/kit/commands/perfil-usuario.md +45 -45
  62. package/kit/commands/pesquisar-fase.md +195 -195
  63. package/kit/commands/planejar-fase.md +67 -67
  64. package/kit/commands/planejar-lacunas.md +33 -33
  65. package/kit/commands/plantar-ideia.md +25 -25
  66. package/kit/commands/progresso.md +24 -24
  67. package/kit/commands/proximo.md +30 -30
  68. package/kit/commands/publicar.md +490 -490
  69. package/kit/commands/rapido.md +35 -35
  70. package/kit/commands/reaplicar-patches.md +124 -124
  71. package/kit/commands/relatorio-sessao.md +19 -19
  72. package/kit/commands/remover-fase.md +31 -31
  73. package/kit/commands/remover-workspace.md +26 -26
  74. package/kit/commands/resumo-marco.md +50 -50
  75. package/kit/commands/retomar-trabalho.md +40 -40
  76. package/kit/commands/revisar-backlog.md +60 -60
  77. package/kit/commands/revisar-ui.md +32 -32
  78. package/kit/commands/revisar.md +37 -37
  79. package/kit/commands/saude.md +21 -21
  80. package/kit/commands/setup-notion.md +93 -93
  81. package/kit/commands/sync-main.md +68 -68
  82. package/kit/commands/validar-fase.md +35 -35
  83. package/kit/commands/verificar-tarefas.md +44 -44
  84. package/kit/commands/verificar-trabalho.md +64 -64
  85. package/kit/file-manifest.json +3 -3
  86. package/kit/framework/bin/lib/commands.cjs +959 -959
  87. package/kit/framework/bin/lib/config.cjs +442 -442
  88. package/kit/framework/bin/lib/core.cjs +1230 -1230
  89. package/kit/framework/bin/lib/frontmatter.cjs +336 -336
  90. package/kit/framework/bin/lib/init.cjs +1442 -1442
  91. package/kit/framework/bin/lib/milestone.cjs +252 -252
  92. package/kit/framework/bin/lib/model-profiles.cjs +68 -68
  93. package/kit/framework/bin/lib/phase.cjs +888 -888
  94. package/kit/framework/bin/lib/profile-output.cjs +952 -952
  95. package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
  96. package/kit/framework/bin/lib/roadmap.cjs +329 -329
  97. package/kit/framework/bin/lib/security.cjs +382 -382
  98. package/kit/framework/bin/lib/state.cjs +1031 -1031
  99. package/kit/framework/bin/lib/template.cjs +222 -222
  100. package/kit/framework/bin/lib/uat.cjs +282 -282
  101. package/kit/framework/bin/lib/verify.cjs +888 -888
  102. package/kit/framework/bin/lib/workstream.cjs +491 -491
  103. package/kit/framework/bin/tools.cjs +918 -918
  104. package/kit/framework/commands/workstreams.md +63 -63
  105. package/kit/framework/references/checkpoints.md +778 -778
  106. package/kit/framework/references/continuation-format.md +249 -249
  107. package/kit/framework/references/decimal-phase-calculation.md +64 -64
  108. package/kit/framework/references/git-integration.md +295 -295
  109. package/kit/framework/references/git-planning-commit.md +38 -38
  110. package/kit/framework/references/model-profile-resolution.md +36 -36
  111. package/kit/framework/references/model-profiles.md +139 -139
  112. package/kit/framework/references/phase-argument-parsing.md +61 -61
  113. package/kit/framework/references/planning-config.md +202 -202
  114. package/kit/framework/references/questioning.md +162 -162
  115. package/kit/framework/references/tdd.md +263 -263
  116. package/kit/framework/references/ui-brand.md +160 -160
  117. package/kit/framework/references/user-profiling.md +657 -657
  118. package/kit/framework/references/verification-patterns.md +612 -612
  119. package/kit/framework/references/workstream-flag.md +58 -58
  120. package/kit/framework/templates/DEBUG.md +164 -164
  121. package/kit/framework/templates/UAT.md +265 -265
  122. package/kit/framework/templates/UI-SPEC.md +100 -100
  123. package/kit/framework/templates/VALIDATION.md +76 -76
  124. package/kit/framework/templates/claude-md.md +122 -122
  125. package/kit/framework/templates/codebase/architecture.md +185 -185
  126. package/kit/framework/templates/codebase/concerns.md +205 -205
  127. package/kit/framework/templates/codebase/conventions.md +204 -204
  128. package/kit/framework/templates/codebase/integrations.md +192 -192
  129. package/kit/framework/templates/codebase/stack.md +158 -158
  130. package/kit/framework/templates/codebase/structure.md +199 -199
  131. package/kit/framework/templates/codebase/testing.md +301 -301
  132. package/kit/framework/templates/config.json +44 -44
  133. package/kit/framework/templates/context.md +352 -352
  134. package/kit/framework/templates/continue-here.md +78 -78
  135. package/kit/framework/templates/copilot-instructions.md +7 -7
  136. package/kit/framework/templates/debug-subagent-prompt.md +91 -91
  137. package/kit/framework/templates/dev-preferences.md +20 -20
  138. package/kit/framework/templates/discovery.md +146 -146
  139. package/kit/framework/templates/discussion-log.md +63 -63
  140. package/kit/framework/templates/milestone-archive.md +123 -123
  141. package/kit/framework/templates/milestone.md +115 -115
  142. package/kit/framework/templates/phase-prompt.md +610 -610
  143. package/kit/framework/templates/planner-subagent-prompt.md +117 -117
  144. package/kit/framework/templates/project.md +186 -186
  145. package/kit/framework/templates/requirements.md +231 -231
  146. package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
  147. package/kit/framework/templates/research-project/FEATURES.md +147 -147
  148. package/kit/framework/templates/research-project/PITFALLS.md +200 -200
  149. package/kit/framework/templates/research-project/STACK.md +120 -120
  150. package/kit/framework/templates/research-project/SUMMARY.md +170 -170
  151. package/kit/framework/templates/research.md +419 -419
  152. package/kit/framework/templates/retrospective.md +54 -54
  153. package/kit/framework/templates/roadmap.md +202 -202
  154. package/kit/framework/templates/state.md +176 -176
  155. package/kit/framework/templates/summary-complex.md +59 -59
  156. package/kit/framework/templates/summary-minimal.md +41 -41
  157. package/kit/framework/templates/summary-standard.md +48 -48
  158. package/kit/framework/templates/summary.md +209 -209
  159. package/kit/framework/templates/user-profile.md +146 -146
  160. package/kit/framework/templates/user-setup.md +256 -256
  161. package/kit/framework/templates/verification-report.md +258 -258
  162. package/kit/framework/workflows/add-phase.md +112 -112
  163. package/kit/framework/workflows/add-tests.md +351 -351
  164. package/kit/framework/workflows/add-todo.md +158 -158
  165. package/kit/framework/workflows/audit-milestone.md +340 -340
  166. package/kit/framework/workflows/audit-uat.md +109 -109
  167. package/kit/framework/workflows/autonomous.md +891 -891
  168. package/kit/framework/workflows/check-todos.md +177 -177
  169. package/kit/framework/workflows/cleanup.md +152 -152
  170. package/kit/framework/workflows/complete-milestone.md +696 -696
  171. package/kit/framework/workflows/diagnose-issues.md +231 -231
  172. package/kit/framework/workflows/discovery-phase.md +289 -289
  173. package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
  174. package/kit/framework/workflows/discuss-phase.md +784 -784
  175. package/kit/framework/workflows/do.md +104 -104
  176. package/kit/framework/workflows/execute-phase.md +838 -838
  177. package/kit/framework/workflows/execute-plan.md +510 -510
  178. package/kit/framework/workflows/fast.md +102 -102
  179. package/kit/framework/workflows/forensics.md +265 -265
  180. package/kit/framework/workflows/health.md +181 -181
  181. package/kit/framework/workflows/help.md +619 -619
  182. package/kit/framework/workflows/insert-phase.md +130 -130
  183. package/kit/framework/workflows/list-phase-assumptions.md +178 -178
  184. package/kit/framework/workflows/list-workspaces.md +56 -56
  185. package/kit/framework/workflows/manager.md +362 -362
  186. package/kit/framework/workflows/map-codebase.md +377 -377
  187. package/kit/framework/workflows/milestone-summary.md +223 -223
  188. package/kit/framework/workflows/new-milestone.md +486 -486
  189. package/kit/framework/workflows/new-project.md +1159 -1159
  190. package/kit/framework/workflows/new-workspace.md +237 -237
  191. package/kit/framework/workflows/next.md +97 -97
  192. package/kit/framework/workflows/node-repair.md +92 -92
  193. package/kit/framework/workflows/note.md +156 -156
  194. package/kit/framework/workflows/pause-work.md +176 -176
  195. package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
  196. package/kit/framework/workflows/plan-phase.md +765 -765
  197. package/kit/framework/workflows/plant-seed.md +169 -169
  198. package/kit/framework/workflows/pr-branch.md +129 -129
  199. package/kit/framework/workflows/profile-user.md +450 -450
  200. package/kit/framework/workflows/progress.md +507 -507
  201. package/kit/framework/workflows/quick.md +757 -757
  202. package/kit/framework/workflows/remove-phase.md +155 -155
  203. package/kit/framework/workflows/remove-workspace.md +90 -90
  204. package/kit/framework/workflows/research-phase.md +82 -82
  205. package/kit/framework/workflows/resume-project.md +326 -326
  206. package/kit/framework/workflows/review.md +228 -228
  207. package/kit/framework/workflows/session-report.md +146 -146
  208. package/kit/framework/workflows/settings.md +283 -283
  209. package/kit/framework/workflows/ship.md +228 -228
  210. package/kit/framework/workflows/stats.md +60 -60
  211. package/kit/framework/workflows/transition.md +671 -671
  212. package/kit/framework/workflows/ui-phase.md +302 -302
  213. package/kit/framework/workflows/ui-review.md +165 -165
  214. package/kit/framework/workflows/update.md +323 -323
  215. package/kit/framework/workflows/validate-phase.md +174 -174
  216. package/kit/framework/workflows/verify-phase.md +252 -252
  217. package/kit/framework/workflows/verify-work.md +637 -637
  218. package/kit/hooks/check-update.js +118 -118
  219. package/kit/hooks/context-monitor.js +163 -163
  220. package/kit/hooks/prompt-guard.js +103 -103
  221. package/kit/hooks/statusline.js +125 -125
  222. package/kit/hooks/workflow-guard.js +101 -101
  223. package/kit/settings.json +45 -45
  224. package/kit/skills/example-skill/SKILL.md +42 -42
  225. package/package.json +63 -59
  226. package/src/core/kit.js +216 -216
  227. package/src/core/metrics.js +135 -10
  228. package/src/core/reflect.js +247 -247
  229. package/src/core/reverse-sync.js +372 -372
  230. package/src/core/sync.js +418 -418
  231. package/src/core/watch.js +121 -121
  232. package/src/mcp-server/index.js +34 -3
@@ -1,140 +1,408 @@
1
1
  ---
2
2
  name: burn-rate-status
3
- description: Tabela de burn rate por SLO % budget gasto, ETA exhaustão, ação (page/ticket/warn/ok). Rodável manualmente ou em /loop. Aplica skill burn-rate-alerting.
4
- argument-hint: "[<slo_name>] [--lookahead 4h] [--baseline 1h]"
3
+ description: Tabela de burn rate dual-window por SLO consumindo .planning/slos/*.yml + .planning/metrics/snapshots/. Calcula SLI atual, fast_burn (1h) + slow_burn (6h) independentes, ETA exhaustão e ação combinada (PAGE/TICKET/WARN/OK). Aplica skill burn-rate-alerting (fator 4× lookahead/baseline canônico Google SRE).
4
+ argument-hint: "[<slo_name>] [--fast-baseline 1h] [--slow-baseline 6h] [--format table|json]"
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Bash
8
- - Task
9
8
  - Glob
10
9
  ---
11
10
 
12
11
  <objective>
13
- Snapshot de burn rate para 1 SLO (se especificado) ou TODOS os SLOs definidos. Aplica skill [`burn-rate-alerting`](../skills/burn-rate-alerting/SKILL.md) — fórmula `burn_rate = error_rate / (1 - target)`, lookahead ≤ 4× baseline.
12
+ Snapshot de burn rate **dual-window** (1h fast + 6h slow) para 1 SLO (se especificado) ou TODOS os SLOs definidos em `.planning/slos/*.yml`. Aplica skill [`burn-rate-alerting`](../skills/burn-rate-alerting/SKILL.md) — fórmula canônica `burn_rate = error_rate / (1 - target)`, com lookahead/baseline obedecendo o **fator 4×** (page-tier: lookahead 1h ≤ 4× baseline 5m equivalente operacional; ticket-tier: lookahead 6h ≤ 4× baseline 30m). Status combinado segue o canonical Google SRE: PAGE quando ambas as janelas críticas, TICKET quando apenas slow erosion sustained, WARN para spike-only ou mild burn, OK quando ambas as janelas em estado saudável.
13
+
14
+ **Lê:** `.planning/slos/*.yml` (definição com `alert_thresholds.page` + `.ticket`) + `.planning/metrics/snapshots/*.json` (eventos persistidos via `metrics.persistSnapshot()` — Phase 99 + Phase 102 auto-snapshot).
14
15
 
15
16
  **Cria/Atualiza:** nada — comando read-only.
16
17
 
17
- **Após:** o user vê tabela com status (PAGE / TICKET / WARN / OK) e pode escolher invocar `/investigar-producao` se há burn ativo.
18
+ **Após:** o user vê tabela com colunas `fast_burn`, `slow_burn`, `combined` (status PAGE / TICKET / WARN / OK) e pode escolher invocar `/investigar-producao` se há burn ativo, ou aguardar mais snapshots se ambas janelas estão `no_data`.
18
19
  </objective>
19
20
 
20
21
  <context>
21
22
  **Argumentos:** `$ARGUMENTS` — opcional `<slo_name>` para 1 SLO; sem args = todos.
22
23
 
23
- **Flags:**
24
- - `--lookahead <duration>` — janela predictive (default: `4h` para short-term)
25
- - `--baseline <duration>` — janela base (default: `1h`)
26
- - `--format <table|json>` — output format (default: `table`)
24
+ **Flags (defaults dual-window — Phase 103):**
25
+ - `--fast-baseline <duration>` — janela fast (page-tier). Default: `1h`.
26
+ - `--slow-baseline <duration>` — janela slow (ticket-tier). Default: `6h`.
27
+ - `--format <table|json>` — output format. Default: `table`.
27
28
 
28
- **Combinações canônicas:**
29
- - short-term: lookahead 4h, baseline 1h (page-tier)
30
- - long-term: lookahead 3d, baseline 18h (ticket-tier)
29
+ **Combinações canônicas (skill burn-rate-alerting):**
30
+ - **Fast (page-tier):** lookahead 1h, baseline 5m, multiplier 14.4× — esgota ~2% do budget mensal em 1h.
31
+ - **Slow (ticket-tier):** lookahead 6h, baseline 30m, multiplier 6× — esgota ~10% do budget mensal em 6h.
31
32
 
32
- **Loop pattern:** rodar este comando via skill `loop` com intervalo 5min para monitoramento contínuo.
33
+ **Fator 4×:** lookahead baseline para extrapolação confiável (skill rule). 1h ≤ 4× 15m e 6h ≤ 4× 90m são ambos respeitados; defaults operacionais são page (1h baseline) + ticket (6h baseline) — a janela `lookahead` propriamente dita está embutida nos `alert_thresholds.{page,ticket}.lookahead` do YAML do SLO.
33
34
 
34
- ```text
35
- /loop 5m /burn-rate-status
36
- ```
35
+ **Phase 99 + 102 wiring:** este comando consome dados persistidos automaticamente pelo handler MCP `metrics-snapshot` (Phase 102 OBS-20-01 — auto-persist via `persistSnapshot()` em cada call com throttle 1s). Sem snapshots na janela, o comando emite "no_data" para o SLO em vez de inventar números.
36
+
37
+ **Cross-reference:** este comando é a implementação do pattern "dashboard de burn rate" canônico documentado em [`kit/skills/burn-rate-alerting/SKILL.md`](../skills/burn-rate-alerting/SKILL.md). A skill é a SSOT da fórmula e dos thresholds; este comando é o renderer.
37
38
  </context>
38
39
 
39
40
  <process>
40
41
 
41
42
  ## 1. Parsear argumentos
42
43
 
44
+ Bash:
43
45
  ```bash
44
- SLO_NAME=$(echo "$ARGUMENTS" | awk '{print $1}' | grep -v '^--' || true)
45
- LOOKAHEAD=$(echo "$ARGUMENTS" | grep -oE -- '--lookahead [^ ]+' | awk '{print $2}')
46
- BASELINE=$(echo "$ARGUMENTS" | grep -oE -- '--baseline [^ ]+' | awk '{print $2}')
46
+ SLO_NAME=$(echo "$ARGUMENTS" | awk '{for(i=1;i<=NF;i++) if($i !~ /^--/) {print $i; exit}}')
47
+ FAST_BASELINE=$(echo "$ARGUMENTS" | grep -oE -- '--fast-baseline [^ ]+' | awk '{print $2}')
48
+ SLOW_BASELINE=$(echo "$ARGUMENTS" | grep -oE -- '--slow-baseline [^ ]+' | awk '{print $2}')
47
49
  FORMAT=$(echo "$ARGUMENTS" | grep -oE -- '--format [^ ]+' | awk '{print $2}')
48
50
 
49
- [ -z "$LOOKAHEAD" ] && LOOKAHEAD="4h"
50
- [ -z "$BASELINE" ] && BASELINE="1h"
51
+ [ -z "$FAST_BASELINE" ] && FAST_BASELINE="1h"
52
+ [ -z "$SLOW_BASELINE" ] && SLOW_BASELINE="6h"
51
53
  [ -z "$FORMAT" ] && FORMAT="table"
52
54
  ```
53
55
 
54
- ## 2. Listar SLOs
56
+ Convert duration to ms (helper):
57
+ ```bash
58
+ to_ms() {
59
+ local d="$1"
60
+ case "$d" in
61
+ *h) echo $(( ${d%h} * 3600000 ));;
62
+ *m) echo $(( ${d%m} * 60000 ));;
63
+ *s) echo $(( ${d%s} * 1000 ));;
64
+ *d) echo $(( ${d%d} * 86400000 ));;
65
+ *) echo 0 ;;
66
+ esac
67
+ }
68
+ FAST_BASELINE_MS=$(to_ms "$FAST_BASELINE")
69
+ SLOW_BASELINE_MS=$(to_ms "$SLOW_BASELINE")
70
+ ```
71
+
72
+ ## 2. Listar SLOs (FIX Phase 99: extension `.yml`, não `.md`)
55
73
 
56
74
  ```bash
57
75
  if [ -n "$SLO_NAME" ]; then
58
- SLO_FILES=(".planning/slos/${SLO_NAME}.md")
76
+ SLO_FILES=(".planning/slos/${SLO_NAME}.yml")
59
77
  else
60
- SLO_FILES=(.planning/slos/*.md)
78
+ SLO_FILES=(.planning/slos/*.yml)
61
79
  fi
62
80
 
63
- if [ ${#SLO_FILES[@]} -eq 0 ] || [ ! -f "${SLO_FILES[0]}" ]; then
64
- echo "Nenhum SLO definido. Rode /definir-slo <feature> primeiro."
81
+ # Filtra entradas inexistentes (caso o glob não tenha match).
82
+ EXISTING_SLOS=()
83
+ for f in "${SLO_FILES[@]}"; do
84
+ [ -f "$f" ] && EXISTING_SLOS+=("$f")
85
+ done
86
+
87
+ if [ ${#EXISTING_SLOS[@]} -eq 0 ]; then
88
+ echo "Nenhum SLO definido em .planning/slos/. Rode /definir-slo <feature> primeiro."
65
89
  exit 0
66
90
  fi
67
91
  ```
68
92
 
69
- ## 3. Para cada SLO, dispatch para `burn-rate-forecaster`
93
+ ## 3. Para cada SLO, carregar metadata + calcular SLI dual-window
94
+
95
+ Para cada `SLO_FILE` em `EXISTING_SLOS`:
70
96
 
71
- Para cada `SLO_FILE`:
97
+ ### 3.1 Extrair campos canônicos do YAML via regex
98
+
99
+ Os SLOs do projeto seguem schema fixo (validado por `test/unit/slo-schema.test.js`). Sem `js-yaml` — regex sobre os keys conhecidos:
72
100
 
73
101
  ```bash
74
- SLO_NAME=$(basename "$SLO_FILE" .md)
75
- TARGET=$(grep -oE 'Target.*[0-9.]+' "$SLO_FILE" | head -1 | grep -oE '[0-9.]+')
102
+ SLO_NAME=$(grep -oE '^\s+name:\s*\S+' "$SLO_FILE" | head -1 | awk '{print $2}')
103
+ SERVICE=$(grep -oE '^\s+service:\s*\S+' "$SLO_FILE" | head -1 | awk '{print $2}')
104
+ SLO_TYPE=$(grep -oE '^\s+type:\s*\S+' "$SLO_FILE" | head -1 | awk '{print $2}')
105
+
106
+ # Availability SLO: target = ratio decimal (e.g. 0.995)
107
+ TARGET_RATIO=$(grep -oE '^target:\s*[0-9.]+' "$SLO_FILE" | awk '{print $2}')
108
+ # Latency SLO: target_ms + percentile
109
+ TARGET_MS=$(grep -oE '^target_ms:\s*[0-9]+' "$SLO_FILE" | awk '{print $2}')
110
+ PERCENTILE=$(grep -oE '^\s+percentile:\s*[0-9]+' "$SLO_FILE" | awk '{print $2}')
76
111
  ```
77
112
 
78
- ```text
79
- Task(
80
- subagent_type="burn-rate-forecaster",
81
- prompt="
82
- slo_name: ${SLO_NAME}
83
- target: ${TARGET}
84
- lookahead: ${LOOKAHEAD}
85
- baseline: ${BASELINE}
86
-
87
- Calcular burn rate atual + ETA + status (PAGE/TICKET/WARN/OK).
88
- Output formato compatível com tabela mestra.
89
- "
90
- )
113
+ ### 3.2 Extrair `alert_thresholds.page` (fast) + `.ticket` (slow) do YAML
114
+
115
+ Phase 103 (OBS-20-02) — leitura dos dois blocos via awk com state machine. Cada bloco tem `lookahead`, `baseline`, `burn_rate_multiplier`. Defaults canônicos aplicados se ausentes (defensive default — ver fallback abaixo).
116
+
117
+ ```bash
118
+ # alert_thresholds.page (fast / page-tier)
119
+ FAST_LOOKAHEAD=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*page:/){p=1;next}else if(p && /^\s+lookahead:/){print $2;exit}else if(p && /^\s*ticket:/){exit}}' "$SLO_FILE")
120
+ FAST_BASELINE_YAML=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*page:/){p=1;next}else if(p && /^\s+baseline:/){print $2;exit}else if(p && /^\s*ticket:/){exit}}' "$SLO_FILE")
121
+ FAST_MULTIPLIER=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*page:/){p=1;next}else if(p && /^\s+burn_rate_multiplier:/){print $2;exit}else if(p && /^\s*ticket:/){exit}}' "$SLO_FILE")
122
+
123
+ # alert_thresholds.ticket (slow / ticket-tier)
124
+ SLOW_LOOKAHEAD=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*ticket:/){t=1;next}else if(t && /^\s+lookahead:/){print $2;exit}}' "$SLO_FILE")
125
+ SLOW_BASELINE_YAML=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*ticket:/){t=1;next}else if(t && /^\s+baseline:/){print $2;exit}}' "$SLO_FILE")
126
+ SLOW_MULTIPLIER=$(awk '/^\s*alert_thresholds:/,/^[a-zA-Z]/{if(/^\s*ticket:/){t=1;next}else if(t && /^\s+burn_rate_multiplier:/){print $2;exit}}' "$SLO_FILE")
127
+
128
+ # Defensive defaults — fator 4× canonical Google SRE values.
129
+ # Se um SLO YAML antigo / parcial não declarar alert_thresholds, aplicamos
130
+ # os defaults da skill burn-rate-alerting verbatim:
131
+ # page: 14.4× / lookahead 1h / baseline 5m
132
+ # ticket: 6× / lookahead 6h / baseline 30m
133
+ [ -z "$FAST_MULTIPLIER" ] && FAST_MULTIPLIER="14.4"
134
+ [ -z "$SLOW_MULTIPLIER" ] && SLOW_MULTIPLIER="6"
135
+ [ -z "$FAST_LOOKAHEAD" ] && FAST_LOOKAHEAD="1h"
136
+ [ -z "$SLOW_LOOKAHEAD" ] && SLOW_LOOKAHEAD="6h"
91
137
  ```
92
138
 
93
- ## 4. Agregar resultados em tabela
139
+ ### 3.3 Carregar snapshots para AMBAS as janelas
94
140
 
141
+ Use a API `loadSnapshots()` (Phase 99) com duas chamadas — uma para fast (1h), uma para slow (6h). Inline node script:
142
+
143
+ ```bash
144
+ DUAL_SNAPS=$(node --input-type=module -e "
145
+ import { loadSnapshots } from './src/core/metrics.js';
146
+ const fast = await loadSnapshots(process.cwd(), $FAST_BASELINE_MS);
147
+ const slow = await loadSnapshots(process.cwd(), $SLOW_BASELINE_MS);
148
+ console.log(JSON.stringify({fast, slow, fastCount: fast.length, slowCount: slow.length}));
149
+ ")
150
+ FAST_COUNT=$(echo "$DUAL_SNAPS" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).fastCount)")
151
+ SLOW_COUNT=$(echo "$DUAL_SNAPS" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).slowCount)")
152
+ ```
153
+
154
+ **no_data conservative semantics:** se EITHER janela tem < 2 snapshots (availability) ou < 1 (latency), o `combined_status` final é `no_data` — preferimos não inventar números a falsamente reportar OK. Isso preserva o contrato "graceful no_data" da Phase 99.
155
+
156
+ ```bash
157
+ # A regra exata é aplicada dentro do node script de combinação (3.5),
158
+ # mas o early-out aqui evita trabalho desnecessário no caso comum.
159
+ if [ "$FAST_COUNT" -lt 2 ] && [ "$SLOW_COUNT" -lt 2 ]; then
160
+ echo "SLO $SLO_NAME: insufficient snapshots in BOTH windows (fast=$FAST_COUNT, slow=$SLOW_COUNT)"
161
+ echo "Generate data: invocações ao MCP tool 'metrics-snapshot' agora auto-persistem (Phase 102 OBS-20-01)."
162
+ COMBINED_STATUS="no_data"
163
+ continue
164
+ fi
165
+ ```
166
+
167
+ ### 3.4 Calcular SLI por tipo de SLO (fast E slow independentes)
168
+
169
+ **Availability (`type: event-based`):**
170
+
171
+ Inline node — primeiro vs último snapshot **dentro de cada janela**. Delta de counters dá good/bad events:
172
+
173
+ ```bash
174
+ DUAL_SLI=$(node --input-type=module -e "
175
+ import { loadSnapshots } from './src/core/metrics.js';
176
+ const fastSnaps = await loadSnapshots(process.cwd(), $FAST_BASELINE_MS);
177
+ const slowSnaps = await loadSnapshots(process.cwd(), $SLOW_BASELINE_MS);
178
+
179
+ function sliFromSnaps(snaps) {
180
+ if (snaps.length < 2) return {sli: null, errorRate: 0, good: 0, total: 0, error: 'no_data'};
181
+ const first = snaps[0];
182
+ const last = snaps[snaps.length - 1];
183
+ let goodFirst = 0, goodLast = 0, totalFirst = 0, totalLast = 0;
184
+ for (const [k,v] of Object.entries(first.counters)) {
185
+ if (k.endsWith(':ok')) goodFirst += v;
186
+ totalFirst += v;
187
+ }
188
+ for (const [k,v] of Object.entries(last.counters)) {
189
+ if (k.endsWith(':ok')) goodLast += v;
190
+ totalLast += v;
191
+ }
192
+ const good = goodLast - goodFirst;
193
+ const total = totalLast - totalFirst;
194
+ const sli = total > 0 ? good / total : null;
195
+ const errorRate = total > 0 ? (total - good) / total : 0;
196
+ return {sli, errorRate, good, total};
197
+ }
198
+
199
+ const fastSli = sliFromSnaps(fastSnaps);
200
+ const slowSli = sliFromSnaps(slowSnaps);
201
+ console.log(JSON.stringify({fast: fastSli, slow: slowSli}));
202
+ ")
95
203
  ```
204
+
205
+ **Latency (`type: percentile`):**
206
+
207
+ Para latency, p95 do último snapshot em CADA janela. SLI = fração de samples NOT acima de target_ms.
208
+
209
+ ```bash
210
+ DUAL_SLI=$(node --input-type=module -e "
211
+ import { loadSnapshots } from './src/core/metrics.js';
212
+ const target = $TARGET_MS;
213
+ const fastSnaps = await loadSnapshots(process.cwd(), $FAST_BASELINE_MS);
214
+ const slowSnaps = await loadSnapshots(process.cwd(), $SLOW_BASELINE_MS);
215
+
216
+ function latencySli(snaps) {
217
+ if (snaps.length < 1) return {sli: null, errorRate: 0, totalSamples: 0, slowSamples: 0, error: 'no_data'};
218
+ const last = snaps[snaps.length - 1];
219
+ let totalSamples = 0, slowSamples = 0;
220
+ for (const lat of Object.values(last.latency)) {
221
+ totalSamples += lat.count;
222
+ if (lat.p95 > target) slowSamples += Math.round(lat.count * 0.05);
223
+ }
224
+ const sli = totalSamples > 0 ? 1 - (slowSamples / totalSamples) : null;
225
+ const errorRate = totalSamples > 0 ? slowSamples / totalSamples : 0;
226
+ return {sli, errorRate, totalSamples, slowSamples};
227
+ }
228
+
229
+ console.log(JSON.stringify({fast: latencySli(fastSnaps), slow: latencySli(slowSnaps)}));
230
+ ")
231
+ ```
232
+
233
+ ### 3.5 Calcular burn rate + status COMBINADO (dual-window)
234
+
235
+ Aplicar fórmula canônica + status enum dual-window (skill `burn-rate-alerting` — fator 4× canonical):
236
+
237
+ ```bash
238
+ DUAL_STATUS=$(node --input-type=module -e "
239
+ const dual = $DUAL_SLI;
240
+ const target = $TARGET_RATIO || (1 - 0.05); // latency: 1 - ratio_above_target (5%)
241
+ const fastMult = $FAST_MULTIPLIER;
242
+ const slowMult = $SLOW_MULTIPLIER;
243
+
244
+ function burnFromSli(sli, target) {
245
+ if (sli.error) return {burnRate: null, error: sli.error};
246
+ const slack = 1 - target;
247
+ const burnRate = slack > 0 ? sli.errorRate / slack : 0;
248
+ return {burnRate, errorRate: sli.errorRate};
249
+ }
250
+
251
+ // Combined status — canonical dual-window logic per skill burn-rate-alerting (fator 4×):
252
+ // PAGE = ambos críticos (fast ≥ 14.4 E slow ≥ 6) → page on-call AGORA
253
+ // TICKET = slow erosion sustained (slow ≥ 6, fast OK) → ticket de investigação
254
+ // WARN = fast spike isolado (fast ≥ 14.4 sozinho) — monitor, NÃO page (alarm flap risk)
255
+ // WARN = mild burn em qualquer janela (≥ 1.0×) — sustained drains budget no horizonte
256
+ // OK = ambos < 1.0× — saudável
257
+ // no_data = qualquer janela com snapshots insuficientes (conservative)
258
+ function combinedStatus(fastBurn, fastMult, slowBurn, slowMult) {
259
+ if (fastBurn === null || slowBurn === null) return 'no_data';
260
+ const fastTriggered = fastBurn >= fastMult;
261
+ const slowTriggered = slowBurn >= slowMult;
262
+ if (fastTriggered && slowTriggered) return 'PAGE';
263
+ if (slowTriggered) return 'TICKET';
264
+ if (fastTriggered) return 'WARN';
265
+ if (fastBurn >= 1.0 || slowBurn >= 1.0) return 'WARN';
266
+ return 'OK';
267
+ }
268
+
269
+ const fastBurn = burnFromSli(dual.fast, target);
270
+ const slowBurn = burnFromSli(dual.slow, target);
271
+ const combined = combinedStatus(fastBurn.burnRate, fastMult, slowBurn.burnRate, slowMult);
272
+
273
+ let action;
274
+ switch (combined) {
275
+ case 'PAGE':
276
+ action = 'Page on-call NOW — invoke /investigar-producao';
277
+ break;
278
+ case 'TICKET':
279
+ action = 'Open ticket — slow erosion sustained, investigate before budget exhausted';
280
+ break;
281
+ case 'WARN':
282
+ action = 'Monitor — burn rate ≥1× either window, sustained drains budget';
283
+ break;
284
+ case 'no_data':
285
+ action = '— (await more snapshots; auto-persist via metrics-snapshot tool — Phase 102)';
286
+ break;
287
+ default:
288
+ action = '—';
289
+ }
290
+
291
+ // ETA exhaustion (predictive). Use slow window (more stable signal for budget extrapolation).
292
+ // For burn=0 (no errors), ETA is ∞.
293
+ const slowBurnRate = slowBurn.burnRate;
294
+ const baselineHours = $SLOW_BASELINE_MS / 3600000;
295
+ const eta = (slowBurnRate !== null && slowBurnRate > 0)
296
+ ? (1 / slowBurnRate) * 30 * 24 / baselineHours
297
+ : null;
298
+ const etaStr = eta === null ? '—' : (eta < 24 ? eta.toFixed(1) + 'h' : (eta/24).toFixed(1) + 'd');
299
+
300
+ const fastBurnFmt = fastBurn.burnRate === null ? '—' : fastBurn.burnRate.toFixed(2) + '×';
301
+ const slowBurnFmt = slowBurn.burnRate === null ? '—' : slowBurn.burnRate.toFixed(2) + '×';
302
+
303
+ // fast_status / slow_status são derivados em isolation (informativo na tabela);
304
+ // combined_status é o veredito operacional.
305
+ function singleStatus(burn, mult) {
306
+ if (burn === null) return 'no_data';
307
+ if (burn >= mult) return mult === parseFloat('$FAST_MULTIPLIER') ? 'PAGE-FAST' : 'TICKET-SLOW';
308
+ if (burn >= 1.0) return 'WARN';
309
+ return 'OK';
310
+ }
311
+ const fastStatus = singleStatus(fastBurn.burnRate, fastMult);
312
+ const slowStatus = singleStatus(slowBurn.burnRate, slowMult);
313
+
314
+ console.log(JSON.stringify({
315
+ fast_burn: fastBurnFmt,
316
+ slow_burn: slowBurnFmt,
317
+ fast_status: fastStatus,
318
+ slow_status: slowStatus,
319
+ combined_status: combined,
320
+ action: action,
321
+ eta: etaStr,
322
+ }));
323
+ ")
324
+ ```
325
+
326
+ ### 3.6 Acumular linha da tabela (colunas dual-window)
327
+
328
+ ```bash
329
+ SLO_ROWS+=("| $SLO_NAME | ${TARGET_RATIO:-${TARGET_MS}ms p$PERCENTILE} | ${FAST_BURN} | ${SLOW_BURN} | **${COMBINED_STATUS}** | $ETA | $ACTION |")
330
+ ```
331
+
332
+ ## 4. Renderizar tabela mestra (Phase 103 dual-window)
333
+
334
+ ```text
96
335
  ═══════════════════════════════════════════════════════════
97
- framework BURN-RATE-STATUS ▸ {timestamp}
336
+ framework BURN-RATE-STATUS (dual-window) ▸ {timestamp}
337
+ fast_baseline=$FAST_BASELINE slow_baseline=$SLOW_BASELINE
338
+ fast_multiplier=$FAST_MULTIPLIER (page) slow_multiplier=$SLOW_MULTIPLIER (ticket)
339
+ snapshots fast=$FAST_COUNT slow=$SLOW_COUNT
98
340
  ═══════════════════════════════════════════════════════════
99
341
 
100
- | SLO | Target | Window | Budget gasto | Burn rate | ETA exhaustão | Status | Ação |
101
- |---|---|---|---|---|---|---|---|
102
- | checkout_success | 99.9% | 30d | 23% | 1.4× | 12d | OK | informativo |
103
- | login_success | 99.95% | 30d | 78% | 8.0× | 4h | **PAGE** | invocar /investigar-producao |
104
- | search_latency | 99% | 30d | 15% | 0.7× | — | OK | — |
342
+ | SLO | Target | Fast (1h) | Slow (6h) | Combined | ETA exhaustão | Ação |
343
+ |---|---|---|---|---|---|---|
344
+ {$SLO_ROWS}
105
345
  ```
106
346
 
107
- ## 5. Sugerir próximas ações
347
+ **Exemplo concreto:**
108
348
 
109
- Se algum SLO em status PAGE ou TICKET:
349
+ ```markdown
350
+ | SLO | Target | Fast (1h) | Slow (6h) | Combined | ETA exhaustão | Ação |
351
+ |---|---|---|---|---|---|---|
352
+ | mcp-tool-availability | 99.5% | 0.42× OK | 0.18× OK | **OK** | — | — |
353
+ | mcp-tool-latency | 200ms p95 | 16.0× PAGE-FAST | 8.5× TICKET-SLOW | **PAGE** | 4.2h | Page on-call NOW — invoke /investigar-producao |
354
+ ```
110
355
 
356
+ ## 5. Sugerir próximas ações
357
+
358
+ ```bash
359
+ # Contar status counts
360
+ PAGE_COUNT=$(echo "$SLO_ROWS" | grep -c "\*\*PAGE\*\*" || echo 0)
361
+ TICKET_COUNT=$(echo "$SLO_ROWS" | grep -c "\*\*TICKET\*\*" || echo 0)
362
+ WARN_COUNT=$(echo "$SLO_ROWS" | grep -c "\*\*WARN\*\*" || echo 0)
363
+ NO_DATA_COUNT=$(echo "$SLO_ROWS" | grep -c "\*\*no_data\*\*" || echo 0)
111
364
  ```
112
- ## ⚠ SLOs em alerta:
113
- 1. login_success — burn rate 8.0×, ETA 4h
114
- → /investigar-producao "login_success burn rate = 8.0× às {timestamp}"
115
365
 
116
- ## SLOs em WARN (>= 80% gasto):
117
- - (nenhum)
366
+ Output:
367
+ ```text
368
+ ## Próximas ações
369
+
370
+ {Se PAGE_COUNT > 0:}
371
+ ⚠ {PAGE_COUNT} SLO(s) em PAGE (ambas janelas críticas) — invocar /investigar-producao "<slo_name> dual-window burn"
372
+
373
+ {Se TICKET_COUNT > 0:}
374
+ ☐ {TICKET_COUNT} SLO(s) em TICKET (slow erosion sustained) — abrir issue, investigar antes do budget esgotar
375
+
376
+ {Se WARN_COUNT > 0:}
377
+ ⓘ {WARN_COUNT} SLO(s) em WARN — fast spike isolado ou mild burn ≥ 1× (não page; monitor)
118
378
 
119
- ## SLOs OK:
120
- - 2 SLOs em compliance saudável
379
+ {Se NO_DATA_COUNT > 0:}
380
+ ⊘ {NO_DATA_COUNT} SLO(s) sem dados em pelo menos uma janela — Phase 102 auto-persist deve popular .planning/metrics/snapshots/ automaticamente em chamadas ao MCP tool 'metrics-snapshot'
121
381
  ```
122
382
 
123
- ## 6. Modo `/loop`
383
+ ## 6. Modo `/loop` (idempotência)
124
384
 
125
385
  Se chamado dentro de `/loop`, comportamento idempotente:
126
- - Não acumular state entre invocações (snapshot fresh)
127
- - Output curto se nada mudou (apenas status; sem repetir tabela completa em todo loop)
128
- - Acionar AskUserQuestion APENAS quando status muda de OK → WARN/TICKET/PAGE (transição)
386
+ - Snapshot fresh em cada invocação (não acumular state).
387
+ - Output curto se `combined_status` não mudou (apenas linha-resumo; sem repetir tabela completa).
388
+ - Acionar AskUserQuestion APENAS quando algum SLO transiciona OK → WARN/TICKET/PAGE no `combined_status`.
129
389
 
130
390
  </process>
131
391
 
132
392
  <success_criteria>
133
- - [ ] $ARGUMENTS parseados (SLO opcional + flags)
134
- - [ ] SLOs descobertos via glob `.planning/slos/*.md`
135
- - [ ] `burn-rate-forecaster` invocado para cada SLO
136
- - [ ] Tabela agregada em formato consistente
137
- - [ ] Status enum: PAGE / TICKET / WARN / OK
138
- - [ ] Sugestões de próximas ações para SLOs em alerta
139
- - [ ] Idempotente (rodável em /loop sem acúmulo)
393
+ - [ ] $ARGUMENTS parseados (SLO opcional + flags --fast-baseline/--slow-baseline/--format)
394
+ - [ ] SLOs descobertos via glob `.planning/slos/*.yml` (FIX Phase 99: extension `.yml`, não `.md`)
395
+ - [ ] alert_thresholds.page (fast) + alert_thresholds.ticket (slow) extraídos via awk com state machine
396
+ - [ ] Defensive defaults aplicados (14.4 / 6 / 1h / 6h) se YAML omitir blocos
397
+ - [ ] Snapshots carregados via `loadSnapshots()` em DUAS chamadas (fast + slow)
398
+ - [ ] SLI calculado por tipo (event-based ratio para availability, percentile para latency) em CADA janela
399
+ - [ ] Burn rate calculado pela fórmula `error_rate / (1 - target)` (skill [`burn-rate-alerting`](../skills/burn-rate-alerting/SKILL.md)) para fast E slow independentemente
400
+ - [ ] Status combinado dual-window: **PAGE** (ambos críticos) / **TICKET** (slow only) / **WARN** (fast only OR mild ≥ 1×) / **OK** (ambos < 1×) / **no_data** (qualquer janela com snapshots insuficientes)
401
+ - [ ] Tabela markdown agregada com colunas Fast (1h) / Slow (6h) / Combined explícitas
402
+ - [ ] ETA exhaustão computada (predictive forecast — usa slow window por estabilidade)
403
+ - [ ] Sugestões de próximas ações contextualizadas pelo combined_status
404
+ - [ ] Idempotente em /loop (sem acúmulo de state; transição combined_status dispara AskUserQuestion)
405
+ - [ ] no_data graceful — Phase 102 auto-persist mencionado como solução
406
+ - [ ] Skill burn-rate-alerting cross-referenced no objective + inline no node script + inline na tabela de fallback (3 hits ≥ 2 mínimo)
407
+ - [ ] Fator 4× explícito no contexto (canonical Google SRE)
140
408
  </success_criteria>