← Back to Game
A chronological record of how Anime Semantle was designed, what broke,
and how it got fixed, newest first. This includes the missteps and
failed attempts, not just the parts that went smoothly. New entries
get added to the top of this list as changes land. For a retrospective
on the AdSense review saga specifically, see
this write-up.
Published an article testing the content-uniformity hypothesis — three questions with no answer
To test the hypothesis that all seven existing analysis articles look
uniform because they share the same "numbers plus commentary" shape,
added
one article in a different format.
It's built from veins F, I, and K — the ones
mine_content_data.py mined but that never became articles.
For a while README listed this as "D/F/I/K," but D had already shipped
as rating-myth,
so it wasn't actually available to reuse; that typo is now fixed and
only F/I/K were used. All three of these veins concluded "we measured
it and got no usable answer," so instead of ending in a table, a
callout box, and a gameplay tip like the other articles, this one
keeps what wasn't known for each question, stated plainly.
Sitemap resubmitted; set a 14-day stability deadline of 09-20 before the 6th application
Right after recovering from the deployment incident below, the sitemap
(35 URLs, all live) was resubmitted to Google Search Console. The gate
for the sixth AdSense application is whether the International
Targeting report holds at zero errors for 14 straight days; the check
deadline was set to 2026-09-20, with margin built in for the fact that
GSC reports can lag by up to 10 days.
Deployed from a stale local tree and rolled the live pool back
After committing the archive fix below locally, running the deploy
went out with a work tree that, unbeknownst to this session, had been
stuck at the 08-25 commit — fifteen commits behind origin,
including the 08-26 follow-up cleanup, several days of daily archive
auto-commits, and the 09-01 monthly data refresh (answer pool 3,110
→ 3,112). git status had reported clean and up to
date only because this session had never actually pulled from origin
— it doesn’t warn you that local is behind remote. The
game itself didn’t break (the two old pool versions were
internally consistent with each other), but for the affected window
(around 2026-09-06 20:24 KST) the answer a real player saw may not
match what the archive eventually records against the new pool, so
that date was added to SKIP_DATES. Merged origin and
redeployed immediately to restore the correct pool. Lesson: always
run git fetch && git log HEAD..origin/main
--oneline before deploying, to check whether local is behind.
Found and removed a solve-rate leak on the indexed archive, then rebuilt its density
While auditing the site before deploying the URL/IA restructure, an
unnoticed case of the exact same pattern as one of the fourth
rejection’s causes turned up in archive.html, which
is indexed. It had been rendering “N players · X% solve
rate” on every entry since 07-18; the only fix applied back then
was noindexing the aggregate stats.html page —
individual archive entries were never part of that audit, and were
most likely live throughout both the fourth (08-03) and fifth (08-19)
rejection windows (confirmed present on 12 of 52 entries). That
rendering was removed entirely. As a low-cost move against the
still-unmeasured “density per page” hypothesis raised
after the fifth rejection, the archive was also restructured into
detailed entries for the last 14 days plus a single compressed
summary row for everything older — raising the
signal-to-noise ratio per page without adding more total volume.
September answer pool update, confirmed live
The monthly pipeline completed normally on 09-01. The answer pool
grew slightly, from 3,110 entries (800 anime + 2,310 characters) to
3,112 (800 anime + 2,312 characters), and the similarity row files
were regenerated for the new pool. We compared the live site’s
data file directly against the repository and confirmed the deploy
had already caught up — in the past, a delayed deploy after a
pool change caused the archive to record answers players never
actually saw; that gap didn’t happen this time. Translation
completeness for the newly added entries wasn’t checked in
this pass, since the source files aren’t present locally.
Where the menu actually pointed, and the first screen
Reopening the Game / AI Analysis / Data menu built the day before, all three
links under “Data” turned out to be pages deliberately kept out of
search. Two of them were the list-style pages that had been a rejection reason
before — so they had ended up in the most visible slot on the site, while
the five articles actually built on the data were not in the menu at all.
Fixed by rearranging what was already there, without writing anything new.
The language-selection screen got the same treatment. It was 263 characters,
yet it is the first impression and the address search engines treat as canonical,
and it said nothing about what the site computes. It also failed to remember the
choice: picking “Continue in English” on a Korean-language browser
landed you on a page that immediately offered to switch you to Korean.
Moving Korean off the root and rebuilding the home page
Until now the Korean edition occupied the site root while only English lived
under /en/. An English speaker typing the domain directly got a
Korean screen, and there was no neutral address to tell search engines what the
default language was. Korean moved to /ko/ and the root became a
language-selection page. No automatic redirect — a redirect once caused a
duplicate-indexing incident here, and search engines advise against it anyway.
Every old address is connected by a permanent redirect so existing links keep
working.
The home page was rebuilt at the same time. The game stays in first position,
but below it Game, AI Analysis, and Data now sit at equal weight, alongside a
diagram showing how the similarity score is actually computed.
6.7x more writing, identical verdict
All six causes diagnosed after the fourth rejection were addressed before
reapplying. Reader-facing writing went from 5,385 to 35,832 characters, the
boilerplate pages were pulled out of the index, and the archive commentary was
rewritten to branch on the underlying data. The result was a rejection citing
exactly the same reason as rounds one through four.
That none of the six had recurred was re-measured and confirmed. Which leaves a
different question: not “what newly broke” but “was this
direction ever the kind of thing that could overturn this verdict.” So the
approach changed — from adding more writing to reworking the structure of
the site itself. The longer version is in the
retrospective.
Genre guides rewritten from full data, plus two new ones
The romance and isekai guides were built around a single anchor title and its
neighbours. Measuring each genre in full turned up three errors. The anchor used
for the romance guide did not carry a Romance tag at all; the claim that
“0.55–0.65 means a school romance” does not hold across all 132
romance titles (school romances sit at 0.771–0.855 with each other); and the
table still carried old translated titles that had already been corrected. All
three were fixed and each guide grew from 1.4k to 3.8k characters. Two new guides
followed, for sports and supernatural. That sports is hard not because it clusters
tightly but because everything outside it falls away only became visible
after measuring ten genres the same way.
Mining the data into five analysis articles
Built a script that sweeps the computed similarity data from several angles, then
wrote up only the axes where the results actually separated. Rating and similarity
correlate at +0.004 across 599,852 pairs — effectively unrelated. For 780 of
794 titles, a character from the work scores closer to it than the nearest other
work does. Theme turned out to split difficulty more than twice as widely as genre.
Some axes were mined and discarded. The intuition that films are harder does not
hold up numerically, and supporting characters scoring closer to their own work
than leads appears to be an artefact of how the text is assembled, so it was not
turned into a gameplay tip.
This is also when six already-published passages turned out to be wrong —
they claimed that a short synopsis produces lower similarity, and measurement
showed the short ones actually score slightly higher. It had been written without
measuring. All six were replaced with corrections.
The day the answer pool collapsed from 3,108 to 142
The monthly automated data refresh cut the answer pool from 3,108 to 142 entries,
then ran to completion and committed the result. The cause was not the intermediate
API but the source upstream of it: only requests already in cache returned, and
everything else failed — deterministically rather than intermittently, so no
amount of retrying would get through.
The intermediate layer was removed in favour of the official API. Identifiers match,
so the accumulated translation work carried over unchanged, and the usage terms are
now cleanly satisfied. To keep the same failure from passing silently again, the
collector now refuses to write anything and exits with an error when it gathers
less than 90% of the expected volume.
From 95MB down to 0.18MB per visit
Opening the game made the browser download the entire similarity dataset —
95MB raw, around 34MB even compressed — and then parse and merge all of it,
freezing the page for seconds. On mobile that meant tens of seconds and a real
data cost.
All that is actually needed is the one row for the day’s answer, so a row
file is now pre-built for every possible answer and only that one gets fetched.
32KB. That the values come out identical was confirmed not by sampling but by
comparing every possible answer-and-guess combination — all 9.3 million
pairs matched.
Strategy guide, answer statistics, and solve-rate tracking
Published a strategy guide built from real similarity data — reference
tables and a full walkthrough using measured values like "when the
answer was One Piece, Naruto scored 0.552 and Haikyu!! scored 0.000."
Also added an answer statistics page aggregating the daily answer
archive (genre distribution, anime/character ratio), regenerated
automatically every day. Finally, deployed a Cloudflare Worker + D1
API that counts daily players, solvers, and give-ups — once enough
data accumulates, per-day solve rates will be published.
Paying down SEO tech debt — redirects and the sitemap
The third review came back with the same rejection, so this time the
whole site got audited from a crawler's point of view. Two problems
surfaced. First, internal links and canonicals pointed at .html URLs
that the host 307-redirects, causing duplicate indexing and canonical
errors — all internal URLs across 50 files were unified to the
extensionless form. Second, Google hadn't re-read the sitemap in three
weeks because an old version contained an invalid changefreq value
(quarterly). A regex bug in the daily automation that silently stopped
sitemap updates was found and fixed too, and the sitemap was
resubmitted in Search Console along with indexing requests for the
new pages.
Contact email address changed
Changed the contact inbox to hograywm@gmail.com. Updated the email on
the main page footer note, the contact page, and the privacy policy
page across both KO and EN.
robots.txt cleanup, a contact page, deploy config fix
Removed a robots.txt Disallow rule that was conflicting with the
noindex meta tags, letting search engines actually read them. Added a
standalone contact page, and fixed a Cloudflare Workers deploy failure
caused by a missing wrangler.jsonc config file.
A fix that made things worse, then a change of direction
On top of the wordlist page, 36 genre pages were added — and the
second review rejected the site for the same "low-value content"
reason. The actual cause turned out to be that this had scaled up a
pattern of repackaging MyAnimeList data 34x over. Those pages were set
to noindex to remove them from search, and a daily-updating past-answers
archive unique to this site was built instead. The about page was also
updated with the actual measured data behind the similarity weighting
choice. A redirect-loop SEO issue on the about/privacy/archive pages
was discovered and fixed during this same investigation.
"Low-value content" rejection, and a list page in response
AdSense rejected the site for "content with little value." Assuming
the standalone game itself was too thin, a full anime/character
wordlist page was built in response. In hindsight, this specific fix
set up the next rejection.
Monthly auto-refresh + Discord notifications
Locked in the monthly data refresh schedule and added a Discord
notification summarizing new/removed titles after each update. The
first version had a bug that misflagged existing entries as new,
fixed the next day.
The fight against CLS (layout shift)
Spent several days tracking down layout shift during page load —
locking the scrollbar, reordering the results section, and chasing
down the remaining sub-pixel shifts. The similarity calculation logic
was also redesigned from scratch around this time, and the "70%
structure + 30% synopsis" weighting used today traces back to this
rework.
Building the auto-update pipeline
Set up GitHub Actions to automate data collection, translation, and
similarity computation for the first time. The pipeline built here is
still the basis for the monthly automated data refresh running today.
Expanding the answer pool and splitting into chunks
The number of precomputed neighbors stored per item grew in stages from
100 to 3,000. As the data grew, we hit Cloudflare Pages' 25MB per-file
limit, so the similarity JSON was split into 5 separate files. The
English version and character translations were also added around this
time.
First deploy
Anime Semantle went live. The initial setup included the meta tags and
ads.txt required for ad review, along with basic SEO tags.