openloops.store#
Where digests live, and where the change-detection cache lives — two different things.
The digest store is data: a local directory of markdown files under
~/.local/share/openloops/digests/, addressed as a MutableMapping[str, str] so
that swapping it for a git-synced directory or an S3-backed store is one keyword
argument rather than a rewrite. Nothing in openloops opens a file by path.
The sync cache is not data: it is a per-machine record of which transcript revisions have already been read, and deleting it is a distinct operation from deleting digests. That separation is ADR-010’s rule — a cache purge must not be able to reach the thing being cached — and it is what makes the regeneration test meaningful: wipe both, sync again, and every digest comes back byte-identical.
Keys are {source}/{state}/{session}.md. The source segment is the machine (or
person) that wrote the digest, so two machines syncing into one git repository never
write the same path and there is nothing to reconcile. The state segment is open or
archive — loop state, not process state. If it ever came to mean “a process is
running”, this would be a session dashboard rather than a record of what sessions said.
>>> digest_key('laptop', 'open', 'abc-123')
'laptop/open/abc-123.md'
>>> parse_digest_key('laptop/open/abc-123.md')
('laptop', 'open', 'abc-123')
Functions
|
The project's data root. |
Where per-machine caches and job logs go. |
|
|
This machine's label for its digest folder. |
|
The default |
|
The store key for one session's digest. |
|
Split a store key back into |
|
Session id → the transcript revision last read. |
|
Write the mtime cache, creating its directory. |
|
Where the mtime cache lives. |
Classes
|
|
- class openloops.store.Utf8TextFiles(*args, delete_func=None, **kwargs)[source]#
Bases:
PrefixRelativizationMixin,Storedol’s text-file store with the encoding pinned rather than inherited.dolopens text withencoding=None, i.e. whatever the locale says. Every digest contains an em dash, so on a machine where the locale resolves to ASCII — a container or a cron job withLANGunset, an Alpine image — writing one raises, and in a store two machines share it is worse: one writes bytes the other cannot read back. Pinning UTF-8 is the whole fix.- is_valid_key(k, *args, __name='is_valid_key', **kwargs)#
is_valid_keyon the inner key – seemk_relative_path_store.
- validate_key(k, *args, __name='validate_key', **kwargs)#
validate_keyon the inner key – seemk_relative_path_store.
- openloops.store.data_dir()[source]#
The project’s data root. Override with
OPENLOOPS_DATA_DIR.Per-kind subdirectories hang off this; nothing is written directly into it, so a later kind of data needs no second migration.
- Return type:
- openloops.store.default_source(directory=None)[source]#
This machine’s label for its digest folder. Override with
OPENLOOPS_SOURCE.A short, filename-safe name. It exists so several machines can sync digests into one place without colliding — not to identify anybody, though on a machine whose hostname was never changed it may well do; every
ol syncprints it, andOPENLOOPS_SOURCEoverrides it.It is sticky. The hostname is a seed, written once to a file under the state directory and read thereafter. macOS rewrites the hostname when it joins a network where the name collides — appending
-2,-3— and a label that moved would fork the store into two complete copies with no dedup and no warning.- Return type:
- openloops.store.digest_key(source, state, session_key)[source]#
The store key for one session’s digest.
Both segments are validated, not just the state.
transcript_source=is a seam whose documented purpose is “another machine’s synced transcripts”, so a session id can come from a listing this process did not produce — and a key containing..would make a file-backed store write outside its own root whilesyncreported success. Validating here means every backend inherits the check.- Return type:
>>> digest_key('mac', 'archive', 's1') 'mac/archive/s1.md' >>> digest_key('mac', 'open', '../../etc/passwd') Traceback (most recent call last): ... ValueError: session key must be a single safe path segment, got '../../etc/passwd'
- openloops.store.digests_store(rootdir=None)[source]#
The default
digests_store: markdown files under the data root.Any
MutableMapping[str, str]works in its place — a plaindictfor tests, a git-synced directory, an S3-backed store. The keys are what carry the layout, so a different backend gets the same{source}/{state}/{session}.mdstructure for free.- Return type:
>>> store = digests_store(rootdir='/tmp/openloops-doctest-store') >>> store['demo/open/s1.md'] = '# hi' >>> sorted(store) ['demo/open/s1.md'] >>> del store['demo/open/s1.md']
- openloops.store.load_sync_state(directory=None)[source]#
Session id → the transcript revision last read. Missing or corrupt reads empty.
A corrupt cache is treated as an absent one on purpose: the only cost of being wrong here is re-deriving digests that were already correct.
- openloops.store.parse_digest_key(key)[source]#
Split a store key back into
(source, state, session_key).Both separators are accepted. A file-backed store hands keys back joined with the platform’s separator, so on Windows every key would fail to parse and every read path would quietly return nothing while
syncreported digests written.>>> parse_digest_key('mac/archive/s1.md') ('mac', 'archive', 's1') >>> parse_digest_key('mac' + chr(92) + 'archive' + chr(92) + 's1.md') ('mac', 'archive', 's1')
- openloops.store.save_sync_state(state, directory=None)[source]#
Write the mtime cache, creating its directory. Returns the path written.
- Return type:
- openloops.store.state_dir()[source]#
Where per-machine caches and job logs go. Override with
OPENLOOPS_STATE_DIR.Deliberately not under
data_dir(): everything here is disposable, and keeping it elsewhere means “clear the cache” can never be mistyped into “delete the digests”.- Return type: