pumuki 6.3.202 → 6.3.203

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.
@@ -27,6 +27,7 @@ import {
27
27
  hasSwiftLegacyOnChangeUsage,
28
28
  hasSwiftLegacyExpectationDescriptionUsage,
29
29
  hasSwiftLegacySwiftUiObservableWrapperUsage,
30
+ hasSwiftMainThreadBlockingSleepUsage,
30
31
  hasSwiftMixedTestingFrameworksUsage,
31
32
  hasSwiftLegacyXCTestImportUsage,
32
33
  hasSwiftModernizableXCTestSuiteUsage,
@@ -354,6 +355,28 @@ let sample = "TextAlignment.right"
354
355
  assert.equal(hasSwiftPhysicalTextAlignmentUsage(ignored), false);
355
356
  });
356
357
 
358
+ test('detector iOS de performance detecta sleeps bloqueantes sin confundir Task.sleep', () => {
359
+ const source = `
360
+ final class SplashDelay {
361
+ func wait() {
362
+ Thread.sleep(forTimeInterval: 0.25)
363
+ sleep(1)
364
+ usleep(100)
365
+ }
366
+ }
367
+ `;
368
+ const ignored = `
369
+ func wait() async throws {
370
+ try await Task.sleep(for: .seconds(1))
371
+ let text = "Thread.sleep(forTimeInterval: 1)"
372
+ // sleep(1)
373
+ }
374
+ `;
375
+
376
+ assert.equal(hasSwiftMainThreadBlockingSleepUsage(source), true);
377
+ assert.equal(hasSwiftMainThreadBlockingSleepUsage(ignored), false);
378
+ });
379
+
357
380
  test('hasSwiftUncheckedSendableUsage detecta @unchecked Sendable', () => {
358
381
  const source = `
359
382
  final class LegacyBox: @unchecked Sendable {}
@@ -593,6 +593,21 @@ export const hasSwiftPhysicalTextAlignmentUsage = (source: string): boolean => {
593
593
  });
594
594
  };
595
595
 
596
+ export const hasSwiftMainThreadBlockingSleepUsage = (source: string): boolean => {
597
+ const withoutBlockComments = source.replace(/\/\*[\s\S]*?\*\//g, '\n');
598
+ return withoutBlockComments.split(/\r?\n/).some((line) => {
599
+ if (/^\s*\/\//.test(line)) {
600
+ return false;
601
+ }
602
+ const sanitized = stripSwiftLineForSemanticScan(line);
603
+ return (
604
+ /\bThread\s*\.\s*sleep\s*\(/.test(sanitized) ||
605
+ /(^|[^\w.])sleep\s*\(/.test(sanitized) ||
606
+ /(^|[^\w.])usleep\s*\(/.test(sanitized)
607
+ );
608
+ });
609
+ };
610
+
596
611
  export const hasSwiftUncheckedSendableUsage = (source: string): boolean => {
597
612
  return scanCodeLikeSource(source, ({ source: swiftSource, index, current }) => {
598
613
  if (current !== '@' || !swiftSource.startsWith('@unchecked', index)) {
@@ -658,6 +658,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
658
658
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftLooseAssetResourceUsage, ruleId: 'heuristics.ios.assets.loose-resource.ast', code: 'HEURISTICS_IOS_ASSETS_LOOSE_RESOURCE_AST', message: 'AST heuristic detected loose image resource loading in iOS production code; Asset Catalogs remain the preferred baseline.' },
659
659
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftFixedFontSizeUsage, ruleId: 'heuristics.ios.accessibility.fixed-font-size.ast', code: 'HEURISTICS_IOS_ACCESSIBILITY_FIXED_FONT_SIZE_AST', message: 'AST heuristic detected fixed font sizing in iOS production code; Dynamic Type semantic text styles remain the preferred baseline.' },
660
660
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPhysicalTextAlignmentUsage, ruleId: 'heuristics.ios.localization.physical-text-alignment.ast', code: 'HEURISTICS_IOS_LOCALIZATION_PHYSICAL_TEXT_ALIGNMENT_AST', message: 'AST heuristic detected physical left/right text alignment in iOS production code; leading/trailing remain the preferred RTL-safe baseline.' },
661
+ { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftMainThreadBlockingSleepUsage, ruleId: 'heuristics.ios.performance.blocking-sleep.ast', code: 'HEURISTICS_IOS_PERFORMANCE_BLOCKING_SLEEP_AST', message: 'AST heuristic detected blocking sleep usage in iOS production code; async clocks, suspension or cancellable scheduling remain the preferred baseline.' },
661
662
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftUncheckedSendableUsage, ruleId: 'heuristics.ios.unchecked-sendable.ast', code: 'HEURISTICS_IOS_UNCHECKED_SENDABLE_AST', message: 'AST heuristic detected @unchecked Sendable usage.' },
662
663
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPreconcurrencyUsage, ruleId: 'heuristics.ios.preconcurrency.ast', code: 'HEURISTICS_IOS_PRECONCURRENCY_AST', message: 'AST heuristic detected @preconcurrency usage.' },
663
664
  { platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNonisolatedUnsafeUsage, ruleId: 'heuristics.ios.nonisolated-unsafe.ast', code: 'HEURISTICS_IOS_NONISOLATED_UNSAFE_AST', message: 'AST heuristic detected nonisolated(unsafe) usage.' },
@@ -3,7 +3,7 @@ import test from 'node:test';
3
3
  import { iosRules } from './ios';
4
4
 
5
5
  test('iosRules define reglas heurísticas locked para plataforma ios', () => {
6
- assert.equal(iosRules.length, 56);
6
+ assert.equal(iosRules.length, 57);
7
7
 
8
8
  const ids = iosRules.map((rule) => rule.id);
9
9
  assert.deepEqual(ids, [
@@ -30,6 +30,7 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
30
30
  'heuristics.ios.assets.loose-resource.ast',
31
31
  'heuristics.ios.accessibility.fixed-font-size.ast',
32
32
  'heuristics.ios.localization.physical-text-alignment.ast',
33
+ 'heuristics.ios.performance.blocking-sleep.ast',
33
34
  'heuristics.ios.unchecked-sendable.ast',
34
35
  'heuristics.ios.preconcurrency.ast',
35
36
  'heuristics.ios.nonisolated-unsafe.ast',
@@ -126,6 +127,10 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
126
127
  byId.get('heuristics.ios.localization.physical-text-alignment.ast')?.then.code,
127
128
  'HEURISTICS_IOS_LOCALIZATION_PHYSICAL_TEXT_ALIGNMENT_AST'
128
129
  );
130
+ assert.equal(
131
+ byId.get('heuristics.ios.performance.blocking-sleep.ast')?.then.code,
132
+ 'HEURISTICS_IOS_PERFORMANCE_BLOCKING_SLEEP_AST'
133
+ );
129
134
  assert.equal(
130
135
  byId.get('heuristics.ios.preconcurrency.ast')?.then.code,
131
136
  'HEURISTICS_IOS_PRECONCURRENCY_AST'
@@ -415,6 +415,24 @@ export const iosRules: RuleSet = [
415
415
  code: 'HEURISTICS_IOS_LOCALIZATION_PHYSICAL_TEXT_ALIGNMENT_AST',
416
416
  },
417
417
  },
418
+ {
419
+ id: 'heuristics.ios.performance.blocking-sleep.ast',
420
+ description: 'Detects blocking sleep calls where cancellable async scheduling is the preferred iOS baseline.',
421
+ severity: 'WARN',
422
+ platform: 'ios',
423
+ locked: true,
424
+ when: {
425
+ kind: 'Heuristic',
426
+ where: {
427
+ ruleId: 'heuristics.ios.performance.blocking-sleep.ast',
428
+ },
429
+ },
430
+ then: {
431
+ kind: 'Finding',
432
+ message: 'AST heuristic detected blocking sleep usage in iOS production code; async clocks, suspension or cancellable scheduling remain the preferred baseline.',
433
+ code: 'HEURISTICS_IOS_PERFORMANCE_BLOCKING_SLEEP_AST',
434
+ },
435
+ },
418
436
  {
419
437
  id: 'heuristics.ios.unchecked-sendable.ast',
420
438
  description: 'Detects @unchecked Sendable usage in iOS production code.',
@@ -633,6 +633,11 @@ struct APIEndpoint: Sendable {
633
633
  - `skills.ios.guideline.ios.rtl-support-right-to-left-para-a-rabe-hebreo` se mapea a `heuristics.ios.localization.physical-text-alignment.ast`.
634
634
  - En `PROJECT MODE: brownfield`, este hallazgo detecta alineaciones físicas `.left`/`.right` en texto y frames (`multilineTextAlignment`, `frame(alignment:)`, `TextAlignment`, `NSTextAlignment`) como señal de adopción hacia `.leading`/`.trailing`. No marca `.leading` ni `.trailing`.
635
635
 
636
+ ### Enforcement AST inicial de bloqueo de thread iOS
637
+
638
+ - `skills.ios.guideline.ios.background-threads-no-bloquear-main-thread` se mapea a `heuristics.ios.performance.blocking-sleep.ast`.
639
+ - En `PROJECT MODE: brownfield`, este hallazgo detecta sleeps bloqueantes (`Thread.sleep`, `sleep`, `usleep`) en Swift production como señal de adopción hacia scheduling cancellable, clocks o suspensión asíncrona. No marca `Task.sleep`.
640
+
636
641
  ### Combine (Reactive):
637
642
  ✅ **Publishers** - AsyncSequence para async, Combine para streams complejos
638
643
  ✅ **@Published** - En ViewModels para binding con Views
@@ -127,6 +127,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
127
127
  'ios.localization.physical-text-alignment',
128
128
  ['heuristics.ios.localization.physical-text-alignment.ast']
129
129
  ),
130
+ 'skills.ios.guideline.ios.background-threads-no-bloquear-main-thread': heuristicDetector(
131
+ 'ios.performance.blocking-sleep',
132
+ ['heuristics.ios.performance.blocking-sleep.ast']
133
+ ),
130
134
  'skills.ios.no-unchecked-sendable': heuristicDetector('ios.unchecked-sendable', [
131
135
  'heuristics.ios.unchecked-sendable.ast',
132
136
  ]),
@@ -407,6 +407,15 @@ const normalizeKnownRuleTarget = (
407
407
  if (includes('rtl support') || includes('right to left')) {
408
408
  return 'skills.ios.guideline.ios.rtl-support-right-to-left-para-a-rabe-hebreo';
409
409
  }
410
+ if (
411
+ includes('background threads') ||
412
+ includes('bloquear main thread') ||
413
+ includes('no bloquear main thread') ||
414
+ includes('thread.sleep') ||
415
+ includes('blocking sleep')
416
+ ) {
417
+ return 'skills.ios.guideline.ios.background-threads-no-bloquear-main-thread';
418
+ }
410
419
  if (
411
420
  includes('mixing legacy xctest style') ||
412
421
  includes('mixed xctest and swift testing') ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.202",
3
+ "version": "6.3.203",
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-13T11:47:21.054Z",
4
+ "generatedAt": "2026-05-13T11:57:22.186Z",
5
5
  "bundles": [
6
6
  {
7
7
  "name": "android-guidelines",
@@ -5764,7 +5764,7 @@
5764
5764
  "name": "ios-guidelines",
5765
5765
  "version": "1.0.0",
5766
5766
  "source": "file:vendor/skills/ios-enterprise-rules/SKILL.md",
5767
- "hash": "4689c3cc0d9f813517c298e0cddac9fffa9c029951fbc1c6f58d95994938f2d9",
5767
+ "hash": "54d70e010210d043da474f55a1031af4f0d897b30082dd522be5d85c44ba27ab",
5768
5768
  "rules": [
5769
5769
  {
5770
5770
  "id": "skills.ios.guideline.ios.accessibility-identifiers-para-localizar-elementos",
@@ -5967,7 +5967,7 @@
5967
5967
  "sourcePath": "vendor/skills/ios-enterprise-rules/SKILL.md",
5968
5968
  "confidence": "MEDIUM",
5969
5969
  "locked": true,
5970
- "evaluationMode": "DECLARATIVE",
5970
+ "evaluationMode": "AUTO",
5971
5971
  "origin": "core"
5972
5972
  },
5973
5973
  {