Repository navigation
feat(ios): add unified local search — private FTS5 index, Search screen, and Linux eval package - #62
Draft
obvious-autobuild[bot] wants to merge 11 commits into
Draft
obvious-autobuild[bot] wants to merge 11 commits into
obvious-autobuild[bot] wants to merge 11 commits into
Conversation
cookingJournal() read cooked_at with `as? Date`, which failed on the stored value and silently fell back to the current date, corrupting journal timestamps downstream (including search date tokens). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
A private SQLite FTS5 index (own file, no shared migration ownership) built from read-only adapters over ingredient, inventory, recipe, and cooking-journal sources. Includes typed record kinds and canonical IDs, query normalization/tokenization with a length cap, deterministic bm25-based ranking, live hit resolution with stale-record cleanup, epoch-driven lazy revalidation, and restore-notification handling. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…tion A Linux SwiftPM package (no Xcode required) that copies production search and persistence sources and runs document, engine, and lifecycle tests plus a retrieval evaluation over a frozen 83-document, 19-query corpus generated by its Scripts/gen_corpus.py, scoring recall@K, precision@K, and MRR with an optional JSON report. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
AppDependencies builds the shared SearchIndexService beside the app database, using a file-stat epoch provider and keeping the restore observer alive for the process lifetime. A magnifying-glass entry on the home stack opens the Search screen, which debounces queries, filters by source kind, and resolves tapped results against the live repositories into a detail preview. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
This reverts commit 5341489.
…ompile errors Restores the searchIndexService and searchRestoreToken stored properties in AppDependencies (a parallel edit had dropped the declarations while keeping their initializers), resolves the SearchScreen type-inference errors flagged by hosted CI (explicit Hashable on SearchKindFilter, String empty-state message), and reverts the out-of-scope UserDataRepository timestamp-decoding change. The journal search adapter now reads cooking_history joined to recipes directly, so journal date tokens no longer depend on the reverted repository path, and SearchSources drops its journal repository parameter. Co-authored-by: obvious-autobuild[bot] <obvious-autobuild[bot]@users.noreply.github.com>
Replaces the 19-query hand-written corpus with 839 frozen queries whose expectations come from a construction-based oracle: the oracle re-implements the matching contract (unicode61 tokenization per indexed column, implicit-AND prefix terms, underscore-kept query tokens as FTS5 adjacency phrases) independently of the engine code path. Adds the committed evaluation report for the frozen corpus (recall@K, precision@K, MRR all 1.0) and updates the check-package fixtures and journal-adapter mirror to the current sources. Co-authored-by: obvious-autobuild[bot] <obvious-autobuild[bot]@users.noreply.github.com>
…rload collision The earlier wiring commit contained only the navigateToSearch references in destination tracking and state reset; the stored property, the entry point, and the destination never made it into the pushed file (a parallel-edit clobber before commit). This adds the @State declaration, a trailing magnifying-glass toolbar button on the home stack, an openSearch() action consistent with the other navigation actions, and the SearchScreen navigation destination. Also replaces the index-path construction with plain concatenation: on macOS, UniformTypeIdentifiers adds appendingPathComponent(_:conformingTo:) to String, and that overload won resolution over the NSString one, failing hosted CI with a missing-argument error (Linux never sees it). Co-authored-by: obvious-autobuild[bot] <obvious-autobuild[bot]@users.noreply.github.com>
SearchScreen's tap path called SearchIndexService.resolve, which did not exist (hosted run 38095507072, SearchScreen.swift:154: value of type 'SearchIndexService' has no member 'resolve'). The service already holds the same CompositeSearchHitResolver the engine uses for ranking; this exposes resolution through the service so taps read live repositories, never cached index data. resolve(_:) refuses deleted records (repairing the dead document out of the index) and refuses stale hits when the kind carries a checkable live revision (inventory, journal), leaving the document in place for the next epoch-triggered rebuild to heal, matching the engine's documented staleness contract. Isolated tests cover: tap on valid recipe/journal/inventory/ingredient hits resolves the canonical record; deleted records are refused and repaired out of the index; stale journal and inventory hits are refused without mutating the index. The 839-query evaluator reproduces the committed report byte-identically across two fresh runs. Linux: unified-search-check 42/42, historical-nutrition-check 12/12. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.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.
What changed and why
Unified local search for FridgeLuck, built so the search index never shares migration ownership with the app database:
apps/ios/Platform/Search/): the index lives in its own SQLite file (fridgeluck-search-index.sqlite, created next to the app database). The shared migration list inDatabaseMigrationsis untouched — the index stores its own schema version and rebuilds from sources when the file is missing, corrupt, or at an older schema version.IngredientRepository, inventory,RecipeRepository, and cooking-journal sources produce typed documents (ingredient / inventory / recipe / journal) with canonical string IDs. Inventory canonical IDs matchInventoryActiveItem.idexactly.cooking_historyjoined torecipesthrough typed GRDB reads directly, so search date tokens never depend on the journal aggregate path. An earlier commit also fixedUserDataRepository.cookingJournal()silently falling back to the current date forcooked_at; per review, that out-of-scope change was reverted (commitbf3f2ba) — the fix belongs to a dedicated follow-up PR.AppDependenciesbuilds the sharedSearchIndexServiceand keeps the restore observer alive for the process lifetime. A magnifying-glass entry on the home stack opens the new Search screen (Feature/Search/SearchScreen.swift), which debounces queries, filters by kind, and resolves tapped results against the live repositories into a detail preview.apps/ios/Tools/unified-search-check/): copies production search and persistence sources (no Xcode needed) and runs document, engine, lifecycle, and evaluation tests. Retrieval evaluation uses a frozen corpus (Scripts/gen_corpus.py→ 83 documents: 24 ingredients, 9 inventory records, 30 recipes, 20 journal entries; 839 queries) scored with recall@K, precision@K, and MRR. Expectations come from a construction-based oracle that re-implements the matching contract (unicode61 tokenization per indexed column, implicit-AND prefix terms, underscore-kept query tokens as FTS5 adjacency phrases) independently of the engine code path, so an engine regression shows up as a metric drop rather than a silent pass. The committed report isReports/eval-report.json(corpusunified-search-eval-corpus-v2).Review fixes (round 2)
searchIndexService/searchRestoreTokenstored-property declarations inAppDependenciesthat a parallel-edit clobber had dropped (their initializers and uses were pushed but the declarations were missing — the root cause of the hosted-CI compile errors).SearchScreentype errors CI reported: explicitHashableonSearchKindFilter, the empty-state call passes aString, and the filter-button content is disambiguated.UserDataRepositorytimestamp-decoding change and reworked the journal adapter to readcooking_historydirectly;SearchSourcesno longer takes a journal repository.chickenalso matches four "Chicken …" journal entries.Tap-resolution fix (round 3)
Hosted job
114340457509(run38095507072, branch heade1e2c52) failed atSearchScreen.swift:154: the tap path calledSearchIndexService.resolve, which did not exist on the service. Fixed branch-locally in1e72cba(base branch and CI files untouched):SearchIndexServicenow exposesresolve(_ hit:). It runs the epoch revalidation, then resolves the hit through the sameCompositeSearchHitResolverthe engine uses for ranking — so taps read live repositories and never cached index data — and refuses:SearchResolveTests.swift) use the production resolver through the service — no stubs — with a constant test epoch so direct database writes do not trigger a rebuild and the index intentionally keeps stale/deleted documents whileresolveitself must do the refusing. Coverage: valid taps on recipe / journal / inventory / ingredient hits resolve the canonical record; deleted records are refused and repaired (6 → 5 docs); stale journal (rating change) and stale inventory (last_updated_atchange) hits are refused without mutating the index.How I checked
swift testin the check package on Linux (Swift 6.4, x86_64-unknown-linux-gnu): 42/42 tests pass — 35/35 baseline before the fix, +7 new resolve tests, all green after (mirrors refreshed viaScripts/refresh.sh).EVAL_REPORT_PATHset,--filter SearchEvalTests) wrote reports byte-identical to each other and to the committedReports/eval-report.json— SHA-2562e914784dce6071a4cd01fa1ae32781c57c0603da52c0ceabed42eabf41a57aafor all three. Metrics: mean recall@K, precision@K, and MRR all 1.0.historical-nutrition-checkon Linux: 12/12 pass after running its ownScripts/refresh.sh(its mirrors are untracked in a fresh checkout and must be refreshed first).1e72cba: run38103789437— success; both jobs (build-and-test,dead-code-advisory) passed. This is the compile gate for the app target and includes the app-side tests.SearchText.tokenswas caught by the eval itself (cheddar_cheese) and fixed in the oracle.What I could not check
apps/ios/Tools/barcode-checkdoes not exist on this branch — it belongs to the barcode-intake branch (PR feat(ios): barcode intake — FLBarcode module, GTIN validation, source-pinned cache (milestone 1) #51) and cannot be run here. On this branch onlyunified-search-checkandhistorical-nutrition-checkare Linux-runnable.Scripts/refresh.shper check package beforeswift test; the mirrored sources are untracked.Handoff
1e72cbaonobv/fl-l2-unified-search; hosted run38103789437succeeded on both jobs. The previously failing run38095507072(job114340457509,SearchScreen.swift:154—SearchIndexServicehas no memberresolve) was fixed by1e72cba, and that fix's own CI run is the green one recorded above. No "compilation is fixed" claim predates that run.AppDependenciesstored properties; explicitHashable+Stringempty state inSearchScreen); ② 500+ query evaluation → 839 frozen queries with a construction-based oracle and committed report (reproduced byte-identically this round); ③ out-of-scope timestamp change → reverted, journal adapter readscooking_historydirectly; ④ incorporate snapshot parent6ee55e7→ merged, history preserved; ⑤ missing tap-path resolution →SearchIndexService.resolve(_:)with deleted/stale refusal, isolated tests added.FLFeatureLogiccontains an inheritedimport CoreGraphicsinFeatureLogic/Assistant/LiveAssistantPanelLayout.swift(from commitd48f385). CoreGraphics does not exist on Linux, so any Linux suite that path-depends on theapps/iospackage cannot build here. This PR's check packages copy individual files instead of path-depending, so they are unaffected. Fixing that import belongs to whoever owns the Live Assistant feature.cd apps/ios/Tools/unified-search-check && bash Scripts/refresh.sh && EVAL_REPORT_PATH=<worktree-internal path>/eval-report.json swift test(Swift 6.4 on this sandbox; avoid/tmpfor the report path on shared sandboxes). Regenerate the corpus withpython3 Scripts/gen_corpus.py(deterministic, byte-identical).UserDataRepository.cookingJournal()current-date fallback remains a real production bug now that the fix is out of this PR — it deserves its own small PR; restore-notification posting is still unwired (as before, by design).Assumptions
FridgeLuckUserDataDidRestore) is observed but never posted by this module; a follow-up wires it to the backup/restore flow (PR feat(ios): add local backup archive codec, validator, and staged restore engine #61's restore currently replaces source records without posting it).Human author: Sam Gu (sgu07966@gmail.com)
🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact