Skip to content

Remove tools.allow_tasks and the model task tools - #162

Closed
gregjkal wants to merge 6 commits into
cppalliance:masterfrom
gregjkal:remove-allow-tasks
Closed

gregjkal wants to merge 6 commits into
cppalliance:masterfrom
gregjkal:remove-allow-tasks

Conversation

@gregjkal

Copy link
Copy Markdown
Contributor

Removes tools.allow_tasks and the four task tools it let a section give the model: task, task_cancel, task_status, and await_tasks. Those tools copied the Lua tasks API, which is the wrong shape for a model. Lua tasks return futures that are awaited and can outlive their section; a model's work never outlives its section. The right model-facing shape also differs per model. The model will get a subagents Plugin instead, designed separately.

The Lua tasks object keeps its functions and behavior.

What changes

  • tools.allow_tasks is gone. A section that calls it fails with Lua's usual error for calling a nil field.
  • No model round offers a task tool, and the four names are no longer reserved.
  • Everything that existed only for tasks the model started is removed:
    • the scheduler's built-in tool route
    • parking a model turn on await_tasks
    • the queue of task notices for the model
    • TaskOrigin
  • Public facade: TaskOrigin, Event::TaskNotice, Emitter::task_notice, and the origin field of Event::TaskStarted are removed, and public-api.txt is regenerated.
  • tasks.status tables no longer have an origin key.
  • tasks.pending takes no argument. Any argument, in any position, raises tasks.pending takes no arguments, got <type>.
  • Old run logs still load. Event ignores unknown fields, so an old origin is skipped. Workshop's typed event readers already skip an entry that fails to decode, so old task_notice entries are skipped.

The plan is vibe/2026-10-20-1-remove-allow-tasks.md.

Tests

  • Deleted the tests that existed only for the model task tools. Tests that used tools.allow_tasks only as setup keep their other assertions.
  • New tests:
    • The tools table holds exactly its own functions.
    • A model round offers only the section's tools.
    • tasks.pending refuses any argument.
    • tasks.status has no origin key.
    • An old task_started log line that carries origin still loads.
  • New tests that keep coverage the deleted tests had: a running task abandoned with OwnerReturned, OwnerFailed, or ToolLoopExhausted when its owner ends, and the AbandonReason::why() text.

Verification (local, macOS)

Passing:

  • cargo nextest run for the workspace and for the workshop crates
  • both clippy runs
  • cargo fmt --check
  • workspace, runvil, harness, and private-items Engine docs with -D warnings
  • xtask api --check
  • cargo test -p build-xtask
  • cargo check -p gateway --no-default-features
  • Workshop UI typecheck and build

These also fail on master at fc4a1c0 locally, so this branch does not cause them:

  • Workspace clippy: large_enum_variant at crates/gateway/app/src/tray/macos.rs:88 (clippy 0.1.99).
  • workshop-server test stalled_browser_cleanup_is_bounded_after_gateway_disconnect.
  • Nine Workshop UI test files on macOS.
  • cargo deny check advisories RUSTSEC-2026-0382 (rustls-webpki) and RUSTSEC-2026-0383 (webpki-root-certs). Cargo.lock is unchanged by this branch.

Not in this PR

The guide in runvil-docs still documents tools.allow_tasks and task origin, and needs its own edit.

Remove the section opt-in that offered the model four task tools copied from the Lua task functions, because a model needs a different shape than futures that can outlive their section. The scheduler loses every path that served only those tools: the built-in tool route, parking a model call until one of its tasks ends, and the queue of task-end notices pushed into the next model round. The four tool names are no longer reserved, so a catalog tool can take one. The Lua task functions keep their behavior, and the task origin type stays.

- `crates/runvil-internal/engine/src/execute/scheduler/builtins.rs` is deleted with `builtins-schemas.rs`, `builtins-tests.rs`, `await_tasks.rs`, and `notices.rs`. `ToolRoute` loses `Builtin`, `DispatchTarget` loses `Builtin`, and `ToolCallDispatch` keeps only `Issued` and `Answered`.
- `Chain` drops `awaiting` and `task_notices`, so `wake_waiter` always delivers the member as a `JoinAny` answer and now returns nothing.
- `cancel_task` returns `Result<()>` and becomes private to `waits.rs`, and `prepare_timer` becomes private to `timer.rs`.
- `task_lifecycle` and `terminals_per_started_task` move unchanged into `fixtures.rs` for the fanout and run-termination suites.
- `offer_refusal` no longer refuses the names `task`, `task_cancel`, `task_status`, and `await_tasks`; `tool_route` sends them to a local tool or the catalog like any other name.
- `prepare_chat` no longer pushes notices onto the list, so an empty list is refused before any round, as `an_empty_list_raises_before_any_round` pins.
- `dispatch_cancel` no longer queues a notice when the author cancels a task.
- `drop_timer` wakes its waiter with the interrupted `JoinAny` error in every case.
- `a_section_tools_table_holds_only_its_api_and_its_round_advertises_only_the_offered_tool` pins the exact keys of a section's `tools` table and a `models.loop` round that advertises only `tools_echo`.
- `unofferable_tool` becomes `hidden/shape` with a scalar schema, since a wire name no longer makes a tool unofferable.
- `abandon_owned_tasks` still leaves `TaskOrigin::Model` tasks out of the leaked ids.

Design: removes surface-growth @ crates/runvil-internal/lua/src/tools.rs::install_allow_tasks
  boundary: pub
Design: removes value-object @ crates/runvil-internal/lua/src/scope.rs::TaskAllowlist
Design: removes surface-growth @ crates/runvil-internal/engine/src/execute/scheduler/builtins-schemas.rs::advertise_task_builtins deps: BTreeMap,TaskAllowlist,Vec
  boundary: wire
Design: removes dispatch-on-tag @ crates/runvil-internal/engine/src/execute/scheduler/builtins.rs::answer_task_builtin
Design: replaces pure-function @ crates/runvil-internal/engine/src/execute/tests/fixtures.rs::task_lifecycle deps: Observation,String
  was: crates/runvil-internal/engine/src/execute/tests/model_task_acceptance.rs::task_lifecycle
Deferred: An Engine test that calls `tools.allow_tasks()` and pins Lua's nil-field call error.
Plan: vibe/2026-10-20-1-remove-allow-tasks.md
The model can no longer start tasks, so every task is the author's and the record of who started a task goes away. Task start events, task status, and spawn requests stop carrying an origin, and every task left running when its owner ends now counts as leaked. The pending-tasks call loses its filter: any argument is the call's error naming its type, and the protocol parser owns that check rather than the Lua wrapper. The task notice event and its emitter are removed because nothing produces them any more. Logs written before this change still load, since the extra origin key on a task start entry is ignored.

- `TaskOrigin` and `ParseTaskOriginError` are deleted from the types crate and the facade's `ids` module; `public-api.txt` drops them along with `Event::TaskStarted::origin` and every `Event::TaskNotice` field.
- `Request::Pending` becomes a unit variant. `tasks_pending` passes its argument through as `argument`, and `parse_pending` turns any non-nil value into the call's error, so the Lua shim holds no check of its own.
- `prepare_spawn` and `dispatch_spawn` lose their `origin` parameter and with it their `clippy::too_many_arguments` expectations; `live_tasks_of` and `dispatch_pending` lose their origin filter.
- `on_task_notice` and `TaskNoticeReport` leave the recording test support together with the event they forwarded.
- `abandon_owned_tasks` counts every abandoned live task as leaked, so any task left live turns its owner's success into `tasks_live`. The `Error::TasksLive` message now reads "chain ended with tasks still live", without "author".
- `parse_pending` refuses an argument with exactly `tasks.pending takes no arguments, got <type>`; tests cover `{}`, `{ origin = 'author' }`, and a string at the parser, and both tables through `pcall` in a run.
- `task_status_table` no longer sets an `origin` key, and `status_reports_a_parked_task_and_then_a_finished_one` pins the full key list of both status tables.
- `shim_origin` is gone, so `parse_spawn` no longer reads or requires an `origin` key on a spawn yield.
- `a_task_started_line_carrying_an_origin_still_reads_back` pins that an older `task_started` line carrying `"origin":"author"` still deserializes.

Design: removes value-object @ crates/runvil-internal/types/src/ids.rs::TaskOrigin boundary: pub
Design: removes value-object @ crates/runvil-internal/types/src/ids.rs::ParseTaskOriginError boundary: pub
Plan: vibe/2026-10-20-1-remove-allow-tasks.md
Plan: vibe/2026-10-20-1-remove-allow-tasks.md
Removing the model task tests left no run-level test of why a running task is abandoned when its owner ends, so three chain-end tests and a phrase test now pin each reason. The pending shim now refuses an argument in any position, and helpers that only the removed notice code needed are narrowed.

- `run_abandoning_a_running_task` holds a child on a model round while its owner returns, fails, or exhausts its tool loop, and asserts the child's observations are exactly `TaskStarted` then `TaskAbandoned` with `OwnerReturned`, `OwnerFailed`, or `ToolLoopExhausted`.
- `every_abandon_reason_renders_its_own_phrase` pins the `why()` text of all five `AbandonReason` variants.
- `tasks_pending` scans every argument and passes the first non-nil one to the parser, so `tasks.pending(nil, {})` raises the same error as `tasks.pending({})`.
- `MessageList::push` is `pub(crate)`, and `live_tasks_of` and `task_status` are private to `waits.rs`.

Plan: vibe/2026-10-20-1-remove-allow-tasks.md
Only a local Lua tool's answer is now settled on the driver thread; a preparation failure returns Err and is answered by the caller's error arm.

Plan: vibe/2026-10-20-1-remove-allow-tasks.md
@gregjkal
gregjkal requested a review from vinniefalco October 20, 2026 23:51
@gregjkal
gregjkal marked this pull request as ready for review October 21, 2026 00:19
The tasks.pending shim raises on any non-nil argument itself, like its
sibling shims, so the pending request carries no fields and the parse
reads none. complete_task stores the outcome through the slot it
already holds. The emitter doc drops the removed task built-in, and the
doc comments the removal rewrote are rewrapped.
@gregjkal
gregjkal deleted the remove-allow-tasks branch October 22, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant