Skip to content

Client tools: a client attaches tools to a conversation; collection-held conversations are kept - #2334

Merged
AbirAbbas merged 7 commits into
workstreams/mainfrom
workstreams/e1-client-tools
Nov 3, 2026
Merged

AbirAbbas merged 7 commits into
workstreams/mainfrom
workstreams/e1-client-tools

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

A1a of the Workstreams move: workstream logic now lives in the desk, the engine stays the AI harness (design: the Workstreams plan's ARCH-DESIGN §1, row A1a).

Summary

  • New pure-Rust crate crates/workstreams (workstreams-core), no Tauri types. One SQLite file per engine home (store_path: <app data>/workstreams/<16 hex of sha256(engine home)>/workstreams.db, WORKAF_WORKSTREAMS_DB overrides) with canvas's habits: rusqlite 0.37 bundled, WAL, user_version migrations under BEGIN IMMEDIATE, a newer file refused untouched, a 10 s writer wait mapped to busy.
  • Schema v1 holds workstreams (with name and collection_id), member-job claims (UNIQUE(factory, job)), pending creates, plans, pages with versions and deleted state, records, go-aheads, sources, suggestions, suggestion edits, wakes and wake items.
  • All 36 contract v0.5 methods as Store methods named like the Backend methods, taking and returning the contract's serde types, with its errors (not_found, conflict, version_conflict + detail, too_large, bad_params, in_other_workstream + detail, cycle, busy), stage rule, size limits, Read-by-jobs defaults and go-ahead rules.
  • Engine facts are inputs: job states, job costs and agent spend come through the EngineFacts trait, read outside any transaction; ChangeSink receives one workstream.changed per changed workstream after commit; page documents go through the PageDoc seam (A1b's crate implements it).
  • Agent-attributed domain operations for A3 (by_agent, agent page writes, proposals, suggestion create, source proposals; Done checked inside the write) and the wake store for A4 (begin_wake with key dedup and daily caps, settle_wake, expire_wakes, parse_wake_answer).
  • The serde types moved from crates/core/src/engine/workstreams.rs into the crate; ui2-core re-exports them, so no import changes. Outside the crate: the workspace member line, the core dependency line, the re-export and Cargo.lock.
  • No Backend method is switched (A2 does that); no markdown renderer (A1b).

Validation contract

item tests
(a) every local method behaves as the contract says, every error code tests/appworkstreams*.rs, pages.rs, records_goaheads.rs, sources.rs, suggestions.rs, wakes.rs, method_refusals.rs (each of the 36 methods: its own not_found and bad_params)
(b) the engine's store tests, ported one Rust test per Go test in internal/workstream/*_test.go (store, pages, records/go-aheads, limits, sources, agent, agent-done, v0.2 fields, suggestions, wakes, wake answer, wake fix 1/2) and the store parts of cmd/codeaf/appworkstreams*_test.go; each names its Go origin and contract item
(c) the store never needs the engine validation.rs engine_facts_are_the_only_number_inputs_and_run_outside_locks, review_fix1.rs finding 3
(d) schema v1 migration idempotent validation.rs migration_is_idempotent_across_store_handles, newer_schema_is_refused_without_changing_database_bytes
(e) one workstream per job validation.rs one_workstream_per_job_is_enforced_by_every_door_and_sqlite, store.rs ownership tests
(f) keep→grant atomic validation.rs keep_grant_is_atomic_when_the_member_job_disappears, workstream_fields_v02.rs keep refusals and rollback
(g) size limits limits.rs, validation.rs row_and_page_parameter_bounds_refuse_atomically, source and suggestion caps
(h) shape test against the contract examples contract_shapes.rs: reads docs/workstreams-wire.md itself (examples and field tables), sends every example's params through the store, checks result, error and event shapes; fails on an unused example or a method with no case

Review: one fresh review round (2 major, 3 minor), all fixed with tests (review_fix1.rs, method_refusals.rs).

Left for others

  • A2: implement PageDoc for A1b's crate and re-run the markdown-sensitive tests with it; feed EngineFacts from collections.open / factory.queue / agents.open; route ChangeSink into the engine-event delivery; create orchestration (pending_create, collections.create, agents.create, close an unused agent); claim_jobs/release_jobs on collections.add/remove, replace_jobs at start; collections.rename first, then set_name; origin-chat check; map WorkstreamError to EngineError.
  • A3: tool scoping and source read-safety before calling the agent operations. A4: the scheduler (08:00 slot) on top of begin_wake/settle_wake/expire_wakes; whether errand costs already land in the agent's spent_usd (never count wake costs twice).
  • Engine schema 3–6 compatibility tests are not ported: the desk does not import engine files (design §6).

Contract changes

None. docs/workstreams-wire.md is unchanged.

🤖 Generated with Claude Code

AbirAbbas and others added 7 commits November 3, 2026 15:57
agents.tools_set attaches, replaces or removes one named set of tools on a
conversation; agents.tool_call carries each model call to the client and
agents.tool_result answers it. The hello says agents.client_tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A conversation with a client tool door offers the client's tools after every
built-in, with each set's instructions on its own page and nothing added
when no set is attached. Tools follow their declared category, a built-in
name wins, and child tasks and discussions get neither tools nor
instructions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Launch cleanup, the blank-chat sweep and the background places sweep keep a
never-spoken conversation that a collection references, reading the
collections database read-only and keeping the folder when it cannot be
read. Opening such a conversation by id after a restart restores its owned,
model, archive and spend facts from its metadata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app backend keeps each conversation's client tool sets for as long as
the connection lasts, emits one agents.tool_call per model call and waits
for the answer, its deadline, the turn's end or the connection closing.
Call ids are stable for a replayed call, and a late or repeated answer is
ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The registry's sender runs under guard.Go, every registry lock is released
from a defer, and a test name no longer spells the old product name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AbirAbbas
AbirAbbas merged commit e9a571c into workstreams/main Nov 3, 2026
7 of 15 checks passed
@AbirAbbas
AbirAbbas deleted the workstreams/e1-client-tools branch November 3, 2026 22:29
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