Skip to content

Comments

feat: PRSDM-9647 Runtime Frontmatter Validation System#72

Closed
DustinFischer wants to merge 24 commits intomainfrom
feat/PRSDM-9647-validation-logic
Closed

feat: PRSDM-9647 Runtime Frontmatter Validation System#72
DustinFischer wants to merge 24 commits intomainfrom
feat/PRSDM-9647-validation-logic

Conversation

@DustinFischer
Copy link
Contributor

@DustinFischer DustinFischer commented Dec 5, 2025

Description

Implements PRSDM-9647: Runtime Frontmatter Validation System - provides build-time validation of Hugo page frontmatter against a JSON schema, generating warnings for invalid configurations.

Related Work:

  • PRSDM-9508 Phase 1 (branch: feat/PRSDM-9508-phase1-json-schema-generator) - DEPENDENCY: Schema generator must be merged first
  • Future: Boolean and Datetime validators (skipped for MVP, can be added incrementally)

Key Features:

  • Root Validator Pattern for lifecycle management
  • 8 validators (4 types + 4 formats) with comprehensive constraint checking
  • Build integration via single.html
  • Non-blocking warnings (uses warnf not errorf)
  • Configurable schema location via params.data.namespace
  • .Scratch pattern for reliable data passing between partials

Architecture:

single.html (every page build)
    ↓
validate.html (orchestrator)
    ↓ Loads schema from data/presidium/frontmatter-schema.json
    ↓ Iterates configured frontmatter fields
    ↓
validators/root.html (lifecycle)
    ↓ Checks required/null
    ↓ Routes to type/format validator
    ↓
Type/Format Validators (business logic)
    ↓ Validate constraints
    ↓ Store results in .Scratch
    ↓
Root aggregates errors → Hugo warnf

Issue

Dependencies

  • PRSDM-9508 Phase 1 (branch: feat/PRSDM-9508-phase1-json-schema-generator) - Schema generator must be merged first as this PR consumes data/presidium/frontmatter-schema.json

Testing Instructions

Prerequisites

  • Hugo 0.147.7+ installed
  • A Hugo project with presidium-layouts-base module imported
  • PRSDM-9508 Phase 1 merged (schema generator available)

Step 1: Generate Schema

First, generate the frontmatter schema following PRSDM-9508 Phase 1 instructions.

Create config-gen.yaml:

module:
  mounts:
    - excludeFiles: "**"
      source: content
      target: content
    - excludeFiles: "**"
      source: static
      target: static
    - excludeFiles: "**"
      source: assets
      target: assets
  imports:
    - path: github.com/spandigital/presidium-styling-base 
      noMounts: true
    - path: github.com/spandigital/presidium-layouts-base
      mounts:
        - source: layouts
          target: layouts
        - source: data
          target: data

disableKinds:
  - "page"
  - "section"
  - "taxonomy"
  - "term"
  - "RSS"
  - "sitemap"
  - "robotsTXT"
  - "404"

outputs:
  home:
    - frontmatterSchema

Generate schema:

rm -rf data/presidium
hugo --cleanDestinationDir --config=config.yml,config-gen.yaml --destination data/presidium

Verify schema exists:

ls -la data/presidium/frontmatter-schema.json

Step 2: Create Test Configuration

Create config-test.yaml:

module:
  mounts:
    - excludeFiles: "**README.md"
      source: content
      target: content

Note: This is a temporary testing mechanism. In the future, validation configuration will be directly integrated into Hugo projects. For now, this configuration mounts your test content directory. Update the source path if your test files are in a subdirectory like content/validation-tests/test-<validator>.

Step 3: Configure Frontmatter Validation Rules

Add frontmatter configuration to config.yml:

params:
  frontmatter:
    title:
      type: string
      required: true
      min_length: 5
      max_length: 100
      validation_message: "Title must be between 5-100 characters"
    
    author_email:
      type: email
      required: true
      max_length: 100
      validation_message: "A valid author email is required"
    
    status:
      type: enum
      required: true
      items:
        - draft
        - published
        - archived
      validation_message: "Status must be draft, published, or archived"
    
    tags:
      type: array
      required: false
      min_items: 1
      max_items: 5

Step 4: Create Test Content

Create test markdown files with valid and invalid frontmatter:

Valid (content/test-valid.md):

---
title: "Valid Test Article"
author_email: "author@example.com"
status: "published"
tags: ["hugo", "validation"]
---

This should build without warnings.

Invalid - Missing Required Field (content/test-missing-required.md):

---
title: "Test Article"
status: "published"
---

Missing required author_email field.

Invalid - Constraint Violation (content/test-too-short.md):

---
title: "Test"
author_email: "author@example.com"
status: "published"
---

Title is too short (min: 5 characters).

Invalid - Wrong Type (content/test-invalid-email.md):

---
title: "Test Article"
author_email: "not-an-email"
status: "published"
---

Invalid email pattern.

Invalid - Enum Value (content/test-invalid-status.md):

