pumuki 6.3.252 → 6.3.254
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,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [6.3.254] - 2026-05-14
|
|
4
|
+
|
|
5
|
+
- iOS Swift Testing parity: `skills.ios.no-legacy-expectation-description` now emits AST-style line and node evidence for legacy `expectation(description:)` scaffolding, with actionable replacement guidance toward `await confirmation(...)` or awaited fulfillment flows.
|
|
6
|
+
|
|
7
|
+
## [6.3.253] - 2026-05-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **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.
|
|
11
|
+
|
|
3
12
|
All notable changes to `pumuki` are documented here.
|
|
4
13
|
|
|
5
14
|
This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
findSwiftOpenClosedSwitchMatch,
|
|
7
7
|
findSwiftConcreteDependencyDipMatch,
|
|
8
8
|
findSwiftPresentationSrpMatch,
|
|
9
|
+
collectSwiftLegacyExpectationDescriptionLines,
|
|
10
|
+
collectSwiftWaitForExpectationsLines,
|
|
9
11
|
hasSwiftAnyViewUsage,
|
|
10
12
|
hasSwiftAsyncWithoutAwaitUsage,
|
|
11
13
|
hasSwiftCallbackStyleSignature,
|
|
@@ -1721,14 +1723,20 @@ test('hasSwiftWaitForExpectationsUsage detecta waits legacy y excluye await fulf
|
|
|
1721
1723
|
let expectation = expectation(description: "Done")
|
|
1722
1724
|
wait(for: [expectation], timeout: 1)
|
|
1723
1725
|
waitForExpectations(timeout: 1)
|
|
1726
|
+
self.wait(for: [expectation], timeout: 1)
|
|
1727
|
+
XCTWaiter.wait(for: [expectation], timeout: 1)
|
|
1724
1728
|
`;
|
|
1725
1729
|
const modernWait = `
|
|
1726
1730
|
let expectation = expectation(description: "Done")
|
|
1727
1731
|
await fulfillment(of: [expectation], timeout: 1)
|
|
1732
|
+
let sample = "waitForExpectations(timeout: 1)"
|
|
1733
|
+
// wait(for: [expectation], timeout: 1)
|
|
1728
1734
|
`;
|
|
1729
1735
|
|
|
1730
1736
|
assert.equal(hasSwiftWaitForExpectationsUsage(legacyWait), true);
|
|
1731
1737
|
assert.equal(hasSwiftWaitForExpectationsUsage(modernWait), false);
|
|
1738
|
+
assert.deepEqual(collectSwiftWaitForExpectationsLines(legacyWait), [3, 4, 5, 6]);
|
|
1739
|
+
assert.deepEqual(collectSwiftWaitForExpectationsLines(modernWait), []);
|
|
1732
1740
|
});
|
|
1733
1741
|
|
|
1734
1742
|
test('hasSwiftLegacyExpectationDescriptionUsage detecta expectation(description:) sin flujo moderno', () => {
|
|
@@ -1751,6 +1759,8 @@ await confirmation("Done") { confirm in
|
|
|
1751
1759
|
assert.equal(hasSwiftLegacyExpectationDescriptionUsage(legacyExpectation), true);
|
|
1752
1760
|
assert.equal(hasSwiftLegacyExpectationDescriptionUsage(modernExpectation), false);
|
|
1753
1761
|
assert.equal(hasSwiftLegacyExpectationDescriptionUsage(confirmationOnly), false);
|
|
1762
|
+
assert.deepEqual(collectSwiftLegacyExpectationDescriptionLines(legacyExpectation), [2]);
|
|
1763
|
+
assert.deepEqual(collectSwiftLegacyExpectationDescriptionLines(confirmationOnly), []);
|
|
1754
1764
|
});
|
|
1755
1765
|
|
|
1756
1766
|
test('hasSwiftNSManagedObjectBoundaryUsage detecta boundaries con NSManagedObject y excluye IDs o subclases', () => {
|
|
@@ -1362,20 +1362,27 @@ 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
|
|
1367
|
-
source,
|
|
1368
|
-
/\bwait\s*\(\s*for\s*:|\bwaitForExpectations\s*\(/
|
|
1369
|
-
);
|
|
1374
|
+
return collectSwiftWaitForExpectationsLines(source).length > 0;
|
|
1370
1375
|
};
|
|
1371
1376
|
|
|
1372
|
-
export const
|
|
1373
|
-
|
|
1377
|
+
export const collectSwiftLegacyExpectationDescriptionLines = (source: string): readonly number[] => {
|
|
1378
|
+
return sortedUniqueLines(collectSwiftRegexLines(
|
|
1374
1379
|
source,
|
|
1375
1380
|
/\bexpectation\s*\(\s*description\s*:/
|
|
1376
|
-
)
|
|
1381
|
+
));
|
|
1382
|
+
};
|
|
1377
1383
|
|
|
1378
|
-
|
|
1384
|
+
export const hasSwiftLegacyExpectationDescriptionUsage = (source: string): boolean => {
|
|
1385
|
+
if (collectSwiftLegacyExpectationDescriptionLines(source).length === 0) {
|
|
1379
1386
|
return false;
|
|
1380
1387
|
}
|
|
1381
1388
|
|
|
@@ -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,8 +720,8 @@ 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.' },
|
|
718
|
-
{ 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.' },
|
|
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.' },
|
|
724
|
+
{ platform: 'ios', pathCheck: isIOSSwiftTestPath, excludePaths: [], detect: TextIOS.hasSwiftLegacyExpectationDescriptionUsage, locateLines: TextIOS.collectSwiftLegacyExpectationDescriptionLines, primaryNode: (lines) => ({ kind: 'call', name: 'legacy XCTest expectation(description:) call', lines }), relatedNodes: (lines) => [{ kind: 'call', name: 'replacement: await confirmation or awaited fulfillment flow', lines }], why: 'Legacy expectation(description:) scaffolding keeps async tests coupled to XCTest-style callbacks instead of expressing confirmation intent directly.', impact: 'Tests can remain harder to read and migrate because the assertion flow is split between expectation creation, callback fulfillment and a later wait.', expected_fix: 'Prefer await confirmation(...) for callback confirmation, or pair legacy expectations with await fulfillment(of:timeout:) when the target still requires XCTest compatibility.', 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.' },
|
|
721
727
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNSManagedObjectBoundaryUsage, ruleId: 'heuristics.ios.core-data.nsmanagedobject-boundary.ast', code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_BOUNDARY_AST', message: 'AST heuristic detected NSManagedObject in a shared boundary.' },
|
|
@@ -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,15 @@ 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.254)
|
|
10
|
+
- **Paridad AST iOS testing:** `skills.ios.no-legacy-expectation-description` ancla `expectation(description:)` a líneas y nodos accionables, con reemplazo recomendado hacia `await confirmation(...)` o `await fulfillment(of:timeout:)`.
|
|
11
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.254` y repinear consumers que dependan de paridad iOS/Swift Testing.
|
|
12
|
+
|
|
13
|
+
### 2026-05-14 (v6.3.253)
|
|
14
|
+
|
|
15
|
+
- **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:)`.
|
|
16
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.253` y continuar con `skills.ios.no-legacy-expectation-description` si no entra bug externo nuevo.
|
|
17
|
+
|
|
9
18
|
### 2026-05-14 (v6.3.252)
|
|
10
19
|
|
|
11
20
|
- **Parser de tracking estricto:** el refresh SDD ya no interpreta bullets operativos (`Siguiente`, `next`, `delegable`) como IDs de task activa.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.254",
|
|
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": {
|