Skip to content

visualize: percent-encoded link targets are never decoded — bundles with spaces in filenames render with 0 edges #200

Description

@oierreaemme

Summary

_extract_links in okf/src/reference_agent/viewer/generator.py never percent-decodes link targets, so any cross-link whose target is percent-encoded — the only CommonMark-conformant way to link to a file whose name contains a space — fails to match its concept and produces no edge. A bundle whose filenames contain spaces therefore renders as a fully disconnected node cloud, even when every link is well-formed and every target exists.

This is distinct from #48 (absolute /-prefixed links being skipped): the two bugs compound, but applying the #48 fix alone (e.g. PR #184) still yields 0 edges for percent-encoded targets. It is also the consumer-side counterpart of #112 / PR #186, whose proposed SPEC §5.4 says consumers resolving against the filesystem SHOULD percent-decode before matching — the reference viewer currently doesn't.

Repro (2 files)

repro-bundle/concepts/Customer Orders.md:

---
type: Reference
title: Customer Orders
---
See [Join Key](/concepts/Join%20Key.md).

repro-bundle/concepts/Join Key.md:

---
type: Reference
title: Join Key
---
Target concept.
python -m reference_agent visualize --bundle ./repro-bundle

Expected: 2 concepts, 1 edge. Actual: Wrote 2 concept(s), 0 edge(s), …

Varying only the link in Customer Orders.md (verified at d44368c):

link in Customer Orders.md edges why
[Join Key](Join%20Key.md) 0 resolved as the literal path Join%20Key.md; derived id Join%20Key never matches the concept id Join Key
[Join Key](/concepts/Join%20Key.md) 0 skipped as absolute (#48) and not decoded
[Join Key](Join Key.md) 0 _LINK_RE forbids whitespace, so the link is never extracted
control: [join-key](join-key.md) (no spaces anywhere) 1

Row 2 stays at 0 even with PR #184's absolute-link resolution applied (verified by patching _extract_links accordingly): decoding is a separate, unaddressed step.

Root cause

In okf/src/reference_agent/viewer/generator.py:

  • _extract_links (L48–L66) resolves the raw regex match against the filesystem without urllib.parse.unquote, so a percent-encoded target is looked up as a literal path and the derived id can never match the concept id that _walk_concepts derives from the (decoded) filename.
  • L54 if "://" in target or target.startswith("/"): continue additionally drops all absolute bundle-relative links — already tracked in visualize: 0 edges for spec-recommended absolute links and CommonMark link titles #48, listed here only because the two together are what take a real bundle to zero.
  • L12 _LINK_RE = re.compile(r"\]\(([^)\s]+\.md)(?:#[A-Za-z0-9_\-]*)?\)") cannot match a destination containing a raw space. Per CommonMark a destination with spaces must be wrapped in <…> or percent-encoded, so this is secondary — but the regex supports neither, leaving no working way at all to link to a file with a space in its name.

Real-world impact

A 166-concept bundle using the SPEC §5.1-recommended absolute form with percent-encoded path segments (874 cross-links in total) renders with 0 edges — the graph view degrades to an unconnected node cloud precisely for bundles that follow the spec's recommended link style.

Suggested fix

Decode the target before resolving, alongside the absolute-link resolution from #48/#184:

from urllib.parse import unquote

# in _extract_links:
target = unquote(m.group(1))

If PR #186's SPEC §5.4 lands, the reference viewer — as the format's reference consumer — should demonstrate exactly that behavior. A regression test in okf/tests/test_viewer.py (e.g. test_percent_encoded_links_become_edges, asserting 1 edge for the 2-file bundle above) would pin it.

Happy to send a PR if useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions