mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
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:
co-authored by
Claude Opus 4.8
parent
97da477424
commit
5e206df166
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"feature_name": "harnessx-self-improving-rulesets",
|
||||
"created_at": "2026-06-22T10:07:18Z",
|
||||
"updated_at": "2026-06-22T14:05:00Z",
|
||||
"updated_at": "2026-06-22T14:25:00Z",
|
||||
"language": "en",
|
||||
"phase": "implementation",
|
||||
"approvals": {
|
||||
|
||||
@@ -122,7 +122,7 @@ This plan splits the fused regex layer into three governed owners (rules detect,
|
||||
|
||||
## Phase 3 — Integration: HarnessX Execution & Self-Improvement (optional extra)
|
||||
|
||||
- [ ] 5. Gate and package the HarnessX dependency-weight decision
|
||||
- [x] 5. Gate and package the HarnessX dependency-weight decision
|
||||
- [x] 5.1 Run the dependency dry-run audit and record the transitive graph
|
||||
- Before adopting the HarnessX execution stages, run a dependency dry-run audit and record the realized transitive dependency graph.
|
||||
- If the audited graph pulls a browser-binary, container-SDK, or web-server stack as a mandatory dependency and it is judged unacceptable, select the pre-specified vendored lean subset under the same extra name with the heavy subtrees removed.
|
||||
@@ -130,7 +130,7 @@ This plan splits the fused regex layer into three governed owners (rules detect,
|
||||
- _Requirements: 7.3, 7.4, 7.7_
|
||||
- _Boundary: Packaging Configuration_
|
||||
|
||||
- [ ] 5.2 Configure HarnessX as a SHA-pinned optional extra with a capability guard
|
||||
- [x] 5.2 Configure HarnessX as a SHA-pinned optional extra with a capability guard
|
||||
- Keep the core dependency list empty and expose HarnessX only as an optional extra pinned to a specific commit revision; initialize any submodule non-recursively so the large RL fork subtree is not pulled.
|
||||
- Guard every HarnessX import behind a capability check and import it lazily so heavy code paths stay cold in the default install.
|
||||
- Observable completion: the default install resolves with an empty core dependency list and HarnessX symbols are only imported after the capability check passes, verified by an import-cold test.
|
||||
|
||||
@@ -10,6 +10,17 @@ readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = []
|
||||
|
||||
[project.optional-dependencies]
|
||||
# HarnessX is the optional agentic plane (LLM hunter/verifier + self-improvement).
|
||||
# SHA-pinned for reproducibility; the heavy transitive footprint (see
|
||||
# .kiro/specs/harnessx-self-improving-rulesets/dependency-audit.md) lands ONLY when
|
||||
# this extra is installed. The core install stays zero-dependency and every HarnessX
|
||||
# import is guarded by openultrasast.harness_ext (lazy). pip does not pull the
|
||||
# recipe/verl_harnessX submodule (it is outside the packaged harnessx* modules).
|
||||
harnessx = [
|
||||
"harnessx @ git+https://github.com/Darwin-Agent/HarnessX.git@274c512eea67c64701f992d3b6fe06f0fd590ef0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
ousast = "openultrasast.cli:main"
|
||||
|
||||
|
||||
@@ -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")
|
||||
@@ -0,0 +1,46 @@
|
||||
"""HarnessX optional-extra packaging + capability guard (Phase 3 task 5.2)."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.harness_ext import HARNESSX_EXTRA, HarnessXUnavailableError, has_harnessx, require_harnessx
|
||||
|
||||
PYPROJECT = tomllib.loads(Path("pyproject.toml").read_text())
|
||||
|
||||
|
||||
def test_core_dependencies_stay_empty() -> None:
|
||||
assert PYPROJECT["project"]["dependencies"] == []
|
||||
|
||||
|
||||
def test_harnessx_is_a_sha_pinned_optional_extra() -> None:
|
||||
extras = PYPROJECT["project"]["optional-dependencies"]["harnessx"]
|
||||
assert len(extras) == 1
|
||||
assert re.fullmatch(
|
||||
r"harnessx @ git\+https://github\.com/Darwin-Agent/HarnessX\.git@[0-9a-f]{40}",
|
||||
extras[0],
|
||||
), extras[0]
|
||||
|
||||
|
||||
def test_importing_openultrasast_does_not_import_harnessx() -> None:
|
||||
# The big modules (cli, harness_ext) must not eagerly import the heavy extra.
|
||||
import openultrasast.cli # noqa: F401
|
||||
import openultrasast.harness_ext # noqa: F401
|
||||
|
||||
assert "harnessx" not in sys.modules
|
||||
|
||||
|
||||
def test_has_harnessx_probes_without_importing() -> None:
|
||||
available = has_harnessx()
|
||||
assert isinstance(available, bool)
|
||||
assert "harnessx" not in sys.modules # find_spec must not import the module
|
||||
|
||||
|
||||
def test_require_harnessx_raises_with_install_guidance_when_absent() -> None:
|
||||
if has_harnessx():
|
||||
pytest.skip("HarnessX extra is installed in this environment")
|
||||
with pytest.raises(HarnessXUnavailableError, match=re.escape(HARNESSX_EXTRA)):
|
||||
require_harnessx()
|
||||
Reference in New Issue
Block a user