pumuki 6.3.33 → 6.3.35

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.
@@ -97,6 +97,101 @@ Defined in `integrations/gate/stagePolicies.ts`:
97
97
  - `PRE_PUSH`: block `ERROR`, warn from `WARN`
98
98
  - `CI`: block `ERROR`, warn from `WARN`
99
99
 
100
+ ## Degraded mode (offline / air-gapped)
101
+
102
+ Pumuki supports a deterministic degraded contract by stage:
103
+
104
+ - `PRE_WRITE`
105
+ - `PRE_COMMIT`
106
+ - `PRE_PUSH`
107
+ - `CI`
108
+
109
+ Resolution precedence:
110
+
111
+ 1. Environment variables (`PUMUKI_DEGRADED_MODE=1`)
112
+ 2. File contract (`.pumuki/degraded-mode.json`)
113
+
114
+ Environment variables:
115
+
116
+ - `PUMUKI_DEGRADED_MODE`: enable/disable (`1|0`, `true|false`, `yes|no`)
117
+ - `PUMUKI_DEGRADED_REASON`: operator-visible reason
118
+ - `PUMUKI_DEGRADED_ACTION`: global default action (`allow|block`)
119
+ - `PUMUKI_DEGRADED_ACTION_PRE_WRITE`: per-stage override
120
+ - `PUMUKI_DEGRADED_ACTION_PRE_COMMIT`: per-stage override
121
+ - `PUMUKI_DEGRADED_ACTION_PRE_PUSH`: per-stage override
122
+ - `PUMUKI_DEGRADED_ACTION_CI`: per-stage override
123
+
124
+ File contract (`.pumuki/degraded-mode.json`):
125
+
126
+ ```json
127
+ {
128
+ "version": "1.0",
129
+ "enabled": true,
130
+ "reason": "offline-airgapped",
131
+ "stages": {
132
+ "PRE_WRITE": "block",
133
+ "PRE_COMMIT": "allow",
134
+ "PRE_PUSH": "block",
135
+ "CI": "block"
136
+ }
137
+ }
138
+ ```
139
+
140
+ Runtime behavior:
141
+
142
+ - `action=block`:
143
+ - gate adds finding `governance.degraded-mode.blocked`
144
+ - SDD returns `SDD_DEGRADED_MODE_BLOCKED`
145
+ - `action=allow`:
146
+ - gate adds informational finding `governance.degraded-mode.active`
147
+ - SDD returns `ALLOWED` with degraded metadata in `decision.details`
148
+
149
+ Traceability:
150
+
151
+ - `policyTrace.degraded` is emitted for gate stages.
152
+ - hook summaries include `degraded_mode`, `degraded_action`, and `degraded_reason` when active.
153
+ - evidence/telemetry include degraded metadata in policy trace when available.
154
+
155
+ ## SDD sync-docs learning artifact
156
+
157
+ When `pumuki sdd sync-docs` runs with `--change=<change-id>`, the command emits a machine-readable learning payload.
158
+
159
+ Write path:
160
+
161
+ - `openspec/changes/<change-id>/learning.json`
162
+
163
+ Payload schema (`v1.0`):
164
+
165
+ ```json
166
+ {
167
+ "version": "1.0",
168
+ "change_id": "rgo-1700-01",
169
+ "stage": "PRE_COMMIT",
170
+ "task": "P12.F2.T67",
171
+ "generated_at": "2026-03-04T10:05:00.000Z",
172
+ "failed_patterns": ["ai-gate.blocked"],
173
+ "successful_patterns": ["sync-docs.completed", "sync-docs.updated"],
174
+ "rule_updates": [
175
+ "ai-gate.unblock.required",
176
+ "ai-gate.violation.EVIDENCE_STALE.review"
177
+ ],
178
+ "gate_anomalies": ["ai-gate.violation.EVIDENCE_STALE"],
179
+ "sync_docs": {
180
+ "updated": true,
181
+ "file_paths": [
182
+ "docs/technical/08-validation/refactor/pumuki-integration-feedback.md"
183
+ ]
184
+ }
185
+ }
186
+ ```
187
+
188
+ Behavior:
189
+
190
+ - `--dry-run`: includes learning payload in JSON output with `learning.written=false` and does not write files.
191
+ - non dry-run: persists `learning.json` deterministically and reports digest/path in output.
192
+ - `rule_updates`: deterministic recommendations derived from evidence/gate signals (`missing`, `invalid`, `blocked`, `allowed`).
193
+ - dedicated command: `pumuki sdd learn --change=<id> [--stage=<stage>] [--task=<task>] [--dry-run] [--json]` generates/persists the same artifact without requiring `sync-docs`.
194
+
100
195
  ## Gate telemetry export (optional)
101
196
 
102
197
  Structured telemetry output is disabled by default and can be enabled with environment variables:
@@ -104,6 +199,9 @@ Structured telemetry output is disabled by default and can be enabled with envir
104
199
  - `PUMUKI_TELEMETRY_JSONL_PATH`:
105
200
  - JSONL file path for `telemetry_event_v1` records.
106
201
  - Accepts absolute path or repo-relative path.
202
+ - `PUMUKI_TELEMETRY_JSONL_MAX_BYTES`:
203
+ - Optional max size for JSONL file growth.
204
+ - When current file size plus next event exceeds this value, current file rotates to `<path>.1` before append.
107
205
  - `PUMUKI_TELEMETRY_OTEL_ENDPOINT`:
108
206
  - OTLP HTTP logs endpoint (`/v1/logs`).
109
207
  - `PUMUKI_TELEMETRY_OTEL_SERVICE_NAME`:
@@ -116,6 +214,21 @@ Notes:
116
214
  - You can enable JSONL only, OTel only, or both.
117
215
  - If unset, no telemetry export is attempted.
118
216
  - Gate execution remains deterministic even when OTel endpoint is unavailable (best-effort dispatch).
217
+ - Rotation is opt-in; without `PUMUKI_TELEMETRY_JSONL_MAX_BYTES`, append behavior remains unchanged.
218
+
219
+ Quick verification (JSONL):
220
+
221
+ ```bash
222
+ PUMUKI_TELEMETRY_JSONL_PATH=.pumuki/artifacts/gate-telemetry.jsonl \
223
+ npx --yes --package pumuki@latest pumuki-pre-commit
224
+ ```
225
+
226
+ Expected JSONL keys for enterprise audit ingestion:
227
+
228
+ - `schema=telemetry_event_v1` with `schema_version=1.0`
229
+ - `stage`, `gate_outcome`, `severity_counts`
230
+ - `policy.bundle`, `policy.hash`, `policy.version`, `policy.signature`, `policy.policy_source`
231
+ - `policy.validation_status`, `policy.validation_code` (when policy-as-code validation is available)
119
232
 
120
233
  ## Heuristic pilot flag
121
234
 
@@ -3,6 +3,53 @@
3
3
  This file tracks the active deterministic framework line used in this repository.
4
4
  Detailed commit history remains available through Git history (`git log` / `git show`).
5
5
 
6
+ ## 2026-03 (enterprise hardening updates)
7
+
8
+ ### 2026-03-04 (v6.3.35)
9
+
10
+ - SDD enterprise incremental hardening shipped:
11
+ - New dedicated command `pumuki sdd learn` with `--change`, optional `--stage/--task`, `--dry-run`, and `--json`.
12
+ - `sync-docs` learning artifact now emits deterministic signal-derived `rule_updates`.
13
+ - Learning payload remains deterministic across missing/invalid/blocked/allowed evidence states.
14
+ - Traceability:
15
+ - implementation PRs: `#593`, `#596`, `#599`
16
+ - Consumer quick verification:
17
+ - `npx --yes --package pumuki@latest pumuki sdd learn --change=rgo-quickstart-01 --dry-run --json`
18
+ - `npx --yes --package pumuki@latest pumuki sdd sync-docs --change=rgo-quickstart-01 --stage=PRE_WRITE --task=P12.F2.T68 --dry-run --json`
19
+ - expected signal:
20
+ - `command=pumuki sdd learn` available in CLI help.
21
+ - `learning.artifact.rule_updates` populated deterministically when evidence is blocked/invalid.
22
+
23
+ ### 2026-03-04 (v6.3.34)
24
+
25
+ - Telemetry hardening shipped for long-running enterprise repos:
26
+ - Gate telemetry JSONL supports deterministic size-guard rotation with `PUMUKI_TELEMETRY_JSONL_MAX_BYTES`.
27
+ - Enterprise contract suite now includes profile `telemetry-rotation` to validate JSONL rollover behavior.
28
+ - Traceability:
29
+ - implementation PRs: `#574`, `#577`
30
+ - release PR: `#580`
31
+ - Consumer quick verification:
32
+ - `npx --yes --package pumuki@latest pumuki doctor --json`
33
+ - `npm run -s validation:contract-suite:enterprise -- --json`
34
+ - `PUMUKI_TELEMETRY_JSONL_PATH=.pumuki/artifacts/gate-telemetry.jsonl PUMUKI_TELEMETRY_JSONL_MAX_BYTES=512 npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json`
35
+ - expected signal:
36
+ - contract suite list includes profile `telemetry-rotation`
37
+ - after repeated validations, files `gate-telemetry.jsonl` and `gate-telemetry.jsonl.1` are present
38
+
39
+ ### 2026-03-04 (v6.3.33)
40
+
41
+ - Runtime hardening shipped for enterprise diagnosis:
42
+ - `pumuki doctor --deep --json` now includes explicit compatibility contract payload under `deep.contract`.
43
+ - New deep check id `compatibility-contract` validates the active contract for `pumuki/openspec/hooks/adapter`.
44
+ - Traceability:
45
+ - implementation PR: `#563`
46
+ - release PR: `#567`
47
+ - Consumer quick verification:
48
+ - `npx --yes --package pumuki@latest pumuki doctor --deep --json`
49
+ - expected signal in JSON:
50
+ - `deep.checks` contains an item with `id=compatibility-contract`
51
+ - `deep.contract.overall` resolves to `compatible` or `incompatible` deterministically
52
+
6
53
  ## 2026-02 (enterprise-refactor updates)
7
54
 
8
55
  ### 2026-02-27 (v6.3.24)
@@ -7,7 +7,8 @@
7
7
  ## Estado actual
8
8
  - Plan activo: `docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md`
9
9
  - Estado del plan: EN CURSO
10
- - Task activa (`🚧`): `P12.F2.T55` (publicar patch release del contrato de compatibilidad `doctor --deep`, issue `#565`).
10
+ - Última task cerrada (`✅`): `P12.F2.T68` (nuevo comando `pumuki sdd learn`, issue `#597`, PR `#599`).
11
+ - Task activa (`🚧`): `P12.F2.T69` (publicar release `6.3.35` con cierre SDD incremental en npm).
11
12
 
12
13
  ## Historial resumido
13
14
  - No se mantienen MDs históricos de seguimiento en este repositorio.
@@ -1780,11 +1780,173 @@ Criterio de salida F5:
1780
1780
  - `npm run -s validation:contract-suite:enterprise -- --json`
1781
1781
  - `npm run -s validation:tracking-single-active`
1782
1782
 
1783
- - 🚧 `P12.F2.T55` Publicar patch release con el contrato de compatibilidad de `doctor --deep` ya mergeado en `#563`.
1783
+ - `P12.F2.T55` Publicar patch release con el contrato de compatibilidad de `doctor --deep` ya mergeado en `#563`.
1784
+ - cierre ejecutado:
1785
+ - issue creada y cerrada: `#565`.
1786
+ - rama release creada y mergeada: `release/6.3.33` -> `#567` (`https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/567`).
1787
+ - publicación npm completada: `pumuki@6.3.33`.
1788
+ - commit de merge en `develop`: `6f36830`.
1789
+ - evidencia:
1790
+ - `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/doctor.test.ts integrations/lifecycle/__tests__/cli.test.ts`
1791
+ - `npm run -s typecheck`
1792
+ - `npm run -s validation:contract-suite:enterprise -- --json`
1793
+ - `npm run -s validation:package-manifest`
1794
+ - `npm publish --access public`
1795
+ - `npm view pumuki version` => `6.3.33`
1796
+
1797
+ - ✅ `P12.F2.T56` Publicar documentación de adopción de `6.3.33` (release notes + verificación operativa rápida).
1798
+ - cierre ejecutado:
1799
+ - issue documental creada: `#568`.
1800
+ - release notes actualizadas con sección `2026-03-04 (v6.3.33)` y comandos de verificación para consumidores.
1801
+ - documentación operativa alineada con el release publicado `6.3.33`.
1802
+ - evidencia:
1803
+ - `docs/RELEASE_NOTES.md` incluye delta funcional de `doctor --deep` para `compatibility-contract`.
1804
+ - `npm view pumuki version` => `6.3.33`
1805
+ - `npm run -s validation:tracking-single-active`
1806
+
1807
+ - ✅ `P12.F2.T57` Ejecutar siguiente mejora estratégica: export de telemetría de gates en JSONL determinista para auditoría enterprise.
1808
+ - cierre ejecutado:
1809
+ - issue creada y cerrada: `#569`.
1810
+ - rama técnica creada y mergeada: `feature/569-telemetry-jsonl-audit-hardening` -> `#571` (`https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/571`).
1811
+ - commit de merge en `develop`: `b98ef25`.
1812
+ - evidencia:
1813
+ - `npx --yes tsx@4.21.0 --test integrations/telemetry/__tests__/gateTelemetry.test.ts`
1814
+ - `npm run -s typecheck`
1815
+ - `npm run -s validation:tracking-single-active`
1816
+ - `docs/CONFIGURATION.md` actualizado con verificación operativa JSONL y claves esperadas.
1817
+
1818
+ - ✅ `P12.F2.T58` Ejecutar siguiente mejora estratégica: rotación/guard de tamaño para telemetría JSONL en repos enterprise de larga duración.
1819
+ - cierre ejecutado:
1820
+ - issue creada y cerrada: `#572`.
1821
+ - rama técnica creada y mergeada: `feature/572-telemetry-jsonl-rotation-guard` -> `#574` (`https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/574`).
1822
+ - commit de merge en `develop`: `b3a5b27`.
1823
+ - evidencia:
1824
+ - `npx --yes tsx@4.21.0 --test integrations/telemetry/__tests__/gateTelemetry.test.ts`
1825
+ - `npm run -s typecheck`
1826
+ - `npm run -s validation:tracking-single-active`
1827
+ - `docs/CONFIGURATION.md` actualizado con `PUMUKI_TELEMETRY_JSONL_MAX_BYTES`.
1828
+
1829
+ - ✅ `P12.F2.T59` Ejecutar siguiente mejora estratégica: cubrir la rotación JSONL en la suite contractual enterprise.
1830
+ - cierre ejecutado:
1831
+ - issue creada y cerrada: `#575`.
1832
+ - rama técnica creada y mergeada: `feature/575-contract-suite-telemetry-rotation` -> `#577` (`https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/577`).
1833
+ - commit de merge en `develop`: `89e2045`.
1834
+ - evidencia:
1835
+ - `npx --yes tsx@4.21.0 --test scripts/__tests__/enterprise-contract-suite-contract.test.ts scripts/__tests__/enterprise-contract-suite-report-lib.test.ts scripts/__tests__/enterprise-contract-suite-args-lib.test.ts integrations/telemetry/__tests__/gateTelemetry.test.ts`
1836
+ - `npm run -s typecheck`
1837
+ - `npm run -s validation:contract-suite:enterprise -- --json`
1838
+ - `npm run -s validation:tracking-single-active`
1839
+ - `docs/validation/README.md` actualizado con perfiles activos de la suite contractual.
1840
+
1841
+ - ✅ `P12.F2.T60` Publicar patch release con mejoras de telemetría (`#574`, `#577`) para disponibilidad inmediata en npm.
1842
+ - cierre ejecutado:
1843
+ - issue de release creada y cerrada: `#578`.
1844
+ - rama release creada y mergeada: `release/6.3.34` -> `#580` (`https://github.com/SwiftEnProfundidad/ast-intelligence-hooks/pull/580`).
1845
+ - publicación npm completada: `pumuki@6.3.34`.
1846
+ - evidencia:
1847
+ - `npm run -s validation:tracking-single-active`
1848
+ - `npm view pumuki version` => `6.3.34`
1849
+ - `gh issue close 578 --comment "Closed via PR #580 and npm publish 6.3.34 (telemetry rotation + contract profile telemetry-rotation)."`
1850
+
1851
+ - ✅ `P12.F2.T61` Publicar documentación de adopción de `6.3.34` (release notes + verificación operativa rápida).
1852
+ - cierre ejecutado:
1853
+ - issue documental creada y cerrada: `#581`.
1854
+ - `docs/RELEASE_NOTES.md` actualizado con entrada `2026-03-04 (v6.3.34)` y comandos de verificación operativa.
1855
+ - trazabilidad sincronizada en plan activo + registro maestro.
1856
+ - evidencia:
1857
+ - `npm run -s validation:tracking-single-active`
1858
+ - `gh issue close 581 --comment "Closed via release notes update for 6.3.34 and tracking sync."`
1859
+
1860
+ - ✅ `P12.F2.T62` Ejecutar la siguiente mejora estratégica pendiente: modo degradado offline/air-gapped para gates enterprise (`#583`).
1861
+ - cierre ejecutado:
1862
+ - contrato reutilizable implementado en `integrations/gate/degradedMode.ts` con precedencia `env -> .pumuki/degraded-mode.json`.
1863
+ - `resolvePolicyForStage` expone `trace.degraded` por stage.
1864
+ - `runPlatformGate` aplica enforcement:
1865
+ - `action=block` -> finding `governance.degraded-mode.blocked` + gate `BLOCK`.
1866
+ - `action=allow` -> finding `governance.degraded-mode.active` + gate permite continuar.
1867
+ - hooks exitosos publican resumen degradado (`degraded_mode`, `degraded_action`, `degraded_reason`).
1868
+ - SDD integra el contrato:
1869
+ - `action=block` -> `SDD_DEGRADED_MODE_BLOCKED`.
1870
+ - `action=allow` -> `ALLOWED` con metadata degradada en `decision.details`.
1871
+ - documentación operativa actualizada en `docs/CONFIGURATION.md`.
1872
+ - evidencia:
1873
+ - `npx --yes tsx@4.21.0 --test integrations/gate/__tests__/stagePolicies.test.ts integrations/git/__tests__/runPlatformGate.test.ts integrations/git/__tests__/hookGateSummary.test.ts integrations/sdd/__tests__/policy.test.ts` => `58 passed, 0 failed`.
1874
+ - `npx --yes tsx@4.21.0 --test integrations/telemetry/__tests__/gateTelemetry.test.ts` => `4 passed`.
1875
+ - `npx --yes tsx@4.21.0 --test integrations/git/__tests__/EvidenceService.test.ts` => `15 passed`.
1876
+
1877
+ - ✅ `P12.F2.T63` Iniciar SDD pendiente enterprise: extender `pumuki sdd sync-docs` con contexto explícito (`--change`, `--stage`, `--task`) y trazabilidad canónica (`#585`).
1878
+ - cierre ejecutado:
1879
+ - `runSddSyncDocs` amplía contrato con contexto explícito (`change`, `stage`, `task`) en salida canónica.
1880
+ - `parseLifecycleCliArgs` soporta:
1881
+ - `pumuki sdd sync-docs --change=<id> --stage=<stage> --task=<task-id> [--dry-run] [--json]`.
1882
+ - `runLifecycleCli` propaga contexto al runtime y lo imprime en salida texto/json.
1883
+ - cobertura de tests ampliada para parsing + ejecución `dry-run` + contrato JSON de contexto.
1884
+ - evidencia:
1885
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `31 passed, 0 failed`.
1886
+
1887
+ - ✅ `P12.F2.T64` Continuar SDD pendiente enterprise: ampliar `sync-docs` a múltiples documentos canónicos y secciones gestionadas (`#587`).
1888
+ - cierre ejecutado:
1889
+ - `runSddSyncDocs` soporta objetivos múltiples (`targets`) con secciones gestionadas por archivo.
1890
+ - actualización determinista por archivo/sección y salida con diffs por cada target.
1891
+ - fail-safe preservado: conflicto en cualquier archivo aborta el sync antes de cualquier escritura parcial.
1892
+ - cobertura de tests ampliada para:
1893
+ - actualización multi-documento.
1894
+ - garantía fail-safe sin escrituras parciales.
1895
+ - evidencia:
1896
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `33 passed, 0 failed`.
1897
+
1898
+ - ✅ `P12.F2.T65` Continuar SDD pendiente enterprise: artefacto de aprendizaje machine-readable por change durante `sync-docs` (`#589`).
1899
+ - cierre ejecutado:
1900
+ - `runSddSyncDocs` incorpora contrato `learning` en salida con:
1901
+ - `path`, `written`, `digest`, `artifact`.
1902
+ - cuando se provee `--change`:
1903
+ - `--dry-run`: no escribe archivo y retorna payload de aprendizaje.
1904
+ - ejecución normal: persiste `openspec/changes/<change>/learning.json`.
1905
+ - cobertura de tests ampliada para dry-run y persistencia real del artefacto.
1906
+ - documentación mínima del esquema añadida en `docs/CONFIGURATION.md`.
1907
+ - evidencia:
1908
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `34 passed, 0 failed`.
1909
+
1910
+ - ✅ `P12.F2.T66` Continuar SDD pendiente enterprise: enriquecer `learning.json` con señales reales de gate/evidence (`#591`).
1911
+ - cierre ejecutado:
1912
+ - `runSddSyncDocs` incorpora señales deterministas de runtime para poblar:
1913
+ - `failed_patterns`
1914
+ - `successful_patterns`
1915
+ - `gate_anomalies`
1916
+ - el cálculo cubre casos `evidence missing/invalid/valid` y decisión SDD permitida/bloqueada.
1917
+ - soporte explícito para inyectar lector de evidencia en tests (`evidenceReader`) sin romper contrato CLI.
1918
+ - PR mergeada: `#593` (`commit 0da619a3804ec939bc33385cfc57032c195b4ee1`).
1919
+ - evidencia:
1920
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `35 passed, 0 failed`.
1921
+ - `npm run -s typecheck` => `exit 0`.
1922
+
1923
+ - ✅ `P12.F2.T67` Continuar SDD pendiente enterprise: hacer `rule_updates` accionable en `learning.json` (`#594`).
1924
+ - cierre ejecutado:
1925
+ - `runSddSyncDocs` ahora deriva `rule_updates` de señales de evidencia/gate (`missing`, `invalid`, `blocked`, `allowed`) de forma determinista.
1926
+ - añadidas recomendaciones específicas por familia de señal (`evidence.*`, `ai-gate.*`, `sdd.*`, `snapshot.*`).
1927
+ - ampliada cobertura en `syncDocs` para escenarios `invalid` y `allow` además de `blocked`/persistencia.
1928
+ - documentación de contrato actualizada en `docs/CONFIGURATION.md`.
1929
+ - PR mergeada: `#596` (`commit 51d894c8835c9b04cb57dd810197ac7fc3d0cd3e`).
1930
+ - evidencia:
1931
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `37 passed, 0 failed`.
1932
+ - `npm run -s typecheck` => `exit 0`.
1933
+
1934
+ - ✅ `P12.F2.T68` Continuar SDD pendiente enterprise: añadir comando dedicado `pumuki sdd learn` (`#597`).
1935
+ - cierre ejecutado:
1936
+ - nuevo runtime `runSddLearn` en capa SDD con salida/persistencia de `learning.json` sin depender de `sync-docs`.
1937
+ - CLI ampliada con:
1938
+ - `pumuki sdd learn --change=<id> --stage=<stage> --task=<task-id> [--dry-run] [--json]`.
1939
+ - cobertura de regresión añadida en parser y ejecución (`cli.test.ts`) y documentación actualizada en `docs/CONFIGURATION.md`.
1940
+ - PR mergeada: `#599` (`commit c971c643883ccce679c0c5cdb3363bdd6e6cace6`).
1941
+ - evidencia:
1942
+ - `npx --yes tsx@4.21.0 --test integrations/sdd/__tests__/syncDocs.test.ts integrations/lifecycle/__tests__/cli.test.ts` => `38 passed, 0 failed`.
1943
+ - `npm run -s typecheck` => `exit 0`.
1944
+
1945
+ - 🚧 `P12.F2.T69` Publicar release `6.3.35` con cierre SDD incremental.
1784
1946
  - salida esperada:
