Skip to main content

blackice

A human-in-the-loop adversarial review panel for mission-critical code.

kuang convenes a panel of adversarial reviewer personas — each tasked to break a change, not approve it — and runs them in a bounded iteration loop until a halting condition, with a ruin-class circuit-breaker that stops the moment a survivability-threatening ("UGLY") finding appears.

It's the tool form of a review pattern for code where a subtle bug is expensive, silent, or irreversible. The pattern and its prior art are in two-pass-adversarial-review-pattern.md.

What you install, import and run is kuang. pip install kuang, import kuang, kuang --repo …. blackice is the repository this is developed in, and nothing else. The import name blackice is already taken on PyPI by an unrelated project that ships a top-level blackice package and a blackice console script from the same entry point — installing both merges the two into one directory, hands the command to whichever landed last, and breaks the other's uninstall, all silently. Hence Kuang, after the intrusion program that cuts through ICE.

When to use it

Execution paths where being wrong is expensive, silent, or irreversible — money movement, tax/regulatory records, data integrity, safety interlocks, migrations, auth. A change that "looks obviously right" in a place you can't afford a bug. Not for routine, reversible, or well-covered changes: it is deliberately heavy and spends tokens.

How it works

Three subpackages, three concerns:

  • kuang/cli/ — the entry point you run (kuang). Wires a backend into the engine and exposes the CLI. This is what the examples below invoke.
  • kuang/engine/ — the deterministic engine. Owns the control loop: halting predicates, dedup/stall detection, token/time budget, and the UGLY circuit-breaker. Backend-agnostic (dependency-injected seams); not run directly. It imports nothing from a backend, and a test enforces that.
  • kuang/backends/claude_code/ — a backend: binds the engine to the Claude Code CLI (one claude -p subprocess per persona per epoch) and sources the panel. A different runtime would be its own subpackage, swapped in by the entry point — the engine is unchanged.

Each epoch fans out the panel (independent within the epoch), adjudicates findings against source, and dedups against the running ledger — a coarse signature always, plus an opt-in semantic reduce (--semantic-dedup) that folds the same concept raised by several personas (differently worded or located) into one canonical cluster, so stall/convergence and the human summary count issues, not raw restatements. A cluster's severity is the max of its members, so a merge can never hide an UGLY. Epoch N>1 receives all prior findings (cross-epoch memory). A human convenes and gates; the panel informs — it does not decide.

Halting set (OR of predicates, ruin checked first): ESCALATE_UGLY · CONVERGED · BUDGET · EPOCH · STALL. Severity ladder: GOOD (nothing open) / BAD (bugs, weak logic, scope-creep — iterate or track) / UGLY (non-linear, cascading, irreversible — circuit-break; never halt with one open).

Install

pipx install kuang          # recommended for a CLI: its own environment, command on PATH
pip install kuang           # or into an environment you already have
pip install 'kuang[yaml]'   # only if a target repo defines its panel in panel.yaml

Python 3.11+. No runtime dependencies — the core is stdlib-only.

The claude CLI is a prerequisite, and installing this does not install it. Each persona is a real claude -p subprocess, so the Claude Code CLI has to be on the machine; it is not a Python package and cannot be declared as a dependency. It is looked for in this order:

  1. $CLAUDE_BIN, if set and the path exists
  2. claude on PATH
  3. ~/.local/bin/claude

