Skip to content

Batch edit check

batch_edit_check

Strictly read-only preflight for a batch_edit operation set.

Mirrors the shape of :mod:axm_edit.tools.batch_edit: a module-level :func:render_text plus an :class:~axm.tools.base.AXMTool whose execute never raises — every failure is shaped into ToolResult(success=False, error=...).

No code path here opens a file for writing, creates a directory, or calls batch_apply / create_checkpoint: rule evaluation and ordering are fully delegated to the shared read-only core :mod:axm_edit.core.preflight.

BatchEditCheckTool

Bases: AXMTool

Validate a batch_edit operation set without touching the disk.

Orchestration and shaping layer only: the rules, their ordering and the blocking verdict live in :mod:axm_edit.core.preflight.

Source code in packages/axm-edit/src/axm_edit/tools/batch_edit_check.py
Python
class BatchEditCheckTool(AXMTool):
    """Validate a ``batch_edit`` operation set without touching the disk.

    Orchestration and shaping layer only: the rules, their ordering and
    the blocking verdict live in :mod:`axm_edit.core.preflight`.
    """

    expose_directly = False
    domain = "edit"
    tags = frozenset({"edit", "check", "preflight"})

    agent_hint: str = (
        "Preflight a batch_edit operation set read-only: reports broken"
        " anchors, creates on existing files and unknown edit keys.\n"
        f"{ANCHOR_RULES_HINT}"
    )

    @property
    def name(self) -> str:
        """Tool name used for MCP registration."""
        return "batch_edit_check"

    def execute(
        self,
        *,
        path: str = ".",
        operations: list[dict[str, object]] | None = None,
        **kwargs: object,
    ) -> ToolResult:
        """Check a batch of file operations without applying any of them.

        Args:
            path: Project root the batch would be applied to.
            operations: List of operation dicts with ``op`` discriminator.
            kwargs: Ignored extra arguments (MCP forward-compatibility).

        Returns:
            ``ToolResult(success=True)`` with ``data["ok"]``, the
            serialised ``data["diagnostics"]`` and the severity partition
            ``data["blocking"]`` / ``data["error_count"]`` /
            ``data["warning_count"]`` when the check could run;
            ``ToolResult(success=False, error=...)`` when the tool itself
            failed (missing root, malformed operations). Never raises.
        """
        raw_operations: list[dict[str, object]] = operations or []

        if not raw_operations:
            return ToolResult(success=False, error="No operations provided")

        try:
            root = Path(path).resolve()
            if not root.is_dir():
                return ToolResult(
                    success=False,
                    error=f"Path is not a directory: {path}",
                )
            diagnostics = _collect_diagnostics(root, raw_operations)
        except (OSError, ValueError, TypeError) as exc:
            return ToolResult(success=False, error=str(exc))

        report = partition_diagnostics(diagnostics)
        payload: list[dict[str, object]] = [
            diagnostic.model_dump() for diagnostic in report.diagnostics
        ]
        data: dict[str, object] = {
            "ok": not diagnostics,
            "diagnostics": payload,
            "blocking": report.blocking,
            "error_count": len(report.errors),
            "warning_count": len(report.warnings),
        }
        return ToolResult(success=True, data=data, text=render_text(diagnostics))
name property

Tool name used for MCP registration.

execute(*, path='.', operations=None, **kwargs)

Check a batch of file operations without applying any of them.

Parameters:

Name Type Description Default
path str

Project root the batch would be applied to.

'.'
operations list[dict[str, object]] | None

List of operation dicts with op discriminator.

None
kwargs object

Ignored extra arguments (MCP forward-compatibility).

{}

Returns:

Type Description
ToolResult

ToolResult(success=True) with data["ok"], the

ToolResult

serialised data["diagnostics"] and the severity partition

ToolResult

data["blocking"] / data["error_count"] /

ToolResult

data["warning_count"] when the check could run;

ToolResult

ToolResult(success=False, error=...) when the tool itself

ToolResult

failed (missing root, malformed operations). Never raises.

Source code in packages/axm-edit/src/axm_edit/tools/batch_edit_check.py
Python
def execute(
    self,
    *,
    path: str = ".",
    operations: list[dict[str, object]] | None = None,
    **kwargs: object,
) -> ToolResult:
    """Check a batch of file operations without applying any of them.

    Args:
        path: Project root the batch would be applied to.
        operations: List of operation dicts with ``op`` discriminator.
        kwargs: Ignored extra arguments (MCP forward-compatibility).

    Returns:
        ``ToolResult(success=True)`` with ``data["ok"]``, the
        serialised ``data["diagnostics"]`` and the severity partition
        ``data["blocking"]`` / ``data["error_count"]`` /
        ``data["warning_count"]`` when the check could run;
        ``ToolResult(success=False, error=...)`` when the tool itself
        failed (missing root, malformed operations). Never raises.
    """
    raw_operations: list[dict[str, object]] = operations or []

    if not raw_operations:
        return ToolResult(success=False, error="No operations provided")

    try:
        root = Path(path).resolve()
        if not root.is_dir():
            return ToolResult(
                success=False,
                error=f"Path is not a directory: {path}",
            )
        diagnostics = _collect_diagnostics(root, raw_operations)
    except (OSError, ValueError, TypeError) as exc:
        return ToolResult(success=False, error=str(exc))

    report = partition_diagnostics(diagnostics)
    payload: list[dict[str, object]] = [
        diagnostic.model_dump() for diagnostic in report.diagnostics
    ]
    data: dict[str, object] = {
        "ok": not diagnostics,
        "diagnostics": payload,
        "blocking": report.blocking,
        "error_count": len(report.errors),
        "warning_count": len(report.warnings),
    }
    return ToolResult(success=True, data=data, text=render_text(diagnostics))

render_text(diagnostics)

Render a diagnostic set as the compact text consumed by the CLI.

Parameters:

Name Type Description Default
diagnostics Sequence[CheckDiagnostic]

Diagnostics to render, in batch order.

required

Returns:

Type Description
str

A header — "batch_edit_check | ✓ | 0 diagnostic(s)" when

str

diagnostics is empty — plus one line per diagnostic carrying its

str

code, its message and its hint, and a final

str

"blocking: …" summary line.

Source code in packages/axm-edit/src/axm_edit/tools/batch_edit_check.py
Python
def render_text(diagnostics: Sequence[CheckDiagnostic]) -> str:
    """Render a diagnostic set as the compact text consumed by the CLI.

    Args:
        diagnostics: Diagnostics to render, in batch order.

    Returns:
        A header — ``"batch_edit_check | ✓ | 0 diagnostic(s)"`` when
        *diagnostics* is empty — plus one line per diagnostic carrying its
        ``code``, its ``message`` and its ``hint``, and a final
        ``"blocking: …"`` summary line.
    """
    if not diagnostics:
        return f"{_EMPTY_RENDER}\n{_summary_line(diagnostics)}"

    lines = [f"batch_edit_check | ✗ | {len(diagnostics)} diagnostic(s)"]
    for diagnostic in diagnostics:
        lines.append(
            f"  [{diagnostic.severity}] op#{diagnostic.op_index}"
            f" {diagnostic.file}: {diagnostic.code}{diagnostic.message}"
        )
        if diagnostic.hint:
            lines.append(f"      hint: {diagnostic.hint}")
    lines.append(_summary_line(diagnostics))
    return "\n".join(lines)