pumuki 6.3.183 → 6.3.184

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 CHANGED
@@ -1 +1 @@
1
- v6.3.183
1
+ v6.3.184
@@ -6,6 +6,7 @@ import {
6
6
  findKotlinLiskovSubstitutionMatch,
7
7
  findKotlinOpenClosedWhenMatch,
8
8
  findKotlinPresentationSrpMatch,
9
+ hasKotlinCoroutineTryCatchUsage,
9
10
  hasKotlinDispatcherMainBoundaryLeakUsage,
10
11
  hasKotlinGlobalScopeUsage,
11
12
  hasKotlinHardcodedBackgroundDispatcherUsage,
@@ -223,6 +224,53 @@ class SyncOrdersUseCase {
223
224
  assert.equal(hasKotlinSupervisorScopeUsage(source), false);
224
225
  });
225
226
 
227
+ test('hasKotlinCoroutineTryCatchUsage detecta try-catch dentro de contexto coroutine', () => {
228
+ const suspendSource = `
229
+ class SyncOrdersUseCase {
230
+ suspend fun execute() {
231
+ try {
232
+ syncRemote()
233
+ } catch (error: IOException) {
234
+ recover(error)
235
+ }
236
+ }
237
+ }
238
+ `;
239
+ const launchSource = `
240
+ class SyncOrdersUseCase {
241
+ fun execute() {
242
+ launch {
243
+ try {
244
+ syncRemote()
245
+ } catch (error: IOException) {
246
+ recover(error)
247
+ }
248
+ }
249
+ }
250
+ }
251
+ `;
252
+ assert.equal(hasKotlinCoroutineTryCatchUsage(suspendSource), true);
253
+ assert.equal(hasKotlinCoroutineTryCatchUsage(launchSource), true);
254
+ });
255
+
256
+ test('hasKotlinCoroutineTryCatchUsage ignora imports, comentarios, strings y try-catch no coroutine', () => {
257
+ const source = `
258
+ import kotlin.runCatching
259
+ // suspend fun execute() { try { syncRemote() } catch (error: IOException) { recover(error) } }
260
+ val sample = "try { syncRemote() } catch (error: IOException) { recover(error) }"
261
+ class SyncOrdersUseCase {
262
+ fun execute() {
263
+ try {
264
+ syncRemote()
265
+ } catch (error: IOException) {
266
+ recover(error)
267
+ }
268
+ }
269
+ }
270
+ `;
271
+ assert.equal(hasKotlinCoroutineTryCatchUsage(source), false);
272
+ });
273
+
226
274
  test('findKotlinPresentationSrpMatch devuelve payload semantico para SRP-Android en presentation', () => {
227
275
  const source = `
228
276
  import android.content.SharedPreferences
@@ -263,6 +263,19 @@ export const hasKotlinSupervisorScopeUsage = (source: string): boolean => {
263
263
  return collectKotlinRegexLines(source, /\bsupervisorScope\s*(?:<[^>\n]+>\s*)?(?:\(|\{)/).length > 0;
264
264
  };
265
265
 
266
+ export const hasKotlinCoroutineTryCatchUsage = (source: string): boolean => {
267
+ const sanitizedSource = source
268
+ .split(/\r?\n/)
269
+ .map((line) => stripKotlinLineForSemanticScan(line))
270
+ .filter((line) => !line.trimStart().startsWith('import '))
271
+ .join('\n');
272
+
273
+ return (
274
+ /\btry\s*\{[\s\S]*\bcatch\s*\(/.test(sanitizedSource) &&
275
+ /\b(?:suspend\s+fun|launch\s*\{|async\s*\{|withContext\s*\(|supervisorScope\s*(?:<[^>\n]+>\s*)?(?:\(|\{))/.test(sanitizedSource)
276
+ );
277
+ };
278
+
266
279
  export const hasKotlinThreadSleepCall = (source: string): boolean => {
267
280
  return scanCodeLikeSource(source, ({ source: kotlinSource, index, current }) => {
268
281
  if (current !== 'T') {
@@ -650,6 +650,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
650
650
  { platform: 'android', pathCheck: isAndroidNonPresentationKotlinPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinDispatcherMainBoundaryLeakUsage, ruleId: 'heuristics.android.coroutines.dispatchers-main-boundary-leak.ast', code: 'HEURISTICS_ANDROID_COROUTINES_DISPATCHERS_MAIN_BOUNDARY_LEAK_AST', message: 'AST heuristic detected Dispatchers.Main outside Android presentation code.' },
651
651
  { platform: 'android', pathCheck: isAndroidDomainOrApplicationPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinHardcodedBackgroundDispatcherUsage, ruleId: 'heuristics.android.coroutines.hardcoded-background-dispatcher.ast', code: 'HEURISTICS_ANDROID_COROUTINES_HARDCODED_BACKGROUND_DISPATCHER_AST', message: 'AST heuristic detected hard-coded Dispatchers.IO or Dispatchers.Default in Android domain/application code.' },
652
652
  { platform: 'android', pathCheck: isAndroidDomainOrApplicationPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinSupervisorScopeUsage, ruleId: 'heuristics.android.coroutines.supervisor-scope.ast', code: 'HEURISTICS_ANDROID_COROUTINES_SUPERVISOR_SCOPE_AST', message: 'AST heuristic detected supervisorScope usage in Android domain/application code.' },
653
+ { platform: 'android', pathCheck: isAndroidDomainOrApplicationPath, excludePaths: [isKotlinTestPath], detect: TextAndroid.hasKotlinCoroutineTryCatchUsage, ruleId: 'heuristics.android.coroutines.try-catch.ast', code: 'HEURISTICS_ANDROID_COROUTINES_TRY_CATCH_AST', message: 'AST heuristic detected try-catch handling in Android coroutine code.' },
653
654
  ];
654
655
 
655
656
  const extractWorkflowHeuristicFacts = (
@@ -3,7 +3,7 @@ import test from 'node:test';
3
3
  import { androidRules } from './android';
4
4
 
5
5
  test('androidRules define reglas heurísticas locked para plataforma android', () => {
6
- assert.equal(androidRules.length, 13);
6
+ assert.equal(androidRules.length, 14);
7
7
 
8
8
  const ids = androidRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -15,6 +15,7 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
15
15
  'heuristics.android.coroutines.dispatchers-main-boundary-leak.ast',
16
16
  'heuristics.android.coroutines.hardcoded-background-dispatcher.ast',
17
17
  'heuristics.android.coroutines.supervisor-scope.ast',
18
+ 'heuristics.android.coroutines.try-catch.ast',
18
19
  'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
19
20
  'heuristics.android.solid.ocp.discriminator-branching.ast',
20
21
  'heuristics.android.solid.dip.concrete-framework-dependency.ast',
@@ -65,6 +66,10 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
65
66
  byId.get('heuristics.android.coroutines.supervisor-scope.ast')?.then.code,
66
67
  'HEURISTICS_ANDROID_COROUTINES_SUPERVISOR_SCOPE_AST'
67
68
  );
69
+ assert.equal(
70
+ byId.get('heuristics.android.coroutines.try-catch.ast')?.then.code,
71
+ 'HEURISTICS_ANDROID_COROUTINES_TRY_CATCH_AST'
72
+ );
68
73
  assert.equal(
69
74
  byId.get('heuristics.android.solid.srp.presentation-mixed-responsibilities.ast')?.then.code,
70
75
  'HEURISTICS_ANDROID_SOLID_SRP_PRESENTATION_MIXED_RESPONSIBILITIES_AST'
@@ -155,6 +155,26 @@ export const androidRules: RuleSet = [
155
155
  code: 'HEURISTICS_ANDROID_COROUTINES_SUPERVISOR_SCOPE_AST',
156
156
  },
157
157
  },
158
+ {
159
+ id: 'heuristics.android.coroutines.try-catch.ast',
160
+ description:
161
+ 'Detects try-catch error handling in Android coroutine code.',
162
+ severity: 'WARN',
163
+ platform: 'android',
164
+ locked: true,
165
+ when: {
166
+ kind: 'Heuristic',
167
+ where: {
168
+ ruleId: 'heuristics.android.coroutines.try-catch.ast',
169
+ },
170
+ },
171
+ then: {
172
+ kind: 'Finding',
173
+ message:
174
+ 'AST heuristic detected try-catch handling in Android coroutine code.',
175
+ code: 'HEURISTICS_ANDROID_COROUTINES_TRY_CATCH_AST',
176
+ },
177
+ },
158
178
  {
159
179
  id: 'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
160
180
  description:
@@ -127,7 +127,8 @@ app/
127
127
  ✅ `skills.android.guideline.android.viewmodelscope-scope-de-viewmodel-cancelado-automa-ticamente` debe mapear a señales ejecutables de scopes manuales dentro de `ViewModel`, empezando por `CoroutineScope(...)` construido en presentation.
128
128
  ✅ `skills.android.guideline.android.dispatchers-main-ui-io-network-disk-default-cpu` debe mapear a señales ejecutables de dispatchers mal ubicados: `Dispatchers.Main` fuera de `presentation` y `Dispatchers.IO` / `Dispatchers.Default` hardcodeados en `domain` o `application` en lugar de entrar por frontera inyectable.
129
129
  ✅ `skills.android.guideline.android.supervisorscope-errores-no-cancelan-otros-jobs` debe mapear a señal ejecutable de `supervisorScope` en `domain` o `application`.
130
- El baseline inicial de coroutines es parcial: cubre APIs bloqueantes, scopes no estructurados, scopes manuales en `ViewModel`, filtraciones de `Dispatchers.Main`, hardcode de dispatchers de background en dominio/aplicación y `supervisorScope`, pero no declara todavía cobertura completa de `Flow`, dispatchers, try-catch ni cancelación cooperativa.
130
+ `skills.android.guideline.android.try-catch-manejo-de-errores-en-coroutines` debe mapear a señal ejecutable de `try/catch` dentro de código coroutine en `domain` o `application`.
131
+ ✅ El baseline inicial de coroutines es parcial: cubre APIs bloqueantes, scopes no estructurados, scopes manuales en `ViewModel`, filtraciones de `Dispatchers.Main`, hardcode de dispatchers de background en dominio/aplicación, `supervisorScope` y `try/catch` en coroutines, pero no declara todavía cobertura completa de `Flow`, dispatchers ni cancelación cooperativa.
131
132
  ✅ Si se amplía esta cobertura, cada nueva regla debe aterrizar como detector AST/textual semántico con test dirigido antes de declararse cubierta en el registry.
132
133
 
133
134
  ### Enforcement AST inicial de Flow/StateFlow Android
@@ -235,6 +235,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
235
235
  'android.coroutines.supervisor-scope',
236
236
  ['heuristics.android.coroutines.supervisor-scope.ast']
237
237
  ),
238
+ 'skills.android.guideline.android.try-catch-manejo-de-errores-en-coroutines': heuristicDetector(
239
+ 'android.coroutines.try-catch',
240
+ ['heuristics.android.coroutines.try-catch.ast']
241
+ ),
238
242
  'skills.android.guideline.android.stateflow-estado-mutable-observable': heuristicDetector(
239
243
  'android.flow.state-exposure',
240
244
  ['heuristics.android.flow.livedata-state-exposure.ast']
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.183",
3
+ "version": "6.3.184",
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": {
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-12T20:42:36.407Z",
4
+ "generatedAt": "2026-05-12T20:47:37.241Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",