Repository navigation
refactor: pre-task-communication cleanup (C1–C14, one commit per item) - #229
Merged
Merged
Conversation
The table added in ce9751f never had a producer: its only writer was assistant_create_session with a parent_session_id the frontend declared but never sent, and compaction rotates the CLI session id in place rather than creating a child session. No workspace DB holds a row. Removed: - repository: parent_session_id, create_session_rotation_link, the include_ancestors recursive-CTE branch of count_session_chain_messages (now count_session_messages) and their tests - commands: CreateAssistantSessionRequest.parent_session_id, the parent validation/link creation, LoadSessionMessagesPageRequest.include_ancestors and the ancestor-walking page loop (single-session page now, same cursor/has_more/total_count semantics) - frontend: parentSessionId / includeAncestors params and the ancestor comments in client.ts, sessionStore.ts, useAssistantSession.ts, Workspace.tsx, WorkspaceTaskTranscriptPanel.tsx New migration 20261012000000_drop_assistant_session_links.sql drops the table. Bindings regenerated (doc comment on AssistantMessagePage.totalCount). AssistantMessageCursor.session_id (assistant/types.rs) is still set but no longer read by the backend: listing is keyed on the request's session. Kept for wire compatibility; it goes with C6a.
Remove `tool_scopes` and `inter_agent_call` from `SessionContext`, and the `InterAgentCallContext` struct that only the latter referenced. - `tool_scopes` was filled at every context build (workspace command, scheduled runner, task worker) but never read; tool availability is derived from the agent config and execution capabilities instead. The `WorkspaceDescriptor.tool_scopes` field that existed only to feed it goes with it. - `inter_agent_call` had no `Some(..)` constructor anywhere; it was only copied forward from the existing session row, so it was always `None`. `inter_agent_call_depth` on `ToolExecutionContext` is unrelated and stays. No migration: both fields were serde-defaulted and `SessionContext` does not use `deny_unknown_fields`, so old `context_json` rows deserialize unchanged. Side effect observed in review: before this change the two reconcilers of the manager session disagreed on `tool_scopes` (`commands/workspace.rs` hard-coded `["web"]`, `agents/runner.rs` wrote `required_tools()`), so each chat-open / scheduled-tick alternation rewrote the row and emitted `SessionCreated`. Removing the field removes that ping-pong.
Owner decision (cleanup C7): scheduled runs use exactly one provider connection. The fallback loop masked provider failures behind extra run rows, duplicated the queued-followup kick on both exit paths, and will be superseded by a dispatcher-level retry policy once task communication lands. Deleted from agents/runner.rs: resolve_agent_connections, run_scheduled_agent_with_fallback (loop, fallback_index logging, clippy cognitive_complexity allow), run_allows_fallback, message_contains_output and their now-unused imports. Replaced by run_scheduled_agent: one run row, one turn, one start_queued_followup_if_idle call. Added AgentConfig::first_enabled_connection, the single resolver for the "first entry of provider_connection_ids that is enabled" rule; both the scheduled runner and the workspace-task runner now call it. Config shape (provider_connection_ids: Vec<String>) and error types are unchanged, so no migration. User-visible change: if the first enabled connection fails, the scheduled run now ends as a single failed run (surfaced by the existing run-error banner) instead of silently retrying on the next connection. No enabled connection still yields NoProviderConfigured with no run row. The agent settings form now states that only the first connection is used.
…e DB Session- and run-scoped Tauri commands now take a required workspace_id and open only that workspace's database; a session/run missing there is "not found" instead of probing every indexed workspace. - Add workspace_id to get_session, delete_session, load_session_messages_page, list_runs, get_tool_call_input/_result, send_message, compact_session, delete/edit_queued_message, cancel_run; FE wrappers and callers pass the owning workspace. - Delete the session_pool/run_pool/pool_for_new_session probes. - Delete commands with no frontend caller (from C8): assistant_create_session, assistant_list_sessions, assistant_load_session_messages, assistant_list_tool_calls, assistant_retry_run, plus repository::list_tool_calls, list_sessions, list_non_task_sessions and the FE wrappers. - Add repository::find_manager_session: newest session for the manager's automation_id that is not linked to a workspace_tasks row (kind-agnostic, id tie-break). Used by the scheduled runner, workspace details, get-or-create, and the workspace_list message count. - Fix: the scheduled runner picked any session with the manager's automation_id, so a self-assigned task's newer session could take the scheduled tick and the busy check. It now excludes task sessions, like the UI. - Drop select_workspace_session and its workspace/agent-id filter; the selected DB already scopes the workspace, and descriptor.agent_id is always None.
Since C6a every session/run command opens the owning workspace's DB explicitly, so the persisted `workspaceId`/`agentWorkspaceId` in `SessionContext` were a second, possibly stale, copy of that fact. - Remove `workspace_id` and `agent_workspace_id` from `SessionContext` and from `ToolExecutionContext`; stop writing them in `desired_workspace_context`, the MCP-update path, the scheduled runner and task sessions. - `AssistantDeps` now carries `workspace_id` next to its pool (and the local MCP `ToolBinding` carries it to CLI tool calls). The engine, API/CLI turns, image resolution, CLI cwd, tools (permission/path-grant requests, workspace tasks) and `live_agent_description` read it there. - `available_tools` takes the run's resolved `workspace_root`; the workspace-bound tools (history_query, create_vega_chart, bash_exec, fs_request_grant) and the web opt-in are gated on it. The system prompt's local-execution section is gated the same way and gets the workspace id explicitly. - `emit_event` takes the workspace id explicitly; the event envelope's `workspaceId` is now always present (binding: `string`). - `spawn_run_task`/`start_queued_followup_if_idle` take `AssistantDeps`; `fresh_execution_for_session`/`sweep_session_images` take the command's workspace id. Behaviour: unchanged for every session current code writes (all had `agentWorkspaceId == workspaceId` with a resolvable root). Unreconciled legacy rows with a null or agent-id `agentWorkspaceId` now use the root of the DB they live in instead of none/an engine error. Old `context_json` keeps deserializing (unknown keys are ignored; pinned by `session_context_ignores_removed_keys`) and is rewritten without the keys on next update. New test covers both tool-availability branches.
workspace_get/create/update_agent return Rust WorkspaceAgentDetail (Option for get), not WorkspaceAgentResponse. Mirror that shape in api/client.ts, drop the `as unknown as` cast in AgentBehaviorForm, and report a missing agent as "Agent not found." with a test for the null case.
juacker
force-pushed
the
cleanup/pre-task-communication
branch
from
October 13, 2026 08:27
9e9362d to
08345b4
Compare
…ncile SessionContext persisted copies of the agent's config (MCP servers, execution grants, name, team roster) that four reconcilers refreshed at different times, so edits reached a run only after a UI re-hydrate, and the hydrate's read-modify-write could restore a stale CLI session id. Changes: - SessionContext keeps only automation_id, cli_session_id and cli_session_provider. Old rows with the removed keys still deserialize. - New assistant::run_context: RunContext (never persisted) is resolved once per run in engine::run_session_turn from the live workspace config and roster, and handed to the API turn, the CLI turn and, via ToolBinding, the local MCP server. The MCP server no longer re-reads the session row on every tools/list or tools/call. - build_system_prompt, available_tools and build_trigger_message take &RunContext; the agent description (with skills) is a field. - Manager-session lookup is find-or-create (repository::find_or_create_manager_session) in both the hydrate command and the scheduler; an existing row is never rewritten. - Deleted: desired_workspace_context, the reconcile branches, fresh_execution_for_session, live_agent_description, workspace_agent_runtime_description, resolve_workspace_manager_agent, agent_config_from_row, WorkspaceDescriptor.execution/automation_name, task_session_context, and the title sync. - workspace_update_session_mcp only writes the Main's MCP refs. - FE: WorkspaceContextBar drops the session.context.mcpServerIds fallback and shows the MCP-toggle error inline. Behaviour: - Config edits (MCP, grants, shell/web, roster, skills, name) apply to the next run of any session, with no hydrate or scheduler tick needed. - A session whose agent was deleted or unassigned (or whose library definition is gone) fails at run start with a readable error and the run row is marked failed; it no longer runs on a stale snapshot. A missing workspace root now also fails the run row instead of leaving it queued. - Manager-less workspaces run with web access and no MCP servers; the MCP toggle returns "Set up a main agent for this workspace first." - Hydrate no longer bumps updated_at, so no spurious message reloads. - Task sessions store only the assignee id and resolve its config per run, as before but live.
… to start `run_next_agent` claimed an instance with `next_ready` (marking it and the scheduler's single slot running) and then had several `?` exits before `complete_agent`: missing instance, agent config load/not-found, workspace DB open, manager-session lookup, no enabled provider connection, and manager-session creation/emit. Any of them left the slot held, so every later scheduled run was skipped until a config edit or app restart. Split the tick into `claim_next_instance` + `run_claimed_instance`, wrapped by `release_slot_on_error`: on error, if the instance still holds the slot, it is completed as failed with a retry in 5 minutes (not ready-now, which would hot-loop every 5 s tick and could starve other workspaces). The error is still returned to and logged by the runner loop as before. Adds a regression test that a failed start (no provider) frees the slot and leaves the instance schedulable again.
assign_task no longer builds a throwaway AgentConfig: the connection is picked from the roster row, and the session stores only the assignee id. Each task run resolves MCP servers, grants, execution and roster from the assignee's live config at run start, like a Manager run; an unassigned assignee or a deleted library definition fails the run instead of running on stale settings. first_enabled_connection becomes a free function over the id list so the scheduler and assign_task share it. AgentConfig drops selected_mcp_server_ids and execution, and WorkspaceAgentRow drops the skill/MCP/execution copies: nothing reads them. The dead AgentConfig::required_tools duplicate of the scheduler's tool list goes with them.
…s and notices storage Cleanup C8. Everything removed had no importer or caller. Backend: - Delete 10 registered commands no frontend code invokes: the ai_provider get/set/clear/validate, available providers and provider models commands, provider_connection_get, provider_connection_list_models, get_mcp_server and skill_sources_list, plus their providers/config helpers. - Remove the RunTrigger::Retry and RunTrigger::InterAgentCall variants; migration 20261013000000 maps stored rows of those triggers to user_message and workspace_task. - Remove run-notice storage; the "completed with warnings" status stays. - ipc_surface: lower the parser floor to > 80 (100 commands registered). Frontend: - src/api/client.ts: the legacy "AI Provider Functions" section, getSkillSources and getMcpServer. - src/assistant/client.ts: getProviderConnection and listProviderModels. - ProviderSettings.tsx, the only user of the ai_provider wrappers, was never mounted. ProviderSettings.module.css stays because AssistantProviderSettings imports it; its classes used only by the deleted component are pruned. - AgentCard.tsx and its CSS module, plus their barrel exports. - NoticesBanner and its CSS. Enable react-hooks/rules-of-hooks as an error in both rule blocks. The only two violations were anonymous `default:` mock components in Routes.test.tsx and SettingsModal.test.tsx; they are now named functions. Frontend and backend land together so ipc_surface's every_registered_command_is_invoked_by_the_frontend holds at every commit.
The UI commands and the agents' task tools each had their own workspace_tasks row, column list, mapper and response, and their own WorkspaceAgentRow, roster loader and row builder. The copies had already drifted (roster order, blank-name label). - New assistant/workspace_tasks_repo.rs owns the task row, the column list, load/list/create/update, and WorkspaceTaskResponse (TS shape unchanged; bindings.ts identical). - commands/workspace.rs keeps the single WorkspaceAgentRow loader, now pub(crate), with WorkspaceAgentRow::display_name() as the one label rule (name, or definition id when blank). The task tools use it. - creator_workspace_agent_id replaces find_workspace_agent_for_definition: the definition-id match could never hit (a run's automation_id is the workspace agent id), and it reuses the roster already loaded. Visible changes: - workspace_assignTask / workspace_getTaskResult `task` JSON is the UI response: adds createdByDisplayName, assignedAgentDisplayName, attentionAcknowledgedAt; None-valued optional keys are now omitted instead of null. - workspace_listAgents and the unknown/disabled-agent error hints list agents Main first then by join time (created_at), matching the prompt roster and crew list; previously by id. - A blank-named agent is labelled with its definition id in workspace_listAgents displayName, the error hints, and task cards' assignee/creator names (previously "").
assistant_messages.role, assistant_runs.status/trigger,
assistant_tool_calls.status and the assistant_compactions enums held
serde-JSON strings ('"running"') because they were bound with the generic
JSON helper. workspace_tasks.status and assistant_message_queue.status
were already plain, so hand-written SQL needed to know which spelling
each table used.
- enum_text/parse_enum_text bind and read unit enums as bare strings;
serde stays the single spelling of each variant.
- Migration 20261015000000 unquotes existing rows (guarded so plain
values are left alone) and rebuilds assistant_compactions with
plain-value CHECKs, keeping its indexes.
- ACTIVE_RUN_STATUSES replaces four copies of the non-terminal set;
every literal in repository SQL is now plain.
update_run_status and assistant_cancel_run each kept an inline terminal set that omitted CompletedWithWarnings: update_run_status left completed_at unset for such runs, and cancelling a finished warnings run overwrote it as Cancelled and emitted RunCancelled. RunStatus::is_active/is_terminal is now the single rule; the ACTIVE_RUN_STATUSES SQL list is tested against it.
…ayer WorkspaceAgentRow, its loader, the row and summary builders and the display-name helpers move to assistant/workspace_agents.rs. run_context and the workspace task tools no longer import from commands::workspace; the command layer calls the assistant module instead. Summaries are built from rows directly rather than via the command response type.
…pies ToolExecutionContext copied five RunContext fields at both construction sites. It now holds run_context: Arc<RunContext>, built once in the engine and shared with ToolBinding.
workspace_delete learned which scheduler entries to remove by reading the workspace config, so an unreadable config left the Main's instance behind; it then failed its agent lookup on every fire and was retried every five minutes, logging an error each time, until restart. Scheduler definitions and instances now record their workspace id, and delete removes them with Scheduler::remove_workspace without reading the config. The unused Scheduler::remove_definition goes.
When the worker's turn returned, any run status other than completed, failed or cancelled - including a missing run row or a failed lookup - marked the task completed. Nothing advances the task after that point, so those cases now fail it with an explicit error instead.
AssistantDeps::emit(session, run_id, event) replaces the repeated emit_event(&deps..., workspace_id, ...) boilerplate at 35 call sites.
…m the session store
load_workspace_agent_as_config walked every workspace in the index and returned on the first config it could not read, so one corrupt workspace config stopped every other workspace's scheduled run from starting, each retried every five minutes. Scheduler instances record their workspace id, so the runner now reads only that workspace's config.
…ctory Removing the pool from the index only dropped a handle; the SQLite connections stayed open until their worker threads exited, so on Windows deleting the workspace directory failed with os error 32 while data.sqlite was still held.
juacker
force-pushed
the
cleanup/pre-task-communication
branch
from
October 13, 2026 13:39
0e057c6 to
3de0b87
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pre-feature cleanup for the inter-agent task-communication work. Net about −1,690 lines (+4,646 / −6,335 across 92 files). One commit per item; every commit builds and passes its tests on its own, so any point of the branch can be checked out locally.
Plan and rationale:
TODO.md→ "Pre-feature cleanup" (workspace), data-model reviewplans/db-model-review-backend.md/-frontend.md.Commits (in order)
assistant_session_linksDROP TABLE IF EXISTSmigration; removesparent_session_id,include_ancestors, the ancestor-walking page loop (nowbuild_message_page, unit-tested), FEparentSessionId/includeAncestors.SessionContextfieldstool_scopes(3 writers, 0 readers),inter_agent_call+InterAgentCallContext(never constructed). No migration (serde-defaulted; testsession_context_ignores_removed_keys). Also ends a session-row rewrite ping-pong between the two reconcilers that disagreed ontool_scopes.useAssistantSessionhookgetOrCreateWorkspaceSession). −156 lines.Workspace.tsxpassed a one-time copy (viewingTask) to the panel, so status/title/assignee/sessionIdnever refreshed after open. Now derives the live task from the polleddetails.tasks(fallback to the snapshot). Test fails without the fix.rules-of-hooksviolations (hook afterif (!task) return null;useMemoinside a role branch). No behaviour change. Follow-up: enablereact-hooks/rules-of-hooksin eslint.workspace_idand open only that DB; deletes thesession_pool/run_pool/pool_for_new_sessionprobes and five FE-uncalled commands (assistant_create_session,_list_sessions,_load_session_messages,_list_tool_calls,_retry_run). Onerepository::find_manager_session(newest non-task session,NOT EXISTS, id tie-break) shared by runner and UI. Fix: the scheduled runner could run inside a newer self-assigned task's transcript.SessionContextworkspace_id/agent_workspace_idremoved from the persisted context and the tool context; the id now comes fromAssistantDeps/ToolBinding/command args. Tool gate that readagent_workspace_id.is_some()now uses the run's resolved workspace root (test for both branches).emit_eventtakes the workspace explicitly; eventworkspaceIdis always set. Old rows still load.WorkspaceAgentDetailas unknown ascast; explicit "Agent not found." + test.SessionContextkeeps onlyautomation_id+ CLI session id/provider. New non-persistedRunContext(MCP ids, execution, roster, name, description) built once per run from live config, for API and CLI paths (ridesToolBinding). Deletes the reconcile code; get-or-create never rewrites existing rows (fixes stale CLI session id write-back and spuriousupdated_atbumps). MCP toggle edits the main agent's config, errors visibly without one. Task runs load the assignee's live config. A run whose agent is gone fails at start.run_next_agentafternext_readyskippedcomplete_agent, so the agent never ran again until restart/config edit. On a start error the slot is now released, the run marked failed and retried in 5 min. Regression test fails without the fix.AgentConfiginassign_task; sharedfirst_enabled_connection. F5:AgentConfiglosesselected_mcp_server_ids/execution; deadrequired_tools/agent_config_to_definitionand unreadWorkspaceAgentRowfields removed. Behaviour: a task whose assignee was removed fails instead of running on stale settings.commands/provider.rsand dead helpers;AppConfig.ai_providerdropped (old config still loads, tested).RunTrigger::Retry/InterAgentCalldeleted with a migration mapping legacy rows (quoted and plain forms, tested);inter_agent_call_depthremoved.notices_jsonno longer read/written;AssistantRun.noticesgone; "completed with warnings" status kept. Column drop in C9. FE: legacyai_providerwrappers,ProviderSettings,AgentCard,NoticesBanner, unused provider/MCP/skill wrappers removed;react-hooks/rules-of-hooksis now an error. FE and Rust halves are one commit soipc_surfaceholds at every commit.user_response_at→attention_acknowledged_at(keeps old tasks out of the attention count), then dropsworkspace_tasks.user_response/user_response_at/result_json,assistant_sessions.kind/title,assistant_runs.notices_json. In-memory notices become onehad_warningsflag (RunNotice/RunNoticeKinddeleted; same status).SessionKindgone. Migration tests seed old schemas with raw SQL. Agent-visible: task results no longer includeresultJson(it duplicatedresultSummary).assistant/workspace_tasks_repo.rsholds the only task row/columns/load/list/create/update andWorkspaceTaskResponse; the tool-side copies are gone. OneWorkspaceAgentRowloader withdisplay_name(). Visible: agent task-tool JSON now equals the UI response (adds display names, omits empty optionals);workspace_listAgentsorders Main first then join time; blank agent names show the definition id.assistant_list_runs/workspace details return ≤2 runs (newest + newest active) instead of the whole history every 5 s poll. DropsWorkspaceConfig.preferred_provider_connection_id(old config loads, tested),WorkspaceAgentSummary.agent_definition_id/role,WorkspaceAgentResponse.role; manager check usesagent.id(tested). Context bar: the provider picker and MCP toggle are disabled while a save runs, the values are reloaded from the backend afterwards, and the bar remounts per workspace (key), so overlapping saves can't happen.assistant_messages.role,assistant_runs.status/trigger,assistant_tool_calls.status(guarded byjson_valid/json_type), rebuildsassistant_compactionswith plain CHECKs.enum_text/parse_enum_textbind/read every enum; oneACTIVE_RUN_STATUSES; no hand-written SQL special-cases quoted values any more. Migration test seeds quoted + plain rows. One-time rewrite of all message rows on upgrade.started_atties by id inget_active_runcurrent_runsordering.assistant_cancel_rundid not treatCompletedWithWarningsas finished, so cancelling such a run overwrote it as Cancelled and emittedRunCancelled. OneRunStatus::is_active()/is_terminal()rule now.| 20 | — | move workspace agent roster out of the command layer | Roster code moves to
assistant/workspace_agents.rs; the assistant no longer imports fromcommands/. || 21 | — | tool context holds the run context |
ToolExecutionContextholdsArc<RunContext>instead of field copies. || 22 | — | drop scheduler entries by workspace id on delete | Scheduler instances carry
workspace_id; deleting a workspace removes them (otherwise the C6c start-failure path retried them every 5 min). || 23 | — | fail a task whose worker run is missing or still active | Fix: such a task was reported as completed. The
BLOCKED:rule applies only to terminal runs. || 24 | — |
AssistantDeps::emitfor session events | One helper replaces repeated emit boilerplate (−58 lines). || 25 | — | generate workspace agent detail types from Rust | ts-rs bindings replace hand-written TS copies of
WorkspaceAgentDetailand related types. || 26 | — | drop unused
tabId/getActiveSessionForTabfrom the session store | Leftover of C5. || 27 | — | resolve a scheduled agent in its own workspace only | Looks up the agent in its own workspace instead of scanning all of them, so another workspace's unreadable config can't block it. |
| 28 | — | close the workspace database before deleting its directory | Fix: on Windows, deleting a workspace failed with os error 32 because the DB pool was still open. |
Review
Every commit had a read-only Rust or frontend review (production_quality) before the first squash. Four whole-PR review rounds followed. On owner direction (simplicity first), the fixes from those rounds were cut back to the items above (rows 20–28): regressions and changes that remove code. Hardening of older behaviour was dropped from this PR. That covers the unreadable-config sweep on MCP/skill delete,
ConfigManager::try_update, MCP secret-clear ordering, the manual-run retry cap and the optimistic-save hook. Each of the 28 commits passescargo check --all-targetsandtsc --noEmiton its own.Not verified
DROP TABLEmigration against a real user DB that still has the table (the migration chain creates then drops it in the test DB).Upgrade note: migrations
20261013000000–20261015000000rewrite stored enum/trigger values and drop columns; a binary built frommaincannot read a DB migrated by this branch.