feat(harness-bench): bind any registered harness by name (ACP + community plugins) - #2265
Conversation
The bench could only probe an official profile (the 4 SDK harnesses + auto-derived native-tui) or a dotted module:attr BenchProfile reference. A harness registered in the omnigent registry but neither official nor native-tui -- the in-repo generic ACP harness (`acp`, ACP_SUBPROCESS), or an entry-point community plugin (`rovo`/`rovo-cli` from omnigent-rovo) -- KeyError'd on resolve_profile, so `--harness acp` / `--harness rovo` could not run. Add a registry fallback to resolve_profile: after the official + reference checks, derive a BenchProfile for any harness in the omnigent registry (_registry_profile in manifest.py). It resolves aliases (rovo -> rovo-cli), keys off harness_modules() so it covers plugins that declare no capabilities entry, maps integration_mode -> transport family (SDK/CLI/ACP subprocess -> sdk-inproc family = the existing drivers; NATIVE_TUI -> native-tui), and skip-gates on the harness's install-spec binary when present (rovo -> acli). No new transport driver: an ACP harness registers as an omnigent agent (config.harness=acp:<slug>) and runs on the existing SDK-wrap drivers. Both harnesses are OWN_AUTH, so they run only where their vendor binary is installed + authed, and skip cleanly otherwise (verified live: rovo skips on missing `acli`). tool_calling/policy_deny stay `·` for ACP (agent runs its own tools / gates via session/request_permission) -- the same documented gap as native. Tests: resolve_profile binds acp (sdk-inproc) and rovo/rovo-cli (alias, acli gate); unknown still KeyErrors; plugin cases skip if omnigent-rovo absent. Offline suite 71 passed / 18 skipped, ruff clean.
|
…model, ACP-login SKIP
Three fixes from PR review + a live rovo run:
1. (blocking, Polly) A MODELED integration_mode the bench has no driver for
(NATIVE_SERVER, e.g. opencode-native) was silently degrading to the
sdk-inproc default via `.get(mode, "sdk-inproc")` — binding a vendor-server
harness to the wrong driver and dropping its skip-gate. _registry_profile now
distinguishes: no caps (unmodeled plugin) -> assume SDK family; a modeled
mode NOT in the transport map -> return None so resolve_profile KeyErrors
(honest "unrunnable" rather than a wrong profile). resolve_profile("opencode
-native") KeyErrors again.
2. A live rovo run (acli absent) reported `!!✓>✗` DRIFT: the ACP-session /
vendor-login failure ("Ensure `acli` is installed and you are logged in",
"AcpProcessExited", "ACP subprocess/session") wasn't an infra marker, so it
read as a real UNSUPPORTED against the SUPPORTED declaration. Added those
markers + a reason so an own-auth harness with no vendor login SKIPs (env
gap), never drifts.
3. Registry profiles stamped a databricks-* placeholder model even for own-auth
harnesses (rovo/acp), which is misleading — the runner drops the gateway
model for them. Now: gateway-credential harness -> the databricks default;
own-auth or capless -> empty model (the harness owns it).
Tests: NATIVE_SERVER refusal; a plugin-independent happy-path (fake registered
CLI harness via monkeypatch) so the fallback's positive path isn't skip-gated
away in CI; rovo model=="" assertion. Offline suite 73 passed / 18 skipped.
|
Thanks - the blocking issue was a real bug; fixed in acef57f. Blocking: NATIVE_SERVER silent mis-bind. Fixed. Non-blocking: skip-gated positive-path coverage. Added Non-blocking: Also fixed two things a live
|
| """ | ||
| from types import SimpleNamespace | ||
|
|
||
| import tests.harness_bench.manifest as man |
| """ | ||
| from types import SimpleNamespace | ||
|
|
||
| import tests.harness_bench.manifest as man |
My previous "empty model for own-auth" change broke agent registration: the omnigent executor spec mandates a model (spec/omnigent.py: "executor.type= 'omnigent' requires a model"), so model="" -> 400 "llm.model must be present when llm block is present" on register_agent. Seen live: rovo got past auth + skip-gate into provisioning, then failed registration. A model is always required for registration, so stamp the databricks default in all cases. For an own-auth harness it is inert: the generic ACP harness drops databricks-* models (workflow.py::_build_acp_spawn_env), and rovo has no spawn-env builder + reads HARNESS_ROVO_MODEL directly from env (which the runner never sets for it), so rovo gets no model and lets Rovo Dev pick its own default at session/new. The placeholder satisfies registration and never reaches acli. Tests updated to assert a non-empty model (registration invariant) rather than empty.
`acp:<slug>` is a first-class omnigent harness id — the base `acp` harness is
registered and the slug selects a user-configured ACP agent at spawn (resolved
from the ~/.omnigent `acp:` block). The registry fallback now recognizes it:
look up caps/module/install-spec by the base `acp`, but keep the full `acp:<slug>`
as the profile harness so `config.harness=acp:<slug>` reaches the runner, and
sanitize the colon in the env-prefix/marker stem (acp:qwen -> HARNESS_ACP_QWEN_).
An empty slug ("acp:") is refused.
Lets `--harness acp:qwen` bind to a specific ACP agent for a live turn (qwen is
installed + authed), vs the bare `acp` which needs HARNESS_ACP_COMMAND. Test
added. Offline suite 73 passed / 18 skipped.
The bench built its agent name as bench-<harness>, but an acp:<slug> harness id has a colon, which the agent-name validator rejects ([a-zA-Z0-9_-]+). So a --harness acp:qwen run would 400 at registration. Replace ":" with "-" in the NAME only (bench-acp-qwen); config.harness keeps the real acp:<slug> id so the runner still resolves the right ACP agent at spawn.
|
Live-verified end to end: the registry fallback now drives a real ACP agent turn. Configured a qwen ACP agent (
Also validated the honest-skip path against two other ACP harnesses whose vendor-side setup wasn't satisfied: a community plugin blocked on an interactive vendor onboarding prompt, and qwen before its own auth was configured — both reported specific, diagnosed SKIPs rather than false verdicts or hangs. That's the seamlessness guarantee working: bind by name, run on the existing drivers, and never lie about a capability the environment simply couldn't exercise. |
|
/review |
|
|
🏷️ Doc impact: Changes are confined to the internal harness_bench test tooling (profile resolution, skip-gating, tests) with no user-facing surface, integration, or built-in policy change. Auto-classified on merge. Set the label manually before merging to override. · run |
…nity plugins) (omnigent-ai#2265) * feat(harness-bench): bind any registered harness passed by name The bench could only probe an official profile (the 4 SDK harnesses + auto-derived native-tui) or a dotted module:attr BenchProfile reference. A harness registered in the omnigent registry but neither official nor native-tui -- the in-repo generic ACP harness (`acp`, ACP_SUBPROCESS), or an entry-point community plugin (`rovo`/`rovo-cli` from omnigent-rovo) -- KeyError'd on resolve_profile, so `--harness acp` / `--harness rovo` could not run. Add a registry fallback to resolve_profile: after the official + reference checks, derive a BenchProfile for any harness in the omnigent registry (_registry_profile in manifest.py). It resolves aliases (rovo -> rovo-cli), keys off harness_modules() so it covers plugins that declare no capabilities entry, maps integration_mode -> transport family (SDK/CLI/ACP subprocess -> sdk-inproc family = the existing drivers; NATIVE_TUI -> native-tui), and skip-gates on the harness's install-spec binary when present (rovo -> acli). No new transport driver: an ACP harness registers as an omnigent agent (config.harness=acp:<slug>) and runs on the existing SDK-wrap drivers. Both harnesses are OWN_AUTH, so they run only where their vendor binary is installed + authed, and skip cleanly otherwise (verified live: rovo skips on missing `acli`). tool_calling/policy_deny stay `·` for ACP (agent runs its own tools / gates via session/request_permission) -- the same documented gap as native. Tests: resolve_profile binds acp (sdk-inproc) and rovo/rovo-cli (alias, acli gate); unknown still KeyErrors; plugin cases skip if omnigent-rovo absent. Offline suite 71 passed / 18 skipped, ruff clean. * fix(harness-bench): address review — NATIVE_SERVER refusal, own-auth model, ACP-login SKIP Three fixes from PR review + a live rovo run: 1. (blocking, Polly) A MODELED integration_mode the bench has no driver for (NATIVE_SERVER, e.g. opencode-native) was silently degrading to the sdk-inproc default via `.get(mode, "sdk-inproc")` — binding a vendor-server harness to the wrong driver and dropping its skip-gate. _registry_profile now distinguishes: no caps (unmodeled plugin) -> assume SDK family; a modeled mode NOT in the transport map -> return None so resolve_profile KeyErrors (honest "unrunnable" rather than a wrong profile). resolve_profile("opencode -native") KeyErrors again. 2. A live rovo run (acli absent) reported `!!✓>✗` DRIFT: the ACP-session / vendor-login failure ("Ensure `acli` is installed and you are logged in", "AcpProcessExited", "ACP subprocess/session") wasn't an infra marker, so it read as a real UNSUPPORTED against the SUPPORTED declaration. Added those markers + a reason so an own-auth harness with no vendor login SKIPs (env gap), never drifts. 3. Registry profiles stamped a databricks-* placeholder model even for own-auth harnesses (rovo/acp), which is misleading — the runner drops the gateway model for them. Now: gateway-credential harness -> the databricks default; own-auth or capless -> empty model (the harness owns it). Tests: NATIVE_SERVER refusal; a plugin-independent happy-path (fake registered CLI harness via monkeypatch) so the fallback's positive path isn't skip-gated away in CI; rovo model=="" assertion. Offline suite 73 passed / 18 skipped. * fix(harness-bench): registry profiles need a valid model to register My previous "empty model for own-auth" change broke agent registration: the omnigent executor spec mandates a model (spec/omnigent.py: "executor.type= 'omnigent' requires a model"), so model="" -> 400 "llm.model must be present when llm block is present" on register_agent. Seen live: rovo got past auth + skip-gate into provisioning, then failed registration. A model is always required for registration, so stamp the databricks default in all cases. For an own-auth harness it is inert: the generic ACP harness drops databricks-* models (workflow.py::_build_acp_spawn_env), and rovo has no spawn-env builder + reads HARNESS_ROVO_MODEL directly from env (which the runner never sets for it), so rovo gets no model and lets Rovo Dev pick its own default at session/new. The placeholder satisfies registration and never reaches acli. Tests updated to assert a non-empty model (registration invariant) rather than empty. * feat(harness-bench): bind acp:<slug> ids to a specific ACP agent `acp:<slug>` is a first-class omnigent harness id — the base `acp` harness is registered and the slug selects a user-configured ACP agent at spawn (resolved from the ~/.omnigent `acp:` block). The registry fallback now recognizes it: look up caps/module/install-spec by the base `acp`, but keep the full `acp:<slug>` as the profile harness so `config.harness=acp:<slug>` reaches the runner, and sanitize the colon in the env-prefix/marker stem (acp:qwen -> HARNESS_ACP_QWEN_). An empty slug ("acp:") is refused. Lets `--harness acp:qwen` bind to a specific ACP agent for a live turn (qwen is installed + authed), vs the bare `acp` which needs HARNESS_ACP_COMMAND. Test added. Offline suite 73 passed / 18 skipped. * fix(harness-bench): sanitize colon in bench agent name for acp:<slug> The bench built its agent name as bench-<harness>, but an acp:<slug> harness id has a colon, which the agent-name validator rejects ([a-zA-Z0-9_-]+). So a --harness acp:qwen run would 400 at registration. Replace ":" with "-" in the NAME only (bench-acp-qwen); config.harness keeps the real acp:<slug> id so the runner still resolves the right ACP agent at spawn. Signed-off-by: Aditya Devarapalli <adityareddyd2@gmail.com>
Related issue
N/A
Summary
Tests the bench's "plugs in with no bench edit" promise against two new harnesses
and closes the gap it exposed:
acp— the in-repo generic ACP harness (feat(acp): generic ACP harness + Omnigent-tool MCP bridge for all ACP harnesses #2152),ACP_SUBPROCESS.rovo/rovo-cli— an external community plugin (omnigent-rovo),discovered via the
omnigent.community.harnessentry point; ACP-based, and itdeclares no capabilities entry (only a harness module + install spec).
Both are registered in the omnigent registry, but
resolve_profile("acp")andresolve_profile("rovo")KeyError'd — the bench only knew official profiles(4 SDK harnesses + auto-derived
NATIVE_TUI) or a dottedmodule:attrreference.So
--harness acp/--harness rovocouldn't run.Fix: a registry fallback in
resolve_profile. After the official + referencechecks,
_registry_profilederives aBenchProfilefor any harness in theomnigent registry:
rovo->rovo-cli),harness_modules()so it covers plugins with no capabilities entry,integration_mode-> transport family (SDK / CLI / ACP subprocess ->the sdk-inproc family = existing drivers;
NATIVE_TUI-> native-tui),rovo->acli).No new transport driver. An ACP harness registers as an omnigent agent
(
config.harness=acp:<slug>) and runs on the existing SDK-wrap drivers(sdk-inproc / full-server) — the same server+runner+session path as
claude-sdk/codex/pi. Both harnesses are
OWN_AUTH, so they run only where theirvendor binary is installed + authenticated, and skip cleanly otherwise.
tool_calling/policy_denystay·for ACP (the agent runs its own tools andgates via
session/request_permission, not a server-dispatched builtin) — thesame documented observation gap as native harnesses.
Test Plan
pytest tests/harness_bench/-> 71 passed / 18 skipped;ruffclean.New tests:
resolve_profilebindsacp(sdk-inproc) androvo/rovo-cli(alias resolves,
cli_binary=acli); an unknown name still KeyErrors; theplugin cases
pytest.skipwhenomnigent-rovoisn't installed (so CI withoutthe plugin passes).
--harness acp --harness rovo --no-liverenders both rows, no KeyError. acp declares streaming/interrupt (
✓); rovo hasno caps entry so those are
?(honest UNKNOWN).acli):--harness rovo --profile oss->rovo-cli skipped: 'acli' CLI is not on PATH— the bind + install-spec skip-gate proven end-to-end.
Demo
The bench bound to an external entry-point plugin by name, resolved its vendor
binary, and skipped honestly — zero per-harness bench edits.
Type of change
Test coverage
Coverage notes
Profile resolution for
acp+ therovoplugin (name + alias + skip-gate) iscovered offline; the plugin tests skip when
omnigent-rovoisn't installed. Thelive turn path for an ACP harness needs a vendor-authed ACP binary on the host
(not available in CI), so it was verified by hand to the skip-gate boundary; a
full live turn is host-dependent.
Changelog
The harness bench can now probe any registered harness by name — including the
generic ACP harness and community entry-point plugins — with no bench edits.