All checks were successful
ci / validate (push) Successful in 1m16s
These files carried no type field at all. Selection is by 'type: workplan', so they were invisible to every projection while the hub held a record for each — and a forge-derived reset read those correct records as no longer deriving and queued them for retirement. Only the type line is added. Refs STATE-WP-0083-T05 Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
115 lines
4.2 KiB
Markdown
115 lines
4.2 KiB
Markdown
---
|
|
id: GLAS-WP-0003
|
|
type: workplan
|
|
title: "First channel extension: formalize the CLI channel contract"
|
|
status: finished
|
|
state_hub_workstream_id: "78da8b34-27dd-4807-b7c8-321328dd2db8"
|
|
---
|
|
|
|
Scoped by `GLAS-WP-0002-T04`: of glas-harness's four charter pillars
|
|
(unified API, profile catalog, extension platform, observability),
|
|
"extension platform" is entirely unbuilt for channels — `glas_harness.cli`
|
|
is a channel in substance (a way a caller invokes a harness session) but
|
|
not in form (no `Channel` contract exists; it's just a CLI module).
|
|
|
|
This workplan does **one thing**: formalize the CLI as the first
|
|
channel extension, so the *contract* exists before a second channel
|
|
(Slack, HTTP, whatever) is attempted — not build a second channel now.
|
|
Same discipline as ADR-002/ADR-004: generalize from a real second
|
|
example, not in anticipation of one.
|
|
|
|
## Task: Define the Channel contract
|
|
|
|
A `Channel` mirrors `Rein`'s shape at a different layer: it turns an
|
|
external invocation (CLI args, a Slack message, an HTTP request) into
|
|
the `sandbox_profile`/`repo`/`title`/`description`/`rein` inputs
|
|
`run_task_through_rein` needs, and turns its `dict[str, Any]` result back
|
|
into whatever the invocation medium expects (stdout JSON for CLI, a
|
|
Slack reply, an HTTP response body). Minimal ABC:
|
|
|
|
```python
|
|
class Channel(ABC):
|
|
@abstractmethod
|
|
def parse_invocation(self, raw: Any) -> GatewayInvocation: ...
|
|
|
|
@abstractmethod
|
|
def render_result(self, result: dict[str, Any]) -> Any: ...
|
|
```
|
|
|
|
Document in `docs/channel-contract.md`, parallel to
|
|
`docs/harness-contract.md`.
|
|
|
|
**Done (2026-07-26).** `src/glas_harness/channels/base.py`:
|
|
`GatewayInvocation` dataclass + `Channel` ABC as sketched above.
|
|
Documented in `docs/channel-contract.md`.
|
|
|
|
```task
|
|
id: GLAS-WP-0003-T01
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "f6a63483-2780-4b6c-8e0a-791e10d9c91a"
|
|
```
|
|
|
|
## Task: Refactor glas_harness.cli into a CLIChannel
|
|
|
|
`src/glas_harness/channels/cli_channel.py`: `CLIChannel(Channel)`
|
|
implementing `parse_invocation` (argparse → `GatewayInvocation`) and
|
|
`render_result` (→ `json.dumps(..., indent=2)`, matching today's output
|
|
exactly — this is a refactor, not a behavior change). `cli.py` becomes a
|
|
thin `main()` that constructs a `CLIChannel` and calls it; existing CLI
|
|
usage and output are unchanged.
|
|
|
|
**Done (2026-07-26).** `cli.py`'s `run` handler now constructs
|
|
`CLIChannel()`, calls `parse_invocation(args)`, invokes
|
|
`run_task_through_rein` with the resulting `GatewayInvocation` fields,
|
|
and prints `channel.render_result(result)` — same shape as before.
|
|
Also fixed a real bug found while live-testing this: `ReinAharness`'s
|
|
default `cli_bin` was still `"agent-harness"`, stale from before the
|
|
rename — now `"rein-aharness"`.
|
|
|
|
```task
|
|
id: GLAS-WP-0003-T02
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "f7d26f3b-0df2-4db5-be7b-33065868087c"
|
|
```
|
|
|
|
## Task: Tests + verify no behavior change
|
|
|
|
Unit tests for `CLIChannel.parse_invocation`/`render_result` in
|
|
isolation, plus re-running the existing live-proof commands (both
|
|
reins) to confirm identical CLI output before/after the refactor.
|
|
|
|
**Done (2026-07-26).** `tests/test_cli_channel.py`, 4 new tests
|
|
(27/27 passing overall). Live-verified with a real run through
|
|
`python3 -m glas_harness.cli run ...` against `rein-aharness`/real
|
|
Claude Code: real commit landed, output shape unchanged,
|
|
`executor-sandbox` itself untouched (as with every prior live proof —
|
|
`ext.bwrap` only mutates its ephemeral copy).
|
|
|
|
```task
|
|
id: GLAS-WP-0003-T03
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "5012c88c-0b9c-4d12-97a8-07a58b7b84c4"
|
|
```
|
|
|
|
## Task: Document the pattern for a second channel
|
|
|
|
A short section in `docs/channel-contract.md` sketching what a second
|
|
channel (e.g. a Slack or HTTP channel) would need to implement,
|
|
without building one — this workplan stops at "the contract exists and
|
|
one real implementation proves it," matching the discipline applied
|
|
throughout this session's other deferred-until-a-second-example
|
|
decisions.
|
|
|
|
**Done (2026-07-26).** "Adding a second channel" section in
|
|
`docs/channel-contract.md`, sketching a Slack channel without building
|
|
one.
|
|
|
|
```task
|
|
id: GLAS-WP-0003-T04
|
|
status: done
|
|
priority: low
|
|
state_hub_task_id: "0a06387f-c7ed-4de9-999d-a7ba50e33d50"
|
|
```
|