Skip to content

feat(todos): add CRUD tool + RPC for the agent task board#1983

Merged
senamakel merged 4 commits into
tinyhumansai:mainfrom
senamakel:feat/todo-tool-crud-rpc
May 17, 2026
Merged

feat(todos): add CRUD tool + RPC for the agent task board#1983
senamakel merged 4 commits into
tinyhumansai:mainfrom
senamakel:feat/todo-tool-crud-rpc

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented May 17, 2026

Summary

  • New unified todo agent tool dispatching on op (add / edit / update_status / remove / replace / clear / list), replacing the old wholesale todowrite tool.
  • New openhuman.todos_* JSON-RPC surface so the desktop UI (or any other RPC caller) can drive the same per-thread task board.
  • Markdown rendering (- [ ] / - [~] / - [!] / - [x]) bundled in every tool result + RPC response so transcripts and the kanban UI can render without re-formatting.
  • Added a Git workflow rule to both CLAUDE.md and AGENTS.md requiring all code edits to land on a branch forked from main.

Problem

The previous todowrite tool only supported wholesale list replacement, so agents had to resend the entire list to change one card and there was no way for users to nudge the list from the UI without rewriting it themselves. Output was also a plain-text checklist, which the chat UI had to re-parse to render. Two surfaces (agent tool + user RPC) needed parity so a user-driven status flip and an agent-driven status flip touch the same state.

Solution

  • New src/openhuman/todos/ domain with ops.rs (CRUD + markdown renderer), store.rs (process-global scratch fallback when no thread context exists), and schemas.rs (controller registry). All CRUD goes through one set of pure ops that load → mutate → persist via the existing per-thread TaskBoardStore, so agent and user edits share identical persistence and rendering.
  • openhuman.todos_* controllers: list, add, edit, update_status, remove, replace, clear. Wired into src/core/all.rs for both schemas and registered controllers.
  • src/openhuman/tools/impl/agent/todo.rs exposes a single todo tool that branches on op. Returns {threadId, cards, markdown} JSON so the orchestrator gets a ready-to-render markdown payload alongside the structured cards. Falls back to the process-global scratch store when invoked outside a chat thread.
  • The orchestrator profile already uses allowed_tools: None, so the new todo tool surfaces automatically. The Writer profile fixture and the all_tools registry test were updated from "todowrite" to "todo".
  • Tool tests serialize on a shared scratch_test_lock() so cargo's parallel runner doesn't race on the process-global scratch store.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • N/A: Diff coverage gate runs in CI — pnpm test:coverage / pnpm test:rust not run locally in this environment; tests added cover happy path + failure cases for every CRUD op and tool dispatch branch.
  • N/A: Coverage matrix — behaviour-only refactor of an existing agent tool; no new user-facing feature row.
  • N/A: No related feature-matrix IDs.
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • N/A: Does not touch release-cut smoke surfaces.
  • N/A: No linked issue.

Impact

  • Desktop: agents now manipulate the task board with granular ops; the UI can drive the same board via openhuman.todos_* RPC without rewriting the whole list. No behavior change for already-completed boards (data on disk is unchanged — same TaskBoardCard shape).
  • Breaking for tool callers: the todowrite tool is removed in favor of todo. Any external profile referencing todowrite by name needs to be updated to todo.
  • No security, migration, or perf implications.

Related

  • Closes:
  • Follow-up PR(s)/TODOs:

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/todo-tool-crud-rpc
  • Commit SHA: 3e077ca

Validation Run

  • pnpm --filter openhuman-app format:check (auto-fixes applied by pre-push hook and committed as chore: apply auto-fixes)
  • N/A: pnpm typecheck (no TS changes in this PR)
  • Focused tests: cargo test --lib -- openhuman::todos openhuman::tools::implementations::agent::todo openhuman::agent::profiles — 53 + 16 passed
  • Rust fmt/check: cargo fmt + GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml --lib — clean
  • N/A: Tauri fmt/check (no Tauri shell changes)

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: replace todowrite with granular todo CRUD tool + matching JSON-RPC.
  • User-visible effect: agent transcripts now contain markdown-rendered checklists; the desktop UI can edit todos directly via RPC.

Parity Contract

  • Legacy behavior preserved: op: replace reproduces the old todowrite wholesale-replace semantics, persisted via the same TaskBoardStore.
  • Guard/fallback/dispatch parity checks: orchestrator picks up the new tool automatically (allowed_tools: None); profile fixture and all_tools registry test updated from todowrite to todo.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this PR
  • Resolution: N/A

Summary by CodeRabbit

  • New Features

    • Unified "todo" tool provides full CRUD (add, edit, update status, remove, replace, clear, list), task-board markdown rendering, and a process-level fallback board when thread context is absent.
    • New RPC surface for task-board management under the todos namespace.
  • Refactor

    • Legacy "todowrite" tool replaced by the unified "todo" interface.
  • Documentation

    • Git workflow updated to prohibit committing directly to main.
  • Tests

    • Tests updated to reflect the new todo naming and behaviors.

Review Change Stack

senamakel added 3 commits May 16, 2026 20:49
Add a "Never write code on `main`" rule to the Git workflow sections of
both AGENTS.md and CLAUDE.md so agents fork a feature branch off the
latest `main` before any code edits. Keeps `main` clean and advancing
only via merged PRs.
Replace the wholesale `todowrite` tool with a single `todo` tool that
dispatches on an `op` field (`add` / `edit` / `update_status` / `remove`
/ `replace` / `clear` / `list`), and expose the same operations over
JSON-RPC as `openhuman.todos_*` so the desktop UI can drive the list
directly. Both surfaces share `openhuman::todos::ops` and persist into
the existing per-thread `TaskBoardStore`, so agent and user edits stay
in lock-step.

Tool results and RPC responses include a markdown rendering of the
list (`- [ ] …` / `- [~] …` / `- [!] …` / `- [x] …`) with card ids and
indented notes/blockers so transcripts and the task board UI can render
without re-formatting.

Surfaced to the orchestrator via the existing global tool registry
(orchestrator has `allowed_tools: None`); the `Writer` profile fixture
and `all_tools` registry test updated from `todowrite` to `todo`.
@senamakel senamakel requested a review from a team May 17, 2026 04:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b9960f61-b02d-4e25-a897-f4ac56198142

📥 Commits

Reviewing files that changed from the base of the PR and between 3e077ca and 4384c27.

📒 Files selected for processing (3)
  • src/openhuman/todos/ops.rs
  • src/openhuman/todos/store.rs
  • src/openhuman/tools/impl/agent/todo.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/todos/store.rs
  • src/openhuman/tools/impl/agent/todo.rs
  • src/openhuman/todos/ops.rs

📝 Walkthrough

Walkthrough

This PR consolidates todo/task board management by removing the single-operation todowrite tool and replacing it with a unified todo tool backed by a new openhuman.todos RPC subsystem that exposes full CRUD operations, markdown rendering, and both per-thread persistent and process-global scratch storage.

Changes

Git workflow documentation

Layer / File(s) Summary
Feature branch workflow guidance
AGENTS.md, CLAUDE.md
Contributors must create a feature branch off the latest main before making changes, preventing direct commits to main.

Todos subsystem refactor and tool unification

Layer / File(s) Summary
Scratch store and CRUD operations
src/openhuman/todos/store.rs, src/openhuman/todos/ops.rs
ScratchTodoStore provides in-memory fallback when no thread context is available. ops.rs implements status alias parsing, full CRUD pipeline (load current state, apply mutations with trimming/validation, enforce single InProgress invariant, persist, emit progress events, render markdown), and unit tests covering operations and scratch-store behavior.
JSON-RPC controller and handlers
src/openhuman/todos/schemas.rs
RPC contracts for list, add, edit, update_status, remove, replace, clear with typed params, async handlers that resolve thread id → BoardLocation, delegate to ops, and serialize TodosSnapshot. Includes schema consistency tests and namespace validation.
Module structure and public exports
src/openhuman/mod.rs, src/openhuman/todos/mod.rs
Adds pub mod todos;, wires ops, schemas, store, and re-exports all_todos_controller_schemas, all_todos_registered_controllers, and scratch-store types for public consumption.
TodoTool implementation and dispatch
src/openhuman/tools/impl/agent/todo.rs
New TodoTool implements Tool trait, declares supported ops in JSON schema, selects thread vs scratch location, parses arguments into patches, dispatches to ops::*, and normalizes TodosSnapshot into ToolResult. Includes async tests using the scratch store.
Tool registration and old tool removal
src/openhuman/tools/impl/agent/mod.rs, src/openhuman/tools/ops.rs, src/openhuman/tools/ops_tests.rs
Replaces mod todo_write with mod todo, re-exports TodoTool, registers TodoTool::new() in the tool registry instead of the deleted TodoWriteTool, and updates tests to expect "todo" rather than "todowrite".
Core RPC controller registration
src/core/all.rs
Adds todos controller schemas and registered controllers to the main RPC dispatch and discovery registries.
Test fixtures and documentation updates
src/openhuman/agent/profiles.rs, src/openhuman/agent/progress.rs, src/openhuman/agent/schemas.rs, src/openhuman/agent/task_board.rs
Profile test fixtures and assertions updated to reflect "todo" tool name; module docs and progress docstrings updated to reference todo and openhuman.todos_* RPC surfaces.

Sequence Diagram

sequenceDiagram
  participant Agent
  participant TodoTool
  participant ops as openhuman::todos::ops
  participant TaskBoardStore
  participant ScratchStore

  Agent->>TodoTool: execute(op: "add", content, ...)
  TodoTool->>TodoTool: current_location()
  alt Thread context available
    TodoTool->>ops: add(BoardLocation::Thread, ...)
  else No thread context
    TodoTool->>ops: add(BoardLocation::Scratch, ...)
  end
  ops->>TaskBoardStore: load_cards() or ScratchStore.snapshot()
  ops->>ops: validate & apply mutations
  ops->>ops: enforce single in-progress
  ops->>TaskBoardStore: save_cards() or ScratchStore.replace()
  ops->>ops: render_markdown(cards)
  ops->>TodoTool: TodosSnapshot
  TodoTool->>Agent: ToolResult {cards, markdown, threadId}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • tinyhumansai/openhuman#1208: Introduces the earlier todowrite tool and registry entries that this PR removes and replaces with the unified todo/openhuman.todos subsystem.
  • tinyhumansai/openhuman#1717: Also modifies the tool registry (all_tools_with_runtime), making its changes related at the tool-registration level.

"🐇 I hopped through code with eager paws,
Swapped todowrite for tidy laws.
Threads and scratch now sing as one,
CRUD and markdown — job well done!
Branch from main, my carrot calls, hooray!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly and concisely summarizes the main changes: adding CRUD operations (tool + RPC) for the agent task board, which is the primary objective of this changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 17, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/openhuman/todos/store.rs (1)

34-39: ⚡ Quick win

Add structured logs on scratch-store init/access paths.

This new shared-state path currently has no observability. Please add debug logs with stable prefixes and context (e.g., init vs reuse).

