docs(project-state): truth-up four stale statuses, record the batch blockers
Deploy / build (push) Successful in 1m4s
Deploy / build (push) Successful in 1m4s
Measured against the live site and the cron outputs today, not carried over:
- A1 (author photo) and A3 (featured strip) were still ⬜ although both shipped
(B4 and 6e9d58b) — marked done with the evidence, /start-here left as a
deliberate deferral.
- E3 is fully automated: the LinkedIn half was already a daily no_agent cron
(id 35f8985f609a). 5 posts published as of today's 12:30 tick; its silence is
not proof of work, so the output files are the record.
- E6 closed: HSTS waived by the owner ("无视吧"), re-measured still max-age=0.
Do not re-open.
- E1: CF Web Analytics re-measured absent (0 cloudflareinsights hits, one Umami
tag). It is an owner dashboard action — no token here can enable it and CF
injects the beacon itself, so there is no repo change to make.
- B6 now states the real shape of the artwork work: 0 of 72 posts rolled out,
the live site still serves the v1 images, the 3 consultant questions are
unanswered, the rendered OG card is measured at 558 KB PNG vs 84 KB JPEG q85,
and the promised OCR pseudo-text gate does not exist yet.
This commit is contained in:
1 parent
a41448bb4a
commit
2060aaa166
2 files changed
+31
-21
No files matched your search
+30
-21
@@ -45,8 +45,11 @@ Living list of what's done and what's next for blog.hoelee.com. Work through the
|
||||
- **Self-fitting headline engine implemented**: dynamically measures DOM lines in headless Chrome, stepping down font size (50px → 26px) until headline fits `<= 3 lines` with no orphan/widow lines; CJK titles intelligently split at punctuation; numbers glued with NBSP.
|
||||
- Front-end homepage enhanced: `Featured` section upgraded to 2×2 Cyber-Bento Grid (with metric pills and pulsing green status dots); bilingual navigation brand link fixed to `/zh/`; in-post category tags fixed to `/zh/categories/`.
|
||||
- **Shipped to production 2026-10-18 (commit `6e9d58b`)**, after a measured pass on both themes: the bento scrim and metric pill were hardcoded to a *dark* blend, which on the light theme produced a muddy grey band and **1.96:1** pill text contrast; both are now theme tokens (`--card-veil`, `--pill-bg`, `--bento-visual-opacity`), and the "pulsing" dot finally has an actual `@keyframes bento-pulse` (it had none, and it is disabled under `prefers-reduced-motion`). Measured in headless Chrome, 1280px and 390px, EN + ZH: 4 cards, `2 cols / 1 col`, `documentElement.scrollWidth − clientWidth = 0`, no card overflow, all four `-800.webp` thumbs `naturalWidth 800` — contrast dark pill 6.56 / badge 4.83 / h3 11.86 / desc 5.78, light pill 6.10 / badge 4.85 / h3 16.36 / desc 6.74 (WCAG AA ≥ 4.5). Tag chips restyled to mono + hairline border (4px radius) site-wide; ZH post pages link `/zh/categories/<cat>/`, ZH brand links `/zh/`.
|
||||
- Full catalog of 72 posts' unique physical metaphors and prompts extracted to `scripts/og-gen/prompts_catalog.json` (working-tree tooling, not committed) and `Desktop\blog-og-image-redesign-brief\03-72-Posts-Visual-Prompts-Catalog.md` (outside the repo).
|
||||
- Round 4 delivery bundle packaged at `C:\Users\hoelee\Desktop\blog-og-image-redesign-brief\delivery-for-consultant-round4.zip`.
|
||||
- Full catalog of 72 posts' unique physical metaphors and prompts extracted to `scripts/og-gen/prompts_catalog.json`, with the batch renderer `scripts/og-gen/render_from_art.mjs` + `art-meta.json` (committed 2026-10-18; the readable version of the catalog ships outside the repo at `Desktop\blog-og-image-redesign-brief\03-72-Posts-Visual-Prompts-Catalog.md`).
|
||||
- **Batch rollout: 0 of 72 posts.** Everything above is **template-level**: the two sample posts (`running-production-infrastructure-solo`, `docker-reported-exit-0-the-oom-killer-disagreed`) are rendered into `Desktop\blog-og-image-redesign-brief\delivery-for-consultant-round4\` only. The repo/live site still serves the **v1** set — 72 `public/banners/*.png` + 72 `public/og/*.png` generated by the terminal-panel generators on 2026-10-06…10-16 — so nothing on the site changed appearance from this work yet.
|
||||
- **Three consultant questions are still unanswered** (they gate the batch; the ask is written verbatim in `delivery-for-consultant-round4\00-第四轮-实施反馈与3个待定细节.md` §二): (1) English headline rag — (A) keep the stepped shape, (B) drop 3–5px for equal-width lines, **(C) shorten the OG-only title via a new `ogTitle` frontmatter field**, (D) other; (2) the hard CJK length cap for OG titles (currently assumed **≤30 chars incl. punctuation**, EN ≤65); (3) whether list thumbnails should keep 100% right-focus or hold back to 85% for breathing room at ~398px display width.
|
||||
- **Asset weight measured 2026-10-18 (must be decided before the batch).** A composited OG card is **558 KB as rendered PNG** vs the **42 KB** average of today's 72 cards; ×144 (EN+ZH) that is **~84 MB in the repo** and a much heavier share payload. Same card re-encoded: 256-colour PNG **240 KB**, 128-colour PNG 194 KB, **mozjpeg q85 84 KB**, q78 70 KB. So the batch should ship **JPEG** for `og:image` (the precedent already set by `/og-default.jpg`, 45 KB) — which means an `ogImage:` path change on 144 frontmatter files, or a deliberate decision to keep PNG at ~240 KB and eat ~35 MB.
|
||||
- Known gap in the plan: the **OCR pseudo-text gate** on the base art (regenerate a frame when any readable character is detected) was promised to the consultant and is **not implemented**.
|
||||
- ✅ **B4 identity & distribution batch, 2026-10-16 (fourth batch)** — the photo and the About rewrite:
|
||||
- **Real author photo** (source `Professional Portrait with Blue Gradient Background.png`, 1254×1254, kept untouched in `Pictures/` with a converted JPG master beside it). Two derivatives, WebP first with a JPEG fallback: `lee-teong-hoe-640` (42 KB JPG / 26 KB WebP) and `-128` (3 KB / 2 KB). Appears in the About hero, the author card on every post, the home hero (both languages) and **JSON-LD `Person.image`** — the node search engines use to bind name to face.
|
||||
- **About page 1,024 → 7,357 characters**, in five sections: identity + photo, "By the numbers" (162 containers / 3 hosts / 71 essays / 0 hosting companies between me and the servers), the real tech stack in four columns, the Hoelee Enterprise block (website design and development is the main business, email hosting secondary) and a contact split between hiring and business (WhatsApp for enquiries). Chinese twin rewritten to match.
|
||||
@@ -148,6 +151,7 @@ The user asked for the dashboard to be reachable remotely **without** publishing
|
||||
| **Tracker wired into the two sites (2026-09-29) ✅** | Website records created: `blog.hoelee.com` → `1817d8f6-0e49-4b9a-a50d-307e4f2962c7`, `www.digikedai.com` → `61b57143-f3f4-4353-9ede-9c615146de84`. Snippet added to `blog/src/layouts/BaseLayout.astro` (commit **de17bea**, pushed Gitea+GitHub, Gitea Actions → live in ~1 min; covers EN **and** ZH since both use the layout) and to `dsm-resource-management/www/src/layouts/Base.astro` (CF Pages via `./deploy.sh`, deployment `4d3d6aa6.digikedai.pages.dev`). Both tags carry `is:inline` (Astro would otherwise process the script) + `data-domains` so local dev never reports. Verified live HTML on both domains contains the `data-website-id`; verified end-to-end with a **real Chrome** (Canary CDP 9222): blog `/` + `/zh/` → 4 pageviews/2 visitors, digikedai `/` + `/products/` → 2 pageviews/1 visitor, geo `MY / MY-07 / George Town`, chrome/Windows 10; then `POST /api/websites/<id>/reset` cleared the test rows (both back to 0) |
|
||||
| ⚠ **Verification pitfall: headless browsers are ignored** | Umami drops bot/headless user agents — `HeadlessChrome/151` (DSM browserless) → `200 {"beep":"boop"}` and **nothing stored**; a normal Chrome UA → `200 {"cache":…}` and stored. So `beep boop` = *ignored*, not success, and an empty dashboard after a headless test is not a bug. Verify tracking with a **real** browser (Canary CDP 9222: `curl -X PUT "http://127.0.0.1:9222/json/new?<url>"`), or at least a normal-UA curl |
|
||||
| Not done yet | CF Web Analytics beacon + GSC `sitemap-index.xml` submission (both independent of Umami) |
|
||||
| **Re-measured 2026-10-18** | Live homepage carries **exactly one** tracker tag (`stats.hoelee.com/script.js`, `data-website-id=1817d8f6…`) and **zero** `cloudflareinsights` references. Enabling CF Web Analytics is an **owner dashboard action** — there is no API token on this host with account scope (the deploy token purges only, and `~/.secrets/cloudflare-stats-token.txt` is still an unfilled placeholder), and CF injects its beacon itself once the site is added, so there is **no repo change to make**. GSC submission likewise only answers in its own UI. |
|
||||
| Known trade-off (honest) | (1) SSO protects the *dashboard route*, but Umami has **no OIDC**, so after SSO the user still sees **Umami's own login page** — double login, expected, one-click after the first time (2FA is available per-account in Settings → Profile; `TWO_FACTOR_ENCRYPTION_KEY` already in the stack env). (2) `<http://192.168.1.1:5411>` remains a **LAN break-glass path that bypasses SSO** (still needs Umami credentials; verified closed from the internet) — delete/close it if that is not wanted. (3) If the authentik outpost goes down, the dashboard goes down with it (tracker too — but the site keeps loading, the tracker fails silently) |
|
||||
| Rollback | Start the gate again (`sudo /usr/local/bin/docker start umami-gateway`, or start stack 285 in Portainer) → re-point the vhost + `ReverseProxy.json` to `5410` → reload nginx. Backups of both files sit next to the originals. (The gate container is now **`Exited (0)`** — it was stopped once the outpost took over, so port 5410 is free; the stack is kept as the rollback asset) |
|
||||
| Note | The `hermes3` authentik API token expired mid-session (DB says `expires 2026-09-28 22:17:08Z`); the leftover self-test account was removed through authentik's own ORM (`ak shell`), so no admin-group test user is left behind |
|
||||
@@ -163,8 +167,7 @@ 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.** 🔵 dev.to half **automated 2026-10-17**; LinkedIn still manual
|
||||
(report §9 "Month 2+ — promote": never executed)
|
||||
**Step E3 — Syndicate 2–3 flagship posts.** ✅ Both halves automated (dev.to 2026-10-17, LinkedIn 2026-10-16; measured 2026-10-18)
|
||||
|
||||
`seo-reference.md` already carries the syndication policy (cross-post with canonical back to the original domain);
|
||||
until 2026-10-17 **nothing had ever been syndicated**, so all 72 posts existed only for people who already knew
|
||||
@@ -185,8 +188,15 @@ the URL — the daily dev.to drip below is the first distribution channel outsid
|
||||
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.
|
||||
- [x] ✅ **LinkedIn half is automated too (installed 2026-10-16, measured 2026-10-18).** Hermes no_agent cron
|
||||
`linkedin-daily-post` (id `35f8985f609a`, `30 12 * * *`) runs `scripts/publish_next.py --commit` in
|
||||
`D:/dev/hoelee-career-agent` and stays **silent unless it published / failed / ran dry** — silence is not proof
|
||||
of work, so read `cron/output/35f8985f609a/*.md`: **5 posts published** as of 2026-10-18 12:30 (today's was
|
||||
`read-only-nocodb-dashboard-for-a-remote-database`). Its "already published" ledger is NocoDB
|
||||
`activity_events`, not a local file. So the old manual criterion is superseded: what remains is **feeding the
|
||||
approved-draft queue**, not clicking share.
|
||||
- **Done when:** ✅ each dev.to copy is live with the canonical set (2 of 69 so far, one per day), and the LinkedIn
|
||||
drip is publishing (5 measured, daily 12:30).
|
||||
|
||||
**Step E4 — Chinese-side routes and nav are broken.** ✅ Done 2026-10-16 (batch B2), re-verified live 2026-10-17
|
||||
|
||||
@@ -206,7 +216,7 @@ ZH post pages emit `<meta property="og:locale" content="en">` with `og:locale:al
|
||||
|
||||
- **Done when:** ✅ measured 2026-10-17 — a ZH post page emits `og:locale=zh_CN` with `og:locale:alternate=en_US`.
|
||||
|
||||
**Step E6 — Cloudflare edge HTML caching.** 🔵 Cache Rule live 2026-10-17 and **measured working**; the HSTS half is still open
|
||||
**Step E6 — Cloudflare edge HTML caching.** ✅ Done 2026-10-17 (Cache Rule + purge + freshness proven); HSTS waived by the owner 2026-10-18
|
||||
|
||||
A post page used to return `cf-cache-status: DYNAMIC`. A static site behind CF should be edge-cached — lower TTFB and
|
||||
less origin traffic through the tunnel.
|
||||
@@ -221,16 +231,14 @@ less origin traffic through the tunnel.
|
||||
- [x] ✅ **The deploy-side purge is proven live, not assumed.** The Gitea Actions run for `112cf7d` (task 252 / run 11313,
|
||||
job 22965) logs `Purging zone *** … {"success":true,"errors":[],"messages":[],"result":{"id":"…"}}` — `CF_TOKEN` and
|
||||
`CF_ZONE_ID` are set and the API call succeeded, so a publish is not stuck behind the 2 h Edge TTL.
|
||||
- [ ] **HSTS is still `max-age=0; includeSubDomains; preload`** (re-measured 2026-10-17) — the decision (12 months,
|
||||
`includeSubDomains`, **preload OFF**) is still an owner action. `max-age=0` makes the `preload` directive inert,
|
||||
so nothing is broken today; the zone setting is simply not doing anything yet.
|
||||
- [x] ~~HSTS is still `max-age=0; includeSubDomains; preload`~~ — **waived by the owner 2026-10-18** (「HSTS 无视吧,没有关系的」). Re-measured the same day: still `max-age=0; includeSubDomains; preload`. Nothing is broken by it (`max-age=0` makes `preload` inert, and Cloudflare's "Always Use HTTPS" already covers the http→https hop), so this is a deliberate no-action, not a gap. **Do not re-open it** unless the owner asks.
|
||||
- [x] ✅ **Freshness proven end-to-end, 2026-10-17.** Before the push, `/posts/` was a `HIT` still serving
|
||||
`last-modified 14:35:23`; **67 s after** the push the same URL served `last-modified 14:56:41` with `age=0` — the
|
||||
purge invalidated the 2 h-cached copy and the new build was live **inside a minute**, so the Edge TTL cannot hide a
|
||||
publish. ⚠ Recipe: compare the **`last-modified` header** before/after a push (nginx stamps it at deploy time) —
|
||||
`cf-cache-status: HIT` alone can be a 2-hour-old build, which is exactly the trap this test exists to catch.
|
||||
- **Done when:** a repeat request shows `cf-cache-status: HIT` ✅ **and** a deploy goes live within ~1 minute ✅
|
||||
(67 s measured) **and** HSTS carries the decided value ⬜.
|
||||
(67 s measured) **and** HSTS carries the decided value ⏸ **waived 2026-10-18 by the owner** — the step is closed.
|
||||
|
||||
**Step E7 — Content-volume guardrail.** ⬜ ongoing
|
||||
|
||||
@@ -264,23 +272,24 @@ less origin traffic through the tunnel.
|
||||
|
||||
### Phase A — Identity (highest ROI, ~2–3 hrs total)
|
||||
|
||||
**Step A1 — Add a real author photo (headshot).**
|
||||
**Step A1 — Add a real author photo (headshot).** ✅ Done 2026-10-16 (batch B4)
|
||||
The #1 gap vs. every reference blog: the avatar is a letter "M" placeholder and there's no photo anywhere on the site. Every credible personal dev blog has a human face.
|
||||
- [ ] User provides one headshot (square, ≥800×800 for avatar; also source for og).
|
||||
- [ ] Replace `avatar` letter with the photo in: nav/brand (optional), author card (About + every post), `ProfilePage` JSON-LD `image`.
|
||||
- [ ] Add the photo to `og-default.png` template so the default share card has a face.
|
||||
- [x] Headshot supplied; two derivatives built (`lee-teong-hoe-640`: 42 KB JPG / 26 KB WebP, `-128`: 3 KB / 2 KB).
|
||||
- [x] Photo renders in the About hero, the author card on every post, the home hero (EN + ZH) and JSON-LD `Person.image`.
|
||||
- [x] Faceless `og-default` replaced twice since: the outside delivery's card now carries the same headshot (`/og-default.jpg`).
|
||||
- **Governing doc:** `design-guide.md` §2 (author box), §5 (E-E-A-T name+photo consistency).
|
||||
- **Done when:** a real face renders in the author card on About + every post; `curl` shows no 404 for the asset.
|
||||
- **Done when:** ✅ a real face renders in the author card on About + every post; no 404 for any derivative.
|
||||
- ⚠ This checkbox stayed ⬜ for two days after the work shipped — the doc is a claim, the page is the fact. Re-checked 2026-10-18.
|
||||
|
||||
**Step A2 — Align the homepage title & hero framing.** ✅ Done 2026-10-18
|
||||
- [x] Pick one line: "Full-Stack Developer & DevOps Engineer" aligned across `SITE.title`, `SITE.description`, homepage hero `<h1>`, and JSON-LD `jobTitle`.
|
||||
- **Done when:** ✅ homepage title, meta description, and hero all say the same thing about who Hoelee is.
|
||||
|
||||
**Step A3 — Add a "Start here" / featured posts route.**
|
||||
**Step A3 — Add a "Start here" / featured posts route.** ✅ Homepage strip done 2026-10-18 (`6e9d58b`); dedicated page still deferred on purpose
|
||||
New visitors land on reverse-chronological "Latest posts" with no guidance to the best content (Julia Evans' Favorites, Josh Comeau's featured posts both solve this).
|
||||
- [ ] Add a "Start here" (or "Featured") section on the homepage surfacing 2–3 flagship case studies.
|
||||
- [ ] Optionally add a `/favorites` or `/start-here` page (defer the dedicated page until ≥6 strong posts; the homepage strip is the immediate win).
|
||||
- **Done when:** homepage shows a featured/start-here strip above or beside "Latest posts".
|
||||
- [x] Homepage `Featured` section: a 2×2 Cyber-Bento grid over four pinned flagship slugs, each with its OG thumbnail, category badge and a measured metric pill (`3 Hosts · 162 Containers`, `11 Cron Jobs · 8 Pure Loops`, …). EN + ZH twins, 1 column under 680px.
|
||||
- [ ] `/start-here` or `/favorites` page — **deliberately deferred**: the homepage strip is the immediate win, and the `posts` listing plus `related` rows already cover discovery; revisit only if the four flagships stop being the strongest content.
|
||||
- **Done when:** ✅ homepage shows a featured/start-here strip above "Latest posts" (verified in the built HTML: 4 `bento-card`, `featured-bento-grid` present).
|
||||
|
||||
### Phase B — Content (80% of value; the long game)
|
||||
|
||||
@@ -894,7 +903,7 @@ not needed), cross-checked against the local repo at `D:/dev/hoelee-blog`.
|
||||
- Ideogram 4 pipeline running locally (`1360x768`, ar 16:9).
|
||||
- Outside consultant reviewed Round 1..4. Frosted Bento layout + blueprint grid + headline auto-fitting (`FIT <n>px`) verified on real Chrome Headless.
|
||||
- Delivery package Round 4 zipped (`delivery-for-consultant-round4.zip`, 2.4 MB).
|
||||
- Ready for 72-post batch run once consultant finalises the 3 minor typography questions.
|
||||
- Ready for the 72-post batch run — **blocked on**: the consultant's answers to the 3 typography questions, the `og:image` format decision (measured: 558 KB PNG → 84 KB JPEG q85), a GPU window for the ~70 missing base-art frames, and the promised OCR pseudo-text gate.
|
||||
|
||||
|
||||
Method: `curl` from the Windows host, the unRaid nginx access log **read over the Portainer API** (no SSH to that host),
|
||||
|
||||
@@ -42,6 +42,7 @@ No entry → a generic default terminal.
|
||||
- PNG (not JPG) is intentional: for flat UI/terminal art, PNG is smaller and
|
||||
sharper than JPG (see commit history).
|
||||
- The `logo-square.png` referenced lives at `public/logo-square.png`.
|
||||
|
||||
---
|
||||
|
||||
## v2 pipeline ("one art, four targets") — `render_from_art.mjs`
|
||||
|
||||
Reference in new issue
Block a user