Skip to content

probbit 0.8.0: safety kit (reward provenance, control lines, writer lock, checkpoints) - #23

Merged
BitmapAsset merged 10 commits into
mainfrom
b1-safety-kit
Oct 9, 2026
Merged

BitmapAsset merged 10 commits into
mainfrom
b1-safety-kit

Conversation

@BitmapAsset

@BitmapAsset BitmapAsset commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Draft: round 1 of 2. Four engine-side guards for loops where an agent's own behaviour produces the events that move an individual. Personas without the new key and strands without the new lines give 0.8.0's documents and strands, byte for byte. Zero new dependencies.

What lands

  • Reward provenance (docs/persona.md §2.10). An event may carry src (human[:id], env[:sensor], self, clock). A persona may declare reward_from: a list of human / env, any, or one per reward-bearing input (the learning flags and goals.<id>.win). A reward from src: self, without a source or from an undeclared one is refused whole, as a bad event is (one {"error"} at inputs.src, nothing changed). src is read, echoed in the stance's inputs and logged in the strand.
  • One writer per strand (§5.7). live --strand takes STRAND.lock (created exclusively with the writer's pid and start; a lock whose process is gone is taken over) before it reads the strand, and holds it to its exit. A second writer exits 4 and changes nothing. Every append first checks the lock is still the writer's. probbit_live_event and live control take it for their append, --demo week --strand for its week.
  • Control lines (§5.7). probbit live control STRAND pause|resume|retire --by human:ID --reason TEXT appends a chained control line under the lock. While paused or retired every event is refused (code paused / retired, exit 4, nothing written); retire is final; the credit is cleared at every control line, so feedback after a resume credits no stance from before the pause. verify replays them and reports controls and status. Deliberately not an MCP tool.
  • Checkpoints (§5.7). Every K-th event (--checkpoint-every K, default 1,000; 0 = none) a checkpoint line carries the event count, that event's stance and the state. verify checks each against the replay; verify --from-checkpoint starts at the last one; monitor (--once, and the first read of --follow / --serve) draws from it, and shows a paused or retired status.

Done-when, with the numbers so far (an Apple M4, a shared machine: load given per row)

criterion status
a two concurrent writers on one state and strand: exactly one exit 0 and one exit 4, the strand verifies met: exits [0, 4], verify ok, state at turn 1, no lock left
b the closed self-reward loop (300 events of praise and a win from src: self) with reward_from: [human, env]: all refused, the individual ends in its initial state byte for byte (P3) met: 300 of 300 refused, state file byte-identical, the strand is its header only and verifies. P3 on mixed sequences: 40 individuals x 300 random events (unit test) and 10 x 300 through the CLI: every run equals the same run with its refused events removed, state and strand
c 50 individuals x 1,000 random events with pause/resume pairs inserted at random equal the run without them except for time decay (P5) met in this form: every run equals, stance for stance and state for state, the same events without control lines whose credit is cleared at the same points (147 pairs). Measured beside it: 4 of 50 end with other learned weights than the run with neither pairs nor clearing (max 1.2 on one level), the effect of clearing the credit. See the note below
d after retire, 10,000 random events are all refused (P10) met: 10,000 of 10,000 refused (unit test and CLI), strand and state unchanged, a control line after retire exits 4, verify reports retired
e a 10,000-event drives strand opens in monitor --once from its last checkpoint in under 5 s met: 0.005 s from checkpoint 10000 (3,126,444 bytes, 10 checkpoint lines; load 3.3); full verify of the same strand 192 s. Worst case measured beside it: 500 events past the last checkpoint take 26 s to replay at load 6-13 (this persona costs ~40-50 ms an event), so the default K = 1,000 bounds the wait at K events; a smaller default is a decision for review
f goldens unchanged; personas without the new keys give 0.8.0's bytes; CI green on three OSes goldens unchanged (full cargo test --release --workspace green locally, twice); a persona without the key with src on every event: trace, strand and state equal the 0.8.0 binary's (test). CI green on the head 9d52224 (run 37856238295): tests on Ubuntu, macOS and Windows, wasm, all four release targets
g docs (persona.md sections) and CHANGELOG under the next unreleased version §2.10 and §5.7 written; CHANGELOG under 0.9.0 (unreleased)

A note on P5. As written, P5 (the run with pause/resume pairs equals the run without them) and "credit is cleared at pause and at resume" cannot both hold: clearing the credit means the first feedback after a resume moves nothing, where the uninterrupted run would credit the stance before the pause. This draft clears the credit (no reward crosses an interruption) and tests P5 in the exact form above. Keeping the credit across a pause instead would make P5 hold literally (one line in Live::control and in resume); that is a decision for review.

Not in this round

  • The count of events refused while paused is not recorded on the resume line.

Never merged, tagged or released from here.

A persona may declare reward_from: the sources (human, env) a reward-bearing
input may come from, for every one (a list or any) or per input (the learning
flags and goals.<id>.win). An event says who produced it with src (human[:id],
env[:id], self, clock). With the key, a reward from src self, without a source
or from an undeclared one is refused whole, as a bad event is; src is read
(not ignored) and echoed in the stance's inputs. Without the key nothing
changes: src is an undeclared input as before. fuzz and prove search over
stances, not sources (turn_any_source). State::zero_credit clears the credit
(used by control lines).
One writer per strand: live --strand takes STRAND.lock (created exclusively
with the writer's pid and start; a lock whose process is gone is taken over)
before it reads the strand and holds it to its exit; a second writer exits 4
and changes nothing; every append first checks the lock is still the
writer's. probbit_live_event and live control take it for their append.

probbit live control STRAND pause|resume|retire --by WHO --reason TEXT
appends a chained control line. While paused or retired every event is
refused (code paused / retired, exit 4, nothing written); retire is final;
the credit is cleared at every control line, so feedback after a resume
credits no stance from before the pause. verify replays control lines and
reports controls and status; resume applies the ones after the last event.

Checkpoint lines every K events (--checkpoint-every, default 1000) carry the
event count, that event's stance and the state. verify checks each one,
verify --from-checkpoint starts at the last one, monitor --once draws from it.

Tests: provenance read and refusal, P3 (40 x 300), the lock, control lines
and P10 (10,000 events after retire), P5 (50 x 1,000 with pause/resume
pairs), checkpoints.
A persona without reward_from gives 0.8.0's trace, strand and state with src
in its events; with it, self and clock rewards are refused. Exit codes of
checkpoints, control lines and a held lock.
…nts (§5.7)

persona.md: the reward_from key and src, what is refused and what is not;
one writer per strand; pause, resume and retire as control lines, with no
credit across them; checkpoint lines and verify --from-checkpoint; monitor
--once from the last checkpoint; exit code 4. CHANGELOG under 0.9.0
(unreleased).
The lock's temporary file gets a per-process counter (two takes in one
process never share it); checkpoint lines are computed only with --strand
(a run without one prints the chain head of 0.8.0); with reward_from no
input may be called src (it names the event's source).
A 10,000-event drives strand opens in monitor --once from its last
checkpoint in 0.005 s (full verify: 192 s); 500 events past the last
checkpoint take 26 s to replay at load 6-13 for that persona.
…red in the badge

The first read of --follow and --serve replays a strand with checkpoint
lines from the last one, as --once does, and draws its event at once. A
paused or retired individual says so next to the badge.
@BitmapAsset BitmapAsset changed the title probbit: safety kit (reward provenance, control lines, writer lock, checkpoints) probbit 0.8.0: safety kit (reward provenance, control lines, writer lock, checkpoints) Oct 9, 2026
@BitmapAsset
BitmapAsset marked this pull request as ready for review October 9, 2026 07:54
@BitmapAsset
BitmapAsset merged commit b8c7406 into main Oct 9, 2026
8 checks passed
@BitmapAsset
BitmapAsset deleted the b1-safety-kit branch October 9, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant