Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

void_furnace

File a GitHub issue. Walk away. Wake up to a merged pull request.

void_furnace is a dark factory — an autonomous code-generation pipeline that turns operator-filed GitHub issues into merged pull requests on a separate target repo, with no human typing code in the loop. One Claude subprocess plans the work, another writes the code, and a third — the critic — reviews the diff without ever seeing what the coder wrote about it. The factory runs on a systemd timer every 10 minutes. The operator files the issue and reviews the result.

First substrate UAT: PR #11 on void_furnace_calc merged 2026-05-15 21:15:21Z. $0.088 synthesized cost across 5 ticks. See postmortems/2026-05-15-phase-1.7-substrate-uat.md.

Why does this exist? A big prompt to one model would produce a working calculator faster and simpler. void_furnace is not for that goal — the calculator is a benchmark scaffold; the loop around it is the product. See documentation/wiki/why-void-furnace.md.

Two repos

void_furnace operates on a separate target repo — the factory never touches its own code in the same iteration that touches the target's code, and the target repo never sees the factory's machinery. The separation is structural: different repos, different CI, different deploy lifecycle, different review surface.

Repo Purpose Visibility
aberson/void_furnace (this repo) Factory machinery: orchestrator, workflows, prompts, scripts, tests Private
aberson/void_furnace_calc Target product: Python calculator (lib + CLI + Streamlit UI) Private

The target's three governance docs (MISSION.md, FACTORY_RULES.md, CLAUDE.md) are read-only from the factory side. The factory authenticates against the target via a fine-grained PAT scoped to that one repo. See documentation/wiki/target-separation.md (built out in Phase G) for the full no-cross-touch model.

The protected-files allowlist enforces the no-cross-touch boundary at PR level: any coder PR that modifies the governance docs, the target's CI workflow, ui-smoke.sh, or the target's pyproject.toml is auto-rejected before the critic ever sees it. The rule is operator-time-only: only the operator can change those files, by hand, with no factory involvement.

What makes this different

The critic's prompt is constructed from the issue body, the unified diff, the test output, and the target repo's three governance docs — and nothing else. The coder's narration, PR description, planner notes, and tool transcript are structurally excluded. It is not possible for coder rhetoric to influence critic verdict, because the prompt-template function signature cannot accept those inputs. An AST regression guard locks the signature shape forever.

  • Holdout principle — the critic never sees coder artifacts. Enforced by prompt-template construction with a function signature that literally cannot accept those inputs, plus tests/test_holdout.py::test_signature_excludes_coder_artifacts asserting the absence.
  • Three-doc constitutionMISSION.md, FACTORY_RULES.md, CLAUDE.md in the target repo govern what the factory may build, how it operates, and what gates it runs verbatim. Precedence is MISSION > FACTORY_RULES > CLAUDE.
  • Protected files allowlist — coder PRs are auto-rejected if they touch governance docs, CI config, ui-smoke.sh, or pyproject.toml deps in the target repo.
  • Recovery sweep — every tick reconciles 6 enumerated inconsistency cases (stranded in-progress issue, unverdicted PR, exhausted fix budget, stale lock, role-attempt-cap exhaustion, stale needs-fix).
  • Runs on Claude today; open models are a future update (TBD) — the live factory (harness/CURRENT = v1) runs its planner, coder, and critic on Claude Sonnet 4.6 via subscription OAuth (CLAUDE_CODE_OAUTH_TOKEN, not a paid API key). This is the current, proven configuration — the factory's merged-PR track record was produced on v1. A future harness version (v5 or later) will run the same loop on an open-weight/local-model pipeline (models served locally by llama.cpp — no paid API key and no subscription). That open runtime is built and has run end-to-end on the substrate with zero OAuth, but it is not yet the default: its coder quality is still being validated, so the live loop stays on Claude/v1 until the open pipeline is proven to ship clean, mergeable PRs.

The autonomous coding loop

Each tick fires every 10 minutes and either advances one issue's state or runs a recovery sweep. The 9 factory:* labels are the state machine; the holdout barrier is the load-bearing safety property between coder and critic.

                  ┌─────── one tick (every 10 minutes) ──────────┐
                  │                                              │
operator files ──►│ triaging ──► accepted ──► in-progress        │
issue             │      │                       │               │
                  │      │                       ▼               │
                  │      │            ┌─── coder writes diff     │
                  │      │            │                          │
                  │      │            ▼                          │
                  │      │    ┌─── holdout barrier ─────┐        │
                  │      │    │  (critic sees diff +    │        │
                  │      │    │   issue + tests +       │        │
                  │      │    │   governance only)      │        │
                  │      │    └─────────────────────────┘        │
                  │      │                       │               │
                  │      │                       ▼               │
                  │      │            critic verdict on PR       │
                  │      │                       │               │
                  │      ▼                       ▼               │
                  │ rejected                 approved ──► auto-merge
                  │ (terminal)               needs-fix ──► back to in-progress
                  │                          needs-human (escalation)
                  └──────────────────────────────────────────────┘

The fix loop is bounded: after 2 fix attempts on the same PR, the orchestrator escalates the issue to factory:needs-human so the operator can decide. Recovery sweep catches mid-tick crashes; the kill switch (factory:paused) lets the operator stop work on any issue without killing the timer.

Why a held-out critic

The point of the critic is to answer "is this change good?" — not "did the coder make a convincing case?". Separating those two questions means the critic's verdict is grounded in the diff, the test output, and the target's governance — not in coder narration. The wall:

 ┌─────────── critic subprocess receives ──────────┐ ┌─── critic NEVER sees ───┐
 │  issue body                                     │ │  PR description         │
 │  unified diff (gh pr diff)                      │ │  PR title               │
 │  pytest stdout/stderr                           │ │  commit message body    │
 │  ui-smoke stdout/stderr                         │ │  planner plan/notes     │
 │  gh pr checks                                   │ │  coder tool transcript  │
 │  CLAUDE.md gate list (verbatim)                 │ │  coder "summary of work"│
 │  FACTORY_RULES.md gate list (verbatim)          │ │                         │
 └─────────────────────────────────────────────────┘ └─────────────────────────┘
                       ▲                                            ▲
                       │              enforced by                   │
                       └──── prompt-template function signature ────┘
                              (compiler-checked + unit-tested)

src/void_furnace/holdout.py exposes the critic-prompt builder with a function signature that takes only the left-side inputs. Adding a right-side input would require changing the signature, which the AST guard test fails on. Future contributors cannot accidentally relax it.

The factory has two other AST-enforced invariants in the same shape: the 9 factory:* labels are defined exclusively in src/void_furnace/labels.py (so no inline label literal can drift); and the src/void_furnace_ui/services/ modules are forbidden from importing streamlit (so the UI render layer cannot reach down past the service layer). The pattern is "make the dangerous thing impossible, then assert that impossibility in CI." Documentation is not a control; signatures and grammars are.

Story so far

A bulleted timeline. Dates are accurate at landing; the README is intentionally coarser-grained than the postmortems and retrospectives it cites.

