Skip to content

feat(memory): MemoryDistributionGrant + read-enforced revocation wired to HellGraph#43

Merged
mdheller merged 2 commits into
mainfrom
feat/memory-distribution-grant
Jul 17, 2026
Merged

feat(memory): MemoryDistributionGrant + read-enforced revocation wired to HellGraph#43
mdheller merged 2 commits into
mainfrom
feat/memory-distribution-grant

Conversation

@mdheller

Copy link
Copy Markdown
Member

Why

Prompted by @hegu-1 on #18: approval ≠ sync/distribution authority. A globally-approved memory can still be too sensitive for a given endpoint, and revocation was propagation-latency-dependent.

We traced the estate first. Two of the three axes were already owned:

The third axis was genuinely missing: per-endpoint distribution authority, and revocation never reached the graph materialization.

What

Contract surface

  • schemas/memory-distribution-grant.schema.json + 4 fixtures + Python validator + CI + architecture doc.
  • Chains upstream WallGuard + lifecycle refs rather than re-deciding them (no second compartment engine). Adds: endpoint sensitivity ceiling, grant expiry, sha256 content digest, read-enforced revocation cursor, explicit offline posture, evidence-fabric receipt.

Runtime surfaces (not just the spec)

  • memoryd: read-enforced revocation across InMemory / SQLite / Postgres backends + POST /v1/revoke. A revoked memory is excluded from every subsequent recall, including externally-indexed vector hits.
  • HellGraph: revocation now propagates into the graph — memoryd retracts the derived edges (services/memoryd/app/hellgraph_retract.py, graceful-degrade when HELLGRAPH_URL unset). Closes the derived-graph-lane leak so recall, the vector index, and the graph all stop serving a revoked memory.
  • specs: /v1/revoke added to memoryd.openapi.yaml + memory.mesh.v1.trpc.yaml.
  • openclaw edge adapter: EdgeMemoryStore.revoke() + read-time revoked filter.

Verification

  • services/memoryd/tests/test_distribution_revocation.py (in-memory + durable SQLite tombstone + graceful graph degrade).
  • End-to-end through the FastAPI app: recall → revoke → recall drops the hit, re-revoke 404s, memory.revocation_propagated event fires.
  • Schema validator + full memoryd/workspace_ingestion suites green.

Closes the design gap raised in #18.

…wired to HellGraph

Adds the per-endpoint distribution authority layer that sits between scope approval
(governed-learning lifecycle) and compartment admission (WallGuard) — the third axis
neither owned: may THIS device/profile/surface hold an already-approved memory, until when.

Contract surface:
- schemas/memory-distribution-grant.schema.json (+ 4 fixtures, validator, CI, doc).
  Chains upstream WallGuard + lifecycle refs rather than re-deciding them. Adds endpoint
  sensitivity ceiling, grant expiry, content digest, read-enforced revocation cursor,
  explicit offline posture, evidence-fabric receipt.

Runtime surfaces (not just the spec):
- memoryd: read-enforced revocation across InMemory/SQLite/Postgres backends + POST /v1/revoke.
  A revoked memory is excluded from every subsequent recall, incl. externally-indexed vectors.
- HellGraph: revocation propagates into the graph materialization — memoryd retracts the
  derived edges (services/memoryd/app/hellgraph_retract.py, graceful-degrade). Closes the
  derived-graph-lane leak: recall, vector index, and graph all stop serving a revoked memory.
- specs: /v1/revoke added to memoryd.openapi.yaml + memory.mesh.v1.trpc.yaml.
- openclaw edge adapter: EdgeMemoryStore.revoke() + read-time revoked filter.

Tests: services/memoryd/tests/test_distribution_revocation.py; e2e verified via FastAPI.

Prompted by @hegu-1 on #18 (approved != synced).
…linkage

HellGraph edges are keyed by workspace-source:{surface}/{slug} refs (the CSKG ingest
path), not by memory id. Revoke was only using caller-supplied refs + a memory://{id}
self-ref, so revoking a PKG-ingest summary memory would have retracted zero real edges —
the workspace-source refs were stranded in the summary's content blob.

- workspace_ingestion: write the derived source_refs into the summary memory's
  metadata.provenance_refs (not only the opaque content), so the linkage is durable.
- memoryd: /v1/revoke now reads the stored memory's metadata.provenance_refs and merges
  them into the retract call (get_memory_metadata added to all three stores).
- Tests: workspace_ingestion asserts the writeback carries workspace-source provenance;
  memoryd e2e asserts revoking a memory with stored provenance retracts those exact refs.
@mdheller
mdheller merged commit 535e7b4 into main Jul 17, 2026
16 checks passed
@mdheller
mdheller deleted the feat/memory-distribution-grant branch July 17, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant