Files
openhuman/docs/tinyagents-port-plan.md

30 KiB
Raw Permalink Blame History

TinyAgents Port — Plan & Audit (inference / tools / agent_orchestration)

Status: superseded on 2026-07-22 by tinyagents-migration-plan-2026-07-22.md. This document is retained as historical design detail; its version pins, phase status, and present-tense file inventory are not current. Anchor precedent: the TinyAgents harness migration (#4249 / #4399 / #4473) and the TinyCortex memory migration plan (docs/tinycortex-memory-migration-plan.md). Target: move the genuinely framework-shaped parts of src/openhuman/inference/, src/openhuman/tools/, and src/openhuman/agent_orchestration/ down into the tinyagents crate (vendored git submodule at vendor/tinyagents, https://github.com/tinyhumansai/tinyagents), and delete the in-tree duplicates in favor of crate primitives.


0. Ground truth (as audited)

0.1 Sizes and shape

Host module LOC (approx) Role
src/openhuman/inference/ ~53,000 (121 files) Provider trait + OpenAI-compatible wire client, factory/routing, local runtime (Ollama/LM Studio/Whisper/Piper), voice, OAuth, RPC surface
src/openhuman/tools/ ~38,500 Tool trait + metadata model, schema cleaning, generated-tool admission, ~200-tool registry assembly, impl/ families (filesystem, system, browser, computer, network, presentation), RPC surface
src/openhuman/agent_orchestration/ ~25,800 Product control plane over sub-agents: in-memory session, detached-run registry, workflow runs, agent teams, command center, worktree isolation, RPC/tools surface
src/openhuman/tinyagents/ (the seam) ~15,200 (25 files) Adapters implementing tinyagents traits over openhuman services — where all ported code plugs back in

TinyAgents (sibling repo, v1.7.1, edition 2024, GPL-3.0-only, published to crates.io) already provides: harness/ (ChatModel, Tool<State>, agent loop, middleware, retry/fallback, usage/cost, cache, embeddings + vector store, summarization, steering, SubAgent/SubAgentSession/SubAgentTool, observability journals, WorkspaceIsolation, testkit), graph/ (durable typed graphs, checkpointers incl. SQLite, recursion policy, map_reduce, orchestration::TaskStore/SteeringRegistry, topology export), registry/, and the .rag/.ragsh languages. ~30 public traits are the extension points.

License: both repos are GPL-3.0 — moving code down is license-clean. But tinyagents is publicly redistributed on crates.io, so only genuinely generic code moves (no product policy, backend phrases as load-bearing API, or keys).

0.2 The single most important audit finding

Most of the "bulky" code is not portable — it is a parallel implementation of things tinyagents already ships. The heavy clusters duplicate crate primitives rather than extend them:

openhuman (in-tree) tinyagents (already shipped)
inference/provider/traits.rs (Provider, ChatMessage, ChatResponse, UsageInfo) harness/model/ (ChatModel, ModelRequest/Response), harness/usage/ (field-for-field)
inference/provider/compatible*.rs (~6.6k + 4.9k tests) harness/providers/openai/ (same 9-provider OpenAI-compat fan-out)
inference/provider/reliable.rs (retry wrapper) harness/retry/ + middleware/library/resilience.rsreliable.rs's own module doc says it is "slated for removal once the tinyagents crate owns retry/fallback" and pins retry to 1 attempt
tools/traits.rs Tool trait / ToolSpec / flat-Vec registry harness/tool/ Tool<State> / ToolSchema / deduping ToolRegistry
agent_orchestration/ops.rs AgentOrchestrationSession + running_subagents.rs graph/orchestration/ TaskStore + SteeringRegistry + harness/subagent/ SubAgentSession
inference/provider/router.rs tier routing harness/middleware fallback + registry/ ModelCatalog

So the correct plan has three motions, not one:

  1. Consolidate (delete in favor of the crate) — the duplication above. This is where nearly all of the LOC reduction comes from.
  2. True ports (net-new upstream) — a short list of clean, generic pieces tinyagents lacks (§2).
  3. Stays in openhuman — RPC/controllers, config/credentials/policy glue, local runtime + voice, product tools, UI surfaces (§3).

0.3 Version status

Phase 0 closed: the host branch first aligned vendor/tinyagents to v1.6.0 and closed the baseline seam fallout: ToolCompleted outcome projection, SHA-256 prompt-cache fingerprinting, and the redaction-layer audit.

Phase 1 partial cutover: TinyAgents v1.7.1 is tagged at 3e81e493, and the host now pins both lockfiles plus vendor/tinyagents to that release. SchemaCleanr is crate-backed through the stable OpenHuman import path; model_context.rs delegates generic raw-model hints to tinyagents::harness::model::context_window_for_model_id after checking OpenHuman tier aliases and the cost catalog; and the observed run path now drives invoke_stream_in_context, whose Send stream fix shipped in tinyagents#28.

0.4 Contribution workflow (same convention as tinycortex)

Engine changes are made inside vendor/tinyagents, committed on a branch there, PR'd against tinyhumansai/tinyagents, released, then the host bumps the submodule SHA + version pin in a standalone chore(vendor): bump tinyagents — <summary> (tinyagents#<PR>) commit. Host PRs never contain engine source edits. tinyagents house rules apply to upstreamed code: types.rs/test.rs/mod.rs module discipline, centralized lib.rs re-exports, ≥80% coverage, tiny dependency tree (no new default deps; anything heavy goes behind a cargo feature), offline tests via MockModel (network tests are live_* and key-gated).


1. Target ownership split

1.1 Moves to / consolidates into TinyAgents

Host code Motion tinyagents destination
inference/provider/error_classify.rs (777 L, 35 tests, extracted for this purpose in #4249) port harness/retry/ classification (or harness/providers/ error module)
inference/model_context.rs pattern table (minus OH tier/cost-catalog arms) port harness/model/ ModelProfile/context metadata
inference/parse.rs (sanitize_inline_completion, zero deps) port (low priority) text post-process util
inference/provider/compatible*.rs + reliable.rs + router.rs delete after cutover to harness/providers/openai + retry/resilience + registry routes n/a (crate already has it)
tools/schema.rs SchemaCleanr (+ schema_tests.rs, zero deps) port provider adapters (per-provider JSON-Schema cleaning — genuine gap)
tools/traits.rs display/metadata morsels: humanize_tool_name, context_detail_from_args, display_label/display_detail, ToolTimeout semantics port (additive) harness/tool/ (ToolRuntime/policy metadata)
tools/generated.rs admission/provenance model align (don't duplicate) registry/capability + registry/component
tools/impl/filesystem/ (file_read/write/edit_file/apply_patch/grep/glob/list_files/read_diff) port (behind a tools feature) new harness/tools/ builtin family, Tool<State>-native
tools/impl/system/{current_time,resolve_time}.rs port (first movers — nearly pure) same builtin family
tools/impl/network/{http_request,web_fetch,curl,url_guard}.rs (SSRF guard is 842 L of largely pure logic) port same builtin family
tools/impl/system/{shell,node_exec,npm_exec}.rs port later (multi-seam: SecurityPolicy, NodeBootstrap, sandbox, tool_timeout) same builtin family, gated on §4 blockers
agent_orchestration/worktree.rs (617 L — already an impl of tinyagents' WorkspaceIsolation; one publish_global seam) port harness/workspace/ git-worktree isolation provider
agent_orchestration/ops.rs (AgentOrchestrationSession) + running_subagents.rs (1.9k L) delete in favor of graph::orchestration::TaskStore + SteeringRegistry + SubAgentSession; route the live control path through the crate (today only re-exported, per seam orchestration.rs:23-33) n/a
agent_orchestration/types.rs AgentStatus vocabulary reconcile into OrchestrationTaskStatus (two parallel status enums today) graph/orchestration/types
agent_orchestration/workflow_runs/ phase-DAG validation + agent_teams/ dependency-DAG/atomic-claim/quality-gate logic evaluate upstreaming the validation/scheduling slices as graph extensions; durability (session_db::run_ledger) and RPC stay host-side graph/
Reasoning-content channel (historically smuggled via ContentBlock::ProviderExtension; host now writes ContentBlock::Thinking for new messages and only reads legacy ProviderExtension from persisted transcripts — seam convert.rs) largely done — the crate's first-class typed reasoning channel (ContentBlock::Thinking) is live per Phase 1.4 / ledger P1-5 harness/message/

1.2 Stays in OpenHuman (product policy, I/O, surfaces)

  • All RPC surfaces: inference/{ops,schemas}.rs, provider/ops*, tools/schemas.rs, agent_orchestration/*_schemas.rs, subagent_control.rs, command_center/, worktree_schemas.rs. JSON-RPC method names and payload shapes must not change.
  • Provider resolution & auth: provider/factory.rs (provider-string grammar), openhuman_backend.rs, openai_codex.rs, claude_code/, claude_agent_sdk/, openai_oauth/, thread_context.rs, backend billing-envelope parsing (openhuman.usage.* / openhuman.billing.*).
  • Local runtime & voice (~17k L): all of inference/local/ (Ollama/LM Studio admin, Whisper/Piper install + engines), voice/, device.rs+presets.rs+model_ids.rs+paths.rs (device→tier product policy), sentiment.rs, http/ (OpenAI-compat serving surface). tinyagents has no local-runtime provisioning and should not grow one now. (device.rs is technically portable — zero openhuman deps — but only valuable if tinyagents ever grows local provisioning; skip.)
  • Tool product surface: tools/ops.rs registry assembly (~200 registrations over ~50 domains), user_filter.rs (UI-toggle families), orchestrator_tools.rs, local_cli.rs, impl/computer/ (macOS CGEvent/AX), impl/presentation/, network app integrations (polymarket, gitbooks, gmail_unsubscribe, mcp_setup).
  • Orchestration product plane: all agent_orchestration/tools/* (openhuman Tool impls re-pointed at crate primitives), subagent_events.rs + run_ledger_finalize.rs (event-bus bridges), background_{completions,delivery}.rs (chat idle-delivery UX), pairing* (tiny.place consent), parent_context/ (DI bootstrap), session_db::run_ledger durability.
  • The seam itself (src/openhuman/tinyagents/) — it shrinks as duplication is deleted, but the ChatModel/Tool/middleware/journal adapters remain openhuman's integration layer.

2. Blockers to resolve before tool ports (design decisions)

These are API mismatches the current seam bridges lossily; porting tools natively forces a decision on each:

  1. ToolResult shape — openhuman uses MCP-style content blocks + markdown_formatted; tinyagents uses flat string content (the seam always sets raw: None, discarding structure). Proposal: add an optional structured-content field (or raw population convention) to tinyagents' ToolResult so ported tools stop losing block structure.
  2. PermissionLevel (5 ordered levels, with per-call *_with_args overrides) vs ToolSideEffects booleans — today Write/Execute/Dangerous all collapse to writes_files + WorkspaceAccess::Any, and per-call gating is dropped. Proposal: extend ToolPolicy with an ordered permission level and an optional per-call classifier hook, or accept the boolean model and encode levels host-side only (document the loss).
  3. SecurityPolicy is the universal seam across all of impl/ (path/command/host gating). tinyagents models the same concept as ToolAccess/SandboxMode/WorkspaceDescriptor; the security_for_tool_context shims in impl/{filesystem,system}/mod.rs already bridge workspace roots. Ported tools must depend only on the crate-side abstractions; openhuman injects its SecurityPolicy behind them. This likely means growing ToolAccess (e.g. trusted roots, command classification hook) — design this once, before the first filesystem tool moves.
  4. file_state edit-tracking (filesystem family) — either port a minimal read-before-write tracker into the crate tools or leave tracking host-side via middleware.
  5. Status vocabularyAgentStatus (Pending/Running/Waiting/Completed/Failed/Cancelled/Closed) vs OrchestrationTaskStatus (…/Awaiting/Timeout). Pick the crate vocabulary, map host wire compat in the RPC layer.

3. Step-by-step plan

Each phase is a coherent PR set: tinyagents PR(s) → release/tag → host chore(vendor): bump → host cutover PR. Every phase ends green on both CI lanes (tinyagents ci.yml; host fast lane + release lane).

Phase 0 — Version alignment & baseline (no behavior change)

  1. Done: bump vendor/tinyagents to v1.6.0 and the host requirement 1.5.01.6.0 (both root and app/src-tauri manifests; keep the [patch.crates-io] path entries).
  2. Done: fix seam fallout from the bump: SHA-256 prompt fingerprint vs the seam's KV-cache drift guard (tinyagents/middleware.rs), idempotent RedactionMiddleware vs journal.rs double-redaction, adopt ToolCompleted outcome fields in observability.rs (retire ToolFailureMap reconstruction). The context-preserving invoke_stream host path is now closed by the Send stream follow-up released in TinyAgents v1.7.1.
  3. Record a baseline: LOC per module, test counts, and the duplication map (§0.2) as the drift ledger for this migration (mirroring docs/tinycortex-drift-ledger.md).
  4. Exit: host builds and full release lane passes on tinyagents 1.6.0 with zero in-tree deletions yet. The host has since moved on to the Phase 1 v1.7.1 partial cutover.

Phase 1 — Quick wins upstream (small tinyagents PRs, no host deletions)

Land these as separate small PRs against tinyagents (each with its ported tests re-expressed in crate conventions):

  1. SchemaCleanr (+ its 15-test suite) → provider-layer schema cleaning. Zero-dep, highest value/effort ratio. Status: released in TinyAgents v1.7.0; host tools/schema.rs re-exports the crate implementation so existing import paths keep working.
  2. error_classify.rs classifiers → retry/HTTP-error classification (strip openhuman-backend phrase matches into a host-side extension table; keep the generic HTTP/status logic). Status: released in TinyAgents v1.7.0; host retry/failure call-site swaps remain pending because OpenHuman-specific backend phrase and billing-envelope rules must stay host-side.
  3. model_context.rs pattern-match table (substring-vs-segment matching, incl. the o1/o3 regression guard) → ModelProfile context resolution. OH tier aliases and cost-catalog arms stay host-side. Status: released in TinyAgents v1.7.0; host now delegates generic fallback lookup to the crate after checking tiers and the OpenHuman cost catalog.
  4. First-class reasoning channel on AssistantMessage → delete the ProviderExtension smuggling in seam convert.rs. Status: closed for new messages: TinyAgents v1.6.0 provides typed thinking blocks, and the host now writes reasoning_content to ContentBlock::Thinking while retaining legacy ProviderExtension reads.
  5. Git-worktree WorkspaceIsolation provider from agent_orchestration/worktree.rs (+ worktree_tests.rs, which exercises real git repos — fits the crate's offline test policy). The single publish_global event becomes a host-side wrapper. Status: released in TinyAgents v1.7.0; OpenHuman's wrapper stays for event-bus emissions, OutsideWorkspace, and host policy mapping until a safer adapter pass.
  6. Tool display metadata (humanize_tool_name, display_label/display_detail, ToolTimeout semantics) → harness/tool/. Status: released in TinyAgents v1.7.0; host adjusted ToolRuntime.timeout, but the OpenHuman Tool trait still owns richer legacy metadata until Phase 2 reconciles the tool model.
  7. current_time / resolve_time as the first two builtin tools (pilot for the tools feature layout). Status: released in TinyAgents v1.7.0 behind the optional tools feature; host wrappers stay until the Phase 2 tool model reconciliation adopts crate builtins.
  8. Exit: TinyAgents v1.7.1 tagged and host bumped. SchemaCleanr, generic model-context lookup, reasoning, and the context-preserving observed stream path are crate-backed; worktree/time/error-classification/display APIs are available but host deletion waits for their adapter/model reconciliation steps.

Phase 2 — Tool model reconciliation + builtin tool families

  1. Resolve §2 blockers 14 as a tinyagents design PR (ToolResult structure, permission model, ToolAccess extension, edit tracking).
  2. Port impl/filesystem/ (8 generic tools) natively onto Tool<State> behind a tools cargo feature, depending only on ToolAccess/WorkspaceDescriptor. Port inline tests + git_operations/run_tests siblings where generic.
  3. Port impl/network/{http_request,web_fetch,curl,url_guard} (SSRF guard first — it is mostly pure).
  4. Host cutover: tools/ops.rs registers the crate builtins (wrapped with openhuman SecurityPolicy injected behind ToolAccess); delete the in-tree implementations; user_filter.rs table re-points at crate tool names (watch the web_search"web_search_tool" name-drift risk).
  5. Defer shell/node_exec/npm_exec to a follow-up within this phase — they need the command-classification hook (classify_command/gate_decision stays host-side; the crate exposes the hook).
  6. generated.rs: re-target GeneratedTool onto the crate Tool<State> + align admission/provenance with registry/capability instead of a parallel mechanism.
  7. Exit: filesystem + network + time tools live in tinyagents with their tests; host impl/ shrinks by ~10k L; tool_policy_from_openhuman_tool lossy mapping replaced by the reconciled model.

Phase 3 — Provider consolidation (deletion, not porting)

The endgame of #4249's Workstream 11:

  1. Cut the live chat path over from Provider::chat/compatible*.rs to harness/providers/openai via the seam's ProviderModel — provider-by-provider, behind the existing route projection (routes.rs).
  2. Un-wrap ReliableProvider and restore crate-owned retry (RunPolicy retry is currently pinned to 1 attempt at seam mod.rs:117-120 precisely because of the double-retry hazard).
  3. Move the openhuman-backend billing/usage envelope parsing into the backend provider adapter (host-side ChatModel impl), not the generic wire client.
  4. Delete: compatible*.rs (~6.6k L), reliable.rs (909 L, self-declared dead-in-waiting), router.rs (route via registry ModelCatalog), legacy StreamChunk/stream_chat_* streaming surface in traits.rs (superseded by ProviderDelta, itself superseded by crate streaming).
  5. compatible_tests.rs (4,851 L / 207 tests — the richest suite in the domain): re-express the behavioral cases (SSE edge cases, tool-arg normalization, reasoning round-trip) against providers/openai fixtures upstream; keep host-side only the backend-envelope tests.
  6. Provider trait stays temporarily as the host-side seam for factory/local/claude-code providers, now thin; evaluate retiring it entirely once all consumers are ChatModel.
  7. Exit: one wire client (the crate's); inference/provider/ reduced to factory + app-specific providers + ops/RPC.

Phase 4 — Orchestration consolidation

  1. Route the detached sub-agent control plane through graph::orchestration for real: replace running_subagents.rs's bespoke registry/steer/abort/ownership machinery with TaskStore + SteeringRegistry (+ upstream anything missing: ownership enforcement, soft-cap sweep — evaluate as crate PRs). The seam's orchestration.rs stops being a partial re-export.
  2. Replace AgentOrchestrationSession (ops.rs, 679 L) with crate SubAgentSession + TaskStore; consumers (workflow_runs/engine.rs, agent_teams/runtime.rs) move onto the crate API.
  3. Unify status vocabularies (§2.5); host RPC keeps wire compat via mapping.
  4. Reconcile subagent_sessions/ (durable reusable sessions) with crate JsonlTaskStore/checkpoint reuse instead of a parallel JSON store.
  5. Thin spawn_parallel_graph.rs further into the seam (it already runs on crate map_reduce); fix the two-spawn-path inconsistency (worktree diff results threaded in one path, hardcoded empty in the other — ops.rs:559-562).
  6. Harden the steering_forwarder.rs (#4456) 50 ms poll-loop coupling upstream: give SteeringHandle/RunQueue bridging a first-class crate-side lifecycle instead of an abort-on-drop guard.
  7. Exit: one sub-agent lifecycle (the crate's); agent_orchestration/ reduced to product tools, event bridges, ledger, RPC.

Phase 5 — Workflow/team generic slices (optional, evaluate after Phase 4)

  1. Upstream the validation slices: workflow phase-DAG structural/cycle validation (workflow_runs/{types,ops}), team dependency validation + atomic CAS claim + quality-gate state machine (agent_teams/ops.rs) — as graph/ extensions if they generalize cleanly; otherwise leave host-side. Durability (session_db::run_ledger) and command_center/ stay host-side regardless.
  2. *_topology() exports already funnel through seam topology.rs::all_graph_topologies — keep that pattern for anything upstreamed.

Phase 6 — Cleanup & docs

  1. Delete transitional shims (ToolAdapter test-only wrapper, subagent_graph.rs no-op skeleton once the graph path is the real one, retrieve_tool_output vs tokenjuice duplication).
  2. Update gitbooks/developing/architecture/agent-harness.md, orchestration.md, module READMEs, and about_app if user-facing behavior shifted.
  3. Final LOC/coverage ledger vs the Phase 0 baseline.

4. Test migration strategy

  • Port with the code (self-contained suites): schema_tests.rs (15 fns), generated_tests.rs (24 fns), error_classify inline (35 tests), model_context inline, parse.rs inline (11), worktree_tests.rs (real-git, offline), filesystem/network tool inline suites, policy.rs inline. Re-express in tinyagents conventions: co-located test.rs, offline MockModel, testkit doubles, e2e_*.rs for integration, live_*.rs (key-gated) only where a real endpoint is essential.
  • Re-express behaviorally (harness-bound): compatible_tests.rs (207 tests) → crate providers/openai fixture tests; reliable_tests.rs (26) → crate retry/resilience tests (most already exist — diff first, port the gaps).
  • Stay host-side: factory_tests.rs (150), ops_tests.rs suites everywhere, user_filter/orchestrator_tools inline, all agent_orchestration harness-dependent suites (engine_tests, runtime_tests, spawn_parallel_agents_tests, tools_e2e_tests), all /tests/*_e2e.rs whole-app harnesses. These keep guarding the host wiring after cutover — they are the regression net for each deletion phase.
  • Coverage gates: tinyagents expects ≥80%; host PR lanes gate ≥80% on changed lines. Cutover PRs that mostly delete code should still touch the seam with tests proving the crate-backed path preserves behavior (golden transcripts via MockModel scripts are the cheap way).

5. Bugs, gaps, and improvements found during the audit

Fix-in-place candidates (independent of the port; several become moot as phases delete their hosts):

inference/

  • presets.rs:200-205recommend_tier ignores its RAM input and always returns MVP_MAX_TIER; misleading dead logic (its own test asserts the non-scaling). Either implement scaling or rename/strip the parameter.
  • provider/traits.rs:734 — default stream_chat_with_history hardcodes provider_name = "unknown", yielding the useless diagnostic "unknown does not support streaming".
  • provider/traits.rs — two parallel streaming abstractions coexist (StreamChunk/stream_chat_* legacy vs ProviderDelta); the legacy surface is near-dead and scheduled for deletion in Phase 3.
  • provider/reliable.rs — live wrapper that no longer retries (pinned to one attempt); dead-code-in-waiting.
  • provider/factory.rs:404NO_MODEL_CONFIGURED_ANCHOR correctness depends on a string literal matched by a separate classifier in provider/ops.rs:19; fragile cross-file coupling.

tools/

  • tools/ops.rsall_tools_with_runtime takes 12 positional args (#[allow(clippy::too_many_arguments)]; the thin all_tools shim forwards 9); a builder is overdue. The Vec<Box<dyn Tool>> container doesn't dedupe (a test exists because of this); the crate's ToolRegistry fixes it structurally.
  • tools/traits.rs:15ToolScope::AgentOnly is a dead variant; ToolCategory::Workflow is pinned to wire "skill" (documented tech-debt to resolve before porting the type).
  • tools/traits.rs:367-372is_concurrency_safe is advisory-only (harness runs tools serially); tinyagents 1.6 has concurrent independent tool calls — adopting it makes the flag real.
  • orchestrator_tools.rs:38-41,87-89 — dead SpawnWorkerThreadTool registration (pending #1624); mis-attached doc-comment at lines 592-603 describes a different test than the one it precedes.
  • user_filter.rs:168-188skill_manage and workflow_manage families carry identical rust_names (deliberate alias, duplication footgun); web_search"web_search_tool" name mapping is drift-prone against the search domain.
  • impl/network/polymarket* (~2.5k L incl. tests) — a large app integration living under the "cross-cutting families only" impl/ rule; should move to a domain per the module's own README.
  • Seam tools.rstool_policy_from_openhuman_tool silently drops category, scope, and all *_with_args per-call gating (§2.2).

agent_orchestration/

  • ops.rs:138-168message_agent records metadata only and never injects into the running loop; a real functional hole that agent_teams/runtime.rs and command_center inherit.
  • subagent_control.rs — the SteerError::NotOwned arm is unreachable on the trusted RPC steer_control path (which performs no parent-ownership check), but the variant is not dead: the agent-tool running_subagents::steer path enforces ownership and returns it (guarded by steer_rejects_cross_parent_and_unknown). Real RPC-path enforcement is deferred to Phase 4 (SteeringRegistry ownership consolidation).
  • ops.rs:559-562 — in-memory session path hardcodes worktree_path: None, changed_files: [] on completion even for worktree-isolated workers (inconsistent with spawn_parallel_graph).
  • delegation.rs:120-127 — human-approval interrupt permanently disabled; the durable interrupt path in the seam is wired but unreachable until a review surface exists.
  • Two parallel status enums (AgentStatus vs OrchestrationTaskStatus) — latent drift hazard (§2.5).
  • Process-wide OnceLock<Mutex<…>> singletons in background_completions/background_delivery/running_subagents serialized through TEST_ENV_LOCK in tests — the crate favors injected stores; Phase 4 removes most of them.

seam (src/openhuman/tinyagents/)

  • mod.rs:117-120 — retry pinned to 1 attempt (Workstream 11 debt; resolved in Phase 3).
  • orchestration.rs:23-33 — bridge is re-export-only; live control path still on RunQueue (resolved in Phase 4).
  • retriever.rs:14-27 — crate Retriever/InMemoryVectorStore built but unused on the live path (dead-until-swap; out of scope here, note for the memory migration).
  • steering_forwarder.rs (#4456) — abort-on-drop guard papering over a fragile poll-loop coupling; harden upstream (Phase 4.6).
  • middleware.rs — prompt-cache drift detection duplicated between seam and crate PromptCacheGuardMiddleware, with differing fingerprints until the 1.6.0 bump (Phase 0).

6. Risks & mitigations

Risk Mitigation
Behavioral drift cutting compatible*.rs over to crate providers (SSE edge cases, tool-arg normalization, backend envelope) Provider-by-provider cutover behind the route projection; port the 207-test behavioral suite upstream before deleting; keep backend-envelope parsing host-side
Double-retry / retry semantics during Phase 3 transition Keep the 1-attempt pin until ReliableProvider is un-wrapped in the same PR that enables crate retry
Lossy tool-metadata mapping ossifying (permissions, MCP blocks) §2 design decisions land as a tinyagents PR before any tool family moves
tinyagents is public crates.io GPL code Audit each upstream PR for product policy/backend phrase coupling; backend-specific classifiers stay host-side as extension tables
Version skew between submodule pin, crates.io version req, and upstream tags Every bump is a standalone chore(vendor) commit pinning tag + version together; never float the submodule
The two-Cargo-world topology (root crate vs Tauri shell, #3877) means two lockfiles to bump Bump both manifests in the same chore(vendor) commit; CI full lane validates both
Big-bang deletion breaking the fast PR lane's changed-file coverage gate Phase PRs pair each deletion with seam tests exercising the crate-backed path

7. Expected outcome (rough accounting)

  • Deleted from host: compatible*.rs + tests (~11.5k), reliable.rs + tests (~2k), router.rs (~0.8k), orchestration session + detached registry + parallel-graph thinning (~45k), tool families moved (~10k incl. tests), misc (streaming legacy, shims) — ~30k LOC leaves the host across Phases 14.
  • Added to tinyagents: builtin tools (feature-gated), schema cleaning, error classification, reasoning channel, worktree isolation, context-window table, orchestration hardening — ~1215k LOC upstream, most of it accompanied by ported tests.
  • Unchanged: RPC wire surface, local AI runtime, voice, provider factory grammar, product tool catalog, run ledger, UI.