| type | chapter | ||
|---|---|---|---|
| id | 09-validation | ||
| title | Validation | ||
| description | Validation levels, field validation rules, and error reporting | ||
| section | 9 | ||
| conformance_levels |
|
||
| test_categories |
|
||
| depends_on |
|
Validation ensures that files conform to their type schemas. This section defines what is validated, when validation occurs, and how errors are reported.
Implementations MUST support three validation levels:
| Level | Behavior |
|---|---|
off |
No validation performed |
warn |
Validation runs; issues are reported but operations succeed |
error |
Validation runs; issues cause operations to fail |
The default level is configured via settings.default_validation (default: "warn").
At warn level, operations MUST return success and include issues as warnings. For operation outputs that include a valid flag, valid MUST be true even when warnings are present.
Operations MAY override the default level:
# Force error-level validation
mdbase validate --level error
# Create with no validation
mdbase create --no-validateFor each typed file, validation checks the following:
Fields marked required: true MUST be:
- Present in the effective frontmatter (defaults applied; computed fields excluded)
- Non-null (value is not
null)
Note: exists(field) checks for a present key in raw persisted frontmatter even if its value is null. Required fields must be present in the effective frontmatter and non-null.
# Type definition
fields:
title:
type: string
required: true
# Valid
title: "My Document"
# Invalid: missing
# (no title key)
# Invalid: null
title: null
title:Values MUST match their declared type (or be coercible):
# Type definition
fields:
priority:
type: integer
# Valid
priority: 5
priority: "5" # Coerced to integer
# Invalid
priority: "high"
priority: 5.5Type-specific constraints MUST be satisfied:
| Type | Constraints |
|---|---|
string |
min_length, max_length, pattern |
integer, number |
min, max |
list |
min_items, max_items, unique |
enum |
values |
link |
validate_exists |
When a type has strict: true, unknown fields cause validation failure:
# Type definition: strict: true
fields:
title:
type: string
# Valid
title: "Doc"
# Invalid: unknown field
title: "Doc"
extra_field: "not allowed"With strict: "warn", unknown fields trigger warnings but pass validation.
Implicit fields: The following frontmatter keys are always implicitly allowed, even in strict mode:
type/types— type declaration keys (configurable viasettings.explicit_type_keys)- Any keys listed in
settings.explicit_type_keys
These keys are structural and do not need to be declared in the type's fields definition.
When a file matches multiple types, it MUST validate against ALL of them:
# File matches both 'task' and 'urgent' types
# Must satisfy:
# - All required fields from 'task'
# - All constraints from 'task'
# - All required fields from 'urgent'
# - All constraints from 'urgent'For link fields with validate_exists: true, the target file MUST exist:
# Type definition
fields:
parent:
type: link
validate_exists: true
# Valid (if file exists)
parent: "[[existing-task]]"
# Invalid (file doesn't exist)
parent: "[[nonexistent]]"If a type defines path_pattern (or the filename_pattern alias), paths MAY be validated:
# Type definition
path_pattern: "{id}.md"
# File: task-001.md with id: "task-001" → valid
# File: random-name.md with id: "task-001" → warning (mismatch)Path pattern validation is RECOMMENDED but not strictly required.
If settings.id_field is configured (default: id), values of that field MUST be
unique across the collection. If duplicates exist, validation MUST emit a
duplicate_id issue for each file that shares the duplicated value.
Each validation issue MUST include:
| Field | Type | Description |
|---|---|---|
path |
string | File path relative to collection root |
field |
string | Field path (e.g., author.email, tags[0]) |
code |
string | Error code (see Appendix C) |
message |
string | Human-readable error description |
severity |
enum | error or warning |
Optional fields:
| Field | Type | Description |
|---|---|---|
expected |
any | Expected value or type |
actual |
any | Actual value found |
type |
string | Type name that triggered the issue |
line |
integer | 1-based line number in the source file |
column |
integer | 1-based column number |
end_line |
integer | End line of the issue range |
end_column |
integer | End column of the issue range |
Implementations SHOULD include line and column fields when source position information is available. These fields enable LSP-style diagnostics and precise issue reporting in CI tooling.
{
"path": "tasks/fix-bug.md",
"field": "priority",
"code": "constraint_violation",
"message": "Value 7 exceeds maximum of 5",
"severity": "error",
"expected": { "max": 5 },
"actual": 7,
"type": "task",
"line": 5,
"column": 11,
"end_line": 5,
"end_column": 12
}Implementations MAY validate at different times:
| When | Description |
|---|---|
| On read | Validate when loading a file |
| On write | Validate before creating or updating |
| On demand | Validate via explicit command |
| Continuous | Watch mode; validate on file changes |
The specification does not mandate when validation occurs, only the behavior when it does.
- Create/Update operations: Validate before writing; fail if
validation: error - Read/Query operations: Optionally validate; report issues but don't fail
- Explicit validate command: Full collection validation with detailed report
Implementations SHOULD provide explicit validation commands:
# Validate entire collection
mdbase validate
# Validate specific files
mdbase validate tasks/fix-bug.md notes/meeting.md
# Validate files of a specific type
mdbase validate --type task
# Validate with specific level
mdbase validate --level error
# Output validation report as JSON
mdbase validate --format jsonFor large collections, implementations MAY support partial validation:
- Validate only modified files (since last validation)
- Validate only files in specific folders
- Validate only files matching certain types
This is an optimization; full validation MUST remain available.
Human-readable format:
Validation Report
================
Errors: 3
Warnings: 5
tasks/fix-bug.md
ERROR [missing_required] Field 'title' is required but missing
ERROR [type_mismatch] Field 'priority': expected integer, got string "high"
WARNING [unknown_field] Field 'custom' is not defined in type 'task'
notes/meeting.md
ERROR [constraint_violation] Field 'attendees': minimum 1 item required, got 0
WARNING [deprecated_field] Field 'old_field' is deprecated
tasks/subtask.md
WARNING [link_not_found] Field 'parent': target '[[nonexistent]]' not found
JSON format:
{
"summary": {
"files_checked": 42,
"files_valid": 39,
"files_invalid": 3,
"errors": 3,
"warnings": 5
},
"issues": [
{
"path": "tasks/fix-bug.md",
"field": "title",
"code": "missing_required",
"message": "Field 'title' is required but missing",
"severity": "error",
"type": "task"
}
]
}Implementations MAY support automatic fixing of certain issues:
| Issue | Auto-Fix |
|---|---|
| Missing field with default | Apply default value |
| Type coercion possible | Coerce value |
| Missing generated field | Generate value |
Auto-fix MUST NOT:
- Delete user data
- Make changes that could lose information
- Fix issues where the correct resolution is ambiguous
# Preview fixes
mdbase validate --fix --dry-run
# Apply fixes
mdbase validate --fixWhen a file matches multiple types, validation follows these rules:
- All types validated: The file must pass validation for ALL matched types
- Issues attributed: Each issue includes which type triggered it
- Conflict detection: If types have incompatible field definitions, report as error
Example conflict:
# Type 'a' defines: status as string
# Type 'b' defines: status as enum [open, closed]
# File matches both types
# File has: status: "pending"
# Result:
# - Passes type 'a' validation (valid string)
# - Fails type 'b' validation ("pending" not in enum)
# - Overall: FAIL (must pass all types)Certain scenarios may warrant skipping validation:
- Migration: Importing data that doesn't yet conform
- Bulk operations: Performance-critical batch updates
- Emergency fixes: Bypassing validation to fix broken state
Implementations SHOULD support:
# Skip validation on create
mdbase create --no-validate task.md
# Skip validation on update
mdbase update --no-validate task.mdSkipping validation SHOULD be logged for audit purposes.