Skip to content

Gate pull requests on the run receipt

ubunye gate compares a run's receipt with a baseline and fails when it regresses. In CI the baseline is the task run on the pull request's base, the candidate the same task on its head.

ubunye gate -d pipelines -u shop -p orders -t clean                     # previous run vs latest
ubunye gate --baseline base.json --candidate head.json                  # two exported records
ubunye gate ... --max-slowdown 0.5 --max-row-change 0.1 --summary "$GITHUB_STEP_SUMMARY"

--baseline and --candidate take a run record file (ubunye lineage show --json writes one), a run id (or its first characters), previous or latest.

What fails the gate

Rule Fails when
run the candidate run did not succeed
expectation a fail expectation was broken
data an output's data changed and the task's VERSION did not
schema an output's columns or types changed and VERSION did not
output an output is missing from the candidate
time slower than the baseline by more than --max-slowdown (runs under 1 s are noise), or longer than --max-seconds
rows an output's row count moved by more than --max-row-change

A deliberate change bumps VERSION in the task's config.yaml, or passes --allow-data-change (then it warns). Every changed output also says what else changed between the runs (config, code, environment, which inputs); a change with none of them is reported as a transform that is not deterministic.

Warnings never fail the gate: a warn expectation broken, rows quarantined, a new output, hashes that cannot be compared (a record from before 0.7.0, or an unreadable output).

Exit code 1 when the gate fails; --json prints the findings; --summary FILE appends a Markdown table.

The GitHub Action

on: pull_request

jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ubunye-ai-ecosystems/ubunye_engine/.github/actions/gate@v0.7.0
        with:
          usecase-dir: pipelines
          usecase: shop
          package: orders
          task: clean
          gate-args: --max-slowdown 0.5

It installs the engine, runs the task on the pull request's base and on its head (pandas by default, no Java needed), gates head against base, and writes the table to the job summary. Inputs: backend, engine (a pip requirement), extra-packages, baseline-ref (default: the pull request's base), gate-args, python-version. Output: passed.

Tasks that call a model

For a task that calls a language model (ubunye.llm), the gate reports the calls: how many, how many replayed, the tokens, and their cost at list prices. Two more rules:

Flag Fails when
--require-replay Any of the candidate's model calls went live instead of replaying.
--max-llm-cost-increase 0.2 The model bill grew by more than 20%: the list price of the recorded tokens, or the tokens themselves when a model has no price.

The cost is measured from the recorded tokens, so it works on replayed runs, which spend nothing. The Action's llm-mode input (default replay) sets UBUNYE_LLM_MODE for both runs: a pull request is gated on the committed answers, with no key and no spend, and a changed prompt fails the run closed until its answers are recorded again.