diff --git a/docs/project-state.md b/docs/project-state.md
index 658ef38..52bd7a6 100644
--- a/docs/project-state.md
+++ b/docs/project-state.md
@@ -144,12 +144,23 @@ domain passes no authority and offers no click-path to the portfolio — one-way
into that overhaul (together with the v2.1 audit finding that its agency framing conflicts with the job hunt).
Tracked here so it isn't lost.
-**Step E3 — Syndicate 2–3 flagship posts.** ⬜ (report §9 "Month 2+ — promote": never executed)
+**Step E3 — Syndicate 2–3 flagship posts.** 🔵 dev.to half **automated 2026-10-17**; LinkedIn still manual
+(report §9 "Month 2+ — promote": never executed)
`seo-reference.md` already carries the syndication policy (cross-post with canonical back to the original domain);
-nothing has ever been syndicated, so the 53 posts only exist for people who already know the URL.
+until 2026-10-17 **nothing had ever been syndicated**, so all 72 posts existed only for people who already knew
+the URL — the daily dev.to drip below is the first distribution channel outside the site.
-- [ ] Cross-post to dev.to with `` pointing at the original: `how-i-built-the-digikedai-telegram-bot`, `one-prometheus-for-unraid-synology-and-a-vps`, `self-hosting-mem0`.
+- [x] ✅ **dev.to cross-posting is automated (2026-10-17).** `scripts/devto-crosspost.py` in the Hermes scripts dir
+ (`C:\Users\hoelee\AppData\Local\hermes\scripts\`) publishes **one queued post per day** with `canonical_url`
+ pointing at the blog original, from a Hermes **no_agent cron** (`devto daily crosspost`, id `ce8fc8bd23e6`,
+ `25 12 * * *`, deliver Telegram). Queue = 10 ranked titles in `devto-queue.json`; `devto-state.json` makes it
+ idempotent per slug; the API key lives in `~/.secrets/devto-api-key.txt`, never in the repo.
+ First article live and verified: **https://dev.to/hoelee/how-i-built-the-digikedai-telegram-ai-bot-39of** —
+ `canonical_url` matches the blog post in the API read-back **and** in the live page's ``,
+ tags `ai, python, showdev, automation`, cover image re-hosted by dev.to (so the banner landed).
+ ⚠ Quirks written into the `hoelee-blog` skill: the banner and the layout hero are frontmatter (a verbatim body copy
+ ships imageless), relative links must be absolutised **outside** code fences only, and dev.to rejects unknown tags.
- [ ] One LinkedIn share per flagship (same photo + name identity as the blog).
- **Done when:** each dev.to copy is live with the canonical set, and each has one LinkedIn post.
@@ -861,7 +872,7 @@ and a DOM probe for the regenerated card. Spot values: `/zh/posts/` 200 · `/tag
| Check | Result |
|---|---|
| **Closed since the Sept audit** | A1 real photo ✅ (B4) · A3 featured strip ✅ (B3) · D1 search ✅ (B3 fixed the ES-module loader) · E4 ZH routes + nav ✅ (B2) · E5 `og:locale` ✅ (B2) · E8 draft bank ✅ (now empty) |
-| **Still open** | E1 CF Web Analytics beacon + GSC sitemap (owner) · E6 HTML caching + HSTS (**CF zone write — owner**) · E8 `og-default.png` (**rejected once; being designed outside the repo — brief on the Desktop**) + the one banner-less post · E9 the design system names two fonts the site never loads · A2 title/hero wording (cosmetic) · E2 (recorded, deferred by the owner 2026-10-17) · E3 syndication (needs dev.to + LinkedIn accounts) |
+| **Still open** | E1 CF Web Analytics beacon + GSC sitemap (owner) · E6 HTML caching + HSTS (**CF zone write — owner**) · E8 `og-default.png` (**rejected once; being designed outside the repo — brief on the Desktop**) + the one banner-less post · E9 the design system names two fonts the site never loads · A2 title/hero wording (cosmetic) · E2 (recorded, deferred by the owner 2026-10-17) · E3 syndication (dev.to ✅ automated 2026-10-17 · LinkedIn still needs the account) |
| **GitHub mirror** | was **24 commits behind** — nothing had been pushed since `d2b15d6`, so all of B1–B4 existed only on Gitea. Pushed 2026-10-17: `d2b15d6..f4b2ce9`, now **0/0** with both remotes |
| **`/posts/zh/` redirect scheme** | the 301 came back as `Location: http://blog.hoelee.com/zh/posts/` (nginx absolutised it with the scheme the tunnel reached it on), costing an extra Cloudflare "Always Use HTTPS" 308 hop. Fixed 2026-10-17 in the host nginx conf with `absolute_redirect off` (backup `default.conf.bak-20261017` beside it), validated in a throwaway container against the real file, `nginx -t` + reload in the live container — now `Location: /zh/posts/` on both the LAN origin and the public URL |
| **`/zh/posts/` 502 blip** | 3 short 502s inside one polling window, then **45/45 × 200** (15 each on `/`, `/posts/`, `/zh/posts/`). The nginx access log over 4 h holds **zero 5xx** (411 × 200 / 31 × 404 / 11 × 304 / 2 × 301) and the LAN origin answers in 3–20 ms ⇒ the 502s came from the tunnel/edge path, not from the page or the origin. Worth watching; not a defect in the archive |