# Project State & Enhancement Backlog
Living list of what's done and what's next for blog.hoelee.com. Work through these **one by one** โ don't batch unrelated changes. Check off items as they land.
**Status key:** โ
done ยท ๐ต in progress ยท โฌ not started
> **How to resume the project:** start at the top-most โฌ item in the **Execution Plan** (ยง2) below and work downward. Each step is self-contained, has a "done when" criterion, and references the governing doc. Don't jump ahead โ earlier steps unlock later ones.
---
## 1. Done (foundation)
- โ
Astro 5 static + Markdown, Gitea Actions CI/CD โ nginx โ Cloudflare (deployed)
- โ
Design system: hoelee.com brand palette, Inter + JetBrains Mono, light/dark, sticky nav, code copy button
- โ
Brand "Mr Hoelee" (replaced "hoelee.dev")
- โ
Author card + `Person`/`ProfilePage` JSON-LD (E-E-A-T), article meta, reading time, related posts
- โ
Full SEO: canonical, Open Graph (+dims), twitter:card, favicon (all sizes), RSS + sitemap
- โ
Category pages (`/categories/`, `/categories/[category]/`)
- โ
Locale scheme: English flat in `posts/`, Chinese in `posts/zh/` (lang derived from folder)
- โ
Language switcher in nav โ links to the **same post** in the other language (auto-matches by `zh/` prefix)
- โ
Locale-aware nav labels (EN/ZH)
- โ
Dark mode default (light opt-in via toggle)
- โ
Case study "How I Host This Blog"
- โ
Chinese translation of the case study (`posts/zh/how-i-host-this-blog.md`)
- โ
hello-world intro post
- โ
Case study "How I Built the DigiKedai Telegram AI Bot" + Chinese twin (`posts/zh/how-i-built-the-digikedai-telegram-bot/`)
- โ
Business framing corrected (website design & development = primary; email hosting = secondary)
- โ
Knowledge guides in `docs/` + README index + `hoelee-blog` skill
- โ
Both repos public (Gitea + GitHub) with title/description/homepage/topics + `v1.0.0` release
---
## 2. Execution Plan (work top โ bottom, one step at a time)
> This plan comes from a full research pass (Sept 2026) comparing blog.hoelee.com against reference developer blogs (Simon Willison, Josh Comeau, Dan Abramov/overreacted, Julia Evans) + industry surveys. Priority is fixed: **identity & content โ discovery โ polish.** Don't reorder unless the user says so.
### Phase A โ Identity (highest ROI, ~2โ3 hrs total)
**Step A1 โ Add a real author photo (headshot).**
The #1 gap vs. every reference blog: the avatar is a letter "M" placeholder and there's no photo anywhere on the site. Every credible personal dev blog has a human face.
- [ ] User provides one headshot (square, โฅ800ร800 for avatar; also source for og).
- [ ] Replace `avatar` letter with the photo in: nav/brand (optional), author card (About + every post), `ProfilePage` JSON-LD `image`.
- [ ] Add the photo to `og-default.png` template so the default share card has a face.
- **Governing doc:** `design-guide.md` ยง2 (author box), ยง5 (E-E-A-T name+photo consistency).
- **Done when:** a real face renders in the author card on About + every post; `curl` shows no 404 for the asset.
**Step A2 โ Align the homepage title & hero framing.**
The `
` says "engineering, DevOps & self-hosting" but the hero says "full-stack developer and DevOps engineer" โ two slightly different framings.
- [ ] Pick one line (recommend: "full-stack developer & DevOps engineer") and use it in both ``/meta description and hero paragraph.
- **Done when:** homepage title, meta description, and hero all say the same thing about who Hoelee is.
**Step A3 โ Add a "Start here" / featured posts route.**
New visitors land on reverse-chronological "Latest posts" with no guidance to the best content (Julia Evans' Favorites, Josh Comeau's featured posts both solve this).
- [ ] Add a "Start here" (or "Featured") section on the homepage surfacing 2โ3 flagship case studies.
- [ ] Optionally add a `/favorites` or `/start-here` page (defer the dedicated page until โฅ6 strong posts; the homepage strip is the immediate win).
- **Done when:** homepage shows a featured/start-here strip above or beside "Latest posts".
### Phase B โ Content (80% of value; the long game)
**Step B1 โ Write the 2nd flagship case study: "Self-Hosting a Mem0 Memory Stack".** โ
Done 2026-09-16
The Mem0 flagship is already the single highest-value unwritten post in the backlog.
- [x] Write `src/content/posts/self-hosting-mem0.md` (category `case-studies`).
- [x] Write Chinese twin `src/content/posts/zh/self-hosting-mem0.md` (same filename โ auto language-switch).
- [x] Follow the "hard job โ post" template (ยง4 content-guide) + open with "why it matters" + end with hire CTA (ยง8 post-guideline).
- **Governing doc:** `content-guide.md` ยง4/ยง8, `post-guideline.md` ยง8.
- **Done when:** โ
both EN + ZH pages live, language-switch works, hire CTA present.
**Step B1b โ Write the self-hosted STT case study.** โ
Done 2026-09-19
`self-hosted-speech-to-text-api.md` (EN + ZH): whisper.cpp on GPU + n8n auth gate +
nginx gateway, with the four build traps and the "5x faster than typing" business case.
- [x] EN + ZH posts, custom OG + banner, hire CTA.
- **Done when:** โ
both pages build, language-switch verified, images generated.
**Step B2 โ Write 2โ3 short "gotcha" posts (Google-friendly, compound over time).**
- [x] "Replacing RDPGuard With IPBan: The Traps Nobody Documents" (EN + ZH, `devops`, 2026-09-19) โ the uninstaller that unbans 12 attackers, `--install-service` doesn't exist in v4.1.0, `ExpireTime` vs `BanTime`. Both images custom.
- [x] "When Your Database Client Lies to You: Patching Workbench 26 for MariaDB" (EN + ZH, `devops`, 2026-09-19) โ a client whose error handler crashed while reporting its own errors, masking every real failure; three patches to Oracle's bundled code, all stemming from `major >= 8` being an invalid MySQL-vs-MariaDB test. Both images custom.
- [ ] "The Traefik forward-auth gotcha that cost me a day"
- [ ] "Site-to-site OpenVPN behind CGNAT"
- [ ] "Fixing the WordPress /cv 301โ404 chain" (from own audit)
- **Governing doc:** `content-guide.md` ยง3 (post type #3), `post-guideline.md`.
- **Done when:** โฅ2 gotcha posts live (these are `notes`/`devops`, no Chinese translation required per ยง8).
- โ **Note:** `post-guideline.md` ยง8 (newer) says *every* post gets a ZH twin โ the "no Chinese required" note above is stale. The RDPGuard post was published EN + ZH.
**Step B2b โ Draft bank (written, held as `draft: true`, publish when content runs short).** โ
Drafted 2026-09-20
Two finished posts (EN + ZH, each with frontmatter pointing at OG + banner paths) sitting in the repo but
**not built or listed** โ `draft: true` excludes them from all listings and generates no pages.
| Slug | Category | Status | Assets |
|---|---|---|---|
| `migrating-codeigniter-iis-to-openlitespeed` | `engineering` | drafted, unpublished | OG + banner PNGs **not yet generated** |
| `upgrading-codeigniter-46-to-47` | `notes` | drafted, unpublished | OG + banner PNGs **not yet generated** |
**To publish one later:**
1. Flip `draft: true` โ `draft: false` in **both** `src/content/posts/.md` and `src/content/posts/zh/.md`.
2. Set the real `pubDate` (currently `2026-09-20`, the draft date) in both files.
3. Generate its images: `node scripts/og-gen/generate.mjs ` and `node scripts/banner-gen/generate.mjs ` (add a `TERMINALS[slug]` / `BANNERS[slug]` entry first for the custom panel).
4. `npm run build`, commit, `git push origin main`.
5. Verify both URLs return 200 and the language switcher links them.
- **Why these two:** `engineering` had only 1 post and `tutorials` only 1 โ the blog was ~all `devops`/`case-studies`. These put PHP/CodeIgniter (the actual day-job stack) on the blog, which is what a PHP full-stack recruiter searches for.
- **Governing doc:** `content-guide.md` ยง3/ยง4, `post-guideline.md` ยง8.
- **Done when:** both are published live with EN+ZH, custom OG + banner, and verified 200.
**Step B3 โ Adopt the "hard job โ post" habit.**
Every solved problem becomes a `notes` entry the same week.
- [ ] Revisit cadence target: 2 posts/month โ 1/week (`content-guide.md` ยง5).
- **Done when:** 3 consecutive months hit the 2-posts/month floor.
### Phase C โ Discovery & structure (Tier 2)
**Step C1 โ Per-post custom OG images (at least for case studies).**
Currently every post shares the generic 14KB `og-default.png` โ flagship posts share the same bland card as category pages.
- [ ] Build a branded 1200ร630 OG template (name + face + title).
- [ ] Generate a custom `ogImage` for each case study (frontmatter `ogImage:` field already supported).
- **Governing doc:** `design-guide.md` ยง2/ยง3, `content-guide.md` ยง6 (ogImage field).
- **Done when:** each case study's `og:image` is unique and 1200ร630.
**Step C2 โ Tag pages** (`/tags/[tag]/` archive pages for fine-grained discovery + internal linking).
- [ ] Add tag archive routes (tags currently render as labels only).
- **Done when:** clicking a tag on any post opens a working `/tags//` page.
**Step C3 โ Categories page shows all 7 categories** (not just those with posts), with "0 posts / coming soon" for empty ones โ signals intended coverage. โ
Done 2026-09-13
- [x] `/categories/` lists all 7 categories with name, description, per-category terminal-style SVG illustration (CategoryArt/Grid components) and a post count; empty ones show "0 posts ยท coming soon" as a non-link.
- **Done when:** โ
all 7 render with a placeholder for empty ones + descriptions + illustrations.
**Step C4 โ Dedicated `/zh/posts/` and `/zh/categories/` archive pages.** ๐ต In progress
- [x] `/zh/categories/` index + `/zh/categories/[category]/` detail pages live (zh nav "ๅ็ฑป" points there; PostList is locale-aware with zh-CN dates).
- [ ] `/zh/posts/` archive still missing โ zh nav "ๆ็ซ " falls back to `/zh/` landing (zh post count already 17, the archive is due).
- **Done when:** zh nav links to real `/zh/posts/` + `/zh/categories/` archives.
### Phase D โ Polish / later (Tier 3)
**Step D1 โ Search** (AstroPaper-style fuzzy search). Low priority until >20 posts.
**Step D2 โ Google Search Console submission** โ submit `sitemap-index.xml` for faster indexing.
**Step D3 โ Newsletter / email capture** โ only after real traffic exists (agree: do NOT add yet).
---
## 3. Research Findings Snapshot (Sept 2026)
What the reference blogs do that blog.hoelee.com should mirror, ranked:
| Finding | Reference example | Status on blog.hoelee.com |
|---|---|---|
| Real name + photo + one-line identity | All four | โ ๏ธ name โ
, photo โ (letter "M") โ **Step A1** |
| Focused thesis (one sentence on what it's about) | Julia Evans, Simon Willison | โ ๏ธ has it, but title/hero drift โ **Step A2** |
| Honesty about what you *don't* know | Simon, Dan Abramov | โ
strong (DigiKedai "bugs that ate an afternoon") |
| Specific detail: code, diagrams, numbers, bug stories | All four | โ
strong |
| Consistent cadence (slow is fine, dead is not) | Julia (~monthly), Simon (daily) | โ ๏ธ only 3 posts, all Sept 4โ6 โ **Phase B** |
| "Start here" / Favorites route | Julia Evans, Josh Comeau | โ โ **Step A3** |
| RSS + sitemap + clean SEO | All four | โ
|
| Per-post OG images | Josh Comeau | โ โ **Step C1** |
| Search (once >15โ20 posts) | Josh Comeau | โ deferred โ **Step D1** |
---
## 4. Conventions (non-negotiable)
- Push git.hoelee.com first, then GitHub
- English-first; Chinese selective (2โ3 flagship case studies); no Malay
- No overclaiming, especially Web3
- Name identity: "Lee Teong Hoe" / "Mr Hoelee" + same photo + same `sameAs` handles everywhere
- Business framing: website design & development is primary; email hosting is secondary
- English post titles use Title Case
- See `docs/post-guideline.md` for post-writing rules; `docs/content-guide.md` for strategy; `docs/design-guide.md` for UI