Formalize the CLI as the first Channel extension (GLAS-WP-0003)
channels/base.py: GatewayInvocation dataclass + Channel ABC (parse_invocation/render_result), mirroring Rein one layer up -- a channel turns an external invocation into what run_task_through_rein needs, and turns its result back into whatever the invocation medium expects. channels/cli_channel.py: CLIChannel, a pure refactor of what cli.py did inline -- argparse Namespace -> GatewayInvocation, result dict -> json.dumps(..., indent=2). cli.py's run handler is now a thin construct-parse-invoke-render wrapper. docs/channel-contract.md documents the pattern and sketches (without building) a second channel. Found and fixed a real bug while live-testing this refactor: ReinAharness's default cli_bin was still "agent-harness", stale from before HARNESS-WP-0002-T02's rename. 4 new tests (27/27 passing), plus a real live run through python3 -m glas_harness.cli against rein-aharness/real Claude Code confirming identical output shape and a real commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
a87b924126
commit
d3130162b1
9 changed files with 251 additions and 17 deletions
54
tests/test_cli_channel.py
Normal file
54
tests/test_cli_channel.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import argparse
|
||||
import json
|
||||
|
||||
from glas_harness.channels.base import Channel, GatewayInvocation
|
||||
from glas_harness.channels.cli_channel import CLIChannel
|
||||
|
||||
|
||||
def test_cli_channel_is_a_channel() -> None:
|
||||
assert issubclass(CLIChannel, Channel)
|
||||
|
||||
|
||||
def _args(**overrides) -> argparse.Namespace:
|
||||
defaults = dict(
|
||||
sandbox_profile="profile.bwrap-local",
|
||||
repo="/tmp/repo",
|
||||
title="t",
|
||||
description="d",
|
||||
actor="agt",
|
||||
project="glas-harness",
|
||||
no_hub=False,
|
||||
)
|
||||
defaults.update(overrides)
|
||||
return argparse.Namespace(**defaults)
|
||||
|
||||
|
||||
def test_parse_invocation_maps_all_fields() -> None:
|
||||
channel = CLIChannel()
|
||||
invocation = channel.parse_invocation(_args())
|
||||
|
||||
assert invocation == GatewayInvocation(
|
||||
sandbox_profile="profile.bwrap-local",
|
||||
repo="/tmp/repo",
|
||||
title="t",
|
||||
description="d",
|
||||
actor="agt",
|
||||
project="glas-harness",
|
||||
report_to_hub=True,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_invocation_inverts_no_hub_flag() -> None:
|
||||
channel = CLIChannel()
|
||||
invocation = channel.parse_invocation(_args(no_hub=True))
|
||||
assert invocation.report_to_hub is False
|
||||
|
||||
|
||||
def test_render_result_matches_pre_refactor_output() -> None:
|
||||
channel = CLIChannel()
|
||||
result = {"sandbox_id": "sbx1", "tool_ok": True, "summary": {"committed": "True"}}
|
||||
|
||||
rendered = channel.render_result(result)
|
||||
|
||||
assert rendered == json.dumps(result, indent=2)
|
||||
assert json.loads(rendered) == result
|
||||
Loading…
Add table
Add a link
Reference in a new issue