If none resolve, a live run currently fails with a FileNotFoundError traceback at the first spawn rather than a diagnosis (#51). --dry-run spawns nothing, so it works without claude installed and is the cheapest way to confirm the rest of the wiring.

Quickstart

# Pre-flight (spawns nothing, costs nothing): confirm which personas were sourced
kuang --repo <root> --base <base> --head <head> --dry-run

# Live, read-only (the safe default)
kuang --repo <root> --base <base> --head <head> \
  --why "why this matters" --what "what changed" --max-epochs 2

# Permissioned: let reviewers verify against source / run the suite (scoped)
kuang --repo <root> --base <base> --head <head> \
  --allow-tools Read Grep Glob 'Bash(git:*)' 'Bash(pytest:*)' --permission-mode default

# Path mode: review existing code (whole files/dirs), not a diff — proactive
# bug-hunting, or a repo with no reviewable diff. Dirs expand via git ls-files.
kuang --repo <root> --paths src/pkg/a.py src/pkg/b/ --max-epochs 2

# Semantic dedup: fold the same concept (raised by several personas, worded or
# located differently) into one canonical issue — sharpens stall/convergence and
# the summary. Opt-in: adds a cheap clustering call per epoch (--cluster-model to
# pick a model); the default is a deterministic signature dedup.
kuang --repo <root> --base <base> --head <head> \
  --semantic-dedup --max-epochs 2

# Cross-run memory: seed a re-run with the previous run's ledger, so the panel
# reports which findings the fixes actually closed instead of re-deriving them
# cold. Takes a saved findings.json, a run's JSON output, or a raw run.log with
# the '--- JSON ---' block in it. Both modes.
kuang --repo <root> --paths src/pkg/ \
  --prior-findings runs/2026-01-01/run.log

Exactly one mode per run: --base/--head (diff) or --paths (whole-file).

Personas

Sourced by precedence: a repo's CLAUDE.md "Resident Experts"panel.yaml/panel.md → a distilled default set. A completeness-critic and a survivability (ruin) lens are always ensured. Mandates stay open-ended (the persona's role is its lens; we don't lead the witness). See SKILL.md.

Permission model

Deny-by-default, read-only (Read/Grep/Glob; no shell or edits) — because headless claude -p runs any allowed tool unsupervised, and HITL here is per-epoch, not per-command. Scoped verification (Bash(pytest:*), Bash(git diff:*)) is opt-in via --allow-tools. Never bare Bash.

Layout

Path Role
kuang/cli/ entry point — run this; wires a backend into the engine
kuang/engine/ deterministic engine: findings · protocols · halting · reduce · loop
kuang/backends/claude_code/ Claude Code CLI backend: personas · surface · spawn · contract · memory · cluster · permissions · session
kuang/report.py presentation shared by the above (ledger lines, argv rendering)
SKILL.md the skill definition (how a convening agent runs it)
NOTES.md design notes, open decisions, backlog
RELEASING.md how a release is cut and published (Trusted Publishing)
two-pass-adversarial-review-pattern.md the pattern + origin case study

Status

Experimental; dogfooded end-to-end. Semantic dedup is implemented as an opt-in reduce step (--semantic-dedup, UGLY-preserving). Open work is organised into eight sequenced epochs in ROADMAP.md, which also states what would make this 1.0; the design reasoning behind individual items is in NOTES.md.


Licensed under either of MIT (LICENSE-MIT) or Apache-2.0 (LICENSE-APACHE) at your option. Copyright © 2026 Agilit Ltd.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kuang-0.2.0.tar.gz (44.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kuang-0.2.0-py3-none-any.whl (50.6 kB view details)

Uploaded Python 3

File details

Details for the file kuang-0.2.0.tar.gz.

File metadata

  • Download URL: kuang-0.2.0.tar.gz
  • Upload date:
  • Size: 44.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for kuang-0.2.0.tar.gz
Algorithm Hash digest
SHA256 dca83202affaf2fbd3feaafa7bddcbab8a85693b20095ec926f3c8fa0c6bb19d
MD5 cc5d0ea2e22991bbc1ffdb143c116a58
BLAKE2b-256 e2c2937e0f2b20532bef570341c87f03d318999d44779cf2bed03577839d3011

See more details on using hashes here.

Provenance

The following attestation bundles were made for kuang-0.2.0.tar.gz:

Publisher: release.yml on aigora-de/blackice

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file kuang-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: kuang-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 50.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for kuang-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cf73a34b79b233f22ba983fdd440533091e20ce68f5d50b5393679f6c85a8f66
MD5 724f4813da6acc2b58e040b009cf5cf6
BLAKE2b-256 6e9d286bac6b95602e7aa5ba19f6435a3b8423daa5c2f132a670d8783a3c592a

See more details on using hashes here.

Provenance

The following attestation bundles were made for kuang-0.2.0-py3-none-any.whl:

Publisher: release.yml on aigora-de/blackice

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page