Something a model could not do, and so wrote a note about instead

Asked to remind somebody every Monday, a model looked down its tool list, found
notes_create described as "something worth having in a later conversation" and
memory_add beginning with the word Remember, wrote a note, and reported that it
had scheduled something. Every screen agreed with it. There was no scheduling
tool at all -- the near-misses were the only thing there was to reach for, and
nothing anywhere said the thing it was being asked for existed.

The seam had been left open on purpose: Schedule.origin has defined
ORIGIN_MODEL, with no writer, since scheduling shipped, and services/schedules.py
says in its first line that it holds what the routes *and the tools* both need.
This is the tool that was meant to go through it.

Four of them, and a thin layer: rule.validate is still the one total normaliser
the form and the compile share, schedules.create still writes the row and the
task chat together, and rule.describe still says what came out. A second dialect
for models would mean two definitions of "every other Tuesday" and one of them
going quietly wrong.

The result is that description, never "done". A schedule is invisible until it
fires, which may be days away, so the sentence in the reply is the only moment
anybody can check that Monday was read as Monday -- and the tool says so, in the
text the model reads back. The list badges the ones nobody typed.

Gated on schedule.use rather than a permission of its own: somebody who may set
one up by hand may say so to a model instead, and a second checkbox beside the
first would only ever be answered "the same as that one".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-06 10:28:05 +02:00
parent 14b1428f9f
commit feca8861a1
10 changed files with 955 additions and 12 deletions
+45
View File
@@ -139,3 +139,48 @@ and hiding it means it can never be found again, let alone filed into. And with
agent chats switched off there is no switch and no filtering at all, rather than
one side of a fork nobody can move: an administrator turning the feature off
would otherwise strand whoever last left it on Agents in an empty sidebar.
## A model can schedule, and could not before
**There was no scheduling tool, and that was the whole failure.** Asked to
"remind me every Monday at noon", a model looked down its list, found
`notes_create` described as *"something worth having in a later conversation"*
and `memory_add` beginning with the word *Remember*, wrote a note, and said it
had scheduled something. Every screen agreed with it. No amount of prompting
fixes that: the near-misses were the only thing there was to reach for, and
nothing anywhere said scheduling existed.
The seam had been left open. `Schedule.origin` has defined `ORIGIN_MODEL` since
the feature shipped with **no writer**, and `services/schedules.py` says in its
first line that it holds "what the routes *and the tools* both need".
`services/schedule/tool.py` is what was meant to go through it.
**One vocabulary, not a second one.** The four tools are a thin layer over what
the form already uses: `rule.validate` is the single total normaliser — the
manual form, the compile step and the tool all hand it the same raw shape —
`schedules.create` writes the row and the task chat together, and
`rule.describe` says what came out in words. A separate dialect for models would
mean two definitions of "every other Tuesday" and one of them going quietly
wrong. The `tool.schedule` fragment is deliberately worded from
`task.schedule_compile`, which has been turning people's words into this same
JSON since the feature shipped.
**The tool answers with `rule.describe`, never "done".** A schedule is invisible
until it fires, which may be days away, so the sentence in the reply is the only
moment anybody can check that Monday was understood as Monday. The tool hands
the description over and says, in the result text, to quote it. `ORIGIN_MODEL`
goes on the row for the matching reason: the Scheduled list badges the ones
nobody typed, because otherwise a model's decision and the reader's own are the
same row.
**Gated on `schedule.use`, not on a `tools.schedule` of its own.** A reader who
may set a schedule up by hand may say so to a model instead, and a second
permission beside the first would only ever be answered "the same as that one".
The instance switch is passed into `_family_allowed` the way `images` is, so an
instance with scheduling off offers nothing — a model handed a tool that cannot
work spends a round finding out, which in a one-round reply is the whole reply.
**`tool.notes` and `tool.memory` both say what they are not for.** They are what
the model actually reached for, so each ends with the line that redirects:
anything that should *happen* at a time is a schedule, and remembering that
something should happen does not make it happen.