relate-search 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ # Zig
2
+ zig-out/
3
+ .zig-cache/
4
+ zig-cache/
5
+ zig-pkg/
6
+
7
+ # Rust (bindings/rust)
8
+ target/
9
+ # The crates.io landing page, minted from the repository README by
10
+ # `tools/registry_readme.py`. Generated at package time, never committed:
11
+ # a checked-in copy is a second README to keep in step with the first.
12
+ bindings/rust/PROJECT_README.md
13
+
14
+ # Go (bindings/go) — the cross-repo development override. A published go.mod
15
+ # carries no `replace`, because Go ignores one in a dependency; a `go.work` is
16
+ # the sanctioned way to point at a sibling checkout instead of the proxy, and it
17
+ # is one developer's local wiring rather than a fact about the module.
18
+ go.work
19
+ go.work.sum
20
+
21
+ # Python (bindings/python)
22
+ __pycache__/
23
+ *.py[cod]
24
+ *.egg-info/
25
+ .venv/
26
+ dist/
27
+ .pytest_cache/
28
+ .ruff_cache/
29
+
30
+ # Search artifact home (index · atlas · shelf), shared with the sibling CLIs
31
+ .gist/
32
+
33
+ # Upstream clones kept for study
34
+ upstream/
35
+
36
+ # Toolchain overrides for one machine. `.mise.toml` and `mise.lock` are
37
+ # tracked; these are the per-checkout escape hatch and nobody else's business.
38
+ mise.local.toml
39
+ mise.local.lock
40
+
41
+ # Machine-local scratch. Globbed, not literal: ad-hoc probe dirs get named
42
+ # `.local-something`, land one character outside a literal rule, and are then
43
+ # one `git add -A` away from committing a hardcoded home path.
44
+ .local*/
45
+ .DS_Store
@@ -0,0 +1,528 @@
1
+ Metadata-Version: 2.4
2
+ Name: relate-search
3
+ Version: 1.0.0
4
+ Summary: Code similarity search for Python - find near-duplicate files, clone families, and where a snippet came from, using compression instead of embeddings.
5
+ Project-URL: Homepage, https://github.com/The-Billy-Company/relate
6
+ Project-URL: Documentation, https://github.com/The-Billy-Company/relate#readme
7
+ Project-URL: Repository, https://github.com/The-Billy-Company/relate
8
+ Project-URL: Issues, https://github.com/The-Billy-Company/relate/issues
9
+ Project-URL: Changelog, https://github.com/The-Billy-Company/relate/blob/main/CHANGELOG.md
10
+ Author: The Billy Company
11
+ License-Expression: Apache-2.0
12
+ Keywords: agent-tools,clone-detection,code-clones,code-search,code-similarity,compression,compression-distance,copy-paste-detector,cpd,deduplication,developer-tools,duplicate-code,duplicate-detection,information-theory,jaccard,jscpd,lzjd,minhash,moss,ncd,near-duplicate,plagiarism-detection,refactoring,retrieval,simian,similarity-search,static-analysis,ziv-merhav
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Information Technology
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Programming Language :: Zig
24
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
25
+ Classifier: Topic :: Software Development :: Libraries
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Topic :: Software Development :: Quality Assurance
28
+ Classifier: Topic :: System :: Archiving :: Compression
29
+ Classifier: Topic :: Text Processing
30
+ Classifier: Topic :: Text Processing :: Indexing
31
+ Classifier: Topic :: Utilities
32
+ Requires-Python: >=3.12
33
+ Requires-Dist: irregex<2,>=1.0.0
34
+ Description-Content-Type: text/markdown
35
+
36
+ # relate: code similarity search, by compression
37
+
38
+ Near-duplicate files, clone families, and the source of a pasted snippet - no
39
+ embeddings, no model, no vector database.
40
+
41
+ ## What it is
42
+
43
+ `relate` came from the question after Gist. If text is bits, and compressors
44
+ give repeated structure a shorter description, could I stop before producing
45
+ one compressed blob and return the things that share that structure instead?
46
+ That is compression as search.
47
+
48
+ Where `gist` asks _"where is this exact pattern?"_, `relate` handles the
49
+ set-shaped questions beside it: _what is this thing like, what repeats in here,
50
+ which files cover a topic together, and where did this pasted text come from?_
51
+
52
+ There are exactly **two kinship questions**, and the surface now says so.
53
+ `similar` is the **neighbor verb**: one probe, one ranked answer. `echoes` is the
54
+ **repetition verb**: no probe, a survey of the corpus against itself. Everything
55
+ that used to be a separate verb — `search`, `dups`, `clusters`, `concepts` — was
56
+ a _corner_ of one of those two, reached by a flag rather than a name. Five query
57
+ verbs and two lifecycle verbs now tell the whole story.
58
+
59
+ The positive product thesis, the mathematical ancestry, and the falsification
60
+ record are kept separately in
61
+ `relate/research/relate/CLAIM.md`,
62
+ `relate/research/relate/PRIOR_ART.md`, and
63
+ `relate/research/relate/TESTING.md`. This README explains the
64
+ shipped instrument; the dossier explains why compression earns each verb.
65
+
66
+ ```text
67
+ relate similar <path | path#Lnnn | text>
68
+ [--as copies|twins|shapes|any] [--unit file|function]
69
+ [--matching PAT]... [--min-grade G]
70
+ [--top N] [--json] [--no-index] [ROOT...]
71
+ THE NEIGHBOR VERB — one probe, one ranked answer. The probe's own
72
+ shape picks the question:
73
+ a PATH scores compression kinship against every other unit;
74
+ `path#Lnnn` scores the FUNCTION containing that line (and adopts
75
+ --unit function and the shapes channel, because a 40-line body
76
+ cannot fill an LZ78 dictionary the way a file can);
77
+ bare TEXT scores coding gain — recall, "which files describe this
78
+ most cheaply" — unless --as names a kinship channel, which turns
79
+ the same text into a record to compare against.
80
+ Ranking always returns rows, so each one is graded and a
81
+ background-only answer says so on stderr instead of looking like a find
82
+
83
+ relate echoes [--unit file|function|match] [--as copies|twins|shapes|any]
84
+ [--shape pairs|families|distinct]
85
+ [--max-distance T] [--min-echo E] [--min-size N]
86
+ [--min-lines N] [--min-mass N] [--include-generated]
87
+ [--matching PAT]... [--min-grade G]
88
+ [--top N] [--brief] [--json] [--no-index] [ROOT...]
89
+ THE REPETITION VERB — no probe: the corpus against itself, along
90
+ three independent axes.
91
+ --unit what a row IS file · function · match
92
+ --as which repetition copies · twins · shapes · any
93
+ --shape what the answer is FOR pairs · families · distinct
94
+ The default (`file`, `twins`, `pairs`) is the DRY signal byte kinship
95
+ cannot see: far apart in bytes, close in structure. Corners of the
96
+ same cube are the four verbs this absorbed — `--as copies` is
97
+ verified near-duplicate pairs, `+ --shape families` is their
98
+ transitive closure, `--unit function --shape families` is the same
99
+ idea cloned across files, and `--shape distinct` inverts the whole
100
+ question into "what has no kin at all?"
101
+
102
+ relate pack <text> [--matching PAT]... [--match any|all] [-F] [-i]
103
+ [--top N] [--json] [ROOT...]
104
+ the SET of files that jointly describes <text> cheapest; greedy
105
+ max-coverage over corpus-priced query chunks; each pick priced by the
106
+ bits it ADDS beyond the picks before it (anti-redundant context
107
+ assembly). With --matching, novelty is priced INSIDE the exact filter
108
+ and every pick names the patterns that admitted it
109
+
110
+ relate quote <text> [--json]
111
+ rewrite <text> as maximal verbatim quotations from the WHOLE corpus,
112
+ priced in bits; the Ziv–Merhav cross-parse on the persisted codex
113
+ shelf is O(|text|) after load; CLI latency also includes loading the
114
+ shelf and checking filesystem freshness
115
+
116
+ relate patterns -e P [-e P…] [-f FILE] [-F] [-i]
117
+ [--by pattern|file] [--under GLOB] [--top N] [--json] [ROOT...]
118
+ ONE walk, N patterns, exact per-pattern attribution, shaped
119
+ engine-side (--by groups, --under filters, --top limits)
120
+
121
+ relate index [--shelf] build + persist the kinship atlas (and, with
122
+ --shelf, the codex shelf quote reads)
123
+ relate status [--json] atlas + shelf readiness and freshness
124
+ ```
125
+
126
+ ### The names that folded
127
+
128
+ Four verbs are gone as names and intact as questions. Typing one is not an
129
+ unknown-command error — it exits 2 with the invocation that answers it:
130
+
131
+ | was | is now |
132
+ | ----------------- | ------------------------------------------------------------ |
133
+ | `search` | `relate similar <text>` |
134
+ | `dups` | `relate echoes --as copies` |
135
+ | `clusters` | `relate echoes --as copies --shape families` |
136
+ | `concepts` | `relate echoes --as shapes --shape families --unit function` |
137
+ | `irregex context` | `relate pack --matching PAT` |
138
+ | `irregex family` | `relate echoes --matching PAT` |
139
+
140
+ The last two are the composition fold: exact-then-compression is a **modifier**
141
+ (`--matching`) on the questions relate already asks, not a parallel binary of
142
+ its own.
143
+ `irregex` keeps only the two verbs that are genuinely new compositions rather
144
+ than a filtered relate query — `provenance` and `blast`.
145
+
146
+ Plus the conventions every irregex face keeps: `--help` / `--version` /
147
+ `--schema` (JSON capability manifest), results on stdout (`--json` = NDJSON),
148
+ diagnostics on stderr, unknown verbs exit 2.
149
+
150
+ ## Ergonomics: ask the question, then choose the verb
151
+
152
+ Relate is the native lane of irregex. It does not preserve grep syntax because
153
+ these are not grep-shaped questions. Its ergonomic contract is instead one
154
+ question per verb, with a small shared vocabulary for scope, result count,
155
+ machine output, and acceleration.
156
+
157
+ | If your reflex is to… | What you actually want | Native Relate choice |
158
+ | -------------------------------------------------- | ------------------------------------------- | ------------------------------------------------ |
159
+ | search several vague terms and inspect every hit | files that best explain some text | `relate similar TEXT` |
160
+ | collect a top-K list and deduplicate it by hand | a non-redundant context set | `relate pack TEXT` |
161
+ | ask where a pasted passage came from | corpus-attributed verbatim provenance | `relate quote TEXT` |
162
+ | diff one file against many candidates | nearest units to one known unit | `relate similar PATH` |
163
+ | ask whether a helper you are about to write exists | nearest FUNCTIONS to this one | `relate similar PATH#Lnnn` |
164
+ | compare likely duplicate files | verified near-duplicate pairs | `relate echoes --as copies` |
165
+ | reconnect duplicate pairs yourself | complete fork families | `relate echoes --as copies --shape families` |
166
+ | miss renamed copy-paste with byte similarity | shared structure under different vocabulary | `relate echoes` (the default) |
167
+ | find the same FUNCTION duplicated across files | function-level families | `relate echoes --unit function --shape families` |
168
+ | audit what is genuinely one-of-a-kind | the complement of every family | `relate echoes --shape distinct` |
169
+ | grep first, then reason inside the hits | compression scoped to an exact filter | `… --matching PAT` |
170
+ | run N independent exact searches | one attributed walk for N patterns | `relate patterns -e A -e B …` |
171
+
172
+ ### The default move
173
+
174
+ For humans and coding agents:
175
+
176
+ 1. Decide whether you have a **probe** or not. One thing whose neighbors you
177
+ want is `similar`; the corpus against itself is `echoes`. That single
178
+ question picks the verb, and everything after it is a flag.
179
+ 2. For `echoes`, name the three axes in the order you actually think in: what a
180
+ row IS (`--unit`), which repetition you mean (`--as`), and what you will DO
181
+ with the answer (`--shape` — `pairs` to inspect, `families` to act on,
182
+ `distinct` to audit the complement).
183
+ 3. Pass roots positionally to constrain corpus work. Use `--top N` to bound
184
+ human output and `--json` when another tool or agent will consume records.
185
+ 4. Let the atlas accelerate kinship verbs. Use `--no-index` only as the live
186
+ differential oracle, `relate status` to inspect freshness, and
187
+ `relate index --shelf` when you want both the warm atlas and quotation
188
+ shelf.
189
+ 5. Read each score in its own direction — and let the **grade** do it for you.
190
+ Lower distance is closer (`copies`/`shapes`/`any`); higher is stronger for
191
+ the `twins` gap and for `recall` coding gain; `pack` reports the marginal
192
+ bits each new choice contributes. Every row carries the band for its own
193
+ polarity, so you never have to remember which way a number runs.
194
+
195
+ ### Niche choices that change the question
196
+
197
+ - **A path probe versus a text probe:** the same verb, two different
198
+ measurements, chosen by what you handed it. A path is a **record** — it has
199
+ bytes and a skeleton, so it is compared, and the answer is a distance. Bare
200
+ text is a **query** — prose has no skeleton to compare, so a structural number
201
+ over it would be a number about nothing; it is priced instead by coding gain
202
+ against the corpus. Naming `--as` on text overrides that and says "no, treat
203
+ this snippet as a record": legitimate when you paste code and want to know
204
+ what is shaped like it.
205
+ - **A fragment probe adopts the channel its scale supports:** `path#Lnnn` moves
206
+ the unit to `function`, and with no explicit `--as` also moves the channel to
207
+ `shapes`. Measured on this corpus, a function's nearest **byte** neighbor sits
208
+ at ~0.81 — grade `none`, indistinguishable from background — while its nearest
209
+ **silhouette** neighbor sits at 0.52 and is the sibling implementation the
210
+ reader was looking for. A 40-line body cannot fill an LZ78 dictionary the way
211
+ a file can, so normalizing identifiers away is what leaves any signal at all.
212
+ - **Ranking versus a survey versus a set:** `similar` ranks candidates
213
+ independently against one probe. `echoes` has no probe — it surveys the corpus
214
+ against itself. `pack` chooses a _set_ whose members pay only for information
215
+ not already covered by earlier picks. Use `pack` for context assembly, and
216
+ `similar` when independent rank is the desired output.
217
+ - **One channel vocabulary:** every kinship verb reads the same `--as`
218
+ channel. `copies` (the default) respects vocabulary and finds copy-paste
219
+ drift; `shapes` normalizes identifiers, numbers, strings, and comments so
220
+ renamed twins surface; `twins` ranks the gap between those two, which is the
221
+ `echoes` signal; `any` accepts whichever channel sees the stronger kinship.
222
+ The metric names `bytes`/`structure`/`echo`/`fused` remain accepted as
223
+ `--lens` aliases — they are spellings of the same enum, not a second path.
224
+ `recall` is the one channel a flag cannot name: it is chosen by handing the
225
+ verb text instead of a path, because asking a file to be a query is a category
226
+ error dressed as a flag.
227
+ - **Grades, so background never reads as a hit:** ranking verbs always return
228
+ rows, which is why an answer with no real kin used to look exactly like a
229
+ find. Every score is now banded (`identical`/`strong`/`moderate`/`weak`/
230
+ `none`) against the thresholds this README documents, the band rides each
231
+ `--json` row, `--min-grade G` withholds anything weaker than `G`, and an
232
+ answer that is entirely background explains itself on stderr in gist's hint
233
+ grammar (`GIST_HINTS=0` mutes it). A trimmed but genuine answer reports what
234
+ it withheld without recanting the finding.
235
+ - **Pairs, families, and the complement:** `--shape pairs` (with
236
+ `--max-distance T`, or `--min-echo E` on the gap channel) verifies nominated
237
+ pairs at or past a threshold. Seed buckets are probabilistic and capped, so
238
+ this guarantees emitted-pair precision, not exhaustive recall. `--shape
239
+ families` returns the transitive components of that emitted graph — the unit a
240
+ restructure sweep actually acts on — and admits `--min-size N`; a family is
241
+ graded by its **loosest** edge, so one weak link cannot hide inside a strong
242
+ cluster. `--shape distinct` inverts the whole question: the units with no
243
+ admitted edge, each carrying its nearest miss as the receipt for why it is
244
+ alone. The default channel stays the `twins` gap rather than pretending
245
+ structure has one universal duplicate threshold.
246
+ - **Noise floors are per-unit, not per-verb:** a survey applies a mass floor
247
+ (files too small to fingerprint would otherwise pair with each other at
248
+ distance 0, since two empty sketches really are identical) and, at
249
+ `--unit function`, a line floor. Generated files are withheld from surveys by
250
+ default — a codegen tree is _supposed_ to repeat, and left in it drowns every
251
+ authored finding — and `--include-generated` turns that back into the question
252
+ ("did the generators drift?"). A probe keeps generated candidates, because
253
+ "what resembles this" has a legitimate generated answer.
254
+ - **Pattern attribution:** `patterns` preserves which pattern hit which line.
255
+ Use repeated `-e`, `-f FILE`, `-F`, and `-i` for matching; `--by pattern|file`
256
+ groups counts, `--under GLOB` filters paths, and `--top N` limits results
257
+ engine-side.
258
+ - **Quotation requires the shelf:** `quote` reads the whole persisted codex,
259
+ not a root-scoped live corpus. Build it with `relate index --shelf`; a stale
260
+ shelf is reported rather than silently treated as current.
261
+ - **Exact first, compression inside (`--matching`):** every query verb takes
262
+ repeated `--matching PAT` (plus `--match any|all`, `-F`, `-i`). The exact
263
+ engine narrows the corpus to a typed candidate set, and the compression
264
+ question is then asked **only inside that subset** — so the statistics are
265
+ priced against the files that matched rather than against 20k strangers, and
266
+ each pick can name the patterns that admitted it. This is the whole of what
267
+ the retired `irregex context` / `irregex family` verbs did; composition is a
268
+ modifier, not a second binary.
269
+ - **Warm coverage is verb-specific:** a text probe and `pack` nominate from
270
+ Gist's mmap-backed trigram codebook, then fold changed files through the same
271
+ freshness overlay; every kinship question reads the kinship atlas (and, at
272
+ `--unit function`, the parallel fragment atlas). Narrow explicit kinship
273
+ scopes rebuild live when that is cheaper than loading the global atlas.
274
+ Missing or corrupt acceleration changes cost, never results.
275
+ - **Corpus admission is shared with Gist:** positional roots, nested
276
+ `.gitignore` / `.ignore` / `.rgignore` precedence, hidden-file exclusion,
277
+ and freshness admission all use the same corpus-layer matcher. Relate adds
278
+ only the corpus-specific VCS/build skip list.
279
+ - **Scores are honest at the boundary:** a negative recall score means the
280
+ candidate describes the text worse than cold encoding, not an error.
281
+ `pack` reports foreign fingerprints instead of pretending the corpus covered
282
+ them, and `quote` prices unknown text rather than forcing attribution.
283
+ - **Deterministic machine use:** `--json` emits NDJSON on stdout while
284
+ diagnostics stay on stderr. Pair, family, and pattern outputs have stable
285
+ orderings, so agents should parse records instead of scraping prose.
286
+
287
+ The checked-in `relate/contract/kinship.toml` is the
288
+ versioned verb contract. The sections below explain the math, corpus policy,
289
+ and evidence behind each choice.
290
+
291
+ This directory is only the face. `repertoire.zig` declares the verb surface
292
+ once — each row carrying its usage form, its human blurb, its machine summary,
293
+ its typed flags, and the handler that runs it — and
294
+ [`surface/cli/manifest.zig`](https://github.com/The-Billy-Company/gist/blob/main/src/surface/cli/manifest.zig) renders `--help`,
295
+ `--schema`, the dispatch, the unknown-verb line, **and the process itself**
296
+ from that one table. So `main.zig` holds no surface at all: it names its
297
+ repertoire and hands over. The work lives in five sibling drivers — `probe.zig`
298
+ (the neighbor verb) · `repeat.zig` (the repetition verb) · `pack.zig` ·
299
+ `quote.zig` · `attribute.zig` — plus `lifecycle.zig`, over three shared layers
300
+ that exist precisely because the two kinship verbs used to duplicate them:
301
+ `options.zig` parses one flag vocabulary into one `Opts`, `units.zig` resolves
302
+ any `unit × warmth × optional exact filter` into one comparison table, and
303
+ `kinship.zig` holds the parallel fingerprinting and pair machinery. Scoring,
304
+ sorting, grading, and the closing verdict are shared through
305
+ [`surface/cli/grade.zig`](https://github.com/The-Billy-Company/gist/blob/main/src/surface/cli/grade.zig)'s `Sift`, so a verb contributes
306
+ only its question. The engines live under
307
+ `relate/src/kernel/kinship/`
308
+ (sketch · silhouette · concepts · lexicon · zipper),
309
+ `irregex/src/kernel/slate/`
310
+ (patterns · loom), `relate/src/kernel/codex/` (FM
311
+ math) + `relate/src/corpus/index/shelf/` (the
312
+ persisted SHLF behind `quote`), and
313
+ `relate/src/corpus/index/atlas/` (the persisted kinship
314
+ atlas behind the warm verbs).
315
+
316
+ ## The warm tier: why relate is an engine, not a shim
317
+
318
+ I persist one LZJD sketch (~1 KiB) and one structure silhouette (~2 KiB) per
319
+ corpus file into the **kinship atlas**. Then a broad `similar` or `echoes` query
320
+ can read the compressed view instead of re-reading the corpus; narrow explicit
321
+ scopes take the cheaper live path. A text probe and `pack` reuse Gist's
322
+ persisted trigram codebook for nomination and read only a bounded exact-decider
323
+ pool. `--unit function` reads a parallel **fragment atlas** (`concepts.frag`):
324
+ one structural silhouette per function fragment, folded for freshness the same
325
+ way, so function-level questions answer warm too — byte sketches are the only
326
+ live read there, and only for the fragments a byte-bearing channel actually
327
+ nominates. The committed contract is useful current-byte answers, not a timeless
328
+ speed ratio: measure both rungs on the corpus and machine you care about.
329
+
330
+ I keep the same covenant as Gist: an index is an accelerator, never an
331
+ authority. Queries fold in every file changed since the build anchor, emitted
332
+ rows are checked against deletion, and `--no-index` or missing/corrupt state
333
+ falls back to live work. The recall path's exact decider sees bounded windows
334
+ around the query evidence rather than constructing suffix automata over
335
+ multi-MiB files, so top-K latency is bounded by query and evidence-pool size
336
+ instead of the total corpus byte count.
337
+
338
+ ## Why these verbs
339
+
340
+ I kept watching agents rebuild the same workflows outside the engine. Each verb
341
+ pulls one of those loops into the kernel. Two of these were once four verb names
342
+ apiece; the question each answers is unchanged, so the argument for it is kept
343
+ under the name that now carries it:
344
+
345
+ - **`patterns`** collapses the N-run loop. The fused alternation is a
346
+ skip-only gate; it cannot by itself satisfy the real contract: a
347
+ `PatternSet` answer must equal N independent Gist runs bit for bit, with the
348
+ prefilter forced both on and off. `patterns_test.zig` gates exactness;
349
+ `bench/races/multipattern.sh` is an ad hoc throughput race, not a committed
350
+ performance certificate.
351
+ - **`pack`** answers a question independent top-K does not: ranked lists can
352
+ surface near-duplicates together, so an agent pays for the same information
353
+ K times. Coverage over corpus-priced query chunks is submodular, so the
354
+ greedy sweep is a (1−1/e)-approximation for that objective
355
+ (Nemhauser–Wolsey–Fisher 1978) and emits exact marginal-bit receipts.
356
+ Set-aware RAG is prior art too; Relate's distinction is the model-free,
357
+ auditable bit objective.
358
+ - **`similar`** makes kinship a primitive instead of a per-tool hack: hand it
359
+ one thing, get its neighbors. Byte kinship has no parser or language registry;
360
+ the structure channel adds one pan-language token squint rather than
361
+ per-language ASTs. Folding retrieval into it was not tidying — a text probe and
362
+ a path probe are the _same_ request ("what in this corpus is near this?") over
363
+ two kinds of probe, and keeping them as two verbs meant an agent had to know
364
+ which noun it held before it could ask.
365
+ - **`echoes`** is the survey shape of that primitive, and the reason it is one
366
+ verb rather than four is that `dups`, `clusters`, and `concepts` were never
367
+ different questions — they were the same comparison with a different unit, a
368
+ different channel, and a different output shape. Naming them separately forced
369
+ the caller to know which corner had been given a name (there was no
370
+ `--unit function --shape pairs` verb at all, though the question is perfectly
371
+ sensible), and it duplicated the score-sort-grade-emit-report flow four times:
372
+ `echoes.zig` and `similar.zig` sat at a 0.2180 structural gap — the second
373
+ widest in this directory — which is precisely that shared flow measured from
374
+ the outside. Its default channel reports what neither raw channel can say
375
+ alone. Byte kinship calls a renamed twin unrelated; structure distance alone
376
+ has no clean absolute threshold (measured: family-max vs cross-min overlap at
377
+ every winnow setting). The _difference_ — `echo = bytes − structure` — is
378
+ self-calibrated per pair: high echo means "far more shared shape than shared
379
+ vocabulary," the Type-2 clone an abstraction should collapse. The structure
380
+ channel is MOSS-style winnowed shingles over a normalized token stream
381
+ (identifiers→I, numbers→N, strings→S, comments dropped, pan-language keywords
382
+ kept) — one language-agnostic squint, not a per-language parse.
383
+ - **`--unit function`** drops kinship from the file to the FUNCTION. Files answer
384
+ "what forked from what?"; the finer question an agent asks is "which functions
385
+ across the tree are the same idea — the repeated engine, the duplicated JSON
386
+ dump, the copy-pasted validator — regardless of name or file?" The comparison
387
+ unit becomes the function fragment (`regions.extractAll` over authored
388
+ brace-family + Python source), so a helper cloned into six files surfaces as
389
+ one six-member family instead of hiding in six unrelated files. It reuses the
390
+ same channels, the same seed-nomination and union-find pass, and the same
391
+ warm-fold discipline — over the fragment atlas rather than the file atlas.
392
+ Families are ranked by conservative repeated-line opportunity, never a fused
393
+ similarity number, and the channels stay side by side so the reader judges the
394
+ relation.
395
+ - **`quote`** is the corpus-global tier: text the corpus knows quotes at
396
+ **0.14–0.17 bits/byte**, foreign bytes at **12.65–15.16** in the committed
397
+ scale table—an **88–94×** separation. Each phrase is attributed to an
398
+ exemplar file, with query work linear in text length
399
+ (`zig build codex-scale`, tables in
400
+ `relate/src/kernel/codex/README.md`).
401
+ - **the `recall` channel** is the retrieval shape of the same idea, and it lives
402
+ inside `similar` because that is the same request with a query for a probe:
403
+ rank files by how cheaply each would describe the text, two-stage so the exact
404
+ (expensive) decider only prices nominated candidates.
405
+ - **`--matching`** is the composition.
406
+ A hand-rolled `gist -l | relate …` pipe throws the match information away
407
+ between the two steps and then pays whole-corpus statistical noise on a subset;
408
+ narrowing inside the kernel keeps the exact and statistical scores in separate
409
+ fields, prices novelty against the candidate set, and lets each row name the
410
+ patterns that admitted it. Composed verbs of their own turned out to be the
411
+ wrong shape for this: `context` was `pack` narrowed and `family` was `echoes`
412
+ narrowed, so both are now the flag.
413
+
414
+ ## Evidence status
415
+
416
+ The proof strength is intentionally uneven and visible:
417
+
418
+ | claim | authority | status |
419
+ | --------------------------------------- | ------------------------------------------------------------- | ------------------------------------- |
420
+ | `patterns` equals N solo Gist runs | irregex `src/kernel/slate/patterns_test.zig`, prefilter on/off | gated (in the library) |
421
+ | both prefilter tiers equal that oracle | irregex `src/kernel/slate/trawl_test.zig`, each tier forced | gated (dragnet and trawl, at every N) |
422
+ | `patterns` answers the `gist -l` corpus | gist `bench/conformance/gates/parity/patterns_corpus_parity.sh` | gated (index armed and stripped) |
423
+ | warm atlas equals `--no-index` | atlas fold/deletion tests | gated |
424
+ | quote scale and bit separation | `zig build codex-scale` + codex tables | committed measurement |
425
+ | compression versus semantic embeddings | `bench/conformance/relate/knn.zig` | harness only — no labeled corpus here |
426
+ | warm latency | local comparison only | no committed timing artifact |
427
+ | echo ranking quality | heuristic + unit properties | no checked-in labeled evaluation |
428
+
429
+ The first three rows are gated in the packages that own that code — the
430
+ N-pattern slate is the library's and the corpus-parity gate is the product
431
+ chassis's — so a clone of this repo alone does not run them. The durable test
432
+ inventory is [`research/relate/TESTING.md`](https://github.com/The-Billy-Company/relate/blob/main/research/relate/TESTING.md).
433
+ Numbers without a committed artifact do not become product guarantees.
434
+
435
+ ## Corpus policy: read this before comparing to `gist`
436
+
437
+ I make two deliberate choices here, both documented at the seam:
438
+
439
+ - **relate analytics read the INDEX corpus** (every non-binary file under
440
+ the roots minus VCS/build subtrees, the same wider-than-gitignore policy
441
+ `gist index` uses), because they are corpus analytics, not per-file greps.
442
+ `gist <pattern>` keeps the rg-parity gitignore walk. The two file sets are
443
+ intentionally not identical (`verbs.zig` header).
444
+ - **`quote` reads the persisted shelf** (`relate index --shelf`, the same
445
+ artifact `gist codex build` writes; one shelf, two product faces), not a
446
+ per-invocation build: a cross-parse is only corpus-global if the index
447
+ actually spans the corpus, and an FM-index build is a lifecycle event, not
448
+ a query cost. Staleness is reported on stderr the same way `gist codex`
449
+ reports it (`quote.zig` header).
450
+
451
+ ## Research claim and prior art
452
+
453
+ I did not invent the math. The central spark was Benedetto, Caglioti, and
454
+ Loreto's
455
+ [_Language Trees and Zipping_](https://doi.org/10.1103/PhysRevLett.88.048702)
456
+ (Phys. Rev. Lett. 2002): use compressor-defined relative entropy to measure how
457
+ well one text's language describes another. That paper turned compression from
458
+ storage into comparison for me.
459
+
460
+ The positive case for files, sets, families, and provenance lives in
461
+ `relate/research/relate/CLAIM.md`. The full citation trail—LZJD,
462
+ winnowing/MOSS, Ziv–Merhav, FM-indexes, submodular selection, and the
463
+ neighboring systems we measured and left—lives in
464
+ `relate/research/relate/PRIOR_ART.md`. Exactness, atlas
465
+ identity, the embedding boundary, and reproduction commands live in
466
+ `relate/research/relate/TESTING.md`.
467
+
468
+ What is mine here is the measured composition, not the theorems. The stronger
469
+ novel-math claim in this kernel is Gist's Crest sieve
470
+ (`irregex/research/crest/PROOF.md`).
471
+
472
+ ## Layout
473
+
474
+ - `src/kernel/kinship/` - metric · cluster · recall
475
+ - `src/kernel/anatomy/` - structure silhouettes (the "shapes" channel)
476
+ - `src/kernel/codex/` - the compression codebook + FM-index (vendored libsais)
477
+ - `src/kernel/compose/` - the composed queries: blast radius,
478
+ provenance, `--matching` candidates, family, regions (the engines the
479
+ `blast` face drives)
480
+ - `src/corpus/index/{atlas,frag,shelf}/` - the persisted artifacts:
481
+ file kinship atlas, function fragment atlas, the codex shelf
482
+ - `src/exec/retrieval/` - text-probe retrieval by coding gain
483
+ - `src/exec/session/warm/` - the warm tier: fold changed files into a
484
+ persisted atlas, byte-identical to a cold rebuild
485
+ - verb surface / CLI - `src/surface/face/`
486
+
487
+ ## Install
488
+
489
+ Build from source with Zig. On Windows, the PowerShell installer builds the
490
+ binary, places it on the user PATH without elevation, and creates the atlas:
491
+
492
+ ```powershell
493
+ .\install.ps1
494
+ ```
495
+
496
+ Pass `-NoIndex` when setup should leave the corpus untouched; every query still
497
+ has the correct live path.
498
+
499
+ ## Build and test
500
+
501
+ Zig 0.16, no network; libsais builds from `vendor/libsais/` (the zon
502
+ entry is a `.lazy` url + hash pin for provenance only).
503
+
504
+ ```bash
505
+ zig build check # compile everything, run nothing
506
+ zig build test # the unit suite
507
+ zig build coverage # per-function coverage
508
+ ```
509
+
510
+ ## Using it
511
+
512
+ ```zig
513
+ // build.zig.zon
514
+ .relate = .{ .path = "../relate" }, // dev: sibling checkout
515
+ // releases pin url + hash
516
+ ```
517
+
518
+ Depends on `irregex` - the library - for the corpus walk,
519
+ the pattern engines behind `--matching`, and the shared primitives.
520
+ Architecture is machine-checked by `contract/relate.ward`.
521
+
522
+ ## Provenance
523
+
524
+ Extracted from a package path inside a private monorepo
525
+ (cut at ce430bbaab). The engine was born as the kernel's
526
+ kinship/codex tiers and split out along the tuning boundary: everything
527
+ priced against the same corpus statistics stays here, together.
528
+ Apache-2.0; `NOTICE` attributes the vendored libsais.