Task board — hand it to the AI, dates and reporting¶
Part of task-board. The AI hand-off pipeline (proposal, per-item review, apply), calendars and reminders, and record and reporting. Text moved verbatim from task-board; references such as "above" and "below" point into that note or its sibling files.
Hand it to the AI¶
The interesting one, and the one to go slowest on.
assignee = 'ai' queues the task. A worker picks it up and runs a Claude Agent SDK session
with a tool set chosen by task class — not a general agent with a shell. It works
against the sandbox, produces a proposal, and a person confirms before anything
reaches production. Same draft-then-confirm contract as the rest of the assistant; this
feature does not get an exemption from it.
Classes worth doing first, because the tools already exist:
- parts with no description → drafts them, shows a diff
- SOP front-matter stale or version out of step with its history → drafts the edit
- PO line with no price → looks the price up and drafts it
Classes it must refuse, out loud, with the reason:
- parts with no image — somebody has to take a photograph
- anything not in the class table. An unrecognised task is not an invitation to improvise.
The proposal is a document, not a chat message¶
An AI run does not end in a dialogue box. It ends in a proposal: a durable, dated artifact a person can read away from the screen that produced it.
- A report file, written to the proposal store and downloadable from the task: a self-contained HTML page (opens offline, no styling to fetch) plus a CSV of the same rows for anyone who would rather read it in a spreadsheet. Retained after the decision — the report is the evidence of what was proposed, separate from what was applied.
- An email via Resend to the task's assignee and to Administration: what the task was, how many items, the headline counts, and a link back to the review page. The summary and the link, never the full contents, and never anything a mail server should not hold.
- The sandbox result is shown, not claimed. The report carries the outcome of the sandbox run — what it changed there and whether it succeeded — so "it worked in the sandbox" is something the reviewer can see rather than take on trust.
Every item gets its own decision¶
The thing to defend against is a person clicking one big green button on forty-three changes they did not read. So there is no big green button.
- One row per proposed change, showing the current value, the proposed value, and the AI's one-line reason. A row is undecided until a person makes it Accept, Reject or Edit.
- Edit is first-class. The common case is "nearly right", and forcing a reject-and-rerun for a wording change is what teaches people to just accept everything.
- No select-all, no approve-all. A running counter reads "12 of 43 reviewed", and the Implement button stays disabled until that reaches all of them. Long batches paginate, so there is no single scroll in which forty rows can be waved through.
- The decision is attributed. Each item records who decided it and when, as its own
task_eventsrow. Not one approval for the batch — one per item, because that is the claim being made. - Reject needs no reason; accept carries the weight. The reviewer owns what they accepted. That belongs in SOP-017 in those words.
Applying, and what happens when the world moved¶
- A proposal expires (seven days), because a stale proposal reviewed carefully is still a wrong change.
- Each row remembers the value the AI actually read. At apply time, if the live record no longer matches, that row fails loudly and is skipped — never overwritten. Somebody edited it in the meantime, and their edit wins.
- Items apply one at a time through the same allow-listed actions the assistant already uses. One failure does not abandon the rest.
- The run produces an outcome report — applied, failed, skipped, with reasons — stored next to the proposal and emailed the same way. The proposal says what was intended; the outcome says what happened; both are kept.
- Only then may a person close the task. The AI still never closes it.
Hard limits: never delete, never touch a person's account, never write to production, and
never close its own task. It does the work; a human marks it done. Otherwise the board
is a machine grading its own homework. Every run writes task_events rows, a transcript
and its dollar cost, against the existing monthly cap.
Dates, calendars and reminders¶
The server still cannot write to Google Calendar — Workspace blocks service-account keys (task #113, meetings-and-tasks). Two things that work without a key:
- A per-person iCal subscription feed Google can poll by URL. It has to be reachable without the Access gate, so it is an unguessable secret path, revocable from admin, and it carries titles and dates only — never the detail. Refresh is on Google's schedule, which is hours, so it is a calendar view, not a reminder.
- A morning digest email (Resend, already planned): what is due today, what is overdue, per person. This is the reminder that actually works.
Record and reporting¶
task_events is the record. tasks-export keeps writing ~/Notes/tasks.md so a plain-text
copy survives the server. Grafana Quality folder gets: opened vs closed per week by
department, median days to close, overdue count, SOP findings open vs closed over time —
which is exactly the real data SOP-016 Quality Objectives is currently missing. The
quarterly pack reads the same numbers.