diff --git a/docs/post-guideline.md b/docs/post-guideline.md index 6a21353..45d1618 100644 --- a/docs/post-guideline.md +++ b/docs/post-guideline.md @@ -1,108 +1,194 @@ -# Post-Writing Guideline +# Post-Writing & Publishing SOP (v2 Standard) -A single reference for writing a post on blog.hoelee.com. Read this **before** creating or editing any post. It consolidates the style, locale, and frontmatter rules — the "how to write it correctly the first time" checklist. +A single authoritative standard for creating, illustrating, validating, and deploying articles on **blog.hoelee.com**. Read this **before** writing, editing, or translating any post. -> Broader *what-to-write* strategy (categories, post types, cadence, anti-patterns) lives in `content-guide.md`. This file is the *how* — mechanics and style. +> **Guiding Strategy:** Broader editorial philosophy (E-E-A-T, categories, topic selection) lives in `content-guide.md`. This file governs the mechanics, persona, schema, and deployment pipeline. --- -## 1. File location & language (enforced by code) +## 1. File Structure & Bilingual Rule (Mandatory Sync) -| Language | Path | URL | +| Language | File Path | Live URL | |---|---|---| -| English | `src/content/posts/.md` | `/posts//` | -| Chinese | `src/content/posts/zh/.md` | `/posts/zh//` | +| **English (Primary)** | `src/content/posts/.md` | `https://blog.hoelee.com/posts//` | +| **Chinese (Mirror)** | `src/content/posts/zh/.md` | `https://blog.hoelee.com/posts/zh//` | -- **Do NOT set a `lang:` field** in frontmatter — language is derived from the folder. -- English is primary. Chinese is selective (2–3 flagship case studies). No Malay. -- To make a Chinese translation of a post, give it the **same filename** in the `zh/` folder — the language switcher auto-links them. If filenames differ, add a `translation: "zh/"` field to link them manually. +- **No `lang:` field in Frontmatter** — language is inferred strictly from the directory (`zh/` vs root). +- **100% Bilingual Coverage**: Every English post MUST have a corresponding Chinese twin with the exact same filename (`.md`) in `zh/`. +- **Atomic Commits**: English drafts, Chinese translations, and their visual assets must be updated and committed together. Never allow the two language twins to drift. +- **English Title Case**: English post titles MUST use Title Case (capitalize all major words). Chinese titles use natural phrasing. --- -## 2. Title style — Title Case +## 2. Voice & Persona Guardrails (Enterprise Production First) -All English post titles use **Title Case** (capitalize the first letter of every significant word): +The blog establishes senior credibility for job recruitment and B2B client acquisition. Enthusiast hobbyist phrasing damages this positioning. -- ✅ `How I Host This Blog: Astro, Gitea Actions, and Self-Hosted CI/CD` -- ✅ `Hello, World — About This Blog` -- ❌ `How I host this blog: ...` - -Exceptions stay lowercase (articles, prepositions, conjunctions — but our house style capitalizes them after punctuation like `—` or `:` for a clean look): "About", "This", "And", "and" are all acceptable; be **consistent**. - -Chinese titles need no capitalization change. +- ❌ **Strictly Forbidden Terms**: `homelab`, `home lab`, `家用`, `小主机`, `玩具`, `折腾`. +- ✅ **Sanctioned Replacements**: + - `the infrastructure I manage` / `self-hosted production node` / `dedicated server hardware` + - 中文:“我管理的私有基础设施”、“自托管生产节点”、“企业级自建网关”、“生产服务器”。 +- **First-Person Production Record**: + - Open on what was measured or executed (`"When a container stopped unexpectedly on my production node, Docker reported exit 0..."`), never an address to an imagined hiring manager. + - No second-person (`you`/`your`) in the technical body. Second-person belongs strictly in closing conversion hooks. +- **Honest Metrics**: + - Quoted numbers must exist verbatim in the terminal logs, commit history, or benchmarks. + - Never invent incidents or exaggerate scale. --- -## 3. Frontmatter shape +## 3. Frontmatter Specification (v2 Schema) + +All posts (and their Chinese twins) must follow the updated Zod schema: ```yaml --- -title: "How I Built the DigiKedai Telegram AI Bot" # Title Case -description: "A ~155-char meta description with the target keyword." +title: "How I Built the DigiKedai Telegram AI Bot" +description: "A customer-support AI bot that answers questions and provisions free-trial accounts over Telegram and WhatsApp — TypeScript, grammY, and a self-hosted LiteLLM gateway." pubDate: 2026-09-06 -updatedDate: 2026-09-10 # optional, when revised -category: case-studies # one of the 7 below -tags: ["telegram", "n8n", "docker", "cloudflare"] -translation: "zh/how-i-built-digikedai-bot" # optional, only if slug differs from zh counterpart -ogImage: "/og/digikedai-bot.png" # optional, 1200×630 custom image -draft: false # true = hidden from build +category: case-studies +tags: ["telegram", "docker", "cloudflare", "litellm", "typescript", "ai"] +ogImage: /og/how-i-built-the-digikedai-telegram-bot.jpg +banner: /banners/how-i-built-the-digikedai-telegram-bot.jpg +draft: false + +# [V2 Feature] Executive Summary Bento Card (Rendered automatically at top) +summary: + audience: "Business Owners & Product Leads" # 中文:“企业主与产品/运营负责人” + painPoint: "A solo digital goods business faces a non-stop stream of repetitive pre-sales questions, leading to missed midnight sales and high manual support overhead." + impact: + - metric: "24/7 Instant Reply" + detail: "Handles customer inquiries across Telegram and WhatsApp with zero human intervention for free-trial provisioning." + - metric: "Zero Price Hallucination" + detail: "3-tier deterministic SKU retrieval and state-machine purchase interception prevent hallucinated prices." + - metric: "~175 Unit Tests" + detail: "Runs reliably in a single container on a dedicated node with LiteLLM gateway failover." + stack: ["TypeScript", "grammY", "LiteLLM", "NocoDB", "Cloudflare Tunnel", "PostgreSQL"] + jumpLinks: + business: "#why-a-business-needs-a-bot-like-this" + architecture: "#the-architecture" + result: "#the-result" + +# [V2 Feature] High-Conversion Dynamic CTA Matrix (Rendered automatically at bottom) +dynamicCta: true --- ``` -### Categories (the only 7 allowed) -`engineering` · `devops` · `ai` · `web3` · `tutorials` · `case-studies` · `notes` +### Supported Categories (Only 7 allowed) +`case-studies` · `devops` · `engineering` · `ai` · `tutorials` · `web3` · `notes` + +### Key Frontmatter Rules: +1. `ogImage` for English is `/og/.jpg`; for Chinese it is `/og/zh-.jpg`. +2. `banner` is identical for both: `/banners/.jpg`. +3. When `dynamicCta: true` is set, **DO NOT write a static contact/CTA section in markdown body** — `` injects the contextual dual-track lead form automatically based on category and tags. --- -## 4. The "hard job → post" template +## 4. Body Content & Layout Blueprint -When you finish a difficult piece of work, use this shape — it's simultaneously a tutorial, a case study, and a proof-of-expertise: +Every technical post follows the 5-phase "Hard Job → Post" narrative: -``` -① The problem → phrased as the searchable question a learner would type -② What I tried & why it failed → the debugging story (no one else can copy this) -③ The fix → runnable code/config, explained -④ What I'd do differently → shows judgment -⑤ The result → one quantified outcome -``` +1. **The Business/Technical Context**: Why this system was built, or what incident triggered the investigation. +2. **The Failure Mode & Evidence**: What broke, how standard metrics/tools lied, and what raw evidence revealed the truth (e.g., `dmesg`, kernel logs, Wireshark). +3. **The Fix / Architecture**: The exact runnable configuration, patch, or state-machine code. +4. **Technical Deep Dive Accordions**: + - For long code dumps, verbose config files, or raw logs (>20 lines), wrap them in native HTML `
`: + ```html +
+ 🔍 查看完整的 Docker Compose 配置与网络策略 ---- - -## 5. Language & identity rules - -- **Business framing:** website design & development is the primary business; email hosting is a **side offering** — never describe it as "an email-hosting business". -- **Name:** "Lee Teong Hoe" / "Mr Hoelee" — identical across blog, LinkedIn, GitHub, git.hoelee.com. -- **No overclaiming**, especially Web3 (learning projects, not production DeFi). - ---- - -## 6. Code blocks - -- Wrap code in fenced blocks with the language tag (```yaml, ```bash, ```ts). -- Keep code/commands in their **original language** (don't translate code or commands inside a Chinese post). -- The copy button is added automatically by the layout — no action needed. - ---- - -## 7. Publish flow - -1. Write the `.md` file in the correct folder (see §1). -2. `npm run build` locally to confirm it compiles (optional but recommended). -3. Commit + push to both remotes: - ```bash - git push origin main && git push github main + ```yaml + services: + traefik: + ... ``` -4. Gitea Actions CI builds and deploys automatically; verify with `curl -I https://blog.hoelee.com/posts//` → 200. +
+ ``` +5. **Lessons & Judgment**: What to do differently next time; architectural trade-offs. +6. **Quantified Result**: One concrete metric (latency dropped by 42%, zero crashes in 30 days, 182 tests passing). -## 8. i18n sync — Chinese translation (do this automatically, every post) +--- -Every post gets a Chinese translation, **every time**, without being asked: +## 5. Visual Pipeline: Local ComfyUI "One Art, Four Targets" -1. **Translate every post.** Any new post gets a Chinese version at `src/content/posts/zh/.md` — **same filename**, so the language switcher auto-links the two (no `translation:` field needed). -2. **Keep EN and ZH in sync.** If you edit the English post (add a section, fix a fact, update a link), make the **same edit** to the Chinese version in the same commit. Never let the two versions drift. -3. **Translate the *frontmatter* too** — title and description go to Chinese, but `category`, `tags`, and `pubDate` stay identical to the English post (they're data, not prose). -4. **Code/commands stay in English** inside the Chinese post (see §6) — only the prose around them is translated. -5. **Every case study ends with a hire CTA** (in the post body, not frontmatter): a "Want this for your business?" section. The contact must be **one-tap, not plain text** — a clickable WhatsApp link (`https://wa.me/60127972969`) and a `mailto:` link (`mailto:me@hoelee.com?subject=...`), plus `hoelee.com` — and it should **name the concrete service offered** (e.g. "I build Telegram support bots like this one", "I set up self-hosted monitoring pipelines"), so a reader can tap straight through and ask for that specific thing. This is a business blog — every flagship post doubles as a lead magnet. -6. **Case studies open with a "why it matters" section** (the business benefit: saves money, 24/7, converts browsers, remembers customers) before the technical architecture. +Every new post requires dedicated, text-free conceptual artwork rendered on the local RTX 3060 GPU. -**Verify after build:** the English page links to `/posts/zh//` and the Chinese page links back to `/posts//` (the auto language-switch). +### Steps to Generate Post Artwork: +```bash +# 1. Verify ComfyUI is listening on port 8188 +curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8188/system_stats # must return 200 + +# 2. Draft and render base frame (1360×768) +cd /c/AI/ideogram4 +uv run --with rapidocr-onnxruntime --with pillow --with numpy --with scipy python post_art.py \ + --slug \ + --metaphor "" \ + --evidence "$ " +``` + +### Visual Quality & OCR Gates: +- **No Text Rule**: The metaphor must describe shapes and physics (e.g., `"an illuminated optical prism deflecting laser beams"`). Never include words like `"badge"`, `"label"`, `"screen"`, `"numbers"`, which cause diffusion models to bake in gibberish text. +- **OCR Gate**: Automatically discards seeds with text bounding boxes ≥ 12px. +- **Output Artifacts Generated**: + 1. `public/banners/.webp` (1600×900, page hero) + 2. `public/banners/.jpg` (fallback) + 3. `public/og/.jpg` (1200×630, English share card with typography) + 4. `public/og/zh-.jpg` (1200×630, Chinese share card with typography) + 5. `public/og/thumb/-800.webp` & `-400.webp` (list thumbnails) + +--- + +## 6. Pre-Flight Verification Gates + +Before committing, run the automated verification suite from `D:/dev/hoelee-blog`: + +```bash +# 1. Asset completeness check (fails if any banner or OG card is missing) +node scripts/check-assets.mjs + +# 2. Production build & search index verification +npm run build +``` +✅ **Success Criteria**: +- `assets: N published posts OK` +- Astro builds all HTML pages without errors (e.g., 311+ pages). +- Pagefind indexes all words without failing. + +--- + +## 7. Dual-Remote Push & CI/CD Deployment + +We maintain self-hosted Gitea as the primary repository with GitHub as an external mirror. + +```bash +# 1. Push to Primary (Gitea git.hoelee.com) +git push origin main + +# 2. Push to Mirror (GitHub with PAT helper fix) +TOK=$(grep github.com ~/.git-credentials | head -1 | sed -E 's#https://[^:]+:([^@]+)@.*#\1#' | tr -d '\r') +git -c credential.helper= push "https://hoelee:$TOK@github.com/hoelee/hoelee-blog.git" main +git fetch github main +``` + +### Automated CI/CD Actions: +1. Gitea Actions (`deploy.yml`) is triggered automatically by the push to `origin main`. +2. The `act_runner` on unRaid clones over high-speed LAN HTTP (`192.168.1.6:3000`), executes `npm ci`, runs `npm run build`, and copies static assets to `/mnt/user/docker/nginx-hoelee-blog/html/`. +3. Cloudflare cache is automatically purged via API token. + +--- + +## 8. Post-Deploy Health Check + +Verify that the live site is serving the new build: + +```bash +# 1. Check HTTP response and Last-Modified header +curl -s -I "https://blog.hoelee.com/posts//" | grep -E "HTTP|cf-cache-status|last-modified" + +# 2. Assert Executive Summary presence +curl -s "https://blog.hoelee.com/posts//?v=$(date +%s)" | grep -o "exec-summary" + +# 3. Assert Dynamic CTA presence +curl -s "https://blog.hoelee.com/posts//?v=$(date +%s)" | grep -o "dynamic-cta" +``` +Ensure both English and Chinese pages return `HTTP/1.1 200 OK` and contain the required components. diff --git a/docs/post-publishing-sop-v2.md b/docs/post-publishing-sop-v2.md new file mode 100644 index 0000000..45d1618 --- /dev/null +++ b/docs/post-publishing-sop-v2.md @@ -0,0 +1,194 @@ +# Post-Writing & Publishing SOP (v2 Standard) + +A single authoritative standard for creating, illustrating, validating, and deploying articles on **blog.hoelee.com**. Read this **before** writing, editing, or translating any post. + +> **Guiding Strategy:** Broader editorial philosophy (E-E-A-T, categories, topic selection) lives in `content-guide.md`. This file governs the mechanics, persona, schema, and deployment pipeline. + +--- + +## 1. File Structure & Bilingual Rule (Mandatory Sync) + +| Language | File Path | Live URL | +|---|---|---| +| **English (Primary)** | `src/content/posts/.md` | `https://blog.hoelee.com/posts//` | +| **Chinese (Mirror)** | `src/content/posts/zh/.md` | `https://blog.hoelee.com/posts/zh//` | + +- **No `lang:` field in Frontmatter** — language is inferred strictly from the directory (`zh/` vs root). +- **100% Bilingual Coverage**: Every English post MUST have a corresponding Chinese twin with the exact same filename (`.md`) in `zh/`. +- **Atomic Commits**: English drafts, Chinese translations, and their visual assets must be updated and committed together. Never allow the two language twins to drift. +- **English Title Case**: English post titles MUST use Title Case (capitalize all major words). Chinese titles use natural phrasing. + +--- + +## 2. Voice & Persona Guardrails (Enterprise Production First) + +The blog establishes senior credibility for job recruitment and B2B client acquisition. Enthusiast hobbyist phrasing damages this positioning. + +- ❌ **Strictly Forbidden Terms**: `homelab`, `home lab`, `家用`, `小主机`, `玩具`, `折腾`. +- ✅ **Sanctioned Replacements**: + - `the infrastructure I manage` / `self-hosted production node` / `dedicated server hardware` + - 中文:“我管理的私有基础设施”、“自托管生产节点”、“企业级自建网关”、“生产服务器”。 +- **First-Person Production Record**: + - Open on what was measured or executed (`"When a container stopped unexpectedly on my production node, Docker reported exit 0..."`), never an address to an imagined hiring manager. + - No second-person (`you`/`your`) in the technical body. Second-person belongs strictly in closing conversion hooks. +- **Honest Metrics**: + - Quoted numbers must exist verbatim in the terminal logs, commit history, or benchmarks. + - Never invent incidents or exaggerate scale. + +--- + +## 3. Frontmatter Specification (v2 Schema) + +All posts (and their Chinese twins) must follow the updated Zod schema: + +```yaml +--- +title: "How I Built the DigiKedai Telegram AI Bot" +description: "A customer-support AI bot that answers questions and provisions free-trial accounts over Telegram and WhatsApp — TypeScript, grammY, and a self-hosted LiteLLM gateway." +pubDate: 2026-09-06 +category: case-studies +tags: ["telegram", "docker", "cloudflare", "litellm", "typescript", "ai"] +ogImage: /og/how-i-built-the-digikedai-telegram-bot.jpg +banner: /banners/how-i-built-the-digikedai-telegram-bot.jpg +draft: false + +# [V2 Feature] Executive Summary Bento Card (Rendered automatically at top) +summary: + audience: "Business Owners & Product Leads" # 中文:“企业主与产品/运营负责人” + painPoint: "A solo digital goods business faces a non-stop stream of repetitive pre-sales questions, leading to missed midnight sales and high manual support overhead." + impact: + - metric: "24/7 Instant Reply" + detail: "Handles customer inquiries across Telegram and WhatsApp with zero human intervention for free-trial provisioning." + - metric: "Zero Price Hallucination" + detail: "3-tier deterministic SKU retrieval and state-machine purchase interception prevent hallucinated prices." + - metric: "~175 Unit Tests" + detail: "Runs reliably in a single container on a dedicated node with LiteLLM gateway failover." + stack: ["TypeScript", "grammY", "LiteLLM", "NocoDB", "Cloudflare Tunnel", "PostgreSQL"] + jumpLinks: + business: "#why-a-business-needs-a-bot-like-this" + architecture: "#the-architecture" + result: "#the-result" + +# [V2 Feature] High-Conversion Dynamic CTA Matrix (Rendered automatically at bottom) +dynamicCta: true +--- +``` + +### Supported Categories (Only 7 allowed) +`case-studies` · `devops` · `engineering` · `ai` · `tutorials` · `web3` · `notes` + +### Key Frontmatter Rules: +1. `ogImage` for English is `/og/.jpg`; for Chinese it is `/og/zh-.jpg`. +2. `banner` is identical for both: `/banners/.jpg`. +3. When `dynamicCta: true` is set, **DO NOT write a static contact/CTA section in markdown body** — `` injects the contextual dual-track lead form automatically based on category and tags. + +--- + +## 4. Body Content & Layout Blueprint + +Every technical post follows the 5-phase "Hard Job → Post" narrative: + +1. **The Business/Technical Context**: Why this system was built, or what incident triggered the investigation. +2. **The Failure Mode & Evidence**: What broke, how standard metrics/tools lied, and what raw evidence revealed the truth (e.g., `dmesg`, kernel logs, Wireshark). +3. **The Fix / Architecture**: The exact runnable configuration, patch, or state-machine code. +4. **Technical Deep Dive Accordions**: + - For long code dumps, verbose config files, or raw logs (>20 lines), wrap them in native HTML `
`: + ```html +
+ 🔍 查看完整的 Docker Compose 配置与网络策略 + + ```yaml + services: + traefik: + ... + ``` +
+ ``` +5. **Lessons & Judgment**: What to do differently next time; architectural trade-offs. +6. **Quantified Result**: One concrete metric (latency dropped by 42%, zero crashes in 30 days, 182 tests passing). + +--- + +## 5. Visual Pipeline: Local ComfyUI "One Art, Four Targets" + +Every new post requires dedicated, text-free conceptual artwork rendered on the local RTX 3060 GPU. + +### Steps to Generate Post Artwork: +```bash +# 1. Verify ComfyUI is listening on port 8188 +curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8188/system_stats # must return 200 + +# 2. Draft and render base frame (1360×768) +cd /c/AI/ideogram4 +uv run --with rapidocr-onnxruntime --with pillow --with numpy --with scipy python post_art.py \ + --slug \ + --metaphor "" \ + --evidence "$ " +``` + +### Visual Quality & OCR Gates: +- **No Text Rule**: The metaphor must describe shapes and physics (e.g., `"an illuminated optical prism deflecting laser beams"`). Never include words like `"badge"`, `"label"`, `"screen"`, `"numbers"`, which cause diffusion models to bake in gibberish text. +- **OCR Gate**: Automatically discards seeds with text bounding boxes ≥ 12px. +- **Output Artifacts Generated**: + 1. `public/banners/.webp` (1600×900, page hero) + 2. `public/banners/.jpg` (fallback) + 3. `public/og/.jpg` (1200×630, English share card with typography) + 4. `public/og/zh-.jpg` (1200×630, Chinese share card with typography) + 5. `public/og/thumb/-800.webp` & `-400.webp` (list thumbnails) + +--- + +## 6. Pre-Flight Verification Gates + +Before committing, run the automated verification suite from `D:/dev/hoelee-blog`: + +```bash +# 1. Asset completeness check (fails if any banner or OG card is missing) +node scripts/check-assets.mjs + +# 2. Production build & search index verification +npm run build +``` +✅ **Success Criteria**: +- `assets: N published posts OK` +- Astro builds all HTML pages without errors (e.g., 311+ pages). +- Pagefind indexes all words without failing. + +--- + +## 7. Dual-Remote Push & CI/CD Deployment + +We maintain self-hosted Gitea as the primary repository with GitHub as an external mirror. + +```bash +# 1. Push to Primary (Gitea git.hoelee.com) +git push origin main + +# 2. Push to Mirror (GitHub with PAT helper fix) +TOK=$(grep github.com ~/.git-credentials | head -1 | sed -E 's#https://[^:]+:([^@]+)@.*#\1#' | tr -d '\r') +git -c credential.helper= push "https://hoelee:$TOK@github.com/hoelee/hoelee-blog.git" main +git fetch github main +``` + +### Automated CI/CD Actions: +1. Gitea Actions (`deploy.yml`) is triggered automatically by the push to `origin main`. +2. The `act_runner` on unRaid clones over high-speed LAN HTTP (`192.168.1.6:3000`), executes `npm ci`, runs `npm run build`, and copies static assets to `/mnt/user/docker/nginx-hoelee-blog/html/`. +3. Cloudflare cache is automatically purged via API token. + +--- + +## 8. Post-Deploy Health Check + +Verify that the live site is serving the new build: + +```bash +# 1. Check HTTP response and Last-Modified header +curl -s -I "https://blog.hoelee.com/posts//" | grep -E "HTTP|cf-cache-status|last-modified" + +# 2. Assert Executive Summary presence +curl -s "https://blog.hoelee.com/posts//?v=$(date +%s)" | grep -o "exec-summary" + +# 3. Assert Dynamic CTA presence +curl -s "https://blog.hoelee.com/posts//?v=$(date +%s)" | grep -o "dynamic-cta" +``` +Ensure both English and Chinese pages return `HTTP/1.1 200 OK` and contain the required components.