import type { MonographDb } from '../../storage/db.js'; import type { EdgeRelation } from '../../types.js'; import type { PipelinePhase } from '../types.js'; export interface CommunitiesOutput { memberships: Map; communityLabels: Map; cohesionScores: Map; } /** * The relation families that participate in clustering, and the weight each * family contributes to the clustering graph. * * This selection is deliberate, not incidental: different relations mean very * different things structurally, so they must not all count as the same * evidence of coupling. * * - **Coupling (weight 1)** — one symbol genuinely depends on another at * runtime or type-check time. These are what a module boundary is supposed * to contain, so they drive cluster boundaries. * - **Weak coupling (weight 0.5)** — real but indirect or partial links * (pass-through re-exports, member access, framework wiring). They pull * nodes together, but should not outvote a direct call or import. * - **Containment (weight 0.25)** — a file *owns* a symbol; it is not evidence * that the two are coupled. Counted at full weight, containment dominates * every real repo (there is one CONTAINS edge per symbol) and clustering * degenerates into a restatement of the file tree. It is kept at a low weight * only so that symbols with no coupling edges still attach to their parent * rather than dropping out of the clustering entirely. * * Everything not listed is excluded: process/step relations (produced after * this phase runs), and the document-knowledge-graph families (REFERENCES, * PARENT_SECTION, TAGGED_AS, CO_OCCURS and the LLM-inferred prose relations), * which describe prose adjacency rather than code coupling. */ export declare const CLUSTERING_RELATION_WEIGHTS: Readonly>>; export interface ClusteringEdge { sourceId: string; targetId: string; weight: number; } /** * Read the clustering input from the committed canonical graph — the `edges` * table this build has already written — rather than from any single phase's * in-memory output. * * This is the whole point of the query: edges produced by the resolution * phases (scope-resolution's resolved CALLS, bridge-resolver, wildcard * synthesis, mro, routes/tools/orm) are inserted straight into the DB and * never surface in `parse` or `cross-file` phase output. Clustering from those * outputs therefore ran over a thinner graph than the one every other consumer * queries. Reading the table means clustering sees exactly the edges the rest * of the system sees — filtered to the families selected above. * * The phase runs inside the build's open transaction on the same connection, * so it observes this build's writes. */ export declare function loadClusteringEdges(db: MonographDb): ClusteringEdge[]; export declare function computeCohesion(communityId: number, memberships: Map, edges: Array<{ sourceId: string; targetId: string; }>): number; /** * Compute cohesion scores for all communities in a single O(N+E) pass. * * This replaces calling `computeCohesion` inside a loop, which was O(K*(N+E)) * because each call re-scanned all memberships (O(N)) and all edges (O(E)). * * The score is a structural measurement — the fraction of possible internal * pairs that are actually connected by a selected edge — and nothing more. It * is not a quality, health, or design-correctness judgment: a low score says * the clustering grouped nodes that have few links *among the relation * families listed in CLUSTERING_RELATION_WEIGHTS*, which can equally mean the * coupling is real but expressed through a family this phase excludes. * * @param memberships - nodeId → communityId map from the clustering step * @param edges - the same selected edge set that was clustered (see * `loadClusteringEdges`), so the score describes the graph that produced the * communities rather than some other edge set * @returns Map of communityId → internal-edge density ∈ [0, 1] */ export declare function computeAllCohesionScores(memberships: Map, edges: Array<{ sourceId: string; targetId: string; }>): Map; /** * Community detection over the committed canonical graph. * * What the output does and does not claim: communities here are the clusters * a modularity-maximising algorithm (Leiden-style refinement over Louvain) * finds in the weighted edge set defined by `CLUSTERING_RELATION_WEIGHTS`. * They are a summary of *that* connectivity, and they have not been measured * against package manifests, directory layout, or runtime deployment units — * so a community is a candidate grouping to look at, not a verified * architectural module or package boundary. Any consumer that presents these * as architecture should say which basis it is using. * * `deps` lists every phase that writes a selected relation family into * `edges`, so that the table is complete when this phase reads it. Adding a * phase that emits a clustering relation means adding it here too. */ export declare const communitiesPhase: PipelinePhase; /** * Split a community that is too large into smaller sub-groups. * By graphify convention, communities >25% of total graph nodes are split. */ export declare function splitOversizedCommunity(memberIds: string[], maxGroupSize: number): string[][]; //# sourceMappingURL=communities.d.ts.map