glas-harness/workplans/GLAS-WP-0001-harness-router-foundation.md
codex 02b29af9ca
All checks were successful
ci / validate (push) Successful in 1m16s
fix(workplans): declare type: workplan on records the hub already holds
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
2026-08-26 20:56:02 +02:00

167 lines
7.2 KiB
Markdown

---
id: GLAS-WP-0001
type: workplan
title: "Harness router foundation: contract + first two reins"
status: finished
state_hub_workstream_id: "4b8740d9-9ea8-4c11-abfc-af9e9ac1c2df"
---
Turn glas-harness from a charter into the actual harness router per
`docs/adr/ADR-001-rein-harness-family.md`. Define the contract concrete
harness backends ("reins") implement, register the first two
(`rein-aharness`, `rein-openweights`), and stand up a minimal gateway
that can run a session through either. No channel work, no memory/skills
layer yet — this workplan is the sand-boxer-extension-model equivalent
for harnesses: contract + registry + one working round trip.
## Task: Define the harness contract
Specify the interface a rein implements: session start/resume/end, tool
dispatch + policy check, actor attribution (`adm`/`agt`/`atm`), sandbox
policy resolution (mode/scope/workspaceAccess) handed to sand-boxer, and
the audit envelope reported to State Hub. Mirror sand-boxer's
`SandboxExtension` ABC shape (`provision`/`wait_ready`/`teardown`) at the
harness level: something like `start_session`/`dispatch_tool`/
`end_session`. Document in `docs/harness-contract.md`.
```task
id: GLAS-WP-0001-T01
status: done
priority: high
state_hub_task_id: "e4e6e7e4-ad30-42b7-a278-39d24ec5cecc"
```
## Task: Rein registry
A named, versioned registry of reins (`registry/reins/rein-aharness.yaml`,
`registry/reins/rein-openweights.yaml`) analogous to sand-boxer's
`extensions/ext.*.yaml` — each entry declares its capabilities (e.g.
`unattended`/`interactive`, `model-class: frontier|open-weight`,
`credential-source`) and a handler reference. Extend
`registry/indexes/capabilities.yaml` with a `capability.platform.
glas-harness` entry per the registry-first-reuse principle.
```task
id: GLAS-WP-0001-T02
status: done
priority: medium
state_hub_task_id: "c09e617b-a2d1-42c4-8c90-47ab0056c09c"
```
## Task: Harness profile catalog seed
First harness profile, `harness.agent-dev`, paired with sand-boxer's
`profile.agent-dev` (existing `docs/integrations/glas-harness.md` contract
in sand-boxer already assumes this pairing — verify it still matches).
Add a second profile, `harness.agent-dev-local`, paired with sand-boxer's
forthcoming `profile.bwrap-local` (SAND-WP-0013) once that lands, for
fast local iteration without an SSH hop.
```task
id: GLAS-WP-0001-T03
status: done
priority: medium
state_hub_task_id: "e599f276-a010-4284-bddd-a5a121b57de9"
```
## Task: Minimal CLI gateway — prove the contract against rein-aharness
Implement just enough of the harness contract to run one existing
rein-aharness `run` (task-file path) *through* glas-harness: resolve
`harness.agent-dev`, request a sandbox from sand-boxer, dispatch to
rein-aharness's adapter, verify commit, report to State Hub. This is the
parity proof gating any later "retire agent-harness as a standalone
concern" conversation — deliberately not attempted until this exists.
Wiring is built and unit-tested: `src/glas_harness/gateway.py` +
`reins/rein_aharness.py` implement start_session/dispatch_tool/
end_session, tested against a real local git repo and a mocked
`SandboxManager`/`agent-harness` subprocess (`tests/test_gateway.py`,
`tests/test_rein_aharness.py`).
**The gateway pattern is now live-proven — via `rein-openweights`, not
yet `rein-aharness`.** With the user's explicit go-ahead, ran
`run_task_through_rein(sandbox_profile="profile.bwrap-local", ...,
rein=ReinOpenWeights(model="qwen/qwen-2.5-72b-instruct"))` for real: a
real `ext.bwrap` sandbox was provisioned, a real OpenRouter call drove a
2-turn tool-calling loop, a real commit landed
(`2effe57ac62ab7a5e1cd59caa86d4c9c3338bf10`) inside the ephemeral
sandbox workspace, verified via `git rev-parse HEAD`, and the sandbox
was torn down — `executor-sandbox` itself was untouched (bwrap only
mutates its copy). Full result: `tool_ok: true`, `committed: "True"`.
This proves the glas-harness → sand-boxer → rein → verified-commit path
end-to-end for one rein.
**Now also live-proven via `rein-aharness`/the real Claude Code CLI**
(2026-07-26, user's explicit go-ahead): same gateway, same
`profile.bwrap-local` sandbox, `rein=ReinAharness()`. Real `kaizen-agentic
schedule prepare coach` persona load (`persona_source: "prepare"`), real
`claude --print --permission-mode acceptEdits --allowedTools ...`
session under the `green-commit-only` allow-list, real commit
(`8b63424a514fb2e5d1b4c6e8919bb1d03ca6b873`) in 11.4s, verified via `git
rev-parse HEAD`, sandbox torn down, `executor-sandbox` untouched. Both
reins now have a live end-to-end proof through the same gateway code —
this is the parity proof HARNESS-WP-0002 and any later "retire
rein-aharness as a standalone concern" conversation was gated on.
Only remaining gap: State Hub reporting from the gateway itself is
still unwired (`--no-hub` was passed through to both reins' own CLIs
in both live runs — each rein's own hub reporting path exists, but
glas-harness doesn't yet post its own session-level event).
```task
id: GLAS-WP-0001-T04
status: done
priority: high
state_hub_task_id: "cdcacf98-62a1-47ae-92b9-e0320968ac1f"
```
## Task: Charter and bootstrap rein-openweights
Turn the `~/rein-openweights` scaffold into a working minimal rein: an
agentic tool-use loop (plan → tool call → observe → repeat) driving an
open-weight model via OpenRouter, implementing the GLAS-WP-0001-T01
contract. Start with the same tool surface as rein-aharness's
`green-commit-only` profile (Read/Write/Edit/Glob/Grep + local git) so
it's directly comparable. Needs its own Forgejo remote before anything
is pushed — coordinate repo creation with the operator.
`rein-openweights/REIN-OW-WP-0001` (T01-T04) is done: standalone agentic
loop, own OpenRouter client (not llm-connect — see that workplan's T01
for why), tool surface, credential acquisition, commit-verified success
+ hub reporting, 26 tests.
`glas_harness/reins/rein_openweights.py` now implements the `Rein` ABC
by shelling out to `rein-openweights run --task-file ... --no-hub
[--model ...]`, mirroring `reins/rein_aharness.py` exactly (both now
share `reins/_shared.py` for `git_head`/`write_task_file` — the
duplication was real, so it got factored out). `registry/reins/
rein-openweights.yaml` flipped to `status: implemented`. 8 new tests,
all mocked.
```task
id: GLAS-WP-0001-T05
status: done
priority: high
state_hub_task_id: "84fa62ae-3b34-42fd-a263-f65c35e87586"
```
## Task: Decide credential-brokering ownership for rein-openweights
Resolved in `docs/adr/ADR-002-credential-brokering-and-composable-reins.md`:
**Option B** — rein-openweights acquires its OpenRouter credential
directly (OpenBao/ops-warden), consistent with rein-aharness's own
credential-holder principle. glas-harness does not broker LLM-provider
credentials. Confirmed llm-connect itself never brokers credentials
either (`resolve_api_key()` only reads an already-materialized key) —
the earlier "does using llm-connect impose a credential model" concern
does not apply. A separate, deliberately deferred question (composable
reins as middleware for monitoring/eval/optimization) is recorded in the
same ADR as open, not blocking this task or T05.
```task
id: GLAS-WP-0001-T06
status: done
priority: medium
state_hub_task_id: "b4e775d6-ad81-40a4-9fd0-974ba27182fd"
```