BrainGrid

A11y-Ally Agent - Technical Specification

Agentic QE Fleet is an open-source AI-powered quality engineering platform designed for use with Claude Code, featuring specialized agents and skills to support testing activities for a product at any stage of the SDLC. Free to use, fork, build, and contribute. Based on the Agentic QE Framework created by Dragan Spiridonov.

Used in: 1 reposUpdated: recently

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

1/**
2 * Comprehensive accessibility scan parameters
3 */
4export interface AccessibilityScanParams {
5  /** Target to scan */
6  target: URLTarget | HTMLTarget;
7
8  /** WCAG compliance level */
9  level: WCAGLevel;
10
11  /** Scan configuration */
12  config: ScanConfiguration;
13
14  /** Advanced options */
15  options?: AdvancedOptions;
16}
17
18export type WCAGLevel = 'A' | 'AA' | 'AAA';
19
20export interface URLTarget {
21  type: 'url';
22  url: string;
23  /** Authenticate before scanning */
24  authentication?: {
25    type: 'basic' | 'bearer' | 'cookie';
26    credentials: Record<string, string>;
27  };
28}
29
30export interface HTMLTarget {
31  type: 'html';
32  html: string;
33  baseUrl?: string;
34}
35
36export interface ScanConfiguration {
37  /** Specific areas to scan */
38  scope?: {
39    include?: string[]; // CSS selectors
40    exclude?: string[]; // CSS selectors
41    pages?: string[];   // Multiple pages
42  };
43
44  /** Which validators to run */
45  validators: {
46    axeCore?: boolean;
47    playwright?: boolean;
48    customRules?: boolean;
49  };
50
51  /** Which tests to run */
52  tests: {
53    keyboard?: boolean;
54    screenReader?: boolean;
55    colorContrast?: boolean;
56    semanticHTML?: boolean;
57    focus?: boolean;
58  };
59
60  /** Remediation options */
61  remediation: {
62    enabled: boolean;
63    contextAware?: boolean;
64    generateTests?: boolean;
65    autoFixSafe?: boolean;
66  };
67}
68
69export interface AdvancedOptions {
70  /** Browser to use */
71  browser?: 'chromium' | 'firefox' | 'webkit';
72
73  /** Viewport size */
74  viewport?: { width: number; height: number };
75
76  /** Screenshot violations */
77  screenshots?: boolean;
78
79  /** Learning integration */
80  learning?: {
81    queryPastPatterns?: boolean;
82    storeLearnings?: boolean;
83  };
84
85  /** Performance limits */
86  timeout?: number;
87  maxConcurrency?: number;
88}

#2.2 Result Types

