Skip to content

Specify 0.3.0-rc.5: concurrent edits, reported validity, regex profile - #61

Merged
callumalpass merged 5 commits into
mainfrom
spec/v0.3.0-rc.5-draft
Oct 4, 2026
Merged

callumalpass merged 5 commits into
mainfrom
spec/v0.3.0-rc.5-draft

Conversation

@callumalpass

@callumalpass callumalpass commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Specifies 0.3.0-rc.5. Collections keep spec_version: "0.3.0". Most changes relax rc.4 rules; the few that tighten are reported as diagnostics rather than breaking compatibility.

Full release notes: docs/releases/0.3.0-rc.5.md. Chapter 13, "Changes Since rc.4", has the behaviour-change table and an engine checklist.

What changes

Validity is reported, never guaranteed (00, 01, 04). Checks fall into three tiers:

  • request/safety checks always reject;
  • single-record checks reject writes made through an engine only at error;
  • cross-record checks (uniqueness, validate_exists, link targets) are reported and never block.

Concurrent edits (new chapter 12A):

  • record identity without in-file IDs;
  • move detection;
  • a three-way record merge, with per-field strategies in collection.merge (conflict/max/min/union); defaults are max for lifecycle now/today and union for tags and uniqueItems arrays;
  • a line-based body merge with append-append union;
  • a format-fidelity rule for writers.

Updates (12):

  • add/remove list operations;
  • body_edits: text edits against a known base body, rebased with the 12A body merge;
  • if_revision is opt-in, and engines must never add it on a caller's behalf.

Paths (02): NFC, full case folding, then NFC again, plus a deterministic (n) collision rule. Path-pattern values containing / are rejected.

Uniqueness (07): unique.enforce: write | report, defaulting to report, with real scope semantics.

One regex profile (06, 07, 10): RE2 syntax with regex-lite semantics.

  • \d, \w, \s, \b and (?i) are ASCII-only.
  • Unsupported constructs such as \p{…} make a type invalid (invalid_pattern).
  • The same profile applies to CEL matches(), JSON Schema pattern and match.where.

Determinism:

  • match.expr must not depend on time or on other records. In rc.5 this is a nondeterministic_match warning; it becomes an error in 0.3.0 stable.
  • generated: sequence has no v0.3 destination.

Fixes for drift:

  • id_field has no default;
  • ambiguous IDs resolve to null with ambiguous_link, with no filename fallback.

Deterministic link resolution and rename (08, 12):

  • the filename tiebreakers are required: referring directory, then fewest path segments, then code-point order, never scan or storage order;
  • a rename with reference updating rewrites the links that resolved to the renamed record, including tiebreaker-selected matches, and never rewrites ambiguous links or links to other records. The rewrites are listed in references_updated.

The earlier Unreleased entries ship in rc.5: YAML document records and Bases, seed type upgrades, and saved-view identification.

Conformance

The v0.3 suite gains a merge profile and a merge_records fixture format, documented in tests/v0.3/README.md.

  • New tests carry since: 0.3.0-rc.5; changed tests carry changed: 0.3.0-rc.5.
  • scripts/concurrent_edits_model.py is an executable model. CI runs 175 fixtures against it; 215 more need a real engine.
  • Only one existing test changes its expected outcome: links.duplicate_id_ambiguous now also reports ambiguous_link. The release notes list every new test on which an rc.4 engine would diverge.

Decided vs provisional

  • Decided:
    • \p{…} makes a type invalid;
    • body_edits conflicts are detected per line, and a finer granularity would be a compatible refinement;
    • time-dependent match.expr warns now and becomes an error at stable.
  • Provisional: 35 choices, listed in the release notes for review.

Related open PRs

Reconciled in e6fb2de. None of the other PRs was changed.

Recommended merge order:

  1. Specify 0.3.0-rc.5: concurrent edits, reported validity, regex profile #61.
  2. Align rename references with deterministic resolution #51, whenever it is ready; it is independent of Specify 0.3.0-rc.5: concurrent edits, reported validity, regex profile #61.
  3. Remove the saved-view source operations #58 once the hold lifts, rebased as in reconcile/pr58-rc5.

Close #49.

Context

These semantics come from the mdbase-next redesign. Its feasibility prototype measured conflict rates of 0.08% of writes with field-level merging, against about 2% with whole-file revision checks.

How to review

Start with docs/releases/0.3.0-rc.5.md, then 12a-concurrent-edits.md, then chapter 13's "Changes Since rc.4". Checks:

python scripts/check_v03_tests.py

plus the site build.

Validity is reported, never guaranteed: checks fall into request and
safety, single-record, and cross-record tiers, and cross-record checks
never block a write made through an engine.

