feat(harnessx): Phase 3 task 5.2 — SHA-pinned optional extra + capability guard

Wire HarnessX as an optional, SHA-pinned extra with a lazy capability guard,
keeping the core install zero-dependency.

- pyproject: add [project.optional-dependencies] harnessx pinned to commit
  274c512 (Darwin-Agent/HarnessX). Core dependencies stays []. uv_build emits
  `Requires-Dist: harnessx @ git+...@<sha> ; extra == 'harnessx'` +
  `Provides-Extra: harnessx` (verified via uv build); release ships to GitHub
  Releases, not PyPI, so the direct-reference is fine.
- harness_ext.py: the capability seam. has_harnessx() probes via find_spec
  (never imports harnessx); require_harnessx() imports lazily on demand and
  raises HarnessXUnavailableError with install guidance when absent. Every
  Phase 3+ HarnessX path routes through here so heavy imports stay cold by default.
- uv.lock refreshed: the universal lock now resolves the harnessx tree (149 pkgs)
  but `uv sync --group dev` installs only 57 — harnessx is NOT importable in the
  default/CI venv (verified), so CI stays lean and never re-locks.
- Tests: core deps empty; the extra is a 40-hex SHA-pinned git URL; importing
  openultrasast.cli does not import harnessx; has_harnessx probes without
  importing; require_harnessx raises with guidance when absent.

98 tests pass; ruff + mypy clean; detection gate 93.6%/0% intact. Spec task 5
(5.1 + 5.2) checked off; next is task 6 (build_sast hunter sub-harness).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-06-22 16:08:43 +02:00
co-authored by Claude Opus 4.8
parent 97da477424
commit 5e206df166
6 changed files with 2616 additions and 4 deletions
+33
View File
@@ -0,0 +1,33 @@
"""Capability guard for the optional HarnessX agentic plane.
HarnessX is an optional extra (``pip install 'openultrasast[harnessx]'``). The core
install is zero-dependency, so nothing here imports ``harnessx`` at module load:
:func:`has_harnessx` only probes importability via ``find_spec`` and
:func:`require_harnessx` performs the import lazily, on demand. Every HarnessX-backed
code path (Phase 3+: build_sast, the hunter sub-harness, the llm-judge verifier, the
MetaAgent.evolve loop) routes through this seam.
"""
from __future__ import annotations
import importlib
import importlib.util
from types import ModuleType
HARNESSX_EXTRA = "openultrasast[harnessx]"
class HarnessXUnavailableError(RuntimeError):
"""Raised when a HarnessX-backed capability is requested but the extra is absent."""
def has_harnessx() -> bool:
"""Return True if the HarnessX extra is importable, without importing it."""
return importlib.util.find_spec("harnessx") is not None
def require_harnessx() -> ModuleType:
"""Lazily import and return the ``harnessx`` module, or raise with install guidance."""
if not has_harnessx():
raise HarnessXUnavailableError(f"HarnessX is not installed. Install the optional agentic extra: pip install '{HARNESSX_EXTRA}'")
return importlib.import_module("harnessx")