The first few months were substrate and pipeline scaffolding; from May 2026 the project has been alternating "ship features the factory needs" with "run the factory against a real target and learn what broke." Each substrate UAT and each retrospective surfaces real-world failure modes that feed back into the plans. The Phase 1.7 Operator UI exists because reading raw journalctl to understand a tick is too slow; the recovery-sweep extension from 4 to 6 cases exists because soak tests found two new inconsistency shapes; bug #50/#51 exist because Gen-0 produced eleven zombie PRs on a single stuck issue.

  • Q4 2025 — Initial plan landed. Substrate model chosen (WSL2 + systemd timer on Ubuntu 24.04 LTS under Windows), three-doc constitution model designed, holdout principle written into KDD-2.
  • 2026-05-09 — V1 manifest complete. All 12 code-only build steps shipped across Phases 0–2. First end-to-end tick fires; no real target work yet.
  • 2026-05-11 — Tier 1 substrate smoke surfaced four substrate-only bugs that 176 unit tests + 4-pass code review had missed. Lesson codified at .claude/rules/substrate-testing.md: unit tests alone are necessary but not sufficient.
  • 2026-05-13 — Phase 1.5 V1 hardening (4 of 6 steps): run-dir schema reconciliation, recovery sweep extended from 4 to 6 cases, invariant assertions + heartbeats, triage pre-flight.
  • 2026-05-13void_furnace_calc retro target edits applied: HI-1 arbitrary-precision Decimal, HI-3 single-surface Streamlit UI. Invalidated iter-1 merged PRs #5/#8/#9.
  • 2026-05-14 — Phase 1.6 critic + triage hardening: deterministic MISSION-named artifact pre-flight + edge-of-range acceptance-test rule.
  • 2026-05-15 — Phase 1.7 Operator UI shipped: Streamlit 5-page app at 127.0.0.1:8502, deployed as void_furnace_ui.service. PR #11 merged on void_furnace_calc at $0.088 across 5 ticks (first substrate UAT PASS).
  • 2026-05-15 — Phase 1.5 + 1.6 substrate UATs PASS (combined Tier 1).
  • 2026-05-20 — Bug fixes #50 (one PR per issue, retries push to existing branch) and #51 (orchestrator skips factory:needs-human). Closed 8 sites of label-flip-without-state-record. iter-1 zombie PRs cleaned up.
  • 2026-05-20 — Gen-1 (iteration_id=2) launched against the new Decimal HI-1 + single-surface HI-3 governance.
  • 2026-05-20 — V2 Phase A landed: per-iteration aggregation, hard-invariant scoring, 5-gate verdicts. Adds iteration_id propagation, iteration_aggregates SQLite table, post-PR analyzer, and a new Iterations page in the operator UI.
  • 2026-05-21 — Phase 1.8 Reliability Hardening plan landed (12 step issues). Goal: factory consistently produces mergeable PRs, not just calculator-correct PRs.
  • 2026-05-21 — Phase G GitHub presence buildout plan landed (this plan; 12 steps). README rewrite, 15-page wiki, MIT LICENSE, 5-badge cluster, 6 PNG screenshots, repo topics. Visibility flip remains explicitly out-of-scope.
  • 2026-05-26 — V2 plan landed (since folded into documentation/master_plan.md): 5 phases closing the issue→factory→retro→governance-PR→reseed loop. V2 Phase 1 shipped: vf-check-e2e skill orchestrates vf-check-launcher + vf-check-ui plus 6 cross-surface assertions (substrate ↔ launcher ↔ UI agreement). V2 Phase 2 (early) shipped: operator-launcher gains write actions via orthogonal ArgSpec type (text_input / text_area) + to_tempfile fields, plus a submit-issue-target catalog entry that files issues against the target repo with operator-typed bytes routed through tempfiles — full shell-escape immunity at the PowerShell -Command boundary.
  • 2026-05-26 — V2 Phase 2-late shipped: L2 launcher startup sweep (bounds the orphan-tempfile leak from bug #92 to one-per-launcher-session); L3 retro-start launcher entry with ArgSpec.validate_pattern regex enforcement at Python layer BEFORE PowerShell substitution (closed HIGH-sev subexpression-injection bypass found in review); L4 hid the UI Submit tab behind VOID_FURNACE_UI_SUBMIT_TAB env-var (default OFF) and removed the now-redundant Retro launch hint — operator-launcher's :8503 catalog became the canonical write surface.
  • 2026-05-27 — V2 Phase 2-late L5 shipped: the UI Submit tab is deleted entirely (function, services, env-var flag, related tests). pages/1_Operator.py now calls _render_retro_tab directly. Added an AST regression guard asserting _render_submit_tab and VOID_FURNACE_UI_SUBMIT_TAB cannot be re-introduced. 804 tests passing.
  • 2026-06-03 — Phase OSS Steps 1–7 shipped (public-readiness mechanics): Hetzner host references reframed to WSL2; committed runs/ test logs untracked; a cross-platform Python public-snapshot builder (scripts/build-public-snapshot.py) + verification gate (scripts/verify-public-snapshot.py) + snapshot-exclude.manifest that produce and audit curated, history-free public exports (stub prompts, omit the calc constitution, fail-loud on secret/cross-project/dangling-ref leaks); PROMPTS.md + canonical stub prompts; void_furnace_calc public-prep; and a provisional open-model harness/v2 (non-coder roles → open providers, coder → OAuth dev/test bridge, CURRENT stays v1). 1794 tests passing. The open agentic coder (Steps 8+) and the public visibility flip remained — the flip stays HELD until the open loop is proven on the substrate.
  • 2026-06-03 — Phase OSS Steps 8–10 shipped (open agentic coder, the flip-blocking track): a spike proved a hand-rolled tool-use loop on a local open-weight model (qwen2.5:7b-instruct via ollama) can make a real file edit and run a test — it even self-corrected a failing import — at zero API cost; that loop is productized as the OllamaAgenticAdapter (src/void_furnace/models/ollama_agentic.py, provider ollama_agentic) behind the ModelClient seam (path-confined writes, a process-tree-kill bash timeout, cost pinned to $0 for local runs); and an integration test drives the real coder.run → adapter.invoke path end-to-end, failing loudly if the wire is severed. The adapter is built but not yet the production coder (that lands with the all-open harness/v3 once benchmarked). 1839 tests passing. Steps 11 (per-role open-model benchmark) → 13 (substrate soak with OAuth unset) + the flip remain.
  • 2026-06-06 — Phase OMR Step 2 (#186) — the open-model runtime is up on the WSL2 substrate: built mainline llama.cpp from source with CUDA (no prebuilt Linux CUDA binary exists — ggml-org#16205), installed llama-swap v223 + both candidate GGUFs (Qwen3-Coder-30B-A3B-Instruct-UD-Q3_K_XL + Qwen3.6-35B-A3B-IQ3_S), generated the config, enabled the service, and verified coder-30b serves a structured tool_calls (get_weather({"city":"Paris"})) via --jinja with zero OAuth. Two Step-1 substrate-only bugs were found + fixed (013bf99, each with a regression test): config.py emitted a bare --flash-attn (current llama.cpp requires --flash-attn on); install-llama-runtime.sh mkdir'd the runtime dir as root before the factory-run config write (now chowns it). 8 of 18 OMR steps done; #186 closed. Production untouched (harness/CURRENT=v1/OAuth/paused; runtime INERT until harness checkout v4); the v4 flip stays HELD pending the Step 6 bake-off + soak.
  • 2026-06-05 — Phase OMR (Open-Model Runtime) — 7 of 18 code steps shipped via /build-phase (the dependency-buildable ones): a llama-server launch-config generator + llama-swap lifecycle manager (scripts/llama_server/); a credential-free llamacpp single-shot adapter + a llamacpp_agentic coder adapter against llama-server's OpenAI /v1/chat/completions (shared agentic tool layer extracted to models/_agentic_tools.py); the coder benchmark re-scored on the produced diff instead of the agentic transcript (fixes the no_diff-blind score); a Python Playwright screenshot wrapper (judges/ui_capture.py); a system-metrics service (services/system_metrics.py, CPU/GPU/mem via psutil + nvidia-smi) surfaced on the /Health page. 2102 tests passing. The remaining 11 steps are operator/substrate (install + RAM), the wait-type model bake-off (35B-VL vs 30B-coder, diff-scored), the bake-off-gated harness/v4 + VL UI-judge + governance judge + transparency banner, and the smoke/soak/UAT. Production untouched (harness/CURRENT=v1/OAuth/paused); the v4 flip stays HELD until the soak.
  • 2026-06-06 — Phase OMR Steps 6 + 7: diff-scored zero-OAuth bake-off on the substrate (#190) → coder-30b wins the coder role (mean 3.18 vs 1.67 for general-35b-as-coder), general-35b wins all non-coder roles (triage 9.0, validator 9.0, retro 8.5); harness/v4/ authored (#191) with all roles on llamacpp/llamacpp_agentic per bake-off winners; --timeout-s CLI flag added to benchmark. Code review gauntlet applied 5 fixes. 2108 tests passing.
  • 2026-06-06 — Phase OMR Steps 10 + 11 + 14 (judges + transparency banner): Step 10 (#194) — Playwright VL UI-judge (judges/ui_visual_judge.py, holdout-safe gather/classify/sweep/cli, ui_verdicts table, void_furnace ui-judge run CLI); Step 11 (#195) — governance-compliance judge (judges/governance_compliance.py, governance_verdicts table, FACTORY_RULES/MISSION check, void_furnace governance-judge run CLI); Step 14 (#198) — transparency banner: deployed_version.py now resolves active per-role model (active_models dict, "provider:model_id" format) and live llama.cpp runtime params (RuntimeInfo frozen dataclass from scripts/llama_server/status.read_status), _banner.py renders 3-line caption (sha/harness · models · runtime), degrades gracefully when llama-server unreachable. 13 of 18 OMR steps DONE. 2173 tests passing. Production untouched (harness/CURRENT=v1/OAuth/paused); v4 flip HELD until Step-16 soak.
  • 2026-06-07 — Phase OMR Step 15 smoke gate (#199) ATTEMPTED: v4 routing CONFIRMED (all harness.model_resolved lines show harness_version=v4, zero OAuth, usd=0); two blockers: (B1) triage cold-swap timeout — general-35b takes ~194s to load after coder-30b; adapter timeout hardcoded to 180s; (B2) coder no_diff (pre-existing T1/T2, ~27% rate). Added 6 Step 15 launcher buttons; app.py transparency block moved below the flow diagram. 2175 tests passing.
  • 2026-06-07 — Phase OMR B1 cold-swap timeout fix (7ea4ad9): triage.py + retro.py now read params.timeout_s from harness assignment; harness/v4/models.toml sets timeout_s=360 for both roles (2× safety margin over the observed 194s cold-swap). v1/OAuth path unchanged. 2177 tests passing. Next: deploy → reset target → re-run Step 15 smoke gate.
  • 2026-06-07 — Iteration report feature shipped (issues #208–#210): void_furnace iteration report [--id N] [--list] generates a Markdown summary of a factory run — per-issue label timeline, triage-verdict snippets (120-char truncation), PR diff stats, retry count, cost, GitHub links — and writes iteration-report-<id>.md to cwd. Two launcher buttons ("Iteration report (latest)", "Iteration report (select)") added to the operator-launcher catalog. 2186 tests passing.
  • 2026-06-17 — Phase SWF (Sonnet-window fixes) shipped (issues #218–#224): six surgical fixes from an Opus re-review of the commits authored while the main-loop model was silently downgraded Opus→Sonnet (Jun 4–17). The load-bearing ones: the llamacpp agentic adapter's recovered-from-text tool calls now carry OpenAI's required type:"function" (would 400 the next turn on the flaky-parser recovery path); the harness checkout v0 all-Claude rollback is fixed (the M5 wiring resolved only the split validator slots and silently routed the adversarial critic to DeepSeek anyway — now a unified validator slot drives single-critic mode, and a malformed config fails safe to the R8 fallback instead of crashing the tick); plus the iteration-report's dead validator_verdicts query removed, the llamacpp credential-free regression test restored, the governance judge's protected-files list sourced from policy (one source of truth), and two OSS-soak launcher buttons hardened. None touch the live v1/OAuth path. 2198 tests passing.
  • 2026-07-04 — Phase RG (Readiness Gate) Step 1-prep shipped (#250): a research-tooling judge-bench harness at scripts/readiness_bench/ — a labeled-set builder over the Investigation-01 ground-truth key (4 canonical v5/coder-30b archetypes: ops→TRACTABLE, cli→ENRICHABLE, ui→INTRACTABLE, identity→BLOCKED, loaded from the real seed files + authored specs + escalation-history cases), three feasibility-judge runners behind a model_client_factory DI seam — (A) Claude ceiling, (B) local-LLM via the existing LlamaCppAdapter (parameterized model_id → coder-30b or a general-instruct model), (C) a zero-model deterministic classifier — and a scorer + CLI (python -m scripts.readiness_bench --judge deterministic|local|claude). Holdout-safe prompt (byte-identical regardless of ground_truth); the deterministic judge scores 4/4 on real seed text (independently verified). Imports void_furnace read-only; no src/ changes. The phase HALTS at Step 1 (#251) — the operator judge-path decision gate, which runs the bench on the substrate to pick the feasibility-judge path; Steps 2–7 (#252–#257) are parameterized on that decision. 2354 tests passing.
  • 2026-07-04 — Phase RG (Readiness Gate) Steps 2–5 shipped (#252–#255): a new intake gate between triage-ACCEPT and the coder that forecasts whether the open coder can build an issue and adapts it — without splitting issues. The judge is (C) deterministic (Step 1 substrate bench: 4/4 on the calc archetypes = the Claude ceiling, but fully local + zero-cost + zero-OAuth; coder-30b-as-judge was too slow and mode-collapsed). New src/void_furnace/readiness/ package (a ReadinessJudge seam + the deterministic classifier, one source of truth — the research bench imports it). A new orchestrator picker branch over factory:accepted issues escalates INTRACTABLE/BLOCKED to factory:needs-human and auto-enriches ENRICHABLE (appends the strict-mypy annotation clause + re-admits — crash-safe, attempt-capped, deterministic, no LLM). readiness_verdicts + readiness_enrichments tables; new (reserved) factory:refining label; read-only Flow-page annotation. Ships DARK behind VF_READINESS_GATE (default OFF) — inert in production until the operator activates it for the soak. Holdout-safe (reads the issue spec only). 2423 tests passing. Phase halts at Step 6 (#256), the substrate soak that measures the autonomous-success lift.
  • 2026-07-06 — Phase CV (Convergence & Valid Measurement) automated span shipped (umbrella #262, issues #263–#274 closed): the convergence-fix + valid-measurement track that gates the open-runtime flip. Steps 1–5 fix the FACTORY convergence bugs the RG Step 6 soak surfaced — a per-round-delta + deterministic-progress convergence gate (orchestrator.py, policy.py); machine red-gate evidence threaded into the coder fix prompt (validator.pycoder.py, fences in labels.py); a verdict-vs-gates contradiction detector with refute-precondition reform (validator.py); a stale-feedback skip (unchanged-head no_diff) + provider-accurate no_diff messaging (coder.py, run_dir.py); and hygiene (quiet checks-fetch, escalate-path readiness_verdicts row, coder timeout from params.timeout_s). Steps 6–9 fix the benchmark instrument: G2 fallback-abort, G1 production build_prompt assembly (deletes the {{key}} substitution path + closes a pr_description holdout leak), G3 deterministic expected_outcome accuracy scoring via production parse_verdict (judge demoted to secondary), and G4 per-suite anchor calibration + k=3-median judge robustness. Step 10 freezes the launcher (deletes the in-app edit/add/icon-picker UI + icon_catalog.py). Step 11 pre-registers the flip-gate D6 criteria (docs). Step 12-prep lands the Windows-inference kit (scripts/llama_server/windows/, dry-run install-verify + tool_calls gate). 2512 tests passing, 0 type errors, 0 lint violations. Only the automated span shipped — the operator/wait/conditional Steps 12–17 (#275–#281: A1 native-Windows-inference migration, substrate smoke, RG-Step-6 re-soak, judges run, thesis test, conditional v6 bake-off) are PENDING; nothing is deployed or flipped, the live harness/CURRENT=v1/OAuth path is unchanged, and the open-runtime flip stays HELD.

The full architectural context lives in documentation/master_plan.md; deeper background is in the wiki, the postmortems, and the retrospectives. The future direction (V2 self-improvement loop) is sketched in documentation/master_plan.md § V2.

What's next

The factory ships its target product when both autonomous iterations close out without operator intervention. Phase 1.8's job is to make that consistent. Phase G's job is to make the repo presentable. The eventual public flip — after Phase G commits land, after a security pass, after a final operator review — is the project's exit gate from "private prototype" to "public proof of concept." Until then, every operator-side button has a halt-the-world stop, and every coder PR sits behind the critic's wall.

Built with

Software the factory depends on. Thanks to the maintainers of each.

  • uv — Python package manager and runtime.
  • Claude Code CLI + Claude Sonnet 4.6 — the LLM subprocess that plans, writes, and reviews.
  • Streamlit — both the operator UI and the target product UI.
  • pytest — test gate.
  • ruff — lint + format gate.
  • mypy — strict type gate.
  • GitHub Actions — CI.
  • systemd — substrate timing on Ubuntu.
  • Ubuntu 24.04 LTS — substrate OS.
  • WSL2 — runs the Ubuntu substrate under Windows.

Stack
Layer Tool
Language Python 3.12
Package manager uv
LLM Claude Sonnet 4.6 via Claude Code CLI (live today — harness/CURRENT = v1). An open-weight/local-model pipeline via llama.cpp is a planned future update (harness v5+, TBD)
Auth CLAUDE_CODE_OAUTH_TOKEN — subscription OAuth, not a paid API key (live today). The future open-model pipeline needs no API key and no subscription
State SQLite (mutable) + JSONL (telemetry)
Runtime systemd timer + service on Ubuntu 24.04 LTS
Host WSL2 (Ubuntu 24.04 LTS under Windows 11)
Target product UI Streamlit on port 8501
Factory operator UI Streamlit on port 8502 (void_furnace_ui.service, optional --extra ui)

Full table with rationale: master_plan.md § Stack.

Setup
cd c:\Users\abero\dev\void_furnace
uv sync
uv sync --extra ui   # optional — installs the operator UI deps

The factory expects /etc/void_furnace/secrets.env on the substrate (mode 0600) with CLAUDE_CODE_OAUTH_TOKEN and a GitHub fine-grained PAT. The full procedure (substrate provisioning, systemd-unit install, secret seeding, deploy lifecycle) lives in documentation/runbook.md. Do not read or print secret values — see SECURITY.md for the rationale.

Operator UI

The Streamlit operator UI runs on the substrate at http://127.0.0.1:8502, served by void_furnace_ui.service. Three pages, each a separate st.Page route.

Page (URL) Shows
Flow (/) Per-issue stage view of the 9 factory:* labels, current PR (if any), and transcript tail of the active role. List + inline detail expander.
Operator (/Operator) Retro launchpad — ripe-for-retro merged PRs + past retrospectives. Read-only; issues are filed via the operator-launcher's submit-issue-target catalog entry on :8503.
Health (/Health) Status banner, factory timer + heartbeat color, cost, last 10 ticks, per-iteration aggregate table (inline-expandable for HI breakdown), Pause/Resume break-glass card.

The UI is read-mostly. The canonical write surface for issue submission and retro launching is the operator-launcher Streamlit app on :8503 (24+ catalog entries; see operator-launcher/run-launcher.ps1). The UI Retro tab continues to list ripe-for-retro PRs and past retros, but no longer shows copy-paste launch hints — operators use the launcher's retro-start entry instead.

Usage

The five commands below are the CI gate sequence.github/workflows/ci.yml runs them verbatim on every PR and push to master. Run them locally before pushing:

cd c:\Users\abero\dev\void_furnace
uv sync                           # resolve + install into .venv
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run mypy src                   # strict mode on src/ only
Project layout
src/void_furnace/         orchestrator, coder, critic, triage, state, recovery, holdout, labels
src/void_furnace_ui/      Streamlit operator UI: app.py + 5 pages + 8 service modules
tests/                    pytest suite (~2350 tests; AST guards on holdout + labels)
scripts/                  deploy.sh, install-systemd.sh, reset-target.sh, _dashboard.sh
systemd/                  void_furnace.service + .timer + void_furnace_ui.service unit files
documentation/plans/      one plan per phase; each becomes GitHub issues via /repo-sync
documentation/wiki/       in-tree docs (the directory; not the GitHub-wiki feature)
documentation/investigations/   one-off deep dives that feed plans
documentation/retrospectives/   per-iteration target-product reviews
documentation/appendix/   plan-companion reference material
postmortems/              dated incident + UAT writeups
seed-issues/              starter task templates the factory consumes
Build pipeline
/plan-init   or   /plan-feature        produce documentation/plans/<name>.md
        │
        ▼
/plan-review                           gap-check the plan
        │
        ▼
/plan-wrap                             clean-context check; plan must be
                                       self-contained before repo-sync
        │
        ▼
/repo-init   or   /repo-sync           mint GitHub issues from plan steps
        │
        ▼
/build-phase   or   /build-step        execute steps autonomously
        │
        ▼
/repo-update                           commit, update docs, push

/build-phase walks the plan and dispatches /build-step per row. /build-step spawns a developer agent in an isolated worktree, runs reviewers (auto or code or full), and merges back on PASS. Plan-pipeline rules live in .claude/rules/ (project-scoped) and dev/.claude/rules/ (workspace-scoped); both are read-only references the agents follow.


About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages