activity-core/workplans/ACTIVITY-WP-0032-glas-profile-execution-contract.md
tegwick 17f2caee01
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 5s
feat: guard prod status since-arg; plan Glas contract adoption
scripts/prod_automation_status.sh took a positional since value, so the
Makefile's own `SINCE=sunday` form passed the literal string into timestamptz:
all five sections errored while the run still looked like it produced a report.
Now `SINCE=` is accepted and the value is parsed and normalised to UTC up
front, failing fast with usage and exit 2 instead of five parse errors.

ACTIVITY-WP-0032 plans adoption of the glas-harness contract 1.0 reported in
GLAS-WP-0004. WP-0026 is finished, so this gets its own plan. The motivation is
concrete: ops_run.approach_hint binds at claim time, which produced a failed
run on 2026-08-17 ("no approach matched labels/definition") after it had
already consumed a claim and a lease.

T01 is deliberately blocking: our claim path is pull-based and the Glas
contract is a call, so the invocation shape is an architectural decision, not a
port. Two questions are outstanding with glas-harness.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 09:05:04 +02:00

6.4 KiB

id type title domain repo status owner topic_slug priority created updated related
ACTIVITY-WP-0032 workplan Adopt the Glas profile-driven execution contract infotech activity-core proposed claude activity-core medium 2026-08-21 2026-08-21
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).

Open questions (asked of glas-harness 2026-08-21, not yet answered)

  1. Is approach_hint expected to be replaced by harness_profile_ref, or to coexist?
  2. Is the profile registry authoritative in glas-harness, so we validate refs by calling them rather than mirroring a catalogue that can drift?

T01 is blocked on these; the rest follows the answer.

Decide the invocation shape

id: ACTIVITY-WP-0032-T01
status: wait
priority: high

Our claim path is pull-based: we insert ops_run(open) and rein-aharness claims it. The Glas contract is shaped as a call. Those do not compose automatically, and the choice is architectural, not mechanical. Record a decision (ACT-ADR) between:

  • A — queue carries the profile. ops_run grows harness_profile_ref plus the attribution refs; a Glas-aware claimer resolves the profile. Keeps lease semantics, durability, and the operator console unchanged. Keeps one more indirection between authorization and execution.
  • B — activity-core calls the Glas gateway. Direct invocation with the approved profile. Matches the contract as designed and fails unroutable requests before sandbox creation. Costs us the claim/lease/retry machinery ACTIVITY-WP-0026 just built, and moves activity-core closer to the executor boundary we deliberately hold.

Do not start T02+ before this is resolved. Prefer A unless glas-harness states the profile must be resolved at emission time to keep its refusal guarantee.

Carry the profile and attribution refs

id: ACTIVITY-WP-0032-T02
status: wait
priority: medium

Depends on T01. 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. Decide approach_hint's fate per open question 1 — if it coexists, document which wins; if it is replaced, strangle it rather than leaving two routing fields.

Validate refs at emission

id: ACTIVITY-WP-0032-T03
status: wait
priority: medium

Depends on T01/T02. An unknown or incompatible profile must be refused when the ActivityDefinition is synced or the run is emitted — not discovered at claim time, which is the failure mode this workplan exists to remove. Per open question 2, prefer calling the glas-harness registry over mirroring it. Include the offline/unreachable behaviour: a registry we cannot reach must not silently downgrade to "emit anything".

Record normalized execution evidence

id: ACTIVITY-WP-0032-T04
status: wait
priority: low

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

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

  • Invocation shape decided and recorded as an ADR, with the boundary stated
  • ops_run carries an approved harness_profile_ref; no definition names a concrete rein
  • Unroutable profiles are refused at emission, not at claim
  • approach_hint is either strangled or documented as subordinate
  • Normalized Glas evidence is visible in production status
  • One definition proven on railiance01 end to end