rag-your-code 1.2.0__tar.gz → 1.3.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.
- {rag_your_code-1.2.0/src/rag_your_code.egg-info → rag_your_code-1.3.0}/PKG-INFO +87 -35
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/README.md +86 -34
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/pyproject.toml +1 -1
- {rag_your_code-1.2.0 → rag_your_code-1.3.0/src/rag_your_code.egg-info}/PKG-INFO +87 -35
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/__init__.py +1 -1
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/config.py +14 -6
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/search.py +1 -1
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_evidence.py +4 -3
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_metadata.py +32 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/LICENSE +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/setup.cfg +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/rag_your_code.egg-info/SOURCES.txt +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/rag_your_code.egg-info/dependency_links.txt +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/rag_your_code.egg-info/entry_points.txt +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/rag_your_code.egg-info/requires.txt +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/rag_your_code.egg-info/top_level.txt +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/agentic.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/annotate.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/cli.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/descriptions.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/document.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/embeddings.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/graph.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/indexer.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/models.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/parser.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/providers.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/py.typed +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/src/ragyourcode/workflow.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_absent_queries.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_agent_protocol.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_agentic.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_config.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_descriptions.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_doc_comments.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_document.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_e2e_cli.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_golden.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_graph_incremental.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_language_fixtures.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_large_repo.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_local_model.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_multilanguage.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_parser_edges.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_providers.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_ragyourcode.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_ranking.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_repo_queries.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_resilience.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_retrieval_correctness.py +0 -0
- {rag_your_code-1.2.0 → rag_your_code-1.3.0}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rag-your-code
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A local, explainable RAG index for codebases and coding agents
|
|
5
5
|
Author: rag-your-code contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -293,10 +293,18 @@ repository had grown by ninety units.
|
|
|
293
293
|
|
|
294
294
|
| ruler | what it represents | n | hit@1 | hit@3 | MRR |
|
|
295
295
|
|---|---|---|---|---|---|
|
|
296
|
-
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.
|
|
296
|
+
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.371 | 0.286 |
|
|
297
297
|
| **B** this repo, generated descriptions only | a cold index of familiar code | 70 | 0.314 | 0.471 | 0.383 |
|
|
298
298
|
| **C** this repo, agent-written descriptions | the warmest case supported | 70 | 0.443 | 0.614 | 0.507 |
|
|
299
299
|
|
|
300
|
+
The corpora, without which none of the above is reproducible — **A**
|
|
301
|
+
cc-enforcer, 1,345 units, `471b78f9f806`; **B** 579 units, `dca2a4656659`;
|
|
302
|
+
**C** 579 units, `0a2f050dbafd`. Ruler A's repository is one this project does
|
|
303
|
+
not control. Between the previous release and this table it grew by 78 units
|
|
304
|
+
and renamed a declaration two questions pointed at, which stopped the ruler
|
|
305
|
+
running at all until they were repointed — hit@3 0.400 → 0.371 and MRR 0.300 →
|
|
306
|
+
0.286 are that, not a change in retrieval.
|
|
307
|
+
|
|
300
308
|
**Refusal — the fourth ruler, 30 questions with no answer anywhere**
|
|
301
309
|
|
|
302
310
|
| | this repo | foreign repo |
|
|
@@ -310,25 +318,39 @@ repository had grown by ninety units.
|
|
|
310
318
|
|
|
311
319
|
| gate | A hit@1/3/MRR | B hit@1/3/MRR | C hit@1/3/MRR | silence own / foreign |
|
|
312
320
|
|---|---|---|---|---|
|
|
313
|
-
| neither (pre-1.0.0) | 0.229/0.
|
|
314
|
-
| coverage only (1.0.0) | 0.229/0.
|
|
315
|
-
| **both (1.1.0)** | **0.229/0.
|
|
321
|
+
| neither (pre-1.0.0) | 0.229/0.371/0.286 | 0.314/0.486/0.391 | 0.486/0.700/0.567 | 0.000 / 0.000 |
|
|
322
|
+
| coverage only (1.0.0) | 0.229/0.371/0.286 | 0.314/0.471/0.383 | 0.486/0.686/0.562 | 0.600 / 0.767 |
|
|
323
|
+
| **both (1.1.0)** | **0.229/0.371/0.286** | **0.314/0.471/0.383** | 0.443/0.614/0.507 | **0.967 / 0.933** |
|
|
316
324
|
|
|
317
|
-
Rulers A and B are **identical to three decimals**. The entire cost is
|
|
318
|
-
questions of seventy on the warmest ruler. On these
|
|
325
|
+
Rulers A and B are **identical to three decimals**. The entire cost is three
|
|
326
|
+
questions of seventy at hit@1 on the warmest ruler, and six at hit@3. On these
|
|
327
|
+
four rulers concentration
|
|
319
328
|
subsumes coverage — stated plainly because it is true; coverage is kept because
|
|
320
329
|
it answers a different question and names a different diagnosis.
|
|
321
330
|
|
|
322
|
-
**Latency** — warm corpus,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|---|---|
|
|
326
|
-
| query, median | **0.83 ms** |
|
|
327
|
-
| query, p95 | 1.68 ms |
|
|
328
|
-
| refusing an unanswerable query | **0.03 ms** |
|
|
331
|
+
**Latency** — warm corpus, 579 units `0a2f050dbafd`, five consecutive
|
|
332
|
+
invocations of `python -m benchmarks.query_latency --repeats 10` (420 samples
|
|
333
|
+
each):
|
|
329
334
|
|
|
330
|
-
|
|
331
|
-
|
|
335
|
+
| | | across the five |
|
|
336
|
+
|---|---|---|
|
|
337
|
+
| query, median | **0.65 ms** | 0.62 – 0.92 |
|
|
338
|
+
| query, p95 | 1.13 ms | 1.09 – 1.48 |
|
|
339
|
+
| refusing an unanswerable query | **0.022 ms** | 0.019 – 0.024 |
|
|
340
|
+
| refusal cheaper than answering by | **~35×** | 31 – 39 |
|
|
341
|
+
|
|
342
|
+
Two significant figures and a spread, because that is the precision the
|
|
343
|
+
measurement has. A second set of five taken earlier the same hour, while the
|
|
344
|
+
machine was busy, put the median at 1.0 ms and p95 at 3.0 ms; the gap between
|
|
345
|
+
the two sets is the machine, and it is wider than any change the code has ever
|
|
346
|
+
made to this number. Earlier releases published `0.83 ms / p95 1.68 ms` to
|
|
347
|
+
three figures from a script that was never committed, on a corpus that no
|
|
348
|
+
longer exists — both values sit inside today's range, which is the point: they
|
|
349
|
+
were unfalsifiable rather than wrong.
|
|
350
|
+
|
|
351
|
+
Refusal is cheap for a structural reason, not a tuned one: an unanswerable
|
|
352
|
+
query touches only the posting lists of its own distinctive words, and never
|
|
353
|
+
reaches ranking at all.
|
|
332
354
|
|
|
333
355
|
**Scale**, synthetic 10,000-unit repository (500 files):
|
|
334
356
|
|
|
@@ -350,8 +372,11 @@ touches only the posting lists of its own distinctive words, never the corpus.
|
|
|
350
372
|
| with a usable signature | **91 / 91** |
|
|
351
373
|
| units invented that do not exist | **0** |
|
|
352
374
|
|
|
353
|
-
Directional local measurements, not service levels
|
|
354
|
-
|
|
375
|
+
Directional local measurements, not service levels — but every one of them is
|
|
376
|
+
now a command rather than a memory, which two of them were not before. Each
|
|
377
|
+
prints the corpus fingerprint beside its score; quote both or neither.
|
|
378
|
+
[`benchmarks/README.md`](benchmarks/README.md) lists the five scripts and what
|
|
379
|
+
each is for.
|
|
355
380
|
|
|
356
381
|
## 7 · `rag-your-code search` vs a Grep loop
|
|
357
382
|
|
|
@@ -364,23 +389,23 @@ declaration spans.
|
|
|
364
389
|
**On a repository nobody has described, Grep wins.** That is the measured
|
|
365
390
|
result and it is not softened here.
|
|
366
391
|
|
|
367
|
-
| foreign repository · 35 questions · 1,
|
|
392
|
+
| foreign repository · 35 questions · 1,345 units `471b78f9f806` · no descriptions | Grep loop | rag-your-code |
|
|
368
393
|
|---|---|---|
|
|
369
|
-
| right file first | **
|
|
370
|
-
| right file in top 3 | **
|
|
371
|
-
| lines
|
|
372
|
-
| characters returned, all questions |
|
|
373
|
-
| questions it answers | 35 | 28 |
|
|
394
|
+
| right file first | **40.0%** | 28.6% |
|
|
395
|
+
| right file in top 3 | **62.9%** | 48.6% |
|
|
396
|
+
| lines it hands back, all questions | 8,937 | — |
|
|
397
|
+
| characters returned, all questions | 841,484 | **282,102** |
|
|
398
|
+
| questions it answers | **35** | 28 |
|
|
374
399
|
|
|
375
400
|
**Once the vocabulary exists, it is not close.**
|
|
376
401
|
|
|
377
|
-
| this repository · 70 questions ·
|
|
402
|
+
| this repository · 70 questions · 579 units `0a2f050dbafd` · 304 described | Grep loop | rag-your-code |
|
|
378
403
|
|---|---|---|
|
|
379
|
-
| right file first |
|
|
380
|
-
| right file in top 3 |
|
|
381
|
-
| lines
|
|
382
|
-
| characters returned, all questions |
|
|
383
|
-
| questions it answers |
|
|
404
|
+
| right file first | 22.9% | **58.6%** |
|
|
405
|
+
| right file in top 3 | 55.7% | **75.7%** |
|
|
406
|
+
| lines it hands back, all questions | 11,636 | — |
|
|
407
|
+
| characters returned, all questions | 1,101,618 | **621,837** |
|
|
408
|
+
| questions it answers | **61** | 60 |
|
|
384
409
|
|
|
385
410
|
Those two tables are the whole argument of section 3.3, measured against a real
|
|
386
411
|
baseline instead of asserted. A cold index retrieves against a sentence the
|
|
@@ -390,15 +415,32 @@ to guess which of the matching files is the definition. Descriptions put words
|
|
|
390
415
|
in the index that the source never contained, and first-place accuracy goes
|
|
391
416
|
from below Grep's to **more than double** it.
|
|
392
417
|
|
|
393
|
-
|
|
418
|
+
**Both tables come from `python -m benchmarks.grep_baseline`**, which is what
|
|
419
|
+
changed in 1.3.0. Until then this section — the strongest claim the project
|
|
420
|
+
makes — was published from a script that had never been committed, so nothing
|
|
421
|
+
here could be checked and the word "Grep loop" had no precise meaning. The
|
|
422
|
+
committed version defines it: take the query's words, drop the ones the corpus
|
|
423
|
+
itself shows are everywhere, run one substring search per remaining word over
|
|
424
|
+
exactly the files the index was built from, rank each file by how many distinct
|
|
425
|
+
words hit it, break ties on path. Reconstructing it reproduced this side's
|
|
426
|
+
figures exactly and moved Grep's, which is the expected shape — the ranked arm
|
|
427
|
+
was always a call into shipped code, and the baseline never was.
|
|
428
|
+
|
|
429
|
+
Four qualifications, because the table would otherwise flatter both sides:
|
|
394
430
|
|
|
395
431
|
- **Scored at file granularity**, which understates this side. A Grep hit is a
|
|
396
432
|
file; a hit here is a declaration with an exact span, a score, and the words
|
|
397
433
|
it matched on. The agent that reads the result opens 40 lines, not a file.
|
|
398
|
-
- **
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
434
|
+
- **Dropping the corpus-common words is generous to Grep**, and it is what
|
|
435
|
+
makes the baseline a fair one rather than a straw man: an agent that greps
|
|
436
|
+
`the` gets every file back in no order. It is also why Grep declines nine of
|
|
437
|
+
the seventy questions here — those had no word left that this corpus does not
|
|
438
|
+
use everywhere.
|
|
439
|
+
- **Payload is counted in characters on both sides.** Grep hands back 18,000
|
|
440
|
+
characters per question it answers, unranked and without spans; this returns
|
|
441
|
+
10,400, ranked, capped by `search.max_chars`. That is a factor of 1.7, not
|
|
442
|
+
the order of magnitude an earlier version of this table implied by counting
|
|
443
|
+
one side in lines and the other in characters. Seven of 35 and ten of 70
|
|
402
444
|
questions come back empty here instead, with a reason.
|
|
403
445
|
- **Grep wins outright when you know the string.** `grep -rn "COMMON_TERM"` is
|
|
404
446
|
exact, instant and complete, and nothing here replaces it.
|
|
@@ -651,6 +693,16 @@ need a dependency, and the policy for those is settled: they follow the
|
|
|
651
693
|
embedding provider's pattern — optional, user-selected, never in the default
|
|
652
694
|
install. Full reasoning in [docs/ROADMAP.md](docs/ROADMAP.md).
|
|
653
695
|
|
|
696
|
+
**Whether the skill fires unprompted is not measured**, and until 1.2.1 this
|
|
697
|
+
project claimed no command could measure it. That was wrong: `claude plugin
|
|
698
|
+
eval` grades exactly this, with `tool_used: Skill` as a plugin-fired indicator
|
|
699
|
+
and a no-plugin baseline arm. It is unmeasured because the command is in early
|
|
700
|
+
access on the account here and its case schema is undocumented, so a suite
|
|
701
|
+
written from `--help` fragments could not be run even once to see whether it
|
|
702
|
+
loads — and a suite that silently fails to load reads as a gate while checking
|
|
703
|
+
nothing. Since 1.2.0 the four commands give an entry path that does not depend
|
|
704
|
+
on it.
|
|
705
|
+
|
|
654
706
|
## 12 · Development
|
|
655
707
|
|
|
656
708
|
```bash
|
|
@@ -264,10 +264,18 @@ repository had grown by ninety units.
|
|
|
264
264
|
|
|
265
265
|
| ruler | what it represents | n | hit@1 | hit@3 | MRR |
|
|
266
266
|
|---|---|---|---|---|---|
|
|
267
|
-
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.
|
|
267
|
+
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.371 | 0.286 |
|
|
268
268
|
| **B** this repo, generated descriptions only | a cold index of familiar code | 70 | 0.314 | 0.471 | 0.383 |
|
|
269
269
|
| **C** this repo, agent-written descriptions | the warmest case supported | 70 | 0.443 | 0.614 | 0.507 |
|
|
270
270
|
|
|
271
|
+
The corpora, without which none of the above is reproducible — **A**
|
|
272
|
+
cc-enforcer, 1,345 units, `471b78f9f806`; **B** 579 units, `dca2a4656659`;
|
|
273
|
+
**C** 579 units, `0a2f050dbafd`. Ruler A's repository is one this project does
|
|
274
|
+
not control. Between the previous release and this table it grew by 78 units
|
|
275
|
+
and renamed a declaration two questions pointed at, which stopped the ruler
|
|
276
|
+
running at all until they were repointed — hit@3 0.400 → 0.371 and MRR 0.300 →
|
|
277
|
+
0.286 are that, not a change in retrieval.
|
|
278
|
+
|
|
271
279
|
**Refusal — the fourth ruler, 30 questions with no answer anywhere**
|
|
272
280
|
|
|
273
281
|
| | this repo | foreign repo |
|
|
@@ -281,25 +289,39 @@ repository had grown by ninety units.
|
|
|
281
289
|
|
|
282
290
|
| gate | A hit@1/3/MRR | B hit@1/3/MRR | C hit@1/3/MRR | silence own / foreign |
|
|
283
291
|
|---|---|---|---|---|
|
|
284
|
-
| neither (pre-1.0.0) | 0.229/0.
|
|
285
|
-
| coverage only (1.0.0) | 0.229/0.
|
|
286
|
-
| **both (1.1.0)** | **0.229/0.
|
|
292
|
+
| neither (pre-1.0.0) | 0.229/0.371/0.286 | 0.314/0.486/0.391 | 0.486/0.700/0.567 | 0.000 / 0.000 |
|
|
293
|
+
| coverage only (1.0.0) | 0.229/0.371/0.286 | 0.314/0.471/0.383 | 0.486/0.686/0.562 | 0.600 / 0.767 |
|
|
294
|
+
| **both (1.1.0)** | **0.229/0.371/0.286** | **0.314/0.471/0.383** | 0.443/0.614/0.507 | **0.967 / 0.933** |
|
|
287
295
|
|
|
288
|
-
Rulers A and B are **identical to three decimals**. The entire cost is
|
|
289
|
-
questions of seventy on the warmest ruler. On these
|
|
296
|
+
Rulers A and B are **identical to three decimals**. The entire cost is three
|
|
297
|
+
questions of seventy at hit@1 on the warmest ruler, and six at hit@3. On these
|
|
298
|
+
four rulers concentration
|
|
290
299
|
subsumes coverage — stated plainly because it is true; coverage is kept because
|
|
291
300
|
it answers a different question and names a different diagnosis.
|
|
292
301
|
|
|
293
|
-
**Latency** — warm corpus,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|---|---|
|
|
297
|
-
| query, median | **0.83 ms** |
|
|
298
|
-
| query, p95 | 1.68 ms |
|
|
299
|
-
| refusing an unanswerable query | **0.03 ms** |
|
|
302
|
+
**Latency** — warm corpus, 579 units `0a2f050dbafd`, five consecutive
|
|
303
|
+
invocations of `python -m benchmarks.query_latency --repeats 10` (420 samples
|
|
304
|
+
each):
|
|
300
305
|
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
| | | across the five |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| query, median | **0.65 ms** | 0.62 – 0.92 |
|
|
309
|
+
| query, p95 | 1.13 ms | 1.09 – 1.48 |
|
|
310
|
+
| refusing an unanswerable query | **0.022 ms** | 0.019 – 0.024 |
|
|
311
|
+
| refusal cheaper than answering by | **~35×** | 31 – 39 |
|
|
312
|
+
|
|
313
|
+
Two significant figures and a spread, because that is the precision the
|
|
314
|
+
measurement has. A second set of five taken earlier the same hour, while the
|
|
315
|
+
machine was busy, put the median at 1.0 ms and p95 at 3.0 ms; the gap between
|
|
316
|
+
the two sets is the machine, and it is wider than any change the code has ever
|
|
317
|
+
made to this number. Earlier releases published `0.83 ms / p95 1.68 ms` to
|
|
318
|
+
three figures from a script that was never committed, on a corpus that no
|
|
319
|
+
longer exists — both values sit inside today's range, which is the point: they
|
|
320
|
+
were unfalsifiable rather than wrong.
|
|
321
|
+
|
|
322
|
+
Refusal is cheap for a structural reason, not a tuned one: an unanswerable
|
|
323
|
+
query touches only the posting lists of its own distinctive words, and never
|
|
324
|
+
reaches ranking at all.
|
|
303
325
|
|
|
304
326
|
**Scale**, synthetic 10,000-unit repository (500 files):
|
|
305
327
|
|
|
@@ -321,8 +343,11 @@ touches only the posting lists of its own distinctive words, never the corpus.
|
|
|
321
343
|
| with a usable signature | **91 / 91** |
|
|
322
344
|
| units invented that do not exist | **0** |
|
|
323
345
|
|
|
324
|
-
Directional local measurements, not service levels
|
|
325
|
-
|
|
346
|
+
Directional local measurements, not service levels — but every one of them is
|
|
347
|
+
now a command rather than a memory, which two of them were not before. Each
|
|
348
|
+
prints the corpus fingerprint beside its score; quote both or neither.
|
|
349
|
+
[`benchmarks/README.md`](benchmarks/README.md) lists the five scripts and what
|
|
350
|
+
each is for.
|
|
326
351
|
|
|
327
352
|
## 7 · `rag-your-code search` vs a Grep loop
|
|
328
353
|
|
|
@@ -335,23 +360,23 @@ declaration spans.
|
|
|
335
360
|
**On a repository nobody has described, Grep wins.** That is the measured
|
|
336
361
|
result and it is not softened here.
|
|
337
362
|
|
|
338
|
-
| foreign repository · 35 questions · 1,
|
|
363
|
+
| foreign repository · 35 questions · 1,345 units `471b78f9f806` · no descriptions | Grep loop | rag-your-code |
|
|
339
364
|
|---|---|---|
|
|
340
|
-
| right file first | **
|
|
341
|
-
| right file in top 3 | **
|
|
342
|
-
| lines
|
|
343
|
-
| characters returned, all questions |
|
|
344
|
-
| questions it answers | 35 | 28 |
|
|
365
|
+
| right file first | **40.0%** | 28.6% |
|
|
366
|
+
| right file in top 3 | **62.9%** | 48.6% |
|
|
367
|
+
| lines it hands back, all questions | 8,937 | — |
|
|
368
|
+
| characters returned, all questions | 841,484 | **282,102** |
|
|
369
|
+
| questions it answers | **35** | 28 |
|
|
345
370
|
|
|
346
371
|
**Once the vocabulary exists, it is not close.**
|
|
347
372
|
|
|
348
|
-
| this repository · 70 questions ·
|
|
373
|
+
| this repository · 70 questions · 579 units `0a2f050dbafd` · 304 described | Grep loop | rag-your-code |
|
|
349
374
|
|---|---|---|
|
|
350
|
-
| right file first |
|
|
351
|
-
| right file in top 3 |
|
|
352
|
-
| lines
|
|
353
|
-
| characters returned, all questions |
|
|
354
|
-
| questions it answers |
|
|
375
|
+
| right file first | 22.9% | **58.6%** |
|
|
376
|
+
| right file in top 3 | 55.7% | **75.7%** |
|
|
377
|
+
| lines it hands back, all questions | 11,636 | — |
|
|
378
|
+
| characters returned, all questions | 1,101,618 | **621,837** |
|
|
379
|
+
| questions it answers | **61** | 60 |
|
|
355
380
|
|
|
356
381
|
Those two tables are the whole argument of section 3.3, measured against a real
|
|
357
382
|
baseline instead of asserted. A cold index retrieves against a sentence the
|
|
@@ -361,15 +386,32 @@ to guess which of the matching files is the definition. Descriptions put words
|
|
|
361
386
|
in the index that the source never contained, and first-place accuracy goes
|
|
362
387
|
from below Grep's to **more than double** it.
|
|
363
388
|
|
|
364
|
-
|
|
389
|
+
**Both tables come from `python -m benchmarks.grep_baseline`**, which is what
|
|
390
|
+
changed in 1.3.0. Until then this section — the strongest claim the project
|
|
391
|
+
makes — was published from a script that had never been committed, so nothing
|
|
392
|
+
here could be checked and the word "Grep loop" had no precise meaning. The
|
|
393
|
+
committed version defines it: take the query's words, drop the ones the corpus
|
|
394
|
+
itself shows are everywhere, run one substring search per remaining word over
|
|
395
|
+
exactly the files the index was built from, rank each file by how many distinct
|
|
396
|
+
words hit it, break ties on path. Reconstructing it reproduced this side's
|
|
397
|
+
figures exactly and moved Grep's, which is the expected shape — the ranked arm
|
|
398
|
+
was always a call into shipped code, and the baseline never was.
|
|
399
|
+
|
|
400
|
+
Four qualifications, because the table would otherwise flatter both sides:
|
|
365
401
|
|
|
366
402
|
- **Scored at file granularity**, which understates this side. A Grep hit is a
|
|
367
403
|
file; a hit here is a declaration with an exact span, a score, and the words
|
|
368
404
|
it matched on. The agent that reads the result opens 40 lines, not a file.
|
|
369
|
-
- **
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
405
|
+
- **Dropping the corpus-common words is generous to Grep**, and it is what
|
|
406
|
+
makes the baseline a fair one rather than a straw man: an agent that greps
|
|
407
|
+
`the` gets every file back in no order. It is also why Grep declines nine of
|
|
408
|
+
the seventy questions here — those had no word left that this corpus does not
|
|
409
|
+
use everywhere.
|
|
410
|
+
- **Payload is counted in characters on both sides.** Grep hands back 18,000
|
|
411
|
+
characters per question it answers, unranked and without spans; this returns
|
|
412
|
+
10,400, ranked, capped by `search.max_chars`. That is a factor of 1.7, not
|
|
413
|
+
the order of magnitude an earlier version of this table implied by counting
|
|
414
|
+
one side in lines and the other in characters. Seven of 35 and ten of 70
|
|
373
415
|
questions come back empty here instead, with a reason.
|
|
374
416
|
- **Grep wins outright when you know the string.** `grep -rn "COMMON_TERM"` is
|
|
375
417
|
exact, instant and complete, and nothing here replaces it.
|
|
@@ -622,6 +664,16 @@ need a dependency, and the policy for those is settled: they follow the
|
|
|
622
664
|
embedding provider's pattern — optional, user-selected, never in the default
|
|
623
665
|
install. Full reasoning in [docs/ROADMAP.md](docs/ROADMAP.md).
|
|
624
666
|
|
|
667
|
+
**Whether the skill fires unprompted is not measured**, and until 1.2.1 this
|
|
668
|
+
project claimed no command could measure it. That was wrong: `claude plugin
|
|
669
|
+
eval` grades exactly this, with `tool_used: Skill` as a plugin-fired indicator
|
|
670
|
+
and a no-plugin baseline arm. It is unmeasured because the command is in early
|
|
671
|
+
access on the account here and its case schema is undocumented, so a suite
|
|
672
|
+
written from `--help` fragments could not be run even once to see whether it
|
|
673
|
+
loads — and a suite that silently fails to load reads as a gate while checking
|
|
674
|
+
nothing. Since 1.2.0 the four commands give an entry path that does not depend
|
|
675
|
+
on it.
|
|
676
|
+
|
|
625
677
|
## 12 · Development
|
|
626
678
|
|
|
627
679
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rag-your-code
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A local, explainable RAG index for codebases and coding agents
|
|
5
5
|
Author: rag-your-code contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -293,10 +293,18 @@ repository had grown by ninety units.
|
|
|
293
293
|
|
|
294
294
|
| ruler | what it represents | n | hit@1 | hit@3 | MRR |
|
|
295
295
|
|---|---|---|---|---|---|
|
|
296
|
-
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.
|
|
296
|
+
| **A** foreign repo, no descriptions | what a first-time user gets | 35 | 0.229 | 0.371 | 0.286 |
|
|
297
297
|
| **B** this repo, generated descriptions only | a cold index of familiar code | 70 | 0.314 | 0.471 | 0.383 |
|
|
298
298
|
| **C** this repo, agent-written descriptions | the warmest case supported | 70 | 0.443 | 0.614 | 0.507 |
|
|
299
299
|
|
|
300
|
+
The corpora, without which none of the above is reproducible — **A**
|
|
301
|
+
cc-enforcer, 1,345 units, `471b78f9f806`; **B** 579 units, `dca2a4656659`;
|
|
302
|
+
**C** 579 units, `0a2f050dbafd`. Ruler A's repository is one this project does
|
|
303
|
+
not control. Between the previous release and this table it grew by 78 units
|
|
304
|
+
and renamed a declaration two questions pointed at, which stopped the ruler
|
|
305
|
+
running at all until they were repointed — hit@3 0.400 → 0.371 and MRR 0.300 →
|
|
306
|
+
0.286 are that, not a change in retrieval.
|
|
307
|
+
|
|
300
308
|
**Refusal — the fourth ruler, 30 questions with no answer anywhere**
|
|
301
309
|
|
|
302
310
|
| | this repo | foreign repo |
|
|
@@ -310,25 +318,39 @@ repository had grown by ninety units.
|
|
|
310
318
|
|
|
311
319
|
| gate | A hit@1/3/MRR | B hit@1/3/MRR | C hit@1/3/MRR | silence own / foreign |
|
|
312
320
|
|---|---|---|---|---|
|
|
313
|
-
| neither (pre-1.0.0) | 0.229/0.
|
|
314
|
-
| coverage only (1.0.0) | 0.229/0.
|
|
315
|
-
| **both (1.1.0)** | **0.229/0.
|
|
321
|
+
| neither (pre-1.0.0) | 0.229/0.371/0.286 | 0.314/0.486/0.391 | 0.486/0.700/0.567 | 0.000 / 0.000 |
|
|
322
|
+
| coverage only (1.0.0) | 0.229/0.371/0.286 | 0.314/0.471/0.383 | 0.486/0.686/0.562 | 0.600 / 0.767 |
|
|
323
|
+
| **both (1.1.0)** | **0.229/0.371/0.286** | **0.314/0.471/0.383** | 0.443/0.614/0.507 | **0.967 / 0.933** |
|
|
316
324
|
|
|
317
|
-
Rulers A and B are **identical to three decimals**. The entire cost is
|
|
318
|
-
questions of seventy on the warmest ruler. On these
|
|
325
|
+
Rulers A and B are **identical to three decimals**. The entire cost is three
|
|
326
|
+
questions of seventy at hit@1 on the warmest ruler, and six at hit@3. On these
|
|
327
|
+
four rulers concentration
|
|
319
328
|
subsumes coverage — stated plainly because it is true; coverage is kept because
|
|
320
329
|
it answers a different question and names a different diagnosis.
|
|
321
330
|
|
|
322
|
-
**Latency** — warm corpus,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|---|---|
|
|
326
|
-
| query, median | **0.83 ms** |
|
|
327
|
-
| query, p95 | 1.68 ms |
|
|
328
|
-
| refusing an unanswerable query | **0.03 ms** |
|
|
331
|
+
**Latency** — warm corpus, 579 units `0a2f050dbafd`, five consecutive
|
|
332
|
+
invocations of `python -m benchmarks.query_latency --repeats 10` (420 samples
|
|
333
|
+
each):
|
|
329
334
|
|
|
330
|
-
|
|
331
|
-
|
|
335
|
+
| | | across the five |
|
|
336
|
+
|---|---|---|
|
|
337
|
+
| query, median | **0.65 ms** | 0.62 – 0.92 |
|
|
338
|
+
| query, p95 | 1.13 ms | 1.09 – 1.48 |
|
|
339
|
+
| refusing an unanswerable query | **0.022 ms** | 0.019 – 0.024 |
|
|
340
|
+
| refusal cheaper than answering by | **~35×** | 31 – 39 |
|
|
341
|
+
|
|
342
|
+
Two significant figures and a spread, because that is the precision the
|
|
343
|
+
measurement has. A second set of five taken earlier the same hour, while the
|
|
344
|
+
machine was busy, put the median at 1.0 ms and p95 at 3.0 ms; the gap between
|
|
345
|
+
the two sets is the machine, and it is wider than any change the code has ever
|
|
346
|
+
made to this number. Earlier releases published `0.83 ms / p95 1.68 ms` to
|
|
347
|
+
three figures from a script that was never committed, on a corpus that no
|
|
348
|
+
longer exists — both values sit inside today's range, which is the point: they
|
|
349
|
+
were unfalsifiable rather than wrong.
|
|
350
|
+
|
|
351
|
+
Refusal is cheap for a structural reason, not a tuned one: an unanswerable
|
|
352
|
+
query touches only the posting lists of its own distinctive words, and never
|
|
353
|
+
reaches ranking at all.
|
|
332
354
|
|
|
333
355
|
**Scale**, synthetic 10,000-unit repository (500 files):
|
|
334
356
|
|
|
@@ -350,8 +372,11 @@ touches only the posting lists of its own distinctive words, never the corpus.
|
|
|
350
372
|
| with a usable signature | **91 / 91** |
|
|
351
373
|
| units invented that do not exist | **0** |
|
|
352
374
|
|
|
353
|
-
Directional local measurements, not service levels
|
|
354
|
-
|
|
375
|
+
Directional local measurements, not service levels — but every one of them is
|
|
376
|
+
now a command rather than a memory, which two of them were not before. Each
|
|
377
|
+
prints the corpus fingerprint beside its score; quote both or neither.
|
|
378
|
+
[`benchmarks/README.md`](benchmarks/README.md) lists the five scripts and what
|
|
379
|
+
each is for.
|
|
355
380
|
|
|
356
381
|
## 7 · `rag-your-code search` vs a Grep loop
|
|
357
382
|
|
|
@@ -364,23 +389,23 @@ declaration spans.
|
|
|
364
389
|
**On a repository nobody has described, Grep wins.** That is the measured
|
|
365
390
|
result and it is not softened here.
|
|
366
391
|
|
|
367
|
-
| foreign repository · 35 questions · 1,
|
|
392
|
+
| foreign repository · 35 questions · 1,345 units `471b78f9f806` · no descriptions | Grep loop | rag-your-code |
|
|
368
393
|
|---|---|---|
|
|
369
|
-
| right file first | **
|
|
370
|
-
| right file in top 3 | **
|
|
371
|
-
| lines
|
|
372
|
-
| characters returned, all questions |
|
|
373
|
-
| questions it answers | 35 | 28 |
|
|
394
|
+
| right file first | **40.0%** | 28.6% |
|
|
395
|
+
| right file in top 3 | **62.9%** | 48.6% |
|
|
396
|
+
| lines it hands back, all questions | 8,937 | — |
|
|
397
|
+
| characters returned, all questions | 841,484 | **282,102** |
|
|
398
|
+
| questions it answers | **35** | 28 |
|
|
374
399
|
|
|
375
400
|
**Once the vocabulary exists, it is not close.**
|
|
376
401
|
|
|
377
|
-
| this repository · 70 questions ·
|
|
402
|
+
| this repository · 70 questions · 579 units `0a2f050dbafd` · 304 described | Grep loop | rag-your-code |
|
|
378
403
|
|---|---|---|
|
|
379
|
-
| right file first |
|
|
380
|
-
| right file in top 3 |
|
|
381
|
-
| lines
|
|
382
|
-
| characters returned, all questions |
|
|
383
|
-
| questions it answers |
|
|
404
|
+
| right file first | 22.9% | **58.6%** |
|
|
405
|
+
| right file in top 3 | 55.7% | **75.7%** |
|
|
406
|
+
| lines it hands back, all questions | 11,636 | — |
|
|
407
|
+
| characters returned, all questions | 1,101,618 | **621,837** |
|
|
408
|
+
| questions it answers | **61** | 60 |
|
|
384
409
|
|
|
385
410
|
Those two tables are the whole argument of section 3.3, measured against a real
|
|
386
411
|
baseline instead of asserted. A cold index retrieves against a sentence the
|
|
@@ -390,15 +415,32 @@ to guess which of the matching files is the definition. Descriptions put words
|
|
|
390
415
|
in the index that the source never contained, and first-place accuracy goes
|
|
391
416
|
from below Grep's to **more than double** it.
|
|
392
417
|
|
|
393
|
-
|
|
418
|
+
**Both tables come from `python -m benchmarks.grep_baseline`**, which is what
|
|
419
|
+
changed in 1.3.0. Until then this section — the strongest claim the project
|
|
420
|
+
makes — was published from a script that had never been committed, so nothing
|
|
421
|
+
here could be checked and the word "Grep loop" had no precise meaning. The
|
|
422
|
+
committed version defines it: take the query's words, drop the ones the corpus
|
|
423
|
+
itself shows are everywhere, run one substring search per remaining word over
|
|
424
|
+
exactly the files the index was built from, rank each file by how many distinct
|
|
425
|
+
words hit it, break ties on path. Reconstructing it reproduced this side's
|
|
426
|
+
figures exactly and moved Grep's, which is the expected shape — the ranked arm
|
|
427
|
+
was always a call into shipped code, and the baseline never was.
|
|
428
|
+
|
|
429
|
+
Four qualifications, because the table would otherwise flatter both sides:
|
|
394
430
|
|
|
395
431
|
- **Scored at file granularity**, which understates this side. A Grep hit is a
|
|
396
432
|
file; a hit here is a declaration with an exact span, a score, and the words
|
|
397
433
|
it matched on. The agent that reads the result opens 40 lines, not a file.
|
|
398
|
-
- **
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
434
|
+
- **Dropping the corpus-common words is generous to Grep**, and it is what
|
|
435
|
+
makes the baseline a fair one rather than a straw man: an agent that greps
|
|
436
|
+
`the` gets every file back in no order. It is also why Grep declines nine of
|
|
437
|
+
the seventy questions here — those had no word left that this corpus does not
|
|
438
|
+
use everywhere.
|
|
439
|
+
- **Payload is counted in characters on both sides.** Grep hands back 18,000
|
|
440
|
+
characters per question it answers, unranked and without spans; this returns
|
|
441
|
+
10,400, ranked, capped by `search.max_chars`. That is a factor of 1.7, not
|
|
442
|
+
the order of magnitude an earlier version of this table implied by counting
|
|
443
|
+
one side in lines and the other in characters. Seven of 35 and ten of 70
|
|
402
444
|
questions come back empty here instead, with a reason.
|
|
403
445
|
- **Grep wins outright when you know the string.** `grep -rn "COMMON_TERM"` is
|
|
404
446
|
exact, instant and complete, and nothing here replaces it.
|
|
@@ -651,6 +693,16 @@ need a dependency, and the policy for those is settled: they follow the
|
|
|
651
693
|
embedding provider's pattern — optional, user-selected, never in the default
|
|
652
694
|
install. Full reasoning in [docs/ROADMAP.md](docs/ROADMAP.md).
|
|
653
695
|
|
|
696
|
+
**Whether the skill fires unprompted is not measured**, and until 1.2.1 this
|
|
697
|
+
project claimed no command could measure it. That was wrong: `claude plugin
|
|
698
|
+
eval` grades exactly this, with `tool_used: Skill` as a plugin-fired indicator
|
|
699
|
+
and a no-plugin baseline arm. It is unmeasured because the command is in early
|
|
700
|
+
access on the account here and its case schema is undocumented, so a suite
|
|
701
|
+
written from `--help` fragments could not be run even once to see whether it
|
|
702
|
+
loads — and a suite that silently fails to load reads as a gate while checking
|
|
703
|
+
nothing. Since 1.2.0 the four commands give an entry path that does not depend
|
|
704
|
+
on it.
|
|
705
|
+
|
|
654
706
|
## 12 · Development
|
|
655
707
|
|
|
656
708
|
```bash
|
|
@@ -201,10 +201,15 @@ SETTINGS: tuple[Setting, ...] = (
|
|
|
201
201
|
),
|
|
202
202
|
# How much of a question has to reach the index before an answer counts as
|
|
203
203
|
# evidence rather than as a guess. Measured, not chosen: across two
|
|
204
|
-
# repositories, two languages and
|
|
205
|
-
# at which every question that was being answered
|
|
206
|
-
#
|
|
207
|
-
#
|
|
204
|
+
# repositories, two languages and every question set under `benchmarks/`,
|
|
205
|
+
# 0.40 is the largest value at which every question that was being answered
|
|
206
|
+
# correctly still is. On its own it silences three fifths of the questions
|
|
207
|
+
# whose answer is not in the repository at all; the rest is what
|
|
208
|
+
# `search.min_concentration` below adds. The command is the claim --
|
|
209
|
+
# `repo_queries --questions benchmarks/absent_queries.json
|
|
210
|
+
# --min-concentration 0` -- because a count typed into a comment is a figure
|
|
211
|
+
# nothing checks, and both numbers this sentence used to carry had rotted.
|
|
212
|
+
# It is a ratio inside the query, so unlike a score threshold it does
|
|
208
213
|
# not move when the corpus or the scale of the ranking does -- the defect
|
|
209
214
|
# that made `confidence_threshold = 0.8` stop meaning anything.
|
|
210
215
|
Setting(
|
|
@@ -221,8 +226,11 @@ SETTINGS: tuple[Setting, ...] = (
|
|
|
221
226
|
# unrelated declarations -- four of six words found in four places with
|
|
222
227
|
# nothing to do with one another or with what was asked. Measured across
|
|
223
228
|
# four rulers, requiring a quarter of a query's rarity to land inside one
|
|
224
|
-
# unit leaves the two rulers over undescribed code unchanged and
|
|
225
|
-
#
|
|
229
|
+
# unit leaves the two rulers over undescribed code unchanged and removes
|
|
230
|
+
# most of what the coverage bar alone still answers; the ablation table
|
|
231
|
+
# carries the numbers, in docs/ROADMAP.md, rather than this line, which
|
|
232
|
+
# said "roughly halves" while that table said an order of magnitude.
|
|
233
|
+
# Rarity-
|
|
226
234
|
# weighted rather than counted, because a unit holding two ordinary words is
|
|
227
235
|
# not better evidence than one holding the rare word the question is about.
|
|
228
236
|
Setting(
|
|
@@ -119,7 +119,7 @@ class Evidence:
|
|
|
119
119
|
|
|
120
120
|
Ranking answers "which of these is best". It cannot answer "is any of this
|
|
121
121
|
an answer", and reading the first as the second is what let a repository
|
|
122
|
-
reply to
|
|
122
|
+
reply to every question in `benchmarks/absent_queries.json` -- each one
|
|
123
123
|
about a subject neither repository implements. `where are CUDA kernels
|
|
124
124
|
dispatched to the device` came back with a test about word counting, on the
|
|
125
125
|
evidence of `are`, `the`, `to` and `where`. That is not a Chinese problem
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Retrieval must be able to say it has no answer.
|
|
2
2
|
|
|
3
3
|
Ranking always produces a least-bad unit and returns it with a score and a
|
|
4
|
-
rank, which read exactly like an answer. Graded against
|
|
5
|
-
subjects neither this repository nor
|
|
6
|
-
repository implements
|
|
4
|
+
rank, which read exactly like an answer. Graded against every question in
|
|
5
|
+
`benchmarks/absent_queries.json` -- subjects neither this repository nor
|
|
6
|
+
`benchmarks/cold_queries.json`'s repository implements -- every single one came
|
|
7
|
+
back answered, on both repositories. `where are CUDA
|
|
7
8
|
kernels dispatched to the device` on the evidence of `are`, `the`, `to` and
|
|
8
9
|
`where`. These assert the second question retrieval now asks: not which unit
|
|
9
10
|
ranks highest, but whether any of this is evidence at all.
|
|
@@ -321,3 +321,35 @@ def test_every_documented_provider_block_actually_configures_that_provider(tmp_p
|
|
|
321
321
|
# arrangement exists to prevent.
|
|
322
322
|
assert "sk-" not in settings
|
|
323
323
|
assert {"sentence-transformers", "openai-compatible"} <= seen, f"undocumented providers; README shows {sorted(seen)}"
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# --- a measurement nobody can re-run is a claim, not a measurement ----------
|
|
327
|
+
|
|
328
|
+
BENCHMARKS = tuple(
|
|
329
|
+
sorted(
|
|
330
|
+
path.name
|
|
331
|
+
for path in (ROOT / "benchmarks").glob("*.py")
|
|
332
|
+
if path.name != "__init__.py"
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def test_every_benchmark_script_is_listed_in_its_own_index():
|
|
338
|
+
"""The index of the rulers is discovered, not maintained by hand.
|
|
339
|
+
|
|
340
|
+
1.3.0 found two figures in the README -- query latency, and the entire Grep
|
|
341
|
+
head-to-head -- produced by scripts that had never been committed. Nobody
|
|
342
|
+
could re-derive either, and the second one meant the phrase "a Grep loop"
|
|
343
|
+
had no definition a reader could argue with.
|
|
344
|
+
|
|
345
|
+
Both directions matter. A script absent from the index is one nobody knows
|
|
346
|
+
to run; a command in the index naming a script that does not exist is the
|
|
347
|
+
install-line defect this repository shipped twice. Discovery by glob is
|
|
348
|
+
what keeps the seventh script from being the one nothing checks.
|
|
349
|
+
"""
|
|
350
|
+
index = (ROOT / "benchmarks" / "README.md").read_text(encoding="utf-8")
|
|
351
|
+
assert BENCHMARKS, "benchmarks/ holds no scripts; this guard would pass vacuously"
|
|
352
|
+
for name in BENCHMARKS:
|
|
353
|
+
assert f"benchmarks.{Path(name).stem}" in index, f"{name} is in benchmarks/ and not in its README"
|
|
354
|
+
for module in re.findall(r"benchmarks\.([a-z_]+)", index):
|
|
355
|
+
assert (ROOT / "benchmarks" / f"{module}.py").is_file(), f"the README runs benchmarks.{module}, which does not exist"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|