Skip to content

feat(todos,goals): task runs, dispatch policy, and goal budget enforcement - #121

Merged
senamakel merged 31 commits into
mainfrom
tinyagents-task-runtime
Aug 21, 2026
Merged

feat(todos,goals): task runs, dispatch policy, and goal budget enforcement#121
senamakel merged 31 commits into
mainfrom
tinyagents-task-runtime

Conversation

@senamakel

@senamakel senamakel commented Aug 21, 2026

Copy link
Copy Markdown
Member

Summary

Ports the host-agnostic half of OpenHuman's task-run, task-dispatcher, and goal-budget logic into the crate. The task board already lives here (graph::todos); what was still host-side was everything that makes a board actually run: who claimed a card, whether that claimant is still alive, which card goes next, and what stops a goal from spending without a ceiling.

Three new surfaces, all provider-neutral and offline-testable:

graph::todos::runs — claim / heartbeat / reclaim

A TaskRun records an attempt at a card: who took it, since when, and how it ended. A worker that dies mid-card would otherwise leave the card InProgress forever, and the board's single-in-progress rule then wedges the whole thread. reclaim_stale closes the silent run and hands the card back — bounded by RunLimits::max_reclaim_count, so a card that keeps killing its workers parks at Blocked instead of cycling through them forever.

  • TaskRun, RunOutcome, RunLimits, ReclaimResult / ReclaimDetail
  • staleness_reason(run, now_ms, limits) — the policy as a pure, clock-injected function. TTL is checked before heartbeat, and an unparsable stamp reads as healthy, so a corrupt record never yanks a card away from a live worker.
  • create_run / update_heartbeat / complete_run / list_runs / get_run / find_stale_runs / count_reclaims_for_card / reclaim_stale / spawn_heartbeat_task / import_if_absent

Addressed (Store, thread_id) exactly like the board, under its own per-thread lock map so a run write never blocks a card write. Namespace graph.todos.runs. The crate emits no events — a host derives its own from ReclaimResult::details.

