election.my
Malaysian election news, mapped to the seat and the party.

How this index decides what a story is about

Every page here makes one claim: that the constituency, party or candidate attached to a story is the right one. This page is that claim's working. It is the rules themselves — what has to be true before a row is written, and what this index will not answer even when asked.

The one invariant

A lookup that misses returns nothing. It never falls through to a fuzzy, prefix or closest match. A miss goes to a human review queue — it is not guessed.

Everything below is a consequence of that sentence. Ayer does not resolve to Ayer Keroh, Ayer Molek or Ayer Limau; it resolves to nobody, and a person decides later whether it should ever have. The reason is not caution for its own sake. A fuzzy matcher does not produce a few soft answers — it produces confident wrong ones, silently, at the scale of the whole corpus, in the single thing this index claims to get right. The miss → reviewed alias → permanent exact match loop is slower than fuzzy matching and it is the only version of this that is worth publishing.

Two gates, two different questions

A story has to clear both before anything is recorded about it. They are independent, and neither can do the other's job — conflating them was the first real defect this index had in live data.

Relevance Is this election coverage at all? Scored from the upstream category, the electoral vocabulary in the title and summary, the topics, and the organisations named. Fails closed: no signal means not relevant.
Resolution Which seat, party or person does this string denote? Matches the registry exactly, or returns nothing.

Gate one — a mention is not aboutness

The failure that produced this gate is worth stating plainly, because it is the kind a green test suite never catches. A story headlined Trio charged with torching cars belonging to Cikgu Chandra's wife was published by an earlier version of this index as coverage of a state constituency, because it happened in Bandar Kinrara — the township. The string matched correctly and unambiguously. The claim this article is about that seat was still false.

Relevance is scored per article and the threshold depends on where the item came from, because the two ingest paths hold different evidence:

Newsroom feed Arrives saying nothing about elections, so it must earn 3 points.
Seat-targeted search Already carries the fact that someone searched for a named seat, so it is admitted at 2 — and the front page then filters harder still, so a weak match appears on its own seat's page as one scored row among many, never as a lead.

Of 99,803 articles scanned, 16,517 ended up carrying a resolved entity and 83,286 carried none. The refused majority is the gate working, not the gate failing; what it threw away is published in full, broken down by the upstream newsroom's own category labels.

One sharp edge inside the gate, because it bit: term matching is a substring test, which in Malay is a feature — calon reaches dicalonkan, kempen reaches berkempen, and the whole morphological family comes free. For short terms the same rule was a live defect: mp fired inside pimpin and kampung, dun inside kandungan and dunia, and together they were enough to admit a drug-seizure story as election coverage. Terms of four characters or fewer now require a word boundary on both sides; longer ones do not.

Gate two — which entity, exactly

The registry is generated from official sources, never hand-edited, and it is the authority. Where a published mockup or a secondary source disagrees with it, the registry wins and the other is corrected.

822Seats
600DUN, across 13 states
222Parliamentary
2,211Seat aliases
82Parties & coalitions
8,021Candidates

Redelineation is the core hazard, and it runs the opposite way to intuition. Seat numbers move between cycles while names persist: Melaka's Alor Gajah was P110 in 1986 and is P135 today; Johor's Tenang was N5 and later N7. So the registry is tiered. A current name of the current delineation is authoritative. A historical name resolves only where it is unambiguous and not shadowed by a current name. A historical name that points at two different seats is dropped — 75 of them are, and they are recorded rather than silently discarded. A current name always beats a historical one.

DUN and parliamentary names live in separate alias namespaces, because a name can legitimately be both: Bukit Katil is a Melaka state seat today and the former name of a parliamentary one. A lookup returns both. That is two entities, not an ambiguity.

Places that are not constituencies

Malaysia's biggest towns are parliamentary seats, which means dozens of registry names appear constantly in copy that has nothing to do with the constituency. Putrajaya stands in for the federal executive. Petaling Jaya is where the newsrooms are. Kota Bharu is where a party congress happened to be held. Admitting those names on sight added hundreds of mentions and almost none of them were coverage.

So an ambient name — one that is both a big town and a seat — resolves only on evidence beside it: the seat code itself, or an electoral marker (kerusi Subang, ahli parlimen Subang). The evidence gates the seat, not the string that matched, or every alias of an ambient seat would walk straight past it. Which names are ambient is derived from the registry, never hand-written.

Two things were tried here and removed, and they stay removed. Agreement between the article's filing location and the seat's state is nearly free in a large state, and for a Federal Territory it is free by construction — it admitted federal-government stories to Putrajaya on no other evidence at all. And this is not a dateline artefact: datelines are stripped separately, and they explained a handful of cases out of hundreds. The cause is metonymy and institutional geography.

People

A person's name is not a fixed string. The candidate roll holds one form and newsrooms use another, and no rule derives one from the other:

As the news writes itAs the roll records it
Muhyiddin YassinTAN SRI HAJI MUHYIDDIN MOHD. YASSIN
Rafizi RamliMOHD RAFIZI BIN RAMLI
Anthony LokeLOKE SIEW FOOK
Zahid HamidiDATUK AHMAD ZAHID B. HAMID

Stripping honorifics and patronymics and folding case closes the mechanical half. The rest is genuine short-forming, and it is curated by hand — 130 aliases so far, each one a recorded human decision rather than a derived rule. Two constraints stop this becoming a guessing machine:

  1. A single token never resolves from the registry. Anwar, Zahid and Loke are among the most common unmatched strings in the corpus, and a one-word key over 8,021 people is an invitation to be confidently wrong. A bare name resolves only through a curated alias — and never shadows a fuller one, because the full normalised name is tried first. Where the operator has not been willing to say which person a bare name means, it resolves to nobody and stays that way, however often it appears.
  2. A name two people claim resolves to neither. 137 normalised names collide in the roll and are dropped. These are not duplicate records waiting to be merged: two men can share a name, a party and a decade. The roll keeps them apart on their registry identifier and it is right to — merging them would fuse two political biographies, and every article about either would then carry a claim that is false about the other.

Identity is the roll's own candidate identifier, not the name, which is what lets one person keep one identity across every spelling and every election. Anyone who has never contested — appointed senators, party officials, civil servants — has no such identifier and is therefore not in the registry at all. They can only arrive through the review queue and a human decision.

Parties and coalitions

Party tagging reads the entities already extracted from an article, never the article text. The registry holds 82 parties and coalitions with 174 aliases, and the same fail-closed rule applies: an organisation named in political news is not thereby a political party. Election commissions, police, ministries, statutory bodies, pollsters, broadcasters and universities are refused by name. State chapters resolve to their parent only where someone has written that alias down.

One case makes the shape of the problem concrete. PBB is the United Nations in Malay and a Sarawak political party. The registry is consulted before the refusal list, not after, so a genuine party is never suppressed by a list someone wrote earlier.

What this index refuses to do

These are not omissions. Each one is a thing that would make the site look more complete and would make it less true.

  1. No fuzzy matching, anywhere in the resolver. Approximate matching exists in exactly one place — a script that builds a review list for a person to read. It never writes an alias and the resolver never imports it. Its own output is the argument for the boundary: of its top near-match suggestions, most were wrong, including an Indonesian city onto a Johor seat and a Kuala Lumpur district onto a Selangor one. A fuzzy resolver would have accepted every one of them silently.
  2. No full article text, ever. Headline, a short extract and a link to the original publisher. The stories belong to the newsrooms that reported them.
  3. No structured data claiming authorship of a borrowed headline. The machine-readable markup on an entity page describes the entity — the constituency, the party, the person — because that is what is ours to describe. Marking a borrowed headline up as this site's own would contradict, in machine-readable form, what the footer says in plain words.
  4. No guessed external identities. No page here asserts that a named person is the same person as an entry in some other database. The registry holds no such identifiers, and inventing one during an election period is a false claim about a real person. The candidate identifier is published instead, because it is real.
  5. No identity numbers or other sensitive personal data. Candidate names and public party affiliation only.
  6. No language guessed from the query, and none from a single word. Most of this corpus is in Bahasa Melayu, some of it is in English and a little is in Chinese, and there is no language column anywhere to read it from — not in the newsroom store, not in this index's own. The one adjacent signal is the language a search query was issued in, which is a fact about the query and not about the article. So each rendered string is decided on its own evidence: Chinese by script, which is not an inference at all, and otherwise by counting closed-class function words. A verdict requires every marker on one side and at least three of them — the same rule as the one that stops a bare first name resolving to a candidate. A string with markers on both sides is genuinely mixed and is left unmarked, as is a headline too short to carry three. That is most headlines, and they simply inherit the page's language exactly as they did before.
  7. No modification dates on the sitemap. There is no honest per-page value available, and a build-time date would tell a crawler that every page on the site changed on every deploy — which is how a site teaches a crawler to ignore the field.
  8. No forecast, and no polling date that has not been called. This is a news index, not a prediction. Seat-by-seat analysis of a single contest is a different product and lives at pilihanraya.my.

Coverage, and what it is not

The crawl plan is the seat registry: this index asks for coverage of a named constituency rather than reading everything and hoping. On a matched sample that approach returned a seat for roughly a third of what it fetched, against a few percent for a conventional source-first crawl of the same corpus under the same gate — and, more usefully, most of what it found was not in the general crawl at all, because hyperlocal outlets that cover one state well are not on a national source list.

Alias curation is demand-driven: a seat earns richer aliases when coverage actually names it. So the registry is national and complete from the first day, and the alias depth is not uniform across it. A quiet seat page is not evidence of a quiet constituency — it may be evidence of a thin crawl. Every seat page therefore renders even when it has no coverage at all, because the absence is itself an honest answer to a reasonable question.

810 place strings, 1,666 person strings and 442 organisation strings are currently unresolved and queued for review, on top of the 285 names — states, countries, foreign capitals — refused before anything is looked up. That queue is the backlog, and it is published rather than described.

Corrections

The intended failure mode of this index is a missing answer, not a wrong one. If a seat, party or candidate is attached to a story it is not about, that is a defect and worth reporting — as is a name that should resolve and does not, which is an alias nobody has written yet. Every resolution on this site is shown with the string that matched, the method that matched it and the registry version it was decided under, so a disputed one can be pointed at precisely.

The resolver can be run against any string, live, on the seat lookup — it is the same code path this page describes, and it refuses the same way.