Repository navigation
fix(browse): refresh the table once an import's job completes - #1938
Merged
Merged
Conversation
An import into a table that is already open left the grid, its count and the database overview on the rows from before the import, until a manual Refresh. The import only refetched describe_all and navigated to the table. Navigating to the table already on screen fetches nothing, because every Browse read has a 60s staleTime. This regressed in #1471, which moved the import modal into DatabaseActionModals and dropped the table view's own refreshTable() call for the same-table case. #1880's job-following toast did not change it. useImportDataMutation now refreshes from the mutation's own onSettled, so the refresh does not depend on the modal still being mounted. It invalidates the table's queries ([entityId, database, table]: the page, a filtered search, the exact count, describe_table) and describe_all. The refresh is not awaited, so the import still settles, and the modal still closes, at the job's verdict. onImported waits for describe_all before it navigates, as it did before, but now joins the mutation's refetch (cancelRefetch: false) instead of starting another. Every outcome invalidates, because any of them can have written: a job that skipped rows, a job that ended in ERROR (csv_url_load inserts chunk by chunk, so earlier chunks stay written), a wait that timed out on a load still running, a load refused after the import's create_table. A failure with no verdict (no response, or a 502/503/504) only marks the reads stale instead of refetching them, since they would fail too and add their own error toasts. The databases DESIGN.md records where the refresh lives and why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth
marked this pull request as ready for review
October 13, 2026 16:02
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
This was referenced Oct 13, 2026
Contributor
There was a problem hiding this comment.
Code Review
This pull request refactors the database import flow to trigger Browse refreshes directly from the useImportDataMutation's onSettled callback rather than the modal, ensuring that data is refreshed even if the modal unmounts. It introduces logic to refetch queries when the instance responds, or mark them stale when there is no verdict (e.g., network errors or 502/503/504 statuses). Additionally, comprehensive tests and design documentation have been added. Feedback on the changes suggests adding .catch() handlers to the fire-and-forget queryClient.invalidateQueries calls in refreshImportedTable to prevent unhandled promise rejections.
The import refresh only marked Browse stale, without refetching, when a failure had no verdict (no response, or a 502/503/504). But that is the case whose toast says "Outcome unknown" and asks the user to check whether the import took effect. For example, central manager can answer 504 on a long insert that Harper goes on to apply. The open grid then kept the cached rows from before the import, so the check found nothing new, and a retry would insert every record without an id a second time. Every outcome now refetches what is on screen. When the instance really is down, the re-reads fail too and add their own error toasts, which is cheaper than a grid that invites a duplicating retry. invalidateQueries does not reject on a failed refetch unless throwOnError is set, so a failed re-read is not an unhandled rejection; a test pins that. The refresh also invalidates the open record's search_by_id key, which the table prefix does not reach. Every import only inserts, so this only matters for a cached "not found" for an id the import then wrote. NO_VERDICT_STATUSES goes back to being private to unansweredWrite.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Problem
A CSV import into the open table follows its job, but when the job succeeds and the dialog closes, the grid, its count and the database overview still show the rows from before the import until a manual Refresh (found by Dawson hand-testing the release candidate on stage).
Every Browse read has a 60s
staleTime, and the import only refetcheddescribe_alland navigated to the table already on screen, which fetches nothing. #1880 did not cause this. #1471 (125b6e57d) did: when it moved the import modal fromDatabaseTableViewintoDatabaseActionModals, it dropped the table view'srefreshTable()call.Solution
useImportDataMutationrefreshes from its ownonSettled, so the refresh still happens after the modal unmounts (React Query skips amutatecall's own callbacks then). It invalidates[entityId, database, table](page, filtered search, exact count,describe_table), the open record'ssearch_by_idkey, anddescribe_all.The refresh is not awaited, so the import settles and the modal closes at the job's verdict, as before.
onImportedstill waits fordescribe_allbefore navigating, orresolveDatabasesRedirectwould send a table the import just created back to the overview. It now joins the mutation's refetch (awaitDatabaseMapRefresh,cancelRefetch: false) instead of starting a second one.Every outcome refetches, failures included, because any of them can have written rows. The strongest case is a submission with no verdict (no response, or a 502/503/504): its toast says "Outcome unknown, check whether it took effect", and a grid left on cached rows would invite a retry that duplicates every record without an id. When the instance is really down, the re-reads add their own error toasts; that is the accepted cost.
Every outcome that can write, and why a failed re-read is safe
successfully loaded 3 of 5 records, or a JSON insert's(N skipped)).ERROR:csv_url_loadinserts in 2 MB chunks (harperdataLayer/bulkLoad.ts,insertChunk), so the chunks before the failing one stay written.create_tablemade the table.invalidateQueriesswallows fetch failures unlessthrowOnErroris set (query-core 5.104.1src/queryClient.ts:520-521), so a failed re-read is not an unhandled rejection. A throw from the refresh itself is caught, so it cannot turn a completed import into a failure.No other job follower has this gap.
waitForJobis used only by the import, andfollowRestartJob's callers await it throughmutateAsyncand then invalidate.The databases
DESIGN.mdrecords where the refresh lives and why.Verification
importData.test.tsxandDatabaseActionModals.test.tsxuse the real query-key builders and Browse's own query options. Against stage's sources, 10 of the 21 tests in those two files fail.npx vitest run --maxWorkers=4(524 files),npx tsc -b,npx oxlint --format stylish .andnpx dprint check. Under load from other sessions,JobsTab's 200-row test (from feat(status): list Harper jobs on a Jobs tab, and follow a CSV import's job in a toast #1880, untouched here) times out at 5s, even run alone.Test cases, mutants and review history
Tests:
describe_tableanddescribe_allafter the modal unmounted. It marks the filtered search, exact count and open record stale, and leaves another table alone.describe_allis in flight, and the map wait joins that refetch;onImportednavigates only oncedescribe_allis back.The 11 mutants:
onImportednot waiting;throwOnError: true, where vitest fails the run with an unhandledAxiosError: Network Error.Cross-model review, five rounds (codex, gemini, Cursor, Harper domain adjudication). The closing full round ran on
e59d02f37, and the delta round on3d8f58efcconverged.Changed from review:
create_tablerefetches;search_by_idis invalidated;Declined:
.tsimport extensions: none of the 1,369 relative imports insrchas one, and tsconfig doesn't allow them.settleQuietlyand join-order concerns:progress.onSuccessis a synchronous toast, andonImportedruns afteronSettled.Pre-existing, worth a separate issue:
waitForJobgiving up on a running load reports a plain failure, not "Outcome unknown" (getJob.ts:67-68,:89-90).describe_allrefetch still letsonImportednavigate against the old map.Related PRs: #1864 overlaps (already in stage; same DatabaseActionModals files, but the Add Records path, not the import refresh), 4 others independent
🤖 Generated with Claude Code
Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex,cursor-muse; adjudicated=domain; declined=cursor-grok,cursor-kimi,muse; rounds=5; full=2 @ 3d8f58e
Review-Attention: read ~3m (decisions: refetch-after-any-outcome, describe-all-refetch-all-on-failure, join-relies-on-callback-order, do-less-alternative) @ 3d8f58e