1/**
2 * Complete scan result
3 */
4export interface AccessibilityScanResult {
5  /** Unique scan identifier */
6  scanId: string;
7
8  /** When scan was performed */
9  timestamp: string;
10
11  /** Scan metadata */
12  metadata: ScanMetadata;
13
14  /** Overall compliance assessment */
15  compliance: ComplianceStatus;
16
17  /** All violations found */
18  violations: Violation[];
19
20  /** Remediation suggestions */
21  remediations: Remediation[];
22
23  /** Generated test cases */
24  testCases?: TestCase[];
25
26  /** Detailed category analysis */
27  pourAnalysis: POURAnalysis;
28
29  /** Performance metrics */
30  performance: PerformanceMetrics;
31
32  /** Raw results from tools */
33  raw?: {
34    axeCore?: AxeCoreResult;
35    playwright?: PlaywrightResult;
36  };
37}
38
39export interface ScanMetadata {
40  target: string;
41  level: WCAGLevel;
42  scanDuration: number;
43  elementsAnalyzed: number;
44  toolsUsed: string[];
45  browserUsed: string;
46  viewport: { width: number; height: number };
47}
48
49export interface ComplianceStatus {
50  /** Overall status */
51  status: 'compliant' | 'partially-compliant' | 'non-compliant';
52
53  /** Numerical score (0-100) */
54  score: number;
55
56  /** Target level tested */
57  level: WCAGLevel;
58
59  /** By success criterion */
60  byCriterion: Record<string, {
61    passed: boolean;
62    violations: number;
63  }>;
64
65  /** Readiness for production */
66  productionReady: boolean;
67
68  /** Estimated fix effort */
69  estimatedEffort: {
70    hours: number;
71    complexity: 'trivial' | 'low' | 'medium' | 'high';
72  };
73}
74
75export interface Violation {
76  /** Unique violation ID */
77  id: string;
78
79  /** WCAG success criterion violated */
80  criterion: WCAGCriterion;
81
82  /** How severe is this */
83  severity: ViolationSeverity;
84
85  /** Impact on users */
86  impact: ImpactAssessment;
87
88  /** Elements with this violation */
89  elements: ViolationElement[];
90
91  /** Who detected this */
92  detectedBy: DetectionSource;
93
94  /** How confident are we */
95  confidence: number; // 0-1
96
97  /** Related violations */
98  relatedViolations?: string[];
99}
100
101export interface WCAGCriterion {
102  /** e.g., "1.4.3" */
103  number: string;
104
105  /** e.g., "Contrast (Minimum)" */
106  name: string;
107
108  /** WCAG level (A, AA, AAA) */
109  level: WCAGLevel;
110
111  /** POUR category */
112  category: 'perceivable' | 'operable' | 'understandable' | 'robust';
113
114  /** Link to WCAG documentation */
115  url: string;
116}
117
118export type ViolationSeverity = 'critical' | 'serious' | 'moderate' | 'minor';
119
120export interface ImpactAssessment {
121  /** Human-readable description */
122  description: string;
123
124  /** Affected user groups */
125  affectedGroups: UserGroup[];
126
127  /** Estimated number of affected users */
128  estimatedUsers?: number;
129
130  /** Business impact */
131  businessImpact?: 'high' | 'medium' | 'low';
132
133  /** Legal risk */
134  legalRisk?: 'high' | 'medium' | 'low';
135}
136
137export type UserGroup =
138  | 'blind'
139  | 'low-vision'
140  | 'color-blind'
141  | 'deaf'
142  | 'hard-of-hearing'
143  | 'motor-impaired'
144  | 'cognitive-impaired'
145  | 'seizure-sensitive';
146
147export interface ViolationElement {
148  /** CSS selector */
149  selector: string;
150
151  /** HTML snippet */
152  html: string;
153
154  /** Location on page */
155  location: {
156    x: number;
157    y: number;
158    width: number;
159    height: number;
160  };
161
162  /** Accessibility tree info */
163  a11yTree?: {
164    role: string;
165    name: string;
166    description?: string;
167    states?: string[];
168  };
169
170  /** Screenshot (if enabled) */
171  screenshot?: string;
172}
173
174export type DetectionSource =
175  | 'axe-core'
176  | 'playwright'
177  | 'custom-heuristic'
178  | 'ml-model';

#2.3 Remediation Types

1/**
2 * Context-aware remediation suggestion
3 */
4export interface Remediation {
5  /** Unique remediation ID */
6  id: string;
7
8  /** Related violation IDs */
9  violationIds: string[];
10
11  /** Type of fix */
12  type: RemediationType;
13
14  /** Priority for fixing */
15  priority: 'critical' | 'high' | 'medium' | 'low';
16
17  /** The actual suggestion */
18  suggestion: RemediationSuggestion;
19
20  /** Can this be auto-fixed? */
21  autoFixable: boolean;
22
23  /** Auto-fix details (if applicable) */
24  autoFix?: AutoFix;
25
26  /** How confident are we */
27  confidence: number; // 0-1
28
29  /** Validation status */
30  validation?: {
31    tested: boolean;
32    successful?: boolean;
33    testResult?: string;
34  };
35}
36
37export type RemediationType =
38  | 'aria-label'
39  | 'aria-describedby'
40  | 'alt-text'
41  | 'color-contrast'
42  | 'keyboard-navigation'
43  | 'focus-management'
44  | 'semantic-html'
45  | 'heading-structure'
46  | 'form-labels'
47  | 'captions'
48  | 'transcripts'
49  | 'skip-links'
50  | 'landmarks'
51  | 'other';
52
53export interface RemediationSuggestion {
54  /** Human-readable title */
55  title: string;
56
57  /** Detailed description */
58  description: string;
59
60  /** Specific code changes */
61  codeChanges: CodeChange[];
62
63  /** Why this works */
64  reasoning: string;
65
66  /** Step-by-step instructions */
67  steps: string[];
68
69  /** Resources for learning */
70  resources?: Resource[];
71
72  /** Estimated effort */
73  effort: {
74    time: 'minutes' | 'hours' | 'days';
75    amount: number;
76    complexity: 'trivial' | 'low' | 'medium' | 'high';
77  };
78
79  /** Example implementation */
80  example?: {
81    before: string;
82    after: string;
83    explanation: string;
84  };
85}
86
87export interface CodeChange {
88  /** File path (if known) */
89  file?: string;
90
91  /** Element selector */
92  selector: string;
93
94  /** Type of change */
95  type: 'add-attribute' | 'modify-attribute' | 'remove-attribute' |
96        'add-element' | 'modify-element' | 'remove-element' |
97        'add-css' | 'modify-css';
98
99  /** Current state */
100  before: string;
101
102  /** Desired state */
103  after: string;
104
105  /** Explanation of change */
106  explanation: string;
107
108  /** Context */
109  context?: {
110    surroundingCode?: string;
111    relatedElements?: string[];
112  };
113}
114
115export interface AutoFix {
116  /** Safe to apply automatically */
117  safe: boolean;
118
119  /** Patch to apply */
120  patch: {
121    format: 'unified-diff' | 'json-patch';
122    content: string;
123  };
124
125  /** Test to verify fix */
126  verification?: {
127    test: string;
128    expectedResult: string;
129  };
130
131  /** Rollback information */
132  rollback?: {
133    enabled: boolean;
134    preserveOriginal?: boolean;
135  };
136}
137
138export interface Resource {
139  title: string;
140  url: string;
141  type: 'documentation' | 'tutorial' | 'example' | 'tool';
142}

#2.4 Context Analysis Types

1/**
2 * Element context for intelligent remediation
3 */
4export interface ElementContext {
5  /** The element being analyzed */
6  element: {
7    tagName: string;
8    attributes: Record<string, string>;
9    innerHTML: string;
10    outerHTML: string;
11  };
12
13  /** Surrounding context */
14  surrounding: {
15    /** Text before element */
16    precedingText?: string;
17
18    /** Text after element */
19    followingText?: string;
20
21    /** Nearby headings */
22    nearbyHeadings?: string[];
23
24    /** Parent container description */
25    parentDescription?: string;
26  };
27
28  /** Semantic context */
29  semantic: {
30    /** Inferred purpose */
31    purpose?: string;
32
33    /** User flow/journey */
34    userFlow?: string;
35
36    /** Page section */
37    section?: string;
38
39    /** Related form (if applicable) */
40    relatedForm?: FormContext;
41  };
42
43  /** Visual context */
44  visual?: {
45    /** Position on page */
46    position: 'header' | 'footer' | 'sidebar' | 'main' | 'nav';
47
48    /** Visibility */
49    visible: boolean;
50
51    /** Colors */
52    colors?: {
53      foreground: string;
54      background: string;
55      contrast: number;
56    };
57  };
58
59  /** Interactive context */
60  interactive?: {
61    /** Is interactive */
62    isInteractive: boolean;
63
64    /** Event handlers */
65    eventHandlers: string[];
66
67    /** Focus state */
68    focusable: boolean;
69
70    /** Tab index */
71    tabIndex?: number;
72  };
73}
74
75export interface FormContext {
76  formId?: string;
77  formPurpose?: string;
78  fieldType?: string;
79  validationRules?: string[];
80  relatedFields?: string[];
81}

#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:

1{
2  target: URLTarget | HTMLTarget;
3  level: 'A' | 'AA' | 'AAA';
4  config: ScanConfiguration;
5  options?: AdvancedOptions;
6}

Returns:

1QEToolResponse<AccessibilityScanResult>

Example Usage:

1const result = await mcp__agentic_qe__a11y_scan_comprehensive({
2  target: {
3    type: 'url',
4    url: 'https://example.com'
5  },
6  level: 'AA',
7  config: {
8    validators: {
9      axeCore: true,
10      playwright: true
11    },
12    tests: {
13      keyboard: true,
14      screenReader: true,
15      colorContrast: true
16    },
17    remediation: {
18      enabled: true,
19      contextAware: true,
20      generateTests: true
21    }
22  },
23  options: {
24    screenshots: true,
25    learning: {
26      queryPastPatterns: true,
27      storeLearnings: true
28    }
29  }
30});
31
32if (result.success) {
33  console.log(`Compliance Score: ${result.data.compliance.score}%`);
34  console.log(`Violations Found: ${result.data.violations.length}`);
35  console.log(`Remediations: ${result.data.remediations.length}`);
36}

#3.2 MCP Tool: analyze-context

Tool Name: mcp__agentic_qe__a11y_analyze_context

Description: Analyze element context for intelligent remediation suggestions

Parameters:

1{
2  element: {
3    selector: string;
4    html: string;
5  };
6  pageContext?: {
7    url?: string;
8    fullHTML?: string;
9  };
10  options?: {
11    inferPurpose?: boolean;
12    analyzeSemantics?: boolean;
13    visualAnalysis?: boolean;
14  };
15}

Returns:

1QEToolResponse<{
2  context: ElementContext;
3  suggestions: RemediationSuggestion[];
4  confidence: number;
5}>

#3.3 MCP Tool: generate-remediation

Tool Name: mcp__agentic_qe__a11y_generate_remediation

Description: Generate context-aware remediation for specific violations

Parameters:

1{
2  violations: Violation[];
3  context: ElementContext[];
4  options?: {
5    prioritize?: boolean;
6    includeAutoFix?: boolean;
7    includeExamples?: boolean;
8  };
9}

Returns:

1QEToolResponse<{
2  remediations: Remediation[];
3  priorityOrder: string[];
4  estimatedTotalEffort: number;
5}>

#3.4 MCP Tool: test-keyboard-navigation

Tool Name: mcp__agentic_qe__a11y_test_keyboard_navigation

Description: Test keyboard navigation and focus management

Parameters:

1{
2  target: URLTarget;
3  options?: {
4    testTabOrder?: boolean;
5    testFocusIndicators?: boolean;
6    testKeyboardTraps?: boolean;
7    testSkipLinks?: boolean;
8  };
9}

Returns:

1QEToolResponse<{
2  passed: boolean;
3  issues: KeyboardNavigationIssue[];
4  tabOrder: string[];
5  focusIndicators: boolean;
6  keyboardTraps: KeyboardTrap[];
7}>

#4. Algorithms

#4.1 Context-Aware ARIA Label Generation