---
title: "Test Article"
author_email: "author@example.com"
status: "invalid-status"
---

Status not in allowed items list.

Step 5: Run Hugo Build

Build your site and check for validation warnings:

hugo --renderToMemory --cleanDestinationDir --config=config.yml,config-test.yaml

Step 6: Verify Validation Output

Expected Warnings for Invalid Files:

WARN  [Frontmatter Validation] test-missing-required.md: author_email is required
WARN  [Frontmatter Validation] test-too-short.md: Title must be between 5-100 characters
WARN  [Frontmatter Validation] test-invalid-email.md: A valid author email is required
WARN  [Frontmatter Validation] test-invalid-status.md: Status must be draft, published, or archived

No Warnings for Valid File:

  • test-valid.md should build without warnings

Step 7: Test Optional Fields

Create content with optional fields:

Optional Present (content/test-optional-present.md):

---
title: "Test Article"
author_email: "author@example.com"
status: "published"
tags: ["hugo", "test", "validation"]
---

Optional tags field included - should validate array constraints.

Optional Missing (content/test-optional-missing.md):

---
title: "Test Article"
author_email: "author@example.com"
status: "published"
---

Optional tags field omitted - should build without warnings.

Both files should build successfully (no warnings).

Step 8: Test Constraint Violations

Array Too Many Items (content/test-array-too-many.md):

---
title: "Test Article"
author_email: "author@example.com"
status: "published"
tags: ["tag1", "tag2", "tag3", "tag4", "tag5", "tag6"]
---

Too many tags (max: 5).

Expected: WARN [Frontmatter Validation] test-array-too-many.md: tags must have no more than 5 items

Technical Notes

Root Validator Pattern

The validation system uses a centralized root validator that handles:

  1. Field existence checking (isset for proper handling of falsy values like empty arrays)
  2. Required field validation
  3. Routing to appropriate type/format validator
  4. Error message formatting with validation_message override

Benefits:

  • Single source of truth for lifecycle management
  • Type validators contain only business logic
  • Automatic feature inheritance for new validators

.Scratch Pattern

All data passing between partials uses Hugo's .Scratch to avoid template.HTML conversion:

{{/* Caller */}}
{{- partial "validator.html" (dict "value" $value "rules" $rules "fieldName" $fieldName "context" $ctx) -}}
{{- $result := $ctx.Scratch.Get "type_validation_result" -}}

{{/* Validator */}}
{{- $ctx := .context -}}
{{- $result := dict "valid" true "errors" slice -}}
{{- $ctx.Scratch.Set "type_validation_result" $result -}}

Format Validator Inheritance

Format validators delegate to base type validators:

  • emailstring (adds email pattern)
  • datestring (adds YYYY-MM-DD pattern)
  • textstring (multiline UI hint)
  • optionenum (dropdown UI hint)

All type constraints automatically inherited.

Configurable Schema Location

params:
  data:
    namespace: "presidium"  # Default: loads data/presidium/frontmatter-schema.json

Documentation

  • FRONTMATTER.md - Complete developer reference for validation system
  • README.md - Added reference to FRONTMATTER.md
  • .claude/artifacts/prsdm-9647/implementation-progress.md - Detailed implementation tracker

PR Readiness Checks

  • Your PR title conforms to conventional commits <type>: <jira-ticket-num><title>, for example: feat: PRSDM-9647 Runtime frontmatter validation
  • You have performed a self-review of your changes via the GitHub UI
  • Comments were added to new code that can not explain itself
  • New code adheres to the following quality standards:
    • Function Length - All validators <50 lines, focused single responsibility
    • Meaningful Names - Clear, descriptive naming throughout
    • DRY - Root validator eliminates duplication across validators
    • YAGNI - Only implemented validators needed for MVP

@DustinFischer DustinFischer self-assigned this Dec 5, 2025
@DustinFischer DustinFischer marked this pull request as ready for review December 5, 2025 01:17
@DustinFischer DustinFischer requested a review from a team as a code owner December 5, 2025 01:17
@DustinFischer DustinFischer requested review from NicholasDunham and christopherbrunsdon and removed request for a team December 5, 2025 01:17
@DustinFischer DustinFischer marked this pull request as draft January 21, 2026 09:38
@DustinFischer DustinFischer changed the base branch from feat/PRSDM-9508-phase1-json-schema-generator to main January 21, 2026 14:29
DustinFischer added a commit that referenced this pull request Jan 23, 2026
Initial implementation attempts for JSON schema generation (PR #71)
and runtime validation system (PR #72). These approaches were
superseded by the flat YAML schema architecture implemented in
subsequent commits.

This commit consolidates 29 commits from the exploratory phase
including:
- JSON schema generation (replaced with YAML)
- Inheritance-based schema model (replaced with flat model)
- Initial validation system implementation
- Multiple merge commits and experimental work

The superseded work established foundational concepts that informed
the final flat schema architecture.

Related: PRSDM-9508, PRSDM-9647
Supersedes: PR #71, PR #72
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant