Chapter 9: Modules, Daily Reports, SEO, and Share Cards
Y splits a 3,600-line engine into four modules and adds daily reports and SEO. L trims leaderboards to ten places, adds achievement badges, and designs long-press event cards so the game can tell its own stories.
The following plots are purely fictitious and any similarity is purely coincidental.
Opening
On March 14, Y started taking things apart again.
The last time I split it was Worker, which was split from one file into eight. This time it was the game engine itself that was dismantled. `game-engine.js` has grown to 3600 lines. All verification logic, DOM operations, expression parsing, and passport docking are all piled in one file. L doesn't mind changing it every day, but Y looks too big. It's not because it's messy - everything modified by L can be run - but because once this kind of file exceeds a certain size, no one dares to touch it casually.
"It's time to dismantle it." He opened the Codex and began to plan how to dismantle it.
Three thousand six hundred lines become four modules
Y tells Codex what boundaries he wants, and Codex moves them. Four items were dismantled.
`shared-validators.js`: The authoritative source for all validation logic. Nickname standardization, passport domain name verification, vibe_uid format check - originally scattered in three places: engine, worker, passport-bridge, each written in its own way, they look similar but the details are different. Now make a unified copy and quote it elsewhere. The `helpers.mjs` on the Worker side is used as a mirror, and the test file `validation-parity.test.js` is stuck on both sides.
`dom-refs.js`: Receive all `getElementById()` calls into a `getEl()` function. One hundred and three DOM searches, all going through the same entrance. When a node cannot be found, an error will be reported uniformly, and a null reference in a corner will no longer crash the page.
`expression-parser.js`: 263 lines of expression evaluator, originally hidden in the middle of the engine. Tokenizer, reverse Polish conversion, conditional matching - they have nothing to do with game logic, they are purely analytical tools. After picking it out separately, 88 test cases were added.
`db.mjs`: The database operations on the Worker side are centralized. All the more than 80 lines of inline SQL scattered in `passport.mjs` have been moved here.
After dismantling the engine, it was reduced from 3603 lines to 3309 lines. Not too fierce. But it's not the number of rows that matters. It's the border. Each file has its own responsibilities. When making changes, you no longer have to look through 3,000 lines to find where to move.
The game learned to report on itself
On the same day, Y and Codex did another thing: connected daily data emails to the project.
`daily-report.mjs`. Y tells Codex what data, format, and frequency are needed for the report, Codex is written out, and Y adjusts it. Every day at noon Shanghai time, a report is automatically sent to the operation mailbox. What's inside--Seven-day rolling funnel table: from opening the game to seeing the checkout page, how many people are there at each step and what is the conversion rate. Yesterday's funnel snapshot. The month-on-month changes compared with the day before yesterday are shown in green if they have increased, and in red if they have fallen. Total runs, cause of death ranking, average lifespan. Settlement quality indicators: whether there are high-value players and email binding rate.
The email is in HTML format, with tables, colors, and up and down arrows. Y is very serious when writing this kind of thing. Whether the report can be understood determines whether the operation can make judgments.
Later, L asked Kimi to add two pieces of content on this basis: user source distribution—how many UTM tags and referrers each account for—and player game distribution—how many played one game, how many played three to five games, and how many played more than twenty games. She also conveniently fixed the time zone issue and changed the reporting date from UTC to Beijing time, so that yesterday's data would not be different by eight hours.
Later, Y added retention queues: D1, D3, D7, and D30. Rely on a `user_first_seen` materialized table to backfill the first occurrence time from the `session_start` event. Grouping by day can be done without full table scan.
This email has arrived on time every day since then. No matter whether two people are busy or not, the game will report it itself.
The leaderboard is shorter, but stronger
The rankings are also changing these days.
Y first asked Codex to fix an atomicity problem. The original logic of submitting scores was to check the old scores first and then decide whether to update them. Two requests coming in at the same time will cause a fight. Changed to `INSERT...ON CONFLICT` and write one SQL each according to the aggregation method (sum, min, max). A 15-second timeout is also added to the request, and errors will be reported separately for network disconnection and timeout. There is an additional "Retry to List" button on the settlement page. If the submission fails, you don't have to play the game again.
Then he changed the classification of the misinformation. `error_category` is divided into three types: auth, validation, and server. The client first matches by category, then matches by specific code, and finally knows the details. There will no longer be a technical error that users cannot understand.
There is another small detail: the server will now tell you whether this submission refreshes your personal record. `improved: true` is highlighted on the front end. In the past, I would play regardless of whether I was making progress or not, but if I played too much, I wouldn’t feel it anymore.
L made a simpler decision: the ranking list was changed from showing the top 100 to only showing the top ten. Both `LEADERBOARD_DEFAULT_LIMIT` and `LEADERBOARD_MAX_LIMIT` were changed to 10. There are still few users in the beta stage, and a hundred people are looking at space. Ten is just right. compact. There is a sense of competition.
She lets the screenshots speak
On March 15th, L made two things related to sharing.
The first one: Ranking information starts to be displayed on the settlement screenshot. If you are on the Tiandao Gold List, there will be a golden box at the bottom of the screenshot that says "🏆 Tiandao Gold List". Below is the list of which list you are on, your rank, and your score. When you post it to the group, others will know at a glance that you are not just how old you are - you are also ranked among the top ten in the server.
The second one takes more effort: long press the event log to generate a sharing card.While playing the game, events will continue to scroll out of the log area. Some incidents are funny, some dramatic. L wants players to capture single events and share them. She described the effect she wanted to Kimi, and Kimi helped her create a long-press trigger mechanism - a 600 millisecond threshold. After pressing and holding, a translucent mask will appear, and an event card will pop up when released.
Cards are drawn using Canvas. It has a wood grain background and event type labels on the top - death events have "catastrophe" written on a red background, legendary events have "legend" written on a gold background, purple means "adventure", green means "chance", pink means "romance", and ordinary means "everyday". In the middle is the event text, centered and typed, with automatic line wrapping. At the bottom there are game attributes, QR code, vibesims branding and timestamp.
Two sets of fonts were used: Mashan block script for the main text, which looks like calligraphy; and Zpix pixel font for the annotations. The entire card is saved as a PNG. The file name is `Cultivation Simulator_[timestamp].png`.
When L was doing this function, he was probably already thinking about spreading it. A screenshot of your settlement can tell the story of your life. An event card can convey a moment. The former is a transcript and the latter is a joke. Both things live on social media.
Search engines can also find it
On the same day, Y was doing something else.
SEO.
He asked Codex to add a `seo.mjs` module to the Worker. The core is a registry called `GAME_SEO_REGISTRY` - one record for each game, clearly describing the title, description, keywords, OG pictures, and canonical URL. When adding new games in the future, just add a line to the registry, and the sitemap, JSON-LD, and meta tags will be automatically generated.
Then there are three new routes: `/robots.txt` tells the crawler what to crawl and what not to crawl. `/sitemap.xml` is an index pointing to `/sitemap-pages.xml` (static pages) and `/sitemap-devlog.xml` (development log). The devlog sitemap automatically checks the database and only publishes published articles.
The SSR output of the homepage and development log pages has also been changed. Each page header injects the correct `<title>`, `<meta description>`, `<link canonical>`, Open Graph tags and JSON-LD structured data. Search engines and social platforms can capture the right title and cover when sharing.
There are also PWAs. `app/manifest.json`, icon sizes 192 and 512, scope set to `/xiuxian/`, display standalone. After adding this, "add to home screen" on the phone can be used as a native app.
The OG cover image goes to `/xiuxian/assets/og-xiuxian.png`, and the Worker proxy goes to the Pages origin site. From now on, each game will have a 1200×630 image.
After Y completed this, he updated CLAUDE.md and included the rules for SEO and multi-game architecture. One registry record for each game, and everything else is fully automatic.
Two kinds of shoutingFrom March 14th to 15th, this project learned two kinds of shouts at the same time.
Y teaches it to talk to the machine. A sitemap lets search engines know what's here. JSON-LD lets Google understand that this is a game. The OG tag gives the shared link a title and cover. Daily reports let operations know what the data looks like every day. These things are invisible to the user, but without them, the game would not be found in the outside world.
L Teach it to talk to people. Event cards allow players to capture funny moments and post them to friends. The gold medal badge allows screenshots to show off their attributes. Eleven pieces of random copywriting make each shared picture different. The ranking list has been reduced to ten people, so compact that you want to squeeze in.
One is for search engines and the other is for WeChat groups. It’s all about making the game visible. The approach is completely different.
At this point, the Immortal Cultivation Simulator is no longer just a thing that can be played. It has a backend, a database, an account, a ranking list, a daily report, SEO, sharing cards, Little Red Book links, and a development log system. It's starting to become a product.
In this product, every line of copywriting is written by L - she tells Teacher G how she wants to feel, and then tells Kimi how to achieve it. Each pipeline is laid by Y - he tells Codex what the architecture should look like, and then iterates round after round until it can go online. Two people were never online for more than an hour at the same time. Both people use different AI tools. But the project keeps moving.