1/**
2 * Generate appropriate ARIA label from element context
3 *
4 * Algorithm:
5 * 1. Extract element and surrounding context
6 * 2. Identify element purpose through multiple signals:
7 *    a. Icon analysis (SVG content, classes)
8 *    b. Surrounding text proximity scoring
9 *    c. Parent container semantic analysis
10 *    d. Form relationship inference
11 * 3. Combine signals with confidence weights
12 * 4. Generate natural language label
13 * 5. Validate against ARIA best practices
14 */
15async function generateARIALabel(
16  element: Element,
17  context: ElementContext
18): Promise<{ label: string; confidence: number }> {
19  const signals: Signal[] = [];
20
21  // Signal 1: Icon analysis (weight: 0.3)
22  const iconSignal = analyzeIcon(element);
23  if (iconSignal) {
24    signals.push({ type: 'icon', value: iconSignal, weight: 0.3 });
25  }
26
27  // Signal 2: Surrounding text (weight: 0.4)
28  const textSignal = analyzeSurroundingText(context.surrounding);
29  if (textSignal) {
30    signals.push({ type: 'text', value: textSignal, weight: 0.4 });
31  }
32
33  // Signal 3: Parent container (weight: 0.2)
34  const parentSignal = analyzeParentContainer(context.surrounding.parentDescription);
35  if (parentSignal) {
36    signals.push({ type: 'parent', value: parentSignal, weight: 0.2 });
37  }
38
39  // Signal 4: Form relationship (weight: 0.1)
40  if (context.semantic.relatedForm) {
41    const formSignal = analyzeFormRelationship(context.semantic.relatedForm);
42    if (formSignal) {
43      signals.push({ type: 'form', value: formSignal, weight: 0.1 });
44    }
45  }
46
47  // Combine signals
48  const combinedLabel = combineSignals(signals);
49  const confidence = calculateConfidence(signals);
50
51  return { label: combinedLabel, confidence };
52}
53
54function analyzeIcon(element: Element): string | null {
55  // Check for common icon patterns
56  const svg = element.querySelector('svg');
57  if (!svg) return null;
58
59  // Icon class analysis
60  const classes = element.className.toLowerCase();
61  if (classes.includes('cart')) return 'shopping cart';
62  if (classes.includes('user')) return 'user profile';
63  if (classes.includes('search')) return 'search';
64  // ... more patterns
65
66  // SVG content analysis
67  const svgContent = svg.innerHTML.toLowerCase();
68  if (svgContent.includes('path') && svgContent.includes('cart')) {
69    return 'shopping cart';
70  }
71
72  return null;
73}
74
75function analyzeSurroundingText(surrounding: ElementContext['surrounding']): string | null {
76  const precedingText = surrounding.precedingText?.trim();
77  const followingText = surrounding.followingText?.trim();
78
79  // Prefer preceding text (usually more relevant)
80  if (precedingText && precedingText.length < 50) {
81    return precedingText;
82  }
83
84  // Check nearby headings
85  if (surrounding.nearbyHeadings && surrounding.nearbyHeadings.length > 0) {
86    return surrounding.nearbyHeadings[0];
87  }
88
89  // Fallback to following text
90  if (followingText && followingText.length < 50) {
91    return followingText;
92  }
93
94  return null;
95}
96
97function combineSignals(signals: Signal[]): string {
98  // Weight-based combination
99  let combinedLabel = '';
100  let totalWeight = 0;
101
102  // Prefer higher-weight signals
103  signals.sort((a, b) => b.weight - a.weight);
104
105  for (const signal of signals) {
106    if (signal.value && totalWeight < 1.0) {
107      if (!combinedLabel) {
108        combinedLabel = signal.value;
109      }
110      totalWeight += signal.weight;
111    }
112  }
113
114  return combinedLabel || 'Interactive element';
115}
116
117function calculateConfidence(signals: Signal[]): number {
118  if (signals.length === 0) return 0.3;
119
120  const totalWeight = signals.reduce((sum, s) => sum + s.weight, 0);
121  const hasHighConfidenceSignal = signals.some(s => s.weight >= 0.3);
122
123  let confidence = totalWeight;
124  if (hasHighConfidenceSignal) confidence += 0.1;
125  if (signals.length >= 3) confidence += 0.1;
126
127  return Math.min(confidence, 1.0);
128}

#4.2 Color Contrast Optimization

