pumuki 6.3.79 → 6.3.80
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/VERSION +1 -1
- package/docs/operations/RELEASE_NOTES.md +4 -4
- package/integrations/evidence/trackingContract.ts +2 -0
- package/package.json +1 -1
- package/scripts/check-tracking-single-active.sh +1 -1
- package/scripts/framework-menu-system-notifications-cause.ts +8 -0
- package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
- package/scripts/framework-menu-system-notifications-remediation.ts +72 -26
- package/scripts/framework-menu-system-notifications-text.ts +7 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.80
|
|
@@ -6,11 +6,11 @@ This file keeps only the operational highlights and rollout notes that matter wh
|
|
|
6
6
|
|
|
7
7
|
## 2026-04 (CLI stability and macOS notifications)
|
|
8
8
|
|
|
9
|
-
### 2026-04-
|
|
9
|
+
### 2026-04-17 (v6.3.80)
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **Rollout recomendado**: publicar `pumuki@6.3.
|
|
11
|
+
- **Tracking canónico**: la línea viva acepta el formato contractual `[🚧] - tarea` en el plan maestro y en tablas con backticks, tanto en el parser de tracking como en el guardrail shell de una sola task activa.
|
|
12
|
+
- **Notificaciones bloqueantes**: copy y remediaciones de `gate.blocked` salen en español cuando el payload legacy llega en inglés; el banner macOS corta por palabra y conserva una solución breve y accionable.
|
|
13
|
+
- **Rollout recomendado**: publicar `pumuki@6.3.80` y repin ordenado `RuralGo -> SAAS -> Flux`, revalidando después `pumuki status`, `pumuki doctor` y hooks Git en cada consumer.
|
|
14
14
|
|
|
15
15
|
### 2026-04-16 (v6.3.78)
|
|
16
16
|
|
|
@@ -15,8 +15,10 @@ const TRACKING_PRIORITY_SOURCE = new Set(['AGENTS.md']);
|
|
|
15
15
|
const IN_PROGRESS_PATTERNS = [
|
|
16
16
|
/^- Estado:\s*🚧/m,
|
|
17
17
|
/^- 🚧 (\`?P[0-9A-Za-z.-]+\`?)/m,
|
|
18
|
+
/^\`?\[\s*🚧\s*\]\s*-\`?/m,
|
|
18
19
|
/^\|\s*🚧(?:\s|\|)/m,
|
|
19
20
|
/^\|[^|\n]+\|\s*🚧(?:\s|\|)/m,
|
|
21
|
+
/^\|[^|\n]+\|\s*\`?\[\s*🚧\s*\]\s*-\`?/m,
|
|
20
22
|
] as const;
|
|
21
23
|
|
|
22
24
|
const toRepoRelativePath = (repoRoot: string, absolutePath: string): string => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.80",
|
|
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": {
|
|
@@ -31,7 +31,7 @@ for FILE in "${FILES[@]}"; do
|
|
|
31
31
|
fi
|
|
32
32
|
|
|
33
33
|
if [[ "${FILE}" == "PUMUKI-RESET-MASTER-PLAN.md" ]]; then
|
|
34
|
-
ACTIVE_PATTERN='^- Estado
|
|
34
|
+
ACTIVE_PATTERN='(^- Estado:\s*🚧\b)|(^`?\[\s*🚧\s*\]\s*-`?)|(^\|\s*[^|]+\|\s*`?\[\s*🚧\s*\]\s*-`?)'
|
|
35
35
|
else
|
|
36
36
|
ACTIVE_PATTERN='^- 🚧 (`?P[0-9A-Za-z.-]+`?)'
|
|
37
37
|
fi
|
|
@@ -18,6 +18,8 @@ const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
18
18
|
OPENSPEC_MISSING: 'OpenSpec no está instalado en este repositorio.',
|
|
19
19
|
MCP_ENTERPRISE_RECEIPT_MISSING: 'Falta el recibo enterprise de MCP.',
|
|
20
20
|
BACKEND_AVOID_EXPLICIT_ANY: 'Se detectó uso de "any" explícito en backend.',
|
|
21
|
+
GIT_ATOMICITY_TOO_MANY_SCOPES: 'Se han cambiado demasiados scopes en el mismo commit.',
|
|
22
|
+
SOLID_HEURISTIC: 'Se detectó una violación estructural en el cambio actual.',
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
const toKnownSpanishCauseFromMessage = (message: string): string | null => {
|
|
@@ -31,6 +33,12 @@ const toKnownSpanishCauseFromMessage = (message: string): string | null => {
|
|
|
31
33
|
if (normalized.includes('no upstream tracking reference')) {
|
|
32
34
|
return BLOCKED_CAUSE_SUMMARY_BY_CODE.PRE_PUSH_UPSTREAM_MISSING;
|
|
33
35
|
}
|
|
36
|
+
if (normalized.includes('too many scopes changed') || normalized.includes('atomicity budget exceeded')) {
|
|
37
|
+
return BLOCKED_CAUSE_SUMMARY_BY_CODE.GIT_ATOMICITY_TOO_MANY_SCOPES;
|
|
38
|
+
}
|
|
39
|
+
if (normalized.includes('heuristic violation')) {
|
|
40
|
+
return BLOCKED_CAUSE_SUMMARY_BY_CODE.SOLID_HEURISTIC;
|
|
41
|
+
}
|
|
34
42
|
return null;
|
|
35
43
|
};
|
|
36
44
|
|
|
@@ -22,7 +22,7 @@ export const buildGateBlockedPayload = (
|
|
|
22
22
|
resolveBlockedCauseSummary(event, causeCode),
|
|
23
23
|
72
|
|
24
24
|
);
|
|
25
|
-
const remediation = resolveBlockedRemediation(event, causeCode);
|
|
25
|
+
const remediation = resolveBlockedRemediation(event, causeCode, { variant: 'banner' });
|
|
26
26
|
return {
|
|
27
27
|
title: '🔴 Pumuki bloqueado',
|
|
28
28
|
subtitle: `${projectPrefix}${event.stage} · ${causeSummary}`,
|
|
@@ -4,55 +4,101 @@ import {
|
|
|
4
4
|
truncateNotificationText,
|
|
5
5
|
} from './framework-menu-system-notifications-text';
|
|
6
6
|
|
|
7
|
+
type BlockedRemediationVariant = 'banner' | 'dialog';
|
|
8
|
+
|
|
7
9
|
const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
|
|
8
|
-
EVIDENCE_MISSING: 'Genera evidencia del slice actual y vuelve a validar
|
|
9
|
-
EVIDENCE_INVALID: 'Regenera la evidencia de
|
|
10
|
+
EVIDENCE_MISSING: 'Genera la evidencia del slice actual y vuelve a validar esta fase.',
|
|
11
|
+
EVIDENCE_INVALID: 'Regenera la evidencia de esta iteración y repite la validación.',
|
|
10
12
|
EVIDENCE_CHAIN_INVALID: 'Regenera la evidencia para restaurar la cadena de integridad y vuelve a validar.',
|
|
11
|
-
EVIDENCE_STALE: '
|
|
12
|
-
EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en esta rama y
|
|
13
|
-
EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera evidencia desde este repositorio y
|
|
14
|
-
PRE_PUSH_UPSTREAM_MISSING: 'Configura upstream con `git push --set-upstream origin <branch>` y
|
|
15
|
-
SDD_SESSION_MISSING: 'Abre sesión SDD del change activo y repite la validación
|
|
16
|
-
SDD_SESSION_INVALID: 'Refresca la sesión SDD
|
|
17
|
-
OPENSPEC_MISSING: 'Instala OpenSpec en
|
|
18
|
-
MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP y vuelve a
|
|
19
|
-
BACKEND_AVOID_EXPLICIT_ANY: '
|
|
13
|
+
EVIDENCE_STALE: 'Refresca la evidencia y vuelve a validar PRE_WRITE/PRE_PUSH.',
|
|
14
|
+
EVIDENCE_BRANCH_MISMATCH: 'Regenera la evidencia en esta rama y repite la validación.',
|
|
15
|
+
EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera la evidencia desde este repositorio y vuelve a validar.',
|
|
16
|
+
PRE_PUSH_UPSTREAM_MISSING: 'Configura upstream con `git push --set-upstream origin <branch>` y repite PRE_PUSH.',
|
|
17
|
+
SDD_SESSION_MISSING: 'Abre la sesión SDD del change activo y repite la validación.',
|
|
18
|
+
SDD_SESSION_INVALID: 'Refresca la sesión SDD activa y vuelve a validar esta fase.',
|
|
19
|
+
OPENSPEC_MISSING: 'Instala OpenSpec en este repositorio y vuelve a validar el gate.',
|
|
20
|
+
MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP y vuelve a validar.',
|
|
21
|
+
BACKEND_AVOID_EXPLICIT_ANY: 'Sustituye `any` por tipos concretos en backend y relanza el gate.',
|
|
22
|
+
GIT_ATOMICITY_TOO_MANY_SCOPES: 'Divide el cambio por scope o en commits más pequeños y vuelve a ejecutar el gate.',
|
|
23
|
+
SOLID_HEURISTIC: 'Corrige la violación detectada y vuelve a ejecutar el gate.',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT: Readonly<Record<BlockedRemediationVariant, number>> = {
|
|
27
|
+
banner: 120,
|
|
28
|
+
dialog: 220,
|
|
20
29
|
};
|
|
21
30
|
|
|
22
|
-
const
|
|
31
|
+
const GENERIC_BLOCKED_REMEDIATION =
|
|
32
|
+
'Corrige el bloqueo indicado y vuelve a ejecutar el comando.';
|
|
33
|
+
|
|
34
|
+
const normalizeBlockedRemediation = (value: string): string =>
|
|
35
|
+
normalizeNotificationText(value)
|
|
36
|
+
.replace(/^cómo solucionarlo:\s*/i, '')
|
|
37
|
+
.replace(/^remediation:\s*/i, '')
|
|
38
|
+
.replace(/^solution:\s*/i, '')
|
|
39
|
+
.replace(/^fix:\s*/i, '');
|
|
40
|
+
|
|
41
|
+
const resolveFallbackRemediation = (causeCode: string): string =>
|
|
42
|
+
BLOCKED_REMEDIATION_BY_CODE[causeCode] ?? GENERIC_BLOCKED_REMEDIATION;
|
|
43
|
+
|
|
44
|
+
const hasEnglishHints = (message: string): boolean => {
|
|
45
|
+
const normalized = message.toLowerCase();
|
|
46
|
+
return [
|
|
47
|
+
'avoid explicit any',
|
|
48
|
+
'set-upstream',
|
|
49
|
+
'refresh evidence',
|
|
50
|
+
'fix and retry',
|
|
51
|
+
'split the change',
|
|
52
|
+
'smaller commits',
|
|
53
|
+
're-run',
|
|
54
|
+
'rerun',
|
|
55
|
+
'retry',
|
|
56
|
+
'to continue',
|
|
57
|
+
'run ',
|
|
58
|
+
].some((hint) => normalized.includes(hint));
|
|
59
|
+
};
|
|
23
60
|
|
|
24
|
-
const toKnownSpanishRemediationFromMessage = (message: string): string | null => {
|
|
61
|
+
const toKnownSpanishRemediationFromMessage = (message: string, causeCode: string): string | null => {
|
|
25
62
|
const normalized = message.toLowerCase();
|
|
26
63
|
if (normalized.includes('avoid explicit any')) {
|
|
27
64
|
return BLOCKED_REMEDIATION_BY_CODE.BACKEND_AVOID_EXPLICIT_ANY;
|
|
28
65
|
}
|
|
29
|
-
if (normalized.includes('set-upstream')) {
|
|
66
|
+
if (normalized.includes('set-upstream') || normalized.includes('no upstream tracking reference')) {
|
|
30
67
|
return BLOCKED_REMEDIATION_BY_CODE.PRE_PUSH_UPSTREAM_MISSING;
|
|
31
68
|
}
|
|
32
|
-
if (normalized.includes('refresh evidence')) {
|
|
69
|
+
if (normalized.includes('refresh evidence') || normalized.includes('evidence is stale')) {
|
|
33
70
|
return BLOCKED_REMEDIATION_BY_CODE.EVIDENCE_STALE;
|
|
34
71
|
}
|
|
72
|
+
if (normalized.includes('split the change')) {
|
|
73
|
+
return BLOCKED_REMEDIATION_BY_CODE.GIT_ATOMICITY_TOO_MANY_SCOPES;
|
|
74
|
+
}
|
|
75
|
+
if (normalized.includes('fix and retry') || normalized.includes('retry')) {
|
|
76
|
+
return resolveFallbackRemediation(causeCode);
|
|
77
|
+
}
|
|
35
78
|
return null;
|
|
36
79
|
};
|
|
37
80
|
|
|
38
81
|
export const resolveBlockedRemediation = (
|
|
39
82
|
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
|
|
40
|
-
causeCode: string
|
|
83
|
+
causeCode: string,
|
|
84
|
+
options?: {
|
|
85
|
+
variant?: BlockedRemediationVariant;
|
|
86
|
+
}
|
|
41
87
|
): string => {
|
|
88
|
+
const variant = options?.variant ?? 'dialog';
|
|
89
|
+
const maxLength = BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT[variant];
|
|
42
90
|
const fromEvent = event.remediation
|
|
43
|
-
?
|
|
44
|
-
.replace(/^cómo solucionarlo:\s*/i, '')
|
|
45
|
-
.replace(/^remediation:\s*/i, '')
|
|
91
|
+
? normalizeBlockedRemediation(event.remediation)
|
|
46
92
|
: '';
|
|
47
93
|
if (fromEvent.length > 0) {
|
|
48
|
-
const translated = toKnownSpanishRemediationFromMessage(fromEvent);
|
|
94
|
+
const translated = toKnownSpanishRemediationFromMessage(fromEvent, causeCode);
|
|
49
95
|
if (translated) {
|
|
50
|
-
return truncateNotificationText(translated,
|
|
96
|
+
return truncateNotificationText(translated, maxLength);
|
|
97
|
+
}
|
|
98
|
+
if (hasEnglishHints(fromEvent)) {
|
|
99
|
+
return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
|
|
51
100
|
}
|
|
52
|
-
return truncateNotificationText(fromEvent,
|
|
101
|
+
return truncateNotificationText(fromEvent, maxLength);
|
|
53
102
|
}
|
|
54
|
-
|
|
55
|
-
BLOCKED_REMEDIATION_BY_CODE[causeCode]
|
|
56
|
-
?? 'Corrige el bloqueo indicado y vuelve a ejecutar el comando.';
|
|
57
|
-
return truncateNotificationText(fallback, BLOCKED_REMEDIATION_MAX_LENGTH);
|
|
103
|
+
return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
|
|
58
104
|
};
|
|
@@ -7,7 +7,13 @@ export const truncateNotificationText = (value: string, maxLength: number): stri
|
|
|
7
7
|
if (value.length <= maxLength) {
|
|
8
8
|
return value;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
const limit = Math.max(1, maxLength - 1);
|
|
11
|
+
const trimmed = value.slice(0, limit).trimEnd();
|
|
12
|
+
const boundary = trimmed.lastIndexOf(' ');
|
|
13
|
+
const shortened = boundary >= Math.floor(limit * 0.6)
|
|
14
|
+
? trimmed.slice(0, boundary).trimEnd()
|
|
15
|
+
: trimmed;
|
|
16
|
+
return `${shortened}…`;
|
|
11
17
|
};
|
|
12
18
|
|
|
13
19
|
export const resolveProjectLabel = (params: {
|