pumuki 6.3.47 → 6.3.48

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.
@@ -5,6 +5,24 @@ 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.48)
9
+
10
+ - Anti-drift hardening for consumer validation flows:
11
+ - `pumuki watch` now applies manifest integrity guard and restores unexpected mutations to:
12
+ - `package.json`
13
+ - `package-lock.json`
14
+ - `pnpm-lock.yaml`
15
+ - `yarn.lock`
16
+ - If mutation is detected, watch blocks with `MANIFEST_MUTATION_DETECTED`.
17
+ - Hook hardening parity:
18
+ - `pre-commit` / `pre-push` manifest guard now covers npm + pnpm + yarn lockfiles (not only npm).
19
+ - Operational impact:
20
+ - avoids silent manifest/lockfile drift in consumers during validation loops unless upgrade is explicitly requested.
21
+ - Validation evidence:
22
+ - `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/watch.test.ts integrations/git/__tests__/stageRunners.test.ts` (`38 pass / 0 fail`)
23
+ - `npm run -s typecheck` (`PASS`)
24
+ - consumer smoke (Flux, local core bin): `watch --once --json` with hashes unchanged for `package.json` / `pnpm-lock.yaml`.
25
+
8
26
  ### 2026-03-05 (v6.3.47)
9
27
 
10
28
  - Manifest integrity hardening in hook stages (`PRE_COMMIT` / `PRE_PUSH`):
@@ -2516,4 +2516,34 @@
2516
2516
  - vigilancia:
2517
2517
  - `npm run -s validation:backlog-watch:tick`.
2518
2518
 