1785
- - issue de release creada con alcance/doD verificable (`#565`).
1786
- - versión npm nueva publicada con el fix de `#562`.
1787
- - trazabilidad cerrada en plan activo + registro maestro con evidencia de publicación.
1947
+ - bump de versión (`package.json`, `package-lock.json`) + nota de release.
1948
+ - publicación npm exitosa y verificación `npm view pumuki version`.
1949
+ - smoke mínimo con `npx --yes --package pumuki@latest pumuki --help`.
1788
1950
 
1789
1951
  Criterio de salida F6:
1790
1952
  - veredicto final trazable y cierre administrativo completo.
@@ -16,6 +16,11 @@ Este directorio contiene solo documentación oficial y estable de validación pa
16
16
  - Master de seguimiento: `docs/registro-maestro-de-seguimiento.md`.
17
17
  - Plan activo: `docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md`.
18
18
  - Suite contractual enterprise (MVP): `npm run -s validation:contract-suite:enterprise`.
19
+ - Perfiles activos del reporte JSON:
20
+ - `minimal`
21
+ - `block`
22
+ - `minimal-repeat`
23
+ - `telemetry-rotation`
19
24
 
20
25
  ## Política de higiene
21
26
 
@@ -91,6 +91,11 @@ export type RulesetState = {
91
91
  source?: string;
92
92
  validation_status?: 'valid' | 'invalid' | 'expired' | 'unknown-source';
93
93
  validation_code?: string;
94
+ degraded_mode_enabled?: boolean;
95
+ degraded_mode_action?: 'allow' | 'block';
96
+ degraded_mode_reason?: string;
97
+ degraded_mode_source?: 'env' | 'file:.pumuki/degraded-mode.json';
98
+ degraded_mode_code?: 'DEGRADED_MODE_ALLOWED' | 'DEGRADED_MODE_BLOCKED';
94
99
  };
95
100
 
96
101
  export type HumanIntentConfidence = 'high' | 'medium' | 'low' | 'unset';
@@ -0,0 +1,131 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ export type DegradedStage = 'PRE_WRITE' | 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
5
+ export type DegradedAction = 'allow' | 'block';
6
+ export type DegradedCode = 'DEGRADED_MODE_ALLOWED' | 'DEGRADED_MODE_BLOCKED';
7
+
8
+ export type DegradedResolution = {
9
+ enabled: true;
10
+ action: DegradedAction;
11
+ reason: string;
12
+ source: 'env' | 'file:.pumuki/degraded-mode.json';
13
+ code: DegradedCode;
14
+ };
15
+
16
+ const DEGRADED_MODE_FILE_PATH = '.pumuki/degraded-mode.json';
17
+
18
+ const toBooleanFlag = (value: string | undefined): boolean | null => {
19
+ const normalized = value?.trim().toLowerCase();
20
+ if (!normalized) {
21
+ return null;
22
+ }
23
+ if (normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on') {
24
+ return true;
25
+ }
26
+ if (normalized === '0' || normalized === 'false' || normalized === 'no' || normalized === 'off') {
27
+ return false;
28
+ }
29
+ return null;
30
+ };
31
+
32
+ const toAction = (value: unknown): DegradedAction | null => {
33
+ if (typeof value !== 'string') {
34
+ return null;
35
+ }
36
+ const normalized = value.trim().toLowerCase();
37
+ if (normalized === 'allow') {
38
+ return 'allow';
39
+ }
40
+ if (normalized === 'block') {
41
+ return 'block';
42
+ }
43
+ return null;
44
+ };
45
+
46
+ const toCode = (action: DegradedAction): DegradedCode => {
47
+ return action === 'block' ? 'DEGRADED_MODE_BLOCKED' : 'DEGRADED_MODE_ALLOWED';
48
+ };
49
+
50
+ const resolveActionFromEnv = (stage: DegradedStage): DegradedAction => {
51
+ const stageKey = `PUMUKI_DEGRADED_ACTION_${stage}`;
52
+ const byStage = toAction(process.env[stageKey]);
53
+ if (byStage) {
54
+ return byStage;
55
+ }
56
+ const globalAction = toAction(process.env.PUMUKI_DEGRADED_ACTION);
57
+ if (globalAction) {
58
+ return globalAction;
59
+ }
60
+ return 'allow';
61
+ };
62
+
63
+ type DegradedModeFile = {
64
+ version?: unknown;
65
+ enabled?: unknown;
66
+ reason?: unknown;
67
+ action?: unknown;
68
+ stages?: unknown;
69
+ };
70
+
71
+ const isRecord = (value: unknown): value is Record<string, unknown> => {
72
+ return typeof value === 'object' && value !== null;
73
+ };
74
+
75
+ const resolveFromFile = (
76
+ repoRoot: string,
77
+ stage: DegradedStage
78
+ ): DegradedResolution | undefined => {
79
+ const configPath = join(repoRoot, DEGRADED_MODE_FILE_PATH);
80
+ if (!existsSync(configPath)) {
81
+ return undefined;
82
+ }
83
+ try {
84
+ const parsed = JSON.parse(readFileSync(configPath, 'utf8')) as DegradedModeFile;
85
+ if (parsed.enabled !== true) {
86
+ return undefined;
87
+ }
88
+ const stageActions = isRecord(parsed.stages) ? parsed.stages : undefined;
89
+ const stageAction = stageActions ? toAction(stageActions[stage]) : null;
90
+ const globalAction = toAction(parsed.action);
91
+ const action = stageAction ?? globalAction ?? 'allow';
92
+ const reason =
93
+ typeof parsed.reason === 'string' && parsed.reason.trim().length > 0
94
+ ? parsed.reason.trim()
95
+ : 'degraded-mode';
96
+ return {
97
+ enabled: true,
98
+ action,
99
+ reason,
100
+ source: 'file:.pumuki/degraded-mode.json',
101
+ code: toCode(action),
102
+ };
103
+ } catch {
104
+ return undefined;
105
+ }
106
+ };
107
+
108
+ export const resolveDegradedMode = (
109
+ stage: DegradedStage,
110
+ repoRoot: string = process.cwd()
111
+ ): DegradedResolution | undefined => {
112
+ const enabledFromEnv = toBooleanFlag(process.env.PUMUKI_DEGRADED_MODE);
113
+ if (enabledFromEnv === true) {
114
+ const action = resolveActionFromEnv(stage);
115
+ const reason =
116
+ process.env.PUMUKI_DEGRADED_REASON?.trim().length
117
+ ? process.env.PUMUKI_DEGRADED_REASON.trim()
118
+ : 'degraded-mode';
119
+ return {
120
+ enabled: true,
121
+ action,
122
+ reason,
123
+ source: 'env',
124
+ code: toCode(action),
125
+ };
126
+ }
127
+ if (enabledFromEnv === false) {
128
+ return undefined;
129
+ }
130
+ return resolveFromFile(repoRoot, stage);
131
+ };
@@ -10,6 +10,7 @@ import {
10
10
  loadSkillsPolicy,
11
11
  } from '../config/skillsPolicy';
