pumuki 6.3.135 → 6.3.136
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 +8 -0
- package/core/facts/detectors/text/ios.test.ts +19 -0
- package/core/facts/detectors/text/ios.ts +30 -0
- package/core/facts/extractHeuristicFacts.ts +1 -0
- package/core/gate/evaluateRules.test.ts +7 -0
- package/core/gate/evaluateRules.ts +1 -2
- package/docs/operations/RELEASE_NOTES.md +6 -0
- package/docs/validation/ios-avdlee-parity-matrix.md +1 -1
- package/integrations/config/skillsCompilerTemplates.ts +10 -0
- package/integrations/config/skillsDetectorRegistry.ts +4 -0
- package/integrations/config/skillsMarkdownRules.ts +7 -0
- package/integrations/evidence/buildEvidence.ts +5 -4
- package/integrations/gate/stagePolicies.ts +24 -18
- package/integrations/git/stageRunners.ts +5 -1
- package/integrations/lifecycle/audit.ts +4 -4
- package/integrations/lifecycle/cli.ts +2 -2
- package/integrations/lifecycle/policyReconcile.ts +24 -3
- package/integrations/lifecycle/preWriteAutomation.ts +4 -4
- package/integrations/policy/policyProfiles.ts +24 -18
- package/integrations/policy/preWriteEnforcement.ts +1 -1
- package/package.json +1 -1
- package/skills.lock.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [6.3.136] - 2026-05-05
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **PUMUKI-INC-059 all-severities blocking:** `PRE_WRITE`, `PRE_COMMIT`, `PRE_PUSH` y `CI` bloquean cualquier finding de reglas/skills AST Intelligence, incluyendo `WARN/MEDIUM` e `INFO/LOW`.
|
|
14
|
+
- **Políticas no relajables:** `skills.policy`, perfiles hard-mode y `PRE_WRITE=advisory` ya no pueden rebajar el threshold efectivo por debajo de `INFO`.
|
|
15
|
+
- **Replay RuralGo:** validado con binario local contra RuralGo: PRE_WRITE `115/115` findings bloqueantes y PRE_COMMIT `118/118` findings bloqueantes.
|
|
16
|
+
|
|
9
17
|
## [6.3.135] - 2026-05-03
|
|
10
18
|
|
|
11
19
|
### Fixed
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
hasSwiftOperationQueueUsage,
|
|
38
38
|
hasSwiftContainsUserFilterUsage,
|
|
39
39
|
hasSwiftPassedValueStateWrapperUsage,
|
|
40
|
+
hasSwiftStateWrapperWithoutPrivateUsage,
|
|
40
41
|
hasSwiftPreconcurrencyUsage,
|
|
41
42
|
hasSwiftSheetIsPresentedUsage,
|
|
42
43
|
hasSwiftScrollViewShowsIndicatorsUsage,
|
|
@@ -425,6 +426,24 @@ struct DetailView: View {
|
|
|
425
426
|
assert.equal(hasSwiftPassedValueStateWrapperUsage(validOwnership), false);
|
|
426
427
|
});
|
|
427
428
|
|
|
429
|
+
test('hasSwiftStateWrapperWithoutPrivateUsage detecta @State y @StateObject sin private en SwiftUI Views', () => {
|
|
430
|
+
const positive = `
|
|
431
|
+
struct DetailView: View {
|
|
432
|
+
@State var filter: String = ""
|
|
433
|
+
@StateObject var viewModel = DetailViewModel()
|
|
434
|
+
}
|
|
435
|
+
`;
|
|
436
|
+
const negative = `
|
|
437
|
+
struct DetailView: View {
|
|
438
|
+
@State private var filter: String = ""
|
|
439
|
+
@StateObject private var viewModel = DetailViewModel()
|
|
440
|
+
}
|
|
441
|
+
`;
|
|
442
|
+
|
|
443
|
+
assert.equal(hasSwiftStateWrapperWithoutPrivateUsage(positive), true);
|
|
444
|
+
assert.equal(hasSwiftStateWrapperWithoutPrivateUsage(negative), false);
|
|
445
|
+
});
|
|
446
|
+
|
|
428
447
|
test('hasSwiftModernizableXCTestSuiteUsage detecta suites legacy y excluye mixed/UI', () => {
|
|
429
448
|
const legacySuite = `
|
|
430
449
|
import XCTest
|
|
@@ -527,6 +527,36 @@ export const hasSwiftLegacySwiftUiObservableWrapperUsage = (source: string): boo
|
|
|
527
527
|
return hasSwiftSanitizedRegexMatch(source, /@\s*(?:StateObject|ObservedObject)\b/);
|
|
528
528
|
};
|
|
529
529
|
|
|
530
|
+
export const hasSwiftStateWrapperWithoutPrivateUsage = (source: string): boolean => {
|
|
531
|
+
const typeDeclarations = parseSwiftTypeDeclarations(source);
|
|
532
|
+
if (typeDeclarations.length === 0) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const sourceLines = source.split(/\r?\n/);
|
|
537
|
+
const stateWrapperPattern = /@\s*(?:State|StateObject)\b/;
|
|
538
|
+
|
|
539
|
+
for (const typeDeclaration of typeDeclarations) {
|
|
540
|
+
if (!typeDeclaration.conformances.includes('View')) {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const hasViolation = visitSwiftTopLevelTypeBodyLines(sourceLines, typeDeclaration, ({ line }) => {
|
|
545
|
+
if (!stateWrapperPattern.test(line)) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return !/\bprivate\b/.test(line);
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
if (hasViolation) {
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return false;
|
|
558
|
+
};
|
|
559
|
+
|
|
530
560
|
const hasSwiftPassedValueWrapperInitialization = (
|
|
531
561
|
source: string,
|
|
532
562
|
options: {
|
|
@@ -711,6 +711,7 @@ const textDetectorRegistry: ReadonlyArray<TextDetectorRegistryEntry> = [
|
|
|
711
711
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftObservableObjectUsage, ruleId: 'heuristics.ios.observable-object.ast', code: 'HEURISTICS_IOS_OBSERVABLE_OBJECT_AST', message: 'AST heuristic detected ObservableObject usage.' },
|
|
712
712
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftLegacySwiftUiObservableWrapperUsage, ruleId: 'heuristics.ios.legacy-swiftui-observable-wrapper.ast', code: 'HEURISTICS_IOS_LEGACY_SWIFTUI_OBSERVABLE_WRAPPER_AST', message: 'AST heuristic detected @StateObject/@ObservedObject usage in a modern SwiftUI path.' },
|
|
713
713
|
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftPassedValueStateWrapperUsage, ruleId: 'heuristics.ios.passed-value-state-wrapper.ast', code: 'HEURISTICS_IOS_PASSED_VALUE_STATE_WRAPPER_AST', message: 'AST heuristic detected a passed value stored as @State/@StateObject via init wrapper ownership.' },
|
|
714
|
+
{ platform: 'ios', pathCheck: isIOSSwiftPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftStateWrapperWithoutPrivateUsage, ruleId: 'heuristics.ios.swiftui.state-wrapper-private.ast', code: 'HEURISTICS_IOS_SWIFTUI_STATE_WRAPPER_PRIVATE_AST', message: 'AST heuristic detected @State/@StateObject usage in a SwiftUI View without private visibility.' },
|
|
714
715
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftForEachIndicesUsage, ruleId: 'heuristics.ios.foreach-indices.ast', code: 'HEURISTICS_IOS_FOREACH_INDICES_AST', message: 'AST heuristic detected ForEach(...indices...) usage where stable element identity may be preferred.' },
|
|
715
716
|
{ platform: 'ios', pathCheck: isIOSApplicationOrPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftContainsUserFilterUsage, ruleId: 'heuristics.ios.contains-user-filter.ast', code: 'HEURISTICS_IOS_CONTAINS_USER_FILTER_AST', message: 'AST heuristic detected contains() in a user-facing filter where localizedStandardContains() may be preferred.' },
|
|
716
717
|
{ platform: 'ios', pathCheck: isIOSPresentationPath, excludePaths: [isSwiftTestPath], detect: TextIOS.hasSwiftGeometryReaderUsage, ruleId: 'heuristics.ios.geometryreader.ast', code: 'HEURISTICS_IOS_GEOMETRYREADER_AST', message: 'AST heuristic detected GeometryReader usage that may be replaceable with modern layout APIs.' },
|
|
@@ -38,8 +38,15 @@ test('evaluateRules genera finding cuando la condicion coincide y usa code expli
|
|
|
38
38
|
code: 'BACKEND_FILE_MODIFIED',
|
|
39
39
|
message: 'Backend file modified.',
|
|
40
40
|
filePath: 'apps/backend/src/main.ts',
|
|
41
|
+
lines: undefined,
|
|
41
42
|
matchedBy: 'FileChange',
|
|
42
43
|
source: 'git',
|
|
44
|
+
blocking: true,
|
|
45
|
+
primary_node: undefined,
|
|
46
|
+
related_nodes: undefined,
|
|
47
|
+
why: undefined,
|
|
48
|
+
impact: undefined,
|
|
49
|
+
expected_fix: undefined,
|
|
43
50
|
});
|
|
44
51
|
});
|
|
45
52
|
|
|
@@ -23,8 +23,7 @@ type FindingTarget = {
|
|
|
23
23
|
expected_fix?: string;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const isBlockingSeverity = (
|
|
27
|
-
severity === 'CRITICAL' || severity === 'ERROR';
|
|
26
|
+
const isBlockingSeverity = (_severity: RuleDefinition['severity']): boolean => true;
|
|
28
27
|
|
|
29
28
|
export type EvaluateRulesCoverageResult = {
|
|
30
29
|
findings: ReadonlyArray<Finding>;
|
|
@@ -4,6 +4,12 @@ This file tracks the active deterministic framework line used in this repository
|
|
|
4
4
|
Canonical release chronology lives in `CHANGELOG.md`.
|
|
5
5
|
This file keeps only the operational highlights and rollout notes that matter while running the framework.
|
|
6
6
|
|
|
7
|
+
### 2026-05-05 (v6.3.136)
|
|
8
|
+
|
|
9
|
+
- **All-severities blocking para consumers:** `PRE_WRITE`, `PRE_COMMIT`, `PRE_PUSH` y `CI` bloquean por cualquier violación de skills/reglas AST Intelligence, sin permitir rebajas por `skills.policy`, hard-mode `critical-high` ni `PRE_WRITE=advisory`.
|
|
10
|
+
- **RuralGo primero:** replay local previo a publicación confirma PRE_WRITE `115/115` bloqueantes y PRE_COMMIT `118/118` bloqueantes, con iOS y Android visibles en la auditoría.
|
|
11
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.136`, repinear RuralGo antes que cualquier otro consumer y revalidar `status`, `doctor`, `audit --stage=PRE_WRITE --json`, `audit --stage=PRE_COMMIT --json` y hooks gestionados.
|
|
12
|
+
|
|
7
13
|
### 2026-05-03 (v6.3.135)
|
|
8
14
|
|
|
9
15
|
- **Bootstrap de pre-push por delta real:** en ramas sin upstream, `PRE_PUSH` elige la base con menor delta real entre `main` y `develop`, evitando que un rollout nacido de `main` se bloquee como si heredara todo `develop`.
|
|
@@ -20,7 +20,7 @@ Artefacto contractual de `phase10-avdlee-parity-closure`.
|
|
|
20
20
|
| skill avdlee | bundle Pumuki | rule ids canónicos | tests contractuales | evidence runtime |
|
|
21
21
|
| --- | --- | --- | --- | --- |
|
|
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
|
-
| `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` |
|
|
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.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear`, `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
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` |
|
|
26
26
|
|
|
@@ -183,6 +183,16 @@ export const skillsCompilerTemplates: Record<string, SkillsCompilerTemplate> = {
|
|
|
183
183
|
stage: 'PRE_PUSH',
|
|
184
184
|
locked: true,
|
|
185
185
|
},
|
|
186
|
+
{
|
|
187
|
+
id: 'skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear',
|
|
188
|
+
description:
|
|
189
|
+
'Always mark @State and @StateObject as private (makes dependencies clear).',
|
|
190
|
+
severity: 'WARN',
|
|
191
|
+
platform: 'ios',
|
|
192
|
+
confidence: 'MEDIUM',
|
|
193
|
+
stage: 'PRE_PUSH',
|
|
194
|
+
locked: true,
|
|
195
|
+
},
|
|
186
196
|
{
|
|
187
197
|
id: 'skills.ios.no-navigation-view',
|
|
188
198
|
description: 'Avoid NavigationView in modern iOS code; prefer NavigationStack.',
|
|
@@ -69,6 +69,10 @@ const registryByRuleId: Record<string, SkillsDetectorBinding> = {
|
|
|
69
69
|
'skills.ios.no-passed-value-state-wrapper': heuristicDetector('ios.passed-value-state-wrapper', [
|
|
70
70
|
'heuristics.ios.passed-value-state-wrapper.ast',
|
|
71
71
|
]),
|
|
72
|
+
'skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear':
|
|
73
|
+
heuristicDetector('ios.swiftui.state-wrapper-private', [
|
|
74
|
+
'heuristics.ios.swiftui.state-wrapper-private.ast',
|
|
75
|
+
]),
|
|
72
76
|
'skills.ios.no-navigation-view': heuristicDetector('ios.navigation-view', [
|
|
73
77
|
'heuristics.ios.navigation-view.ast',
|
|
74
78
|
]),
|
|
@@ -346,6 +346,13 @@ const normalizeKnownRuleTarget = (
|
|
|
346
346
|
) {
|
|
347
347
|
return 'skills.ios.no-passed-value-state-wrapper';
|
|
348
348
|
}
|
|
349
|
+
if (
|
|
350
|
+
(includes('always mark') && includes('state') && includes('stateobject') && includes('private')) ||
|
|
351
|
+
(includes('state and stateobject as private') && includes('dependencies clear')) ||
|
|
352
|
+
(includes('mark state and stateobject as private') && includes('makes dependencies clear'))
|
|
353
|
+
) {
|
|
354
|
+
return 'skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear';
|
|
355
|
+
}
|
|
349
356
|
if (includes('navigationview') || includes('navigation view')) {
|
|
350
357
|
return 'skills.ios.no-navigation-view';
|
|
351
358
|
}
|
|
@@ -282,6 +282,10 @@ const equivalentRuleFamilies: ReadonlyArray<ReadonlyArray<string>> = [
|
|
|
282
282
|
'skills.ios.no-passed-value-state-wrapper',
|
|
283
283
|
'heuristics.ios.passed-value-state-wrapper.ast',
|
|
284
284
|
],
|
|
285
|
+
[
|
|
286
|
+
'skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear',
|
|
287
|
+
'heuristics.ios.swiftui.state-wrapper-private.ast',
|
|
288
|
+
],
|
|
285
289
|
['skills.ios.no-navigation-view', 'heuristics.ios.navigation-view.ast'],
|
|
286
290
|
['skills.ios.no-foreground-color', 'heuristics.ios.foreground-color.ast'],
|
|
287
291
|
['skills.ios.no-corner-radius', 'heuristics.ios.corner-radius.ast'],
|
|
@@ -581,10 +585,7 @@ const normalizeAndDedupeFindings = (
|
|
|
581
585
|
};
|
|
582
586
|
|
|
583
587
|
const toGateOutcome = (findings: ReadonlyArray<SnapshotFinding>): GateOutcome => {
|
|
584
|
-
|
|
585
|
-
return 'BLOCK';
|
|
586
|
-
}
|
|
587
|
-
return findings.length > 0 ? 'WARN' : 'PASS';
|
|
588
|
+
return findings.length > 0 ? 'BLOCK' : 'PASS';
|
|
588
589
|
};
|
|
589
590
|
|
|
590
591
|
const bySeverity = (findings: ReadonlyArray<SnapshotFinding>): Record<Severity, number> => {
|
|
@@ -93,15 +93,21 @@ type EnterpriseStageThresholds = {
|
|
|
93
93
|
warnOnOrAbove: EnterpriseSeverity;
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
+
const enforceAllSeverityBlockingPolicy = (policy: GatePolicy): GatePolicy => ({
|
|
97
|
+
...policy,
|
|
98
|
+
blockOnOrAbove: 'INFO',
|
|
99
|
+
warnOnOrAbove: 'INFO',
|
|
100
|
+
});
|
|
101
|
+
|
|
96
102
|
const toGatePolicyFromEnterpriseThresholds = (
|
|
97
103
|
stage: SkillsStage,
|
|
98
104
|
thresholds: EnterpriseStageThresholds
|
|
99
105
|
): GatePolicy => {
|
|
100
|
-
return {
|
|
106
|
+
return enforceAllSeverityBlockingPolicy({
|
|
101
107
|
stage,
|
|
102
108
|
blockOnOrAbove: mapEnterpriseSeverityToGateSeverity(thresholds.blockOnOrAbove),
|
|
103
109
|
warnOnOrAbove: mapEnterpriseSeverityToGateSeverity(thresholds.warnOnOrAbove),
|
|
104
|
-
};
|
|
110
|
+
});
|
|
105
111
|
};
|
|
106
112
|
|
|
107
113
|
const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
@@ -127,41 +133,41 @@ const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
|
127
133
|
const defaultPolicyByStage: Record<SkillsStage, GatePolicy> = {
|
|
128
134
|
PRE_WRITE: {
|
|
129
135
|
stage: 'PRE_WRITE',
|
|
130
|
-
blockOnOrAbove: '
|
|
131
|
-
warnOnOrAbove: '
|
|
136
|
+
blockOnOrAbove: 'INFO',
|
|
137
|
+
warnOnOrAbove: 'INFO',
|
|
132
138
|
},
|
|
133
139
|
PRE_COMMIT: {
|
|
134
140
|
stage: 'PRE_COMMIT',
|
|
135
|
-
blockOnOrAbove: '
|
|
136
|
-
warnOnOrAbove: '
|
|
141
|
+
blockOnOrAbove: 'INFO',
|
|
142
|
+
warnOnOrAbove: 'INFO',
|
|
137
143
|
},
|
|
138
144
|
PRE_PUSH: {
|
|
139
145
|
stage: 'PRE_PUSH',
|
|
140
|
-
blockOnOrAbove: '
|
|
141
|
-
warnOnOrAbove: '
|
|
146
|
+
blockOnOrAbove: 'INFO',
|
|
147
|
+
warnOnOrAbove: 'INFO',
|
|
142
148
|
},
|
|
143
149
|
CI: {
|
|
144
150
|
stage: 'CI',
|
|
145
|
-
blockOnOrAbove: '
|
|
146
|
-
warnOnOrAbove: '
|
|
151
|
+
blockOnOrAbove: 'INFO',
|
|
152
|
+
warnOnOrAbove: 'INFO',
|
|
147
153
|
},
|
|
148
154
|
};
|
|
149
155
|
|
|
150
156
|
const hardModeEnterpriseThresholdsByStage: Record<SkillsStage, EnterpriseStageThresholds> = {
|
|
151
157
|
PRE_WRITE: {
|
|
152
|
-
blockOnOrAbove: '
|
|
158
|
+
blockOnOrAbove: 'LOW',
|
|
153
159
|
warnOnOrAbove: 'LOW',
|
|
154
160
|
},
|
|
155
161
|
PRE_COMMIT: {
|
|
156
|
-
blockOnOrAbove: '
|
|
162
|
+
blockOnOrAbove: 'LOW',
|
|
157
163
|
warnOnOrAbove: 'LOW',
|
|
158
164
|
},
|
|
159
165
|
PRE_PUSH: {
|
|
160
|
-
blockOnOrAbove: '
|
|
166
|
+
blockOnOrAbove: 'LOW',
|
|
161
167
|
warnOnOrAbove: 'LOW',
|
|
162
168
|
},
|
|
163
169
|
CI: {
|
|
164
|
-
blockOnOrAbove: '
|
|
170
|
+
blockOnOrAbove: 'LOW',
|
|
165
171
|
warnOnOrAbove: 'LOW',
|
|
166
172
|
},
|
|
167
173
|
};
|
|
@@ -624,7 +630,7 @@ export const resolvePolicyForStage = (
|
|
|
624
630
|
const profilePolicy = profileName
|
|
625
631
|
? hardModePolicyProfileByStage[profileName][stage]
|
|
626
632
|
: null;
|
|
627
|
-
const hardModePolicy = profilePolicy ?? hardModePolicyByStage[stage];
|
|
633
|
+
const hardModePolicy = enforceAllSeverityBlockingPolicy(profilePolicy ?? hardModePolicyByStage[stage]);
|
|
628
634
|
const bundle = profileName
|
|
629
635
|
? `gate-policy.hard-mode.${profileName}.${stage}`
|
|
630
636
|
: `gate-policy.hard-mode.${stage}`;
|
|
@@ -657,7 +663,7 @@ export const resolvePolicyForStage = (
|
|
|
657
663
|
};
|
|
658
664
|
}
|
|
659
665
|
|
|
660
|
-
const defaults = defaultPolicyByStage[stage];
|
|
666
|
+
const defaults = enforceAllSeverityBlockingPolicy(defaultPolicyByStage[stage]);
|
|
661
667
|
const loadedPolicy = loadSkillsPolicy(repoRoot);
|
|
662
668
|
const stageOverride = loadedPolicy?.stages[stage];
|
|
663
669
|
|
|
@@ -691,11 +697,11 @@ export const resolvePolicyForStage = (
|
|
|
691
697
|
};
|
|
692
698
|
}
|
|
693
699
|
|
|
694
|
-
const resolvedPolicy: GatePolicy = {
|
|
700
|
+
const resolvedPolicy: GatePolicy = enforceAllSeverityBlockingPolicy({
|
|
695
701
|
stage: defaults.stage,
|
|
696
702
|
blockOnOrAbove: stageOverride.blockOnOrAbove,
|
|
697
703
|
warnOnOrAbove: stageOverride.warnOnOrAbove,
|
|
698
|
-
};
|
|
704
|
+
});
|
|
699
705
|
|
|
700
706
|
const bundle = `gate-policy.skills.policy.${stage}`;
|
|
701
707
|
const hash = createPolicyTraceHash({
|
|
@@ -530,6 +530,7 @@ const patchOperationalHintsAfterDocumentationOnlyEvidenceSync = (repoRoot: strin
|
|
|
530
530
|
const syncTrackedEvidenceAfterSuccessfulPreCommit = (params: {
|
|
531
531
|
dependencies: StageRunnerDependencies;
|
|
532
532
|
repoRoot: string;
|
|
533
|
+
gateBlocked: boolean;
|
|
533
534
|
}): boolean => {
|
|
534
535
|
const evidenceAbsolutePath = join(params.repoRoot, EVIDENCE_FILE_PATH);
|
|
535
536
|
if (!existsSync(evidenceAbsolutePath)) {
|
|
@@ -562,6 +563,9 @@ const syncTrackedEvidenceAfterSuccessfulPreCommit = (params: {
|
|
|
562
563
|
process.stderr.write(
|
|
563
564
|
`[pumuki][evidence-sync] unable to restage tracked ${EVIDENCE_FILE_PATH}: ${details}\n`
|
|
564
565
|
);
|
|
566
|
+
if (params.gateBlocked) {
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
565
569
|
params.dependencies.notifyGateBlocked({
|
|
566
570
|
repoRoot: params.repoRoot,
|
|
567
571
|
stage: 'PRE_COMMIT',
|
|
@@ -824,10 +828,10 @@ export async function runPreCommitStage(
|
|
|
824
828
|
return 1;
|
|
825
829
|
}
|
|
826
830
|
if (
|
|
827
|
-
result.exitCode === 0 &&
|
|
828
831
|
syncTrackedEvidenceAfterSuccessfulPreCommit({
|
|
829
832
|
dependencies: activeDependencies,
|
|
830
833
|
repoRoot,
|
|
834
|
+
gateBlocked: result.exitCode !== 0,
|
|
831
835
|
})
|
|
832
836
|
) {
|
|
833
837
|
return 1;
|
|
@@ -71,10 +71,10 @@ const countUntrackedMatchingExtensions = (
|
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
const isFindingBlocking = (finding: SnapshotFinding): boolean => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
return finding.severity === 'CRITICAL' ||
|
|
75
|
+
finding.severity === 'ERROR' ||
|
|
76
|
+
finding.severity === 'WARN' ||
|
|
77
|
+
finding.severity === 'INFO';
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
const toLifecycleAuditFinding = (finding: SnapshotFinding): LifecycleAuditFinding => ({
|
|
@@ -220,8 +220,8 @@ Pumuki lifecycle commands:
|
|
|
220
220
|
|
|
221
221
|
const LOOP_RUN_POLICY: GatePolicy = {
|
|
222
222
|
stage: 'STAGED',
|
|
223
|
-
blockOnOrAbove: '
|
|
224
|
-
warnOnOrAbove: '
|
|
223
|
+
blockOnOrAbove: 'INFO',
|
|
224
|
+
warnOnOrAbove: 'INFO',
|
|
225
225
|
};
|
|
226
226
|
|
|
227
227
|
const isLifecycleCommand = (value: string): value is LifecycleCommand =>
|
|
@@ -117,6 +117,9 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const preWriteStage = params.report.stages.PRE_WRITE;
|
|
120
|
+
const preCommitStage = params.report.stages.PRE_COMMIT;
|
|
121
|
+
const prePushStage = params.report.stages.PRE_PUSH;
|
|
122
|
+
const ciStage = params.report.stages.CI;
|
|
120
123
|
const signatures = {
|
|
121
124
|
PRE_WRITE: createPolicyAsCodeSignature({
|
|
122
125
|
stage: 'PRE_COMMIT',
|
|
@@ -125,9 +128,27 @@ const tryApplyPolicyAutofix = (params: {
|
|
|
125
128
|
hash: preWriteStage.hash,
|
|
126
129
|
version: '1.0',
|
|
127
130
|
}),
|
|
128
|
-
PRE_COMMIT:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
PRE_COMMIT: createPolicyAsCodeSignature({
|
|
132
|
+
stage: 'PRE_COMMIT',
|
|
133
|
+
source: toContractSource(preCommitStage.source),
|
|
134
|
+
bundle: preCommitStage.bundle,
|
|
135
|
+
hash: preCommitStage.hash,
|
|
136
|
+
version: '1.0',
|
|
137
|
+
}),
|
|
138
|
+
PRE_PUSH: createPolicyAsCodeSignature({
|
|
139
|
+
stage: 'PRE_PUSH',
|
|
140
|
+
source: toContractSource(prePushStage.source),
|
|
141
|
+
bundle: prePushStage.bundle,
|
|
142
|
+
hash: prePushStage.hash,
|
|
143
|
+
version: '1.0',
|
|
144
|
+
}),
|
|
145
|
+
CI: createPolicyAsCodeSignature({
|
|
146
|
+
stage: 'CI',
|
|
147
|
+
source: toContractSource(ciStage.source),
|
|
148
|
+
bundle: ciStage.bundle,
|
|
149
|
+
hash: ciStage.hash,
|
|
150
|
+
version: '1.0',
|
|
151
|
+
}),
|
|
131
152
|
};
|
|
132
153
|
if (!signatures.PRE_COMMIT || !signatures.PRE_PUSH || !signatures.CI) {
|
|
133
154
|
return {
|
|
@@ -112,8 +112,8 @@ export const buildPreWriteAutomationTrace = async (params: {
|
|
|
112
112
|
const gateExitCode = await params.runPlatformGate({
|
|
113
113
|
policy: {
|
|
114
114
|
stage: 'PRE_COMMIT',
|
|
115
|
-
blockOnOrAbove: '
|
|
116
|
-
warnOnOrAbove: '
|
|
115
|
+
blockOnOrAbove: 'INFO',
|
|
116
|
+
warnOnOrAbove: 'INFO',
|
|
117
117
|
},
|
|
118
118
|
scope: {
|
|
119
119
|
kind: 'workingTree',
|
|
@@ -148,8 +148,8 @@ export const buildPreWriteAutomationTrace = async (params: {
|
|
|
148
148
|
const gateExitCode = await params.runPlatformGate({
|
|
149
149
|
policy: {
|
|
150
150
|
stage: 'PRE_PUSH',
|
|
151
|
-
blockOnOrAbove: '
|
|
152
|
-
warnOnOrAbove: '
|
|
151
|
+
blockOnOrAbove: 'INFO',
|
|
152
|
+
warnOnOrAbove: 'INFO',
|
|
153
153
|
},
|
|
154
154
|
scope: {
|
|
155
155
|
kind: 'workingTree',
|
|
@@ -32,6 +32,12 @@ type EnterpriseStageThresholds = {
|
|
|
32
32
|
warnOnOrAbove: EnterpriseSeverity;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
const enforceAllSeverityBlockingPolicy = (policy: GatePolicy): GatePolicy => ({
|
|
36
|
+
...policy,
|
|
37
|
+
blockOnOrAbove: 'INFO',
|
|
38
|
+
warnOnOrAbove: 'INFO',
|
|
39
|
+
});
|
|
40
|
+
|
|
35
41
|
const mapEnterpriseSeverityToGateSeverity = (
|
|
36
42
|
severity: EnterpriseSeverity
|
|
37
43
|
): GatePolicy['blockOnOrAbove'] => {
|
|
@@ -51,7 +57,7 @@ const toGatePolicyFromEnterpriseThresholds = (
|
|
|
51
57
|
stage: SkillsStage,
|
|
52
58
|
thresholds: EnterpriseStageThresholds
|
|
53
59
|
): GatePolicy => {
|
|
54
|
-
return {
|
|
60
|
+
return enforceAllSeverityBlockingPolicy({
|
|
55
61
|
stage,
|
|
56
62
|
blockOnOrAbove: mapEnterpriseSeverityToGateSeverity(
|
|
57
63
|
thresholds.blockOnOrAbove
|
|
@@ -59,7 +65,7 @@ const toGatePolicyFromEnterpriseThresholds = (
|
|
|
59
65
|
warnOnOrAbove: mapEnterpriseSeverityToGateSeverity(
|
|
60
66
|
thresholds.warnOnOrAbove
|
|
61
67
|
),
|
|
62
|
-
};
|
|
68
|
+
});
|
|
63
69
|
};
|
|
64
70
|
|
|
65
71
|
const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
@@ -88,23 +94,23 @@ const toGatePolicyRecordFromEnterpriseThresholds = (
|
|
|
88
94
|
const defaultPolicyByStage: Record<SkillsStage, GatePolicy> = {
|
|
89
95
|
PRE_WRITE: {
|
|
90
96
|
stage: 'PRE_WRITE',
|
|
91
|
-
blockOnOrAbove: '
|
|
92
|
-
warnOnOrAbove: '
|
|
97
|
+
blockOnOrAbove: 'INFO',
|
|
98
|
+
warnOnOrAbove: 'INFO',
|
|
93
99
|
},
|
|
94
100
|
PRE_COMMIT: {
|
|
95
101
|
stage: 'PRE_COMMIT',
|
|
96
|
-
blockOnOrAbove: '
|
|
97
|
-
warnOnOrAbove: '
|
|
102
|
+
blockOnOrAbove: 'INFO',
|
|
103
|
+
warnOnOrAbove: 'INFO',
|
|
98
104
|
},
|
|
99
105
|
PRE_PUSH: {
|
|
100
106
|
stage: 'PRE_PUSH',
|
|
101
|
-
blockOnOrAbove: '
|
|
102
|
-
warnOnOrAbove: '
|
|
107
|
+
blockOnOrAbove: 'INFO',
|
|
108
|
+
warnOnOrAbove: 'INFO',
|
|
103
109
|
},
|
|
104
110
|
CI: {
|
|
105
111
|
stage: 'CI',
|
|
106
|
-
blockOnOrAbove: '
|
|
107
|
-
warnOnOrAbove: '
|
|
112
|
+
blockOnOrAbove: 'INFO',
|
|
113
|
+
warnOnOrAbove: 'INFO',
|
|
108
114
|
},
|
|
109
115
|
};
|
|
110
116
|
|
|
@@ -113,19 +119,19 @@ const hardModeEnterpriseThresholdsByStage: Record<
|
|
|
113
119
|
EnterpriseStageThresholds
|
|
114
120
|
> = {
|
|
115
121
|
PRE_WRITE: {
|
|
116
|
-
blockOnOrAbove: '
|
|
122
|
+
blockOnOrAbove: 'LOW',
|
|
117
123
|
warnOnOrAbove: 'LOW',
|
|
118
124
|
},
|
|
119
125
|
PRE_COMMIT: {
|
|
120
|
-
blockOnOrAbove: '
|
|
126
|
+
blockOnOrAbove: 'LOW',
|
|
121
127
|
warnOnOrAbove: 'LOW',
|
|
122
128
|
},
|
|
123
129
|
PRE_PUSH: {
|
|
124
|
-
blockOnOrAbove: '
|
|
130
|
+
blockOnOrAbove: 'LOW',
|
|
125
131
|
warnOnOrAbove: 'LOW',
|
|
126
132
|
},
|
|
127
133
|
CI: {
|
|
128
|
-
blockOnOrAbove: '
|
|
134
|
+
blockOnOrAbove: 'LOW',
|
|
129
135
|
warnOnOrAbove: 'LOW',
|
|
130
136
|
},
|
|
131
137
|
};
|
|
@@ -374,7 +380,7 @@ export const resolveExplicitPolicyProfileForStage = (
|
|
|
374
380
|
const profilePolicy = profileName
|
|
375
381
|
? hardModePolicyProfileByStage[profileName][stage]
|
|
376
382
|
: null;
|
|
377
|
-
const hardModePolicy = profilePolicy ?? hardModePolicyByStage[stage];
|
|
383
|
+
const hardModePolicy = enforceAllSeverityBlockingPolicy(profilePolicy ?? hardModePolicyByStage[stage]);
|
|
378
384
|
return {
|
|
379
385
|
policy: hardModePolicy,
|
|
380
386
|
source: 'hard-mode',
|
|
@@ -388,15 +394,15 @@ export const resolveExplicitPolicyProfileForStage = (
|
|
|
388
394
|
};
|
|
389
395
|
}
|
|
390
396
|
|
|
391
|
-
const defaults = resolveCorePolicyForStage(stage);
|
|
397
|
+
const defaults = enforceAllSeverityBlockingPolicy(resolveCorePolicyForStage(stage));
|
|
392
398
|
const stageOverride = explicitPack.policy.stages[stage];
|
|
393
399
|
|
|
394
400
|
return {
|
|
395
|
-
policy: {
|
|
401
|
+
policy: enforceAllSeverityBlockingPolicy({
|
|
396
402
|
stage: defaults.stage,
|
|
397
403
|
blockOnOrAbove: stageOverride.blockOnOrAbove,
|
|
398
404
|
warnOnOrAbove: stageOverride.warnOnOrAbove,
|
|
399
|
-
},
|
|
405
|
+
}),
|
|
400
406
|
source: 'skills.policy',
|
|
401
407
|
layer: 'policy-pack',
|
|
402
408
|
activation: 'explicit',
|
|
@@ -21,7 +21,7 @@ export const resolvePreWriteEnforcement = (): PreWriteEnforcementResolution => {
|
|
|
21
21
|
return {
|
|
22
22
|
mode: experimentalFeature.mode,
|
|
23
23
|
source: experimentalFeature.source,
|
|
24
|
-
blocking: experimentalFeature.
|
|
24
|
+
blocking: experimentalFeature.mode !== 'off',
|
|
25
25
|
layer: experimentalFeature.layer,
|
|
26
26
|
activationVariable: experimentalFeature.activationVariable,
|
|
27
27
|
legacyActivationVariable: experimentalFeature.legacyActivationVariable,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.136",
|
|
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-03T20:19:41.600Z",
|
|
5
5
|
"bundles": [
|
|
6
6
|
{
|
|
7
7
|
"name": "android-guidelines",
|
|
@@ -8299,18 +8299,19 @@
|
|
|
8299
8299
|
"name": "ios-swiftui-expert-guidelines",
|
|
8300
8300
|
"version": "1.0.0",
|
|
8301
8301
|
"source": "file:vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8302
|
-
"hash": "
|
|
8302
|
+
"hash": "59dcc140ff7423c1ec320af0389139183571ca2a61afc7272d773727432cba17",
|
|
8303
8303
|
"rules": [
|
|
8304
8304
|
{
|
|
8305
8305
|
"id": "skills.ios.guideline.ios-swiftui-expert.always-mark-state-and-stateobject-as-private-makes-dependencies-clear",
|
|
8306
|
-
"description": "Always mark @State and @StateObject as private (makes dependencies clear)",
|
|
8306
|
+
"description": "Always mark @State and @StateObject as private (makes dependencies clear).",
|
|
8307
8307
|
"severity": "WARN",
|
|
8308
8308
|
"platform": "ios",
|
|
8309
|
-
"sourceSkill": "ios-swiftui-expert-guidelines",
|
|
8310
|
-
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8311
8309
|
"confidence": "MEDIUM",
|
|
8310
|
+
"stage": "PRE_PUSH",
|
|
8312
8311
|
"locked": true,
|
|
8313
|
-
"
|
|
8312
|
+
"sourceSkill": "ios-swiftui-expert-guidelines",
|
|
8313
|
+
"sourcePath": "vendor/skills/swiftui-expert-skill/SKILL.md",
|
|
8314
|
+
"evaluationMode": "AUTO",
|
|
8314
8315
|
"origin": "core"
|
|
8315
8316
|
},
|
|
8316
8317
|
{
|