111 lines
4.6 KiB
Markdown
111 lines
4.6 KiB
Markdown
# Harness contract
|
|
|
|
Contract version: `1.0` (`glas_harness.contract.CONTRACT_VERSION`).
|
|
|
|
This is the stable boundary between Glas, its consumers, and concrete harness
|
|
backends (reins). Glas owns profile resolution and the outer lifecycle; a rein
|
|
owns its inner agentic loop. Sand-boxer owns environment provisioning.
|
|
|
|
## Consumer boundary
|
|
|
|
`ExecutionRequest` requires an explicit `harness_profile_ref`, repository,
|
|
title, and description. It may carry correlation and organizational references
|
|
(`assignment_ref`, `role_ref`, `duty_ref`, `goal_refs`, and
|
|
`resource_envelope_refs`). Those references link execution evidence to
|
|
leadership/workforce records; they do not transfer organizational authority to
|
|
Glas.
|
|
|
|
The gateway returns `GatewayResult` with:
|
|
|
|
- `ok`, derived from normalized outcome;
|
|
- `evidence`, safe for compact State Hub reporting; and
|
|
- direct-caller-only `tool_output` and `tool_error`.
|
|
|
|
Raw prompts, model output, tool output, and credential material are excluded
|
|
from the State Hub detail.
|
|
|
|
## Rein lifecycle
|
|
|
|
```python
|
|
class Rein(ABC):
|
|
def start_session(
|
|
self, profile: HarnessProfile, inputs: dict[str, str], sandbox: SandboxHandle
|
|
) -> dict[str, Any]: ...
|
|
|
|
def dispatch_tool(
|
|
self, session: dict[str, Any], tool_call: ToolCall
|
|
) -> ToolResult: ...
|
|
|
|
def end_session(self, session: dict[str, Any]) -> ExecutionSummary: ...
|
|
|
|
def cleanup_session(self, session: dict[str, Any]) -> None: ...
|
|
```
|
|
|
|
```text
|
|
Glas rein sand-boxer
|
|
---- ---- ----------
|
|
resolve profile + rein descriptor
|
|
create sandbox ------------------------------------------> create
|
|
derive workspace + transport from handle
|
|
start_session(profile, inputs, handle) -> sandbox-local setup
|
|
dispatch_tool(session, call) -> inner loop across transport
|
|
end_session(session) -> normalized facts
|
|
cleanup_session(session) -> remove ephemeral task material
|
|
destroy sandbox -----------------------------------------> destroy
|
|
publish compact ExecutionEvidence
|
|
```
|
|
|
|
After sandbox creation, the caller's source checkout is no longer an execution
|
|
path. A same-host descriptor must contain `pid` plus `workspace_dir` and every
|
|
rein command is wrapped with `nsenter`; a remote descriptor must contain `ssh`
|
|
plus `remote_dir` and every command crosses SSH. Incomplete, mixed, or unknown
|
|
reachability refuses at session start. The transport also bounds the outer rein
|
|
subprocess with the profile timeout. A host must make the selected rein command
|
|
and its dependencies available inside that transport; host-only installation is
|
|
not treated as sandbox availability.
|
|
|
|
There is no production default rein. Direct `Rein` injection remains a narrow
|
|
library/test seam but still requires a valid profile so profile, sandbox,
|
|
model, tool policy, and evidence are explicit.
|
|
|
|
## Stability and extension rules
|
|
|
|
All contract models use Pydantic with unknown fields forbidden. Stable `1.0`
|
|
fields are the declared fields in `src/glas_harness/contract.py`. Optional
|
|
measurements such as tokens, duration, resolved model, commit, and artifacts
|
|
remain `null` when unknown; unsupported tool-event visibility is
|
|
`unavailable`, not an empty claim of completeness.
|
|
|
|
Rein-specific, non-secret additions belong only in declared `metadata` fields.
|
|
Adding a required field, changing a field meaning/type, or removing a field
|
|
requires a new contract version. Adding an optional field may remain compatible
|
|
only when old consumers can safely ignore it and every strict boundary model is
|
|
updated together. A profile and rein descriptor must both declare the exact
|
|
supported contract version.
|
|
|
|
Sensitive values are never contract extensions. Profiles may contain
|
|
credential-route references, while credential resolution and provider setup
|
|
remain rein-local under ADR-002.
|
|
|
|
## Failure semantics
|
|
|
|
The gateway returns evidence for every normal refusal/failure path:
|
|
|
|
- `resolution`: unknown, disabled, ambiguous, incompatible, or unsafe profile;
|
|
- `sandbox_create`;
|
|
- `session_start`;
|
|
- `execution` (including a rein-declared unsuccessful result);
|
|
- `session_end`; and
|
|
- `teardown`.
|
|
|
|
Profile resolution happens before sandbox creation. Once a sandbox exists,
|
|
rein cleanup and sandbox teardown are attempted on every path. `refused` means
|
|
governed execution did not proceed; `failed` means an attempted lifecycle did
|
|
not complete successfully.
|
|
|
|
## Deliberate non-goals
|
|
|
|
The contract does not schedule work, source blueprints, allocate workers,
|
|
resolve leadership, broker credentials, select an unapproved model by price, or
|
|
standardize rein internals. Composable rein middleware remains deferred by
|
|
ADR-004 until a second concrete need exists.
|