Skip to content

Feat/ci check baton - #31

Merged
hyperpolymath merged 6 commits into
mainfrom
feat/ci-check-baton
Jul 31, 2026
Merged

Feat/ci check baton#31
hyperpolymath merged 6 commits into
mainfrom
feat/ci-check-baton

Conversation

@hyperpolymath

@hyperpolymath hyperpolymath commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 💥 Breaking change (would change existing behaviour)
  • 🕳️ Soundness fix (fixes a checker/proof false-negative)
  • 📖 Documentation
  • 🧹 Refactor / tech debt (behaviour-preserving)
  • ⚡ Performance
  • 🔧 Build / CI / tooling

How has this been verified?

Checklist

  • My commits are signed (git commit -S).
  • I ran the project's own checks/tests locally and they pass.
  • New files carry the correct SPDX-License-Identifier (code/config MPL-2.0,
    prose CC-BY-SA-4.0); I did not relicense existing files.
  • Docs are updated, and no public claim now overstates what the code does.
  • I have not introduced a soundness hole (or I have flagged where I might have).

Notes for reviewers


Summary by Gitar

  • New CI-Check Baton system:
    • Added CheckBaton in src/main.zig with HMAC-SHA256 attestation, freeze, and thaw support
    • Added Elixir counterparts Bag.CiBaton, Bag.CiSweep, and mesh integration in bag/lib/bag/mesh.ex
  • Toolchain capabilities & estate manifest:
    • Added toolchain capabilities (.zig, .rust, .cargo, .deno) and tropical costs across Zig and Idris proofs
  • Tooling & docs:
    • Added scripts/ci-baton-demo.sh, ci-checks.exs, comprehensive AsciiDoc documentation, and a new Justfile

This will update automatically on new commits.

hyperpolymath and others added 5 commits June 11, 2026 22:16
…ss the mesh

The first CI-check Baton: an estate CI check that runs on owned compute (zero
GitHub Actions minutes), is routed only to a node with the required capability,
and whose verdict is frozen + HMAC-attested into a portable envelope that other
nodes verify without re-execution. This is the bag-of-actions answer to the
Actions billing wall — the check GitHub would refuse to start runs anyway.

Wired across all three layers (per the "always wire first" rule):

- Idris (source of truth): add Zig/Rust/Cargo/Deno toolchain capabilities to
  Protocol.capToTag and mirror them in the Estate manifest. Fix three
  pre-existing build breaks so `idris2 --build bag.ipkg` is green again
  (List.find -> import Data.List; with-clause shorthand; non-linear patterns in
  handoffPreservesLinearity) — mechanical, no change to what the proofs assert.
- Zig host: CheckBaton with freeze/thaw + HMAC-SHA256 attestation; check / thaw
  / nodes subcommands; fail-safe match (an unknown/unprovable capability never
  matches).
- Elixir: Bag.CiBaton, Executor.run_check/thaw_check/list_nodes, Bag.Mesh
  .submit_check (routes through the orchestrator to a capability-matched node,
  reading the node list from the single mirrored manifest), Bag.CiSweep batch
  emitter (the entry point hypatia/ci-health will call).

Tests: 6 Zig (incl. tamper-detection) + 11 Elixir (incl. mesh routing + sweep),
all green; Idris package builds. Demo: scripts/ci-baton-demo.sh. Design doc:
docs/ci-check-baton.adoc.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… local half)

Wire Bag.CiSweep into a real, invocable tool — `mix bag.sweep [manifest.exs]` —
so the emitter has an actual entry point (not just a library function). It runs
estate CI checks as Batons on owned compute (zero GitHub Actions minutes) and:
  - emits ci-health-compatible TSV on stdout: check_id<TAB>BATON-<VERDICT><TAB>node
  - prints a human summary on stderr
  - exits 0 if every check passed, 1 otherwise (a CI gate)

ci-checks.exs is the dogfood manifest: bag-of-actions runs its OWN checks
(zig fmt, zig build test) as Batons — proven green by ci_sweep_manifest_test.

This is the bag-of-actions end of the hypatia/ci-health bridge. The hypatia-side
caller (a script that invokes this tool and folds Baton verdicts into the estate
report) is a separate, push-gated change.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… clean)

