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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
run: python -m pip install PyYAML==6.0.3 jsonschema==4.26.0
- name: Legacy conformance level guardrail
run: python scripts/check_test_levels.py
- name: Validate v0.3 fixture manifest and coverage
- name: Validate v0.3 fixture manifest, coverage, and executable fixtures
run: python scripts/check_v03_tests.py
- name: Verify migration fixture
run: python scripts/prototype_tasknotes_v03_migration.py --check-fixture
Expand Down
39 changes: 36 additions & 3 deletions 00-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ dependencies.
**Files are the source of truth.** Tools read from and write to the filesystem.
Indexes, caches, and derived databases can be rebuilt from collection state.

**Plain Markdown.** A collection never requires mdbase metadata in a user's
files. Record identity, revisions, merge state, and other engine bookkeeping
live outside records, so a file written by any editor is a complete record.

**Validity is reported, not guaranteed.** Files are edited by tools that know
nothing about types. A conforming tool reads, indexes, and reports every record
whatever its validity. Engines may reject an invalid write made through them,
as feedback to the writer, but only for checks within that single record.
Chapter 04 defines the principle and its three tiers.

**Human-readable first.** Persistent collection data uses open text formats. A
user with a text editor can read and modify every record and type file.

Expand Down Expand Up @@ -243,13 +253,28 @@ projections, ordering, grouping, and summaries remain machine-readable, while
the Markdown body documents the view for people. Optional presentation metadata
can select a renderer without changing query results.

### Validation is progressive
### Validation is progressive and reported

Files in a collection can remain untyped records. Types can be added
incrementally, and validation severity is configurable as `off`, `warn`, or
`error`. JSON Schema controls field shape and unknown-property handling.
Collection rules add checks that depend on other records or paths.

Validity is a property reported when records are read. At level `error`, a
write made through an engine fails when the resulting record breaks one of
its own checks. Checks that span records, such as link existence and
uniqueness, are reported and never block a write, unless a uniqueness rule
explicitly opts into `enforce: write`.

### Concurrent edits merge field by field

When two edits to one record meet, for example an application update and an
edit made in a text editor, a tool that reconciles them uses the three-way
record merge of Chapter 12A. Different fields merge, timestamps take the
later value, set-like lists take the union, appends to the body are both kept,
and only real disagreements are conflicts. How a tool surfaces a conflict, and
whether it replicates collections at all, is outside this specification.

### Links connect records across the collection

Records can reference each other with wikilinks such as `[[alice]]`, Markdown
Expand Down Expand Up @@ -296,6 +321,7 @@ keep those claims precise and independently testable.
| [10-cel-profile.md](./10-cel-profile.md) | Portable expressions and host bindings |
| [11-querying.md](./11-querying.md) | Filters, ordering, projection, and result envelopes |
| [12-operations.md](./12-operations.md) | Read and write operations, concurrency, and diagnostics |
| [12a-concurrent-edits.md](./12a-concurrent-edits.md) | Record identity, move detection, three-way merge, and writer format fidelity |
| [13-migrations-and-compatibility.md](./13-migrations-and-compatibility.md) | Migration from earlier versions and compatibility |
| [14-conformance.md](./14-conformance.md) | Profiles, claims, fixtures, and runners |

Expand All @@ -315,7 +341,9 @@ directly comparable without making one product's internal API normative.

## Versioning

This specification uses semantic versioning. The current version is **0.3.0**.
This specification uses semantic versioning. The current version is **0.3.0**,
in its fifth release candidate (`0.3.0-rc.5`). 0.3.0 is declared stable once
implementations pass the conformance suite.
Collections declare their specification version with `spec_version` in
`mdbase.yaml`. Tools declare the profiles and versions they implement.

Expand All @@ -341,7 +369,12 @@ through version requirements.
The keywords `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are to be
interpreted as described in RFC 2119.

Draft notes use ordinary prose and are non-normative.
Draft notes use ordinary prose and are non-normative. A paragraph that begins
with **Provisional (rc.5).** records a choice made where the design input
left a detail open. It is normative in the release candidate and may change
before 0.3.0 is declared stable. The
[0.3.0-rc.5 release notes](./docs/releases/0.3.0-rc.5.md) list every such
choice.

## License

Expand Down
40 changes: 34 additions & 6 deletions 01-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ or data contract file, and not another reserved collection file. A record has:
The persisted frontmatter object is the raw record value. Effective read values
may additionally include `collection.read_defaults`.

A record is identified by its path. A record carries no required mdbase
metadata: no ID, revision, or merge state is ever required in the file.
Implementations MAY keep internal record identities outside the collection's
records and follow a record across moves with the move detection of Chapter
12A.

## Type

A type is a Markdown file, usually under `_types/`, whose frontmatter has
Expand Down Expand Up @@ -62,13 +68,26 @@ record matches multiple types, it is valid only if it validates against every
matched type's JSON Schema and every matched type's mdbase collection
validators.

Membership should be a deterministic function of the record's path, its
persisted frontmatter, and the type registry, independent of the current time
and of other records. Chapter 07 defines the diagnostic for match rules that
break this.

## Validity

Validity is a property reported when a record is read, never a guarantee.
Any tool can write any bytes to a file, so a collection may always contain
invalid records. Conforming tools read, index, and query them, and report
their issues. Chapter 04 defines which checks may reject a write made through
an engine.

## Collection Semantics

Collection semantics are rules that require knowledge of the file tree or
runtime context. Examples:

- link parsing and target resolution
- cross-file uniqueness
- cross-record uniqueness
- effective read defaults
- path generation
- display metadata
Expand All @@ -86,14 +105,23 @@ simple transforms.
Lifecycle policy is deterministic operation behavior within Core Write. It runs
from type policy during the active mutation.

## Merge

A merge combines two concurrent edits of one record against their common base
version. Each top-level frontmatter field has a merge strategy, declared in
`collection.merge` or derived from the type, and the body merges line by line.
Chapter 12A defines the merge function. When and where merges happen is an
implementation concern.

## Expression

Portable v0.3 expressions use the mdbase CEL profile. Expressions appear in
queries, projections, runtime conditions, workflow input templates, and optional
lifecycle guards.
mdbase has one expression language: the mdbase CEL profile. Expressions appear
in `match.expr`, queries, projections, lifecycle guards, runtime conditions,
and workflow input templates.

`match.where` uses the standalone structured predicate language defined in
Chapter 07.
`match.where` is a structured predicate written as YAML data, not an
expression language; Chapter 07 defines it. Other expression syntaxes, such
as Obsidian Bases formulas, are adapter dialects (Chapter 10).

## View

Expand Down
63 changes: 63 additions & 0 deletions 02-collection-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,66 @@ required.

Implementations MAY reject platform-reserved filenames or characters when a
write operation targets a filesystem where those paths cannot be represented.

## Path Equivalence

Two collection paths name the same record path when their **path keys** are
equal. The path key of a path is computed as follows:

1. normalize the path to Unicode Normalization Form C (NFC)
2. apply Unicode default case folding (the full `C` and `F` mappings of
`CaseFolding.txt`, without locale tailoring)
3. normalize the result to NFC again

`Notes/Café.md` written with a precomposed `é` and `notes/CAFE\u0301.md`
written with a combining accent have the same path key. So do `Straße.md` and
`STRASSE.md`. Path keys never appear in results; paths are always reported as
written.

Path equivalence exists because macOS and Windows file systems treat such
paths as one file, while Linux does not. Every tool therefore agrees on what
collides, whatever file system it runs on:

- A write MUST NOT create a record whose path key equals the path key of a
different existing record. The collision rule below decides what happens
instead.
- A rename whose source and target have the same path key, such as
`tasks/todo.md` to `tasks/Todo.md`, changes only the spelling of the path
and is not a collision.
- When record discovery finds several files with one path key, which can
happen on a case-sensitive file system, each file is still a record. Core
Read reports a `path_collision` warning on every record of the group, with
`details.paths` listing the group in code-point order.

Path globs (above) remain case-sensitive and match paths as written.

**Provisional (rc.5).** Case folding uses the full mappings, so `ß` and
`ss` collide. This flags more collisions than some file systems would, never
fewer.

## Path Collisions

When a new record would take a path whose path key is already in use, the
outcome depends on where the path came from:

| Path source | Outcome |
| --- | --- |
| an explicit path supplied by the caller of create or rename | the operation fails with `path_conflict` before any write |
| a path derived from `collection.path.pattern` (Chapter 07) | the record receives the first free suffixed path |
| two records that already hold equivalent paths, for example after concurrent creates or an engine copying records onto another file system | the earlier-ordered record keeps the path; each later one receives the first free suffixed path |

A suffixed path inserts ` (n)`, a space and a decimal integer in parentheses,
before the final extension of the last path component: `tasks/Call Bob.md`
becomes `tasks/Call Bob (2).md`. Candidates are tried with `n = 2, 3, 4, …`,
and the first candidate whose path key is unused is chosen. Existing suffixes
are not parsed: the next candidate for `Call Bob (2).md` is
`Call Bob (2) (2).md`.

The ordering of records is supplied by whatever applies the rule, for example
the order in which an engine confirmed two creates. When no order exists
between the records, the record whose path as written is smaller in Unicode
code-point order is earlier. Every tool that applies the rule to the same
records in the same order computes the same paths.

A suffixed path is an ordinary path. Applying the rule never edits a record's
frontmatter or body, and the record keeps whatever identity its tool tracks.
20 changes: 13 additions & 7 deletions 03-records-and-frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,23 +171,29 @@ explicitly maps it to ordinary fields.

## Serialization

Write-capable tools SHOULD preserve unrelated body text and line ending style.
Write-capable tools MUST preserve unrelated body text and SHOULD preserve the
line ending style.

A YAML document record serializes as its frontmatter mapping alone. A create or
update that supplies a non-empty body for a YAML document record fails with
`invalid_request` before any write. Structured writes re-emit the mapping and
need not preserve comments, key order, or quoting style; a whole-document
`document` replacement (Chapter 12) is written exactly as supplied. Tools that
edit files another application owns SHOULD use whole-document replacement.
`invalid_request` before any write. A whole-document `document` replacement
(Chapter 12) is written exactly as supplied.

A write that changes some frontmatter keys of an existing record follows the
format fidelity rule of Chapter 12A: it re-emits only the changed top-level
entries, keeps every other entry byte-identical, including comments, quoting,
blank lines, and order, and keeps a changed entry's collection style. The rule
applies to Markdown records and YAML document records alike.

When serializing frontmatter, tools MUST:

- write an explicit null value for a key whose value is null
- omit keys that are missing
- quote empty strings

Tools SHOULD preserve array and object structure and SHOULD produce
deterministic key ordering when an operation rewrites a generated file.
Tools SHOULD produce deterministic key ordering when an operation writes a new
record or rewrites a generated file. New keys added to an existing record are
appended after its existing entries.

A null value in written frontmatter always means explicit null. Removing a key
is a distinct operation; Chapter 12 defines how an update requests it.
Expand Down
Loading
Loading