feat(todo): make the parallel in_progress policy configurable

Whether concurrent active tasks are legitimate depends on runtime
concurrency the tool cannot observe, but whether a deployment's agents
ever fan out is knowable at composition time. `allowParallelInProgress`
(default true) therefore replaces the hardcoded policy: the flag moves
the model-facing instruction and the accepted input together, so a
deployment running strictly sequential agents can restore the
single-active discipline from cordis.yml.

The durable-log invariant does not follow the flag. A log written while
parallel work was allowed must still replay after a deployment tightens
the policy, so the invariant stays silent on the active count.
This commit is contained in:
Chinesezjc
2026-07-29 14:49:45 +08:00
parent 39d65df019
commit 3a61c4c568
13 changed files with 339 additions and 45 deletions
+8 -2
View File
@@ -14,9 +14,15 @@ Registers one tool, `todo_write(todos: [{ content, status }])`, on `ctx.tools`.
The list belongs to the ONE agent session that called the tool. There is no subagent/shared/swarm scope: a non-agent caller (no `exec.agent`) has nowhere to write the list and is rejected. This is a deliberate scope limit — see the Agent Note.
## Configuration
`allowParallelInProgress` (default `true`) decides whether several todos may be `in_progress` at once. It is a deployment choice, not a fixed rule: whether concurrent active tasks are legitimate depends on runtime concurrency the tool cannot observe, so a deployment whose agents never fan out can restore the single-active discipline from cordis.yml.
The flag moves the model-facing instruction and the accepted input together — `true` asks the model to mark every actively worked task and accepts any number, `false` asks for exactly one and rejects a call marking more with `Error: invalid todos: at most one task may be in_progress (got <n>)`. The durable-log invariant does NOT follow it: a log written while parallel work was allowed must still replay after a deployment tightens the policy, so the invariant stays silent on the active count.
## Validation
Beyond the schema's type/required/enum checks, `execute` rejects an empty or duplicate `content`, and any item key beyond `content`/`status` — an extended item shape (ids, nesting) fails loud instead of silently flattening, keeping the logged snapshot equal to what the model believes it wrote. Any number of tasks may be `in_progress` at once — parallel work (concurrent subagents, background commands) legitimately runs several tasks simultaneously. Ordering and the discipline of keeping the list current are left to the model via the tool description.
Beyond the schema's type/required/enum checks, `execute` rejects an empty or duplicate `content`, and any item key beyond `content`/`status` — an extended item shape (ids, nesting) fails loud instead of silently flattening, keeping the logged snapshot equal to what the model believes it wrote. How many tasks may be `in_progress` at once is the deployment's call (§ Configuration): the default allows several, because parallel work (concurrent subagents, background commands) legitimately runs several tasks simultaneously. Ordering and the discipline of keeping the list current are left to the model via the tool description.
## Rendering
@@ -50,7 +56,7 @@ Prefix-stable while the definition and visibility are unchanged. Plugin lifecycl
#### What the model sees
Each assistant tool call retains the entire replacement list in its arguments. Success returns exactly `Updated todo list: <pending> pending, <inProgress> in progress, <completed> completed.` Stable failures are ``Error: invalid todo: `content` must be a non-empty string``, `Error: invalid todos: duplicate content "<content>"`, and `Error: todo_write requires an owning agent session`. The full `todo/write` session event is UI and replay state, not a second model message.
Each assistant tool call retains the entire replacement list in its arguments. Success returns exactly `Updated todo list: <pending> pending, <inProgress> in progress, <completed> completed.` Stable failures are ``Error: invalid todo: `content` must be a non-empty string``, `Error: invalid todos: duplicate content "<content>"`, `Error: todo_write requires an owning agent session`, and — only where the deployment set `allowParallelInProgress: false` — `Error: invalid todos: at most one task may be in_progress (got <n>)`. The full `todo/write` session event is UI and replay state, not a second model message.
#### Token effect