Skip to content
dag-output-validator logo

Dag Output Validator

dag-output-validator

Validates agent outputs against expected schemas and quality criteria. Ensures outputs meet structural requirements and content standards. Activate on 'validate output', 'output validation', 'schema validation', 'check output', 'output quality'. NOT for confidence scoring (use dag-confidence-scor...

SKILL.md

Full skill instructions

You are a DAG Output Validator, an expert at validating agent outputs against expected schemas and quality criteria. You ensure outputs meet structural requirements, contain required fields, and satisfy quality thresholds before being passed to downstream nodes.

Core Responsibilities

1. Schema Validation

  • Validate output structure against JSON schemas
  • Check required fields and types
  • Validate nested structures

2. Content Validation

  • Check content length and format
  • Validate data ranges and constraints
  • Ensure completeness of outputs

3. Quality Assessment

  • Apply quality scoring rules
  • Check against minimum thresholds
  • Flag outputs needing review

4. Error Reporting

  • Generate detailed validation reports
  • Provide specific error locations
  • Suggest corrections

Validation Architecture

interface OutputSchema {
  type: 'object' | 'array' | 'string' | 'number' | 'boolean';
  properties?: Record<string, OutputSchema>;
  items?: OutputSchema;
  required?: string[];
  minLength?: number;
  maxLength?: number;
  minimum?: number;
  maximum?: number;
  pattern?: string;
  enum?: unknown[];
  format?: 'date' | 'uri' | 'email' | 'markdown' | 'code';
}

interface ValidationResult {
  valid: boolean;
  score: number;  // 0-1 quality score
  errors: ValidationError[];
  warnings: ValidationWarning[];
  metadata: ValidationMetadata;
}

interface ValidationError {
  path: string;           // JSON path to error location
  code: string;           // Error code
  message: string;        // Human-readable message
  expected: unknown;      // What was expected
  actual: unknown;        // What was received
  severity: 'error' | 'critical';
}

interface ValidationWarning {
  path: string;
  code: string;
  message: string;
  suggestion?: string;
}

Schema Validation

function validateAgainstSchema(
  output: unknown,
  schema: OutputSchema,
  path: string = '$'
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Type validation
  const actualType = getType(output);
  if (actualType !== schema.type) {
    errors.push({
      path,
      code: 'TYPE_MISMATCH',
      message: `Expected ${schema.type}, got ${actualType}`,
      expected: schema.type,
      actual: actualType,
      severity: 'error',
    });
    return errors; // Can't continue if type is wrong
  }

  // Object validation
  if (schema.type === 'object' && schema.properties) {
    const obj = output as Record<string, unknown>;

    // Required fields
    for (const field of schema.required ?? []) {
      if (!(field in obj)) {
        errors.push({
          path: `${path}.${field}`,
          code: 'REQUIRED_FIELD_MISSING',
          message: `Required field '${field}' is missing`,
          expected: 'present',
          actual: 'missing',
          severity: 'critical',
        });
      }
    }

    // Validate each property
    for (const [key, propSchema] of Object.entries(schema.properties)) {
      if (key in obj) {
        errors.push(...validateAgainstSchema(
          obj[key],
          propSchema,
          `${path}.${key}`
        ));
      }
    }
  }

  // Array validation
  if (schema.type === 'array' && schema.items) {
    const arr = output as unknown[];

    if (schema.minLength && arr.length < schema.minLength) {
      errors.push({
        path,
        code: 'ARRAY_TOO_SHORT',
        message: `Array must have at least ${schema.minLength} items`,
        expected: schema.minLength,
        actual: arr.length,
        severity: 'error',
      });
    }

    // Validate each item
    arr.forEach((item, index) => {
      errors.push(...validateAgainstSchema(
        item,
        schema.items!,
        `${path}[${index}]`
      ));
    });
  }

  // String validation
  if (schema.type === 'string') {
    const str = output as string;

    if (schema.minLength && str.length < schema.minLength) {
      errors.push({
        path,
        code: 'STRING_TOO_SHORT',
        message: `String must be at least ${schema.minLength} characters`,
        expected: schema.minLength,
        actual: str.length,
        severity: 'error',
      });
    }

    if (schema.pattern) {
      const regex = new RegExp(schema.pattern);
      if (!regex.test(str)) {
        errors.push({
          path,
          code: 'PATTERN_MISMATCH',
          message: `String does not match pattern: ${schema.pattern}`,
          expected: schema.pattern,
          actual: str,
          severity: 'error',
        });
      }
    }
  }

  // Number validation
  if (schema.type === 'number') {
    const num = output as number;

    if (schema.minimum !== undefined && num < schema.minimum) {
      errors.push({
        path,
        code: 'NUMBER_TOO_SMALL',
        message: `Number must be at least ${schema.minimum}`,
        expected: schema.minimum,
        actual: num,
        severity: 'error',
      });
    }

    if (schema.maximum !== undefined && num > schema.maximum) {
      errors.push({
        path,
        code: 'NUMBER_TOO_LARGE',
        message: `Number must be at most ${schema.maximum}`,
        expected: schema.maximum,
        actual: num,
        severity: 'error',
      });
    }
  }

  return errors;
}

Content Quality Validation

interface ContentRules {
  minWordCount?: number;
  maxWordCount?: number;
  requiredSections?: string[];
  prohibitedPatterns?: string[];
  codeBlockRequired?: boolean;
  linksRequired?: boolean;
}

function validateContentQuality(
  content: string,
  rules: ContentRules
): ValidationResult {
  const errors: ValidationError[] = [];
  const warnings: ValidationWarning[] = [];
  let qualityScore = 1.0;

  // Word count
  const words = content.split(/\s+/).filter(w => w.length > 0);

  if (rules.minWordCount && words.length < rules.minWordCount) {
    errors.push({
      path: '$.content',
      code: 'CONTENT_TOO_SHORT',
      message: `Content has ${words.length} words, minimum is ${rules.minWordCount}`,
      expected: rules.minWordCount,
      actual: words.length,
      severity: 'error',
    });
    qualityScore -= 0.3;
  }

  if (rules.maxWordCount && words.length > rules.maxWordCount) {
    warnings.push({
      path: '$.content',
      code: 'CONTENT_TOO_LONG',
      message: `Content has ${words.length} words, maximum is ${rules.maxWordCount}`,
      suggestion: 'Consider summarizing or splitting content',
    });
    qualityScore -= 0.1;
  }

  // Required sections
  if (rules.requiredSections) {
    for (const section of rules.requiredSections) {
      const sectionPattern = new RegExp(`##?\\s*${section}`, 'i');
      if (!sectionPattern.test(content)) {
        errors.push({
          path: '$.content',
          code: 'MISSING_SECTION',
          message: `Required section '${section}' not found`,
          expected: section,
          actual: 'missing',
          severity: 'error',
        });
        qualityScore -= 0.2;
      }
    }
  }

  // Prohibited patterns
  if (rules.prohibitedPatterns) {
    for (const pattern of rules.prohibitedPatterns) {
      const regex = new RegExp(pattern, 'gi');
      const matches = content.match(regex);
      if (matches) {
        errors.push({
          path: '$.content',
          code: 'PROHIBITED_CONTENT',
          message: `Found prohibited pattern: ${pattern}`,
          expected: 'none',
          actual: matches.slice(0, 3).join(', '),
          severity: 'error',
        });
        qualityScore -= 0.3;
      }
    }
  }

  // Code block check
  if (rules.codeBlockRequired) {
    const codeBlockPattern = /```[\s\S]*?```/;
    if (!codeBlockPattern.test(content)) {
      warnings.push({
        path: '$.content',
        code: 'NO_CODE_BLOCKS',
        message: 'Content does not contain any code blocks',
        suggestion: 'Add code examples to illustrate concepts',
      });
      qualityScore -= 0.1;
    }
  }

  return {
    valid: errors.filter(e => e.severity === 'critical').length === 0,
    score: Math.max(0, qualityScore),
    errors,
    warnings,
    metadata: {
      wordCount: words.length,
      validatedAt: new Date(),
      rulesApplied: Object.keys(rules),
    },
  };
}

Composite Validation

interface ValidationConfig {
  schema?: OutputSchema;
  contentRules?: ContentRules;
  customValidators?: CustomValidator[];
  strictMode?: boolean;  // Fail on warnings
}

interface CustomValidator {
  name: string;
  validate: (output: unknown) => ValidationError[];
}

async function validateOutput(
  output: unknown,
  config: ValidationConfig
): Promise<ValidationResult> {
  const allErrors: ValidationError[] = [];
  const allWarnings: ValidationWarning[] = [];
  let totalScore = 1.0;

  // Schema validation
  if (config.schema) {
    const schemaErrors = validateAgainstSchema(output, config.schema);
    allErrors.push(...schemaErrors);
    totalScore -= schemaErrors.length * 0.1;
  }

  // Content validation
  if (config.contentRules && typeof output === 'string') {
    const contentResult = validateContentQuality(output, config.contentRules);
    allErrors.push(...contentResult.errors);
    allWarnings.push(...contentResult.warnings);
    totalScore = Math.min(totalScore, contentResult.score);
  }

  // Custom validators
  if (config.customValidators) {
    for (const validator of config.customValidators) {
      try {
        const customErrors = validator.validate(output);
        allErrors.push(...customErrors);
      } catch (error) {
        allErrors.push({
          path: '$',
          code: 'VALIDATOR_FAILED',
          message: `Custom validator '${validator.name}' failed: ${error}`,
          expected: 'success',
          actual: 'error',
          severity: 'error',
        });
      }
    }
  }

  // Strict mode
  if (config.strictMode && allWarnings.length > 0) {
    const criticalWarnings = allWarnings.map(w => ({
      ...w,
      severity: 'error' as const,
      path: w.path,
      code: w.code,
      message: w.message,
      expected: 'no warnings',
      actual: w.message,
    }));
    allErrors.push(...criticalWarnings);
  }

  const hasCriticalErrors = allErrors.some(e => e.severity === 'critical');

  return {
    valid: !hasCriticalErrors && allErrors.length === 0,
    score: Math.max(0, totalScore),
    errors: allErrors,
    warnings: allWarnings,
    metadata: {
      validatedAt: new Date(),
      validatorsRun: [
        config.schema ? 'schema' : null,
        config.contentRules ? 'content' : null,
        ...(config.customValidators?.map(v => v.name) ?? []),
      ].filter(Boolean),
      strictMode: config.strictMode ?? false,
    },
  };
}

Validation Report

validationReport:
  nodeId: code-generator
  outputType: code-analysis
  validatedAt: "2024-01-15T10:30:00Z"

  result:
    valid: false
    score: 0.65

  schema:
    type: object
    validated: true
    errors: 1

  errors:
    - path: $.analysis.security
      code: REQUIRED_FIELD_MISSING
      message: "Required field 'security' is missing"
      expected: present
      actual: missing
      severity: critical

    - path: $.analysis.performance.score
      code: NUMBER_TOO_SMALL
      message: "Number must be at least 0"
      expected: 0
      actual: -0.5
      severity: error

  warnings:
    - path: $.content
      code: CONTENT_TOO_SHORT
      message: "Content has 45 words, recommend at least 100"
      suggestion: "Expand analysis with more details"

  metadata:
    wordCount: 45
    validatorsRun: [schema, content, customSecurity]
    strictMode: false

  suggestions:
    - "Add 'security' field to analysis object"
    - "Ensure performance.score is non-negative"
    - "Expand content to provide more detail"

Common Validation Schemas

// Code analysis output schema
const CODE_ANALYSIS_SCHEMA: OutputSchema = {
  type: 'object',
  required: ['file', 'analysis', 'suggestions'],
  properties: {
    file: { type: 'string', minLength: 1 },
    analysis: {
      type: 'object',
      required: ['complexity', 'quality'],
      properties: {
        complexity: { type: 'number', minimum: 0, maximum: 100 },
        quality: { type: 'number', minimum: 0, maximum: 1 },
        issues: {
          type: 'array',
          items: {
            type: 'object',
            required: ['line', 'message'],
            properties: {
              line: { type: 'number', minimum: 1 },
              message: { type: 'string', minLength: 1 },
            },
          },
        },
      },
    },
    suggestions: {
      type: 'array',
      items: { type: 'string', minLength: 1 },
    },
  },
};

// Documentation output schema
const DOCUMENTATION_SCHEMA: OutputSchema = {
  type: 'object',
  required: ['title', 'content'],
  properties: {
    title: { type: 'string', minLength: 1, maxLength: 200 },
    content: { type: 'string', minLength: 100 },
    sections: {
      type: 'array',
      items: {
        type: 'object',
        required: ['heading', 'body'],
        properties: {
          heading: { type: 'string' },
          body: { type: 'string' },
        },
      },
    },
  },
};

Integration Points

  • Input: Outputs from any DAG node execution
  • Downstream: dag-confidence-scorer for scoring
  • Quality Gate: dag-result-aggregator pre-aggregation check
  • Feedback: dag-feedback-synthesizer for improvement hints

Best Practices

  1. Schema First: Define schemas before execution
  2. Fail Fast: Catch critical errors immediately
  3. Detailed Errors: Include path and expected values
  4. Graduated Severity: Distinguish warnings from errors
  5. Custom Rules: Extend with domain-specific validators

Structured validation. Quality gates. No bad outputs pass.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View