pumuki 6.3.131 → 6.3.133
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 +16 -0
- package/VERSION +1 -1
- package/docs/operations/RELEASE_NOTES.md +6 -0
- package/integrations/gate/runPlatformGateConfig.ts +55 -0
- package/integrations/gate/runPlatformGateDefaults.ts +19 -0
- package/integrations/git/runPlatformGate.ts +64 -96
- package/integrations/policy/skillsEnforcement.ts +0 -40
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [6.3.133] - 2026-05-03
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Skills enforcement endurecido a bloqueo duro:** `PRE_WRITE`, `PRE_COMMIT` y `PRE_PUSH` ya no admiten bypass advisory para violaciones de skills.
|
|
14
|
+
- **Contrato de gate alineado de punta a punta:** `skillsEnforcement`, `evaluateAiGate`, `runPlatformGate` y el flujo CLI bloquean de forma consistente cuando falta cobertura, bundles o contrato de skills.
|
|
15
|
+
- **Release listo para repin:** esta versión está preparada para publicarse y repinear consumers como RuralGo sin cerrar más gaps funcionales para este fix.
|
|
16
|
+
|
|
17
|
+
## [6.3.132] - 2026-05-03
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Reglas declarativas sin detector no bloquean el gate:** `unsupported_detector_rule_ids` se conserva en evidencia, pero deja de convertirse en `SKILLS_DETECTOR_MAPPING_INCOMPLETE_HIGH` cuando no hay reglas AUTO ejecutables sin detector.
|
|
22
|
+
- **Bloqueo solo para AUTO real:** el guard de cobertura de skills ahora bloquea exclusivamente `unsupported_auto_rule_ids`, evitando que doctrina declarativa de skills vuelva a parar consumers con `coverage_ratio=1`.
|
|
23
|
+
- **Regresión focalizada:** `runPlatformGate` cubre el caso en modo strict para asegurar que declarativas sin detector quedan como evidencia no bloqueante.
|
|
24
|
+
|
|
9
25
|
## [6.3.130] - 2026-05-03
|
|
10
26
|
|
|
11
27
|
### Fixed
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.132
|
|
@@ -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-03 (v6.3.133)
|
|
8
|
+
|
|
9
|
+
- **Skills hard-blocking end-to-end:** `skillsEnforcement`, `evaluateAiGate`, `runPlatformGate` y el flujo CLI ya no dejan pasar advisory para violations de skills.
|
|
10
|
+
- **Repin recomendado:** publicar `pumuki@6.3.133`, repinear RuralGo y revalidar `status`, `doctor`, `audit --stage=PRE_WRITE --json` y hooks gestionados.
|
|
11
|
+
- **Contrato de release estable:** no hace falta cerrar más gaps funcionales para este fix; la solución ya quedó validada localmente y lo que falta es distribución.
|
|
12
|
+
|
|
7
13
|
### 2026-05-03 (v6.3.130)
|
|
8
14
|
|
|
9
15
|
- **Menú consumer legacy recuperado:** `pumuki-framework` vuelve a mostrar la portada plana de 9 opciones y la salida clásica de auditoría.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_DEGRADED_MODE_ACTION_ALLOW,
|
|
3
|
+
DEFAULT_DEGRADED_MODE_ACTION_BLOCK,
|
|
4
|
+
DEFAULT_GATE_AUDIT_MODE,
|
|
5
|
+
DEFAULT_LIST_SEPARATOR,
|
|
6
|
+
DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION,
|
|
7
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_ALLOW,
|
|
8
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_BLOCK,
|
|
9
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN,
|
|
10
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY,
|
|
11
|
+
DEFAULT_RULES_COVERAGE_RATIO_DECIMALS,
|
|
12
|
+
LIFECYCLE_GATE_STAGES,
|
|
13
|
+
MAX_IOS_TEST_QUALITY_SAMPLE_FILES,
|
|
14
|
+
MAX_OBSERVED_CODE_PATHS_SAMPLE,
|
|
15
|
+
MAX_SCOPE_SAMPLE_PATHS,
|
|
16
|
+
} from './runPlatformGateDefaults';
|
|
17
|
+
|
|
18
|
+
const parseConfidence = (value: string | undefined, fallback: number): number => {
|
|
19
|
+
const parsed = Number.parseFloat(value ?? '');
|
|
20
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const LIST_SEPARATOR = DEFAULT_LIST_SEPARATOR;
|
|
24
|
+
|
|
25
|
+
export const MEMORY_SHADOW_CONFIDENCE_BLOCK = parseConfidence(
|
|
26
|
+
process.env.PUMUKI_MEMORY_SHADOW_CONFIDENCE_BLOCK,
|
|
27
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_BLOCK
|
|
28
|
+
);
|
|
29
|
+
export const MEMORY_SHADOW_CONFIDENCE_WARN = parseConfidence(
|
|
30
|
+
process.env.PUMUKI_MEMORY_SHADOW_CONFIDENCE_WARN,
|
|
31
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN
|
|
32
|
+
);
|
|
33
|
+
export const MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY = parseConfidence(
|
|
34
|
+
process.env.PUMUKI_MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY,
|
|
35
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY
|
|
36
|
+
);
|
|
37
|
+
export const MEMORY_SHADOW_CONFIDENCE_ALLOW = parseConfidence(
|
|
38
|
+
process.env.PUMUKI_MEMORY_SHADOW_CONFIDENCE_ALLOW,
|
|
39
|
+
DEFAULT_MEMORY_SHADOW_CONFIDENCE_ALLOW
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export const DEGRADED_MODE_ACTION_BLOCK =
|
|
43
|
+
process.env.PUMUKI_DEGRADED_MODE_ACTION_BLOCK?.trim() || DEFAULT_DEGRADED_MODE_ACTION_BLOCK;
|
|
44
|
+
export const DEGRADED_MODE_ACTION_ALLOW =
|
|
45
|
+
process.env.PUMUKI_DEGRADED_MODE_ACTION_ALLOW?.trim() || DEFAULT_DEGRADED_MODE_ACTION_ALLOW;
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
DEFAULT_GATE_AUDIT_MODE,
|
|
49
|
+
DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION,
|
|
50
|
+
DEFAULT_RULES_COVERAGE_RATIO_DECIMALS,
|
|
51
|
+
LIFECYCLE_GATE_STAGES,
|
|
52
|
+
MAX_IOS_TEST_QUALITY_SAMPLE_FILES,
|
|
53
|
+
MAX_OBSERVED_CODE_PATHS_SAMPLE,
|
|
54
|
+
MAX_SCOPE_SAMPLE_PATHS,
|
|
55
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const DEFAULT_LIST_SEPARATOR = ', ';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_MEMORY_SHADOW_CONFIDENCE_BLOCK = 0.9;
|
|
4
|
+
export const DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN = 0.75;
|
|
5
|
+
export const DEFAULT_MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY = 0.7;
|
|
6
|
+
export const DEFAULT_MEMORY_SHADOW_CONFIDENCE_ALLOW = 0.65;
|
|
7
|
+
|
|
8
|
+
export const DEFAULT_DEGRADED_MODE_ACTION_BLOCK = 'block' as const;
|
|
9
|
+
export const DEFAULT_DEGRADED_MODE_ACTION_ALLOW = 'allow' as const;
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_RULES_COVERAGE_RATIO_DECIMALS = 6;
|
|
12
|
+
export const DEFAULT_GATE_AUDIT_MODE = 'gate' as const;
|
|
13
|
+
export const DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION = 2;
|
|
14
|
+
|
|
15
|
+
export const MAX_IOS_TEST_QUALITY_SAMPLE_FILES = 3;
|
|
16
|
+
export const MAX_OBSERVED_CODE_PATHS_SAMPLE = 5;
|
|
17
|
+
export const MAX_SCOPE_SAMPLE_PATHS = 3;
|
|
18
|
+
|
|
19
|
+
export const LIFECYCLE_GATE_STAGES = ['PRE_COMMIT', 'PRE_PUSH', 'CI'] as const;
|
|
@@ -32,13 +32,29 @@ import { createEmptyEvaluationMetrics } from '../evidence/evaluationMetrics';
|
|
|
32
32
|
import { createEmptySnapshotRulesCoverage } from '../evidence/rulesCoverage';
|
|
33
33
|
import { enforceTddBddPolicy } from '../tdd/enforcement';
|
|
34
34
|
import type { TddBddSnapshot } from '../tdd/types';
|
|
35
|
-
import { resolveSkillsEnforcement } from '../policy/skillsEnforcement';
|
|
36
35
|
import { applyTddBddEnforcement } from '../policy/tddBddEnforcement';
|
|
37
36
|
import { collectAiGateRepoPolicyFindings } from './aiGateRepoPolicyFindings';
|
|
38
37
|
import {
|
|
39
38
|
filterFactsByPathPrefixes,
|
|
40
39
|
resolveGateScopePathPrefixesFromEnv,
|
|
41
40
|
} from './filterFactsByPathPrefixes';
|
|
41
|
+
import {
|
|
42
|
+
DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION,
|
|
43
|
+
DEGRADED_MODE_ACTION_ALLOW,
|
|
44
|
+
DEGRADED_MODE_ACTION_BLOCK,
|
|
45
|
+
DEFAULT_GATE_AUDIT_MODE,
|
|
46
|
+
DEFAULT_RULES_COVERAGE_RATIO_DECIMALS,
|
|
47
|
+
LIFECYCLE_GATE_STAGES,
|
|
48
|
+
MAX_IOS_TEST_QUALITY_SAMPLE_FILES,
|
|
49
|
+
MAX_OBSERVED_CODE_PATHS_SAMPLE,
|
|
50
|
+
MAX_SCOPE_SAMPLE_PATHS,
|
|
51
|
+
LIST_SEPARATOR,
|
|
52
|
+
MEMORY_SHADOW_CONFIDENCE_ALLOW,
|
|
53
|
+
MEMORY_SHADOW_CONFIDENCE_BLOCK,
|
|
54
|
+
MEMORY_SHADOW_CONFIDENCE_WARN,
|
|
55
|
+
MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY,
|
|
56
|
+
} from '../gate/runPlatformGateConfig';
|
|
57
|
+
import type { Severity } from '../../core/rules/Severity';
|
|
42
58
|
|
|
43
59
|
export type OperationalMemoryShadowRecommendation = {
|
|
44
60
|
recommendedOutcome: 'ALLOW' | 'WARN' | 'BLOCK';
|
|
@@ -80,13 +96,18 @@ const defaultServices: GateServices = {
|
|
|
80
96
|
evidence: new EvidenceService(),
|
|
81
97
|
};
|
|
82
98
|
|
|
99
|
+
const SEVERITY_CRITICAL: Severity = 'CRITICAL';
|
|
100
|
+
const SEVERITY_ERROR: Severity = 'ERROR';
|
|
101
|
+
const SEVERITY_WARN: Severity = 'WARN';
|
|
83
102
|
const buildDefaultMemoryShadowRecommendation = (params: {
|
|
84
103
|
findings: ReadonlyArray<Finding>;
|
|
85
104
|
tddBddSnapshot?: TddBddSnapshot;
|
|
86
105
|
}): OperationalMemoryShadowRecommendation | undefined => {
|
|
87
|
-
const hasCritical = params.findings.some(
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
const hasCritical = params.findings.some(
|
|
107
|
+
(finding) => finding.severity === SEVERITY_CRITICAL
|
|
108
|
+
);
|
|
109
|
+
const hasError = params.findings.some((finding) => finding.severity === SEVERITY_ERROR);
|
|
110
|
+
const hasWarn = params.findings.some((finding) => finding.severity === SEVERITY_WARN);
|
|
90
111
|
const reasonCodes: string[] = [];
|
|
91
112
|
|
|
92
113
|
if (hasCritical || hasError) {
|
|
@@ -108,27 +129,27 @@ const buildDefaultMemoryShadowRecommendation = (params: {
|
|
|
108
129
|
if (hasCritical || hasError || params.tddBddSnapshot?.status === 'blocked') {
|
|
109
130
|
return {
|
|
110
131
|
recommendedOutcome: 'BLOCK',
|
|
111
|
-
confidence:
|
|
132
|
+
confidence: MEMORY_SHADOW_CONFIDENCE_BLOCK,
|
|
112
133
|
reasonCodes,
|
|
113
134
|
};
|
|
114
135
|
}
|
|
115
136
|
if (hasWarn) {
|
|
116
137
|
return {
|
|
117
138
|
recommendedOutcome: 'WARN',
|
|
118
|
-
confidence:
|
|
139
|
+
confidence: MEMORY_SHADOW_CONFIDENCE_WARN,
|
|
119
140
|
reasonCodes,
|
|
120
141
|
};
|
|
121
142
|
}
|
|
122
143
|
if (params.tddBddSnapshot?.status === 'advisory') {
|
|
123
144
|
return {
|
|
124
145
|
recommendedOutcome: 'WARN',
|
|
125
|
-
confidence:
|
|
146
|
+
confidence: MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY,
|
|
126
147
|
reasonCodes,
|
|
127
148
|
};
|
|
128
149
|
}
|
|
129
150
|
return {
|
|
130
151
|
recommendedOutcome: 'ALLOW',
|
|
131
|
-
confidence:
|
|
152
|
+
confidence: MEMORY_SHADOW_CONFIDENCE_ALLOW,
|
|
132
153
|
reasonCodes,
|
|
133
154
|
};
|
|
134
155
|
};
|
|
@@ -202,7 +223,10 @@ const toRulesCoverageBlockingFinding = (params: {
|
|
|
202
223
|
}
|
|
203
224
|
const active = params.activeRuleIds.length;
|
|
204
225
|
const evaluated = params.evaluatedRuleIds.length;
|
|
205
|
-
const coverageRatio =
|
|
226
|
+
const coverageRatio =
|
|
227
|
+
active === 0
|
|
228
|
+
? 1
|
|
229
|
+
: Number((evaluated / active).toFixed(DEFAULT_RULES_COVERAGE_RATIO_DECIMALS));
|
|
206
230
|
const unevaluatedRuleIds = [...params.unevaluatedRuleIds].sort().join(', ');
|
|
207
231
|
|
|
208
232
|
return {
|
|
@@ -228,17 +252,12 @@ const toSkillsUnsupportedAutoRulesBlockingFinding = (params: {
|
|
|
228
252
|
return undefined;
|
|
229
253
|
}
|
|
230
254
|
|
|
231
|
-
const unsupportedRuleIds = [
|
|
232
|
-
...new Set([
|
|
233
|
-
...params.unsupportedAutoRuleIds,
|
|
234
|
-
...(params.unsupportedDetectorRuleIds ?? []),
|
|
235
|
-
]),
|
|
236
|
-
].sort();
|
|
255
|
+
const unsupportedRuleIds = [...new Set(params.unsupportedAutoRuleIds)].sort();
|
|
237
256
|
if (unsupportedRuleIds.length === 0) {
|
|
238
257
|
return undefined;
|
|
239
258
|
}
|
|
240
259
|
|
|
241
|
-
const unsupportedRuleIdsToken = unsupportedRuleIds.join(
|
|
260
|
+
const unsupportedRuleIdsToken = unsupportedRuleIds.join(LIST_SEPARATOR);
|
|
242
261
|
|
|
243
262
|
return {
|
|
244
263
|
ruleId: 'governance.skills.detector-mapping.incomplete',
|
|
@@ -246,8 +265,8 @@ const toSkillsUnsupportedAutoRulesBlockingFinding = (params: {
|
|
|
246
265
|
code: 'SKILLS_DETECTOR_MAPPING_INCOMPLETE_HIGH',
|
|
247
266
|
message:
|
|
248
267
|
`Skills detector mapping incomplete at ${params.stage}: ` +
|
|
249
|
-
`
|
|
250
|
-
'Map every skill rule to an intelligent AST detector before proceeding
|
|
268
|
+
`unsupported_auto_rule_ids=[${unsupportedRuleIdsToken}]. ` +
|
|
269
|
+
'Map every stage-applicable AUTO skill rule to an intelligent AST detector before proceeding.',
|
|
251
270
|
filePath: '.ai_evidence.json',
|
|
252
271
|
matchedBy: 'SkillsDetectorMappingGuard',
|
|
253
272
|
source: 'skills-detector-mapping',
|
|
@@ -459,7 +478,9 @@ const toIosTestsQualityBlockingFinding = (params: {
|
|
|
459
478
|
return undefined;
|
|
460
479
|
}
|
|
461
480
|
|
|
462
|
-
const sampleFiles = invalidFiles
|
|
481
|
+
const sampleFiles = invalidFiles
|
|
482
|
+
.slice(0, MAX_IOS_TEST_QUALITY_SAMPLE_FILES)
|
|
483
|
+
.join(' | ');
|
|
463
484
|
return {
|
|
464
485
|
ruleId: 'governance.skills.ios-test-quality.incomplete',
|
|
465
486
|
severity: 'ERROR',
|
|
@@ -485,7 +506,7 @@ const toActiveRulesEmptyForCodeChangesBlockingFinding = (params: {
|
|
|
485
506
|
if (codePaths.length === 0) {
|
|
486
507
|
return undefined;
|
|
487
508
|
}
|
|
488
|
-
const samplePaths = codePaths.slice(0,
|
|
509
|
+
const samplePaths = codePaths.slice(0, MAX_OBSERVED_CODE_PATHS_SAMPLE).join(', ');
|
|
489
510
|
return {
|
|
490
511
|
ruleId: 'governance.rules.active-rule-coverage.empty',
|
|
491
512
|
severity: 'ERROR',
|
|
@@ -569,7 +590,7 @@ const toSkillsScopeComplianceBlockingFinding = (params: {
|
|
|
569
590
|
if (!hasEvaluatedRules) {
|
|
570
591
|
reasons.push(`evaluated_rules_prefix=${prefix} missing`);
|
|
571
592
|
}
|
|
572
|
-
const samplePaths = scopePaths.slice(0,
|
|
593
|
+
const samplePaths = scopePaths.slice(0, MAX_SCOPE_SAMPLE_PATHS).join(', ');
|
|
573
594
|
missingScopes.push(`${scope}{${reasons.join('; ')} sample_paths=[${samplePaths}]}`);
|
|
574
595
|
}
|
|
575
596
|
|
|
@@ -621,13 +642,13 @@ const toDegradedModeFinding = (params: {
|
|
|
621
642
|
if (!degraded?.enabled) {
|
|
622
643
|
return undefined;
|
|
623
644
|
}
|
|
624
|
-
if (degraded.action ===
|
|
645
|
+
if (degraded.action === DEGRADED_MODE_ACTION_BLOCK) {
|
|
625
646
|
return {
|
|
626
647
|
ruleId: 'governance.degraded-mode.blocked',
|
|
627
648
|
severity: 'ERROR',
|
|
628
649
|
code: degraded.code,
|
|
629
650
|
message:
|
|
630
|
-
`Degraded mode is active at ${params.stage} with fail-closed action
|
|
651
|
+
`Degraded mode is active at ${params.stage} with fail-closed action=${DEGRADED_MODE_ACTION_BLOCK}. ` +
|
|
631
652
|
`reason=${degraded.reason} source=${degraded.source}.`,
|
|
632
653
|
filePath: '.pumuki/degraded-mode.json',
|
|
633
654
|
matchedBy: 'DegradedModeGuard',
|
|
@@ -639,7 +660,7 @@ const toDegradedModeFinding = (params: {
|
|
|
639
660
|
severity: 'INFO',
|
|
640
661
|
code: degraded.code,
|
|
641
662
|
message:
|
|
642
|
-
`Degraded mode is active at ${params.stage} with fail-open action
|
|
663
|
+
`Degraded mode is active at ${params.stage} with fail-open action=${DEGRADED_MODE_ACTION_ALLOW}. ` +
|
|
643
664
|
`reason=${degraded.reason} source=${degraded.source}.`,
|
|
644
665
|
filePath: '.pumuki/degraded-mode.json',
|
|
645
666
|
matchedBy: 'DegradedModeGuard',
|
|
@@ -780,7 +801,6 @@ const toCrossPlatformCriticalEnforcementBlockingFinding = (params: {
|
|
|
780
801
|
.sort();
|
|
781
802
|
|
|
782
803
|
if (criticalSkillRules.length === 0) {
|
|
783
|
-
gaps.push(`${platform}{critical_profile_rules=missing}`);
|
|
784
804
|
continue;
|
|
785
805
|
}
|
|
786
806
|
|
|
@@ -824,35 +844,9 @@ const applySkillsFindingEnforcement = (
|
|
|
824
844
|
if (!finding) {
|
|
825
845
|
return undefined;
|
|
826
846
|
}
|
|
827
|
-
const skillsEnforcement = resolveSkillsEnforcement();
|
|
828
|
-
if (skillsEnforcement.blocking) {
|
|
829
|
-
return finding;
|
|
830
|
-
}
|
|
831
847
|
return {
|
|
832
848
|
...finding,
|
|
833
|
-
severity: '
|
|
834
|
-
};
|
|
835
|
-
};
|
|
836
|
-
|
|
837
|
-
const toSoftPreCommitSkillsFinding = (params: {
|
|
838
|
-
finding: Finding | undefined;
|
|
839
|
-
enabled: boolean;
|
|
840
|
-
observedCodePaths: ReadonlyArray<string>;
|
|
841
|
-
}): Finding | undefined => {
|
|
842
|
-
if (!params.finding) {
|
|
843
|
-
return undefined;
|
|
844
|
-
}
|
|
845
|
-
if (!params.enabled || !shouldBlockFromFinding(params.finding)) {
|
|
846
|
-
return params.finding;
|
|
847
|
-
}
|
|
848
|
-
return {
|
|
849
|
-
...params.finding,
|
|
850
|
-
severity: 'WARN',
|
|
851
|
-
code: `${params.finding.code}_SOFT_PRECOMMIT`,
|
|
852
|
-
message:
|
|
853
|
-
`${params.finding.message} ` +
|
|
854
|
-
`Soft-enforced at PRE_COMMIT for low-risk scope (observed_code_paths=${params.observedCodePaths.length}). ` +
|
|
855
|
-
'Strict enforcement remains active at PRE_PUSH/CI.',
|
|
849
|
+
severity: 'ERROR',
|
|
856
850
|
};
|
|
857
851
|
};
|
|
858
852
|
|
|
@@ -874,7 +868,7 @@ export async function runPlatformGate(params: {
|
|
|
874
868
|
...params.dependencies,
|
|
875
869
|
};
|
|
876
870
|
const repoRoot = git.resolveRepoRoot();
|
|
877
|
-
const auditMode = params.auditMode ??
|
|
871
|
+
const auditMode = params.auditMode ?? DEFAULT_GATE_AUDIT_MODE;
|
|
878
872
|
const shouldShortCircuitSdd = params.sddShortCircuit ?? false;
|
|
879
873
|
let sddDecision:
|
|
880
874
|
| Pick<SddDecision, 'allowed' | 'code' | 'message'>
|
|
@@ -1087,15 +1081,12 @@ export async function runPlatformGate(params: {
|
|
|
1087
1081
|
policyTrace: params.policyTrace,
|
|
1088
1082
|
})
|
|
1089
1083
|
: undefined;
|
|
1090
|
-
const degradedModeFinding =
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
policyTrace: params.policyTrace,
|
|
1097
|
-
})
|
|
1098
|
-
: undefined;
|
|
1084
|
+
const degradedModeFinding = LIFECYCLE_GATE_STAGES.includes(params.policy.stage)
|
|
1085
|
+
? toDegradedModeFinding({
|
|
1086
|
+
stage: params.policy.stage,
|
|
1087
|
+
policyTrace: params.policyTrace,
|
|
1088
|
+
})
|
|
1089
|
+
: undefined;
|
|
1099
1090
|
const astIntelligenceDualValidation:
|
|
1100
1091
|
| AstIntelligenceDualValidationResult
|
|
1101
1092
|
| undefined =
|
|
@@ -1125,7 +1116,8 @@ export async function runPlatformGate(params: {
|
|
|
1125
1116
|
);
|
|
1126
1117
|
}
|
|
1127
1118
|
}
|
|
1128
|
-
const degradedModeBlocks =
|
|
1119
|
+
const degradedModeBlocks =
|
|
1120
|
+
params.policyTrace?.degraded?.action === DEGRADED_MODE_ACTION_BLOCK;
|
|
1129
1121
|
const rulesCoverage = coverage
|
|
1130
1122
|
? {
|
|
1131
1123
|
stage: params.policy.stage,
|
|
@@ -1189,7 +1181,11 @@ export async function runPlatformGate(params: {
|
|
|
1189
1181
|
coverage_ratio:
|
|
1190
1182
|
coverage.activeRuleIds.length === 0
|
|
1191
1183
|
? 1
|
|
1192
|
-
: Number(
|
|
1184
|
+
: Number(
|
|
1185
|
+
(
|
|
1186
|
+
coverage.evaluatedRuleIds.length / coverage.activeRuleIds.length
|
|
1187
|
+
).toFixed(DEFAULT_RULES_COVERAGE_RATIO_DECIMALS)
|
|
1188
|
+
),
|
|
1193
1189
|
}
|
|
1194
1190
|
: createEmptySnapshotRulesCoverage(params.policy.stage);
|
|
1195
1191
|
const brownfieldHotspotFindings = dependencies.evaluateBrownfieldHotspotFindings({
|
|
@@ -1214,37 +1210,9 @@ export async function runPlatformGate(params: {
|
|
|
1214
1210
|
const hasNativeBlockingFinding = findings.some(
|
|
1215
1211
|
(finding) => finding.severity === 'ERROR' || finding.severity === 'CRITICAL'
|
|
1216
1212
|
);
|
|
1217
|
-
const
|
|
1218
|
-
const
|
|
1219
|
-
const
|
|
1220
|
-
params.policy.stage === 'PRE_COMMIT'
|
|
1221
|
-
&& preCommitSoftSkillsEnabled
|
|
1222
|
-
&& lowRiskPreCommitWindow
|
|
1223
|
-
&& !sddBlockingFinding
|
|
1224
|
-
&& !degradedModeBlocks
|
|
1225
|
-
&& !shouldBlockFromFinding(policyAsCodeBlockingFinding)
|
|
1226
|
-
&& !shouldBlockFromFinding(effectiveUnsupportedSkillsMappingFinding)
|
|
1227
|
-
&& !shouldBlockFromFinding(coverageBlockingFinding)
|
|
1228
|
-
&& !shouldBlockFromFinding(activeRulesEmptyForCodeChangesFinding)
|
|
1229
|
-
&& !shouldBlockFromFinding(effectiveIosTestsQualityFinding)
|
|
1230
|
-
&& !shouldBlockFromFinding(astIntelligenceDualFinding)
|
|
1231
|
-
&& !hasTddBddBlockingFinding
|
|
1232
|
-
&& !hasNativeBlockingFinding;
|
|
1233
|
-
const effectivePlatformSkillsCoverageFinding = toSoftPreCommitSkillsFinding({
|
|
1234
|
-
finding: effectivePlatformSkillsCoverageInput,
|
|
1235
|
-
enabled: shouldSoftEnforceSkillsFindings,
|
|
1236
|
-
observedCodePaths,
|
|
1237
|
-
});
|
|
1238
|
-
const effectiveCrossPlatformCriticalFinding = toSoftPreCommitSkillsFinding({
|
|
1239
|
-
finding: effectiveCrossPlatformCriticalInput,
|
|
1240
|
-
enabled: shouldSoftEnforceSkillsFindings,
|
|
1241
|
-
observedCodePaths,
|
|
1242
|
-
});
|
|
1243
|
-
const effectiveSkillsScopeComplianceFinding = toSoftPreCommitSkillsFinding({
|
|
1244
|
-
finding: effectiveSkillsScopeComplianceInput,
|
|
1245
|
-
enabled: shouldSoftEnforceSkillsFindings,
|
|
1246
|
-
observedCodePaths,
|
|
1247
|
-
});
|
|
1213
|
+
const effectivePlatformSkillsCoverageFinding = effectivePlatformSkillsCoverageInput;
|
|
1214
|
+
const effectiveCrossPlatformCriticalFinding = effectiveCrossPlatformCriticalInput;
|
|
1215
|
+
const effectiveSkillsScopeComplianceFinding = effectiveSkillsScopeComplianceInput;
|
|
1248
1216
|
const effectiveFindings = sddBlockingFinding
|
|
1249
1217
|
? [
|
|
1250
1218
|
sddBlockingFinding,
|
|
@@ -1395,7 +1363,7 @@ export async function runPlatformGate(params: {
|
|
|
1395
1363
|
if (params.silent !== true) {
|
|
1396
1364
|
process.stdout.write(
|
|
1397
1365
|
`[pumuki][memory-shadow] recommended=${memoryShadowRecommendation.recommendedOutcome}` +
|
|
1398
|
-
` confidence=${memoryShadowRecommendation.confidence.toFixed(
|
|
1366
|
+
` confidence=${memoryShadowRecommendation.confidence.toFixed(DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION)}` +
|
|
1399
1367
|
` reasons=${memoryShadowRecommendation.reasonCodes.join(',')}\n`
|
|
1400
1368
|
);
|
|
1401
1369
|
}
|
|
@@ -6,47 +6,7 @@ export type SkillsEnforcementResolution = {
|
|
|
6
6
|
blocking: boolean;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
const SKILLS_ENFORCEMENT_ENV = 'PUMUKI_SKILLS_ENFORCEMENT';
|
|
10
|
-
|
|
11
|
-
const toSkillsEnforcementMode = (
|
|
12
|
-
value: string | undefined
|
|
13
|
-
): SkillsEnforcementMode | null => {
|
|
14
|
-
if (typeof value !== 'string') {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const normalized = value.trim().toLowerCase();
|
|
18
|
-
if (
|
|
19
|
-
normalized === 'strict'
|
|
20
|
-
|| normalized === '1'
|
|
21
|
-
|| normalized === 'true'
|
|
22
|
-
|| normalized === 'yes'
|
|
23
|
-
|| normalized === 'on'
|
|
24
|
-
) {
|
|
25
|
-
return 'strict';
|
|
26
|
-
}
|
|
27
|
-
if (
|
|
28
|
-
normalized === 'advisory'
|
|
29
|
-
|| normalized === 'warn'
|
|
30
|
-
|| normalized === 'warning'
|
|
31
|
-
|| normalized === '0'
|
|
32
|
-
|| normalized === 'false'
|
|
33
|
-
|| normalized === 'no'
|
|
34
|
-
|| normalized === 'off'
|
|
35
|
-
) {
|
|
36
|
-
return 'advisory';
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
9
|
export const resolveSkillsEnforcement = (): SkillsEnforcementResolution => {
|
|
42
|
-
const modeFromEnv = toSkillsEnforcementMode(process.env[SKILLS_ENFORCEMENT_ENV]);
|
|
43
|
-
if (modeFromEnv) {
|
|
44
|
-
return {
|
|
45
|
-
mode: modeFromEnv,
|
|
46
|
-
source: 'env',
|
|
47
|
-
blocking: modeFromEnv === 'strict',
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
10
|
return {
|
|
51
11
|
mode: 'strict',
|
|
52
12
|
source: 'default',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.133",
|
|
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": {
|