Repository navigation
corpus: structural cross-prover identity keys + 256-dim structural embedder - #449
Conversation
Verification vocabulary was absent from every cross-prover dictionary. _wordnet_math (81 rows) and _conceptnet_seed (53 rows) cover undergraduate and competition pure mathematics, the same shape as the ProofNet and MiniF2F adapters; 21 of 23 probe concepts (zero_extend, wrapping_add, slice_disjoint, dioid, noninterference, ...) had no entry at all. * data/synonyms/_verification_systems.toml: 657 new rows in 12 families -- bitvectors, memory layout/ABI, ownership and slicing, separation and region framing, contracts and proof obligations, termination measures, refinement/semantics, resource algebras, information flow/epistemic, concurrency, toolchains, and estate layering. * estate policy encoded in the vocabulary: Idris2 ABIs, Zig APIs and FFIs, SNIF (not bare NIF) boundaries, Rust/Creusot, Julia, Chapel; no Python. * _msc2020.toml: 87 -> 177 codes. 68Qxx (26) and 68Nxx (10) are now complete, 03B/03C/03D/03F completed, 68M/68P/68T/68V/68W and 06B35 added. FIFTEEN pre-existing rows carried MSC2010 or transferred wording (03B15, 68T15 are MSC2010-only; all four 68V rows were mislabelled; 03D03, 03F50, 06A11, 18N10 were wrong) and are repaired with notes. * wiring: CrossProverDicts::verification_systems (Rust) and SaturationSynonyms.load_verification_systems (Julia), both exported and documented; state/meta manifests and prose counts updated. * docs/reports/2026-10-22-vocabulary-recon.adoc: the recon, including the finding that the dictionaries are loaded but have no ranking consumer yet, and that '250 codes in 68Q/68N/03B70' is impossible (those sections define 37 codes in total). Validated with a local mirror of schema.ncl (Nickel is not installed in this sandbox): 19 tables, 1357 rows, 0 failures, all reject fixtures still reject. CI must still run just lexicon-check. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…nd idris2 - CrossProverDicts::tables()/len()/is_empty(); SynonymTable::merge_cross_prover_dicts() - corpus crossquery now searches the 5 cross-prover dictionaries (--provers-only to opt out) and matches a canonical name directly, so 'crossquery 68Q60' works - run_training.jl preloads all 5 dictionaries; add SaturationSynonyms.load_disciplines - _msc2020.toml: 19 rows repaired against zbMATH, 4 codes added (18G35/18M05/18N45/18N60); 181 rows - idris2.toml 6 -> 26 rows: ABI-layer vocabulary (Fin/Vect/So/LTE/Bits/foreign_import/...) - tests/lexicon_v2_test.rs: assert the verification dictionary is non-empty, classes resolve, merge reaches them, and the estate layer vocabulary exists - docs: drop the invented with_msc2020()/with_wordnet()/with_conceptnet() API from the wikis; five-dictionary wording everywhere; CHANGELOG + recon report updated Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Three adapters for the languages the estate's own stack is written in, rather than for another public corpus. - src/rust/corpus/spark_ada.rs: *.ads/*.adb; packages, with/use clauses, Pre/Post/Contract_Cases/Global/Depends aspects, and the GNATprove escape hatches. pragma Assume (and the old --# assume form) is reported as a postulate; sparklib's Ghost Lemma_* convention is tagged sparklib:lemma. Spec and body are paired across files into one entry. - src/rust/corpus/creusot.rs: *.rs; folds #[requires]/#[ensures]/#[invariant] /#[variant]/#[logic]/#[predicate]/#[cfg(creusot)] into the statement and tags pearlite!/proof_assert!/unsafe/transmute/extern "C". #[trusted] is a postulate. #[test] items excluded. - src/rust/corpus/profile.rs + data/corpus-profiles/hyperpolymath-estate.toml: the hyperpolymath_estate profile. Every repo's src/abi Idris2 tree via a "*" rule, plus echo/epistemic/secret/tropical/choreographic-types (Agda). Sub-trees indexed by their own adapters, then merged with repo:: prefixes and repo:/layer: tags. - Corpus::reindex_links() split out of reindex() so a merge keeps the sub-adapters' discipline tags. - crates/echidna-core-creusot/src/estate_layers.rs: Creusot-annotated layer classification (PO-L1..L5) - stated, not proved, mirrored by tests. - tests/corpus_estate_adapters.rs + fixtures for both adapters. - _verification_systems.toml +8 rows for the syntax the adapters detect. - 17 -> 20 adapters across README/EXPLAINME/wikis/Justfile/capnp/a2ml. - Backlog re-ordered: iris + tlaps to the top, alphaproof + naproche to the bottom, with the reasons recorded next to each. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The Zig/Julia/Chapel/BEAM boundaries have no corpus adapter, so returning a name for them made the round-trip test assert a mapping that does not exist (and failed CI). Those layers now return None. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…ent) Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…assertion Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…heckout Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
… behavior and errors
They served their purpose: they reported the real compiler output and committed the cargo fmt result. They also tripped the Hypatia baseline gate (external POSTs from a workflow), so they must not stay. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…l embedder E6 cross_prover_identity_key was the SHA-256 of a declaration's name tail, so two provers joined only when their authors happened to pick the same local name (`+-comm` vs `Nat.add_comm` vs `bvadd_comm`). It is now the SHA-256 of the *canonical shape of the type*: variables renamed v0,v1,..., module paths, operator spellings and binder keywords erased, sorts canonicalised (scheme dk-shape-v1). No dictionary is consulted. Some provers cannot state the same shape -- Isabelle elides binders, Why3 uses int where the others use nat -- so a second tier is emitted: cross_prover_skeleton_key (binders and sorts erased). Consumers join on tier 1 then tier 2; identity_scheme travels with every key and canonical_type keeps the rendered shape for debugging. E7 VectorModality moves from the 32-dim raw-text HashEmbedder to a 256-dim structural embedder: 1-3-grams of the canonical atom stream, L2-normalised, model echidna-corpus-structural-v2. corpus near canonicalises its query, so an SMT-LIB-shaped query retrieves Coq-shaped entries. HashEmbedder stays as compute_embeddings_legacy for GNN FEATURE_DIM parity and old octads. Also: echidna corpus identity <pattern> exposes the grouping; GraphPayload and QueryResultEntry carry the structural key; capnp gains crossProverSkeletonKey/identityScheme and documents embedderDim; docs, ADR, CHANGELOG, STATE and the wiki source pages say what is actually true. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Pushes made with GITHUB_TOKEN do not trigger workflows, so the style commit that the temporary ZZ fmt job pushed carries no CI signal. This empty commit exists only to run the normal gates against that tree. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Formatting is applied; the branch no longer needs a workflow that commits cargo fmt output, and the file should not land on main. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing: 55
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Add Carrot credits or activate Agent usage billing to use Autofix |
|
Open the task to resolve the delivery issue or retry. |
Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com>
💡 Codex Reviewechidna/src/rust/corpus/profile.rs Line 122 in c6a239b Each Agda rule includes both echidna/src/rust/corpus/canonical.rs Lines 1051 to 1054 in c6a239b For statements containing unrecognized short lowercase constants, this heuristic renames the constants as variables even though they are not binders. For example, echidna/src/rust/corpus/creusot.rs Lines 734 to 738 in c6a239b Ordinary Creusot contracts now add echidna/src/rust/corpus/creusot.rs Lines 599 to 601 in c6a239b For any Lines 1656 to 1657 in c6a239b When the index is produced by echidna/src/rust/corpus/query.rs Lines 230 to 239 in c6a239b
Lines 1652 to 1655 in c6a239b Groups are always keyed by the primary structural key, so declarations that are only supposed to match through the advertised skeleton fallback—such as Isabelle with elided binders or Why3 with a different numeric sort—remain in separate groups. The echidna/src/rust/corpus/profile.rs Lines 316 to 318 in c6a239b A sub-corpus module path is relative to the adapter base, but merging only prefixes the module name and leaves that path unchanged. For example, an ABI ingested from echidna/src/rust/corpus/creusot.rs Lines 104 to 108 in c6a239b The Creusot parser indexes functions inside traits and echidna/crates/echidna-core-creusot/src/estate_layers.rs Lines 150 to 152 in c6a239b
echidna/data/synonyms/_verification_systems.toml Lines 1042 to 1044 in c6a239b The new dictionary declares echidna/src/rust/corpus/spark_ada.rs Lines 412 to 416 in c6a239b For the standard package-level form echidna/src/rust/corpus/canonical.rs Lines 235 to 239 in c6a239b The declaration-header recognizer includes the word echidna/src/rust/corpus/octad.rs Lines 260 to 263 in c6a239b
echidna/src/rust/corpus/creusot.rs Lines 764 to 771 in c6a239b Hazards are searched in the original statement/body using unrestricted substring checks, even though the parser already has a comment- and string-cleaned source. A safe function containing a comment such as ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
`super::canonical` inside `mod tests` resolves to `corpus::octad::canonical`, which does not exist, so `cargo check --all-targets` failed on main after #449 merged. The module path is `crate::corpus::canonical`. Also drops the temporary diagnostics workflow. Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
# Conflicts: # src/rust/corpus/octad.rs Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…#450) Follow-up to #449, which merged with a red `cargo check`. ## The bug `src/rust/corpus/octad.rs`, in `mod tests`: ``` error[E0433]: cannot find `canonical` in `super` --> src/rust/corpus/octad.rs:758:54 | assert_eq!(coq.graph.identity_scheme, super::canonical::SCHEME); ``` Inside `mod tests`, `super` is `corpus::octad`, not `corpus`. The path should be `crate::corpus::canonical::SCHEME`. Every other `super::canonical` in the tree is at module scope (`corpus::embed`, `corpus::octad`), where `super` *is* `corpus`, so those are correct and unchanged. This is the only compile error `cargo check --locked --workspace --all-targets` reported; `Cargo test` and `llvm-cov line coverage` were skipped/failing behind it, so this PR is also the first run that actually exercises the new tests. ## Also * Removes the temporary `zz-diag` workflow (this branch is prepared without a local Rust toolchain, so it used a throwaway job to get compiler output). * No Python, no new dependencies. ## Why it slipped #449 was prepared without a local `cargo`; the Rust was written and reviewed by hand and the gate that would have caught a one-line module-path error did not run before the merge. Nothing here changes behaviour — the structural identity keys and the 256-dim embedder are as described in #449. --------- Signed-off-by: Jonathan D.A. Jewell <6759885+hyperpolymath@users.noreply.github.com> Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: echidna fmt bot <fmt-bot@users.noreply.github.com> Co-authored-by: echidna-fmt-bot <echidna-fmt-bot@users.noreply.github.com>
…ge 6c PR 3) (#453) ## Summary This is PR 3 of the lexicon-v2 stack (`docs/decisions/2026-10-19-lexicon-v2-and-identity-arbitration.adoc`). It adds `echidna lexicon scan`, a candidate generator that compares `training_data/` corpora along two axes: the theorem **name** and the canonical **shape** of its statement. | | same shape | different shape | |---|---|---| | **same name** | cognate | false friend, or specialisation | | **different name** | synonym candidate | not reported | The scan reuses #449's canonicaliser (`corpus::canonical`, `dk-shape-v1`) rather than building a second normaliser. That is a departure from the 2026-10-20 plan, recorded in the ADR. Measuring `dk-shape-v1` against real corpora found a panic, two stack overflows and several false-identity causes. All are fixed or reported here. The one key change is that string literals are now opaque atoms; it is recorded in the CHANGELOG. ## Changes - **`fix(corpus)`: canonicaliser panic.** When the last token is an unbalanced `(`, `matching()` returns the bracket's own index, and eight `open + 1..close` slices panic. `corpus ingest` and the octad identity keys reach this through `structural_key`. A `between()` helper guards the slices. The regression test panics on the old code and passes now. - **`fix(corpus)`: bounded parser.** Bracket nesting and infix-operator nesting both count against `MAX_NESTING` (256). Before this, 50,000 nested brackets or a 200,000-operator `a -> a -> …` chain overflowed the stack. Past the limit the key is marked incomplete. - **`feat(corpus)`: `StructuralKey::complete`** (additive, `#[serde(default)]`). It reports whether the shape accounts for the whole statement. It is false when: - the parser stops early, invents a missing operand, or gives a binary-only prefix operator one operand; - the input ends inside an open bracket, quote or block comment, or a closer meets a group of another kind; - the statement contains notation the scheme has no rule for (HOL `! ? @ \`, Isabelle `\<…>`, Lean 3 `$`); - the header drop discards the statement's own tokens, or a leading group is misread as a telescope. Every #449 fixture spelling still counts as complete. `corpus identity` skips incomplete keys and says how many. - **`fix(corpus)`: string literals** are one opaque atom unless the quote wraps the whole formula (Isabelle). `drop_ascriptions` is linear and matches the shipped rule exactly, which a proptest checks against the shipped implementation. - **`feat(lexicon)`: `echidna lexicon scan`** (`src/rust/lexicon/scan.rs`, `just lexicon-scan`). - Joins: name (exact and folded), then identical text, shape and skeleton. - Classes: `cognate_exact`, `cognate_shape`, `cognate_skeleton`, `differs`, and `unparsed` when either side is incomplete. - Joins between different names skip uninformative shapes and count them: a variable in function position, fewer than three constants, or a bare type signature. - The per-name cap bounds exact pairs per raw name and folded pairs per whole folded group. - Output is JCS JSON Lines (`pairs/`, `summary.jsonl`, `coverage.jsonl`, `collisions/`), or one JCS summary object with `--format json`. Each record has a UUIDv8 pair id that is the same whichever way the pair is scanned. - Every record has `evidence = none`, and `data/synonyms/` is never written (D4). - Presets: `learning`, `spanning` (one corpus per family), `in-family` and `full` (33 pairs). - **`docs(lexicon)`.** The ADR gains a "PR 3 results" section with: - the coverage table; - the four rule-level false-identity causes, put to the owner as **D6** (`dk-shape-v2`); - data problems in the ACL2 and ACL2s extractors, as **D7**; - the bridge/octad identity-key ruling, **D8** (additive flag, own PR); - the knot-theory reading of equivalence (invariants vs witnesses); - why the scan emits JCS and not TOML. Also: a `CORPUS.adoc` note on `complete`, an addendum to the #449 ADR's "Open" section, the synonym README and the CHANGELOG. ### What the measurement showed (full preset, 13 corpora, 33 pairs, latest commit) - **Share of each corpus read completely by `dk-shape-v1`:** Isabelle/ZF 0.4%, HOL Light 1.9%, HOL4 8.7%, AFP 10.6%, Lean 3 25.8%, λProlog 93.5%. - **Same-name statement pairs emitted:** 167,040. Of these, 94% are `unparsed`, which is the measured case for D6. A further 90,191 pairs under 308 over-large names are counted, not emitted. - **Synonym candidates under different names** (315 by text, 433 by shape, 819 by skeleton) include real cross-family correspondences: - `sqrt_zero ~ lemSqrt0` - `dist_comm ~ dist_sym` - `lt_add_one_iff ~ Th13` - Mizar's article-local labels (`Th29`) matched by content to named AFP and Lean lemmas - **HOL4 ↔ HOL Light (pilot pair):** 1,033 exact cognates and 144 text synonyms. The rest needs HOL notation in the canonicaliser. ## RSR Quality Checklist ### Required - [x] Tests pass: `cargo test --lib` (1,276 passed, 3 ignored), `lexicon_scan_test` (8), `corpus_estate_adapters` (5), `corpus_cli_dispatch` (2), `corpus_health_integration_test` (8) - [x] Code is formatted (`cargo fmt --all -- --check`) - [x] Linter is clean (`cargo clippy --all-targets`) - [x] No banned language patterns - [x] No `unsafe` blocks without `// SAFETY:` comments (none added) - [x] No banned functions - [x] SPDX headers on new source files; JSONL fixtures are covered by the `tests/*` stanza in `.reuse/dep5` - [x] No secrets, credentials, or `.env` files included ### As Applicable - [x] `docs/decisions/` updated (lexicon-v2 ADR: PR 3 results, D6, D7, D8; #449 ADR: an addendum to its Open section) - [x] Documentation updated for user-facing changes (`data/synonyms/README.adoc`, `docs/CORPUS.adoc`, Justfile recipe) - [x] `CHANGELOG` updated - [x] No new dependencies ## Testing - The panic and both stack overflows were reproduced before they were fixed, and each now has a regression test. - Each `complete` condition has a test that pins the `dk-shape-v1` behaviour it flags and asserts that the key is unchanged. - The scan's integration tests use small JCS fixture corpora: real HOL4 and HOL Light rows for the pilot pair, plus Lean-style and Isabelle-style rows. They check every join and class, the caps, valid JCS output, direction-independent UUIDv8 ids, byte-identical reruns, cleanup of stale files, and `--format json`. - Every review fix was followed by a full 13-corpus rescan, and the ADR numbers come from the latest one. - `T1 Guix manifest check` fails on `main` too; see the comment on this PR. - Not run: Julia (unavailable here), and anything needing a prover. Every number is syntactic, and nothing raises a verdict above `belief`. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WS4mfQeC3N1DWjcCQJaQqw --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
What this does
Two things: it lands the fixes that were still sitting on the branch after #448
merged, and it replaces the corpus's name-based cross-prover identity with a
structural one.
1. Corpus identity is now the shape of the type, not the name
E6 GraphModality.cross_prover_identity_keyused to be the SHA-256 of adeclaration's name tail, so
+-comm(Agda),Nat.add_comm(Coq/Lean),addComm(Idris 2) andbvadd_comm(SMT-LIB) shared nothing. The newsrc/rust/corpus/canonical.rsnormalises the statement into a nameless prefixterm — variables renamed
v0, v1, …, module paths dropped, operatorspellings and binder keywords canonicalised, sorts mapped to
NAT/INT/TYPE, Isabelle ascriptions unwrapped, Dedukti's explicit sortargument dropped — and hashes it (scheme
dk-shape-v1). No dictionary isconsulted on the identity path.
Because some provers cannot state the same shape (Isabelle writes
lemma add_comm: "(x::nat) + y = y + x"with no binders; Why3 usesintwhere the others use
nat), a second tier is emitted:cross_prover_identity_keycross_prover_skeleton_keyidentity_schemedk-shape-v1)canonical_typecross_prover_idValidated against the fixtures before porting: Agda, Coq, Lean 4, Idris 2,
SMT-LIB and Dedukti agree on
(PI [NAT NAT] (EQ (ADD v0 v1) (ADD v1 v0)))foradd_comm; Isabelle and Why3 join on(EQ (ADD v0 v1) (ADD v1 v0));add_zeroagrees across four provers; associativity and commutativity stay distinct.
The unit tests pin all of it. Equational rewriting is deliberately not
applied —
x + yandy + xare different keys.2. The Vector octad is 256-dim and structural
StructuralEmbedderhashes 1–3-grams of the canonical atom stream into 256buckets (model
echidna-corpus-structural-v2), replacing the 32-dim raw-textHashEmbedderas the default.corpus nearcanonicalises the query too, so aquery written in SMT-LIB syntax retrieves entries written in Coq syntax
(tested).
HashEmbedderstays asCorpus::compute_embeddings_legacyfor GNNFEATURE_DIMparity and for replaying older octads; the capnpembedder/embedder_dimfields carry both and consumers are told to check them.New CLI:
echidna corpus identity <pattern> [--skeleton] [--cross-only].3. Carried fixes from earlier in the branch
estate_layers.rs::adapter_of_layeronly names layers that actually have anadapter (
creusot-coreis notspark_ada).SPARK_Moderather than the bareSPARKtheparser emits; the profile/creusot adapter tests no longer depend on the
checkout layout.
cargo fmtapplied (bot commit — this branch is prepared without a localRust toolchain, so formatting is done in CI).
4. Honesty pass over docs, ADR, wiki
docs/CORPUS.adoc,docs/architecture/VERISIM-ER-SCHEMA.adoc,docs/handover/STATE.adoc,CLAUDE.md,CHANGELOG.adoc, the 2026-04-28 ADR(annotated as superseded in part) and the wiki source pages
(
docs/wikis/{Home,Architecture}.md) now describe what the code actuallydoes — 20 adapters, 256-dim structural embeddings, two-tier identity. New ADR:
docs/decisions/2026-10-22-structural-cross-prover-identity.adoc.Notes for the reviewer
DeclGraph,GraphPayloadandQueryResultEntrygained fields; all are#[serde(default)], so existing octad JSONL still loads.old octads.
.github/workflows/zz-fmt.ymlis temporary scaffolding for this branch(no local toolchain) and is removed in a follow-up commit; it has no
uses:steps because newly added workflows in this repo fail at startup when they
reference an action.
the Rust module is the artefact of record.