pumuki 6.3.144 → 6.3.146

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.
@@ -532,6 +532,23 @@ let text = "XCTAssertEqual(value, expected)"
532
532
  assert.equal(hasSwiftXCTestAssertionUsage(ignored), false);
533
533
  });
534
534
 
535
+ test('hasSwiftXCTestAssertionUsage excluye XCTest compatible con UI automation', () => {
536
+ const uiSource = `
537
+ import XCTest
538
+
539
+ final class BuyerCommerceUISmokeTests: XCTestCase {
540
+ func test_buyer_flow() {
541
+ let app = XCUIApplication()
542
+ app.launch()
543
+ XCTAssertTrue(app.buttons["Comprar"].exists)
544
+ }
545
+ }
546
+ `;
547
+
548
+ assert.equal(hasSwiftXCTestAssertionUsage(uiSource), false);
549
+ assert.equal(hasSwiftXCTUnwrapUsage(`${uiSource}\nlet value = try XCTUnwrap(optional)`), false);
550
+ });
551
+
535
552
  test('hasSwiftXCTUnwrapUsage detecta XCTUnwrap real y evita strings', () => {
536
553
  const source = `
537
554
  let value = try XCTUnwrap(optionalValue)
@@ -767,6 +767,10 @@ export const hasSwiftMixedTestingFrameworksUsage = (source: string): boolean =>
767
767
  };
768
768
 
769
769
  export const hasSwiftXCTestAssertionUsage = (source: string): boolean => {
770
+ if (hasSwiftLegacyXCTestUiOrPerformanceUsage(source)) {
771
+ return false;
772
+ }
773
+
770
774
  return (
771
775
  collectSwiftRegexLines(source, /\bXCTAssert[A-Za-z0-9_]*\s*\(/).length > 0 ||
772
776
  collectSwiftRegexLines(source, /\bXCTFail\s*\(/).length > 0
@@ -774,6 +778,10 @@ export const hasSwiftXCTestAssertionUsage = (source: string): boolean => {
774
778
  };
775
779
 
776
780
  export const hasSwiftXCTUnwrapUsage = (source: string): boolean => {
781
+ if (hasSwiftLegacyXCTestUiOrPerformanceUsage(source)) {
782
+ return false;
783
+ }
784
+
777
785
  return collectSwiftRegexLines(source, /\bXCTUnwrap\s*\(/).length > 0;
778
786
  };
779
787
 
@@ -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.145)
8
+
9
+ - **RuralGo PUMUKI-INC-124:** `skills.ios.critical-test-quality` deja de bloquear tests XCTest de UI automation/performance cuando usan `XCUIApplication`, `XCTMetric` o `measure`.
10
+ - **Swift Testing sin regresión:** los unit/integration tests XCTest modernizables siguen bloqueados si incumplen el contrato; solo se respeta la compatibilidad explícita que `swift-testing-expert` permite para UI/performance.
11
+ - **Rollout:** publicar `pumuki@6.3.145`, repinear primero RuralGo y revalidar el smoke UI XCTest que estaba bloqueado.
12
+
7
13
  ### 2026-05-05 (v6.3.144)
8
14
 
9
15
  - **RuralGo PUMUKI-INC-122:** `pumuki sdd evidence` serializa escrituras concurrentes del artefacto `.pumuki/artifacts/pumuki-evidence-v1.json` con lock local y rename atómico.
@@ -0,0 +1,40 @@
1
+ type BlockingCauseCandidate = {
2
+ code?: string;
3
+ message?: string;
4
+ remediation?: string;
5
+ severity?: string;
6
+ };
7
+
8
+ const UMBRELLA_EVIDENCE_CODES = new Set([
9
+ 'EVIDENCE_GATE_BLOCKED',
10
+ 'AI_GATE_BLOCKED',
11
+ 'GATE_BLOCKED',
12
+ ]);
13
+
14
+ export const isTddBddBlockingCause = (candidate?: {
15
+ code?: string;
16
+ message?: string;
17
+ }): boolean => {
18
+ const code = candidate?.code ?? '';
19
+ const message = candidate?.message ?? '';
20
+ return code.startsWith('TDD_BDD_') || /\bTDD_BDD_[A-Z0-9_]+\b/u.test(message);
21
+ };
22
+
23
+ export const resolvePrimaryBlockingCause = <T extends BlockingCauseCandidate>(
24
+ candidates: ReadonlyArray<T>
25
+ ): T | undefined => {
26
+ const tddBddCause = candidates.find(isTddBddBlockingCause);
27
+ if (tddBddCause) {
28
+ return tddBddCause;
29
+ }
30
+
31
+ const specificCause = candidates.find((candidate) => {
32
+ const code = candidate.code ?? '';
33
+ return code.length > 0 && !UMBRELLA_EVIDENCE_CODES.has(code);
34
+ });
35
+ if (specificCause) {
36
+ return specificCause;
37
+ }
38
+
39
+ return candidates[0];
40
+ };
@@ -12,6 +12,14 @@ export const REMEDIATION_HINT_BY_CODE: Readonly<Record<string, string>> = {
12
12
  EVIDENCE_BRANCH_MISMATCH: 'Regenera evidencia en la rama actual y reintenta.',
13
13
  TDD_BDD_BASELINE_BLOCKED:
14
14
  'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de continuar.',
15
+ TDD_BDD_EVIDENCE_INVALID:
16
+ 'Regenera la evidencia TDD/BDD válida del escenario afectado y vuelve a ejecutar el gate.',
17
+ TDD_BDD_SCENARIO_FILE_MISSING:
18
+ 'Crea o corrige el fichero .feature referenciado por la evidencia TDD/BDD y revalida.',
19
+ TDD_BDD_EVIDENCE_STALE:
20
+ 'Reejecuta los tests baseline del componente tocado, refresca la evidencia TDD/BDD y revalida.',
21
+ TDD_BDD_EVIDENCE_MISSING:
22
+ 'Genera evidencia TDD/BDD para el cambio actual antes de continuar.',
15
23
  EVIDENCE_RULES_COVERAGE_MISSING: 'Ejecuta auditoría completa para recalcular rules_coverage.',
16
24
  EVIDENCE_RULES_COVERAGE_INCOMPLETE: 'Asegura coverage_ratio=1 y unevaluated=0.',
17
25
  ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
@@ -442,6 +442,10 @@ const isXCTestSource = (content: string): boolean => {
442
442
  return /\bimport\s+XCTest\b/.test(content) || /\bXCTestCase\b/.test(content);
443
443
  };
444
444
 
445
+ const isXCTestUiOrPerformanceCompatibilitySource = (content: string): boolean => {
446
+ return /\bXCUIApplication\b|\bXCTMetric\b|\bmeasure\s*(?:\(|\{)/.test(content);
447
+ };
448
+
445
449
  const hasMakeSUTPattern = (content: string): boolean => /\bmakeSUT\s*\(/.test(content);
446
450
 
447
451
  const hasTrackForMemoryLeaksPattern = (content: string): boolean =>
@@ -461,6 +465,9 @@ const toIosTestsQualityBlockingFinding = (params: {
461
465
  if (!isXCTestSource(testFile.content)) {
462
466
  continue;
463
467
  }
468
+ if (isXCTestUiOrPerformanceCompatibilitySource(testFile.content)) {
469
+ continue;
470
+ }
464
471
  const missingMarkers: string[] = [];
465
472
  if (!hasMakeSUTPattern(testFile.content)) {
466
473
  missingMarkers.push('makeSUT()');
@@ -35,6 +35,7 @@ import {
35
35
  DEFAULT_GATE_REMEDIATION as DEFAULT_BLOCKED_REMEDIATION,
36
36
  REMEDIATION_HINT_BY_CODE as BLOCKED_REMEDIATION_BY_CODE,
37
37
  } from '../gate/remediationCatalog';
38
+ import { resolvePrimaryBlockingCause } from '../gate/blockingCause';
38
39
 
39
40
  const PRE_PUSH_UPSTREAM_REQUIRED_MESSAGE =
40
41
  'pumuki pre-push blocked: branch has no upstream tracking reference. Configure upstream first (for example: git push --set-upstream origin <branch>) and retry.';
@@ -273,10 +274,16 @@ const notifyGateBlockedForStage = (params: {
273
274
  evidence?.snapshot.stage === params.stage
274
275
  ? evidence.snapshot.findings
275
276
  : [];
276
- const primaryStageFinding = resolvePrimaryBlockedStageFinding(stageFindings);
277
- const firstViolation = evidence?.ai_gate.violations[0];
278
- const causeCode = primaryStageFinding?.code ?? firstViolation?.code ?? params.fallbackCauseCode;
279
- const causeMessage = primaryStageFinding?.message ?? firstViolation?.message ?? params.fallbackCauseMessage;
277
+ const blockingStageFindings = stageFindings.filter((finding) =>
278
+ isSeverityAtLeast(finding.severity, 'ERROR')
279
+ );
280
+ const primaryCause = resolvePrimaryBlockingCause([
281
+ ...blockingStageFindings,
282
+ ...(evidence?.ai_gate.violations ?? []),
283
+ ]);
284
+ const primaryStageFinding = primaryCause ?? resolvePrimaryBlockedStageFinding(stageFindings);
285
+ const causeCode = primaryStageFinding?.code ?? params.fallbackCauseCode;
286
+ const causeMessage = primaryStageFinding?.message ?? params.fallbackCauseMessage;
280
287
  const remediation =
281
288
  BLOCKED_REMEDIATION_BY_CODE[causeCode]
282
289
  ?? params.fallbackRemediation
@@ -21,6 +21,7 @@ import {
21
21
  type PreWriteEnforcementResolution,
22
22
  } from '../policy/preWriteEnforcement';
23
23
  import { readLifecycleExperimentalFeaturesSnapshot } from './experimentalFeaturesSnapshot';
24
+ import { resolvePrimaryBlockingCause } from '../gate/blockingCause';
24
25
 
25
26
  import {
26
27
  type ParsedArgs,
@@ -321,9 +322,9 @@ export const runSddCommand = async (parsed: ParsedArgs, activeDependencies: Life
321
322
  return 1;
322
323
  }
323
324
  if (aiGate && !aiGate.allowed && preWriteEnforcement.blocking) {
324
- const firstViolation = aiGate.violations[0];
325
- const causeCode = firstViolation?.code ?? 'AI_GATE_BLOCKED';
326
- const causeMessage = firstViolation?.message ?? 'AI gate blocked PRE_WRITE stage.';
325
+ const primaryViolation = resolvePrimaryBlockingCause(aiGate.violations);
326
+ const causeCode = primaryViolation?.code ?? 'AI_GATE_BLOCKED';
327
+ const causeMessage = primaryViolation?.message ?? 'AI gate blocked PRE_WRITE stage.';
327
328
  activeDependencies.emitGateBlockedNotification({
328
329
  repoRoot: process.cwd(),
329
330
  stage: result.stage,
@@ -36,6 +36,7 @@ import {
36
36
  resolveGitAtomicityEnforcement,
37
37
  type GitAtomicityEnforcementResolution,
38
38
  } from '../policy/gitAtomicityEnforcement';
39
+ import { resolvePrimaryBlockingCause } from '../gate/blockingCause';
39
40
 
40
41
  export type LifecycleWatchStage = 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
41
42
  export type LifecycleWatchScope = 'workingTree' | 'staged' | 'repoAndStaged' | 'repo';
@@ -119,6 +120,11 @@ type LifecycleWatchDependencies = {
119
120
  };
120
121
 
121
122
  const defaultGitService = new GitService();
123
+ const GIT_STATUS_PORCELAIN_ARGS = [
124
+ 'status',
125
+ '--porcelain=v1',
126
+ '--untracked-files=all',
127
+ ] as const;
122
128
 
123
129
  class RepoScopedGitService extends GitService implements IGitService {
124
130
  constructor(private readonly repoRoot: string) {
@@ -137,7 +143,7 @@ class RepoScopedGitService extends GitService implements IGitService {
137
143
  const defaultDependencies: LifecycleWatchDependencies = {
138
144
  resolveRepoRoot: () => defaultGitService.resolveRepoRoot(),
139
145
  readChangeToken: (repoRoot) =>
140
- defaultGitService.runGit(['status', '--porcelain=v1', '--untracked-files=all'], repoRoot),
146
+ defaultGitService.runGit([...GIT_STATUS_PORCELAIN_ARGS], repoRoot),
141
147
  resolvePolicyForStage: (stage) => resolvePolicyForStage(stage),
142
148
  resolveUpstreamRef,
143
149
  resolvePrePushBootstrapBaseRef,
@@ -155,7 +161,10 @@ const defaultDependencies: LifecycleWatchDependencies = {
155
161
  ensureRuntimeArtifactsIgnored: (repoRoot) => {
156
162
  try {
157
163
  ensureRuntimeArtifactsIgnored(repoRoot);
158
- } catch {
164
+ } catch (error) {
165
+ process.stderr.write(
166
+ `[pumuki][watch] unable to ensure runtime artifacts are ignored: ${String(error)}\n`
167
+ );
159
168
  }
160
169
  },
161
170
  emitAuditSummaryNotificationFromEvidence,
@@ -180,6 +189,16 @@ const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
180
189
  SDD_SESSION_MISSING: 'Abre sesión SDD y vuelve a intentar.',
181
190
  SDD_SESSION_INVALID: 'Refresca la sesión SDD y vuelve a intentar.',
182
191
  OPENSPEC_MISSING: 'Instala OpenSpec y reintenta la validación.',
192
+ TDD_BDD_EVIDENCE_INVALID:
193
+ 'Regenera la evidencia TDD/BDD válida del escenario afectado y vuelve a ejecutar el gate.',
194
+ TDD_BDD_SCENARIO_FILE_MISSING:
195
+ 'Crea o corrige el fichero .feature referenciado por la evidencia TDD/BDD y revalida.',
196
+ TDD_BDD_EVIDENCE_STALE:
197
+ 'Reejecuta los tests baseline del componente tocado, refresca la evidencia TDD/BDD y revalida.',
198
+ TDD_BDD_EVIDENCE_MISSING:
199
+ 'Genera evidencia TDD/BDD para el cambio actual antes de continuar.',
200
+ TDD_BDD_BASELINE_BLOCKED:
201
+ 'Corrige el baseline TDD/BDD roto y regenera la evidencia antes de continuar.',
183
202
  MCP_ENTERPRISE_RECEIPT_MISSING: 'Genera el receipt enterprise de MCP antes de continuar.',
184
203
  MANIFEST_MUTATION_DETECTED:
185
204
  'Validación no debe mutar package.json/lockfiles. Revisa wiring y realiza upgrades solo con comando explícito.',
@@ -369,12 +388,13 @@ const toFirstCause = (params: {
369
388
  evidence: AiEvidenceV2_1 | undefined;
370
389
  matchedFindings: ReadonlyArray<SnapshotFinding>;
371
390
  }): { code: string; message: string; remediation: string } => {
372
- const firstFinding = params.matchedFindings[0];
373
- const firstViolation = params.evidence?.ai_gate.violations[0];
374
- const code = firstFinding?.code ?? firstViolation?.code ?? 'WATCH_GATE_BLOCKED';
391
+ const primaryCause = resolvePrimaryBlockingCause([
392
+ ...params.matchedFindings,
393
+ ...(params.evidence?.ai_gate.violations ?? []),
394
+ ]);
395
+ const code = primaryCause?.code ?? 'WATCH_GATE_BLOCKED';
375
396
  const message =
376
- firstFinding?.message ??
377
- firstViolation?.message ??
397
+ primaryCause?.message ??
378
398
  `Watch gate bloqueado (${code}).`;
379
399
  const remediation =
380
400
  BLOCKED_REMEDIATION_BY_CODE[code] ??
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.144",
3
+ "version": "6.3.146",
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": {
@@ -2,6 +2,7 @@ import {
2
2
  evaluateAiGate,
3
3
  type AiGateCheckResult,
4
4
  } from '../integrations/gate/evaluateAiGate';
5
+ import { resolvePrimaryBlockingCause } from '../integrations/gate/blockingCause';
5
6
  import { readLifecycleExperimentalFeaturesSnapshot } from '../integrations/lifecycle/experimentalFeaturesSnapshot';
6
7
  import { LifecycleGitService } from '../integrations/lifecycle/gitService';
7
8
  import { readGovernanceObservationSnapshot } from '../integrations/lifecycle/governanceObservationSnapshot';
@@ -36,6 +37,8 @@ const defaultDependencies: ConsumerPreflightDependencies = {
36
37
  readGovernanceNextAction,
37
38
  };
38
39
 
40
+ const SECONDS_PER_MINUTE = 60;
41
+
39
42
  const buildNotificationEvents = (
40
43
  result: AiGateCheckResult
41
44
  ): ReadonlyArray<PumukiCriticalNotificationEvent> => {
@@ -45,7 +48,7 @@ const buildNotificationEvents = (
45
48
  events.push({
46
49
  kind: 'evidence.stale',
47
50
  evidencePath: '.ai_evidence.json',
48
- ageMinutes: Math.max(1, Math.ceil(ageSeconds / 60)),
51
+ ageMinutes: Math.max(1, Math.ceil(ageSeconds / SECONDS_PER_MINUTE)),
49
52
  });
50
53
  }
51
54
  if (hasViolationCode(result.violations, 'GITFLOW_PROTECTED_BRANCH')) {
@@ -56,13 +59,13 @@ const buildNotificationEvents = (
56
59
  });
57
60
  }
58
61
  if (result.status === 'BLOCKED') {
59
- const firstViolation = result.violations[0];
60
- const causeCode = firstViolation?.code ?? 'GATE_BLOCKED';
62
+ const primaryViolation = resolvePrimaryBlockingCause(result.violations);
63
+ const causeCode = primaryViolation?.code ?? 'GATE_BLOCKED';
61
64
  const causeMessage =
62
- firstViolation?.message
65
+ primaryViolation?.message
63
66
  ?? `Detected ${result.violations.length} blocking violations in stage ${result.stage}.`;
64
67
  const remediation =
65
- (firstViolation ? ACTIONABLE_HINTS_BY_CODE[firstViolation.code] : undefined)
68
+ (primaryViolation ? ACTIONABLE_HINTS_BY_CODE[primaryViolation.code] : undefined)
66
69
  ?? 'Corrige la causa bloqueante y vuelve a ejecutar el gate.';
67
70
  events.push({
68
71
  kind: 'gate.blocked',
@@ -7,9 +7,16 @@ import {
7
7
  buildNotificationTrackingCauseSummary,
8
8
  extractNotificationTrackingContext,
9
9
  } from './framework-menu-system-notifications-tracking';
10
+ import { isTddBddBlockingCause } from '../integrations/gate/blockingCause';
10
11
 
11
12
  const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
12
13
  EVIDENCE_GATE_BLOCKED: 'El gate de evidencia/gobernanza está bloqueado.',
14
+ TDD_BDD_EVIDENCE_INVALID: 'La evidencia TDD/BDD actual es inválida.',
15
+ TDD_BDD_SCENARIO_FILE_MISSING:
16
+ 'Falta el fichero de escenario TDD/BDD referenciado por la evidencia.',
17
+ TDD_BDD_EVIDENCE_STALE: 'La evidencia TDD/BDD está caducada.',
18
+ TDD_BDD_EVIDENCE_MISSING: 'Falta evidencia TDD/BDD para el cambio actual.',
19
+ TDD_BDD_BASELINE_BLOCKED: 'La baseline TDD/BDD está bloqueada.',
13
20
  EVIDENCE_MISSING: 'Falta evidencia para validar este paso.',
14
21
  EVIDENCE_INVALID: 'La evidencia actual es inválida.',
15
22
  EVIDENCE_CHAIN_INVALID: 'La cadena de evidencia no es válida.',
@@ -32,6 +39,13 @@ const BLOCKED_CAUSE_SUMMARY_BY_CODE: Readonly<Record<string, string>> = {
32
39
  'No hay reglas activas para cambios de código.',
33
40
  };
34
41
 
42
+ const TRACKING_COMPATIBLE_UMBRELLA_CODES = new Set([
43
+ 'EVIDENCE_GATE_BLOCKED',
44
+ 'AI_GATE_BLOCKED',
45
+ 'AI_GATE_BLOCK',
46
+ 'GATE_BLOCKED',
47
+ ]);
48
+
35
49
  const ENGLISH_CAUSE_HINTS = [
36
50
  'detected',
37
51
  'avoid explicit any',
@@ -97,13 +111,22 @@ export const resolveBlockedCauseSummary = (
97
111
  causeCode: string
98
112
  ): string => {
99
113
  const trackingContext = extractNotificationTrackingContext(event.causeMessage);
100
- if (trackingContext) {
114
+ if (isTddBddBlockingCause({ code: causeCode, message: event.causeMessage })) {
115
+ return (
116
+ BLOCKED_CAUSE_SUMMARY_BY_CODE[causeCode] ??
117
+ 'La evidencia TDD/BDD bloquea el gate; revisa el escenario y el artefacto de evidencia.'
118
+ );
119
+ }
120
+ if (trackingContext && TRACKING_COMPATIBLE_UMBRELLA_CODES.has(causeCode)) {
101
121
  return buildNotificationTrackingCauseSummary(trackingContext);
102
122
  }
103
123
  const mapped = BLOCKED_CAUSE_SUMMARY_BY_CODE[causeCode];
104
124
  if (mapped) {
105
125
  return mapped;
106
126
  }
127
+ if (trackingContext) {
128
+ return buildNotificationTrackingCauseSummary(trackingContext);
129
+ }
107
130
  if (event.causeMessage && event.causeMessage.trim().length > 0) {
108
131
  const rawMessage = normalizeNotificationText(event.causeMessage).replace(/^[A-Z0-9_]+:\s*/, '');
109
132
  const translated = toKnownSpanishCauseFromMessage(rawMessage);
@@ -7,12 +7,23 @@ import {
7
7
  extractNotificationTrackingContext,
8
8
  TRACKING_BLOCKED_REMEDIATION,
9
9
  } from './framework-menu-system-notifications-tracking';
10
+ import { isTddBddBlockingCause } from '../integrations/gate/blockingCause';
10
11
 
11
12
  type BlockedRemediationVariant = 'banner' | 'dialog';
12
13
 
13
14
  const BLOCKED_REMEDIATION_BY_CODE: Readonly<Record<string, string>> = {
14
15
  EVIDENCE_GATE_BLOCKED:
15
16
  'Revisa status/doctor para ver la causa exacta del gate, corrígela y revalida.',
17
+ TDD_BDD_EVIDENCE_INVALID:
18
+ 'Regenera la evidencia TDD/BDD válida del escenario afectado y vuelve a ejecutar el gate.',
19
+ TDD_BDD_SCENARIO_FILE_MISSING:
20
+ 'Crea o corrige el fichero .feature referenciado por la evidencia TDD/BDD y revalida.',
21
+ TDD_BDD_EVIDENCE_STALE:
22
+ 'Reejecuta los tests baseline del componente tocado, refresca la evidencia TDD/BDD y revalida.',
23
+ TDD_BDD_EVIDENCE_MISSING:
24
+ 'Genera evidencia TDD/BDD para el cambio actual antes de continuar.',
25
+ TDD_BDD_BASELINE_BLOCKED:
26
+ 'Corrige la baseline TDD/BDD rota, registra evidencia pasada y vuelve a ejecutar el gate.',
16
27
  EVIDENCE_MISSING: 'Genera la evidencia del slice actual y vuelve a validar esta fase.',
17
28
  EVIDENCE_INVALID: 'Regenera la evidencia de esta iteración y repite la validación.',
18
29
  EVIDENCE_CHAIN_INVALID: 'Regenera la evidencia para restaurar la cadena de integridad y vuelve a validar.',
@@ -105,6 +116,9 @@ export const resolveBlockedRemediation = (
105
116
  const fromEvent = event.remediation
106
117
  ? normalizeBlockedRemediation(event.remediation)
107
118
  : '';
119
+ if (isTddBddBlockingCause({ code: causeCode, message: event.causeMessage })) {
120
+ return truncateNotificationText(resolveFallbackRemediation(causeCode), maxLength);
121
+ }
108
122
  if (extractNotificationTrackingContext(event.causeMessage)) {
109
123
  return truncateNotificationText(TRACKING_BLOCKED_REMEDIATION, maxLength);
110
124
  }