feat(api): chat AI tools — tag CRUD, full executor routing, declarative ordering#185
Conversation
…ve ordering Bundles four correlated chat-agent changes: - #83: tag chat tools (list/create/update/delete_tag) delegating to existing MediatR handlers, registered in DI and catalogued under tags.read/write/delete. - #89: ReorderGoalsTool (mirrors ReorderHabitsTool) + unit tests for the new/write tag and goal tools; reorder_goals added to GoalsWrite.chatTools. - #88: full executor routing — every mutating MCP method (GoalTools, TagTools, ProfileTools, NotificationTools, UserFactTools, SubscriptionTools) now routes through McpExecutorBridge → IAgentOperationExecutor for shared policy + audit, mapping mismatched methods to their consolidated chat ops. Hard cases routed too: assign_tags via a new tag_ids id-path on AssignTagsTool (id-based, replace-all, no auto-create; MCP id contract unchanged) and get_referral_code via a new GetReferralCodeTool + ReferralsWrite capability + WriteReferrals scope (added to ClaudeDefaultScopes). Destructive routed deletes (goal/tag/notification/user-fact) accept and forward a confirmation token. No mutating MCP method remains on direct MediatR. update_goal_progress widened to accept goal_id so its MCP method can route. - #87: tool ordering is now data — int Order default-interface member on IAiTool (create_habit=0, create_sub_habit=1, assign_tags=2, default int.MaxValue); the hardcoded switch in ProcessUserChatCommand is replaced by the registry lookup. Adds Chat/Tools/README.md documenting the contract, Order, catalog invariant, and MCP-routing relationship. Existing MCP toolset unit tests migrated to the executor-routed pattern. Refs thomasluizon/orbit-ui-mobile#83, thomasluizon/orbit-ui-mobile#89, thomasluizon/orbit-ui-mobile#88, thomasluizon/orbit-ui-mobile#87 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
This lands four correlated chat-agent changes cleanly. The executor-routing refactor is the biggest structural move — every MCP mutation now flows through McpExecutorBridge → IAgentOperationExecutor, giving uniform ownership pre-checks, policy gating, and audit trails where previously each tool called MediatR directly. The new chat tools (tag CRUD, reorder_goals, get_referral_code) are all properly registered in DI and catalogued in AgentCatalogService (startup throws on any unmapped tool, so the catalog invariant is enforced). The IAiTool.Order default interface member cleanly replaces the hardcoded switch. The assign_tags id-path addition is backward-compatible, and moving get_referral_code to isMutation: true under referrals.write is semantically correct. Test coverage is solid: unit tests for all new tools, MCP unit tests migrated to the executor-routed pattern, and a dedicated McpMutationExecutorRoutingTests integration suite that verifies audit rows, PendingConfirmation gate, and read-only credential denial across the newly-routed toolsets.
Summary
One backend PR bundling four correlated chat-agent changes (implemented in dependency order: tag/goal chat tools + hard-case prerequisites → executor routing → ordering refactor).
#83 — Tag CRUD chat tools
ListTagsTool/CreateTagTool/UpdateTagTool/DeleteTagTool(inTagTools.cs), each delegating to its existing MediatR handler.IAiToolin DI and catalogued:tags.read←list_tags,tags.write←create_tag/update_tag,tags.delete←delete_tag.#89 — reorder_goals + write-tool test coverage
ReorderGoalsTool(mirrorsReorderHabitsTool, schemapositions[]={goal_id,position}, sendsReorderGoalsCommand); added togoals.write.reorder_goals, andget_referral_code; metadata test registers all six.#88 — Full executor routing (no direct-MediatR mutation exceptions)
GoalTools,TagTools,ProfileTools,NotificationTools,UserFactTools,SubscriptionToolsnow routes throughMcpExecutorBridge→IAgentOperationExecutor(AgentExecutionSurface.Mcp), sharing policy evaluation + theAgentAuditLogstrail. Reads stay on MediatR.update_profile_preferences,update_notifications,delete_notifications,delete_user_facts).assign_tags:AssignTagsToolgains an optionaltag_idsid-path (tracked-load by id, replace-all, no auto-create); MCP forwardstag_ids, preserving its id-based external contract. Thetag_namesauto-create path is unchanged.get_referral_code: newGetReferralCodeTool+ newReferralsWritecapability (referrals.write) +WriteReferralsscope (added toClaudeDefaultScopes);get_referral_coderemoved fromReferralsRead.mcpTools.update_goal_progressonly acceptedgoal_name; widened to also acceptgoal_idso the MCP method can route losslessly.#87 — Declarative tool ordering
int Order => int.MaxValue;default-interface member onIAiTool;create_habit=0,create_sub_habit=1,assign_tags=2.ProcessUserChatCommandordering usesregistry.GetTool(name)?.Order ?? int.MaxValue(hardcoded switch removed; unknown tools now run last instead of tying withcreate_sub_habit).Chat/Tools/README.mddocuments theIAiToolcontract,Orderconvention, the catalog-entry-required startup invariant, and the MCP-routes-through-executor relationship.Validation
dotnet build Orbit.slnx: PASS (0 errors).Orbit.Application.Tests: 1718 passed.Orbit.Infrastructure.Tests: 936 passed (incl. the six MCP toolset test files migrated to the executor-routed pattern).McpMutationExecutorRoutingTests(new) +McpHabitExecutorRoutingTests: 9 passed — per-toolset audit-row + read-only-credential-denial assertions, theassign_tagsid-path tag replacement,get_referral_code→referrals.write, anddelete_user_factspending-confirmation.ApiKeyTests403s — both confirmed identical on the clean baseline).Refs thomasluizon/orbit-ui-mobile#83, thomasluizon/orbit-ui-mobile#89, thomasluizon/orbit-ui-mobile#88, thomasluizon/orbit-ui-mobile#87
🤖 Generated with Claude Code