# OKF validation reference

> `OKFValidator` rules: reserved files (`index.md`, `log.md`), frontmatter presence and YAML shape, required `type` field, `validate_file`/`quick_check` helpers, and `hermes-okf validate` error output format.

- Repository: EliaszDev/hermes-okf
- GitHub: https://github.com/EliaszDev/hermes-okf
- Human docs: https://grok-wiki.com/public/docs/eliaszdev-hermes-okf-b71befaafe02
- Complete Markdown: https://grok-wiki.com/public/docs/eliaszdev-hermes-okf-b71befaafe02/llms-full.txt

## Source Files

- `src/hermes_okf/validators.py`
- `src/hermes_okf/cli.py`
- `docs/ARCHITECTURE.md`
- `tests/test_validators.py`
- `src/hermes_okf/bundle.py`

---

---
title: OKF validation reference
description: OKFValidator rules for reserved files, YAML frontmatter shape, the required type field, validate_file and quick_check helpers, and hermes-okf validate CLI output.
---

Hermes OKF enforces a **minimal OKF v0.1 conformance profile**: every concept markdown file must have parseable YAML frontmatter containing a `type` field, and the bundle root must contain `index.md` and `log.md`. Validation is intentionally narrow — additional frontmatter keys, body content, and directory layout are not checked. Use `OKFValidator` programmatically or `hermes-okf validate` from the terminal.

`OKFBundle.write_concept()` does **not** enforce the `type` requirement at write time; conformance is a separate check you run after edits or in CI.

## Validation scope

`OKFValidator` performs two passes on a bundle:

| Pass | What is checked | What is skipped |
|------|-----------------|-----------------|
| Reserved files | `index.md` and `log.md` must exist at the **bundle root** | Frontmatter on any `index.md` or `log.md` anywhere in the tree |
| Concept files | Every other `*.md` file under the bundle root (recursive) | Files named `index.md` or `log.md` at any depth |

The validator does **not** inspect:

- Whether `type` values match a fixed taxonomy (types are user-defined)
- Optional frontmatter fields (`title`, `tags`, `timestamp`, and so on)
- Markdown body syntax or link targets
- Hermes-native subdirectories (`config/`, `sessions/`, `plans/`, and similar) beyond the generic markdown rules above

For bundle layout and concept field semantics, see the [OKF bundle model](/okf-bundle-model) page.

## Reserved files

<ParamField body="REQUIRED_RESERVED_FILES" type="tuple[str, str]">
  Class constant on `OKFValidator`: `("index.md", "log.md")`.
</ParamField>

**Existence rule.** Both files must be present directly under the bundle root. If either is missing, validation records:

```
Missing required reserved file: index.md
```

(or the equivalent message for `log.md`).

**Frontmatter exemption.** Any markdown file whose basename is `index.md` or `log.md` — including subdirectory stubs like `projects/index.md` — is excluded from frontmatter and `type` checks. This is why a freshly initialized bundle passes validation even though root `log.md` has no YAML frontmatter and root `index.md` carries `okf_version` but no `type`.

`OKFBundle._init_bundle()` creates conformant starter content: root `index.md` with `okf_version: "0.1"`, plain `log.md`, and seeded subdirectory `index.md` files with `type: Directory`.

## Concept frontmatter rules

For every non-reserved `*.md` file, `OKFValidator._check_file()` applies these rules in order. The first failure stops further checks on that file.

### 1. Opening delimiter

Content must start with `---`.

<ResponseField name="error" type="string">
  `Missing YAML frontmatter (must start with ---)`
</ResponseField>

### 2. Closing delimiter

The file is split on `---` with a maximum of two splits (`content.split("---", 2)`). The result must have at least three segments: empty prefix, YAML block, body.

<ResponseField name="error" type="string">
  `Malformed frontmatter: missing closing ---`
</ResponseField>

### 3. Valid YAML

The block between delimiters is parsed with `yaml.safe_load()`. Parse failures produce:

<ResponseField name="error" type="string">
  `Invalid YAML frontmatter: {exc}`
</ResponseField>

where `{exc}` is the PyYAML error string.

### 4. Mapping type

Frontmatter must deserialize to a YAML mapping (Python `dict`). A scalar or list top-level value fails with:

<ResponseField name="error" type="string">
  `Frontmatter must be a YAML mapping (dict)`
</ResponseField>

### 5. Required type field

The mapping must contain a `type` key. Absence fails with:

<ResponseField name="error" type="string">
  `Missing required 'type' field in frontmatter`
</ResponseField>

The error includes `line=1` in bundle-level validation (frontmatter starts at line 1).

No other frontmatter keys are required. A minimal valid concept:

```markdown
---
type: Project
---

# My project
```

## OKFValidationError

Each failure is an `OKFValidationError` with three attributes:

<ParamField body="file" type="string" required>
  Relative path from the bundle root during full validation (e.g. `projects/my_app.md`), or the path string passed to `validate_file()` (often absolute).
</ParamField>

<ParamField body="message" type="string" required>
  Human-readable failure description.
</ParamField>

<ParamField body="line" type="int | None">
  Optional line number. Set to `1` for missing `type` in bundle validation; omitted for most other errors.
</ParamField>

`repr()` formats errors for CLI output:

| Condition | Format |
|-----------|--------|
| `line` is set | `OKFValidationError({file}:{line} -> {message})` |
| `line` is `None` | `OKFValidationError({file} -> {message})` |

Access fields directly when building custom reporting:

```python
from hermes_okf.bundle import OKFBundle
from hermes_okf.validators import OKFValidator

bundle = OKFBundle("./knowledge")
validator = OKFValidator(bundle)
errors = validator.validate()

for err in errors:
    print(f"{err.file}: {err.message}")
```

## OKFValidator instance API

Construct with an `OKFBundle`:

```python
from hermes_okf.bundle import OKFBundle
from hermes_okf.validators import OKFValidator

validator = OKFValidator(OKFBundle("/path/to/bundle"))
```

<ResponseField name="validate()" type="list[OKFValidationError]">
  Runs `_check_reserved_files()` then `_check_all_concepts()`, stores results in `validator.errors`, and returns the list. Replaces any prior errors.
</ResponseField>

<ResponseField name="is_valid()" type="bool">
  Calls `validate()` and returns `True` when the error list is empty.
</ResponseField>

<ResponseField name="errors" type="list[OKFValidationError]">
  Populated after the most recent `validate()` call.
</ResponseField>

`OKFValidator` is exported from the public `hermes_okf` package alongside `OKFBundle`.

## Static helpers

Use these when validating a single file outside full bundle context — for example, a pre-commit hook on one edited concept.

### validate_file

```python
errors = OKFValidator.validate_file("projects/my_project.md")
```

<ParamField body="path" type="str | Path" required>
  Path to a single `.md` file.
</ParamField>

<ResponseField name="returns" type="list[OKFValidationError]">
  Zero or more errors. Does not check reserved-file presence or scan sibling files.
</ResponseField>

`validate_file()` applies the same frontmatter and `type` rules but uses **shorter error messages**:

| Full bundle check | `validate_file()` equivalent |
|-------------------|------------------------------|
| `Missing YAML frontmatter (must start with ---)` | `Missing YAML frontmatter` |
| `Malformed frontmatter: missing closing ---` | `Malformed frontmatter` |
| `Invalid YAML frontmatter: {exc}` | `Invalid YAML: {exc}` |
| `Frontmatter must be a YAML mapping (dict)` | *(combined)* `Missing required 'type' field` |
| `Missing required 'type' field in frontmatter` | `Missing required 'type' field` |

Additional `validate_file()`-only checks:

| Condition | Message |
|-----------|---------|
| Path does not exist | `File does not exist` |
| Frontmatter is valid YAML but not a dict, or dict lacks `type` | `Missing required 'type' field` |

### quick_check

```python
ok = OKFValidator.quick_check("projects/my_project.md")
```

Returns `True` when `validate_file()` returns an empty list. Useful for guard clauses:

```python
if not OKFValidator.quick_check(path):
    raise ValueError(f"Non-conformant OKF file: {path}")
```

## hermes-okf validate

The standalone CLI subcommand wraps `OKFValidator` for terminal and CI use.

```bash
hermes-okf validate
hermes-okf validate --path ./knowledge
hermes-okf validate --path ~/.hermes/okf_memory
```

<ParamField body="--path" type="string" default=".">
  Bundle root directory. Inherited from the shared path parent parser; may appear after the subcommand name.
</ParamField>

### Success output

<RequestExample>
```bash
hermes-okf validate --path ./knowledge
```
</RequestExample>

<ResponseExample>
```
Bundle is valid.
```
</ResponseExample>

Exit code: **0**.

### Error output

When one or more errors exist, the CLI prints a summary line followed by one bullet per error, using each error's `repr()`:

<RequestExample>
```bash
hermes-okf validate --path ./knowledge
```
</RequestExample>

<ResponseExample>
```
Found 2 validation error(s):
  - OKFValidationError(bad.md:1 -> Missing required 'type' field in frontmatter)
  - OKFValidationError(log.md -> Missing required reserved file: log.md)
```
</ResponseExample>

Exit code: **1**.

Multiple concept failures are all listed in a single run; the validator does not stop at the first error.

### Interaction with OKFBundle initialization

`hermes-okf validate` constructs `OKFBundle(args.path)` before validating. If the path does not exist, `OKFBundle` creates the directory and runs `_init_bundle()`, then validation typically succeeds on the freshly seeded bundle. Point `--path` at an existing bundle when checking real content. See [Quickstart](/quickstart) for the expected first-run workflow.

## Write-time vs validate-time enforcement

| Operation | Enforces `type`? | Notes |
|-----------|------------------|-------|
| `OKFBundle.write_concept()` | No | Docstring directs callers to `OKFValidator` |
| `OKFBundle.read_concept()` | No | Missing `type` parses as `type="Unknown"` |
| `OKFValidator.validate()` | Yes | Authoritative conformance check |
| `hermes-okf validate` | Yes | CLI wrapper around `OKFValidator` |

Run validation after manual edits, scripted imports, or agent writes that bypass `write_concept()`.

## Common failure modes

<AccordionGroup>
<Accordion title="Concept missing type after manual edit">
A file with frontmatter but no `type` key fails bundle validation:

```
OKFValidationError(my_concept.md:1 -> Missing required 'type' field in frontmatter)
```

Add `type` to the YAML block or rewrite via `write_concept(..., type="YourType")`.
</Accordion>

<Accordion title="No frontmatter at all">
Plain markdown without leading `---` fails with:

```
OKFValidationError(notes.md -> Missing YAML frontmatter (must start with ---))
```
</Accordion>

<Accordion title="Malformed YAML delimiter">
A single `---` or unclosed frontmatter block fails with:

```
OKFValidationError(broken.md -> Malformed frontmatter: missing closing ---)
```
</Accordion>

<Accordion title="Invalid YAML syntax">
PyYAML parse errors surface the parser message:

```
OKFValidationError(broken.md -> Invalid YAML frontmatter: while parsing a flow sequence ...
```
</Accordion>

<Accordion title="Missing root reserved file">
Deleting root `log.md` or `index.md` fails even when all concepts are valid:

```
OKFValidationError(log.md -> Missing required reserved file: log.md)
```

Re-run `hermes-okf init` on an empty directory or restore the file manually.
</Accordion>
</AccordionGroup>

## Extending validation

`OKFValidator` is hardcoded to the minimal OKF v0.1 profile. To add project-specific rules — mandatory `title`, allowed `type` values, link integrity — subclass `OKFValidator` and override `_check_file()` or add post-pass hooks. The architecture docs describe this as the supported extension point for custom conformance policies.

## Related pages

<Card href="/okf-bundle-model" title="OKF bundle model">
  Bundle layout, `Concept` fields, reserved directories, and how `write_concept()` formats frontmatter.
</Card>

<Card href="/standalone-cli-reference" title="Standalone CLI reference">
  Full `hermes-okf` subcommand list, global flags, and exit codes.
</Card>

<Card href="/standalone-cli-workflows" title="Standalone CLI workflows">
  When to run `validate` in day-to-day bundle operations and CI.
</Card>

<Card href="/quickstart" title="Quickstart">
  First successful `hermes-okf init` → write concept → `hermes-okf validate` flow.
</Card>

<Card href="/python-sdk-reference" title="Python SDK reference">
  Public exports including `OKFValidator` and `OKFBundle`.
</Card>

<Card href="/troubleshooting" title="Troubleshooting">
  Recovery when bundles are missing, paths are wrong, or plugin install issues mask validation targets.
</Card>
