Files
proffesor-for-testing__agen…/docs/plans/a11y-ally-technical-spec.md
Lalitkumar Bhamare b37d102f1d feat(a11y): add qe-a11y-ally agent with context-aware remediation code generation
Add comprehensive accessibility testing agent with:
- Full WCAG 2.2 Level AA compliance scanning via axe-core
- EN 301 549 EU compliance mapping
- ARIA Authoring Practices Guide (APG) patterns
- EU Accessibility Act legal framework support
- AccName (Accessible Name) computation

Enhanced remediation code generation:
- Framework detection (Slick, Swiper, Bootstrap, Owl Carousel, etc.)
- Context-aware HTML/CSS/JS fix templates
- WebVTT caption file generation with multi-language support
- Playwright + axe-core test generation for violations
- CSS accessibility utilities (skip links, focus indicators, etc.)

Agent registration:
- Added ACCESSIBILITY_ALLY to QEAgentType enum
- Created AccessibilityAllyAgent with full task implementation
- Registered in AgentRegistry with 'qe-a11y-ally' and 'a11y-ally' aliases
- Added to QEAgentFactory with 10 capabilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 22:49:21 +00:00

34 KiB

A11y-Ally Agent - Technical Specification

Document Information


1. System Architecture

1.1 Component Diagram

┌─────────────────────────────────────────────────────────────────┐
│                        qe-a11y-ally Agent                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────┐ │
│  │  Scan Engine     │  │  Context Engine  │  │  Remediation │ │
│  │                  │  │                  │  │  Engine      │ │
│  │  • axe-core      │  │  • DOM analysis  │  │  • ARIA gen  │ │
│  │  • Playwright    │  │  • Semantic      │  │  • Contrast  │ │
│  │  • Custom rules  │  │    inference     │  │  • Keyboard  │ │
│  └────────┬─────────┘  └────────┬─────────┘  └──────┬───────┘ │
│           │                     │                    │         │
│           └─────────────────────┴────────────────────┘         │
│                              │                                 │
├──────────────────────────────┼─────────────────────────────────┤
│                              │                                 │
│  ┌───────────────────────────▼──────────────────────────────┐  │
│  │              MCP Tool Layer                              │  │
│  │                                                          │  │
│  │  • scan-comprehensive      • analyze-context            │  │
│  │  • generate-remediation    • test-keyboard-navigation   │  │
│  │  • simulate-screen-reader  • analyze-color-contrast     │  │
│  └──────────────────────────────────────────────────────────┘  │
│                              │                                 │
└──────────────────────────────┼─────────────────────────────────┘
                               │
┌──────────────────────────────▼─────────────────────────────────┐
│                    Integration Layer                           │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  qe-visual-tester    qe-test-generator    qe-quality-gate     │
│  (screenshots)       (test generation)    (compliance gates)   │
│                                                                │
└────────────────────────────────────────────────────────────────┘
                               │
┌──────────────────────────────▼─────────────────────────────────┐
│                    Learning & Memory Layer                     │
├────────────────────────────────────────────────────────────────┤
│                                                                │
│  • Learning patterns (aqe/learning/patterns/accessibility/*)   │
│  • Violation history (aqe/accessibility/violations/*)          │
│  • Remediation success (aqe/accessibility/remediations/*)      │
│                                                                │
└────────────────────────────────────────────────────────────────┘

2. Data Models

2.1 Core Types

/**
 * Comprehensive accessibility scan parameters
 */
export interface AccessibilityScanParams {
  /** Target to scan */
  target: URLTarget | HTMLTarget;

  /** WCAG compliance level */
  level: WCAGLevel;

  /** Scan configuration */
  config: ScanConfiguration;

  /** Advanced options */
  options?: AdvancedOptions;
}

export type WCAGLevel = 'A' | 'AA' | 'AAA';

export interface URLTarget {
  type: 'url';
  url: string;
  /** Authenticate before scanning */
  authentication?: {
    type: 'basic' | 'bearer' | 'cookie';
    credentials: Record<string, string>;
  };
}

export interface HTMLTarget {
  type: 'html';
  html: string;
  baseUrl?: string;
}

export interface ScanConfiguration {
  /** Specific areas to scan */
  scope?: {
    include?: string[]; // CSS selectors
    exclude?: string[]; // CSS selectors
    pages?: string[];   // Multiple pages
  };

  /** Which validators to run */
  validators: {
    axeCore?: boolean;
    playwright?: boolean;
    customRules?: boolean;
  };

  /** Which tests to run */
  tests: {
    keyboard?: boolean;
    screenReader?: boolean;
    colorContrast?: boolean;
    semanticHTML?: boolean;
    focus?: boolean;
  };

  /** Remediation options */
  remediation: {
    enabled: boolean;
    contextAware?: boolean;
    generateTests?: boolean;
    autoFixSafe?: boolean;
  };
}

export interface AdvancedOptions {
  /** Browser to use */
  browser?: 'chromium' | 'firefox' | 'webkit';

  /** Viewport size */
  viewport?: { width: number; height: number };

  /** Screenshot violations */
  screenshots?: boolean;

  /** Learning integration */
  learning?: {
    queryPastPatterns?: boolean;
    storeLearnings?: boolean;
  };

  /** Performance limits */
  timeout?: number;
  maxConcurrency?: number;
}

2.2 Result Types

/**
 * Complete scan result
 */
export interface AccessibilityScanResult {
  /** Unique scan identifier */
  scanId: string;

  /** When scan was performed */
  timestamp: string;

  /** Scan metadata */
  metadata: ScanMetadata;

  /** Overall compliance assessment */
  compliance: ComplianceStatus;

  /** All violations found */
  violations: Violation[];

  /** Remediation suggestions */
  remediations: Remediation[];

  /** Generated test cases */
  testCases?: TestCase[];

  /** Detailed category analysis */
  pourAnalysis: POURAnalysis;

  /** Performance metrics */
  performance: PerformanceMetrics;

  /** Raw results from tools */
  raw?: {
    axeCore?: AxeCoreResult;
    playwright?: PlaywrightResult;
  };
}

export interface ScanMetadata {
  target: string;
  level: WCAGLevel;
  scanDuration: number;
  elementsAnalyzed: number;
  toolsUsed: string[];
  browserUsed: string;
  viewport: { width: number; height: number };
}

export interface ComplianceStatus {
  /** Overall status */
  status: 'compliant' | 'partially-compliant' | 'non-compliant';

  /** Numerical score (0-100) */
  score: number;

  /** Target level tested */
  level: WCAGLevel;

  /** By success criterion */
  byCriterion: Record<string, {
    passed: boolean;
    violations: number;
  }>;

  /** Readiness for production */
  productionReady: boolean;

  /** Estimated fix effort */
  estimatedEffort: {
    hours: number;
    complexity: 'trivial' | 'low' | 'medium' | 'high';
  };
}

export interface Violation {
  /** Unique violation ID */
  id: string;

  /** WCAG success criterion violated */
  criterion: WCAGCriterion;

  /** How severe is this */
  severity: ViolationSeverity;

  /** Impact on users */
  impact: ImpactAssessment;

  /** Elements with this violation */
  elements: ViolationElement[];

  /** Who detected this */
  detectedBy: DetectionSource;

  /** How confident are we */
  confidence: number; // 0-1

  /** Related violations */
  relatedViolations?: string[];
}

export interface WCAGCriterion {
  /** e.g., "1.4.3" */
  number: string;

  /** e.g., "Contrast (Minimum)" */
  name: string;

  /** WCAG level (A, AA, AAA) */
  level: WCAGLevel;

  /** POUR category */
  category: 'perceivable' | 'operable' | 'understandable' | 'robust';

  /** Link to WCAG documentation */
  url: string;
}

export type ViolationSeverity = 'critical' | 'serious' | 'moderate' | 'minor';

export interface ImpactAssessment {
  /** Human-readable description */
  description: string;

  /** Affected user groups */
  affectedGroups: UserGroup[];

  /** Estimated number of affected users */
  estimatedUsers?: number;

  /** Business impact */
  businessImpact?: 'high' | 'medium' | 'low';

  /** Legal risk */
  legalRisk?: 'high' | 'medium' | 'low';
}

export type UserGroup =
  | 'blind'
  | 'low-vision'
  | 'color-blind'
  | 'deaf'
  | 'hard-of-hearing'
  | 'motor-impaired'
  | 'cognitive-impaired'
  | 'seizure-sensitive';

export interface ViolationElement {
  /** CSS selector */
  selector: string;

  /** HTML snippet */
  html: string;

  /** Location on page */
  location: {
    x: number;
    y: number;
    width: number;
    height: number;
  };

  /** Accessibility tree info */
  a11yTree?: {
    role: string;
    name: string;
    description?: string;
    states?: string[];
  };

  /** Screenshot (if enabled) */
  screenshot?: string;
}

export type DetectionSource =
  | 'axe-core'
  | 'playwright'
  | 'custom-heuristic'
  | 'ml-model';

2.3 Remediation Types

/**
 * Context-aware remediation suggestion
 */
export interface Remediation {
  /** Unique remediation ID */
  id: string;

  /** Related violation IDs */
  violationIds: string[];

  /** Type of fix */
  type: RemediationType;

  /** Priority for fixing */
  priority: 'critical' | 'high' | 'medium' | 'low';

  /** The actual suggestion */
  suggestion: RemediationSuggestion;

  /** Can this be auto-fixed? */
  autoFixable: boolean;

  /** Auto-fix details (if applicable) */
  autoFix?: AutoFix;

  /** How confident are we */
  confidence: number; // 0-1

  /** Validation status */
  validation?: {
    tested: boolean;
    successful?: boolean;
    testResult?: string;
  };
}

export type RemediationType =
  | 'aria-label'
  | 'aria-describedby'
  | 'alt-text'
  | 'color-contrast'
  | 'keyboard-navigation'
  | 'focus-management'
  | 'semantic-html'
  | 'heading-structure'
  | 'form-labels'
  | 'captions'
  | 'transcripts'
  | 'skip-links'
  | 'landmarks'
  | 'other';

export interface RemediationSuggestion {
  /** Human-readable title */
  title: string;

  /** Detailed description */
  description: string;

  /** Specific code changes */
  codeChanges: CodeChange[];

  /** Why this works */
  reasoning: string;

  /** Step-by-step instructions */
  steps: string[];

  /** Resources for learning */
  resources?: Resource[];

  /** Estimated effort */
  effort: {
    time: 'minutes' | 'hours' | 'days';
    amount: number;
    complexity: 'trivial' | 'low' | 'medium' | 'high';
  };

  /** Example implementation */
  example?: {
    before: string;
    after: string;
    explanation: string;
  };
}

export interface CodeChange {
  /** File path (if known) */
  file?: string;

  /** Element selector */
  selector: string;

  /** Type of change */
  type: 'add-attribute' | 'modify-attribute' | 'remove-attribute' |
        'add-element' | 'modify-element' | 'remove-element' |
        'add-css' | 'modify-css';

  /** Current state */
  before: string;

  /** Desired state */
  after: string;

  /** Explanation of change */
  explanation: string;

  /** Context */
  context?: {
    surroundingCode?: string;
    relatedElements?: string[];
  };
}

export interface AutoFix {
  /** Safe to apply automatically */
  safe: boolean;

  /** Patch to apply */
  patch: {
    format: 'unified-diff' | 'json-patch';
    content: string;
  };

  /** Test to verify fix */
  verification?: {
    test: string;
    expectedResult: string;
  };

  /** Rollback information */
  rollback?: {
    enabled: boolean;
    preserveOriginal?: boolean;
  };
}

export interface Resource {
  title: string;
  url: string;
  type: 'documentation' | 'tutorial' | 'example' | 'tool';
}

2.4 Context Analysis Types

/**
 * Element context for intelligent remediation
 */
export interface ElementContext {
  /** The element being analyzed */
  element: {
    tagName: string;
    attributes: Record<string, string>;
    innerHTML: string;
    outerHTML: string;
  };

  /** Surrounding context */
  surrounding: {
    /** Text before element */
    precedingText?: string;

    /** Text after element */
    followingText?: string;

    /** Nearby headings */
    nearbyHeadings?: string[];

    /** Parent container description */
    parentDescription?: string;
  };

  /** Semantic context */
  semantic: {
    /** Inferred purpose */
    purpose?: string;

    /** User flow/journey */
    userFlow?: string;

    /** Page section */
    section?: string;

    /** Related form (if applicable) */
    relatedForm?: FormContext;
  };

  /** Visual context */
  visual?: {
    /** Position on page */
    position: 'header' | 'footer' | 'sidebar' | 'main' | 'nav';

    /** Visibility */
    visible: boolean;

    /** Colors */
    colors?: {
      foreground: string;
      background: string;
      contrast: number;
    };
  };

  /** Interactive context */
  interactive?: {
    /** Is interactive */
    isInteractive: boolean;

    /** Event handlers */
    eventHandlers: string[];

    /** Focus state */
    focusable: boolean;

    /** Tab index */
    tabIndex?: number;
  };
}

export interface FormContext {
  formId?: string;
  formPurpose?: string;
  fieldType?: string;
  validationRules?: string[];
  relatedFields?: string[];
}

3. API Specifications

3.1 MCP Tool: scan-comprehensive

Tool Name: mcp__agentic_qe__a11y_scan_comprehensive

Description: Perform comprehensive WCAG 2.2 accessibility scan with context-aware remediation

Parameters:

{
  target: URLTarget | HTMLTarget;
  level: 'A' | 'AA' | 'AAA';
  config: ScanConfiguration;
  options?: AdvancedOptions;
}

Returns:

QEToolResponse<AccessibilityScanResult>

Example Usage:

const result = await mcp__agentic_qe__a11y_scan_comprehensive({
  target: {
    type: 'url',
    url: 'https://example.com'
  },
  level: 'AA',
  config: {
    validators: {
      axeCore: true,
      playwright: true
    },
    tests: {
      keyboard: true,
      screenReader: true,
      colorContrast: true
    },
    remediation: {
      enabled: true,
      contextAware: true,
      generateTests: true
    }
  },
  options: {
    screenshots: true,
    learning: {
      queryPastPatterns: true,
      storeLearnings: true
    }
  }
});

if (result.success) {
  console.log(`Compliance Score: ${result.data.compliance.score}%`);
  console.log(`Violations Found: ${result.data.violations.length}`);
  console.log(`Remediations: ${result.data.remediations.length}`);
}

3.2 MCP Tool: analyze-context

Tool Name: mcp__agentic_qe__a11y_analyze_context

Description: Analyze element context for intelligent remediation suggestions

Parameters:

{
  element: {
    selector: string;
    html: string;
  };
  pageContext?: {
    url?: string;
    fullHTML?: string;
  };
  options?: {
    inferPurpose?: boolean;
    analyzeSemantics?: boolean;
    visualAnalysis?: boolean;
  };
}

Returns:

QEToolResponse<{
  context: ElementContext;
  suggestions: RemediationSuggestion[];
  confidence: number;
}>

3.3 MCP Tool: generate-remediation

Tool Name: mcp__agentic_qe__a11y_generate_remediation

Description: Generate context-aware remediation for specific violations

Parameters:

{
  violations: Violation[];
  context: ElementContext[];
  options?: {
    prioritize?: boolean;
    includeAutoFix?: boolean;
    includeExamples?: boolean;
  };
}

Returns:

QEToolResponse<{
  remediations: Remediation[];
  priorityOrder: string[];
  estimatedTotalEffort: number;
}>

3.4 MCP Tool: test-keyboard-navigation

Tool Name: mcp__agentic_qe__a11y_test_keyboard_navigation

Description: Test keyboard navigation and focus management

Parameters:

{
  target: URLTarget;
  options?: {
    testTabOrder?: boolean;
    testFocusIndicators?: boolean;
    testKeyboardTraps?: boolean;
    testSkipLinks?: boolean;
  };
}

Returns:

QEToolResponse<{
  passed: boolean;
  issues: KeyboardNavigationIssue[];
  tabOrder: string[];
  focusIndicators: boolean;
  keyboardTraps: KeyboardTrap[];
}>

4. Algorithms

4.1 Context-Aware ARIA Label Generation

/**
 * Generate appropriate ARIA label from element context
 *
 * Algorithm:
 * 1. Extract element and surrounding context
 * 2. Identify element purpose through multiple signals:
 *    a. Icon analysis (SVG content, classes)
 *    b. Surrounding text proximity scoring
 *    c. Parent container semantic analysis
 *    d. Form relationship inference
 * 3. Combine signals with confidence weights
 * 4. Generate natural language label
 * 5. Validate against ARIA best practices
 */
async function generateARIALabel(
  element: Element,
  context: ElementContext
): Promise<{ label: string; confidence: number }> {
  const signals: Signal[] = [];

  // Signal 1: Icon analysis (weight: 0.3)
  const iconSignal = analyzeIcon(element);
  if (iconSignal) {
    signals.push({ type: 'icon', value: iconSignal, weight: 0.3 });
  }

  // Signal 2: Surrounding text (weight: 0.4)
  const textSignal = analyzeSurroundingText(context.surrounding);
  if (textSignal) {
    signals.push({ type: 'text', value: textSignal, weight: 0.4 });
  }

  // Signal 3: Parent container (weight: 0.2)
  const parentSignal = analyzeParentContainer(context.surrounding.parentDescription);
  if (parentSignal) {
    signals.push({ type: 'parent', value: parentSignal, weight: 0.2 });
  }

  // Signal 4: Form relationship (weight: 0.1)
  if (context.semantic.relatedForm) {
    const formSignal = analyzeFormRelationship(context.semantic.relatedForm);
    if (formSignal) {
      signals.push({ type: 'form', value: formSignal, weight: 0.1 });
    }
  }

  // Combine signals
  const combinedLabel = combineSignals(signals);
  const confidence = calculateConfidence(signals);

  return { label: combinedLabel, confidence };
}

function analyzeIcon(element: Element): string | null {
  // Check for common icon patterns
  const svg = element.querySelector('svg');
  if (!svg) return null;

  // Icon class analysis
  const classes = element.className.toLowerCase();
  if (classes.includes('cart')) return 'shopping cart';
  if (classes.includes('user')) return 'user profile';
  if (classes.includes('search')) return 'search';
  // ... more patterns

  // SVG content analysis
  const svgContent = svg.innerHTML.toLowerCase();
  if (svgContent.includes('path') && svgContent.includes('cart')) {
    return 'shopping cart';
  }

  return null;
}

function analyzeSurroundingText(surrounding: ElementContext['surrounding']): string | null {
  const precedingText = surrounding.precedingText?.trim();
  const followingText = surrounding.followingText?.trim();

  // Prefer preceding text (usually more relevant)
  if (precedingText && precedingText.length < 50) {
    return precedingText;
  }

  // Check nearby headings
  if (surrounding.nearbyHeadings && surrounding.nearbyHeadings.length > 0) {
    return surrounding.nearbyHeadings[0];
  }

  // Fallback to following text
  if (followingText && followingText.length < 50) {
    return followingText;
  }

  return null;
}

function combineSignals(signals: Signal[]): string {
  // Weight-based combination
  let combinedLabel = '';
  let totalWeight = 0;

  // Prefer higher-weight signals
  signals.sort((a, b) => b.weight - a.weight);

  for (const signal of signals) {
    if (signal.value && totalWeight < 1.0) {
      if (!combinedLabel) {
        combinedLabel = signal.value;
      }
      totalWeight += signal.weight;
    }
  }

  return combinedLabel || 'Interactive element';
}

function calculateConfidence(signals: Signal[]): number {
  if (signals.length === 0) return 0.3;

  const totalWeight = signals.reduce((sum, s) => sum + s.weight, 0);
  const hasHighConfidenceSignal = signals.some(s => s.weight >= 0.3);

  let confidence = totalWeight;
  if (hasHighConfidenceSignal) confidence += 0.1;
  if (signals.length >= 3) confidence += 0.1;

  return Math.min(confidence, 1.0);
}

4.2 Color Contrast Optimization

/**
 * Suggest color adjustments to meet WCAG contrast requirements
 *
 * Algorithm:
 * 1. Calculate current contrast ratio
 * 2. Determine required ratio based on WCAG level
 * 3. Identify which color to adjust (prefer background)
 * 4. Calculate minimum adjustment needed
 * 5. Suggest nearest accessible color from palette
 * 6. Preserve brand colors when possible
 */
async function optimizeColorContrast(
  foreground: string,
  background: string,
  level: WCAGLevel,
  textSize: 'normal' | 'large'
): Promise<ContrastOptimization> {
  // Calculate current contrast
  const currentRatio = calculateContrastRatio(foreground, background);

  // Determine required ratio
  const requiredRatio = getRequiredRatio(level, textSize);

  // Already passes
  if (currentRatio >= requiredRatio) {
    return {
      passes: true,
      currentRatio,
      requiredRatio,
      suggestions: []
    };
  }

  // Calculate adjustment needed
  const adjustmentNeeded = requiredRatio - currentRatio;

  // Generate suggestions
  const suggestions: ColorSuggestion[] = [];

  // Suggestion 1: Darken background
  const darkerBg = adjustColor(background, 'darken', adjustmentNeeded);
  if (calculateContrastRatio(foreground, darkerBg) >= requiredRatio) {
    suggestions.push({
      type: 'background',
      original: background,
      suggested: darkerBg,
      ratio: calculateContrastRatio(foreground, darkerBg),
      change: 'darken'
    });
  }

  // Suggestion 2: Lighten background
  const lighterBg = adjustColor(background, 'lighten', adjustmentNeeded);
  if (calculateContrastRatio(foreground, lighterBg) >= requiredRatio) {
    suggestions.push({
      type: 'background',
      original: background,
      suggested: lighterBg,
      ratio: calculateContrastRatio(foreground, lighterBg),
      change: 'lighten'
    });
  }

  // Suggestion 3: Adjust foreground (less preferred)
  const darkerFg = adjustColor(foreground, 'darken', adjustmentNeeded);
  if (calculateContrastRatio(darkerFg, background) >= requiredRatio) {
    suggestions.push({
      type: 'foreground',
      original: foreground,
      suggested: darkerFg,
      ratio: calculateContrastRatio(darkerFg, background),
      change: 'darken',
      priority: 'low' // Prefer background changes
    });
  }

  // Sort by minimal change and priority
  suggestions.sort((a, b) => {
    if (a.priority === 'low' && b.priority !== 'low') return 1;
    if (a.priority !== 'low' && b.priority === 'low') return -1;
    return Math.abs(a.ratio - requiredRatio) - Math.abs(b.ratio - requiredRatio);
  });

  return {
    passes: false,
    currentRatio,
    requiredRatio,
    suggestions
  };
}

5. Integration Patterns

5.1 Learning Integration

/**
 * Learning protocol for a11y-ally agent
 */

// BEFORE task: Query past learnings
async function queryPastLearnings() {
  const learnings = await mcp__agentic_qe__learning_query({
    agentId: 'qe-a11y-ally',
    taskType: 'accessibility-scan',
    minReward: 0.8,
    queryType: 'all',
    limit: 10
  });

  // Apply learned patterns
  if (learnings.success) {
    applyLearnedPatterns(learnings.data);
  }
}

// AFTER task: Store learnings
async function storeLearnings(scanResult: AccessibilityScanResult) {
  // Calculate reward
  const reward = calculateReward(scanResult);

  // Store experience
  await mcp__agentic_qe__learning_store_experience({
    agentId: 'qe-a11y-ally',
    taskType: 'accessibility-scan',
    reward,
    outcome: {
      violationsDetected: scanResult.violations.length,
      complianceScore: scanResult.compliance.score,
      remediationsGenerated: scanResult.remediations.length,
      contextAccuracy: calculateContextAccuracy(scanResult)
    },
    metadata: {
      wcagLevel: scanResult.metadata.level,
      toolsUsed: scanResult.metadata.toolsUsed,
      targetType: 'url'
    }
  });

  // Store successful remediation patterns
  for (const remediation of scanResult.remediations) {
    if (remediation.confidence >= 0.9) {
      await mcp__agentic_qe__learning_store_pattern({
        pattern: `${remediation.type}: ${remediation.suggestion.description}`,
        confidence: remediation.confidence,
        domain: 'accessibility-remediation',
        metadata: {
          violationType: remediation.type,
          autoFixable: remediation.autoFixable
        }
      });
    }
  }
}

function calculateReward(scanResult: AccessibilityScanResult): number {
  const { violations, compliance, performance } = scanResult;

  // Base score from compliance
  let reward = compliance.score / 100;

  // Bonus for comprehensive detection
  if (violations.length > 0) {
    reward += 0.1; // Found issues
  }

  // Bonus for fast performance
  if (performance.scanTime < 10000) {
    reward += 0.1;
  }

  // Penalty for low confidence remediations
  const lowConfidenceCount = scanResult.remediations.filter(
    r => r.confidence < 0.7
  ).length;
  reward -= lowConfidenceCount * 0.05;

  return Math.max(0, Math.min(1, reward));
}

5.2 Fleet Coordination

/**
 * Coordinate with other QE agents
 */

// Example: Full accessibility workflow
async function fullAccessibilityWorkflow(url: string) {
  // 1. Scan for violations (a11y-ally)
  const scanResult = await Task(
    'Scan accessibility',
    `Comprehensive WCAG 2.2 AA scan of ${url}`,
    'qe-a11y-ally'
  );

  // Store results in memory for other agents
  await mcp__agentic_qe__memory_store({
    key: `aqe/accessibility/scan-results/${scanResult.scanId}`,
    value: scanResult,
    namespace: 'aqe',
    persist: true
  });

  // 2. Generate tests from violations (test-generator)
  if (scanResult.violations.length > 0) {
    await Task(
      'Generate a11y tests',
      `Generate regression tests for ${scanResult.violations.length} violations`,
      'qe-test-generator'
    );
  }

  // 3. Visual regression for accessibility (visual-tester)
  await Task(
    'Visual a11y check',
    'Capture annotated screenshots of violations',
    'qe-visual-tester'
  );

  // 4. Quality gate check (quality-gate)
  const gateResult = await Task(
    'A11y quality gate',
    `Check if compliance score ${scanResult.compliance.score}% passes threshold`,
    'qe-quality-gate'
  );

  return {
    scanResult,
    gateResult,
    productionReady: gateResult.passed
  };
}

6. Performance Considerations

6.1 Optimization Strategies

  1. Parallel Scanning

    • Run axe-core and Playwright tests concurrently
    • Analyze multiple pages in parallel
    • Use worker threads for intensive computation
  2. Caching

    • Cache analyzed element contexts
    • Store ARIA label suggestions
    • Reuse color contrast calculations
  3. Progressive Scanning

    • Scan critical elements first
    • Defer non-critical analysis
    • Stream results as they're found
  4. Resource Limits

    • Maximum scan time: 30 seconds per page
    • Maximum concurrent pages: 5
    • Maximum elements analyzed: 1000 per page

6.2 Performance Targets

Metric Target Rationale
Single page scan <10s Fast enough for CI/CD
Multi-page scan (10 pages) <60s Acceptable for comprehensive audits
Memory usage <512MB DevPod/Codespace friendly
CPU usage <80% Leave room for other processes

7. Security Considerations

7.1 Input Validation

/**
 * Validate and sanitize inputs
 */
function validateScanParams(params: AccessibilityScanParams): void {
  // URL validation
  if (params.target.type === 'url') {
    const url = new URL(params.target.url); // Throws if invalid

    // Block dangerous protocols
    if (!['http:', 'https:'].includes(url.protocol)) {
      throw new Error('Only HTTP/HTTPS protocols allowed');
    }

    // Block localhost in production
    if (process.env.NODE_ENV === 'production' &&
        url.hostname === 'localhost') {
      throw new Error('Cannot scan localhost in production');
    }
  }

  // HTML validation
  if (params.target.type === 'html') {
    // Sanitize HTML to prevent XSS
    params.target.html = sanitizeHTML(params.target.html);
  }

  // Selector validation (prevent injection)
  if (params.config.scope?.include) {
    params.config.scope.include = params.config.scope.include.map(
      selector => sanitizeSelector(selector)
    );
  }
}

7.2 Safe Auto-Fix

/**
 * Ensure auto-fixes are safe to apply
 */
function validateAutoFix(autoFix: AutoFix): boolean {
  // Only allow safe attribute additions/modifications
  const safeChanges = [
    'add-aria-label',
    'add-alt-text',
    'add-aria-describedby'
  ];

  // Never allow:
  // - Script injection
  // - Removing security-related attributes
  // - Modifying authentication elements

  return autoFix.safe &&
         autoFix.verification !== undefined &&
         !containsDangerousPatterns(autoFix.patch.content);
}

8. Error Handling

8.1 Error Types

export enum A11yErrorCode {
  // Scan errors
  SCAN_TIMEOUT = 'A11Y_SCAN_TIMEOUT',
  SCAN_FAILED = 'A11Y_SCAN_FAILED',
  INVALID_TARGET = 'A11Y_INVALID_TARGET',

  // Analysis errors
  CONTEXT_ANALYSIS_FAILED = 'A11Y_CONTEXT_ANALYSIS_FAILED',
  REMEDIATION_GENERATION_FAILED = 'A11Y_REMEDIATION_GENERATION_FAILED',

  // Tool errors
  AXE_CORE_ERROR = 'A11Y_AXE_CORE_ERROR',
  PLAYWRIGHT_ERROR = 'A11Y_PLAYWRIGHT_ERROR',

  // Learning errors
  LEARNING_QUERY_FAILED = 'A11Y_LEARNING_QUERY_FAILED',
  LEARNING_STORE_FAILED = 'A11Y_LEARNING_STORE_FAILED'
}

export interface A11yError extends QEError {
  code: A11yErrorCode;
  recoverable: boolean;
  retryable: boolean;
}

8.2 Graceful Degradation

/**
 * Handle tool failures gracefully
 */
async function scanWithGracefulDegradation(
  params: AccessibilityScanParams
): Promise<AccessibilityScanResult> {
  const results: Partial<AccessibilityScanResult> = {
    violations: [],
    remediations: []
  };

  // Try axe-core
  try {
    const axeResult = await runAxeCore(params);
    results.violations.push(...axeResult.violations);
  } catch (error) {
    logError('axe-core failed, continuing with other tools', error);
  }

  // Try Playwright
  try {
    const playwrightResult = await runPlaywright(params);
    results.violations.push(...playwrightResult.violations);
  } catch (error) {
    logError('Playwright failed, continuing with other tools', error);
  }

  // Try custom heuristics
  try {
    const customResult = await runCustomHeuristics(params);
    results.violations.push(...customResult.violations);
  } catch (error) {
    logError('Custom heuristics failed', error);
  }

  // If all tools failed, throw
  if (results.violations.length === 0) {
    throw new Error('All scanning tools failed');
  }

  return results as AccessibilityScanResult;
}

9. Testing Specifications

9.1 Unit Test Coverage Requirements

Minimum Coverage: 95%

Critical Paths (100% coverage required):

  • scan-comprehensive.ts - All code paths
  • analyze-context.ts - Context inference logic
  • generate-remediation.ts - Suggestion generation
  • test-keyboard-navigation.ts - Keyboard testing

9.2 Integration Test Scenarios

  1. Full scan workflow
  2. Context analysis pipeline
  3. Remediation generation accuracy
  4. Fleet coordination
  5. Learning integration

9.3 Journey Test Scenarios

  1. Developer fixes violations
  2. CI/CD integration
  3. Fleet coordination workflow

10. Monitoring & Observability

10.1 Metrics to Track

interface A11yMetrics {
  // Scan metrics
  totalScans: number;
  successfulScans: number;
  failedScans: number;
  averageScanTime: number;

  // Detection metrics
  violationsDetected: number;
  violationsByType: Record<RemediationType, number>;
  violationsBySeverity: Record<ViolationSeverity, number>;

  // Remediation metrics
  remediationsGenerated: number;
  autoFixableCount: number;
  averageConfidence: number;

  // Learning metrics
  patternsLearned: number;
  patternAccuracy: number;

  // User impact metrics
  averageFixTime: number; // After vs before
  complianceImprovement: number;
}

10.2 Logging

// Structured logging for observability
logger.info('A11y scan started', {
  scanId,
  target: params.target,
  level: params.level,
  timestamp: Date.now()
});

logger.info('Violations detected', {
  scanId,
  count: violations.length,
  critical: violations.filter(v => v.severity === 'critical').length
});

logger.info('Remediations generated', {
  scanId,
  count: remediations.length,
  autoFixable: remediations.filter(r => r.autoFixable).length
});

Document Status: Draft - Ready for Implementation Next Steps: Begin Milestone 1 development