From 1c4b3c592ca10b800a38c4d3be812beb04524981 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 21 Aug 2026 13:57:48 +0200 Subject: [PATCH] docs: accept ACT-ADR-006, profile-driven execution over the pull queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glas-harness answered both open questions from ACTIVITY-WP-0032, so T01 is resolved as option A: the ops_run pull queue stays and carries the versioned harness_profile_ref plus attribution refs. 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. Two answers shaped the plan: - harness_profile_ref and approach_hint coexist with distinct semantics. approach_hint must never override, synthesize, or fall back from an absent or invalid profile ref on governed execution — T02 must enforce that in code. - The glas-harness catalog is authoritative and must not be mirrored. Glas has no network validation service today, so emit-time remote validation is not available. T03 is corrected accordingly: validate locally and structurally, rely on the execution-side Glas resolver as the mandatory fail-closed check, and record the residual gap rather than papering over it with a mirrored catalogue. Hub decision 147beec6-7fe7-4837-8e3a-4264a240379d. Co-Authored-By: Claude Opus 5 --- SCOPE.md | 2 + docs/adr/adr-006-glas-profile-execution.md | 137 ++++++++++++++++++ ...WP-0032-glas-profile-execution-contract.md | 106 ++++++++------ 3 files changed, 204 insertions(+), 41 deletions(-) create mode 100644 docs/adr/adr-006-glas-profile-execution.md diff --git a/SCOPE.md b/SCOPE.md index 069516a..f9b39b5 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -282,6 +282,8 @@ Open work (SBOM catch-up parked — CUST-WP-0062 / ACTIVITY-WP-0030): - `docs/adr/adr-002-definition-format.md` - `docs/adr/adr-003-rule-instruction-model.md` - `docs/adr/adr-004-producer-trust-boundary.md` +- `docs/adr/adr-005-ops-runs-vs-dev-work-records.md` +- `docs/adr/adr-006-glas-profile-execution.md` --- diff --git a/docs/adr/adr-006-glas-profile-execution.md b/docs/adr/adr-006-glas-profile-execution.md new file mode 100644 index 0000000..e3cef9a --- /dev/null +++ b/docs/adr/adr-006-glas-profile-execution.md @@ -0,0 +1,137 @@ +--- +id: ACT-ADR-006 +type: architecture-decision-record +title: "Profile-driven execution selection over the ops_run pull queue" +status: accepted +owner: activity-core +revision: "accepted-1" +last_reviewed: "2026-08-21" +review_interval: 6m +decided_by: Bernd Worsch +date: "2026-08-21" +scope: cross-repo +affects: + - activity-core + - glas-harness + - rein-aharness + - sand-boxer +tags: + - architecture + - activity-core + - ops-run + - glas-harness + - execution +supersedes_notes: > + Extends ACT-ADR-005. Does not change the ops_run claim/lease plane; it + changes what a queued run says about how it should execute. +--- + +# ACT-ADR-006: Profile-driven execution selection over the ops_run pull queue + +## Status + +**Accepted** (2026-08-21). Hub decision `147beec6-7fe7-4837-8e3a-4264a240379d` +("Glas contract 1.0 makes execution constellation selection explicit"). + +## Context + +ACT-ADR-005 gave internal scheduled automation a claimable `ops_run` plane. +What an ops_run said about *how* to execute was `approach_hint` — free text, +interpreted by the claiming executor at claim time. + +That binding is too late. Live failure, 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'] +``` + +The run was claimed and leased, *then* died because nothing could execute it. +A claim was consumed to discover the request was unroutable. + +`glas-harness` GLAS-WP-0004 closed contract 1.0 and proved it by running one +bounded task through two different reins (`rein-aharness`, `rein-openweights`) +via an explicit `harness_profile_ref`, with real commits and verified sandbox +destruction. Meanwhile every activity-core claim in the week to 2026-08-21 was +`rein-aharness@railiance01` — a coupling that is now avoidable rather than +inherent. + +Two shapes were considered: keep the pull queue and carry the profile as +payload, or have activity-core call the Glas gateway directly. glas-harness, +which owns the contract, advised the first for the initial migration. + +## Decision + +### 1. The queue stays; the payload changes + +activity-core continues to insert `ops_run(open)` and executors continue to +claim under lease. Scheduling topology does not change. What changes is that a +queued run carries an exact **versioned `harness_profile_ref`** plus the +correlation / assignment / role / duty / goal / resource-envelope references. +The claiming executor passes that request into Glas, which resolves or refuses +it **before sandbox creation**. + +This changes the execution contract without simultaneously changing the +scheduling topology — one variable at a time. A direct activity-core → Glas +call may be evaluated later, independently, if pull delivery stops meeting +operational needs. + +### 2. `harness_profile_ref` is authoritative; `approach_hint` is legacy + +The two coexist with **distinct semantics**, not as fallbacks for one another: + +- `harness_profile_ref` is the authoritative execution-constellation selector. +- `approach_hint` remains only a legacy activity/definition-matching hint while + producers and consumers migrate. + +`approach_hint` must **not** override, synthesize, or fall back from an absent +or invalid `harness_profile_ref` on governed execution. A missing profile is an +error, never an invitation to guess from a hint. Once inventory shows no caller +depends on `approach_hint` for runtime selection, that use is deprecated. + +### 3. activity-core does not mirror the profile catalog + +The catalog in glas-harness is authoritative. activity-core must not maintain +an independent list, which would drift and produce a second, disagreeing +opinion about what is executable. + +Consequence, accepted knowingly: Glas exposes deterministic validation through +its package and CLI but **no network validation service** today, so +activity-core cannot remotely validate a profile ref at emission time. We +therefore validate only what is local and structural — that a ref is present +and well-formed for governed execution — and rely on the **execution-side Glas +resolver as the mandatory fail-closed check**. + +This is weaker than refusing at emission, and deliberately so: an unreachable +or absent validation service must never downgrade into "emit anything". If +emit-time remote validation is later required, it needs a separately scoped +Glas API and its own decision — not a mirrored catalogue here. + +Even unvalidated at emit, this is strictly better than the 2026-08-17 failure: +refusal happens deterministically before sandbox creation rather than after a +claim and lease were consumed. + +### 4. activity-core does not become an executor + +We emit an authorized, profile-named request and record normalized evidence. +We do not select reins, provision sandboxes, acquire credentials, or run any +inner agentic loop. Glas owns profile resolution and the outer loop; the rein +owns its inner loop; `sand-boxer` owns isolation. This is SCOPE drift risk #1 +("convenience execution") and this ADR does not relax it. + +The attribution references we carry (`assignment_ref`, `role_ref`, `duty_ref`, +`goal_refs`, `resource_envelope_refs`) are **passed through**, not authored +here. Their vocabulary belongs to `info-tech-canon`; activity-core must not +invent org roles (ACTIVITY-WP-0029 responsibility map). + +## Consequences + +- `ops_runs` grows `harness_profile_ref` and the attribution refs; the emission + path, queue projection, and run artefacts carry them. +- Definitions declare a profile; rules pass it through. +- Unroutable requests fail before sandbox creation instead of at claim time. +- activity-core is no longer coupled to one rein in practice. +- Emit-time validation remains a known gap, owned by a future Glas API. +- Implementation: **ACTIVITY-WP-0032**. diff --git a/workplans/ACTIVITY-WP-0032-glas-profile-execution-contract.md b/workplans/ACTIVITY-WP-0032-glas-profile-execution-contract.md index 5d74866..3e936f3 100644 --- a/workplans/ACTIVITY-WP-0032-glas-profile-execution-contract.md +++ b/workplans/ACTIVITY-WP-0032-glas-profile-execution-contract.md @@ -4,13 +4,14 @@ type: workplan title: "Adopt the Glas profile-driven execution contract" domain: infotech repo: activity-core -status: proposed +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 @@ -69,77 +70,98 @@ We also do not author the attribution refs the contract accepts `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) +## Answered by glas-harness (2026-08-21) -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? +Both questions are settled; recorded as **ACT-ADR-006**, hub decision +`147beec6-7fe7-4837-8e3a-4264a240379d`. -T01 is blocked on these; the rest follows the answer. +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: wait +status: done 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: +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. -- **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. +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. -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. +Written up as `docs/adr/adr-006-glas-profile-execution.md`. ## Carry the profile and attribution refs ```task id: ACTIVITY-WP-0032-T02 -status: wait -priority: medium +status: todo +priority: high ``` -Depends on T01. Add `harness_profile_ref` and the correlation/attribution refs +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. +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 refs at emission +## Validate what we can, fail closed on the rest ```task id: ACTIVITY-WP-0032-T03 -status: wait +status: todo 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". +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: low +priority: medium ``` Depends on T02. Glas emits normalized, non-secret execution evidence. Land it in @@ -164,10 +186,12 @@ provider-credential failures and would mask the result. ## Acceptance -- [ ] Invocation shape decided and recorded as an ADR, with the boundary stated +- [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 -- [ ] Unroutable profiles are refused at emission, not at claim -- [ ] `approach_hint` is either strangled or documented as subordinate +- [ ] 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