openloops.base#
Data structures shared across openloops: what a session is, and what a digest is.
Nothing here does I/O and nothing here makes a judgement. openloops.transcripts
reads sessions, openloops._classify judges them, openloops.digest renders
them, and all three speak in the types defined here.
The types are deliberately flat and JSON-shaped. A session read from Claude Code’s
on-disk state and a session supplied by some other reader are the same object to the
rest of the package — that is what makes transcript_source= a one-argument swap.
Module Attributes
the session's own last word left something for the human. |
|
the session's own last word closed out. |
|
The two loop states, in the order they are shown. |
Classes
|
One rendered digest: its store key and its markdown text. |
|
A typed, human-readable pointer to something outside the digest. |
|
What one Claude Code session's persisted state says, parsed but not judged. |
|
A loop-state judgement, with the rule that produced it and the cues it saw. |
- openloops.base.ARCHIVE = 'archive'#
the session’s own last word closed out.
- Type:
Loop state
- class openloops.base.Digest(key, text, session_key, source, state, verdict=<factory>)[source]#
Bases:
objectOne rendered digest: its store key and its markdown text.
textis a pure function of theSessionit was built from. It carries no generation timestamp, which is what lets the regeneration test intests/test_sync.pycompare bytes rather than fields.
- class openloops.base.Locator(type, url='', text='', at='')[source]#
Bases:
objectA typed, human-readable pointer to something outside the digest.
The exportable half of ADR-015’s evidence split: a short typed reference that another tool (or a person) can resolve, never a content hash and never a byte offset into a file that is garbage-collected after thirty days.
>>> Locator("pr", url="https://github.com/o/r/pull/1").as_dict()["type"] 'pr'
- openloops.base.OPEN = 'open'#
the session’s own last word left something for the human.
- Type:
Loop state
- openloops.base.STATES = ('open', 'archive')#
The two loop states, in the order they are shown.
- class openloops.base.Session(key, title='', ai_title='', cwd='', project='', git_branches=(), started_at='', ended_at='', last_turn_at='', last_user_prompt='', last_prompt_at='', last_assistant_text='', recap='', recap_at='', compaction='', compaction_at='', turn_count=0, model='', ended_mid_turn=False, ended_with_error=False, locators=())[source]#
Bases:
objectWhat one Claude Code session’s persisted state says, parsed but not judged.
Every field is a fact read from the transcript, never an inference about the world.
last_assistant_textis what the session said last; it is not a claim that the thing it describes is still true.keyis the session id. It is the only identifier openloops uses, and per ADR-014 nothing in the model hangs off it: a digest is a view keyed by session, not a record owned by one.- compaction: str = ''#
The context-compaction summary, a different and much longer thing than the recap, covering only the part of the session that preceded it.
- ended_mid_turn: bool = False#
The transcript’s final conversational record is an unanswered human prompt, or an assistant tool call whose result never arrived — i.e. the session stopped mid-turn.
- ended_with_error: bool = False#
The last thing the session said is a usage-limit or API-error banner rather than the assistant’s own words: it was cut off, not finished.
- class openloops.base.Verdict(state, reason, cues=(), at='', confidence='high')[source]#
Bases:
objectA loop-state judgement, with the rule that produced it and the cues it saw.
reasonandcuesexist so a reader can disagree. A classification whose grounds are not shown is an assertion, and openloops does not make assertions about sessions — it reports what they said and why it read them that way.