Skip to content

Commit 5fd0034

Browse files
authored
Merge pull request #420 from AdaWorldAPI/claude/activate-lance-graph-att-k2pHI
callcenter: D-ODOO-SAV-4 odoo-savant reasoners + audit/Policy fixes
2 parents 5f28df0 + 0536b34 commit 5fd0034

8 files changed

Lines changed: 759 additions & 0 deletions

File tree

.claude/board/AGENT_LOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## [Main-thread] D-ODOO-SAV-4 — odoo-savant Reasoner layer (4 impls, one per ReasoningKind)
2+
3+
Implemented `crates/lance-graph-callcenter/src/savant_reasoners.rs`: `SavantConclusion { savant_id, query_strategy, confidence: NarsTruth, rationale }` (suggestion-only, **no serde** — the one-binary contract; JSON only at the MedCareV2 FFI boundary) + the 4 `Reasoner` impls per the dispatch decision pinned in PR #419: `CustomerCategoryReasoner` / `PostingAnomalyReasoner` / `NextBestActionReasoner` / `OtherReasoner`, covering all 25 savants in `contract::savants::SAVANTS`. Each resolves the concrete savant from `(kind, namespace)`, selects `QueryStrategy` via `InferenceType::default_strategy()`, and fuses evidence-ref coverage into a NARS `(frequency, confidence)`.
4+
5+
**Dispatch resolution lives in callcenter** — the contract stays an untouched inheritance vow (no `namespace` field added to `Savant`). `resolve_savant(kind, namespace)` filters the roster by kind; for ambiguous kinds it splits via `DISPATCH_NS` (the `Other(RECONCILE_MATCH)` 19-vs-21 split per #419: `erp.k3.reconcile_match` / `erp.k3.payment_reconcile`) then by `namespace == savant.name`.
6+
7+
**Scope:** all 25 dispatch through the 4 impls; the 14 `NEEDS-INPUT` savants dispatch fine here (they're blocked on woa-rs *evidence feeds*, not the impl). Row-level column fusion is deferred to when woa-rs supplies materialized evidence — v1 fusion is coverage-based + monotone-in-evidence.
8+
9+
**Tests:** 8 new (`savant_reasoners::tests`) — resolution, RECONCILE_MATCH namespace split, single-candidate, strategy↔inference, monotone confidence, async-trait dispatch, kind-mismatch — all green; 137 prior callcenter tests pass; `zone_serialize_check` (no-JSON guard) clean.
10+
11+
**Branch:** `claude/activate-lance-graph-att-k2pHI`, synced to main via merge `20da477` (preserving the `with_jsonl_audit → Result<Self,AuditError>` fix + the `Policy`/`smb_policy` re-export). `cargo test -p lance-graph-callcenter --features jsonl` green. This was the follow-on PR gated on the dispatch-shape review that #419 resolved.
12+
13+
---
14+
115
## [Main-thread → woa-rs HANDOFF] Odoo savant AXIS-B evidence-contract scaffold (carve-out request)
216

317
Wrote `.claude/odoo/savants/_SCAFFOLD-EVIDENCE-CONTRACT.md` — a self-contained handover asking the **woa-rs session** (roster/evidence-schema owner) to carve out the **4 AXIS-B slots per savant** (Arrow `EvidenceRef` schema · odoo field→signal map · property-level OWL alignment · the decision in evidence terms) so lance-graph can implement the `Reasoner` impls (D-ODOO-2 / D-ODOO-SAV-4) in one pass without cross-session ping-pong. Includes the fixed dispatch tuple for all 25 (priority-tiered) + the target `Reasoner` shape + the open dispatch-shape question (N impls vs savant-config registry). Hand-back: fill per-savant docs + note here. No code; doc only. On branch `splat3d-cpu-simd-renderer-MAOO0` (PR #416).

.claude/board/EPIPHANIES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 2026-05-27 — E-AUDIT-1 — `with_jsonl_audit` jsonl-feature build break: a default-feature `cargo check` masks feature-gated error-type mismatches
2+
3+
**Status:** FINDING
4+
5+
**Click:** `UnifiedBridge::with_jsonl_audit` (added in PR #366 as the OQ-7-3 opt-in constructor, `#[cfg(feature = "jsonl")]`) was typed `-> std::io::Result<Self>`, but its body is `JsonlAuditSink::new(...)?` and `JsonlAuditSink::new -> Result<Self, AuditError>`. `AuditError` carries `Io(#[from] std::io::Error)` (the io→AuditError direction) but there is **no** reverse `From<AuditError> for std::io::Error`, so the `?` could not coerce `AuditError` into the declared `io::Error` return — E0277. The **default-feature `cargo check` skips this path entirely** (the constructor is feature-gated), so the break only surfaced when CI built `--features jsonl`.
6+
7+
**Fix (commit `ea2a378`, branch `claude/activate-lance-graph-att-k2pHI`):** one line — return the honest error type `Result<Self, crate::audit_sink::AuditError>`. Two equivalent ~3-line forms existed: (1) change the return type to `Result<Self, AuditError>` [taken — "W2's instinct"]; (2) add a crate-wide `impl From<AuditError> for std::io::Error` and keep the `io::Result` signature. Form 1 chosen: the form-2 coercion would lossily flatten `ChannelFull` / `Serialize` / `SchemaMigration` / `Lance` / `Arrow` into `io::Error::other`, lying about the failure class for every future caller. Zero callers depended on the old signature (grep across all cloned repos: only the def + one doc-comment mention), so the signature change broke nothing. `cargo check/test -p lance-graph-callcenter --features jsonl` clean (137 tests).
8+
9+
**Lesson (generalizes):** a `#[cfg(feature = …)]` fn whose body uses `?` across two error types is invisible to a default-feature `cargo check`. Any crate with optional-feature error paths needs a CI matrix that builds each feature (or `--all-features`), else these E0277s ship to whoever first enables the feature — here, the still-queued MedCare-rs sprint-2 item 5 that consumes this constructor. And: error-type honesty beats `From`-coercion convenience — return the real domain error rather than a lossy `into()` to a narrower std type.
10+
11+
**Cross-ref:** PR #366 (constructor introduction, OQ-7-2/7-3 locks); `audit_sink/mod.rs::AuditError`; TD-SDR-AUDIT-PERSIST-1 in TECH_DEBT.md (the JSON-as-debt reframe — orthogonal to this signature fix).
112
## 2026-05-27 — E-RUBICON-RACTOR — the Σ10 Rubicon commit IS the Heckhausen action-phase crossing; ractor start/stop = crossing/closing the Rubicon; Libet "free won't" = the pre-commit veto; the kanban is a SurrealDB VIEW over leading LanceDB storage
213

314
**Status:** CONJECTURE / design-grounding. Names the psychological origin of the *already-shipped* Σ10 Rubicon doctrine. **Provenance note:** "Σ10 Rubicon" is canonical and implemented (`SigmaTierRouter` Rubicon-resonance dispatch, `D-CSV-10` shipped PR #388; origin `linguistic-epiphanies-2026-04-19.md` E21), but **"Libet" and "Heckhausen" appear nowhere in the board/code/transcripts** — that grounding was a different session or verbal, recorded here now.

.claude/board/INTEGRATION_PLANS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 2026-05-27 — multi-server-cognition-expansion-v1 (legacy-stack displacement + Raft-log / SoA-state-machine)
2+
3+
**Status:** PROPOSAL — §2 displacement is current-state; §3 multi-server expansion is unbuilt, gated on the §4 determinism probe
4+
**Confidence:** HIGH vs JanusGraph + Cassandra; Zitadel displaced by Ory (Kratos+Hydra) for authN; HIGH the log-replicated-SoA shape is correct; LOW it is proven (determinism unproven)
5+
**Plan file:** `.claude/plans/multi-server-cognition-expansion-v1.md`
6+
**Predecessors:** `cognitive-substrate-convergence-v1` (belief-delta log = the episodic arc); `surrealdb/core/src/kvs/lance` (the LSM-on-lance engine the WAL-as-Raft-log rides)
7+
8+
### Scope
9+
10+
Kills the "for multiple servers we need JanusGraph / Cassandra / Zitadel" argument. (1) Component displacement: distributed graph + CP via SurrealDB-on-TiKV + lance-graph; Cassandra's AP is the wrong consistency model for a belief substrate; **Ory** (Kratos+Hydra) fills the one real gap (authN/IdP), binary stays verification-only. (2) Multi-server path: Raft replicates the **belief-delta / episodic LOG**, the zero-copy SoA is the per-node **state machine**, the Rubikon `commit_gate` is the Raft **append point**; TiKV-the-log sits *under* lance-the-state (not "instead of"). Hard prerequisite: byte-deterministic NARS apply (`reencode_safety` / D-SDR-26) — **unproven**; next deliverable is the determinism probe, not more synthesis. authN (Ory) is orthogonal to the consensus layer.
111
## 2026-05-27 — bindspace-singleton-to-mailbox-soa-v1 (dissolve the shared `Arc<BindSpace>` into per-mailbox `MailboxSoA<N>` ephemeral thoughtspace)
212

313
**Status:** PROPOSAL / design (migration spec; NOT yet implemented). **Plan file:** `.claude/plans/bindspace-singleton-to-mailbox-soa-v1.md`. **Epiphany:** `E-MAILBOX-IS-BINDSPACE`.

.claude/board/PR_ARC_INVENTORY.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
3636
---
3737

38+
## callcenter/audit-fix — fix(callcenter): `with_jsonl_audit` returns `Result<Self, AuditError>` (branch work)
39+
40+
**Status:** On branch `claude/activate-lance-graph-att-k2pHI` (HEAD `ea2a378`, not yet a PR). 1-line `.rs` change + this board record (EPIPHANIES E-AUDIT-1, prepended 2026-05-27).
41+
42+
- **Added** — nothing new; retypes `UnifiedBridge::with_jsonl_audit` return from `std::io::Result<Self>``Result<Self, crate::audit_sink::AuditError>` (`unified_bridge.rs:315`). Resolves an E0277 that only the `--features jsonl` build surfaced (the default `cargo check` skips the feature-gated path).
43+
- **Locked** — audit constructors return the **domain** error (`AuditError`), not `io::Result`. **No** crate-wide `From<AuditError> for std::io::Error` coercion (rejected: lossy across the non-`Io` variants `ChannelFull`/`Serialize`/`SchemaMigration`/`Lance`/`Arrow`). Optional-feature error paths must be CI-checked under their feature, not just default-feature (E-AUDIT-1).
44+
- **Deferred** — none. MedCare-rs sprint-2 item 5 (first real caller) consumes the `AuditError` signature directly; any caller needing `io::Result` interop adds a local `map_err`, not a crate-wide `From`.
45+
- **Docs** — EPIPHANIES E-AUDIT-1.
46+
- **Confidence (2026-05-27):** working — `cargo check/test -p lance-graph-callcenter --features jsonl` clean at commit time (137 tests); tree clean; zero callers depend on the old signature.
3847
## PR #411 — Cognitive substrate: locked 33-TSV atom layer + 34-tactic recipes + escalation loop (MERGED 2026-05-27 → main)
3948

4049
**Status:** MERGED. Branch `claude/splat3d-cpu-simd-renderer-MAOO0``main`, 39 commits.

.claude/board/TECH_DEBT.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,81 @@ filter discipline — agents pull their own debt by `@`-mention.
253253
(Seeded with known deferrals from recent PRs. New items PREPEND
254254
with today's date.)
255255

256+
## 2026-05-27 — TD-ARIGRAPH-EPISODIC-FIDELITY-1: AriGraph episodic retrieval was transcoded as the RAG baseline the paper beats, not the paper's structural search
257+
258+
**Status:** Open
259+
**Priority:** P1 (the transcoded substrate silently behaves as the baseline AriGraph outperforms; correctness-of-port, not a crash)
260+
**Scope:** crate:lance-graph domain:arigraph domain:retrieval D-CSV-6 D-CSV-7
261+
**Introduced by:** the Python→Rust AriGraph transcode (`crates/lance-graph/src/graph/arigraph/`, per `.claude/knowledge/integration-plan-grammar-crystal-arigraph.md` E11)
262+
**Payoff estimate:** Option A ~150-250 LOC in-place; Option B = D-CSV-6 (~600) + D-CSV-7 (~350), substrate change
263+
264+
### What (ground truth: arxiv 2407.04363 §2 + Alg.1 + eq.1)
265+
266+
The paper's world model is `G = (V_s, E_s, V_e, E_e)`: semantic vertices/edges (triplets) + episodic vertices (observations) + **episodic edges** `e^t_e = (v^t_e, E^t_s)` linking each observation to the triplets extracted at that step. Retrieval is **two-stage and structural**: semantic search returns `E^Q_s`; episodic search then scores each episode by triplet-incidence `rel(v^i_e) = (n_i / max(N_i,1)) · log(max(N_i,1))` (single-triplet observations weighted 0).
267+
268+
The transcode diverged on **both** stages:
269+
- `episodic.rs::EpisodicMemory::top_k` ranks episodes by **Hamming distance between observation fingerprints** (`label_fp`) — RAG-style similarity on raw observation text, *decoupled* from the semantic hits. The structural `n_i/N_i` relevance is absent.
270+
- `retrieval.rs::OsintRetriever` semantic search is exact `entity_index` **name** lookup + BFS (no embedding retrieval; loses the paper's "grill"→"grilling" generalization).
271+
- The **episodic edge `E_e` does not exist as a structure**: the transcoded `triplet_graph.rs` `Triplet` is `{subject, object, relation, truth, timestamp}` — the W5-spec `witness_ref: u64` (the W-slot) was **dropped**. `Episode.triplets: Vec<String>` is used only for unbundle/rebundle, never retrieval.
272+
- Net: **three disconnected episodic/provenance representations**`episodic.rs` (fingerprint-RAG), `witness_corpus.rs` (`WitnessIndexHashMap` spo→positions + `WitnessIndexCamPq`, wired to neither Triplet nor episodic), and the dropped W-slot.
273+
274+
### Options (types kept in both; this is a mechanism fix, not a deletion — "fix from the beginning")
275+
276+
- **Option A — narrow in-place eq.1 fix.** Add a triplet-id link from `Episode` to `E_s`; couple episodic search to consume `E^Q_s`; replace `top_k` fingerprint scoring with eq.1 incidence relevance (zero-weight single-triplet episodes). Keeps `Episode`/`EpisodicMemory`. Lower risk, no substrate change. **Leaves `witness_corpus.rs` still disconnected** (its own residual debt). Semantic-search embedding generalization still open.
277+
- **Option B — mailbox / W-slot convergence (recommended end-state; = existing plan).** Restore the W-slot on `Triplet`/`CausalEdge64` (v2 layout `[53:58]`, the "discourse corpus-root handle"); make `witness_corpus.rs` (already CAM-PQ + HashMap indexed) THE episodic store = the per-`MailboxId` "spatial-temporal meaning accumulator" (`contract::collapse_gate::MailboxId`); retire `episodic.rs`'s fingerprint-RAG. The episodic edge `E_e` = `triplet.W-slot → MailboxId`; eq.1 `n_i/N_i` falls out of `WitnessIndexHashMap::lookup(spo)` incidence. Collapses the three stores into one and stays serialization-free ("the `(source_mailbox, chain_position)` tuple is the wire"). **This is `cognitive-substrate-convergence-v1` D-CSV-6 (`WitnessCorpus`) + D-CSV-7 (`MailboxSoA` W-slot)** — already planned, HIGH risk, gated on the CSV OQ ratification.
278+
279+
**Recommendation:** B is the architecturally-correct convergence and is already the planned direction; A is a legitimate interim that makes retrieval *faithful to the paper* without waiting on the substrate change, at the cost of leaving the `witness_corpus.rs` duplication for B to collapse later.
280+
281+
### Cross-references
282+
283+
- Paper source: arxiv 2407.04363 §2 "AriGraph World Model" + Alg.1 (Memory Graph Search) + eq.1 (episodic relevance).
284+
- `crates/lance-graph/src/graph/arigraph/{episodic.rs, retrieval.rs, triplet_graph.rs, witness_corpus.rs}`
285+
- `.claude/plans/cognitive-substrate-convergence-v1.md` D-CSV-6 (`WitnessCorpus` replaces `SpoWitnessChain<32>`) + D-CSV-7 (`MailboxSoA` W-slot) + §6 v2 layout `[53:58]` W slot
286+
- `.claude/knowledge/spo-schema-and-mailbox-sidecar.md` (SPO-W tetrahedron; `MailboxId` = meaning accumulator); `.claude/knowledge/integration-plan-grammar-crystal-arigraph.md` E11 (transcode provenance)
287+
288+
---
289+
290+
## 2026-05-27 — TD-JSON-SERIALIZATION-SITES-1: JSON/serde occurrences catalogued; internal-substrate serde is debt, outer-boundary ingestion is not
291+
292+
**Status:** Open
293+
**Priority:** P2 (no crash; violates the single-binary no-serialization invariant where it occurs internally)
294+
**Scope:** crate:lance-graph crate:lance-graph-callcenter domain:serialization domain:invariant
295+
**Introduced by:** AriGraph transcode (serde-on-substrate) + D-SDR-4/5 audit sinks (JSON egress)
296+
**Payoff estimate:** substrate serde-derive strip ~per-file small; audit JSON→binary canonical_bytes is the larger item (tracked separately)
297+
298+
### The invariant
299+
300+
lance-graph compiles every "program" into **one statically-linked binary** — there is no internal IPC/network boundary, so serialization between in-binary parts is meaningless. The rule (BOOT.md #6): **"No JSON serialization in types. Serde stays debug-only."** Serialization is legitimate ONLY at the **outer ingestion boundary** — post-compile input that must be parsed: files / REST / a query language / external tokens. JSON is excluded everywhere else because the canonical bytes (`canonical_bytes()` / Arrow columns / the CAM bar-code) *are* the value; JSON would be a redundant second representation.
301+
302+
### Acceptable — outer-boundary ingestion (serde correct by design, NOT debt)
303+
304+
| Site | Boundary |
305+
|---|---|
306+
| `lance-graph/src/{ast.rs, logical_plan.rs}` | Cypher text → AST → plan IR feeding DataFusion (cold-path parse) |
307+
| `lance-graph/src/parameter_substitution.rs` | `HashMap<String, serde_json::Value>` query params (post-compile input) |
308+
| `lance-graph/src/config.rs` | TOML config load at startup |
309+
| `lance-graph-callcenter/src/auth.rs` | JWT claims (`serde_json::from_slice`) — JWT is base64url-JSON by RFC 7519 |
310+
| `lance-graph-catalog/src/unity_catalog.rs` | Databricks Unity Catalog REST (external service) |
311+
| `cognitive-shader-driver/src/wire.rs`, `lance-graph-callcenter/src/postgrest.rs`, `*/serve.rs` | post-compile REST ingestion points (lab/research surface per `lab-vs-canonical-surface.md`) |
312+
| `lance-graph-contract/src/literal_graph.rs::ingest_aiwar_json` | physical parser for an external `.json` data file (zero-dep, hand-rolled) |
313+
314+
### Debt — internal / substrate / egress (no boundary; violates the invariant)
315+
316+
1. **serde derived on AriGraph cognitive substrate types**`graph/arigraph/orchestrator.rs` (`MetaOrchestrator`, `StyleTopology`, `TopologyEdge`, `MulAssessment`, `DkPosition`, `TrustTexture`, `FlowState`, `GraphSensorium`, …) and `graph/arigraph/sensorium.rs` (`GraphSensorium`/`GraphBias`/`HealingAction`/`HealingType`, also intra-crate-duplicated with orchestrator.rs) + `graph/spo/truth.rs::TruthValue`. These are hot-path substrate (transcode cruft from the Python source's dicts). The only legitimate egress is the `/mri` `OrchestratorSnapshot`/`TopologyEdgeSnapshot` DTOs — serde belongs **only** on those boundary DTOs, stripped from the core types. (CONJECTURE: the `/mri` HTTP handler that serializes the snapshot was not located this session; confirm before stripping.)
317+
2. **Audit log emitted as JSON**`lance-graph-callcenter/src/audit_sink/jsonl_sink.rs` (JSON lines) and `lance_sink.rs:151` (a JSON string stuffed into an Arrow column), read back by `bin/audit_verify.rs`; reachable via `UnifiedBridge::with_jsonl_audit`. The audit event's canonical form is already the 26-byte binary `UnifiedAuditEvent::canonical_bytes()` that the merkle chain hashes — JSON is a redundant second representation. Canonical egress should be the binary append-log or typed Arrow columns, not JSON. **Reframes TD-SDR-AUDIT-PERSIST-1** (which treats the JSONL sink as owed *work*; under this invariant the JSON form itself is the debt).
318+
319+
`serde_json` is an **optional** dep gated behind `jsonl`/`realtime`/`auth-jwt`/`lance-sink`; the default callcenter build pulls zero JSON.
320+
321+
### Cross-references
322+
323+
- `BOOT.md` #6 (serde-out-of-types); `.claude/knowledge/integration-plan-grammar-crystal-arigraph.md:166` ("serde kept out of types by project convention")
324+
- `TD-SDR-AUDIT-PERSIST-1` (the JSONL-sink-as-deliverable entry this reframes)
325+
- `crates/lance-graph-callcenter/src/audit_sink/{jsonl_sink.rs, lance_sink.rs}`, `bin/audit_verify.rs`, `unified_bridge.rs::with_jsonl_audit`
326+
- `crates/lance-graph/src/graph/arigraph/{orchestrator.rs, sensorium.rs}`, `graph/spo/truth.rs`
327+
328+
---
329+
330+
256331
## 2026-05-13 — TD-Q2-STUBS-DEDUP-1: q2 carries local `lance-graph` + `q2-ndarray` stubs that must be replaced with re-exports from the canonical crates before FMA demo can compile
257332

258333
**Status:** Open

0 commit comments

Comments
 (0)