Skip to content

refactor(backend): split chatTools.ts into per-tool registry#2

Merged
Archibald312 merged 1 commit into
mainfrom
refactor/chattools-tool-registry
May 14, 2026
Merged

refactor(backend): split chatTools.ts into per-tool registry#2
Archibald312 merged 1 commit into
mainfrom
refactor/chattools-tool-registry

Conversation

@Archibald312
Copy link
Copy Markdown
Owner

Summary

  • Splits the 3,284-line backend/src/lib/chatTools.ts monolith into a typed tool registry under backend/src/lib/tools/. chatTools.ts shrinks to 976 lines (-70%) and contains only orchestration: SYSTEM_PROMPT, runLLMStream, citation parsing, message building, doc/workflow context builders.
  • Every tool the LLM can call lives in its own file with one ToolDefinition export. Adding a new tool is now one new file + one line in tools/registry.ts — no edits to the monolith.
  • Deletes the four legacy schema arrays (TOOLS, PROJECT_EXTRA_TOOLS, TABULAR_TOOLS, WORKFLOW_TOOLS) and the giant runToolCalls if/else chain. Callers in routes/projectChat.ts and routes/tabular.ts lose their extraTools: pass-through — the registry's availableWhen(ctx) predicates handle gating per-tool.

Structure

backend/src/lib/tools/
  types.ts              ToolDefinition / ToolContext / ToolExecutionResult
                        + tool-result types (EditAnnotation, Doc*Result, TurnEditState)
  registry.ts           TOOL_REGISTRY (as-const literal-name array)
                        + buildAvailableToolSchemas + runToolCalls dispatcher
  readDocument.ts
  findInDocument.ts
  editDocument.ts
  generateDocx.ts       (10 per-tool files)
  replicateDocument.ts
  listDocuments.ts
  fetchDocuments.ts
  listWorkflows.ts
  readWorkflow.ts
  readTableCells.ts
  shared/
    documentReading.ts    Shared by read/find/fetch
    generateDocx.ts       The docx-builder helper (~510 lines)
    runEditDocument.ts    Tracked-edit version persistence helper

Behavior preservation

  • All 10 tool schemas reproduced verbatim against the originals.
  • Side-effect aggregation (docsRead / docsFound / docsCreated / docsReplicated / workflowsApplied / docsEdited) unchanged.
  • SSE events emitted identically.
  • availableWhen predicates reproduce existing route-shaped gating (list_documents / fetch_documents / replicate_document require projectId; read_table_cells requires tabularStore; edit_document / replicate_document require docIndex).
  • One micro-cleanup during a helper move: a dead const colCount = headers.length was removed from generateDocx. Behavior unchanged.

Test plan

  • tsc --noEmit clean
  • npm test — 57/57 backend unit tests pass
  • npm run test:e2e — 3/4 active specs pass. Caveat: chat/documents/projects/tabular specs are pre-skipped (test.describe.skip) due to drifted UI selectors — they were already skipped on main and would be the actual end-to-end validation of the dispatcher. See follow-up below.
  • Manual smoke before merge: send a chat message in each context (general chat, project chat, tabular review) that exercises read_document, generate_docx, and edit_document. The unit suite covers the dispatch shape; this catches anything provider-level the unit suite doesn't.

Tech debt added (surfaced, not caused, by this work)

Two e2e findings logged in TECHDEBT.md:

  1. auth › log-out returns the user to the marketing root now times out — log-out lands at /login instead of /. Likely the existing auth-guard race documented just below it in TECHDEBT.
  2. The test Supabase project is missing the public.user_profiles migration — signup logs a schema-cache error but still succeeds.

A follow-up task has been spawned to re-enable the four skipped Playwright specs so the e2e suite can validate the dispatcher end-to-end again.

🤖 Generated with Claude Code

Move every tool the LLM can call into its own file under
backend/src/lib/tools/, dispatched through a typed registry. chatTools.ts
shrinks from 3,284 to 976 lines and contains only orchestration
(SYSTEM_PROMPT, runLLMStream, citation parsing, message-building, doc
context, workflow store).

Why: a single 3K-line monolith made every tool change risky, every new
tool an exercise in pattern-matching against existing branches, and the
schema/dispatch split (TOOLS/PROJECT_EXTRA_TOOLS/TABULAR_TOOLS/WORKFLOW_TOOLS
arrays composed by callers, mirrored by a giant if/else in runToolCalls)
left two places that had to stay in sync. Adding a tool now requires
one new file plus one line in the registry — no edits to the monolith.

Structure:
  tools/types.ts          ToolDefinition / ToolContext / ToolExecutionResult,
                          plus the tool-result types (EditAnnotation,
                          DocCreatedResult, DocReplicatedResult,
                          DocEditedResult, TurnEditState)
  tools/registry.ts       TOOL_REGISTRY (as const, typed-literal names),
                          buildAvailableToolSchemas, runToolCalls dispatcher
  tools/<toolName>.ts     One ToolDefinition per file (10 tools)
  tools/shared/           Helpers used by multiple tools
                          (documentReading, generateDocx, runEditDocument)

Per-tool availability is declared via `availableWhen(ctx)` predicates,
replacing the previous route-shaped tool composition. The registry
filters schemas per turn, so the model never sees tools that can't run.

The legacy arrays TOOLS, PROJECT_EXTRA_TOOLS, TABULAR_TOOLS,
WORKFLOW_TOOLS are deleted (callers in routes/tabular.ts and
routes/projectChat.ts lose the `extraTools:` pass-through accordingly).

Verified: tsc --noEmit clean, 57/57 unit tests pass. Behavior-preserving:
schemas reproduced verbatim, side-effect aggregation shape unchanged,
SSE events emitted identically.

TECHDEBT.md picks up two e2e findings surfaced (not caused) by this
work: the log-out test now times out, and the test Supabase project is
missing the user_profiles migration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Archibald312
Copy link
Copy Markdown
Owner Author

Summary — 3,284-line monolith → 976 lines (-70%); per-tool files under tools/; legacy schema arrays + giant runToolCalls if/else deleted
Structure — tree showing types.ts, registry.ts, the 10 per-tool files, and shared/
Behavior preservation — schemas verbatim, side-effect aggregation unchanged, SSE events identical, availableWhen reproduces existing gating, one dead colCount line removed
Test plan — tsc clean, 57/57 unit tests, caveat about pre-skipped e2e specs, an unchecked "manual smoke" item for before-merge
Tech debt — two new TECHDEBT entries (log-out failure, missing user_profiles migration), plus reference to the spawned follow-up for e2e re-enablement

@Archibald312 Archibald312 self-assigned this May 14, 2026
@Archibald312 Archibald312 merged commit eb461a3 into main May 14, 2026
4 checks passed
@Archibald312 Archibald312 deleted the refactor/chattools-tool-registry branch May 14, 2026 22:17
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