Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.

Commit 1d1fb37

Browse files
authored
bean 2.0.0 — Rust runtime, oracle gate, native hooks
Rust runtime (bean-check + bean-verify + bean-run + bean-hook), the 2.0 oracle gate, and native Stop-hook coupling for Claude Code and Codex. JS line preserved on js-reference as the conformance spec. 24/24 conformance + driver + gate; CI green. See CHANGELOG 2.0.0.
1 parent 0cb56e8 commit 1d1fb37

18 files changed

Lines changed: 2442 additions & 16 deletions

File tree

‎.claude-plugin/marketplace.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
"metadata": {
88
"description": "bean — a recursive convergence loop for large tasks: investigate, record evidence, compile, revise beliefs, repeat until converged. Runs on the grainulator runtime. Modeled on how the Fable model actually worked.",
9-
"version": "1.2.0"
9+
"version": "2.0.0"
1010
},
1111
"plugins": [
1212
{
@@ -16,7 +16,7 @@
1616
"url": "https://github.com/grainulation/bean.git"
1717
},
1818
"description": "Execution-discipline skill for Claude Code and Codex",
19-
"version": "1.2.0",
19+
"version": "2.0.0",
2020
"author": {
2121
"name": "Grainulation"
2222
},

‎.claude-plugin/plugin.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bean",
3-
"version": "1.2.0",
3+
"version": "2.0.0",
44
"description": "Run a task as a recursive convergence loop: investigate the most decisive open question, record evidence in a claim ledger, let a compiler score what is still weak or contradictory, revise beliefs, and loop until it converges. Runs on grainulator/wheat (or a built-in ledger). Verbose by default.",
55
"author": {
66
"name": "Grainulation"

‎.codex-plugin/plugin.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bean",
3-
"version": "1.2.0",
3+
"version": "2.0.0",
44
"description": "A documentation-only Codex plugin adding one explicit /bean skill: run a task as a recursive convergence loop — investigate, record evidence in a ledger, compile, revise beliefs, and loop until converged.",
55
"author": {
66
"name": "Grainulation"

‎.github/workflows/ci.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,16 @@ jobs:
3232
- name: Zero runtime-dependency guard
3333
run: |
3434
node -e "const p=require('./package.json'); if (p.dependencies && Object.keys(p.dependencies).length) { console.error('bean ships zero runtime dependencies (devDependencies only)'); process.exit(1); } console.log('ok: zero runtime dependencies');"
35+
36+
# The Rust runtime (bean 2.0) is gated by the differential conformance oracle: the JS
37+
# bean-check is the reference, the Rust port must match it on every fixture.
38+
conformance:
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v4
42+
- uses: actions/setup-node@v4
43+
with:
44+
node-version: 22
45+
- uses: dtolnay/rust-toolchain@stable
46+
- name: Differential conformance (JS reference vs Rust)
47+
run: node test/conformance.mjs

‎.github/workflows/release.yml‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: release
2+
3+
# Cut a release by pushing a tag: git tag v2.0.0 && git push origin v2.0.0
4+
on:
5+
push:
6+
tags: ["v*"]
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
# Build the runtime on a NATIVE runner per platform (no cross-compilation), package the four
13+
# binaries into a per-target tarball, and upload as a workflow artifact.
14+
build:
15+
strategy:
16+
matrix:
17+
include:
18+
- os: macos-latest
19+
target: aarch64-apple-darwin
20+
- os: macos-13
21+
target: x86_64-apple-darwin
22+
- os: ubuntu-latest
23+
target: x86_64-unknown-linux-gnu
24+
- os: ubuntu-24.04-arm
25+
target: aarch64-unknown-linux-gnu
26+
runs-on: ${{ matrix.os }}
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: dtolnay/rust-toolchain@stable
30+
- name: Build (release)
31+
run: cargo build --release --manifest-path rs/Cargo.toml
32+
- name: Package
33+
run: |
34+
set -eu
35+
dir="bean-${GITHUB_REF_NAME}-${{ matrix.target }}"
36+
mkdir -p "$dir"
37+
cp rs/target/release/bean-check rs/target/release/bean-verify \
38+
rs/target/release/bean-run rs/target/release/bean-hook "$dir"/
39+
cp README.md LICENSE "$dir"/ 2>/dev/null || true
40+
tar -czf "$dir.tar.gz" "$dir"
41+
shasum -a 256 "$dir.tar.gz" > "$dir.tar.gz.sha256"
42+
- uses: actions/upload-artifact@v4
43+
with:
44+
name: ${{ matrix.target }}
45+
path: |
46+
bean-*.tar.gz
47+
bean-*.tar.gz.sha256
48+
49+
# Gather all platform tarballs and publish the GitHub release.
50+
release:
51+
needs: build
52+
runs-on: ubuntu-latest
53+
steps:
54+
- uses: actions/checkout@v4
55+
- uses: actions/download-artifact@v4
56+
with:
57+
path: dist
58+
merge-multiple: true
59+
- name: Publish release
60+
uses: softprops/action-gh-release@v2
61+
with:
62+
files: dist/*
63+
generate_release_notes: true
64+
body: |
65+
bean ${{ github.ref_name }} — Rust runtime (gate + verify + driver + native Stop hook).
66+
67+
Prebuilt binaries per platform are attached. Or build from source:
68+
`git clone … && ./install.sh` (requires Rust; the binary itself has no runtime deps).

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ review/
66
bean-stalk.md
77
**/.bean/state.json
88

9+
rs/target/
10+
/bin/bean-check
11+
/bin/bean-verify
12+
/bin/bean-run
13+
/bin/bean-hook

‎CHANGELOG.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
# Changelog
22

3+
## 2.0.0 — unreleased (Rust runtime)
4+
5+
bean 2.0 reimplements the runtime as a **single Rust static binary** (no install dependency —
6+
Node was itself the dependency that undercut "runs anywhere"), reconverging with the Bran core,
7+
and **couples it to execution natively** in both Claude Code and Codex via their Stop hooks. The
8+
JS line is preserved on the `js-reference` branch as the behavior spec; it is not shipped.
9+
10+
### The runtime (`rs/`, four binaries)
11+
12+
- **`bean-check`** — the compiler: static checks, temporal checks (state/dry-round/budget), and
13+
the 2.0 **oracle gate** (verification mode `compat`/`advisory`/`strict`, `verified_by`,
14+
recorded verdicts, `converged-with-residuals` exit 4). Certificate is byte-identical to the JS
15+
reference when no 2.0 field is present, and binds the full regime when the gate is active.
16+
- **`bean-verify`** — the only execution path for oracles: runs a declared command (argv, no
17+
shell, claim JSON on stdin) and writes a scrubbed verdict; `bean-check` adjudicates it.
18+
- **`bean-run`** — the driver: per round it injects the compiler signal into the agent's prompt,
19+
records emitted claims, and enforces linear progress (stuck detection). Model-agnostic
20+
`--agent` command.
21+
- **`bean-hook`** — the native Stop hook for Claude Code and Codex (shared contract): blocks the
22+
agent from finishing a bean-tracked task until the loop converges; inert when no `.bean/`
23+
ledger exists; honors the loop guard. `--register` wires it into settings.json / hooks.json.
24+
25+
### Bootstrap mechanic + tests
26+
27+
The JS `bean-check` is the independent reference; the Rust port is held to it by a **differential
28+
conformance oracle** (`test/conformance.mjs`): 14 differential checks (static + temporal,
29+
certificates byte-identical) + 2 driver-smoke + 8 oracle-gate behavioral = **24/24**, run in CI
30+
alongside the Node tests.
31+
32+
### Install
33+
34+
`./install.sh` builds the binaries and registers the native Stop hook for both clients — "install,
35+
then it just works." Requires Rust; the shipped binary has no runtime dependency.
36+
337
## 1.2.0 — 2026-06-19
438

539
The "not quite there" release. The core failure was _satisficing_: the loop under-delivered

‎hooks/hooks.json‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"hooks": {
3+
"Stop": [
4+
{
5+
"matcher": "",
6+
"hooks": [
7+
{
8+
"type": "command",
9+
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/bin/bean-hook"
10+
}
11+
]
12+
}
13+
]
14+
}
15+
}

‎install.sh‎

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,63 @@
11
#!/usr/bin/env bash
22
#
3-
# bean installer (fallback to the plugin marketplace).
4-
# Copies the bean skill + command into your Claude Code config, and the skill into
5-
# your Codex config when present. Documentation-only: no dependencies, no servers.
3+
# bean installer — builds the Rust runtime and wires it into Claude Code (and Codex).
4+
#
5+
# What you get after this:
6+
# - the bean runtime binaries (bean-check, bean-verify, bean-run, bean-hook) built into ./bin
7+
# - the /bean skill installed
8+
# - a native Stop hook registered so bean COUPLES to execution: an agent can't finish a
9+
# bean-tracked task (one with a .bean/ ledger) until the loop converges. Inert otherwise.
610
#
711
# Usage:
8-
# ./install.sh # install for the current user
9-
# CLAUDE_CONFIG_DIR=... ./install.sh
12+
# ./install.sh # build + install for the current user
13+
# CLAUDE_CONFIG_DIR=... ./install.sh # custom Claude config dir
1014
#
15+
# Requires: Rust (cargo). No runtime dependencies — the binaries are self-contained.
1116
set -euo pipefail
1217

1318
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14-
1519
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
1620
CODEX_DIR="${CODEX_CONFIG_DIR:-$HOME/.codex}"
1721

22+
build_runtime() {
23+
command -v cargo >/dev/null 2>&1 || {
24+
echo " ERROR: cargo (Rust) is required to build the bean runtime." >&2
25+
echo " Install from https://rustup.rs and re-run." >&2
26+
exit 1
27+
}
28+
echo " building the Rust runtime (release)..."
29+
cargo build --release --quiet --manifest-path "$REPO_DIR/rs/Cargo.toml"
30+
mkdir -p "$REPO_DIR/bin"
31+
for b in bean-check bean-verify bean-run bean-hook; do
32+
cp "$REPO_DIR/rs/target/release/$b" "$REPO_DIR/bin/$b"
33+
done
34+
echo " binaries -> $REPO_DIR/bin (bean-check, bean-verify, bean-run, bean-hook)"
35+
}
36+
1837
install_claude() {
1938
mkdir -p "$CLAUDE_DIR/skills"
2039
rm -rf "$CLAUDE_DIR/skills/bean"
2140
cp -R "$REPO_DIR/skills/bean" "$CLAUDE_DIR/skills/bean"
22-
echo " installed bean -> $CLAUDE_DIR/skills/bean (provides /bean)"
41+
echo " skill -> $CLAUDE_DIR/skills/bean (provides /bean)"
42+
# register the native Stop hook (idempotent JSON merge into settings.json)
43+
"$REPO_DIR/bin/bean-hook" --register "$CLAUDE_DIR" | sed 's/^/ /'
2344
}
2445

2546
install_codex() {
2647
if [ -d "$CODEX_DIR" ]; then
2748
mkdir -p "$CODEX_DIR/skills"
2849
rm -rf "$CODEX_DIR/skills/bean"
2950
cp -R "$REPO_DIR/skills/bean" "$CODEX_DIR/skills/bean"
30-
echo " installed bean -> $CODEX_DIR/skills/bean"
51+
echo " skill -> $CODEX_DIR/skills/bean"
52+
# Codex's Stop hook uses the SAME contract as Claude's; register into hooks.json
53+
"$REPO_DIR/bin/bean-hook" --register "$CODEX_DIR" hooks.json | sed 's/^/ /'
3154
else
3255
echo " (Codex config dir $CODEX_DIR not found — skipping Codex install)"
3356
fi
3457
}
3558

3659
echo "Installing bean..."
60+
build_runtime
3761
install_claude
3862
install_codex
39-
echo "Done. Restart your client, then invoke /bean."
63+
echo "Done. Restart Claude Code. In a project, run /bean; the Stop hook keeps the loop honest."

‎package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)