Skip to content

Add relative path support (target_relative field + execute fallback) #13

@djdarcy

Description

@djdarcy

Add relative path support (target_relative field + execute fallback)

Problem

DazzleLink stores absolute paths only. When a dazzlelink is created on machine A (D:\M\...) and the library syncs to machine B (E:\Library\...), the execute command fails because the absolute path doesn't exist:

$ dazzlelink execute "Taleb - The Black Swan (see Philosophy)"
Error: Target not found: D:\M\Literature\Books\__Philosophy\2010 - The Black Swan...

The relative_path field in the schema is a boolean flag only -- it records whether the path was relative at creation time but doesn't store an actual relative path or use one for resolution.

Meanwhile, the structural relationship between the dazzlelink and its target is preserved by the sync tool (both files maintain their relative positions). A relative path like ../../../../__Philosophy/2010 - The Black Swan... would work on any machine.

Proposed solution

Store both absolute and relative paths; resolve with a fallback chain.

After creation, a dazzlelink would contain:

{
  "link": {
    "target_path": "D:\\M\\Literature\\Books\\__Philosophy\\2010 - The Black Swan...",
    "target_relative": "../../../../__Philosophy/2010 - The Black Swan...",
    "relative_path": false,
    ...
  }
}

The execute resolution order:

1. Try target_path (absolute) --> works on same machine
2. Try resolve(dazzlelink_dir, target_relative) --> works on any synced machine
3. Try path_representations (UNC, drive variants) --> works on network
4. Fail with diagnostic message

Optional: create --relative-only flag stores only the relative path (sets target_path to the relative path and relative_path: true).

Implementation approach

Files affected:

File Change
dazzlelink/operations/core.py serialize_link(): compute target_relative via os.path.relpath(target, dazzlelink_dir)
dazzlelink/operations/recreate.py execute_dazzlelink(): add fallback chain (absolute -> relative -> representations)
dazzlelink/data.py Add target_relative to schema; bump schema version 1 -> 2
dazzlelink/cli.py Add --relative-only flag to create subparser
dazzlelink.py Mirror changes in standalone module

Key implementation detail for execute:

# In execute_dazzlelink(), after loading target_path:
if not os.path.exists(target_path):
    # Try relative path from dazzlelink location
    target_relative = link_data.get("link", {}).get("target_relative")
    if target_relative:
        dazzlelink_dir = os.path.dirname(os.path.abspath(dazzlelink_path))
        resolved = os.path.normpath(os.path.join(dazzlelink_dir, target_relative))
        if os.path.exists(resolved):
            target_path = resolved

Design considerations

  • The existing relative_path boolean field is kept for backward compatibility -- it's not renamed or repurposed.
  • target_relative uses forward slashes for cross-platform portability (Python's os.path.join handles both on Windows).
  • Schema v1 dazzlelinks without target_relative continue to work -- the fallback chain simply skips step 2.
  • On Windows, os.path.relpath() fails across drives (D: -> E:). This is fine -- within a single library tree, all files are on the same drive.

Acceptance criteria

  • create stores target_relative field computed from dazzlelink location
  • execute tries absolute path first, falls back to relative resolution
  • execute resolves relative path from the dazzlelink file's directory, NOT from CWD
  • create --relative-only stores only relative path with relative_path: true
  • Schema version bumped to 2
  • Backward compatible: schema v1 dazzlelinks without target_relative still execute
  • Test: create dazzlelink, move library root, verify relative resolution works
  • Test: --relative-only creates a working portable dazzlelink

Related issues

Analysis

See 2026-03-30__22-32-33__dazzlelink-relative-paths-for-library-crossrefs.md for detailed design analysis.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions