Ghostlight/docs/architecture/persistence-spine.md
Meta Vens 22937b71f0 Replace vendor/cultcache-py submodule with published PyPI dependency
CultLib's packages/cultcache-py is now published as cultcache-py==0.2.0 on
PyPI (MIT), so the standalone GameCult/cultcache-py fork that Ghostlight
vendored as a submodule is retired. Symbol-for-symbol and on-disk-format
verification against the pinned submodule commit 9b4bbb8 found no
incompatibility: every symbol tools/ghostlight_state_store.py imports
(CultCache, JsonLinesBackingStore, define_document_type) and every method it
calls kept its signature, and JsonLinesBackingStore's on-disk JSONL envelope
fields and sort order are unchanged, confirmed by a real round-trip against
Ghostlight's production state/ghostlight-state.cultcache.jsonl.

tools/ghostlight_state_store.py no longer pushes vendor/cultcache-py/src onto
sys.path; it imports cultcache_py directly. Since Ghostlight's session
bootstrap previously depended on the submodule needing no install step,
tools/requirements.txt now pins cultcache-py==0.2.0 and npm run
state:bootstrap (pip install -r tools/requirements.txt, idempotent) runs as a
pre-hook ahead of state:status and state:prepare-compaction; AGENTS.md's
Session Bootstrap protocol and Useful Commands section call it out explicitly
for the direct ghostlight_state.py CLI invocations that don't go through npm.

Verified for real: `python tools/ghostlight_state.py status` and `python
tools/ghostlight_prepare_compaction.py`, run from a venv with cultcache-py
installed from PyPI, against Ghostlight's actual production state file (22 ok
findings, 2 pre-existing unrelated warnings, exit 0 both times).

Idunn's deployment/idunn/recipe.toml never declared vendor/cultcache-py in
required_gitlinks (only vendor/eve) and its build/test/service steps never
touch Python state tooling, so no recipe change is needed for admission.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 16:08:19 +02:00

2.6 KiB

Persistence Spine

Ghostlight's machine-managed state is moving behind a small CultCache-style persistence layer instead of letting every tool paw raw files directly.

The Python port is cultcache-py, published by CultLib to PyPI and pinned in tools/requirements.txt. It used to be vendored as the vendor/cultcache-py git submodule against the now-retired standalone GameCult/cultcache-py repository; PyPI distribution replaced that fork.

Current Store

  • Cache store: state/ghostlight-state.cultcache.jsonl
  • Ghostlight seam: tools/ghostlight_state_store.py
  • Library dependency: pinned cultcache-py==0.2.0, declared in tools/requirements.txt and installed by npm run state:bootstrap (an idempotent pip install -r tools/requirements.txt, wired as a pre* hook ahead of state:status and state:prepare-compaction)
  • Readable exports: state/branches.json, state/evidence.jsonl

The JSONL store is deliberate bootstrap plumbing. The package also exposes a MessagePack store for environments that install the optional msgpack dependency, but Ghostlight's state CLI must run on the bundled Python runtime without global package assumptions.

For cross-runtime DatabaseEntry payloads, use cultcache-py's define_database_entry_type(...) formatter. It writes MessagePack arrays by explicit slot key, preserves nil gaps for deleted/reserved fields, and uses defaults for missing older slots. That is the wire-compatibility contract shared with cultcache-rs and the updated CultLib MessagePack generator.

Live Rule

New machine-managed state should go through registered document definitions in tools/ghostlight_state_store.py or a successor module. Direct writes to state/branches.json and state/evidence.jsonl are compatibility debt.

Human-readable exports remain useful because git diffs, handoff review, and emergency recovery should not require decoding a cache file while the house is on fire.

Migrated Documents

  • ghostlight.branches.v0
    • global document
    • exported to state/branches.json
  • ghostlight.evidence_record.v0
    • one record per evidence entry
    • indexed by type, status, and branch
    • exported to state/evidence.jsonl

Expansion Path

Move compact control-plane state next:

  • corpus coverage ledger metadata
  • active fixture run manifests
  • persistent reviewer receipt indexes
  • compaction scratch registration
  • branch and initiative run ledgers

Keep large corpora, generated artifacts, images, and full story payloads outside the single state store unless they are represented by compact refs. A cache file is not a warehouse just because it has pockets.