Repository navigation
pail events v2: resume cursor, safety poll, sibling-prefix object events, Quint model - #50
Merged
Merged
Conversation
…nts, and a Quint model
A notification is a hint; truth is re-reading the key. The consumer is idempotent,
delivery is at-least-once, and a dropped notification costs latency, never correctness.
- `--cursor FILE|bucket:AREA/KEY`: on start, diff every watched key against the cursor
(deletions too), emit with source "resume", then stream. Per key the cursor records the
intent (dirty), the line is written, then the cursor takes the state and clears the mark:
a crash may repeat a line, never skip one. File cursors are written by temp file and
rename; bucket cursors by CAS (one consumer per cursor).
- `--safety-poll SECS` (default 60): with Pub/Sub, re-list everything every SECS.
- `--prefix AREA/PREFIX`: object events ({key, version, old, op, source, at}) for the
repo's sibling area <repo>.AREA/, same nudge-then-re-list rule and cursor.
- `events::Watcher` (observe, scan, mark, emit, save), `HintSource` (injectable hints).
- verify/events: model, simulation, witnesses, mutation script (6 switches), scenarios;
tests/events_refinement.rs refines the real Watcher against it (refs with a cursor file,
objects with a bucket cursor). The model found that a cursor written only after the line
loses a deletion (or a ref reset) after a crash: hence the write-ahead mark.
- pail-ci keeps a cursor; docs and CLI help updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Replaces #48 (replayed onto main after #47; conflicts in package.json, README, model.yml resolved by keeping both).
Merge order: independent of #47. The Actions wake-up model that builds on this contract is a separate PR stacked on #47 (it merges after both). Expect a trivial conflict in
verify/package.json,verify/README.mdandmodel.ymlbetween this and #47: both add scripts and rows next to each other.What
pail eventsv2. Design: a bucket notification is only a hint; truth is re-reading the key. Consumers are idempotent, delivery is at-least-once, a dropped notification costs latency, never correctness.--cursor FILE | bucket:AREA/KEY: on start, diff every watched key against the saved heads (deletions too), emit with"source":"resume", then stream. The cursor moves only after the line is out. File cursor: temp file, fsync, rename. Bucket cursor: CAS, and a second consumer on it fails loudly.--safety-poll SECS(default 60,0off): with Pub/Sub, also re-list everything. Polling already re-lists every--interval-ms.--prefix AREA/PREFIX: object events{key, version, old, op: put|delete, source, at}for the sibling area<repo>.AREA/(--prefix actions/=<repo>.actions/). Same nudge-then-re-list rule, same cursor. Pub/Sub messages for the area are hints too. The cursor object in the area is never an event.verify/events/(in a subdirectory: the digest of the other generated artifacts is untouched) and a refinement of the realevents::Watcheragainst it.pail-cikeeps a cursor (default per store under$XDG_STATE_HOME), so a restart builds what landed while it was down. Docs,--help, README updated.Model and refinement
noLoss(up, quiescent, one scan later: emitted state == store),cursorBehindOut(never skip),intentRecorded,noRedundant; in the simnoPhantom,perKeyOrder,cursorMonotone.tests/events_refinement.rs, 8 scenarios x 2 modes): the harness holds the notifications and drops the watcher to crash it, and drives the realWatcher::{open, observe, scan, mark, emit, save}overfile://. Compared after every step: the store, the cursor read back from its file or bucket object, the watcher's beliefs, the lines written. Refs with a cursor file, and sibling-area objects with a bucket cursor.events.rs(no mark, dirty not unsure, cursor before line, no dedupe, Unsure == anything, no deletions in scan, mark not cleared, memory not updated) each fail a scenario.What the model found
pail eventswas two of the model's mutants: a restart seeded its memory from the store (events during downtime lost) and with Pub/Sub nothing re-listed (a lost notification lost the event).Verification
cargo fmt --check,cargo test --release(all): pass. (cargo clippyis not in CI; the repo has pre-existing warnings, none in the new code.)verify:npm run check,npm test,witness:events,simulate:events(100k runs),mutate:events: pass. All six switches are caught (table inverify/README.md)..github/workflows/model.yml(manual), notci.yml.Source::Hints) in the loop tests, and stores arefile://. No cloud resources were created.Flags
--prefix,--cursor,--safety-poll.--cursorconflicts with--initial. Without--cursorbehaviour is as before (a restart takes the present state as seen).🤖 Generated with Claude Code