2519
- - 🚧 PUMUKI-187: Publicar corte con fix `#720`, actualizar consumers a la versión publicada y mover `PUMUKI-INC-063` de `🚧 REPORTED` a `✅ FIXED` con referencia de issue/commit/release.
2519
+ - PUMUKI-187: Publicar corte con fix `#720`, actualizar consumers a la versión publicada y mover `PUMUKI-INC-063` de `🚧 REPORTED` a `✅ FIXED` con referencia de issue/commit/release.
2520
+ - Resultado (2026-03-05):
2521
+ - release publicada: `pumuki@6.3.47`.
2522
+ - rollout en consumers:
2523
+ - `R_GO`: upgrade a `pumuki@6.3.47` (con `npm_config_engine_strict=false` por restricción de engines del consumer), `PRE_WRITE/PRE_COMMIT/PRE_PUSH` en `RC=0`.
2524
+ - `SAAS`: upgrade a `pumuki@6.3.47`, validación local con binarios `node node_modules/pumuki/bin/*` (bloqueos existentes del repo por reglas/gate, no regresión de manifests).
2525
+ - `Flux_training`: upgrade a `pumuki@6.3.47`, `PRE_WRITE/PRE_COMMIT` en `RC=0` y `PRE_PUSH` bloqueado por atomicidad del propio worktree (`changed_files=720`), sin mutación de manifests.
2526
+ - verificación de no mutación de manifests:
2527
+ - `R_GO`: `package.json` y `package-lock.json` sin cambios de hash antes/después de `validate + pre-commit + pre-push`.
2528
+ - `SAAS`: `package.json` y `package-lock.json` sin cambios de hash antes/después de `validate + pre-commit + pre-push`.
2529
+ - `Flux_training`: `package.json` y `pnpm-lock.yaml` sin cambios de hash antes/después de `validate + pre-commit + pre-push`.
2530
+ - MD canónico RuralGo actualizado:
2531
+ - `PUMUKI-INC-063` -> `✅ FIXED (#720, release 6.3.47)`.
2532
+ - activos High reducidos a `PUMUKI-INC-064` como único foco.
2533
+ - Evidencia:
2534
+ - `npm publish --access public` -> `+ pumuki@6.3.47`.
2535
+ - `npm ls pumuki --depth=0` (`R_GO`/`SAAS`) y `pnpm list -D pumuki --depth 0` (`Flux_training`) -> `6.3.47`.
2536
+ - hashes before/after de manifests en los tres consumers.
2537
+
2538
+ - ✅ PUMUKI-188: Cerrar trazabilidad de RuralGo tras validación de `PUMUKI-INC-064` y ajustar foco activo al siguiente hallazgo neto real.
2539
+ - Resultado (2026-03-05):
2540
+ - MD canónico RuralGo quedó 100% cerrado:
2541
+ - `✅ Cerrados: 100`
2542
+ - `🚧/⏳/⛔: 0`.
2543
+ - `PUMUKI-INC-064` se consolidó en `✅ FIXED` con referencia de release `6.3.47` y evidencia de `PRE_PUSH=ALLOW` sin workaround manual de `skills.sources.json`/`skills.lock.json`.
2544
+ - `issue #720` cerrada tras validación del fix de mutación de manifests.
2545
+ - Evidencia:
2546
+ - `sed -n '1,60p' /Users/.../R_GO/docs/.../pumuki-integration-feedback.md` (estado cerrado 100/0).
2547
+ - `gh issue close 720 --repo SwiftEnProfundidad/ast-intelligence-hooks ...`.
2548
+
2549
+ - 🚧 PUMUKI-189: Ejecutar siguiente bug prioritaria activa de Flux (`PUM-013`) sobre drift de dependencia en manifests durante validación y cerrar con fix + release + validación consumer.
@@ -241,7 +241,12 @@ const shouldRetryAfterPolicyReconcile = (params: {
241
241
 
242
242
  type HookStage = 'PRE_COMMIT' | 'PRE_PUSH';
243
243
  type HookPolicyTrace = NonNullable<ReturnType<typeof resolvePolicyForStage>['trace']>;
244
- const MANIFEST_GUARD_FILES = ['package.json', 'package-lock.json'] as const;
244
+ const MANIFEST_GUARD_FILES = [
245
+ 'package.json',
246
+ 'package-lock.json',
247
+ 'pnpm-lock.yaml',
248
+ 'yarn.lock',
249
+ ] as const;
245
250
 
246
251
  type ManifestGuardEntry = {
247
252
  relativePath: (typeof MANIFEST_GUARD_FILES)[number];
@@ -1,4 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
2
4
  import { setTimeout as sleepTimer } from 'node:timers/promises';
3
5
  import { isSeverityAtLeast, type Severity } from '../../core/rules/Severity';
4
6
  import type { GateScope } from '../git/runPlatformGateFacts';
@@ -125,6 +127,8 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
125
127
  SDD_SESSION_INVALID: 'Refresca la sesión SDD y vuelve a intentar.',
126
128
  OPENSPEC_MISSING: 'Instala OpenSpec y reintenta la validación.',
127
129
  MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP antes de continuar.',
130
+ MANIFEST_MUTATION_DETECTED:
131
+ 'Validación no debe mutar package.json/lockfiles. Revisa wiring y realiza upgrades solo con comando explícito.',
128
132
  };
129
133
 
130
134
  const WATCH_POLICY_RECONCILE_CODES = new Set<string>([
@@ -142,6 +146,61 @@ const THRESHOLD_TO_SEVERITY: Record<LifecycleWatchSeverityThreshold, Severity> =
142
146
  low: 'INFO',
143
147
  };
144
148
 
149
+ const WATCH_MANIFEST_GUARD_FILES = [
150
+ 'package.json',
151
+ 'package-lock.json',
152
+ 'pnpm-lock.yaml',
153
+ 'yarn.lock',
154
+ ] as const;
155
+
156
+ type WatchManifestGuardEntry = {
157
+ relativePath: (typeof WATCH_MANIFEST_GUARD_FILES)[number];
158
+ absolutePath: string;
159
+ existed: boolean;
160
+ contents: string;
161
+ };
162
+
163
+ const captureWatchManifestGuardSnapshot = (
164
+ repoRoot: string
165
+ ): Array<WatchManifestGuardEntry> =>
166
+ WATCH_MANIFEST_GUARD_FILES.map((relativePath) => {
167
+ const absolutePath = join(repoRoot, relativePath);
168
+ const existed = existsSync(absolutePath);
169
+ return {
170
+ relativePath,
171
+ absolutePath,
172
+ existed,
173
+ contents: existed ? readFileSync(absolutePath, 'utf8') : '',
174
+ };
175
+ });
176
+
177
+ const restoreWatchManifestGuardSnapshot = (
178
+ snapshot: ReadonlyArray<WatchManifestGuardEntry>
179
+ ): Array<string> => {
180
+ const mutated: Array<string> = [];
181
+ for (const entry of snapshot) {
182
+ const existsNow = existsSync(entry.absolutePath);
183
+ if (entry.existed) {
184
+ if (!existsNow) {
185
+ writeFileSync(entry.absolutePath, entry.contents, 'utf8');
186
+ mutated.push(entry.relativePath);
187
+ continue;
188
+ }
189
+ const current = readFileSync(entry.absolutePath, 'utf8');
190
+ if (current !== entry.contents) {
191
+ writeFileSync(entry.absolutePath, entry.contents, 'utf8');
192
+ mutated.push(entry.relativePath);
193
+ }
194
+ continue;
195
+ }
196
+ if (existsNow) {
197
+ unlinkSync(entry.absolutePath);
198
+ mutated.push(entry.relativePath);
199
+ }
200
+ }
201
+ return Array.from(new Set(mutated));
202
+ };
203
+
145
204
  const toGateScope = (scope: LifecycleWatchScope): GateScope => {
146
205
  if (scope === 'staged') {
147
206
  return { kind: 'staged' };
@@ -322,21 +381,52 @@ export const runLifecycleWatch = async (
322
381
  gateOutcome: 'BLOCK' | 'WARN' | 'ALLOW' | 'NO_EVIDENCE';
323
382
  topCodes: ReadonlyArray<string>;
324
383
  }> => {
325
- const gateExitCode = await activeDependencies.runPlatformGate({
384
+ const manifestSnapshot = captureWatchManifestGuardSnapshot(repoRoot);
385
+ let gateExitCode = await activeDependencies.runPlatformGate({
326
386
  policy: resolvedPolicy.policy,
327
387
  policyTrace: resolvedPolicy.trace,
328
388
  scope: gateScope,
329
389
  silent: true,
330
390
  });
331
391
  const evidence = activeDependencies.readEvidence(repoRoot);
332
- const allFindings = evidence?.snapshot.findings ?? [];
333
- const matchedFindings = allFindings.filter((finding) =>
392
+ const allFindingsBase = evidence?.snapshot.findings ?? [];
393
+ const matchedFindingsBase = allFindingsBase.filter((finding) =>
334
394
  isSeverityAtLeast(finding.severity, thresholdSeverity)
335
395
  );
396
+ const manifestMutations = restoreWatchManifestGuardSnapshot(manifestSnapshot);
397
+ const manifestMutationFinding: SnapshotFinding | null =
398
+ manifestMutations.length > 0
399
+ ? {
400
+ ruleId: 'governance.manifest.no-silent-mutation',
401
+ severity: 'ERROR',
402
+ code: 'MANIFEST_MUTATION_DETECTED',
403
+ message:
404
+ `Unexpected manifest mutation detected during watch and reverted: ${manifestMutations.join(', ')}.`,
405
+ file: manifestMutations[0] ?? 'package.json',
406
+ matchedBy: 'LifecycleWatch',
407
+ source: 'skills.backend.runtime-hygiene',
408
+ }
409
+ : null;
410
+ const allFindings = manifestMutationFinding
411
+ ? [...allFindingsBase, manifestMutationFinding]
412
+ : allFindingsBase;
413
+ const matchedFindings = manifestMutationFinding
414
+ ? [...matchedFindingsBase, manifestMutationFinding]
415
+ : matchedFindingsBase;
336
416
  const rawGateOutcome =
337
417
  evidence?.snapshot.outcome ??
338
418
  (gateExitCode !== 0 ? 'BLOCK' : 'NO_EVIDENCE');
339
- const gateOutcome = rawGateOutcome === 'PASS' ? 'ALLOW' : rawGateOutcome;
419
+ const gateOutcome = manifestMutationFinding
420
+ ? 'BLOCK'
421
+ : rawGateOutcome === 'PASS'
422
+ ? 'ALLOW'
423
+ : rawGateOutcome;
424
+ if (manifestMutationFinding) {
425
+ gateExitCode = 1;
426
+ process.stderr.write(
427
+ `[pumuki][watch-manifest-guard] unexpected manifest mutation detected and reverted: ${manifestMutations.join(', ')}\n`
428
+ );
429
+ }
340
430
  const topCodes = toTopCodes(matchedFindings);
341
431
  return {
342
432
  gateExitCode,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.47",
3
+ "version": "6.3.48",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {