From 22937b71f056d2000dd7ea1f6af0c45825de495f Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 13 Sep 2026 15:59:07 +0200 Subject: [PATCH] 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 --- .gitmodules | 3 --- AGENTS.md | 13 +++++++++++-- docs/architecture/persistence-spine.md | 11 ++++++++--- package.json | 3 +++ state/map.yaml | 2 +- tools/ghostlight_state_store.py | 18 +++++++++++------- tools/requirements.txt | 1 + vendor/cultcache-py | 1 - 8 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 tools/requirements.txt delete mode 160000 vendor/cultcache-py diff --git a/.gitmodules b/.gitmodules index 5a9bf41..578af4b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "vendor/cultcache-py"] - path = vendor/cultcache-py - url = https://github.com/GameCult/cultcache-py.git [submodule "vendor/eve"] path = vendor/eve url = https://github.com/GameCult/Eve.git diff --git a/AGENTS.md b/AGENTS.md index 6f1c37d..d0ad6d4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,15 +53,22 @@ inside another project. - Architecture rationale: `F:\Projects\Ghostlight\notes\architecture-rationale.md` - State CLI: `F:\Projects\Ghostlight\tools\ghostlight_state.py` - CultCache state seam: `F:\Projects\Ghostlight\tools\ghostlight_state_store.py` -- CultCache-Py submodule: `F:\Projects\Ghostlight\vendor\cultcache-py` +- CultCache-Py dependency: pinned `cultcache-py==0.2.0` from PyPI, declared in + `F:\Projects\Ghostlight\tools\requirements.txt` - CultCache state store: `F:\Projects\Ghostlight\state\ghostlight-state.cultcache.jsonl` - Pre-compaction helper: `F:\Projects\Ghostlight\tools\ghostlight_prepare_compaction.py` ## Useful Commands -Use the bundled Python runtime if `python` is not on PATH: +Use the bundled Python runtime if `python` is not on PATH. `npm run state:status` +and `npm run state:prepare-compaction` install the pinned `cultcache-py` +dependency automatically (a `pre*` npm hook runs `state:bootstrap` first, an +idempotent `pip install -r tools/requirements.txt`). A direct +`ghostlight_state.py` invocation does not go through npm, so run +`state:bootstrap` once per session before the first direct call: ```powershell +npm run state:bootstrap npm run state:status npm run state:prepare-compaction & 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' '.\tools\ghostlight_state.py' add-evidence --type research --status ok --note '...' @@ -94,6 +101,8 @@ On fresh session load, do this before wandering off into implementation: - `notes/ghostlight-current-system-map.md` - `notes/ghostlight-implementation-plan.md` 2. run: + - `npm run state:bootstrap` (installs the pinned `cultcache-py` dependency; + idempotent, safe to rerun) - `npm run state:status` 3. restate the current next action from the persisted state before starting edits diff --git a/docs/architecture/persistence-spine.md b/docs/architecture/persistence-spine.md index a0bd5ab..c15e615 100644 --- a/docs/architecture/persistence-spine.md +++ b/docs/architecture/persistence-spine.md @@ -3,14 +3,19 @@ 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 lives in `vendor/cultcache-py`, backed by the standalone -GameCult repository `cultcache-py`. +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 submodule: `vendor/cultcache-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 diff --git a/package.json b/package.json index 29d2640..ace65ef 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,10 @@ "dungeon:run": "cargo run -p ghostlight-dungeon", "sdk:build": "npm --prefix sidecar/claude-sdk run build", "sdk:test": "npm --prefix sidecar/claude-sdk test", + "state:bootstrap": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" -m pip install -q -r tools\\requirements.txt", + "prestate:status": "npm run state:bootstrap", "state:status": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\ghostlight_state.py status", + "prestate:prepare-compaction": "npm run state:bootstrap", "state:prepare-compaction": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\ghostlight_prepare_compaction.py", "agent-state:validate": "\"C:\\Users\\Meta\\.cache\\codex-runtimes\\codex-primary-runtime\\dependencies\\python\\python.exe\" .\\tools\\validate_agent_state.py", "schema:validate": "npm run agent-state:validate && npm run projection:validate && npm run projected-context:validate && npm run lore-grounding:validate && npm run coordinator:validate && npm run responder-packets:validate && npm run responder-outputs:validate && npm run training-loop:validate && npm run initiative:validate && npm run ink:validate && npm run coverage:status", diff --git a/state/map.yaml b/state/map.yaml index 19aee29..59aa3c1 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -292,7 +292,7 @@ current_artifacts: live_examples: - state/ghostlight-state.cultcache.jsonl - tools/ghostlight_state_store.py - - vendor/cultcache-py + - tools/requirements.txt - state/corpus-coverage.json - examples/agent-state.call-of-the-void.json - examples/projections/call-of-the-void.scene-broken-taxi-oz.jsonl diff --git a/tools/ghostlight_state_store.py b/tools/ghostlight_state_store.py index 6d5839e..fc36f92 100644 --- a/tools/ghostlight_state_store.py +++ b/tools/ghostlight_state_store.py @@ -2,24 +2,28 @@ from __future__ import annotations import hashlib import json -import sys from pathlib import Path from typing import Any +try: + from cultcache_py import CultCache, JsonLinesBackingStore, define_document_type +except ModuleNotFoundError as error: + if error.name != "cultcache_py": + raise + raise SystemExit( + "Ghostlight state tooling needs cultcache-py. Install it with " + "`npm run state:bootstrap`, or with this interpreter directly: " + "`python -m pip install -r tools/requirements.txt`." + ) from error + ROOT = Path(__file__).resolve().parents[1] STATE_DIR = ROOT / "state" -VENDOR_SRC = ROOT / "vendor" / "cultcache-py" / "src" CACHE_PATH = STATE_DIR / "ghostlight-state.cultcache.jsonl" BRANCHES_PATH = STATE_DIR / "branches.json" EVIDENCE_PATH = STATE_DIR / "evidence.jsonl" ARCHIVE_PATH = STATE_DIR / "evidence.archive.jsonl" -if str(VENDOR_SRC) not in sys.path: - sys.path.insert(0, str(VENDOR_SRC)) - -from cultcache_py import CultCache, JsonLinesBackingStore, define_document_type - BRANCHES_DOC = define_document_type( "ghostlight.branches.v0", diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..25fb93a --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1 @@ +cultcache-py==0.2.0 diff --git a/vendor/cultcache-py b/vendor/cultcache-py deleted file mode 160000 index 9b4bbb8..0000000 --- a/vendor/cultcache-py +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9b4bbb8a4d842dd02dc8b2eefda7e742aa47578c