Replace LLM ticker discovery with a curated Ticker Profile - #1060
Merged
Conversation
Query-analysis asked an LLM who each ticker's competitors and regulators were, then cached the guess in ticker_discovery for 14 days. The answers were unverifiable, capped at six competitors, and derived from IDX classification that is stale or wrong for a meaningful set of issuers. A hand-curated, search-grounded profile now supplies that landscape for 949 issuers, uploaded through the dashboard like the existing IDX import. The discovery LLM call, its cache table, TTL and contract-version invalidation are gone; recon still runs but builds its queries from curated competitors. Article-analysis gating and content-generation read the profile too, so both stop depending on the knowledge graph.
kevinhermawan
force-pushed
the
feat/ticker-profile
branch
from
July 28, 2026 09:25
e8e0aa9 to
a35f6b8
Compare
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
Query-analysis used to ask an LLM who a ticker's competitors and regulators were, then cache the answer in
ticker_discoveryfor 14 days. Those answers were guesses: unverifiable, capped at six competitors, and re-derived from IDX classification that is wrong or stale for a meaningful number of issuers. AIMS is filed as coal but exited in 2023. BATA is filed as footwear manufacturing but stopped making shoes in 2024. GMFI is filed as an airport operator but actually does aircraft MRO.A curated reference file replaces the guess.
seed-data/ticker-profiles.jsonholds 949 issuers with verified competitors, regulators, aliases, prose, and a four-level classification in both Indonesian and English. It is uploaded through the dashboard, the same way IDX tickers already are.How it works
TickerProfileis a new table, one row per ticker, populated by an admin JSON upload rather than a seed script — so correcting the data is a re-upload, not a redeploy. A read-only Ticker Profiles table renders what was loaded.The agent no longer runs a discovery LLM call at all. Recon still fires, because it is the only thing supplying current headlines to query generation, but it now builds its searches from curated competitors instead of guessed ones. A ticker with no profile still runs, degrading to own-company only with a warning, so a newly listed issuer is never blocked.
Article-analysis relevance gating and content-generation both read the profile now. Gating aliases went from
[symbol, name]to symbol, name, ticker aliases, profile aliases, competitor names and their aliases. The newsletter competitor list was empty in practice and is now populated.Notes for review
Classification is stored in both languages on purpose. The relevance gate word-matches these strings against Indonesian article text. Storing only the English side would have quietly reduced matching with no error and no failing test.
sub_industryis no longer a relevance match term. After curation it is unique across all 949 rows and averages five-plus words, so it could never match an article. It remains as prompt context.The upload payload is about 2 MB, which exceeds the default Next.js Server Action body limit, so the dashboard limit is raised to 16 MB.
Removed:
ticker_discovery(table, service, two routes, contract types), the 14-day TTL, contract-version cache invalidation,discover-entities.tsand its schema, fourDISCOVERY_*constants, andissuer-context.ts.Verification
Full
pnpm code-qualitypasses: 108/108 tasks, lint, typecheck, tests and Prettier across the workspace.The migration swaps
ticker_discoveryforticker_profile. It is applied locally but applying it to production is a separate deploy decision — the drop is not reversible.Related issues: Closes #1058