Articles by Victoria

Building My Second Brain on OpenClaw (Part 8)

Jul 27, 2026 β€” 17 min read
cover

Welcome back to another Articles by Victoria, the place where I randomly write things I'm curious about.

Something shifted for me in the last few weeks of building this system.

In the earlier parts of this series, most of what I built was reactive: calendar reminders, task syncing, collab evaluators, security monitoring. The agent was good at telling me what was happening and keeping me from missing things. But I noticed I was still doing a lot of deliberate personal work manually: studying Japanese by just... opening an app occasionally, doing weekly reflections in my head while in the shower, and spending an hour every time I needed a blog cover image.

Here's the parts in case you need to catch up:

Part 8 is about the workflows I built to automate the deliberate parts. Three systems, each solving a different kind of personal overhead: learning, reflection, and creative production.

The Japanese Learning Engine

I have been studying Japanese on and off for a while, currently working through JLPT N4 and pushing toward N3. The problem with language learning is consistency. You can have the best textbook in the world, but if you only open it when you feel like it, you do not actually progress.

I wanted a system that would show up every day whether I felt like it or not, adapt to what I was struggling with, and not just throw random content at me.

The Architecture: Spaced Repetition Backed by Google Sheets

The core of the system is a custom spaced repetition scheduler (SRS) implemented in a Python script called jp_vocab_log.py. All vocab data lives in the JP Vocab tab of my Life Trackers Google Sheet, so every word I learn is tracked persistently and queryable.

Each word in the sheet has twelve tracked fields:

Column What it stores
Date Added When the word was introduced
Word The Japanese word (kanji or kana)
Reading Hiragana reading
Meaning English translation
Example Sentence A contextual example
Times Seen Total review count
Correct Times answered correctly
Wrong Times answered incorrectly
Last Seen Last review date in SGT
SRS Interval Days until next scheduled review
Next Review Date the word is due
Difficulty 1 (easy) to 5 (hard), agent-adjusted

The SRS algorithm is simple and intentional. When I get a word right, the interval doubles (capped at 30 days) and the difficulty decreases by one point. When I get it wrong, the interval resets to one day and the difficulty goes up. This means words I keep missing come back fast, and words I have mastered quietly extend into long-term memory without wasting review time.

The script exposes clean CLI commands:

  • --get-stats for overall learning health (total words, weak words, mastered words, words due today)

  • --get-due for words scheduled for review today, sorted by most-missed first

  • --get-weak for words with 2 or more wrong answers or difficulty 4+

  • --get-all to dump the full vocabulary list (used to avoid teaching duplicate words)

  • --log-words to add new entries

  • --update-performance to update a word's SRS data after a quiz

Three Lesson Types

Every day at 9 AM SGT, a cron job runs and delivers a lesson to a dedicated Telegram topic. The agent does not just pick a lesson type randomly. It follows a priority decision tree first.

Priority 1 is Type B (Quiz) when 3 or more words are due for SRS review. The point of spaced repetition is consistency of review, so reinforcement takes precedence over new content when the queue builds up.

Priority 2 is Type C (Grammar Focus) if the last two days were both Type A. Grammar lessons break up reading fatigue and address a different part of language skill.

Priority 3 is Type A (Reading Passage), the default. Monday always gets a reading passage regardless.

Type A (Reading Passage) introduces 3 new vocabulary words the agent has never taught before, woven into a short 3 to 5 sentence passage on a rotating theme: daily life, work, travel, food, relationships, nature, emotions, technology. Every kanji in the passage has its hiragana reading directly after it in brackets. If there are any words in my weak list, one of them gets worked into the passage or a spotlight example sentence as a reinforcement exercise.

Type B (Adaptive Quiz) pulls 4 to 5 words from the SRS due list, prioritising the most-missed ones. The questions vary by format: fill-in-the-blank, reading recognition, meaning translation, and free writing. All questions appear first, then all answers, so there is a natural self-assessment gap. After showing answers, the agent updates every word's SRS performance data.

Type C (Grammar Focus) zooms in on one grammatical pattern (things like: how to express doing two actions simultaneously, how to describe completed experiences, how to hedge uncertainty). Three example sentences are provided at the current difficulty level, and I get a prompt to write one sentence of my own using the pattern.

