Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
msrv:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@1.78.0
- run: cargo build --release --locked --workspace
test:
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand All @@ -13,6 +23,9 @@ jobs:
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v6
with:
node-version: '24'
- name: build
run: cargo build --release --workspace
- name: test
Expand All @@ -26,20 +39,84 @@ jobs:
run: cargo run --release -p probbit-cli -- stats --pretty
- name: agent_router example
run: cargo run --release --example agent_router
- name: Python and MCP integration contracts
shell: bash
run: |
B="$PWD/target/release/probbit"
if [ "$RUNNER_OS" = Windows ]; then B="$(cygpath -w "$B.exe")"; fi
export PROBBIT_BIN="$B"
python -m unittest discover -s python -p 'test_*.py'
node examples/node/decide.mjs
- name: npm platform and process contracts
run: node --test npm/test-wrapper.cjs
- name: clean-prefix install and npm package contracts
shell: bash
run: |
set -euo pipefail
TARGET=$(rustc -vV | sed -n 's/^host: //p')
TAG=v$(node -p "require('./npm/package.json').version")
BIN=target/release/probbit
[ "$RUNNER_OS" = Windows ] && BIN="$BIN.exe"
if [ "$RUNNER_OS" != Windows ]; then
python3 scripts/tests/test_install_contract.py
sh scripts/bench/test_install_sh.sh "$BIN" "$TARGET" "$TAG"
else
sh scripts/bench/package_like_release.sh "$BIN" "$TARGET" "$TAG" "target/install-fixture/good/$TAG"
cp -R target/install-fixture/good target/install-fixture/bad
printf '%064d %s\n' 0 "probbit-$TAG-$TARGET.zip" > "target/install-fixture/bad/$TAG/probbit-$TAG-$TARGET.zip.sha256"
for PS in pwsh powershell; do
"$PS" -NoProfile -ExecutionPolicy Bypass -File scripts/bench/test_install_ps1.ps1 -Srv target/install-fixture -Tag "$TAG"
done
fi
sh scripts/bench/test_npm.sh "$BIN" "$TARGET" "$TAG"
- name: npm 12 clean-prefix contracts
if: runner.os == 'Linux'
shell: bash
run: |
npm install -g npm@12
sh scripts/bench/test_npm.sh target/release/probbit x86_64-unknown-linux-gnu
npm-node18:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: '18'
- run: node --test npm/test-wrapper.cjs
wasm:
timeout-minutes: 15
# probbit-wasm, the browser build (wasm32-unknown-unknown, no threads): built as playground/build.sh builds it, then loaded by
# Node with the playground's own loader and run on the 300-task demo (--sweeps 3200) and the evaluate example; check.mjs
# exits 1 unless both answer with 0 violations. The no-thread = 4-thread equality runs natively in `test` (probbit-wasm tests).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: '24'
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- name: build
run: sh playground/build.sh
- name: run under Node
run: node --version && node playground/check.mjs playground/probbit.wasm
- name: WASM integer boundaries and native parity
run: |
cargo build --release -p probbit-cli
node probbit-wasm/tests/boundaries.mjs playground/probbit.wasm target/release/probbit
- name: real browser interaction and narrow layout
run: node playground/check-browser.mjs target/browser-check
- name: real monitor playback and live append in the browser
env:
PROBBIT_BIN: ${{ github.workspace }}/target/release/probbit
PROBBIT_BROWSER_OUT: ${{ github.workspace }}/target/browser-check/monitor
run: node probbit-cli/tests/monitor_browser.mjs
- uses: actions/upload-artifact@v7
if: always()
with:
name: browser-check
path: target/browser-check/
release-targets:
# The release archives' targets that `test` does not build (release.yml runs only on tags): built here, so a tag is
# never the first build of a target. Static musl (x86_64 runs here, aarch64 is cross-linked), macOS x86_64 under
Expand All @@ -50,6 +127,7 @@ jobs:
include:
- { os: ubuntu-latest, target: x86_64-unknown-linux-musl, run: true }
- { os: ubuntu-latest, target: aarch64-unknown-linux-musl, linker: aarch64-linux-gnu-gcc }
- { os: ubuntu-24.04-arm, target: aarch64-unknown-linux-musl, linker: aarch64-linux-gnu-gcc, run: true }
- { os: macos-latest, target: x86_64-apple-darwin, run: true, rosetta: true }
- { os: windows-latest, target: x86_64-pc-windows-msvc, run: true, rustflags: "-C target-feature=+crt-static" }
runs-on: ${{ matrix.os }}
Expand Down
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,28 @@ __pycache__/
playground/probbit.wasm
playground/probbit-wasm.js
playground/puzzle-personas.js

# Local credentials, agent context and generated release artifacts are not product sources.
.env
.env.*
*.credentials*
*credentials.json
cookies.txt
login_response*
.secrets/
.agents/
.ouroboros/
AGENTS.md
SOUL.md
USER.md
MEMORY.md
IDENTITY.md
memory/
identity/
diary/
state/
audits/
data/
node_modules/
dist/
*.tgz
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@
All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## Unreleased

### Fixed
- Reject invalid JSON number grammar and raw control characters, platform-sized integer overflow, overflowing linear-cap
totals, and invalid pair-table dimensions before allocation. WASM no longer wraps large precedence gaps, counts or weights.
- Validate supplied anneal/polish starting assignments even when the work budget is zero.
- Validate stored strand hashes and lifecycle transitions before continuation or control appends; a checkpoint cannot hide
retirement. Full inference verification and external anchoring remain separate responsibilities.
- Python rejects an explicitly missing binary instead of falling back, and rejects invalid false/array input values rather
than silently treating them as empty objects. MCP and Python preserve command-specific error/lifecycle contracts.
- Persona `--pretty` consistently formats single-document stdout without changing canonical state files. JSONL replay and
text explain reject the flag with guidance. Source-restricted fuzz exports require an explicitly labelled synthetic source
to produce runnable replay commands; production source checks are not bypassed.
- npm forwards each termination signal unchanged, bounds download waits and selects both x86_64 and ARM64 musl assets.
Shell installation defaults to the user's `~/.local/bin`. Installers validate candidate binaries before replacing a
working installation on the shell/PowerShell paths; PowerShell stages replacement on the destination filesystem.
Release/target selectors reject paths.
- Monitor labels scripted demos, loop restarts, lifecycle and checkpoint status; preserves original `why` text, explains
display terms and supports narrow screens. Browser playground and puzzle tables no longer force horizontal page overflow.
- Keep the lockfile readable by the declared Rust 1.78 minimum; CI builds that compiler with `--locked`.

### Added
- Decision documents with a candidate `plan` gain `plan_status` and `released_plan`. The old full candidate is preserved for
compatibility; a diagnostic/refused plan is not permission to act. Partial projections may not be independently feasible.
- `probbit monitor --demo drives` shows eight replayable synthetic goal events. The default tutor demo is unchanged.
- `examples/agent-harness`: a local dispatch gate with a recorded incident, history-dependent retry regression, strict
replay and a scoped repaired-rule proof. No model, credentials or external tool action required.
- Independent enumeration/router oracles, actual WASM boundary tests, real Chromium interaction/layout checks, Node process
contracts and cross-platform clean-prefix install tests (including Windows PowerShell 5.1/7 and npm 12).

These changes are a source candidate, not an update to the published 0.8.0 artifacts. Probability gate thresholds and frozen
benchmark corpora are unchanged. Passing finite tests is not exhaustive validation of every possible program or application.

## 0.8.0 - 2026-10-09

### Added
Expand Down
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ rules that keep its claims honest.

```
cargo build --release --workspace # no network needed: there are no external crates
cargo test --release --workspace # 127 tests: 3 core, 48 CLI, 11 stress (+1 ignored), 18 acceptance, 47 probbit-ir
cargo test --release --workspace # unit, integration, regression and exact-oracle tests
cargo run --release -p probbit-cli -- demo --tasks 12 | cargo run --release -p probbit-cli -- decide --pretty
```

Expand All @@ -19,6 +19,11 @@ loaded machine; re-run it alone before reading anything into a failure.
The default build is portable (no CPU pin). `RUSTFLAGS="-C target-cpu=native"` gives the last
bit of speed on your own machine; that binary may not run elsewhere.

Release-contract checks also run on every pull request: the Python and MCP suites, npm platform/signal tests,
and clean-prefix shell, PowerShell 5.1/7 and npm installs using locally packaged candidate binaries.
`node --test npm/test-wrapper.cjs` needs no npm dependencies. Set `PROBBIT_BIN` to the release executable and run
`python3 -m unittest discover -s python -p 'test_*.py'` for the integration contracts.

## Ground rules

- **No external crates on the shipped path.** `cargo build` must keep working offline.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions PORTABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ No release exists yet, so every installer was tested against a local server that
| npm wrapper (`npm/`) | `ubuntu-latest`, `windows-latest`, local M4 | `npm pack`, `npm install -g` (scratch prefix), postinstall fetch and SHA-256 check, `which probbit`, exit codes 0 / 1 / 2 / 3 passed through, `npm uninstall -g`; an `--ignore-scripts` install fetches on first run; `PROBBIT_BINARY`; a wrong `.sha256` fails the install |
| `docs/agents.md` recipes | shell, Python (`python/test_probbit.py`), Node (`examples/node/decide.mjs`): Linux, macOS arm64 and x86_64, Windows (Git Bash); PowerShell 7.6: Windows | each recipe as written, plus one call per exit code |