12
12
  import type { SkillsStage } from '../config/skillsLock';
13
+ import { resolveDegradedMode } from './degradedMode';
13
14
 
14
15
  const heuristicsPromotionStageAllowList = new Set<GateStage>([
15
16
  'PRE_COMMIT',
@@ -58,6 +59,13 @@ export type ResolvedStagePolicy = {
58
59
  message: string;
59
60
  strict: boolean;
60
61
  };
62
+ degraded?: {
63
+ enabled: true;
64
+ action: 'allow' | 'block';
65
+ reason: string;
66
+ source: 'env' | 'file:.pumuki/degraded-mode.json';
67
+ code: 'DEGRADED_MODE_ALLOWED' | 'DEGRADED_MODE_BLOCKED';
68
+ };
61
69
  };
62
70
  };
63
71
 
@@ -519,6 +527,7 @@ export const resolvePolicyForStage = (
519
527
  stage: SkillsStage,
520
528
  repoRoot: string = process.cwd()
521
529
  ): ResolvedStagePolicy => {
530
+ const degraded = resolveDegradedMode(stage, repoRoot);
522
531
  const hardModeState = resolveHardModeState(repoRoot);
523
532
  if (hardModeState.enabled) {
524
533
  const profileName = hardModeState.profileName;
@@ -553,6 +562,7 @@ export const resolvePolicyForStage = (
553
562
  signature: policyAsCode.signature,
554
563
  policySource: policyAsCode.policySource,
555
564
  validation: policyAsCode.validation,
565
+ ...(degraded ? { degraded } : {}),
556
566
  },
557
567
  };
558
568
  }
