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

7.2 KiB

id type title status state_hub_workstream_id
GLAS-WP-0001 workplan Harness router foundation: contract + first two reins finished 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.

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.

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.

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).

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.

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.

id: GLAS-WP-0001-T06
status: done
priority: medium
state_hub_task_id: "b4e775d6-ad81-40a4-9fd0-974ba27182fd"