From 350f6bed59e68fe5007e80083cf6c516857c353f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 04:55:05 +0000 Subject: [PATCH 1/4] =?UTF-8?q?contract:=20flip=20FMA=5FANATOMY=20to=20V3?= =?UTF-8?q?=20(CLASSID=5FFMA=5FV3)=20=E2=80=94=20matches=20q2=20/helix=20b?= =?UTF-8?q?ody=20substrate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit q2's osint-bake/body (the /helix body.soa substrate) already mints CLASSID_FMA_V3, so lance-graph's V1-pinned FMA_ANATOMY domain was the inconsistency. Flip it to CLASSID_FMA_V3 (cfg-selected, V1 fallback under --no-default-features), mirroring OSINT_GOTHAM. The routing was already tail-aware: `hhtl_path` dispatches V3 classids to `from_guid_prefix_v3` (folds HEEL·HIP·TWIG·LEAF, classid NOT folded) and `nearest_anchor` uses it. So the anchor hop depths legitimately change under V3 (the classid_lo tier drops out of the fold): the `nearest_anchor` test now asserts the V3 values (lcp 3 ⇒ 26, lcp 0 ⇒ 32) under the feature and keeps the V1 values (18/24) under --no-default-features. Ordering is invariant either way. (An earlier revert of this flip was wrong — it mistook the correct V3 fold depth for a degraded fallback; the operator corrected it: q2 /helix is V3.) PROJECT/ERP stay V1 — no CLASSID_*_V3 registered for them yet. Verification: default 854 lib tests green (V3, 26/32); --no-default-features anchor test green (V1, 18/24); fmt clean. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MLBnPuScZy6w9di2QEjsXM --- crates/lance-graph-contract/src/soa_graph.rs | 33 +++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/crates/lance-graph-contract/src/soa_graph.rs b/crates/lance-graph-contract/src/soa_graph.rs index 52c0b466..be6c3e59 100644 --- a/crates/lance-graph-contract/src/soa_graph.rs +++ b/crates/lance-graph-contract/src/soa_graph.rs @@ -108,8 +108,18 @@ pub const OSINT_GOTHAM: DomainSpec = DomainSpec { /// structural entities, family = body region, `out_family` = part-of. Anchor /// families are the *bones* (the skeleton the soft tissue hangs off); the /// default declares none — a caller supplies the bone families. +/// The FMA domain classid: the V3 anatomy class (`CLASSID_FMA_V3`) in a normal +/// build; the legacy V1 `CLASSID_FMA` only under `--no-default-features`. +/// Matches q2's `osint-bake/body` (`/helix` substrate) which already mints +/// `CLASSID_FMA_V3`. V3 routes on the `from_guid_prefix_v3` fold (classid not +/// folded) — `hhtl_path`/`nearest_anchor` are already tail-aware. +#[cfg(feature = "guid-v3-tail")] +const FMA_ANATOMY_CLASSID: u32 = NodeGuid::CLASSID_FMA_V3; +#[cfg(not(feature = "guid-v3-tail"))] +const FMA_ANATOMY_CLASSID: u32 = NodeGuid::CLASSID_FMA; + pub const FMA_ANATOMY: DomainSpec = DomainSpec { - classid: NodeGuid::CLASSID_FMA, + classid: FMA_ANATOMY_CLASSID, name: "FMA-Anatomy", anchor_families: &[], in_family_edge: "adjacent-to", @@ -622,9 +632,24 @@ mod tests { hops[2].hops, hops[3].hops ); - // The exact fixed-depth-16 values: 2·(16−7)=18 and 2·(16−4)=24. - assert_eq!(hops[2].hops, 18); - assert_eq!(hops[3].hops, 24); + // The exact fixed-depth-16 values depend on the fold: + // - V3 (default, `from_guid_prefix_v3`): folds HEEL·HIP·TWIG·LEAF, classid + // NOT folded, so lcp counts only the shared HEEL nibbles. node2 HEEL + // 0x1009 shares 0x100 (lcp 3) ⇒ 2·(16−3)=26; node3 HEEL 0xF000 diverges + // at nibble 0 (lcp 0) ⇒ 2·(16−0)=32. + // - V1 (`--no-default-features`, `from_guid_prefix`): folds + // classid_lo·HEEL·HIP·TWIG; the shared classid_lo adds 4 to the lcp ⇒ + // lcp 7 ⇒ 18 and lcp 4 ⇒ 24. + #[cfg(feature = "guid-v3-tail")] + { + assert_eq!(hops[2].hops, 26); + assert_eq!(hops[3].hops, 32); + } + #[cfg(not(feature = "guid-v3-tail"))] + { + assert_eq!(hops[2].hops, 18); + assert_eq!(hops[3].hops, 24); + } } #[test] From 774d34fc33bcb383c829f37891e2cbc2b8d19553 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 05:32:45 +0000 Subject: [PATCH 2/4] causal-edge: silence v1/v2 deprecation + dead-code warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-site #[allow(deprecated)] / #[allow(dead_code)] on the v1 accessor paths that remain live under one feature config and deprecated under the other. No semantics change — the mantissa-bug fix (v2 signed 4-bit read via from_mantissa; pack writes to_mantissa) is untouched; this only quiets the noise so consumers vendoring causal-edge build clean. - edge.rs from_bits / PLAST_SHIFT: dead under v2 (superseded by mantissa decode / crate::layout::PLAST_SHIFT), live under v1 → #[allow(dead_code)]. - edge.rs compose t_out + resolved_infer v1 arm: v1 reads deprecated under v2 layout; the pack() below already drops temporal → per-arm allow. - edge.rs Debug fmt / network.rs evidence_trail sort: v1 diagnostic/ordering views, deprecated but harmless under v2 → #[allow(deprecated)]. Verified warning-free for both default (v2) and --no-default-features (v1); 48/48 tests green. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MLBnPuScZy6w9di2QEjsXM --- crates/causal-edge/src/edge.rs | 14 +++++++++++++- crates/causal-edge/src/network.rs | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/causal-edge/src/edge.rs b/crates/causal-edge/src/edge.rs index e5b1abc3..02aeb407 100644 --- a/crates/causal-edge/src/edge.rs +++ b/crates/causal-edge/src/edge.rs @@ -30,6 +30,10 @@ pub enum InferenceType { } impl InferenceType { + // Used only on the v1 (non-`causal-edge-v2-layout`) read path; under v2 the + // 4-bit signed mantissa decode (`from_mantissa`) supersedes it. Dead under + // the default v2 feature, live under `--no-default-features`. + #[allow(dead_code)] #[inline] fn from_bits(v: u8) -> Self { match v & 0b111 { @@ -165,6 +169,9 @@ const CONF_SHIFT: u32 = 32; const CAUSAL_SHIFT: u32 = 40; const DIR_SHIFT: u32 = 43; const INFER_SHIFT: u32 = 46; +// v1 plasticity bit position; under v2 plasticity moves to `crate::layout::PLAST_SHIFT`. +// Live on the v1 pack/read paths, dead under the default v2 feature. +#[allow(dead_code)] const PLAST_SHIFT: u32 = 49; const TEMPORAL_SHIFT: u32 = 52; @@ -639,6 +646,7 @@ impl CausalEdge64 { // weight.inference_type() is the v1 fallback below; v2 uses mantissa let resolved_infer = InferenceType::from_mantissa(weight.inference_mantissa()); #[cfg(not(feature = "causal-edge-v2-layout"))] + #[allow(deprecated)] // v1 layout: 3-bit unsigned inference type is the canonical read let resolved_infer = weight.inference_type(); let (f_out, c_out) = match resolved_infer { InferenceType::Deduction => { @@ -679,7 +687,8 @@ impl CausalEdge64 { let mask_out = CausalMask::from_bits((self.causal_mask() as u8) & (weight.causal_mask() as u8)); - // 4. Temporal: latest of the two + // 4. Temporal: latest of the two (v1 read; the pack() below drops it under v2) + #[allow(deprecated)] let t_out = self.temporal().max(weight.temporal()); // 5. Inherit plasticity from weight (the "learned" edge) @@ -1082,6 +1091,9 @@ impl CausalEdge64 { } impl std::fmt::Debug for CausalEdge64 { + // Debug prints the v1 views (`inference_type`/`temporal`) for readability; under + // v2 these are deprecated accessors but harmless for a diagnostic dump. + #[allow(deprecated)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CausalEdge64") .field("spo", &(self.s_idx(), self.p_idx(), self.o_idx())) diff --git a/crates/causal-edge/src/network.rs b/crates/causal-edge/src/network.rs index 8a44e5cf..98e102f7 100644 --- a/crates/causal-edge/src/network.rs +++ b/crates/causal-edge/src/network.rs @@ -156,6 +156,10 @@ impl CausalNetwork { .filter(|e| e.s_idx() == s && e.p_idx() == p && e.o_idx() == o) .collect(); + // v1 temporal ordering; under v2 `temporal()` is deprecated (temporal is + // structural — chain position + Triplet.timestamp — so the sort degrades to + // a stable no-op there). Kept for v1 evidence-trail parity. + #[allow(deprecated)] trail.sort_by_key(|e| e.temporal()); trail } From 1eb034f56e53b8c6a49f1166065b4ccf49cca335 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 06:59:04 +0000 Subject: [PATCH 3/4] =?UTF-8?q?contract:=20mint=20PROJECT/ERP=20V3=20class?= =?UTF-8?q?ids=20=E2=80=94=20flip=20PROJECT/ERP=20DomainSpecs=20to=20V3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds CLASSID_PROJECT_V3 (0x0101_1000) / CLASSID_ERP_V3 (0x0202_1000) + _LEGACY pre-flip aliases (0x1000_0100 / 0x1000_0200), mirroring the exact CLASSID_OSINT_V3/CLASSID_FMA_V3 encoding: canon-HIGH domain:appid byte pair, fixed 0x1000 V3-generation marker in the custom-LOW half. Registers ReadMode::PROJECT_V3/ERP_V3 (same Cognitive value model as their V1 read-modes) in BUILTIN_READ_MODES, both new-form and legacy-alias keys. Encoding-verification finding: the existing V3 consts' custom/low u16 does NOT carry an AppPrefix render-lens value (render_classid's concept-HIGH / app-prefix-LOW composition) — it carries the fixed 0x1000 marker, with the appid instead packed into the canon-HIGH half's low byte alongside the domain (q2 = 0x01 for OSINT/FMA/CPIC). Per the operator's ruling that PROJECT/ERP own their own appid (OpenProject=0x01, Odoo=0x02, not q2's 0x01), those bytes are substituted into the SAME canon-HIGH position: 0x01<<8|0x01 = 0x0101 (PROJECT), 0x02<<8|0x02 = 0x0202 (ERP) — verified against the already-existing test fixtures canonical_concept_domain(0x0101)==ProjectMgmt / (0x0206)==Commerce, which independently confirm the domain-byte-only routing these canon halves need. Flips soa_graph::PROJECT/ERP DomainSpecs to the new V3 classids under the same #[cfg(feature = "guid-v3-tail")] / --no-default-features V1-fallback pattern already used for OSINT_GOTHAM/FMA_ANATOMY. Both domains have empty anchor_families, so no nearest_anchor hop-value test needed cfg-branching. Tests: cargo test -p lance-graph-contract — 854 passed (default), 840 passed (--no-default-features); no new/changed test assertions needed. Co-Authored-By: Claude Opus 4.8 --- .../src/canonical_node.rs | 60 +++++++++++++++++++ crates/lance-graph-contract/src/soa_graph.rs | 24 ++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/crates/lance-graph-contract/src/canonical_node.rs b/crates/lance-graph-contract/src/canonical_node.rs index 40fca1a9..3fe9375c 100644 --- a/crates/lance-graph-contract/src/canonical_node.rs +++ b/crates/lance-graph-contract/src/canonical_node.rs @@ -156,6 +156,42 @@ impl NodeGuid { #[cfg(feature = "guid-v3-tail")] pub const CLASSID_CPIC_V3_LEGACY: u32 = 0x1000_0E00; + /// **PROJECT-V3** — project-management (OpenProject ↔ Redmine) on a + /// [`TailVariant::V3`] cascade tail, minted for **OpenProject** (appid + /// `0x01` — the consumer's OWN appid, operator ruling 2026-07-08: "consumers + /// own their own appid … they do NOT ride q2's 0x01"). Canon `0x0101` HIGH + /// (project-mgmt domain `0x01`, appid `0x01`); the V3 marker `0x1000` in the + /// LOW/custom u16 — `0x01:01::1000`. Same encoding shape as + /// [`CLASSID_OSINT_V3`] (domain:appid canon-HIGH, fixed `0x1000` marker + /// custom-LOW) — NOT the `render_classid`/`AppPrefix` render-lens layout + /// (concept-HIGH, app-prefix-LOW): the V3 mint tail and the OGAR render + /// lens are two independent composition axes that happen to share the + /// `0x01`/`0x02` app byte values here. + /// [`classid_concept_domain`](crate::ogar_codebook::classid_concept_domain) + /// routes [`ProjectMgmt`](crate::ogar_codebook::ConceptDomain::ProjectMgmt). + /// Resolves to [`ReadMode::PROJECT_V3`] (same hot `Cognitive` model as legacy PROJECT). + #[cfg(feature = "guid-v3-tail")] + pub const CLASSID_PROJECT_V3: u32 = 0x0101_1000; + /// Pre-flip stored form of [`CLASSID_PROJECT_V3`] (marker HIGH, canon + /// `0x0100` LOW — pre-normalization appid `:00`) — read-only legacy alias. + #[cfg(feature = "guid-v3-tail")] + pub const CLASSID_PROJECT_V3_LEGACY: u32 = 0x1000_0100; + + /// **ERP-V3** — commerce/ERP (Odoo ↔ OSB) on a [`TailVariant::V3`] cascade + /// tail, minted for **Odoo** (appid `0x02` — the consumer's OWN appid, same + /// 2026-07-08 ruling as [`CLASSID_PROJECT_V3`]). Canon `0x0202` HIGH + /// (commerce domain `0x02`, appid `0x02`); the V3 marker `0x1000` in the + /// LOW/custom u16 — `0x02:02::1000`. + /// [`classid_concept_domain`](crate::ogar_codebook::classid_concept_domain) + /// routes [`Commerce`](crate::ogar_codebook::ConceptDomain::Commerce). + /// Resolves to [`ReadMode::ERP_V3`] (same hot `Cognitive` model as legacy ERP). + #[cfg(feature = "guid-v3-tail")] + pub const CLASSID_ERP_V3: u32 = 0x0202_1000; + /// Pre-flip stored form of [`CLASSID_ERP_V3`] (marker HIGH, canon `0x0200` + /// LOW — pre-normalization appid `:00`) — read-only legacy alias. + #[cfg(feature = "guid-v3-tail")] + pub const CLASSID_ERP_V3_LEGACY: u32 = 0x1000_0200; + /// Construct from the six canonical groups. `family`/`identity` use their low 3 bytes. /// /// Panics (incl. const-eval) when `family` or `identity` exceed 24 bits — the @@ -1155,6 +1191,26 @@ impl ReadMode { edge_codec: EdgeCodecFlavor::CoarseOnly, }; + /// The **PROJECT-V3** read-mode ([`NodeGuid::CLASSID_PROJECT_V3`]): the same + /// hot [`ValueSchema::Cognitive`] value model as legacy [`PROJECT`](ReadMode::PROJECT), + /// read through the new-generation [`TailVariant::V3`] cascade tail. + #[cfg(feature = "guid-v3-tail")] + pub const PROJECT_V3: ReadMode = ReadMode { + tail_variant: TailVariant::V3, + value_schema: ValueSchema::Cognitive, + edge_codec: EdgeCodecFlavor::CoarseOnly, + }; + + /// The **ERP-V3** read-mode ([`NodeGuid::CLASSID_ERP_V3`]): the same hot + /// [`ValueSchema::Cognitive`] value model as legacy [`ERP`](ReadMode::ERP), + /// read through the new-generation [`TailVariant::V3`] cascade tail. + #[cfg(feature = "guid-v3-tail")] + pub const ERP_V3: ReadMode = ReadMode { + tail_variant: TailVariant::V3, + value_schema: ValueSchema::Cognitive, + edge_codec: EdgeCodecFlavor::CoarseOnly, + }; + /// All three axes are layout-preserving (a tail-variant/preset/flavor /// re-interprets reserved bytes, never a stride change), so adopting any /// read-mode needs no `ENVELOPE_LAYOUT_VERSION` bump. @@ -1218,9 +1274,13 @@ static BUILTIN_READ_MODES: LazyLock> = LazyLock::new(|| { m.insert(NodeGuid::CLASSID_OSINT_V3, ReadMode::OSINT_V3); m.insert(NodeGuid::CLASSID_FMA_V3, ReadMode::FMA_V3); m.insert(NodeGuid::CLASSID_CPIC_V3, ReadMode::CPIC_V3); + m.insert(NodeGuid::CLASSID_PROJECT_V3, ReadMode::PROJECT_V3); + m.insert(NodeGuid::CLASSID_ERP_V3, ReadMode::ERP_V3); m.insert(NodeGuid::CLASSID_OSINT_V3_LEGACY, ReadMode::OSINT_V3); m.insert(NodeGuid::CLASSID_FMA_V3_LEGACY, ReadMode::FMA_V3); m.insert(NodeGuid::CLASSID_CPIC_V3_LEGACY, ReadMode::CPIC_V3); + m.insert(NodeGuid::CLASSID_PROJECT_V3_LEGACY, ReadMode::PROJECT_V3); + m.insert(NodeGuid::CLASSID_ERP_V3_LEGACY, ReadMode::ERP_V3); } m }); diff --git a/crates/lance-graph-contract/src/soa_graph.rs b/crates/lance-graph-contract/src/soa_graph.rs index be6c3e59..56362901 100644 --- a/crates/lance-graph-contract/src/soa_graph.rs +++ b/crates/lance-graph-contract/src/soa_graph.rs @@ -127,12 +127,20 @@ pub const FMA_ANATOMY: DomainSpec = DomainSpec { member_edge: "part-of", }; -/// The **project-management** domain (classid [`NodeGuid::CLASSID_PROJECT`], +/// The project-management domain classid: the V3 class +/// [`NodeGuid::CLASSID_PROJECT_V3`] in a normal build; the legacy V1 +/// [`NodeGuid::CLASSID_PROJECT`] only under `--no-default-features`. +#[cfg(feature = "guid-v3-tail")] +const PROJECT_CLASSID: u32 = NodeGuid::CLASSID_PROJECT_V3; +#[cfg(not(feature = "guid-v3-tail"))] +const PROJECT_CLASSID: u32 = NodeGuid::CLASSID_PROJECT; + +/// The **project-management** domain (V3 class [`NodeGuid::CLASSID_PROJECT_V3`], /// OGAR `0x01XX`): OpenProject ↔ Redmine work items. Family = project / version; /// `in_family` = relates-to, `out_family` = blocks (cross-project dependency). /// Anchor families are caller-supplied (the milestone / release hubs). pub const PROJECT: DomainSpec = DomainSpec { - classid: NodeGuid::CLASSID_PROJECT, + classid: PROJECT_CLASSID, name: "Project", anchor_families: &[], in_family_edge: "relates-to", @@ -140,12 +148,20 @@ pub const PROJECT: DomainSpec = DomainSpec { member_edge: "in-project", }; -/// The **commerce / ERP** domain (classid [`NodeGuid::CLASSID_ERP`], OGAR +/// The commerce/ERP domain classid: the V3 class [`NodeGuid::CLASSID_ERP_V3`] +/// in a normal build; the legacy V1 [`NodeGuid::CLASSID_ERP`] only under +/// `--no-default-features`. +#[cfg(feature = "guid-v3-tail")] +const ERP_CLASSID: u32 = NodeGuid::CLASSID_ERP_V3; +#[cfg(not(feature = "guid-v3-tail"))] +const ERP_CLASSID: u32 = NodeGuid::CLASSID_ERP; + +/// The **commerce / ERP** domain (V3 class [`NodeGuid::CLASSID_ERP_V3`], OGAR /// `0x02XX`): Odoo ↔ OSB invoices / partners / taxes. Family = partner / journal; /// `in_family` = line-of, `out_family` = paid-by (cross-partner settlement). /// Anchor families are caller-supplied (the key accounts / journals). pub const ERP: DomainSpec = DomainSpec { - classid: NodeGuid::CLASSID_ERP, + classid: ERP_CLASSID, name: "ERP", anchor_families: &[], in_family_edge: "line-of", From 8f48ba167731e080607b15133747ced20eb18c9c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 06:59:21 +0000 Subject: [PATCH 4/4] =?UTF-8?q?board:=20ISS-V1-TAIL-RESIDUE=20=E2=80=94=20?= =?UTF-8?q?woa-rs=20make=5Faccount=5Fguid=5Fbytes=20arm=20RESOLVED?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Board hygiene only, no code change in this repo. woa-rs's ERP account-GUID minter (src/erp/canon.rs::make_account_guid_bytes) was migrated to the V3 tail today (2026-07-08, operator ruling) in the sibling woa-rs repo: leaf(u16)/family(u16)/identity(u16) byte-identical to new_v2/mint_for(V3); Personenkonten trailing digits (SKR03 70000-99999) now a real LEAF (part_of:is_a) rail via skr_leaf() instead of the old V1 family-hash stuffing; Parallelbetrieb invariant pinned (MySQL ORM stays authoritative, identity = MySQL row id, u16-by-signature with a loud try_from at call sites, never a silent alias). 11/11 tests green in woa-rs. Prepended as a new dated entry (this file's own discipline: newest first, double-entry, no deletions) — this is a THIRD residue arm under the ISS-V1-TAIL-RESIDUE umbrella, alongside the two lance-graph-contract mint sites resolved 2026-07-07 (ocr.rs/aiwar.rs, below). Co-Authored-By: Claude Opus 4.8 --- .claude/board/ISSUES.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index 3f1693ac..2274e8e1 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,5 +1,40 @@ # Issues Log — Open + Resolved (double-entry, append-only) +## 2026-07-08 — ISS-V1-TAIL-RESIDUE — woa-rs arm — RESOLVED (`make_account_guid_bytes` migrated to the V3 tail) + +**Status:** RESOLVED (operator ruling 2026-07-08, landed in woa-rs — sibling repo, +not this branch). This is a THIRD residue arm under the `ISS-V1-TAIL-RESIDUE` +umbrella, alongside the two lance-graph-contract mint sites (`ocr.rs`/`aiwar.rs`, +resolved 2026-07-07 below) — woa-rs's ERP account-GUID minter carried its own +independent V1-tail residue (`family` hash stuffing the Personenkonten trailing +digits) that the lance-graph-side fix did not touch. + +**Landed:** woa-rs `src/erp/canon.rs::make_account_guid_bytes` now produces +`leaf(u16)/family(u16)/identity(u16)` byte-identical to `NodeGuid::new_v2`/ +`mint_for(TailVariant::V3, …)` — the V1→V3 semantic move: the Personenkonten +trailing digits (SKR03 70000-99999) that V1 stuffed into the `family` **hash** +now live in the **LEAF tier** as a real `(ten_bucket:final_digit)` `(part_of:is_a)` +rail (`skr_leaf()` decomposes `70123` → LEAF `(2,3)`), matching the same +canonical `HEEL·HIP·TWIG·LEAF·family·identity` cascade shape this branch's +`mint_for` dispatch uses. **Parallelbetrieb invariant pinned** (doc block +"READ THIS FIRST" in `canon.rs`): the MySQL ORM mapping stays authoritative — +`identity` = the MySQL `erp_accounts` row id, converted `u16`-by-signature with +a loud `try_from` (`.expect("Parallelbetrieb: erp_accounts row id must …")`) at +every call site, never a silent `as` alias/truncation. **11/11 tests green** in +woa-rs (`src/erp/canon.rs` — 4-digit accounts, Personenkonten LEAF decompose, +round-trip byte layout, MySQL-id round-trip). + +**Scope note:** woa-rs is a sibling repo (not this lance-graph worktree) — +this entry is board-hygiene bookkeeping only, landed same-commit as any other +ISSUES.md bookkeeping in this session per the V3 plan's standing gate 3 +("board hygiene same-commit"). No lance-graph-contract code changed for this +arm. + +Cross-ref: woa-rs `src/erp/canon.rs` (`make_account_guid_bytes`, `skr_leaf`); +this file's `ISS-V1-TAIL-RESIDUE` 2026-07-07 / 2026-07-04 entries (the +lance-graph-contract arms of the same residue umbrella); `.claude/v3/INTEGRATION-PLAN.md` +standing gate 3. + ## 2026-07-07 — ISS-V1-TAIL-RESIDUE — RESOLVED (un-gate + default-on + both mint sites V3-routed; aiwar mints real V3) **Status:** RESOLVED. Landed in one PR (#663):