= infra-tools

== purpose

Provides the concrete tool execution infrastructure: a sandboxed executor that wraps tool calls with file backup/restore, diff computation, and event emission; a dispatching executor that routes tool requests to registered tools; a default tool registry; a shell command tool; and a tool configuration system that maps config to enabled tool instances.

== responsibilities

* Wrap tool execution with sandbox directory management and file backup/restore
* Compute unified diffs for file-affecting tool results
* Emit `ToolExecutionStartedEvent`, `ToolExecutionCompletedEvent`, and `ToolExecutionFailedEvent`
* Dispatch tool requests by name to registered tool implementations
* Register tools by name via `DefaultToolRegistry`
* Execute shell commands with timeout, stdout/stderr capture, and executable allowlisting
* Convert `ToolConfig` to a list of `Tool` implementations via `buildTools()`

== non-responsibilities

* Does not define tool contracts — that belongs to `core:tools`
* Does not implement filesystem tools — that belongs to `infra:tools:filesystem`
* Does not enforce approval gating — that belongs to `core:approvals`
* Does not perform workflow-level tool routing

== key types

=== SandboxedToolExecutor
* **kind**: class
* **purpose**: Decorator around a `ToolExecutor` that handles per-invocation working directories, file backup/restore for file-affecting tools, diff computation, and event emission. Operates on `Dispatchers.IO`.
* **fields**: `delegate` (inner executor), `registry`, `eventDispatcher`, `workDir` (default `/tmp/correx-sandbox`)

=== DispatchingToolExecutor
* **kind**: class
* **purpose**: Routes a `ToolRequest` to the appropriate `ToolExecutor` by resolving the tool name from a `ToolRegistry`.

=== DefaultToolRegistry
* **kind**: class
* **purpose**: Simple map-based `ToolRegistry` built from a vararg or list of `Tool` instances. Keyed by `tool.name`.

=== ShellTool
* **kind**: class
* **purpose**: Implements both `Tool` and `ToolExecutor` for shell command execution. Supports executable allowlisting, configurable timeout (default 30s), and working directory. Validates that `argv` is non-empty and the executable is allowed. Tier T2.
* **fields**: `allowedExecutables`, `timeoutMs` (default 30000), `workingDir`

=== DiffUtil
* **kind**: object
* **purpose**: Computes unified-diff strings from old and new file contents using LCS-based edit detection. Used by `SandboxedToolExecutor` to record changes in tool receipts.

=== ToolConfig
* **kind**: data class
* **purpose**: Configuration structure for enabling and parameterizing tools. Contains sub-configs for shell, file_read, file_write, and file_edit tools.
* **fields**: `shell`, `fileRead`, `fileWrite`, `fileEdit`

=== ToolConfig.buildTools()
* **kind**: extension function
* **purpose**: Converts a `ToolConfig` into a `List<Tool>` by creating instances for each enabled sub-config.

== event flow

*Inbound:* `ToolRequest` (from core tools layer)

*Outbound:*
* `ToolExecutionStartedEvent` — emitted before delegate execution begins
* `ToolExecutionCompletedEvent` — includes `ToolReceipt` with exit code, output, metadata, affected entities, duration, tier, and diff
* `ToolExecutionFailedEvent` — emitted on execution failure

== integration points

* `core:events` — `ToolExecutionStartedEvent`, `ToolExecutionCompletedEvent`, `ToolExecutionFailedEvent`, `ToolReceipt`, `ToolRequest`, `ToolInvocationId`, `SessionId`
* `core:tools` — `Tool`, `ToolExecutor`, `ToolResult`, `FileAffectingTool`, `ToolRegistry`, `ToolCapability`, `ValidationResult`
* `core:approvals` — `Tier`
* `infra:tools:filesystem` — `FileReadTool`, `FileWriteTool`, `FileEditTool`

== invariants

* `SandboxedToolExecutor` computes `affectedPaths` once (before delegate execution) to avoid double-invocation divergence
* File backup is performed before delegate execution; on failure, originals are restored; on success, backups are deleted
* `ShellTool` rejects executables not in the allowed set with `ValidationResult.Invalid`
* All tools default to `enabled = false` in configuration

== PlantUML diagram

[plantuml, infra-tools, "png"]
----
include::../../diagrams/infra-tools.puml[]
----




== known issues

* `SandboxedToolExecutor` cleanup (`cleanWorkingDir`, `restoreOrClean`) is best-effort and may leave files on IO errors
* `FileEditTool.patch()` spawns an external `patch` process — not a pure-Kotlin implementation

== open questions

* Should `SandboxedToolExecutor` support configurable sandbox root resolution?
* Should tool timeout be configurable per-tool-type rather than per-call?
