Merge Orchestration · BatonEmitter (bag-of-actions mesh actuation backend) - #500
Merged
Merged
Conversation
…e bag-of-actions mesh
The alternative actuation backend to merge-decisions.jsonl, built against
bag-of-actions' REAL API (read by public clone; the repo is not in this
session's MCP scope so this is hypatia-side only).
* Bag.Mesh.submit_planned(spec, budget) takes %{check_id, command, required_cap,
mutating?, risk?}; the planner routes to the cheapest capable node and GATES
mutating:true work on a verifier -- that is the independent re-verification.
* A merge is just command=[gh, pr, merge, N, --repo, R, --<method>] carrying
required_cap="secret-access" -- the capability ONLY mesh-github-runner holds
in the estate nodes.scm (the token-bearer). The brain's node lacks it, so the
Baton cannot run on the brain and migrates to the runner: the token-free-brain
invariant expressed as capability routing. No bag-of-actions changes needed.
* Hypatia.MergeOrchestration.BatonEmitter.to_spec/2 (pure) -> the submit_planned
spec, carrying lease_id + route + rationale as the trust/residue link;
emit/2 submits one Baton per ARMED Loop entry. The Bag.Mesh call is late-bound
(apply/3) + injectable, so this stays compile-decoupled from bag-of-actions and
the logic tests run without it.
74 ExUnit (was 71): +3 BatonEmitter (spec shape + gh-flag/risk mapping + emit
filters to armed only). 0 failures, mix-format-clean, scanner-clean. LEDGER
v0.10.0. Companion to the design doc in #498.
🔍 Hypatia Security ScanFindings: 42 issues detected
View findings[
{
"reason": "Repository has 5 non-main remote branch(es). Policy: single main branch only.",
"type": "GS007",
"file": ".",
"action": "delete_remote_branches",
"rule_module": "git_state",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "src/ui/gossamer/README.adoc",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "scripts/ci-tools/Cargo.toml",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "scripts/bench-tools/Cargo.toml",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "ffi/zig/README.adoc",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "docs/reports/audit/audit-2026-04-15-post.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "docs/integration/github-registry.adoc",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "docs/integration/github-registry.adoc",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "docs/integration/a2ml-k9.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
},
{
"reason": "Code scanning (Hypatia): hypatia/structural_drift/SD022 -- Hypatia structural_drift: SD022 -- 11 day(s) old",
"type": "CSA001",
"file": "docs/architecture/system-integration.md",
"action": "review",
"rule_module": "code_scanning_alerts",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
added a commit
that referenced
this pull request
Jun 14, 2026
…:baton|:both) (#501) Finishes the live bag-of-actions wiring for the merge-orchestration runtime (the BoA follow-on tracked in `.machine_readable/merge-orchestration/LEDGER.a2ml`). ## What - `Loop.run/1` gains `:actuation` — `:manifest` (default) | `:baton` | `:both`. `:baton` submits one Baton per **armed** entry via `BatonEmitter.emit/2`, late-bound to `Bag.Mesh.submit_planned(spec, budget)` so a `:manifest`-only run never references `Bag.*` (compile-decoupled; no new dep). - Threaded through `Scheduler.cycle` and `mix hypatia.merge_orchestrate --actuation`. - **Two interop fixes in the as-merged `BatonEmitter` (#500)**, verified against bag-of-actions' real source — each would otherwise make every merge Baton fail: - `required_cap "secret-access"` → `"secret_access"` (the Zig bridge / `Bag.Planner` tag; a hyphenated tag is unprovable → routes to no node). - `to_spec` now carries a `:verifier`, so a mutating merge passes the planner's mutation gate instead of `{:rejected, :mutation_requires_verifier}`. ## Token-free brain The brain only emits/reads. `required_cap "secret_access"` is held **only** by the `mesh-github-runner` node in bag-of-actions' estate, so every merge Baton migrates off the brain to the runner — the token-free-brain invariant as capability routing. ## Tests - `+4` `Loop`/`Scheduler` `:actuation` tests; `baton_emitter_test` updated. - `mix test test/merge_orchestration/` → **81 tests, 0 failures**. - Full suite: the ~14 failures are pre-existing and unrelated (workflow-audit count drift, missing `:proof_model_retrain_count` metric, watcher `:already_started` isolation) — identical with this branch reverted. Cross-repo counterpart: hyperpolymath/bag-of-actions `claude/peaceful-pascal-IRlgq` (brain node + end-to-end test). `BatonEmitter.to_spec` output matches that PR's routed spec byte-for-byte. Core-tier → **draft for owner review** (not auto-armed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hyperpolymath
added a commit
that referenced
this pull request
Sep 15, 2026
…gex (#790) ## What this does Phase 1c of the hypatia campaign, and the first arm of the owner's **"2 then 1"** sequencing ruling — finish arming the scanner at source before spending fleet PRs. Three changes, all zero-PR: hypatia is resolved by `git ls-remote … HEAD` at runtime by `hypatia-scan-reusable.yml`, so these reach every consumer on its next scan. **Severities were chosen by measurement, not by taste.** ### 1. The secret-history coverage rule A deduplicated local census (579 raw roots → **573 distinct repos** after deduping on `git rev-parse --git-common-dir`) found **409 hypatia consumers, 398 secret-scanner callers, and 11 repos with no secret-history gate at all**. Rather than reimplement history scanning in Elixir alongside the existing pinned, checksum-verified gitleaks rail, this asserts the gate exists **and can actually see history**: | Rule | Severity | Condition | |---|---|---| | `missing_secret_history_scan` | `:medium` | No rail call, no direct scanner. Fires on the 11. | | `secret_scan_without_history` | `:low` | A scanner exists but is `--no-git` only (`:add_history_pass`) or fed by a checkout without `fetch-depth: 0` (`:set_fetch_depth_zero`). | `:medium` keeps it visible at the default threshold while staying **below the reusable's blocking high/critical refusal** — the 11 repos get a visible finding, and none gets a permanently-red required check with no PR to review. It deliberately does **not** assert `secrets: inherit`. The rail's own header still requires it, but lines 44-49 record that #500 replaced the gitleaks action with a pinned binary and that the guidance *"became wrong when the binary replaced it — but it was left in place."* Probing for it would false-positive fleet-wide. ### 2. GS008 — shallow-clone instrument health A history rule that sees no history is a **fake gate by construction**, so the coverage rule needs a companion reporting on the scan *environment*. GS008 fires when `.git/shallow` exists, marking every history-dependent finding in that report as vacuous rather than as a pass. >⚠️ **The anchor is load-bearing and counter-intuitive.** `file:` must **not** be > `.git/shallow`, however naturally that reads. `.git/` is in `@universal_excludes` > (`scanner_suppression.ex`), so a finding anchored there is **silently deleted by > the path filter between the rule module and the CLI output**. Measured: with that > anchor the rule was a **complete no-op** on a real `git clone --depth 1` while > passing a full-clone test perfectly. The exclusion exists to avoid scanning files > *inside* `.git/`; it also eats findings *about* it. Anchored at `.` (matching > GS005/GS007) and pinned by a dedicated regression test. `:medium` not `:high` because the condition is controlled by **one shared line in `standards`** — at `:high`, a single regression there would redden every consumer simultaneously. It is a filesystem probe rather than `git rev-parse --is-shallow-repository` because an unguarded `System.cmd` raises `ErlangError :enoent` — the exact defect that got `secret_scanner_verification.ex` deleted. ### 3. Restore the discarded `flawed_regex` findings Pre-existing defect, found while reading. `flawed_regexes` was computed at the top of `audit/3` and summed into `flawed_regex_count:`, but **was never added to the `findings:` chain** — so the rule ran on every scan in the estate, reported a count nothing consumes, and discarded every finding it produced. `cli.ex`'s own normalizer comment names `flawed_regex` as a source it handles, so the omission was accidental, not a deliberate mute. Gate-safe: the rule emits `:low`. ## Verification - **Four-arm planted-positive control on GS008** — repo as-is (silent) · real `git clone --depth 1` (**fires**) · `git fetch --unshallow` (silent again) · and **arm 4 against the escript being replaced**, where none of the new findings appear. - **All five new-or-restored emitters were EXECUTED**, not merely compiled. `test/` is path-suppressed under `@training_corpus_paths`, so a fixture placed there would have faked every arm; planted fixtures live outside it and ran at `--severity info` (`info` is rank 5 — `--severity low` would have hidden them). - **Real-estate validation**: 10 of the 11 predicted gap repos emit exactly one `missing_secret_history_scan`. The 11th (`me-dialect`) was **explained, not waved away** — a concurrent writer added a pinned trufflehog after the census ran, and direct invocation confirms the rule correctly returns `secret_scan_without_history` / `:set_fetch_depth_zero` instead. 8 of 8 rail-calling control repos stay silent. - **No new fleet noise**: the whole `secret_scan_without_history` class is invisible at the default severity threshold. - `mix test` — **1585 tests, 2 failures**: the same two pre-existing `ResearchExtensionsTest` `re001_missing_harden_runner` failures present on `main` without these changes. **Zero regressions.** - Escript soundness gate **14/14, rc=0**. `mix format --check-formatted` clean. Compiles clean under `--warnings-as-errors`. ## Known limit, stated rather than worked around No soundness-manifest entries were added for the new rules. An *absence* finding names no file, and the manifest matcher requires a real fixture file — contorting the anchor to satisfy the gate would be worse than documenting the limit. `missing_secret_history_scan` does execute inside the soundness scan and is message-checked by the placeholder assertion. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_011YTttTxnPc1V2sATvzBDN7 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BatonEmitter — actuate armed merges on the bag-of-actions mesh
Item #2 of what you picked: the real bag-of-actions integration. Since the repo still isn't in this session's MCP scope (your grant applies to a new session; the
add_repotool isn't available here), I read its real API by publicgit cloneand built the integration hypatia-side — which is where it belongs anyway.What the real API turned out to be
Bag.Mesh.submit_planned(spec, budget)takes%{check_id, command, required_cap, mutating?, risk?}; the planner routes to the cheapest capable node and gatesmutating: truework on a verifier — which is the independent re-verification. So a merge needs no custom Ephapax action: it's just acommand(gh pr merge …) carryingrequired_cap: "secret-access"— the capability onlymesh-github-runnerholds in the estatenodes.scm(the token-bearer). The brain's node lacks it, so the Baton can't run on the brain and migrates to the runner: the token-free-brain invariant expressed as capability routing. No bag-of-actions changes needed.Hypatia.MergeOrchestration.BatonEmitterto_spec/2(pure) → thesubmit_plannedspec, carryinglease_id+route+rationaleas the trust/residue link.emit/2→ submits one Baton per armedLoopentry (gate{:armed, lease}); deferred/flagged entries are skipped. TheBag.Meshcall is late-bound (apply/3) and injectable, so this stays compile-decoupled from bag-of-actions and the logic tests run without it.This is the alternative actuation backend to
merge-decisions.jsonlfrom the #498 design doc — now real, not just sketched.Testing (actual, not looks-right)
74 ExUnit, 0 failures (was 71), mix-format-clean, scanner-clean:
The +3 BatonEmitter tests: the spec shape (check_id,
gh pr mergecommand,required_cap: "secret-access",mutating: true, attestation), the method→gh-flag + aggressive-pool→:high-risk mapping, and thatemitsubmits only armed entries (via an injectedsubmit).Scope / what's left on BoA
lib/, your review.emitinto the liveLoopas the actuation backend (alongsidewrite_manifest) and register hypatia as a mesh node — no new emitter logic.LEDGER
v0.10.0records the real-API findings + this build.Generated by Claude Code