Skip to content

Impact

impact

ImpactHook & DocImpactHook — blast-radius and doc-impact analysis.

Protocol hooks registered via axm.hooks entry points:

  • ast:impactImpactHook — calls analyze_impact, returns the complete impact report as HookResult metadata. Supports newline-separated symbol lists with max-score merge semantics.

  • ast:doc-impactDocImpactHook — calls analyze_doc_impact, returns doc_refs as HookResult metadata. Supports newline-separated symbol lists.

DocImpactHook

Run doc impact analysis on one or more symbols.

Reads path from params (or working_dir from context) and symbol from params. When symbol contains newline characters, each line is treated as a separate symbol.

Source code in packages/axm-ast/src/axm_ast/hooks/impact.py
class DocImpactHook:
    """Run doc impact analysis on one or more symbols.

    Reads ``path`` from *params* (or ``working_dir`` from context)
    and ``symbol`` from *params*.  When *symbol* contains newline
    characters, each line is treated as a separate symbol.
    """

    def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
        """Execute the hook action.

        Args:
            context: Session context dictionary.
            **params: Must include ``symbol`` (name to analyze).
                Optional ``path`` (overrides ``working_dir`` from context).

        Returns:
            HookResult with full report (``doc_refs``, ``undocumented``,
                ``stale_signatures``) in metadata on success.
        """
        symbol = params.get("symbol")
        if not symbol:
            return HookResult.fail("Missing required param 'symbol'")

        path = params.get("path") or context.get("working_dir", ".")
        working_dir = Path(path)
        if not working_dir.is_dir():
            return HookResult.fail(f"working_dir not a directory: {working_dir}")

        try:
            from axm_ast.core.doc_impact import analyze_doc_impact

            symbols = [s.strip() for s in symbol.splitlines() if s.strip()]
            report = analyze_doc_impact(working_dir, symbols)
            return HookResult.ok(**report)
        except Exception as exc:  # noqa: BLE001
            return HookResult.fail(f"Doc impact analysis failed: {exc}")
execute(context, **params)

Execute the hook action.

Parameters:

Name Type Description Default
context dict[str, Any]

Session context dictionary.

required
**params Any

Must include symbol (name to analyze). Optional path (overrides working_dir from context).

{}

Returns:

Type Description
HookResult

HookResult with full report (doc_refs, undocumented, stale_signatures) in metadata on success.

Source code in packages/axm-ast/src/axm_ast/hooks/impact.py
def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
    """Execute the hook action.

    Args:
        context: Session context dictionary.
        **params: Must include ``symbol`` (name to analyze).
            Optional ``path`` (overrides ``working_dir`` from context).

    Returns:
        HookResult with full report (``doc_refs``, ``undocumented``,
            ``stale_signatures``) in metadata on success.
    """
    symbol = params.get("symbol")
    if not symbol:
        return HookResult.fail("Missing required param 'symbol'")

    path = params.get("path") or context.get("working_dir", ".")
    working_dir = Path(path)
    if not working_dir.is_dir():
        return HookResult.fail(f"working_dir not a directory: {working_dir}")

    try:
        from axm_ast.core.doc_impact import analyze_doc_impact

        symbols = [s.strip() for s in symbol.splitlines() if s.strip()]
        report = analyze_doc_impact(working_dir, symbols)
        return HookResult.ok(**report)
    except Exception as exc:  # noqa: BLE001
        return HookResult.fail(f"Doc impact analysis failed: {exc}")

ImpactHook dataclass

Run impact analysis on one or more symbols.

Reads path from params (or working_dir from context) and symbol from params. When symbol contains newline characters, each line is analyzed separately and results are merged (max score, concatenated lists, deduplicated modules/tests).

Source code in packages/axm-ast/src/axm_ast/hooks/impact.py
@dataclass
class ImpactHook:
    """Run impact analysis on one or more symbols.

    Reads ``path`` from *params* (or ``working_dir`` from context)
    and ``symbol`` from *params*.  When *symbol* contains newline
    characters, each line is analyzed separately and results are
    merged (max score, concatenated lists, deduplicated modules/tests).
    """

    def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
        """Execute the hook action.

        Args:
            context: Session context dictionary.
            **params: Must include ``symbol`` (name to analyze).
                Optional ``path`` (overrides ``working_dir`` from context).

        Returns:
            HookResult with ``impact`` dict in metadata on success.
        """
        symbol = params.get("symbol")
        if not symbol:
            return HookResult.fail("Missing required param 'symbol'")

        path = params.get("path") or context.get("working_dir", ".")
        working_dir = Path(path)
        if not working_dir.is_dir():
            return HookResult.fail(f"working_dir not a directory: {working_dir}")

        exclude_tests = bool(params.get("exclude_tests", False))

        try:
            from axm_ast.core.impact import analyze_impact

            symbols = [s.strip() for s in symbol.splitlines() if s.strip()]

            if len(symbols) == 1:
                report = analyze_impact(
                    working_dir,
                    symbols[0],
                    project_root=working_dir.parent,
                    exclude_tests=exclude_tests,
                )
                return HookResult.ok(impact=report)

            # Multiple symbols — analyze in parallel (I/O-bound: git log, file reads).
            def _analyze(sym: str) -> dict[str, Any]:
                return analyze_impact(
                    working_dir,
                    sym,
                    project_root=working_dir.parent,
                    exclude_tests=exclude_tests,
                )

            with ThreadPoolExecutor(max_workers=min(len(symbols), 4)) as pool:
                reports = list(pool.map(_analyze, symbols))
            merged = _merge_impact_reports(symbol, reports)
            return HookResult.ok(impact=merged)
        except Exception as exc:  # noqa: BLE001
            return HookResult.fail(f"Impact analysis failed: {exc}")
execute(context, **params)

Execute the hook action.

Parameters:

Name Type Description Default
context dict[str, Any]

Session context dictionary.

required
**params Any

Must include symbol (name to analyze). Optional path (overrides working_dir from context).

{}

Returns:

Type Description
HookResult

HookResult with impact dict in metadata on success.

Source code in packages/axm-ast/src/axm_ast/hooks/impact.py
def execute(self, context: dict[str, Any], **params: Any) -> HookResult:
    """Execute the hook action.

    Args:
        context: Session context dictionary.
        **params: Must include ``symbol`` (name to analyze).
            Optional ``path`` (overrides ``working_dir`` from context).

    Returns:
        HookResult with ``impact`` dict in metadata on success.
    """
    symbol = params.get("symbol")
    if not symbol:
        return HookResult.fail("Missing required param 'symbol'")

    path = params.get("path") or context.get("working_dir", ".")
    working_dir = Path(path)
    if not working_dir.is_dir():
        return HookResult.fail(f"working_dir not a directory: {working_dir}")

    exclude_tests = bool(params.get("exclude_tests", False))

    try:
        from axm_ast.core.impact import analyze_impact

        symbols = [s.strip() for s in symbol.splitlines() if s.strip()]

        if len(symbols) == 1:
            report = analyze_impact(
                working_dir,
                symbols[0],
                project_root=working_dir.parent,
                exclude_tests=exclude_tests,
            )
            return HookResult.ok(impact=report)

        # Multiple symbols — analyze in parallel (I/O-bound: git log, file reads).
        def _analyze(sym: str) -> dict[str, Any]:
            return analyze_impact(
                working_dir,
                sym,
                project_root=working_dir.parent,
                exclude_tests=exclude_tests,
            )

        with ThreadPoolExecutor(max_workers=min(len(symbols), 4)) as pool:
            reports = list(pool.map(_analyze, symbols))
        merged = _merge_impact_reports(symbol, reports)
        return HookResult.ok(impact=merged)
    except Exception as exc:  # noqa: BLE001
        return HookResult.fail(f"Impact analysis failed: {exc}")