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.
- relate_search-1.0.0/.gitignore +45 -0
- relate_search-1.0.0/PKG-INFO +528 -0
- relate_search-1.0.0/PROJECT_README.md +493 -0
- relate_search-1.0.0/README.md +112 -0
- relate_search-1.0.0/hatch_readme.py +83 -0
- relate_search-1.0.0/pyproject.toml +141 -0
- relate_search-1.0.0/relate/README.md +19 -0
- relate_search-1.0.0/relate/__init__.py +97 -0
- relate_search-1.0.0/relate/corpus.py +257 -0
- relate_search-1.0.0/relate/kinship.py +493 -0
- relate_search-1.0.0/relate/lifecycle.py +159 -0
- relate_search-1.0.0/relate/py.typed +0 -0
- relate_search-1.0.0/relate/retrieval.py +295 -0
- relate_search-1.0.0/relate/sweep.py +160 -0
- relate_search-1.0.0/tests/README.md +9 -0
- relate_search-1.0.0/tests/test_atlas.py +112 -0
- relate_search-1.0.0/tests/test_description.py +119 -0
- relate_search-1.0.0/tests/test_grade_parity.py +195 -0
- relate_search-1.0.0/tests/test_kinship.py +499 -0
- relate_search-1.0.0/tests/test_packaging.py +254 -0
- relate_search-1.0.0/tests/test_retrieval.py +154 -0
- relate_search-1.0.0/uv.lock +182 -0
|
@@ -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.
|