pumuki 6.3.251 → 6.3.253

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 CHANGED
@@ -1,11 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [6.3.253] - 2026-05-14
4
+
5
+ ### Added
6
+ - **iOS AST node evidence for XCTest waits:** `skills.ios.no-wait-for-expectations` now emits actionable line and node metadata for `wait(for:)`, `self.wait(for:)`, `waitForExpectations(timeout:)` and `XCTWaiter.wait(for:)`, while ignoring strings and comments.
7
+
3
8
  All notable changes to `pumuki` are documented here.
4
9
 
5
10
  This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
11
 
7
12
  ## [Unreleased]
8
13
 
14
+ ## [6.3.252] - 2026-05-14
15
+
16
+ ### Fixed
17
+
18
+ - **SDD tracking parser precision:** active tracking resolution now ignores operational bullets such as `- 🚧 Siguiente`, `- 🚧 next` or `- 🚧 delegable`; only real task identifiers are considered when realigning `pumuki sdd session --refresh`.
19
+
9
20
  ## [6.3.251] - 2026-05-14
10
21
 
11
22
  ### Fixed
@@ -6,6 +6,7 @@ import {
6
6
  findSwiftOpenClosedSwitchMatch,
7
7
  findSwiftConcreteDependencyDipMatch,
8
8
  findSwiftPresentationSrpMatch,
9
+ collectSwiftWaitForExpectationsLines,
9
10
  hasSwiftAnyViewUsage,
10
11
  hasSwiftAsyncWithoutAwaitUsage,
11
12
  hasSwiftCallbackStyleSignature,
@@ -1721,14 +1722,20 @@ test('hasSwiftWaitForExpectationsUsage detecta waits legacy y excluye await fulf
1721
1722
  let expectation = expectation(description: "Done")
1722
1723
  wait(for: [expectation], timeout: 1)
1723
1724
  waitForExpectations(timeout: 1)
1725
+ self.wait(for: [expectation], timeout: 1)
1726
+ XCTWaiter.wait(for: [expectation], timeout: 1)
1724
1727
  `;
1725
1728
  const modernWait = `
1726
1729
  let expectation = expectation(description: "Done")
1727
1730
  await fulfillment(of: [expectation], timeout: 1)
1731
+ let sample = "waitForExpectations(timeout: 1)"
1732
+ // wait(for: [expectation], timeout: 1)
1728
1733
  `;
1729
1734
 
1730
1735
  assert.equal(hasSwiftWaitForExpectationsUsage(legacyWait), true);
1731
1736
  assert.equal(hasSwiftWaitForExpectationsUsage(modernWait), false);
1737
+ assert.deepEqual(collectSwiftWaitForExpectationsLines(legacyWait), [3, 4, 5, 6]);
1738
+ assert.deepEqual(collectSwiftWaitForExpectationsLines(modernWait), []);
1732
1739
  });
1733
1740
 
1734
1741
  test('hasSwiftLegacyExpectationDescriptionUsage detecta expectation(description:) sin flujo moderno', () => {
@@ -1362,11 +1362,16 @@ const hasSwiftConfirmationUsage = (source: string): boolean => {
1362
1362
  return hasSwiftSanitizedRegexMatch(source, /\bawait\s+confirmation\b/);
1363
1363
  };
1364
1364
 
1365
+ export const collectSwiftWaitForExpectationsLines = (source: string): readonly number[] => {
1366
+ return sortedUniqueLines([
1367
+ ...collectSwiftRegexLines(source, /\b(?:self\s*\.\s*)?wait\s*\(\s*for\s*:/),
1368
+ ...collectSwiftRegexLines(source, /\bwaitForExpectations\s*\(/),
1369
+ ...collectSwiftRegexLines(source, /\bXCTWaiter\s*\.\s*wait\s*\(\s*for\s*:/),
1370
+ ]);
1371
+ };
1372
+
1365
1373
  export const hasSwiftWaitForExpectationsUsage = (source: string): boolean => {
1366
- return hasSwiftSanitizedRegexMatch(
1367
- source,
1368
- /\bwait\s*\(\s*for\s*:|\bwaitForExpectations\s*\(/
1369
- );
1374
+ return collectSwiftWaitForExpectationsLines(source).length > 0;
1370
1375
  };
1371
1376
 
1372
1377
  export const hasSwiftLegacyExpectationDescriptionUsage = (source: string): boolean => {
@@ -627,6 +627,12 @@ type TextDetectorRegistryEntry = {
627
627
  readonly pathCheck: (path: string) => boolean;
628
628
  readonly excludePaths: ReadonlyArray<(path: string) => boolean>;
629
629
  readonly detect: (content: string) => boolean;
630
+ readonly locateLines?: (content: string) => readonly number[];
631
+ readonly primaryNode?: (lines: readonly number[]) => HeuristicFact['primary_node'];
632
+ readonly relatedNodes?: (lines: readonly number[]) => HeuristicFact['related_nodes'];
633
+ readonly why?: string;
634
+ readonly impact?: string;
635
+ readonly expected_fix?: string;
630
636
  readonly ruleId: string;
631
637
  readonly code: string;
632
638
  readonly message: string;
@@ -714,7 +720,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
714
720
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftModernizableXCTestSuiteUsage, ruleId: 'heuristics.ios.testing.xctest-suite-modernizable.ast', code: 'HEURISTICS_IOS_TESTING_XCTEST_SUITE_MODERNIZABLE_AST', message: 'AST heuristic detected XCTestCase/test... suite that may be modernizable to Swift Testing with import Testing and @Test.' },
715
721
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftXCTestAssertionUsage, ruleId: 'heuristics.ios.testing.xctassert.ast', code: 'HEURISTICS_IOS_TESTING_XCTASSERT_AST', message: 'AST heuristic detected XCTest assertion usage where #expect may be preferred.' },
716
722
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftXCTUnwrapUsage, ruleId: 'heuristics.ios.testing.xctunwrap.ast', code: 'HEURISTICS_IOS_TESTING_XCTUNWRAP_AST', message: 'AST heuristic detected XCTUnwrap usage where #require may be preferred.' },
717
- { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftWaitForExpectationsUsage, ruleId: 'heuristics.ios.testing.wait-for-expectations.ast', code: 'HEURISTICS_IOS_TESTING_WAIT_FOR_EXPECTATIONS_AST', message: 'AST heuristic detected wait(for:)/waitForExpectations usage where await fulfillment(of:) may be preferred.' },
723
+ { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftWaitForExpectationsUsage, locateLines: TextIOS.collectSwiftWaitForExpectationsLines, primaryNode: (lines) => ({ kind: 'call', name: 'legacy XCTest wait call', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: await fulfillment(of:timeout:)', lines }], why: 'Legacy XCTest wait APIs block the current test thread and hide async intent that Swift concurrency can express directly.', impact: 'Async tests become less deterministic, harder to cancel and easier to keep tied to XCTest-only migration paths.', expected_fix: 'Replace wait(for:timeout:), self.wait(for:timeout:) or waitForExpectations(timeout:) with await fulfillment(of:timeout:) when the test target supports async XCTest migration.', ruleId: 'heuristics.ios.testing.wait-for-expectations.ast', code: 'HEURISTICS_IOS_TESTING_WAIT_FOR_EXPECTATIONS_AST', message: 'AST heuristic detected wait(for:)/waitForExpectations usage where await fulfillment(of:) may be preferred.' },
718
724
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftLegacyExpectationDescriptionUsage, ruleId: 'heuristics.ios.testing.legacy-expectation-description.ast', code: 'HEURISTICS_IOS_TESTING_LEGACY_EXPECTATION_DESCRIPTION_AST', message: 'AST heuristic detected expectation(description:) usage without modern fulfillment/confirmation flow.' },
719
725
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftMixedTestingFrameworksUsage, ruleId: 'heuristics.ios.testing.mixed-frameworks.ast', code: 'HEURISTICS_IOS_TESTING_MIXED_FRAMEWORKS_AST', message: 'AST heuristic detected XCTestCase and Swift Testing markers mixed in the same test file without explicit compatibility reason.' },
720
726
  { platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftQuickNimbleUsage, ruleId: 'heuristics.ios.testing.quick-nimble.ast', code: 'HEURISTICS_IOS_TESTING_QUICK_NIMBLE_AST', message: 'AST heuristic detected Quick/Nimble usage where native Swift Testing remains the preferred baseline.' },
@@ -812,12 +818,19 @@ export const extractHeuristicFacts = (
812
818
  (entry.excludePaths ?? []).every((exclude) => !exclude(fileFact.path)) &&
813
819
  entry.detect(fileFact.content)
814
820
  ) {
821
+ const lines = entry.locateLines?.(fileFact.content);
815
822
  heuristicFacts.push(
816
823
  createHeuristicFact({
817
824
  ruleId: entry.ruleId,
818
825
  code: entry.code,
819
826
  message: entry.message,
820
827
  filePath: fileFact.path,
828
+ lines,
829
+ primary_node: lines && lines.length > 0 ? entry.primaryNode?.(lines) : undefined,
830
+ related_nodes: lines && lines.length > 0 ? entry.relatedNodes?.(lines) : undefined,
831
+ why: entry.why,
832
+ impact: entry.impact,
833
+ expected_fix: entry.expected_fix,
821
834
  })
822
835
  );
823
836
  }
@@ -6,6 +6,16 @@ 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-05-14 (v6.3.253)
10
+
11
+ - **Paridad AST iOS testing:** `skills.ios.no-wait-for-expectations` queda respaldada por evidencia accionable de llamada Swift, con líneas, nodo primario y remediación hacia `await fulfillment(of:timeout:)`.
12
+ - **Rollout recomendado:** publicar `pumuki@6.3.253` y continuar con `skills.ios.no-legacy-expectation-description` si no entra bug externo nuevo.
13
+
14
+ ### 2026-05-14 (v6.3.252)
15
+
16
+ - **Parser de tracking estricto:** el refresh SDD ya no interpreta bullets operativos (`Siguiente`, `next`, `delegable`) como IDs de task activa.
17
+ - **Rollout recomendado:** sustituir `6.3.251` por `6.3.252` en RuralGo y repetir `pumuki sdd session --refresh --ttl-minutes=90`.
18
+
9
19
  ### 2026-05-14 (v6.3.251)
10
20
 
11
21
  - **Refresh SDD alineado con tracking activo:** `pumuki sdd session --refresh` deja de reutilizar silenciosamente una sesión antigua cuando `docs/RURALGO_SEGUIMIENTO.md` ya marca otra task activa.
@@ -53,7 +53,7 @@ const collectActiveTrackingChangeIds = (markdown: string): ReadonlyArray<string>
53
53
  changeIds.push(normalizeChangeId(tableMatch[1]));
54
54
  continue;
55
55
  }
56
- const bulletMatch = line.match(/^- 🚧 (`?[A-Z0-9][0-9A-Za-z.-]*`?)/u);
56
+ const bulletMatch = line.match(/^- 🚧 (`?(?:P[A-Z0-9.-]*|RGO-\d[0-9.-]*)`?)/u);
57
57
  if (bulletMatch?.[1]) {
58
58
  changeIds.push(normalizeChangeId(bulletMatch[1].replace(/`/gu, '')));
59
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.251",
3
+ "version": "6.3.253",
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": {