Install locations: `install.sh` writes `/usr/local/bin` when it can (every runner image above: their user can write it)
and `~/.local/bin` otherwise (the local M4, where `/usr/local/bin` belongs to root), and never uses sudo; `install.ps1`
Historical install locations in the measurements above: `install.sh` wrote `/usr/local/bin` when writable,
and `~/.local/bin` otherwise. The current source defaults to `~/.local/bin` on every shell host; use
`PROBBIT_INSTALL_DIR` for an explicit system-wide destination. It never uses sudo. `install.ps1`
writes `$HOME\.local\bin`, adds it to the session's PATH, and to the user PATH only with `-AddToPath`. Names: `probbit` is
free on npm and `probbit`, `probbit-core`, `probbit-ir`, `probbit-decide` and `probbit-cli` are free on crates.io (checked 2026-10-01
22:31 PDT); nothing was published.
Expand All @@ -139,7 +140,8 @@ free on npm and `probbit`, `probbit-core`, `probbit-ir`, `probbit-decide` and `p
2. **The Linux release binary needs glibc 2.34.** `release.yml` builds `x86_64-unknown-linux-gnu` on `ubuntu-latest`.
The static musl build above runs on any x86_64 Linux and was within 4% (run 2) and 7% (run 1) of the glibc build on
the same VM, with 2.5-3.2 MiB less peak memory (BENCHMARK-MATRIX.md). Shipping it, plus `aarch64-unknown-linux-musl`,
would also give `install.sh` something for Alpine and arm64 Linux, which it refuses today.
now provides the released binaries for Alpine and arm64 Linux. The current shell/npm installers select these
targets automatically; the earlier refusal described in these historical measurements is superseded.
3. **The Windows binary needs `VCRUNTIME140.dll`.** Linking the CRT statically (`-C target-feature=+crt-static` for the
msvc release build) would remove that; not built or measured here.
4. **Two wall-clock test bounds failed on shared runners** in run 1: `run_deadline_ms_bounds_the_whole_call` on
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ were verified from clean machines on macOS (Apple silicon) and Linux (x86_64).
npm may print an `allow-scripts` warning: the package's only install script downloads the prebuilt binary for your platform
and checks its SHA-256. If npm blocks the script, the binary is fetched the first time you run `probbit` instead.

The shell installer defaults to `~/.local/bin`, without replacing a system-wide installation. Set
`PROBBIT_INSTALL_DIR` to choose another directory. Both the shell and npm installers select static musl binaries
for Linux ARM64 and for x86_64 musl systems such as Alpine. The shell and PowerShell installers check that a
downloaded candidate starts successfully before replacing an existing binary; failed checksums leave it unchanged.

From source, with Rust 1.78 or later: `cargo install --git https://github.com/BitmapAsset/probbit probbit-cli`, or clone and
`cargo build --release -p probbit-cli` (the binary lands in `target/release/probbit`). Nothing is downloaded after the clone:
there are no external crates.
Expand All @@ -128,7 +133,7 @@ At a terminal, `install.sh` ends with probbit's own hero screen. Platform notes

```
git clone https://github.com/BitmapAsset/probbit && cd probbit
cargo test --release --workspace # 127 tests (3 core, 48 CLI, 11 stress, 18 acceptance, 47 probbit-ir; 1 ignored), ~25 s once built
cargo test --release --workspace # unit, integration, regression and exact-oracle tests
cargo run --release -p probbit-cli -- demo --tasks 12 | cargo run --release -p probbit-cli -- decide --pretty
cargo run --release --example agent_router # the full narrated demo, ~3 s
```
Expand Down Expand Up @@ -437,11 +442,11 @@ to be measured per model (no such measurement has been made here).
### Test a character

<p align="center"><img src="docs/art/the-fuzz-finds-the-flaw.jpg" alt="a corridor of glass event cards; one card cracks cyan beside a rabbit mark; a replay trace runs under the floor" width="100%"></p>
<p align="center"><sub><i>The fuzz finds the flaw: the shortest event script that pushes an individual out of character, shrunk and replayable.</i></sub></p>
<p align="center"><sub><i>The fuzz finds the flaw: an event script that pushes an individual out of character, shrunk and replayable.</i></sub></p>


A character property is a rule in habit syntax the stance must never break. `fuzz` searches event scripts for each
individual's shortest counterexample; `prove` says `held by construction`, `proved for every event sequence` or `unknown`. The
individual's short counterexample (not a guaranteed global minimum); `prove` says `held by construction`, `proved for every event sequence` or `unknown`. The
tutor as it shipped in 0.5.0 is kept as a fixture, so this runs from a clone with no keys and no model:

```sh
Expand Down Expand Up @@ -529,6 +534,7 @@ anywhere ([docs/persona.md](docs/persona.md) §5.8). No strand yet? The tutor's

```sh
probbit monitor --demo --open
probbit monitor --demo drives --open # source checkout: synthetic goals/drive demo, not yet in the 0.8.0 release
```

![probbit monitor: the tutor's week in the browser, the bars moving with every event](docs/probbit-monitor.gif)
Expand Down
Loading
Loading