Skip to content

build_context renders unresolved forward references as [[None]] instead of the stored target name #955

@phernandez

Description

@phernandez

Summary

build_context with output_format="text" renders unresolved forward references as [[None]]:

### Relations
- see_also [[None]]
- see_also [[None]]
- links_to [[None]]
- see_also [[bm-note(5)]]

The knowledge format explicitly encourages forward references ("targets need not exist yet"), so any growing knowledge base hits this. The original target name is stored and available — the formatter just doesn't use it.

Reproduction

Against v0.21.6 cloud (also present in main @ 650f88a):

  1. write_note a note containing relations to not-yet-existing targets, e.g. - see_also [[edit-note(3)]]
  2. build_context(url="<that-note>", output_format="text")
  3. Relations section shows - see_also [[None]] for every unresolved target

Meanwhile schema_validate on the same note correctly reports see_also values ["edit-note(3)", ...] — the data is intact, only the context rendering loses it.

Root cause

  • models/knowledge.py:296Relation.to_name stores the literal forward-reference target text
  • services/context_service.py (recursive CTE, ~line 458) — relation rows embed to_name only inside the composite title (r.relation_type || ': ' || r.to_name); to_id is NULL for unresolved relations and to_name is not selected as its own column
  • api/v2/utils.py:134to_title = entity_title_lookup.get(item.to_id) if item.to_id else NoneRelationSummary.to_entity = None
  • mcp/tools/build_context.py:57f"- {rel.relation_type} [[{rel.to_entity}]]"[[None]]

JSON output has the same gap: to_entity: null with no field carrying the forward-reference name (the consumer can only recover it by parsing the relation permalink).

Expected

### Relations
- see_also [[edit-note(3)]]   ← the name the relation was written with

Suggested fix

Select r.to_name as a proper column in the context CTE relation rows, add it to ContextResultRow and RelationSummary (additive, optional), and have the text formatter fall back: rel.to_entity or rel.to_name. This also fixes the JSON consumers' gap.

Context

Found during live verification for #952 (manual pages) — surfaced on the very first build_context traversal of the manual's SEE ALSO graph, since a new manual is mostly forward references. Fix with regression test planned on the #952 worktree branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions