Repository navigation
fix(openclaw): read a strand's tail past checkpoint lines; pause a sealed lane - #94
Draft
BitmapAsset wants to merge 6 commits into
Draft
BitmapAsset wants to merge 6 commits into
BitmapAsset wants to merge 6 commits into
Conversation
…vent-count cutoff
This branch has not been deployed
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.
After each event the OpenClaw plugin reads the strand's tail: for the event number (the last line) and, with
sensesModeshadow or feed, for thetemporeading (theelapsed_hoursof recent events). Four cases were wrong.In each one the engine's own record (the strand, its chain,
live verify) was right; only what the plugin showed orfed was wrong.
Stacked on #93 (
chore/drop-jev).--checkpoint-every K, default 1,000)probbit liveappends a checkpoint line,{"checkpoint":n,"prev":...,"stance":{...},"state":{...}}(docs/persona.md section5.7). The plugin accepted only an event line or the header as the last line, so on every 1,000th event it logged
no stance this turn (strand unreadable after the event)and injected no stance. A last line longer than theplugin's 64 KiB read (a checkpoint carries the whole state) was cut and taken for a header, so the turn reported
event 0. Now a checkpoint line gives its
checkpointnumber, and the read widens (largest window 16 MiB) until itholds the start of the last line. A torn last line, an empty file or a missing file still give no number.
filled it, and the next turn's
tempowas 1 (this event alone). The read is now repeated with a larger window (2,8, then 16 MiB) until it holds more than an hour of gaps or the whole file; the largest window is 16 MiB (with the
re-reads at most 26.5 MiB are read in one turn). There is no event-count cutoff: every event of the last hour that
is inside the window counts.
probbit live control STRAND sealcontinues a strand inNAME.0001.strand. The pluginkept reading the first file: before change 1 every later turn got no stance (or reported event 0 when the sealing
line was longer than the 64 KiB read); with change 1 alone every later turn
would have shown the sealing line's event number. The plugin does not follow segments in this version. If the
first segment file exists (named as the engine names it:
NAME.0001.strand, orPATH.0001for a path without the.strandsuffix), the lane is paused:once per lane:
no stance this turn (the strand was sealed into segments (NAME.0001.strand exists); this plugin version does not follow segments, so the lane is paused; warned once per lane);that turn gets no stance either: after a successful
probbit livethe plugin checks for the segment again (theengine appends to a segment only if it existed when it took its writer lock, and never removes one). Its event is
in the engine's record; the plugin writes no lane-log line, no
last/timestamp and no senses line for it, as onany failed turn. A seal that lands after that check leaves the turn its stance, with the right number;
dedupe), with that event's number;
before_tool_call) does not read the strand and keeps logging.The plugin README says so under "Logs and receipts".
n, so the consolidation line a rest pass writes ({"at","consolidation","n","prev","state"}) added one to thecount. Only event lines count now, by the engine's own rule (
strand::kind: a line with acontrol,checkpoint,rest,reflectionorconsolidationkey is not an event). The plugin README'stempoparagraphnow also describes the widening read and its 16 MiB limit.
Who was affected: every plugin lane on its 1,000th, 2,000th, ... event (the plugin passes no
--checkpoint-every, sothe default applies); lanes with a very large expectation memory (checkpoint lines over 64 KiB or 512 KiB); lanes
sealed by hand (the plugin never seals a lane itself); with
sensesModeshadow or feed, lanes whose strand has restpasses (change 4).
Tests (
plugins/openclaw/test/strand.test.ts, 12 new; plugin suite 153 -> 165):checkpoint longer than 64 KiB, a torn line, an empty file, no file;
first 512 KiB window holds: 3001; tempo over five events with a consolidation line (as the engine writes it), a
checkpoint and a control, rest and reflection line between them, each of those with an
n: 6;.strandsuffix;on which the engine appends its checkpoint) gets its stance and event 1001 works; a lane with three events, sealed
with the real binary, then two turns: no stance block on either, one warning, every file under the plugin's state
directory byte-identical before and after the two turns; a retry of the message answered before the seal gets the
identical block (event 3) and changes no file; a destructive tool call then changes only the gate log (one line);
probbit live verifypasses on the segment. The same test checks that a normal turn writes the strand, the state,last/, the lane log and the senses line.runLiveEvent's binary check (awaited after the first segmentcheck, before the engine call) runs the real
live control STRAND seal; the result is the sealed refusal, theengine's event 3 is the last line of segment 1, and the base ends on the sealing checkpoint (2).
put back, the tempo test gives 1834 instead of 3001; with the sealed-lane check removed, the paused turn gets a
stance labelled event 3 while the engine has written event 4 into the segment; with the second check removed, the
racing turn returns a stance with event number 2 for the event the engine wrote as 3; with the line-kind check
removed, the tempo test gives 10 instead of 6.
CHANGELOG "Unreleased / Fixed" has one entry per change. The plugin version stays 0.3.0 (not yet published). The test
count in docs/adapters.md is 165.
Validation on the host (macOS arm64), plugin tests run against a release build of
probbitfrom this stack, as CIdoes:
npm testat the branch head: 165 tests, 165/165 on five consecutive runs. Two tests with a 500 ms spawnbudget ("hook, a binary with --label", the safety "exit 2" test; both on
maintoo) failed now and then inearlier runs of this branch and pass on a rerun.
python3 python/test_probbit.py: 16/16 (at the first commit; later commits touch no Python).cargo test --offline -p probbit-cli: 305 passed, 0 failed, 3 ignored (at the first commit; later commits touchno Rust).
scripts/generate_llms_full.py --checkpasses.