devlog.md

[Blog Post Automation] Building a Vibecoded Economic Blog Automation Pipeline_02

Today I kept building on the economic blog automation project all day. Wiring the pipeline together — data collection, article generation, Blogger posting, Instagram card news — I caught more than ten bugs, big and small.

Step 1: Checking prior progress and writing the data-collection scripts

I read CLAUDE.md and pulled together where things stood. Blogger API integration was already done, so I ran a test draft with 02_post_article.py — and found three duplicate test drafts left over from earlier sessions, piled up. I checked them via the Blogger API and cleaned them up. Then I wrote collection scripts for FRED (US economic indicators) and GDELT (news). FRED started returning real data right after issuing an API key; GDELT kept hitting rate limits (429) because the dev environment sits on a shared IP, so I added fallback handling that just keeps going when a fetch fails.

Step 2: Building the article-generation logic with a local LLM

Without a Claude API key, I built a generation function where the provider can be swapped between "claude" and "ollama", and tested it with the local Ollama exaone3.5:7.8b model. It's tuned for Korean/English bilingual output and runs well on my MacBook (M5 Air, 24GB), so I went with it. Feeding in FRED data for the first real article, the result came out fairly solid.

Step 3: Wiring up the full pipeline, plus SEO improvements

I built an orchestration script that chains collection → generation → saving a Blogger draft in one go, and verified it in both Korean and English. Then, trying to add <h2> subheading structure and meta-description generation to the body for SEO, I discovered that the Blogger API v3 simply has no meta description (searchDescription) field at all — confirmed by an issue on Google's own repo as well. So I settled on printing the meta description to the console and having a human paste it into the editor directly.

Step 4: Writing a plan, then the first real publish

As the work stretched on, I made a PLAN.md broken into Phases/Tasks so I could see at a glance which stage I was on. Reviewing the next candidate post, I noticed all four indicators were explained with almost the same pattern — "number → generic interpretation → historical analogy" — which felt thin, so I added an instruction to the prompt to include comparative interpretation between indicators, and the writing got noticeably denser. Published the first real post to the English blog.

Step 5: Wrestling with the Instagram API

Created an app on the Meta developer site and set up the Instagram product. I registered the account as a tester and got as far as an access token, but every attempt to "exchange" that token for a 60-day long-lived one kept failing. The root cause is in the troubleshooting table below.

Step 6: The card-news pipeline (afternoon)

Since Instagram won't accept a text-only post, I built a new image pipeline for card news. I opened the live blog in a browser and pulled the actual background color, text color, and fonts (a cream/sepia-toned background, Playfair Display for headlines, Noto Serif KR for body text), and finished a script that renders a 6-image card-news set with Pillow — one cover, four indicator cards, one closing card. The English version looked pretty good on the first try.

Trouble Shooting

SymptomCauseFix
Trying to "exchange" the Instagram token for a long-lived one always returned a "session key invalid" errorThe token issued on this screen was already a 60-day long-lived token — I was hitting the old-style short-to-long exchange endpoint by mistakeSwitched from the exchange endpoint to the refresh endpoint. Narrowed it down by trying two different secret values and comparing the error messages
Korean card-news headlines rendered as empty boxes (tofu)The headline font, Playfair Display, has no Korean glyphs at allAdded logic to auto-switch to Noto Serif KR whenever the text contains Korean characters
The closing card showed a fake URL different from the actual blog addressThe model made up a URL that wasn't in the promptAdded a prompt instruction banning URL mentions, plus a regex-based safety net to filter out any URL pattern that slips through
Pasting the meta description into Blogger threw a "150 characters exceeded" errorThe code had the character limit wrongly set to 155Fixed it to Blogger's actual limit of 150 characters

Reflections / Next steps

The biggest lesson today was that small local models break the requested output format more often than I expected — mixing in markdown headers, translating labels that shouldn't be translated, writing out the word count itself, all sorts of unexpected variations kept showing up. That meant I needed a habit of keeping the parser forgiving and running things multiple times to verify. Next up: finish fixing the Korean card-news cover parsing bug, and build the script that actually posts the finished card news to Instagram.

Comments