Skip to content

feat(graph): deterministic Louvain community detection (hq-zlph)#31

Merged
scbrown merged 1 commit into
mainfrom
hq-zlph-louvain
Jun 30, 2026
Merged

feat(graph): deterministic Louvain community detection (hq-zlph)#31
scbrown merged 1 commit into
mainfrom
hq-zlph-louvain

Conversation

@scbrown

@scbrown scbrown commented Jun 30, 2026

Copy link
Copy Markdown
Owner

What (aegis-1p0 Gap 3)

Finishes the planned community detection on quipu's existing petgraph Projection API. New tool_project algorithm louvain (alias community) that detects modularity-based communities and can persist them as queryable facts.

Implementation

  • graph::louvain — hand-rolled, pure-Rust deterministic Louvain (modularity local-moving phase). The projected directed multigraph is read undirected/weighted (parallel edges summed, self-loops dropped). Determinism is a hard AC: ascending entity-id visitation, ascending-label candidate evaluation, lowest-label tie-break → the same graph always yields the same partition, independent of hash order. No C-backed clustering crate (quipu = zero-unsafe/minimal-dep). Returns groups + Newman–Girvan modularity.
  • tool_project now takes &mut Store (handler ro_handler!rw_handler!); read-only by default, persist:true opt-in.
  • persist_communities — writes quipu:memberOfCommunity via the store write path with provenance source = "algo:louvain". Bitemporally supersedes the prior derivation (AC 5): reconciles by (entity, community-term), retracting changed memberships and asserting new ones, so a current-facts query always returns exactly the latest partition — no stale accumulation. (Diff rather than retract-all+reassert because the facts table is keyed (e,a,v,tx) — retracting and re-asserting an identical triple in one tx would collide.)
  • Not an access boundary (AC 6): emergent clustering, distinct from group_id; documented in the module header, the persist fn, and the design doc so no consumer builds access control on it.

Design provenance

Scope + write-path semantics resolved with dearing (opt-in persist, store write path) and ian-endorsed (AC 5 supersede-on-rerun, AC 6 not-an-access-primitive). The bead's original "persist like quipu:pageRank" was corrected — tool_project arms were all read-only; this adds the first write path deliberately and opt-in.

Tests (all green on both CI feature sets)

  • louvain_finds_two_clusters_deterministically — two-clique fixture separates correctly; same graph → identical partition (AC 1/3).
  • louvain_persist_writes_queryable_membershippersist:true writes SPARQL-queryable quipu:memberOfCommunity; read-only default writes nothing (AC 2).
  • persist_supersedes_prior_membership — re-run with changed partition leaves exactly one active membership per entity (AC 5).

cargo fmt --all --check, clippy -D warnings, and full tests pass under --no-default-features and --features shacl. No new MCP tool (algorithm param on existing quipu_project), so no tool-count change.

Docs: pagerank.md community section, mcp-tools.md reference.

Closes hq-zlph. (quipu in hq rig until qp- stands up.)

🤖 Generated with Claude Code

aegis-1p0 Gap 3. Finish the planned community detection on the existing
petgraph Projection API.

- graph::louvain: hand-rolled, pure-Rust deterministic Louvain (modularity
  local-moving phase). Reads the projected directed multigraph as undirected
  weighted (parallel edges summed, self-loops dropped). Determinism is a hard
  AC: ascending entity-id visitation, ascending-label candidate evaluation,
  lowest-label tie-break — same graph always yields the same partition. No
  C-backed clustering dependency. Returns groups + Newman-Girvan modularity.
- tool_project: new algorithm "louvain" (alias "community"). Read-only by
  default; takes &mut Store (handler ro_handler -> rw_handler) so an opt-in
  persist:true can write through the store.
- persist_communities: writes quipu:memberOfCommunity facts via the store
  write path (provenance source "algo:louvain"). Bitemporally SUPERSEDES the
  prior derivation — reconciles by (entity, community-term), retracting changed
  memberships and asserting new ones, so a current-facts query always returns
  exactly the latest partition (no stale accumulation, AC 5). Diff rather than
  retract-all+reassert because the facts table is keyed (e,a,v,tx).
- Communities are emergent clustering, NOT an access boundary (AC 6) —
  documented in the module header, persist fn, and design doc.

Adds quipu:memberOfCommunity + community_<k> under the quipu namespace.
Tests: two-clique fixture separates deterministically (AC 1/3), persist writes
SPARQL-queryable membership + read-only default (AC 2), re-run supersedes prior
membership without accumulation (AC 5). Clippy + fmt + tests green on both CI
feature sets. Docs: pagerank.md community section, mcp-tools.md reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdgxbhR39MuL7qwHP8vhFT
@scbrown
scbrown merged commit 62fe4fa into main Jun 30, 2026
10 checks passed
scbrown added a commit that referenced this pull request Jul 14, 2026
Louvain community detection (#31), live graph report + quipu_report tool (#32),
episode-scoped retraction (#33), and --base-ns/--timestamp CLI flags (#34).

Manual release: release-plz's release-pr step is blocked by the repo's
"Actions may not create PRs" setting (403); cutting the tag + GitHub release by
hand per the established fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsuPDxSUjMYqHvQyPvSAPW
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