Jason Belnick

Writing · · 6 min read

My timesheet drafts itself, and I still approve every line

An agent I built in monday.com drafts my workday from my calendar, meetings, and email. Nothing reaches the real time log until I approve it, and every uncertain row says why.

Like most consultants, I log my time at the end of every workday. Done by hand, that means opening the calendar, rebuilding the day from memory, guessing which project each call belonged to, and padding the gaps until the total looks right. It is easy to do badly and tedious to do well.

Now an agent writes the first draft. Each weekday it reads my calendar, the video meetings I hosted, and my email, then builds a draft of my day on a staging board in monday.com. Each entry is labeled, linked to a project where one applies, and the day adds up to eight hours. I read it, fix what it flagged, and approve. The goal at the top of its instructions is a draft I can approve without edits. When it cannot get there, it says so on the row.

The draft and the record live in different places

The most important decision had nothing to do with the model. The agent writes only to a staging board. The live time board, which is the actual record, is somewhere it cannot write on its own.

A separate job moves entries across. It copies a staging entry to the live board only after I have marked it approved, and only after its own checks pass. If anything about the entry is uncertain, it does not transfer it. It marks the entry blocked and writes the reason. It can add new entries to the live board, but it cannot edit, move, or delete one that is already there. The approval column belongs to me, and no job is allowed to set it.

That split is what makes the agent safe to trust. A bad draft costs me a correction. It cannot become a bad record.

It reads and never acts

The agent has read-only access to my calendar, email, and video meetings. It never sends, drafts, accepts, declines, or deletes anything in them. Each run sends exactly two messages. One is a summary posted on a run-log item on the board. The other is a direct message to me. It never messages anyone else and never posts in a channel.

Narrow permissions are easy to agree on and easy to forget. I wrote them into the agent's role, so every job it runs inherits them.

Written as steps

The instructions read like a runbook. Load the current state of the board and my project lists. Read today's calendar. Add any video meeting I hosted that never made it onto the calendar. Classify each entry and link it to a project. Resolve overlaps. Fill the remaining gaps from email, and only for calls that happened today. Reconcile with what is already on the board. Top the day up to eight hours. Check the result against worked examples. Post the summary.

Each step spells out the cases that go wrong. The rule above all of them is that the agent never invents a meeting, a client, a project, or an hour. If it cannot place something, it still drafts the entry, marks it Low, and explains why in the notes.

Confidence is a column

Every row carries a confidence of High, Medium, or Low. Every row also has a short note that says where the entry came from, what times it covers, and why it got its label.

The most honest rule in the agent is about attendance. None of its tools can prove I attended a meeting. The calendar tools usually do not return my response to an invite, and the video tool only lists meetings I hosted. So the agent never marks a meeting High, and every meeting note says "Attendance unconfirmed." It skips an invite I declined or never answered, and drafts a tentative one as tentative. It does not claim to know more than its sources show, and it tells me where the gap is.

Every minute counts once

Calendars overlap. A meeting lands in the middle of a focus block. Two syncs get booked into the same half hour. A client call runs into an internal one. A timesheet that just adds up the calendar counts all of that twice.

The agent resolves each overlap so that every minute lands on exactly one row. Client meetings outrank internal meetings. Internal meetings outrank admin sessions like trainings and onboarding. Any real meeting outranks my own solo blocks, and solo blocks outrank tentative invites. A row that loses minutes gets a note naming the exact span it gave up and the meeting that took it.

When two real meetings share a slot and nothing tells the agent which one I joined, it does not guess. It splits the time evenly, marks both Low, and adds "Same slot; pick one." If trimming leaves an entry with less than fifteen minutes, that entry gets no row, and the summary lists it so I can see what was dropped.

The gaps stay visible

Once the meetings are placed, most days still have time nobody accounted for. The agent fills the rest of the eight hours with placeholder rows named "Unassigned time," at most three hours each, all marked Low. The day adds up. The unknown part of the day stays labeled as unknown instead of being spread across meetings to make the total work.

Safe to run twice

The agent can run more than once a day, and I may edit the board between runs, so a rerun has to be harmless. Every entry carries the ID of the calendar event, meeting, or email it came from. The agent never creates a second entry for an ID it has already seen, including entries I archived or rejected. It never overwrites a draft. If the calendar changed after it drafted an entry, it adds a note saying what the entry would be now, lowers the confidence, and leaves the decision to me. A draft I rejected never comes back.

It also knows the difference between an empty result and a failure. When I started the job, I had no client assignments yet, so my project lists came back empty. The agent treats "read the board, found nothing assigned to me" as normal. It treats "could not read the board" as a failure, retries once, and only then lowers confidence. Even then it lowers it only on entries that could have needed a project from that board.

It checks its work before it writes

Before writing anything, the agent checks every draft against a separate set of worked examples and checks. A draft that disagrees with an example of the same kind, or fails a check, gets fixed and checked again. It also confirms that no minute is counted on two rows and that every trimmed row explains its cut.

When an older example conflicts with the current rules, the rules win. The job says so in plain words, so the model never has to decide which one to follow.

Tested on fake days first

I try changes with A/B test jobs. A test job writes only to its own test board and never to the real staging board. Two more boards hold fictional stress-test data, and the agent is told never to treat them as mine or even read them unless a job names them.

Sunday night

Another small job runs on Sunday evening. It marks every entry from the week for archive, and a board automation moves them out, so staging starts each Monday empty. It changes one column and nothing else.

What changed for me

My end-of-day time entry is now a review. One message tells me the day's total, lists each entry with its hours, type, and confidence, and names anything it flagged. I fix the Low rows, approve the rest, and the transfer job writes the record.

None of this is specific to timesheets. It is the shape I push for whenever a team wants an agent to touch a system of record. The agent drafts. A person approves. A separate step with its own checks writes the record. Every uncertain output carries its reason. Here it is applied to one person's day.