Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a100de-2e1c-7e23-9ae9-2da0e0499411
327 lines
16 KiB
Markdown
327 lines
16 KiB
Markdown
---
|
||
id: ACTIVITY-WP-0032
|
||
type: workplan
|
||
title: "Adopt the Glas profile-driven execution contract"
|
||
domain: infotech
|
||
repo: activity-core
|
||
status: blocked
|
||
flavor: implementation
|
||
owner: claude
|
||
topic_slug: activity-core
|
||
priority: medium
|
||
created: "2026-08-21"
|
||
updated: "2026-10-03"
|
||
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
|
||
depends_on: [GLAS-WP-0012]
|
||
blocking_reason: "No admitted Glas profile: waits on GLAS-WP-0012 (upstream GLAS-IN-0002, sand-boxer nsenter denial); do not rerun the old pilot."
|
||
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.
|
||
|
||
Rechecked 2026-09-04. The blocker has not changed: State Hub intake
|
||
`01a02b76-f020-7d60-a3ce-12a34c13ebce` remains open, sand-boxer has no active
|
||
workplan for it, and both Glas local profiles explicitly report
|
||
`operational_readiness: blocked`. Sand-boxer still publishes only a PID and
|
||
workspace plus a consumer-side `nsenter` hint; its Glas integration prose has
|
||
not yet been reconciled with the proven permission failure. The bwrap profile
|
||
also contains no rein runtime and declares network default-deny with no egress.
|
||
|
||
State Hub message `c57919ed-1cfd-491b-98a2-d72042ef10cb` asks sand-boxer to
|
||
promote the intake and deliver an owner-mediated execution boundary (or an
|
||
equivalent reviewed contract), in-sandbox runtime, explicit egress, identity
|
||
binding, and teardown proof. Credential delivery must use catalog references;
|
||
the open-weight lane is `rein-openweights-openrouter-approle`, whose current
|
||
delegation explicitly remains interim and lacks an owner-fronted read. No key
|
||
was requested or copied, and the disabled pilot remains untouched.
|
||
|
||
## 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
|
||
|
||
## Loose-end review — 2026-09-27
|
||
|
||
T05 remains blocked outside this repository. Current owner evidence supersedes
|
||
the September 4 description: sand-boxer has implemented owner-mediated execution
|
||
and protected runtime placement, but GLAS-WP-0012 remains blocked. The original
|
||
1.0.0 pilot profile is still explicitly blocked; 1.1.1 is unverified and needs
|
||
its updated runtime and combined production proof. Native credential admission,
|
||
provider execution and profile readiness remain owner gates. Do not rerun the
|
||
old pilot or silently change its profile. Resume T05 after Glas supplies an
|
||
admitted profile and matching railiance01 runtime/credential evidence. Workplan
|
||
state is now blocked; the four completed implementation tasks remain done.
|
||
|
||
2026-10-03: the earlier depends_on (T03, T04, ACTIVITY-WP-0031-T01) were all terminal, so C-40 read this as satisfied. The real blocker named above was prose only. CUST-WP-0075 encoded it as depends_on GLAS-WP-0012 and returned the task to wait.
|
||
|
||
|
||
## Dependency recheck — 2026-10-03
|
||
|
||
T05 remains wait and the plan blocked. GLAS-WP-0012-T02–T05 still await the
|
||
owner runtime/credential delivery and combined real-provider proof. Catalog
|
||
`harness.agent-dev-local@1.0.0` remains blocked; `1.1.1` remains unverified and
|
||
requires the rebuilt runtime through its owner on railiance01. No accepted
|
||
profile is available for this repo's pilot. Existing carry/validation code is
|
||
complete; do not enable the disabled pilot or substitute an unverified profile.
|