pumuki 6.3.132 → 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 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.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
+
9
17
  ## [6.3.132] - 2026-05-03
10
18
 
11
19
  ### Fixed
@@ -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((finding) => finding.severity === 'CRITICAL');
88
- const hasError = params.findings.some((finding) => finding.severity === 'ERROR');
89
- const hasWarn = params.findings.some((finding) => finding.severity === 'WARN');
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: 0.9,
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: 0.75,
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: 0.7,
146
+ confidence: MEMORY_SHADOW_CONFIDENCE_WARN_ADVISORY,
126
147
  reasonCodes,
127
148
  };
128
149
  }
129
150
  return {
130
151
  recommendedOutcome: 'ALLOW',
131
- confidence: 0.65,
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 = active === 0 ? 1 : Number((evaluated / active).toFixed(6));
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 {
@@ -233,7 +257,7 @@ const toSkillsUnsupportedAutoRulesBlockingFinding = (params: {
233
257
  return undefined;
234
258
  }
235
259
 
236
- const unsupportedRuleIdsToken = unsupportedRuleIds.join(', ');
260
+ const unsupportedRuleIdsToken = unsupportedRuleIds.join(LIST_SEPARATOR);
237
261
 
238
262
  return {
239
263
  ruleId: 'governance.skills.detector-mapping.incomplete',
@@ -454,7 +478,9 @@ const toIosTestsQualityBlockingFinding = (params: {
454
478
  return undefined;
455
479
  }
456
480
 
457
- const sampleFiles = invalidFiles.slice(0, 3).join(' | ');
481
+ const sampleFiles = invalidFiles
482
+ .slice(0, MAX_IOS_TEST_QUALITY_SAMPLE_FILES)
483
+ .join(' | ');
458
484
  return {
459
485
  ruleId: 'governance.skills.ios-test-quality.incomplete',
460
486
  severity: 'ERROR',
@@ -480,7 +506,7 @@ const toActiveRulesEmptyForCodeChangesBlockingFinding = (params: {
480
506
  if (codePaths.length === 0) {
481
507
  return undefined;
482
508
  }
483
- const samplePaths = codePaths.slice(0, 5).join(', ');
509
+ const samplePaths = codePaths.slice(0, MAX_OBSERVED_CODE_PATHS_SAMPLE).join(', ');
484
510
  return {
485
511
  ruleId: 'governance.rules.active-rule-coverage.empty',
486
512
  severity: 'ERROR',
@@ -564,7 +590,7 @@ const toSkillsScopeComplianceBlockingFinding = (params: {
564
590
  if (!hasEvaluatedRules) {
565
591
  reasons.push(`evaluated_rules_prefix=${prefix} missing`);
566
592
  }
567
- const samplePaths = scopePaths.slice(0, 3).join(', ');
593
+ const samplePaths = scopePaths.slice(0, MAX_SCOPE_SAMPLE_PATHS).join(', ');
568
594
  missingScopes.push(`${scope}{${reasons.join('; ')} sample_paths=[${samplePaths}]}`);
569
595
  }
570
596
 
@@ -616,13 +642,13 @@ const toDegradedModeFinding = (params: {
616
642
  if (!degraded?.enabled) {
617
643
  return undefined;
618
644
  }
619
- if (degraded.action === 'block') {
645
+ if (degraded.action === DEGRADED_MODE_ACTION_BLOCK) {
620
646
  return {
621
647
  ruleId: 'governance.degraded-mode.blocked',
622
648
  severity: 'ERROR',
623
649
  code: degraded.code,
624
650
  message:
625
- `Degraded mode is active at ${params.stage} with fail-closed action=block. ` +
651
+ `Degraded mode is active at ${params.stage} with fail-closed action=${DEGRADED_MODE_ACTION_BLOCK}. ` +
626
652
  `reason=${degraded.reason} source=${degraded.source}.`,
627
653
  filePath: '.pumuki/degraded-mode.json',
628
654
  matchedBy: 'DegradedModeGuard',
@@ -634,7 +660,7 @@ const toDegradedModeFinding = (params: {
634
660
  severity: 'INFO',
635
661
  code: degraded.code,
636
662
  message:
637
- `Degraded mode is active at ${params.stage} with fail-open action=allow. ` +
663
+ `Degraded mode is active at ${params.stage} with fail-open action=${DEGRADED_MODE_ACTION_ALLOW}. ` +
638
664
  `reason=${degraded.reason} source=${degraded.source}.`,
639
665
  filePath: '.pumuki/degraded-mode.json',
640
666
  matchedBy: 'DegradedModeGuard',
@@ -775,7 +801,6 @@ const toCrossPlatformCriticalEnforcementBlockingFinding = (params: {
775
801
  .sort();
776
802
 
777
803
  if (criticalSkillRules.length === 0) {
778
- gaps.push(`${platform}{critical_profile_rules=missing}`);
779
804
  continue;
780
805
  }
781
806
 
@@ -819,35 +844,9 @@ const applySkillsFindingEnforcement = (
819
844
  if (!finding) {
820
845
  return undefined;
821
846
  }
822
- const skillsEnforcement = resolveSkillsEnforcement();
823
- if (skillsEnforcement.blocking) {
824
- return finding;
825
- }
826
847
  return {
827
848
  ...finding,
828
- severity: 'WARN',
829
- };
830
- };
831
-
832
- const toSoftPreCommitSkillsFinding = (params: {
833
- finding: Finding | undefined;
834
- enabled: boolean;
835
- observedCodePaths: ReadonlyArray<string>;
836
- }): Finding | undefined => {
837
- if (!params.finding) {
838
- return undefined;
839
- }
840
- if (!params.enabled || !shouldBlockFromFinding(params.finding)) {
841
- return params.finding;
842
- }
843
- return {
844
- ...params.finding,
845
- severity: 'WARN',
846
- code: `${params.finding.code}_SOFT_PRECOMMIT`,
847
- message:
848
- `${params.finding.message} ` +
849
- `Soft-enforced at PRE_COMMIT for low-risk scope (observed_code_paths=${params.observedCodePaths.length}). ` +
850
- 'Strict enforcement remains active at PRE_PUSH/CI.',
849
+ severity: 'ERROR',
851
850
  };
852
851
  };
853
852
 
@@ -869,7 +868,7 @@ export async function runPlatformGate(params: {
869
868
  ...params.dependencies,
870
869
  };
871
870
  const repoRoot = git.resolveRepoRoot();
872
- const auditMode = params.auditMode ?? 'gate';
871
+ const auditMode = params.auditMode ?? DEFAULT_GATE_AUDIT_MODE;
873
872
  const shouldShortCircuitSdd = params.sddShortCircuit ?? false;
874
873
  let sddDecision:
875
874
  | Pick<SddDecision, 'allowed' | 'code' | 'message'>
@@ -1082,15 +1081,12 @@ export async function runPlatformGate(params: {
1082
1081
  policyTrace: params.policyTrace,
1083
1082
  })
1084
1083
  : undefined;
1085
- const degradedModeFinding =
1086
- params.policy.stage === 'PRE_COMMIT' ||
1087
- params.policy.stage === 'PRE_PUSH' ||
1088
- params.policy.stage === 'CI'
1089
- ? toDegradedModeFinding({
1090
- stage: params.policy.stage,
1091
- policyTrace: params.policyTrace,
1092
- })
1093
- : 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;
1094
1090
  const astIntelligenceDualValidation:
1095
1091
  | AstIntelligenceDualValidationResult
1096
1092
  | undefined =
@@ -1120,7 +1116,8 @@ export async function runPlatformGate(params: {
1120
1116
  );
1121
1117
  }
1122
1118
  }
1123
- const degradedModeBlocks = params.policyTrace?.degraded?.action === 'block';
1119
+ const degradedModeBlocks =
1120
+ params.policyTrace?.degraded?.action === DEGRADED_MODE_ACTION_BLOCK;
1124
1121
  const rulesCoverage = coverage
1125
1122
  ? {
1126
1123
  stage: params.policy.stage,
@@ -1184,7 +1181,11 @@ export async function runPlatformGate(params: {
1184
1181
  coverage_ratio:
1185
1182
  coverage.activeRuleIds.length === 0
1186
1183
  ? 1
1187
- : Number((coverage.evaluatedRuleIds.length / coverage.activeRuleIds.length).toFixed(6)),
1184
+ : Number(
1185
+ (
1186
+ coverage.evaluatedRuleIds.length / coverage.activeRuleIds.length
1187
+ ).toFixed(DEFAULT_RULES_COVERAGE_RATIO_DECIMALS)
1188
+ ),
1188
1189
  }
1189
1190
  : createEmptySnapshotRulesCoverage(params.policy.stage);
1190
1191
  const brownfieldHotspotFindings = dependencies.evaluateBrownfieldHotspotFindings({
@@ -1209,37 +1210,9 @@ export async function runPlatformGate(params: {
1209
1210
  const hasNativeBlockingFinding = findings.some(
1210
1211
  (finding) => finding.severity === 'ERROR' || finding.severity === 'CRITICAL'
1211
1212
  );
1212
- const preCommitSoftSkillsEnabled = process.env.PUMUKI_PRE_COMMIT_SOFT_SKILLS !== '0';
1213
- const lowRiskPreCommitWindow = observedCodePaths.length > 0 && observedCodePaths.length <= 3;
1214
- const shouldSoftEnforceSkillsFindings =
1215
- params.policy.stage === 'PRE_COMMIT'
1216
- && preCommitSoftSkillsEnabled
1217
- && lowRiskPreCommitWindow
1218
- && !sddBlockingFinding
1219
- && !degradedModeBlocks
1220
- && !shouldBlockFromFinding(policyAsCodeBlockingFinding)
1221
- && !shouldBlockFromFinding(effectiveUnsupportedSkillsMappingFinding)
1222
- && !shouldBlockFromFinding(coverageBlockingFinding)
1223
- && !shouldBlockFromFinding(activeRulesEmptyForCodeChangesFinding)
1224
- && !shouldBlockFromFinding(effectiveIosTestsQualityFinding)
1225
- && !shouldBlockFromFinding(astIntelligenceDualFinding)
1226
- && !hasTddBddBlockingFinding
1227
- && !hasNativeBlockingFinding;
1228
- const effectivePlatformSkillsCoverageFinding = toSoftPreCommitSkillsFinding({
1229
- finding: effectivePlatformSkillsCoverageInput,
1230
- enabled: shouldSoftEnforceSkillsFindings,
1231
- observedCodePaths,
1232
- });
1233
- const effectiveCrossPlatformCriticalFinding = toSoftPreCommitSkillsFinding({
1234
- finding: effectiveCrossPlatformCriticalInput,
1235
- enabled: shouldSoftEnforceSkillsFindings,
1236
- observedCodePaths,
1237
- });
1238
- const effectiveSkillsScopeComplianceFinding = toSoftPreCommitSkillsFinding({
1239
- finding: effectiveSkillsScopeComplianceInput,
1240
- enabled: shouldSoftEnforceSkillsFindings,
1241
- observedCodePaths,
1242
- });
1213
+ const effectivePlatformSkillsCoverageFinding = effectivePlatformSkillsCoverageInput;
1214
+ const effectiveCrossPlatformCriticalFinding = effectiveCrossPlatformCriticalInput;
1215
+ const effectiveSkillsScopeComplianceFinding = effectiveSkillsScopeComplianceInput;
1243
1216
  const effectiveFindings = sddBlockingFinding
1244
1217
  ? [
1245
1218
  sddBlockingFinding,
@@ -1390,7 +1363,7 @@ export async function runPlatformGate(params: {
1390
1363
  if (params.silent !== true) {
1391
1364
  process.stdout.write(
1392
1365
  `[pumuki][memory-shadow] recommended=${memoryShadowRecommendation.recommendedOutcome}` +
1393
- ` confidence=${memoryShadowRecommendation.confidence.toFixed(2)}` +
1366
+ ` confidence=${memoryShadowRecommendation.confidence.toFixed(DEFAULT_MEMORY_SHADOW_DISPLAY_PRECISION)}` +
1394
1367
  ` reasons=${memoryShadowRecommendation.reasonCodes.join(',')}\n`
1395
1368
  );
1396
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.132",
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": {