pumuki 6.3.116 → 6.3.117

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/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [6.3.117] - 2026-04-28
10
+
11
+ ### Fixed
12
+
13
+ - **`pumuki audit --json` queda accionable cuando bloquea:** la salida ahora expone `findings_count`, `blocking_findings_count` y `findings`; si el gate bloquea sin findings persistidos, emite un blocker sintético `AUDIT_BLOCKED_WITHOUT_FINDINGS` para evitar JSON no accionable.
14
+ - **Auditoría previa a edición:** `pumuki audit --stage=PRE_WRITE --json` queda soportado para dar a consumers como RuralGo un contrato machine-friendly antes de continuar feature work.
15
+ - **Tracking externo prioritario:** el reset interno queda alineado con el bloqueo vivo de RuralGo `PUMUKI-INC-109`/`PUMUKI-INC-110`.
16
+
9
17
  ## [6.3.116] - 2026-04-25
10
18
 
11
19
  ### Fixed
package/VERSION CHANGED
@@ -1 +1 @@
1
- v6.3.116
1
+ v6.3.117
@@ -1,12 +1,23 @@
1
1
  import { readEvidence } from '../evidence/readEvidence';
2
- import { GitService } from '../git/GitService';
2
+ import type { AiEvidenceV2_1, SnapshotFinding } from '../evidence/schema';
3
+ import { GitService, type IGitService } from '../git/GitService';
3
4
  import { hasAllowedExtension } from '../git/gitDiffUtils';
4
5
  import { runPlatformGate } from '../git/runPlatformGate';
5
6
  import { evaluatePlatformGateFindings } from '../git/runPlatformGateEvaluation';
6
7
  import { DEFAULT_FACT_FILE_EXTENSIONS } from '../git/runPlatformGateFacts';
7
- import { resolvePolicyForStage } from '../gate/stagePolicies';
8
+ import { resolvePolicyForStage, type ResolvedStagePolicy } from '../gate/stagePolicies';
8
9
 
9
- export type LifecycleAuditStage = 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
10
+ export type LifecycleAuditStage = 'PRE_WRITE' | 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
11
+
12
+ export type LifecycleAuditFinding = {
13
+ ruleId: string;
14
+ severity: string;
15
+ code: string;
16
+ message: string;
17
+ file: string;
18
+ lines?: SnapshotFinding['lines'];
19
+ blocking: boolean;
20
+ };
10
21
 