1/**
2 * Suggest color adjustments to meet WCAG contrast requirements
3 *
4 * Algorithm:
5 * 1. Calculate current contrast ratio
6 * 2. Determine required ratio based on WCAG level
7 * 3. Identify which color to adjust (prefer background)
8 * 4. Calculate minimum adjustment needed
9 * 5. Suggest nearest accessible color from palette
10 * 6. Preserve brand colors when possible
11 */
12async function optimizeColorContrast(
13  foreground: string,
14  background: string,
15  level: WCAGLevel,
16  textSize: 'normal' | 'large'
17): Promise<ContrastOptimization> {
18  // Calculate current contrast
19  const currentRatio = calculateContrastRatio(foreground, background);
20
21  // Determine required ratio
22  const requiredRatio = getRequiredRatio(level, textSize);
23
24  // Already passes
25  if (currentRatio >= requiredRatio) {
26    return {
27      passes: true,
28      currentRatio,
29      requiredRatio,
30      suggestions: []
31    };
32  }
33
34  // Calculate adjustment needed
35  const adjustmentNeeded = requiredRatio - currentRatio;
36
37  // Generate suggestions
38  const suggestions: ColorSuggestion[] = [];
39
40  // Suggestion 1: Darken background
41  const darkerBg = adjustColor(background, 'darken', adjustmentNeeded);
42  if (calculateContrastRatio(foreground, darkerBg) >= requiredRatio) {
43    suggestions.push({
44      type: 'background',
45      original: background,
46      suggested: darkerBg,
47      ratio: calculateContrastRatio(foreground, darkerBg),
48      change: 'darken'
49    });
50  }
51
52  // Suggestion 2: Lighten background
53  const lighterBg = adjustColor(background, 'lighten', adjustmentNeeded);
54  if (calculateContrastRatio(foreground, lighterBg) >= requiredRatio) {
55    suggestions.push({
56      type: 'background',
57      original: background,
58      suggested: lighterBg,
59      ratio: calculateContrastRatio(foreground, lighterBg),
60      change: 'lighten'
61    });
62  }
63
64  // Suggestion 3: Adjust foreground (less preferred)
65  const darkerFg = adjustColor(foreground, 'darken', adjustmentNeeded);
66  if (calculateContrastRatio(darkerFg, background) >= requiredRatio) {
67    suggestions.push({
68      type: 'foreground',
69      original: foreground,
70      suggested: darkerFg,
71      ratio: calculateContrastRatio(darkerFg, background),
72      change: 'darken',
73      priority: 'low' // Prefer background changes
74    });
75  }
76
77  // Sort by minimal change and priority
78  suggestions.sort((a, b) => {
79    if (a.priority === 'low' && b.priority !== 'low') return 1;
80    if (a.priority !== 'low' && b.priority === 'low') return -1;
81    return Math.abs(a.ratio - requiredRatio) - Math.abs(b.ratio - requiredRatio);
82  });
83
84  return {
85    passes: false,
86    currentRatio,
87    requiredRatio,
88    suggestions
89  };
90}

#5. Integration Patterns

#5.1 Learning Integration

1/**
2 * Learning protocol for a11y-ally agent
3 */
4
5// BEFORE task: Query past learnings
6async function queryPastLearnings() {
7  const learnings = await mcp__agentic_qe__learning_query({
8    agentId: 'qe-a11y-ally',
9    taskType: 'accessibility-scan',
10    minReward: 0.8,
11    queryType: 'all',
12    limit: 10
13  });
14
15  // Apply learned patterns
16  if (learnings.success) {
17    applyLearnedPatterns(learnings.data);
18  }
19}
20
21// AFTER task: Store learnings
22async function storeLearnings(scanResult: AccessibilityScanResult) {
23  // Calculate reward
24  const reward = calculateReward(scanResult);
25
26  // Store experience
27  await mcp__agentic_qe__learning_store_experience({
28    agentId: 'qe-a11y-ally',
29    taskType: 'accessibility-scan',
30    reward,
31    outcome: {
32      violationsDetected: scanResult.violations.length,
33      complianceScore: scanResult.compliance.score,
34      remediationsGenerated: scanResult.remediations.length,
35      contextAccuracy: calculateContextAccuracy(scanResult)
36    },
37    metadata: {
38      wcagLevel: scanResult.metadata.level,
39      toolsUsed: scanResult.metadata.toolsUsed,
40      targetType: 'url'
41    }
42  });
43
44  // Store successful remediation patterns
45  for (const remediation of scanResult.remediations) {
46    if (remediation.confidence >= 0.9) {
47      await mcp__agentic_qe__learning_store_pattern({
48        pattern: `${remediation.type}: ${remediation.suggestion.description}`,
49        confidence: remediation.confidence,
50        domain: 'accessibility-remediation',
51        metadata: {
52          violationType: remediation.type,
53          autoFixable: remediation.autoFixable
54        }
55      });
56    }
57  }
58}
59
60function calculateReward(scanResult: AccessibilityScanResult): number {
61  const { violations, compliance, performance } = scanResult;
62
63  // Base score from compliance
64  let reward = compliance.score / 100;
65
66  // Bonus for comprehensive detection
67  if (violations.length > 0) {
68    reward += 0.1; // Found issues
69  }
70
71  // Bonus for fast performance
72  if (performance.scanTime < 10000) {
73    reward += 0.1;
74  }
75
76  // Penalty for low confidence remediations
77  const lowConfidenceCount = scanResult.remediations.filter(
78    r => r.confidence < 0.7
79  ).length;
80  reward -= lowConfidenceCount * 0.05;
81
82  return Math.max(0, Math.min(1, reward));
83}

#5.2 Fleet Coordination

1/**
2 * Coordinate with other QE agents
3 */
4
5// Example: Full accessibility workflow
6async function fullAccessibilityWorkflow(url: string) {
7  // 1. Scan for violations (a11y-ally)
8  const scanResult = await Task(
9    'Scan accessibility',
10    `Comprehensive WCAG 2.2 AA scan of ${url}`,
11    'qe-a11y-ally'
12  );
13
14  // Store results in memory for other agents
15  await mcp__agentic_qe__memory_store({
16    key: `aqe/accessibility/scan-results/${scanResult.scanId}`,
17    value: scanResult,
18    namespace: 'aqe',
19    persist: true
20  });
21
22  // 2. Generate tests from violations (test-generator)
23  if (scanResult.violations.length > 0) {
24    await Task(
25      'Generate a11y tests',
26      `Generate regression tests for ${scanResult.violations.length} violations`,
27      'qe-test-generator'
28    );
29  }
30
31  // 3. Visual regression for accessibility (visual-tester)
32  await Task(
33    'Visual a11y check',
34    'Capture annotated screenshots of violations',
35    'qe-visual-tester'
36  );
37
38  // 4. Quality gate check (quality-gate)
39  const gateResult = await Task(
40    'A11y quality gate',
41    `Check if compliance score ${scanResult.compliance.score}% passes threshold`,
42    'qe-quality-gate'
43  );
44
45  return {
46    scanResult,
47    gateResult,
48    productionReady: gateResult.passed
49  };
50}

#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

MetricTargetRationale
Single page scan<10sFast enough for CI/CD
Multi-page scan (10 pages)<60sAcceptable for comprehensive audits
Memory usage<512MBDevPod/Codespace friendly
CPU usage<80%Leave room for other processes

#7. Security Considerations

#7.1 Input Validation

1/**
2 * Validate and sanitize inputs
3 */
4function validateScanParams(params: AccessibilityScanParams): void {
5  // URL validation
6  if (params.target.type === 'url') {
7    const url = new URL(params.target.url); // Throws if invalid
8
9    // Block dangerous protocols
10    if (!['http:', 'https:'].includes(url.protocol)) {
11      throw new Error('Only HTTP/HTTPS protocols allowed');
12    }
13
14    // Block localhost in production
15    if (process.env.NODE_ENV === 'production' &&
16        url.hostname === 'localhost') {
17      throw new Error('Cannot scan localhost in production');
18    }
19  }
20
21  // HTML validation
22  if (params.target.type === 'html') {
23    // Sanitize HTML to prevent XSS
24    params.target.html = sanitizeHTML(params.target.html);
25  }
26
27  // Selector validation (prevent injection)
28  if (params.config.scope?.include) {
29    params.config.scope.include = params.config.scope.include.map(
30      selector => sanitizeSelector(selector)
31    );
32  }
33}

#7.2 Safe Auto-Fix

