- **Fixed graph-navigation agent tools trusting LLM-supplied IDs/keys and returning a well-formed empty result instead of an error, causing agents to falsely tell users "no such citation exists".** Confirmed live, twice, with real LLMs.
  - `opencontractserver/enrichment/authorities.py::candidate_keys` (~line 127) now also tries the `canonical_key` with every `_` replaced by `-` (real canonical keys use hyphens exclusively in their namespace prefix, e.g. `exchange-act:16`, never `exchange_act:16`; LLMs occasionally emit the underscore variant by analogy to Python-identifier conventions), and applies the existing subsection-root fallback to both the exact and normalized-underscore variants. Consumed transitively by `find_authority_target` / `read_reference_target`.
  - `opencontractserver/llms/tools/core_tools/graph_navigation.py::find_documents_citing` (~line 300) now anchors its `canonical_key` filter through `candidate_keys(...)` (`canonical_key__in=...`) instead of an exact-match `canonical_key=...` filter, so it benefits from the same underscore-normalization and subsection-root fallback as `read_reference_target`.
  - `opencontractserver/llms/tools/core_tools/graph_navigation.py::get_document_references` (~line 97) now resolves `document_id` via `BaseService.get_or_none(Document, document_id, user)` right after user resolution and returns an explicit `error` envelope (`outbound`/`inbound` empty, counts 0) when the id doesn't resolve to a document visible to the caller, instead of silently returning a "successful" empty-references envelope for a nonexistent/invisible/wrong-type id (e.g. a corpus_id mistakenly passed as document_id).