- collection.merge declares conflict, max, min, or union per top-level
  field, with max for lifecycle now/today fields and union for tags and
  uniqueItems arrays by default.
- Update accepts add and remove list operations.
- collection.unique gains enforce: write | report (default report) and
  exact governed-record and comparison-set scope semantics.
- Paths are equivalent under NFC plus full case folding, with a
  deterministic ' (n)' suffix for derived and concurrent collisions.
- A time-dependent or cross-record match.expr loads with a
  nondeterministic_match warning; it becomes an error in 0.3.0 stable.
- Lifecycle has no sequence provider; CEL is the one expression language
  and Obsidian Bases expressions are an adapter dialect.
- New chapter 12A defines record identity without in-file IDs, move
  detection, the three-way record merge with append-append body union,
  and the writer format fidelity rule.
- settings.id_field has no default, path-pattern values may not contain
  '/', and ambiguous IDs resolve to null with ambiguous_link.
- Chapter 13 lists the changes since rc.4 and how engines report the
  tightenings.
- The v0.3 type-file schema gains collection.merge and unique[].enforce,
  and the claim schema gains the merge profile.
Regex: CEL matches(), JSON Schema pattern, and match.where matches share
one flavor, the mdbase regex profile defined in chapter 10: RE2 syntax
with ASCII-only \d, \w, \s, \b and case folding over Unicode scalar
values, as in regex-lite. Unicode classes, backreferences, and
look-around are invalid_pattern and make a type invalid. JSON Schema
pattern following the profile instead of ECMA-262 is provisional.

Body edits: update accepts body_edits, ranges whose offsets count Unicode
scalar values of a base body identified by a SHA-256 body_base digest,
with optional body_base_text. Edits apply directly to an unchanged body
and otherwise rebase with the chapter 12A body merge; conflicts are found
per line and fail with body_conflict, and a missing base fails with
body_base_unavailable. They are exclusive with body and document and need
no live-collaboration support.
New tests carry since: 0.3.0-rc.5 and changed tests carry
changed: 0.3.0-rc.5. The manifest marks the rc.5 requirements and adds
the merge profile and the merge and watch fixture sets.

- merge/merge.yaml converts the 13 mdbase-next prototype merge fixtures
  into a merge_records format (base + first + second + type declarations
  -> exact merged bytes and conflicts) and adds 24 merge and strategy
  cases; merge/body-edits.yaml covers applying and rebasing body_edits.
- core/paths.yaml, watch/move-detection.yaml, and cel/regex-profile.yaml
  cover path keys and suffixes, move pairing, and ASCII-versus-Unicode
  regex classes.
- Adapter-target suites cover validation tiers, uniqueness modes and
  scope, non-blocking link checks, list operations, format fidelity, path
  collisions, body_edits through update, nondeterministic_match, and the
  id_field and ambiguous-ID rules.
- links.duplicate_id_ambiguous now also expects ambiguous_link.
- scripts/concurrent_edits_model.py is an executable model of the pure
  functions; check_v03_tests.py runs the 95 pure fixtures against it,
  validates the since/changed markers, and checks that the claim schema
  lists the manifest's profiles.
The rc.5 entry covers concurrent edits, reported validity, the regex
profile, and body_edits, and folds in the changes made since rc.4 to YAML
document records and Bases, seed type upgrades, and saved-view
identification.

The release notes list the behavior changes, the one existing test whose
expectation changed and the new tests an rc.4 engine fails, the three
choices decided for rc.5 (unsupported patterns invalidate the type,
body-edit conflicts are per line, time-dependent match.expr becomes an
error in 0.3.0 stable), and the remaining provisional choices.
Reconciles rc.5 with the open link PRs #49 and #51.

- Chapter 08: the second tiebreaker counts path segments, and no tool may
  choose among duplicate filename matches by scan, index, or storage order.
  A tiebreaker-resolved link is not ambiguous.
- Chapter 12: a rename with reference updating rewrites the links that
  resolved to the renamed record, including tiebreaker-selected filename
  matches, and never rewrites ambiguous links or links to other records.
  The result lists rewrites in references_updated. A rename keeps the
  record's identity, as a detected move does in Chapter 12A.
- Chapter 14, Chapter 13, the changelog, and the release notes follow.
- Conformance: links.filename_tiebreakers and core_write.rename_references,
  with four tiebreaker tests in core/link-ambiguity.yaml and four rename
  tests in the new core/rename-references.yaml (155 rc.5 tests in all).
@callumalpass
callumalpass merged commit 61c57b1 into main Oct 4, 2026
20 checks passed
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