Skip to content

ogar-loco: VocabularyRegistry — plug-and-play vocabularies, USB-shaped - #254

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/rust-scratch-abi-soa-gsamge
Aug 7, 2026
Merged

ogar-loco: VocabularyRegistry — plug-and-play vocabularies, USB-shaped#254
AdaWorldAPI merged 1 commit into
mainfrom
claude/rust-scratch-abi-soa-gsamge

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Brings ogar-loco the plug-and-play pattern ogar-vocab and lance-graph-contract already use — classid prefix → resolve — one level down: a classid now selects a semantic table, not just a domain tag, so a consumer routes stored function nodes without ever branching on which vocabulary produced them.

The USB handshake, made typed

USB Here
Enumeration plug accepts only a CheckedVocabulary — a device that hasn't passed conformance::validate cannot reach the port, so the hub can never route to an unproven table. No "register now, validate later" path exists.
Device descriptor resolve_classid reads the hi u16 (concept, canon-high per D-CLASSID-CANON-HIGH-FLIP) and ignores the lo u16 — two apps with different render skins share one semantics.
Driver vs. data Registration copies the composed VocabularyTable (the R4 data-first artifact). No trait objects, no lifetimes back into the vocabulary crate, no generics on the registry type — unplugging the crate that registered it cannot invalidate the table.
Address conflict A contested concept is refused loudly (RegistryError::ConceptTaken), never last-write-wins. Two crates claiming one concept must surface at boot, not at read time.

Each device ships its own plug

ogar_blockly::plug_into registers the Blocks content concept — Inventory rows are registry entries, not function bodies, so they deliberately carry no call vocabulary. ogar_ro::plug_into registers the relation-body concept. A consumer (blockly-rs, lance-graph) builds ONE hub at boot from whatever crates it deps:

let mut hub = VocabularyRegistry::new();
ogar_blockly::plug_into(&mut hub)?;
ogar_ro::plug_into(&mut hub)?;
// …then nothing vocabulary-specific, ever again:
let table = hub.resolve_classid(node_classid).ok_or(Unknown)?;

Falsified across crates

crates/ogar-ro/tests/plug_and_play.rs builds exactly that boot sequence and proves:

  • the same FnIndex resolves to a covered predicate under one classid and refuses under the other — routing by classid is doing real work;
  • the shared core stays byte-identical across every plugged device (all four columns, whole sub-floor range), anti-vacuity-anchored on ADD/IF_ELSE so two empty tables couldn't pass;
  • an unplugged concept resolves to None rather than a default — fail-closed, including the deliberately-unplugged Inventory concept;
  • a double-plug is refused and the first device keeps its port.

Verification

  • cargo test -p ogar-loco -p ogar-ro -p ogar-blockly — 64 green (3 new registry unit tests + 4 new cross-crate integration tests)
  • cargo clippy --all-targets -- -D warnings — clean
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps — clean
  • cargo fmt — clean

Generated by Claude Code

The plug-and-play pattern ogar-vocab and lance-graph-contract already use
(classid prefix -> resolve), one level down: a classid now selects a
*semantic table*, not just a domain tag, so a consumer routes stored
function nodes without ever branching on which vocabulary they came from.

The USB handshake, made typed:

- Enumeration IS validation. `plug` accepts only a CheckedVocabulary, so
  a device that has not passed conformance cannot reach the port and the
  hub can never route to an unproven table.
- The descriptor IS the classid. `resolve_classid` reads the hi u16
  (concept, canon-high per D-CLASSID-CANON-HIGH-FLIP) and ignores the lo
  u16 — two apps with different render skins share one semantics.
- What is stored is data, not a driver: registration copies the composed
  VocabularyTable (R4), so no trait objects, no lifetimes back into the
  vocabulary crate, and unplugging that crate cannot invalidate the table.
- A contested concept is refused loudly (RegistryError::ConceptTaken),
  never last-write-wins — two crates claiming one concept must surface at
  boot, not at read time.

Each vocabulary crate now ships its own `plug_into(&mut registry)`:
ogar-blockly plugs the Blocks *content* concept (Inventory rows are
registry entries, not bodies, so they carry no call vocabulary); ogar-ro
plugs the relation-body concept. A consumer (blockly-rs, lance-graph)
builds ONE hub at boot from whatever crates it deps.

Falsified across crates in ogar-ro/tests/plug_and_play.rs: the same
FnIndex resolves to a covered predicate under one classid and refuses
under the other; the shared core stays byte-identical across every
plugged device (anti-vacuity anchored on ADD/IF_ELSE); an unplugged
concept resolves to None rather than a default; a double-plug is refused
and the first device keeps its port.
@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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_d321178f-c1d1-4a73-9056-8c0e3b6d2187)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review August 7, 2026 11:01
@AdaWorldAPI
AdaWorldAPI merged commit 8b8d7cd into main Aug 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants