Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
404b748
Add R2IL behavioral-IR plan + implementation spec; exclude ruff_r2il …
claude Aug 18, 2026
700faed
ruff_r2il: facet.rs — the VarnodeFacet drill key (W2, ungated)
claude Aug 18, 2026
bd1b096
ruff_r2il: crate manifest, module map, and the ore carrier (W1, ungated)
claude Aug 18, 2026
2ad6f7f
ruff_r2il: slag.rs — addressed residual ledger, no catch-all (W6, ung…
claude Aug 18, 2026
2138c82
ruff_r2il: convention.rs — longest-prefix-wins drill config tree (W3,…
claude Aug 18, 2026
13cd5f0
ruff_r2il: vocab.rs — DTO/codebook factoring stage (W7, ungated)
claude Aug 18, 2026
90b709a
plan: carry forward the ruff/r2sleigh half of the console ruling
claude Aug 18, 2026
6bc3c0b
ruff_r2il: lossless fixtures + corpus profile example (W8, W10, ungated)
claude Aug 18, 2026
313f0ca
ruff_r2il: ore.rs — deterministic fact enumeration + native-address a…
claude Aug 18, 2026
0ae1662
ruff_r2il: harvest_r2il.rs — the six-artifact harvest set (W9, ungated)
claude Aug 18, 2026
bfd819b
ruff_r2il: furnace.rs — the melt to flat addressed rows (W5, ungated)
claude Aug 18, 2026
f70ec9b
ruff_r2il: gates green — address every residual, fix two falsifiers, …
claude Aug 18, 2026
82c80be
Fix the four pre-existing CI failures; make ruff_r2il lift-feature cl…
claude Aug 18, 2026
56cac97
ruff_python_ast: drop redundant binders the `..` pattern already covers
claude Aug 18, 2026
60450c6
Clippy 1.97 sweep: ruff_csharp_spo + ruff_spo_triplet (35 sites, by h…
claude Aug 18, 2026
a509063
Clippy 1.97 sweep: spo_address, ty_*, and ruff core (82 sites, all by…
claude Aug 18, 2026
bb16e26
docs: satisfy markdownlint on the R2IL plan files
claude Aug 18, 2026
e579177
Close hyphenated `mis-` compounds so the typos hook stops mangling them
claude Aug 18, 2026
4c7d73c
docs: close the last hyphenated `mis-` compound in the impl spec
claude Aug 18, 2026
d01a8de
harvest: PR-1 pass-1 artifact set — B1/B3 PASS, B2 KILL (recorded, no…
claude Aug 18, 2026
18e307e
convention: space-class fall-through for every fixed space; B2 KILL -…
claude Aug 18, 2026
fd77087
harvest: gzip the two large artifacts (39.7k -> ~10.6k LOC diff)
claude Aug 18, 2026
5a010f0
harvest: move bulk artifacts to a GitHub Release + S3 scratch (10.6k …
claude Aug 18, 2026
97c3c0f
Stop the fixer hooks from mangling data, and repair the three values …
claude Aug 18, 2026
80820c1
prek: mechanical formatter pass over the four docs it had never run on
claude Aug 18, 2026
bbaebda
Document staged codegen off the R2IL harvest for the sibling session
claude Aug 18, 2026
581007b
Allowlist two more identifiers the spell checker reads as prose
claude Aug 18, 2026
63093fe
Fix the rustdoc errors that the nav_digest failure was masking
claude Aug 18, 2026
eabbd62
Fix the next rustdoc layer: ruff_ruby_spo, ruff_cpp_spo, ruff_python_spo
claude Aug 18, 2026
3747d27
Close out the rustdoc sweep: ruff_spo_address, ruff_sqlalchemy_spo, t…
claude Aug 18, 2026
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
26 changes: 26 additions & 0 deletions .claude/harvest/r2il/PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# R2IL pass-1 harvest — PROVENANCE

Hashes below are **FNV-1a 64** (not a cryptographic hash) over the raw file bytes, computed inline in this example — no hashing dependency.

## Corpus

| path | bytes | fnv1a64 | status |
|---|---|---|---|
| /home/user/ruff/crates/ruff_r2il/../../../r2sleigh/tests/e2e/stress_test | 52128 | d60c9fe34de17594 | harvested (71 functions) |
| /home/user/ruff/crates/ruff_r2il/../../../r2sleigh/tests/e2e/stress_test_opt | 83880 | 40d985a73341e4c8 | harvested (72 functions) |
| /bin/ls | 142312 | c3453ae463c7fa3c | skipped (no symtab) |
| /usr/bin/env | 48072 | 7c3bd066635cc085 | skipped (no symtab) |

## Environment

- `r2sleigh` commit: `60942f6`
- Architecture: `x86-64`
- `sleigh-config` = "1.0", feature `x86` (exact resolved patch pinned by the committed `Cargo.lock`)
- Convention: `R2ilConvention::from_arch(&spec, [Copy, IntAdd, Load, Store, CBranch, Call, Return])` — one convention, built once, reused for every harvested function
- Caps in force: `R2IL_HARVEST_MAX_FUNCS=200`, `R2IL_HARVEST_MAX_SECTION_BYTES=262144`

## Invocation

```sh
cargo run --manifest-path crates/ruff_r2il/Cargo.toml --features lift --example harvest_r2il
```
74 changes: 74 additions & 0 deletions .claude/harvest/r2il/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# R2IL pass-1 harvest — the intake arm's artifact set

Produced by `crates/ruff_r2il/examples/harvest_r2il.rs`. **These artifacts are
evidence, never a re-ingest path — nothing in ruff parses them back.**

## Where the bulk artifacts live (NOT in git)

Following the escalation MedCare-rs's `.claude/harvest/README.md` already
names — *"If it ever grows past a few MB, move it to a GitHub Release asset and
keep only this provenance file in-tree"* — the two bulk files are out of the
tree entirely. Uncompressed they were **3.4 MB / 10,729 rows**, which made
generated data **75 % of this branch's diff** (29,682 of 39,652 insertions).

**Canonical:** GitHub Release
[`r2il-harvest-pass1`](https://github.com/AdaWorldAPI/ruff/releases/tag/r2il-harvest-pass1)

```sh
curl -sL -o r2il-pass1.ore.tsv.gz \
https://github.com/AdaWorldAPI/ruff/releases/download/r2il-harvest-pass1/r2il-pass1.ore.tsv.gz
curl -sL -o r2il-convention.toml.gz \
https://github.com/AdaWorldAPI/ruff/releases/download/r2il-harvest-pass1/r2il-convention.toml.gz
zcat r2il-pass1.ore.tsv.gz | head # read without unpacking
```

**Scratch mirror:** S3 (Tigris), `s3://$AWS_S3_BUCKET_NAME/r2il-arc/harvest/`
— the full set including the small files, plus `STAGED-CODEGEN-GUIDE.md`, for
cross-session scratch. Read via `AWS_ENDPOINT_URL` / `AWS_ACCESS_KEY_ID` /
`AWS_SECRET_ACCESS_KEY` from the environment; never hardcode an endpoint or a
key. The Release is authoritative; S3 is a working mirror that may be pruned.

The bucket is **shared** with other work (`q2`, `MedCare-rs`, `OSM`,
`ontologies`, …), so everything this arc wrote lives under the single
`r2il-arc/` prefix and nothing is written at the root. The one exception is the
MedCare-rs config/harvest backup, which belongs to that repo's own prefix and
sits at `MedCare-rs/harvest/2026-08-18/` — datestamped, per the
`bakes/<name>-<date>/` convention already in use there.

Both are gitignored, so a regenerate run leaves the tree clean.

## What IS in git

| file | why it stays |
|---|---|
| `TRIAGE-RESULT.md` | the pre-registered bars B1/B2/B3, stated **before** the measured section — the point of the whole run |
| `r2il-pass1-slag.tsv` | the addressed residual ledger; the artifact a reviewer actually reads |
| `r2il-pass1-census.md` | per-fact-kind and per-opcode counts |
| `PROVENANCE.md` | corpus manifest (FNV-1a 64 per input), r2sleigh commit pin, caps, exact invocation |

Together under 32 KB. The Release assets are reproducible from these plus the
pinned corpus; these are not reproducible from the Release.

## Regenerate

```sh
cargo run --manifest-path crates/ruff_r2il/Cargo.toml --features lift \
--example harvest_r2il

# gzip AFTER the run: `gzip -9` compresses in place and removes its source, so
# compressing first would leave the next run writing an uncompressed sibling
# beside a stale archive.
gzip -9 .claude/harvest/r2il/r2il-pass1.ore.tsv \
.claude/harvest/r2il/r2il-convention.toml
```

Then re-upload to the Release (new tag per pass — assets are immutable
evidence, never overwritten in place) and to the S3 prefix.

## Measured, this pass

143 functions across 4 x86-64 binaries (2 with symtab; 2 stripped and skipped
with a printed note). Conservation `54304 / 17557 / 36747 / 0`.
**B1 PASS · B2 91.30 % INVESTIGATE · B3 PASS.** The entire remaining B2 gap is
one named reason — `memory_object_escaped`, 1670 rows — which is legitimate
slag, not a defect. See `TRIAGE-RESULT.md`.
141 changes: 141 additions & 0 deletions .claude/harvest/r2il/STAGED-CODEGEN-GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Staged codegen off the R2IL harvest — without breaking what exists

> **Audience:** the sibling session that consumes this arc's output (the Ghidra
> console work, and any codegen/target-profile work downstream).
> **Status of the substrate:** PR 1 shipped the intake arm; PR 2 (routes → V3)
> has NOT landed. Everything below is written so you can start staging now and
> not have to unpick it when PR 2 changes the physicalization.

## 0. The one rule

**Every export is additive. A consumer written against version N must keep
working, unread, against version N+1.**

That is not a style preference — it is the same rule the substrate already runs
on. lance-graph's V3 canon says *"RESERVE, DON'T RECLAIM: a zero tier means
`not consulted`, never `compacted away`"*, and `ruff_spo_triplet::ir::ModelGraph`
is schema-locked at the top level with per-language growth confined to sibling
`Vec`s carrying `skip_serializing_if`. Copy that discipline; do not invent a
migration story you will then have to run.

## 1. What you may consume today, and what is still moving

| artifact | stability | use it for |
|---|---|---|
| `TRIAGE-RESULT.md` | **stable** | the bars and their verdicts. Read `B1` before trusting anything else — if conservation ever fails, the run is void. |
| `PROVENANCE.md` | **stable** | corpus identity (FNV-1a per input), r2sleigh pin, exact invocation. Cite this, never re-derive it. |
| `r2il-pass1-slag.tsv` | **stable shape** | the residual work queue. New `reason` values WILL appear; treat an unknown reason as "not yet classified", never as an error. |
| `r2il-pass1-census.md` | **stable shape** | counts per fact-kind / opcode. |
| `r2il-pass1.ore.tsv.gz` | **shape stable, columns additive** | the melted rows. Read by the `#schema` header, never by column position. |
| `r2il-convention.toml.gz` | **stable** | the drill tree. Every row is `unmeasured` until something measures it. |
| `FlatFact`'s two payload slots (`a`, `b`) | ⚠ **NOT stable** | their per-kind meaning is documented in `furnace.rs` and may be re-carved in PR 2. Do not hardcode the bit layout; go through the accessor or re-read the module table. |
| `OpTag::as_str()` opcode tags | **stable, with one correction** | one tag shipped briefly as `int_scary` — a spellchecker rewrite of `int_scarry` (P-code `INT_SCARRY`, signed carry) that reached the enum's `as_str`. Corrected; the pass-1 artifacts never carried it (no SCARRY op classified in the corpus). If you pinned the misspelling, repin. |
| the 16-byte `VarnodeFacet` **as an address** | ⚠ **provisional** | `PROVISIONAL_R2IL_VARNODE = 0x0000` is a local placeholder. The real classid is minted in `ogar_codebook` (PR 3). Treat the facet as an opaque key today; do not persist it as a durable address. |

## 2. Staging order

Stage in this order; each step is independently useful and none of them blocks
on PR 2.

```text
S1 read the ledger slag + census only. Answers "what does the arm not
yet explain?" Needs no codegen at all.
S2 read the ore rows per-function fact rows, joined back to native
addresses via ore::instruction_addr. Enables
navigation and evidence display.
S3 emit into a landing generated code goes to an ADDITIVE landing zone (a
zone, not in place new module/crate), never edited into existing files.
openproject-nexgen-rs's `op-generated` is the worked
precedent: 16 structs emitted beside hand code, with
a `// @generated` header.
S4 wire ONE consumer prove the seam on a single real call site before
scaling. That is what "no wave scales out before
P-REHOST is green" means in a2ui-rs.
S5 target profiles only here does "ordinary Java vs Valhalla/Panama
Java" become a real fork. Until S4 is green it is a
design conversation, not a code path.
```

**Do not skip to S3.** The MedCare and OpenProject transcodes both earned their
numbers by measuring at S1/S2 first (`99.6 %` recoverable, `98.4 %` recipe
coverage) — those figures are what made the later codegen defensible.

## 3. The old/new SoA and `Va*` format question

There are two independent axes here and conflating them is the failure mode.

**Axis 1 — the physical row.** The V3 512-byte `NodeRow` (`16 | 16 | 480`) is
CANON and unchanged. What is V1-legacy is the *reading* of two fields: the
`NodeGuid` u24 tail (new mints go through the 4+12 content-blind facet) and the
`EdgeBlock`'s 12+4 carving (resolve `ClassView::edge_codec_flavor`). Neither
requires an `ENVELOPE_LAYOUT_VERSION` bump, and neither is something this
harvest emits.

**Axis 2 — the `Va*` carrier family** (`Vsa16kF32` / `Vsa16kBF16` / `Vsa16kF16`
/ `Vsa16kI8` / `Binary16K`). These are *compute* formats, selected per workload,
not a schema. The relevant standing rulings: `Vsa16kF32` is deprecated **as a
cross-boundary carrier** (it never crosses a mailbox boundary), and VSA is
demoted to its `I-VSA-IDENTITIES` niche — lossless role superposition of
**identities**, `N ≤ √d/4 ≈ 32`, never of content or of quantized codes.

**What that means for you concretely:** the R2IL harvest emits *neither*. It
emits flat facet-addressed rows and a residual ledger. If you find yourself
about to bundle R2IL facts into a `Va*` carrier, run the four
`I-VSA-IDENTITIES` tests first — in particular Test 0 (register laziness: does
this thing have a natural id? then use the id) and Test 1 (bundle size). R2IL
facts have natural ids (`FactId`, `InstId`, `ValueId`), so Test 0 short-circuits
and the answer is almost certainly "not a VSA workload".

## 4. Additive export — the mechanics

When you extend the export (and you will), obey these five:

1. **Version the header, never the reader's assumptions.** Every row artifact
carries `#version N` and a `#schema` line naming its columns. Bump `N` when
you ADD; a reader that keys off `#schema` names needs no change at all.
2. **Append columns; never reorder or remove.** A column that becomes
meaningless gets an explicit empty value, not deletion — the same
RESERVE-DON'T-RECLAIM rule the node key follows.
3. **New enum variants are expected, not exceptional.** `ResidualReason` grows
as the furnace learns; a consumer must render an unknown reason as its raw
string and carry on. Never `match` exhaustively across a process boundary.
4. **Never widen a field to fit one outlier.** That is what route-local
overflow is for (`mint_factored`'s base-255 cascade; `MAX_SIBLINGS_PER_TIER
= 255` is a design smell, *not* a storage ceiling). One 900-way phi does not
get to change the ABI for everyone.
5. **A new pass gets a NEW release tag.** Harvest assets are immutable
evidence. Re-running and overwriting `r2il-harvest-pass1` in place destroys
the ability to diff pass N against pass N+1 — which is the whole point of
keeping the failing B2 run in history.

## 5. What "without breaking existing" means in practice

- **Generated code lands beside hand code, never inside it.** A `// @generated`
header and its own module. If a generated item must be specialised, the
specialisation lives in hand code that *calls* it.
- **The SPO projection stays optional and lossy.** `ModelGraph → expand() →
Vec<Triple>` is a projection of *semantic* facts (calls, reads, writes). It is
not the behavioral roundtrip oracle and must never become the source you
reconstruct R2IL from.
- **Conservation is your regression test.** If a staged consumer starts
dropping facts, `harvested == classified + residual` with `dropped == 0` is
the invariant that catches it. Assert it in your own pipeline too, not just
in ours.
- **When the arm can't explain something, that is data.** Do not paper a gap
with a catch-all so your codegen compiles. A named residual with an address
is worth more than a generated stub that silently means nothing.

## 6. Where things live

- **Canonical bulk evidence:** GitHub Release `r2il-harvest-pass1`
(`r2il-pass1.ore.tsv.gz`, `r2il-convention.toml.gz`).
- **Scratch mirror + config backup:** `s3://$AWS_S3_BUCKET_NAME/r2il-arc/`
(`harvest/` and `config-backup/<repo>/`), and this guide at
`r2il-arc/harvest/STAGED-CODEGEN-GUIDE.md`. The MedCare-rs backup is the one
thing NOT under `r2il-arc/` — it belongs to that repo and lives at
`MedCare-rs/harvest/2026-08-18/`, datestamped like the `bakes/` siblings
beside it. Credentials from `AWS_*` env only — the bucket is **shared** with
other work (`q2`, `MedCare-rs`, `OSM`, `ontologies`), so stay inside your own
prefix and never write at the root.
- **In-tree:** `.claude/harvest/r2il/` keeps the small readable artifacts;
the plan and impl spec are in `.claude/plans/`.
31 changes: 31 additions & 0 deletions .claude/harvest/r2il/TRIAGE-RESULT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# R2IL pass-1 TRIAGE RESULT

Caps in force: `R2IL_HARVEST_MAX_FUNCS=200`, `R2IL_HARVEST_MAX_SECTION_BYTES=262144`

## Pre-registered bars (stated BEFORE the measured section below)

- **B1 — conservation (absolute).** `dropped == 0` and `harvested == classified + residual`. Any violation **KILLS** the pass: the enumerator, not the corpus, is wrong. Not a percentage.
- **B2 — coverage of the declared seven.** Of ore facts whose parent opcode is one of `{Copy, IntAdd, Load, Store, CBranch, Call, Return}`, **>=99% classify -> PASS; <90% -> KILL.** The 90-99% band is INVESTIGATE (expected causes: operand rows with no convention row at their address, `CallSite` rows with no `direct_target` — both legitimate slag under a parent that classified).
- **B3 — the slag is named and addressed, not lumped.** `residual > 0`, distinct `shape_id` count **>= 5**, `dominant_share() < 0.60`, and **every** residual except `NoFacetCoordinate` carries `at.is_some()`. `residual == 0` is a **KILL** too — it means someone widened the ladder.

Also **pre-register a prediction that is NOT a bar** (so it can be wrong without moving a goalpost): on an x86-64 corpus `Copy/IntAdd/Load/Store` dominate, so pass 1 is expected to classify roughly **60-80%** of all `Op` facts. Record the measured figure either way.

---

## Measured

Functions harvested: 143

Conservation line: harvested 54304 / classified 17557 / residual 36747 / dropped 0

**B1: PASS** — dropped == 0: true; harvested == classified + residual: true

**B2: INVESTIGATE** — 17528 classified / 19198 total ore facts under a seven-opcode parent = 91.30%.

Derivation note: `ResidualFact` does not carry its parent opcode directly, so the denominator's residual half is APPROXIMATED by summing residual reasons that can *only* fire on a row whose parent op is one of the seven (`no_convention_row_at_address`, `indirect_target`, `memory_object_escaped`, `op_site_join_mismatch`, `custom_space_not_in_convention`, `facet_overflow_at_key`) — reasons that can only fire on a non-seven or no-parent-op row (`opcode_not_in_convention`, `user_op_not_in_convention`, `phi_fan_in_exceeds_predecessors`, `variadic_arity`, `no_facet_coordinate`) are excluded. Labelled APPROXIMATION, not exact — see the module doc comment above `SEVEN_ELIGIBLE_RESIDUAL_REASONS`.

**B3: PASS** — residual > 0: true (36747); distinct shape_id count: 43 (>=5: true); dominant_share: 0.215 (<0.60: true).

Spot check: every grouped bucket except no_facet_coordinate reports an example facet address.

**Non-bar prediction, measured:** 5340 / 37728 `Op` facts classified = 14.15% (predicted 60-80%; OUTSIDE the predicted band — recorded honestly, not a bar).
27 changes: 27 additions & 0 deletions .claude/harvest/r2il/r2il-pass1-census.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# R2IL pass-1 census

Total classified `FlatFact` rows: 17557

## By fact kind

| kind | count |
|---|---|
| call_site | 108 |
| edge | 456 |
| op | 5340 |
| operand_in | 7416 |
| operand_out | 4237 |

## By opcode

| opcode | count |
|---|---|
| branch | 25 |
| call | 428 |
| call_ind | 4 |
| cbranch | 995 |
| copy | 6753 |
| int_add | 4768 |
| load | 2394 |
| return | 222 |
| store | 1968 |
Loading
Loading