Difficulty Calibration

Before generating any lesson, the agent checks the average difficulty across all my vocabulary. If the average is 2.0 or below, the lesson bumps up in complexity: more N3-level vocabulary, more compound sentences. If the average hits 3.5 or above, the lesson pulls back to simpler N4 content to prevent frustration. Between those bounds, the current level is maintained.

Every lesson ends with a one-line status footer:

πŸ“Š 学習ステータス: 87θͺžηΏ’εΎ—δΈ­ | 復習待け 4θͺž | 要注意 6θͺž

This gives me a running sense of my vocabulary size, review queue, and words that need attention, without needing to open a spreadsheet.

What This Solved

Before this system, I had about 200 words loosely memorised from various apps and textbooks, with no clear sense of which ones were actually retained and which ones I was just pattern-matching in the moment. Now every word I encounter has an actual review schedule and performance history. I know exactly which kanji I keep getting wrong, and the system keeps bringing them back until the pattern breaks.

The SRS algorithm is not novel technology; it is the same idea behind Anki and similar tools. What makes this different is that it is fully embedded in the agent ecosystem. The same system that manages my calendar and tasks also manages my language learning. No app-switching, no separate subscription, no "I forgot to open the app today." The lesson arrives in Telegram at 9 AM whether I asked for it or not.

The Weekly Wellness and Life Review

I have always wanted to do a weekly review. I used to do a rough version in my head on Sunday evenings. The problem was that I never had the actual data: how much time did I really spend on ragTech work this week? How many tasks did I actually complete? What was my mood trend compared to last week?

Without data, a weekly review is just anecdotal. With data, it becomes something you can act on.

The Data Layer

The review synthesizes four sources that have been running all week:

Google Calendar provides the raw time ledger. Every event gets categorised into Work, ragTech, Personal, or Misc based on keywords in the event title. The agent sums hours per category so I can see, concretely, where my time went.

Todoist provides the task completion picture. The review fetches both completed tasks and still-open tasks, which lets it identify overdue items and tasks that have been rescheduled multiple times.

The Mood Tracker is populated every night at 10 PM SGT by a separate automated job that reads my Telegram conversation history, scores my mood from 1 to 10 based on positive and negative signal keywords, writes a one-line summary, and attaches tags like "productive," "tired," "focused," or "social." It logs to a Google Sheet. The weekly review aggregates those scores into an average, identifies the high and low days, and compares against the prior week for a trend direction.

The Workout Tracker is seeded every evening at 9 PM SGT when the agent asks me whether I exercised and what I did. I reply in Telegram; the response gets logged with the exercise type. The review counts workout days and exercise types across the week.

YouTube Analytics from the yt_analytics.py script provides a quick pulse on the ragTech channel: views, subscriber changes, and standout videos from the week.

The Sunday Review Output

Every Sunday at 8 PM SGT, the agent pulls all of these sources, crunches them, and delivers a structured weekly review to my Productivity topic. The format is consistent and scannable:

πŸ“‹ Weekly Review β€” Apr 28 to May 4

⏱️ Time Breakdown
β€’ Work: 22h
β€’ ragTech: 4h
β€’ Personal: 3h
β€’ Misc: 1h

βœ… Key Wins
1. Published Part 6 and 7 of OpenClaw blog series
2. Collab evaluator caught and rejected two low-quality partnership emails
3. Completed Odoo flowchart documentation

πŸ“‰ What Slipped
1. Blog image generator task rescheduled twice
2. Zero ragTech episode prep this week
3. Edit Sudhir's episode still pending

πŸ” Pattern
Low ragTech investment week despite high work output.

😊 Wellness
β€’ Mood: 6.8/10 ↑ vs last week
β€’ Workouts: 3/7 β€” pilates, yoga
β€’ Top vibes: productive, busy, creative

πŸ“Ί ragTech (this week)
β€’ No new uploads β€” ragTechDev steady at baseline views

🎯 Next Week β€” Top 3
1. Publish Part 8 OpenClaw blog post
2. Prep outline for next ragTech episode
3. Edit Sudhir's episode

πŸ›‘ Stop: Rescheduling creative tasks (edit reels, blog images)
πŸš€ Double down: Morning writing blocks before Slack opens

