pumuki 6.3.42 → 6.3.44
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.
- package/docs/RELEASE_NOTES.md +13 -0
- package/docs/USAGE.md +2 -0
- package/docs/seguimiento-activo-pumuki-saas-supermercados.md +252 -1
- package/integrations/config/skillsMarkdownRules.ts +25 -1
- package/integrations/config/skillsRuleSet.ts +34 -1
- package/integrations/gate/evaluateAiGate.ts +83 -7
- package/integrations/git/runPlatformGateOutput.ts +4 -0
- package/integrations/git/stageRunners.ts +25 -3
- package/integrations/git/worktreeAtomicSlices.ts +130 -0
- package/integrations/lifecycle/artifacts.ts +83 -2
- package/integrations/lifecycle/cli.ts +56 -2
- package/integrations/lifecycle/install.ts +2 -0
- package/integrations/lifecycle/policyReconcile.ts +136 -3
- package/integrations/lifecycle/watch.ts +118 -15
- package/integrations/mcp/autoExecuteAiStart.ts +46 -4
- package/integrations/mcp/preFlightCheck.ts +30 -0
- package/integrations/sdd/evidenceScaffold.ts +53 -2
- package/integrations/sdd/stateSync.ts +3 -3
- package/package.json +1 -1
package/docs/RELEASE_NOTES.md
CHANGED
|
@@ -5,6 +5,19 @@ Detailed commit history remains available through Git history (`git log` / `git
|
|
|
5
5
|
|
|
6
6
|
## 2026-03 (enterprise hardening updates)
|
|
7
7
|
|
|
8
|
+
### 2026-03-05 (v6.3.43)
|
|
9
|
+
|
|
10
|
+
- `pumuki sdd evidence` alinea su salida con el contrato TDD/BDD del gate:
|
|
11
|
+
- `version: "1"` (antes `1.0`),
|
|
12
|
+
- `slices[]` generado por defecto con estructura `red/green/refactor`.
|
|
13
|
+
- Compatibilidad de transición mantenida:
|
|
14
|
+
- el artefacto conserva campos legacy (`scenario_id`, `test_run`, `ai_evidence`) para flujos existentes,
|
|
15
|
+
- `pumuki sdd state-sync` acepta source evidence `version=1` y `version=1.0`.
|
|
16
|
+
- Evidencia de validación:
|
|
17
|
+
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/evidenceScaffold.test.ts integrations/sdd/__tests__/stateSync.test.ts integrations/lifecycle/__tests__/cli.test.ts` (`47 pass / 0 fail`)
|
|
18
|
+
- `npm run -s typecheck` (`PASS`)
|
|
19
|
+
- smoke real en Flux con CLI local: `pumuki sdd evidence ... --json` -> artefacto `version: "1"` con `slices[]`.
|
|
20
|
+
|
|
8
21
|
### 2026-03-05 (v6.3.42)
|
|
9
22
|
|
|
10
23
|
- Modal flotante de bloqueo (macOS) ajustado para legibilidad real:
|
package/docs/USAGE.md
CHANGED
|
@@ -327,6 +327,8 @@ Watch runtime behavior:
|
|
|
327
327
|
- `--notify-cooldown-ms` enables anti-spam throttling for repeated alerts with equal signature.
|
|
328
328
|
- `--no-notify` keeps watch output without OS notifications.
|
|
329
329
|
- `--once` or `--iterations=<n>` is recommended for CI/scripts to avoid long-running sessions.
|
|
330
|
+
- `--json` now includes per-tick file traceability: `lastTick.changedFiles[]` and `lastTick.evaluatedFiles[]`.
|
|
331
|
+
- Runtime artifacts hygiene is self-healed in `.git/info/exclude` (`.ai_evidence.json`, `.AI_EVIDENCE.json`, `.pumuki/`) to avoid dirty worktree noise.
|
|
330
332
|
|
|
331
333
|
<a id="backlog-tooling"></a>
|
|
332
334
|
Backlog tooling behavior (`watch` + `reconcile` scripts):
|
|
@@ -1851,9 +1851,260 @@
|
|
|
1851
1851
|
- longitud de remediación ampliada (`max 220`) para preservar pasos útiles sin recorte agresivo.
|
|
1852
1852
|
- validación manual de modales de prueba (corto/medio/largo/extremo) en helper Swift flotante con rendering consistente y sin regresión de acciones (`Desactivar`, `Silenciar 30 min`, `Mantener activas`).
|
|
1853
1853
|
|
|
1854
|
-
-
|
|
1854
|
+
- ✅ PUMUKI-149: Preparar corte release del bloque UX-notificaciones y ejecutar upgrade/smoke en consumidores (`SAAS`, `RuralGo`, `Flux_training`).
|
|
1855
1855
|
- Alcance:
|
|
1856
1856
|
- cerrar changelog/release notes del bloque `PUMUKI-145..148`,
|
|
1857
1857
|
- publicar versión npm con trazabilidad,
|
|
1858
1858
|
- ejecutar `install/update` + smoke mínimo en los tres repos consumidores,
|
|
1859
1859
|
- actualizar estado final en MDs externos por leyenda.
|
|
1860
|
+
- Resultado (2026-03-05):
|
|
1861
|
+
- publicación npm completada: `pumuki@6.3.42`.
|
|
1862
|
+
- upgrade aplicado con `pumuki install` en:
|
|
1863
|
+
- `SAAS:APP_SUPERMERCADOS` (`lifecycle_version=6.3.42`)
|
|
1864
|
+
- `R_GO` (`lifecycle_version=6.3.42`)
|
|
1865
|
+
- `Flux_training` (`lifecycle_version=6.3.42`)
|
|
1866
|
+
- smoke de salud post-upgrade en 3/3 consumidores:
|
|
1867
|
+
- `doctor_issues=0`
|
|
1868
|
+
- `openspec_compatible=true`
|
|
1869
|
+
- `session_active=true`
|
|
1870
|
+
|
|
1871
|
+
- ✅ PUMUKI-150: Ejecutar siguiente pendiente real en backlog externo (Flux `PUM-005`) y cerrar incidencia de `pumuki sdd evidence` con contrato v1 compatible con gate TDD.
|
|
1872
|
+
- Alcance:
|
|
1873
|
+
- reproducir `PUM-005` con evidencia local controlada,
|
|
1874
|
+
- aplicar fix mínimo en core (`sdd evidence` -> `version=\"1\"` + `slices[]`),
|
|
1875
|
+
- validar RED->GREEN con smoke en `Flux_training`,
|
|
1876
|
+
- actualizar leyenda en `docs/BUGS_Y_MEJORAS_PUMUKI.md` (Flux) sin tocar código del consumer.
|
|
1877
|
+
- Resultado (2026-03-05):
|
|
1878
|
+
- fix aplicado en core:
|
|
1879
|
+
- `integrations/sdd/evidenceScaffold.ts`: `artifact.version=\"1\"` + `artifact.slices[]`, manteniendo campos legacy (`scenario_id`, `test_run`, `ai_evidence`).
|
|
1880
|
+
- `integrations/sdd/stateSync.ts`: compatibilidad de lectura `version=1|1.0` para rollout sin ruptura.
|
|
1881
|
+
- validación local:
|
|
1882
|
+
- `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/evidenceScaffold.test.ts integrations/sdd/__tests__/stateSync.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `47 pass / 0 fail`.
|
|
1883
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1884
|
+
- release asociado: `pumuki@6.3.43` publicado en npm.
|
|
1885
|
+
- revalidación en Flux con `@latest`:
|
|
1886
|
+
- `pumuki sdd evidence ... --json` => `artifact.version=\"1\"` + `slices[]`,
|
|
1887
|
+
- `pnpm exec pumuki watch --once --stage=PRE_COMMIT --scope=staged --json` en `ALLOW` sin `TDD_BDD_EVIDENCE_INVALID`.
|
|
1888
|
+
- leyenda Flux actualizada: `PUM-005` -> `✅ Cerrado`; `PUM-006` -> `🚧 En construccion`.
|
|
1889
|
+
|
|
1890
|
+
- ✅ PUMUKI-151: Ejecutar siguiente pendiente activo de backlog Flux (`PUM-006`) para enriquecer salida JSON de `pumuki watch` con `changedFiles/evaluatedFiles`.
|
|
1891
|
+
- Resultado (2026-03-05):
|
|
1892
|
+
- `integrations/lifecycle/watch.ts`:
|
|
1893
|
+
- contrato extendido de tick con `changedFiles[]` y `evaluatedFiles[]`,
|
|
1894
|
+
- cálculo determinista por `facts` del scope evaluado.
|
|
1895
|
+
- `integrations/lifecycle/__tests__/watch.test.ts`:
|
|
1896
|
+
- cobertura de ambos campos en ticks `BLOCK/ALLOW/WARN`.
|
|
1897
|
+
- `integrations/lifecycle/__tests__/cli.test.ts`:
|
|
1898
|
+
- payload JSON estable de `watch --once --json` validando ambos campos.
|
|
1899
|
+
- `docs/USAGE.md`:
|
|
1900
|
+
- documentación de trazabilidad por tick en salida JSON.
|
|
1901
|
+
- Evidencia:
|
|
1902
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/watch.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `42 pass / 0 fail`.
|
|
1903
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1904
|
+
- leyenda Flux actualizada: `PUM-006` -> `✅ Cerrado`; `PUM-007` -> `🚧 En construccion`.
|
|
1905
|
+
|
|
1906
|
+
- ✅ PUMUKI-152: Ejecutar siguiente pendiente activo de backlog Flux (`PUM-007`) para evitar artefactos efimeros en raíz del repo tras hooks/watch.
|
|
1907
|
+
- Alcance:
|
|
1908
|
+
- reproducir contaminación de worktree por `.ai_evidence.json` y `.pumuki/**`,
|
|
1909
|
+
- definir cleanup post-ejecución sin perder trazabilidad oficial del repo,
|
|
1910
|
+
- validar con tests + smoke en `Flux_training`,
|
|
1911
|
+
- cerrar leyenda en MD externo Flux al completar fix.
|
|
1912
|
+
- Avance actual (2026-03-05):
|
|
1913
|
+
- `integrations/lifecycle/artifacts.ts`:
|
|
1914
|
+
- nuevo `ensureRuntimeArtifactsIgnored(repoRoot)` con bloque gestionado en `.git/info/exclude`,
|
|
1915
|
+
- soporte para repos normales y `git worktree` (`.git` como `gitdir`).
|
|
1916
|
+
- integración aplicada en:
|
|
1917
|
+
- `integrations/lifecycle/install.ts`
|
|
1918
|
+
- `integrations/lifecycle/watch.ts`
|
|
1919
|
+
- `integrations/git/stageRunners.ts`
|
|
1920
|
+
- cobertura de regresión:
|
|
1921
|
+
- `integrations/lifecycle/__tests__/artifacts.test.ts` (bloque insert/replace/idempotent),
|
|
1922
|
+
- `integrations/git/__tests__/stageRunners.test.ts` (pre-commit asegura ignore runtime),
|
|
1923
|
+
- `integrations/lifecycle/__tests__/install.test.ts` (worktree sin regresión).
|
|
1924
|
+
- evidencia local:
|
|
1925
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/install.test.ts integrations/lifecycle/__tests__/artifacts.test.ts integrations/git/__tests__/stageRunners.test.ts integrations/lifecycle/__tests__/watch.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `86 pass / 0 fail`.
|
|
1926
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1927
|
+
- smoke en consumer Flux:
|
|
1928
|
+
- `node /Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/bin/pumuki.js watch --once --stage=PRE_COMMIT --scope=staged --json` ejecutado en `Flux_training`,
|
|
1929
|
+
- `.git/info/exclude` con bloque `pumuki-runtime-artifacts`,
|
|
1930
|
+
- `git status --short` sin ruido de `.ai_evidence.json` / `.pumuki/**`.
|
|
1931
|
+
- leyenda Flux actualizada: `PUM-007` -> `✅ Cerrado`; `PUM-008` -> `🚧 En construccion`.
|
|
1932
|
+
|
|
1933
|
+
- ✅ PUMUKI-153: Ejecutar siguiente pendiente activo de backlog Flux (`PUM-008`) para hacer accionable el bloqueo `skills.frontend.no-solid-violations` en cambios incrementales.
|
|
1934
|
+
- Resultado (2026-03-05):
|
|
1935
|
+
- `integrations/config/skillsRuleSet.ts`:
|
|
1936
|
+
- mensajes enriquecidos para reglas `*.no-solid-violations` con `ast_nodes`, `observed_paths` y `sample_paths`.
|
|
1937
|
+
- `integrations/git/runPlatformGateOutput.ts`:
|
|
1938
|
+
- `next_action` específico y progresivo para `SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS` (extracción incremental por componente/hook).
|
|
1939
|
+
- cobertura:
|
|
1940
|
+
- `integrations/config/__tests__/skillsRuleSet.test.ts`,
|
|
1941
|
+
- `integrations/git/__tests__/runPlatformGateOutput.test.ts`.
|
|
1942
|
+
- Evidencia:
|
|
1943
|
+
- `npx --yes tsx@4.21.0 --test integrations/config/__tests__/skillsRuleSet.test.ts integrations/git/__tests__/runPlatformGateOutput.test.ts` -> `17 pass / 0 fail`.
|
|
1944
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1945
|
+
- leyenda Flux actualizada: `PUM-008` -> `✅ Cerrado`.
|
|
1946
|
+
|
|
1947
|
+
- ✅ PUMUKI-154: Ejecutar siguiente pendiente crítico del backlog SAAS (`PUMUKI-016`) para convertir bloqueos de `PRE_WRITE` por regla iOS crítica ausente en remediación guiada y determinista de bootstrap/reconcile.
|
|
1948
|
+
- Resultado (2026-03-05):
|
|
1949
|
+
- `integrations/mcp/preFlightCheck.ts`:
|
|
1950
|
+
- hints accionables para `EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING` y `EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE`.
|
|
1951
|
+
- `integrations/mcp/autoExecuteAiStart.ts`:
|
|
1952
|
+
- `next_action` determinista con comando `policy reconcile --strict` + revalidación `PRE_WRITE`.
|
|
1953
|
+
- clasificación de confianza alineada para ambos códigos críticos.
|
|
1954
|
+
- `integrations/lifecycle/cli.ts`:
|
|
1955
|
+
- `PRE_WRITE_HINTS_BY_CODE` enriquecido para esos códigos críticos.
|
|
1956
|
+
- `resolvePreWriteNextAction` ahora prioriza ruta de reconcile estricto en bloqueos de reglas críticas.
|
|
1957
|
+
- cobertura añadida:
|
|
1958
|
+
- `integrations/mcp/__tests__/preFlightCheck.test.ts`
|
|
1959
|
+
- `integrations/mcp/__tests__/autoExecuteAiStart.test.ts`
|
|
1960
|
+
- `integrations/lifecycle/__tests__/cli.test.ts`
|
|
1961
|
+
- Evidencia:
|
|
1962
|
+
- `npx --yes tsx@4.21.0 --test integrations/mcp/__tests__/preFlightCheck.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `50 pass / 0 fail`.
|
|
1963
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1964
|
+
- leyenda SAAS actualizada:
|
|
1965
|
+
- `PUMUKI-016` -> `✅ Cerrado`
|
|
1966
|
+
- `PUMUKI-012` -> `🚧 En construcción` (siguiente foco).
|
|
1967
|
+
|
|
1968
|
+
- ✅ PUMUKI-155: Ejecutar siguiente pendiente HIGH del backlog SAAS (`PUMUKI-012`) para cerrar la recurrencia de `active_rule_ids` vacío con cambios de código en `PRE_COMMIT`.
|
|
1969
|
+
- Alcance:
|
|
1970
|
+
- reproducir recurrencia en contexto real (diffs pequeños y grandes),
|
|
1971
|
+
- endurecer contrato de `rules_coverage` en `PRE_COMMIT` para evitar falso verde con cobertura vacía,
|
|
1972
|
+
- validar con tests focales + typecheck,
|
|
1973
|
+
- actualizar leyenda SAAS manteniendo una única `🚧`.
|
|
1974
|
+
- Avance técnico (2026-03-05):
|
|
1975
|
+
- `integrations/git/stageRunners.ts`:
|
|
1976
|
+
- en bloqueos de `PRE_COMMIT/PRE_PUSH/CI` prioriza `snapshot.findings` del stage actual como causa primaria (en lugar de depender solo de `ai_gate.violations`),
|
|
1977
|
+
- remediación específica añadida para `ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH`.
|
|
1978
|
+
- `integrations/git/runPlatformGateOutput.ts`:
|
|
1979
|
+
- `next_action` explícito para `ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH` con flujo `policy reconcile --strict` + `pumuki-pre-commit`.
|
|
1980
|
+
- cobertura añadida:
|
|
1981
|
+
- `integrations/git/__tests__/stageRunners.test.ts`
|
|
1982
|
+
- `integrations/git/__tests__/runPlatformGateOutput.test.ts`
|
|
1983
|
+
- evidencia local:
|
|
1984
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/runPlatformGateOutput.test.ts integrations/git/__tests__/stageRunners.test.ts integrations/mcp/__tests__/preFlightCheck.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `82 pass / 0 fail`.
|
|
1985
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1986
|
+
- Avance técnico adicional (2026-03-05, cierre de falso bloqueo cross-platform en PRE_WRITE):
|
|
1987
|
+
- `integrations/gate/evaluateAiGate.ts`:
|
|
1988
|
+
- detección efectiva de plataformas en `PRE_WRITE` basada en `rules_coverage` cuando hay plataformas detectadas, para evitar arrastre de scope legado (ej.: iOS) en diffs backend.
|
|
1989
|
+
- mantiene enforcement hard sin relajar contrato: si no hay señal efectiva de coverage por prefijo, conserva fallback al scope detectado original.
|
|
1990
|
+
- `integrations/gate/__tests__/evaluateAiGate.test.ts`:
|
|
1991
|
+
- nuevo caso de regresión: `platforms` arrastra `ios`, pero la cobertura efectiva real es `backend` y el gate debe quedar en `ALLOWED`.
|
|
1992
|
+
- evidencia local:
|
|
1993
|
+
- `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/evaluateAiGate.test.ts` -> `26 pass / 0 fail`.
|
|
1994
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/runPlatformGateOutput.test.ts integrations/git/__tests__/stageRunners.test.ts integrations/mcp/__tests__/preFlightCheck.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `82 pass / 0 fail`.
|
|
1995
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
1996
|
+
- Avance técnico adicional (2026-03-05, hardening de cobertura vacía en PRE_WRITE):
|
|
1997
|
+
- `integrations/gate/evaluateAiGate.ts`:
|
|
1998
|
+
- `EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES` ahora también bloquea cuando `active_rule_ids=[]` y la cobertura evaluada infiere plataforma (`skills.<scope>.*`) aunque `platforms` venga vacío.
|
|
1999
|
+
- mensaje distingue modo de detección (`detected`/`inferred`) para trazabilidad de root-cause.
|
|
2000
|
+
- `integrations/gate/__tests__/evaluateAiGate.test.ts`:
|
|
2001
|
+
- nuevo caso de regresión: `platforms={}` + `evaluated_rule_ids=['skills.backend.no-empty-catch']` + `active_rule_ids=[]` => `BLOCKED`.
|
|
2002
|
+
- evidencia local:
|
|
2003
|
+
- `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/evaluateAiGate.test.ts` -> `27 pass / 0 fail`.
|
|
2004
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/runPlatformGate.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `73 pass / 0 fail`.
|
|
2005
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2006
|
+
- Cierre operativo final (2026-03-05):
|
|
2007
|
+
- `integrations/mcp/autoExecuteAiStart.ts`, `integrations/mcp/preFlightCheck.ts` y `integrations/lifecycle/cli.ts` alineados con remediación accionable para `EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES`.
|
|
2008
|
+
- pruebas de regresión completas:
|
|
2009
|
+
- `npx --yes tsx@4.21.0 --test integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/mcp/__tests__/preFlightCheck.test.ts integrations/lifecycle/__tests__/cli.test.ts integrations/gate/__tests__/evaluateAiGate.test.ts` -> `80 pass / 0 fail`.
|
|
2010
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2011
|
+
- leyenda SAAS actualizada:
|
|
2012
|
+
- `PUMUKI-012` -> `✅ Cerrado`
|
|
2013
|
+
- `PUMUKI-M008` -> `🚧 En construcción` (siguiente foco único).
|
|
2014
|
+
|
|
2015
|
+
- ✅ PUMUKI-156: Ejecutar siguiente pendiente HIGH del backlog SAAS (`PUMUKI-017`) para reducir falsos bloqueos de `skills.backend.no-solid-violations` en `PRE_COMMIT` y mantener enforcement fuerte en `PRE_PUSH`.
|
|
2016
|
+
- Resultado (2026-03-05):
|
|
2017
|
+
- `integrations/config/skillsMarkdownRules.ts`:
|
|
2018
|
+
- reglas conocidas `*.no-solid-violations` ahora usan stage canónico `PRE_PUSH` cuando el markdown no define stage.
|
|
2019
|
+
- si el markdown define stage explícito, se respeta sin override.
|
|
2020
|
+
- `integrations/config/__tests__/skillsMarkdownRules.test.ts`:
|
|
2021
|
+
- cobertura nueva para stage canónico y respeto de stage explícito.
|
|
2022
|
+
- Evidencia:
|
|
2023
|
+
- `npx --yes tsx@4.21.0 --test integrations/config/__tests__/skillsMarkdownRules.test.ts integrations/config/__tests__/skillsRuleSet.test.ts` -> `20 pass / 0 fail`.
|
|
2024
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/runPlatformGate.test.ts integrations/git/__tests__/stageRunners.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `101 pass / 0 fail`.
|
|
2025
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2026
|
+
|
|
2027
|
+
- ✅ PUMUKI-157: Ejecutar siguiente pendiente HIGH del backlog SAAS (`PUMUKI-M008`) para entregar asistente de partición atómica accionable en `PRE_WRITE` antes del bloqueo.
|
|
2028
|
+
- Alcance:
|
|
2029
|
+
- generar `suggested-atomic-slices` por contexto (scope/ruta/tipo de archivo),
|
|
2030
|
+
- exponer `next_action` con comandos concretos de staging por lote,
|
|
2031
|
+
- mantener comportamiento anti-spam y sin auto-aplicar cambios.
|
|
2032
|
+
- Resultado (2026-03-05):
|
|
2033
|
+
- helper nuevo `integrations/git/worktreeAtomicSlices.ts` para agrupar cambios por scope y proponer `git add -- ...` por lote.
|
|
2034
|
+
- `pre_flight_check` ahora expone `ATOMIC_SLICES` y `ATOMIC_SLICES[next]` en bloqueos de higiene de worktree.
|
|
2035
|
+
- `auto_execute_ai_start` ahora devuelve `next_action` con primer lote atómico accionable + comando de revalidación `PRE_WRITE`.
|
|
2036
|
+
- `pumuki sdd validate --stage=PRE_WRITE` ahora expone `next_action` con slice atómico cuando el umbral se supera.
|
|
2037
|
+
- Evidencia:
|
|
2038
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/worktreeAtomicSlices.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/mcp/__tests__/preFlightCheck.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `56 pass / 0 fail`.
|
|
2039
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2040
|
+
- leyenda SAAS actualizada:
|
|
2041
|
+
- `PUMUKI-M008` -> `✅ Cerrado`
|
|
2042
|
+
- `PUMUKI-M005` -> `🚧 En construcción` (siguiente foco único).
|
|
2043
|
+
|
|
2044
|
+
- ✅ PUMUKI-158: Ejecutar siguiente pendiente MEDIUM del backlog SAAS (`PUMUKI-M005`) para cerrar convergencia determinista de `policy reconcile --strict` (diagnóstico + autofix + verificación).
|
|
2045
|
+
- Alcance:
|
|
2046
|
+
- definir salida `next_action` estable de autofix cuando detecte drift crítico,
|
|
2047
|
+
- añadir modo `--apply` seguro e idempotente para convergencia guiada,
|
|
2048
|
+
- dejar evidencia machine-readable de antes/después (`drift -> reconciled`).
|
|
2049
|
+
- Resultado (2026-03-05):
|
|
2050
|
+
- `integrations/lifecycle/policyReconcile.ts` ahora soporta `apply=true` y produce bloque `autofix` (`attempted/status/actions/details`).
|
|
2051
|
+
- `policy reconcile --strict --apply` escribe contrato determinista `.pumuki/policy-as-code.json` (`WRITE_POLICY_AS_CODE_CONTRACT`) y reevalúa convergencia.
|
|
2052
|
+
- `integrations/lifecycle/cli.ts` añade flag `--apply` + salida textual de autofix.
|
|
2053
|
+
- cobertura añadida:
|
|
2054
|
+
- `integrations/lifecycle/__tests__/policyReconcile.test.ts` (convergencia `--strict --apply`),
|
|
2055
|
+
- `integrations/lifecycle/__tests__/cli.test.ts` (parse + ejecución `--strict --apply --json`).
|
|
2056
|
+
- Evidencia:
|
|
2057
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/policyReconcile.test.ts integrations/lifecycle/__tests__/cli.test.ts integrations/git/__tests__/worktreeAtomicSlices.test.ts integrations/mcp/__tests__/preFlightCheck.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts` -> `63 pass / 0 fail`.
|
|
2058
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2059
|
+
- leyenda SAAS actualizada:
|
|
2060
|
+
- `PUMUKI-M005` -> `✅ Cerrado`
|
|
2061
|
+
- `PUMUKI-006` -> `🚧 En construcción` (siguiente foco único).
|
|
2062
|
+
|
|
2063
|
+
- ✅ PUMUKI-159: Ejecutar siguiente pendiente LOW del backlog SAAS (`PUMUKI-006`) para alinear `package_version` y `lifecycle_version` en `ai_gate_check`/MCP.
|
|
2064
|
+
- Resultado (2026-03-05):
|
|
2065
|
+
- `integrations/gate/evaluateAiGate.ts` incorpora normalización defensiva para evitar payload desalineado cuando `captureRepoState` mezcla fuentes de versión.
|
|
2066
|
+
- se alinea `repo_state.lifecycle.package_version` con `lifecycle_version` en salida de gate/MCP, manteniendo trazabilidad sin drift en contratos JSON.
|
|
2067
|
+
- regresión cubierta en `integrations/gate/__tests__/evaluateAiGate.test.ts` con fixture desalineada.
|
|
2068
|
+
- Evidencia:
|
|
2069
|
+
- `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/evaluateAiGate.test.ts` -> `PASS`.
|
|
2070
|
+
- `npx --yes tsx@4.21.0 --test integrations/mcp/__tests__/preFlightCheck.test.ts integrations/mcp/__tests__/autoExecuteAiStart.test.ts integrations/mcp/__tests__/aiGateCheck.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `PASS`.
|
|
2071
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2072
|
+
- leyenda SAAS actualizada:
|
|
2073
|
+
- `PUMUKI-006` -> `✅ Cerrado`
|
|
2074
|
+
- backlog SAAS externo queda en `✅ 100% cerrado`.
|
|
2075
|
+
|
|
2076
|
+
- ✅ PUMUKI-160: Iniciar siguiente ciclo activo sobre backlog RuralGo/consumidores (solo hallazgos netos nuevos) sin reabrir incidencias ya cerradas.
|
|
2077
|
+
- Resultado (2026-03-05):
|
|
2078
|
+
- validado estado canónico externo:
|
|
2079
|
+
- SAAS: `✅ Cerrados: 25`, `0` en `🚧/⏳/⛔`.
|
|
2080
|
+
- RuralGo: `✅ Cerrados: 98`, `0` en `🚧/⏳/⛔`.
|
|
2081
|
+
- Flux: backlog localizado en `docs/BUGS_Y_MEJORAS_PUMUKI.md` (ruta real), con foco activo en `PUM-010`.
|
|
2082
|
+
- cerrado `PUM-009` en Flux tras verificación directa contra paquete publicado (`pumuki@latest`) con salida válida de `sdd evidence` (`version: "1"` + `slices[]`).
|
|
2083
|
+
- Evidencia:
|
|
2084
|
+
- `npx --yes --package pumuki@latest pumuki sdd evidence --scenario-id=docs/validation/features/p3_t1_web_shell_dashboard --test-command='pnpm test' --test-status=passed --json` -> artefacto válido.
|
|
2085
|
+
- actualización de leyenda en `Flux_training/docs/BUGS_Y_MEJORAS_PUMUKI.md`:
|
|
2086
|
+
- `PUM-009` -> `✅ Cerrado`
|
|
2087
|
+
- `PUM-010` -> `🚧 En construccion`
|
|
2088
|
+
- `PUM-011` -> `⏳ Pendiente`.
|
|
2089
|
+
|
|
2090
|
+
- ✅ PUMUKI-161: Ejecutar siguiente pendiente activo de Flux (`PUM-010`) para estabilizar persistencia de bundles/rules de `skills.frontend.*` entre iteraciones sin bootstrap manual repetitivo.
|
|
2091
|
+
- Resultado (2026-03-05):
|
|
2092
|
+
- `integrations/lifecycle/watch.ts` ahora detecta drift de skills coverage en runtime (`SKILLS_PLATFORM_COVERAGE_INCOMPLETE_HIGH`, `SKILLS_SCOPE_COMPLIANCE_INCOMPLETE_HIGH` y equivalentes `EVIDENCE_*`) y ejecuta auto-reconcile determinista (`policy reconcile --strict --apply`) dentro del mismo tick.
|
|
2093
|
+
- si el autofix aplica contrato (`WRITE_POLICY_AS_CODE_CONTRACT`), `watch` reevalúa el gate en la misma iteración para evitar bootstrap manual repetido.
|
|
2094
|
+
- cobertura añadida: test dedicado en `integrations/lifecycle/__tests__/watch.test.ts` (`auto-reconcilia policy en drift de skills y reevalua en la misma iteracion`).
|
|
2095
|
+
- leyenda Flux actualizada:
|
|
2096
|
+
- `PUM-010` -> `✅ Cerrado`
|
|
2097
|
+
- `PUM-011` -> `🚧 En construccion`.
|
|
2098
|
+
- Evidencia:
|
|
2099
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/watch.test.ts integrations/lifecycle/__tests__/cli.test.ts integrations/lifecycle/__tests__/policyReconcile.test.ts` -> `53 pass / 0 fail`.
|
|
2100
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
2101
|
+
|
|
2102
|
+
- 🚧 PUMUKI-162: Ejecutar siguiente pendiente activo de Flux (`PUM-011`) para cerrar paridad consumer de `watch --once --json` con `lastTick.changedFiles[]` y `lastTick.evaluatedFiles[]`.
|
|
2103
|
+
- Alcance:
|
|
2104
|
+
- verificar contrato JSON final en paquete publicado vs consumer runtime,
|
|
2105
|
+
- eliminar drift de payload entre core y distribución npm,
|
|
2106
|
+
- cerrar con validación reproducible en consumer (sin tocar código funcional del consumer).
|
|
2107
|
+
- Avance actual (2026-03-05):
|
|
2108
|
+
- verificado en core local (`bin/pumuki.js`): `lastTick.changedFiles[]` y `lastTick.evaluatedFiles[]` sí aparecen.
|
|
2109
|
+
- verificado en paquete publicado (`pumuki@latest`): esos campos aún no aparecen en JSON de `watch`.
|
|
2110
|
+
- conclusión: bug activo de rollout/distribución (no de lógica local), pendiente de corte/release para cerrar `PUM-011`.
|
|
@@ -87,6 +87,25 @@ const inferRuleStage = (raw: string): SkillsStage | undefined => {
|
|
|
87
87
|
return undefined;
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
+
const KNOWN_RULE_DEFAULT_STAGE: Readonly<Record<string, SkillsStage>> = {
|
|
91
|
+
'skills.backend.no-solid-violations': 'PRE_PUSH',
|
|
92
|
+
'skills.frontend.no-solid-violations': 'PRE_PUSH',
|
|
93
|
+
'skills.backend.enforce-clean-architecture': 'PRE_PUSH',
|
|
94
|
+
'skills.frontend.enforce-clean-architecture': 'PRE_PUSH',
|
|
95
|
+
'skills.backend.no-god-classes': 'PRE_PUSH',
|
|
96
|
+
'skills.frontend.no-god-classes': 'PRE_PUSH',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const resolveDefaultStageForKnownRule = (
|
|
100
|
+
ruleId: string,
|
|
101
|
+
inferredStage: SkillsStage | undefined
|
|
102
|
+
): SkillsStage | undefined => {
|
|
103
|
+
if (inferredStage) {
|
|
104
|
+
return inferredStage;
|
|
105
|
+
}
|
|
106
|
+
return KNOWN_RULE_DEFAULT_STAGE[ruleId];
|
|
107
|
+
};
|
|
108
|
+
|
|
90
109
|
const isRuleCandidateLine = (line: string): boolean => {
|
|
91
110
|
if (CHECK_RULE_PREFIX.test(line)) {
|
|
92
111
|
return true;
|
|
@@ -339,6 +358,11 @@ export const extractCompiledRulesFromSkillMarkdown = (params: {
|
|
|
339
358
|
const evaluationMode: SkillsRuleEvaluationMode =
|
|
340
359
|
knownRuleId || astNodeIds.length > 0 ? 'AUTO' : 'DECLARATIVE';
|
|
341
360
|
|
|
361
|
+
const inferredStage = inferRuleStage(rawLine);
|
|
362
|
+
const resolvedStage = knownRuleId
|
|
363
|
+
? resolveDefaultStageForKnownRule(knownRuleId, inferredStage)
|
|
364
|
+
: inferredStage;
|
|
365
|
+
|
|
342
366
|
rules.push({
|
|
343
367
|
id: nextId,
|
|
344
368
|
description,
|
|
@@ -346,7 +370,7 @@ export const extractCompiledRulesFromSkillMarkdown = (params: {
|
|
|
346
370
|
platform,
|
|
347
371
|
sourceSkill: params.sourceSkill,
|
|
348
372
|
sourcePath: params.sourcePath,
|
|
349
|
-
stage:
|
|
373
|
+
stage: resolvedStage,
|
|
350
374
|
confidence: inferRuleConfidence(rawLine),
|
|
351
375
|
locked: true,
|
|
352
376
|
evaluationMode,
|
|
@@ -256,6 +256,34 @@ const toSkillsRuntimeIrSource = (params: {
|
|
|
256
256
|
);
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
+
const buildRuleFindingMessage = (params: {
|
|
260
|
+
rule: SkillsCompiledRule;
|
|
261
|
+
mappedHeuristicRuleIds: ReadonlyArray<string>;
|
|
262
|
+
observedFilePaths?: ReadonlyArray<string>;
|
|
263
|
+
}): string => {
|
|
264
|
+
if (!params.rule.id.endsWith('.no-solid-violations')) {
|
|
265
|
+
return params.rule.description;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const relevantObservedPaths = (params.observedFilePaths ?? [])
|
|
269
|
+
.map((path) => normalizeObservedPath(path))
|
|
270
|
+
.filter((path) =>
|
|
271
|
+
isObservedPathForPlatform({
|
|
272
|
+
platform: params.rule.platform,
|
|
273
|
+
path,
|
|
274
|
+
})
|
|
275
|
+
);
|
|
276
|
+
const samplePaths = [...new Set(relevantObservedPaths)].slice(0, 3);
|
|
277
|
+
const astNodes = [...params.mappedHeuristicRuleIds].sort();
|
|
278
|
+
const astNodesToken = astNodes.length > 0 ? astNodes.join(',') : 'none';
|
|
279
|
+
const sampleToken = samplePaths.length > 0 ? ` sample_paths=[${samplePaths.join(', ')}].` : '';
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
`${params.rule.description} ` +
|
|
283
|
+
`Criteria: ast_nodes=[${astNodesToken}], observed_paths=${relevantObservedPaths.length}.${sampleToken}`
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
259
287
|
const stageApplies = (
|
|
260
288
|
currentStage: Exclude<GateStage, 'STAGED'>,
|
|
261
289
|
ruleStage?: Exclude<GateStage, 'STAGED'>
|
|
@@ -365,6 +393,11 @@ const toRuleDefinition = (params: {
|
|
|
365
393
|
rule: params.rule,
|
|
366
394
|
mappedHeuristicRuleIds,
|
|
367
395
|
});
|
|
396
|
+
const findingMessage = buildRuleFindingMessage({
|
|
397
|
+
rule: params.rule,
|
|
398
|
+
mappedHeuristicRuleIds,
|
|
399
|
+
observedFilePaths: params.observedFilePaths,
|
|
400
|
+
});
|
|
368
401
|
|
|
369
402
|
if (evaluationMode === 'AUTO') {
|
|
370
403
|
if (mappedHeuristicRuleIds.length === 0) {
|
|
@@ -395,7 +428,7 @@ const toRuleDefinition = (params: {
|
|
|
395
428
|
when,
|
|
396
429
|
then: {
|
|
397
430
|
kind: 'Finding',
|
|
398
|
-
message:
|
|
431
|
+
message: findingMessage,
|
|
399
432
|
code: toCode(params.rule.id),
|
|
400
433
|
source: runtimeIrSource,
|
|
401
434
|
},
|
|
@@ -168,6 +168,23 @@ const toWarnViolation = (code: string, message: string): AiGateViolation => ({
|
|
|
168
168
|
message,
|
|
169
169
|
});
|
|
170
170
|
|
|
171
|
+
const normalizeRepoStateLifecycleVersions = (repoState: RepoState): RepoState => {
|
|
172
|
+
const packageVersion = repoState.lifecycle.package_version;
|
|
173
|
+
const lifecycleVersion = repoState.lifecycle.lifecycle_version;
|
|
174
|
+
if (packageVersion === lifecycleVersion) {
|
|
175
|
+
return repoState;
|
|
176
|
+
}
|
|
177
|
+
const canonicalVersion = packageVersion ?? lifecycleVersion ?? null;
|
|
178
|
+
return {
|
|
179
|
+
...repoState,
|
|
180
|
+
lifecycle: {
|
|
181
|
+
...repoState.lifecycle,
|
|
182
|
+
package_version: canonicalVersion,
|
|
183
|
+
lifecycle_version: canonicalVersion,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
|
|
171
188
|
const toPositiveInteger = (value: unknown, fallback: number): number => {
|
|
172
189
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
173
190
|
return fallback;
|
|
@@ -272,6 +289,45 @@ const toDetectedSkillsPlatforms = (
|
|
|
272
289
|
return detected;
|
|
273
290
|
};
|
|
274
291
|
|
|
292
|
+
const toCoverageInferredPlatforms = (
|
|
293
|
+
coverage: NonNullable<Extract<EvidenceReadResult, { kind: 'valid' }>['evidence']['snapshot']['rules_coverage']> | undefined
|
|
294
|
+
): ReadonlyArray<PreWriteSkillsPlatform> => {
|
|
295
|
+
if (!coverage) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
const ruleIds = [...coverage.active_rule_ids, ...coverage.evaluated_rule_ids];
|
|
299
|
+
const inferred = new Set<PreWriteSkillsPlatform>();
|
|
300
|
+
for (const ruleId of ruleIds) {
|
|
301
|
+
for (const platform of PREWRITE_SKILLS_PLATFORMS) {
|
|
302
|
+
const prefix = PLATFORM_SKILLS_RULE_PREFIXES[platform];
|
|
303
|
+
if (ruleId.startsWith(prefix)) {
|
|
304
|
+
inferred.add(platform);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return PREWRITE_SKILLS_PLATFORMS.filter((platform) => inferred.has(platform));
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const toEffectiveSkillsPlatforms = (params: {
|
|
312
|
+
platforms: Extract<EvidenceReadResult, { kind: 'valid' }>['evidence']['platforms'] | undefined;
|
|
313
|
+
coverage: NonNullable<Extract<EvidenceReadResult, { kind: 'valid' }>['evidence']['snapshot']['rules_coverage']> | undefined;
|
|
314
|
+
}): ReadonlyArray<PreWriteSkillsPlatform> => {
|
|
315
|
+
const detectedPlatforms = toDetectedSkillsPlatforms(params.platforms);
|
|
316
|
+
if (detectedPlatforms.length === 0) {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
const inferredFromCoverage = toCoverageInferredPlatforms(params.coverage);
|
|
320
|
+
if (inferredFromCoverage.length === 0) {
|
|
321
|
+
return detectedPlatforms;
|
|
322
|
+
}
|
|
323
|
+
const inferredSet = new Set(inferredFromCoverage);
|
|
324
|
+
const intersection = detectedPlatforms.filter((platform) => inferredSet.has(platform));
|
|
325
|
+
if (intersection.length > 0) {
|
|
326
|
+
return intersection;
|
|
327
|
+
}
|
|
328
|
+
return detectedPlatforms;
|
|
329
|
+
};
|
|
330
|
+
|
|
275
331
|
const collectActiveRuleIdsCoverageViolations = (params: {
|
|
276
332
|
stage: AiGateStage;
|
|
277
333
|
evidence: Extract<EvidenceReadResult, { kind: 'valid' }>['evidence'];
|
|
@@ -280,14 +336,23 @@ const collectActiveRuleIdsCoverageViolations = (params: {
|
|
|
280
336
|
if (params.coverage.active_rule_ids.length > 0) {
|
|
281
337
|
return [];
|
|
282
338
|
}
|
|
283
|
-
const
|
|
284
|
-
|
|
339
|
+
const effectivePlatforms = toEffectiveSkillsPlatforms({
|
|
340
|
+
platforms: params.evidence.platforms,
|
|
341
|
+
coverage: params.coverage,
|
|
342
|
+
});
|
|
343
|
+
const inferredPlatforms =
|
|
344
|
+
effectivePlatforms.length > 0 ? [] : toCoverageInferredPlatforms(params.coverage);
|
|
345
|
+
const blockedPlatforms = effectivePlatforms.length > 0
|
|
346
|
+
? effectivePlatforms
|
|
347
|
+
: inferredPlatforms;
|
|
348
|
+
if (blockedPlatforms.length === 0) {
|
|
285
349
|
return [];
|
|
286
350
|
}
|
|
351
|
+
const detectionMode = effectivePlatforms.length > 0 ? 'detected' : 'inferred';
|
|
287
352
|
return [
|
|
288
353
|
toErrorViolation(
|
|
289
354
|
'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES',
|
|
290
|
-
`Active rules coverage is empty at ${params.stage} with
|
|
355
|
+
`Active rules coverage is empty at ${params.stage} with ${detectionMode} code platforms=[${blockedPlatforms.join(', ')}].`
|
|
291
356
|
),
|
|
292
357
|
];
|
|
293
358
|
};
|
|
@@ -296,7 +361,10 @@ const collectPreWritePlatformSkillsViolations = (params: {
|
|
|
296
361
|
evidence: Extract<EvidenceReadResult, { kind: 'valid' }>['evidence'];
|
|
297
362
|
coverage: NonNullable<Extract<EvidenceReadResult, { kind: 'valid' }>['evidence']['snapshot']['rules_coverage']>;
|
|
298
363
|
}): AiGateViolation[] => {
|
|
299
|
-
const detectedPlatforms =
|
|
364
|
+
const detectedPlatforms = toEffectiveSkillsPlatforms({
|
|
365
|
+
platforms: params.evidence.platforms,
|
|
366
|
+
coverage: params.coverage,
|
|
367
|
+
});
|
|
300
368
|
if (detectedPlatforms.length === 0) {
|
|
301
369
|
return [];
|
|
302
370
|
}
|
|
@@ -393,7 +461,10 @@ const collectPreWriteCrossPlatformCriticalViolations = (params: {
|
|
|
393
461
|
evidence: Extract<EvidenceReadResult, { kind: 'valid' }>['evidence'];
|
|
394
462
|
coverage: NonNullable<Extract<EvidenceReadResult, { kind: 'valid' }>['evidence']['snapshot']['rules_coverage']>;
|
|
395
463
|
}): AiGateViolation[] => {
|
|
396
|
-
const detectedPlatforms =
|
|
464
|
+
const detectedPlatforms = toEffectiveSkillsPlatforms({
|
|
465
|
+
platforms: params.evidence.platforms,
|
|
466
|
+
coverage: params.coverage,
|
|
467
|
+
});
|
|
397
468
|
if (detectedPlatforms.length === 0) {
|
|
398
469
|
return [];
|
|
399
470
|
}
|
|
@@ -447,7 +518,10 @@ const toSkillsContractAssessment = (params: {
|
|
|
447
518
|
}
|
|
448
519
|
|
|
449
520
|
const coverage = params.evidenceResult.evidence.snapshot.rules_coverage;
|
|
450
|
-
const detectedPlatforms =
|
|
521
|
+
const detectedPlatforms = toEffectiveSkillsPlatforms({
|
|
522
|
+
platforms: params.evidenceResult.evidence.platforms,
|
|
523
|
+
coverage,
|
|
524
|
+
});
|
|
451
525
|
if (detectedPlatforms.length === 0) {
|
|
452
526
|
return {
|
|
453
527
|
stage: params.stage,
|
|
@@ -977,7 +1051,9 @@ export const evaluateAiGate = (
|
|
|
977
1051
|
const protectedBranches = new Set(params.protectedBranches ?? Array.from(DEFAULT_PROTECTED_BRANCHES));
|
|
978
1052
|
const nowMs = activeDependencies.now();
|
|
979
1053
|
const evidenceResult = activeDependencies.readEvidenceResult(params.repoRoot);
|
|
980
|
-
const repoState =
|
|
1054
|
+
const repoState = normalizeRepoStateLifecycleVersions(
|
|
1055
|
+
activeDependencies.captureRepoState(params.repoRoot)
|
|
1056
|
+
);
|
|
981
1057
|
const policyStage = toPolicyStage(params.stage);
|
|
982
1058
|
const resolvedPolicy = activeDependencies.resolvePolicyForStage(
|
|
983
1059
|
policyStage,
|
|
@@ -17,6 +17,10 @@ const BLOCK_NEXT_ACTION_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
17
17
|
'git restore --staged . && separa cambios en commits más pequeños',
|
|
18
18
|
GIT_ATOMICITY_TOO_MANY_SCOPES:
|
|
19
19
|
'git restore --staged . && git add <scope>/ && git commit -m "<tipo>: <scope>"',
|
|
20
|
+
ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
|
|
21
|
+
'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
|
|
22
|
+
SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS:
|
|
23
|
+
'Aplica refactor incremental: extrae 1 componente/hook por commit y vuelve a ejecutar PRE_COMMIT.',
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
const severityWeight = (severity: string): number => {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import { readFileSync } from 'node:fs';
|
|
22
22
|
import { readEvidence, readEvidenceResult } from '../evidence/readEvidence';
|
|
23
23
|
import type { EvidenceReadResult } from '../evidence/readEvidence';
|
|
24
|
+
import { ensureRuntimeArtifactsIgnored } from '../lifecycle/artifacts';
|
|
24
25
|
|
|
25
26
|
const PRE_PUSH_UPSTREAM_REQUIRED_MESSAGE =
|
|
26
27
|
'pumuki pre-push blocked: branch has no upstream tracking reference. Configure upstream first (for example: git push --set-upstream origin <branch>) and retry.';
|
|
@@ -43,6 +44,10 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
43
44
|
EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en la rama actual y reintenta.',
|
|
44
45
|
EVIDENCE_RULES_COVERAGE_MISSING: 'Ejecuta auditoría completa para recalcular rules_coverage.',
|
|
45
46
|
EVIDENCE_RULES_COVERAGE_INCOMPLETE: 'Asegura coverage_ratio=1 y unevaluated=0.',
|
|
47
|
+
ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
|
|
48
|
+
'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
|
|
49
|
+
EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
|
|
50
|
+
'Reconcilia policy/skills y revalida PRE_WRITE: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
|
|
46
51
|
GITFLOW_PROTECTED_BRANCH: 'Trabaja en feature/* y evita ramas protegidas.',
|
|
47
52
|
PRE_PUSH_UPSTREAM_MISSING: 'Ejecuta: git push --set-upstream origin <branch>',
|
|
48
53
|
PRE_PUSH_UPSTREAM_MISALIGNED:
|
|
@@ -83,6 +88,7 @@ type StageRunnerDependencies = {
|
|
|
83
88
|
now: () => number;
|
|
84
89
|
writeHookGateSummary: (message: string) => void;
|
|
85
90
|
isQuietMode: () => boolean;
|
|
91
|
+
ensureRuntimeArtifactsIgnored: (repoRoot: string) => void;
|
|
86
92
|
};
|
|
87
93
|
|
|
88
94
|
const defaultDependencies: StageRunnerDependencies = {
|
|
@@ -132,6 +138,12 @@ const defaultDependencies: StageRunnerDependencies = {
|
|
|
132
138
|
process.stdout.write(`${message}\n`);
|
|
133
139
|
},
|
|
134
140
|
isQuietMode: () => process.argv.includes('--quiet'),
|
|
141
|
+
ensureRuntimeArtifactsIgnored: (repoRoot) => {
|
|
142
|
+
try {
|
|
143
|
+
ensureRuntimeArtifactsIgnored(repoRoot);
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
},
|
|
135
147
|
};
|
|
136
148
|
|
|
137
149
|
const getDependencies = (
|
|
@@ -160,9 +172,14 @@ const notifyGateBlockedForStage = (params: {
|
|
|
160
172
|
}): void => {
|
|
161
173
|
const repoRoot = params.dependencies.resolveRepoRoot();
|
|
162
174
|
const evidence = params.dependencies.readEvidence(repoRoot);
|
|
175
|
+
const stageFindings =
|
|
176
|
+
evidence?.snapshot.stage === params.stage
|
|
177
|
+
? evidence.snapshot.findings
|
|
178
|
+
: [];
|
|
179
|
+
const firstStageFinding = stageFindings[0];
|
|
163
180
|
const firstViolation = evidence?.ai_gate.violations[0];
|
|
164
|
-
const causeCode = firstViolation?.code ?? params.fallbackCauseCode;
|
|
165
|
-
const causeMessage = firstViolation?.message ?? params.fallbackCauseMessage;
|
|
181
|
+
const causeCode = firstStageFinding?.code ?? firstViolation?.code ?? params.fallbackCauseCode;
|
|
182
|
+
const causeMessage = firstStageFinding?.message ?? firstViolation?.message ?? params.fallbackCauseMessage;
|
|
166
183
|
const remediation =
|
|
167
184
|
BLOCKED_REMEDIATION_BY_CODE[causeCode]
|
|
168
185
|
?? params.fallbackRemediation
|
|
@@ -170,7 +187,9 @@ const notifyGateBlockedForStage = (params: {
|
|
|
170
187
|
params.dependencies.notifyGateBlocked({
|
|
171
188
|
repoRoot,
|
|
172
189
|
stage: params.stage,
|
|
173
|
-
totalViolations:
|
|
190
|
+
totalViolations: stageFindings.length > 0
|
|
191
|
+
? stageFindings.length
|
|
192
|
+
: evidence?.ai_gate.violations.length ?? 0,
|
|
174
193
|
causeCode,
|
|
175
194
|
causeMessage,
|
|
176
195
|
remediation,
|
|
@@ -338,6 +357,7 @@ export async function runPreCommitStage(
|
|
|
338
357
|
): Promise<number> {
|
|
339
358
|
const activeDependencies = getDependencies(dependencies);
|
|
340
359
|
const repoRoot = activeDependencies.resolveRepoRoot();
|
|
360
|
+
activeDependencies.ensureRuntimeArtifactsIgnored(repoRoot);
|
|
341
361
|
if (
|
|
342
362
|
enforceGitAtomicityGate({
|
|
343
363
|
dependencies: activeDependencies,
|
|
@@ -379,6 +399,7 @@ export async function runPrePushStage(
|
|
|
379
399
|
): Promise<number> {
|
|
380
400
|
const activeDependencies = getDependencies(dependencies);
|
|
381
401
|
const repoRoot = activeDependencies.resolveRepoRoot();
|
|
402
|
+
activeDependencies.ensureRuntimeArtifactsIgnored(repoRoot);
|
|
382
403
|
const upstreamRef = activeDependencies.resolveUpstreamRef();
|
|
383
404
|
if (!upstreamRef) {
|
|
384
405
|
const prePushInput = activeDependencies.readPrePushStdin();
|
|
@@ -519,6 +540,7 @@ export async function runCiStage(
|
|
|
519
540
|
): Promise<number> {
|
|
520
541
|
const activeDependencies = getDependencies(dependencies);
|
|
521
542
|
const repoRoot = activeDependencies.resolveRepoRoot();
|
|
543
|
+
activeDependencies.ensureRuntimeArtifactsIgnored(repoRoot);
|
|
522
544
|
const ciBaseRef = activeDependencies.resolveCiBaseRef();
|
|
523
545
|
if (
|
|
524
546
|
enforceGitAtomicityGate({
|