Repository navigation
refactor(search): take API and graph types from the generated client package - #43
Merged
Merged
Conversation
…changed The Search API now lists each part's assumptions and closes the sentence with an "Assumed: ..." clause in `assumption` tokens. The answer block draws that clause as its own paragraph after the departures, and "About this answer" states each assumption from `part.assumptions` in place of the place-scope and role-scope warnings, so each is said once. A dataset_resubmit identity entry echoes the part's assumptions back unchanged, so an edit keeps the defaults it did not touch. `no_data.options` replaces `no_data.data_query`, and an entity pick posts the matching option's request. `source_code_not_held`, `source_code_ambiguous` and `source_code_shares_variable` get their own messages.
An empty list tells the engine the previous answer stated no assumptions, so the edited answer states none the echo lacks. Only a response without the field leaves the echo off.
…part sentence A sentence written part by part labels each byline with its part, so its clauses take the same label even when only one part states a clause; otherwise that clause reads as covering the whole answer.
… by their lead, and keep a share's assumptions The entity picker mapped a picked candidate to its option by dcid. A group candidate on a members row posts the group's registered name with no dcid, so the match failed and the fallback pinned the group with `members: true`, a request the API refuses. Both pickers (`SearchCards.tsx`, `SearchSentence.tsx`) now list `no_data.options` grouped by their mentions (`entityChoiceGroups`) and post the chosen option's `data_query` part unchanged; the restatement rewrite is gone. A parsed entry keeps the operations it was built with. `assumptionClauses` labels a clause by the owner of its lead token whenever the answer has more than one part, so a definite part beside a candidates or no-data part names its clause, and a clause every folded part shares carries no single part's name. A share replays through `dataset_resubmit`, where a missing echo states no declared default, so a shared ODA answer lost "official development assistance". `share_ref` keeps each part's `assumptions` (migration `0018_share_ref_assumptions.sql`), and replay echoes them; a share stored before the migration replays with none.
`isAssumption` checked an echoed assumption's shape but none of Search's bounds, so a share could store a text past 200 code points, two refs or 68 items, and every replay of it failed with `request_invalid`. The share request now applies Search's bounds on `Assumption` and on a part's list, as it does for `EntityGroup`.
…ing's on a candidates entry A candidates part's `assumptions` holds only what its readings share, and a pick echoed that list, so the picked answer dropped the reading's own open role or place scope. A pick (`candidatePickEntry`) now echoes `reading_assumptions[spec_id]`, a candidates entry echoes each reading's list on its candidate, and a reading the response gives no list for posts no echo. Only a definite part's identity entry echoes the part's own list: an edit of a no_data part reads fresh. `types.ts` mirrors `CandidatesPart.reading_assumptions` and `ResubmitCandidate.assumptions`, and the `AssumptionKind` comment uses the API's wording for an open role.
A permalink rebuilt each identity entry from four fields and dropped its `assumptions`, so a reloaded candidates pick, which echoes its reading's own list, replayed with no echo and stated no declared default. `replayableEntry` keeps the echo, `[]` included, when it passes the share request's validator (`isAssumptions`, now shared), and drops an echo Search would refuse.
A permalink replay posts each stored edit with the echo it was posted with. `withAssumptions` overwrote that echo with the fresh part's list, so an edit that had narrowed the list (a flow set back to ODA after an edit to OOF posts `[]`) replayed with the default stated again. An identity entry that already carries `assumptions` now keeps them; a live edit's entry carries none and still takes its part's list.
Search sends "Answered N of M parts." as tokens of its own `coverage` segment (DevelopmentFinanceObservatory/apis `feat/search-answer-statements`). The answer block draws it as a muted lead above the `<h1>`, beside "Closest match", and `planSentenceText` reads heading tokens only, so the heading's accessible name, the run label and the Studio hand-off question no longer open with the coverage line. `planCoverageText` reads it.
…ut this answer
Search sends an open destination role as binding_ref "where:", the slot the destination shares with the subject. assumptionSentence branched on "destination:", so the sentence dropped the provider that limits the scope ("covers every recipient" for "covers every recipient from Germany"). The why and resultNotes fixtures now carry the served ref.
…faulted slots from the About summary comments
… name the definite part a replay echoes
The site depends on @developmentfinance/client through a vendored tarball and reads every API body it uses from the package's generated wire types, so src/lib/search/types.ts derives its shapes from the served OpenAPI document. Parts narrow through isKnownPart before any payload read, optional wire members default where the site reads them, and open enums fall back where the site maps every known value. A stored rendering cut with a layout or without party totals no longer counts as the default file.
src/lib/graph/wire.ts re-exports the graph types from @developmentfinance/client, whose served document covers the Graph surface. The second generator goes: schema.d.ts, graph-api-schema.json, scripts/graph-wire.mts and its test, the graph:types and graph:schema scripts and recipes, and openapi-typescript.
flattenSpec takes the generated Spec and reads resolved.trace; the resolved.resolution fallback read a field the wire lacks and is gone. Slot options are the generated SlotOption, the share's assumptions column is typed as the wire's Assumption list, and ASSUMPTION_KINDS is a Record over the generated kind union, so a new kind fails the type check. Two fixes ride along. A shared answer whose assumption omits refs, which the wire allows, is accepted. A sentence token with no segment reads as heading in readingsSummary, as it does in sentenceBlocks, through the shared tokenSegment.
… types The stream payloads keep their hand-written envelope, since the OpenAPI document does not describe the SSE stream, and every wire member inside them takes its generated type: slot options, sentence slots, observations, facet and entity options, and the normalised response. SlotOption and ObservationPoint reads go through the generated types, and normalizeResponse returns the page ResolveResponse; tests read fixtures through resolution(), which narrows on kind. Every reader treats a token with no segment as heading through tokenSegment in sentencePlan.ts, and a segment the site does not know stays its own block, which no reader draws. The share's entityGroups and operations columns are typed from the wire, and docs/development.md says where the vendored client package comes from and how to refresh it.
vendor/developmentfinance-client-0.1.0.tgz is the TypeScript client at the sdk commit 69f7439, with the contract timeouts, assumptions on candidates and datasets, and the generated warning codes; pnpm-lock.yaml carries its integrity.
…ent's wire types SearchInput, DatasetFormat, the entity-options scope and the page-spec subsets in definitionMembers.ts, frozenObservations.ts and measureMenu.ts derive from the wire types. A spec whose stat vars the site cannot read is UnreadableSpec, one side of the PartSpec union, and statVarsUnreadable is its type guard; flattenSpec returns the union with no cast. The stream's facet_options entries are FacetOptionsEntry, FacetOptions without source, which the stream does not send. The suggestion state, the streamed observations and facets, the cards' parts and the share, event and sentence readers carry their types, and hasDefiniteSpec narrows a hydrated part to one with a spec. The shared edits move into sharedEdit.ts as pure functions with tests: a definite spec takes an edit when its slots hold the edit's axis, readable or not, and a spec without slots is skipped. fetchDatasetCapabilities, which had no caller, is gone.
The package resolves its key when the client is built, carries each response's trace id, follows the download rules in a browser and refuses a secret key in a worker. pnpm-lock.yaml pins the new tarball's integrity.
The package keeps decimals exact, follows only a 302 from the files route, raises a non-retryable 429 poll and checks where a browser's followed redirect ends. pnpm-lock.yaml pins the new tarball's integrity.
The client refuses a files-route 302 whose Location is empty.
Split mid-sentence colons and semicolons into sentences, and drop em dashes and contrast tails in the comments the generated client types brought, and in the vendoring note in docs/development.md.
The package carries the tightened JSDoc in its type declarations.
jm-rivera
added this pull request to stack #44
October 21, 2026 15:15
jm-rivera
marked this pull request as ready for review
October 23, 2026 06:25
# Conflicts: # db/schema.ts # package.json # pnpm-lock.yaml # src/components/organisms/DataSearch/SearchSentence.tsx # src/lib/search/datasetEntries.ts # src/lib/search/departures.ts # src/lib/search/readings.ts # src/lib/search/searchClient.ts # src/lib/search/searchShare.ts # src/lib/search/searchShareRequest.ts # src/lib/search/sentencePlan.ts # src/lib/search/test/datasetEntries.test.ts # src/lib/search/test/departures.test.ts # src/lib/search/test/fixtures.test.ts # src/lib/search/test/parsedEdit.test.ts # src/lib/search/test/searchShareRequest.test.ts # src/lib/search/test/view.test.ts # src/lib/search/types.ts # src/lib/search/view.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacks on #42.
The website takes its API and graph types from
@developmentfinance/client, vendored asvendor/developmentfinance-client-0.1.0.tgzand built from DevelopmentFinanceObservatory/sdk at 3d8bb48 (DevelopmentFinanceObservatory/sdk#2).src/lib/search/types.tsholds only the page's own types and the stream payloads the OpenAPI document does not describe, and this removes the hand-mirrored wire types,scripts/graph-wire.mts,src/lib/graph/schema.d.tsandsrc/lib/graph/graph-api-schema.json. Specs, slot options, assumptions, stream entries and observation members read as the generated wire types, and an open enum keeps any value a newer server sends.Typing against the wire changed some behaviour, and each change needs a reviewer's eye.
renderingArtifactinsnapshot.tsskips layout andwithout_party_totalsrenderings, and a null member reads as absent.storedDownloadreturns the file's digest.handoffSeedsdrops a verb it does not know. Unknown verbs, roles and gates get fallback text. A shared edit skips a spec with no slots (sharedEdit.ts). A specflattenSpeccannot read counts as 0 stat vars insearchEvents.ts.NO_DATA_REASON_TEXTinview.tsholds the copy for each no-data reason the client types name, and that copy needs an editor's read.docs/development.mddescribes how to vendor a new package version.Testing: typecheck, lint, the 1,612 tests outside
db/andpnpm buildpass.