Integration with the ci-health baton-bridge surfaced that Bag.Mesh.submit_check logged routing/verdict lines to stdout, polluting the machine-readable TSV that mix bag.sweep emits. Operational logs now go to stderr; stdout stays a clean contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…tion gate, residue

Adopt the on-point subset of the typed-orchestration design frame (the rest is
split out to hypatia + a budget-planner via dev-notes/prompts). Keeps
bag-of-actions focused on compute routing rather than becoming a generic
orchestrator.

- Node carries a tropical (min-plus) money `cost` grade, mirrored across the
  three layers: Estate.idr (source of truth) + estate.zig + the `nodes` output
  (`name<TAB>cost`), read by Executor.node_costs/0. Idris adds `cheapestCapable`
  as the formal routing objective the Elixir planner mirrors.
- Bag.Budget: typed, NON-FUNGIBLE budgets (money/mutation/human_review/repair) —
  exhausting one dimension is a context change that removes a route.
- Bag.Planner: among capable nodes the budget can afford, pick the cheapest
  (tropical min); reserve the paid route for work whose capability only it has;
  gate mutating/irreversible work on a verifier.
- Bag.ActionResult: structured residue (echo) — a relegated pass still owes the
  GitHub required-status-check; a dirty partial yields a repair obligation.
- Bag.Mesh.submit_planned wires Planner -> execute -> residue end to end.

Tests: Idris builds; 6 Zig; 19 Elixir + 1 doctest — all green. Covers
"paid budget too low -> work needing it suspends, cheap route remains",
"cheapest-capable relegation", "mutation rejected without verifier",
"non-fungible dimensions", and "dirty partial -> repair obligation".

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Comment thread src/main.zig Outdated
Comment thread bag/test/mesh_check_test.exs
Comment thread bag/lib/bag/mesh.ex
@gitar-bot

gitar-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ✅ Approved 3 resolved / 3 findings

Introduces a capability-routed CI-check Baton system with HMAC attestation and mesh integration, but attestation silently falls back to a hardcoded public key when the environment variable is unset.

✅ 3 resolved
Security: Attestation silently falls back to a hardcoded public key

📄 src/main.zig:176-181
attestKey returns the source-embedded string "bag-of-actions-dev-key" whenever BAG_ATTEST_KEY is unset (and it catches all errors from getEnvVarOwned, not just NotFound). Because the docs advertise frozen verdicts as "attested" and "tamper-evident", any deployment that forgets to set the env var gets zero real protection: anyone can recompute the HMAC with the public default and forge a pass verdict that thaw/verify will accept. The fallback is silent — nothing warns the operator that attestation has degraded to a no-op. Consider failing (or emitting a loud stderr warning) when the key is absent, or gating the default behind an explicit dev flag.

Edge Case: Fail-path tests depend on src/estate.zig being permanently misformatted

📄 bag/test/mesh_check_test.exs:35-41 📄 bag/test/mesh_check_test.exs:59-70 📄 bag/test/ci_baton_test.exs:36-43
Several tests (mesh-fail, sweep-bad, and the Elixir/Zig demo) assert a :fail verdict by running zig fmt --check src/estate.zig, relying on that source file staying badly-formatted forever. If anyone runs zig fmt on the repo (or a formatter pre-commit hook fires), src/estate.zig becomes clean and these fail-expectation tests flip to green, silently breaking the negative test. Use a dedicated fixture file that is intentionally misformatted (and excluded from formatting) rather than a real source file.

Quality: Freeze path derived only from check_id risks concurrent overwrites

📄 bag/lib/bag/mesh.ex:102-103 📄 bag/lib/bag/mesh.ex:139
Both submit_check and submit_planned compute freeze_path as System.tmp_dir!()/#{check_id}.baton. Two runs of the same check_id (e.g. re-runs, or parallel sweeps sharing a check_id) write and thaw the same file, so one run can clobber another's frozen verdict, yielding a wrong thawed result. Consider incorporating the unique baton id (already cib-<rand>) into the path, as the Executor-level tests already do with a random suffix.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@hyperpolymath
hyperpolymath merged commit e7a39ab into main Jul 31, 2026
11 checks passed
@hyperpolymath
hyperpolymath deleted the feat/ci-check-baton branch July 31, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant