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
Related issues
Analysis
See 2026-03-30__22-32-33__dazzlelink-relative-paths-for-library-crossrefs.md for detailed design analysis.
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\...), theexecutecommand fails because the absolute path doesn't exist:The
relative_pathfield 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
executeresolution order:Optional:
create --relative-onlyflag stores only the relative path (setstarget_pathto the relative path andrelative_path: true).Implementation approach
Files affected:
dazzlelink/operations/core.pyserialize_link(): computetarget_relativeviaos.path.relpath(target, dazzlelink_dir)dazzlelink/operations/recreate.pyexecute_dazzlelink(): add fallback chain (absolute -> relative -> representations)dazzlelink/data.pytarget_relativeto schema; bump schema version 1 -> 2dazzlelink/cli.py--relative-onlyflag tocreatesubparserdazzlelink.pyKey implementation detail for
execute:Design considerations
relative_pathboolean field is kept for backward compatibility -- it's not renamed or repurposed.target_relativeuses forward slashes for cross-platform portability (Python'sos.path.joinhandles both on Windows).target_relativecontinue to work -- the fallback chain simply skips step 2.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
createstorestarget_relativefield computed from dazzlelink locationexecutetries absolute path first, falls back to relative resolutionexecuteresolves relative path from the dazzlelink file's directory, NOT from CWDcreate --relative-onlystores only relative path withrelative_path: truetarget_relativestill execute--relative-onlycreates a working portable dazzlelinkRelated issues
Analysis
See
2026-03-30__22-32-33__dazzlelink-relative-paths-for-library-crossrefs.mdfor detailed design analysis.