diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index efc49c557..d8beb3630 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -77,9 +77,9 @@ Plan: `.claude/plans/v3-mandatory-hotplug-reading-v1.md`. | **D-HPR-3** | Consumers move (lance-graph, q2, MedCare); `classid_read_mode` deprecated | Queued (2026-10-11 census: MedCare has 0 callers; q2's are offline bake mints; deepnsm-v2 is blocked on a concept mint, not code) | no NEW reading selected through the table (rule 5 keeps stored keys readable) | | **D-HPR-4** | `0x1000` / render prefixes documented as deprecated, kept (no eviction) | Shipped for `0x1000` (#1464; doc notes on `CLASSID_*_V3` + `classid_read_mode`); render prefixes, `NodeGuid::read_mode` and OGAR/q2 sites still carry no note | — | | **D-HPR-5** | Stored legacy keys keep resolving; a `SlabDeclaration` may override for its own data; no forced removal | Holds today (status quo). Caveat: a `SlabDeclaration` overrides `value_schema` only, never the tail, so it cannot protect a V1/V2-tail slab (`hotplug.rs:231-257, 392`) | — | -| **D-HPR-6** | No V1 default for an unknown classid; `guid-v3-tail` not a gate | Queued — split: **6a** un-gate `guid-v3-tail` (default-on, and no fleet consumer disables it, so only `--no-default-features` builds change; there the 5 V3 fossils read `DEFAULT` today); **6b** unknown classid → error. Decided 2026-10-11: go, staged after 6a, D-HPR-3 and a stored-classid check | unknown classid → `NoReadingFor` | -| **D-HPR-7** | Storage readings leave the concept key: out of `concept_override`, into the slab's `SlabDeclaration` or the owning consumer's plug (O3, decided (a) 2026-10-11) | In PR (#1474; the five domain arms removed; the domain models differed from plug-and-play only in `value_schema`, which `SlabDeclaration` overrides; tests red before, green after) | a non-Odoo plug of `0x0202` reads `PLUG_AND_PLAY_V3`; Odoo's slab still reads `ERP_V3` | -| **D-HPR-8** | A plug that asks only for a reading succeeds (reading is not a capability; decided 2026-10-11) | In PR (#1474; `OgarAuthority::activate` skips the capability join when `covered` is empty; ids still must be minted; red before the fix with `NoCapabilitiesFor(0x0103)`; disable run turns the twin red) | `[0x0103]` with `covered: &[]` → `Ok`, `PLUG_AND_PLAY_V3`; with a covered capability → refused as today | +| **D-HPR-6** | No V1 default for an unknown classid; `guid-v3-tail` not a gate | **6a** In PR (un-gated: `guid-v2-tail`/`guid-v3-tail` are empty names, `new_v2` + the five V3 constants + `codebook` unconditional, the panic arm gone; scope ruled 2026-10-11: the 128-bit opt-in — identity and edge facet as 16×u8, lo = is_a, hi = part_of — is not touched, and the new guard `v3_mint_is_the_128_bit_facet` pins that a V3 mint is byte-identical to `FacetCascade` for key and edge; a byte-swap in `new_v2` turns 2 of its tests red). **6b** Queued — unknown classid → error. Decided 2026-10-11: go, staged after 6a, D-HPR-3 and a stored-classid check | unknown classid → `NoReadingFor` | +| **D-HPR-7** | Storage readings leave the concept key: out of `concept_override`, into the slab's `SlabDeclaration` or the owning consumer's plug (O3, decided (a) 2026-10-11) | Shipped (#1474; the five domain arms removed; the domain models differed from plug-and-play only in `value_schema`, which `SlabDeclaration` overrides; tests red before, green after) | a non-Odoo plug of `0x0202` reads `PLUG_AND_PLAY_V3`; Odoo's slab still reads `ERP_V3` | +| **D-HPR-8** | A plug that asks only for a reading succeeds (reading is not a capability; decided 2026-10-11) | Shipped (#1474; `OgarAuthority::activate` skips the capability join when `covered` is empty; ids still must be minted; red before the fix with `NoCapabilitiesFor(0x0103)`; disable run turns the twin red) | `[0x0103]` with `covered: &[]` → `Ok`, `PLUG_AND_PLAY_V3`; with a covered capability → refused as today | ## D-XGP — Cross-glove business parity: Odoo × SAP over one Quack algebra (2026-10-10) diff --git a/.claude/plans/v3-mandatory-hotplug-reading-v1.md b/.claude/plans/v3-mandatory-hotplug-reading-v1.md index 3c767763b..8e25b13eb 100644 --- a/.claude/plans/v3-mandatory-hotplug-reading-v1.md +++ b/.claude/plans/v3-mandatory-hotplug-reading-v1.md @@ -63,6 +63,7 @@ What it means, in rules: - [ ] **W6 — D-HPR-6: V1 cannot be reached by default.** `ReadMode::DEFAULT` stops being the fallback for an unknown classid (it becomes `NoReadingFor`, as on the plug path), and `guid-v3-tail` stops being a feature gate. - Split (2026-10-11). **W6a**: un-gate `guid-v3-tail`. It is default-on and no fleet consumer disables it, so only `--no-default-features` builds change; there the 5 V3 fossils read `DEFAULT` today (coresearch probe P-X3). **W6b**: an unknown classid errors. This is a behaviour change, and both q2 and MedCare have already hit the V1 default once, so it needs operator approval. - **Decided (operator, 2026-10-11: "Recommended accepted"): go, staged.** 1. W6a. 2. W3 moves the remaining mint paths onto plug readings. 3. A check that every stored artifact's classid resolves. 4. Only then W6b. + - **W6a scope (2026-10-11, decision):** removing the gate must not move a byte. The identity key and the edge facet are each already read as 16×u8 through the 128-bit opt-in (`facet::FacetCascade`, `classid(4) | 6×(8:8)`, lo = is_a, hi = part_of); `new_v2` writes those same bytes. W6a therefore only drops the cfg gates (the feature names stay empty for consumers) and adds a byte-identity guard; it does not re-carve the key. - [x] **W7 — D-HPR-7: storage readings leave the concept key** (O3, decided (a)). **Shipped (#1474):** the five domain arms are removed from `concept_override`. The domain models differed from `PLUG_AND_PLAY_V3` only in `value_schema`, which a `SlabDeclaration` overrides, so no new mechanism was needed. Test `canon_domains_read_plug_and_play_and_their_slab_restores_the_domain_model`. The five domain storage readings move out of `concept_override` (keyed by concept) into the slab's `SlabDeclaration` or the owning consumer's plug. A consumer plugging a shared concept then reads `PLUG_AND_PLAY_V3`. Falsifier: a plug of `0x0202` by a non-Odoo consumer reads `PLUG_AND_PLAY_V3`, and Odoo's own slab still reads `ERP_V3`. - [x] **W8 — D-HPR-8: a plug that asks only for a reading succeeds** (decided). **Shipped:** `lance-graph-ogar` `OgarAuthority::activate`, tests `a_plug_covering_nothing_activates_for_its_reading_only` (SAP glove on `0x0103`, a non-executor on `0x0901`) and `a_plug_that_covers_a_capability_is_still_refused_without_its_table`. The glove-parity pin now says both glove plugs activate for their reading; claiming a capability on `0x0103` is still `NoCapabilitiesFor`. Reading is not a capability. A plug with `covered` empty gets readings only, also for a concept that has a capability table. A plug that names a capability still needs the table. Falsifier: `[0x0103]` with `covered: &[]` → `Ok` and `read_mode_for(0x0103) == PLUG_AND_PLAY_V3`; with `covered: &["anything"]` → refused as today. diff --git a/crates/lance-graph-contract/Cargo.toml b/crates/lance-graph-contract/Cargo.toml index d306b9b60..b04aa5731 100644 --- a/crates/lance-graph-contract/Cargo.toml +++ b/crates/lance-graph-contract/Cargo.toml @@ -36,37 +36,24 @@ name = "i4_batch" harness = false [features] -# default-on `guid-v3-tail` (operator ruling 2026-07-04, ISS-V1-TAIL-RESIDUE): -# every normal build mints real V3 keys via `mint_for`. `--no-default-features` -# still compiles (mint_for's V1 arm is un-gated; V2/V3 arms fall back to V1). -default = ["guid-v3-tail"] +# No default features. The V2/V3 tail is unconditional since D-HPR-6a +# (2026-10-11); `--no-default-features` and a default build compile the same. +default = [] # A-unlock-stepdomain — `step_trajectory_hash` forward stub for the E4 # cross-PR bridge between PR #278 audit log + PR #279 grammar trajectory. # No-op alias today; concrete impl lands once the bridge PR ships. trajectory-audit = [] -# guid-v2-tail (D-GV2-1, plan: guid-v2-tail-per-family-codebook-v1.md) — the v2 -# basin tail leaf(u16)·family(u16)·identity(u16) replacing v1 family(u24)· -# identity(u24), leaf as the 4th HHTL tier. Default OFF; additive v2 accessors -# (`leaf`/`*_v2`) coexist with v1 until cutover (D-GV2-5). Layout reclaim → -# I-LEGACY-API-FEATURE-GATED (field-isolation matrix + version gate). +# guid-v2-tail / guid-v3-tail — EMPTY since D-HPR-6a (2026-10-11). They gated +# the v2/v3 tail constructor (`new_v2`), the five V3 classid constants and the +# `codebook` module; all of that is now unconditional. The names stay so the +# consumers that request them (deepnsm-v2, MedCare-rs, q2) still resolve. +# `new_v2` writes the same 16 bytes `facet::FacetCascade` holds +# (`classid(4) | 6×(8:8)`, lo = is_a, hi = part_of); see +# `canonical_node::v3_mint_is_the_128_bit_facet`. guid-v2-tail = [] - -# guid-v3-tail (P-A, plan: soa-value-tenant-migration-v2.md §2.1/§2.2) — gates the -# V3 cascade-key per-classid entries. The V3 identity AXIS itself (TailVariant::V3 -# + ReadMode::tail_variant) is unconditional, latent (nothing reads tail_variant -# yet) → non-breaking. Since the classid half-order flip (P1, 2026-07-02, -# classid-canon-custom-flip-v1.md) the CANON `domain:appid` sits in the HIGH u16 -# and the gen-marker 0x1000 in the LOW (custom) u16: OSINT-V3 = 0x0701_1000, -# FMA-V3 = 0x0A01_1000, CPIC-V3 = 0x0E01_1000; the pre-flip 0x1000_DDCC forms -# stay registered as read-only legacy aliases (mint-forward). Default OFF. -# -# Implies `guid-v2-tail`: V3 is a *reading* of the SAME leaf·family·identity 3×u16 -# tail bytes v2 mints (the (part_of:is_a) cascade reinterprets them, never re-carves), -# so the V3 mint path (`NodeGuid::mint_for`'s V3 arm) dispatches to `new_v2`. The -# tail constructor must exist whenever a V3 classid can be minted. -guid-v3-tail = ["guid-v2-tail"] +guid-v3-tail = [] # tenant-counters — per-ValueTenant update counters for debug instrumentation of # the SoA write cascade (the capstone NaN-census / seam-wiring measurement). OFF diff --git a/crates/lance-graph-contract/src/aiwar.rs b/crates/lance-graph-contract/src/aiwar.rs index f5503c1ee..08eac81c2 100644 --- a/crates/lance-graph-contract/src/aiwar.rs +++ b/crates/lance-graph-contract/src/aiwar.rs @@ -75,12 +75,8 @@ pub fn aiwar_node_rows(graph: &LiteralGraph) -> Vec { // ONE OSINT class, minted V3 (ISS-V1-TAIL-RESIDUE): route through `mint_for` // so the classid's registered `tail_variant` drives the tail. `OSINT_GOTHAM` // filters on the SAME selector, and `family_of`/`identity_of` decode the V3 - // basin tail-aware — so the round-trip is consistent. `--no-default-features` - // falls back to the V1 `CLASSID_OSINT` (the V3 class is gated out there). - #[cfg(feature = "guid-v3-tail")] + // basin tail-aware — so the round-trip is consistent. let osint_classid = NodeGuid::CLASSID_OSINT_V3; - #[cfg(not(feature = "guid-v3-tail"))] - let osint_classid = NodeGuid::CLASSID_OSINT; aiwar_node_rows_with(graph, osint_classid, classid_read_mode(osint_classid)) } @@ -213,12 +209,8 @@ mod tests { #[test] fn rows_are_osint_class_and_head_only() { - // ONE OSINT class: V3 in a normal build, V1 under `--no-default-features` - // (mirrors `aiwar_node_rows` + `OSINT_GOTHAM`). - #[cfg(feature = "guid-v3-tail")] + // ONE OSINT class, V3 (mirrors `aiwar_node_rows` + `OSINT_GOTHAM`). let expected_classid = NodeGuid::CLASSID_OSINT_V3; - #[cfg(not(feature = "guid-v3-tail"))] - let expected_classid = NodeGuid::CLASSID_OSINT; let g = ingest_aiwar_json(SAMPLE).unwrap(); for row in aiwar_node_rows(&g) { assert_eq!(row.key.classid(), expected_classid); @@ -228,7 +220,6 @@ mod tests { /// D-HPR-1: `aiwar_node_rows_with` reproduces `aiwar_node_rows` when handed /// the same reading, and follows a different reading when handed one. - #[cfg(feature = "guid-v3-tail")] #[test] fn with_form_follows_the_handed_reading() { use crate::canonical_node::{EdgeCodecFlavor, TailVariant, ValueSchema}; diff --git a/crates/lance-graph-contract/src/canonical_node.rs b/crates/lance-graph-contract/src/canonical_node.rs index 901bd67f3..192fa91ce 100644 --- a/crates/lance-graph-contract/src/canonical_node.rs +++ b/crates/lance-graph-contract/src/canonical_node.rs @@ -115,12 +115,10 @@ impl NodeGuid { /// (`Activation::resolve_for_context`), not from this value. Existing keys /// and callers keep working and nothing is evicted; new code should not /// give classview `0x1000` a meaning (plan `v3-mandatory-hotplug-reading-v1`). - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_OSINT_V3: u32 = 0x0701_1000; /// Pre-flip stored form of [`CLASSID_OSINT_V3`] (marker HIGH, canon /// `0x0700` LOW — note the pre-normalization appid `:00`) — read-only /// legacy alias for persisted rows; do NOT mint with this. - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_OSINT_V3_LEGACY: u32 = 0x1000_0700; /// **FMA-V3** — FMA anatomy on a [`TailVariant::V3`] cascade tail, minted @@ -135,10 +133,8 @@ impl NodeGuid { /// (`Activation::resolve_for_context`), not from this value. Existing keys /// and callers keep working and nothing is evicted; new code should not /// give classview `0x1000` a meaning (plan `v3-mandatory-hotplug-reading-v1`). - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_FMA_V3: u32 = 0x0A01_1000; /// Pre-flip stored form of [`CLASSID_FMA_V3`] — read-only legacy alias. - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_FMA_V3_LEGACY: u32 = 0x1000_0A01; /// **CPIC-V3** — CPIC pharmacogenomics (gene–drug guidelines) on a @@ -167,11 +163,9 @@ impl NodeGuid { /// (`Activation::resolve_for_context`), not from this value. Existing keys /// and callers keep working and nothing is evicted; new code should not /// give classview `0x1000` a meaning (plan `v3-mandatory-hotplug-reading-v1`). - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_CPIC_V3: u32 = 0x0E01_1000; /// Pre-flip stored form of [`CLASSID_CPIC_V3`] (marker HIGH, canon /// `0x0E00` LOW — pre-normalization appid `:00`) — read-only legacy alias. - #[cfg(feature = "guid-v3-tail")] pub const CLASSID_CPIC_V3_LEGACY: u32 = 0x1000_0E00; /// **PROJECT-V3** — project-management (OpenProject ↔ Redmine) on a @@ -194,11 +188,9 @@ impl NodeGuid { /// (`Activation::resolve_for_context`), not from this value. Existing keys /// and callers keep working and nothing is evicted; new code should not /// give classview `0x1000` a meaning (plan `v3-mandatory-hotplug-reading-v1`). - #[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 @@ -215,11 +207,9 @@ impl NodeGuid { /// (`Activation::resolve_for_context`), not from this value. Existing keys /// and callers keep working and nothing is evicted; new code should not /// give classview `0x1000` a meaning (plan `v3-mandatory-hotplug-reading-v1`). - #[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. @@ -383,20 +373,13 @@ impl NodeGuid { /// `new` vs `new_v2`. Migrating a class's identity to V3 is then a one-line /// flip of its `tail_variant` in the registry, with zero consumer rewrite. /// - /// **Un-gated dispatch (`ISS-V1-TAIL-RESIDUE`):** `mint_for` itself is always - /// available so production mints (`ocr.rs`, `aiwar.rs`) route through it in - /// every build. The [`V1`](TailVariant::V1) arm is unconditional - /// ([`new`](NodeGuid::new)); the [`V2`](TailVariant::V2)/[`V3`](TailVariant::V3) - /// arms lower to `new_v2` only under `guid-v2-tail`. With the feature off the - /// V2/V3 arm **panics** rather than falling back to V1. - /// - /// That arm was previously a silent fallback, justified as dead code - /// because "no classid registers a V2/V3 `tail_variant`". Since - /// D-BLOCKS-HOTPLUG-1 a hot-plugged consumer's reading rides the - /// authority's `Activation::read_modes` instead of [`classid_read_mode`], - /// so a caller can pass V3 with no registry entry involved — the arm is - /// reachable, and a fallback would silently mint a V1 tail for a caller - /// that asked for V3. + /// **Un-gated (D-HPR-6a, 2026-10-11):** every arm is unconditional. The + /// [`V1`](TailVariant::V1) arm lowers to [`new`](NodeGuid::new); the + /// [`V2`](TailVariant::V2)/[`V3`](TailVariant::V3) arms lower to `new_v2`, + /// which writes the same 16 bytes a [`FacetCascade`](crate::facet::FacetCascade) + /// holds (`classid(4) | 6×(8:8)`). The `guid-v2-tail` / `guid-v3-tail` + /// features remain as empty names only. Before, a build without them + /// panicked here instead of minting V3. /// /// **No silent truncation:** the V2/V3 arm asserts `family`/`identity` fit /// `u16`, mirroring [`new`](NodeGuid::new)'s own 24-bit guard. @@ -413,7 +396,6 @@ impl NodeGuid { ) -> Self { match tail_variant { TailVariant::V1 => Self::new(classid, heel, hip, twig, family, identity), - #[cfg(feature = "guid-v2-tail")] TailVariant::V2 | TailVariant::V3 => { assert!( family <= 0xFFFF, @@ -433,38 +415,6 @@ impl NodeGuid { identity as u16, ) } - #[cfg(not(feature = "guid-v2-tail"))] - TailVariant::V2 | TailVariant::V3 => { - // A V3 MINT NEVER DEGRADES TO V1 (operator, 2026-09-07). - // - // This arm used to fall back to `new`, justified as dead code: - // "no classid registers a V2/V3 tail_variant, so the fallback - // arm is unreachable". D-BLOCKS-HOTPLUG-1 falsified that - // premise. A hot-plugged consumer's reading no longer comes - // from `classid_read_mode` at all — it rides the authority's - // `Activation::read_modes`, and the consumer passes the - // `TailVariant` straight in. `blockly-store` does exactly - // that, and `medcare-cohorts` calls `mint_for(V3, …)` - // directly. The arm is live. - // - // Falling back would hand a caller that ASKED for V3 a V1 - // `family:identity` u24 key, and the tail is not recorded in - // the key — `decode` versus `decode_v2` is chosen by classid - // alone — so the corruption is unobservable at the mint and - // surfaces as garbage identities much later. - // - // `mint_for` is `const fn`, so this fails a const-context mint - // at COMPILE time and panics loudly otherwise. Scoped - // deliberately: no default is flipped, no feature set changes, - // no dependency is added, and the V1 arm above still mints V1 - // for classes that legitimately read V1. - let _ = (classid, heel, hip, twig, leaf, family, identity); - panic!( - "mint_for was asked for a V2/V3 tail without `guid-v2-tail`; \ - enable the feature (it is implied by the default-on \ - `guid-v3-tail`) — a V3 mint must never silently become V1" - ) - } } } } @@ -533,7 +483,6 @@ mod facet_bridge_tests { // I-LEGACY-API-FEATURE-GATED the v2 names are distinct (`leaf`/`*_v2`), so no // function silently changes semantics, and `GUID_TAIL_LAYOUT_VERSION_V2` is the // version gate marking a v2-tail packet. -#[cfg(feature = "guid-v2-tail")] impl NodeGuid { /// Construct a v2-tail GUID: `classid·HEEL·HIP·TWIG` identical to v1, then the /// 3×u16 basin tail `leaf·family·identity`. Each tail field is a full `u16` — @@ -624,8 +573,7 @@ impl NodeGuid { /// The v2-tail GUID decoded — `classid · HEEL · HIP · TWIG · leaf · family · /// identity`, every tier a native integer (no `u24`). The v2 counterpart of -/// [`GuidParts`]. (D-GV2-1; feature `guid-v2-tail`.) -#[cfg(feature = "guid-v2-tail")] +/// [`GuidParts`]. (D-GV2-1.) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct GuidPartsV2 { /// 0..4 — prefix-routable class id. @@ -648,7 +596,6 @@ pub struct GuidPartsV2 { /// MUST refuse a v2 blob (and vice-versa) — the version gate per /// `I-LEGACY-API-FEATURE-GATED`. Wired into the `SoaEnvelope` version at cutover /// (D-GV2-5). -#[cfg(feature = "guid-v2-tail")] pub const GUID_TAIL_LAYOUT_VERSION_V2: u16 = 2; /// The whole canonical key decoded in one shot — `classid · HEEL · HIP · TWIG · @@ -1690,7 +1637,6 @@ impl ReadMode { /// read through the new-generation [`TailVariant::V3`] cascade tail. The first /// V3 exemplar; [`FMA_V3`](ReadMode::FMA_V3) + [`CPIC_V3`](ReadMode::CPIC_V3) /// complete the Phase-1 V3 set. - #[cfg(feature = "guid-v3-tail")] pub const OSINT_V3: ReadMode = ReadMode { tail_variant: TailVariant::V3, value_schema: ValueSchema::Cognitive, @@ -1700,7 +1646,6 @@ impl ReadMode { /// The **FMA-V3** read-mode ([`NodeGuid::CLASSID_FMA_V3`]): the same cold /// [`ValueSchema::Compressed`] value model as legacy [`FMA`](ReadMode::FMA), /// read through the new-generation [`TailVariant::V3`] cascade tail. - #[cfg(feature = "guid-v3-tail")] pub const FMA_V3: ReadMode = ReadMode { tail_variant: TailVariant::V3, value_schema: ValueSchema::Compressed, @@ -1712,7 +1657,6 @@ impl ReadMode { /// [`ValueSchema::Compressed`] is the **Phase-1 provisional** (biomedical /// reference, mirroring FMA's cold treatment); Phase 2 pins the V3-shaped /// tenants. Edges [`EdgeCodecFlavor::CoarseOnly`]. - #[cfg(feature = "guid-v3-tail")] pub const CPIC_V3: ReadMode = ReadMode { tail_variant: TailVariant::V3, value_schema: ValueSchema::Compressed, @@ -1722,7 +1666,6 @@ impl ReadMode { /// 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, @@ -1732,7 +1675,6 @@ impl ReadMode { /// 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, @@ -1797,7 +1739,6 @@ static BUILTIN_READ_MODES: LazyLock> = LazyLock::new(|| { // (`domain:appid`) is the HIGH u16 (`0x0701_1000`-form), so // `classid_concept_domain` routes Osint / Anatomy / Genetics directly off // the canon half; the pre-flip `0x1000_DDCC` forms stay as aliases. - #[cfg(feature = "guid-v3-tail")] { m.insert(NodeGuid::CLASSID_OSINT_V3, ReadMode::OSINT_V3); m.insert(NodeGuid::CLASSID_FMA_V3, ReadMode::FMA_V3); @@ -2551,14 +2492,12 @@ mod tests { /// — measured, not assumed. The third fails earlier still, at compile time /// (`E0599`), because `family_v2`/`identity_v2` are themselves gated. #[test] - #[cfg(feature = "guid-v2-tail")] #[should_panic(expected = "v2/v3 identity must fit in 16 bits")] fn mint_for_v3_panics_on_identity_overflow() { let _ = NodeGuid::mint_for(TailVariant::V3, 0, 0, 0, 0, 0, 0, 0x0001_0000); } #[test] - #[cfg(feature = "guid-v2-tail")] #[should_panic(expected = "v2/v3 family must fit in 16 bits")] fn mint_for_v3_panics_on_family_overflow() { let _ = NodeGuid::mint_for(TailVariant::V3, 0, 0, 0, 0, 0, 0x0001_0000, 0); @@ -2568,7 +2507,6 @@ mod tests { /// information as one that never fires. The widest legal tail must mint and /// read back intact. #[test] - #[cfg(feature = "guid-v2-tail")] fn mint_for_v3_admits_the_widest_legal_tail() { let g = NodeGuid::mint_for(TailVariant::V3, 0, 0, 0, 0, 0, 0xFFFF, 0xFFFF); assert_eq!(g.family_v2(), 0xFFFF); @@ -3193,7 +3131,6 @@ mod tests { assert!(TailVariant::V2.is_layout_preserving()); } - #[cfg(feature = "guid-v3-tail")] #[test] fn read_mode_osint_v3_routes_v3_tail_and_osint_domain() { // The wired V3 exemplar proves BOTH facts at once — the canon/custom @@ -3242,7 +3179,6 @@ mod tests { assert_eq!(NodeGuid::CLASSID_OSINT_V3_LEGACY, 0x1000_0700); } - #[cfg(feature = "guid-v3-tail")] #[test] fn read_mode_fma_v3_and_cpic_v3_route_their_domains() { use crate::ogar_codebook::{ @@ -3323,7 +3259,6 @@ mod tests { assert_ne!(NodeGuid::CLASSID_FMA_V3, NodeGuid::CLASSID_OSINT_V3); } - #[cfg(feature = "guid-v3-tail")] #[test] fn mint_for_osint_v3_is_end_to_end_routable() { // Phase-1 end-to-end (soa-value-tenant-migration-v2.md §2): mint a class's @@ -3394,7 +3329,6 @@ mod tests { ); } - #[cfg(feature = "guid-v3-tail")] #[test] fn osint_v3_cognitive_tenant_carve_field_isolation_matrix() { // Phase-2 seam (the CPIC doc's "Phase 2 shapes the V3 tenants … on top"): @@ -3500,7 +3434,6 @@ mod tests { /// Shared body of the tenant-carve matrix: flip every byte of one tenant's /// lane; assert the lane changed and every byte OUTSIDE it did not — per /// tenant, in carve order (the I-LEGACY mandatory field-isolation test). - #[cfg(feature = "guid-v3-tail")] fn assert_value_lane_isolation(row: &mut NodeRow, lanes: &[ValueTenant]) { for &t in lanes { let before = row.value; @@ -3518,7 +3451,6 @@ mod tests { } } - #[cfg(feature = "guid-v3-tail")] #[test] fn fma_cpic_v3_compressed_tenant_carve_field_isolation_matrix() { // The cold half of the Phase-1 V3 set: FMA-V3 and CPIC-V3 both resolve to @@ -3602,7 +3534,6 @@ mod tests { ); } - #[cfg(feature = "guid-v2-tail")] #[test] fn mint_for_dispatches_to_the_right_constructor_per_tail() { // The carrier is exactly `new` (V1) / `new_v2` (V2 & V3) — no new layout, @@ -3637,7 +3568,6 @@ mod tests { // ── GUID v2 tail (D-GV2-1) — field-isolation matrix + coexistence ───────── - #[cfg(feature = "guid-v2-tail")] #[test] fn v2_field_isolation_matrix() { // Each tier carries a distinct value; every accessor reads back exactly @@ -3678,7 +3608,6 @@ mod tests { assert_eq!(base.to_hex_v2(), "11112222-3333-4444-5555-6666-7777-8888"); } - #[cfg(feature = "guid-v2-tail")] #[test] fn v1_and_v2_share_prefix_differ_in_tail() { // v1 and v2 agree on the prefix (classid·HEEL·HIP·TWIG)… @@ -3697,38 +3626,82 @@ mod tests { } } -/// A V3 mint must never silently degrade to a V1 tail (operator, 2026-09-07). -/// -/// Only compiled when `guid-v2-tail` is OFF — the one configuration in which -/// [`NodeGuid::mint_for`]'s V2/V3 arm can be reached without `new_v2` behind -/// it. In every default build the feature is on (implied by the default-on -/// `guid-v3-tail`) and this arm does not exist at all. -#[cfg(all(test, not(feature = "guid-v2-tail")))] -mod v3_never_degrades_to_v1 { +/// D-HPR-6a: the un-gated V3 mint writes exactly the 16 bytes the 128-bit +/// opt-in reads. Identity and edge are each `classid(4) | 6×(8:8)`; per tile +/// `lo` is the `is_a` byte and `hi` the `part_of` byte. Removing the gate must +/// not move one byte of that, or keys already written as `FacetCascade` stop +/// matching keys minted through `mint_for`. +#[cfg(test)] +mod v3_mint_is_the_128_bit_facet { use super::*; + use crate::facet::FacetCascade; + + /// The fixed input; every tier has distinct `hi`/`lo` bytes so a swapped or + /// widened byte is visible. + const CLASSID: u32 = 0x0A01_1000; + const TIERS: [u16; 6] = [0x1211, 0x2221, 0x3231, 0x4241, 0x5251, 0x6261]; + + /// The bytes written by hand: classid LE, then each tier as `lo, hi`. + fn expected() -> [u8; 16] { + let mut b = [0u8; 16]; + b[0..4].copy_from_slice(&CLASSID.to_le_bytes()); + for (i, t) in TIERS.iter().enumerate() { + b[4 + 2 * i..6 + 2 * i].copy_from_slice(&t.to_le_bytes()); + } + b + } - /// Can-fire: asking for V3 without the feature bangs. - /// - /// The behaviour this replaced was a silent `new(…)` fallback, which - /// handed back a V1 `family:identity` u24 key. Nothing downstream could - /// notice — the tail is not recorded in the key, so the wrong reading - /// surfaces only as garbage identities much later. + fn mint(v: TailVariant) -> NodeGuid { + let [heel, hip, twig, leaf, family, identity] = TIERS; + NodeGuid::mint_for( + v, + CLASSID, + heel, + hip, + twig, + leaf, + u32::from(family), + u32::from(identity), + ) + } + + /// Identity: a V3 mint is byte-identical to the facet, and the facet's + /// `lo` chain is the is_a bytes, its `hi` chain the part_of bytes. + #[test] + fn a_v3_identity_mint_is_the_facet_bytes() { + let g = mint(TailVariant::V3); + assert_eq!(*g.as_bytes(), expected()); + let f = FacetCascade::from(g); + assert_eq!(f.to_bytes(), expected()); + assert_eq!(f.lo_chain(), [0x11, 0x21, 0x31, 0x41, 0x51, 0x61]); + assert_eq!(f.hi_chain(), [0x12, 0x22, 0x32, 0x42, 0x52, 0x62]); + assert_eq!(NodeGuid::from(f), g); + } + + /// Edge: the same 16 bytes carried as the edge facet survive the + /// round trip unchanged. + #[test] + fn the_edge_facet_carries_the_same_bytes() { + let f = FacetCascade::from(mint(TailVariant::V3)); + let e = EdgeFacet::from(f); + assert_eq!(e.to_bytes(), expected()); + assert_eq!(FacetCascade::from(e), f); + } + + /// V2 and V3 write the same bytes; V3 is a reading of them, not a carving. #[test] - #[should_panic(expected = "must never silently become V1")] - fn a_v3_mint_without_the_feature_bangs_instead_of_minting_v1() { - let _ = NodeGuid::mint_for(TailVariant::V3, 0x1717_1000, 0, 0, 0, 0, 0, 1); + fn v2_and_v3_mint_the_same_bytes() { + assert_eq!(mint(TailVariant::V2), mint(TailVariant::V3)); } - /// …and V2 takes the same arm, so the guard is not V3-only. + /// Can-stay-silent: a V1 mint is NOT the facet layout (the u24 tail packs + /// `family:identity` into bytes 10..16 and drops `leaf`), so the equality + /// above is not true of every mint. #[test] - #[should_panic(expected = "must never silently become V1")] - fn a_v2_mint_without_the_feature_bangs_too() { - let _ = NodeGuid::mint_for(TailVariant::V2, 0x1717_1000, 0, 0, 0, 0, 0, 1); + fn a_v1_mint_is_not_the_facet_layout() { + assert_ne!(*mint(TailVariant::V1).as_bytes(), expected()); } - /// Can-stay-silent: a class that legitimately reads V1 still mints, in the - /// same configuration. Without this half the guard could be "refuse every - /// mint", which carries exactly as much information as refusing none. #[test] fn a_v1_mint_is_untouched_and_still_carries_the_u24_tail() { let g = NodeGuid::mint_for(TailVariant::V1, 0x0000_0000, 0, 0, 0, 0xFFFF, 0, 1); diff --git a/crates/lance-graph-contract/src/hhtl.rs b/crates/lance-graph-contract/src/hhtl.rs index b687928eb..8fd3b948e 100644 --- a/crates/lance-graph-contract/src/hhtl.rs +++ b/crates/lance-graph-contract/src/hhtl.rs @@ -344,7 +344,6 @@ impl NiblePath { /// are the basin tail (NOT in the path). Two GUIDs differing only in /// family/identity therefore share a path; differing in any HHT tier (incl. /// `leaf`) do not — the property v2 hop-distance relies on. - #[cfg(feature = "guid-v2-tail")] #[must_use] pub const fn from_guid_prefix_v2(guid: &crate::canonical_node::NodeGuid) -> Self { let path = ((guid.heel() as u64) << 48) @@ -381,7 +380,6 @@ impl NiblePath { /// [`EMPTY`](NiblePath::EMPTY). This is why "custom half is reserved-zero" /// is a **v1-fold** statement, NOT a global classid law — the schema's /// `tail_variant` selects the fold. - #[cfg(feature = "guid-v3-tail")] #[must_use] pub const fn from_guid_prefix_v3(guid: &crate::canonical_node::NodeGuid) -> Self { // Read the 4 HHTL tiers as full LE `u16` from the raw key bytes [4..12] — @@ -833,7 +831,6 @@ mod tests { assert_eq!(NiblePath::EMPTY.common_ancestor(a), None); } - #[cfg(feature = "guid-v2-tail")] #[test] fn from_guid_prefix_v2_includes_leaf_not_basin_tail() { use crate::canonical_node::NodeGuid; @@ -979,7 +976,6 @@ mod tests { assert!(NiblePath::from_guid_prefix(&g).is_some()); } - #[cfg(feature = "guid-v3-tail")] #[test] fn from_guid_prefix_v3_routes_both_bytes_of_part_of_is_a_and_ignores_classid() { use crate::canonical_node::NodeGuid; diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index 2118896b8..635ee089b 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -70,8 +70,7 @@ pub mod class_view; /// `classify_form`, `AdoptionCounts`, `count_adoption`). See /// `.claude/v3/soa_layout/routing.md` §5. pub mod classid_scan; -/// D-GV2-2 — per-family codebook (`family → Codebook`), gated on the v2 tail. -#[cfg(feature = "guid-v2-tail")] +/// D-GV2-2 — per-family codebook (`family → Codebook`) over the v2 tail. pub mod codebook; pub mod codegen_manifest; pub mod codegen_spine; diff --git a/crates/lance-graph-contract/src/ocr.rs b/crates/lance-graph-contract/src/ocr.rs index 704e1e09f..879d7f132 100644 --- a/crates/lance-graph-contract/src/ocr.rs +++ b/crates/lance-graph-contract/src/ocr.rs @@ -299,9 +299,7 @@ mod tests { /// D-HPR-1: `to_node_row_with` mints with the tail and value schema it is /// handed. The classid is unknown to the lookup, which would answer the - /// V1 / Full default; the handed reading is V3 / Bootstrap. Gated: minting - /// a V3 tail needs the tail feature (`mint_for` refuses it otherwise). - #[cfg(feature = "guid-v3-tail")] + /// V1 / Full default; the handed reading is V3 / Bootstrap. #[test] fn to_node_row_with_follows_the_handed_reading() { use crate::canonical_node::{classid_read_mode, EdgeCodecFlavor, TailVariant}; diff --git a/crates/lance-graph-contract/src/soa_graph.rs b/crates/lance-graph-contract/src/soa_graph.rs index c15f9369d..1f7c26978 100644 --- a/crates/lance-graph-contract/src/soa_graph.rs +++ b/crates/lance-graph-contract/src/soa_graph.rs @@ -83,14 +83,10 @@ pub struct DomainSpec { pub member_edge: &'static str, } -/// The OSINT domain classid: the V3 OSINT class (`CLASSID_OSINT_V3`) in a -/// normal build; the legacy V1 `CLASSID_OSINT` only under -/// `--no-default-features` (where the V3 class is gated out). aiwar mints under -/// the same selector, so its rows always match this domain's projector filter. -#[cfg(feature = "guid-v3-tail")] +/// The OSINT domain classid: the V3 OSINT class (`CLASSID_OSINT_V3`). aiwar +/// mints under the same selector, so its rows always match this domain's +/// projector filter. const OSINT_GOTHAM_CLASSID: u32 = NodeGuid::CLASSID_OSINT_V3; -#[cfg(not(feature = "guid-v3-tail"))] -const OSINT_GOTHAM_CLASSID: u32 = NodeGuid::CLASSID_OSINT; /// The **OSINT / Palantir-Gotham** domain (V3 class [`NodeGuid::CLASSID_OSINT_V3`]): /// a neo4j-emulation entity graph. Anchor families are caller-supplied (the key @@ -108,15 +104,11 @@ 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`. +/// The FMA domain classid: the V3 anatomy class (`CLASSID_FMA_V3`). /// 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: FMA_ANATOMY_CLASSID, @@ -128,12 +120,8 @@ pub const FMA_ANATOMY: DomainSpec = DomainSpec { }; /// 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")] +/// [`NodeGuid::CLASSID_PROJECT_V3`]. 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; @@ -148,13 +136,8 @@ pub const PROJECT: DomainSpec = DomainSpec { member_edge: "in-project", }; -/// 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")] +/// The commerce/ERP domain classid: the V3 class [`NodeGuid::CLASSID_ERP_V3`]. 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; @@ -188,11 +171,9 @@ fn family_node_id(family: u32) -> String { /// ([`domain_tail`]) — never looked up per row. #[inline] fn hhtl_path(guid: &NodeGuid, tail: TailVariant) -> NiblePath { - #[cfg(feature = "guid-v3-tail")] if tail == TailVariant::V3 { return NiblePath::from_guid_prefix_v3(guid); } - let _ = tail; NiblePath::from_guid_prefix(guid).unwrap_or(NiblePath::EMPTY) } @@ -210,15 +191,12 @@ fn domain_tail(domain: &DomainSpec) -> TailVariant { /// tail in bytes 10..13 ([`NodeGuid::family`]). Reading the V1 `family()` on a /// V2/V3 row would fold the `leaf` byte into the id (`I-LEGACY-API-FEATURE-GATED`), /// so the family decode is routed through the canonical `classid → tail_variant` -/// mapping exactly like the path is. Under no tail feature every classid is V1, so -/// this is just `family()`. +/// mapping exactly like the path is. #[inline] fn family_of(guid: &NodeGuid, tail: TailVariant) -> u32 { - #[cfg(feature = "guid-v2-tail")] if matches!(tail, TailVariant::V2 | TailVariant::V3) { return guid.family_v2() as u32; } - let _ = tail; guid.family() } @@ -227,11 +205,9 @@ fn family_of(guid: &NodeGuid, tail: TailVariant) -> u32 { /// [`NodeGuid::identity`] (bytes 13..16). #[inline] fn identity_of(guid: &NodeGuid, tail: TailVariant) -> u32 { - #[cfg(feature = "guid-v2-tail")] if matches!(tail, TailVariant::V2 | TailVariant::V3) { return guid.identity_v2() as u32; } - let _ = tail; guid.identity() } @@ -537,7 +513,6 @@ mod tests { ); } - #[cfg(feature = "guid-v3-tail")] #[test] fn v3_rows_decode_family_and_identity_via_tail_variant() { use crate::canonical_node::classid_read_mode; @@ -721,24 +696,12 @@ mod tests { hops[2].hops, hops[3].hops ); - // 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); - } + // V3 (`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. + assert_eq!(hops[2].hops, 26); + assert_eq!(hops[3].hops, 32); } #[test] @@ -784,7 +747,6 @@ mod tests { /// lookup. The classid here is unknown to `BUILTIN_READ_MODES`, so the /// lookup answers V1; the plug says V3. Only the handed tail decodes the V3 /// basin family correctly. - #[cfg(feature = "guid-v3-tail")] #[test] fn with_forms_use_the_handed_tail_not_the_classid_lookup() { use crate::canonical_node::classid_read_mode; diff --git a/crates/lance-graph-contract/tests/v3_mint_reachability_probe.rs b/crates/lance-graph-contract/tests/v3_mint_reachability_probe.rs index a1b28170f..f3b0f12a2 100644 --- a/crates/lance-graph-contract/tests/v3_mint_reachability_probe.rs +++ b/crates/lance-graph-contract/tests/v3_mint_reachability_probe.rs @@ -8,7 +8,7 @@ //! returns V1), so the fallback arm is dead — it exists purely so the crate //! compiles `--no-default-features`."* //! -//! That justification is checkable. The registry's V3 entries are gated on +//! That justification was checkable while the V3 registry entries were gated on //! `guid-v3-tail` (default ON); `mint_for`'s V3 arm is gated on `guid-v2-tail` //! (default OFF). Those are two different features, so the premise and the //! conclusion can come apart. @@ -16,6 +16,9 @@ //! The discriminator is `leaf`: the V3 arm feeds it to `new_v2`, the V1 //! fallback discards it (`let _ = leaf;`). Two mints differing only in `leaf` //! are therefore byte-equal iff the fallback ran. +//! +//! Since D-HPR-6a (2026-10-11) neither half is gated and the fallback arm is +//! gone; both tests assert the V3 path unconditionally. use lance_graph_contract::canonical_node::{classid_read_mode, NodeGuid, TailVariant}; @@ -23,15 +26,13 @@ use lance_graph_contract::canonical_node::{classid_read_mode, NodeGuid, TailVari /// register a V2/V3 tail variant in this build? #[test] fn a_v3_registered_classid_resolves_to_the_v3_tail_variant() { + // Unconditional since D-HPR-6a: no feature gates the V3 registry entries. let m = classid_read_mode(NodeGuid::CLASSID_OSINT_V3); - #[cfg(feature = "guid-v3-tail")] assert_eq!( m.tail_variant, TailVariant::V3, - "CLASSID_OSINT_V3 must register the V3 tail when guid-v3-tail is on" + "CLASSID_OSINT_V3 must register the V3 tail" ); - #[cfg(not(feature = "guid-v3-tail"))] - assert_eq!(m.tail_variant, TailVariant::V1); } /// The CONCLUSION half: given a classid that registers V3, does `mint_for` @@ -52,9 +53,7 @@ fn minting_through_the_registered_tail_variant_honours_leaf() { let leaf_is_live = a.as_bytes() != b.as_bytes(); eprintln!( - "tail_variant={tv:?} guid-v3-tail={} guid-v2-tail={} leaf_is_live={leaf_is_live}\n a={:02x?}\n b={:02x?}", - cfg!(feature = "guid-v3-tail"), - cfg!(feature = "guid-v2-tail"), + "tail_variant={tv:?} leaf_is_live={leaf_is_live}\n a={:02x?}\n b={:02x?}", a.as_bytes(), b.as_bytes(), );