Repository navigation
Version 0.9.0-dev; verify reports unsupported fields; host contract paragraphs - #38
Merged
Merged
Conversation
…nds (exit 5), not divergences WIP: the engine change, its unit and live tests, the committed fixtures (a 0.9.0 strand recorded by this build, the released 0.8.2 binary's output on it, and the two derived strands one version up), the regenerated manual page.
…paragraphs; Python and MCP tests docs/persona.md 5.7 (Unsupported is not diverges; verify exit 5) and 7 (one outcome per stance, host event ids and duplicate delivery, restart time; the verify row, MCP and Python bullets), docs/CHEATSHEET.md (exit codes, the host contract lines), docs/agents.md (the MCP tool row), CHANGELOG.md (0.9.0-dev, unsupported, the host paragraphs). python/test_persona.py and python/test_mcp.py check the unsupported document and the tamper verdict; the manual page is regenerated.
BitmapAsset
marked this pull request as ready for review
October 17, 2026 06:58
Merged
BitmapAsset
added a commit
that referenced
this pull request
Oct 17, 2026
…ows needs, #40 knows state.pace) R117 semantic-conflict fix. #38's tests use RFC 0002's needs block (next-needs.strand; the unit test's header case) and a state.pace field as what this engine does not know. Since #39 the engine knows needs (the strand's header is refused at line 1: a needs block needs a drives block) and since #40 a pace state field (this persona has no pace block): divergences, not unsupported. next-load.strand is derived the same way (R111's make_next_fixtures.py helpers) with the load block of docs/rfcs/0002 Part 2, which no engine has yet; the unit test uses a load block and state.load; docs/persona.md 5.7 names load. Tests, fixtures and one docs sentence only; no engine code, no bar.
BitmapAsset
added a commit
that referenced
this pull request
Oct 17, 2026
* WIP RFC 0002 wave 1a: needs step, omission windows, valence and liking in the engine (compiles; no tests yet) * WIP RFC 0002 wave 1a: surface (describe, lint notes, fuzz amounts and windows, prove boxes, verify needs summary, monitor, MCP refusal) and unit tests * WIP RFC 0002 wave 1a: body example and golden, live and MCP tests, docs (persona.md 2.9/2.12, cheatsheet, man page), CHANGELOG, RFC changes after the freeze, BENCHMARKS 12, bench n7 MCP shape fix * WIP RFC 0002 wave 1a: privacy and law gate fixes * WIP RFC 0002 wave 1a: strict-field test string, monitor needs rows test * Merge branch 'main' into rfc2-wave1a-needs * tests: the newer-engine fixtures carry RFC 0002 part 2's load (#39 knows needs, #40 knows state.pace) R117 semantic-conflict fix. #38's tests use RFC 0002's needs block (next-needs.strand; the unit test's header case) and a state.pace field as what this engine does not know. Since #39 the engine knows needs (the strand's header is refused at line 1: a needs block needs a drives block) and since #40 a pace state field (this persona has no pace block): divergences, not unsupported. next-load.strand is derived the same way (R111's make_next_fixtures.py helpers) with the load block of docs/rfcs/0002 Part 2, which no engine has yet; the unit test uses a load block and state.load; docs/persona.md 5.7 names load. Tests, fixtures and one docs sentence only; no engine code, no bar. * Version 0.10.0-dev on main; the next-* fixtures re-derived at probbit 0.11.0-dev 0.9.0 is released, so main moves to 0.10.0-dev: Cargo.toml, the internal path dependencies, Cargo.lock (cargo update --workspace --offline) and docs/probbit.1 (regenerated with probbit man); one CHANGELOG line under 0.10.0. The derived newer-engine strands stay one version up: next-load.strand and next-rest.strand re-derived at probbit 0.11.0-dev with the same derivation (it reproduces the 0.10.0-dev files byte for byte), and the five test strings in tests/live.rs and the two Python tests that pin their engine follow. No engine code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two contract fixes and three host-contract paragraphs. No other engine change.
1. The version string names the capability set
0.9.0-dev(Cargo.toml), the path dependencies' requirements0.9.0-dev,Cargo.lockupdated by cargo(lockfile format unchanged).
probbit --versionprintsprobbit 0.9.0-dev; the strand header'sengine, MCPserverInfo,run'sengineand the manual page (docs/probbit.1, regenerated) followCARGO_PKG_VERSION.CHANGELOG.md,## 0.9.0 - unreleased: unreleased builds print0.9.0-dev, so a strand header tells them from 0.8.2.npm/package.jsonstays0.8.2: it does not mirror the build.npm/install.jsdownloads the release taggedv<package version>, and CI packages the test binary under that tag; a-devversion there would point the wrapper at arelease that does not exist. The release wave sets it to
0.9.0with the tag, as every release so far did. The installers'v0.8.2examples andscripts/tests/test_install_contract.py(a test of the released 0.8.2 tag) are unchanged.0.8.2as this build's version (the version tests readCARGO_PKG_VERSION).2. "Unsupported" is not "diverges"
live verify(CLI, Pythonlive_verify, MCPprobbit_live_verify) used to map a field this engine does not know to thetamper verdict. It now reports the earliest line with a header key, a persona block or field, a state field, a line kind or a
line field this engine does not know as unsupported:
{"engine":"probbit 0.9.0-dev","line":1,"ok":false,"strand_engine":"probbit 0.10.0-dev","unsupported":"needs"}; stderrprobbit: live verify: line 1: unsupported by this engine: needs (written by probbit 0.10.0-dev; this engine is probbit 0.9.0-dev): not verified, and not a divergence. The same from the last checkpoint and with--actions.probbit_live_verify: the same document asstructuredContent(the text is the CLI's stdout),isError: false: ananswer, as a divergence is.
live_verify: returns the same document (exit 5 is an answer, as exit 1 is), documented in the module header.needs,learning.credit),state.<field>in a state, and by its pathin the line otherwise (
rest,inputs.senses.tool_fail.z).Exit code 5: unused by every command today (0 ok, 1 differs, 2 bad input or flag, 3 refused / declined / unproven, 4 lock /
paused / retired), so it collides with nothing; documented in
live verify --help,probbit --helpand the manual page's EXITSTATUS,
docs/persona.md§5.7 and §7,docs/CHEATSHEET.md(theliveexit-code line, which now also lists 4) anddocs/agents.md(the MCP tool table). The zsh and fish completions carry the help's descriptions from the same command table, so the verify entry names exit 5 there too.A tampered strand still diverges at its line, exit 1: a changed input, a removed or reordered line, a changed header value of
a known field. The hash chain is also checked past an unsupported line, so an edit that breaks the chain after it (or edits the
unsupported line itself) diverges at the earliest line whose
prevshows it;unsupportedis left only for a strand whose chainis whole. An edit that adds an unknown field and rebuilds the chain after it reads as unsupported, and exit 5 is not a pass.
probbit checkkeeps its gate codes: an unsupported strand fails the gate (exit 1, documented), its line readsline K: unsupported by this engine: FIELD (written by ENGINE)(never FAIL), and its--jsonentry addsunsupportedandstrand_engine.live --strandandlive controlname the field when they refuse to continue such a strand (still exit 2).Tests: unit (
probbit-cli/src/live.rs: every kind of unknown field at line 1 and at a later line, the MCP document, tamperingaround an unsupported line), live (
probbit-cli/tests/live.rs: the fixtures below through the CLI, from the header, from thelast checkpoint and with
--actions; tampered copies;probbit checktext and--json), Python (python/test_persona.py) andthe MCP JSON shape (
python/test_mcp.py, which also checksserverInfo.versionagainstprobbit version).Fixtures (
probbit-cli/tests/fixtures/strands/):sensed-credit-0.9.0-dev.strand: recorded by this build fromsensed-credit.json(a senses block and alearning.creditblock) and
sensed-credit.events.jsonl, 5 events, a checkpoint at line 6. It verifies here, exit 0.sensed-credit-0.9.0-dev.verify-0.8.2.txt: the released 0.8.2 binary (sha2562c3455ef..., the binarybench/individuality/fixtures/MANIFESTnames) run on it:{"diverges":"the persona document: senses: unknown field (allowed: ...)","line":1,"ok":false}, exit 1. With both blocks 0.8.2 namessenses; with a credit block alone it nameslearning.credit(checked on the same binary). 0.8.2 cannot be changed; this records what it says.probbit 0.10.0-dev, every later line re-chained:next-needs.strand(an RFC 0002needsblock in the header's personadocument, its digest recomputed) -> unsupported
needsat line 1;next-rest.strand(a{"prev":...,"rest":{"rho":0.5}}line appended) -> unsupported
restat line 8. Exit 5 for both, neverdiverges.3. Host-contract paragraphs (
docs/persona.md§7, one CHEATSHEET line each)the previous stance", B-R2). Open one stance per action (
await: trueper tool call).idkey is an undeclared input); a duplicateobservation event is appended and counted twice. The host keeps an idempotency key per event (its event id -> the stance's
turnand the new state'sdigest) and does not resend.--clock realstarts with the process and refuses an event's ownelapsed_hours, so downtime is notcounted; a host that needs it runs
--clock fixedand stamps the hours (MCP and Python'slive_eventalways do).Each claim was checked against the source and a run of this build (the refusal text,
ignored: ["id"], a duplicate readingmoving the normal twice, the real clock's refusal).
P0
PROBBIT_BIN=$PWD/target/release/probbit python3 bench/individuality/p0.py check: 653 outputs checked, 0 differ (629byte-identical, 24 identical once the one stderr line naming a
senseskey is removed, as on main), 8 recorded strandsverify, the tutor's continues with the recorded bytes. The version string enters no P0 document:
p0.pymasks the strandheader's
engine(and theprevhashes chained from it), and no other P0 output prints the version. No expected difference.Gates
All on head
d64502e.cargo +1.78.0 build --release --locked --workspace(what ci.yml's msrv job runs; it covers-p probbit-cli): ok.cargo +stable test --release --workspace --locked --no-fail-fast, locally: 32 test binaries, 364 passed, 1 failed,1 ignored. The failure is
probbit-decide/tests/acceptance.rssample_on_budget_is_a_deadline_at_many_chains, a wall-clockassertion (381.7 ms for a 60 ms budget, limit 180) on a machine at load average 14-30;
acc1_tv_le_001_within_10ms(p50within 10 ms) failed the same way in an earlier run. This PR changes no source in probbit-decide, probbit-core, probbit-ir or
probbit-wasm (only the version requirement lines), and CI's
cargo test --release --workspacepassed on ubuntu, macOS andWindows.
python -m unittest discover -s python -p 'test_*.py': 48 tests OK;node examples/node/decide.mjs: ok.sh playground/build.sh,node playground/check.mjs playground/probbit.wasm(decide 300 tasks andevaluate: 0 violations),
node probbit-wasm/tests/boundaries.mjs(34 calls, 11 native parity cases): passed. The browserchecks ran in CI's wasm job.
body: 0 BANNED+INTERNAL, 0 law-word hits; 2 pre-existing (two code lines that read the header, moved into closures, byte for
byte on main). Privacy grep: 0 hits.
cirun 38840524416: attempt 1 failed one step,test (macos-latest)"demo pipeline":probbit demo --tasks 300 | probbit decide --budget-ms 300answeredrefused(exit 3) on a 3-thread runner, one chain at 452 rows against about 3,000for the others within the 300 ms budget (
decideis not touched here; that job'scargo teststep had passed). The failedjob was re-run: attempt 2 success, every job green.
checkrun 38840524428: success (dogfood and install-gate, Linux andmacOS).
Not changed
bench/individualityandbench/body: untouched.probbit monitorreplays with the same rules, so an unsupported strand stops it at the same line with theunsupported by this engine: FIELDreason, but its badge and page still say "diverges" (exit 1): a follow-up.action.ymlprints a non-ok strand asFAIL <error>; for an unsupported strand the error readsline K: unsupported by this engine: FIELD. The action's exit handling is unchanged (check's codes are unchanged).strand still diverges at the earliest line where it differs; the header's
enginenames the engine to verify it with(documented in §5.7).
npm/package.json.