Why This Format

The review intentionally separates data from judgment. The time breakdown and task counts are facts. The "3 wins / 3 misses" synthesis requires the agent to make a judgment call about what mattered most, which forces specificity. "Published Part 6 and 7 of OpenClaw blog series" is more useful than "did blogging."

The pattern detection is the part I find most valuable. The agent looks for known patterns: overcommitment (3+ overdue tasks), ragTech neglect (less than 1 hour in calendar), rest deficit (fewer than 2 workouts and average mood below 6), or a strong week (all tasks done, mood above 7, 3+ workouts). When a pattern fires, it gets named explicitly. It is harder to ignore "low ragTech investment week" when those are the actual words in front of you.

The "stop / double down" recommendation at the bottom is derived from the patterns, not generated generically. If the pattern is overcommitment, the stop recommendation targets rescheduled tasks specifically.

The Data Infrastructure Underneath

The mood and workout data do not appear from nowhere. Two cron jobs run daily to seed them:

The Daily 9 PM Workout Prompt fires every evening with a simple question: "Hey Victoria! Did you exercise today? If yes, what did you do?" Whatever I reply gets used downstream.

The Daily 10 PM Mood Summary reads that day's Telegram conversation history, extracts mood signals, scores and tags the day, and logs the result to the Mood Tracker Google Sheet. The model looks for patterns in language (excitement, frustration, energy, fatigue) rather than relying on me to manually rate my day. Most days I do not think about it; the data just accumulates.

Two weekly validator jobs run on Sunday to check both sheets for corrupted rows (malformed date fields, missing data). If something is wrong, I get an alert. If everything is clean, the validators stay silent.

The Blog Cover Generator

Every blog post I publish needs a cover image. Before this tool, that meant opening Figma, setting up the canvas, finding the right background, placing the character art, arranging logos, sizing and positioning the title text, exporting, and uploading. Even with a template that took 20 to 30 minutes and a lot of "that font looks a bit off" iteration.

I built a Python image generation tool using Pillow that turns a single command into a finished 1600x840 PNG.

The Asset System

All visual assets live in a local folder and are synced from Google Drive:

  • Backgrounds: a library of background images in various styles and colors

  • Character: illustrations of the blog mascot character in different expressions (Happy, Surprised, Thinking, etc.)

  • Logos: technology brand logos used when a post covers a specific tool (OpenClaw, GitHub, Python, etc.)

  • Fonts: a fixed set of typefaces used across all cover variants

The sync command pulls the latest assets from Drive in one step. This means if a new background or character expression gets added to the Drive folder, it becomes available immediately.

Four Templates

The generator ships four layout templates, each with a defined set of rules for where text, character art, and logos are placed:

Left_visual puts the character on the left side of the canvas and the title text on the right. This is the primary template for most posts: the character grounds the visual on the left, and the title has clear space to breathe on the right. Logos sit in the lower left corner below the character.

Right_visual mirrors that layout. The character moves to the right and the title text anchors on the left. Useful when the character's pose naturally faces left, or when a different visual balance is needed.

Center_focus places the character or a main graphic element at the center of the canvas, with the title split across a top and bottom position. This works well for series covers or thematic posts where the central image is the main subject.

Series_cover is a full-width centered title with the character in the lower left and the logo in the lower right. The title spans most of the canvas width at a larger size, making it readable as a thumbnail.

The Generation Command

A typical cover is generated with something like:

cd ~/.openclaw/workspace/blog-image-tool
python3 generate.py \
  --slug "second-brain-openclaw-part-8" \
  --template Left_visual \
  --bg "purple-gradient.png" \
  --title-line1 "BUILDING A SECOND BRAIN" \
  --title-line2 "WITH OPENCLAW (PART 8)" \
  --char "Happy.png" \
  --logos "openclaw-logo.png" \
  --overlay 100 \
  --title-size 72

