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

Commit 802a11c

Browse files
aid-ninjaclaude
andcommitted
2.0.1: fix-first hardening from the cross-model (Codex) review
Codex reviewed the shipped 2.0 and found several fail-open bugs (worst kind in a gate) + conformance gaps. All fixed with regression coverage: - bean-hook fails CLOSED on an active ledger (checker missing/crash/unknown-status -> block). - invalid run.json / state.json -> exit 3 (was: silent compat fallback / temporal reset). - a `residual` tag no longer launders a failing/stale/undeclared verifier (it blocks). - bean-verify: a nonzero exit can't be upgraded to pass by stdout JSON. - inputs_hash is byte-based and recurses into directories (dirs previously -> "absent"); identical in bean-check and bean-verify. - bean-run treats converged-with-residuals (exit 4) as terminal. - Rust emits the coverage warnings it had dropped; multi-claim claims_hash matches the reference (\x01 line join). Differential now compares warnings + multi-claim temporal + residual+failing-oracle (27 checks). - certificate binds a canonical oracle-spec hash (cmd+inputs); schemas carry the 2.0 fields; SECURITY.md describes bean-run's --agent surface; release workflow smoke-tests artifacts; marketplace Stop hook degrades via a shim when no binary is present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6d9c9cf commit 802a11c

21 files changed

Lines changed: 430 additions & 116 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": "2.0.0"
9+
"version": "2.0.1"
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": "2.0.0",
19+
"version": "2.0.1",
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": "2.0.0",
3+
"version": "2.0.1",
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": "2.0.0",
3+
"version": "2.0.1",
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/release.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ jobs:
3333
targets: ${{ matrix.target }}
3434
- name: Build (release)
3535
run: cargo build --release --target ${{ matrix.target }} --manifest-path rs/Cargo.toml
36+
- name: Smoke the built binaries
37+
# only run native binaries (cross-built targets can't execute on this host)
38+
if: ${{ !(matrix.os == 'macos-latest' && matrix.target == 'x86_64-apple-darwin') }}
39+
run: |
40+
set -eu
41+
out="rs/target/${{ matrix.target }}/release"
42+
"$out/bean-check" --help >/dev/null
43+
"$out/bean-check" --dir test/fixtures/converged --no-state >/dev/null # exit 0 ready
44+
if "$out/bean-check" --dir test/fixtures/open-risk --no-state; then
45+
echo "expected blocked exit"; exit 1
46+
fi
47+
echo "smoke ok for ${{ matrix.target }}"
3648
- name: Package
3749
run: |
3850
set -eu

CHANGELOG.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
# Changelog
22

3-
## 2.0.0 — unreleased (Rust runtime)
3+
## 2.0.1 — fix-first hardening (independent review)
4+
5+
A cross-model (Codex) review of the shipped 2.0 runtime found several **fail-open** bugs (the
6+
worst kind in a gate) and conformance gaps. All fixed, with regression coverage:
7+
8+
- **Fail closed, not open.** The Stop hook (`bean-hook`) now BLOCKS on an active `.bean` ledger
9+
if `bean-check` can't run / returns no JSON / returns an unknown status (was: allow stop).
10+
An existing-but-invalid `run.json` or `state.json` now errors (exit 3) instead of silently
11+
reverting to compat / resetting temporal state.
12+
- **A residual can't launder a failing oracle.** A claim that declares a verifier which then
13+
fails/staled/errored now BLOCKS regardless of a `residual` tag; residual fallback applies
14+
only when no verifier is declared at all.
15+
- **`bean-verify`: a nonzero exit can't be upgraded to `pass`** by a `{"verdict":"pass"}` on
16+
stdout (JSON may only downgrade/explain).
17+
- **Real freshness for directory inputs.** `inputs_hash` is now byte-based and recurses into
18+
declared directories (a `inputs: ["src/"]` previously collapsed to "absent", so changes were
19+
never detected). Identical in `bean-check` and `bean-verify`.
20+
- **`bean-run` treats `converged-with-residuals` (exit 4) as terminal** instead of looping to
21+
stuck.
22+
- **Conformance parity restored & widened.** Rust now emits the coverage warnings
23+
(`W_SINGLE_SOURCE`/`W_MONOCULTURE`) it had dropped; the multi-claim `claims_hash` delimiter
24+
now matches the reference (`\x01` line join — single-claim tests had hidden the drift). The
25+
differential now compares warnings, with new multi-claim temporal and residual+failing-oracle
26+
cases (27 conformance + driver + gate checks).
27+
- **Polish:** certificate binds a canonical hash of each oracle spec (cmd+inputs); schemas carry
28+
the 2.0 fields (`verification`/`oracles`/`verified_by`); `SECURITY.md` describes the real
29+
execution surface (incl. `bean-run`'s `--agent` command); the release workflow smoke-tests each
30+
built binary before upload; the marketplace Stop hook degrades gracefully via a shim when no
31+
binary is present.
32+
33+
## 2.0.0 — Rust runtime
434

535
bean 2.0 reimplements the runtime as a **single Rust static binary** (no install dependency —
636
Node was itself the dependency that undercut "runs anywhere"), reconverging with the Bran core,

SECURITY.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ bean is a small skill plus a self-contained runtime (Rust binaries, no runtime d
1010
no telemetry). `bean-check` is a pure adjudicator — it only reads/writes local `.bean/*`
1111
files and makes no network calls.
1212

13-
The one execution surface to know about is the **2.0 oracle gate**: `bean-verify` (and the
14-
`bean-run` driver) run **commands you declare** in `.bean/run.json` `oracles` — arbitrary
15-
local code, by design, and they can reach the network if a declared command does. They run
16-
with `shell:false` (argv only, no shell interpolation) and only when you opt in
17-
(`verification.mode: strict`/`advisory` with a `verified_by` claim). Treat a `run.json` like
18-
a makefile: only run a ledger whose oracle commands you trust. The native Stop hook
19-
(`bean-hook`) only ever runs `bean-check` (no oracle execution) and is inert without a
20-
`.bean/` ledger. The other likely concern is prompt-injection or misleading guidance in the
21-
skill text.
13+
The execution surfaces to know about (all `shell:false` — argv only, no shell interpolation):
14+
15+
- **`bean-verify`** runs the **oracle commands you declare** in `.bean/run.json` `oracles`
16+
arbitrary local code, by design, which can reach the network if a declared command does.
17+
- **`bean-run`** runs the **`--agent` command you pass it** each round, and triggers oracles
18+
via `bean-verify`. It is an explicit driver you invoke, not something the hook starts.
19+
- **`bean-check`** and **`bean-hook`** execute no oracles. `bean-check` is a pure adjudicator
20+
(reads ledger/verdicts only); `bean-hook` only runs `bean-check`, and is inert without a
21+
`.bean/` ledger.
22+
23+
Treat a `run.json` like a makefile: only run a ledger whose oracle commands (and any agent
24+
command) you trust. The other likely concern is prompt-injection or misleading guidance in
25+
the skill text.
2226

2327
Please report any concern via GitHub Security Advisories on this repository (preferred),
2428
or by email to security@grainulator.app.

hooks/bean-stop.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
# bean Stop-hook shim (marketplace path). Locates the bean-hook binary and execs it (passing
3+
# the Stop payload through on stdin). If the binary isn't present yet — e.g. a marketplace
4+
# install where you haven't run ./install.sh or dropped a prebuilt binary — it exits 0 (allow
5+
# the stop) so the hook degrades gracefully instead of erroring on every turn.
6+
bin="$(command -v bean-hook 2>/dev/null || true)"
7+
if [ -z "$bin" ] && [ -x "${CLAUDE_PLUGIN_ROOT:-}/bin/bean-hook" ]; then
8+
bin="${CLAUDE_PLUGIN_ROOT}/bin/bean-hook"
9+
fi
10+
[ -z "$bin" ] && exit 0
11+
exec "$bin"

hooks/hooks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"hooks": [
77
{
88
"type": "command",
9-
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/bin/bean-hook"
9+
"command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/bean-stop.sh"
1010
}
1111
]
1212
}

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.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grainulation/bean",
3-
"version": "2.0.0",
3+
"version": "2.0.1",
44
"description": "A recursive convergence loop for Claude Code and Codex: investigate, record evidence in a claim ledger, compile, revise beliefs, and loop until converged. Ships bean-check, a zero-dependency convergence compiler that exits nonzero until the loop converges; grainulator/wheat optional. Verbose by default.",
55
"type": "module",
66
"private": false,

0 commit comments

Comments
 (0)