11
22
  export type LifecycleAuditResult = {
12
23
  command: 'pumuki audit';
@@ -18,14 +29,24 @@ export type LifecycleAuditResult = {
18
29
  files_scanned: number | null;
19
30
  untracked_matching_extensions_count: number;
20
31
  snapshot_outcome: string | null;
32
+ findings_count: number;
33
+ blocking_findings_count: number;
34
+ findings: ReadonlyArray<LifecycleAuditFinding>;
21
35
  policy_reconcile_hint: string;
22
36
  };
23
37
 
38
+ type LifecycleAuditDependencies = {
39
+ git: IGitService;
40
+ readEvidence: typeof readEvidence;
41
+ resolvePolicyForStage: typeof resolvePolicyForStage;
42
+ runPlatformGate: typeof runPlatformGate;
43
+ };
44
+
24
45
  const POLICY_RECONCILE_HINT =
25
46
  'If .pumuki/policy-as-code.json signatures drift after a pumuki upgrade, run: pumuki policy reconcile --apply';
26
47
 
27
48
  const countUntrackedMatchingExtensions = (
28
- git: GitService,
49
+ git: Pick<IGitService, 'resolveRepoRoot' | 'runGit'>,
29
50
  extensions: ReadonlyArray<string>
30
51
  ): number => {
31
52
  const repoRoot = git.resolveRepoRoot();
@@ -37,13 +58,80 @@ const countUntrackedMatchingExtensions = (
37
58
  .filter((path) => hasAllowedExtension(path, extensions)).length;
38
59
  };
39
60
 
61
+ const isFindingBlocking = (finding: SnapshotFinding): boolean => {
62
+ if (typeof finding.blocking === 'boolean') {
63
+ return finding.blocking;
64
+ }
65
+ return finding.severity === 'CRITICAL' || finding.severity === 'ERROR';
66
+ };
67
+
68
+ const toLifecycleAuditFinding = (finding: SnapshotFinding): LifecycleAuditFinding => ({
69
+ ruleId: finding.ruleId,
70
+ severity: finding.severity,
71
+ code: finding.code,
72
+ message: finding.message,
73
+ file: finding.file,
74
+ ...(typeof finding.lines !== 'undefined' ? { lines: finding.lines } : {}),
75
+ blocking: isFindingBlocking(finding),
76
+ });
77
+
78
+ const buildBlockedWithoutFindingsFallback = (params: {
79
+ stage: LifecycleAuditStage;
80
+ gateExitCode: number;
81
+ snapshotOutcome: string | null;
82
+ }): LifecycleAuditFinding => ({
83
+ ruleId: 'audit.gate.blocked-without-findings',
84
+ severity: 'ERROR',
85
+ code: 'AUDIT_BLOCKED_WITHOUT_FINDINGS',
86
+ message:
87
+ `Audit ${params.stage} exited ${params.gateExitCode} with outcome=${params.snapshotOutcome ?? 'unknown'} ` +
88
+ 'but produced no machine-readable findings. Re-run with this Pumuki version and inspect policy/SDD output; this fallback keeps JSON actionable.',
89
+ file: '.ai_evidence.json',
90
+ blocking: true,
91
+ });
92
+
93
+ const extractAuditFindings = (params: {
94
+ evidence: AiEvidenceV2_1 | undefined;
95
+ gateExitCode: number;
96
+ stage: LifecycleAuditStage;
97
+ snapshotOutcome: string | null;
98
+ }): ReadonlyArray<LifecycleAuditFinding> => {
99
+ const findings = Array.isArray(params.evidence?.snapshot.findings)
100
+ ? params.evidence.snapshot.findings.map(toLifecycleAuditFinding)
101
+ : [];
102
+ if (findings.length > 0) {
103
+ return findings;
104
+ }
105
+ if (params.gateExitCode !== 0 || params.snapshotOutcome === 'BLOCK') {
106
+ return [
107
+ buildBlockedWithoutFindingsFallback({
108
+ stage: params.stage,
109
+ gateExitCode: params.gateExitCode,
110
+ snapshotOutcome: params.snapshotOutcome,
111
+ }),
112
+ ];
113
+ }
114
+ return [];
115
+ };
116
+
40
117
  export const runLifecycleAudit = async (params: {
41
118
  stage: LifecycleAuditStage;
42
119
  auditMode: 'gate' | 'engine';
120
+ dependencies?: Partial<LifecycleAuditDependencies>;
43
121
  }): Promise<LifecycleAuditResult> => {
44
- const git = new GitService();
122
+ const activeDependencies: LifecycleAuditDependencies = {
123
+ git: new GitService(),
124
+ readEvidence,
125
+ resolvePolicyForStage,
126
+ runPlatformGate,
127
+ ...params.dependencies,
128
+ };
129
+ const git = activeDependencies.git;
45
130
  const repoRoot = git.resolveRepoRoot();
46
- const resolved = resolvePolicyForStage(params.stage, repoRoot);
131
+ const resolved: ResolvedStagePolicy = activeDependencies.resolvePolicyForStage(
132
+ params.stage,
133
+ repoRoot
134
+ );
47
135
  const extensions = DEFAULT_FACT_FILE_EXTENSIONS;
48
136
  const untrackedMatchingExtensionsCount = countUntrackedMatchingExtensions(git, extensions);
49
137
 
@@ -76,8 +164,8 @@ export const runLifecycleAudit = async (params: {
76
164
  auditMode: 'gate' as const,
77
165
  };
78
166
 
79
- const gateExitCode = await runPlatformGate(gateParams);
80
- const evidence = readEvidence(repoRoot);
167
+ const gateExitCode = await activeDependencies.runPlatformGate(gateParams);
168
+ const evidence = activeDependencies.readEvidence(repoRoot);
81
169
  const filesScanned =
82
170
  typeof evidence?.snapshot.files_scanned === 'number' &&
83
171
  Number.isFinite(evidence.snapshot.files_scanned)
@@ -85,6 +173,12 @@ export const runLifecycleAudit = async (params: {
85
173
  : null;
86
174
  const snapshotOutcome =
87
175
  typeof evidence?.snapshot.outcome === 'string' ? evidence.snapshot.outcome : null;
176
+ const findings = extractAuditFindings({
177
+ evidence,
178
+ gateExitCode,
179
+ stage: params.stage,
180
+ snapshotOutcome,
181
+ });
88
182
 
89
183
  return {
90
184
  command: 'pumuki audit',
@@ -96,6 +190,9 @@ export const runLifecycleAudit = async (params: {
96
190
  files_scanned: filesScanned,
97
191
  untracked_matching_extensions_count: untrackedMatchingExtensionsCount,
98
192
  snapshot_outcome: snapshotOutcome,
193
+ findings_count: findings.length,
194
+ blocking_findings_count: findings.filter((finding) => finding.blocking).length,
195
+ findings,
99
196
  policy_reconcile_hint: POLICY_RECONCILE_HINT,
100
197
  };
101
198
  };
@@ -188,7 +188,7 @@ Pumuki lifecycle commands:
188
188
  pumuki remove
189
189
  pumuki update [--latest|--spec=<package-spec>]
190
190
  pumuki doctor [--remote-checks] [--deep] [--parity] [--json]
191
- pumuki audit [--stage=PRE_COMMIT|PRE_PUSH|CI] [--engine] [--json]
191
+ pumuki audit [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--engine] [--json]
192
192
  pumuki status [--json] [--remote-checks]
193
193
  pumuki watch [--stage=PRE_COMMIT|PRE_PUSH|CI] [--scope=workingTree|staged|repoAndStaged|repo] [--severity=critical|high|medium|low] [--interval-ms=<n>] [--notify-cooldown-ms=<n>] [--no-notify] [--once|--iterations=<n>] [--json]
194
194
  pumuki loop run --objective=<text> [--max-attempts=<n>] [--json]
@@ -266,13 +266,7 @@ const parseSddStage = (value?: string): SddStage => {
266
266
  };
267
267
 
268
268
  const parseAuditStage = (value?: string): LifecycleAuditStage => {
269
- const stage = parseSddStage(value);
270
- if (stage === 'PRE_WRITE') {
271
- throw new Error(
272
- 'PRE_WRITE is not supported for "pumuki audit". Use PRE_COMMIT, PRE_PUSH or CI (aliases GREEN, REFACTOR, CLOSE).'
273
- );
274
- }
275
- return stage;
269
+ return parseSddStage(value);
276
270
  };
277
271
 
278
272
  const parseSddEvidencePath = (value: string): string => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.116",
3
+ "version": "6.3.117",
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": {