Follow-up to #553, fixed by #556 and released in v0.30.3-beta. The original harm is gone — the GPU methods are no longer hidden — but the fix routed the closest matches into the field the display labels Available, so the two labels have in effect swapped, and the Hint line still ends on a colon with nothing after it.
Reported from OptimalControl.jl's solve/gpu.md, whose executed output publishes this message on the documentation site.
What it prints now (CTBase v0.30.3-beta)
julia> solve(ocp, :adnlp, :gpu)
AmbiguousDescription → _complete_description, strategy_builders.jl:260
│
│ cannot find matching description
│
│ Diagnostic No complete match — no description contains all symbols
│ Requested (:adnlp, :gpu)
│ Available (:collocation, :exa, :madnlp, :gpu)
│ (:collocation, :exa, :madncl, :gpu)
│ (:collocation, :adnlp, :uno, :cpu)
│ (:collocation, :adnlp, :madnlp, :cpu)
│ (:collocation, :adnlp, :madncl, :cpu)
│
│ Context description completion
│ Hint Try one of the closest matches:
└─
Two things are wrong with that, and they are the same bug seen from both ends:
Available lists 5 of the 12 available descriptions, chosen by similarity, with nothing saying so. A reader asking "what can I actually call?" is given a filtered subset under a label that claims completeness.
Hint announces a list and prints none. The closest matches are on screen — above, under the wrong heading.
Why, reading the code
The display labels are fixed. src/Exceptions/display.jl:157-159 hardcodes Available for candidates:
if !isnothing(e.candidates) && !isempty(e.candidates)
push!(pairs, ("Available", e.candidates, :default))
end
and display.jl:223-225 renders Hint from suggestion. Neither knows what the caller put in the field.
src/Descriptions/complete.jl:96 now decides between two different kinds of content for that one field:
candidates=isempty(similar_descs) ? all_candidates : similar_descs,
so whenever similar_descs is non-empty — the common case — the Available block holds closest matches, and the Hint at complete.jl:78 is left dangling.
A second consequence: #556's truncation marker is unreachable in practice
all_candidates is computed at complete.jl:74 with the new max_show=20, and _format_description_candidates appends … and N more when the catalog is longer. But that value is now discarded whenever similar_descs is non-empty, and _find_similar_descriptions returns empty only when no description shares a single symbol with the request — _compute_similarity is a Jaccard index and the results are filtered with filter(x -> x[1] > 0.0, similarities). That is exactly the "unknown symbols" diagnostic.
So the marker introduced by #556 can only ever render when both conditions hold: the catalog has more than 20 entries and the request shares no symbol with any of them. In the case #553 was filed about, it never appears.
Verified on v0.30.3-beta with OptimalControl's 12-method registry and Requested = (:adnlp, :gpu):
similar_descs empty? false all_candidates count: 12 marker present? false
Suggested fix — complete.jl only, no change to display.jl
_print_pipe_field already splits a String value on newlines and keeps the │ prefix and label indentation on continuation lines (display.jl:283-291), so a multi-line suggestion renders correctly today. That makes the fix two lines:
@@ src/Descriptions/complete.jl:77
suggestion = if !isempty(similar_descs)
- "Try one of the closest matches:"
+ string("Try one of the closest matches:\n", join(similar_descs, "\n"))
elseif !isempty(all_candidates)
"Choose from the available descriptions listed above"
else
"Check your input symbols and available descriptions"
end
@@ src/Descriptions/complete.jl:96
- candidates=isempty(similar_descs) ? all_candidates : similar_descs,
+ candidates=all_candidates,
Available goes back to meaning available, the hint carries its own list, and all_candidates — with its max_show=20 and its marker — is used on every path instead of being thrown away.
Rendered with that patch applied (real output, not a mock-up):
│ Diagnostic No complete match — no description contains all symbols
│ Requested (:adnlp, :gpu)
│ Available (:collocation, :adnlp, :ipopt, :cpu)
│ (:collocation, :adnlp, :madnlp, :cpu)
│ (:collocation, :adnlp, :uno, :cpu)
│ (:collocation, :adnlp, :madncl, :cpu)
│ (:collocation, :adnlp, :knitro, :cpu)
│ (:collocation, :exa, :ipopt, :cpu)
│ (:collocation, :exa, :madnlp, :cpu)
│ (:collocation, :exa, :uno, :cpu)
│ (:collocation, :exa, :madncl, :cpu)
│ (:collocation, :exa, :knitro, :cpu)
│ (:collocation, :exa, :madnlp, :gpu)
│ (:collocation, :exa, :madncl, :gpu)
│
│ Context description completion
│ Hint Try one of the closest matches:
│ (:collocation, :exa, :madnlp, :gpu)
│ (:collocation, :exa, :madncl, :gpu)
│ (:collocation, :adnlp, :uno, :cpu)
│ (:collocation, :adnlp, :madnlp, :cpu)
│ (:collocation, :adnlp, :madncl, :cpu)
└─
If you would rather the two lists be structurally distinct rather than one of them living inside a hint string, the alternative is a closest::Union{Nothing,Vector{String}} keyword on AmbiguousDescription plus a Closest entry in _build_primary_pairs. Non-breaking (defaulted keyword), but it touches types.jl and display.jl as well; the two-line version above needs neither.
Reproduction
using CTBase
const D, E = CTBase.Descriptions, CTBase.Exceptions
catalog = ((:collocation,:adnlp,:ipopt,:cpu), (:collocation,:adnlp,:madnlp,:cpu),
(:collocation,:adnlp,:uno,:cpu), (:collocation,:adnlp,:madncl,:cpu),
(:collocation,:adnlp,:knitro,:cpu),(:collocation,:exa,:ipopt,:cpu),
(:collocation,:exa,:madnlp,:cpu), (:collocation,:exa,:uno,:cpu),
(:collocation,:exa,:madncl,:cpu), (:collocation,:exa,:knitro,:cpu),
(:collocation,:exa,:madnlp,:gpu), (:collocation,:exa,:madncl,:gpu))
D.complete(:adnlp, :gpu; descriptions=catalog) # Available = 5 closest, Hint empty
Display-only, as before; complete's resolution behaviour is not involved.
Follow-up to #553, fixed by #556 and released in v0.30.3-beta. The original harm is gone — the GPU methods are no longer hidden — but the fix routed the closest matches into the field the display labels
Available, so the two labels have in effect swapped, and theHintline still ends on a colon with nothing after it.Reported from
OptimalControl.jl'ssolve/gpu.md, whose executed output publishes this message on the documentation site.What it prints now (CTBase v0.30.3-beta)
Two things are wrong with that, and they are the same bug seen from both ends:
Availablelists 5 of the 12 available descriptions, chosen by similarity, with nothing saying so. A reader asking "what can I actually call?" is given a filtered subset under a label that claims completeness.Hintannounces a list and prints none. The closest matches are on screen — above, under the wrong heading.Why, reading the code
The display labels are fixed.
src/Exceptions/display.jl:157-159hardcodesAvailableforcandidates:and
display.jl:223-225rendersHintfromsuggestion. Neither knows what the caller put in the field.src/Descriptions/complete.jl:96now decides between two different kinds of content for that one field:so whenever
similar_descsis non-empty — the common case — theAvailableblock holds closest matches, and theHintatcomplete.jl:78is left dangling.A second consequence: #556's truncation marker is unreachable in practice
all_candidatesis computed atcomplete.jl:74with the newmax_show=20, and_format_description_candidatesappends… and N morewhen the catalog is longer. But that value is now discarded wheneversimilar_descsis non-empty, and_find_similar_descriptionsreturns empty only when no description shares a single symbol with the request —_compute_similarityis a Jaccard index and the results are filtered withfilter(x -> x[1] > 0.0, similarities). That is exactly the"unknown symbols"diagnostic.So the marker introduced by #556 can only ever render when both conditions hold: the catalog has more than 20 entries and the request shares no symbol with any of them. In the case #553 was filed about, it never appears.
Verified on v0.30.3-beta with OptimalControl's 12-method registry and
Requested = (:adnlp, :gpu):Suggested fix —
complete.jlonly, no change todisplay.jl_print_pipe_fieldalready splits aStringvalue on newlines and keeps the│prefix and label indentation on continuation lines (display.jl:283-291), so a multi-linesuggestionrenders correctly today. That makes the fix two lines:@@ src/Descriptions/complete.jl:77 suggestion = if !isempty(similar_descs) - "Try one of the closest matches:" + string("Try one of the closest matches:\n", join(similar_descs, "\n")) elseif !isempty(all_candidates) "Choose from the available descriptions listed above" else "Check your input symbols and available descriptions" end @@ src/Descriptions/complete.jl:96 - candidates=isempty(similar_descs) ? all_candidates : similar_descs, + candidates=all_candidates,Availablegoes back to meaning available, the hint carries its own list, andall_candidates— with itsmax_show=20and its marker — is used on every path instead of being thrown away.Rendered with that patch applied (real output, not a mock-up):
If you would rather the two lists be structurally distinct rather than one of them living inside a hint string, the alternative is a
closest::Union{Nothing,Vector{String}}keyword onAmbiguousDescriptionplus aClosestentry in_build_primary_pairs. Non-breaking (defaulted keyword), but it touchestypes.jlanddisplay.jlas well; the two-line version above needs neither.Reproduction
Display-only, as before;
complete's resolution behaviour is not involved.