From 93e518fcd4d4a09a367f1ceff0fbd37b3378c8d3 Mon Sep 17 00:00:00 2001
From: Claude <noreply@anthropic.com>
Date: Sun, 14 Jun 2026 14:20:46 +0000
Subject: [PATCH] docs(merge-orchestration): Bag-of-Actions execution-substrate
 design

Records the mapping from the merge-orchestration runtime onto the bag-of-actions
continuation runtime (the owner-flagged alternative execution substrate), so the
build is ready the moment that repo is in session scope.

  * lease -> capability route; no-double-merge -> Ephapax linear type;
    attestation -> trust proof-chain; actuate hand-off -> Freeze/Thaw migration;
    self-hosted BEAM mesh -> runs off GitHub Actions minutes (bears on the farm
    billing question, which the owner has deferred).
  * Integration sketch: a BatonEmitter on the hypatia side submitting armed
    decisions as Batons whose required capability is {repo, github_merge_token};
    a merge_pr action + capability facts on the bag-of-actions side.

DESIGN ONLY -- bag-of-actions is not in this session's scope, so its exact Baton
format / Bag.Mesh API could not be read first-hand; nothing is wired. LEDGER
v0.8.0 records this plus the P3/mass_squash farm landing (PR #79), the a5
draft-pending status, and the Dispatcher/actuate manifest-shape seam.
---
 .../merge-orchestration/LEDGER.a2ml           |   9 +-
 ...05-execution-substrate-bag-of-actions.adoc | 148 ++++++++++++++++++
 2 files changed, 156 insertions(+), 1 deletion(-)
 create mode 100644 docs/design/merge-orchestration/05-execution-substrate-bag-of-actions.adoc

diff --git a/.machine_readable/merge-orchestration/LEDGER.a2ml b/.machine_readable/merge-orchestration/LEDGER.a2ml
index 87316254..078d6789 100644
--- a/.machine_readable/merge-orchestration/LEDGER.a2ml
+++ b/.machine_readable/merge-orchestration/LEDGER.a2ml
@@ -9,7 +9,7 @@
 
 [metadata]
 workstream = "merge-orchestration"
-version = "0.7.0"
+version = "0.8.0"
 last-updated = "2026-06-14"
 owner = "paraordinate (Jonathan D.A. Jewell)"
 branch = "claude/peaceful-pascal-IRlgq"   # same branch name across all in-scope repos
@@ -67,3 +67,10 @@ other-agents  = "claude/nice-ptolemy-lNB8E (standards), claude/loving-cannon-AtU
 
 [next]
 step = "MERGE-ORCHESTRATION SYSTEM COMPLETE + AUTONOMY LOOP CLOSED + SCHEDULED (cron + tick). Full pipeline on main: SENSE Sensor (#492) + producer observe.sh (#78); DELIBERATE Strategist + KinCompetence/Kin.Council (#494); RUNTIME TIER KinGate + FileStore (#494); LIVE LOOP (#495); SCHEDULED TRIGGER Scheduler.cycle + mix hypatia.merge_orchestrate (#496); ACTUATE Dispatcher manifest -> .git-private-farm actuate.sh (#77). CRON+TICK (this commit): (1) TICK -- Hypatia.MergeOrchestration.Ticker GenServer (mirrors LearningScheduler: send_after :tick -> guarded do_cycle -> reschedule; run_now/status; a cycle failure is rescued+recorded, never crashes the tick). Wired into application.ex via merge_orchestration_children() mirroring tui_children() -- DEFAULT OFF, enabled by `config :hypatia, :merge_orchestration, enabled: true, interval_ms: 900_000`. (2) CRON -- .github/workflows/merge-orchestrate.yml: schedule (hourly 17 * * * *) + workflow_dispatch, SHA-pinned setup-beam/checkout/cache/upload, runs `mix hypatia.merge_orchestrate --store $MERGE_ORCH_STORE` (store from vars.MERGE_ORCH_STORE||data/verisim), uploads merge-decisions.jsonl artifact (if-no-files-found: ignore). Token-free: both compute+write the manifest; the farm actuator (separate, PAT) merges. Tests: 64 ExUnit (was 61: +3 Ticker -- do_cycle happy/records-stats, do_cycle guarded-failure [encoder raises -> rescued, runs stays 0], GenServer run_now->status), 0 failures, local elixir 1.14, mix-format-clean, scanner-clean. PR pending: hypatia (Ticker + app wiring + cron workflow). THE SYSTEM IS NOW FULLY SCHEDULABLE end-to-end. REMAINING = pure owner config/ops, NO code: flip `config :hypatia, :merge_orchestration, enabled: true` to run the in-brain tick, and/or set repo var MERGE_ORCH_STORE + let the cron run (point at a populated store + add push-to-actuator if desired); a5 standards adoption (owner-applied); P3 handshake + mass_squash in actuator; .git-private-farm Actions billing is dead -- fix before the actuator runs scheduled."
+
+[post-completion-followons]
+# Owner picked all four 2026-06-14 (P3/mass_squash, BoA design doc, a5 draft, add-BoA-to-scope).
+p3-mass-squash = "LANDED .git-private-farm PR #79: decide_action gains P3 (FARM_P3_AUTHORIZED=1 owner handshake) + mass_squash (campaign_ref + $FARM_CAMPAIGN_DIR/<ref>.active marker) gates, both owner-held signals the brain can't self-grant. 10/10 bash conformance (was 5), shellcheck-clean, resolves TODO(increment-3b). Farm CI dead-infra (404), admin-merge like #78."
+boa-substrate = "DESIGN DOC landed hypatia: docs/design/merge-orchestration/05-execution-substrate-bag-of-actions.adoc. Maps merge-orchestration -> Bag-of-Actions continuation runtime (lease=capability-route, no-double-merge=Ephapax linear type, attestation=trust-proof-chain, actuate=Freeze/Thaw handoff, self-hosted-BEAM=off-Actions-minutes). Integration (BatonEmitter on hypatia side + merge_pr action/capability facts on BoA side) GATED on bag-of-actions being added to session scope -- it is NOT in scope this session (add_repo tool unavailable; only metadata+README readable via global search/WebFetch)."
+a5-standards = "DRAFT-ONLY pending owner approval (standards is ask-before-touching/license-sensitive): fill INTENT.contractile with the a5 lease/claim schema as an intent-kind + extend TRUST.contractile with the meta trust-deny (an agent cannot self-authorise a meta claim). To be drafted + shown, NOT pushed, until owner signs off the exact text."
+manifest-shape-seam = "FLAGGED (not yet fixed): Dispatcher.to_manifest_entry writes merge-decisions.jsonl as top-level tier/strategy + nested .merge.{...}; actuate.sh decide_action reads the FLAT artifact-1 shape (top-level .safety/.pool/.pr). Both tested + green on their own shapes; reconciling them (or a thin adapter) is an open seam."
diff --git a/docs/design/merge-orchestration/05-execution-substrate-bag-of-actions.adoc b/docs/design/merge-orchestration/05-execution-substrate-bag-of-actions.adoc
new file mode 100644
index 00000000..d5d3d361
--- /dev/null
+++ b/docs/design/merge-orchestration/05-execution-substrate-bag-of-actions.adoc
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
+= Execution substrate: Bag-of-Actions (continuation runtime)
+:toc: macro
+:icons: font
+
+[NOTE]
+====
+*Status: DESIGN ONLY.* `hyperpolymath/bag-of-actions` is not yet in this
+work's session scope, so its exact Baton format / `Bag.Mesh` API could not be
+read first-hand. This doc records the mapping and the integration sketch so the
+build is ready the moment the repo is in scope. Nothing here is wired.
+====
+
+toc::[]
+
+== Why this doc
+
+The cron + tick (artifact-7) make the merge-orchestration loop *runnable on a
+schedule* — a GenServer tick in the brain, or an hourly GitHub Actions cron.
+Both write the decision manifest (`merge-decisions.jsonl`) that the
+`.git-private-farm` actuator polls and executes.
+
+That JSONL+poll seam is the weakest link: it is a *passive* hand-off (the farm
+must poll), the "no double-merge" guarantee is a `MapSet` + a file lock rather
+than a type, and it runs on GitHub-hosted Actions minutes (the farm's billing
+is currently the blocker on scheduled actuation).
+
+*Bag-of-Actions* (BoA) is the owner-flagged alternative: a "distributed OS for
+mobile continuations". It is a near-exact structural fit for what the
+merge-orchestration runtime already is — capability-gated, non-duplicated,
+attested merge actions — and it dissolves all three weaknesses above.
+
+== What Bag-of-Actions is
+
+* *Batons* — units of work are serialized execution states that *migrate to the
+  node matching their required capabilities*, rather than being pushed onto a
+  fixed queue / DAG.
+* *Capability routing* — dynamic dispatch by hardware, identity, and trust.
+* *Ephapax linear types* — one-time consumption of resources (tokens, memory,
+  credits); work is never lost or duplicated.
+* *Trust attestation* — a cryptographic proof-chain for every state transition.
+* *Freeze / Thaw* — pause execution on one node (Wasm host), resume on another.
+* *Elixir/OTP orchestration* — `Bag.Mesh.submit(...)`; Zig/Wasm hosts execute;
+  they communicate via serialized Batons. Capabilities are declared in
+  `nodes.scm` and proved in `verification/` (`Bag.Estate`).
+
+== The mapping (one-to-one)
+
+[cols="1,1,2",options="header"]
+|===
+| merge-orchestration | Bag-of-Actions | what it buys
+
+| `KinGate` per-repo *lease*
+| *capability routing* — the Baton routes to the node holding the repo capability
+| the lease stops being a flat-file lock and *becomes the routing key*
+
+| in-cycle dedup + lease (no double-merge)
+| *Ephapax linear types* — one-time consumption
+| "no double-merge" upgraded from a `MapSet`/`O_EXCL` lock to a *type-level* guarantee
+
+| a3 *attestation* chain (intent → evidence → seal)
+| *trust attestation* — proof-chain per state transition
+| the council's signed verdict rides in the Baton; each transition extends the chain
+
+| sense → deliberate → *actuate* hand-off (brain holds no token; farm does)
+| *Freeze / Thaw* migration
+| the decided Baton *freezes* on the brain and *thaws* on the token-bearing farm node — replacing "write JSONL, farm polls" with a real continuation hand-off
+
+| the cron/tick + the dead farm Actions billing
+| self-hosted *BEAM mesh + Guix builds*
+| runs the loop *off* GitHub-hosted minutes — which is why it bears on the billing question
+|===
+
+== Integration sketch
+
+=== Brain side (hypatia, additive)
+
+A new emit backend alongside `Loop.write_manifest/3` — call it
+`MergeOrchestration.BatonEmitter`:
+
+[source,elixir]
+----
+# for each ARMED decision (one that held its KinGate lease), submit a Baton
+# whose REQUIRED CAPABILITY is {repo, :github_merge_token}. The brain has no
+# such capability, so the Baton cannot execute here — it must migrate.
+Bag.Mesh.submit(%{
+  action: :merge_pr,
+  repo: decision.pr.repo,
+  pr: decision.pr.number,
+  method: decision.method,            # squash | rebase | merge_commit
+  requires: [{:repo, decision.pr.repo}, :github_merge_token],
+  proof: signed_attestation(decision) # the council verdict -> trust chain
+})
+----
+
+`write_manifest` stays as the durable record / fallback; BatonEmitter is the
+*live* path. Both are driven from the same `Loop.plan` result, so the gate
+(in-cycle + lease + LE2) is unchanged — only the *delivery* differs.
+
+=== The hand-off
+
+. Brain runs `Loop.plan` → gated decisions. For each armed one it submits a
+  Baton (above). The Baton *freezes* — the brain holds no `:github_merge_token`.
+. Capability routing migrates the Baton to the node that *does* hold
+  `{repo, :github_merge_token}` — the `.git-private-farm` host.
+. The Baton *thaws* there and runs the merge action, which independently
+  re-verifies (the actuator's existing `decide_action`: veto / meta / not-armed
+  / ci-green / P3-handshake / mass_squash-campaign) before `gh pr merge`.
+. The merge consumes the linear *merge token* exactly once → no double-merge by
+  construction. The outcome + each transition append to the trust proof-chain.
+
+=== BoA side (gated on scope)
+
+* A `merge_pr` action definition (Ephapax/Wasm) that takes the linear merge
+  token and the decision payload.
+* Capability declarations in `nodes.scm`: per-repo capabilities + the
+  `:github_merge_token` capability bound to the farm node (and *only* there —
+  this is the token-free-brain invariant expressed as a capability fact, proved
+  in `Bag.Estate`).
+
+== What it buys over JSONL + poll
+
+* *Type-level no-double-merge* (Ephapax) instead of a lock + a `MapSet`.
+* *Capability-routed actuation* — no hard-coded "the farm polls a file"; the
+  Baton finds the token-holder.
+* *Resumable* — a crashed actuation thaws and retries on another capable node.
+* *Self-hosted* — runs on the BEAM mesh, off GitHub Actions minutes; this is the
+  path that makes the farm billing fix *moot* rather than a workaround. (Owner
+  has deferred the billing fix itself; this is the longer arc it sits on.)
+
+== Honest caveats
+
+* BoA is research-grade (Milestones 1–4, no release artifacts). This is a
+  *direction*, not a drop-in.
+* The integration is *additive*: the `merge-decisions.jsonl` manifest remains
+  the record and the fallback; the actuator's independent re-verification stays
+  exactly as-is (defense in depth holds whether delivery is poll or Baton).
+* Building it needs: (a) BoA added to session scope to read the real Baton
+  format + `Bag.Mesh` surface; (b) `BatonEmitter` on the hypatia side; (c) the
+  `merge_pr` action + capability facts on the BoA side.
+
+== Cross-references
+
+* `00-decision-contract.adoc` — the decision tuple the Baton carries.
+* `02-attestation.adoc` — the signed verdict that becomes the trust-chain link.
+* `04-pool-lease-spec.adoc` — the lease that becomes the routing capability.
+* `.machine_readable/merge-orchestration/LEDGER.a2ml` — workstream state.
