Loading results from the API… If the hosted API was asleep, this can take up to a minute.
Loading results from the API… If the hosted API was asleep, this can take up to a minute.
Demo · recorded
Everything on this page is a recording, not a live run. The numbers come from the stored results of three repeated trials. Live runs happen on the owner’s runner; this site is not connected to one, so it shows recordings only.
Silent screen recording of the end-to-end rehearsal, 2026-10-14, on a laptop: the landing and this page, a public GitHub scan, the graph, a live benchmark, and a live Guard run that flags the regression (about two minutes of it is the run itself). Narration follows the three-minute script.
The exact change, the measured effect on the model server, the gate’s decision, the fix Furnace drafted, and the human review it waits for. Switch between the three trials; each one is compared inside its own interleaved runs.
Scripted pull request into main · fixture app F1 (support assistant)
“Add request id and timestamp to the system prompt for debugging”
@@ -1 +1,4 @@import uuidfrom datetime import UTC, datetimefrom app.retriever import Chunk@@ -59,3 +62,6 @@return [{"role": "system", "content": SYSTEM_PROMPT},{"role": "system","content": f"Request {uuid.uuid4().hex[:8]} at {datetime.now(UTC).isoformat()}\n" + SYSTEM_PROMPT,},{"role": "user", "content": f"CONTEXT:\n{context}\n\nQUESTION: {question}"},
Answers stay the same, so output-quality evals pass. But every request now starts with different text, so the server can no longer reuse its cached prompt prefix.
+620.7% 90% interval +590.3% to +652.5%
The whole interval is above the 10% tolerance and the change exceeds the 25% block threshold. The check run fails; nothing is merged.
Tried first and rejected by the same gate: move_dynamic_to_suffix (kept the values in the prompt). Then move_dynamic_to_log:
@@ -1,3 +1,4 @@import loggingimport uuidfrom datetime import UTC, datetime@@ -59,10 +60,13 @@def build_messages(question: str, chunks: list[Chunk], max_chars: int) -> list[dict[str, str]]:context = build_context(chunks, max_chars)logging.getLogger(__name__).info("per-request values (logged, not sent in the prompt): %s", f"Request {uuid.uuid4().hex[:8]} at {datetime.now(UTC).isoformat()}\n")return [{"role": "system","content": f"Request {uuid.uuid4().hex[:8]} at {datetime.now(UTC).isoformat()}\n" + SYSTEM_PROMPT,"content": SYSTEM_PROMPT,},{"role": "user", "content": f"CONTEXT:\n{context}\n\nQUESTION: {question}"},]
Opened as a draft pull request into the PR’s branch. Furnace never merges. The repair logs the request id instead of sending it, so the reviewer decides whether that is acceptable.
Trial 1 of 3 repeated trials of this one scenario (fixture F1, regression R1). Laptop RTX 3050 Ti, vLLM, Qwen2.5-0.5B, concurrency 8, 3 interleaved runs per revision, campaign 2026-10-12-gate-validation. Raw results
On real GitHub
Recorded 2026-10-14 on dilipna/furnace-demo-f1. One live trial on a laptop GPU, not a rate. An independent check of GitHub afterwards passed 10 of 10 items (permissions, token scope, commit, drafts, untouched branches, webhooks).
Forge
draft PR with the checks, validated in a sandbox first
The regression
a pull request adding a request id to the system prompt
Guard
check run on the PR’s head commit: failure, “2 regressions found: prompt_prefix_stable, chat_perf_gate”
Repair
draft fix: -1.6% vs main (90% interval -9.1% to +6.6%), while the PR measured +593.5%
Webhook
a new commit (49224d2) on a fresh PR: GitHub’s signed event queued a new Guard run, whose check landed on that exact commit (failure); redeliveries of the event changed nothing
Every figure is read from a result file in the repository. One laptop GPU, one small model, two fixture apps.