R2IL intake arm: plan, implementation spec, and ruff_r2il crate (PR 1) - #94
Conversation
…from workspace Plan-of-record for the R2IL intake arm (.claude/plans/r2il-behavioral-ir-v1.md): Phase Zero audit across ruff/r2sleigh/lance-graph, the intake-arm pivot (ore -> furnace -> slag, typed != refined), the facet-as-drill-key ruling (longest-prefix config tree over varnode identity space), and the PR ladder. Implementation spec (r2il-behavioral-ir-v1-impl-spec.md): worker-proof section per module (facet/convention/ore/furnace/slag/behavior/vocab + fixtures + harvest + profile examples), verified upstream signatures, pre-registered triage bars, disjoint-file worker split. Workspace: exclude crates/ruff_r2il (standalone crate, path-deps the r2sleigh sibling checkout; keeps the main workspace and Cargo.lock sibling-free). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_35aca636-bad8-4457-ac33-0113511c2091) |
|
CI status note: the three red checks on
This PR's own additions ( Generated by Claude Code |
Spec §4: 16-byte LE V3-shaped varnode identity (classid|offset_lo|offset_hi| size), FacetPrefix coarse-to-fine keys, CustomSpaceTable with typed FacetOverflow (config keys must be lossless — never `raw as u16`), project/unproject, seven two-sided tests. WIP bank: fleet output committed as workers complete; central gates (fmt/clippy/test + isolation proofs) run before the PR leaves draft. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Spec §3 Cargo.toml (excluded-crate, path deps on r2sleigh r2il/r2ssa, non-default `lift` feature); lib.rs with all seven module declarations up front + the §1 staging docs and §2 honesty notes; behavior.rs FunctionBehavior wrapping one SsaArtifact (ore carrier, never a contract) with borrowed route accessors, provenance helpers, and three grounded unit tests. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…ated)
Spec §8: ShapeId via inline FNV-1a 64 over reason shape only (never
provenance/address), 11-variant ResidualReason with the no-catch-all HARD
RULE, addressed ResidualFact {at, at_prefix}, ResidualLedger with
grouped/by_address/dominant_share, four two-sided tests. is_empty() added
for clippy::len_without_is_empty.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
… ungated) Spec §5: R2ilConvention as a BTreeMap radix tree over FacetPrefix, minimal_pass_one (the seven-opcode ladder as DATA), from_arch bootstrap reading ArchSpec registers/userops/custom-spaces (read, never retype), resolve/resolved_prefix finest-to-coarsest, byte-stable hand-written to_toml with all rows Unmeasured, five two-sided tests. Zero architecture vocabulary in the module. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Spec §13: order-independent VocabTable (BTreeSet-built, no incremental intern), VocabHarvest over SSA names / op spaces / object spaces, userops COUNTED not interned, custom_space_ids_from_blocks as the TYPED oracle, and the string-recovery helpers marked MEASUREMENT-ONLY (they exist to put a number on the loss, never as a data path). Feeds lance-graph ogar_codebook read-only. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Console/Java-extension/forensic-product scope belongs to another session and is excluded. Four properties are pure ruff/r2sleigh R2IL requirements and are recorded as PR-1 constraints: C1 typed library seam survives an external caller (measured yes; CLI/TSV only in lift-gated examples), C2 provenance reaches the native instruction via the op_metadata.instruction_addr sidecar rejoin (SSA does not carry it), C3 conservation ledger is load-bearing rather than QA decoration, C4 transcode is target-architecture projection and the roundtrip oracle is semantic parity, never textual equality. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
tests/lossless_fixtures.rs (spec §10): the 4-block 3-way-merge fixture with the full stressor block (AtomicCAS, StoreConditional incl. None output, Load/StoreGuarded on Custom(7), CallOther 4-in, Insert, 64-bit ram addr, Fence) plus the Multiequal fixture, and the twelve named tests — including the stressor-slag proof and its convention-widening partner. examples/r2il_corpus_profile.rs (spec §12, feature lift): no-dep ELF64 reader, linear-sweep MEASURED-EXACT op-level metrics, symtab-boundary HEURISTIC-DERIVED CFG metrics, with the exact/heuristic labelling rule enforced in the output. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…nchor (W4) Spec §6: OpTag (one variant per SSAOp, total from_op/from_r2il, Copy+Ord+Hash for the landed convention/slag consumers), EdgeTag/CompareTag/OperandPos, FactProvenance, OreFact incl. JoinFailure, and enumerate(behavior, blocks) with the fixed documented order (no HashMap iteration anywhere). Operand coordinates come from the TYPED source Varnodes via the verified op-site join, never from an SSAVar name. Carries plan item C2: ore::instruction_addr(prov, blocks) resolves a fact back to its native instruction via the R2ILBlock::op_metadata sidecar rejoin — SSA does not carry the address, so this is the anchor. Also folds W8/W10 self-corrections to the fixtures and profiler. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Spec §11: one Disassembler + one R2ilConvention::from_arch reused across every STT_FUNC symbol, leader-based block lift -> FunctionBehavior::from_blocks_raw -> furnace::smelt, accumulated across functions, emitting ore.tsv, census.md, slag.tsv (grouped + by_address), convention.toml, PROVENANCE.md (FNV-1a 64, labelled FNV not sha) and TRIAGE-RESULT.md with the three bars stated verbatim BEFORE the measured section. Documented approximation: B2's "facts whose parent opcode is one of the seven" is not exactly computable from ResidualFact (it carries no parent opcode), so it is derived from which ResidualReason variants can structurally fire under a seven-opcode parent, and labelled as an approximation rather than presented as an exact number. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Spec §7: FlatFact = FactId + VarnodeFacet + Concern + FactKind + OpTag + two typed payload slots + provenance, with the const size assert and Copy as the mechanised never-a-nested-object-graph guard. Cardinality is MORE ROWS, never a nested collection; rows reference each other only by FactId. smelt() returns (Vec<FlatFact>, ResidualLedger, HarvestReport) and the pass-1 ladder has no third outcome, so dropped == 0 holds by construction. Completes the ten-worker fleet; central gates run next. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…43/43 Central gate pass (rust 1.97.1): fmt clean, clippy -D warnings clean, 43 tests passing (31 unit + 12 integration), workspace isolation proven (ruff_graph builds, cargo metadata clean of ruff_r2il, root Cargo.lock untouched). Three real defects found by the gates and fixed at the source, not by weakening assertions: 1. Unaddressed residuals. SSA phi rows (block but no op_site) and Edge rows (no prov.inst at all) fell through block_anchor_facet and produced residuals the proposer could not locate. Added any_anchor_facet: falls back to prov.block -> block address. Only phi INPUTS remain unaddressed, which is correct — an SSA join edge is not a varnode occurrence (NoFacetCoordinate). 2. FlatFact size guard. The spec's `<= 64` was an unmeasured guess; the true layout is 88 (id 4 + at 16 + three tags 3 + a/b 16 + FactProvenance 48, align-8 rounded). Replaced with an EXACT measured pin, which is strictly stronger — growth and shrinkage both fail. Flatness itself is unaffected: no heap indirection, Copy still derives. The (u64,u32) shrink to 80 is recorded as a PR-2 item where row width actually costs something. 3. The widening falsifier asserted a wrong model. An unclassified opcode blocks its whole dependent fan-out (op + 4 operands + memory rows = 7, not 1), and widening reclassifies only the subset with nothing else blocking it (3) — the operand rows stay residual under a different named reason. Re-pinned to the true claims: a bound, plus harvested-is-invariant so nothing evaporates. Also: the fixture's Custom(7) lived only on the op's `space:` field, which is not a varnode and never surfaces via inputs()/output() — so the config-key falsifier was testing nothing. Moved the address varnode into Custom(7) so it exercises the real facet::project path. The op-level-space harvest gap is a missing ore fact kind, recorded as a plan item. Plan: records the operator's three-backend substrate ruling (offline V3 / lance-graph zero-copy SoA as storage+audit / S3 via AWS_* env on Railway Tigris). PR 1 needs no change for it — the furnace persists nothing — the constraint is simply not to introduce a persistence assumption. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…ippy-clean CI has been red on the base branch independently of this arc. Each failure was verified against the parent commit before being touched: - cargo clippy: clippy 1.97's `manual_option_zip` fired on `ruff_annotate_snippets::zip_opt`, which carried a literal `// TODO: option_zip` — the lint's suggestion IS the resolution the author left pending, so `a.and_then(|a| b.map(|b| (a, b)))` becomes `a.zip(b)`. The four reported `ruff_python_ast` errors were downstream of that crate failing to compile; `cargo clippy --workspace --all-targets --all-features` is now green. - cargo shear: `ruff_ruby_spo` sat in `[workspace.dependencies]` with no consuming crate (only doc-comment mentions elsewhere). Removed; the crate itself is untouched and the workspace still builds. - shellcheck: SC2086 on unquoted `$LEVEL` in the vendored `vendor/lsp-types/release.sh` (lines 10, 15). Note this file is vendored, so a future re-vendor will clobber the fix — it is tracked and CI lints it, so the quoting has to live here for now. - mkdocs: `docs/OGAR-POLYGLOT-AST-INTEGRATION.md` (added by PR #41) was absent from `nav`, and strict mode aborts on the warning. Added to `not_in_nav` — it is an internal architecture note, not user documentation. ruff_r2il, `--features lift` (the libsla-backed examples) now clippy-clean: type aliases for the two residual buckets, a `LiftCtx` grouping the three invariant lifting handles (readable shape, and settles too_many_arguments), `checked_div`, doc-list indentation, and a narrow visible `#![expect(clippy::disallowed_methods)]` on each example. That last one is deliberate: the workspace policy reads "Use System::… instead **in ty crates**", and this workspace-EXCLUDED crate cannot reach ty's `System` abstraction — so it is suppressed with a stated reason rather than evaded by switching to a non-disallowed API, which would hide that the crate sits outside the policy. Gates: fmt, clippy (default AND lift), 43 tests, plus the three isolation proofs — all green. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
clippy 1.97's `unneeded_wildcard_pattern` on four `Expr::{List,Tuple,...}`
patterns in helpers.rs that spell `range: _, node_index: _, ..` — the trailing
`..` already matches both, so the explicit binders are noise. Removed by hand
(4 sites), formatted, `cargo clippy -p ruff_python_ast --all-targets
--all-features -- -D warnings` clean.
Pre-existing base-branch failure, not introduced by this arc. These four were
previously masked in CI behind `ruff_annotate_snippets` failing to compile
first.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…and)
Pre-existing lint debt, previously masked in CI behind an earlier crate failing
to compile. Fixed by hand — no `clippy --fix` — with nothing deleted:
- recipe_census.rs (16): file-level `#![expect(print_stdout, print_stderr)]`
plus one scoped `#[expect(cast_precision_loss)]`, each with a reason. The
printing IS the example's purpose, so it is suppressed, never removed.
- validate_load.rs (2): same treatment.
- csharp_spo/lib.rs (3), surface_schema.rs (3), exam_config.rs,
concept_split.rs: doc-comment backticks (`WinForms`, `CommonDialog`, …).
- structured_names.rs (4): 3 backticks + one `format_push_string` rewritten to
`write!` (behaviour-identical).
- nav_digest.rs (2): backtick + `type_complexity` resolved by extracting a
module-level `RegionEntries` alias.
- examples/{nav_digest,rekey_exam}.rs: extended the pre-existing
`#![expect(print_stdout)]` to also cover `print_stderr`.
Verified: `cargo clippy -p ruff_csharp_spo -p ruff_spo_triplet --all-targets
--all-features -- -D warnings` clean; ruff_spo_triplet 170 tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
… hand)
Completes the pre-existing lint debt sweep; `cargo clippy --workspace
--all-targets --all-features -- -D warnings` now exits 0 (the sole remaining
warning is inside the vendored `lsp-types` dependency, which is not gated).
No `cargo clippy --fix` was used anywhere in this commit, per operator ruling —
an earlier auto-fix attempt silently destroyed a load-bearing comment, and was
reverted wholesale. Nothing was deleted to silence a lint.
ruff_spo_address (31): 29 `doc_markdown` backticks (`part_of`, `is_a`, `SoA`,
`PartOf`, …) + 2 `cast_possible_truncation` given `#[expect]` with reasons —
both casts are provably safe (`x % 255 < 255`; the other clamped by
`.min(u8::MAX as usize)` immediately prior).
ruff_ruby_spo/menu_regions.rs (6): backticks, one `vec!`→array (only used via
`.iter()`), and `items_after_statements` suppressed rather than hoisting a
helper away from its explanatory comment.
ruff_python_spo/odoo_regions.rs (4): backticks.
ty_* (21): `useless_borrows_in_formatting` throughout, one `question_mark`
(function already returns Option, so `?` is identical), one `manual_assert_eq`
in test code, one follow-on `uninlined_format_args`.
ruff core (18): mostly `useless_borrows_in_formatting`, two `question_mark`,
one `manual_assert_eq`→`debug_assert_eq!`.
The one site that mattered: `ruff_python_formatter/src/range.rs`. clippy wants
the `if let Some(_) = _ else { return None }` collapsed to `?`, which is
behaviour-identical but orphans the two-line comment explaining that a
simple-statement body must not narrow because the formatter has to run
`FormatClauseBody` to decide collapsing. That comment is the whole value of the
branch, so the site keeps its structure behind
`#[expect(clippy::question_mark, reason = ...)]` and the comment survives
byte-for-byte.
`cargo fmt --all --check` clean.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_2e520d16-f54a-4f3a-aaec-f2f5578aa8f6) |
prek's markdownlint gate flagged the two plan docs (they are the only markdown this branch adds). Fixed the three rule classes it reports: - MD040: bare ``` fences given an explicit `text` language (6 sites — the ASCII architecture diagrams and worker tables). - MD036: "Definition of done for PR 1" was bold text acting as a heading; promoted to a real `###` heading. - MD060: table delimiter rows spaced (`|---|` -> `| --- |`). Also accepts markdownlint's ordered-list renumbering: the Phase Zero audit numbered 1..16 continuously across three `###` sections, and each section starts a new list, so restarting at 1 per section is the correct rendering. Verified no prose cross-references those numbers before accepting. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
The `typos` pre-commit hook splits hyphenated words and sees `mis` standalone, then "corrects" `mis-attributed` into `miss-attributed` — actively wrong. These usages are pre-existing in ruff_spo_triplet / ruff_python_spo and were only scanned now because those files changed. Chose closed compounds (`misattributed`, `misdeclared`, `misencode`, …) over allowlisting `mis` in `_typos.toml`: the allowlist would suppress genuine `mis` typos repo-wide to accommodate prose, and the closed forms are the better English anyway. Two sites where no natural closed compound exists (`mis-split` / `mis-rsplit`) are reworded instead. Prose only — no behaviour, no identifiers. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Same typos-hook interaction as the previous commit, in the plan docs rather than source. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…t argued) First real run of the intake arm over 143 functions across 4 x86-64 binaries (2 with symtab, 2 stripped and skipped with a printed note). Six artifacts under .claude/harvest/r2il/, provenance pinned (FNV-1a per corpus file, r2sleigh 60942f6, the exact invocation, caps in force). Conservation: harvested 54304 / classified 10729 / residual 43575 / dropped 0. Against the bars stated BEFORE the run: - B1 conservation: PASS. dropped == 0 and harvested == classified + residual. - B3 slag named and addressed: PASS. 44 distinct shape_ids, dominant share 0.181 (<0.60), every bucket but no_facet_coordinate carries an example address. - **B2 coverage of the declared seven: KILL.** 10700/19198 = 55.73%, against a >=99% PASS / <90% KILL bar. Per the definition of done this blocks PR 2 and is recorded as measured, not rationalised. - Non-bar prediction missed too: 14.15% of Op facts classified vs a predicted 60-80%. Recorded because it was pre-registered as falsifiable. The bar caught a real defect rather than a corpus difficulty. Diagnosis from the ledger: the dominant seven-eligible residual is `no_convention_row_at_address` (6828 rows), whose example facet decodes to classid 0x03 = Const space. `R2ilConvention::from_arch` bootstraps 1440 Register rows plus a coarse `Space{SPACE_REGISTER}` fall-through, but emits NO space-class row for Const, Unique or Ram — so operands in those three spaces cannot resolve against any convention at all. That is a bootstrap omission, not evidence about the corpus. This commit deliberately lands the failing measurement first, so the KILL stays in history when the omission is fixed and the pass is re-measured. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…> INVESTIGATE Fixes the bootstrap omission the failing harvest (d01a8de) diagnosed. `from_arch` emitted a coarse `Space{SPACE_REGISTER}` fall-through row and nothing for Ram / Unique / Const, so operands in those three spaces could not resolve against ANY convention. The doctrine that gives Register a space-class row applies identically to the other three; this was a gap, not a finding about the corpus. Custom spaces deliberately get NO blanket fall-through — an unnamed `Custom(n)` is a real "this convention does not know the architecture's space" signal and must stay slag, which is what the facet.rs config-key falsifier pins. Re-measured on the identical corpus (143 functions, 4 binaries), same bars: before: harvested 54304 / classified 10729 / residual 43575 / dropped 0 after: harvested 54304 / classified 17557 / residual 36747 / dropped 0 B1 PASS (unchanged) B3 PASS (43 shapes, dominant 0.215) B2 55.73% KILL -> 91.30% INVESTIGATE B2 is NOT declared a pass. The bar's own INVESTIGATE band (90-99%) named the expected causes, and the investigation resolves cleanly: the entire remaining gap is ONE reason — `memory_object_escaped`, 1670 rows, exactly 19198 - 17528. That is legitimate slag, not a defect: r2ssa's own `ObjectModel` classified those referents `EscapedUnknown`, so no address exists to resolve them to. Whether the >=99% bar should be recalibrated for pass 2 now that the escaped- object rate is measured is a decision for PR 2 to make explicitly — recording it here rather than quietly moving the goalpost. The failing run stays in history at d01a8de. The non-bar prediction is unchanged at 14.15% (5340/37728 Op facts): this fix moved operand rows, not opcode classification, so Op-level coverage is untouched — internally consistent. 43 tests pass; crate clippy/fmt clean. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
The pass-1 harvest committed a 3.4 MB / 10,729-row ore TSV and a 140 KB convention TOML uncompressed, which made generated data 75% of this branch's diff — 29,682 of 39,652 insertions — and would have bloated the zipball for every future clone. That contradicted a precedent this arc's own plan cites: MedCare-rs's `.claude/harvest/README.md` says "Committed gzipped under `.claude/` … If it ever grows past a few MB, move it to a GitHub Release asset." Its own SPO harvest is committed as `.ndjson.gz` for exactly this reason. I documented the precedent and then did not follow it. r2il-pass1.ore.tsv 3,395,285 -> 204,042 bytes (17x) r2il-convention.toml 139,927 -> 8,193 bytes (17x) Left PLAIN deliberately: the slag ledger, census, provenance, and triage result. Those are the artifacts a reviewer actually opens, and they total under 32 KB. Adds a README recording the split, the regenerate procedure (gzip AFTER the run — `gzip -9` removes its source, so compressing first would leave the next run writing an uncompressed sibling beside a stale archive), how to read the archives without unpacking, and the next escalation threshold if they grow. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…-> 1.5k LOC)
Takes the escalation MedCare-rs's harvest README 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" — rather than stopping at gzip.
The ore TSV and convention TOML are now untracked and gitignored:
canonical GitHub Release `r2il-harvest-pass1` (204 KB + 8 KB assets)
scratch s3://$AWS_S3_BUCKET_NAME/ruff-r2il/harvest/pass1/ (Tigris,
credentials read from AWS_* env; no endpoint or key is hardcoded)
The Release is authoritative; S3 is a working mirror that may be pruned. Both
are gitignored so a regenerate run leaves the tree clean.
Staying in git (under 32 KB total): TRIAGE-RESULT.md (the pre-registered bars —
the point of the run), the addressed slag ledger, the census, and PROVENANCE.md
(FNV-1a per corpus input + the r2sleigh pin). Deliberate asymmetry: the Release
assets are reproducible from these plus the pinned corpus, while these are NOT
reproducible from the Release.
README records both locations, how to read an archive without unpacking, and
that each pass gets a NEW release tag — assets are immutable evidence, never
overwritten in place.
Branch diff: 39,652 -> 10,592 -> 1,504 insertions.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…they corrupted CI was red on three checks. Two of them trace to my own commit e579177, whose message claimed "Prose only — no behaviour, no identifiers". That was false: the `typos` hook, run over files it had never scanned before, rewrote data as if it were English. What it corrupted, and what each break costs: - `OpTag::IntSCarry => "int_scarry"` became `"int_scary"`. That string is an emitted wire tag for P-code INT_SCARRY (signed carry), read by any consumer of the ore rows. The pass-1 artifacts happen not to carry it (no SCARRY op classified in the corpus), so nothing shipped wrong — but the enum was. - A test fixture identifier `app:Aparent` became `app:Apparent` while the assertion string beside it kept the old spelling, so `menu_quad_conflicting_part_of_is_order_independent` failed on linux and macos. Fixed by matching the assertion to the fixture; the test still asserts what it always did (the lexicographically smallest parent wins). - `statistc.h` became `statistic.h` across the ruff_cpp_spo docs and a test name. Tesseract really does ship `src/ccstruct/statistc.h` under that spelling; the "correction" points the harvest docs at a file that does not exist. And, found while reproducing the prek job locally, two more the same class of hook would have landed next: - `typos` read the hex `a8ad31e12dbe9f80` in the harvest slag ledger as a misspelling and dropped a digit, leaving a 15-character shape_id and breaking the join between the ledger's `grouped` and `by_address` sections. - `mdformat` collapsed the YAML frontmatter of `.claude/agents/fuzzy-proposer.md` into a heading plus one run-on paragraph. No frontmatter plugin is configured, so this would have broken every agent card in the repo. - `ruff-format` reformatted golden test INPUT fixtures, changing what the tests exercise. So the fix is not "accept the rewrites". Four paths join the existing exclude list, each for the reason the repo already excludes `crates/ty_vendored/vendor/` and the snapshot directories — they hold data, not prose: vendor/ third-party, ours to consume not reformat .claude/ agent cards (frontmatter), plans, harvest evidence crates/ruff_python_dto_check/tests/golden/ golden test inputs plus two `_typos.toml` allowlist entries (`scarry`, `statistc`) for the two identifiers that are correct as spelled. Verified: 43 ruff_r2il tests green with the corrected tag, ruff_spo_triplet nav_digest 8/8 green, and the harvest ledger restored byte-for-byte. Correction to an earlier commit message in this branch: it reported the diff going "10,592 → 1,504 insertions". The real figure was 10,615. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Pure output of the repo's own configured hooks (`mdformat` + `markdownlint-fix`)
over the `*_spo` READMEs and the polyglot-AST doc — files added before this
gate ran here, so they had accumulated table-alignment, list-numbering and
bracket-escaping drift. Verify it by re-running the command, not by reading it:
uvx prek run --all-files --hook-stage manual
The one thing the fixers cannot do themselves is MD040: a fenced block with no
language. Each of those got `text` by hand after reading the block — they are
ASCII pipeline and byte-layout diagrams, not source in any language.
No prose was changed. The only word-level differences in this diff are
mdformat's `[x]` → `\[x\]` bracket escaping and its `---` → `___` rule style,
both rendering-identical.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
`STAGED-CODEGEN-GUIDE.md` answers the question the other session actually has:
how to consume this arc's output without breaking what already exists.
- **The one rule: every export is additive.** A consumer written against
version N keeps working, unread, against N+1 — the same discipline the V3
canon states as RESERVE-DON'T-RECLAIM and `ModelGraph` implements with a
schema-locked top level plus sibling `Vec`s.
- **A stability table per artifact**, which is the part worth having: the
triage result, provenance and ledgers are stable; `FlatFact`'s two payload
slots and the provisional `VarnodeFacet` classid are NOT, and are flagged so
nobody persists them as durable addresses.
- **A staging order S1–S5** with "do not skip to S3" stated plainly. The
MedCare and OpenProject transcodes earned their numbers by measuring at
S1/S2 first; that is what made their codegen defensible.
- **The old/new SoA and `Va*` question separated into its two axes**, because
conflating them is the failure mode. The 512-byte `NodeRow` is canon and
unchanged; what is V1-legacy is the *reading* of two fields. The `Va*`
family is a set of compute formats, not a schema — and R2IL facts have
natural ids, so `I-VSA-IDENTITIES` Test 0 short-circuits and this is almost
certainly not a VSA workload.
- **Five additive-export mechanics**, including: never widen a field to fit
one outlier (that is what route-local overflow is for), and a new pass gets
a new release tag, because overwriting pass N destroys the ability to diff
it against pass N+1 — which is exactly why the failing B2 run is still in
history.
Also records the `int_scarry` wire-tag correction as a repin note, so a
consumer that pinned the misspelling knows to move.
The README's S3 line is updated to the prefix everything actually sits under
now. The bucket is shared with other repos' data, so this arc keeps to a single
`r2il-arc/` prefix and writes nothing at the root; the MedCare-rs config and
harvest backup went to that repo's own prefix instead, datestamped
(`MedCare-rs/harvest/2026-08-18/`) to match the `bakes/<name>-<date>/`
convention already beside it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
`oce` is the C# harvester's local binding for an `ObjectCreationExpressionSyntax` pattern; `tage` is a legacy column name carried by a codegen fixture. Both are identifiers under test, so "correcting" them changes what the tests exercise — the same failure mode as the three values repaired in 97c3c0f, caught this time before it landed. Both sites predate this branch; the hook only reached them now because the repo-wide run scans everything. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
`cargo test (linux)` runs `cargo doc --all --no-deps` with `RUSTDOCFLAGS=-D
warnings` as its broken-link check. The job never reached that step before,
because it died at the `nav_digest` assertion first — so repairing that test
did not make the job green, it made the next failure visible.
Nine errors in `ruff_python_dto_check`, all pre-existing (both files last
changed in the already-merged c0b24ee):
- seven intra-doc links to private items — the six extraction fns in
`codegen/columns.rs` and `KindRecipe` in `codegen/mod.rs`. De-linked to
plain code spans: same text, no link, no visibility change. Making them
public to satisfy a doc link would be the wrong direction.
- two "unclosed HTML tag" errors in `bin/ruff_py_dto.rs`, where clap help
strings use `<out>` and `<path>` as CLI placeholders and rustdoc parses
them as HTML. Wrapped in backticks, which satisfies rustdoc and keeps the
placeholder token intact in `--help`.
Behind these sits a further cascade of 20 in `ruff_ruby_spo`, also masked by
cargo stopping at the first crate. Handled separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f094f3df-9032-47ca-9181-156dacc09a04) |
`cargo doc` stops at the first crate that fails, so each crate's errors were
hiding the next crate's. Clearing ruff_python_dto_check revealed 20 in
ruff_ruby_spo; clearing those revealed 5 more across ruff_cpp_spo and
ruff_python_spo. All pre-existing, none introduced by this branch.
Same mechanical fix throughout: an intra-doc link to a private item becomes a
plain code span. Identifier text is byte-identical; no visibility was widened,
which would be the wrong direction — making an item public to satisfy a doc
link changes the API to appease a lint.
Two judgement calls worth recording:
- `mod@parse` / `mod@walk` in ruff_ruby_spo: `mod@` is a rustdoc link
DISAMBIGUATOR, meaningless once the brackets are gone. Left as-is it would
render the literal text "mod@parse" to a reader, so the prefix goes too.
- `extract_tree` appears four times in ruff_cpp_spo but only twice as an
error. The other two sit on `#[cfg(feature = "libclang")]` items, which the
default doc build does not compile — so those links are correct when the
feature IS on, and de-linking them would break working links. Only the two
rustdoc actually flags are changed.
Separately noted, NOT fixed: `cargo doc -p ruff_cpp_spo --features libclang`
reports 4 more of the same class. CI runs `cargo doc --all --no-deps` with no
features, so they are not gating anything; fixing them is its own change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
…he rest of ruff_spo_triplet, and the libclang-gated ruff_cpp_spo tail `cargo doc --all --no-deps` stops at the first crate that fails, so this was a cascade: clearing ruff_ruby_spo/ruff_cpp_spo/ruff_python_spo (eabbd62) revealed ruff_spo_address and ruff_sqlalchemy_spo, which in turn revealed six more errors in ruff_spo_triplet itself that had never surfaced before. All pre-existing. Same mechanical fix as the prior two commits: an intra-doc link to a private item becomes a plain code span, one HTML-tag-looking `<N>` gets backtick-wrapped. Identifier text unchanged; nothing made public to satisfy a lint. Independently verified (not just re-running the agent's own check): - `cargo doc --all --no-deps` clean, confirmed twice. - `cargo fmt --check` clean on every touched crate. - `cargo clippy --all-targets` clean on every touched crate. - Full test suite on every touched crate: 0 failures (grepped for FAILED, found none; every `test result: ok`). - Diff shape checked directly: 20 deletions / 20 insertions, zero `pub` added, one file per line touched — no visibility widened, nothing deleted. Also closes a real, separately-discovered gap in `ruff_cpp_spo`: two examples (`harvest_leptonica_scale.rs`, `harvest_textord.rs`) had no `[[example]]` manifest entry at all, unlike their three siblings, so `required-features = ["libclang"]` never applied to them — a bare `cargo test -p ruff_cpp_spo` (no features) failed to compile them against symbols that only exist under `libclang`. Confirmed pre-existing (reproduces identically with this branch's other changes stashed out). Added the missing manifest entries; both feature modes now build and test clean. Independently, `cargo doc --no-deps -p ruff_cpp_spo --features libclang` surfaced 5 more link errors on `clang_walker.rs`'s `libclang`-gated items — CI's `cargo test` step runs `--all-features`, so these were reachable even though the default `cargo doc --all --no-deps` gate never saw them. Same de-link fix; full-workspace `--all-features` doc sweep re-verified clean afterward. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V
Summary
PR 1 of the R2IL behavioral-IR arc: Ruff gains a typed intake arm for r2sleigh's R2IL/SSA — no JSON, no NDJSON, no display-string parsing — entering through the same ore → furnace → slag discipline the existing
ruff_*_spofrontends use, rather than being forced into the closed SPOPredicatevocabulary.~9.9k lines, but only ~8.3k are the new crate and 1.5k are the two design docs. The remaining files are a pre-existing CI repair (see §3) plus a second repair round (see §4). Read in this order and the diff should not need reading at all:
📖 Read these two documents first — they are the review surface
.claude/plans/r2il-behavioral-ir-v1.md(407 lines).claude/plans/r2il-behavioral-ir-v1-impl-spec.md(1071 lines)The crate's own module docs carry the same reasoning inline, so
cargo docis a third entry point.1. The new crate —
crates/ruff_r2il(13 files, 8,278 lines)Workspace-excluded (its own workspace root): it path-deps the
r2sleighsibling checkout, so exclusion keeps ruff's workspace andCargo.locksibling-free. Build with--manifest-path crates/ruff_r2il/Cargo.toml.behavior.rsFunctionBehaviorover oneSsaArtifact. Lossless, zero-copy, names r2sleigh's decomposition; invents no ontology.ore.rsVarnodes via a verified op-site join (mismatch ⇒JoinFailure, never silent misattribution).furnace.rsFlatFactrows. Cardinality is more rows, never nesting.slag.rsOther, noOpaque.convention.rsArchSpec(read, never retyped).facet.rsVarnodeFacet;SpaceId::Customoverflow fails typed at config-key time, never truncates.vocab.rsogar_codebookread-only.Plus
tests/lossless_fixtures.rs(981 lines, 12 tests) and twolift-gated examples (corpus profiler, six-artifact harvest with pre-registered B1/B2/B3 bars).Invariants:
harvested = classified + residual,dropped == 0by construction. Stressors (AtomicCAS,CallOther,Multiequal,StoreGuarded,SpaceId::Custom) must land in named, addressed slag under pass 1 — the ledger naming them is the acceptance criterion. SPO stays an optional lossy projection, never the behavioral truth.2. Changelog
Design (2) —
404b748plan + impl spec + workspace exclusion ·90b709atyped-library-seam, native-address provenance, conservation-as-product-property, transcode-as-projection.Crate, one commit per module (8) —
700faedfacet ·bd1b096manifest/module-map/ore-carrier ·2ad6f7fslag ·2138c82convention ·13cd5f0vocab ·6bc3c0bfixtures + profiler ·313f0caore (+instruction_addranchor) ·0ae1662harvest ·bfd819bfurnace.f70ec9b— three defects the gates caught, fixed at source, not by weakening assertions:Edgerows produced residuals the proposer could not locate → block-address fallback; only phi inputs stay unaddressed (correct — an SSA join edge is not a varnode occurrence).FlatFactsize guard was an unmeasured<= 64; the real layout is 88 → replaced with an exact measured pin (growth and shrinkage now fail).Custom(7)lived only on the op'sspace:field, which is not a varnode, so the config-key falsifier was testing nothing until a varnode was moved into that space.CI repair, round 1 (5) — see §3.
CI repair, round 2 (7) — see §4.
3. Round 1 — why 49 unrelated files changed
CI was already red on the base branch before this arc; every failure was verified against the parent commit before being touched.
82c80be—zip_opt(which carried its own// TODO: option_zip), the vendoredrelease.shSC2086, an unusedruff_ruby_spoworkspace-dep entry, and a doc missing from the mkdocsnav.56cac97/60450c6/a509063— 117 clippy-1.97 lint sites, previously masked because CI stopped at the first crate failure. Mostlydoc_markdownanduseless_borrows_in_formatting.e579177/4c7d73c— thetyposhook splits hyphenated words and "corrects"mis-attributed→miss-attributed; closed compounds chosen over an allowlist that would suppress genuine typos repo-wide.bb16e26— markdownlint on this branch's own docs.4. Round 2 — the
typos/mdformat/ruff-formathooks were corrupting data, plus a deepcargo doccascadeRound 1's
e579177claimed "prose only — no behaviour, no identifiers." That was false, and chasing it open turned into a second repair pass.97c3c0f— three data values thetyposhook silently rewrote in round 1, now fixed at source:OpTag::IntSCarry => "int_scarry"(an emitted wire tag for P-codeINT_SCARRY, signed carry) had become"int_scary".app:Aparentwas renamed toapp:Apparentwhile the assertion string beside it kept the old spelling — thenav_digestfailure on linux/macos in this round's own CI run.statistc.h(a file Tesseract genuinely ships under that spelling) becamestatistic.h, a filename that doesn't exist.Reproducing the
prekjob locally caught three more of the same class before they could land:typosread the hexa8ad31e12dbe9f80in a harvest ledger as a misspelling and dropped a digit, breaking a shape_id join;mdformatcollapsed the YAML frontmatter of an agent card into a heading and one run-on paragraph (no frontmatter plugin is configured, so this would have broken every card under.claude/agents/);ruff-formatwas rewriting golden test input fixtures, changing what the tests exercise. Fixed by excludingvendor/,.claude/, andcrates/ruff_python_dto_check/tests/golden/from the fixer hooks (documented inline in.pre-commit-config.yaml), plus five_typos.tomlallowlist entries for identifiers that are correct as spelled (scarry,statistc,oce,tage, in97c3c0fand581007b).80820c1/bbaebda— the mechanical formatter output over four prose docs the hooks had never reached, split from the repairs above; plus the R2IL staged-codegen guide for the sibling consumer session.63093fe/eabbd62/3747d27—cargo doc --all --no-deps -D warningscascade, six crates deep:cargo docstops at the first crate that fails, so fixing one crate's rustdoc errors revealed the next crate's — all pre-existing, none introduced by this branch:ruff_python_dto_check(9) →ruff_ruby_spo(20) →ruff_cpp_spo/ruff_python_spo(5) →ruff_spo_address/ruff_sqlalchemy_spo(16) → six more insideruff_spo_tripletitself, plus 5 more inruff_cpp_spounder--features libclang(CI's test job runs--all-features). Same mechanical fix throughout: an intra-doc link to a private item becomes a plain code span ([`x`]→`x`); nothing was madepubto satisfy a lint, nothing deleted.Also closed a real, independently-discovered bug while in
ruff_cpp_spo: two examples (harvest_leptonica_scale.rs,harvest_textord.rs) had no[[example]]manifest entry at all, unlike their three siblings — sorequired-features = ["libclang"]never applied to them, and a bare feature-lesscargo test -p ruff_cpp_spofailed to compile them against symbols that only exist under the feature. Confirmed pre-existing (reproduces identically with this branch's other changes stashed out). Added the missing manifest entries; both feature modes build and test clean now. This supersedes the "Known pre-existing, not addressed here" note in an earlier revision of this body — it is addressed.No auto-fixer was used for any of round 2 either — every fix above was hand-verified by re-running the exact failing command and checking the diff shape (insertions == deletions, zero
pubadded, nothing deleted) before committing.Test Plan
All green locally under the repo's pinned 1.97.1:
Crate gates (workspace-excluded, so
--manifest-path):fmt --check,clippy --all-targets -- -D warningswith and without--features lift, andcargo test— 43 passing (31 unit + 12 integration).Workspace-isolation proofs:
cargo check -p ruff_graphbuilds,cargo metadatacontains noruff_r2il, rootCargo.lockuntouched.cargo test -p ruff_cpp_sponow passes with and without--features libclang(round 2 fix — see §4); previously only the--features libclangpath was verified.