pumuki 6.3.124 → 6.3.125
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 +8 -0
- package/VERSION +1 -1
- package/integrations/git/aiGateRepoPolicyFindings.ts +1 -1
- package/integrations/lifecycle/cli.ts +9 -7
- package/integrations/mcp/enterpriseServer.ts +18 -1
- package/integrations/notifications/emitAuditSummaryNotification.ts +21 -1
- package/integrations/sdd/policy.ts +3 -1
- package/package.json +1 -1
- package/scripts/framework-menu-system-notifications-cause.ts +26 -0
- package/scripts/framework-menu-system-notifications-remediation.ts +26 -0
- package/scripts/framework-menu-system-notifications-tracking.ts +42 -0
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.125] - 2026-04-28
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Mensajes coherentes para bloqueos `gate.blocked`:** las notificaciones y diálogos traducen `EVIDENCE_GATE_BLOCKED`, tracking canónico y atomicidad a causas humanas en vez de mostrar copy interno en inglés.
|
|
14
|
+
- **Tracking como causa accionable:** cuando existe `active_entries` / `tracking_source`, la remediación prioriza corregir el MD de tracking y deja de sugerir `policy reconcile && sdd validate` como solución principal.
|
|
15
|
+
- **Cierre de `PUMUKI-INC-118`:** el evento central de bloqueo enriquece la causa con contexto de tracking antes de construir banners, diálogos macOS y payloads de sistema.
|
|
16
|
+
|
|
9
17
|
## [6.3.123] - 2026-04-28
|
|
10
18
|
|
|
11
19
|
### Fixed
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.125
|
|
@@ -54,7 +54,7 @@ export const collectTrackingActiveEntriesFromMarkdown = (
|
|
|
54
54
|
const bulletMatch = line.match(/^- 🚧 (`?P[0-9A-Za-z.-]+`?)/u);
|
|
55
55
|
if (bulletMatch) {
|
|
56
56
|
entries.push({
|
|
57
|
-
taskId: bulletMatch[1]!.
|
|
57
|
+
taskId: bulletMatch[1]!.replace(/`/gu, '').trim(),
|
|
58
58
|
lineNumber: index + 1,
|
|
59
59
|
});
|
|
60
60
|
continue;
|
|
@@ -1689,16 +1689,18 @@ export type PreWriteOpenSpecBootstrapTrace = {
|
|
|
1689
1689
|
export const buildPreWriteExperimentalEnableAdvisoryCommand = (
|
|
1690
1690
|
repoRoot: string = process.cwd()
|
|
1691
1691
|
): string =>
|
|
1692
|
-
`PUMUKI_EXPERIMENTAL_PRE_WRITE=advisory ${buildPinnedPumukiNpxCommand(
|
|
1693
|
-
|
|
1694
|
-
|
|
1692
|
+
`PUMUKI_EXPERIMENTAL_PRE_WRITE=advisory ${buildPinnedPumukiNpxCommand({
|
|
1693
|
+
repoRoot,
|
|
1694
|
+
executableAndArgs: 'pumuki sdd validate --stage=PRE_WRITE --json',
|
|
1695
|
+
})}`;
|
|
1695
1696
|
export const buildSddExperimentalEnableAdvisoryCommand = (
|
|
1696
1697
|
stage: SddStage,
|
|
1697
1698
|
repoRoot: string = process.cwd()
|
|
1698
1699
|
): string =>
|
|
1699
|
-
`PUMUKI_EXPERIMENTAL_SDD=advisory ${buildPinnedPumukiNpxCommand(
|
|
1700
|
-
|
|
1701
|
-
|
|
1700
|
+
`PUMUKI_EXPERIMENTAL_SDD=advisory ${buildPinnedPumukiNpxCommand({
|
|
1701
|
+
repoRoot,
|
|
1702
|
+
executableAndArgs: `pumuki sdd validate --stage=${stage} --json`,
|
|
1703
|
+
})}`;
|
|
1702
1704
|
const buildAnalyticsExperimentalEnableCommand = (action: AnalyticsHotspotsCommand): string =>
|
|
1703
1705
|
`PUMUKI_EXPERIMENTAL_ANALYTICS=advisory npx --yes --package pumuki@latest pumuki analytics hotspots ${action} --json`;
|
|
1704
1706
|
const SAAS_INGESTION_ENABLE_ADVISORY_COMMAND =
|
|
@@ -1804,7 +1806,7 @@ export const buildPreWriteExperimentalDisabledResult = (params: {
|
|
|
1804
1806
|
layer: 'experimental',
|
|
1805
1807
|
activation_env: 'PUMUKI_EXPERIMENTAL_PRE_WRITE',
|
|
1806
1808
|
legacy_activation_env: 'PUMUKI_PREWRITE_ENFORCEMENT',
|
|
1807
|
-
activation_command:
|
|
1809
|
+
activation_command: buildPreWriteExperimentalEnableAdvisoryCommand(),
|
|
1808
1810
|
},
|
|
1809
1811
|
},
|
|
1810
1812
|
});
|
|
@@ -5,6 +5,7 @@ import { execFileSync as runBinarySync } from 'node:child_process';
|
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { readLifecycleStatus, runLifecycleAudit } from '../lifecycle';
|
|
8
|
+
import { GitService } from '../git/GitService';
|
|
8
9
|
import { resolveMcpEnterpriseExperimentalFeature } from '../policy/experimentalFeatures';
|
|
9
10
|
import { evaluateSddPolicy, readSddStatus } from '../sdd';
|
|
10
11
|
import type { SddStage } from '../sdd';
|
|
@@ -337,6 +338,20 @@ type EnterpriseToolExecution = {
|
|
|
337
338
|
warnings?: ReadonlyArray<string>;
|
|
338
339
|
};
|
|
339
340
|
|
|
341
|
+
class EnterpriseRepoGitService extends GitService {
|
|
342
|
+
constructor(private readonly repoRoot: string) {
|
|
343
|
+
super();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
override resolveRepoRoot(): string {
|
|
347
|
+
return this.repoRoot;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
override runGit(args: ReadonlyArray<string>, cwd: string = this.repoRoot): string {
|
|
351
|
+
return super.runGit(args, cwd);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
340
355
|
type CriticalToolGuardResult = {
|
|
341
356
|
allowed: boolean;
|
|
342
357
|
stage: SddStage;
|
|
@@ -398,9 +413,11 @@ const executeEnterpriseTool = async (
|
|
|
398
413
|
switch (toolName) {
|
|
399
414
|
case 'pre_write_guard': {
|
|
400
415
|
const audit = await runLifecycleAudit({
|
|
401
|
-
repoRoot,
|
|
402
416
|
stage: 'PRE_WRITE',
|
|
403
417
|
auditMode: 'gate',
|
|
418
|
+
dependencies: {
|
|
419
|
+
git: new EnterpriseRepoGitService(repoRoot),
|
|
420
|
+
},
|
|
404
421
|
});
|
|
405
422
|
return {
|
|
406
423
|
name: toolName,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AiEvidenceV2_1 } from '../evidence/schema';
|
|
2
2
|
import { readEvidence } from '../evidence/readEvidence';
|
|
3
3
|
import type { AiGateCheckResult } from '../gate/evaluateAiGate';
|
|
4
|
+
import { appendTrackingActionableContext } from '../git/aiGateRepoPolicyFindings';
|
|
4
5
|
import {
|
|
5
6
|
emitSystemNotification,
|
|
6
7
|
type PumukiCriticalNotificationEvent,
|
|
@@ -34,6 +35,22 @@ const isTruthyEnvValue = (value?: string): boolean => {
|
|
|
34
35
|
return normalized === '1' || normalized === 'true' || normalized === 'yes';
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
const withTrackingContext = (params: {
|
|
39
|
+
repoRoot: string;
|
|
40
|
+
causeMessage: string;
|
|
41
|
+
}): string => {
|
|
42
|
+
if (
|
|
43
|
+
params.causeMessage.includes('active_entries=') ||
|
|
44
|
+
params.causeMessage.includes('tracking_source=')
|
|
45
|
+
) {
|
|
46
|
+
return params.causeMessage;
|
|
47
|
+
}
|
|
48
|
+
return appendTrackingActionableContext({
|
|
49
|
+
repoRoot: params.repoRoot,
|
|
50
|
+
message: params.causeMessage,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
37
54
|
export const shouldEmitAuditSummaryNotificationForStage = (
|
|
38
55
|
stage: AuditSummaryNotificationStage,
|
|
39
56
|
env: NodeJS.ProcessEnv = process.env
|
|
@@ -155,7 +172,10 @@ export const emitGateBlockedNotification = (
|
|
|
155
172
|
stage: params.stage,
|
|
156
173
|
totalViolations: params.totalViolations,
|
|
157
174
|
causeCode: params.causeCode,
|
|
158
|
-
causeMessage:
|
|
175
|
+
causeMessage: withTrackingContext({
|
|
176
|
+
repoRoot: params.repoRoot,
|
|
177
|
+
causeMessage: params.causeMessage,
|
|
178
|
+
}),
|
|
159
179
|
remediation: params.remediation,
|
|
160
180
|
},
|
|
161
181
|
repoRoot: params.repoRoot,
|
|
@@ -167,6 +167,7 @@ const evaluateActiveChangeCompleteness = (params: {
|
|
|
167
167
|
|
|
168
168
|
const evaluateSessionRequirements = (params: {
|
|
169
169
|
status: SddStatusPayload;
|
|
170
|
+
repoRoot: string;
|
|
170
171
|
autoRefreshEnabled: boolean;
|
|
171
172
|
autoRefreshAttempted: boolean;
|
|
172
173
|
autoRefreshError?: string;
|
|
@@ -335,7 +336,7 @@ export const evaluateSddPolicy = (params: {
|
|
|
335
336
|
experimentalSource: sddExperimentalFeature.source,
|
|
336
337
|
activation_env: sddExperimentalFeature.activationVariable,
|
|
337
338
|
legacy_activation_env: sddExperimentalFeature.legacyActivationVariable,
|
|
338
|
-
activation_command: buildSddExperimentalEnableCommand(params.stage,
|
|
339
|
+
activation_command: buildSddExperimentalEnableCommand(params.stage, repoRoot),
|
|
339
340
|
},
|
|
340
341
|
},
|
|
341
342
|
};
|
|
@@ -428,6 +429,7 @@ export const evaluateSddPolicy = (params: {
|
|
|
428
429
|
|
|
429
430
|
const sessionDecision = evaluateSessionRequirements({
|
|
430
431
|
status,
|
|
432
|
+
repoRoot,
|
|
431
433
|
autoRefreshEnabled,
|
|
432
434
|
autoRefreshAttempted,
|
|
433
435
|
autoRefreshError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.125",
|
|
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": {
|
|
@@ -3,8 +3,13 @@ import {
|
|
|
3
3
|
normalizeNotificationText,
|
|
4
4
|
truncateNotificationText,
|
|
5
5
|
} from './framework-menu-system-notifications-text';
|
|
6
|
+
import {
|
|
7
|
+
buildNotificationTrackingCauseSummary,
|
|
8
|
+
extractNotificationTrackingContext,
|
|
9
|
+
} from './framework-menu-system-notifications-tracking';
|
|
6
10
|
|
|
7
11
|
const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
|
|
12
|
+
EVIDENCE_GATE_BLOCKED: 'El gate de evidencia/gobernanza está bloqueado.',
|
|
8
13
|
EVIDENCE_MISSING: 'Falta evidencia para validar este paso.',
|
|
9
14
|
EVIDENCE_INVALID: 'La evidencia actual es inválida.',
|
|
10
15
|
EVIDENCE_CHAIN_INVALID: 'La cadena de evidencia no es válida.',
|
|
@@ -18,6 +23,13 @@ const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
18
23
|
OPENSPEC_MISSING: 'OpenSpec no está instalado en este repositorio.',
|
|
19
24
|
MCP_ENTERPRISE_RECEIPT_MISSING: 'Falta el recibo enterprise de MCP.',
|
|
20
25
|
BACKEND_AVOID_EXPLICIT_ANY: 'Se detectó uso de "any" explícito en backend.',
|
|
26
|
+
GIT_ATOMICITY_TOO_MANY_SCOPES: 'El cambio toca demasiados scopes para un commit atómico.',
|
|
27
|
+
TRACKING_CANONICAL_IN_PROGRESS_INVALID:
|
|
28
|
+
'El tracking canónico tiene una tarea activa inválida.',
|
|
29
|
+
TRACKING_CANONICAL_SOURCE_CONFLICT:
|
|
30
|
+
'Hay conflicto entre fuentes de tracking canónico.',
|
|
31
|
+
ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
|
|
32
|
+
'No hay reglas activas para cambios de código.',
|
|
21
33
|
};
|
|
22
34
|
|
|
23
35
|
const ENGLISH_CAUSE_HINTS = [
|
|
@@ -53,9 +65,19 @@ const buildGenericSpanishBlockedCauseSummary = (
|
|
|
53
65
|
|
|
54
66
|
const toKnownSpanishCauseFromMessage = (message: string): string | null => {
|
|
55
67
|
const normalized = message.toLowerCase();
|
|
68
|
+
const trackingContext = extractNotificationTrackingContext(message);
|
|
69
|
+
if (trackingContext) {
|
|
70
|
+
return buildNotificationTrackingCauseSummary(trackingContext);
|
|
71
|
+
}
|
|
56
72
|
if (normalized.includes('avoid explicit any')) {
|
|
57
73
|
return BLOCKED_CAUSE_SUMMARY_BY_CODE.BACKEND_AVOID_EXPLICIT_ANY;
|
|
58
74
|
}
|
|
75
|
+
if (normalized.includes('atomicity')) {
|
|
76
|
+
return BLOCKED_CAUSE_SUMMARY_BY_CODE.GIT_ATOMICITY_TOO_MANY_SCOPES;
|
|
77
|
+
}
|
|
78
|
+
if (normalized.includes('evidence ai gate status is blocked')) {
|
|
79
|
+
return BLOCKED_CAUSE_SUMMARY_BY_CODE.EVIDENCE_GATE_BLOCKED;
|
|
80
|
+
}
|
|
59
81
|
if (normalized.includes('evidence is stale')) {
|
|
60
82
|
return BLOCKED_CAUSE_SUMMARY_BY_CODE.EVIDENCE_STALE;
|
|
61
83
|
}
|
|
@@ -74,6 +96,10 @@ export const resolveBlockedCauseSummary = (
|
|
|
74
96
|
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
|
|
75
97
|
causeCode: string
|
|
76
98
|
): string => {
|
|
99
|
+
const trackingContext = extractNotificationTrackingContext(event.causeMessage);
|
|
100
|
+
if (trackingContext) {
|
|
101
|
+
return buildNotificationTrackingCauseSummary(trackingContext);
|
|
102
|
+
}
|
|
77
103
|
const mapped = BLOCKED_CAUSE_SUMMARY_BY_CODE[causeCode];
|
|
78
104
|
if (mapped) {
|
|
79
105
|
return mapped;
|
|
@@ -3,8 +3,16 @@ import {
|
|
|
3
3
|
normalizeNotificationText,
|
|
4
4
|
truncateNotificationText,
|
|
5
5
|
} from './framework-menu-system-notifications-text';
|
|
6
|
+
import {
|
|
7
|
+
extractNotificationTrackingContext,
|
|
8
|
+
TRACKING_BLOCKED_REMEDIATION,
|
|
9
|
+
} from './framework-menu-system-notifications-tracking';
|
|
10
|
+
|
|
11
|
+
type BlockedRemediationVariant = 'banner' | 'dialog';
|
|
6
12
|
|
|
7
13
|
const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
|
|
14
|
+
EVIDENCE_GATE_BLOCKED:
|
|
15
|
+
'Revisa status/doctor para ver la causa exacta del gate, corrígela y revalida.',
|
|
8
16
|
EVIDENCE_MISSING: 'Genera la evidencia del slice actual y vuelve a validar esta fase.',
|
|
9
17
|
EVIDENCE_INVALID: 'Regenera la evidencia de esta iteración y repite la validación.',
|
|
10
18
|
EVIDENCE_CHAIN_INVALID: 'Regenera la evidencia para restaurar la cadena de integridad y vuelve a validar.',
|
|
@@ -19,6 +27,10 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
19
27
|
BACKEND_AVOID_EXPLICIT_ANY: 'Sustituye `any` por tipos concretos en backend y relanza el gate.',
|
|
20
28
|
GIT_ATOMICITY_TOO_MANY_SCOPES: 'Divide el cambio por scope o en commits más pequeños y vuelve a ejecutar el gate.',
|
|
21
29
|
SOLID_HEURISTIC: 'Corrige la violación detectada y vuelve a ejecutar el gate.',
|
|
30
|
+
TRACKING_CANONICAL_IN_PROGRESS_INVALID: TRACKING_BLOCKED_REMEDIATION,
|
|
31
|
+
TRACKING_CANONICAL_SOURCE_CONFLICT: TRACKING_BLOCKED_REMEDIATION,
|
|
32
|
+
ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
|
|
33
|
+
'Ejecuta `pumuki policy reconcile --strict --json` y revalida antes de continuar.',
|
|
22
34
|
};
|
|
23
35
|
|
|
24
36
|
const BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT: Readonly<Record<BlockedRemediationVariant, number>> = {
|
|
@@ -39,6 +51,11 @@ const normalizeBlockedRemediation = (value: string): string =>
|
|
|
39
51
|
const resolveFallbackRemediation = (causeCode: string): string =>
|
|
40
52
|
BLOCKED_REMEDIATION_BY_CODE[causeCode] ?? GENERIC_BLOCKED_REMEDIATION;
|
|
41
53
|
|
|
54
|
+
const isGenericPolicyReconcileRemediation = (message: string): boolean => {
|
|
55
|
+
const normalized = message.toLowerCase();
|
|
56
|
+
return normalized.includes('policy reconcile') && normalized.includes('sdd validate');
|
|
57
|
+
};
|
|
58
|
+
|
|
42
59
|
const hasEnglishHints = (message: string): boolean => {
|
|
43
60
|
const normalized = message.toLowerCase();
|
|
44
61
|
return [
|
|
@@ -88,7 +105,16 @@ export const resolveBlockedRemediation = (
|
|
|
88
105
|
const fromEvent = event.remediation
|
|
89
106
|
? normalizeBlockedRemediation(event.remediation)
|
|
90
107
|
: '';
|
|
108
|
+
if (extractNotificationTrackingContext(event.causeMessage)) {
|
|
109
|
+
return truncateNotificationText(TRACKING_BLOCKED_REMEDIATION, maxLength);
|
|
110
|
+
}
|
|
91
111
|
if (fromEvent.length > 0) {
|
|
112
|
+
if (
|
|
113
|
+
causeCode === 'EVIDENCE_GATE_BLOCKED' &&
|
|
114
|
+
isGenericPolicyReconcileRemediation(fromEvent)
|
|
115
|
+
) {
|
|
116
|
+
return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
|
|
117
|
+
}
|
|
92
118
|
const translated = toKnownSpanishRemediationFromMessage(fromEvent, causeCode);
|
|
93
119
|
if (translated) {
|
|
94
120
|
return truncateNotificationText(translated, maxLength);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type NotificationTrackingContext = {
|
|
2
|
+
activeEntry?: string;
|
|
3
|
+
trackingSource?: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const TRACKING_CONTEXT_PATTERN = /\b(active_entries=|tracking_source=|TRACKING_CANONICAL_)/u;
|
|
7
|
+
|
|
8
|
+
export const extractNotificationTrackingContext = (
|
|
9
|
+
message?: string
|
|
10
|
+
): NotificationTrackingContext | null => {
|
|
11
|
+
if (!message || !TRACKING_CONTEXT_PATTERN.test(message)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const activeEntry = message
|
|
15
|
+
.match(/\bactive_entries=([^,\s]+)/u)?.[1]
|
|
16
|
+
?.replace(/@L\d+$/u, '')
|
|
17
|
+
.trim();
|
|
18
|
+
const trackingSource = message.match(/\btracking_source=([^\s]+)/u)?.[1]?.trim();
|
|
19
|
+
return {
|
|
20
|
+
activeEntry: activeEntry && activeEntry.length > 0 ? activeEntry : undefined,
|
|
21
|
+
trackingSource: trackingSource && trackingSource.length > 0 ? trackingSource : undefined,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const buildNotificationTrackingCauseSummary = (
|
|
26
|
+
context: NotificationTrackingContext
|
|
27
|
+
): string => {
|
|
28
|
+
if (context.activeEntry && context.trackingSource) {
|
|
29
|
+
return `Tracking bloqueado: ${context.activeEntry} en ${context.trackingSource}.`;
|
|
30
|
+
}
|
|
31
|
+
if (context.activeEntry) {
|
|
32
|
+
return `Tracking bloqueado: ${context.activeEntry}.`;
|
|
33
|
+
}
|
|
34
|
+
if (context.trackingSource) {
|
|
35
|
+
return `Tracking bloqueado en ${context.trackingSource}.`;
|
|
36
|
+
}
|
|
37
|
+
return 'El tracking canónico del repo bloquea la governance.';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const TRACKING_BLOCKED_REMEDIATION =
|
|
41
|
+
'Corrige el MD de tracking: deja una única tarea activa válida y vuelve a ejecutar el gate.';
|
|
42
|
+
|