@@ -586,6 +596,7 @@ export const resolvePolicyForStage = (
586
596
  signature: policyAsCode.signature,
587
597
  policySource: policyAsCode.policySource,
588
598
  validation: policyAsCode.validation,
599
+ ...(degraded ? { degraded } : {}),
589
600
  },
590
601
  };
591
602
  }
@@ -621,6 +632,7 @@ export const resolvePolicyForStage = (
621
632
  signature: policyAsCode.signature,
622
633
  policySource: policyAsCode.policySource,
623
634
  validation: policyAsCode.validation,
635
+ ...(degraded ? { degraded } : {}),
624
636
  },
625
637
  };
626
638
  };
@@ -112,6 +112,15 @@ export class EvidenceService implements IEvidenceService {
112
112
  validation_code: params.policyTrace.validation.code,
113
113
  }
114
114
  : {}),
115
+ ...(params.policyTrace.degraded
116
+ ? {
117
+ degraded_mode_enabled: params.policyTrace.degraded.enabled,
118
+ degraded_mode_action: params.policyTrace.degraded.action,
119
+ degraded_mode_reason: params.policyTrace.degraded.reason,
120
+ degraded_mode_source: params.policyTrace.degraded.source,
121
+ degraded_mode_code: params.policyTrace.degraded.code,
122
+ }
123
+ : {}),
115
124
  });
116
125
  }
117
126