diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 20087816..2d2117f2 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,10 @@ +## 2026-07-07 — E-HOTPLUG-MIGRATION-1 — the generic hot-plug pattern is the migration target for ALL consumers; migration doc is board discipline +**Status:** RULING (operator, 2026-07-07; shipped OGAR #174/#175 + lance-graph #658 + tesseract-rs #13/#14) + +**Every consumer migrates onto the generic hot-plug pattern** — one `HOT_PLUG` const (socket type `lance_graph_contract::hotplug::HotPlug`) + one activation test against `ogar_vocab::capability_registry::resolve_hotplug`; the consumer tells OGAR which classids are hot-plugged and receives BOTH the vocab rows and the action surface. No bespoke per-consumer plug crates, no git pins (sibling path deps only), no ontology payload in lance-graph, contract stays zero-dep (member path deps kill CI workspace load — burned once), classid low u16 = APP prefix, never a shape ordinal. + +**Board discipline:** `.claude/knowledge/hotplug-consumer-migration.md` (mirrored in OGAR `.claude/knowledge/`) is MANDATORY READING before (a) migrating any consumer onto the capability surface, (b) adding a new authoritative domain table (thinking-styles next), (c) any cross-repo capability wiring. It carries the 5-step recipe, the 5 drift arms, the burned-once anti-pattern list, and the evaluated ruff synergies (ActionDef lift via `ogar-from-ruff::lift_actions` + a future C++ arm; `walk_enums` param fidelity; ontology plug-and-play over the exports→OGIT→imports tier; thinking-styles as a one-entry `domain_tables()` addition). + ## 2026-07-07 — E-OCR-FACET-HOME-CORRECTION-1 — operator ruling: domain substrate does NOT live in agnostic lance-graph — textord facets removed; the facet producer is ruff→OGAR **Status:** RULING (operator-corrected same day; code removed in this commit) diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index 464ec803..0a6008bb 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -1,5 +1,17 @@ # LATEST_STATE — What Just Shipped (read this FIRST) + +> **2026-07-07 — NO-PIN + plug-and-play OGAR arming:** all OGAR deps +> (symbiont / lance-graph-ogar / cognitive-stack) switched from +> `git+branch=main` (which always pins a rev in Cargo.lock) to PATH deps on +> the local sibling `/home/user/OGAR` — operator policy: "wenn's knallt, +> dann einmal — nicht 200 Pins monitoren". `lance-graph-contract` gained the +> optional `ogar` feature (`ogar_codebook::armed`): arming pulls `ogar-vocab` +> transitively and activates a contract-side compile-time `COUNT_FUSE` +> (+ per-entry parity test). lance-graph-ogar arms it; the contract stays +> zero-dep by default. Fuse verified green vs OGAR main `68d85f02` (84==84, +> entry-for-entry). + > **Auto-injected at session start via SessionStart hook.** > Updated after every merged PR. > **Last updated:** 2026-05-14 (PR #372 merged: sprint-10 spec sprint, 12-worker CCA2A fleet + Opus meta-review + 8 knowledge docs, governance-only (zero .rs changes), mirrors PR #365 pattern. Sprint-11 implementation wave gated on 5 spec patches + 4 user ratifications: CSI-1 CausalEdge64 bit-reclaim Option, OQ-1 Σ4-Σ5 banding, OQ-3 plasticity granularity, OQ-5 rayon vendor. **Major findings:** (1) dual `CausalEdge64` types in workspace — `causal_edge::CausalEdge64` SPO-palette layout ≠ `thinking_engine::layered::CausalEdge64` 8-channel cascade, same name different semantics, surfaced as duplication entry #13 in TYPE_DUPLICATION_MAP and E-META-7 in EPIPHANIES; (2) p64 drift origin pinpointed at `crates/lance-graph-planner/src/cache/convergence.rs:18-22 #[allow(unused_imports)]` annotation — wiring intended for hot-path convergence never finished; (3) three-zone hot-path mental model corrects prior framing — Zone-1 thinking-engine MatVec 200-500ns + AriGraph entity_index O(1) ~20-200ns is the actual cycle-speed path, not DataFusion. Prior: 2026-05-13 (PR #366 merged: sprint-7 7-worker implementation wave for the sprint-5/6 specs + AuditSink trait unification, ~5 KLOC across 5 crates +2 new (`lance-graph-supervisor`, `lance-graph-consumer-conformance`), ~70 new tests, workspace clippy --tests --no-deps -D warnings exits 0; Opus meta verdict 4A/2B/1B-minus; OQ-7-1/2/3 all locked pre-merge; `UnifiedAuditSink` D-SDR-4 placeholder dropped, all sinks unified on `AuditSink` trait; `UnifiedBridge::with_jsonl_audit()` ergonomic constructor added for MedCare-rs sprint-2 item 5. **Adjacent landings (same day):** MedCare-rs sprint-1 10-PR sweep (#113-#122) including E1-1 OQ-3 direct migration (6 RoleGroups) consuming our `0d725d4` decision. MedCare-rs sprint-2 (5 PRs) is queued on user "go" — item 5 consumes this PR's new constructor. Prior same-day: PR #365 (13 sprint-5/6 specs + meta). Prior: PR #364 (D-SDR-3/4/5 + sprint-log-4 governance + sprint-5-9 roadmap + codex P1/P2 fixes). lance-graph #364 ships D-SDR-3/4/5 + sprint-log-4 governance + sprint-5-9 roadmap + codex P1/P2 surgical fixes (OwlIdentity 3-byte canonical, UnifiedAuditEvent 26 bytes, OgitFamilyTable sparse `HashMap`, audit super_domain via AuditChain). MedCare-rs#112 (PR-B) wires `UnifiedBridge` + medcare-rbac + medcare-realtime substrate (+2963 LOC, 17 files, §73 SGB V + BMV-Ä §57 + BtM regulatory tests). smb-office-rs#31 (PR-C) wires `UnifiedBridge` (+111 LOC). ndarray#142 ships VBMI gate for `permute_bytes` (P0 SIGILL fix on Skylake-X / Cascade Lake / Ice Lake-SP) + Inf clamp for `simd_exp_f32`. D-SDR-5 `UnifiedBridge` surface is now consumed end-to-end across MedCare + smb-office. Prior: 2026-05-07 (PR #354). Prior: 2026-05-07 (PR #353). Prior: 2026-05-07 (PR #352). Prior: 2026-05-06 (splat-osint-ingestion-v1 PR 1+2 of 6 in flight). Prior: 2026-04-21 post PR #243. diff --git a/.claude/knowledge/hotplug-consumer-migration.md b/.claude/knowledge/hotplug-consumer-migration.md new file mode 100644 index 00000000..66eb35d9 --- /dev/null +++ b/.claude/knowledge/hotplug-consumer-migration.md @@ -0,0 +1,134 @@ +# Hot-Plug Consumer Migration — the generic plug-and-play pattern + +> READ BY: any session migrating a consumer (woa-rs, medcare-rs, +> smb-office-rs, odoo-rs, tesseract-rs, osm-website-rs, …) onto the +> OGAR-authoritative capability surface; any session adding a new +> authoritative domain table (thinking-styles best practices, …); +> integration-lead / baton-handoff-auditor before any cross-repo +> capability wiring. +> +> Shipped 2026-07-07 (operator-ruled same day). Reference PRs: +> OGAR #174 (ocr_actions) + #175 (resolve_hotplug), lance-graph #658 +> (hotplug socket + OgarAuthority + CI fuse gate), tesseract-rs #13/#14 +> (executor + HOT_PLUG migration — the template consumer). + +## The model — three roles, three homes, one binary + +Everything links into ONE binary (lance-graph + OGAR + consumer + +ndarray). Nothing serializes; every check is a compile-time const or a +test in the consumer's own suite. "Wenn's knallt, dann einmal — nicht +200 Pins monitoren." + +| Role | Home | Surface | +|---|---|---| +| **SOCKET** (agnostic, zero-dep) | `lance_graph_contract::hotplug` | `HotPlug { consumer, classids, covered }`, `Activation`, `ActivationDrift`, trait `CapabilityAuthority` | +| **AUTHORITY** (declares + resolves) | OGAR `ogar_vocab` | domain action tables (e.g. `ocr_actions`) + `capability_registry::{domain_tables, resolve_hotplug, HotplugDrift}` | +| **BRIDGE** (socket ⇄ authority) | `lance-graph-ogar` (workspace-EXCLUDED) | `OgarAuthority: CapabilityAuthority`; also owns COUNT_FUSE + per-entry mirror parity + the roundtrip green light | +| **CONSUMER** | your repo | ONE `HOT_PLUG` const + ONE activation test + your executor | + +The classid is the join key on BOTH sides: the consumer says "these +canon-high ids are hot", the authority returns BOTH the vocab rows and +every capability whose subject is one of those ids. + +## Migration recipe (per consumer, ~1 hour) + +1. **Authority side (OGAR PR #1):** declare your domain's action table + in `ogar-vocab` next to `ocr_actions` — one `ActionDef` per + capability on YOUR already-minted canon-high concepts (mint via the + normal codebook process if missing; hi u16 = concept, lo u16 = APP + render prefix, NEVER a shape ordinal). Export `_ACTION_NAMES` + (const fingerprint), `_SUBJECT_CLASSIDS`, + `_EXPECTED_EXECUTORS = [""]`. Register ONE entry + in `capability_registry::domain_tables()` mapping + `(capability, subject classid)`. That's the whole authority change. +2. **Consumer side:** add path deps (NO PINS — local siblings): + `ogar-vocab = { path = "/OGAR/crates/ogar-vocab" }` and + `lance-graph-contract = { path = "/lance-graph/crates/lance-graph-contract" }`. +3. Declare the plug: + ```rust + pub const HOT_PLUG: lance_graph_contract::hotplug::HotPlug = + lance_graph_contract::hotplug::HotPlug { + consumer: "", + classids: ogar_vocab::_actions::_SUBJECT_CLASSIDS, + covered: COVERED_CAPABILITIES, // your executor's arms + }; + ``` +4. Close the loop with ONE test: + ```rust + let (concepts, capabilities) = ogar_vocab::capability_registry::resolve_hotplug( + HOT_PLUG.consumer, HOT_PLUG.classids, HOT_PLUG.covered, + ).expect("hot-plug drifted from the authoritative OGAR tables"); + ``` + (Or through the socket: `OgarAuthority.activate(&HOT_PLUG)` via + `dyn CapabilityAuthority` when you want authority-agnostic code.) +5. **CI:** your workflows need the sibling checkouts (OGAR + + lance-graph), same as tesseract-rs `rust.yml` / lance-graph + `rust-test.yml`. Sibling layout: check the repo out into a subdir + (`path: `) so `path: OGAR` lands as a true sibling. + +## The drift arms (each one named bang, test-time, in YOUR binary) + +| Arm | Meaning | +|---|---| +| `UnknownClassid(id)` | plugged id not minted in the codebook | +| `NoCapabilitiesFor(id)` | id resolves to no capability — the table/ontology was forgotten | +| `UnexpectedConsumer` | you are not in the table's expected-executor list | +| `Uncovered(cap)` | authority declares it, your executor has no arm | +| `Undeclared(cap)` | your executor claims it, authority doesn't declare it | + +Plus, CI-side (lance-graph `rust-test.yml`): COUNT_FUSE + per-entry +mirror parity + the roundtrip green light run against the real OGAR +sibling on every push. + +## What NOT to do (each burned once, 2026-07-07) + +- **No bespoke per-consumer plug crate/mechanism.** The registration + surface is the `HOT_PLUG` const + one test. (tesseract-ogar's first + `CapabilityRegistration` draft was replaced for exactly this.) +- **No path/optional deps on `lance-graph-contract` toward OGAR.** The + contract is a workspace MEMBER; any path dep there (even optional) is + resolved at workspace-load time and kills every CI cargo invocation. + The contract stays zero-dep; armed things live in excluded crates. +- **No shape ordinals in the classid low u16.** Low half = APP render + prefix (`PortSpec::APP_PREFIX`). Distinct shapes are distinct hi-u16 + concepts or ClassView payload readings. +- **No ontology payload in lance-graph.** lance-graph carries the wire + mirror + the green light, nothing else. Authority checks live in OGAR. +- **No git deps on OGAR.** git+branch always writes a rev pin into + Cargo.lock. Path deps to the sibling, always. + +## Future synergies (evaluated 2026-07-07 — next arcs, not shipped) + +1. **ActionDef plug-and-play from ruff (HIGH, near-term).** + `ogar-from-ruff::lift_actions` already lifts ActionDefs from + Rails/Python model graphs — the OCR table is hand-declared only + because tesseract has no AR source. The generic path: ruff harvest + (`ruff_*_spo`) → fuzzy-recipe codebook (`(verb, criteria)` recipes, + ruff `.claude/knowledge/fuzzy-recipe-codebook.md`) → `lift_actions` → + the domain table auto-derives instead of being hand-written; a + consumer migration then starts with a harvest, not an authorship + session. Missing piece: a C++ arm for `lift_actions` (ruff_cpp_spo + has walk_tu/walk_free_functions/walk_enums; an action-lift over the + method-body facts is the natural fourth arm) and a + `derive_domain_table!`-style bridge from lifted ActionDefs into + `domain_tables()`. +2. **Param-enum fidelity via `walk_enums` (MEDIUM).** Capability params + that are C/C++ enums (PSM modes, connectivity 4/8, …) can carry + their variants from the ruff enum harvest instead of free-form + names — the same shapes-from-ruff discipline the dawg/dict arc used. +3. **Ontology plug-and-play (MEDIUM, design needed).** Today + `resolve_hotplug` returns vocab rows + capability names. The same + join can return ONTOLOGY fragments per classid: OGAR's + `vocab/exports → OGIT promote → vocab/imports` pipeline already + stages TTL per domain, and `ogar-adapter-ttl` renders it. A + `resolve_hotplug_with_ontology` returning the OGIT NTO fragment + references for the plugged classids would let a consumer pull + concepts + actions + ontology in one activation — still zero + serialization in-binary; the TTL stays an OGAR-internal artifact + (imports/ tier), never hauled through lance-graph. Gate: needs the + imports/ tier populated for the domain (ontologies, not harvests — + harvests stay in the consumer repo). +4. **Thinking-styles best practices (HIGH, planned all along).** One + `domain_tables()` entry + a `_EXPECTED_EXECUTORS` list arms + the thinking-style table for whichever engine executes styles — + same recipe as step 1 above, zero new machinery. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5b10f84..cb628d31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,6 +52,10 @@ jobs: with: repository: AdaWorldAPI/ndarray path: ndarray + - uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR - name: Setup rust toolchain run: | rustup toolchain install ${{ matrix.toolchain }} diff --git a/.github/workflows/jc-proof.yml b/.github/workflows/jc-proof.yml index 4491f96f..b0f4bee8 100644 --- a/.github/workflows/jc-proof.yml +++ b/.github/workflows/jc-proof.yml @@ -23,6 +23,8 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@v4 + - name: Clone AdaWorldAPI/OGAR as parent-level sibling (NO-PIN path deps) + run: git clone --depth 1 https://github.com/AdaWorldAPI/OGAR ../OGAR - name: Setup Rust run: | rustup toolchain install stable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac9b2c28..ba17c88d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,6 +36,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Clone AdaWorldAPI/OGAR as parent-level sibling (NO-PIN path deps) + run: git clone --depth 1 https://github.com/AdaWorldAPI/OGAR ../OGAR with: ref: main token: ${{ secrets.LANCE_RELEASE_TOKEN }} diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index bf9e68be..22fd6b0e 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -64,6 +64,11 @@ jobs: # dequantize_i8_to_f32, quantize_f32_to_i2, dequantize_i2_to_f32 # publicly (ndarray PR #115, merged 2026-04-30). Pin retired. path: ndarray + - name: Checkout AdaWorldAPI/OGAR (sibling dependency, NO-PIN path deps) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR - name: Setup rust toolchain run: | rustup toolchain install ${{ matrix.toolchain }} @@ -108,6 +113,14 @@ jobs: run: cargo test --manifest-path crates/lance-graph-contract/Cargo.toml --tests - name: Run contract doctests run: cargo test --manifest-path crates/lance-graph-contract/Cargo.toml --doc + # lance-graph-ogar is the workspace-EXCLUDED armed tier: it owns the + # mirror<->live-vocab fuses (COUNT_FUSE, per-entry parity, the + # authoritative-table roundtrip green light) against the OGAR sibling + # checked out above. Without this step those fuses only ever ran on a + # developer machine — a same-sized codebook remap would ship green + # (codex/coderabbit finding on PR #658). One scoped step closes it. + - name: Run lance-graph-ogar fuse tests (armed tier, OGAR sibling) + run: cargo test --manifest-path crates/lance-graph-ogar/Cargo.toml # deepnsm: standalone 0-dep codec crate, workspace-excluded, so the # lance-graph test steps above never reached it. ~217 lib + integration + # doctests, fast (no lance/datafusion/ndarray deps). Gating. @@ -155,6 +168,11 @@ jobs: # dequantize_i8_to_f32, quantize_f32_to_i2, dequantize_i2_to_f32 # publicly (ndarray PR #115, merged 2026-04-30). Pin retired. path: ndarray + - name: Checkout AdaWorldAPI/OGAR (sibling dependency, NO-PIN path deps) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR - name: Setup rust toolchain run: | rustup toolchain install stable diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 9ff65515..3392ebf5 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -49,6 +49,11 @@ jobs: # dequantize_i8_to_f32, quantize_f32_to_i2, dequantize_i2_to_f32 # publicly (ndarray PR #115, merged 2026-04-30). Pin retired. path: ndarray + - name: Checkout AdaWorldAPI/OGAR (sibling dependency, NO-PIN path deps) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR - name: Setup rust toolchain run: | rustup toolchain install stable @@ -101,6 +106,11 @@ jobs: # dequantize_i8_to_f32, quantize_f32_to_i2, dequantize_i2_to_f32 # publicly (ndarray PR #115, merged 2026-04-30). Pin retired. path: ndarray + - name: Checkout AdaWorldAPI/OGAR (sibling dependency, NO-PIN path deps) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR - uses: actions-rust-lang/setup-rust-toolchain@v1 with: components: rustfmt diff --git a/Cargo.lock b/Cargo.lock index d649d546..f7cabd2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6043,7 +6043,6 @@ dependencies = [ [[package]] name = "ogar-adapter-surrealql" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#46d2d2ef0144c3a7e86dbad688893d611cd1171c" dependencies = [ "ogar-vocab", ] @@ -6051,7 +6050,6 @@ dependencies = [ [[package]] name = "ogar-class-view" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#46d2d2ef0144c3a7e86dbad688893d611cd1171c" dependencies = [ "lance-graph-contract 0.1.0 (git+https://github.com/AdaWorldAPI/lance-graph?branch=main)", "ogar-vocab", @@ -6060,12 +6058,10 @@ dependencies = [ [[package]] name = "ogar-ontology" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#46d2d2ef0144c3a7e86dbad688893d611cd1171c" [[package]] name = "ogar-vocab" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#46d2d2ef0144c3a7e86dbad688893d611cd1171c" [[package]] name = "once_cell" diff --git a/crates/cognitive-stack/Cargo.lock b/crates/cognitive-stack/Cargo.lock index 7910243c..adb44aba 100644 --- a/crates/cognitive-stack/Cargo.lock +++ b/crates/cognitive-stack/Cargo.lock @@ -4869,6 +4869,7 @@ name = "lance-graph-contract" version = "0.1.0" dependencies = [ "glob", + "ogar-vocab", "serde", "serde_yaml", ] @@ -4878,12 +4879,28 @@ name = "lance-graph-ogar" version = "0.1.0" dependencies = [ "lance-graph-contract", + "lance-graph-ontology", "ogar-adapter-surrealql", "ogar-class-view", "ogar-ontology", "ogar-vocab", ] +[[package]] +name = "lance-graph-ontology" +version = "0.1.0" +dependencies = [ + "lance-graph-contract", + "once_cell", + "oxrdf", + "oxrdfxml", + "oxttl", + "quick-xml 0.37.5", + "sha2 0.10.9", + "thiserror 2.0.18", + "toml", +] + [[package]] name = "lance-graph-planner" version = "0.1.0" @@ -5983,7 +6000,6 @@ dependencies = [ [[package]] name = "ogar-adapter-surrealql" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#f4323c094c4535b6127b0da89440974749df585e" dependencies = [ "ogar-vocab", "surrealdb-ast", @@ -5993,7 +6009,6 @@ dependencies = [ [[package]] name = "ogar-class-view" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#f4323c094c4535b6127b0da89440974749df585e" dependencies = [ "lance-graph-contract", "ogar-vocab", @@ -6002,12 +6017,10 @@ dependencies = [ [[package]] name = "ogar-ontology" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#f4323c094c4535b6127b0da89440974749df585e" [[package]] name = "ogar-vocab" version = "0.1.0" -source = "git+https://github.com/AdaWorldAPI/OGAR?branch=main#f4323c094c4535b6127b0da89440974749df585e" [[package]] name = "once_cell" @@ -6354,6 +6367,59 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" +[[package]] +name = "oxilangtag" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3b4eb570abd4a1dcb062c31fd37b832264d9dc7292c3e69acfe926c87b063f" +dependencies = [ + "serde", +] + +[[package]] +name = "oxiri" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4ed3a7192fa19f5f48f99871f2755047fabefd7f222f12a1df1773796a102" + +[[package]] +name = "oxrdf" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afd5c28e4a399c57ee2bc3accd40c7b671fdc7b6537499f14e95b265af7d7e0" +dependencies = [ + "oxilangtag", + "oxiri", + "rand 0.9.4", + "thiserror 2.0.18", +] + +[[package]] +name = "oxrdfxml" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd5516ae083d09bc57ec65ed5ee97701481725de6ffaa83d968ab42a96157ba1" +dependencies = [ + "oxilangtag", + "oxiri", + "oxrdf", + "quick-xml 0.37.5", + "thiserror 2.0.18", +] + +[[package]] +name = "oxttl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03fd471bd54c23d76631c0a2677aa4bb308d905f6e491ee35dcb0732b7c5c6c" +dependencies = [ + "memchr", + "oxilangtag", + "oxiri", + "oxrdf", + "thiserror 2.0.18", +] + [[package]] name = "p256" version = "0.13.2" @@ -6843,7 +6909,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -6974,6 +7040,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.38.4" @@ -8218,6 +8293,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -9285,6 +9369,27 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -9294,6 +9399,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "winnow 0.7.15", +] + [[package]] name = "toml_edit" version = "0.25.12+spec-1.1.0" @@ -9301,9 +9419,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", - "toml_datetime", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.3", ] [[package]] @@ -9312,7 +9430,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] @@ -10284,6 +10402,15 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "1.0.3" diff --git a/crates/cognitive-stack/Cargo.toml b/crates/cognitive-stack/Cargo.toml index 84703f38..bc2ab7f0 100644 --- a/crates/cognitive-stack/Cargo.toml +++ b/crates/cognitive-stack/Cargo.toml @@ -52,9 +52,9 @@ ndarray = { path = "../../../ndarray" } # surrealdb to main matches OGAR's own surrealdb pin so they resolve to ONE source. ractor = { git = "https://github.com/AdaWorldAPI/ractor", branch = "claude/jirak-math-theorems-harvest-rfii13" } surrealdb-core = { git = "https://github.com/AdaWorldAPI/surrealdb", branch = "main", default-features = false, features = ["kv-lance"] } -ogar-vocab = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-ontology = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-adapter-surrealql = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main", features = ["surrealdb-parser"] } +ogar-vocab = { path = "../../../OGAR/crates/ogar-vocab" } +ogar-ontology = { path = "../../../OGAR/crates/ogar-ontology" } +ogar-adapter-surrealql = { path = "../../../OGAR/crates/ogar-adapter-surrealql", features = ["surrealdb-parser"] } # The OGAR activation crate (in-repo, path) — links the full Active-Record surface # (ogar-class-view's `impl lance_graph_contract::ClassView` + codebook parity-guard). diff --git a/crates/lance-graph-contract/Cargo.toml b/crates/lance-graph-contract/Cargo.toml index ff0283ef..4172f79a 100644 --- a/crates/lance-graph-contract/Cargo.toml +++ b/crates/lance-graph-contract/Cargo.toml @@ -8,8 +8,13 @@ keywords = ["lance", "graph", "contract", "orchestration", "thinking"] build = "build.rs" [dependencies] -# Zero dependencies by design — this is a trait-only crate. -# Consumers depend on this; implementations depend on lance-graph-planner. +# Zero dependencies by design — this is a trait-only crate, and it MUST stay +# dependency-free even of optional path deps: as a WORKSPACE MEMBER, any path +# dep here (optional or not) is resolved at workspace-load time, so a missing +# sibling checkout breaks EVERY cargo invocation in CI (learned 2026-07-07: +# an optional ../../../OGAR dep killed the whole PR pipeline). The armed +# mirror↔vocab fuse + live-vocab re-export live in the workspace-EXCLUDED +# `lance-graph-ogar` crate instead, which already path-deps ogar-vocab. [build-dependencies] # serde_yaml: used ONLY in build.rs to parse modules/*/manifest.yaml. diff --git a/crates/lance-graph-contract/src/hotplug.rs b/crates/lance-graph-contract/src/hotplug.rs new file mode 100644 index 00000000..e627727b --- /dev/null +++ b/crates/lance-graph-contract/src/hotplug.rs @@ -0,0 +1,129 @@ +//! Generic consumer hot-plug — the plug-and-play pattern EVERY consumer +//! migrates to (operator, 2026-07-07). +//! +//! Three roles, three homes: +//! +//! 1. **This module (the SOCKET, zero-dep):** the shapes a consumer uses to +//! declare WHICH classids it hot-plugs, and the [`CapabilityAuthority`] +//! trait the authority implements. No OGAR dep — the contract is a +//! workspace member and MUST stay dependency-free (a path dep here breaks +//! every CI cargo invocation at workspace-load time; learned 2026-07-07). +//! 2. **OGAR (the AUTHORITY):** resolves the hot-plugged classids to BOTH the +//! vocab rows and the action definitions, and verifies the registration +//! (expected consumer, coverage both directions, ids minted exactly once). +//! 3. **The consumer:** declares one [`HotPlug`] const naming its classids + +//! covered capabilities, calls `activate` in its own binary/tests — drift +//! bangs once, no pins, no serialization, no per-consumer plug crate. +//! +//! The classid is the join key on BOTH sides: the consumer says "0x0805, +//! 0x0808, 0x0809 are hot", the authority hands back the concepts and every +//! action whose subject is one of those ids. + +/// A consumer's hot-plug declaration: which classids it activates and which +/// capability names its executor covers. One `const` per consumer — the +/// whole registration surface. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct HotPlug { + /// Consumer name (crate name by convention) — the authority checks it + /// against the expected-executor list of the tables it resolves. + pub consumer: &'static str, + /// The canon-high concept ids the consumer hot-plugs. + pub classids: &'static [u16], + /// Capability names the consumer's executor covers. + pub covered: &'static [&'static str], +} + +/// What the authority returns for a green activation: the vocab rows and +/// capability names resolved for the hot-plugged classids. Plain owned +/// `std` types — zero-dep, no serialization. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Activation { + /// `(concept, classid)` vocab rows for every hot-plugged id. + pub concepts: Vec<(String, u16)>, + /// Capability names whose subject is one of the hot-plugged ids. + pub capabilities: Vec, +} + +/// Why an activation failed — each arm is one named bang. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ActivationDrift { + /// A hot-plugged classid is not minted in the authority's codebook. + UnknownClassid(u16), + /// The consumer is not an expected executor for a resolved table. + UnexpectedConsumer(String), + /// The authority declares a capability on a hot-plugged id that the + /// consumer does not cover. + Uncovered(String), + /// The consumer claims a capability the authority does not declare on + /// its hot-plugged ids. + Undeclared(String), + /// A hot-plugged classid resolves to no declared capability at all — + /// plugging it is either premature or the table was forgotten. + NoCapabilitiesFor(u16), +} + +impl core::fmt::Display for ActivationDrift { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::UnknownClassid(id) => write!(f, "hot-plugged classid 0x{id:04X} is not minted"), + Self::UnexpectedConsumer(c) => write!(f, "consumer `{c}` is not an expected executor"), + Self::Uncovered(cap) => write!(f, "declared capability `{cap}` has no consumer arm"), + Self::Undeclared(cap) => write!(f, "consumer covers `{cap}` which is not declared"), + Self::NoCapabilitiesFor(id) => { + write!(f, "classid 0x{id:04X} resolves to no declared capability") + } + } + } +} + +impl std::error::Error for ActivationDrift {} + +/// Implemented by the authority (OGAR side, same binary): resolve a +/// [`HotPlug`] to its [`Activation`] or the first [`ActivationDrift`]. +pub trait CapabilityAuthority { + /// Verify the plug and hand back the vocab + capability surface for + /// exactly the hot-plugged classids. + fn activate(&self, plug: &HotPlug) -> Result; +} + +#[cfg(test)] +mod tests { + use super::*; + + struct TinyAuthority; + impl CapabilityAuthority for TinyAuthority { + fn activate(&self, plug: &HotPlug) -> Result { + if plug.classids.contains(&0xDEAD) { + return Err(ActivationDrift::UnknownClassid(0xDEAD)); + } + Ok(Activation { + concepts: plug + .classids + .iter() + .map(|&id| (format!("c{id:04x}"), id)) + .collect(), + capabilities: plug.covered.iter().map(|s| (*s).to_string()).collect(), + }) + } + } + + #[test] + fn socket_shape_round_trips_through_a_trait_object() { + let plug = HotPlug { + consumer: "demo", + classids: &[0x0805], + covered: &["recognize_line"], + }; + let auth: &dyn CapabilityAuthority = &TinyAuthority; + let act = auth.activate(&plug).unwrap(); + assert_eq!(act.concepts, vec![("c0805".to_string(), 0x0805)]); + assert_eq!(act.capabilities, vec!["recognize_line".to_string()]); + assert!(matches!( + auth.activate(&HotPlug { + classids: &[0xDEAD], + ..plug + }), + Err(ActivationDrift::UnknownClassid(0xDEAD)) + )); + } +} diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index db27b286..89140f71 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -91,6 +91,7 @@ pub mod hash; pub mod head2head; pub mod hhtl; pub mod high_heel; +pub mod hotplug; pub mod jit; pub mod kanban; pub mod literal_graph; diff --git a/crates/lance-graph-ogar/Cargo.toml b/crates/lance-graph-ogar/Cargo.toml index d01912d0..75a75110 100644 --- a/crates/lance-graph-ogar/Cargo.toml +++ b/crates/lance-graph-ogar/Cargo.toml @@ -70,6 +70,10 @@ serde = ["ogar-vocab/serde", "ogar-adapter-surrealql/serde"] # ogar_codebook wire-compat mirror the parity-guard checks. PATH copy = the # canonical in-repo source (same as lance-graph / symbiont); the [patch] below # folds ogar-class-view's transitive git contract onto it = ONE source. +# THIS crate is the armed tier (workspace-EXCLUDED, so its ogar-vocab path +# dep cannot break workspace load in CI): it owns the COUNT_FUSE + per-entry +# mirror parity + the authoritative-table roundtrip green light. The contract +# stays strictly zero-dep. lance-graph-contract = { path = "../lance-graph-contract" } # OGIT spine (the NamespaceBridge trait + registry + namespace/error types the @@ -82,17 +86,20 @@ lance-graph-ontology = { path = "../lance-graph-ontology" } # symbiont's + cognitive-stack's pins so ALL golden-image roots resolve to # ONE OGAR source) ── # -# Back to branch = "main" now that OGAR #152 (Geo/OSM + network_layer, ALL=79) -# has merged: main == the temp-pinned rev, so the contract mirror (79) and -# ogar_vocab::class_ids::ALL (84 since the 0x0805..0x0809 OCR plan mints, OGAR #172) agree via COUNT_FUSE, and symbiont / -# cognitive-stack (which also `branch = "main"` these crates) resolve the SAME -# single OGAR copy instead of a second one at a rev (codex P2 on #648). -ogar-vocab = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-class-view = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-ontology = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } +# NO PINS — operator policy (2026-07-07): OGAR resolves as a PATH dep to the +# local sibling checkout /home/user/OGAR (clone of AdaWorldAPI/OGAR main), the +# same pattern as the ndarray path dep. A git dep always writes a rev pin into +# Cargo.lock; a path dep never does — the fuse (COUNT_FUSE) then always checks +# against the sibling's CURRENT state. symbiont / cognitive-stack use the same +# sibling paths, so all three resolve ONE OGAR copy (codex P2 on #648 intent +# preserved). CI that builds these excluded crates must sibling-check-out OGAR +# (tesseract-rs CI precedent for lance-graph/ndarray). +ogar-vocab = { path = "../../../OGAR/crates/ogar-vocab" } +ogar-class-view = { path = "../../../OGAR/crates/ogar-class-view" } +ogar-ontology = { path = "../../../OGAR/crates/ogar-ontology" } # default features only = the light emit() DDL formatter (no surrealdb tree); # the parser half is opt-in via this crate's `surrealql-parser` feature. -ogar-adapter-surrealql = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } +ogar-adapter-surrealql = { path = "../../../OGAR/crates/ogar-adapter-surrealql" } # Fold ogar-class-view's transitive `lance-graph-contract` (git # AdaWorldAPI/lance-graph#main) onto the SAME path copy this crate uses, so there diff --git a/crates/lance-graph-ogar/src/lib.rs b/crates/lance-graph-ogar/src/lib.rs index ee4b75d9..4621048a 100644 --- a/crates/lance-graph-ogar/src/lib.rs +++ b/crates/lance-graph-ogar/src/lib.rs @@ -206,6 +206,41 @@ pub mod parity { mod tests { use super::*; + /// The ROUNDTRIP GREEN LIGHT (operator, 2026-07-07): lance-graph does NOT + /// carry ontologies — it only flips this fuse. For every authoritative + /// OGAR capability table (OCR today), assert at ID level that (a) the + /// authority named at least one expected executor ("die Ontologie wurde + /// nicht vergessen"), (b) every subject classid the table binds exists in + /// the wire mirror this crate already guards, (c) the table itself is + /// internally consistent (names unique, non-empty). The consumer-side + /// half of the loop (registration + coverage + classid activation) is + /// asserted in the consumer's own binary via + /// `ogar_vocab::capability_registry::verify_registration`. + #[test] + fn authoritative_ocr_table_roundtrip_is_green() { + use ogar_vocab::ocr_actions; + + assert!( + !ocr_actions::OCR_EXPECTED_EXECUTORS.is_empty(), + "authority declared no expected executor for the OCR table" + ); + let mirror: std::collections::BTreeMap<&str, u16> = + lance_graph_contract::ogar_codebook::CODEBOOK + .iter() + .copied() + .collect(); + for &id in ocr_actions::OCR_SUBJECT_CLASSIDS { + assert!( + mirror.values().any(|&v| v == id), + "OCR table subject 0x{id:04X} missing from the wire mirror" + ); + } + let names = ocr_actions::OCR_ACTION_NAMES; + assert!(!names.is_empty()); + let set: std::collections::BTreeSet<&str> = names.iter().copied().collect(); + assert_eq!(set.len(), names.len(), "duplicate capability names"); + } + #[test] fn ogar_class_view_implements_contract_class_view() { // The activation in one line: an OgarClassView IS a contract ClassView, @@ -216,3 +251,71 @@ mod tests { let _as_trait: &dyn ClassView = &view; } } + +/// The generic hot-plug bridge (operator, 2026-07-07): "lance-graph-contract +/// pulling into OGAR with a generic activation." The contract defines the +/// zero-dep SOCKET ([`lance_graph_contract::hotplug`]); OGAR owns the data +/// resolution (`ogar_vocab::capability_registry::resolve_hotplug`); THIS +/// workspace-excluded crate is where the two meet in one binary. A consumer +/// declares a [`lance_graph_contract::hotplug::HotPlug`] const and activates +/// it through this authority (or calls `resolve_hotplug` directly — same +/// data path, same drift arms). +pub struct OgarAuthority; + +impl lance_graph_contract::hotplug::CapabilityAuthority for OgarAuthority { + fn activate( + &self, + plug: &lance_graph_contract::hotplug::HotPlug, + ) -> Result< + lance_graph_contract::hotplug::Activation, + lance_graph_contract::hotplug::ActivationDrift, + > { + use lance_graph_contract::hotplug::{Activation, ActivationDrift}; + use ogar_vocab::capability_registry::{resolve_hotplug, HotplugDrift}; + + match resolve_hotplug(plug.consumer, plug.classids, plug.covered) { + Ok((concepts, capabilities)) => Ok(Activation { + concepts: concepts + .into_iter() + .map(|(name, id)| (name.to_string(), id)) + .collect(), + capabilities, + }), + Err(HotplugDrift::UnknownClassid(id)) => Err(ActivationDrift::UnknownClassid(id)), + Err(HotplugDrift::NoCapabilitiesFor(id)) => Err(ActivationDrift::NoCapabilitiesFor(id)), + Err(HotplugDrift::UnexpectedConsumer(c)) => Err(ActivationDrift::UnexpectedConsumer(c)), + Err(HotplugDrift::Uncovered(c)) => Err(ActivationDrift::Uncovered(c)), + Err(HotplugDrift::Undeclared(c)) => Err(ActivationDrift::Undeclared(c)), + } + } +} + +#[cfg(test)] +mod hotplug_bridge_tests { + use lance_graph_contract::hotplug::{CapabilityAuthority, HotPlug}; + + /// The generic activation, end to end through the contract socket: the + /// OCR consumer's plug resolves to the 3 vocab rows + 8 capabilities. + #[test] + fn ocr_hotplug_activates_through_the_contract_socket() { + let plug = HotPlug { + consumer: "tesseract-ogar", + classids: &[0x0805, 0x0808, 0x0809], + covered: &[ + "extract_page_image", + "extract_text_layer", + "recognize_line", + "recognize_page", + "render_hocr", + "render_searchable_pdf", + "render_text", + "render_tsv", + ], + }; + let auth: &dyn CapabilityAuthority = &super::OgarAuthority; + let act = auth.activate(&plug).expect("green activation"); + assert_eq!(act.concepts.len(), 3); + assert!(act.concepts.contains(&("textline".to_string(), 0x0805))); + assert_eq!(act.capabilities.len(), 8); + } +} diff --git a/crates/symbiont/Cargo.toml b/crates/symbiont/Cargo.toml index 1d642bee..6b1fb95c 100644 --- a/crates/symbiont/Cargo.toml +++ b/crates/symbiont/Cargo.toml @@ -52,9 +52,9 @@ ractor = { git = "https://github.com/AdaWorldAPI/ractor", branch = "claude/jirak surrealdb-core = { git = "https://github.com/AdaWorldAPI/surrealdb", branch = "main", default-features = false, features = ["kv-lance"] } # OGAR tracks `main` (its jirak commit 3016c78 is an ancestor of main — main is a # strict superset; there is no separate OGAR jirak branch on github). -ogar-vocab = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-ontology = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" } -ogar-adapter-surrealql = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main", features = ["surrealdb-parser"] } +ogar-vocab = { path = "../../../OGAR/crates/ogar-vocab" } +ogar-ontology = { path = "../../../OGAR/crates/ogar-ontology" } +ogar-adapter-surrealql = { path = "../../../OGAR/crates/ogar-adapter-surrealql", features = ["surrealdb-parser"] } # The OGAR activation crate (in-repo, path) — the golden image's whole point is to # prove the FULL AR surface links: ogar-class-view's `impl lance_graph_contract::