diff --git a/packages/cultcache-py/PARITY.md b/packages/cultcache-py/PARITY.md index 9ca9d37b..17394df4 100644 --- a/packages/cultcache-py/PARITY.md +++ b/packages/cultcache-py/PARITY.md @@ -104,19 +104,22 @@ CultCache, CultNet, and CultMesh. It is evidence, not confetti. store beside the cache file by default. The `cultmesh-py-daemon` / `python -m cultmesh_py.daemon` entrypoint launches that same local TCP server body and emits a bounded readiness document for process supervisors while the - served CultNet endpoint remains the capability owner. The daemon can register - and seed the package interop-note document so launched processes advertise a - real payload schema and serve both a real snapshot and a real shard-log entry - over the same CultNet lane. When launched with a Verse id, it publishes its - actual bound endpoint in the Verse and peer catalogs so discovery clients can - find the running Python peer without embedding code-owned catalogs. With a - cache file and durable shard-log path, a restarted daemon can rehydrate the - persisted interop-note snapshot and shard-log entry without reseeding. The - launched daemon also accepts raw puts over a live database subscription stream, - emits the matching change notification, and records the mutation in the served - shard log. If its durable shard log has been compacted, stale shard-log - requests return the same explicit `compacted_history` resync boundary over - the daemon wire endpoint instead of silently looking empty. + served CultNet endpoint remains the capability owner. Readiness now mirrors + the served hello capability metadata, shard ids, and configured snapshot + limits so supervisors can inspect startup state without inventing a second + protocol truth. The daemon can register and seed the package interop-note + document so launched processes advertise a real payload schema and serve both + a real snapshot and a real shard-log entry over the same CultNet lane. When + launched with a Verse id, it publishes its actual bound endpoint in the Verse + and peer catalogs so discovery clients can find the running Python peer + without embedding code-owned catalogs. With a cache file and durable shard-log + path, a restarted daemon can rehydrate the persisted interop-note snapshot and + shard-log entry without reseeding. The launched daemon also accepts raw puts + over a live database subscription stream, emits the matching change + notification, and records the mutation in the served shard log. If its durable + shard log has been compacted, stale shard-log requests return the same + explicit `compacted_history` resync boundary over the daemon wire endpoint + instead of silently looking empty. The daemon CLI snapshot document/byte limits feed the same public server edge; oversized daemon snapshot responses are returned as structured `cultnet.error.v0` peer errors rather than partial payloads. diff --git a/packages/cultcache-py/src/cultmesh_py/daemon.py b/packages/cultcache-py/src/cultmesh_py/daemon.py index c2fd5353..4e738f1e 100644 --- a/packages/cultcache-py/src/cultmesh_py/daemon.py +++ b/packages/cultcache-py/src/cultmesh_py/daemon.py @@ -9,6 +9,7 @@ from pathlib import Path from typing import Any from cultcache_py.interop import INTEROP_SCHEMA_VERSION, interop_note_document +from cultnet_py import hello from .facade import CultMesh from .server import CultMeshLocalServer @@ -133,14 +134,23 @@ def advertise_self( def daemon_ready_document(server: CultMeshLocalServer) -> dict[str, Any]: + hello_response = server.handle_message(hello(runtime_id="daemon-ready-probe").to_wire()) or {} return { "schemaVersion": READY_SCHEMA_VERSION, "runtimeId": server.node.runtime_id, "runtimeKind": server.runtime_kind, + "displayName": hello_response.get("displayName") or server.node.runtime_id, "host": server.host, "port": server.port, "endpoint": f"cultnet://{server.host}:{server.port}", + "shardIds": server.node.database.shard_ids(), "supportedDocumentTypes": [document.type for document in server.node.documents], + "supportedMessageVersions": list(hello_response.get("supportedMessageVersions") or ()), + "supportedMutationContracts": list(hello_response.get("supportedMutationContracts") or ()), + "snapshotLimits": { + "maxSnapshotDocuments": server.max_snapshot_documents, + "maxSnapshotBytes": server.max_snapshot_bytes, + }, } diff --git a/packages/cultcache-py/tests/test_cultcache.py b/packages/cultcache-py/tests/test_cultcache.py index 7c87e3f2..68539cac 100644 --- a/packages/cultcache-py/tests/test_cultcache.py +++ b/packages/cultcache-py/tests/test_cultcache.py @@ -3082,8 +3082,16 @@ class CultCacheTests(unittest.TestCase): ready = json.loads(ready_path.read_text(encoding="utf-8")) self.assertEqual(ready["schemaVersion"], READY_SCHEMA_VERSION) self.assertEqual(ready["runtimeId"], "daemon-test-peer") + self.assertEqual(ready["runtimeKind"], "python") + self.assertEqual(ready["displayName"], "Daemon Test Peer") self.assertEqual(ready["endpoint"], f"cultnet://127.0.0.1:{ready['port']}") + self.assertEqual(ready["shardIds"], ["daemon-interop"]) self.assertEqual(ready["supportedDocumentTypes"], ["cultcache.interop-note"]) + self.assertIn("cultnet.hello.v0", ready["supportedMessageVersions"]) + self.assertIn("cultnet.document_put_raw.v0", ready["supportedMessageVersions"]) + self.assertEqual(ready["supportedMutationContracts"][0]["documentType"], "cultcache.interop-note") + self.assertIn("shardLog", ready["supportedMutationContracts"][0]["operations"]) + self.assertEqual(ready["snapshotLimits"], {"maxSnapshotBytes": None, "maxSnapshotDocuments": None}) client = CultMesh.create_client("127.0.0.1", int(ready["port"]), timeout_seconds=2.0) hello_response = client.request( @@ -3096,6 +3104,8 @@ class CultCacheTests(unittest.TestCase): self.assertIn("cultnet.hello.v0", hello_response["supportedMessageVersions"]) self.assertIn("cultnet.schema_catalog_request.v0", hello_response["supportedMessageVersions"]) self.assertEqual(hello_response["supportedDocumentTypes"], ["cultcache.interop-note"]) + self.assertEqual(ready["supportedMessageVersions"], hello_response["supportedMessageVersions"]) + self.assertEqual(ready["supportedMutationContracts"], hello_response["supportedMutationContracts"]) schema_response = client.fetch_schema_catalog(schema_ids=["cultcache.interop-note"], include_schema_json=True) self.assertEqual(schema_response["schemas"][0]["schemaVersion"], INTEROP_SCHEMA_VERSION) @@ -3556,6 +3566,7 @@ class CultCacheTests(unittest.TestCase): ) try: ready = self._wait_for_ready_file(process, ready_path) + self.assertEqual(ready["snapshotLimits"], {"maxSnapshotBytes": None, "maxSnapshotDocuments": 0}) client = CultMesh.create_client("127.0.0.1", int(ready["port"]), timeout_seconds=2.0) with self.assertRaisesRegex(CultNetPeerError, "Snapshot document limit exceeded") as raised: