How docmgr stores RelatedFiles paths with explicit anchors (repo://, ws://, docs://, abs://), how they resolve, and how to migrate legacy paths.
A RelatedFiles entry like pkg/foo.go is ambiguous: relative to what? The
repository root? The document's directory? The docs root? Historically docmgr
guessed, and the guesses diverged between relate, doctor, search, and the
web UI. Anchored paths remove the guessing by storing an explicit scheme with
every path, so an entry means exactly the same thing no matter which tool reads
it or which directory you run docmgr from.
| Scheme | Example | Resolves relative to |
|---|---|---|
repo:// | repo://pkg/foo.go | The repository root (where .git lives) |
ws:// | ws://glazed/pkg/fields.go | A go.work workspace member: ws://<member>/<rel> resolves against the sibling directory <member> under the directory containing go.work |
docs:// | docs://2026/07/05/MEN-1--x/design/01.md | The docs root (ttmp/) |
doc:// | doc://../reference/01-diary.md | The directory of the document whose frontmatter contains the entry (read-side only; never written by docmgr) |
abs:// | abs:///home/user/x.go | Nothing — it is an absolute path (escape hatch) |
Entries without a scheme are legacy bare paths. They still resolve through the historical multi-anchor guessing logic, so old documents keep working, but new writes always use anchors.
docmgr doc relate and docmgr changelog update --file-note accept absolute
or relative input paths, resolve them, and persist the tightest containing
anchor:
repo://<rel>go.work) → ws://<member>/<rel>docs://<rel>abs://<abs>docmgr never writes doc:// and never writes repo-escaping ../ chains.
# Input path can be absolute or relative; the stored value is anchored:
docmgr doc relate --ticket MEN-4242 \
--file-note "/home/you/project/backend/api/register.go:Registers API routes"
# Resulting frontmatter:
# RelatedFiles:
# - Path: repo://backend/api/register.go
# Note: Registers API routes
You may also pass an already-anchored path in --file-note
(repo://backend/api/register.go:note); the : that ends the scheme is not
mistaken for the path/note separator.
All consumers — doctor, doc search --file/--dir reverse lookup, the
workspace index, and the web UI — resolve anchors through one shared resolver:
repo:// joins against the repository root discovered for the document.ws://member/rel joins against <workspace-root>/<member>/<rel>, where the
workspace root is the directory containing go.work.docs:// joins against the resolved docs root.doc:// joins against the directory of the referencing document (it may
escape the repository; it is tolerated on read for hand-written entries).abs:// is used as-is.Existence is checked honestly on disk; doctor reports related files that do
not resolve to an existing file.
Migrate a ticket (or the whole workspace) with:
# Anchor migration only
docmgr doctor --ticket MEN-4242 --fix-anchors
# Or as part of the full safe-fix pass (frontmatter auto-repair + anchors)
docmgr doctor --ticket MEN-4242 --fix
The migration rewrites each legacy RelatedFiles path to its anchored
equivalent using the same tightest-containing-anchor rule. Entries that do not
resolve to an existing file are left untouched and reported as warnings, so
migration never invents paths.
--file-note; docmgr
anchors them for you. This avoids any dependence on the current directory.docmgr doc relate.ws:// anchors only appear when your repository is part of a go.work
workspace; single-repo setups will only see repo://, docs://, and
abs://.docmgr help how-to-use — daily workflow, including relating filesdocmgr help cli-guide — command referencedocmgr help doctor-validation-workflow — how doctor validates related files