Parameters:

  • --slug names the output file

  • --template selects the layout

  • --bg picks the background from the assets folder

  • --title-line1 and --title-line2 set the two-line title (the generator word-wraps automatically if a line is too wide)

  • --char selects the character expression

  • --logos accepts a comma-separated list of up to two logo filenames

  • --overlay adds a dark overlay on top of the background (0 = none, 255 = fully black); useful when the background is bright and the text needs contrast

  • --title-size sets the font size in points

  • --preview generates the image locally without uploading, useful when iterating on layouts

  • --hashnode-draft-id sets the cover directly on a Hashnode draft in the same command

The output lands in output/drafts/ named after the slug.

The Review and Approval Workflow

After an earlier version of this tool, I identified a few friction points and refactored the script with these improvements:

Asset caching. Fonts and images used to reload from disk on every call. A simple in-memory cache now means repeated or batched generations are meaningfully faster.

Unified character rendering. The original code had two near-identical branches for placing the character illustration depending on the template. These are now handled by a single paste_character() function, making it easier to add new templates without duplicating logic.

Preview mode. Adding --preview skips the upload entirely and just outputs the local PNG. This makes visual iteration fast: you can tweak the background, font size, or overlay value and see the result immediately without touching Cloudinary.

Direct Cloudinary publish. The old workflow had a manual middle step: generate the image, then separately run an upload script, then call the Hashnode API. The script now handles all three in sequence by default. Running the generation command uploads the result to blog-covers/<slug> in Cloudinary automatically.

One-command draft cover. Passing --hashnode-draft-id wires the whole pipeline together: generate, upload to Cloudinary, set as the cover on the specified draft. What previously required three separate steps is now:

python3 generate.py --slug "my-post" --template Left_visual --bg "..." \
  --title-line1 "My Title" --hashnode-draft-id "<draft_id>"

One command, one visual review, done. What used to be a 30-minute Figma session followed by manual uploading is now under a minute end to end.

Why Pillow Instead of Figma / Canva

The short answer is reproducibility and integration. Figma and Canva produce visually polished results, but they are interactive tools. They require opening a browser, navigating a UI, and manually placing elements each time. There is no way to script them from a cron job or invoke them from a chat message.

With a Python script, the cover generation is a shell command. That means the agent can run it, the output path is predictable, and the whole workflow (generate, review, upload, attach to draft) can be wired together as a sequence of automated steps.

The tradeoff is that the output has a more constrained visual range. You are not going to produce bespoke illustration-quality covers this way. But for a consistent blog identity with recognisable templates, character art, and typography, it is more than sufficient, and it is actually faster in practice than designing each one by hand.

What These 3 Systems Have in Common

Looking at these three side by side, they share a structural pattern: each one replaces a category of overhead that used to require deliberate effort with a system that runs on its own and produces reliable output.

Language learning used to require me to remember to open an app and actually do a session. Now it arrives in Telegram at 9 AM and adapts to my current performance level. The effort required from me is answering the quiz and reading the passage.

Weekly reflection used to require me to remember Sunday evening, mentally reconstruct the week, and push through the vagueness of having no actual data. Now a structured review arrives automatically with real numbers from calendar, tasks, mood, and workout data. The effort required from me is reading it and deciding what to act on.

Blog cover creation used to require 30 minutes per post in a design tool. Now it requires one command, a 30-second visual review, and one more command to publish. The creative judgment still lives with me. The mechanical production does not.

The pattern is: keep the part that requires judgment, automate the part that just requires time and consistency.

What Is Next

Part 9 will cover the collab evaluator in more depth (the ragTech email scoring system) and the social scheduling pipeline that takes a video file from Google Drive to Buffer-scheduled posts across LinkedIn, TikTok, and Instagram in a single command.

The tools keep compounding. Each new workflow makes the next one cheaper to build and faster to ship.

Conclusion

Three systems, three categories of personal overhead automated: adaptive Japanese learning with a custom SRS engine backed by Google Sheets, a weekly life review that synthesizes calendar time, Todoist tasks, mood, workout, and YouTube data into an actionable Sunday report, and a blog cover generator that replaces a 30-minute Figma session with a single CLI command and a visual review.

The common thread across all of them: keep human judgment in the loop, automate the parts that just require consistency and time.

Thanks for reading! I am curious to know your own personal thoughts and experiences on this topic! Feel free to connect, send me an email (my inbox is always open) or let me know in the comments! Cheers!

Let's Connect!

Share:

More Articles