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

✅ merged · #2 · Archibald312/GordonOSS ← Archibald312/GordonOSS · opened 12d ago by Archibald312 · merged 12d ago by Archibald312 · self · +2,576-2,344 across 19 files · ↗ on GitHub

From the PR description

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

Our analysis

Split chatTools monolith into a typed tool registry — read the full analysis →

Think the analysis missed something the PR description covers?

Capture this PR into my fork

Download a Markdown prompt that tells Claude how to port every commit in this PR into your working tree. Run it via claude -p < capture-pull-2.md from inside the repo you want the changes in.

⬇ Download capture-pull-2.md