Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/content/deep-dives/domain-model/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The same evidence can power a future `doctor` / `explain` command: list the coll
## Variants route checks, not membership

A collection may run different checks on different items via
[variants]({{< relref "../../reference/configuration.md" >}}#variants), but that is
[variants]({{< relref "../../reference/configs/variants.md" >}}), but that is
a *check-engine* concern, not a base one. A variant's discriminator is a
predicate over an item's **metadata**: portable across every base type, since
each yields a metadata map (frontmatter for a file, columns for a row). It never
Expand Down Expand Up @@ -125,6 +125,6 @@ the definition's pattern are two views of the same thing.
- [Domain model]({{< relref "_index.md" >}}) for the cross-subsystem entity map.
- [Collections]({{< relref "collections.md" >}}) for the collection and item
hierarchy bases expose.
- [Configuration]({{< relref "../../reference/configuration.md" >}}) for the
- [Configs]({{< relref "../../reference/configs/_index.md" >}}) for the
precise `.katalyst/bases/` surface.
- `go doc ./internal/storage` for the code-level base contracts.
2 changes: 1 addition & 1 deletion docs/content/deep-dives/domain-model/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ item, the simplest correct path.
Per item, the engine resolves which checks apply, then runs them.

Resolution starts from the collection's configured checks and adds the checks of
the first [variant]({{< relref "../../reference/configuration.md" >}}) whose `when`
the first [variant]({{< relref "../../reference/configs/variants.md" >}}) whose `when`
predicates the item's metadata satisfies. The object schema is selected by a
precedence the JSON Schema library owns (a forced `--schema`, then an inline
`schema:` directive, then the collection's object checks); see the [domain
Expand Down
4 changes: 2 additions & 2 deletions docs/content/deep-dives/domain-model/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ registry validates a declared `type`, and a collection parses its own block in
`check` lifecycle is driven from here.

This page is the model and the *why*; for the key-by-key surface see the
[configuration reference]({{< relref "../../reference/configuration.md" >}}).
[configs reference]({{< relref "../../reference/configs/_index.md" >}}).

Collections are declared *inside* a [base]({{< relref "base.md" >}}), which owns
the base-to-collection mapping. This page covers the collection model and
Expand Down Expand Up @@ -120,7 +120,7 @@ until real usage shows the need. The base page frames the same decision as

## See also

- The [configuration reference]({{< relref "../../reference/configuration.md" >}})
- The [configs reference]({{< relref "../../reference/configs/_index.md" >}})
for the precise `.katalyst/` surface.
- The [base]({{< relref "base.md" >}}) for how a backend source maps onto
collections, and the base model.
Expand Down
2 changes: 1 addition & 1 deletion docs/content/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ declare a collection inside `.katalyst/bases/local.yaml`, then run
`katalyst check`.

Next:
- [Configuration]({{< relref "reference/configuration.md" >}})
- [Configs]({{< relref "reference/configs/_index.md" >}})
- [Check types reference]({{< relref "reference/check-types/_index.md" >}})
4 changes: 2 additions & 2 deletions docs/content/how-to/add-a-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ katalyst check books --schema ./schemas/strict-book.json
```

The precedence is `--schema` > inline `schema:` key > the collection's object
check. See the [configuration
reference]({{< relref "../reference/configuration.md" >}}) for the key surface,
check. See the [configs
reference]({{< relref "../reference/configs/_index.md" >}}) for the key surface,
or [Collections]({{< relref "../deep-dives/domain-model/collections.md" >}}) for why.

## See also
Expand Down
4 changes: 2 additions & 2 deletions docs/content/how-to/configure-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,5 @@ supported yet.

- [Add a schema]({{< relref "add-a-schema.md" >}}) to validate frontmatter
shape.
- [Configuration reference]({{< relref "../reference/configuration.md" >}})
for every key, including [`variants`]({{< relref "../reference/configuration.md" >}}#variants).
- [Configs reference]({{< relref "../reference/configs/_index.md" >}})
for every key, including [`variants`]({{< relref "../reference/configs/variants.md" >}}).
2 changes: 1 addition & 1 deletion docs/content/how-to/validate-in-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ step enforces canonical frontmatter without modifying files. See
## See also

- [Configure checks for a collection]({{< relref "configure-rules.md" >}})
- [Configuration reference]({{< relref "../reference/configuration.md" >}})
- [Configs reference]({{< relref "../reference/configs/_index.md" >}})
4 changes: 2 additions & 2 deletions docs/content/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Shared across the validating commands (`check`, `fix --check`):
## Filter predicates

The `--filter` flag of `katalyst item list` and the `when:` clause of a
[collection variant]({{< relref "configuration.md#variants" >}}) share one
[collection variant]({{< relref "configs/variants.md" >}}) share one
predicate language, so it is documented here once. A predicate is
`field OP value`; the operator is the first one found scanning left to right:

Expand All @@ -97,6 +97,6 @@ the listing pipeline (`--grep`, `--sort`, `--skip`/`--limit`) is documented in

## See also

- [Configuration reference]({{< relref "configuration.md" >}})
- [Configs reference]({{< relref "configs/_index.md" >}})
- [Check types reference]({{< relref "check-types/_index.md" >}})
- [Inspectors reference]({{< relref "inspectors/_index.md" >}})
49 changes: 49 additions & 0 deletions docs/content/reference/configs/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
+++
title = "Configs"
weight = 30
bookCollapseSection = true
aliases = ["/reference/configuration/"]
+++

# Configs

Katalyst reads a `.katalyst/` directory, found by walking upward from the
current working directory to the nearest ancestor that contains one. That
ancestor is the repo root; all relative paths resolve against it.

The config reference is organized by concept:

- [Discovery]({{< relref "discovery.md" >}}): root discovery, symlinks, file layout, formats, and legacy storage paths.
- [Schemas]({{< relref "schemas.md" >}}): schema file discovery, naming, and schema handles.
- [Bases]({{< relref "bases.md" >}}): backend stores and their top-level config keys.
- [Collections]({{< relref "collections.md" >}}): collection membership, identity, paths, and per-collection files.
- [Checks]({{< relref "checks.md" >}}): `schema:` shorthand, `checks:` entries, text rules, and object-schema precedence.
- [Variants]({{< relref "variants.md" >}}): conditional check routing with `when`.
- [Listing]({{< relref "listing.md" >}}): default behavior for `katalyst item list`.

For *why* the config is shaped this way, see [How collections
work]({{< relref "../../deep-dives/domain-model/collections.md" >}}). To set one up step by
step, see [Configure checks for a
collection]({{< relref "../../how-to/configure-rules.md" >}}).

## Layout

```
.katalyst/
config.yaml # optional: listing defaults and discovery settings
schemas/ # one JSON Schema file per named schema
book.json
bases/ # one file per base
local.yaml # a base + the collections it declares
local/ # optional: one file per collection (escape hatch)
books.yaml
```

## See also

- [Check types reference]({{< relref "../check-types/_index.md" >}}), every check type.
- [Bases]({{< relref "../../deep-dives/domain-model/base.md" >}}), the base /
collection-mapping model and its lineage.
- [Collections]({{< relref "../../deep-dives/domain-model/collections.md" >}}), the
config/collection model and rationale: schema resolution, variants,
unmatched-as-error.
58 changes: 58 additions & 0 deletions docs/content/reference/configs/bases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
+++
title = "Bases"
weight = 30
+++

# Bases

A **base** is one configured backend store, plus the collections it maps onto
the domain model. Each file under `.katalyst/bases/` is one base, named for its
filename stem. There is no implicit base; `katalyst init` writes a default
`local` one.

| Key | Required | Default | Meaning |
|---|---|---|---|
| `type` | no | `filesystem` | Backend kind: `filesystem` or `sqlite`. |
| `root` | no | `.` | Base root directory, relative to the repo root. Collection paths resolve against it. |
| `path` | for `sqlite` | - | SQLite database path, relative to the repo root. Alias for `root` on SQLite bases. |
| `collections` | no | - | Map of collection name -> definition. See [Collections]({{< relref "collections.md" >}}). |

```yaml
# .katalyst/bases/local.yaml
type: filesystem
root: .
collections:
books:
path: notes/books
schema: book
checks:
- kind: markdown_title_matches_h1
```

Collection names are unique across the whole project (selectors are
`<collection>/<item>`, with no base qualifier).

SQLite bases use one table per collection. Each row is one item:

```yaml
# .katalyst/bases/db.yaml
type: sqlite
path: content.sqlite
collections:
books:
table: books
id: slug
attributes:
title: title
status: status
author:
columns:
first: author_first
last: author_last
content:
kind: markdown
column: body
checks:
- kind: object_required_field
field: title
```
59 changes: 59 additions & 0 deletions docs/content/reference/configs/checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
+++
title = "Checks"
weight = 50
+++

# Checks

Each entry in a collection's `checks:` list has a `kind` and the keys that
check type requires. Every check type is documented one per page in the
[check types reference]({{< relref "../check-types/_index.md" >}}):

```yaml
checks:
- kind: object
schema: book
- kind: object_field_type
field: year
type: integer
- kind: markdown_title_matches_h1
- kind: filesystem_name_matches_field
```

## Text rules

The `text_*` check types lint the item **body** as raw text, independent of
markdown structure, and also apply to plain-text items (a `.txt` file or a
markdown file with no frontmatter). Each is evaluated against a set of **spans**
chosen by `target`:

| `target` | Spans |
|---|---|
| `body` (default) | the entire body as one multiline string |
| `line` | each body line |
| `first-line` | the first non-blank body line |
| `matched-lines` | each body line matching `select: <regex>` |

- `text_requires` and `text_forbids` take a Go `pattern`, matched **unanchored**
(it must appear *somewhere* in a span: unlike `filesystem_name_regex`, which
anchors with `^...$`). `text_requires` also takes `match: any` (default, at
least one span matches) or `match: all` (every span must match).
- `text_denylist` takes `values:`, a list of literal substrings; regex
metacharacters are inert.
- `text_forbids` may declare a `fix:`: a replacement template (`$1`, `${name}`
capture syntax) applied to the matched text by `katalyst fix`. The fix
re-checks its own work and fails rather than writing a file the rule would
still reject. `text_requires` and `text_denylist` are report-only.

## Object-schema resolution precedence

When an item is checked against an object schema, the schema is chosen
highest-precedence first:

1. `--schema <path>` flag (applies to every selected item).
2. Inline `schema: <name>` key in the item's frontmatter.
3. The collection's `object` check (from `schema:` or an explicit entry), plus
the matched [variant]({{< relref "variants.md" >}})'s schema: both apply, additively.

Markdown and filesystem checks always come from the collection (and the matched
variant), even when `--schema` is used.
57 changes: 57 additions & 0 deletions docs/content/reference/configs/collections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
+++
title = "Collections"
weight = 40
+++

# Collections

A **collection** is a directory of items plus the checks every item must pass.
Collections are declared inside their base, under `collections:`.

| Key | Required | Default | Meaning |
|---|---|---|---|
| `path` | no | the collection name | Directory, relative to the base `root`. |
| `pattern` | no | `*.md` | Filename glob selecting items in the directory. |
| `table` | for `sqlite` | - | SQLite table backing the collection. |
| `id` | for `sqlite` | - | SQLite column that provides item identity. |
| `attributes` | no | all scalar columns except `id` and `content.column` | SQLite column captures exposed as item attributes. |
| `content` | no | - | Optional SQLite content mapping, with `kind: text` or `kind: markdown` and `column: <name>`. |
| `schema` | no | - | Schema name; shorthand for a leading `object` check. |
| `checks` | no | - | List of checks. See [Checks]({{< relref "checks.md" >}}). |
| `listing` | no | - | `item list` listing defaults for this collection. See [Listing]({{< relref "listing.md" >}}). |

A collection must configure at least one check: set `schema`, or provide a
non-empty `checks` list, or both. Files in the directory that do not match
`pattern` are reported as errors.

SQLite collections do not support filesystem check types. Configure
structured-object checks against captured attributes. Text and markdown
body-text checks require a compatible `content` mapping.

`attributes` accepts shorthand single-column captures and structured
multi-column captures:

```yaml
attributes:
title: title
author:
columns:
first: author_first
last: author_last
```

The structured form above exposes `author.first` and `author.last` as fields
inside the `author` attribute object.

## Per-collection files

A base whose `collections:` block grows unwieldy may split collections into
one file each under `.katalyst/bases/<base>/<collection>.yaml`, named for
its filename stem. Inline and per-file collections coexist; a name declared both
inline and in a file is an error.

```yaml
# .katalyst/bases/local/books.yaml
path: notes/books
schema: book
```
42 changes: 42 additions & 0 deletions docs/content/reference/configs/discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
+++
title = "Discovery"
weight = 10
+++

# Discovery

Katalyst reads a `.katalyst/` directory, found by walking upward from the
current working directory to the nearest ancestor that contains one. That
ancestor is the repo root; all relative paths resolve against it.

Discovery resolves symlinks on both the root and the input path, because on
macOS `$TMPDIR` lives behind `/var` to `/private/var` and relative-path
resolution would otherwise produce garbage.

## Layout

```
.katalyst/
config.yaml # optional: listing defaults and discovery settings
schemas/ # one JSON Schema file per named schema
book.json
bases/ # one file per base
local.yaml # a base + the collections it declares
local/ # optional: one file per collection (escape hatch)
books.yaml
```

By default, schemas and bases are discovered by **convention**:
every file under `schemas/` is a schema whose name is its filename stem
(`book.json` -> `book`), and every file under `bases/` is a
[base]({{< relref "bases.md" >}}) named for its filename stem (`local.yaml` -> `local`).
`config.yaml` is optional; it carries [listing]({{< relref "listing.md" >}}) defaults and can
switch a kind to **explicit** discovery, listing definitions inline instead of
as files.

`config.yaml` is YAML; schema and base files default to YAML/JSON, and the
accepted format is set per kind there.

Legacy projects that still use `storage:` in `config.yaml` or
`.katalyst/storage/` continue to load. Do not mix legacy and new forms in the
same project; move legacy base files to `.katalyst/bases/` when you edit them.
36 changes: 36 additions & 0 deletions docs/content/reference/configs/listing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
+++
title = "Listing"
weight = 70
+++

# Listing

Two `item list` behaviors have configurable defaults. A `listing:` block sets
them project-wide in `.katalyst/config.yaml`, and a collection's file can
override either key for that collection.

| Key | Values | Default | Meaning |
|---|---|---|---|
| `filterTypeMismatch` | `skip` / `error` | `skip` | A `--filter` comparison against an incompatible type either skips the item or exits 2. |
| `sortMissing` | `last` / `lowest` | `last` | Where items lacking the `--sort` key land: at the end (both directions), or below any present value. |

```yaml
# .katalyst/config.yaml - project default
listing:
filterTypeMismatch: skip
sortMissing: last
```

```yaml
# under a base's collections: override for one collection
books:
path: notes/books
schema: book
listing:
filterTypeMismatch: error
```

Resolution is highest-precedence first: the `--on-type-mismatch` /
`--sort-missing` flags, then the collection's `listing:`, then the project
`listing:`, then the built-in default. An unset key falls through to the next
level.
Loading
Loading