1/**
2 * Ensure auto-fixes are safe to apply
3 */
4function validateAutoFix(autoFix: AutoFix): boolean {
5  // Only allow safe attribute additions/modifications
6  const safeChanges = [
7    'add-aria-label',
8    'add-alt-text',
9    'add-aria-describedby'
10  ];
11
12  // Never allow:
13  // - Script injection
14  // - Removing security-related attributes
15  // - Modifying authentication elements
16
17  return autoFix.safe &&
18         autoFix.verification !== undefined &&
19         !containsDangerousPatterns(autoFix.patch.content);
20}

#8. Error Handling

#8.1 Error Types

1export enum A11yErrorCode {
2  // Scan errors
3  SCAN_TIMEOUT = 'A11Y_SCAN_TIMEOUT',
4  SCAN_FAILED = 'A11Y_SCAN_FAILED',
5  INVALID_TARGET = 'A11Y_INVALID_TARGET',
6
7  // Analysis errors
8  CONTEXT_ANALYSIS_FAILED = 'A11Y_CONTEXT_ANALYSIS_FAILED',
9  REMEDIATION_GENERATION_FAILED = 'A11Y_REMEDIATION_GENERATION_FAILED',
10
11  // Tool errors
12  AXE_CORE_ERROR = 'A11Y_AXE_CORE_ERROR',
13  PLAYWRIGHT_ERROR = 'A11Y_PLAYWRIGHT_ERROR',
14
15  // Learning errors
16  LEARNING_QUERY_FAILED = 'A11Y_LEARNING_QUERY_FAILED',
17  LEARNING_STORE_FAILED = 'A11Y_LEARNING_STORE_FAILED'
18}
19
20export interface A11yError extends QEError {
21  code: A11yErrorCode;
22  recoverable: boolean;
23  retryable: boolean;
24}

#8.2 Graceful Degradation

1/**
2 * Handle tool failures gracefully
3 */
4async function scanWithGracefulDegradation(
5  params: AccessibilityScanParams
6): Promise<AccessibilityScanResult> {
7  const results: Partial<AccessibilityScanResult> = {
8    violations: [],
9    remediations: []
10  };
11
12  // Try axe-core
13  try {
14    const axeResult = await runAxeCore(params);
15    results.violations.push(...axeResult.violations);
16  } catch (error) {
17    logError('axe-core failed, continuing with other tools', error);
18  }
19
20  // Try Playwright
21  try {
22    const playwrightResult = await runPlaywright(params);
23    results.violations.push(...playwrightResult.violations);
24  } catch (error) {
25    logError('Playwright failed, continuing with other tools', error);
26  }
27
28  // Try custom heuristics
29  try {
30    const customResult = await runCustomHeuristics(params);
31    results.violations.push(...customResult.violations);
32  } catch (error) {
33    logError('Custom heuristics failed', error);
34  }
35
36  // If all tools failed, throw
37  if (results.violations.length === 0) {
38    throw new Error('All scanning tools failed');
39  }
40
41  return results as AccessibilityScanResult;
42}

#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

1interface A11yMetrics {
2  // Scan metrics
3  totalScans: number;
4  successfulScans: number;
5  failedScans: number;
6  averageScanTime: number;
7
8  // Detection metrics
9  violationsDetected: number;
10  violationsByType: Record<RemediationType, number>;
11  violationsBySeverity: Record<ViolationSeverity, number>;
12
13  // Remediation metrics
14  remediationsGenerated: number;
15  autoFixableCount: number;
16  averageConfidence: number;
17
18  // Learning metrics
19  patternsLearned: number;
20  patternAccuracy: number;
21
22  // User impact metrics
23  averageFixTime: number; // After vs before
24  complianceImprovement: number;
25}

#10.2 Logging

1// Structured logging for observability
2logger.info('A11y scan started', {
3  scanId,
4  target: params.target,
5  level: params.level,
6  timestamp: Date.now()
7});
8
9logger.info('Violations detected', {
10  scanId,
11  count: violations.length,
12  critical: violations.filter(v => v.severity === 'critical').length
13});
14
15logger.info('Remediations generated', {
16  scanId,
17  count: remediations.length,
18  autoFixable: remediations.filter(r => r.autoFixable).length
19});

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