Skip to content

fix(search): keep each ambiguous place its own choice - #119

Merged
jm-rivera merged 1 commit into
devfrom
fix/post-113-findings
Oct 25, 2026
Merged

jm-rivera merged 1 commit into
devfrom
fix/post-113-findings

Conversation

@jm-rivera

@jm-rivera jm-rivera commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Two places written alike in one query merge into one choice on dev. "aid from Congo to Congo", read as the Democratic Republic of the Congo to the Republic of the Congo, offers options that pin both ends to one country, because an entity's text is its identity on the parsed door and the raw-text door renames an extracted name to its span.

An entity's identity is now its dcid, else its name, else its text (ParsedEntity.identity in models/request.py). text holds the request's words and feeds the resolver. The optional name gives an unpinned row an identity apart from its words, so {text: "Congo", name: "Democratic Republic of the Congo"} and {text: "Congo", name: "Republic of the Congo"} ask two choices. A name beside a dcid refuses request_invalid, as does an unpinned row whose name or text is a dcid another row pins. Pins, roles, members, dedupe and per-part resolution key on the identity, so a chosen row posts its words and dcid with no name, two pins are two entities, and "aid from Kenya to Kenya" stays one entity on two axes. The raw-text door keeps extracted names as identities and records each name's grounded span in RegionResult.entity_surfaces, which resolution reads.

Both doors pass the request's words to the leg (ResolveState.entity_words), so a pinned row's words are masked in the variable phrase, and every leg call on both doors passes them, so every user-facing quote of an entity reads them through words_of: warnings, departures, role-not-admitted and unresolved terms. surface_resolution.is_surface and the parsed door compare words through qualifier_match._normalize, so words differing only in case are one text.

conjoin.py builds each option from the asked words and drops any the parsed door would refuse (entity_checks.part_entities). NoDataOption.entity_choice and SentenceToken.entity_choice carry the index into no_data.ambiguous_entities of the group an option settles and the slot asks, since two groups can share their words. Repeated words in an entity_ambiguous message name each group by direction, "Congo" (from) and "Congo" (to), or by ordinal; two untold places written alike carry their graph labels in a role_not_resolved refusal. Part.entity_refs has one entry per restatement row. Conformance clauses C12, C50c, C50e and C141a state the rules.

dfo-website groups menus by entity_choice with a fallback to mention text in DevelopmentFinanceObservatory/website#66, so the site works before and after this deploys. The spec-kit capture and the dfo-ai mirror pick up name, entity_choice and the entity_refs description after deploy.

Testing: each new test fails without its change, including every option of "Congo to Congo" and of two pinned Congos answering 200, a dropped direction quoting "Kenya" for a pinned row, and the alike-words messages. Locally tests/engine, render, models, contract and platform passed, as did ruff, format and ty check.

Co-authored-by: Claude noreply@anthropic.com

@jm-rivera
jm-rivera marked this pull request as ready for review October 24, 2026 16:42
@github-actions github-actions Bot removed the run-eval label Oct 24, 2026
@jm-rivera jm-rivera changed the title fix: keep each ambiguous place its own choice, and reset only a done operation the head check rejected fix: number each ambiguous place choice, and reset only a done operation completed before the head read Oct 24, 2026
@github-actions github-actions Bot removed the run-eval label Oct 24, 2026
@github-actions github-actions Bot removed the run-eval label Oct 24, 2026
@jm-rivera jm-rivera changed the title fix: number each ambiguous place choice, and reset only a done operation completed before the head read fix: keep each ambiguous place its own choice, and reset only a done operation completed before the head read Oct 24, 2026
@jm-rivera
jm-rivera force-pushed the fix/post-113-findings branch from 84c4f2d to 5f28d91 Compare October 25, 2026 07:50
@jm-rivera jm-rivera changed the title fix: keep each ambiguous place its own choice, and reset only a done operation completed before the head read fix(search): keep each ambiguous place its own choice Oct 25, 2026
@github-actions github-actions Bot removed the run-eval label Oct 25, 2026
@jm-rivera
jm-rivera force-pushed the fix/post-113-findings branch from 5f28d91 to 53c6101 Compare October 25, 2026 14:46
@github-actions github-actions Bot removed the run-eval label Oct 25, 2026
@jm-rivera
jm-rivera force-pushed the fix/post-113-findings branch from 53c6101 to a2e2a18 Compare October 25, 2026 16:07
@github-actions github-actions Bot removed the run-eval label Oct 25, 2026
…er's words

Two extracted names written alike in the query ("aid from Congo to
Congo") stay two choices. An entity's identity is its dcid, else its
name, else its text (ParsedEntity.identity). text holds the request's
words and feeds the resolver; the optional name gives an unpinned row
an identity apart from its words. A name beside a dcid refuses
request_invalid, as does an unpinned row whose name or text is a dcid
another row pins. Binding, roles, pins, members and options key on the
identity, so a chosen row posts its words and dcid and two pins are two
entities, while a self-flow pinned twice stays one.

The raw-text door records each name's grounded span in
RegionResult.entity_surfaces, which resolution reads, and both doors
pass the request's words to the leg as entity_words, so a pinned row's
words are masked in the variable phrase. surface_resolution.is_surface
and the parsed door compare words through qualifier_match._normalize.
entity_checks.part_entities holds the parsed door's entity checks, and
an option the door would refuse is not offered.

Every user-facing quote of an entity reads its words (words_of):
warnings, departures, role-not-admitted and unresolved terms. Repeated
words in an entity_ambiguous message name each group by direction or
ordinal, and two untold places written alike carry their graph labels.
NoDataOption and SentenceToken carry entity_choice, the index of the
group an option settles and the slot asks, and Part.entity_refs has one
entry per restatement row. Conformance clauses C12, C50c, C50e and
C141a describe the rules.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant