Orchestrata Ultimate / Specs / 103-tool-calling-refactor
103-tool-calling-refactor: Tool-Calling Code Refactoring
A capability that lets orchestrata refactor an existing codebase in place by driving an LLM (DeepSeek deepseek-v4-pro) through function/tool calls: the model reads, searches, and outlines real source with sandboxed tools, applies exact-match edits, and verifies them — guided by a refactoring system prompt.
At a glance
| Item | Value |
|---|---|
| Spec directory | specs/103-tool-calling-refactor/ |
| Status line in the spec | Draft |
| Requirement IDs defined | 25 |
With a @traces citation in src/ |
21 (84.0%) |
With a @traces citation in tests/ |
1 (4.0%) |
| Tracked files | 2 |
| RTM page | 103-tool-calling-refactor |
Reading this spec
This spec adds the ability to refactor an existing codebase in place by driving an LLM (DeepSeek deepseek-v4-pro) through function calls. The model reads, searches and outlines real source with sandboxed tools, applies exact-match edits and verifies them, guided by a refactoring system prompt. The gap it fills: the volk pipeline from Concierge to CodeDirector is a greenfield builder that scaffolds every intent as a new project and emits whole files, so it cannot modify an existing repository. The REFACTOR intent type, its classifier keywords, AST context injection and routing to U:Cd already exist, but there was no tool-calling layer, no refactor execution driver and no refactor system prompt.
Four user stories set the scope: a developer requests a targeted refactor, the refactor is confined to a safe scope, an operator watches and audits it, and it reuses the existing code-intelligence tools. The 25 requirement IDs (REQ-103-NNN) fall into groups. Protocol fields: Message gains optional tool_calls, tool_call_id and name, all defaulting to empty so the change is backward compatible, and the adapter parses tool_calls from the response and passes them up through the provider manager. Tools: a machine-readable TOOL_SCHEMAS list in OpenAI function-calling format and a RefactorTools.dispatch method, including ast_outline, edit_file and a run_check tool limited to whitelisted read-only commands (ruff, py_compile, pytest, mypy). Agent loop: the full assistant and tool message history persists across turns, and the entry point is refactor(task, root, ...). The prompt tells the model to observe before mutating, make minimal edits, and verify before finishing. Routing a REFACTOR intent to this capability from the CodeDirector path is marked as deferred.
The code is in the refactor package: refactor tools (9 requirements), the refactor agent, the provider protocols and the DeepSeek adapter. The requirements traceability matrix lists each ID. The spec's own status line reads Draft and it tracks 2 files. Measured coverage is 21 of 25 requirements cited by at least one @traces in src/ (84.0%), spread over 5 modules; the module citing the most of them is orchestrata_ultimate/refactor/tools (9 requirements). In tests/, 1 requirement is cited by a test-side @traces. 4 requirements have no citing symbol: REQ-103-004 (backward-compatible defaults), the two prompt requirements REQ-103-020 and REQ-103-021, and REQ-103-024 (ordered logging of tool calls); the behaviour may still exist in code that was never annotated.
Requirements
Checkbox is the state recorded in the requirements checklist; Traced by is the count of src/ symbols whose @traces names the ID.
| ID | Done | Requirement | Traced by |
|---|---|---|---|
REQ-103-001 |
no | Message (SPEC-011) gains optional tool_calls, tool_call_id, and name fields |
1 |
REQ-103-002 |
no | ProviderRequest gains optional tools and tool_choice fields |
1 |
REQ-103-003 |
no | RawProviderResponse and ProviderResponse gain a tool_calls field |
2 |
REQ-103-004 |
no | All new protocol fields default to empty/None, so the added tool-calling surface is fully backward compatible |
none |
REQ-103-005 |
no | The DeepSeek adapter forwards tools/tool_choice to the API via request parameters |
1 |
REQ-103-006 |
no | The adapter serialises assistant tool_calls and tool-role messages (with tool_call_id/name) into the API payload |
1 |
REQ-103-007 |
no | The adapter parses choices[].message.tool_calls into RawProviderResponse.tool_calls, and ProviderManager propagates them through to ProviderResponse |
1 |
REQ-103-008 |
no | All RefactorTools file tools resolve paths under a sandbox root; any path escaping root is rejected |
2 |
REQ-103-009 |
no | Every tool returns a string and never raises into the loop — failures are returned as ERROR: ... text |
1 |
REQ-103-010 |
no | A machine-readable TOOL_SCHEMAS list (OpenAI function-calling format) describes every tool, and RefactorTools.dispatch(name, arguments) -> str executes one tool by name |
2 |
REQ-103-011 |
no | read_file(path) returns raw file contents so exact snippets can be copied for edits |
1 |
REQ-103-012 |
no | list_dir(path) and grep(pattern, path) provide discovery over .py files |
2 |
REQ-103-013 |
no | ast_outline(path) surfaces classes/functions with signatures and line numbers |
1 |
REQ-103-014 |
no | edit_file(path, old_string, new_string) replaces ONE exact, unique occurrence, rejecting the edit if old_string is absent or non-unique |
1 |
REQ-103-015 |
no | write_file(path, content) creates or overwrites a file (for new helper modules) |
1 |
REQ-103-016 |
no | run_check(command) runs a whitelisted, read-only verification command (ruff, py_compile, pytest, mypy) from root |
1 |
REQ-103-017 |
no | The loop sends the system prompt + task + TOOL_SCHEMAS, and while the model returns tool_calls, executes each via dispatch, appends one tool message per call keyed by tool_call_id, and re-invokes the model |
1 |
REQ-103-018 |
no | The loop terminates when the model returns content with no tool calls (status="completed"), after max_iterations (status="max_iterations"), or on a fatal error (status="error") |
1 |
REQ-103-019 |
no | The full assistant/tool message history is preserved across turns so the model retains continuity | 1 |
REQ-103-020 |
no | The refactoring system prompt instructs the model to OBSERVE before MUTATING (read/outline/grep first), make MINIMAL surgical edits via edit_file, preserve behavior unless asked otherwise, and VERIFY with run_check before finishing |
none |
REQ-103-021 |
no | The prompt forbids fabricating file contents and instructs the model to copy exact snippets from read_file for edit_file |
none |
REQ-103-022 |
no | refactor(task: str, root: str | Path, model: str = "deepseek-v4-pro", max_iterations: int = 25) -> RefactorResult runs the tool-use loop and returns a RefactorResult |
1 |
REQ-103-023 |
no | RefactorResult records files_changed, iterations, the ordered tool_calls trace, verified, and status |
2 |
REQ-103-024 |
no | Every tool call (name, arguments, truncated result) and model turn is logged via the SPEC-100 structured logger, in order | none |
REQ-103-025 |
no | When intent classification yields IntentType.REFACTOR, the CodeDirector path MAY dispatch to this refactor capability instead of greenfield scaffolding — a deferred/future MAY requirement (wiring tracked separately); the refactor capability remains independently invocable via the refactor() entry point and a CLI regardless of this wiring |
1 |
Code that traces this spec
Checklists
checklists/requirements.md
The requirement list above comes from this file; 0 of 25 checkbox items are ticked.
Full spec text
Verbatim from specs/103-tool-calling-refactor/spec.md; headings are pushed down two levels.
SPEC-103: Tool-Calling Code Refactoring
Metadata
- Spec ID: 103-tool-calling-refactor
- Title: Tool-Calling Code Refactoring — Agentic In-Place Editing of an Existing Codebase
- Author: Mohamed
- Status: Draft
- Created: 2026-05-28
- Last Updated: 2026-05-28
- Priority: P1
- Version: 0.1.0
- Extends: SPEC-011 (Provider Manager), SPEC-028 (LLM Client Facade)
Overview
What
A capability that lets orchestrata refactor an existing codebase in place by
driving an LLM (DeepSeek deepseek-v4-pro) through function/tool calls: the
model reads, searches, and outlines real source with sandboxed tools, applies
exact-match edits, and verifies them — guided by a refactoring system prompt.
Why
The volk pipeline (Concierge→…→CodeDirector) is a greenfield builder: every
intent is scaffolded as a new project, and the Coder emits whole FILE: blocks
(deepseek_coder_driver). It cannot modify an existing repository in place. The
REFACTOR intent type, classifier keywords, AST context injection, and routing
to U:Cd already exist (orchestrator/impact/, knowledge_graph/volk_context_injector.py),
but there is no tool-calling layer, no refactor execution driver, and no
refactor system prompt. A refactor requires the model to observe existing
code and make surgical changes — which is precisely what tool calls enable and
whole-file regeneration does not.
How
- Extend the provider protocol + DeepSeek adapter with OpenAI-compatible
tool-calling (
tools/tool_choicein,tool_callsout). - Provide a small sandboxed tool registry the model can call: read / list /
grep / AST outline (observe),
edit_fileexact-match +write_file(mutate),run_check(verify). - Run a tool-use loop that sends the tools + a refactoring system prompt, executes each requested tool call, feeds results back, and iterates until the model returns a final answer or a turn budget is reached.
- Expose a
refactor(task, root, ...)entry point returning a structured result (files touched, tool-call trace, verification outcome).
User Scenarios
US-001: Developer requests a targeted refactor
As a developer I want to ask orchestrata to "add type hints and docstrings to module X" So that the existing file is improved in place without regenerating it Acceptance Criteria:
- The model reads the real file before editing (a
read_file/ast_outlinecall precedes anyedit_file). - Only the targeted file(s) are modified; no unrelated files are rewritten.
- The edited file still parses (
run_checkwithpy_compile/ruffpasses). - A trace of every tool call (name + arguments + result summary) is captured.
US-002: Refactor confined to a safe scope
As a maintainer I want the refactor to be unable to touch anything outside the target directory So that an autonomous run cannot corrupt the wider repo or read secrets Acceptance Criteria:
- All tool file access is confined to a sandbox
root; path traversal is rejected. run_checkonly executes whitelisted, read-only verification commands.- API keys/secrets are never passed to tools or logged in the tool trace.
US-003: Operator watches and audits the refactor
As an operator I want to see the model's tool calls and edits as they happen So that I can trust the change and debug a bad run Acceptance Criteria:
- Each tool call and its result are logged (structured, SPEC-100) in order.
- The final result reports files changed, iterations used, and pass/fail of verification.
- A failed/aborted refactor leaves a clear diagnostic, not a half-broken file set.
US-004: Refactor reuses the existing code-intelligence tools
As a core developer I want the refactor agent to use the existing AST/analysis tooling So that the model reasons over verified structure, not hallucinated layout Acceptance Criteria:
- An
ast_outlinetool surfaces classes/functions/signatures with line numbers. greplocates symbols across the tree before edits.
Data Models
Tool schema (OpenAI/DeepSeek function-calling, sent to the model)
{
"type": "function",
"function": {
"name": "edit_file",
"description": "Replace ONE exact, unique occurrence of old_string with new_string.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"old_string": {"type": "string"},
"new_string": {"type": "string"}
},
"required": ["path", "old_string", "new_string"]
}
}
}
Tool-call envelope (returned by the model / parsed by the adapter)
{ "id": "call_abc", "type": "function",
"function": {"name": "read_file", "arguments": "{\"path\": \"x.py\"}"} }
Conversation message fields (added to Message, SPEC-011)
| Field | Type | Role | Meaning |
|---|---|---|---|
tool_calls |
list[dict] | None |
assistant | tool calls the model requested |
tool_call_id |
str | None |
tool | the call this result answers |
name |
str | None |
tool | the tool's name |
RefactorResult
| Field | Type | Required | Meaning |
|---|---|---|---|
task |
str |
✓ | the refactor instruction |
root |
str |
✓ | sandbox root the refactor ran in |
files_changed |
list[str] |
✓ | files written/edited |
iterations |
int |
✓ | tool-use loop turns consumed |
tool_calls |
list[dict] |
✓ | ordered trace: name, arguments, result summary |
verified |
bool | None |
✓ | result of the final run_check, or None if not run |
final_message |
str |
✓ | the model's closing summary |
status |
"completed" | "max_iterations" | "error" |
✓ | terminal status |
Functional Requirements
FR-001 — Tool-calling protocol fields
The provider protocol MUST carry tool-calling data end to end.
REQ-103-001:Messagehas optionaltool_calls,tool_call_id,name.REQ-103-002:ProviderRequesthas optionaltoolsandtool_choice.REQ-103-003:RawProviderResponseandProviderResponsehavetool_calls.REQ-103-004: All fields default to empty/None — fully backward compatible.
FR-002 — DeepSeek adapter tool support
REQ-103-005: The adapter forwardstools/tool_choiceto the API (via requestparameters).REQ-103-006: The adapter serialises assistanttool_callsandtoolrole messages (withtool_call_id/name) into the API payload.REQ-103-007: The adapter parseschoices[].message.tool_callsintoRawProviderResponse.tool_calls; the ProviderManager propagates them.
FR-003 — Sandboxed tool registry
REQ-103-008: All file tools resolve paths under aroot; any path escapingrootis rejected.REQ-103-009: Each tool returns a string; tools NEVER raise into the loop — failures returnERROR: ....REQ-103-010: A machine-readableTOOL_SCHEMASlist (OpenAI function format) describes every tool; adispatch(name, args)executes one by name.
FR-004 — Tool catalog
The registry MUST provide at minimum:
REQ-103-011:read_file(path)— raw contents (so exact snippets can be copied for edits).REQ-103-012:list_dir(path)andgrep(pattern, path)— discovery over.pyfiles.REQ-103-013:ast_outline(path)— classes/functions, signatures, line numbers (code intelligence).REQ-103-014:edit_file(path, old_string, new_string)— replace ONE exact, unique occurrence; reject if absent or non-unique.REQ-103-015:write_file(path, content)— create/overwrite (for new helper modules).REQ-103-016:run_check(command)— run a whitelisted, read-only verification command (ruff,py_compile,pytest,mypy) fromroot.
FR-005 — Tool-use loop
REQ-103-017: A loop sends the system prompt + task +TOOL_SCHEMAS; while the model returnstool_calls, it executes each viadispatch, appends onetoolmessage per call (keyed bytool_call_id), and re-invokes the model.REQ-103-018: The loop terminates when the model returns content with no tool calls (completed), or aftermax_iterations(max_iterations), or on a fatal error (error).REQ-103-019: The full assistant/tool message history is preserved across turns so the model has continuity.
FR-006 — Refactoring system prompt
REQ-103-020: A refactoring system prompt instructs the model to OBSERVE before MUTATING (read/outline/grep first), make MINIMAL surgical edits viaedit_file, preserve behavior unless asked otherwise, and VERIFY withrun_checkbefore finishing.REQ-103-021: The prompt forbids fabricating file contents and instructs the model to copy exact snippets fromread_fileforedit_file.
FR-007 — Entry point & result
REQ-103-022:refactor(task, root, model="deepseek-v4-pro", max_iterations=...)runs the loop and returns aRefactorResult.REQ-103-023: The result recordsfiles_changed,iterations, the orderedtool_callstrace,verified, andstatus.
FR-008 — Observability
REQ-103-024: Every tool call (name, arguments, truncated result) and model turn is logged via the SPEC-100 structured logger, in order.
FR-009 — Pipeline integration (future)
REQ-103-025: When intent classification yieldsIntentType.REFACTOR, the CodeDirector path MAY dispatch to this refactor capability instead of greenfield scaffolding. (Wiring tracked separately; the capability is independently invocable via the entry point and a CLI.)
Error Conditions
| ID | Trigger | Severity | Handling |
|---|---|---|---|
| ERR-103-001 | edit_file old_string not found |
WARNING | Return ERROR: not found; model retries with corrected snippet |
| ERR-103-002 | edit_file old_string not unique |
WARNING | Return ERROR: not unique (N matches); model adds context |
| ERR-103-003 | Path escapes sandbox root | ERROR | Reject with ERROR: path escapes sandbox; never touch the path |
| ERR-103-004 | run_check command not whitelisted |
ERROR | Reject; list permitted prefixes |
| ERR-103-005 | run_check times out (120s) |
WARNING | Return ERROR: command timed out; loop continues |
| ERR-103-006 | Model exceeds max_iterations |
WARNING | Stop with status=max_iterations; return partial trace + files changed |
| ERR-103-007 | LLM call fails after retries (e.g. 429) | ERROR | Surface as status=error with the cause; do not leave a half-edited file unreported |
| ERR-103-008 | Tool raises unexpectedly | ERROR | Caught; returned as ERROR: <type>: <msg> so the loop survives |
API Contracts
def refactor(
task: str,
root: str | Path,
model: str = "deepseek-v4-pro",
max_iterations: int = 25,
) -> RefactorResult: ...
class RefactorTools:
def __init__(self, root: str | Path) -> None: ...
def dispatch(self, name: str, arguments: dict) -> str: ...
# read_file, list_dir, grep, ast_outline, edit_file, write_file, run_check
TOOL_SCHEMAS: list[dict] # OpenAI/DeepSeek function specs
Security Requirements
- SEC-103-001: Filesystem access is confined to the sandbox
root; path traversal (.., absolute paths escaping root, symlink escape) is rejected before any I/O. - SEC-103-002:
run_checkexecutes only whitelisted, read-only commands (no network, no package install, nogit push); arbitrary shell is refused. - SEC-103-003: API keys/secrets are read from env only and never appear in tool arguments, tool results, or the persisted tool-call trace.
- SEC-103-004: The model cannot escape the tool set —
dispatchonly routes to registered tool names; unknown names return an error.
Dependencies
| Component | Spec | Purpose |
|---|---|---|
| Provider Manager | SPEC-011 | Carries tools/tool_calls; routes to DeepSeek |
| LLM Client Facade | SPEC-028 | The tool-use loop builds on the client path |
| Logging & Exceptions | SPEC-100 | Structured tool-call/turn logging |
| CXP / impact routing | SPEC-004 / orchestrator.impact | IntentType.REFACTOR classification + routing (integration, FR-009) |
| DeepSeek API | external | deepseek-v4-pro with OpenAI-compatible function calling |
Testing Requirements
- TST-103-001: Unit — each tool:
read_file,list_dir,grep,ast_outline,edit_file(found / not-found / non-unique),write_file,run_check(whitelisted / rejected), and sandbox path-traversal rejection. - TST-103-002: Unit —
dispatchroutes valid names, errors on unknown names and bad arguments, and never raises. - TST-103-003: Unit — adapter serialises assistant
tool_calls+toolmessages and parsestool_callsfrom a mocked-shape response (real services only per project rules; use a recorded API-shape fixture, not unittest.mock). - TST-103-004: Integration (on-demand, real DeepSeek) — a real refactor on a temp module (e.g., "add type hints") edits in place and
run_checkpasses; the tool-call trace shows read-before-edit. - TST-103-005: Security — path traversal blocked; non-whitelisted
run_checkrefused; no secret appears in the trace. - TST-103-006: Loop — terminates on no-tool-calls, on
max_iterations, and on injected tool errors without corrupting state.
Definition of Done
- [ ] Protocol fields (FR-001) + DeepSeek adapter tool support (FR-002) implemented and import-clean.
- [ ]
RefactorToolsregistry +TOOL_SCHEMAS(FR-003/004) with sandbox + whitelist (SEC-103). - [ ] Tool-use loop + refactoring prompt +
refactor()entry point (FR-005/006/007). - [ ] Structured logging of tool calls/turns (FR-008).
- [ ] A real refactor on a temp module edits in place and verifies (TST-103-004).
- [ ]
@traceson the new code referencesREQ-103-*. - [ ] Unit + security tests (TST-103-001/002/005/006) pass via the threadpool runner (never xdist).
Revision History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1.0 | 2026-05-28 | Mohamed | Initial spec — tool-calling refactor capability |