Skip to content
Draft
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 docs/releases/0.2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
- **Match rules:** explicit-type create match-rule enforcement scoped to Level 2+.
- **Datetime portability:** `settings.timezone` added; applies to `now()`/`today()` and naive comparisons.
- **Expressions:** `reduce()` requires explicit `init`; list methods `value/index/acc` shadow frontmatter fields.
- **Links:** `ambiguous_link` is an error on ID ambiguity, warning after tiebreakers.
- **Links:** `ambiguous_link` fails closed for duplicate ID or filename matches; providers do not choose an arbitrary target.
- **Non-scalar ordering:** `order_by` on lists/objects now specifies deterministic ordering rules.

## Removed / Deprecated
Expand Down
16 changes: 8 additions & 8 deletions tests/level-4/links-non-markdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,10 @@ groups:
field: "assignee"

# =============================================================================
# Group 6: Tiebreaker resolution order (§8.4)
# Group 6: Ambiguous simple-name resolution fails closed (§8.4)
# =============================================================================

- name: "simple name link tiebreaker resolution"
- name: "ambiguous simple name link resolution"
spec_ref: "§8.4"

setup:
Expand All @@ -370,7 +370,7 @@ groups:
---

tests:
- name: "tiebreaker prefers same directory"
- name: "same-directory duplicate does not create an implicit preference"
setup:
files:
tasks/source.md: |
Expand All @@ -394,9 +394,9 @@ groups:
path: "tasks/source.md"
field: "ref"
expect:
resolved_path: "tasks/readme.md"
resolved_path: null

- name: "tiebreaker prefers shortest path when no same-dir match"
- name: "shorter duplicate path does not create an implicit preference"
setup:
files:
tasks/source.md: |
Expand All @@ -420,9 +420,9 @@ groups:
path: "tasks/source.md"
field: "ref"
expect:
resolved_path: "docs/readme.md"
resolved_path: null

- name: "tiebreaker uses alphabetical as final fallback"
- name: "lexical order does not create an implicit preference"
setup:
files:
root-source.md: |
Expand All @@ -446,7 +446,7 @@ groups:
path: "root-source.md"
field: "ref"
expect:
resolved_path: "aaa/target.md"
resolved_path: null

# =============================================================================
# Group 7: Markdown link resolution — relative to containing file (§8.2.2)
Expand Down
20 changes: 10 additions & 10 deletions tests/level-4/links-resolution.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,10 @@ groups:
resolved_path: "people/alice.md"

# =============================================================================
# Group 6: Tiebreaker rules for simple name resolution
# Group 6: Ambiguous simple-name resolution fails closed
# =============================================================================

- name: "simple name tiebreakers"
- name: "same-directory ambiguity"
spec_ref: "§8.4"

setup:
Expand Down Expand Up @@ -400,15 +400,15 @@ groups:
---

tests:
- name: "tiebreaker prefers same directory"
- name: "same-directory duplicate remains ambiguous"
operation: resolve_link
input:
path: "tasks/source.md"
field: ref
expect:
resolved_path: "tasks/target.md"
resolved_path: null

- name: "simple name tiebreaker - shortest path"
- name: "path-length ambiguity"
spec_ref: "§8.4"

setup:
Expand Down Expand Up @@ -439,15 +439,15 @@ groups:
---

tests:
- name: "tiebreaker prefers shortest path when not in same directory"
- name: "shorter path does not win an ambiguous simple name"
operation: resolve_link
input:
path: "source.md"
field: ref
expect:
resolved_path: "a/target.md"
resolved_path: null

- name: "simple name tiebreaker - alphabetical"
- name: "lexical-order ambiguity"
spec_ref: "§8.4"

setup:
Expand Down Expand Up @@ -478,13 +478,13 @@ groups:
---

tests:
- name: "tiebreaker uses alphabetical order for equal-length paths"
- name: "lexical order does not win an ambiguous simple name"
operation: resolve_link
input:
path: "source.md"
field: ref
expect:
resolved_path: "alpha/target.md"
resolved_path: null

# =============================================================================
# Group 7: Ambiguous link detection
Expand Down
14 changes: 9 additions & 5 deletions v0.2/08-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,15 @@ Given a link value and the path of the file containing it:
- If exactly one match, resolve to it
- If multiple matches, resolution MUST fail with `ambiguous_link`
- **Filename match pass**: If no `id_field` match exists, search scoped **markdown files (records)** by filename
- If multiple filename candidates match, apply tiebreakers in order:
a. **Same directory**: Prefer a file in the same directory as the referring file
b. **Shortest path**: Prefer the file with the shortest path (closest to collection root)
c. **Alphabetical**: Sort candidate paths lexicographically and take the first
- If multiple candidates remain after all tiebreakers, resolve to `null` and emit an `ambiguous_link` warning
- If exactly one match exists, resolve to it
- If multiple matches exist, resolution MUST fail closed: resolve to `null` and emit `ambiguous_link`

Implementations MUST NOT choose among duplicate simple-name matches by
referring directory, path length, scan order, or lexical order. Those
choices are unstable across providers and can silently bind a relationship
to a different record after an unrelated move or import. Authors can make
the target unambiguous with a path, a unique `id_field`, or a narrower typed
target constraint.

> **Note:** Non-markdown files are not records and are not candidates for simple name matching (per [§2.9](./02-collection-layout.md)). A wikilink `[[diagram]]` will not match `diagram.png` — only markdown files are searched by `id_field` and filename. Non-markdown files are only resolved when explicitly referenced by path (with extension or relative path).

Expand Down
2 changes: 1 addition & 1 deletion v0.2/appendix-c-error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ This appendix defines standard error codes for validation issues and operation e
| `invalid_link` | Link cannot be parsed | Malformed wikilink |
| `link_not_found` | Link target doesn't exist | Target "[[missing]]" not found |
| `link_wrong_type` | Target is wrong type | Expected person, found task |
| `ambiguous_link` | Multiple candidates for a simple-name link (error on ID ambiguity, warning if still ambiguous after tiebreakers) | "[[note]]" matches notes/note.md and archive/note.md |
| `ambiguous_link` | Multiple candidates exist at the first populated simple-name lookup priority; resolution fails closed | "[[note]]" matches notes/note.md and archive/note.md |

### Date/Time Errors

Expand Down