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