pub struct Journal { /* private fields */ }Expand description
Append-only event log with a project-authoritative path and a best-effort global mirror.
Implementations§
Source§impl Journal
impl Journal
Sourcepub fn new(
project_path: ProjectJournalPath,
global_path: GlobalJournalPath,
) -> Self
pub fn new( project_path: ProjectJournalPath, global_path: GlobalJournalPath, ) -> Self
Create a Journal. Paths are injected via newtypes (F9) to prevent silent
positional swap of the two same-type PathBuf arguments.
The runtime never hard-codes ~/.fno/. The driver CLI wires the real
paths (task 1.2). Tests use tempdir paths.
Sourcepub fn new_raw(project_path: PathBuf, global_path: PathBuf) -> Self
pub fn new_raw(project_path: PathBuf, global_path: PathBuf) -> Self
Convenience constructor accepting plain PathBufs directly. Intended for
tests where importing the newtype wrappers would add noise. Production
callers should prefer Journal::new (which enforces distinct types).
Sourcepub fn append(&self, event_type: &str, data: Value) -> Result<(), LoopError>
pub fn append(&self, event_type: &str, data: Value) -> Result<(), LoopError>
Append one event line to the project journal (fatal on failure) and mirror it to the global file (best-effort).
The envelope shape is:
{"ts":"YYYY-MM-DDTHH:MM:SSZ","type":"<kind>","source":"loop","data":{...}}
Method name is append (NOT emit/emit_fields) so the
daemon-stream parity scanner in lib.rs (which greps for .emit()
does not require these kinds to be registered in KNOWN_EVENT_KINDS.
These are loop-stream events, not daemon-stream events.
Sourcepub fn find_termination(
&self,
session_key: &str,
) -> Result<Option<Evidence>, LoopError>
pub fn find_termination( &self, session_key: &str, ) -> Result<Option<Evidence>, LoopError>
Scan journals for the LAST termination event matching session_key.
§Search order (ab-7303e5d7: cross-cwd delivery via global mirror)
- Scan the project journal first (authoritative for single-cwd target walks).
- When no match is found AND the global path differs from the project path,
scan the global journal (
~/.fno/events.jsonl).
Rationale: worker sessions dispatched by the megawalk walker run /target
in their OWN conductor worktrees, so their termination events land in the
WORKTREE’s events.jsonl. loopcheck’s emit_to_both also mirrors them to
~/.fno/events.jsonl (the global path). The walker’s project journal
lives at the walker’s cwd; only the global mirror is shared across cwds.
Returns None if not found in either journal or on read errors (fail
tolerant: unreadable journal = no pre-existing termination).
Uses BufReader + .lines() to stream files rather than loading them
entirely; the journal rotates at 8 MB but can grow to that before
rotation, so streaming avoids a large allocation on hot paths.
Unknown reason strings are treated as no-match (warn to stderr).
Unreadable lines (I/O errors from .lines()) are skipped silently.
Sourcepub fn find_termination_strict(
&self,
session_key: &str,
) -> Result<Option<Evidence>, LoopError>
pub fn find_termination_strict( &self, session_key: &str, ) -> Result<Option<Evidence>, LoopError>
Strict termination lookup for accounting decisions.
Unlike find_termination, an unreadable existing journal or matching
corrupt termination is an error, not evidence of absence. Missing files
remain a clean no-match. The retained .1 generation is searched after
the active file so completed sessions survive rotation.