ACTIVITY-WP-0032-T02 / ACT-ADR-006. ops_runs grows harness_profile_ref (text, indexed) and execution_refs (jsonb), migration 0008, threaded through the emission path, queue projection, and run artefacts. The important part is the enforcement, not the columns. ACT-ADR-006 says approach_hint must never override, synthesize, or fall back from an absent or invalid harness_profile_ref — a silent fallback would reintroduce the claim-time routing failure of 2026-08-17. resolve_execution_selector never consults the hint: a malformed ref raises even when a hint is present, and ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE turns an absent ref into an error once definitions have migrated. Validation is structural only, since the glas-harness catalogue is authoritative and must not be mirrored. Requiring the <id>@<version> pin is worth doing locally: GlasProfiles.resolve matches an unpinned ref against every version and refuses it as ambiguous, so the pin converts a late failure into an emission-time error without knowing any profile id. Migration verified on real PostgreSQL 16: upgrade, downgrade, re-upgrade, and a legacy-shaped row still inserts and stays claimable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
214 lines
8.8 KiB
Markdown
214 lines
8.8 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-21"
|
|
related:
|
|
- ACT-ADR-006
|
|
- ACTIVITY-WP-0026
|
|
- ACTIVITY-WP-0029
|
|
- ACTIVITY-WP-0031
|
|
- ACT-ADR-005
|
|
- GLAS-WP-0004
|
|
- REIN-A-0002
|
|
---
|
|
|
|
# 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
|
|
```
|
|
|
|
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
|
|
```
|
|
|
|
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: todo
|
|
priority: medium
|
|
```
|
|
|
|
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.
|
|
|
|
## Record normalized execution evidence
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T04
|
|
status: wait
|
|
priority: medium
|
|
```
|
|
|
|
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.
|
|
|
|
## Prove on one definition
|
|
|
|
```task
|
|
id: ACTIVITY-WP-0032-T05
|
|
status: wait
|
|
priority: low
|
|
```
|
|
|
|
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.
|
|
|
|
## Acceptance
|
|
|
|
- [x] Invocation shape decided and recorded as an ADR, with the boundary stated
|
|
(ACT-ADR-006; pull queue preserved)
|
|
- [ ] ops_run carries an approved `harness_profile_ref`; no definition names a
|
|
concrete rein
|
|
- [ ] Malformed/absent profile refs are refused at emission; unknown-but-well-formed
|
|
refs are refused by Glas before sandbox creation, never at claim
|
|
- [ ] `approach_hint` cannot override or substitute for a profile ref, proven by test
|
|
- [ ] Normalized Glas evidence is visible in production status
|
|
- [ ] One definition proven on railiance01 end to end
|