graph::todos::dispatch — scheduling policy

  • select (pure, no store): pick_next_card (urgency from source_metadata.urgency, ties toward the lower board order, optional agent-assigned-only filter), has_card_in_progress, requires_plan_approval (the card's own mode outranks the global gate — Required parks even when the global default is off, or a plan stamped for review would execute before anyone saw it), and PollCadence::next_delay (idle backoff, monotonic and overflow-free for any u32 streak).
  • prompt: build_task_prompt and build_progress_instruction, with TaskPromptTools naming the tools the text points at so a host with different tool names (or no memory tool) still gets coherent output.
  • registry: ActiveRunRegistry<Context>. Its real job is deciding who cleans up — a run finishing naturally and a cancel arriving at the same moment both want to write the card's terminal state, and take / take_if guarantee exactly one wins. take_if matches and removes under one lock, so a cancel for a superseded request cannot tear down the run that replaced it.

Executing a card is deliberately out of scope: that needs an agent, a model, and a host's tool belt.

graph::goals::budget — accounting and the mid-turn stop

store::account_usage was the raw write; this is the policy around it. account_turn charges a finished turn against the thread's active goal (so a paused/complete/limited goal accrues nothing from incidental chat) and clears the one-shot continuation suppression only on a user-initiated turn — a continuation clearing its own flag would loop. GoalBudgetGuard::check adds the in-flight spend to the accounted total and returns BudgetVerdict::Stop once it reaches the ceiling; checked mid-turn, that bounds a run to a small overshoot instead of discovering the overrun afterwards. The guard captures the goal_id it was armed for, so a goal replaced mid-turn quietly disarms it. Neither aborts anything — wiring a stop into a turn stays the host's call.

Tests

  • Unit: src/graph/todos/runs/test.rs (17), src/graph/todos/dispatch/test.rs (26), budget_tests in src/graph/goals/test.rs (10)
  • Feature: tests/feature_graph_task_runs.rs, tests/feature_graph_goal_budget.rs
  • E2E: tests/e2e_graph_task_dispatch.rs — the whole loop assembled from the public surface (pick → approve/park → claim → open run → prompt → MockModel agent driving the real todo tool → complete → write-back), covering cancellation, abandonment/reclaim, and idle backoff. It doubles as the reference assembly for a host building a dispatcher.

Commands run

cargo fmt --all -- --check     # clean
cargo clippy --all-targets -- -D warnings   # clean
cargo test                     # full suite green, 0 failing targets

API changes

Purely additive. New flat re-exports from lib.rs: task_run_store, TaskRun, RunOutcome, RunLimits, ReclaimResult, ReclaimDetail, staleness_reason, ActiveRun, ActiveRunRegistry, PollCadence, TaskPromptTools, build_task_prompt, build_progress_instruction, card_urgency, has_card_in_progress, pick_next_card, requires_plan_approval, BudgetVerdict, GoalBudgetGuard, account_turn, accrues_usage, turn_tokens. graph::goals::budget is a public module so a host can bind its own stop-hook trait to GoalBudgetGuard.

One internal change: graph::todos::types::now_stamp is now built on a new now_millis, so the runs module can evaluate staleness against the same clock the board stamps with.

Docs

src/graph/todos/runs/README.md and src/graph/todos/dispatch/README.md are new; src/graph/todos/README.md, src/graph/goals/README.md, docs/modules/graph/todos.md, and docs/modules/graph/goals.md are updated.

Summary by CodeRabbit

  • New Features

    • Added goal budget tracking and enforcement, including mid-turn checks and automatic stopping when limits are reached.
    • Added autonomous task-run management with claiming, heartbeats, completion tracking, stale-run recovery, and retry limits.
    • Added task dispatching based on urgency, assignment, approval requirements, polling cadence, and active-run tracking.
    • Added structured prompts and progress instructions for autonomous task execution.
  • Documentation

    • Expanded documentation for goal budgets, task dispatch, and task-run lifecycles.
  • Tests

    • Added unit, integration, and end-to-end coverage for budgeting, dispatching, task execution, cancellation, and stale-run recovery.

senamakel and others added 30 commits August 22, 2026 00:30
Introduce a new types module under the graph todos runs path to define the core data structures for representing and managing todo run states. This establishes the foundational types needed to support upcoming run tracking functionality.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The store now creates the runs directory if it does not exist when initializing, preventing a panic when the directory is absent. This ensures the application can start cleanly on first run or after the directory has been deleted.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…string

The `now_stamp` function previously returned a string representation of the current unix time in milliseconds. It now delegates to a new `now_millis` function that returns the raw `u64` value, while `now_stamp` remains as a thin wrapper that converts the millisecond count to a string. This allows callers that need the numeric timestamp to avoid parsing the string representation.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The change adds the `src/graph/todos/runs/mod.rs` file to the repository, which was previously untracked. This ensures the module is included in version control and available for compilation and use.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When selecting files for dispatch, the system now correctly includes untracked files that are listed among the tracked files. Previously, untracked files were silently ignored, causing incomplete selections in workflows that rely on the dispatch mechanism.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When the prompt file is not found, the dispatch module now returns a clear error instead of panicking. This improves robustness in environments where the prompt resource may be absent.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a registry module to manage and look up dispatch handlers for todo items, enabling dynamic handler resolution instead of hardcoded dispatch logic.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add two new public modules on top of the board for hosts that run cards autonomously: `runs` records worker claims and heartbeats, while `dispatch` provides the scheduling policy for card selection, approval, prompting, and cancellation.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When a budget goal is not present in the graph, the traversal now returns an empty result instead of panicking. This ensures graceful handling of incomplete or partially loaded goal configurations.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a new `budget` module that charges a finished turn against the goal and stops an in-flight turn that would exceed its ceiling, providing the graph analogue of OpenHuman's thread-level budget enforcement without app-specific coupling.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Re-export the task-run store, run-log types, and dispatch policy items from the graph and library crates so that external consumers can access the claim/heartbeat/reclaim machinery and the card-selection logic that was previously internal.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new test module for the todo runs functionality to ensure correctness and prevent regressions in the graph component.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test `board_card_type_is_in_scope` was a guard for the `TaskBoardCard` import, but that import is no longer used in the file. Removing both the test and the unused import keeps the codebase clean and avoids dead code.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed the test file for the todos dispatch module as it was no longer needed, containing only untracked content with no functional changes.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a comprehensive test module for the goal budget system covering turn token
accounting, budget enforcement, guard behaviour, and the interaction between
user turns and continuation suppression. The tests verify that only active goals
accrue usage, that crossing the budget flips the goal to budget-limited and
stops further accrual, and that budget guards correctly handle goal replacement,
deletion, and pausing.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new test file for verifying task runs in the feature graph, covering scenarios that were previously untested to improve coverage and catch regressions in task execution ordering.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The heartbeat test now uses tokio's built-in time-pausing attribute instead of manually pausing the timer, and the staleness assertion is moved inside the loop to verify that each tick keeps the run alive. The test also abandons the run before each advance to ensure the heartbeat is the only mechanism preventing staleness, and the cancellation section is tightened to confirm that ticks stop after cancellation.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace direct calls to `tokio::task::yield_now()` with a new `settle()` helper that yields multiple times, giving spawned tasks more opportunities to wake, perform their store writes, and park again. This makes the test more reliable by reducing flakiness from timing-dependent scheduling.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new test file that verifies the heartbeat probe functionality, ensuring periodic connection health checks work correctly under normal conditions.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Rewrite the heartbeat probe test to run with a real tokio runtime instead of paused time, aging the run's heartbeat to a known stale value and then verifying that the heartbeat task updates it within a short real-time window. This makes the test more reliable and easier to reason about by removing the need for manual time advancement and yield loops.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…e version

The heartbeat test previously relied on tokio's paused time to advance the clock artificially, but the heartbeat task uses real wall-clock time to judge staleness, making the paused-time approach unreliable. The test now runs on a real multi-threaded runtime with a fast 20ms tick, sleeps for real durations, and verifies that a heartbeating run is never stale and that cancelling the heartbeat allows the run to become stale. The separate probe test that duplicated this logic is removed.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a new end-to-end test to verify that graph task dispatch works correctly across the system, ensuring that tasks are properly routed and executed according to the graph structure.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new test file to verify the graph goal budget functionality, ensuring that budget constraints are correctly applied and validated within the graph-based goal system.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add documentation files for the dispatch and runs modules under the graph/todos directory to provide an overview of their purpose and usage.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds documentation for the two layers that sit on top of the board for hosts running cards autonomously: the claim/heartbeat/reclaim log in `graph::todos::runs` and the scheduling policy in `graph::todos::dispatch`. Also updates the testing section to reflect the new test files for these layers.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Documents the two halves of budget enforcement — `store::account_usage` as the raw write and `graph::goals::budget` as the policy — along with the `account_turn` function and `GoalBudgetGuard` that together let hosts bound turn spending without reimplementing the logic. Also updates the testing section to mention the new budget-related test files.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat long method chains and function signatures across goals, todos, and dispatch modules to improve readability without changing any behaviour. Update README files to reference the new budget and dispatch submodules.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a new function that imports a run log only when the thread has no existing log, leaving any existing log untouched even with a different schema. This supports a safe one-time migration off a legacy store without risking duplicate reclaim histories that would corrupt the sweep budget.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `budget` module was previously private, making it inaccessible to external consumers of the graph goals API. This change makes it public so that callers can use budget-related functionality alongside the already-public `store` module.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

$0.0000 · 0 in / 0 out · 777 embedded · openrouter/openai/text-embedding-3-small

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Aug 21, 2026
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 74421ae7-7f66-44ba-ab66-9d5e5ef36b0b

📥 Commits

Reviewing files that changed from the base of the PR and between 71d80c3 and e38ebab.

📒 Files selected for processing (25)
  • docs/modules/graph/goals.md
  • docs/modules/graph/todos.md
  • src/graph/goals/README.md
  • src/graph/goals/budget.rs
  • src/graph/goals/mod.rs
  • src/graph/goals/test.rs
  • src/graph/mod.rs
  • src/graph/todos/README.md
  • src/graph/todos/dispatch/README.md
  • src/graph/todos/dispatch/mod.rs
  • src/graph/todos/dispatch/prompt.rs
  • src/graph/todos/dispatch/registry.rs
  • src/graph/todos/dispatch/select.rs
  • src/graph/todos/dispatch/test.rs
  • src/graph/todos/mod.rs
  • src/graph/todos/runs/README.md
  • src/graph/todos/runs/mod.rs
  • src/graph/todos/runs/store.rs
  • src/graph/todos/runs/test.rs
  • src/graph/todos/runs/types.rs
  • src/graph/todos/types.rs
  • src/lib.rs
  • tests/e2e_graph_task_dispatch.rs
  • tests/feature_graph_goal_budget.rs
  • tests/feature_graph_task_runs.rs

📝 Walkthrough

Walkthrough

Adds goal budget accounting and mid-turn enforcement. Adds persistent task-run lifecycle management with heartbeats and stale-run recovery. Adds task dispatch selection, prompts, polling, active-run cancellation, public exports, documentation, and unit, feature, and end-to-end tests.

Changes

Goal budget enforcement

Layer / File(s) Summary
Budget accounting and guard behavior
src/graph/goals/budget.rs, src/graph/goals/mod.rs, src/graph/goals/test.rs
Adds saturating turn-token accounting, active-goal usage tracking, BudgetVerdict, and GoalBudgetGuard. Guards stop projected over-budget turns and stand down when goals are replaced or become inactive.
Public budget coverage and documentation
tests/feature_graph_goal_budget.rs, docs/modules/graph/goals.md, src/graph/goals/README.md, src/graph/mod.rs, src/lib.rs
Adds public API tests and documents budget accounting, mid-turn checks, and exported budget helpers.

Task-run lifecycle

Layer / File(s) Summary
Run data model and persistence
src/graph/todos/runs/types.rs, src/graph/todos/runs/store.rs, src/graph/todos/types.rs, src/graph/todos/runs/mod.rs
Adds serialized per-thread runs, run outcomes, limits, heartbeats, completion, staleness evaluation, and bounded stale-run reclamation.
Lifecycle validation and integration
src/graph/todos/runs/test.rs, tests/feature_graph_task_runs.rs, src/graph/todos/runs/README.md
Tests creation, isolation, heartbeats, completion, stale detection, reclamation, retry blocking, cancellation, and timestamp handling.

Task dispatch

Layer / File(s) Summary
Dispatch policy and prompt construction
src/graph/todos/dispatch/select.rs, src/graph/todos/dispatch/prompt.rs, src/graph/todos/dispatch/mod.rs
Adds urgency-based card selection, approval precedence, polling backoff, task prompts, progress instructions, and dispatch-level exports.
Active-run coordination
src/graph/todos/dispatch/registry.rs
Adds thread-keyed active-run registration, replacement, conditional removal, cancellation, draining, and race-safe terminal ownership.
Dispatch tests and end-to-end flow
src/graph/todos/dispatch/test.rs, tests/e2e_graph_task_dispatch.rs
Tests selection, approval, polling, prompts, registry cleanup, cancellation, stale recovery, card write-back, and dispatch cadence.
Task-board surfaces and documentation
src/graph/todos/mod.rs, src/graph/todos/README.md, src/graph/todos/dispatch/README.md, docs/modules/graph/todos.md, src/graph/mod.rs, src/lib.rs
Exposes the runs and dispatch modules and documents their responsibilities and public APIs.

Estimated code review effort: 5 (Critical) | ~90 minutes

Poem

I’m a rabbit with a budgeted hop,
Counting each token before I stop.
Runs keep heartbeats, cards know where to go,
Stale work returns in a tidy row.
Dispatch picks the brightest task—
Then I nibble carrots from my flask.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@senamakel
senamakel merged commit bbcd0a6 into main Aug 21, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant