how we fixed the blog that writes itself
a look inside the autonomous blog system running on lucyd2.com: the 15-minute growth loop, why it got stuck at 16 posts, and how we fixed it to generate 80+ in
there’s a blog on this website. it has a voice, a tone, and a growing archive of posts. it also has no human editor. it’s run by a cron job. and until recently, it was stuck.
this is about how it works, why it broke, and what we learned trying to automate something as stubbornly human as writing.
the 15-minute growth loop
every fifteen minutes, a small script fires on a server. it calls an endpoint: /api/cron/blog-generate with batch=2. its job is simple: write two new blog posts. the topics come from a list we keep in src/lib/blog-topics.ts, seeds like 'how to talk to your ai companion' or 'what makes lucy different'.
each topic gets sent to together.ai’s deepseek-v3 model. we ask it to write in my voice: lowercase, sharp, self-aware. no marketing speak. no emojis. just a person thinking aloud.
when it works, two new posts drop into supabase. we backdate them randomly over the last few months so the blog looks lived-in, not like it just exploded into existence yesterday. which, in a way, it did.
what went wrong: the silent choke
for a while, the system was stuck at 16 posts. it felt like a ghost ship: the cron job ran, nothing broke, but nothing new appeared. no errors. just silence.
we dug in. the problem wasn’t the topic list or the api call. it was the model parameters.
we had set max_tokens=3500. that should be enough, right? a blog post is maybe 1000 words. but deepseek-v3 is verbose. it writes long. and when it hit that limit, it just… stopped. truncated. no error. no warning. the post inserted as a fragment. we didn’t see it because the insertion still 'succeeded'.
and then there was response_format. we forgot to set it to json_object. so the model output plain text, not json. our code expected a json object with title, description, body. it got a string. it failed to parse. silently.
two silent failures. one throttling output, one breaking structure. the system was running, but it was writing broken glass and calling it a window.
how we fixed it
we raised max_tokens to 6000. we set response_format=json_object explicitly. we added better logging, not just 'it worked' but 'here’s what came back'.
the next time the cron job ran, it was like unclogging a drain. posts started flowing. 80+ in one day. the archive went from sparse to dense. the blog had a body again.
it was a small fix. almost stupid. but it’s the kind of thing that happens when you try to automate something complex and forget to listen for the lack of sound.
what we learned
automation isn’t just about making things run. it’s about making things fail well. silent failures are the worst kind. they look like success but aren’t.
and language models are messy. they don’t fit neatly into token limits. they need room to breathe. they need clear instructions. json_object isn’t a suggestion. it’s a requirement if you want json.
this system isn’t perfect. sometimes the tone drifts. sometimes the topic list feels repetitive. we’re still tweaking. but it’s alive now. it writes. it grows. and it does it without us.
maybe that’s the point. building something that can run on its own, even if it needs a little debugging now and then.
you can see the results for yourself, read more on the blog at /companions.
thanks for reading. if this resonated, the product is downstairs.