As per coding guidelines: src/**/*.rs: Add substantial, development-oriented logs on new/changed flows at entry/exit points, branch decisions, external calls, retries/timeouts, state transitions, and error handling paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/todos/store.rs` around lines 34 - 39, Add structured debug logs
to the global_scratch_store path: when the function is called, log an entry
message with a stable prefix (e.g., "scratch_store:enter"), then log whether you
are initializing a new store or reusing an existing one (detect in the closure
passed to OnceCell::get_or_init and the get path) with context like
"scratch_store:init" vs "scratch_store:reuse". Include any relevant identifiers
or state (e.g., pointer/Arc strong_count or a simple store id) and log an
exit/return message ("scratch_store:exit") so callers can trace
entry->branch->exit; target the function global_scratch_store, the static STORE
OnceCell, and the ScratchTodoStore::new call when adding these debug statements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/openhuman/todos/store.rs`:
- Around line 23-29: The current snapshot() and replace() split the
read-modify-write across separate locks causing races; add an atomic mutation
method (e.g., pub fn mutate<F>(&self, f: F) where F: FnOnce(&mut
Vec<TaskBoardCard>)) that takes the lock once, applies the closure, and returns
any needed result, then refactor all scratch CRUD paths to call this mutate(...)
instead of snapshot()/replace() so load, modify and save happen inside a single
critical section (update uses of snapshot, replace, and direct .lock() mutations
to use the new mutate API and adjust callers to accept any return values the
closure produces).

In `@src/openhuman/tools/impl/agent/todo.rs`:
- Around line 157-162: The helper required_string should trim whitespace and
reject values that are empty after trimming to avoid false "not found" errors
for fields like "id" used by edit/remove/update_status; modify required_string
to extract the string as now, call .trim() on it, return
Ok(trimmed_string.to_string()) only if trimmed is non-empty, otherwise return an
anyhow::anyhow!("missing required field `{key}`") (or similar) so
leading/trailing whitespace is ignored and empty-after-trim values are treated
as missing.

---

Nitpick comments:
In `@src/openhuman/todos/store.rs`:
- Around line 34-39: Add structured debug logs to the global_scratch_store path:
when the function is called, log an entry message with a stable prefix (e.g.,
"scratch_store:enter"), then log whether you are initializing a new store or
reusing an existing one (detect in the closure passed to OnceCell::get_or_init
and the get path) with context like "scratch_store:init" vs
"scratch_store:reuse". Include any relevant identifiers or state (e.g.,
pointer/Arc strong_count or a simple store id) and log an exit/return message
("scratch_store:exit") so callers can trace entry->branch->exit; target the
function global_scratch_store, the static STORE OnceCell, and the
ScratchTodoStore::new call when adding these debug statements.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 351d7240-9e24-4953-a359-f4f7f66548ce

📥 Commits

Reviewing files that changed from the base of the PR and between a6e311e and 3e077ca.

📒 Files selected for processing (17)
  • AGENTS.md
  • CLAUDE.md
  • src/core/all.rs
  • src/openhuman/agent/profiles.rs
  • src/openhuman/agent/progress.rs
  • src/openhuman/agent/schemas.rs
  • src/openhuman/agent/task_board.rs
  • src/openhuman/mod.rs
  • src/openhuman/todos/mod.rs
  • src/openhuman/todos/ops.rs
  • src/openhuman/todos/schemas.rs
  • src/openhuman/todos/store.rs
  • src/openhuman/tools/impl/agent/mod.rs
  • src/openhuman/tools/impl/agent/todo.rs
  • src/openhuman/tools/impl/agent/todo_write.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs
💤 Files with no reviewable changes (1)
  • src/openhuman/tools/impl/agent/todo_write.rs

Comment thread src/openhuman/todos/store.rs
Comment thread src/openhuman/tools/impl/agent/todo.rs
Address CodeRabbit review on tinyhumansai#1983:

- `ops.rs`: take a process-global parking_lot mutex at the entry of
  every public CRUD op when the target is the scratch store, so the
  load → mutate → save sequence is atomic. Without this two concurrent
  scratch `add` / `edit` / `remove` calls could lose updates because
  `snapshot()` and `replace()` each take the inner lock independently.
  Per-thread ops are already atomic at the file-rename level via
  `TaskBoardStore::put`, so they don't take the new lock.

- `tools/impl/agent/todo.rs`: trim required string args and reject
  empty-after-trim, so a tool call passing `" task-123 "` for `id`
  no longer triggers a false "not found" in `edit` / `remove` /
  `update_status`.
@senamakel senamakel merged commit 780510f into tinyhumansai:main May 17, 2026
27 of 28 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 17, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant