I Gave My To-Do List a Voice: Building Tala
I have spent twenty-odd years in broadcast, where the rule is simple: if it isn’t logged, it didn’t happen. And yet my own to-do list lived in three places — a Telegram chat, a Google Tasks list, and my head. The head, predictably, was the least reliable of the three.
So I built Tala. Tala is Filipino for “star,” and also the root of talaan — a record, a list. One place that holds everything I could forget: my own tasks, what my staff owe me, projects, deadlines, and the things sitting in the queues of the other systems I run. This post is about the part I use most: talking to it.
Voice first, because typing is where tasks go to die
The best moment to capture a task is the moment you think of it — usually while walking, or driving, or standing in a server room with greasy hands. So the rule is: send the Telegram bot a voice note. That’s it.
Here is what happens to that voice note:
- The bot downloads the audio from Telegram.
- A local Whisper service on my homelab transcribes it. Nothing leaves the building for that step, which matters when half of what I dictate mentions payroll or a vendor.
- The bot echoes the transcript back (“🎙 …”) so I can see what it heard.
- Claude reads the transcript and decides whether it is a task or just a thought. One ramble can hold several to-dos, so it pulls out each one, with a due date resolved against today (“Wednesday” and “next week” become real dates).
- If I say “ask so-and-so to send the invoices,” the task is assigned to that staff member by name. The name matching is deliberately dull: whole words only, accent-insensitive, and if two people could match, it refuses to guess.
Tala’s own database is the source of truth. My own tasks are mirrored one-way into Google Tasks every couple of minutes, so my phone still shows them wherever I already look. Staff never log in and never get messaged; Tala is only for me.
The first thing that went wrong: Whisper is not a stenographer
Within days I hit the obvious problem. Transcription is good, not perfect. A name comes out wrong, a date lands on the wrong day, and now the wrong task sits in the system looking confident. I could open the web app and fix it — but that defeats the whole point of keeping my hands out of it.
So I added the command I actually wanted. I say:
“Please edit the last task — make it due Friday.”
And it does. “Change it to call the electric cooperative about the bill.” “Give it to Ezra.” “Tanggalin mo yung due date.” (Yes, Tagalog works too. I test with the language I think in.)
The mechanics are less magical than they sound, and I like that:
- “The last task” means the newest task I sent through the bot. Tasks I created in the web app don’t count, so I can’t accidentally rewrite something I made on purpose.
- The command is matched loosely, because voice transcripts are messy — “please,” “can you,” “edit,” “fix,” “change,” “replace” all work. A spoken edit never turns into a brand-new task.
- Claude is asked to return only the fields that must change — title, due date, assignee, notes — never the whole task. If what I said isn’t an edit at all (I tested it with “what is the weather”), it says so and changes nothing.
- The bot replies with a before-and-after, so I can see exactly what changed.
- If I only say “edit the last task” with nothing after it, the bot asks what to change and treats my next message — typed or voice, within ten minutes — as the instruction.
Assigning by spoken name needed one small change on Tala’s side: the update endpoint now accepts a name, runs it through the same strict matcher, and answers with a plain error (“could be A or B”, or “no staff member matches”) rather than quietly picking someone.
A mistake I would have shipped
When I tested the interpretation, my first attempt ran the model the wrong way: claude isn’t on the PATH over a plain SSH session, so every test failed instantly and my code cheerfully reported “couldn’t work out what to change.” The failure message was working perfectly; the test was meaningless. Production reaches Claude through a restricted wrapper script that feeds the prompt on stdin, so I reran the six sample instructions that exact way. Five came back right, including the Tagalog one; one hit a 120-second timeout. In the bot, a timeout lands on that same polite “couldn’t work out what to change” — which, after the earlier false alarm, I trust a little more than I did.
Then I let it read my list
Tala also has an Ask page: a question box backed by my saved notes and reports. It had one blind spot — it couldn’t see Tala’s own tasks. So I gave it a compact snapshot of the live list: what’s open, what’s overdue, who has it, how long since the last update, and what was finished in the last two weeks. Now I can ask, “what’s overdue with Ezra?” and get an answer from the current list, not from memory.
The first real question I asked was “what is overdue right now, and with whom?” It answered that nothing was overdue out of fifteen open tasks — and, unprompted, noted that older diary entries showed a long overdue list but were stale, so the live list should win. That is exactly the behaviour I wanted, and I had told it to do that in the prompt rather than hoping.
If Tala is ever down, Ask still answers from memory and says plainly that the live task list is unavailable. A tool that fails loudly beats one that quietly answers from stale data.
What I took from it
None of this is clever AI. It is a chain of small, boring, checkable steps — transcribe, show what you heard, extract, match names strictly, change only what was asked, and report back what changed. The same instincts I learned in a control room: confirm what you received, make the safe action the default, and never let the system guess in silence.
Next up is a record button in the web app, with a confirm step before anything is created — because a mishear should never silently become a task.