Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ python examples/demo_h2_moat.py # measure cross-tenant skill transfer
python examples/demo_canary_rollback.py # bad merge fails canary → safe auto git-revert
python examples/demo_capability_jail.py # learn a tool's syscalls → deny everything it didn't earn
python examples/demo_polyglot_ci.py # Python/JS/Go + perf + security graders on one bus
python examples/demo_monorepo_ci.py # separate stage per package with a unified gate
python examples/demo_consolidation.py # failures → structured rules → a 2nd-order schema
python examples/demo_distributed_fleet.py # concurrent managers (fencing) + multi-repo DAG
python examples/run_h2.py # LIVE: build skills, measure cross-tenant transfer
Expand Down
1 change: 1 addition & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ Runnable, mostly offline — see the [`examples/`](https://github.com/amitpatole
| gate a repo on tests+lint+types | `verel-ci check --repo .` |
| self-heal failing tests | `python examples/demo_selfheal.py` |
| grade Python/JS/Go + perf + security on one bus | `python examples/demo_polyglot_ci.py` |
| gate a monorepo with separate stages per package | `python examples/demo_monorepo_ci.py` |
| consolidate failures → rules → schema → revise | `python examples/demo_consolidation.py` |
| sandbox a tool to only the syscalls it earned | `python examples/demo_capability_jail.py` |
| run concurrent managers + a multi-repo saga | `python examples/demo_distributed_fleet.py` |
Expand Down
71 changes: 71 additions & 0 deletions examples/demo_monorepo_ci.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Monorepo CI demo (§7.4) — separate stage per package, unified gate.

In a monorepo, different packages might use different languages or tools.
We can run a separate stage per package (e.g. one Python, one Node) and
then gate the entire build on the combined results.

Run: python examples/demo_monorepo_ci.py
"""

from __future__ import annotations

import json

from verel.ci import inner_loop_stage, premerge_stage, run_stage
from verel.verdict import Verdict

# Canned tool output keyed by the binary the stage invokes — the injected runner returns it.
OUT = {
"node": "TAP version 13\nok 1 - renders\nnot ok 2 - submit posts the form\n",
"bandit": json.dumps({"results": []}),
}

def canned(cmd, cwd=None):
head = cmd[0]
if head == "node":
return (1, OUT["node"], "")
if head == "bandit":
return (0, OUT["bandit"], "")
# ruff/eslint/etc. — clean
return (0, "", "")

def show(pkg, res):
print(f"\nPackage [{pkg}] ({res.name}): {res.verdict.value.upper()}")
for r in res.reports:
n = len(r.issues)
flag = " (errored)" if r.errored else ""
print(f" {r.grader.value:9} [{getattr(r, 'model', None) or '-'}] "
f"{n} issue(s){flag}" + (f" — {r.issues[0].message}" if n else ""))

def main() -> None:
# A monorepo with a Python backend and a JS frontend.
packages = [
{"name": "frontend", "path": "/repo/packages/frontend", "language": "js"},
{"name": "backend", "path": "/repo/packages/backend", "language": "python"},
]

print("── Monorepo CI: Running separate stages per package ──")
results = []
overall_verdict = Verdict.PASS

for pkg in packages:
# We can configure stages differently per package
if pkg["language"] == "python":
stage = premerge_stage(pkg["path"], language="python", security=True)
else:
stage = inner_loop_stage(pkg["path"], language=pkg["language"])

# run_stage accepts a runner that intercepts grader shell commands
res = run_stage(stage, runner=canned)
show(pkg["name"], res)
results.append(res)

# Any failing package fails the entire monorepo build
if res.verdict == Verdict.FAIL:
overall_verdict = Verdict.FAIL

print("\n── Unified Monorepo Gate ──")
print(f"Overall Monorepo Verdict: {overall_verdict.value.upper()}")

if __name__ == "__main__":
main()