pumuki 6.3.175 → 6.3.177
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 +1 -1
- package/core/facts/detectors/text/ios.test.ts +39 -0
- package/core/facts/detectors/text/ios.ts +7 -0
- package/core/facts/extractHeuristicFacts.ts +1 -0
- package/core/rules/presets/heuristics/android.test.ts +26 -1
- package/core/rules/presets/heuristics/android.ts +100 -0
- package/core/rules/presets/heuristics/ios.test.ts +6 -1
- package/core/rules/presets/heuristics/ios.ts +18 -0
- package/docs/codex-skills/android-enterprise-rules.md +5 -0
- package/docs/codex-skills/core-data-expert.md +4 -2
- package/docs/rule-packs/ios.md +1 -1
- package/docs/validation/ios-avdlee-parity-matrix.md +1 -1
- package/integrations/config/skillsDetectorRegistry.ts +10 -0
- package/package.json +1 -1
- package/skills.lock.json +26 -2
- package/vendor/skills/core-data-expert/SKILL.md +4 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.177
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
hasSwiftModernizableXCTestSuiteUsage,
|
|
28
28
|
hasSwiftAssumeIsolatedUsage,
|
|
29
29
|
hasSwiftCoreDataLayerLeakUsage,
|
|
30
|
+
hasSwiftSwiftDataLayerLeakUsage,
|
|
30
31
|
hasSwiftNonisolatedUnsafeUsage,
|
|
31
32
|
hasSwiftNSManagedObjectAsyncBoundaryUsage,
|
|
32
33
|
hasSwiftNSManagedObjectBoundaryUsage,
|
|
@@ -618,6 +619,44 @@ struct DetailView: View {
|
|
|
618
619
|
assert.equal(hasSwiftCoreDataLayerLeakUsage(ignored), false);
|
|
619
620
|
});
|
|
620
621
|
|
|
622
|
+
test('hasSwiftSwiftDataLayerLeakUsage detecta SwiftData fuera de infraestructura', () => {
|
|
623
|
+
const source = `
|
|
624
|
+
import SwiftData
|
|
625
|
+
|
|
626
|
+
struct DetailView: View {
|
|
627
|
+
@Environment(\\.modelContext) private var modelContext
|
|
628
|
+
@Query(sort: \\TodoModel.title) private var todos: [TodoModel]
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
final class DetailUseCase {
|
|
632
|
+
private let container: ModelContainer
|
|
633
|
+
private let context: ModelContext
|
|
634
|
+
private let descriptor = FetchDescriptor<TodoModel>()
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
@Model
|
|
638
|
+
final class TodoModel {
|
|
639
|
+
var title: String
|
|
640
|
+
}
|
|
641
|
+
`;
|
|
642
|
+
const ignored = `
|
|
643
|
+
import Foundation
|
|
644
|
+
|
|
645
|
+
struct DetailView: View {
|
|
646
|
+
let selectedID: Todo.ID?
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
final class DetailUseCase {
|
|
650
|
+
func execute() async throws -> [Todo] { [] }
|
|
651
|
+
private let predicate: Predicate<Todo>?
|
|
652
|
+
private let sort = SortDescriptor(\\Todo.title)
|
|
653
|
+
}
|
|
654
|
+
`;
|
|
655
|
+
|
|
656
|
+
assert.equal(hasSwiftSwiftDataLayerLeakUsage(source), true);
|
|
657
|
+
assert.equal(hasSwiftSwiftDataLayerLeakUsage(ignored), false);
|
|
658
|
+
});
|
|
659
|
+
|
|
621
660
|
test('hasSwiftNSManagedObjectStateLeakUsage detecta fugas a SwiftUI state y ViewModels', () => {
|
|
622
661
|
const source = `
|
|
623
662
|
final class TodoEntity: NSManagedObject {}
|
|
@@ -800,6 +800,13 @@ export const hasSwiftCoreDataLayerLeakUsage = (source: string): boolean => {
|
|
|
800
800
|
);
|
|
801
801
|
};
|
|
802
802
|
|
|
803
|
+
export const hasSwiftSwiftDataLayerLeakUsage = (source: string): boolean => {
|
|
804
|
+
return hasSwiftSanitizedRegexMatch(
|
|
805
|
+
source,
|
|
806
|
+
/\bimport\s+SwiftData\b|@\s*Query\b|@\s*Model\b|\b(?:ModelContext|ModelContainer|FetchDescriptor)\b|\.modelContext\b/g
|
|
807
|
+
);
|
|
808
|
+
};
|
|
809
|
+
|
|
803
810
|
export const hasSwiftNSManagedObjectStateLeakUsage = (source: string): boolean => {
|
|
804
811
|
const typeDeclarations = parseSwiftTypeDeclarations(source);
|
|
805
812
|
if (typeDeclarations.length === 0) {
|
|
@@ -630,6 +630,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
630
630
|
{ 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.' },
|
|
631
631
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNSManagedObjectAsyncBoundaryUsage, ruleId: 'heuristics.ios.core-data.nsmanagedobject-async-boundary.ast', code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_ASYNC_BOUNDARY_AST', message: 'AST heuristic detected NSManagedObject in an async boundary.' },
|
|
632
632
|
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftCoreDataLayerLeakUsage, ruleId: 'heuristics.ios.core-data.layer-leak.ast', code: 'HEURISTICS_IOS_CORE_DATA_LAYER_LEAK_AST', message: 'AST heuristic detected Core Data APIs leaking into application/presentation code.' },
|
|
633
|
+
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftSwiftDataLayerLeakUsage, ruleId: 'heuristics.ios.swiftdata.layer-leak.ast', code: 'HEURISTICS_IOS_SWIFTDATA_LAYER_LEAK_AST', message: 'AST heuristic detected SwiftData APIs leaking into application/presentation code.' },
|
|
633
634
|
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftNSManagedObjectStateLeakUsage, ruleId: 'heuristics.ios.core-data.nsmanagedobject-state-leak.ast', code: 'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_STATE_LEAK_AST', message: 'AST heuristic detected NSManagedObject leaking into SwiftUI state or a ViewModel.' },
|
|
634
635
|
|
|
635
636
|
// Android
|
|
@@ -3,13 +3,18 @@ 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,
|
|
6
|
+
assert.equal(androidRules.length, 8);
|
|
7
7
|
|
|
8
8
|
const ids = androidRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
10
10
|
'heuristics.android.thread-sleep.ast',
|
|
11
11
|
'heuristics.android.globalscope.ast',
|
|
12
12
|
'heuristics.android.run-blocking.ast',
|
|
13
|
+
'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
|
|
14
|
+
'heuristics.android.solid.ocp.discriminator-branching.ast',
|
|
15
|
+
'heuristics.android.solid.dip.concrete-framework-dependency.ast',
|
|
16
|
+
'heuristics.android.solid.isp.fat-interface-dependency.ast',
|
|
17
|
+
'heuristics.android.solid.lsp.narrowed-precondition.ast',
|
|
13
18
|
]);
|
|
14
19
|
|
|
15
20
|
const byId = new Map(androidRules.map((rule) => [rule.id, rule]));
|
|
@@ -25,6 +30,26 @@ test('androidRules define reglas heurísticas locked para plataforma android', (
|
|
|
25
30
|
byId.get('heuristics.android.run-blocking.ast')?.then.code,
|
|
26
31
|
'HEURISTICS_ANDROID_RUN_BLOCKING_AST'
|
|
27
32
|
);
|
|
33
|
+
assert.equal(
|
|
34
|
+
byId.get('heuristics.android.solid.srp.presentation-mixed-responsibilities.ast')?.then.code,
|
|
35
|
+
'HEURISTICS_ANDROID_SOLID_SRP_PRESENTATION_MIXED_RESPONSIBILITIES_AST'
|
|
36
|
+
);
|
|
37
|
+
assert.equal(
|
|
38
|
+
byId.get('heuristics.android.solid.ocp.discriminator-branching.ast')?.then.code,
|
|
39
|
+
'HEURISTICS_ANDROID_SOLID_OCP_DISCRIMINATOR_BRANCHING_AST'
|
|
40
|
+
);
|
|
41
|
+
assert.equal(
|
|
42
|
+
byId.get('heuristics.android.solid.dip.concrete-framework-dependency.ast')?.then.code,
|
|
43
|
+
'HEURISTICS_ANDROID_SOLID_DIP_CONCRETE_FRAMEWORK_DEPENDENCY_AST'
|
|
44
|
+
);
|
|
45
|
+
assert.equal(
|
|
46
|
+
byId.get('heuristics.android.solid.isp.fat-interface-dependency.ast')?.then.code,
|
|
47
|
+
'HEURISTICS_ANDROID_SOLID_ISP_FAT_INTERFACE_DEPENDENCY_AST'
|
|
48
|
+
);
|
|
49
|
+
assert.equal(
|
|
50
|
+
byId.get('heuristics.android.solid.lsp.narrowed-precondition.ast')?.then.code,
|
|
51
|
+
'HEURISTICS_ANDROID_SOLID_LSP_NARROWED_PRECONDITION_AST'
|
|
52
|
+
);
|
|
28
53
|
|
|
29
54
|
for (const rule of androidRules) {
|
|
30
55
|
assert.equal(rule.platform, 'android');
|
|
@@ -55,4 +55,104 @@ export const androidRules: RuleSet = [
|
|
|
55
55
|
code: 'HEURISTICS_ANDROID_RUN_BLOCKING_AST',
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
id: 'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
|
|
60
|
+
description:
|
|
61
|
+
'Detects Android presentation types mixing session, networking, persistence and navigation responsibilities.',
|
|
62
|
+
severity: 'WARN',
|
|
63
|
+
platform: 'android',
|
|
64
|
+
locked: true,
|
|
65
|
+
when: {
|
|
66
|
+
kind: 'Heuristic',
|
|
67
|
+
where: {
|
|
68
|
+
ruleId: 'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
then: {
|
|
72
|
+
kind: 'Finding',
|
|
73
|
+
message:
|
|
74
|
+
'AST heuristic detected Android presentation code mixing multiple responsibilities.',
|
|
75
|
+
code: 'HEURISTICS_ANDROID_SOLID_SRP_PRESENTATION_MIXED_RESPONSIBILITIES_AST',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'heuristics.android.solid.ocp.discriminator-branching.ast',
|
|
80
|
+
description:
|
|
81
|
+
'Detects Android application or presentation code branching on discriminators instead of extending behavior.',
|
|
82
|
+
severity: 'WARN',
|
|
83
|
+
platform: 'android',
|
|
84
|
+
locked: true,
|
|
85
|
+
when: {
|
|
86
|
+
kind: 'Heuristic',
|
|
87
|
+
where: {
|
|
88
|
+
ruleId: 'heuristics.android.solid.ocp.discriminator-branching.ast',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
then: {
|
|
92
|
+
kind: 'Finding',
|
|
93
|
+
message:
|
|
94
|
+
'AST heuristic detected Android discriminator branching that weakens OCP.',
|
|
95
|
+
code: 'HEURISTICS_ANDROID_SOLID_OCP_DISCRIMINATOR_BRANCHING_AST',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'heuristics.android.solid.dip.concrete-framework-dependency.ast',
|
|
100
|
+
description:
|
|
101
|
+
'Detects Android application or presentation code depending on concrete framework infrastructure.',
|
|
102
|
+
severity: 'WARN',
|
|
103
|
+
platform: 'android',
|
|
104
|
+
locked: true,
|
|
105
|
+
when: {
|
|
106
|
+
kind: 'Heuristic',
|
|
107
|
+
where: {
|
|
108
|
+
ruleId: 'heuristics.android.solid.dip.concrete-framework-dependency.ast',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
then: {
|
|
112
|
+
kind: 'Finding',
|
|
113
|
+
message:
|
|
114
|
+
'AST heuristic detected Android concrete framework dependency in a high-level layer.',
|
|
115
|
+
code: 'HEURISTICS_ANDROID_SOLID_DIP_CONCRETE_FRAMEWORK_DEPENDENCY_AST',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'heuristics.android.solid.isp.fat-interface-dependency.ast',
|
|
120
|
+
description:
|
|
121
|
+
'Detects Android application or presentation code depending on interfaces broader than the members used.',
|
|
122
|
+
severity: 'WARN',
|
|
123
|
+
platform: 'android',
|
|
124
|
+
locked: true,
|
|
125
|
+
when: {
|
|
126
|
+
kind: 'Heuristic',
|
|
127
|
+
where: {
|
|
128
|
+
ruleId: 'heuristics.android.solid.isp.fat-interface-dependency.ast',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
then: {
|
|
132
|
+
kind: 'Finding',
|
|
133
|
+
message:
|
|
134
|
+
'AST heuristic detected Android dependency on a fat interface.',
|
|
135
|
+
code: 'HEURISTICS_ANDROID_SOLID_ISP_FAT_INTERFACE_DEPENDENCY_AST',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'heuristics.android.solid.lsp.narrowed-precondition.ast',
|
|
140
|
+
description:
|
|
141
|
+
'Detects Android application or presentation subtypes that narrow preconditions and break substitution.',
|
|
142
|
+
severity: 'WARN',
|
|
143
|
+
platform: 'android',
|
|
144
|
+
locked: true,
|
|
145
|
+
when: {
|
|
146
|
+
kind: 'Heuristic',
|
|
147
|
+
where: {
|
|
148
|
+
ruleId: 'heuristics.android.solid.lsp.narrowed-precondition.ast',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
then: {
|
|
152
|
+
kind: 'Finding',
|
|
153
|
+
message:
|
|
154
|
+
'AST heuristic detected Android subtype narrowing a contract precondition.',
|
|
155
|
+
code: 'HEURISTICS_ANDROID_SOLID_LSP_NARROWED_PRECONDITION_AST',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
58
158
|
];
|
|
@@ -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,
|
|
6
|
+
assert.equal(iosRules.length, 43);
|
|
7
7
|
|
|
8
8
|
const ids = iosRules.map((rule) => rule.id);
|
|
9
9
|
assert.deepEqual(ids, [
|
|
@@ -48,6 +48,7 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
48
48
|
'heuristics.ios.core-data.nsmanagedobject-boundary.ast',
|
|
49
49
|
'heuristics.ios.core-data.nsmanagedobject-async-boundary.ast',
|
|
50
50
|
'heuristics.ios.core-data.layer-leak.ast',
|
|
51
|
+
'heuristics.ios.swiftdata.layer-leak.ast',
|
|
51
52
|
'heuristics.ios.core-data.nsmanagedobject-state-leak.ast',
|
|
52
53
|
]);
|
|
53
54
|
|
|
@@ -137,6 +138,10 @@ test('iosRules define reglas heurísticas locked para plataforma ios', () => {
|
|
|
137
138
|
byId.get('heuristics.ios.core-data.layer-leak.ast')?.then.code,
|
|
138
139
|
'HEURISTICS_IOS_CORE_DATA_LAYER_LEAK_AST'
|
|
139
140
|
);
|
|
141
|
+
assert.equal(
|
|
142
|
+
byId.get('heuristics.ios.swiftdata.layer-leak.ast')?.then.code,
|
|
143
|
+
'HEURISTICS_IOS_SWIFTDATA_LAYER_LEAK_AST'
|
|
144
|
+
);
|
|
140
145
|
assert.equal(
|
|
141
146
|
byId.get('heuristics.ios.core-data.nsmanagedobject-state-leak.ast')?.then.code,
|
|
142
147
|
'HEURISTICS_IOS_CORE_DATA_NSMANAGEDOBJECT_STATE_LEAK_AST'
|
|
@@ -743,6 +743,24 @@ export const iosRules: RuleSet = [
|
|
|
743
743
|
code: 'HEURISTICS_IOS_CORE_DATA_LAYER_LEAK_AST',
|
|
744
744
|
},
|
|
745
745
|
},
|
|
746
|
+
{
|
|
747
|
+
id: 'heuristics.ios.swiftdata.layer-leak.ast',
|
|
748
|
+
description: 'Detects SwiftData APIs leaking into iOS application or presentation layers.',
|
|
749
|
+
severity: 'WARN',
|
|
750
|
+
platform: 'ios',
|
|
751
|
+
locked: true,
|
|
752
|
+
when: {
|
|
753
|
+
kind: 'Heuristic',
|
|
754
|
+
where: {
|
|
755
|
+
ruleId: 'heuristics.ios.swiftdata.layer-leak.ast',
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
then: {
|
|
759
|
+
kind: 'Finding',
|
|
760
|
+
message: 'AST heuristic detected SwiftData APIs leaking into application/presentation code.',
|
|
761
|
+
code: 'HEURISTICS_IOS_SWIFTDATA_LAYER_LEAK_AST',
|
|
762
|
+
},
|
|
763
|
+
},
|
|
746
764
|
{
|
|
747
765
|
id: 'heuristics.ios.core-data.nsmanagedobject-state-leak.ast',
|
|
748
766
|
description: 'Detects NSManagedObject leaking into SwiftUI state or ViewModel state.',
|
|
@@ -117,6 +117,11 @@ app/
|
|
|
117
117
|
di/ # Hilt modules
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
### Enforcement AST inicial de arquitectura Android
|
|
121
|
+
✅ `skills.android.no-solid-violations` debe mapear a detectores AST semánticos, no a conteos de líneas ni umbrales arbitrarios.
|
|
122
|
+
✅ El baseline Android debe cubrir al menos SRP en presentation, OCP por branching de discriminadores, DIP por dependencias concretas de framework, ISP por interfaces demasiado anchas y LSP por precondiciones estrechadas.
|
|
123
|
+
✅ Estos detectores son paridad parcial Android: no sustituyen una auditoría completa de arquitectura, pero sí evitan que reglas SOLID críticas queden como doctrina declarativa sin señal ejecutable.
|
|
124
|
+
|
|
120
125
|
### Dependency Injection (Hilt):
|
|
121
126
|
✅ **Hilt** - DI framework (NO manual factories)
|
|
122
127
|
✅ **@HiltAndroidApp** - Application class
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
# Core Data Expert
|
|
1
|
+
# Core Data + SwiftData Expert
|
|
2
2
|
|
|
3
|
-
Use this skill when auditing or implementing Core Data code that must remain safe across contexts, layers, and async boundaries.
|
|
3
|
+
Use this skill when auditing or implementing Core Data or SwiftData code that must remain safe across contexts, layers, and async boundaries.
|
|
4
4
|
|
|
5
5
|
## Focus areas
|
|
6
6
|
|
|
7
7
|
- ✅ Prefer `NSManagedObjectID` or mapped DTO/domain models over passing `NSManagedObject` across boundaries.
|
|
8
8
|
- ✅ Avoid returning or accepting `NSManagedObject` in async APIs that cross actor or context boundaries.
|
|
9
9
|
- ✅ Keep Core Data orchestration inside infrastructure or repository layers instead of presentation or application code.
|
|
10
|
+
- ✅ Keep SwiftData orchestration (`ModelContext`, `ModelContainer`, `@Query`, `@Model`) inside infrastructure or repository layers instead of application or presentation code in enterprise Clean Architecture.
|
|
10
11
|
- ✅ Make context ownership explicit and keep merge boundaries controlled.
|
|
11
12
|
- ✅ Treat managed objects as context-scoped references, not as portable domain entities.
|
|
12
13
|
|
|
@@ -21,4 +22,5 @@ Use this skill when auditing or implementing Core Data code that must remain saf
|
|
|
21
22
|
- ❌ Passing `NSManagedObject` through service, use-case, or presentation boundaries.
|
|
22
23
|
- ❌ Async APIs that return `NSManagedObject` directly.
|
|
23
24
|
- ❌ Using `CoreData`, `@FetchRequest`, `managedObjectContext`, or persistence containers directly in application/presentation code.
|
|
25
|
+
- ❌ Using SwiftData contexts, containers, queries, or persistence models directly in application or presentation code when the repo requires Clean Architecture boundaries.
|
|
24
26
|
- ❌ Leaking context-scoped managed objects into SwiftUI state or view models.
|
package/docs/rule-packs/ios.md
CHANGED
|
@@ -49,4 +49,4 @@
|
|
|
49
49
|
- No XCTest-only `XCTestCase` suites that are directly modernizable to `import Testing` + `@Test`, and no mixed `XCTestCase`/`Testing` suites without explicit compatibility reason
|
|
50
50
|
- No `String(format:)` and no `UIScreen.main.bounds` in SwiftUI presentation/layout paths
|
|
51
51
|
- No `ForEach(...indices...)`, no user-facing `contains()` filters instead of `localizedStandardContains()`, and no avoidable `GeometryReader` / `fontWeight(.bold)` patterns in modern SwiftUI paths
|
|
52
|
-
- No Core Data APIs in iOS `Application` / `Presentation` paths, and no `NSManagedObject` escaping into SwiftUI state or ViewModels
|
|
52
|
+
- No Core Data or SwiftData APIs in iOS `Application` / `Presentation` paths, and no `NSManagedObject` escaping into SwiftUI state or ViewModels
|
|
@@ -22,7 +22,7 @@ Artefacto contractual de `phase10-avdlee-parity-closure`.
|
|
|
22
22
|
| `swift-concurrency` | `ios-concurrency-guidelines` | `skills.ios.no-dispatchqueue`, `skills.ios.no-dispatchgroup`, `skills.ios.no-dispatchsemaphore`, `skills.ios.no-operation-queue`, `skills.ios.no-task-detached`, `skills.ios.no-unchecked-sendable`, `skills.ios.no-preconcurrency`, `skills.ios.no-nonisolated-unsafe`, `skills.ios.no-assume-isolated` | `integrations/config/__tests__/iosAvdleeParity.test.ts`, `core/facts/detectors/text/ios.test.ts`, `core/facts/__tests__/extractHeuristicFacts.test.ts`, `core/rules/presets/heuristics/ios.test.ts`, `integrations/config/__tests__/skillsDetectorRegistry.test.ts`, `integrations/config/__tests__/skillsMarkdownRules.test.ts` | `skills.sources.json`, `skills.lock.json`, `integrations/config/skillsCompilerTemplates.ts`, `integrations/config/skillsDetectorRegistry.ts`, `integrations/evidence/buildEvidence.ts` |
|
|
23
23
|
| `swiftui-expert-skill` | `ios-swiftui-expert-guidelines` | `skills.ios.no-observable-object`, `skills.ios.no-legacy-swiftui-observable-wrapper`, `skills.ios.no-passed-value-state-wrapper`, `skills.ios.no-navigation-view`, `skills.ios.no-foreground-color`, `skills.ios.no-corner-radius`, `skills.ios.no-tab-item`, `skills.ios.no-on-tap-gesture`, `skills.ios.no-string-format`, `skills.ios.no-foreach-indices`, `skills.ios.no-contains-user-filter`, `skills.ios.no-geometryreader`, `skills.ios.no-font-weight-bold`, `skills.ios.no-scrollview-shows-indicators`, `skills.ios.no-sheet-is-presented`, `skills.ios.no-legacy-onchange`, `skills.ios.no-uiscreen-main-bounds` | `integrations/config/__tests__/iosAvdleeParity.test.ts`, `core/facts/detectors/text/ios.test.ts`, `core/facts/detectors/text/iosSwiftUiModernizationSnapshot.test.ts`, `core/facts/__tests__/extractHeuristicFacts.test.ts`, `core/rules/presets/heuristics/ios.test.ts`, `integrations/config/__tests__/skillsDetectorRegistry.test.ts`, `integrations/config/__tests__/skillsMarkdownRules.test.ts` | `skills.sources.json`, `skills.lock.json`, `assets/rule-packs/ios-swiftui-modernization-v1.json`, `assets/rule-packs/ios-swiftui-modernization-v2.json`, `integrations/config/skillsCompilerTemplates.ts`, `integrations/config/skillsDetectorRegistry.ts`, `integrations/evidence/buildEvidence.ts` |
|
|
24
24
|
| `swift-testing-expert` | `ios-swift-testing-guidelines` | `skills.ios.prefer-swift-testing`, `skills.ios.no-xctassert`, `skills.ios.no-xctunwrap`, `skills.ios.no-wait-for-expectations`, `skills.ios.no-legacy-expectation-description`, `skills.ios.no-mixed-testing-frameworks` | `integrations/config/__tests__/iosAvdleeParity.test.ts`, `core/facts/detectors/text/ios.test.ts`, `core/facts/__tests__/extractHeuristicFacts.test.ts`, `core/rules/presets/heuristics/ios.test.ts`, `integrations/config/__tests__/skillsDetectorRegistry.test.ts`, `integrations/config/__tests__/skillsMarkdownRules.test.ts` | `skills.sources.json`, `skills.lock.json`, `integrations/config/skillsCompilerTemplates.ts`, `integrations/config/skillsDetectorRegistry.ts`, `integrations/evidence/buildEvidence.ts` |
|
|
25
|
-
| `core-data-expert` | `ios-core-data-guidelines` | `skills.ios.no-nsmanagedobject-boundary`, `skills.ios.no-nsmanagedobject-async-boundary`, `skills.ios.no-core-data-layer-leak`, `skills.ios.no-nsmanagedobject-state-leak` | `integrations/config/__tests__/iosAvdleeParity.test.ts`, `core/facts/detectors/text/ios.test.ts`, `core/facts/__tests__/extractHeuristicFacts.test.ts`, `core/rules/presets/heuristics/ios.test.ts`, `integrations/config/__tests__/skillsDetectorRegistry.test.ts`, `integrations/config/__tests__/skillsMarkdownRules.test.ts` | `skills.sources.json`, `skills.lock.json`, `integrations/config/skillsCompilerTemplates.ts`, `integrations/config/skillsDetectorRegistry.ts`, `integrations/evidence/buildEvidence.ts` |
|
|
25
|
+
| `core-data-expert` | `ios-core-data-guidelines` | `skills.ios.no-nsmanagedobject-boundary`, `skills.ios.no-nsmanagedobject-async-boundary`, `skills.ios.no-core-data-layer-leak`, `skills.ios.no-nsmanagedobject-state-leak`, `skills.ios.no-swiftdata-layer-leak` | `integrations/config/__tests__/iosAvdleeParity.test.ts`, `core/facts/detectors/text/ios.test.ts`, `core/facts/__tests__/extractHeuristicFacts.test.ts`, `core/rules/presets/heuristics/ios.test.ts`, `integrations/config/__tests__/skillsDetectorRegistry.test.ts`, `integrations/config/__tests__/skillsMarkdownRules.test.ts` | `skills.sources.json`, `skills.lock.json`, `integrations/config/skillsCompilerTemplates.ts`, `integrations/config/skillsDetectorRegistry.ts`, `integrations/evidence/buildEvidence.ts` |
|
|
26
26
|
|
|
27
27
|
## Skill absorbida por snapshot
|
|
28
28
|
|
|
@@ -143,6 +143,9 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
143
143
|
'skills.ios.no-core-data-layer-leak': heuristicDetector('ios.core-data.layer-leak', [
|
|
144
144
|
'heuristics.ios.core-data.layer-leak.ast',
|
|
145
145
|
]),
|
|
146
|
+
'skills.ios.no-swiftdata-layer-leak': heuristicDetector('ios.swiftdata.layer-leak', [
|
|
147
|
+
'heuristics.ios.swiftdata.layer-leak.ast',
|
|
148
|
+
]),
|
|
146
149
|
'skills.ios.no-nsmanagedobject-state-leak': heuristicDetector(
|
|
147
150
|
'ios.core-data.nsmanagedobject-state-leak',
|
|
148
151
|
['heuristics.ios.core-data.nsmanagedobject-state-leak.ast']
|
|
@@ -210,6 +213,13 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
210
213
|
'skills.android.no-runblocking': heuristicDetector('android.run-blocking', [
|
|
211
214
|
'heuristics.android.run-blocking.ast',
|
|
212
215
|
]),
|
|
216
|
+
'skills.android.no-solid-violations': heuristicDetector('android.solid', [
|
|
217
|
+
'heuristics.android.solid.srp.presentation-mixed-responsibilities.ast',
|
|
218
|
+
'heuristics.android.solid.ocp.discriminator-branching.ast',
|
|
219
|
+
'heuristics.android.solid.dip.concrete-framework-dependency.ast',
|
|
220
|
+
'heuristics.android.solid.isp.fat-interface-dependency.ast',
|
|
221
|
+
'heuristics.android.solid.lsp.narrowed-precondition.ast',
|
|
222
|
+
]),
|
|
213
223
|
};
|
|
214
224
|
|
|
215
225
|
export const listSkillsDetectorBindings = (): ReadonlyArray<{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.177",
|
|
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-
|
|
4
|
+
"generatedAt": "2026-05-12T19:46:23.268Z",
|
|
5
5
|
"bundles": [
|
|
6
6
|
{
|
|
7
7
|
"name": "android-guidelines",
|
|
@@ -5644,8 +5644,20 @@
|
|
|
5644
5644
|
"name": "ios-core-data-guidelines",
|
|
5645
5645
|
"version": "1.0.0",
|
|
5646
5646
|
"source": "file:vendor/skills/core-data-expert/SKILL.md",
|
|
5647
|
-
"hash": "
|
|
5647
|
+
"hash": "d91f2cad6499310a4299b39593d47f40a507299a0562c21bc64aad9b5d27c66f",
|
|
5648
5648
|
"rules": [
|
|
5649
|
+
{
|
|
5650
|
+
"id": "skills.ios.guideline.ios-core-data.keep-swiftdata-orchestration-modelcontext-modelcontainer-query-model-i",
|
|
5651
|
+
"description": "Keep SwiftData orchestration (ModelContext, ModelContainer, @Query, @Model) inside infrastructure or repository layers instead of application or presentation code in enterprise Clean Architecture.",
|
|
5652
|
+
"severity": "WARN",
|
|
5653
|
+
"platform": "ios",
|
|
5654
|
+
"sourceSkill": "ios-core-data-guidelines",
|
|
5655
|
+
"sourcePath": "vendor/skills/core-data-expert/SKILL.md",
|
|
5656
|
+
"confidence": "MEDIUM",
|
|
5657
|
+
"locked": true,
|
|
5658
|
+
"evaluationMode": "DECLARATIVE",
|
|
5659
|
+
"origin": "core"
|
|
5660
|
+
},
|
|
5649
5661
|
{
|
|
5650
5662
|
"id": "skills.ios.guideline.ios-core-data.make-context-ownership-explicit-and-keep-merge-boundaries-controlled",
|
|
5651
5663
|
"description": "Make context ownership explicit and keep merge boundaries controlled.",
|
|
@@ -5694,6 +5706,18 @@
|
|
|
5694
5706
|
"evaluationMode": "DECLARATIVE",
|
|
5695
5707
|
"origin": "core"
|
|
5696
5708
|
},
|
|
5709
|
+
{
|
|
5710
|
+
"id": "skills.ios.guideline.ios-core-data.using-swiftdata-contexts-containers-queries-or-persistence-models-dire",
|
|
5711
|
+
"description": "Using SwiftData contexts, containers, queries, or persistence models directly in application or presentation code when the repo requires Clean Architecture boundaries.",
|
|
5712
|
+
"severity": "ERROR",
|
|
5713
|
+
"platform": "ios",
|
|
5714
|
+
"sourceSkill": "ios-core-data-guidelines",
|
|
5715
|
+
"sourcePath": "vendor/skills/core-data-expert/SKILL.md",
|
|
5716
|
+
"confidence": "HIGH",
|
|
5717
|
+
"locked": true,
|
|
5718
|
+
"evaluationMode": "DECLARATIVE",
|
|
5719
|
+
"origin": "core"
|
|
5720
|
+
},
|
|
5697
5721
|
{
|
|
5698
5722
|
"id": "skills.ios.no-core-data-layer-leak",
|
|
5699
5723
|
"description": "Keep Core Data orchestration inside infrastructure or repository layers; avoid Core Data APIs in application or presentation code.",
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
# Core Data Expert
|
|
1
|
+
# Core Data + SwiftData Expert
|
|
2
2
|
|
|
3
|
-
Use this skill when auditing or implementing Core Data code that must remain safe across contexts, layers, and async boundaries.
|
|
3
|
+
Use this skill when auditing or implementing Core Data or SwiftData code that must remain safe across contexts, layers, and async boundaries.
|
|
4
4
|
|
|
5
5
|
## Focus areas
|
|
6
6
|
|
|
7
7
|
- ✅ Prefer `NSManagedObjectID` or mapped DTO/domain models over passing `NSManagedObject` across boundaries.
|
|
8
8
|
- ✅ Avoid returning or accepting `NSManagedObject` in async APIs that cross actor or context boundaries.
|
|
9
9
|
- ✅ Keep Core Data orchestration inside infrastructure or repository layers instead of presentation code.
|
|
10
|
+
- ✅ Keep SwiftData orchestration (`ModelContext`, `ModelContainer`, `@Query`, `@Model`) inside infrastructure or repository layers instead of application or presentation code in enterprise Clean Architecture.
|
|
10
11
|
- ✅ Make context ownership explicit and keep merge boundaries controlled.
|
|
11
12
|
- ✅ Treat managed objects as context-scoped references, not as portable domain entities.
|
|
12
13
|
|
|
@@ -21,3 +22,4 @@ Use this skill when auditing or implementing Core Data code that must remain saf
|
|
|
21
22
|
- ❌ Passing `NSManagedObject` through service, use-case, or presentation boundaries.
|
|
22
23
|
- ❌ Async APIs that return `NSManagedObject` directly.
|
|
23
24
|
- ❌ Leaking context-scoped managed objects into SwiftUI state or view models.
|
|
25
|
+
- ❌ Using SwiftData contexts, containers, queries, or persistence models directly in application or presentation code when the repo requires Clean Architecture boundaries.
|