Summary
dkg:AssertionCreated / dkg:AssertionPromoted events are written to the local _meta graph by the promoting node only (packages/publisher/src/dkg-publisher.ts:3170,3465). Unlike dkg:WorkspaceOperation rows in _shared_memory_meta, replicas don't materialise these lifecycle events when they receive the SWM gossip.
Result: after PR #694 switched the Overview "Recent activity" feed to source from useAssertionLifecycleEvents, a non-authoring node that received SWM-shared assertions from peers shows an empty feed for those assertions, even though the entities themselves are visible in the SWM tab.
Where
- Writer (local only):
packages/publisher/src/dkg-publisher.ts:3170 (assertionCreate) and :3465 (assertionPromote) call generateAssertionCreatedMetadata / generateAssertionPromotedMetadata which write into did:dkg:context-graph:<cgId>/_meta.
- SWM gossip path:
WorkspaceOperation records in _shared_memory_meta ARE replicated as part of SWM sync, but lifecycle events in _meta are NOT.
- Consumer (no peer source):
packages/node-ui/src/ui/hooks/useAssertionLifecycleEvents.ts queries only the local _meta graph.
Symptom
- Peer agent A promotes 10 entities on their node →
AssertionPromoted event written to A's local _meta + SWM gossip carries the entities themselves.
- Non-author node B receives the SWM gossip, sees the 10 entities in the SWM tab.
- Node B's Overview "Recent activity" feed: empty. The lifecycle SPARQL against B's
_meta returns no rows for A's promotion (because A's _meta was never replicated to B).
PR #694's joiner treats lifecycleEvents = [] as authoritative (suppresses legacy 'added' rows) — so B doesn't fall back to entity-derived rows either. The feed reads "no activity" even though there is plenty of peer-shared activity to surface.
Reviewer threads on PR #694 that surfaced this
useAssertionLifecycleEvents.ts:114 — "this query only reads the local _meta graph, but AssertionCreated/AssertionPromoted are only written by the promoting node ... after the Overview switches to this source other agents' promotions disappear on every non-author node"
ProjectView.tsx:517 and :255 — same finding from the consumer side.
Resolved as wontfix on PR #694 with a pointer to this issue, because the fix is architectural and out of scope for a polish PR.
Proposed directions
Pick one. My ordering reflects effort + cleanliness.
Option 1 — Replicate lifecycle events to _shared_memory_meta (backend, recommended long-term)
Have the lifecycle metadata writers ALSO emit a replication-safe copy of the events into _shared_memory_meta (or a sibling graph that's part of the SWM gossip set). The hook then queries that replicated graph instead of (or in addition to) _meta.
- Pros: Single source of truth for the activity feed. Peer activity surfaces naturally on every node. No UI rework.
- Cons: Backend change. Increases SWM payload. Need to be careful that the event subjects are deterministic so multiple peers don't write conflicting copies of the same event.
- Effort: Medium — touches
generateAssertion{Created,Promoted}Metadata, the SWM-share write path, and the hook's SPARQL.
Option 2 — Bring back swmEvents as a peer-activity fallback in the joiner
When lifecycleEvents is supplied but doesn't account for some WorkspaceOperation rows (i.e. there exist promotion ops in _shared_memory_meta whose ?root doesn't appear in any lifecycle event), surface those peer ops via the legacy swmEvents path as "peer activity" rows.
- Pros: UI-only; no backend change.
- Cons: Partially undoes the B-collapse design (peer rows are per-entity, not per-bundle; agent appears as peer-id, not DID). Two row shapes in the same feed. Reintroduces the visual issues we just fixed.
- Effort: Low.
Option 3 — Explicit "peer activity" surface (new UX)
Keep the lifecycle source as local-only-by-design; build a separate "Peer activity" panel sourced from WorkspaceOperation that lists what other agents have shared into this CG.
- Pros: Cleanest separation; local vs peer become two distinct stories.
- Cons: New UX surface; design work; lands in a separate plan section.
Definition of done
Related
Summary
dkg:AssertionCreated/dkg:AssertionPromotedevents are written to the local_metagraph by the promoting node only (packages/publisher/src/dkg-publisher.ts:3170,3465). Unlikedkg:WorkspaceOperationrows in_shared_memory_meta, replicas don't materialise these lifecycle events when they receive the SWM gossip.Result: after PR #694 switched the Overview "Recent activity" feed to source from
useAssertionLifecycleEvents, a non-authoring node that received SWM-shared assertions from peers shows an empty feed for those assertions, even though the entities themselves are visible in the SWM tab.Where
packages/publisher/src/dkg-publisher.ts:3170(assertionCreate) and:3465(assertionPromote) callgenerateAssertionCreatedMetadata/generateAssertionPromotedMetadatawhich write intodid:dkg:context-graph:<cgId>/_meta.WorkspaceOperationrecords in_shared_memory_metaARE replicated as part of SWM sync, but lifecycle events in_metaare NOT.packages/node-ui/src/ui/hooks/useAssertionLifecycleEvents.tsqueries only the local_metagraph.Symptom
AssertionPromotedevent written to A's local_meta+ SWM gossip carries the entities themselves._metareturns no rows for A's promotion (because A's_metawas never replicated to B).PR #694's joiner treats
lifecycleEvents = []as authoritative (suppresses legacy'added'rows) — so B doesn't fall back to entity-derived rows either. The feed reads "no activity" even though there is plenty of peer-shared activity to surface.Reviewer threads on PR #694 that surfaced this
useAssertionLifecycleEvents.ts:114— "this query only reads the local_metagraph, butAssertionCreated/AssertionPromotedare only written by the promoting node ... after the Overview switches to this source other agents' promotions disappear on every non-author node"ProjectView.tsx:517and:255— same finding from the consumer side.Resolved as wontfix on PR #694 with a pointer to this issue, because the fix is architectural and out of scope for a polish PR.
Proposed directions
Pick one. My ordering reflects effort + cleanliness.
Option 1 — Replicate lifecycle events to
_shared_memory_meta(backend, recommended long-term)Have the lifecycle metadata writers ALSO emit a replication-safe copy of the events into
_shared_memory_meta(or a sibling graph that's part of the SWM gossip set). The hook then queries that replicated graph instead of (or in addition to)_meta.generateAssertion{Created,Promoted}Metadata, the SWM-share write path, and the hook's SPARQL.Option 2 — Bring back
swmEventsas a peer-activity fallback in the joinerWhen
lifecycleEventsis supplied but doesn't account for someWorkspaceOperationrows (i.e. there exist promotion ops in_shared_memory_metawhose?rootdoesn't appear in any lifecycle event), surface those peer ops via the legacyswmEventspath as "peer activity" rows.Option 3 — Explicit "peer activity" surface (new UX)
Keep the lifecycle source as local-only-by-design; build a separate "Peer activity" panel sourced from
WorkspaceOperationthat lists what other agents have shared into this CG.Definition of done
agent-docs/context-graph-implementation-plan.mdN6 section as a fold-in.Related
N6inagent-docs/context-graph-implementation-plan.mdalready tracks N6 polish work; the peer-activity question can be folded into either N6 or its own new section depending on which option above is picked.