pumuki 6.3.307 → 6.3.309

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.
@@ -20,8 +20,10 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
20
20
  EVIDENCE_BRANCH_MISMATCH: 'La evidencia no corresponde a esta rama. Regenera el receipt/evidencia y vuelve a validar.',
21
21
  EVIDENCE_REPO_ROOT_MISMATCH: 'Regenera la evidencia desde este repositorio y vuelve a validar.',
22
22
  PRE_PUSH_UPSTREAM_MISSING: 'Configura upstream con `git push --set-upstream origin <branch>` y repite PRE_PUSH.',
23
- SDD_SESSION_MISSING: 'Abre la sesión SDD del change activo y repite la validación.',
24
- SDD_SESSION_INVALID: 'Refresca la sesión SDD activa y vuelve a validar esta fase.',
23
+ SDD_SESSION_MISSING:
24
+ 'Abre una sesión SDD válida para el change activo (`pumuki sdd session --open --change=<id>`) y reejecuta PRE_WRITE. Agente IA: STOP hasta que exista esa sesión.',
25
+ SDD_SESSION_INVALID:
26
+ 'Refresca la sesión SDD activa (`pumuki sdd session --refresh --ttl-minutes=90`) y reejecuta PRE_WRITE. Agente IA: STOP hasta que sea válida.',
25
27
  OPENSPEC_MISSING: 'Instala OpenSpec en este repositorio y vuelve a validar el gate.',
26
28
  MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP y vuelve a validar.',
27
29
  BACKEND_AVOID_EXPLICIT_ANY: 'Sustituye `any` por tipos concretos en backend y relanza el gate.',
@@ -31,6 +33,10 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
31
33
  TRACKING_CANONICAL_SOURCE_CONFLICT: TRACKING_BLOCKED_REMEDIATION,
32
34
  ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
33
35
  'Ejecuta `pumuki policy reconcile --strict --json` y revalida antes de continuar.',
36
+ EVIDENCE_PREWRITE_WORKTREE_WARN:
37
+ 'Reduce el worktree pendiente a un slice atómico antes del commit: stagea solo la tarea activa o guarda el resto en stash nombrado, y reejecuta PRE_WRITE.',
38
+ EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT:
39
+ 'El worktree supera el límite permitido: divide cambios por scope en commits atómicos o stashes nombrados antes de continuar.',
34
40
  };
35
41
 
36
42
  const BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT: Readonly<Record<BlockedRemediationVariant, number>> = {
@@ -54,6 +60,81 @@ const normalizeBlockedRemediation = (value: string): string =>
54
60
  const resolveFallbackRemediation = (causeCode: string): string =>
55
61
  BLOCKED_REMEDIATION_BY_CODE[causeCode] ?? GENERIC_BLOCKED_REMEDIATION;
56
62
 
63
+ const resolveSddSessionCauseCode = (
64
+ event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
65
+ causeCode: string
66
+ ): 'SDD_SESSION_MISSING' | 'SDD_SESSION_INVALID' | null => {
67
+ if (causeCode === 'SDD_SESSION_MISSING' || causeCode === 'SDD_SESSION_INVALID') {
68
+ return causeCode;
69
+ }
70
+ const searchable = [
71
+ event.causeMessage,
72
+ event.remediation,
73
+ ...(event.blockingCauses ?? []).flatMap((cause) => [
74
+ cause.code,
75
+ cause.ruleId,
76
+ cause.message,
77
+ cause.remediation,
78
+ ]),
79
+ ]
80
+ .filter((value): value is string => typeof value === 'string')
81
+ .join(' ')
82
+ .toLowerCase();
83
+ if (
84
+ searchable.includes('sdd_session_missing') ||
85
+ searchable.includes('no hay sesión sdd activa') ||
86
+ searchable.includes('no hay sesion sdd activa') ||
87
+ searchable.includes('no active sdd session')
88
+ ) {
89
+ return 'SDD_SESSION_MISSING';
90
+ }
91
+ if (
92
+ searchable.includes('sdd_session_invalid') ||
93
+ searchable.includes('sesión sdd actual no es válida') ||
94
+ searchable.includes('sesion sdd actual no es valida') ||
95
+ searchable.includes('invalid sdd session')
96
+ ) {
97
+ return 'SDD_SESSION_INVALID';
98
+ }
99
+ return null;
100
+ };
101
+
102
+ const resolveWorktreeHygieneCauseCode = (
103
+ event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
104
+ causeCode: string
105
+ ): 'EVIDENCE_PREWRITE_WORKTREE_WARN' | 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT' | null => {
106
+ if (
107
+ causeCode === 'EVIDENCE_PREWRITE_WORKTREE_WARN' ||
108
+ causeCode === 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT'
109
+ ) {
110
+ return causeCode;
111
+ }
112
+ const searchable = [
113
+ event.causeMessage,
114
+ event.remediation,
115
+ ...(event.blockingCauses ?? []).flatMap((cause) => [
116
+ cause.code,
117
+ cause.ruleId,
118
+ cause.message,
119
+ cause.remediation,
120
+ ]),
121
+ ]
122
+ .filter((value): value is string => typeof value === 'string')
123
+ .join(' ')
124
+ .toLowerCase();
125
+ if (searchable.includes('evidence_prewrite_worktree_over_limit')) {
126
+ return 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT';
127
+ }
128
+ if (
129
+ searchable.includes('evidence_prewrite_worktree_warn') ||
130
+ searchable.includes('worktree hygiene warning') ||
131
+ searchable.includes('warn_threshold=')
132
+ ) {
133
+ return 'EVIDENCE_PREWRITE_WORKTREE_WARN';
134
+ }
135
+ return null;
136
+ };
137
+
57
138
  const isGenericPolicyReconcileRemediation = (message: string): boolean => {
58
139
  const normalized = message.toLowerCase();
59
140
  return normalized.includes('policy reconcile') && normalized.includes('sdd validate');
@@ -105,6 +186,20 @@ export const resolveBlockedRemediation = (
105
186
  ): string => {
106
187
  const variant = options?.variant ?? 'dialog';
107
188
  const maxLength = BLOCKED_REMEDIATION_MAX_LENGTH_BY_VARIANT[variant];
189
+ const sddSessionCauseCode = resolveSddSessionCauseCode(event, causeCode);
190
+ if (sddSessionCauseCode) {
191
+ return truncateNotificationText(
192
+ resolveFallbackRemediation(sddSessionCauseCode),
193
+ maxLength
194
+ );
195
+ }
196
+ const worktreeHygieneCauseCode = resolveWorktreeHygieneCauseCode(event, causeCode);
197
+ if (worktreeHygieneCauseCode) {
198
+ return truncateNotificationText(
199
+ resolveFallbackRemediation(worktreeHygieneCauseCode),
200
+ maxLength
201
+ );
202
+ }
108
203
  const blockingCausesCount = event.blockingCauses?.length ?? 0;
109
204
  if (blockingCausesCount > 1) {
110
205
  return truncateNotificationText(
package/skills.lock.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "1.0",
3
3
  "compilerVersion": "1.0.0",
4
- "generatedAt": "2026-05-19T20:42:51.075Z",
4
+ "generatedAt": "2026-05-19T21:32:21.704Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",