Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
284 lines
13 KiB
Markdown
284 lines
13 KiB
Markdown
---
|
|
id: ACTIVITY-WP-0032
|
|
type: workplan
|
|
title: "Adopt the Glas profile-driven execution contract"
|
|
domain: infotech
|
|
repo: activity-core
|
|
status: active
|
|
owner: claude
|
|
topic_slug: activity-core
|
|
priority: medium
|
|
created: "2026-08-21"
|
|
updated: "2026-08-23"
|
|
related:
|
|
- ACT-ADR-006
|
|
- ACTIVITY-WP-0026
|
|
- ACTIVITY-WP-0029
|
|
- ACTIVITY-WP-0031
|
|
- ACT-ADR-005
|
|
- GLAS-WP-0004
|
|
- REIN-A-0002
|
|
state_hub_workstream_id: "256dad13-28b4-5361-ab8a-7d1373a5d14b"
|
|
---
|
|
|
|
# Adopt the Glas profile-driven execution contract
|
|
|
|
## Origin
|
|
|
|
`glas-harness` reported (2026-08-20) that GLAS-WP-0004 contract 1.0 is
|
|
implemented and proven: the same bounded task completed through **two**
|
|
different reins (`rein-aharness`, `rein-openweights`) via an explicit
|
|
`harness_profile_ref`, with real commits and verified sandbox destruction.
|
|
Their guidance to us: activity-core stays scheduler and task source, and should
|
|
name an **approved profile**, never a concrete rein.
|
|
|
|
ACTIVITY-WP-0026 is `finished`, so this does not belong as an amendment to it.
|
|
|
|
## Why this is worth doing
|
|
|
|
Today an ops_run carries `approach_hint` (`orm.py:172`), a free-text string the
|
|
executor interprets at claim time. That binding is too late and too weak. Live
|
|
example from railiance01, 2026-08-17:
|
|
|
|
```
|
|
state=failed attempt=1 claim_owner=rein-aharness@railiance01
|
|
title=Run SBOM rescan for binect-js
|
|
failure=no approach matched labels/definition;
|
|
labels=['sbom','security','automated'] def='2969816c-…'
|
|
```
|
|
|
|
The run was claimed, then died because nothing could execute it. A
|
|
`harness_profile_ref` is resolved and **refused before sandbox creation**, so
|
|
an unroutable request fails at emission with a clear reason instead of
|
|
consuming a claim and a lease.
|
|
|
|
Second motivation: we are currently coupled to one backend in practice —
|
|
every claim in the last week is `rein-aharness@railiance01`. The dual-rein
|
|
proof means that coupling is now avoidable rather than inherent.
|
|
|
|
## Boundary (must not drift)
|
|
|
|
activity-core stays **when / what / where**. Adopting this contract must not
|
|
turn it into an executor — SCOPE drift risk #1 ("convenience execution"). We
|
|
emit an authorized, profile-named request and record normalized evidence. We do
|
|
not select reins, provision sandboxes, acquire credentials, or run the inner
|
|
agentic loop. Glas owns profile resolution and the outer loop; the rein owns its
|
|
own inner loop; `sand-boxer` owns isolation.
|
|
|
|
We also do not author the attribution refs the contract accepts
|
|
(`assignment_ref`, `role_ref`, `duty_ref`, `goal_refs`,
|
|
`resource_envelope_refs`). Those come from workforce/leadership vocabulary in
|
|
`info-tech-canon`; activity-core carries them through, and must not invent org
|
|
roles here (ACTIVITY-WP-0029 responsibility map).
|
|
|
|
## Answered by glas-harness (2026-08-21)
|
|
|
|
Both questions are settled; recorded as **ACT-ADR-006**, hub decision
|
|
`147beec6-7fe7-4837-8e3a-4264a240379d`.
|
|
|
|
1. **Coexistence with distinct semantics.** `harness_profile_ref` is the
|
|
authoritative execution-constellation selector. `approach_hint` stays only as
|
|
a legacy activity/definition-matching hint and must **not** override,
|
|
synthesize, or fall back from an absent/invalid profile ref on governed
|
|
execution. Deprecate that use once inventory shows no caller depends on it.
|
|
2. **The glas-harness catalog is authoritative — do not mirror it.** Glas
|
|
exposes deterministic validation through its package and CLI but **no network
|
|
validation service** today. Emit-time remote validation would need a
|
|
separately scoped Glas API. The execution-side Glas resolver is the mandatory
|
|
fail-closed check.
|
|
|
|
Transport: **preserve the pull queue** in the first migration slice. The exact
|
|
versioned profile ref and attribution refs go in the queued payload; the
|
|
claiming executor passes the request into Glas, which resolves or refuses before
|
|
sandbox creation. This changes the execution contract without also changing
|
|
scheduling topology. A direct activity-core → Glas call can be evaluated
|
|
independently later.
|
|
|
|
## Decide the invocation shape
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T01
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "a67a896d-ca3f-59c7-b11c-47163505ccd6"
|
|
```
|
|
|
|
Resolved 2026-08-21 as **option A — the queue carries the profile**, on the
|
|
advice of glas-harness as contract owner. `ops_run` grows `harness_profile_ref`
|
|
plus attribution refs; lease semantics, durability, and the operator console are
|
|
unchanged; activity-core stays behind the executor boundary.
|
|
|
|
Option B (activity-core calls the Glas gateway directly) was rejected for this
|
|
slice: it would discard the claim/lease/retry machinery ACTIVITY-WP-0026 just
|
|
built and change scheduling topology at the same time as the execution contract.
|
|
It remains available as an independent later evaluation.
|
|
|
|
Written up as `docs/adr/adr-006-glas-profile-execution.md`.
|
|
|
|
## Carry the profile and attribution refs
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T02
|
|
status: done
|
|
priority: high
|
|
state_hub_task_id: "0b0a5a93-9bd3-5b8a-8f98-97bebf7cc073"
|
|
```
|
|
|
|
Done 2026-08-21. `ops_runs` grows `harness_profile_ref` (text, indexed) and
|
|
`execution_refs` (jsonb, default `{}`), migration `0008`, threaded through
|
|
`ops_run_queue.py`, `activities.py`, and `run_artifacts.py`.
|
|
`src/activity_core/glas_profile.py` holds the selection rules;
|
|
`docs/ops-run-queue.md` documents the contract.
|
|
|
|
The no-fallback rule is enforced in `resolve_execution_selector`, not merely
|
|
documented: a malformed ref raises even when a hint is present, and
|
|
`ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE` makes an absent one an error once
|
|
definitions have migrated. 34 tests, with the `TestNoFallbackToApproachHint`
|
|
class covering the property directly.
|
|
|
|
Migration verified against a real PostgreSQL 16: upgrade → columns and index
|
|
present → downgrade 0007 → clean re-upgrade. A legacy-shaped row (approach_hint
|
|
only, no profile) still inserts, defaults `execution_refs` to `{}`, and stays
|
|
`open`/claimable — existing production runs are unaffected.
|
|
|
|
Add `harness_profile_ref` and the correlation/attribution refs
|
|
to the ops_run model, emission path (`activities.py`), queue projection
|
|
(`ops_run_queue.py`), and run artefacts (`run_artifacts.py`), with a migration.
|
|
Definitions declare the profile; rules pass it through. Per ACT-ADR-006 the two
|
|
fields **coexist with distinct semantics**: enforce in code that `approach_hint`
|
|
cannot override, synthesize, or fall back from an absent/invalid
|
|
`harness_profile_ref` on governed execution, and cover that with a test — a
|
|
silent fallback is exactly the failure this workplan removes.
|
|
|
|
## Validate what we can, fail closed on the rest
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T03
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "50225bac-1798-5cf5-91ad-d4c662de7fc7"
|
|
```
|
|
|
|
Depends on T02. **Corrected after the glas-harness answer:** emit-time *remote*
|
|
validation is not available — Glas has no network validation service, and
|
|
mirroring its catalogue is explicitly forbidden. So:
|
|
|
|
- Validate locally and structurally at definition sync and run emission: a
|
|
governed run has a profile ref, and it is well-formed and versioned.
|
|
- Do **not** resolve the ref against a mirrored list. The execution-side Glas
|
|
resolver is the authority and the mandatory fail-closed check.
|
|
- An absent or malformed ref is an error at emission. It must never degrade into
|
|
"emit anything" or fall back to `approach_hint`.
|
|
|
|
Record the residual gap honestly: a well-formed but unknown profile is still
|
|
caught at execution rather than emission. That is weaker than refusing at emit,
|
|
and still strictly better than the claim-time failure of 2026-08-17, because
|
|
Glas refuses before sandbox creation. If emit-time remote validation becomes
|
|
necessary, raise it as a capability request against glas-harness rather than
|
|
solving it locally.
|
|
|
|
Done 2026-08-22. Definition parsing now validates every declared rule or
|
|
instruction profile structurally and normalizes its version-pinned ref before
|
|
DB sync. With `ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE=true`, sync refuses
|
|
task-emitting declarations that omit a profile; deterministic report-only
|
|
instructions remain exempt. Rule and instruction expansion carry the declared
|
|
profile, legacy hint, and allowlisted attribution refs into the queued request.
|
|
|
|
Emission now preflights the complete task batch before opening either the DB or
|
|
IssueSink. A missing profile in strict mode or any malformed profile becomes a
|
|
non-retryable activity error, so a later invalid item cannot leave earlier
|
|
items partially emitted and `approach_hint` cannot rescue it. Tests also prove
|
|
that an unknown but structurally valid versioned ref is preserved for the
|
|
authoritative execution-side Glas resolver; no local profile catalogue was
|
|
introduced. Verification: 59 focused tests and 426 repository tests passed;
|
|
the one live NATS-to-Temporal bridge test was deselected because it requires
|
|
the local integration stack. Python compilation and `git diff --check` passed.
|
|
|
|
## Record normalized execution evidence
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T04
|
|
status: done
|
|
priority: medium
|
|
state_hub_task_id: "48ff9bca-6251-59ff-8b6f-6b0d79252624"
|
|
```
|
|
|
|
Depends on T02. Glas emits normalized, non-secret execution evidence. Land it in
|
|
`ops_runs.result` and the evidence sink so the production status surface
|
|
(ACTIVITY-WP-0031-T03) shows what actually executed, through which profile and
|
|
rein. Keep the field allowlist discipline from ACTIVITY-WP-0031: no provider
|
|
blobs, no credential material in run artefacts.
|
|
|
|
Done 2026-08-22 against Glas contract 1.0. Both queue completion and failure
|
|
normalize the returned `GatewayResult` before persistence. Activity Core keeps
|
|
the declared `ExecutionEvidence` constellation, outcome/failure stage, bounded
|
|
measurements, commit/artifact references, and allowlisted organizational refs
|
|
under `ops_runs.result.execution_evidence`. It preserves meaningful zero values
|
|
and explicit `unavailable`, while dropping direct `tool_output`, `tool_error`,
|
|
prompts, messages, provider responses, unknown nested blobs, and undeclared
|
|
refs. Historic results are normalized again at read time.
|
|
|
|
The per-run API/UI projection now includes `execution_evidence`, so operators
|
|
can see profile, rein, resolved model, sandbox, outcome, duration, tokens, and
|
|
artifact facts without provider payloads. Tests cover success, failure,
|
|
redaction, invalid enums/measurements, unknown-versus-zero semantics, legacy
|
|
artifact compatibility, and the run summary. Verification: 71 focused tests
|
|
and 433 repository tests passed; the live NATS-to-Temporal bridge test remained
|
|
deselected because it requires the local integration stack.
|
|
|
|
## Prove on one definition
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T05
|
|
status: wait
|
|
priority: low
|
|
state_hub_task_id: "e71423f9-8baf-566b-8a70-eec195e2f65f"
|
|
```
|
|
|
|
Depends on T03/T04. Convert exactly one low-risk definition, run it on
|
|
railiance01, and capture evidence. Do not convert the fleet before one
|
|
definition is proven end to end. Note that FI and Binky are unsuitable as the
|
|
pilot while ACTIVITY-WP-0031-T01 is unresolved — their failures are currently
|
|
provider-credential failures and would mask the result.
|
|
|
|
Resumed 2026-08-23. Rein-aharness source `0f01c3e` is deployed on railiance01:
|
|
profiled rows now preserve `harness_profile_ref` / `execution_refs`, route
|
|
through Glas before legacy selection, refuse without fallback, and return the
|
|
full `GatewayResult`. Handoff confirmation
|
|
`413a4041-cfb3-4d02-a41a-1d92b3de06af` also identified production schema
|
|
drift: the live image expects migration `0008`, while PostgreSQL was still at
|
|
`0007`, making `/ops-runs/claim` fail with `UndefinedColumnError`. Apply and
|
|
verify the additive migration before scheduling the pilot.
|
|
|
|
Live progress 2026-08-23 is recorded in
|
|
`docs/evidence/ACTIVITY-WP-0032-glas-profile-pilot-2026-08-23.md`. Migration,
|
|
current image rollout, definition sync, selector/refs, profile resolution, and
|
|
normalized failure evidence are proven. Installing the missing Ubuntu
|
|
`bubblewrap` package enabled a direct create/destroy preflight.
|
|
|
|
Rein-aharness then fixed and deployed the governance mapping in source
|
|
`c633291`: the queue claim owner remains `rein-aharness@railiance01`, while the
|
|
Glas `ExecutionRequest.actor` is now `agt`. Rein ran the exact disabled pilot
|
|
once after that deployment. Ops run `ededc939-266f-473c-8386-ffd3f027f5f0`
|
|
preserved `harness.agent-dev-local@1.0.0` and both T05 refs, resolved the
|
|
rein/model constellation, created sandbox `d750cd5c`, and failed closed at
|
|
`session_start` before provider dispatch. The sandbox reached `destroyed`, its
|
|
workspace is absent, and no artifact or commit was produced. This resolves the
|
|
actor mismatch without weakening queue ownership identity.
|
|
|
|
Returned to wait on upstream `GLAS-IN-0002`: the managed consumer is denied
|
|
`nsenter`, and the rein/model runtime and egress contract is not yet available
|
|
inside the sandbox. Resume after that contract is implemented; do not trigger
|
|
another pilot until the upstream blocker changes.
|
|
|
|
## Acceptance
|
|
|
|
- [x] Invocation shape decided and recorded as an ADR, with the boundary stated
|
|
(ACT-ADR-006; pull queue preserved)
|
|
- [x] ops_run carries an approved `harness_profile_ref`; no definition names a
|
|
concrete rein
|
|
- [x] Malformed/absent profile refs are refused at emission; unknown-but-well-formed
|
|
refs are refused by Glas before sandbox creation, never at claim
|
|
- [x] `approach_hint` cannot override or substitute for a profile ref, proven by test
|
|
- [x] Normalized Glas evidence is visible in production status
|
|
- [ ] One definition proven on railiance01 end to end
|