Schema validation

Sphinx-Needs (≥ 6.0) includes a powerful schema validation system based on JSON Schema. The schema_definitions_from_json option in [needs] points to a JSON file of validation rules that check individual need properties and cross-need relationships. Use this when per-field schema constraints (see Field schemas) are not enough — for example to enforce that certain need types carry specific fields, or that linked needs satisfy particular conditions.

See also

sphinx-needs schema documentation

The full sphinx-needs schema reference.

Minimal example

[needs]
schema_definitions_from_json = "schemas.json"

With a schemas.json alongside your ubproject.toml:

{
  "schemas": [
    {
      "id": "spec-needs-priority",
      "message": "Specifications must have a priority",
      "select": {
        "properties": { "type": { "const": "spec" } }
      },
      "validate": {
        "local": { "required": ["priority"] }
      }
    }
  ]
}

Overview

Schema validation operates at multiple levels:

  1. Field-level schemas — defined inline on [needs.fields] or [needs.links], these validate every value entered for that field globally (see Field schemas).

  2. Schema definitions — defined in a schemas.json file (via schema_definitions_from_json), these provide advanced validation including type-specific rules, conditional constraints, and cross-need (network) validation.

Both levels complement each other: field-level schemas enforce global constraints for a field, while schema definitions allow complex, type-specific validation logic.

Configuration

schema_definitions_from_json

Type: string (default: "")

Path to a JSON file containing schema definitions. Available since sphinx-needs 6.0.

[needs]
schema_definitions_from_json = "schemas.json"

The schemas.json file

The schema file has two top-level keys:

  • "$defs" (optional): Reusable schema components referenced via $ref

  • "schemas" (required): Array of validation rule objects

{
  "$defs": {
    "type-spec": {
      "properties": {
        "type": { "const": "spec" }
      }
    },
    "safe-need": {
      "properties": {
        "asil": { "enum": ["A", "B", "C", "D"] }
      },
      "required": ["asil"]
    }
  },
  "schemas": [
    {
      "id": "spec-id-pattern",
      "severity": "warning",
      "message": "Spec IDs must be uppercase",
      "select": {
        "$ref": "#/$defs/type-spec"
      },
      "validate": {
        "local": {
          "properties": {
            "id": { "pattern": "^SPEC_[A-Z0-9_]+$" }
          }
        }
      }
    }
  ]
}

Each schema object in the schemas array supports:

  • id (optional): Identifier for the schema rule (used in error messages)

  • severity (optional): "violation" (default), "warning", or "info"

  • message (optional): Custom message shown when validation fails

  • select (optional): JSON Schema that filters which needs this rule applies to. If omitted, the rule applies to all needs.

  • validate (required): Validation rules with two subsections:

    • local: Validates properties of the need itself

    • network: Validates relationships with linked needs

Type system

Sphinx-Needs supports the following types for need fields, defined via schema.type in [needs.fields]:

  • Primitive types: string, boolean, integer, number

  • Array types: array with typed items (e.g. schema = {type = "array", items = {type = "string"}})

Type information from [needs.fields] is automatically injected into schemas.json rules, so you don’t need to repeat "type": "string" in every schema. If you do specify a type in the schema file, it must match the field definition.

Local validation

Local validation checks properties of a single need without any information from other needs. This makes it suitable for instant feedback in IDEs.

Example: enforce that efforts is between 0 and 20, and that approval is required when efforts exceed 15:

{
  "schemas": [
    {
      "id": "efforts-range",
      "select": {
        "properties": { "type": { "enum": ["spec", "feat"] } }
      },
      "validate": {
        "local": {
          "properties": {
            "efforts": {
              "minimum": 0,
              "maximum": 20
            }
          }
        }
      }
    },
    {
      "id": "approval-required-for-high-effort",
      "severity": "violation",
      "message": "Approval required when efforts > 15",
      "select": {
        "properties": {
          "type": { "enum": ["spec", "feat"] },
          "efforts": { "minimum": 16 }
        },
        "required": ["efforts"]
      },
      "validate": {
        "local": {
          "required": ["approval"]
        }
      }
    }
  ]
}

The select filter narrows which needs the rule applies to. The validate.local section uses standard JSON Schema properties: properties, required, allOf, anyOf, oneOf, not, unevaluatedProperties, etc.

Network validation

Network validation checks relationships between linked needs. After link resolution, the validator follows outgoing links and validates properties of the target needs.

Example: a safe implementation must link to at least one safe, approved specification:

{
  "id": "safe-impl-links",
  "message": "Safe impl must link to approved safe specs",
  "select": {
    "allOf": [
      { "$ref": "#/$defs/type-impl" },
      { "$ref": "#/$defs/safe-need" }
    ]
  },
  "validate": {
    "network": {
      "links": {
        "contains": {
          "local": {
            "allOf": [
              { "$ref": "#/$defs/type-spec" },
              { "$ref": "#/$defs/safe-need" },
              {
                "properties": {
                  "approval": { "const": true }
                }
              }
            ]
          }
        },
        "minContains": 1
      }
    }
  }
}

Key network validation properties:

  • validate.network.<link_type>.items — schema that all linked needs must match

  • validate.network.<link_type>.contains — schema that some linked needs must match

  • minContains / maxContains — how many linked needs must satisfy the contains schema

  • validate.local.properties.<link_type>.minItems / maxItems — total link count constraints (checked locally since links are ID lists)

Network validation can be nested to validate multi-hop chains (e.g. impl → spec → feat), up to 4 levels deep.

Reusable definitions ($defs)

Define common schema fragments in $defs and reference them with $ref:

{
  "$defs": {
    "type-impl": {
      "properties": { "type": { "const": "impl" } }
    },
    "type-spec": {
      "properties": { "type": { "const": "spec" } }
    },
    "safe-need": {
      "properties": {
        "asil": { "enum": ["A", "B", "C", "D"] }
      },
      "required": ["asil"]
    },
    "safe-spec": {
      "allOf": [
        { "$ref": "#/$defs/safe-need" },
        { "$ref": "#/$defs/type-spec" }
      ]
    }
  },
  "schemas": []
}

$ref must be the only key in the object where it appears. Recursive references are not allowed.

Severity levels

Each schema rule can specify a severity:

  • "info" — informational, logged as a Sphinx warning

  • "warning" — logged as a Sphinx warning

  • "violation" — logged as a Sphinx error (default)

{
  "severity": "warning",
  "message": "Consider adding a priority field",
  "validate": {
    "local": {
      "required": ["priority"]
    }
  }
}

Validation messages can be suppressed using Sphinx’s suppress_warnings:

# In conf.py
suppress_warnings = [
    "sn_schema_violation",       # all violations
    "sn_schema_warning",         # all warnings
    "sn_schema_info",            # all info messages
    "sn_schema_violation.local_fail",  # only local failures
]

Supported field constraints

The following JSON Schema constraints are available per type. These can be used both in [needs.fields] inline schemas and in schemas.json validation rules.

String: minLength, maxLength, pattern, format, enum, const

Integer / Number: minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, enum, const

Boolean: const

Array: items (with sub-schema), minItems, maxItems, uniqueItems, contains, minContains, maxContains

String format values: "date", "date-time", "time", "duration", "email", "uri", "hostname", "uuid", "regex"

Note

Regex pattern values must be cross-engine compatible (Python, Rust, SQLite). Avoid lookaheads, lookbehinds, backreferences, and nested quantifiers.

Complete example

The following shows a ubproject.toml with typed fields and a schemas.json that validates them:

ubproject.toml:

[needs]
id_required = true
schema_definitions_from_json = "schemas.json"

[[needs.types]]
directive = "feat"
title = "Feature"
prefix = "FEAT_"

[[needs.types]]
directive = "spec"
title = "Specification"
prefix = "SPEC_"

[[needs.types]]
directive = "impl"
title = "Implementation"
prefix = "IMPL_"

[needs.links.implements]
outgoing = "implements"
incoming = "implemented by"

[needs.fields.efforts]
schema = {type = "integer", minimum = 0, maximum = 100}

[needs.fields.approval]
schema = {type = "boolean"}

[needs.fields.asil]
schema = {type = "string", enum = ["QM", "A", "B", "C", "D"]}

schemas.json:

{
  "$defs": {
    "type-impl": {
      "properties": { "type": { "const": "impl" } }
    },
    "type-spec": {
      "properties": { "type": { "const": "spec" } }
    },
    "safe-need": {
      "properties": {
        "asil": { "enum": ["A", "B", "C", "D"] }
      },
      "required": ["asil"]
    }
  },
  "schemas": [
    {
      "id": "effort-limits",
      "message": "Efforts must be between 0 and 20 for specs",
      "select": {
        "properties": { "type": { "const": "spec" } }
      },
      "validate": {
        "local": {
          "properties": {
            "efforts": { "maximum": 20 }
          }
        }
      }
    },
    {
      "id": "safe-impl-chain",
      "message": "Safe impl must link to safe spec",
      "select": {
        "allOf": [
          { "$ref": "#/$defs/type-impl" },
          { "$ref": "#/$defs/safe-need" }
        ]
      },
      "validate": {
        "network": {
          "implements": {
            "contains": {
              "local": {
                "allOf": [
                  { "$ref": "#/$defs/type-spec" },
                  { "$ref": "#/$defs/safe-need" }
                ]
              }
            },
            "minContains": 1
          }
        }
      }
    }
  ]
}

Further reading

For the complete reference including all validation options, error message formats, debug tooling, and migration guides, see the sphinx-needs schema validation documentation.