Skip to content

Fail closed on ambiguous simple links - #49

Closed
callumalpass wants to merge 1 commit into
mainfrom
agent/fail-closed-link-ambiguity
Closed

callumalpass wants to merge 1 commit into
mainfrom
agent/fail-closed-link-ambiguity

Conversation

@callumalpass

Copy link
Copy Markdown
Collaborator

Summary

  • remove provider-dependent directory/path/lexical tiebreakers for duplicate simple-name links
  • require ambiguous filename matches to resolve to null
  • update v0.2 conformance fixtures and error documentation

Validation

  • python3 scripts/check_test_levels.py
  • python3 scripts/check_v03_tests.py
  • mdbase-rs v0.2 conformance: 1794/1794 passing against this branch

This is the normative companion to the Candidate B hosted relationship-resolution work.

callumalpass added a commit that referenced this pull request Oct 4, 2026
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

Copy link
Copy Markdown
Collaborator Author

Closing this unmerged in favour of #61 (0.3.0-rc.5).

What #61 keeps from here:

  • an ambiguous ID resolves to null with ambiguous_link, with no filename fallback;
  • the provider can't influence which record wins.

What #61 rejects: the fail-closed rule for duplicate filenames. rc.5 keeps the filename tiebreakers and makes them mandatory: depth is counted in path segments, ending in code-point order. #51's rename fixture also depends on them, so merging this would have left the v0.2 suite contradicting itself.

Still open: this PR's remaining concern, that a link can be silently re-pointed when a create or rename introduces a closer candidate. It's recorded under "Not in this release" in docs/releases/0.3.0-rc.5.md for a later revision.

callumalpass added a commit that referenced this pull request Oct 4, 2026
#61)

* Specify concurrent-edit semantics and reported validity for rc.5

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.

* Add the regex profile and body_edits to rc.5

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.

* Add the rc.5 conformance cases to the v0.3 suite

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.

* Add the 0.3.0-rc.5 changelog entry and release notes

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.

* Pin filename tiebreakers and rename reference updating for rc.5

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).
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