diff --git a/docs/consultant-review/audit-tracker.json b/docs/consultant-review/audit-tracker.json
index 7632a57..fc4ec20 100644
--- a/docs/consultant-review/audit-tracker.json
+++ b/docs/consultant-review/audit-tracker.json
@@ -1,1185 +1,1224 @@
{
- "adding-a-domain-to-someone-elses-mail-server": {
- "slug": "adding-a-domain-to-someone-elses-mail-server",
- "title": "Adding a Domain to Someone Else's Mail Server",
- "zh_title": "给别人家的邮件服务器加一个域名",
- "category": "notes",
- "pub_date": "2025-05-06",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 7886,
- "zh_chars": 3603
- },
- "adding-english-mode-to-a-chinese-only-web-app": {
- "slug": "adding-english-mode-to-a-chinese-only-web-app",
- "title": "No API for Browser Translation: Adding an English Mode to a Chinese-Only Web App",
- "zh_title": "浏览器翻译没有 API:我给一个纯中文网页应用加上了英文模式",
- "category": "engineering",
- "pub_date": "2025-06-11",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 11914,
- "zh_chars": 6253
- },
- "ai-furniture-compositing-with-flux-kontext": {
- "slug": "ai-furniture-compositing-with-flux-kontext",
- "title": "How I Built an AI Furniture Compositing Demo With FLUX.1 Kontext",
- "zh_title": "我如何用 FLUX.1 Kontext 打造 AI 家具合成演示",
- "category": "case-studies",
- "pub_date": "2026-09-11",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 12512,
- "zh_chars": 7237
- },
- "authentik-css-greater-than-bug": {
- "slug": "authentik-css-greater-than-bug",
- "title": "The Character That Silently Broke My authentik CSS",
- "zh_title": "一个字符让我的 authentik CSS 静默失效",
- "category": "devops",
- "pub_date": "2026-09-20",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 9317,
- "zh_chars": 5589
- },
- "authentik-forward-auth-gate-wasnt-live": {
- "slug": "authentik-forward-auth-gate-wasnt-live",
- "title": "The Forward-Auth Gate That Verified Perfectly — and Wasn't Live",
- "zh_title": "验证完全正确的 Forward-Auth 网关——其实并未上线",
- "category": "devops",
- "pub_date": "2026-08-12",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 16526,
- "zh_chars": 8746
- },
- "authentik-major-upgrade-gotchas": {
- "slug": "authentik-major-upgrade-gotchas",
- "title": "Authentik 2025.8 to 2026.8: The Breakable Parts Nobody Warns You About",
- "zh_title": "Authentik 从 2025.8 升级到 2026.8:那些没人提醒你会坏掉的部分",
- "category": "devops",
- "pub_date": "2026-09-09",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 11710,
- "zh_chars": 6147
- },
- "automating-cyberpanel-without-the-ui": {
- "slug": "automating-cyberpanel-without-the-ui",
- "title": "Automating CyberPanel Without the UI: Reverse-Engineering an Undocumented v2 API",
- "zh_title": "不用界面管理 CyberPanel:逆向一个没有文档的 v2 API",
- "category": "devops",
- "pub_date": "2026-09-09",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 8474,
- "zh_chars": 5205
- },
- "best-ai-video-generators-2026": {
- "slug": "best-ai-video-generators-2026",
- "title": "Best AI Video Generators in 2026: Free vs Paid, and My LTX-2.3 Test",
- "zh_title": "2026 年最佳 AI 视频生成器:免费与付费对比,以及我的 LTX-2.3 实测",
- "category": "ai",
- "pub_date": "2026-09-11",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 24245,
- "zh_chars": 12700
- },
- "calibrating-intel-quick-sync-hevc-quality": {
- "slug": "calibrating-intel-quick-sync-hevc-quality",
- "title": "Calibrating Intel Quick Sync HEVC: A Cliff at 28, and a Preset That Does Nothing",
- "zh_title": "校准 Intel Quick Sync HEVC:28 与 30 之间的悬崖,和一个毫无作用的 preset",
- "category": "tutorials",
- "pub_date": "2026-10-14",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 10968,
- "zh_chars": 5968
- },
- "chainlink-vrf-v2-lottery-contract": {
- "slug": "chainlink-vrf-v2-lottery-contract",
- "title": "An Automated Lottery Contract on Chainlink VRF v2.5 and Automation",
- "zh_title": "一个基于 Chainlink VRF v2.5 与 Automation 的自动化彩票合约",
- "category": "web3",
- "pub_date": "2024-12-10",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:11",
- "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
- "en_chars": 16252,
- "zh_chars": 9707
- },
- "debugging-remotion-video-jitter": {
- "slug": "debugging-remotion-video-jitter",
- "title": "Debugging Remotion Video Jitter: Why Bass Scaling Ruins Background Transitions",
- "zh_title": "排查 Remotion 视频抽搐:为什么在背景图挂载音频低频 Scale 会毁了转场",
- "category": "engineering",
- "pub_date": "2025-11-26",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 9836,
- "zh_chars": 5653
- },
- "docker-reported-exit-0-the-oom-killer-disagreed": {
- "slug": "docker-reported-exit-0-the-oom-killer-disagreed",
- "title": "Docker Reported Exit 0. The Kernel Had Already OOM-Killed It.",
- "zh_title": "Docker 报告退出码 0,内核却早已执行 OOM-Kill",
- "category": "devops",
- "pub_date": "2026-10-16",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 10200,
- "zh_chars": 5488
- },
- "does-hevc-actually-shrink-your-files": {
- "slug": "does-hevc-actually-shrink-your-files",
- "title": "Does Re-encoding to HEVC Actually Shrink Your Files? I Measured It.",
- "zh_title": "转成 HEVC 到底会不会让文件变小?我实测了",
- "category": "tutorials",
- "pub_date": "2026-10-08",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 8042,
- "zh_chars": 4159
- },
- "embedding-fonts-so-a-pdf-looks-the-same-everywhere": {
- "slug": "embedding-fonts-so-a-pdf-looks-the-same-everywhere",
- "title": "Embedding Fonts So a PDF Looks the Same on Every Machine",
- "zh_title": "把字体嵌进 PDF,让它在任何机器上都长一样",
- "category": "engineering",
- "pub_date": "2026-10-15",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 8591,
- "zh_chars": 3747
- },
- "every-player-stuttered-the-file-was-fine": {
- "slug": "every-player-stuttered-the-file-was-fine",
- "title": "Every Player Stuttered on the Same Video. The File Was Blameless.",
- "zh_title": "每个播放器都在同一个视频上卡顿,而文件本身是无辜的",
- "category": "devops",
- "pub_date": "2026-10-08",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 8459,
- "zh_chars": 4296
- },
- "five-step-ai-roadmap-what-i-actually-run": {
- "slug": "five-step-ai-roadmap-what-i-actually-run",
- "title": "The Five-Step AI Roadmap Everyone Shares — and What I Actually Run in Production",
- "zh_title": "全网都在转的 AI 五步学习路线 —— 我真正在生产里跑的是哪几步",
- "category": "ai",
- "pub_date": "2026-10-13",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 13155,
- "zh_chars": 5483
- },
- "from-audio-to-a-4-minute-ai-music-video": {
- "slug": "from-audio-to-a-4-minute-ai-music-video",
- "title": "From Audio to a 4-Minute Music Video: Orchestrating Gemini, AI Diffusion, and Filmora Editing",
- "zh_title": "从一首歌到 4 分钟完整 MV:我如何用 Gemini、AI 生成与 Filmora 剪辑串起全流程实操",
- "category": "case-studies",
- "pub_date": "2026-10-25",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 11027,
- "zh_chars": 5168
- },
- "fully-on-chain-svg-nfts": {
- "slug": "fully-on-chain-svg-nfts",
- "title": "Fully On-Chain SVG NFTs: Putting the Artwork Inside the Contract",
- "zh_title": "全链上 SVG NFT:把艺术作品放进合约里",
- "category": "web3",
- "pub_date": "2024-10-08",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:11",
- "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
- "en_chars": 16205,
- "zh_chars": 10281
- },
- "generating-full-length-songs-with-yue2-comfyui": {
- "slug": "generating-full-length-songs-with-yue2-comfyui",
- "title": "Generating Full-Length Songs with YuE2 BF16: Overcoming Duration Limits and Audio Truncation",
- "zh_title": "用 YuE2 BF16 生成完整长歌:突破时长限制与音频硬截断",
- "category": "tutorials",
- "pub_date": "2026-10-26",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 06:04",
- "notes": "第3批微调项落地并通过:时长校正283.1s/单调时间戳与防死锁/专业人设去二次元俚语/B2B转化增强",
- "en_chars": 134821,
- "zh_chars": 127730
- },
- "generating-subtitles-for-songs-without-timestamps": {
- "slug": "generating-subtitles-for-songs-without-timestamps",
- "title": "Generating Subtitles for Songs That Ship Without Timestamps",
- "zh_title": "给没有时间戳的歌曲自动生成字幕",
- "category": "tutorials",
- "pub_date": "2026-10-20",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:11",
- "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
- "en_chars": 47576,
- "zh_chars": 42677
- },
- "giving-ai-your-mouse-and-keyboard-safely": {
- "slug": "giving-ai-your-mouse-and-keyboard-safely",
- "title": "Giving AI Your Mouse and Keyboard: Safe Guardrails for Windows Desktop MCP",
- "zh_title": "把鼠标和键盘交给 AI 之前:Windows 桌面 MCP 的安全边界与实战防线",
- "category": "ai",
- "pub_date": "2025-10-22",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 5906,
- "zh_chars": 3149
- },
- "hardening-a-tor-onion-service": {
- "slug": "hardening-a-tor-onion-service",
- "title": "Hardening a Tor Onion Service: What Actually Matters",
- "zh_title": "加固 Tor 洋葱服务:真正重要的是什么",
- "category": "devops",
- "pub_date": "2025-12-10",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 14907,
- "zh_chars": 7041
- },
- "hello-world": {
- "slug": "hello-world",
- "title": "Hello, World — About This Blog",
- "zh_title": "你好,世界 —— 关于这个博客",
- "category": "notes",
- "pub_date": "2024-09-24",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 1938,
- "zh_chars": 1074
- },
- "how-i-built-an-automated-music-video-pipeline": {
- "slug": "how-i-built-an-automated-music-video-pipeline",
- "title": "How I Built an Automated Music Video Pipeline with Remotion and Dockerized YouTube CDP",
- "zh_title": "我是如何用 Remotion 与 Docker CDP 打造全自动 MV 生成与多语种油管发布流的",
- "category": "case-studies",
- "pub_date": "2025-04-18",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 9265,
- "zh_chars": 4931
- },
- "how-i-built-the-digikedai-telegram-bot": {
- "slug": "how-i-built-the-digikedai-telegram-bot",
- "title": "How I Built the DigiKedai Telegram AI Bot",
- "zh_title": "我是如何构建 DigiKedai Telegram AI 客服机器人的",
- "category": "case-studies",
- "pub_date": "2026-09-06",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 10719,
- "zh_chars": 5271
- },
- "how-i-host-this-blog": {
- "slug": "how-i-host-this-blog",
- "title": "How I Host This Blog: Astro, Gitea Actions, and Self-Hosted CI/CD",
- "zh_title": "我是如何托管这个博客的:Astro、Gitea Actions 与自托管 CI/CD",
- "category": "case-studies",
- "pub_date": "2026-09-04",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 5908,
- "zh_chars": 4145
- },
- "how-i-made-my-own-songs-with-suno-ai": {
- "slug": "how-i-made-my-own-songs-with-suno-ai",
- "title": "How I Made My Own Songs with Suno AI",
- "zh_title": "我用 Suno AI 做出自己的歌",
- "category": "case-studies",
- "pub_date": "2026-09-11",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 16935,
- "zh_chars": 11737
- },
- "how-i-vetted-20-vps-providers-with-parallel-subagents": {
- "slug": "how-i-vetted-20-vps-providers-with-parallel-subagents",
- "title": "How I Vetted 20 VPS Providers in Three Hours with Parallel Subagents",
- "zh_title": "如何用并行子代理在三小时内核查 20 家 VPS 服务商",
- "category": "case-studies",
- "pub_date": "2025-11-12",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 8593,
- "zh_chars": 3613
- },
- "how-to-verify-a-hosting-provider-before-you-buy": {
- "slug": "how-to-verify-a-hosting-provider-before-you-buy",
- "title": "How to Verify a Hosting Provider Before You Buy",
- "zh_title": "购买主机前,如何核实一家服务商的真实情况",
- "category": "tutorials",
- "pub_date": "2025-11-26",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:11",
- "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
- "en_chars": 7710,
- "zh_chars": 3405
- },
- "i-let-an-agent-manage-my-shopee-vouchers": {
- "slug": "i-let-an-agent-manage-my-shopee-vouchers",
- "title": "I Let an Agent Manage My Shopee Vouchers — And It Created One I Never Approved",
- "zh_title": "我让 AI 助手管我的 Shopee 优惠券 —— 它建了一张我没批准的券",
- "category": "ai",
- "pub_date": "2025-07-08",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 9981,
- "zh_chars": 4824
- },
- "it-reported-success-nothing-had-changed": {
- "slug": "it-reported-success-nothing-had-changed",
- "title": "It Reported Success. Nothing Had Changed.",
- "zh_title": "它报告了成功,可实际上什么都没变",
- "category": "devops",
- "pub_date": "2026-10-08",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 8349,
- "zh_chars": 3886
- },
- "jpa-version-field-lost-update": {
- "slug": "jpa-version-field-lost-update",
- "title": "The JPA Field That Turns a Silent Lost Update Into a 409",
- "zh_title": "那个把静默丢失更新变成 409 的 JPA 字段:@Version 乐观锁",
- "category": "engineering",
- "pub_date": "2026-08-19",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 12536,
- "zh_chars": 7506
- },
- "loop-engineering-without-a-coding-agent": {
- "slug": "loop-engineering-without-a-coding-agent",
- "title": "Loop Engineering Without a Coding Agent: The 11 Cron Jobs That Run My Infrastructure",
- "zh_title": "没有 Coding Agent 的 Loop Engineering:撑起我这套基础设施的 11 个定时任务",
- "category": "case-studies",
- "pub_date": "2026-10-01",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 14249,
- "zh_chars": 6609
- },
- "migrating-codeigniter-iis-to-openlitespeed": {
- "slug": "migrating-codeigniter-iis-to-openlitespeed",
- "title": "Migrating a CodeIgniter 4 App From IIS to OpenLiteSpeed",
- "zh_title": "把 CodeIgniter 4 应用从 IIS 迁移到 OpenLiteSpeed",
- "category": "engineering",
- "pub_date": "2026-09-27",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 14943,
- "zh_chars": 8425
- },
- "my-backup-was-using-someone-elses-api-quota": {
- "slug": "my-backup-was-using-someone-elses-api-quota",
- "title": "My Backup Was Crawling Because I Was Using Someone Else's API Quota",
- "zh_title": "我的备份爬得比爬虫还慢,因为我在用别人的 API 配额",
- "category": "notes",
- "pub_date": "2026-10-15",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 8572,
- "zh_chars": 4527
- },
- "n8n-v1-to-v2-upgrade-gotchas": {
- "slug": "n8n-v1-to-v2-upgrade-gotchas",
- "title": "Upgrading n8n v1 to v2: Seven Deprecations in One Log File",
- "zh_title": "n8n v1 升级到 v2:一个日志文件里的七项废弃警告",
- "category": "devops",
- "pub_date": "2026-09-18",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 12262,
- "zh_chars": 6712
- },
- "nocodb-sso-is-a-licensed-feature": {
- "slug": "nocodb-sso-is-a-licensed-feature",
- "title": "NocoDB SSO Is a Licensed Feature — and the Env Vars Won't Tell You",
- "zh_title": "NocoDB SSO 是授权功能:环境变量不会告诉你的事",
- "category": "notes",
- "pub_date": "2026-09-29",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 9937,
- "zh_chars": 5421
- },
- "one-hostname-public-tracker-sso-dashboard": {
- "slug": "one-hostname-public-tracker-sso-dashboard",
- "title": "One Hostname for a Public Tracker and an SSO-Gated Dashboard",
- "zh_title": "公开的埋点与要 SSO 的仪表盘,怎么共用一个域名",
- "category": "devops",
- "pub_date": "2026-09-30",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 14400,
- "zh_chars": 6837
- },
- "one-prometheus-for-unraid-synology-and-a-vps": {
- "slug": "one-prometheus-for-unraid-synology-and-a-vps",
- "title": "One Prometheus for unRaid, a Synology NAS, and a VPS: What I Got Wrong",
- "zh_title": "用一个 Prometheus 监控 unRaid、群晖 NAS 和 VPS:我做错的四件事",
- "category": "case-studies",
- "pub_date": "2026-07-29",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 8358,
- "zh_chars": 4690
- },
- "passbolt-hang-three-failure-modes": {
- "slug": "passbolt-hang-three-failure-modes",
- "title": "Passbolt UI Kept Hanging — Three Failure Modes From One Missing Config File",
- "zh_title": "Passbolt 界面反复卡死——一个缺失的配置文件引发的三种故障模式",
- "category": "devops",
- "pub_date": "2026-01-07",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 7864,
- "zh_chars": 4187
- },
- "patching-workbench-26-for-mariadb": {
- "slug": "patching-workbench-26-for-mariadb",
- "title": "When Your Database Client Lies to You: Patching Workbench 26 for MariaDB",
- "zh_title": "当数据库客户端对你撒谎:为 MariaDB 修补 Workbench 26",
- "category": "devops",
- "pub_date": "2026-09-19",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 15965,
- "zh_chars": 9501
- },
- "php2shell-patching-every-php-runtime-i-manage": {
- "slug": "php2shell-patching-every-php-runtime-i-manage",
- "title": "PHP2Shell: I Measured Every PHP Runtime I Manage. All of Them Were Vulnerable.",
- "zh_title": "PHP2Shell:我管的每台机器,PHP 运行时全都中了",
- "category": "devops",
- "pub_date": "2026-10-23",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 22354,
- "zh_chars": 12531
- },
- "plugging-windows-desktop-control-into-four-ai-agents": {
- "slug": "plugging-windows-desktop-control-into-four-ai-agents",
- "title": "Plugging Windows Desktop Control into Four AI Agents: Hermes, OpenCode, Claw, and Claude",
- "zh_title": "把 Windows 桌面控制接进 4 款 AI Agent:Hermes、OpenCode、Claw 与 Claude 的实战架构",
- "category": "ai",
- "pub_date": "2025-12-23",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 6554,
- "zh_chars": 5030
- },
- "putting-a-front-door-on-an-app-you-cant-modify": {
- "slug": "putting-a-front-door-on-an-app-you-cant-modify",
- "title": "Putting a Front Door on an App You Can't Modify",
- "zh_title": "给自己改不了源码的应用装一扇前门",
- "category": "devops",
- "pub_date": "2025-06-24",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 13927,
- "zh_chars": 7415
- },
- "read-only-nocodb-dashboard-for-a-remote-database": {
- "slug": "read-only-nocodb-dashboard-for-a-remote-database",
- "title": "A Read-Only NocoDB Dashboard for a Database on Another Machine",
- "zh_title": "给另一台机器上的数据库配一个 NocoDB 只读看板",
- "category": "devops",
- "pub_date": "2026-09-29",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 12009,
- "zh_chars": 6287
- },
- "reading-a-containers-own-api-docs": {
- "slug": "reading-a-containers-own-api-docs",
- "title": "Read a Container's Own API Docs Instead of the Vendor's",
- "zh_title": "与其看供应商文档,不如直接读容器自带的 API 文档",
- "category": "notes",
- "pub_date": "2025-10-01",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 8382,
- "zh_chars": 5489
- },
- "replacing-rdpguard-with-ipban": {
- "slug": "replacing-rdpguard-with-ipban",
- "title": "Replacing RDPGuard With IPBan: The Traps Nobody Documents",
- "zh_title": "用 IPBan 替换 RDPGuard:没人写出来的三个坑",
- "category": "devops",
- "pub_date": "2026-09-19",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 04:09",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
- "en_chars": 13131,
- "zh_chars": 7795
- },
- "running-ai-video-locally-on-a-12gb-gpu": {
- "slug": "running-ai-video-locally-on-a-12gb-gpu",
- "title": "Running AI Video Locally on a 12 GB GPU: Real Numbers for Wan 2.2 and SeedVR2",
- "zh_title": "12GB 显卡跑本地 AI 视频:Wan 2.2 加 SeedVR2 的真实数据",
- "category": "ai",
- "pub_date": "2026-10-20",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 18508,
- "zh_chars": 12878
- },
- "running-production-infrastructure-solo": {
- "slug": "running-production-infrastructure-solo",
- "title": "Running Production Infrastructure Solo: 162 Containers Across Three Hosts",
- "zh_title": "一个人跑生产基础设施:三台主机、162 个容器",
- "category": "case-studies",
- "pub_date": "2026-10-06",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 7975,
- "zh_chars": 3722
- },
- "running-tts-as-a-service-with-token-sidecars": {
- "slug": "running-tts-as-a-service-with-token-sidecars",
- "title": "I Ran a Text-to-Speech Service for a Year on Two Cron Containers",
- "zh_title": "我用两个 Cron 容器跑了一年的语音合成服务",
- "category": "ai",
- "pub_date": "2026-09-18",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 14141,
- "zh_chars": 7929
- },
- "scraping-bot-walled-marketplace-warm-browser-session": {
- "slug": "scraping-bot-walled-marketplace-warm-browser-session",
- "title": "Scraping a Bot-Walled Marketplace With a Warm Browser Session",
- "zh_title": "用「热浏览器会话」爬取有反爬墙的电商网站",
- "category": "engineering",
- "pub_date": "2026-09-13",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 11313,
- "zh_chars": 5734
- },
- "self-healing-digital-goods-entitlements": {
- "slug": "self-healing-digital-goods-entitlements",
- "title": "Self-Healing Access Control for Digital Goods: NocoDB, n8n, and AList",
- "zh_title": "数字商品的自愈式访问控制:NocoDB、n8n 与 AList",
- "category": "case-studies",
- "pub_date": "2026-09-18",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 14765,
- "zh_chars": 6819
- },
- "self-hosted-speech-to-text-api": {
- "slug": "self-hosted-speech-to-text-api",
- "title": "Your Own Speech-to-Text Server: Faster Than Typing, Private by Default",
- "zh_title": "自建语音转文字服务:打字速度的 3 倍以上,而且数据不出内网",
- "category": "case-studies",
- "pub_date": "2026-09-19",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 14708,
- "zh_chars": 7794
- },
- "self-hosted-tts-cosyvoice2-n8n-gpu": {
- "slug": "self-hosted-tts-cosyvoice2-n8n-gpu",
- "title": "How I Replaced Cloud TTS with a Self-Hosted Neural Voice Stack on an RTX 3060",
- "zh_title": "从云端转本地:在 12GB 显卡与群晖 N8N 上搭建中英混读高保真 TTS 语音栈",
- "category": "case-studies",
- "pub_date": "2026-10-22",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 8913,
- "zh_chars": 4773
- },
- "self-hosting-mem0-memory-stack": {
- "slug": "self-hosting-mem0-memory-stack",
- "title": "How I Self-Hosted a Mem0 Memory Stack (So My AI Agent Remembers)",
- "zh_title": "我如何在私有存储节点上自托管 Mem0 记忆栈(让 AI 助手拥有持久记忆)",
- "category": "case-studies",
- "pub_date": "2026-09-16",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 8057,
- "zh_chars": 4849
- },
- "shipping-an-ai-photo-editor-as-a-wordpress-plugin": {
- "slug": "shipping-an-ai-photo-editor-as-a-wordpress-plugin",
- "title": "Shipping an AI Photo Editor as a WordPress Plugin: 22 Versions of Hard Lessons",
- "zh_title": "把 AI 修图工具做成 WordPress 插件:22 个版本换来的经验",
- "category": "case-studies",
- "pub_date": "2026-09-18",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 13879,
- "zh_chars": 6861
- },
- "slashing-agent-context-with-a-code-graph": {
- "slug": "slashing-agent-context-with-a-code-graph",
- "title": "Slashing Agent Context and Token Churn With a Code Graph",
- "zh_title": "别再把整个 Repo 喂给 Agent:用 Code Graph 斩断 Token 膨胀与遗忘陷阱",
- "category": "ai",
- "pub_date": "2026-04-28",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 8122,
- "zh_chars": 5040
- },
- "smartctl-exit-code-32-skips-the-disks-that-matter": {
- "slug": "smartctl-exit-code-32-skips-the-disks-that-matter",
- "title": "smartctl Exit Code 32: The Code That Skips the Disks You Care About",
- "zh_title": "smartctl 退出码 32:专门跳过你最该看的硬盘的那个“错误”",
- "category": "notes",
- "pub_date": "2026-04-14",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 5475,
- "zh_chars": 2881
- },
- "space-reclamation-wasnt-stuck-it-was-waiting": {
- "slug": "space-reclamation-wasnt-stuck-it-was-waiting",
- "title": "Space Reclamation Wasn't Stuck. It Was Waiting for Tomorrow at 09:29.",
- "zh_title": "空间回收没卡住,它只是在等明天早上 09:29",
- "category": "devops",
- "pub_date": "2026-10-14",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 6574,
- "zh_chars": 3505
- },
- "stop-writing-agent-prompts-for-deterministic-work": {
- "slug": "stop-writing-agent-prompts-for-deterministic-work",
- "title": "Stop Writing Agent Prompts for Deterministic Work",
- "zh_title": "确定性的活,别再用 Agent Prompt 去跑",
- "category": "notes",
- "pub_date": "2026-10-01",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 05:22",
- "notes": "外部顾问审阅通过并落地修改",
- "en_chars": 4951,
- "zh_chars": 2216
- },
- "syncing-a-self-improving-ai-agent-across-machines": {
- "slug": "syncing-a-self-improving-ai-agent-across-machines",
- "title": "Syncing a Self-Improving AI Agent Across Machines",
- "zh_title": "在多台机器之间同步一个会自我学习的 AI Agent",
- "category": "case-studies",
- "pub_date": "2026-02-18",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 04:22",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
- "en_chars": 8861,
- "zh_chars": 4541
- },
- "synology-api-401-with-the-correct-password": {
- "slug": "synology-api-401-with-the-correct-password",
- "title": "Why Your Synology API Login Returns 401 With the Correct Password",
- "zh_title": "密码正确,Synology API 登录却返回 401,问题出在哪",
- "category": "notes",
- "pub_date": "2025-09-10",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 5053,
- "zh_chars": 3063
- },
- "synology-spreadsheet-api-is-a-container": {
- "slug": "synology-spreadsheet-api-is-a-container",
- "title": "The Synology Spreadsheet API Is a Container, Not an API Endpoint",
- "zh_title": "Synology 电子表格 API 是一个容器,不是一个 API 端点",
- "category": "devops",
- "pub_date": "2025-08-20",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 16101,
- "zh_chars": 9200
- },
- "that-dying-ssd-was-just-a-bad-sata-cable": {
- "slug": "that-dying-ssd-was-just-a-bad-sata-cable",
- "title": "I Was About to RMA This SSD — the Fault Was a SATA Cable",
- "zh_title": "我差点把这颗 SSD 送修——真正坏的是 SATA 数据线",
- "category": "devops",
- "pub_date": "2026-09-16",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 7471,
- "zh_chars": 3940
- },
- "the-cause-was-trim-not-the-ssds": {
- "slug": "the-cause-was-trim-not-the-ssds",
- "title": "The Corruption Came Back on Different Drives — the Cause Was TRIM, Not the SSDs",
- "zh_title": "换了不同的硬盘,同样的损坏又来了——真凶是 TRIM,不是 SSD",
- "category": "devops",
- "pub_date": "2026-09-18",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 13042,
- "zh_chars": 7310
- },
- "the-nocodb-attachment-that-wouldnt-update": {
- "slug": "the-nocodb-attachment-that-wouldnt-update",
- "title": "The NocoDB Attachment That Wouldn't Update (and a Regex That Missed)",
- "zh_title": "NocoDB 附件字段怎么改都不生效,和一个漏网的正则",
- "category": "notes",
- "pub_date": "2026-02-04",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 3798,
- "zh_chars": 2312
- },
- "the-single-writer-rule-for-coding-agents": {
- "slug": "the-single-writer-rule-for-coding-agents",
- "title": "The Single-Writer Rule: Orchestrating Coding Agents Without Multi-Agent Chaos",
- "zh_title": "代码只让一个 AI 改:我是如何避开多 Agent 协同踩坑的「单写多审」架构",
- "category": "case-studies",
- "pub_date": "2026-08-26",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 13331,
- "zh_chars": 6954
- },
- "the-upstream-was-deleted-then-came-back-rewritten": {
- "slug": "the-upstream-was-deleted-then-came-back-rewritten",
- "title": "The Upstream Was Deleted, Then Came Back as a Rewrite",
- "zh_title": "上游被删了,然后带着一个重写版回来",
- "category": "devops",
- "pub_date": "2026-10-06",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 11636,
- "zh_chars": 5229
- },
- "two-ways-a-javascript-file-stops-working-and-looks-fine": {
- "slug": "two-ways-a-javascript-file-stops-working-and-looks-fine",
- "title": "Two Ways a JavaScript File Stops Working and Looks Fine",
- "zh_title": "JavaScript 文件静默失效的两种方式:而页面看上去一切正常",
- "category": "engineering",
- "pub_date": "2025-02-20",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 8293,
- "zh_chars": 3881
- },
- "unraid-stop-array-hangs-on-swapfile": {
- "slug": "unraid-stop-array-hangs-on-swapfile",
- "title": "Why Unraid Hangs When You Stop the Array With a Swapfile — and the Fix",
- "zh_title": "Unraid 停止阵列时卡死的真相:Swapfile 锁住挂载点 — 附修复方案",
- "category": "devops",
- "pub_date": "2026-09-15",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 10470,
- "zh_chars": 6376
- },
- "upgrading-codeigniter-46-to-47": {
- "slug": "upgrading-codeigniter-46-to-47",
- "title": "Upgrading CodeIgniter 4.6 to 4.7: The Breaking Changes the Guide Misses",
- "zh_title": "CodeIgniter 4.6 升级到 4.7:官方指南没说到的破坏性变更",
- "category": "notes",
- "pub_date": "2026-09-27",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 05:22",
- "notes": "外部顾问审阅通过并落地修改",
- "en_chars": 9469,
- "zh_chars": 5411
- },
- "uploading-large-videos-to-youtube-via-cdp": {
- "slug": "uploading-large-videos-to-youtube-via-cdp",
- "title": "Uploading 100MB+ Videos to YouTube via CDP: The Docker Archive Pattern",
- "zh_title": "用 CDP 自动化上传百兆视频到 YouTube:跨越 DevTools 内存墙的 Docker 归档法",
- "category": "devops",
- "pub_date": "2025-08-22",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 11267,
- "zh_chars": 7173
- },
- "using-chinese-llm-apis-from-malaysia": {
- "slug": "using-chinese-llm-apis-from-malaysia",
- "title": "Using Chinese LLM APIs From Malaysia: What Actually Works",
- "zh_title": "从马来西亚使用中国大模型 API:真正可行的方法",
- "category": "ai",
- "pub_date": "2026-09-11",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 03:00",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 7540,
- "zh_chars": 3536
- },
- "verifying-a-pdf-report-page-by-page": {
- "slug": "verifying-a-pdf-report-page-by-page",
- "title": "Verifying a 19-Page PDF Report Page by Page",
- "zh_title": "逐页验证一份 19 页的 PDF 报告",
- "category": "engineering",
- "pub_date": "2026-09-28",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 12525,
- "zh_chars": 6685
- },
- "vetting-an-open-source-dependency-before-you-bet-on-it": {
- "slug": "vetting-an-open-source-dependency-before-you-bet-on-it",
- "title": "How I Vet an Open-Source Dependency Before Betting On It",
- "zh_title": "如何在押注前验证一个开源依赖:我用的六项检查",
- "category": "devops",
- "pub_date": "2026-09-29",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 8512,
- "zh_chars": 4201
- },
- "when-smart-says-healthy-but-your-raid-is-corrupting-data": {
- "slug": "when-smart-says-healthy-but-your-raid-is-corrupting-data",
- "title": "When SMART Says Healthy and Your RAID Is Silently Corrupting Data",
- "zh_title": "SMART 显示健康,RAID 却在悄悄损坏数据",
- "category": "devops",
- "pub_date": "2026-09-16",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 14160,
- "zh_chars": 6246
- },
- "when-to-give-your-agent-physical-clicks": {
- "slug": "when-to-give-your-agent-physical-clicks",
- "title": "When to Give Your Agent Physical Clicks (And When to Never Touch Them)",
- "zh_title": "什么时候该让 AI 操控物理鼠标(以及什么时候绝对不要用)",
- "category": "case-studies",
- "pub_date": "2026-05-28",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 6882,
- "zh_chars": 3608
- },
- "why-500000-lines-of-commit-churn-only-left-230000-in-production": {
- "slug": "why-500000-lines-of-commit-churn-only-left-230000-in-production",
- "title": "I Committed 518,000 Lines in 30 Days. Production Kept 235,000.",
- "zh_title": "30 天提交 51.8 万行,生产环境只留下 23.5 万行",
- "category": "engineering",
- "pub_date": "2026-10-18",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 8949,
- "zh_chars": 4197
- },
- "why-chrome-forgets-its-tabs-in-a-container": {
- "slug": "why-chrome-forgets-its-tabs-in-a-container",
- "title": "Why Chrome Forgets Its Tabs in a Container — And How I Fixed It",
- "zh_title": "为什么容器里的 Chrome 会忘记标签页 —— 以及我是怎么修好的",
- "category": "devops",
- "pub_date": "2026-09-29",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 15430,
- "zh_chars": 9234
- },
- "why-i-built-a-video-knowledge-base": {
- "slug": "why-i-built-a-video-knowledge-base",
- "title": "Why I Built a Video Knowledge Base: Turning Saved Short Videos into Searchable Notes",
- "zh_title": "为什么我要做视频精读知识库:把收藏夹吃灰的短视频变成可检索笔记",
- "category": "case-studies",
- "pub_date": "2024-11-28",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 5770,
- "zh_chars": 2924
- },
- "why-i-still-bought-a-local-gpu": {
- "slug": "why-i-still-bought-a-local-gpu",
- "title": "Why I Still Bought a GPU to Run AI When Claude and GPT Are Obviously Stronger",
- "zh_title": "Claude 和 GPT 明明更强,我为什么还是买了 GPU 跑本地 AI",
- "category": "ai",
- "pub_date": "2026-09-21",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 11811,
- "zh_chars": 4924
- },
- "why-is-my-tts-model-repeating-percent": {
- "slug": "why-is-my-tts-model-repeating-percent",
- "title": "Why Is My TTS Model Repeating \\\"Percent\\\"? The URL-Encoding Trap in Speech Pipelines",
- "zh_title": "为什么本地语音合成一直在念 “Percent”?语音管线里的 URL 编码与沙盒避坑记",
- "category": "notes",
- "pub_date": "2026-10-22",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 7728,
- "zh_chars": 4968
- },
- "why-multi-agent-coding-fails": {
- "slug": "why-multi-agent-coding-fails",
- "title": "Why Multi-Agent Coding Fails: The Telephone Game and Architectural Drift",
- "zh_title": "为什么多个 AI 一起写代码总会烂尾?传话失真与隐式决策困境",
- "category": "notes",
- "pub_date": "2025-07-29",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 6364,
- "zh_chars": 3353
- },
- "why-my-on-chain-nft-art-changed-on-windows": {
- "slug": "why-my-on-chain-nft-art-changed-on-windows",
- "title": "Why My On-Chain NFT Art Changed When I Cloned It on Windows",
- "zh_title": "为什么在 Windows 上克隆后,我的链上 NFT 图像变了",
- "category": "web3",
- "pub_date": "2026-08-19",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:11",
- "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
- "en_chars": 8423,
- "zh_chars": 4585
- },
- "why-telegram-bot-notifications-die": {
- "slug": "why-telegram-bot-notifications-die",
- "title": "Why Telegram Bot Notifications Die: IPv6, DNS, and a 400 That Took a Day",
- "zh_title": "Telegram 机器人通知为什么发不出去:IPv6、DNS 和一个追查了一整天的 400",
- "category": "devops",
- "pub_date": "2026-03-25",
- "status": "approved",
- "batch_id": "Batch-02",
- "approved_at": "2026-10-27 06:10",
- "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
- "en_chars": 10753,
- "zh_chars": 6005
- },
- "why-your-cdp-clicks-silently-fail": {
- "slug": "why-your-cdp-clicks-silently-fail",
- "title": "Why Your CDP Clicks Silently Fail: Smooth Scroll, Minimised Windows, and JS-Dispatched Events",
- "zh_title": "为什么你的 CDP 点击会静默失效:平滑滚动、最小化窗口与 JS 派发事件",
- "category": "devops",
- "pub_date": "2025-03-19",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 8054,
- "zh_chars": 4536
- },
- "why-your-grafana-dashboard-shows-no-data": {
- "slug": "why-your-grafana-dashboard-shows-no-data",
- "title": "Why Your Grafana Dashboard Shows No Data (When Prometheus Is Fine)",
- "zh_title": "Prometheus 明明有数据,Grafana 面板却全是 No data 的原因",
- "category": "devops",
- "pub_date": "2026-05-17",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 7456,
- "zh_chars": 3919
- },
- "why-your-headless-browser-cant-scrape-everything": {
- "slug": "why-your-headless-browser-cant-scrape-everything",
- "title": "Why Your Headless Browser Can't Scrape Everything",
- "zh_title": "为什么你的无头浏览器不是万能爬虫",
- "category": "case-studies",
- "pub_date": "2026-01-21",
- "status": "approved",
- "batch_id": "Batch-03",
- "approved_at": "2026-10-27 03:49",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
- "en_chars": 8076,
- "zh_chars": 3792
- },
- "why-your-windows-mcp-calls-silently-fail": {
- "slug": "why-your-windows-mcp-calls-silently-fail",
- "title": "Why Your Windows Desktop MCP Calls Silently Fail: Taming the 10-Second Idle Gate",
- "zh_title": "为什么你的 Windows 桌面 MCP 调用会静默失败:攻破 10 秒空闲门禁与避坑实录",
- "category": "notes",
- "pub_date": "2026-03-12",
- "status": "approved",
- "batch_id": "Batch-04",
- "approved_at": "2026-10-27 05:21",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
- "en_chars": 5663,
- "zh_chars": 3857
- },
- "your-disk-full-alert-is-lying": {
- "slug": "your-disk-full-alert-is-lying",
- "title": "Your Disk-Full Alert Is Lying: Stop Alerting on Percentages",
- "zh_title": "你的“磁盘将满”告警在说谎:别再按百分比告警",
- "category": "devops",
- "pub_date": "2026-06-24",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 06:09",
- "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
- "en_chars": 6139,
- "zh_chars": 3090
- },
- "yue2-on-a-12gb-gpu-songs-that-lost-their-endings": {
- "slug": "yue2-on-a-12gb-gpu-songs-that-lost-their-endings",
- "title": "Why My AI Songs Kept Losing Their Endings: Running YuE2 on a 12 GB GPU",
- "zh_title": "为什么我的 AI 歌曲总是丢结尾:在 12GB 显卡上跑 YuE2",
- "category": "ai",
- "pub_date": "2026-10-20",
- "status": "approved",
- "batch_id": "Batch-01",
- "approved_at": "2026-10-27 05:26",
- "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
- "en_chars": 28955,
- "zh_chars": 24414
- }
+ "adding-a-domain-to-someone-elses-mail-server": {
+ "slug": "adding-a-domain-to-someone-elses-mail-server",
+ "title": "Adding a Domain to Someone Else's Mail Server",
+ "zh_title": "给别人家的邮件服务器加一个域名",
+ "category": "notes",
+ "pub_date": "2025-05-06",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 7886,
+ "zh_chars": 3603
+ },
+ "adding-english-mode-to-a-chinese-only-web-app": {
+ "slug": "adding-english-mode-to-a-chinese-only-web-app",
+ "title": "No API for Browser Translation: Adding an English Mode to a Chinese-Only Web App",
+ "zh_title": "浏览器翻译没有 API:我给一个纯中文网页应用加上了英文模式",
+ "category": "engineering",
+ "pub_date": "2025-06-11",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 11914,
+ "zh_chars": 6253
+ },
+ "ai-furniture-compositing-with-flux-kontext": {
+ "slug": "ai-furniture-compositing-with-flux-kontext",
+ "title": "How I Built an AI Furniture Compositing Demo With FLUX.1 Kontext",
+ "zh_title": "我如何用 FLUX.1 Kontext 打造 AI 家具合成演示",
+ "category": "case-studies",
+ "pub_date": "2026-09-11",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 12512,
+ "zh_chars": 7237
+ },
+ "authentik-css-greater-than-bug": {
+ "slug": "authentik-css-greater-than-bug",
+ "title": "The Character That Silently Broke My authentik CSS",
+ "zh_title": "一个字符让我的 authentik CSS 静默失效",
+ "category": "devops",
+ "pub_date": "2026-09-20",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 9317,
+ "zh_chars": 5589
+ },
+ "authentik-forward-auth-gate-wasnt-live": {
+ "slug": "authentik-forward-auth-gate-wasnt-live",
+ "title": "The Forward-Auth Gate That Verified Perfectly — and Wasn't Live",
+ "zh_title": "验证完全正确的 Forward-Auth 网关——其实并未上线",
+ "category": "devops",
+ "pub_date": "2026-08-12",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 16526,
+ "zh_chars": 8746
+ },
+ "authentik-major-upgrade-gotchas": {
+ "slug": "authentik-major-upgrade-gotchas",
+ "title": "Authentik 2025.8 to 2026.8: The Breakable Parts Nobody Warns You About",
+ "zh_title": "Authentik 从 2025.8 升级到 2026.8:那些没人提醒你会坏掉的部分",
+ "category": "devops",
+ "pub_date": "2026-09-09",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 11710,
+ "zh_chars": 6147
+ },
+ "automating-cyberpanel-without-the-ui": {
+ "slug": "automating-cyberpanel-without-the-ui",
+ "title": "Automating CyberPanel Without the UI: Reverse-Engineering an Undocumented v2 API",
+ "zh_title": "不用界面管理 CyberPanel:逆向一个没有文档的 v2 API",
+ "category": "devops",
+ "pub_date": "2026-09-09",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 8474,
+ "zh_chars": 5205
+ },
+ "best-ai-video-generators-2026": {
+ "slug": "best-ai-video-generators-2026",
+ "title": "Best AI Video Generators in 2026: Free vs Paid, and My LTX-2.3 Test",
+ "zh_title": "2026 年最佳 AI 视频生成器:免费与付费对比,以及我的 LTX-2.3 实测",
+ "category": "ai",
+ "pub_date": "2026-09-11",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 24245,
+ "zh_chars": 12700
+ },
+ "calibrating-intel-quick-sync-hevc-quality": {
+ "slug": "calibrating-intel-quick-sync-hevc-quality",
+ "title": "Calibrating Intel Quick Sync HEVC: A Cliff at 28, and a Preset That Does Nothing",
+ "zh_title": "校准 Intel Quick Sync HEVC:28 与 30 之间的悬崖,和一个毫无作用的 preset",
+ "category": "tutorials",
+ "pub_date": "2026-10-14",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 10968,
+ "zh_chars": 5968
+ },
+ "chainlink-vrf-v2-lottery-contract": {
+ "slug": "chainlink-vrf-v2-lottery-contract",
+ "title": "An Automated Lottery Contract on Chainlink VRF v2.5 and Automation",
+ "zh_title": "一个基于 Chainlink VRF v2.5 与 Automation 的自动化彩票合约",
+ "category": "web3",
+ "pub_date": "2024-12-10",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:11",
+ "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
+ "en_chars": 16252,
+ "zh_chars": 9707
+ },
+ "debugging-remotion-video-jitter": {
+ "slug": "debugging-remotion-video-jitter",
+ "title": "Debugging Remotion Video Jitter: Why Bass Scaling Ruins Background Transitions",
+ "zh_title": "排查 Remotion 视频抽搐:为什么在背景图挂载音频低频 Scale 会毁了转场",
+ "category": "engineering",
+ "pub_date": "2025-11-26",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 9836,
+ "zh_chars": 5653
+ },
+ "docker-reported-exit-0-the-oom-killer-disagreed": {
+ "slug": "docker-reported-exit-0-the-oom-killer-disagreed",
+ "title": "Docker Reported Exit 0. The Kernel Had Already OOM-Killed It.",
+ "zh_title": "Docker 报告退出码 0,内核却早已执行 OOM-Kill",
+ "category": "devops",
+ "pub_date": "2026-10-16",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 10200,
+ "zh_chars": 5488
+ },
+ "does-hevc-actually-shrink-your-files": {
+ "slug": "does-hevc-actually-shrink-your-files",
+ "title": "Does Re-encoding to HEVC Actually Shrink Your Files? I Measured It.",
+ "zh_title": "转成 HEVC 到底会不会让文件变小?我实测了",
+ "category": "tutorials",
+ "pub_date": "2026-10-08",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 8042,
+ "zh_chars": 4159
+ },
+ "embedding-fonts-so-a-pdf-looks-the-same-everywhere": {
+ "slug": "embedding-fonts-so-a-pdf-looks-the-same-everywhere",
+ "title": "Embedding Fonts So a PDF Looks the Same on Every Machine",
+ "zh_title": "把字体嵌进 PDF,让它在任何机器上都长一样",
+ "category": "engineering",
+ "pub_date": "2026-10-15",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 8591,
+ "zh_chars": 3747
+ },
+ "every-player-stuttered-the-file-was-fine": {
+ "slug": "every-player-stuttered-the-file-was-fine",
+ "title": "Every Player Stuttered on the Same Video. The File Was Blameless.",
+ "zh_title": "每个播放器都在同一个视频上卡顿,而文件本身是无辜的",
+ "category": "devops",
+ "pub_date": "2026-10-08",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 8459,
+ "zh_chars": 4296
+ },
+ "five-step-ai-roadmap-what-i-actually-run": {
+ "slug": "five-step-ai-roadmap-what-i-actually-run",
+ "title": "The Five-Step AI Roadmap Everyone Shares — and What I Actually Run in Production",
+ "zh_title": "全网都在转的 AI 五步学习路线 —— 我真正在生产里跑的是哪几步",
+ "category": "ai",
+ "pub_date": "2026-10-13",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 13155,
+ "zh_chars": 5483
+ },
+ "from-audio-to-a-4-minute-ai-music-video": {
+ "slug": "from-audio-to-a-4-minute-ai-music-video",
+ "title": "From Audio to a 4-Minute Music Video: Orchestrating Gemini, AI Diffusion, and Filmora Editing",
+ "zh_title": "从一首歌到 4 分钟完整 MV:我如何用 Gemini、AI 生成与 Filmora 剪辑串起全流程实操",
+ "category": "case-studies",
+ "pub_date": "2026-10-25",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 11027,
+ "zh_chars": 5168
+ },
+ "fully-on-chain-svg-nfts": {
+ "slug": "fully-on-chain-svg-nfts",
+ "title": "Fully On-Chain SVG NFTs: Putting the Artwork Inside the Contract",
+ "zh_title": "全链上 SVG NFT:把艺术作品放进合约里",
+ "category": "web3",
+ "pub_date": "2024-10-08",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:11",
+ "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
+ "en_chars": 16205,
+ "zh_chars": 10281
+ },
+ "generating-full-length-songs-with-yue2-comfyui": {
+ "slug": "generating-full-length-songs-with-yue2-comfyui",
+ "title": "Generating Full-Length Songs with YuE2 BF16: Overcoming Duration Limits and Audio Truncation",
+ "zh_title": "用 YuE2 BF16 生成完整长歌:突破时长限制与音频硬截断",
+ "category": "tutorials",
+ "pub_date": "2026-10-26",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 06:04",
+ "notes": "第3批微调项落地并通过:时长校正283.1s/单调时间戳与防死锁/专业人设去二次元俚语/B2B转化增强",
+ "en_chars": 134821,
+ "zh_chars": 127730
+ },
+ "generating-subtitles-for-songs-without-timestamps": {
+ "slug": "generating-subtitles-for-songs-without-timestamps",
+ "title": "Generating Subtitles for Songs That Ship Without Timestamps",
+ "zh_title": "给没有时间戳的歌曲自动生成字幕",
+ "category": "tutorials",
+ "pub_date": "2026-10-20",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:11",
+ "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
+ "en_chars": 47576,
+ "zh_chars": 42677
+ },
+ "giving-ai-your-mouse-and-keyboard-safely": {
+ "slug": "giving-ai-your-mouse-and-keyboard-safely",
+ "title": "Giving AI Your Mouse and Keyboard: Safe Guardrails for Windows Desktop MCP",
+ "zh_title": "把鼠标和键盘交给 AI 之前:Windows 桌面 MCP 的安全边界与实战防线",
+ "category": "ai",
+ "pub_date": "2025-10-22",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 5906,
+ "zh_chars": 3149
+ },
+ "hardening-a-tor-onion-service": {
+ "slug": "hardening-a-tor-onion-service",
+ "title": "Hardening a Tor Onion Service: What Actually Matters",
+ "zh_title": "加固 Tor 洋葱服务:真正重要的是什么",
+ "category": "devops",
+ "pub_date": "2025-12-10",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 14907,
+ "zh_chars": 7041
+ },
+ "hello-world": {
+ "slug": "hello-world",
+ "title": "Hello, World — About This Blog",
+ "zh_title": "你好,世界 —— 关于这个博客",
+ "category": "notes",
+ "pub_date": "2024-09-24",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 1938,
+ "zh_chars": 1074
+ },
+ "how-i-built-an-automated-music-video-pipeline": {
+ "slug": "how-i-built-an-automated-music-video-pipeline",
+ "title": "How I Built an Automated Music Video Pipeline with Remotion and Dockerized YouTube CDP",
+ "zh_title": "我是如何用 Remotion 与 Docker CDP 打造全自动 MV 生成与多语种油管发布流的",
+ "category": "case-studies",
+ "pub_date": "2025-04-18",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 9265,
+ "zh_chars": 4931
+ },
+ "how-i-built-the-digikedai-telegram-bot": {
+ "slug": "how-i-built-the-digikedai-telegram-bot",
+ "title": "How I Built the DigiKedai Telegram AI Bot",
+ "zh_title": "我是如何构建 DigiKedai Telegram AI 客服机器人的",
+ "category": "case-studies",
+ "pub_date": "2026-09-06",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 10719,
+ "zh_chars": 5271
+ },
+ "how-i-host-this-blog": {
+ "slug": "how-i-host-this-blog",
+ "title": "How I Host This Blog: Astro, Gitea Actions, and Self-Hosted CI/CD",
+ "zh_title": "我是如何托管这个博客的:Astro、Gitea Actions 与自托管 CI/CD",
+ "category": "case-studies",
+ "pub_date": "2026-09-04",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 5908,
+ "zh_chars": 4145
+ },
+ "how-i-made-my-own-songs-with-suno-ai": {
+ "slug": "how-i-made-my-own-songs-with-suno-ai",
+ "title": "How I Made My Own Songs with Suno AI",
+ "zh_title": "我用 Suno AI 做出自己的歌",
+ "category": "case-studies",
+ "pub_date": "2026-09-11",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 16935,
+ "zh_chars": 11737
+ },
+ "how-i-vetted-20-vps-providers-with-parallel-subagents": {
+ "slug": "how-i-vetted-20-vps-providers-with-parallel-subagents",
+ "title": "How I Vetted 20 VPS Providers in Three Hours with Parallel Subagents",
+ "zh_title": "如何用并行子代理在三小时内核查 20 家 VPS 服务商",
+ "category": "case-studies",
+ "pub_date": "2025-11-12",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 8593,
+ "zh_chars": 3613
+ },
+ "how-to-verify-a-hosting-provider-before-you-buy": {
+ "slug": "how-to-verify-a-hosting-provider-before-you-buy",
+ "title": "How to Verify a Hosting Provider Before You Buy",
+ "zh_title": "购买主机前,如何核实一家服务商的真实情况",
+ "category": "tutorials",
+ "pub_date": "2025-11-26",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:11",
+ "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
+ "en_chars": 7710,
+ "zh_chars": 3405
+ },
+ "i-let-an-agent-manage-my-shopee-vouchers": {
+ "slug": "i-let-an-agent-manage-my-shopee-vouchers",
+ "title": "I Let an Agent Manage My Shopee Vouchers — And It Created One I Never Approved",
+ "zh_title": "我让 AI 助手管我的 Shopee 优惠券 —— 它建了一张我没批准的券",
+ "category": "ai",
+ "pub_date": "2025-07-08",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 9981,
+ "zh_chars": 4824
+ },
+ "it-reported-success-nothing-had-changed": {
+ "slug": "it-reported-success-nothing-had-changed",
+ "title": "It Reported Success. Nothing Had Changed.",
+ "zh_title": "它报告了成功,可实际上什么都没变",
+ "category": "devops",
+ "pub_date": "2026-10-08",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 8349,
+ "zh_chars": 3886
+ },
+ "jpa-version-field-lost-update": {
+ "slug": "jpa-version-field-lost-update",
+ "title": "The JPA Field That Turns a Silent Lost Update Into a 409",
+ "zh_title": "那个把静默丢失更新变成 409 的 JPA 字段:@Version 乐观锁",
+ "category": "engineering",
+ "pub_date": "2026-08-19",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 12536,
+ "zh_chars": 7506
+ },
+ "loop-engineering-without-a-coding-agent": {
+ "slug": "loop-engineering-without-a-coding-agent",
+ "title": "Loop Engineering Without a Coding Agent: The 11 Cron Jobs That Run My Infrastructure",
+ "zh_title": "没有 Coding Agent 的 Loop Engineering:撑起我这套基础设施的 11 个定时任务",
+ "category": "case-studies",
+ "pub_date": "2026-10-01",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 14249,
+ "zh_chars": 6609
+ },
+ "migrating-codeigniter-iis-to-openlitespeed": {
+ "slug": "migrating-codeigniter-iis-to-openlitespeed",
+ "title": "Migrating a CodeIgniter 4 App From IIS to OpenLiteSpeed",
+ "zh_title": "把 CodeIgniter 4 应用从 IIS 迁移到 OpenLiteSpeed",
+ "category": "engineering",
+ "pub_date": "2026-09-27",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 14943,
+ "zh_chars": 8425
+ },
+ "my-backup-was-using-someone-elses-api-quota": {
+ "slug": "my-backup-was-using-someone-elses-api-quota",
+ "title": "My Backup Was Crawling Because I Was Using Someone Else's API Quota",
+ "zh_title": "我的备份爬得比爬虫还慢,因为我在用别人的 API 配额",
+ "category": "notes",
+ "pub_date": "2026-10-15",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 8572,
+ "zh_chars": 4527
+ },
+ "n8n-v1-to-v2-upgrade-gotchas": {
+ "slug": "n8n-v1-to-v2-upgrade-gotchas",
+ "title": "Upgrading n8n v1 to v2: Seven Deprecations in One Log File",
+ "zh_title": "n8n v1 升级到 v2:一个日志文件里的七项废弃警告",
+ "category": "devops",
+ "pub_date": "2026-09-18",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 12262,
+ "zh_chars": 6712
+ },
+ "nocodb-sso-is-a-licensed-feature": {
+ "slug": "nocodb-sso-is-a-licensed-feature",
+ "title": "NocoDB SSO Is a Licensed Feature — and the Env Vars Won't Tell You",
+ "zh_title": "NocoDB SSO 是授权功能:环境变量不会告诉你的事",
+ "category": "notes",
+ "pub_date": "2026-09-29",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 9937,
+ "zh_chars": 5421
+ },
+ "one-hostname-public-tracker-sso-dashboard": {
+ "slug": "one-hostname-public-tracker-sso-dashboard",
+ "title": "One Hostname for a Public Tracker and an SSO-Gated Dashboard",
+ "zh_title": "公开的埋点与要 SSO 的仪表盘,怎么共用一个域名",
+ "category": "devops",
+ "pub_date": "2026-09-30",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 14400,
+ "zh_chars": 6837
+ },
+ "one-prometheus-for-unraid-synology-and-a-vps": {
+ "slug": "one-prometheus-for-unraid-synology-and-a-vps",
+ "title": "One Prometheus for unRaid, a Synology NAS, and a VPS: What I Got Wrong",
+ "zh_title": "用一个 Prometheus 监控 unRaid、群晖 NAS 和 VPS:我做错的四件事",
+ "category": "case-studies",
+ "pub_date": "2026-07-29",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 8358,
+ "zh_chars": 4690
+ },
+ "passbolt-hang-three-failure-modes": {
+ "slug": "passbolt-hang-three-failure-modes",
+ "title": "Passbolt UI Kept Hanging — Three Failure Modes From One Missing Config File",
+ "zh_title": "Passbolt 界面反复卡死——一个缺失的配置文件引发的三种故障模式",
+ "category": "devops",
+ "pub_date": "2026-01-07",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 7864,
+ "zh_chars": 4187
+ },
+ "patching-workbench-26-for-mariadb": {
+ "slug": "patching-workbench-26-for-mariadb",
+ "title": "When Your Database Client Lies to You: Patching Workbench 26 for MariaDB",
+ "zh_title": "当数据库客户端对你撒谎:为 MariaDB 修补 Workbench 26",
+ "category": "devops",
+ "pub_date": "2026-09-19",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 15965,
+ "zh_chars": 9501
+ },
+ "php2shell-patching-every-php-runtime-i-manage": {
+ "slug": "php2shell-patching-every-php-runtime-i-manage",
+ "title": "PHP2Shell: I Measured Every PHP Runtime I Manage. All of Them Were Vulnerable.",
+ "zh_title": "PHP2Shell:我管的每台机器,PHP 运行时全都中了",
+ "category": "devops",
+ "pub_date": "2026-10-23",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 22354,
+ "zh_chars": 12531
+ },
+ "plugging-windows-desktop-control-into-four-ai-agents": {
+ "slug": "plugging-windows-desktop-control-into-four-ai-agents",
+ "title": "Plugging Windows Desktop Control into Four AI Agents: Hermes, OpenCode, Claw, and Claude",
+ "zh_title": "把 Windows 桌面控制接进 4 款 AI Agent:Hermes、OpenCode、Claw 与 Claude 的实战架构",
+ "category": "ai",
+ "pub_date": "2025-12-23",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 6554,
+ "zh_chars": 5030
+ },
+ "putting-a-front-door-on-an-app-you-cant-modify": {
+ "slug": "putting-a-front-door-on-an-app-you-cant-modify",
+ "title": "Putting a Front Door on an App You Can't Modify",
+ "zh_title": "给自己改不了源码的应用装一扇前门",
+ "category": "devops",
+ "pub_date": "2025-06-24",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 13927,
+ "zh_chars": 7415
+ },
+ "read-only-nocodb-dashboard-for-a-remote-database": {
+ "slug": "read-only-nocodb-dashboard-for-a-remote-database",
+ "title": "A Read-Only NocoDB Dashboard for a Database on Another Machine",
+ "zh_title": "给另一台机器上的数据库配一个 NocoDB 只读看板",
+ "category": "devops",
+ "pub_date": "2026-09-29",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 12009,
+ "zh_chars": 6287
+ },
+ "reading-a-containers-own-api-docs": {
+ "slug": "reading-a-containers-own-api-docs",
+ "title": "Read a Container's Own API Docs Instead of the Vendor's",
+ "zh_title": "与其看供应商文档,不如直接读容器自带的 API 文档",
+ "category": "notes",
+ "pub_date": "2025-10-01",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 8382,
+ "zh_chars": 5489
+ },
+ "replacing-rdpguard-with-ipban": {
+ "slug": "replacing-rdpguard-with-ipban",
+ "title": "Replacing RDPGuard With IPBan: The Traps Nobody Documents",
+ "zh_title": "用 IPBan 替换 RDPGuard:没人写出来的三个坑",
+ "category": "devops",
+ "pub_date": "2026-09-19",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 04:09",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/技术细节修补)",
+ "en_chars": 13131,
+ "zh_chars": 7795
+ },
+ "running-ai-video-locally-on-a-12gb-gpu": {
+ "slug": "running-ai-video-locally-on-a-12gb-gpu",
+ "title": "Running AI Video Locally on a 12 GB GPU: Real Numbers for Wan 2.2 and SeedVR2",
+ "zh_title": "12GB 显卡跑本地 AI 视频:Wan 2.2 加 SeedVR2 的真实数据",
+ "category": "ai",
+ "pub_date": "2026-10-20",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 18508,
+ "zh_chars": 12878
+ },
+ "running-production-infrastructure-solo": {
+ "slug": "running-production-infrastructure-solo",
+ "title": "Running Production Infrastructure Solo: 162 Containers Across Three Hosts",
+ "zh_title": "一个人跑生产基础设施:三台主机、162 个容器",
+ "category": "case-studies",
+ "pub_date": "2026-10-06",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 7975,
+ "zh_chars": 3722
+ },
+ "running-tts-as-a-service-with-token-sidecars": {
+ "slug": "running-tts-as-a-service-with-token-sidecars",
+ "title": "I Ran a Text-to-Speech Service for a Year on Two Cron Containers",
+ "zh_title": "我用两个 Cron 容器跑了一年的语音合成服务",
+ "category": "ai",
+ "pub_date": "2026-09-18",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 14141,
+ "zh_chars": 7929
+ },
+ "scraping-bot-walled-marketplace-warm-browser-session": {
+ "slug": "scraping-bot-walled-marketplace-warm-browser-session",
+ "title": "Scraping a Bot-Walled Marketplace With a Warm Browser Session",
+ "zh_title": "用「热浏览器会话」爬取有反爬墙的电商网站",
+ "category": "engineering",
+ "pub_date": "2026-09-13",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 11313,
+ "zh_chars": 5734
+ },
+ "self-healing-digital-goods-entitlements": {
+ "slug": "self-healing-digital-goods-entitlements",
+ "title": "Self-Healing Access Control for Digital Goods: NocoDB, n8n, and AList",
+ "zh_title": "数字商品的自愈式访问控制:NocoDB、n8n 与 AList",
+ "category": "case-studies",
+ "pub_date": "2026-09-18",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 14765,
+ "zh_chars": 6819
+ },
+ "self-hosted-speech-to-text-api": {
+ "slug": "self-hosted-speech-to-text-api",
+ "title": "Your Own Speech-to-Text Server: Faster Than Typing, Private by Default",
+ "zh_title": "自建语音转文字服务:打字速度的 3 倍以上,而且数据不出内网",
+ "category": "case-studies",
+ "pub_date": "2026-09-19",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 14708,
+ "zh_chars": 7794
+ },
+ "self-hosted-tts-cosyvoice2-n8n-gpu": {
+ "slug": "self-hosted-tts-cosyvoice2-n8n-gpu",
+ "title": "How I Replaced Cloud TTS with a Self-Hosted Neural Voice Stack on an RTX 3060",
+ "zh_title": "从云端转本地:在 12GB 显卡与群晖 N8N 上搭建中英混读高保真 TTS 语音栈",
+ "category": "case-studies",
+ "pub_date": "2026-10-22",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 8913,
+ "zh_chars": 4773
+ },
+ "self-hosting-mem0-memory-stack": {
+ "slug": "self-hosting-mem0-memory-stack",
+ "title": "How I Self-Hosted a Mem0 Memory Stack (So My AI Agent Remembers)",
+ "zh_title": "我如何在私有存储节点上自托管 Mem0 记忆栈(让 AI 助手拥有持久记忆)",
+ "category": "case-studies",
+ "pub_date": "2026-09-16",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 8057,
+ "zh_chars": 4849
+ },
+ "shipping-an-ai-photo-editor-as-a-wordpress-plugin": {
+ "slug": "shipping-an-ai-photo-editor-as-a-wordpress-plugin",
+ "title": "Shipping an AI Photo Editor as a WordPress Plugin: 22 Versions of Hard Lessons",
+ "zh_title": "把 AI 修图工具做成 WordPress 插件:22 个版本换来的经验",
+ "category": "case-studies",
+ "pub_date": "2026-09-18",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 13879,
+ "zh_chars": 6861
+ },
+ "slashing-agent-context-with-a-code-graph": {
+ "slug": "slashing-agent-context-with-a-code-graph",
+ "title": "Slashing Agent Context and Token Churn With a Code Graph",
+ "zh_title": "别再把整个 Repo 喂给 Agent:用 Code Graph 斩断 Token 膨胀与遗忘陷阱",
+ "category": "ai",
+ "pub_date": "2026-04-28",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 8122,
+ "zh_chars": 5040
+ },
+ "smartctl-exit-code-32-skips-the-disks-that-matter": {
+ "slug": "smartctl-exit-code-32-skips-the-disks-that-matter",
+ "title": "smartctl Exit Code 32: The Code That Skips the Disks You Care About",
+ "zh_title": "smartctl 退出码 32:专门跳过你最该看的硬盘的那个“错误”",
+ "category": "notes",
+ "pub_date": "2026-04-14",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 5475,
+ "zh_chars": 2881
+ },
+ "space-reclamation-wasnt-stuck-it-was-waiting": {
+ "slug": "space-reclamation-wasnt-stuck-it-was-waiting",
+ "title": "Space Reclamation Wasn't Stuck. It Was Waiting for Tomorrow at 09:29.",
+ "zh_title": "空间回收没卡住,它只是在等明天早上 09:29",
+ "category": "devops",
+ "pub_date": "2026-10-14",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 6574,
+ "zh_chars": 3505
+ },
+ "stop-writing-agent-prompts-for-deterministic-work": {
+ "slug": "stop-writing-agent-prompts-for-deterministic-work",
+ "title": "Stop Writing Agent Prompts for Deterministic Work",
+ "zh_title": "确定性的活,别再用 Agent Prompt 去跑",
+ "category": "notes",
+ "pub_date": "2026-10-01",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 05:22",
+ "notes": "外部顾问审阅通过并落地修改",
+ "en_chars": 4951,
+ "zh_chars": 2216
+ },
+ "syncing-a-self-improving-ai-agent-across-machines": {
+ "slug": "syncing-a-self-improving-ai-agent-across-machines",
+ "title": "Syncing a Self-Improving AI Agent Across Machines",
+ "zh_title": "在多台机器之间同步一个会自我学习的 AI Agent",
+ "category": "case-studies",
+ "pub_date": "2026-02-18",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 04:22",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/架构蓝图对齐/排版折叠)",
+ "en_chars": 8861,
+ "zh_chars": 4541
+ },
+ "synology-api-401-with-the-correct-password": {
+ "slug": "synology-api-401-with-the-correct-password",
+ "title": "Why Your Synology API Login Returns 401 With the Correct Password",
+ "zh_title": "密码正确,Synology API 登录却返回 401,问题出在哪",
+ "category": "notes",
+ "pub_date": "2025-09-10",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 5053,
+ "zh_chars": 3063
+ },
+ "synology-spreadsheet-api-is-a-container": {
+ "slug": "synology-spreadsheet-api-is-a-container",
+ "title": "The Synology Spreadsheet API Is a Container, Not an API Endpoint",
+ "zh_title": "Synology 电子表格 API 是一个容器,不是一个 API 端点",
+ "category": "devops",
+ "pub_date": "2025-08-20",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 16101,
+ "zh_chars": 9200
+ },
+ "that-dying-ssd-was-just-a-bad-sata-cable": {
+ "slug": "that-dying-ssd-was-just-a-bad-sata-cable",
+ "title": "I Was About to RMA This SSD — the Fault Was a SATA Cable",
+ "zh_title": "我差点把这颗 SSD 送修——真正坏的是 SATA 数据线",
+ "category": "devops",
+ "pub_date": "2026-09-16",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 7471,
+ "zh_chars": 3940
+ },
+ "the-cause-was-trim-not-the-ssds": {
+ "slug": "the-cause-was-trim-not-the-ssds",
+ "title": "The Corruption Came Back on Different Drives — the Cause Was TRIM, Not the SSDs",
+ "zh_title": "换了不同的硬盘,同样的损坏又来了——真凶是 TRIM,不是 SSD",
+ "category": "devops",
+ "pub_date": "2026-09-18",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 13042,
+ "zh_chars": 7310
+ },
+ "the-nocodb-attachment-that-wouldnt-update": {
+ "slug": "the-nocodb-attachment-that-wouldnt-update",
+ "title": "The NocoDB Attachment That Wouldn't Update (and a Regex That Missed)",
+ "zh_title": "NocoDB 附件字段怎么改都不生效,和一个漏网的正则",
+ "category": "notes",
+ "pub_date": "2026-02-04",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 3798,
+ "zh_chars": 2312
+ },
+ "the-single-writer-rule-for-coding-agents": {
+ "slug": "the-single-writer-rule-for-coding-agents",
+ "title": "The Single-Writer Rule: Orchestrating Coding Agents Without Multi-Agent Chaos",
+ "zh_title": "代码只让一个 AI 改:我是如何避开多 Agent 协同踩坑的「单写多审」架构",
+ "category": "case-studies",
+ "pub_date": "2026-08-26",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 13331,
+ "zh_chars": 6954
+ },
+ "the-upstream-was-deleted-then-came-back-rewritten": {
+ "slug": "the-upstream-was-deleted-then-came-back-rewritten",
+ "title": "The Upstream Was Deleted, Then Came Back as a Rewrite",
+ "zh_title": "上游被删了,然后带着一个重写版回来",
+ "category": "devops",
+ "pub_date": "2026-10-06",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 11636,
+ "zh_chars": 5229
+ },
+ "two-ways-a-javascript-file-stops-working-and-looks-fine": {
+ "slug": "two-ways-a-javascript-file-stops-working-and-looks-fine",
+ "title": "Two Ways a JavaScript File Stops Working and Looks Fine",
+ "zh_title": "JavaScript 文件静默失效的两种方式:而页面看上去一切正常",
+ "category": "engineering",
+ "pub_date": "2025-02-20",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 8293,
+ "zh_chars": 3881
+ },
+ "unraid-stop-array-hangs-on-swapfile": {
+ "slug": "unraid-stop-array-hangs-on-swapfile",
+ "title": "Why Unraid Hangs When You Stop the Array With a Swapfile — and the Fix",
+ "zh_title": "Unraid 停止阵列时卡死的真相:Swapfile 锁住挂载点 — 附修复方案",
+ "category": "devops",
+ "pub_date": "2026-09-15",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 10470,
+ "zh_chars": 6376
+ },
+ "upgrading-codeigniter-46-to-47": {
+ "slug": "upgrading-codeigniter-46-to-47",
+ "title": "Upgrading CodeIgniter 4.6 to 4.7: The Breaking Changes the Guide Misses",
+ "zh_title": "CodeIgniter 4.6 升级到 4.7:官方指南没说到的破坏性变更",
+ "category": "notes",
+ "pub_date": "2026-09-27",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 05:22",
+ "notes": "外部顾问审阅通过并落地修改",
+ "en_chars": 9469,
+ "zh_chars": 5411
+ },
+ "uploading-large-videos-to-youtube-via-cdp": {
+ "slug": "uploading-large-videos-to-youtube-via-cdp",
+ "title": "Uploading 100MB+ Videos to YouTube via CDP: The Docker Archive Pattern",
+ "zh_title": "用 CDP 自动化上传百兆视频到 YouTube:跨越 DevTools 内存墙的 Docker 归档法",
+ "category": "devops",
+ "pub_date": "2025-08-22",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 11267,
+ "zh_chars": 7173
+ },
+ "using-chinese-llm-apis-from-malaysia": {
+ "slug": "using-chinese-llm-apis-from-malaysia",
+ "title": "Using Chinese LLM APIs From Malaysia: What Actually Works",
+ "zh_title": "从马来西亚使用中国大模型 API:真正可行的方法",
+ "category": "ai",
+ "pub_date": "2026-09-11",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 03:00",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 7540,
+ "zh_chars": 3536
+ },
+ "verifying-a-pdf-report-page-by-page": {
+ "slug": "verifying-a-pdf-report-page-by-page",
+ "title": "Verifying a 19-Page PDF Report Page by Page",
+ "zh_title": "逐页验证一份 19 页的 PDF 报告",
+ "category": "engineering",
+ "pub_date": "2026-09-28",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 12525,
+ "zh_chars": 6685
+ },
+ "vetting-an-open-source-dependency-before-you-bet-on-it": {
+ "slug": "vetting-an-open-source-dependency-before-you-bet-on-it",
+ "title": "How I Vet an Open-Source Dependency Before Betting On It",
+ "zh_title": "如何在押注前验证一个开源依赖:我用的六项检查",
+ "category": "devops",
+ "pub_date": "2026-09-29",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 8512,
+ "zh_chars": 4201
+ },
+ "when-smart-says-healthy-but-your-raid-is-corrupting-data": {
+ "slug": "when-smart-says-healthy-but-your-raid-is-corrupting-data",
+ "title": "When SMART Says Healthy and Your RAID Is Silently Corrupting Data",
+ "zh_title": "SMART 显示健康,RAID 却在悄悄损坏数据",
+ "category": "devops",
+ "pub_date": "2026-09-16",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 14160,
+ "zh_chars": 6246
+ },
+ "when-to-give-your-agent-physical-clicks": {
+ "slug": "when-to-give-your-agent-physical-clicks",
+ "title": "When to Give Your Agent Physical Clicks (And When to Never Touch Them)",
+ "zh_title": "什么时候该让 AI 操控物理鼠标(以及什么时候绝对不要用)",
+ "category": "case-studies",
+ "pub_date": "2026-05-28",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 6882,
+ "zh_chars": 3608
+ },
+ "why-500000-lines-of-commit-churn-only-left-230000-in-production": {
+ "slug": "why-500000-lines-of-commit-churn-only-left-230000-in-production",
+ "title": "I Committed 518,000 Lines in 30 Days. Production Kept 235,000.",
+ "zh_title": "30 天提交 51.8 万行,生产环境只留下 23.5 万行",
+ "category": "engineering",
+ "pub_date": "2026-10-18",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 8949,
+ "zh_chars": 4197
+ },
+ "why-chrome-forgets-its-tabs-in-a-container": {
+ "slug": "why-chrome-forgets-its-tabs-in-a-container",
+ "title": "Why Chrome Forgets Its Tabs in a Container — And How I Fixed It",
+ "zh_title": "为什么容器里的 Chrome 会忘记标签页 —— 以及我是怎么修好的",
+ "category": "devops",
+ "pub_date": "2026-09-29",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 15430,
+ "zh_chars": 9234
+ },
+ "why-i-built-a-video-knowledge-base": {
+ "slug": "why-i-built-a-video-knowledge-base",
+ "title": "Why I Built a Video Knowledge Base: Turning Saved Short Videos into Searchable Notes",
+ "zh_title": "为什么我要做视频精读知识库:把收藏夹吃灰的短视频变成可检索笔记",
+ "category": "case-studies",
+ "pub_date": "2024-11-28",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 5770,
+ "zh_chars": 2924
+ },
+ "why-i-still-bought-a-local-gpu": {
+ "slug": "why-i-still-bought-a-local-gpu",
+ "title": "Why I Still Bought a GPU to Run AI When Claude and GPT Are Obviously Stronger",
+ "zh_title": "Claude 和 GPT 明明更强,我为什么还是买了 GPU 跑本地 AI",
+ "category": "ai",
+ "pub_date": "2026-09-21",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 11811,
+ "zh_chars": 4924
+ },
+ "why-is-my-tts-model-repeating-percent": {
+ "slug": "why-is-my-tts-model-repeating-percent",
+ "title": "Why Is My TTS Model Repeating \\\"Percent\\\"? The URL-Encoding Trap in Speech Pipelines",
+ "zh_title": "为什么本地语音合成一直在念 “Percent”?语音管线里的 URL 编码与沙盒避坑记",
+ "category": "notes",
+ "pub_date": "2026-10-22",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 7728,
+ "zh_chars": 4968
+ },
+ "why-multi-agent-coding-fails": {
+ "slug": "why-multi-agent-coding-fails",
+ "title": "Why Multi-Agent Coding Fails: The Telephone Game and Architectural Drift",
+ "zh_title": "为什么多个 AI 一起写代码总会烂尾?传话失真与隐式决策困境",
+ "category": "notes",
+ "pub_date": "2025-07-29",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 6364,
+ "zh_chars": 3353
+ },
+ "why-my-on-chain-nft-art-changed-on-windows": {
+ "slug": "why-my-on-chain-nft-art-changed-on-windows",
+ "title": "Why My On-Chain NFT Art Changed When I Cloned It on Windows",
+ "zh_title": "为什么在 Windows 上克隆后,我的链上 NFT 图像变了",
+ "category": "web3",
+ "pub_date": "2026-08-19",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:11",
+ "notes": "Batch-04 review: performUpkeep external-callable correction, _ownerOf dead-code fix, storage vs EIP-170 split, RDAP -L + ASN blocklist, subs-15 retimed from re-run ASR, cross-links, dynamicCta",
+ "en_chars": 8423,
+ "zh_chars": 4585
+ },
+ "why-telegram-bot-notifications-die": {
+ "slug": "why-telegram-bot-notifications-die",
+ "title": "Why Telegram Bot Notifications Die: IPv6, DNS, and a 400 That Took a Day",
+ "zh_title": "Telegram 机器人通知为什么发不出去:IPv6、DNS 和一个追查了一整天的 400",
+ "category": "devops",
+ "pub_date": "2026-03-25",
+ "status": "approved",
+ "batch_id": "Batch-02",
+ "approved_at": "2026-10-27 06:10",
+ "notes": "顾问二次审阅(其编号Batch-05)已落地:修正Multipart join原理解释与源码转义、Btrfs csum字段位置与字节序、losetup -p误用、UDMA_CRC机制、CONFIG_CFS_BANDWIDTH、GiB/h算术;补齐blueprint与机型待确认。详见 reports/report-batch-05-rereview.md",
+ "en_chars": 10753,
+ "zh_chars": 6005
+ },
+ "why-your-cdp-clicks-silently-fail": {
+ "slug": "why-your-cdp-clicks-silently-fail",
+ "title": "Why Your CDP Clicks Silently Fail: Smooth Scroll, Minimised Windows, and JS-Dispatched Events",
+ "zh_title": "为什么你的 CDP 点击会静默失效:平滑滚动、最小化窗口与 JS 派发事件",
+ "category": "devops",
+ "pub_date": "2025-03-19",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 8054,
+ "zh_chars": 4536
+ },
+ "why-your-grafana-dashboard-shows-no-data": {
+ "slug": "why-your-grafana-dashboard-shows-no-data",
+ "title": "Why Your Grafana Dashboard Shows No Data (When Prometheus Is Fine)",
+ "zh_title": "Prometheus 明明有数据,Grafana 面板却全是 No data 的原因",
+ "category": "devops",
+ "pub_date": "2026-05-17",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 7456,
+ "zh_chars": 3919
+ },
+ "why-your-headless-browser-cant-scrape-everything": {
+ "slug": "why-your-headless-browser-cant-scrape-everything",
+ "title": "Why Your Headless Browser Can't Scrape Everything",
+ "zh_title": "为什么你的无头浏览器不是万能爬虫",
+ "category": "case-studies",
+ "pub_date": "2026-01-21",
+ "status": "approved",
+ "batch_id": "Batch-03",
+ "approved_at": "2026-10-27 03:49",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见",
+ "en_chars": 8076,
+ "zh_chars": 3792
+ },
+ "why-your-windows-mcp-calls-silently-fail": {
+ "slug": "why-your-windows-mcp-calls-silently-fail",
+ "title": "Why Your Windows Desktop MCP Calls Silently Fail: Taming the 10-Second Idle Gate",
+ "zh_title": "为什么你的 Windows 桌面 MCP 调用会静默失败:攻破 10 秒空闲门禁与避坑实录",
+ "category": "notes",
+ "pub_date": "2026-03-12",
+ "status": "approved",
+ "batch_id": "Batch-04",
+ "approved_at": "2026-10-27 05:21",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含数字矛盾/OPSEC脱敏/双语CTA/排版折叠/禁用词清理)",
+ "en_chars": 5663,
+ "zh_chars": 3857
+ },
+ "your-disk-full-alert-is-lying": {
+ "slug": "your-disk-full-alert-is-lying",
+ "title": "Your Disk-Full Alert Is Lying: Stop Alerting on Percentages",
+ "zh_title": "你的“磁盘将满”告警在说谎:别再按百分比告警",
+ "category": "devops",
+ "pub_date": "2026-06-24",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 06:09",
+ "notes": "第一批外部顾问审阅完成,已全量采纳并落地修改(含数字矛盾纠正/OPSEC与禁用词清理/并发异常409映射/双语CTA补齐)",
+ "en_chars": 6139,
+ "zh_chars": 3090
+ },
+ "yue2-on-a-12gb-gpu-songs-that-lost-their-endings": {
+ "slug": "yue2-on-a-12gb-gpu-songs-that-lost-their-endings",
+ "title": "Why My AI Songs Kept Losing Their Endings: Running YuE2 on a 12 GB GPU",
+ "zh_title": "为什么我的 AI 歌曲总是丢结尾:在 12GB 显卡上跑 YuE2",
+ "category": "ai",
+ "pub_date": "2026-10-20",
+ "status": "approved",
+ "batch_id": "Batch-01",
+ "approved_at": "2026-10-27 05:26",
+ "notes": "外部顾问审阅完成,已采纳并落地全量修改意见(含GPU/Token成本算账、Gemini净化、双语CTA、代码折叠与简繁转换)",
+ "en_chars": 28955,
+ "zh_chars": 24414
+ },
+ "when-lowering-crf-does-nothing-maxrate-deadlock": {
+ "slug": "when-lowering-crf-does-nothing-maxrate-deadlock",
+ "title": "When Lowering CRF Does Nothing: The -maxrate Ceiling and the SSIM Quality Deadlock",
+ "zh_title": "当降低 CRF 毫无效果:视频转码中 -maxrate 封顶与 SSIM 质量门禁的死锁",
+ "category": "devops",
+ "pub_date": "2026-10-27",
+ "status": "pending",
+ "batch_id": "Batch-07",
+ "approved_at": null,
+ "notes": "2026-10-28 打包送审(Batch-07);发布先于审阅,结论回填后更新状态",
+ "en_chars": 8906,
+ "zh_chars": 5042
+ },
+ "why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux": {
+ "slug": "why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux",
+ "title": "Why ffmpeg -map 0 Breaks Your Remux: The Hidden Matrix of Multi-Track MP4 and Matroska",
+ "zh_title": "为什么 ffmpeg -map 0 会搞崩混流:跨容器多音轨、字幕与字体的深水区",
+ "category": "engineering",
+ "pub_date": "2026-10-27",
+ "status": "pending",
+ "batch_id": "Batch-07",
+ "approved_at": null,
+ "notes": "2026-10-28 打包送审(Batch-07);发布先于审阅,结论回填后更新状态",
+ "en_chars": 10877,
+ "zh_chars": 7332
+ },
+ "when-a-proxy-timeout-returns-html-instead-of-json": {
+ "slug": "when-a-proxy-timeout-returns-html-instead-of-json",
+ "title": "When a Proxy Timeout Returns HTML Instead of JSON: A 499 Post-Mortem",
+ "zh_title": "反代超时返回 HTML,前端报 SyntaxError:一次 499 事故复盘",
+ "category": "engineering",
+ "pub_date": "2026-10-28",
+ "status": "pending",
+ "batch_id": "Batch-07",
+ "approved_at": null,
+ "notes": "2026-10-28 打包送审(Batch-07);发布先于审阅,结论回填后更新状态",
+ "en_chars": 15520,
+ "zh_chars": 8813
+ }
}
\ No newline at end of file
diff --git a/docs/consultant-review/batch-07-consultant-review-pack.md b/docs/consultant-review/batch-07-consultant-review-pack.md
new file mode 100644
index 0000000..85531a9
--- /dev/null
+++ b/docs/consultant-review/batch-07-consultant-review-pack.md
@@ -0,0 +1,1426 @@
+# Batch-07 外部顾问审阅资料包 — 3 篇已上线但缺审核记录的工程文章
+
+> **致顾问**:本轮不是新一轮选题策划,而是**补审**。这 3 篇已经在 blog.hoelee.com 上线(其中 2 篇在 Batch-06 资料包里已给过全文,1 篇是 2026-10-28 新增),但审阅结论回执一直没落到台账里。请您按同一套铁律与评分维度给出结论即可;如需撤回或重写,我会照办,线上可随时下线。
+>
+> **审阅日期**:2026-10-28 | **资料包版本**:v1 | **文章语言**:英中双语(英文为线上主版本,中文为同 slug 镜像)
+
+---
+
+## 一、本轮范围与状态
+
+| # | slug | 分类 | 上线日 | 字数(EN / ZH) | 台账状态 | 本轮需要您给的结论 |
+|---|---|---|---|---|---|---|
+| **A** | `when-a-proxy-timeout-returns-html-instead-of-json` | engineering | 2026-10-28 | 15,520 / 8,813 | pending(本轮新增) | **全文审阅**(资料包第五章) |
+| **B** | `when-lowering-crf-does-nothing-maxrate-deadlock` | devops | 2026-10-27 | 8,906 / 5,042 | pending | 全文在 Batch-06 包,本轮附录重贴,**请给最终结论** |
+| **C** | `why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux` | engineering | 2026-10-27 | 10,877 / 7,332 | pending | 同上 |
+
+**为什么会出现「已上线却未审」**:Batch-06 那批 5 篇里,A/B/C 中的 B、C 按排期先发了(周二主打 + 周四深水区),审阅回执此前一直只看 Batch-01~05 的报告文件;A 是 2026-10-28 当天故障复盘当场写完并直接上线的(业主授权「按建议执行」),没有走 draft 预审闸门。这件事本身是我流程上的缺口,本轮补齐台账(3 条 `status: pending`)。
+
+**未在本轮、但仍挂着的 Batch-06 剩余 3 篇**(都是 `draft: true`,未上线,等同一轮结论):`traefik-yaegi-plugin-package-name-trap`、`architecting-sea-payment-and-offline-licensing-pipelines`、`batch-generating-80-technical-covers-with-comfyui`。
+
+---
+
+## 二、战略任务与定位(与前几轮一致,未变)
+
+1. **双轨目标**:① 技术求职证明(Senior/Staff 全栈 · DevOps · AI 平台架构,US/EU/SG 远程或 hybrid);② 马来西亚中小企业 B2B 获客(网站设计与开发为主业,邮箱托管为次)。
+2. **No-Shopkeeper / No-Founder 红线**:商业交付一律写成高可用生产系统工程,不写成店铺经营、副业、电商小生意。
+3. **A 篇在这条线上的作用**:这是"能把别人的线上事故从一句错误的报错定位到正确层级"的证据。它不涉及任何自有产品,纯排障与架构判断,对求职线价值最高;对 B2B 线的作用是"我管理的基础设施出问题时,我是这样收尾的"。
+
+---
+
+## 三、铁律清单(自检结果,逐条给证据)
+
+| 铁律 | 自检方式 | 结果 |
+|---|---|---|
+| 禁用词 `homelab` / `home lab` / `家用` / `小主机` / `折腾` | 全文正则,EN+ZH 各跑一遍 | **0 命中** |
+| 第一人称单数,禁 `we` / `our` / `我们` | 全文正则计数 | **0 命中**(EN 与 ZH 均为 0) |
+| 技术正文禁第二人称(`you`/`your`/`你`),仅结尾 CTA 可用 | 全文正则 | 初稿有 **5 处**(`Your timeout budget`、`the worst case you have not measured`、`telling you / your code`,中文 2 处「你」),已全部改为第一人称/无人称,**当前 0 命中**,且已重新构建部署、线上实测生效 |
+| 英中 100% 镜像(同 slug、同 commit) | 文件存在性 + 构建产物 | 两处 `dist/posts/`、`dist/posts/zh/` 均生成,`llms.txt` 各 2 条 |
+| 标题 Title Case(EN) | 人工核对 | A/B/C 均符合 |
+| OPSEC:无绝对路径、无内网 IP、无 token/密钥 | 人工逐段过 + 正则扫 `192.168`、`/volume1`、`D:\`、`token` | **0 命中**;文中出现的 `nginx` / `FastAPI` / `authentik` / `Docker` / `cgroups` 均为通用技术名词,非客户标识 |
+| 数字一致(前后文不打架) | 逐条重算 | 13.6÷0.259 = **52.5×**(文中"中位 52×");13.6÷0.244~0.361 = **38~56×**(文中"38–57×");2507÷13.6 = **184.3×**(文中"184×");0.24–0.36 s 五连测中位 **0.259 s** |
+| 长代码进 ``,`` 写明产物与行数 | 人工核对 | A 篇两处:`52 行` 缓存实现、`22 行` 前端读取器;标题均写明产物 |
+| 不手写 CTA(由 `dynamicCta: true` 注入) | frontmatter + 构建产物 | `dynamic-cta` 类在线上页面各 1 处,正文无手写表单 |
+
+---
+
+## 四、技术背景(供您审"技术是不是真的")
+
+### 4.1 A 篇的触发:一句指向错误层级的报障
+
+业主 2026-10-28 报障原文只有一行:
+
+```
+读取自动处理状态失败:SyntaxError: Unexpected token '<', "= vbps × SIZE_CEIL × 0.97`),此时调 CRF 数学上无效,调度器应直接取消重试并记录原因。
+- **C(`-map 0` 跨容器)**:`-map 0` 在 Matroska 输出上遇到 QuickTime 章节(`bin_data`)与手机时间码轨(`tmcd`)会直接 fatal;MP4 输出遇到 ASS/PGS/字体附件同样 fatal;iOS 的轨名只读 `udta/name` 而 ffprobe 只回读 `hdlr`,二次混流会静默丢标签。修法是显式轨道规划 + 包级 MD5 校验(`-f data -` 管道哈希 payload)。
+
+---
+
+## 五、我预判您会追问的两处(并附我的答案与证据边界)
+
+> 这一节是我主动交底的,不是让您放水。若您认为证据不足以支撑文中表述,我会改标题与结论,而不是改数字。
+
+**Q-A:把"缓存读 0.26 s"和"全量审计 13.6 s"放在一起比,是不是苹果比橘子?**
+- 文中表述为"对照它所替代的 13.6 秒审计",指的是**同一端点、同一份计算**在改造前后的对照,不是把它包装成"接口优化 52 倍"。
+- 我手上有:同函数三种条件下的耗时(13.6 / 17.7 / 98.5 / 35.8 s)、缓存读 5 连测(0.244 / 0.257 / 0.259 / 0.262 / 0.361 s)、后台自记的 8 次审计耗时。
+- **证据缺口(可补)**:我没有从访问日志里取该端点改造前后的 p50/p95 分布,因此文中不写"p95 下降多少"。若您要求,我可以补一次基于 access log 的分位统计再回填。
+
+**Q-B:2507 s 里到底有多少是单核限流、多少是锁竞争/IO?**
+- 该数字是**同一函数从进入到返回的墙钟时间**,由应用自己打印,不是我推算的;但它**没有拆段归因**(单核争用 / 磁盘 stat / 哈希 CPU 各占多少)。
+- 我能确定的因果:容器 1 核 + 1/4 权重、同宿主有 HEVC 转码任务、数据量 341 篇;我**没有**做过的:分段埋点、per-file IO 统计。
+- 因此文中只说"耗时是被允许借用多少 CPU 的函数",没有声称具体的归因比例。若您要求,我会加一次分段埋点实验再给比例。
+
+---
+
+## 六、全文双语 — A 篇(本轮待审主角)
+
+### 6.1 英文版(线上主版本)
+
+````markdown
+---
+ogImage: "/og/when-a-proxy-timeout-returns-html-instead-of-json.jpg"
+banner: "/banners/when-a-proxy-timeout-returns-html-instead-of-json.jpg"
+title: "When a Proxy Timeout Returns HTML Instead of JSON: A 499 Post-Mortem"
+description: "Why a self-hosted admin card failed with 'Unexpected token <': an O(n) library audit running inside a GET request, three reverse-proxy hops, and the 499 status no monitor watches — measured from 13.6 s on an idle box to 2,507 s on a busy one."
+pubDate: 2026-10-28
+category: engineering
+blueprint: self-hosted-infrastructure
+blueprintLayer: L2-edge-proxy-runtime
+tags: ["nginx", "fastapi", "reverse-proxy", "timeouts", "caching", "observability", "stale-while-revalidate"]
+draft: false
+summary:
+ audience: "Full-Stack Engineers, SREs & Platform Architects Who Own Their Own Edge"
+ painPoint: "An admin dashboard card died with a JavaScript syntax error, which pointed every investigation at the frontend — while the real failure was an O(n) audit burning one pinned CPU core behind three proxy hops that silently abort at their own timeouts."
+ impact:
+ - metric: "Endpoint Latency −98%"
+ detail: "Moved a 289-to-341-note library audit off the request thread into a single-flight background refresher; the cached read path now answers in 0.24–0.36 s over five consecutive calls instead of 13.6 s idle and 2,507 s under load."
+ - metric: "Failure Now Names Its Layer"
+ detail: "Replaced a blind res.json() with a text-first reader that preserves the HTTP status and the first 80 bytes of an HTML error page, so a proxy abort reports 'HTTP 503 ·
+🔍 View the stale-while-revalidate implementation — cache, single-flight refresh thread, startup warm (52 lines)
+
+```python
+_AUTOMATION_TTL_SEC = float(os.environ.get("AUTOMATION_COUNTS_TTL_SEC", "300") or 300)
+_automation_cache: Dict[str, Any] = {"data": None, "at": 0.0}
+_automation_refreshing_flag = [False]
+_automation_lock = threading.RLock()
+
+
+@app.get("/api/automation", tags=["ops"])
+def automation_get(refresh: bool = False):
+ """Panels must never block on an audit."""
+ flags = autoflow.settings(db=db)
+ now = time.time()
+ cached = dict(_automation_cache)
+ fresh = cached["data"] is not None and (now - cached["at"]) < _AUTOMATION_TTL_SEC
+ if refresh or not fresh:
+ _start_automation_refresh()
+ return {
+ "flags": flags,
+ "counts": (cached["data"] or {}).get("counts")
+ or {"notes": 0, "missing_cover": 0, "tts_stale": 0},
+ "refreshing": _automation_refreshing() or not fresh,
+ "cached_at": (datetime.fromtimestamp(cached["at"]).isoformat(timespec="seconds")
+ if cached["at"] else None),
+ }
+
+
+def _automation_counts() -> Dict[str, Any]:
+ """The slow half: cover gap + stale TTS over the whole library."""
+ notes = [p for p in sorted(CONTENT_DIR.glob("*.md")) if not p.stem.endswith(".en")]
+ missing_cover = [...]
+ stale = tts_mod.stale_targets(content_dir=CONTENT_DIR, audio_root=AUDIO_DIR)
+ return {"counts": {"notes": len(notes), "missing_cover": len(missing_cover),
+ "tts_stale": len(stale)},
+ "missing_cover": missing_cover[:20]}
+
+
+def _start_automation_refresh() -> None:
+ with _automation_lock:
+ if _automation_refreshing_flag[0]:
+ return
+ _automation_refreshing_flag[0] = True
+
+ def _run() -> None:
+ started = time.time()
+ try:
+ data = _automation_counts()
+ _automation_cache.update({"data": data, "at": time.time()})
+ except Exception as exc: # a broken audit must not 500 the card
+ logging.warning("automation counts failed: %s", exc)
+ finally:
+ _automation_refreshing_flag[0] = False
+ logging.info("automation counts refreshed in %.1fs", time.time() - started)
+
+ threading.Thread(target=_run, name="automation-counts", daemon=True).start()
+```
+
+
+
+**2. A stale value must announce itself.** The response carries `refreshing` and `cached_at`, and
+the card renders `计算中…` next to the numbers while a recompute is in flight, then retries in three
+seconds. The alternative — showing `0` until data arrives — is worse than being slow, because a
+zero on a backlog counter is indistinguishable from "nothing to do".
+
+**3. The client never parses blindly.** `res.json()` was the actual defect on the browser side: it
+inferred "malformed JSON" from "HTML error page" and discarded both the status code and the body
+that would have named the failing hop.
+
+
+🔍 View the text-first response reader and the retry contract (22 lines)
+
+```javascript
+function autoReadJson(res) {
+ // A proxy timeout / 502 / 503 returns an HTML error page. Blind res.json() turns that into
+ // "Unexpected token '<'" and hides which layer failed — keep the status and the head instead.
+ return res.text().then(function (text) {
+ try {
+ return { data: JSON.parse(text) };
+ } catch (e) {
+ var head = String(text || '').trim().slice(0, 80).replace(/\s+/g, ' ');
+ return { error: 'HTTP ' + res.status + ' · ' + (head || 'empty response') };
+ }
+ });
+}
+
+// refreshing: show the last known numbers, label them, come back in three seconds.
+document.getElementById('auto-counts').textContent =
+ '笔记 ' + counts.notes + ' · 缺封面 ' + counts.missing_cover
+ + ' · 待朗读 ' + counts.tts_stale + (data.refreshing ? ' · 计算中…' : '');
+if (data.refreshing) { autoRetryTimer = setTimeout(window.autoReload, 3000); }
+```
+
+
+
+A fourth change is smaller but load-bearing: `?refresh=1` forces a recompute. Tests and humans both
+need a way to demand fresh numbers without waiting out a TTL, and without that affordance the test
+suite would have had to sleep for minutes.
+
+The test contract changed with the endpoint, which is the part most people skip: the suite now
+polls until the background thread lands instead of asserting numbers on the first response. 28 tests
+pass; the DOM contract check still confirms 16 cards, 68 buttons and 124 element ids, so the frontend
+rewrite did not quietly drop a control.
+
+---
+
+## Reflection: five rules for panel endpoints
+
+1. **A dashboard endpoint is not a query interface.** It is the most frequently called and least
+ cached surface in an admin UI. Anything O(n) over user data belongs in a background job with a
+ publish step, not in the GET that a browser polls on focus.
+2. **The timeout budget is the smallest hop, not the sum.** Four proxies each "allowing" 120 s do
+ not add up; the request dies at whichever clock expires first, and the application cannot see any
+ of them. The application-side budget has to be an order of magnitude under the smallest one.
+3. **`499` deserves an alert.** It is the only signal that reports "the caller gave up", and it
+ bypasses every 5xx-based monitor. A ratio of 499s to 200s on a single path, scraped from the
+ access log, detects this entire class of failure for almost no cost.
+4. **Never trust `res.json()` on a proxied route.** Read text, keep the status, keep the first line.
+ The error message is the only artefact the next engineer will have.
+5. **Stale-while-revalidate beats a bigger number.** Raising a timeout moves the wall; decoupling
+ compute from read removes it — and it removes it for the worst case I have not measured yet
+ (2,507 s here), not just the one I did (13.6 s).
+
+---
+
+## The result
+
+- **Cached read path: 200 in 0.24–0.36 s** across five consecutive calls measured inside the
+ container — a median of 0.26 s against the 13.6 s audit it replaced, **52× faster at the median
+ and 38–57× across the spread**.
+- **Worst observed case eliminated as a user-facing risk.** The audit still takes 2,507 s when the
+ box is saturated; it now costs a background thread instead of a broken card, and the panel keeps
+ answering throughout.
+- **Correctness preserved, not masked.** Counts at the time of writing: 341 notes, 1 missing cover,
+ 47 notes pending audio — the same computation, moved.
+- **The failure can no longer masquerade.** If any hop aborts again, the card prints
+ `HTTP 503 · …` and the investigation starts at the right layer.
+
+The single most useful line in this whole exercise was the one that lied: `Unexpected token '<'`.
+An HTML error page handed to a JSON caller is not a syntax error — it is a proxy telling me, in the
+only language it has, that it stopped waiting before my code ever woke up.
+````
+
+### 6.2 中文版(同 slug 镜像,`posts/zh/`)
+
+````markdown
+---
+ogImage: "/og/zh-when-a-proxy-timeout-returns-html-instead-of-json.jpg"
+banner: "/banners/when-a-proxy-timeout-returns-html-instead-of-json.jpg"
+title: "反代超时返回 HTML,前端报 SyntaxError:一次 499 事故复盘"
+description: "后台卡片报「Unexpected token '<'」,所有线索都指向前端,真正的故障却是一个跑在 GET 请求线程里的全库审计:289 到 341 篇笔记逐一切章、算哈希、stat 音频,穿过三层反代,实测从空闲时的 13.6 秒恶化到满载时的 2507 秒。"
+pubDate: 2026-10-28
+category: engineering
+blueprint: self-hosted-infrastructure
+blueprintLayer: L2-edge-proxy-runtime
+tags: ["nginx", "fastapi", "反向代理", "超时", "缓存", "可观测性", "stale-while-revalidate"]
+draft: false
+summary:
+ audience: "自建边缘层的全栈工程师、SRE 与平台架构师"
+ painPoint: "一张后台卡片被一句 JavaScript 语法错误判了死刑,排查方向全被带到前端;真实死因是一个 O(n) 全库审计占住单核、被三层反代各自的超时闸门无声掐断。"
+ impact:
+ - metric: "端点延迟 −98%"
+ detail: "把 289 至 341 篇笔记的全库审计挪出请求线程,改由单飞后台线程刷新;缓存读路径连续五次实测 0.24–0.36 秒,替代空闲时 13.6 秒、满载时 2507 秒的同步审计。"
+ - metric: "故障自动指明层级"
+ detail: "用「先读文本、保留状态码与首 80 字节」的读取器替代裸 res.json(),反代中断时面板直接打印「HTTP 503 ·
+
+线上面板报上来只有一行字:
+
+```
+读取自动处理状态失败:SyntaxError: Unexpected token '<', "
+
+出问题的路由是发布控制台的一张状态卡片:
+
+```
+GET /api/automation
+```
+
+它返回三个开关和三个计数。两个计数很便宜,第三个叫 `tts_stale`,为了算它要跑一遍全库审计:枚举所有 markdown 笔记、逐篇切章节、对每章内容算哈希、再对每个目标音频文件 `stat` 一次。而这一切**就跑在请求线程里**,卡片每轮询一次就重算一次。
+
+```python
+# 出问题的写法:一个 GET,顺手把整个库量一遍
+@app.get("/api/automation")
+def automation_get():
+ notes = [p for p in sorted(CONTENT_DIR.glob("*.md")) if not p.stem.endswith(".en")]
+ missing_cover = [...]
+ stale = tts_mod.stale_targets(content_dir=CONTENT_DIR, audio_root=AUDIO_DIR) # O(n) 审计
+ return {"counts": {"notes": len(notes), "tts_stale": len(stale)}, ...}
+```
+
+289 篇笔记时这是一次 13.6 秒的请求。光这个数字就已经足够取消它在轮询卡片上的资格,但让它从「慢」变成「坏」的是另一半原因:这个请求要穿过三层反向代理才摸到这个函数——一层 SSO 门禁、一层 portal nginx、再进容器网络——每一层都有自己的超时,而应用既设不了、也看不见。
+
+portal 的 `/api/` 位置在现行配置里是 `proxy_read_timeout 120s`,而它前面的 SSO 门禁放弃得更早。任何一层的计时器一到,它就按 nginx 的本分处理:停止等待、断开上游连接、把**自己的 HTML 错误页**交给浏览器。前端接着对这份文档执行 `await res.json()`,于是就有了那行 `SyntaxError`。
+
+整条链上没有任何一环返回 500。应用从头到尾都不知道这个请求存在过。
+
+---
+
+## 三条证据钉死「其实什么都没坏」
+
+三个产物合起来就能定位故障,不需要猜。单独看每一条都是歧义的,其中一条还会主动误导。
+
+| 证据 | 观测值 | 排除了什么 |
+|---|---|---|
+| portal 访问日志 | 该路径 7 次请求,**全是 `499`、响应体 0 字节**,无 4xx、无 5xx | 排除应用报错与鉴权拒绝 |
+| 应用日志(uvicorn) | 故障窗口内该路径**一条记录都没有** | 证明请求根本没到应用,是代理先放弃的 |
+| 容器内直连打点 | 同一调用直连应用:连续三次 **17.7 秒 / 98.5 秒 / 35.8 秒** | 确认慢是真的、且慢在审计内部,不是网络抖动 |
+
+`499` 是 nginx 在**客户端**先断开连接时写下的状态码。它不是服务端承认的错误,不是 5xx,FastAPI 的访问日志也永远看不到它——所以这张卡片可以坏上几个星期,而所有存活探测、错误率看板、「服务健康吗」的问答统统是绿的。真正描述这次故障的状态码,由放弃等待的那一层写下,指向的是一个应用从未收到的请求。
+
+而最响的那条证据恰好在误导:`SyntaxError`。它对文档的描述是准确的,对原因的判断是错的。
+
+---
+
+## 隐藏变量:空闲时 13.6 秒,满载时 2507 秒
+
+修之前,审计的耗时看起来像个常数。它不是。控制台容器被钉在单核(`cpuset "4"`),CPU 权重只有默认值的四分之一(`cpu_shares 256`),而宿主机上还跑着 HEVC 转码闸门和内容流水线。这场审计要跟别的任务抢那一个核。
+
+把审计挪出请求路径之后,应用开始自己记录耗时,假设才变成测量:
+
+```
+automation counts refreshed in 76.1s
+automation counts refreshed in 82.3s
+automation counts refreshed in 93.5s
+automation counts refreshed in 124.2s
+automation counts refreshed in 165.1s
+automation counts refreshed in 633.6s
+automation counts refreshed in 1282.5s
+automation counts refreshed in 2507.2s
+```
+
+同一份代码、同一个 341 篇的库,一个数量级的跨度,最坏值比空闲机上量到的数字**慢 184 倍**。这一条就足以否定那个「朴素修法」:把反代超时从 120 秒抬到 300 秒,解决不了一个 2507 秒的计算,只是把 120 秒失败改成 300 秒失败,晚五分钟、多占五分钟容量。
+
+这就是我在边缘层反复吃到的教训:**端点延迟不是代码的属性,而是它被允许借用多少 CPU 的属性。** 单核 cgroup 限流会把一次 O(n) 文件审计变成耗时由邻居决定的无界操作。
+
+---
+
+## 修法:把读路径和计算路径彻底拆开
+
+三处契约改动,每一处都小到可以独立验证。
+
+**一、读路径绝不计算。** 计数放进 TTL 缓存(300 秒,`AUTOMATION_COUNTS_TTL_SEC` 可调)。请求要么拿到新鲜缓存,要么立刻拿到上一次的值并带 `refreshing: true`,同时由一个后台 daemon 线程重算。单飞用一把锁加一个标记守住,成串的轮询不会变成成串的审计。缓存还会在应用 lifespan 里预热一次,第一个打开面板的人不用替所有人付这笔账。
+
+
+🔍 查看 stale-while-revalidate 实现 —— 缓存、单飞刷新线程与启动预热(52 行)
+
+```python
+_AUTOMATION_TTL_SEC = float(os.environ.get("AUTOMATION_COUNTS_TTL_SEC", "300") or 300)
+_automation_cache: Dict[str, Any] = {"data": None, "at": 0.0}
+_automation_refreshing_flag = [False]
+_automation_lock = threading.RLock()
+
+
+@app.get("/api/automation", tags=["ops"])
+def automation_get(refresh: bool = False):
+ """面板绝不在审计上阻塞。"""
+ flags = autoflow.settings(db=db)
+ now = time.time()
+ cached = dict(_automation_cache)
+ fresh = cached["data"] is not None and (now - cached["at"]) < _AUTOMATION_TTL_SEC
+ if refresh or not fresh:
+ _start_automation_refresh()
+ return {
+ "flags": flags,
+ "counts": (cached["data"] or {}).get("counts")
+ or {"notes": 0, "missing_cover": 0, "tts_stale": 0},
+ "refreshing": _automation_refreshing() or not fresh,
+ "cached_at": (datetime.fromtimestamp(cached["at"]).isoformat(timespec="seconds")
+ if cached["at"] else None),
+ }
+
+
+def _automation_counts() -> Dict[str, Any]:
+ """慢的那一半:全库缺封面 + 过期朗读目标。"""
+ notes = [p for p in sorted(CONTENT_DIR.glob("*.md")) if not p.stem.endswith(".en")]
+ missing_cover = [...]
+ stale = tts_mod.stale_targets(content_dir=CONTENT_DIR, audio_root=AUDIO_DIR)
+ return {"counts": {"notes": len(notes), "missing_cover": len(missing_cover),
+ "tts_stale": len(stale)},
+ "missing_cover": missing_cover[:20]}
+
+
+def _start_automation_refresh() -> None:
+ with _automation_lock:
+ if _automation_refreshing_flag[0]:
+ return
+ _automation_refreshing_flag[0] = True
+
+ def _run() -> None:
+ started = time.time()
+ try:
+ data = _automation_counts()
+ _automation_cache.update({"data": data, "at": time.time()})
+ except Exception as exc: # 审计坏了也不能把卡片打成 500
+ logging.warning("automation counts failed: %s", exc)
+ finally:
+ _automation_refreshing_flag[0] = False
+ logging.info("automation counts refreshed in %.1fs", time.time() - started)
+
+ threading.Thread(target=_run, name="automation-counts", daemon=True).start()
+```
+
+
+
+**二、陈旧值必须自己说出来。** 响应里带 `refreshing` 与 `cached_at`,卡片在重算期间把「计算中…」挂在数字后面,3 秒后自动补取一次。反面做法——在数据到达前先显示 `0`——比慢更糟,因为积压计数器上的 0 与「确实没事可做」长得一模一样。
+
+**三、客户端绝不盲解析。** 真正躺在浏览器侧的缺陷是 `res.json()`:它把「收到了 HTML 错误页」推断成「JSON 格式错误」,顺手丢掉了状态码和本该指出故障层级的那段正文。
+
+
+🔍 查看「先读文本」的响应读取器与重试契约(22 行)
+
+```javascript
+function autoReadJson(res) {
+ // 反代超时 / 502 / 503 回的是 HTML 错误页;裸 res.json() 会把它变成
+ // "Unexpected token '<'",并把「哪一层挂了」这条信息彻底吞掉。
+ return res.text().then(function (text) {
+ try {
+ return { data: JSON.parse(text) };
+ } catch (e) {
+ var head = String(text || '').trim().slice(0, 80).replace(/\s+/g, ' ');
+ return { error: 'HTTP ' + res.status + ' · ' + (head || '空响应') };
+ }
+ });
+}
+
+// refreshing 时:先显示上一次的数字并标注状态,3 秒后再来一次
+document.getElementById('auto-counts').textContent =
+ '笔记 ' + counts.notes + ' · 缺封面 ' + counts.missing_cover
+ + ' · 待朗读 ' + counts.tts_stale + (data.refreshing ? ' · 计算中…' : '');
+if (data.refreshing) { autoRetryTimer = setTimeout(window.autoReload, 3000); }
+```
+
+
+
+第四处改动更小但很关键:`?refresh=1` 强制重算。测试和人一样,都需要一条「立刻要新数字」的通道,否则整套测试只能靠 sleep 等 TTL 过期。
+
+测试契约跟着端点一起改了,这是大多数人会跳过的一步:用例现在轮询到后台线程落地为止,而不是对第一次响应就断言数字。28 个测试通过;DOM 契约检查仍然确认 16 张卡片、68 个按钮、124 个元素 id,说明前端那轮改写没有悄悄丢掉控件。
+
+---
+
+## 复盘:我此后对每个面板端点都套用的五条规矩
+
+1. **看板端点不是查询接口。** 它是后台里调用最频繁、缓存最少的表面,任何对用户数据的 O(n) 计算都属于后台任务加一次发布,不属于浏览器聚焦时轮询的那个 GET。
+2. **超时预算等于最小那一跳,不是各跳之和。** 四层反代各自「允许」120 秒并不叠加:请求死在最先到期的那个时钟上,而应用一个都看不见。应用侧的预算必须比最小的那个还低一个数量级。
+3. **`499` 值得配一条告警。** 它是唯一一个表达「调用方放弃了」的信号,且天然绕开所有基于 5xx 的监控。从访问日志抓单条路径上 499 与 200 的比值,几乎零成本覆盖这一整类故障。
+4. **在反代后面永远不要信 `res.json()`。** 先读文本,留下状态码,留下第一行。下一个接手的人手里只有这条错误信息。
+5. **stale-while-revalidate 优于一个更大的数字。** 抬高超时只是把墙往后推;把计算与读拆开才是把墙拆掉——而且拆掉的是我还没量到的那个最坏值(这里的 2507 秒),不只是量到的那个(13.6 秒)。
+
+---
+
+## 交付结果
+
+- **缓存读路径:连续五次容器内实测 200,0.24–0.36 秒**,中位 0.26 秒,对照它所替代的 13.6 秒审计——**中位快 52 倍,整个区间快 38–57 倍**。
+- **最坏情况不再暴露给使用者。** 宿主机饱和时审计仍要 2507 秒,但它现在只花掉一个后台线程,而不是一张坏掉的卡片,面板全程照常应答。
+- **正确性保留而非掩盖。** 撰写时的计数:341 篇笔记、1 个缺封面、47 篇待生成朗读——同一个计算,只是搬了位置。
+- **故障再也无法伪装。** 任何一跳再次中断,卡片会打印 `HTTP 503 · …`,排查从正确的层级开始。
+
+整件事里最有用的一行,恰恰是那行说谎的 `Unexpected token '<'`。把 HTML 错误页递给一个 JSON 调用方,不是语法错误——那是反代用它唯一会说的语言在告诉我:我的代码还没醒,它就已经不等了。
+````
+
+---
+
+## 附录一:B 篇全文 `when-lowering-crf-does-nothing-maxrate-deadlock`
+
+### EN
+
+````markdown
+---
+ogImage: "/og/when-lowering-crf-does-nothing-maxrate-deadlock.jpg"
+banner: "/banners/when-lowering-crf-does-nothing-maxrate-deadlock.jpg"
+title: "When Lowering CRF Does Nothing: The -maxrate Ceiling and the SSIM Quality Deadlock"
+description: "Why dropping CRF by five steps improved SSIM by only 0.00003: discovering the mathematical conflict between -maxrate bitrate ceilings and quality gates in high-throughput video re-encoding."
+pubDate: 2026-10-27
+category: devops
+blueprint: self-hosted-infrastructure
+blueprintLayer: L1-compute-storage-network
+tags: ["ffmpeg", "transcoding", "hevc", "ssim", "video-engineering", "quality-gate"]
+draft: false
+summary:
+ audience: "Video Infrastructure Engineers, SREs & Platform Architects"
+ painPoint: "A high-throughput video space-reclamation pipeline repeatedly burned 106 hours across 487 encoding rounds because quality-gate retries (crf -2) produced zero metric improvement on capped bitrates."
+ impact:
+ - metric: "Exposed Deadlock"
+ detail: "Proved that crf 19 to crf 14 under an identical -maxrate ceiling yielded an SSIM shift of only +0.00003, rendering lower CRF retries mathematically inert."
+ - metric: "Two-Tier Quality Gate"
+ detail: "Introduced a format-aware gate accepting already-compatible streams (H.264+AAC) at 0.990 SSIM while reserving 0.995 for format-remediation encodes."
+ - metric: "Eliminated Wasted Compute"
+ detail: "Bypassed 100% of pointless re-encode cycles on capped files, cutting retry overhead from 106+ hours to zero on boundary video files."
+ stack: ["FFmpeg", "libx265", "SSIM", "Python", "Linux Dedicated Nodes"]
+ jumpLinks:
+ business: "#the-batch-that-burned-106-hours-doing-nothing"
+ architecture: "#the-architectural-solution-two-tier-gates-and-cap-awareness"
+ result: "#the-result"
+dynamicCta: true
+---
+
+## The batch that burned 106 hours doing nothing
+
+In a high-throughput transcoding pipeline running across dedicated on-premise infrastructure,
+one of my automated ingestion queues had a strict rule: **the output must never exceed the size
+of the source** (`SIZE_CEIL = 1.0`).
+
+The goal was automated space reclamation across tens of thousands of archived video files. If an
+HEVC transcode came out larger than the H.264 original, the pipeline rejected it as counterproductive.
+To enforce this, every encode passed `-maxrate ` and `-bufsize `
+to FFmpeg.
+
+At the same time, the pipeline guarded visual fidelity with an automated quality gate:
+every transcoded file had its structural similarity (SSIM) measured against the source frame by frame.
+If the mean SSIM fell below `0.995`, the item failed the quality gate. To recover borderline files,
+the supervisor triggered an automatic retry: `quality+2` (dropping CRF from 29 to 27 or 19 to 17).
+
+Then the production telemetry came in.
+
+Over a two-week run, **487 quality rounds failed**, consuming **106.2 hours of dedicated compute**.
+Worse: of 202 files that went through two consecutive rounds, **194 showed an SSIM movement of
+less than 0.0015**. The second round burned full CPU cycles to produce a file almost byte-for-byte
+identical to the one rejected ten minutes earlier.
+
+---
+
+## The anomaly in the lab
+
+To isolate why the quality ladder was failing to move the needle, I isolated a representative test
+video: a 720p@30 clip (`1280x720`, 96 seconds) with an original video bitrate of 1832 kbps.
+
+I ran three controlled encoding passes using `libx265`:
+
+```text
+Run 1 (Baseline):
+ Command: -crf 19 -maxrate 1832k -bufsize 4580k
+ Size: 20.6 MB (87.5% of source)
+ SSIM Mean: 0.99192 --> REJECTED (below 0.995)
+
+Run 2 (The Aggressive Quality Retry):
+ Command: -crf 14 -maxrate 1832k -bufsize 4580k (5 quality steps lower!)
+ Size: 22.9 MB (97.3% of source)
+ SSIM Mean: 0.99195 --> REJECTED (below 0.995)
+ Delta: +0.00003 SSIM improvement
+
+Run 3 (Uncapped Ceiling):
+ Command: -crf 19 (no -maxrate, no -bufsize)
+ Size: 40.6 MB (172.5% of source)
+ SSIM Mean: 0.99648 --> PASS (above 0.995)
+```
+
+Look at the difference between Run 1 and Run 2: **dropping CRF by five full steps moved the mean
+SSIM by exactly 0.00003**.
+
+Yet in Run 3, the exact same CRF 19 sailed past the 0.995 bar to 0.99648—at the cost of expanding
+the file to **172%** of its original size.
+
+---
+
+## The mathematical trap between CRF and -maxrate
+
+The culprit was not the video encoder, the preset, or the content complexity. It was a fundamental
+contradiction in the pipeline's control architecture:
+
+1. **CRF is a quality target**, not a rate controller. It calculates the quantization parameter needed to maintain uniform visual fidelity across frames.
+2. **`-maxrate` is a hard bitrate ceiling enforced by the VBV (Video Buffer Verifier)** buffer.
+3. When complex scenes demand more bits to meet a lower CRF, the VBV governor intervenes and drops quality to honor the `-maxrate` ceiling.
+
+In high-motion, high-entropy scenes, reaching an SSIM mean of 0.995 required **at least 125% to
+170%** of the source's native bitrate. But the `SIZE_CEIL = 1.0` contract I enforced forbade giving
+the encoder those bits.
+
+Consequently, when an encode ran close to the ceiling (spending >= 97% of the allowable bits),
+lowering the CRF did not buy more detail—it simply forced the encoder to hit the ceiling earlier in
+the GOP and throw the extra bits away. The quality ladder stopped being a ladder.
+
+---
+
+## The architectural solution: Two-tier gates and cap awareness
+
+To eliminate the 106 hours of wasted compute, I refactored the supervisor's decision engine in three ways:
+
+### 1. Identifying the true business objective (`space_reclaim_source`)
+Why was a file being transcoded in the first place?
+* **Case A: Format Remediation.** If the source was VP9, AV1, or inside an MKV container with AC-3 audio, it could not play natively in mobile or web browsers without server-side transcoding. Here, fidelity is paramount: I enforce the strict `0.995` threshold.
+* **Case B: Pure Space Reclamation.** If the source was already an H.264 baseline MP4 with AAC audio, it already played everywhere natively. The *only* purpose of re-encoding was saving disk space. Holding a compatible file to a strict 0.995 bar that forces bitrate expansion is counterproductive.
+
+For Case B, the pipeline now accepts a secondary tier: `SSIM_MEAN_MIN_FALLBACK = 0.990`.
+
+### 2. Detecting the ceiling wall (`CAP_BOUND_SHARE`)
+If an encode used >= 97% of the allowable source bitrate (`obps >= vbps * SIZE_CEIL * 0.97`),
+the file was capped. Retrying with a lower CRF would mathematically yield zero benefit. The pipeline
+now detects this condition and cancels the retry immediately.
+
+
+View pipeline quality-gate logic (Python excerpt)
+
+```python
+SIZE_CEIL = 1.0 # Output video bitrate must not exceed the source's
+CAP_BOUND_SHARE = 0.97 # Spent >= 97% of ceiling -> rate-limited, not gear-limited
+QUALITY_STEPS = 2 # Retry offset (crf - 2)
+
+def space_reclaim_source(info, src):
+ """True if the video and audio streams already decode natively on mobile/web targets."""
+ if info.get("vcodec") not in ("h264", "avc1"):
+ return False
+ if info.get("acodec") not in (None, "aac", "mp4a"):
+ return False
+ return os.path.splitext(src)[1].lower() in (".mp4", ".m4v", ".mov")
+
+def evaluate_quality_gate(info, src, mean_ssim, bad_share, vbps, obps):
+ capped = bool(vbps) and obps >= (vbps * SIZE_CEIL * CAP_BOUND_SHARE)
+ systematic = bad_share > MAX_BAD_SHARE or (mean_ssim < SSIM_TARGET)
+
+ # Second-tier relaxation for files that already play everywhere
+ if systematic and bad_share <= MAX_BAD_SHARE and mean_ssim >= SSIM_MEAN_MIN_FALLBACK:
+ if space_reclaim_source(info, src):
+ return "PASS_SECOND_TIER", "Accepted at secondary 0.990 threshold for space reclamation"
+
+ if systematic:
+ # If output is already hitting the bitrate ceiling, retry is mathematically inert
+ if capped:
+ return "FAIL_CAPPED", f"SSIM {mean_ssim:.4f} below bar, but output is capped ({obps/vbps*100:.1f}%) - retry skipped"
+ return "RETRY", f"Retry with crf - {QUALITY_STEPS}"
+
+ return "PASS", "Clean pass"
+```
+
+
+
+---
+
+## The result
+
+After deploying this change to the production nodes:
+* The test file that previously bounced between `crf 19` and `crf 14` with a `FAIL quality 0.99192` now cleared on its first pass as `PASS_SECOND_TIER` (87.5% of original size, zero retries, 120s encode time).
+* The 106-hour wasted compute overhead on boundary files dropped to **zero**.
+* Pipeline throughput increased by **31%** across the archival queue because the supervisor stopped running provably inert second passes.
+
+When building automated transcoding pipelines, never evaluate quality metrics in isolation from your rate-control constraints. When CRF meets `-maxrate`, the ceiling always wins.
+````
+
+### ZH
+
+````markdown
+---
+ogImage: "/og/zh-when-lowering-crf-does-nothing-maxrate-deadlock.jpg"
+banner: "/banners/when-lowering-crf-does-nothing-maxrate-deadlock.jpg"
+title: "当降低 CRF 毫无效果:视频转码中 -maxrate 封顶与 SSIM 质量门禁的死锁"
+description: "为什么将 CRF 下调 5 档仅让 SSIM 微升 0.00003:深度拆解高吞吐视频转码管线中 -maxrate 码率天花板与客观质量门禁之间的数学死锁与两档放行解法。"
+pubDate: 2026-10-27
+category: devops
+blueprint: self-hosted-infrastructure
+blueprintLayer: L1-compute-storage-network
+tags: ["ffmpeg", "transcoding", "hevc", "ssim", "video-engineering", "quality-gate"]
+draft: false
+summary:
+ audience: "视频基础设施工程师、SRE 与平台架构师"
+ painPoint: "私有高吞吐转码管线在 487 轮转码中空转了 106 小时算力,原因是质量门禁的自动重试(CRF -2)在硬性码率上限拦截下完全失效。"
+ impact:
+ - metric: "揭露死锁根因"
+ detail: "实测证明在相同 -maxrate 上限下,CRF 从 19 降至 14 仅让 SSIM 提升 0.00003,重试在数学上完全惰性。"
+ - metric: "二档差异化门禁"
+ detail: "引入业务目标分流:全平台可播的原生 H.264+AAC 源按 0.990 空间回收档放行,非兼容格式保持 0.995 严格修复档。"
+ - metric: "消除无效算力损耗"
+ detail: "彻底旁路封顶文件的无效重试,将临界视频的重试开销从 106 小时直接清零。"
+ stack: ["FFmpeg", "libx265", "SSIM", "Python", "Linux 生产节点"]
+ jumpLinks:
+ business: "#消耗了-106-小时却毫无进展的转码批次"
+ architecture: "#架构重构二档门禁与封顶自适应旁路"
+ result: "#交付结果"
+dynamicCta: true
+---
+
+## 消耗了 106 小时却毫无进展的转码批次
+
+在我所维护的私有高吞吐媒体转码基础设施中,自动化入库队列设定了一条铁律:**转码输出文件体积绝对不可超过原文件**(`SIZE_CEIL = 1.0`)。
+
+这套管线的核心业务目标是自动化磁盘空间瘦身。如果一次 HEVC(H.265)转码输出的文件比原来的 H.264 原档还要大,系统会直接将其判定为负收益并废弃。为了在编码期严格约束体积,调度器在调用 FFmpeg 时强制注入了 `-maxrate <原视频码率>` 与 `-bufsize <原视频码率 * 2.5>`。
+
+与此同时,管线引入了端到端客观画质门禁:每一个转码产物都必须与原视频逐帧比对结构相似性(SSIM)。如果整片 SSIM 均值低于 `0.995`,该文件就会触发质量拦截。为了抢救临界画质文件,系统设计了自动化重试梯队:`quality+2`(将 CRF 调低 2 档重新编码)。
+
+然而随后的生产监控日志揭露了一个反常现象。
+
+在连续两周的运行中,**系统记录了 487 次质量门禁拦截**,白白消耗了 **106.2 小时的高性能算力**。更严重的是:在经历连续两次编码的 202 个文件中,**有 194 个文件的 SSIM 变动幅度低于 0.0015**。第二次重试在消耗了完全相同的 CPU 周期后,产出了一个与 10 分钟前被拒收的文件近乎完全相同的结果。
+
+---
+
+## 实验室受控排查与数据断点
+
+为了彻底查清为什么质量梯队无法拉动指标,我从队列中抽取了一个典型测试样本:一段 720p@30 规格的视频(`1280x720`,时长 96 秒,原视频码率为 1832 kbps)。
+
+在相同的硬件与 `libx265` 环境下,我进行了三组对照实验:
+
+```text
+第 1 组(基线配置):
+ 参数: -crf 19 -maxrate 1832k -bufsize 4580k
+ 体积: 20.6 MB(原片 87.5%)
+ SSIM 均值:0.99192 --> 拦截拒收(低于 0.995)
+
+第 2 组(激进画质重试):
+ 参数: -crf 14 -maxrate 1832k -bufsize 4580k (整整调优 5 档!)
+ 体积: 22.9 MB(原片 97.3%)
+ SSIM 均值:0.99195 --> 拦截拒收(低于 0.995)
+ 指标增量: +0.00003 SSIM 提升
+
+第 3 组(解除码率天花板):
+ 参数: -crf 19(不设置 -maxrate,不设置 -bufsize)
+ 体积: 40.6 MB(原片 172.5%)
+ SSIM 均值:0.99648 --> 顺利通过(高于 0.995)
+```
+
+仔细对比第 1 组与第 2 组:**CRF 整整下调了 5 档,但测得的 SSIM 均值仅仅前进了 0.00003**。
+
+而在第 3 组中,完全相同的 CRF 19 参数毫无压力地达到了 0.99648——代价是生成的文件膨胀到了原文件的 **172%**。
+
+---
+
+## CRF 与 -maxrate 之间的数学死锁
+
+导致这 106 个小时算力空转的根因不是编码器 Bug,也不是视频场景过于复杂,而是管线控制架构上的内在逻辑冲突:
+
+1. **CRF 是画质目标控制器**,而非码率分配器。它根据帧内复杂度动态计算量化步长,试图维持恒定的视觉保真度。
+2. **`-maxrate` 是硬性码率天花板**,由 VBV(Video Buffer Verifier)缓冲队列物理强制执行。
+3. 当复杂的运动场景需要更多比特来达成更低的 CRF 时,VBV 机制强制介入削减码率,以确保不突破 `-maxrate` 上限。
+
+在纹理密集的高熵场景中,想要达到 0.995 的极端高保真度,物理上需要消耗原片 **125% 到 170%** 的码率。但我设定的 `SIZE_CEIL = 1.0` 契约明令禁止给编码器分配这些比特。
+
+因此,当一次编码输出的码率已经逼近上限(消耗了超过 97% 的天花板容量)时,继续调低 CRF 根本无法换来画质细节——它只会让编码器在 GOP 的开头更快撞墙,然后把多余的比特全部丢弃。所谓的质量梯阶在上限面前彻底失去了调节能力。
+
+---
+
+## 架构重构:二档门禁与封顶自适应旁路
+
+为了根除算力浪费,我对调度器的裁决引擎进行了三重重构:
+
+### 1. 拆解真实业务目标(`space_reclaim_source`)
+一个视频为什么要被送入转码队列?
+* **场景 A:格式兼容性修复**。如果原片是 VP9、AV1 编码,或是封装在 MKV 容器中带 AC-3 音轨,它无法直接在移动端或浏览器内硬解播放。此时兼容性是第一要务:我坚持死守 `0.995` 门禁。
+* **场景 B:纯粹的磁盘瘦身**。如果原片本身已经是标准的 H.264+AAC MP4 封装,在所有设备上都能原生硬解,转码的唯一目标就是省空间。如果对本来就能播的文件死守 0.995,最终只会逼迫码率膨胀,本末倒置。
+
+针对场景 B,管线引入了二档放行标准:`SSIM_MEAN_MIN_FALLBACK = 0.990`。
+
+### 2. 封顶阻断判定(`CAP_BOUND_SHARE`)
+如果编码输出已经消耗了源视频码率的 97%(`obps >= vbps * SIZE_CEIL * 0.97`),说明该视频已经撞死在码率墙上。此时调低 CRF 重试在数学上注定无效。调度器检测到此状态后,直接取消重试并记录原因。
+
+
+点击查看质量门禁自适应裁决核心代码(Python 关键片段)
+
+```python
+SIZE_CEIL = 1.0 # 输出码率严禁超过源视频
+CAP_BOUND_SHARE = 0.97 # 达到上限 97% 表明已触顶,调节阶梯失效
+QUALITY_STEPS = 2 # 重试步长 (crf - 2)
+
+def space_reclaim_source(info, src):
+ """判定原视频图像与音频层是否均已具备原生硬件解码能力"""
+ if info.get("vcodec") not in ("h264", "avc1"):
+ return False
+ if info.get("acodec") not in (None, "aac", "mp4a"):
+ return False
+ return os.path.splitext(src)[1].lower() in (".mp4", ".m4v", ".mov")
+
+def evaluate_quality_gate(info, src, mean_ssim, bad_share, vbps, obps):
+ capped = bool(vbps) and obps >= (vbps * SIZE_CEIL * CAP_BOUND_SHARE)
+ systematic = bad_share > MAX_BAD_SHARE or (mean_ssim < SSIM_TARGET)
+
+ # 针对原生兼容格式启用二档空间回收门禁 (0.990)
+ if systematic and bad_share <= MAX_BAD_SHARE and mean_ssim >= SSIM_MEAN_MIN_FALLBACK:
+ if space_reclaim_source(info, src):
+ return "PASS_SECOND_TIER", "原生全兼容文件:按二档 0.990 空间回收门禁放行"
+
+ if systematic:
+ # 如果输出已触碰码率封顶,重试在数学上完全惰性,立即阻断
+ if capped:
+ return "FAIL_CAPPED", f"SSIM {mean_ssim:.4f} 未达标,但码率已触顶 ({obps/vbps*100:.1f}%) - 跳过无效重试"
+ return "RETRY", f"启动画质补偿重试 (CRF - {QUALITY_STEPS})"
+
+ return "PASS", "完美通过"
+```
+
+
+
+---
+
+## 交付结果
+
+将新策略部署至生产节点后:
+* 此前在 `crf 19` 与 `crf 14` 之间死循环报错 `FAIL quality 0.99192` 的测试文件,在首轮直接以 `PASS_SECOND_TIER` 顺利入库(体积为原片的 87.5%,零重试,单次编码用时 120 秒)。
+* 临界文件上的 106 小时无效算力浪费被**彻底清零**。
+* 由于不再对封顶文件发起注定失败的第二轮重转,长队列的整体处理吞吐量提升了 **31%**。
+
+在构建大规模自动化转码管线时,切忌脱离码率控制策略去孤立考核客观画质指标。当 CRF 遇上 `-maxrate`,退让的永远是画质,而不是天花板。
+````
+
+---
+
+## 附录二:C 篇全文 `why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux`
+
+### EN
+
+````markdown
+---
+ogImage: "/og/why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux.jpg"
+banner: "/banners/why-ffmpeg-map-0-breaks-your-mp4-and-mkv-remux.jpg"
+title: "Why ffmpeg -map 0 Breaks Your Remux: The Hidden Matrix of Multi-Track MP4 and Matroska"
+description: "Why using -map 0 silently aborts Matroska muxers, drops subtitle fonts, and corrupts Apple playback: an empirical teardown of container constraints across multi-track media pipelines."
+pubDate: 2026-10-27
+category: engineering
+blueprint: self-hosted-infrastructure
+blueprintLayer: L1-compute-storage-network
+tags: ["ffmpeg", "containers", "mp4", "matroska", "subtitles", "media-pipeline"]
+draft: false
+summary:
+ audience: "Media Systems Engineers, Full-Stack Architects & Streaming DevOps"
+ painPoint: "A media remuxing daemon using naive -map 0 silently crashed on container conversions, dropped stylised subtitles, and broke track selection on iOS clients."
+ impact:
+ - metric: "Zero Muxer Aborts"
+ detail: "Isolated incompatible QuickTime chapters and mobile timecode streams (tmcd) that caused Matroska muxers to fatal-error on generic -map 0 invocations."
+ - metric: "100% Subtitle Survival"
+ detail: "Architected deterministic conversion from ASS/SRT into mov_text for MP4 while preserving bit-exact font attachments in Matroska."
+ - metric: "Cross-Platform Metadata"
+ detail: "Dual-wrote track titles to both udta/name (Apple) and hdlr (ffprobe/VLC), solving the silent loss of track names during secondary remux passes."
+ stack: ["FFmpeg", "ffprobe", "Python", "Matroska/MP4 Specifications"]
+ jumpLinks:
+ business: "#the-naive-map-0-that-breaks-in-production"
+ architecture: "#the-hidden-matrix-of-container-behavior"
+ result: "#the-result"
+dynamicCta: true
+---
+
+## The naive `-map 0` that breaks in production
+
+Every developer automating FFmpeg reaches for the same shortcut when preserving secondary
+tracks during a transcode:
+
+```bash
+ffmpeg -i source.mkv -map 0 -c:v libx265 -c:a copy output.mp4
+```
+
+The logic seems bulletproof: map all streams (`-map 0`), transcode the video stream to save bits,
+and copy every accompanying audio track, subtitle, and chapter without losing data.
+
+In production across a large, diverse video archive, this command fails catastrophically in three
+distinct ways:
+
+1. **It hard-crashes on Matroska output**: If an MP4 source contains Apple QuickTime text chapters (`bin_data`) or iPhone timecode tracks (`tmcd`), FFmpeg aborts instantly:
+ `Only audio, video, and subtitles are supported for Matroska.`
+2. **It hard-crashes on MP4 output**: In standard stream copy mode (`-c:s copy`), any ASS, SSA, SRT, or graphical Bitmap (PGS) subtitle triggers fatal aborts:
+ `Could not find tag for codec ass in stream #2, codec not currently supported in container.`
+ Even when enabling subtitle transcoding to `-c:s mov_text`, graphical bitmap subtitles (`hdmv_pgs_subtitle` or `dvd_subtitle`) cannot be converted into text atoms and cause identical muxer crashes.
+3. **It silently strips font attachments**: When remuxing into MP4, attached `.ttf` or `.otf` styling fonts cause fatal errors: `Could not find tag for codec ttf`.
+4. **It silently wipes audio track names on iOS**: Apple devices read track labels from `udta/name`, whereas VLC, ExoPlayer, and ffprobe read from `hdlr/handler_name`. FFmpeg's demuxer only reads `handler_name` back, so a secondary remux silently drops the Apple tag.
+
+To build an automated, crash-proof media pipeline, you cannot treat containers as transparent
+envelopes. You need an explicit, policy-driven stream planner.
+
+---
+
+## The hidden matrix of container behavior
+
+Through automated test fixtures and packet-level MD5 hashing across thousands of source files,
+I mapped the exact boundaries between MP4 and Matroska (`.mkv`):
+
+| Feature / Track Type | MP4 (`.mp4`) Constraints | Matroska (`.mkv`) Behavior |
+|---|---|---|
+| **Audio Codecs** | Officially supports AAC. AC-3/E-AC-3 work on Apple, but Android player matrices break. | Accepts anything bit-exact (FLAC, DTS-HD, TrueHD, Opus, AAC). |
+| **Text Subtitles** | **Only accepts `mov_text` (tx3g)**. ASS, SRT, WebVTT cause immediate muxer fatal aborts. | Copies ASS, SSA, SRT verbatim without re-encoding. |
+| **Subtitle Styling** | `mov_text` strips fonts, positioning, shadows, and karaoke fades. Only basic bold/italic survives. | Preserves all Aegisub styling, override tags, and positioning. |
+| **Font Attachments** | **Refused outright**. `Could not find tag for codec ttf`. | Carried verbatim bit-for-bit via `0:t` stream mapping. |
+| **Timecode / Chapter Data** | Holds QuickTime text and `tmcd` streams natively. | **Fatal error** if passed raw QuickTime/timecode data tracks. |
+| **Metadata Preservation** | Drops `creation_time` without `-map_metadata 0`; drops custom tags without `-movflags +use_metadata_tags`. | Preserves arbitrary key-value tags natively. |
+
+Notice the asymmetry: **MP4 is an end-user distribution container with strict parser gates,
+while Matroska is a high-fidelity archival container.** Treating them identically with `-map 0`
+guarantees pipeline crashes.
+
+**Why not standardize exclusively on MKV?** Because container selection is dictated by downstream client decoding contracts. Native Apple playback engines (`AVPlayer` on iOS/macOS and WebKit `