A boundary-crosser is a node in a dependency graphIn our case, a representation of dependencies in a software system. Nodes stand for a program's parts — modules, classes, functions. A directed edge A → B means A refers to B. Clusters are sets of densely-connected nodes, and an edge crosses a boundary when it connects a node inside a cluster to a node outside it. whose edges cross the boundary of its own cluster. Sometimes those are accidents and tangle the code; and sometimes they're intentional, and structurally necessary. But every partition-based coupling metric confuses the two. This note names three species of legitimate crossers, provides descriptive characteristics and metrics for all three — plus the actions that produce them, and the unintentional impostors that mimic them. We validate all against small, runnable Python programs.
Every coupling metric in the partition familyMetrics computed on a partition: a division in which every node belongs to exactly one cluster, with no overlaps. The family includes CK's coupling suite, Newman modularity, and our own v2 pipeline — anything that begins by drawing hard, disjoint boundaries. — from Chidamber & Kemerer through Newman modularity to our own v2 pipeline — treats a cluster boundary as a wall, and counts every edge that crosses it as a problem.
But a node whose edges cross its cluster's boundary is not always evidence of tangle. A coordinator that wires four workers together will always have more cross-boundary edges than those workers. A metric that flags it as disorder isn't being strict; it's simply judging the situation incorrectly.
Our v2 metrics handled one such case with a patch: the CCR orchestrator exclusionOur v2 rule: detect the one cluster whose job is integration (highest cross-boundary edge volume, out-ratio ≥ 0.60, a clear lead over the runner-up) and exclude it from the worker CCR average, so the wiring layer doesn't poison the score of the modules it wires.. Detect the one integration class, drop it from the worker average, move on. It worked — on the 100-program benchmark it fires on 42 of 47 decomposed programs and on none of the unconstrained ones. But it always had the smell of a special case: a hand-tuned exception, carved out of a metric that was otherwise treating every crossing as a sin.
Note III raised the suspicion that the patch was a shadow of something general — that Alexander's semi-latticeAlexander's name for a structure whose clusters may overlap without nesting — as opposed to a tree, where any two clusters either nest or are disjoint. The subject of Note III., measured as overlapping community membership, might subsume the orchestrator rule. We tested that suspicion on our own artifacts. It failed, and the failure was more instructive than the hypothesis.[1]
A well-decomposed orchestrator is not an overlap node. It is a star center — a hub whose neighbours connect to it but not to each other — and a star has no overlap structure at all. The orchestrator exclusion is not a special case of the overlap criterion. Both are members of a family of legitimate boundary-crossers, and the family has (at least) three species, told apart by two independent axes.
This note is the field guide to that family: the field identifiers for each species, live measurements of each on small runnable programs, the programming movements that produce them by different means, the unintentional impostor that mimics all of them, and a census across the published 100-program benchmark showing that the species are not decorative — they are what decomposition produces.
Call a node a boundary-crosser when its edges cross the boundary of its cluster — when it is connected to nodes in clusters other than its own. Every legitimate boundary-crosser answers two independent questions about how it participates in more than one region of the graph.
Direction — does dependency flow out of the node, in to it, or both? Measured by the out-ratioout_ratio(v) = out-degree / (in-degree + out-degree) on the directed dependency graph. 1.0 = pure coordinator (only calls out); 0.0 = pure kernel (only gets called); 0.5 = balanced., a single number on the directed graph.
Embedding — is the node the center of a starA hub whose neighbours are mutually independent — they connect to the hub but not to each other. A star has zero triangles, which means link clustering has nothing to grip., or is it woven into several dense regions at once? Measured by m(v)The number of link communities (Ahn, Bagrow & Lehmann 2010) incident on node v. Link clustering groups edges, so one node can sit in several edge-communities at once — m(v) > 1 is measured overlap., the node's community-membership count under overlapping community detection, together with its triangle participationHow many triangles (3-cycles) the node sits in. Zero triangles = its neighbours don't know each other = star embedding. Many triangles = it lives inside dense, mutually-connected regions..
Two axes, three occupied cells:
High out-degreeout-degree = the number of edges leaving a node; in-degree = the number arriving. On a dependency graph: how many parts this node uses, and how many parts use it., out-ratio ≥ 0.60, zero triangle participation — the nodes it coordinates (its workers) are mutually independent, which is exactly what makes it a good orchestrator. Low m(v), by construction: a star has no overlap for link clustering to find.
The mirror image: high in-degree, out-ratio ≤ 0.40, zero triangles. Everything refers back to it; it refers to nothing. Its strength is not activity but being depended upon.
The one true overlap node: m(v) > 1 under link clustering. It is woven into several dense regions at once — its edges belong to different edge-communities, and it is the shared member.
Try the classifier. It is the exact rule set we run in code (§10), including the guard the measurements forced on us — a high out-ratio node woven into triangles is not an orchestrator, just a busy resident of a dense region:
Watch the dependency flow. The three species are easiest to tell apart by where the particles go.
Alexander's names fit these shapes with almost no forcing, and we will come back to that in §11 — the broker is his Deep Interlock and Ambiguity, the kernel his Void, the orchestrator his field of centers, with one honest caveat about hollow hubs. But the field guide comes first: each species, on a real program, with the instruments running.
Each plate below is a small runnable Python program, designed to exhibit one species cleanly, then parsed and measured by the same pipeline we run on the benchmark. The numbers shown are the pipeline's actual output, not illustrations.
ReportRunnerclass ReportRunner: """The star center: coordinates all four workers; none call back.""" def __init__(self): self.loader = Loader() self.cleaner = Cleaner() self.analyzer = Analyzer() self.renderer = Renderer() def run(self, path): rows = self.loader.load(path) rows = self.cleaner.clean(rows) totals = self.analyzer.totals_by_region(rows) return self.renderer.render(totals)
Moneyclass Money: """The kernel: high in-degree, zero out-degree.""" def __init__(self, cents): self.cents = int(cents) def plus(self, other): return Money(self.cents + other.cents) def times(self, factor): return Money(round(self.cents * factor)) # Pricing, TaxCalculator and ReceiptPrinter each construct Money. # None of them reference each other. Money references nothing.
SyncBridgeclass SyncBridge: """The broker: belongs to the local world AND the remote world.""" def __init__(self): self.store = LocalStore() # local cluster (a triangle) self.index = LocalIndex(self.store.cache) self.api = RemoteAPI() # remote cluster (a triangle) self.queue: "RemoteQueue" = self.api.queue def sync(self, key, value): self.store.save(key, value) self.api.send({key: value})
LocalStore has out_ratio 0.67 — orchestrator territory — but it sits in two triangles, so it is not a star center, and the classifier correctly leaves it a worker.This taxonomy was not designed. It fell out of a hypothesis failing in a useful direction.
The hypothesis, raised in Note III, was tidy: the orchestrator exclusion is a special case of the overlap criterion — run overlapping community detection and orchestrators will surface as high-membership nodes, no hand-tuned rule needed. Plate I is the refutation. On the decomposed fan-effect project from our benchmark — 12 classes, 12 edges at class level — link clustering returns partition density D = 0.018 and max m(v) = 1. Every node, including the orchestrator FanEffectSimulation, sits in exactly one community. The better the decomposition, the more star-like the orchestrator, the less overlap there is to see.
So overlap does not subsume the orchestrator rule. They catch different members of the same family — which is what makes it a family. And direction does real discriminating work where overlap is blind. On a designed two-species program (one coordinator, one shared document type, two parsers between them), the out-ratio separates the species with no ambiguity:
Then granularity complicates the picture — honestly, and measurably. The same fan-effect project, parsed at method level (50 nodes, 27 edges), stops being a star: partition density rises to 0.115, eight link communities appear, and four nodes go multi-member — StimulusGenerator at m=3, FanEffectSimulation, Chunk and StatsReporter at m=2. The class-level star dissolves into method-level interlock.[2] The orchestrator that showed no overlap at one scale is a mild broker at the next scale down.
Meanwhile the unconstrained version of the same project — the tangled one — shows zero overlap at both granularities: one blob, one community, nothing to interlock because nothing was ever separated. Overlap is not noise that decomposition removes. Overlap is structure that only decomposition can create.
The species are structural, not stylistic. Different programming movements produce the same graph fauna by entirely different mechanisms — and one movement can hide a species from the parser altogether.
def build_pipeline(): """The composition point: the only function that knows the others.""" stages = [parse, validate, convert, summarize] def run(raw): value = raw for stage in stages: value = stage(value) return value return run
Measured at function level: build_pipeline has out_ratio 1.00, zero triangles, m(v)=1 — orchestrator, the identical signature to ReportRunner's, achieved with no objects at all. The four stages are pure and mutually ignorant; the composition point is where all the knowledge of the whole lives. Functional programming does not abolish the coordinator; it concentrates it into one honest, stateless place — which is the best version of this species. Hughes (1990) argued that composition is the modularity mechanism of FP; the graph agrees.
A cross-cutting concern — tracing, in this exemplar — is the classic shared kernel: every domain class refers to it, it refers to nothing. But whether the parser can see that depends on the weaving mechanism, and the difference is exactly one toggle:
@trace is a Name reference; the AST draws Orders → trace, Billing → trace, Shipping → trace, and the kernel manifests: in-degree 3, out_ratio 0.00, star embedding.Flip the toggle. The config-woven version wraps the same three methods at runtime from a list of string targets — "PlainOrders.place" — and the AST edge count for trace drops from three to one (the weaver itself). The runtime dependency graph is identical; the measured graph is not. Static AOP is weave-blind: the parser sees the loom, never the cloth. This is not a flaw in the species — the kernel is still there at runtime — it is a measured limit of AST-based instrumentation, and any tool in this family must say so out loud. Kiczales et al. (1997) promised that aspects would make cross-cutting structure explicit; the decorator form keeps that promise to the parser, the config form breaks it.
Field guides warn about look-alikes. A god object, one that is built to do too much, has edges that cross into every cluster in sight — and match the descriptive characteristics of no species in our good boundary-crossing zoo.
class Kiosk: """High in-degree AND high out-degree; mutable state that every collaborator reaches back through.""" def __init__(self): self.dirty = True # shared mutable state self.log = Log(self) # Kiosk → Log, Log → Kiosk self.db = Db(self, self.log) # Kiosk → Db, Db → Kiosk … self.net = Net(self, self.db, self.log) self.ui = Ui(self, self.net, self.log)
This is the deepest point in the guide, so it deserves plain words: the species names describe structure, not virtue. The classifier does not bless a node by calling it an orchestrator, and it does not condemn the god object by name-calling — it simply finds that the god object has no legitimate structural niche. Its signature — bidirectional flow, dense embedding without multiple memberships, balanced ratio, shared mutable state — is the signature of a node that disturbs settled subsystems from the inside, the precise mechanism that makes adaptation exponential in Note I.
The wild confirms the designed exemplar. In the unconstrained half of the benchmark, wa07_api_client.py grows a function called _call that is the top of both degree distributions at once — a bidirectional hub in its natural habitat. Its decomposed twin has no such node; the flow has been straightened into an orchestrator and a set of kernels.
If the three species are real, they should be produced by decomposition and absent from tangle. We ran the classifier across the full published benchmark: fifty specifications, each implemented unconstrained (L0) and decomposition-first (L2), method-level graphs, identical pipeline.[3]
cl09_task_runner). The unconstrained programs grow almost none of this — two files with any overlap, one with any triangle structure. And the old hand-tuned v2 rule agrees with the new species classifier: of the 42 decomposed programs where the cluster-level orchestrator rule fires, the node-level classifier finds an orchestrator inside that same cluster in 40.Two readings of that last row. First, the v2 patch was right — it was measuring a real species with a cheap instrument, which is why it never misfired on tangled code. Second, the patch is now redundant — the family classifier catches the orchestrator and the two species the patch could never see, from first principles, with no magic 2×/+4 lead thresholds.
The wild specimens are worth a naturalist's note. The brokers that appear in decomposed code are recognizably Alexander's drugstore-at-the-traffic-light: su01_logger's handlers belong at once to the logging core and to their output subsystems; wa06_validator's ErrorCollector is shared tissue between validation passes (overlap 0.312, with real triangles). Nothing in the prompt asked for brokers. They grew where two well-formed regions genuinely needed a shared member — which is the semi-lattice thesis of Note III, observed in generated code.
Modern AI code evaluation is incremental: a change is accepted when its unit tests pass. Adding an edge to a dependency graph is behaviour-preserving — every change below clears that bar — so the question is whether the instruments register anything at all at this granularity.[4]
The protocol: take the 47 decomposed benchmark programs and apply to each a stream of thirty realistic, test-passing changes — bug fixes, performance work, dependency updates that break a contract, new features — in two paired arms with identical intents. The hygienic arm does the work with awareness of the species: its crossing edges land on shared kernels or on new adapter nodes at the boundary, and its features attach inside one cluster. The sloppy arm does the same work by convenience: patches reach wherever needed, performance shortcuts skip layers, and a kernel whose contract broke adapts by reaching outward. Every instrument is then read against the structure detected before the stream began.
Three lessons, each measured. First, deltas are only meaningful against a pinned reference. Re-running community detection after each change inverts the signal — it answers "how modular is the best reading of the new graph," not "did this change respect the structure we had." Pinned to the pre-change clustering, the gradient comes out right: a single edge added inside a community moves Q by noise around zero; a single lateral crossing trends negative; and a single kernel-poke — a settled kernel acquiring an outward crossing edge, the exact disturbance of Note I's model — is sign-negative in 99% of trials and flips the kernel's species classification at the touched node in 94%. The classifier turns a delta into a reviewable sentence: this change demotes this kernel.
Second, the instruments divide the labour. Worker CCR is the leading indicator — the sloppy arm crosses its 0.6 alert at the second change. Q is the confirmatory one — slower but statistically strongest. And raw BVC fails as a gate: it false-alarms on the hygienic arm's legitimate kernel traffic at the very moment it catches the sloppy arm. It becomes clean only as species-aware BVC, which forgives a crossing edge that lands on a kernel or routes through an adapter: the hygienic arm then holds at 0.000 for all thirty changes while the sloppy arm still alerts at the second. This is the sharpest result of the sanity check — the quantitative metric is unusable without the qualitative zoo.
Third, the overlap index is a shape, not a gate. At the first change the hygienic arm shows more overlap than the sloppy one (paired t = +4.7): healthy work creates modest, legitimate interlock. By the thirtieth the relationship has inverted — sloppy overlap balloons to 0.73 against hygienic 0.44, higher in 45 of 47 programs — because once crossing edges are pervasive, every node sits in several edge-communities, and overlap stops meaning interlock and starts meaning boundaries dissolving. Overlap is ∩-shaped in health: near zero is a tree or an undifferentiated blob; moderate is a living semi-lattice; runaway is mud. A city is not a tree — and it is not a soup.
The whole field guide compresses into one function plus one flag. This is the code, verbatim from the pipeline that produced every number above.
def classify(m_v, ratio, tri_v, in_d, out_d): """Species classifier. The two axes are BOTH required. Star species additionally require star embedding: zero triangle participation. A node woven into triangles with a high out-ratio is not an orchestrator — just a busy resident of a dense region.""" if ratio is None: # isolated node return "worker" if m_v > 1: # embedding axis first return "broker" if tri_v == 0 and out_d >= 2 and ratio >= 0.60: # star + out-flow return "orchestrator" if tri_v == 0 and in_d >= 2 and ratio <= 0.40: # star + in-flow return "shared-kernel" return "worker"
| instrument | reads | threshold / rule | catches |
|---|---|---|---|
| out_ratio(v) | direction of flow on the directed graph | ≥ 0.60 out · ≤ 0.40 in | orchestrator vs. kernel, on stars |
| m(v) | link-community memberships (Ahn et al.) | > 1 | the broker — measured overlap |
| triangles(v) | embedding: star vs. woven | = 0 required for star species | the guard — busy residents ≠ hubs |
| overlap index | fraction of nodes with m(v) > 1 | granularity-sensitive | how semi-lattice the whole system is |
| Q | partition modularity (LouvainA fast greedy algorithm (Blondel et al. 2008) that searches for the partition maximising Q. Non-deterministic between runs — pin the seed, or pin the partition itself when computing deltas (§09).) | < 0.15 alert | Case-2 tangle — no structure at all |
| bidir-hub flag | top of both degree distributions | in & out maxima, > 1 | the god object — the look-alike |
Link-community measures need triangle density to grip. On sparse class-level star graphs, m(v) collapses to 1 and only the direction axis is informative — that is honest (a star truly has no overlap), not broken. Compute the overlap instruments at method-level granularity or on genuinely dense graphs; read m(v) > 1 as a strong broker signature when the node also participates in triangles, and as a weak junction signature when it does not.
And one rule from the incremental experiments of §09: compute every delta against a pinned reference — the structure detected before the change — never by re-running detection afterwards, which answers a different question.
What the panel replaces: one hand-tuned exception (detect-and-exclude the orchestrator) becomes a principled statement — nodes with a legitimate structural niche have their cross-boundary edges scored as structure, not violation — plus an enumeration of the niches, each independently measurable, each observed both in designed exemplars and in the wild.
The reason this family felt familiar is that we had walked into Alexander's mature subject from the graph side.
His later theory is built on centers — regions that draw their strength from the centers around them — and the family's two axes are his two relational questions. The embedding axis is his distinction between a center that anchors a field and one that interlocks fields; the direction axis, between a center that organizes its neighbours and one that is depended upon by them. The broker is his Deep Interlock and Ambiguity, almost definitionally — interlock is overlap, stated once in prose and once in membership counts. The kernel is his Void and his Not-Separateness: the still core everything refers back to, the node least separable from the rest. The orchestrator is his field of centers, with the caveat he might insist upon: a hub whose workers are hollow is a controller, not a living center, and that difference — workers with real internal cohesion versus workers that exist only to be called — is measurable, and our CCR already gestures at it.
Discipline requires the other half of the sentence: this correspondence covers four of his fifteen properties — the relational ones — and says nothing about the other eleven, which concern a center's internal life or continuous geometry. And his centers are recursive — made of smaller centers, strengthened by them — where our species are, so far, computed flat, at one granularity. §05 showed the flat picture is already granularity-sensitive: the class-level star dissolves into method-level interlock. The genuinely Alexandrian version is the recursion — species at every level, with the strength of a center at one level a function of the strengths of the centers composing it. That fixed-point — a center is strong to the degree that strong centers support it — is the eigenvector centrality question, and part of our current research, not this note.
A field guide helps to build a descriptive theory. Alexander's theory of centers is an explanatory theory. — where Note V begins
StimulusGenerator at m = 3 and FanEffectSimulation, Chunk, StatsReporter at m = 2. The unconstrained twin: zero overlap at both levels. All numbers re-measured for this note with the v3 engine; they match the working-notes values to the third decimal. ↩dp09, wa08, wa10) contain unterminated string literals — generation truncation artifacts — and are excluded from both the published benchmark's usable rows and this census. Species totals at node level, decomposed condition: 99 orchestrators, 221 shared kernels, 45 brokers across 1,022 nodes; the per-file counts shown are the conservative reading (class nodes and their __init__ methods can both classify, so "files containing ≥ 1" avoids double counting). ↩