Status: Implemented
Date: 2026-10-12
OpenAI model: gpt-6-luna (cheap, accurate; $0.10 / $0.50 per 1M tokens)
NL query
→ OpenAI interpretation (gpt-6-luna, reasoning_effort=none)
→ merge with user filter edits
→ Indeed fetch (paginated, max 4 pages)
→ local JobListingFilter
→ dedupe by indeedJobKey
→ availability validation (source signals)
→ optional OpenAI / heuristic ranking
→ in-memory cache (15m TTL)
→ UI results
| Component | Path |
|---|---|
| Orchestrator | Services/Search/JobSearchPipeline.swift |
| Local filter | Services/Search/JobListingFilter.swift |
| Dedupe | Services/Search/JobListingDeduper.swift |
| Criteria merge | Services/Search/SearchCriteriaMerge.swift |
| Ranking | Services/OpenAI/OpenAIRankingService.swift |
| Validation | Services/Validation/ListingValidationServices.swift |
| Cache | Services/Indeed/InMemoryIndeedListingCache.swift |
| Model default | Services/OpenAI/OpenAIModels.swift → gpt-6-luna |
Live DI uses MockJobDataSource (via JobDataSourceIndeedAdapter) with openable Indeed browse/search URLs until the licensed Option C vendor is ready. Fixtures remain for mapper/contract tests.
Why links used to look “expired”: interim keys like fixture-ios-… / ios001 are not real Indeed jk values. Opening /viewjob?jk=… with those keys shows Indeed’s expired/not-found page. The app now opens /jobs?q=…&l=… (live Indeed search for that role) when the key is not a real 16-char hex jk.
JobListingOpener + OpenIndeedLinkButton are available on AI Job Search rows (and Saved Jobs). Xcode console logs (JobOpen / SearchPipeline) explain fallbacks and unavailable listings.
| Policy | Value | Use |
|---|---|---|
| Listing metadata cache | 15 minutes | Search result pages in InMemoryIndeedListingCache |
| Saved job revalidation | 24 hours | Reserved for Phase 5 persistence |
FeatureFlags.isAIInterpretationEnabled // NL → criteria
FeatureFlags.isAIRankingEnabled // optional batch rank (degrades to heuristic)
FeatureFlags.isListingValidationEnabled // source availability pass
HTTP HEAD validation exists (HTTPJobValidationService) but is not wired in live DI yet — fixtures/source signals only, to avoid unsolicited Indeed probing before vendor expired-signal policy.
indeedJobKeygpt-6-luna for interpretation + rankingJobSearchViewModel