ci: hw-gate — two-seat autonomous review rung (Sol decides hardware, Fable decides staging merges) - #679
Merged
Conversation
Every PR gets one required check, hw-gate. Diffs touching the load, serve, or kernel surfaces build the PR on the self-hosted gfx1201 runner, load pinned registry fixtures through the user route (hipfire run), and post the decoded text verbatim. An independent reviewer model (omp, reviewer role) reads the diff plus that evidence and returns greenlight / needs-human / block inside a script-enforced floor: fixture failures, policy files, RATCHET-RAISE, coverage gaps, and low confidence can never be approved by the bot. Fork PRs execute on hardware only after a maintainer applies hw-run, which is removed after every run. RATCHET-RAISE declarations now also require the ratchet-raise label in CI. CODEOWNERS covers the gated surfaces. Hardware: load bucket on master 344e17a (qwen3.6:27b, ornith-1.5:35b-a3b, lfm2.5:1.2b, qwen3.8:27b-mq4-xt) passes on gfx1201 in 42 s with reasoning off; this same fixture set refuses in 2 s on the reverted device-mesh tree.
…e, fail-closed confidence hipfire run writes the answer to stdout and daemon progress to stderr, so the decoded text is stdout untouched (the previous line filter ate indented code bodies). Evidence fences are longer than any backtick run in the answer so code answers render. The reviewer prompt goes to omp as @file (a 400 KiB diff exceeds argv). A verdict without numeric confidence can no longer greenlight. Hardware: load bucket on gfx1201 4/4 pass with full code bodies; review.py driven through real omp (cheap model) posts prelim/evidence/verdict via the fake gh and lands on needs-human for this policy diff.
Master has one merger (304/304) and one approver (64/65), so a review requirement is not a control, it is a lockout; branch protection drops it. The hw-gate status is green only on a floor-approved greenlight; needs-human and block are red, and a repo admin who has read the evidence may merge a red needs-human. No bot approval identity is needed.
…use the recorded verdict needs-human is red until a maintainer who read the evidence applies human-reviewed, a logged signature cleared on every push. Label events run in their own concurrency group (review.py applies labels mid-run and must not cancel the run producing the verdict) and re-evaluate the verdict already recorded for the head SHA, polling for the artifact. unlabeled no longer triggers. With this the hw-gate status can bind admins without turning every policy PR into a protection toggle.
Surface checklist mirrors select.py's buckets (the old crate list was missing half the arch crates), the test plan drops the pre-saddle --features deltanet incantation (deltanet is a default feature), and a 'How this merges' section spells out hw-run, the evidence comment, the verdict, human-reviewed, block, and the ratchet-raise label.
… run A single hipfire run is one request against a fresh daemon and proves nothing about turn-to-turn state, prefix cache, reset, or terminals. Every fixture now runs serve_harness battery (load) and battery+chain (serve) with reasoning off (--thinking off --thinking-effort none --max-think-tokens 0; the registry's reasoning_effort=xhigh otherwise wins and turns answer nothing). Rows fail on attractor, empty, or recall miss; runaway (finish=length) is shown but not fatal on its own. Every turn's assistant_content is posted verbatim. Hardware, gfx1201 device 3, master: 4 fixtures x (battery + chain) = 40 turns, 4/4 pass, 140 s wall. PR template asks for a local serve_harness --out JSON, not a hipfire run transcript.
The bare ornith-1.5:35b-a3b tag resolves to the older plain MQ4 artifact (hipfire bench AR 140 tok/s on gfx1201); the MQ4V2 router-fix artifact from PR #664 is the -mq4r tag (206 tok/s, matching the 2026-07-13 redline record). Also state in VALIDATION.md that the harness's per-turn rates are context, not benchmark numbers (they run ~30% under hipfire bench). Hardware: mq4r battery+chain 10/10 turns pass on gfx1201 device 3.
review.py gains --phase prelim|verdict|all. The prelim job reads the diff with read-only tools before any hw-run authorization and posts what a maintainer needs to decide whether to run hardware; the verdict job consumes the prelim artifact after hw-run. A prelim failure is recorded, not fatal; an unparseable verdict posts needs-human and exits 1 per contract. Both phases proven through real omp with the fake gh.
Hardware runs without a human label when the author is a repository member/collaborator, or when the diff touches no exec-sensitive path (build scripts, manifests, toolchain, CI, scripts, shell/python) and the reviewer's prelim judged execution_risk == none. select.py reports exec_sensitive_paths; review.md asks the reviewer for an execution_risk judgment with concrete criteria. hw-run still authorizes anything and is cleared per run/push. The reason for holding is written to the job summary and the hw-gate status.
…erges, hipx lane Sol (reviewer seat) reads the diff and alone decides whether the PR runs on hardware and which routes run; a maintainer's hw-run label only forces a run. Sol delivers greenlight/needs-human/block on the evidence and never merges. Fable (deciding seat) reads everything including Sol's verdict and returns merge-staging/hold/block, may veto or override Sol with a stated reason, and during probation merges the head into beta under its own identity; master stays human-owned. Hard floor (failed fixture, attractor, policy files, unlabelled RATCHET-RAISE) is the workflow's own rule and no seat overrides it. Contributors may declare routes and a claim in a <!-- hw-gate-request --> JSON block; Sol treats the claim as a claim and reports whether it was proven. Hardware runs as a matrix (hiptrx gfx1201, hipx gfx1100); merge_evidence.py combines lanes and treats a missing lane as failure. Seats act as hipfire-sol[bot] / hipfire-fable[bot] via GitHub App tokens when configured.
Sol's first real verdict on this PR was 'block' with three high-severity
findings, all correct:
- policy-only diffs set needs_hw=false and skipped the seats entirely while
the required status passed; policy paths now make needs_hw true so the
seats review them and the hard floor holds (run.py handles zero routes).
- a failed or conflicting staging merge kept decision_final=merge-staging
and would have turned the status green with nothing merged; it is now a
hold with the reason recorded.
- the battery's substring oracle passed a wrong answer ('eight sheep'
contained a 9) and a refused tool call; prompts now demand canonical
answer forms (Answer: 43; an exact JSON object).
…N' prompt Ornith refused 'reply with exactly this JSON object' on gfx1201 (it reads as a manipulation test to an instruct model) while complying on gfx1100 at temperature 1.0. A natural task with an exact oracle (convert a sentence to a JSON object with named keys) is stable across both. Hardware: hiptrx gfx1201 and hipx gfx1100, 4 fixtures x (battery + chain) each, 80 turns, 8/8 pass; merge_evidence combines both lanes.
…he PR First real PR through the gate (#681) failed all four batteries on both hosts: run.py resolved benchmarks/prompts/hw-gate/serve-battery.json against the PR checkout, which predates the file. Prompts are gate policy and now resolve from the fixtures.json tree; a missing prompts file fails closed. Chain mode (no prompts file) passed 5/5 on the same run, confirming the diagnosis.
The deciding seat gets a real shell in a sandboxed checkout of the PR head: every hiptrx GPU reserved (host flock; lanes take it shared, Fable takes it exclusive), PR and base branch both built for A/B, every registry artifact read-only, a wall-clock budget, no credentials and no gh (the script posts). No fixed route vocabulary: Fable runs what proves the change and records it in an investigation table with evidence files that are uploaded with the decision. Fable is claude-fable-5-1 at xhigh. First real session, PR #682 (G2 admission), 11 min, 58 tool calls: wrote a daemon driver for the refusal-then-generate scenario no harness expresses, proved the PR's transactional claim byte-for-byte against master with a resident single-device and a resident 4-rank EP model, ran pp=1/pp=2 and EP tp=4 A/B, found a pre-existing EP-MoE generation failure on master, and one real new refusal (DS4 EP + vmm). Decision: hold on the ratchet hard floor, with everything else proven.
Collaborator
Author
|
@fivetide @nwoolmer — asking for a real look at this one before it lands; I'm not merging a new auto-reviewer on my own read after reverting the last one. To make it concrete, this exact gate (at
Both dry runs are described in the body with the evidence. What I'd most like eyes on: the trust boundary ( |
This was referenced Sep 3, 2026
Open
1 task
ghazni101
pushed a commit
to ghazni101/hipfire
that referenced
this pull request
Sep 4, 2026
… a seat First live run of the rung (workflow_dispatch on warpfront#686, run 33848923893) never reached Sol: review.py launched omp with cwd=checkout AND `--cwd checkout`, and the workflow passes `--checkout pr` relative to the job workspace, so omp resolved `pr/pr` and exited 1 before reading the diff. prelim.json came back null, hardware was skipped, and the status went red with "sol prelim unavailable". The dry runs on warpfront#679 did not catch it because the fake omp ignored `--cwd` and every test passed an absolute checkout. - review.py: abspath the checkout once after parse_args; all three seat launches and the git helpers inherit it. - tests/fake_omp.py: chdir to `--cwd` like the real binary, exit 1 with the same error shape when it does not exist. - tests/test_review.py: prelim with a relative --checkout from the parent directory must reach the seat and pass an absolute --cwd. Fails on the old script with the runner's exact error; passes with the fix. 103/103 in scripts/hw-gate/tests.
This was referenced Sep 4, 2026
Kaden-Schutt
added a commit
that referenced
this pull request
Sep 5, 2026
…ed by hw-gate Follow-up named in #679: hw-gate is the review rung, so the pre-hw-gate route goes. Deleted tools/change_gate (13 files), autoresearch/ar/review + its tests (22), .github/agentic-review (4), .agents/skills/agentic-pr-{discovery,static-review,review} (10), and tests/test_agentic_pr_review_skills.py. References retargeted at the hw-gate request block / docs/VALIDATION.md § hw-gate: PULL_REQUEST_TEMPLATE.md, CONTRIBUTING.md, docs/VALIDATION.md, scripts/leanup-thresholds.txt; scripts/no-gpu-ci.sh drops the three change_gate unittest modules (the redline modules it still runs all exist). .gitignore loses the dead providers.local.json entry. Left in place on purpose: docs/governance and docs/perf-checkpoints (immutable ledgers), dated plan/spec/result records under docs/, and the unrelated `autoresearch` kernel-loop package.
ghazni101
pushed a commit
to ghazni101/hipfire
that referenced
this pull request
Sep 5, 2026
Second live run on warpfront#686 (33849478193): Sol authorized hardware, both lanes started, both failed inside 40 s with "[Errno 2] No such file or directory: 'cargo'". No GPU was ever touched. Both runners are systemd services. `actions-runner/.path` was written at install (2026-07-11) and is the distro default PATH; the service environment has no ~/.cargo/bin, no /opt/rocm/core/bin, no ROCM_PATH. The warpfront#679 evidence runs were driven by hand from a login shell, which is why this never surfaced. Nothing on either host changed. hw-run and fable-decide now import the runner user's login PATH and the ROCm variables (ROCM_PATH, HIP_PATH, HSA_PATH, HIP_PLATFORM, LD_LIBRARY_PATH) via GITHUB_PATH / GITHUB_ENV before the build steps. Verified under a bare service environment on hiptrx: the step yields ROCM_PATH=/opt/rocm/core, HIP_PATH, LD_LIBRARY_PATH, and cargo/hipcc/omp resolve. hipx's login shell carries the same variables.
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.
Summary
hw-gate: hipfire's autonomous review rung — two model seats, one human owner, hardware evidence on every load/serve/kernel change. Replaces
tools.change_gate+ the unconfigured agentic-review pipeline as the CI route (deletion of those is a follow-up).Motivation: #666 (comment) — five PRs merged on static review broke every Qwen3.5-family load. The
loadfixture set here refuses in 2 s on that tree and passes on master.Requesting review from @fivetide and @nwoolmer. After reverting the last auto-reviewer I'm not merging a new one on my own read; please push on anything below.
The rung
gpt-5.6-sol, read-only tools in a PR checkout) alone decides whether the PR's code runs on the maintainers' hardware and which routes run: mandatory fixtures for the touched buckets ∪ the author's requested routes ∪ its own. A maintainer'shw-runlabel can only force a run.serve_harness.py(battery; +chain for serve; +Redline parity for kernel) with reasoning off, on both lanes; every decoded turn is posted verbatim.merge_evidence.pycombines lanes and treats a missing lane as failure.greenlight/needs-human/block, regressions cited byfile:lineand fixture, and whether the author's claim was proven. Sol never merges or approves.claude-fable-5-1, xhigh) reads everything incl. Sol's verdict, then investigates on the hardware: a real shell in a sandboxed checkout of the PR head, all five hiptrx GPUs reserved (host flock), PR + base built for A/B, every registry artifact read-only, no credentials, nogh, 45-min budget. No fixed route list — it runs what proves the change and returns an investigation table with evidence files.merge-staging/hold/block, may veto or override Sol with a stated reason. Probation:merge-stagingmerges the head intobetaunder Fable's identity;masteris promoted by a human.RATCHET-RAISE, failed staging merge → hold/block. Soft floor (coverage gaps, confidence, Sol's needs-human) is Fable's call.<!-- hw-gate-request -->JSON block in the PR body names routes and a claim (template ships the skeleton).hipfire-sol[bot]andhipfire-fable[bot]— org-owned GitHub Apps (ids 4803129 / 4803130), installed, secrets set, tokens verified to mint with Contents/PRs/Issues/Checks: write.What lands
.github/workflows/hw-gate.yml,.github/CODEOWNERS,scripts/hw-gate/{select,run,review,merge_evidence}.py,scripts/hw-gate/{sol,fable}.md,scripts/hw-gate/fixtures.json(qwen3.6:27b,ornith-1.5:35b-a3b-mq4r,lfm2.5:1.2b,qwen3.8:27b-mq4-xt, sha256-pinned),benchmarks/prompts/hw-gate/,scripts/ratchet-diff.sh+ci.yml(RATCHET-RAISEnow also needs theratchet-raiselabel),docs/VALIDATION.md, PR template,CONTRIBUTING.md.Which surface(s) does this touch?
Evidence
python3 -m pytest scripts/hw-gate/tests -q→ 102 passed (fake omp/gh, no GPU).ornith-1.5:35b-a3b-mq4rbenches 205.7 tok/s AR (hipfire bench) vs 139.7 for the bare tag — hence the-mq4rpin.ghso nothing posted):lfm2.5:1.2broute.702efc0f4: policy-only diffs skipped the seats entirely while the status passed; a failed staging merge keptmerge-staging(green with nothing merged); the battery's substring oracle passed a wrong answer ("eight sheep" contained a9).select.jsonit was given was stale. Both true.merge_evidence.py: hiptrx gfx1201 (daemonf198ed22…) and hipx gfx1100 RX 7900 XTX (daemona620c1db…), 4 fixtures × (battery + chain) each = 80 turns, 8/8 fixtures pass, every fixture sha256-verified on both hosts. hipx ran from a clean detached git worktree at the PR head (no rsync). The first battery draft tripped on ornith refusing a bare "echo this JSON" prompt on one arch — replaced with a natural extraction task; that is the kind of oracle defect this evidence loop exists to surface.gh, nothing posted): Sol authorized both, both passed 4 fixtures on both archs; Solneeds-humanon each (PP/TP and the refusal path not exercised). Fable:merge-stagingon feat(runtime): add DeviceMesh topology (fixed #673 G1) #681 overriding Sol (Gpus.meshhas zero readers; single-device load is the only regressable surface and passed) — and on feat(loader): source-aware admission — classify and admit before teardown (fixed #674 G2) #682, Fable investigated for 11 min / 58 tool calls: wrote a daemon driver for load-A → refused-load-B → generate-from-A, proved the PR's claim byte-for-byte against master with a resident single-device and a resident 4-rank EP model, ran pp=1/pp=2 and EP tp=4 A/B, found a pre-existing EP-MoE generation failure on master, and one real new refusal (DS4 EP + vmm) →holdon the ratchet hard floor with everything else proven. Recovered transcript: the first real PR through the gate also found arun.pyprompt-path bug (c66abf3d8).kernelPR is its proof.After merge (maintainer)
hw-gateto the required status checks.tools/change_gate,autoresearch/ar/review,.github/agentic-review,.agents/skills/agentic-pr-*.Architecture-trait change?
No.