Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ set(_ripwire_query_names
java
javascript
json
gdscript
kotlin
lua
objc
Expand Down Expand Up @@ -474,6 +475,17 @@ add_ts_grammar(lua https://github.com/tree-sitter-grammars/tree-sitter-lua.gi
# deliberately not a kLangTable row yet — see the note there.
add_ts_grammar(kotlin https://github.com/fwcd/tree-sitter-kotlin.git 1852ea17b7f60fb3f9d84e0b1555d56b46b39fb1) # main, post-0.3.8

# GDScript (.gd): PrestonKnopp/tree-sitter-gdscript — the only maintained GDScript grammar; there is no
# first-party one under tree-sitter/. LANGUAGE_VERSION 14, inside [MIN_COMPATIBLE, LANGUAGE_VERSION]=[13,15]
# for the vendored core. Ships a scanner.c (significant indentation: INDENT/DEDENT/NEWLINE, like Python's)
# which the macro picks up. Untagged upstream, so the SHA below is a plain master commit, pinned as always.
# PARSE RATE MEASURED, not assumed, before vendoring (STEP 0 of prompts/add-a-language.md): 98.88% of 2938
# real .gd files parse with zero ERROR/MISSING nodes — 13 open-source Godot projects (2611 files, 98.81%)
# plus a private 327-file game (99.39%); 8159 error bytes out of 25.3 MB (0.032%). The three known blind
# spots are disclosed in queries/gdscript/tags.scm and test/gdscriptcheck.sh; all three are upstream grammar
# gaps, NOT patched here (guardrail G3 — this repo carries no grammar fork).
add_ts_grammar(gdscript https://github.com/PrestonKnopp/tree-sitter-gdscript.git c5c8fa4861b5a4f04a7e60d97587fc3b6cc5639e) # master, ABI 14

# PHP (.php): the official tree-sitter org grammar. Like TypeScript this repo hosts TWO sub-grammars
# in subdirectories — `php/` (the full one: HTML text interleaved with `<?php … ?>` blocks) and
# `php_only/` (no surrounding text). ONLY `php/` is vendored and built: a real .php file in a real
Expand Down Expand Up @@ -535,6 +547,7 @@ set(RIPWIRE_TS_OBJECTS
$<TARGET_OBJECTS:ts_lua>
$<TARGET_OBJECTS:ts_dart>
$<TARGET_OBJECTS:ts_kotlin>
$<TARGET_OBJECTS:ts_gdscript>
$<TARGET_OBJECTS:ts_elixir>
$<TARGET_OBJECTS:ts_php>
$<TARGET_OBJECTS:ts_toml>
Expand Down Expand Up @@ -745,7 +758,7 @@ endif()
set(RIPWIRE_GRAMMAR_TARGETS
ts_cpp ts_python ts_go ts_rust ts_tsts ts_tstsx ts_swift ts_objc
ts_javascript ts_bash ts_java ts_ruby ts_json ts_toml ts_yaml ts_csharp ts_c ts_cuda ts_markdown
ts_php ts_lua ts_elixir ts_dart ts_kotlin)
ts_php ts_lua ts_elixir ts_dart ts_kotlin ts_gdscript)
set(RIPWIRE_RUNTIME_COMPILE_TARGETS ripwire_probe ripwire tree-sitter ${RIPWIRE_GRAMMAR_TARGETS})
set(RIPWIRE_RUNTIME_LINK_TARGETS ripwire_probe ripwire)
# The C++ subset of the compile list — the only targets that include a C++ standard library at all, and so
Expand Down Expand Up @@ -1072,5 +1085,6 @@ if(RIPWIRE_FUZZ)
add_ripwire_fuzzer(elixir tree_sitter_elixir elixir)
add_ripwire_fuzzer(lua tree_sitter_lua lua)
add_ripwire_fuzzer(kotlin tree_sitter_kotlin kotlin)
add_ripwire_fuzzer(gdscript tree_sitter_gdscript gdscript)
add_custom_target(ripwire_fuzzers DEPENDS ${RIPWIRE_FUZZ_TARGETS})
endif()
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1816,9 +1816,9 @@ wrong, and it has. These are the results that say so, all in-tree, all published
### In the tests

<details>
<summary><b>618 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->
<summary><b>619 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->

`test/regression.sh` names **618 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **619 gate scripts** and is the authoritative list; <!-- gatecount -->
`python3 test/pargates.py . ./build/ripwire -j 6` runs the same set in parallel. On top of them sit the
contracts that do not fit a unit test: two runs byte-identical, warm output identical to cold, output
that pipes clean through `xmllint --noout`, a sanitizer build with `-fno-sanitize-recover=all`, and a
Expand Down Expand Up @@ -2649,7 +2649,7 @@ file, and one row in the extension table.
| Metal (MSL) | `.metal` | Indexed with the C++ grammar. |
| CUDA | `.cu`, `.cuh` | `<<<>>>` launch sites are call edges. |
| Python | `.py` | |
| TypeScript / JavaScript | `.ts`, `.tsx`, `.js`, `.jsx` | Named imports and default imports resolve. One vendored dependency supplies two of the 24 grammars, `typescript` and `tsx`. |
| TypeScript / JavaScript | `.ts`, `.tsx`, `.js`, `.jsx` | Named imports and default imports resolve. One vendored dependency supplies two of the 25 grammars, `typescript` and `tsx`. |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| Java | `.java` | Qualified `new` calls resolve in a precise tier. |
| Kotlin | `.kt` | Shares one call graph with Java. A file with string templates past 128 levels is refused and listed by `--skipped`. |
| Ruby | `.rb` | Superclasses, mixins, `autoload`, and constant receivers are read. |
Expand All @@ -2662,6 +2662,7 @@ file, and one row in the extension table.
| Go | `.go` | Qualified calls are rejected and fenced, not guessed. |
| Rust | `.rs` | Scoped, turbofish, and `Self::` calls resolve in a precise tier. |
| Bash | `.sh`, `.bash` | |
| GDScript | `.gd` | A file is a class body: `class_name` names it and file-scope `func`/`var` are its members. A signal is indexed as a member. `preload`/`load` produce no dependency edge. `.tscn`, `.tres`, and `.gdshader` are not indexed. |
| JSON | `.json` | Config keys become symbols. The lane emits no call edges. |
| TOML | `.toml` | A table header is one symbol. Keys below it are one level down. |
| YAML | `.yml`, `.yaml` | Mapping depth 2 is the cut. Sequence levels are transparent. |
Expand Down
3 changes: 3 additions & 0 deletions THIRD_PARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,16 @@ is why the sizes are what they are — `parser.c` is one big static table, not h
| `deps/lua` | tree-sitter-lua (v0.5.0) | Munif Tanjim | MIT | `10fe0054734eec83049514ea2e718b2a56acd0c9` | https://github.com/tree-sitter-grammars/tree-sitter-lua | 392 KB |
| `deps/markdown` | tree-sitter-markdown (v0.5.3; the block grammar `tree-sitter-markdown/` only) | Matthias Deiml | MIT | `f969cd3ae3f9fbd4e43205431d0ae286014c05b5` | https://github.com/tree-sitter-grammars/tree-sitter-markdown | 2.1 MB |
| `deps/kotlin` | tree-sitter-kotlin (ABI 14) | fwcd | MIT | `1852ea17b7f60fb3f9d84e0b1555d56b46b39fb1` | https://github.com/fwcd/tree-sitter-kotlin | 32 MB |
| `deps/gdscript` | tree-sitter-gdscript (master) | Preston Knopp | MIT | `c5c8fa4861b5a4f04a7e60d97587fc3b6cc5639e` | https://github.com/PrestonKnopp/tree-sitter-gdscript | 2624 KB |
| `deps/doctest` | doctest (v2.4.12) | Viktor Kirilov | MIT | `1da23a3e8119ec5cce4f9388e91b065e20bf06f5` | https://github.com/doctest/doctest | 0.7 MB |

Notes:

- `deps/tree_sitter` keeps upstream's own `CMakeLists.txt` (the build `add_subdirectory`s it),
`lib/src`, `lib/include` and `lib/tree-sitter.pc.in`. Its `lib/src/unicode/` is a subset of ICU
carrying its own `LICENSE` (Unicode-DFS-2016) and `ICU_SHA` provenance file, left untouched.
- `deps/gdscript` is pinned to a bare commit for the same reason as `deps/swift`: upstream
publishes no release tags at all, so a 40-hex commit is the only immutable pin available.
- `deps/swift` is pinned to a bare commit rather than a tag because upstream's default branch does
not carry a generated `parser.c`; that commit's generated output is what is vendored here.
- `deps/kotlin` is pinned to a bare commit rather than the last tag (`v0.3.8`, Aug 2024) because
Expand Down
25 changes: 25 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,31 @@ Elixir extraction landed at revision 78 (rich 79) — `kParserVer` in `src/inges
`kIngestParserVerMirror` in `src/quality.h`. The required `qschemetrip` source-change pin is refreshed
for this extraction change; snapshot scheme 8 is unchanged.

<a id="gdscript-extraction"></a>

GDScript needs no capture-filter module: unlike Elixir, its grammar carries real definition nodes, so
`queries/gdscript/tags.scm` alone is the extraction. A `.gd` FILE IS A CLASS BODY — `class_name` names
the class and file-scope `func`/`var` are its members — so a file-scope `func` is `fn` (as every other
language treats a file-scope definition) while a `func` inside an explicit `class Inner:` is `method`.
`enum` rides `@definition.type` as Java/C#/TypeScript do. Two capture choices are forced by gates in
`ingest_names.h`, not taste: enum MEMBERS ride `@definition.constant` because `@definition.enummember`
is gated by `isPyEnumMemberTarget` and would silently drop every GDScript enumerator, and member
variables ride `@definition.var` because `fieldCaptureKept()` returns false for every language but
Python and C/C++. A `signal` has no SymKind of its own and is DISCLOSED as `t="var"`. The Godot 4
spellings were read off real parses, not node types; `queries/gdscript/tags.scm` records which shapes
are not what the node-type list implies.

THE FLOOR, measured before vendoring: 98.88% of 2938 real `.gd` files parse clean. Three upstream
grammar gaps survive — the `%` unique-name inside a path, a column-0 comment in an indented block, and
the Godot 3 RPC keywords still reserved — and none is patched (guardrail G3). Recovery is LOCAL, so a
file holding them still yields every definition and call edge; `test/gdscriptcheck.sh` asserts that
survival rather than the failure. `preload`/`load("res://…")` resolution is NOT implemented, so a `.gd`
file is never a node in the `--deps`/`--arch` graph and `dependencyCapable()` is not claimed for it.
`.tscn`, `.tres` and `.gdshader` are not indexed.

GDScript extraction landed at revision 98 — `kParserVer` in `src/ingest_cache.h`, mirrored by
`kIngestParserVerMirror` in `src/quality.h`; snapshot scheme is unchanged.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
The three config lanes are *data*, not code: they emit `t="sec"` symbols and **zero call edges**, and
`langCompatible` keeps a config key from ever resolving a same-spelled code symbol. They differ in
where the navigable unit sits. JSON cuts at document depth — top-level and second-level object
Expand Down
4 changes: 2 additions & 2 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ current shape.
> ripwire — the "ripgrep of AI context": parse a codebase, rank symbols by Personalized PageRank,
> stream a deterministic minified XML map to stdout. Zero runtime deps. Languages: C++, C, ObjC/ObjC++,
> Metal (MSL, .metal — C++ grammar), CUDA (.cu/.cuh — tree-sitter-cuda, <<<>>> launches are call edges),
> Python, TypeScript, JavaScript, Java, Ruby, PHP (.php/.phtml), Lua, Elixir (.ex/.exs), Dart (.dart), Kotlin (.kt), Bash, Go, Rust, Swift, C#;
> Python, TypeScript, JavaScript, Java, Ruby, PHP (.php/.phtml), Lua, Elixir (.ex/.exs), Dart (.dart), Kotlin (.kt), Bash, Go, Rust, Swift, C#,
> GDScript (.gd — Godot; .tscn/.tres/.gdshader are NOT indexed);
> JSON, TOML, YAML (config keys); Markdown (.md/.markdown — headings are section symbols with spans).
> usage: ripwire <dir> [flags] # default = the ranked map of <dir> on stdout

## How to read a section

Expand Down
6 changes: 3 additions & 3 deletions docs/EVALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ section, and it is not an afterthought.
| **Co-change / known-item evals** | `--eval`, `--eval-retrieval` (see `bench/ANSWERQUALITY.md`) | Whether the tool surfaces the other files a real historical commit touched; and known-item retrieval across four rankers. |
| **Ensemble calibration harness** | `bench/ensemblecal/` | Whether `--ensemble`'s four evidence families are actually orthogonal, how often each fires, how stable each is across commits — and the preset ladder derived from that (§9). |
| **Differential argv harness** | `test/argvdiffcheck.sh` | That a refactor changed *nothing observable*: two binaries, every argv vector, stdout + stderr + exit code byte-identical. |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 618 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **The gate suite** | `test/regression.sh`, `test/pargates.py` | 619 gate scripts plus the determinism, cache-transparency and golden contracts. <!-- gatecount --> |
| **`--quality-delta`** | `src/quality.h` | Ten measured code-quality failure modes, reported only where a change made them worse. |

### The labeling protocol (why the held-out eval is allowed to disagree with the ranker)
Expand Down Expand Up @@ -5834,7 +5834,7 @@ copy here would be exactly the dialect divergence that gate exists to catch. Com
tags, wrap, stable-order defaults), seven individually invoked standalone gates (`g1freshcheck`,
`skillscan`, `htmlexport`, `compresscheck`, `handoffcheck`, `releaseinstallcheck`,
`taskroutecheck`), and a single loop
naming **618 gate scripts**, all of which exist on disk. <!-- gatecount -->
naming **619 gate scripts**, all of which exist on disk. <!-- gatecount -->

`python3 test/pargates.py . ./build/ripwire -j 6` runs the same scripts in parallel so a full
verification fits in one sitting. It does not modify `regression.sh`.
Expand Down Expand Up @@ -6846,7 +6846,7 @@ Listed because the reason is more useful than the silence.
shipped**. See `bench/locbench/anchorhop_calib.json`. The mention anchor's reproducible numbers are
the ablations in §4.
- **A single round gate-count.** Two in-tree numbers disagree (`test/pargates.py`'s docstring says
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 618. The <!-- gatecount -->
~210; `test/argvdiffcheck.sh` says 200+), while the loop in `test/regression.sh` names 619. The <!-- gatecount -->
loop is the authority; the stale docstrings are a known drift. Since 2026-09-10 the number is not
written by hand anywhere: `docs/gatecount_build.py` derives it from the loop and rewrites every
published site, `test/gatecountcheck.sh` fails if any of them drifts, and `test/manifestcheck.sh`
Expand Down
6 changes: 3 additions & 3 deletions present/deck5_ripwire_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ function storyCards(s, { kick, head, stories, footText }){
kicker(s, "// how it stays true", AMBER);
title(s, "Proven, not promised");
const cards = [
["618 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["619 gate scripts", "the suite runs on every push — plus determinism, cache-transparency and golden contracts; the gate count itself is gated against the runner's own loop"], // gatecount
["byte-identical, always", "two runs over the same tree produce the same bytes; warm equals cold. Enforced in CI, twice — Release AND a plain flavour, because NDEBUG once blinded a whole class of checks"],
["differential refactoring", "a refactor must prove it changed nothing observable: two binaries, hundreds of argv vectors, stdout + stderr + exit codes byte-identical"],
["held-out labels, authored blind", "eval labels were written by reading source before the ranker ever ran on them — so the eval is allowed to say the ranker is wrong. It has."],
Expand All @@ -1147,7 +1147,7 @@ function storyCards(s, { kick, head, stories, footText }){
title(s, "Claims you can trust, because we publish what failed", { size: 32 });

card(s, MX, 1.72, 3.86, 1.72);
stat(s, "618", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
stat(s, "619", "gate scripts named by test/regression.sh — and the COUNT itself is gated against the runner's own loop, so it cannot go stale quietly", // gatecount
MX+0.15, 1.86, 3.56, CYAN, { bsize: 42, bh: 0.66, lsize: 9.5 });
card(s, 4.68, 1.72, 3.86, 1.72, CARD2);
stat(s, "8", "registered NEGATIVES — changes built, gated green, measured against a band written before the code, and reverted rather than tuned",
Expand Down Expand Up @@ -1397,7 +1397,7 @@ function storyCards(s, { kick, head, stories, footText }){
["180 long flags · 34 slides", "bash test/deckclaimcheck.sh"],
["every --flag named here exists", "bash test/deckcheck.sh"],
["74.7% fewer element bytes", "bash test/showcasecapturecheck.sh"],
["618 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["619 gate scripts", "bash test/manifestcheck.sh"], // gatecount
["49 repos · 71 papers · 237 surveyed","bash test/readmedriftcheck.sh"],
["the ten moments, any row", "ripwire . --callers=SYM | wc -c"],
["the head-to-head table", "bench/headtohead/r4-2026-08-06/"],
Expand Down
Loading