Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/board/INTEGRATION_PLANS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2026-10-10 (2) — v3-mandatory-hotplug-reading-v1 — the reading comes from the plug, never from the classid value → `.claude/plans/v3-mandatory-hotplug-reading-v1.md`

**Status:** ACTIVE (D-HPR-0..6). Operator DECISION 2026-10-10: V3 mandatory, hotplug.rs + OGAR plug-and-play is the only reading path, lockstep deprecated, concepts are immutable addresses and language names are labels.

- Retires `classid_read_mode` / `BUILTIN_READ_MODES` (selects V3 by the `0x1000` marker; V1 fallback for unknown ids) in six waves; stored `0x1000` keys stay readable.

## 2026-10-10 — cross-glove-business-parity-v1 — Odoo × SAP over one Quack algebra → `.claude/plans/cross-glove-business-parity-v1.md`

**Status:** PROPOSAL (D-XGP-0..7). No code authorized. Addendum to `.claude/plans/sap-glove-quack-v1.md` (baseline, committed verbatim; W0–W6 preserved). **Corrected same day (plan §C):** the contract bullet below is superseded. The landing is shipped `OntologyRegistry` attribute rows plus a CATS `impl Binder` (missing) behind a registry front; no new contract module.
Expand Down
14 changes: 14 additions & 0 deletions .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## D-HPR — V3 mandatory; reading from the plug only (2026-10-10)

Plan: `.claude/plans/v3-mandatory-hotplug-reading-v1.md`.

| D-id | Deliverable | Status | Gate |
|---|---|---|---|
| **D-HPR-0** | Ruling + plan + caller inventory | In PR (#1464) | — |
| **D-HPR-1** | Contract modules (`ocr`, `aiwar`, `nan_projection`, `soa_graph`) take the reading as a parameter | In PR (#1464; 4 `_with`/`_plugged` forms, disable runs 4/4 red) | new form returns the handed reading even where `classid_read_mode` disagrees |
| **D-HPR-2** | Canon domain readings move into `OgarAuthority::concept_override` | Queued | `read_mode_for(concept)` == today's `classid_read_mode(<V3 classid>)` per domain |
| **D-HPR-3** | Consumers move (lance-graph, q2, MedCare); `classid_read_mode` deprecated | Queued | no production caller left |
| **D-HPR-4** | `0x1000` / render prefixes documented as deprecated, kept (no eviction) | In PR (#1464; doc notes on `CLASSID_*_V3` + `classid_read_mode`) | — |
| **D-HPR-5** | Stored legacy keys keep resolving; a `SlabDeclaration` may override for its own data; no forced removal | Queued | — |
| **D-HPR-6** | No V1 default for an unknown classid; `guid-v3-tail` not a gate | Queued | unknown classid → `NoReadingFor` |

## D-XGP — Cross-glove business parity: Odoo × SAP over one Quack algebra (2026-10-10)

Plan: `plans/cross-glove-business-parity-v1.md`. Board entry: `entries/2026-10-10-d-xgp-0-cross-glove-inventory.md`.
Expand Down
23 changes: 23 additions & 0 deletions .claude/board/entries/2026-10-10-v3-mandatory-hotplug-reading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# D-HPR-0 — V3 mandatory; the reading comes from the plug; concepts are immutable addresses (2026-10-10)

**Status:** DECISION (operator, 2026-10-10). Plan: `.claude/plans/v3-mandatory-hotplug-reading-v1.md`.

> "Yes V3 mandatory, everything else in regards to capabilities hotplug.rs plug and play > ogar pattern making sure lockstep is deprecated"
>
> "Any concept is immutable. That's why Ontologies exist. Different languages are a label. The concept needs to be immutable adress"

- **V3 is mandatory:** no V1 mint, no silent V1 fallback.
- **One resolution path:** the reading comes from `HotPlug` → `OgarAuthority` → `Activation::resolve_for_context` plus the slab's `SlabDeclaration`. Lockstep tables are deprecated.
- **A concept is an immutable address.** Names in other languages ("Stundenzettel", "TimeSheet", "Zeiterfassung") are labels of it.
- **The classview fossils retire** (`0x1000` V3 marker, per-app render prefixes). Keys already stored with them stay readable.
- **Residue to remove:** `classid_read_mode` / `BUILTIN_READ_MODES` (`ISS-CLASSID-READ-MODE-IS-A-SECOND-RESOLUTION-PATH`). It selects V3 by the `0x1000` marker and falls back to V1 for an unknown classid. Callers: 4 contract modules, 4 lance-graph crates, q2 osint-bake/geo, MedCare cohorts, OGAR osm/ro/dismech/loco (plan table).
- **Waves W1–W6:**
- W1: the contract takes the reading as a parameter.
- W2: the canon domains' readings move into `concept_override`.
- W3: consumers move, then `#[deprecated]`.
- W4: stop minting at the fossil classviews.
- W5: legacy reads go through `SlabDeclaration`.
- W6: no V1 default.
- **Open (operator):** O1, the classview of a new canon mint once `0x1000` retires; O2, which WoA row "Stundenzettel" labels.

**Refined (operator, 2026-10-10):** *"1000 isn't hurting, just document and Mark it as deprecated. No need to force evict."* The `0x1000` marker and `classid_read_mode` are documented as deprecated and kept; stored keys and existing callers are not migrated by force. Done as doc notes, not `#[deprecated]`, which would fail every existing caller under `-D warnings`.
3 changes: 2 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
opposite directions; the stranding this convention prevents shows up in
exactly one of them, never both.

297 entries, 2026-08-06 .. 2026-10-10.
298 entries, 2026-08-06 .. 2026-10-10.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-10 | `D-XGP-8` | | [2026-10-10-woa-anchors-timesheet.md](2026-10-10-woa-anchors-timesheet.md) |
| 2026-10-10 | `D-HPR-0` | | [2026-10-10-v3-mandatory-hotplug-reading.md](2026-10-10-v3-mandatory-hotplug-reading.md) |
| 2026-10-10 | `render-classid-reversal-audit` | | [2026-10-10-render-classid-reversal-audit.md](2026-10-10-render-classid-reversal-audit.md) |
| 2026-10-10 | `E-W2-SPACE-AXIS-CUSTOM-SPACE-LEAVES-THE-CLASSID-1` | W2's space axis is decided: fixed spaces 0–3 stay in the classid, custom spaces move to the payload or edge | [2026-10-10-e-w2-space-axis-custom-space-leaves-the-classid-1.md](2026-10-10-e-w2-space-axis-custom-space-leaves-the-classid-1.md) |
| 2026-10-10 | `D-XGP-3-BILLABLE` | | [2026-10-10-d-xgp-3-billable-converted.md](2026-10-10-d-xgp-3-billable-converted.md) |
Expand Down
70 changes: 70 additions & 0 deletions .claude/plans/v3-mandatory-hotplug-reading-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# v3-mandatory-hotplug-reading-v1 — the reading comes from the plug, never from the classid value

**Status:** ACTIVE (D-HPR-0..6). DECISION by the operator, 2026-10-10. W0 (this plan + the ruling record) lands first; W1 onward are code.

## The ruling (operator, 2026-10-10, verbatim)

> "Yes V3 mandatory, everything else in regards to capabilities hotplug.rs plug and play > ogar pattern making sure lockstep is deprecated"
>
> "Any concept is immutable. That's why Ontologies exist. Different languages are a label. The concept needs to be immutable adress"

Earlier the same day: *"only the hotplug.rs and Ontology slab metadata envelope define if 32 + 96 is in storage or 128"* and *"1000 V3 Marker is a fossil too"* (plan `cross-glove-business-parity-v1.md` §C.9.3).

What it means, in rules:

1. **V3 is mandatory.** No new key is minted with a V1 tail, and no reading falls back to V1 silently.
2. **One resolution path.** How a row is read (tail, value schema, edge codec, `Facet96` vs `Register128`) comes from the plug (`HotPlug` → `CapabilityAuthority::activate` → `Activation::resolve_for_context`) plus the slab's own `SlabDeclaration`. Nothing else selects a reading. Lockstep tables (a central list every consumer must be added to) are deprecated.
3. **A concept is an immutable address.** Its id never changes meaning. This is what ontologies are for.
4. **Names in different languages are labels of one concept, not separate concepts.** "Stundenzettel", "TimeSheet", "Zeiterfassung" and "TimeEntry" are labels; the concept is the address (here `0x0103` / `ogit.WorkOrder:TimeSheet`). A label table maps names to the address; it never mints.
5. **The classview fossils are deprecated, not evicted** (operator, 2026-10-10: *"1000 isn't hurting, just document and Mark it as deprecated. No need to force evict"*). The `0x1000` V3 marker and the per-app render prefixes `0x0000`–`0x000C` stop carrying meaning for new work. Keys, constants and callers that already use them keep working; nothing is migrated by force.

## Where things stand (measured 2026-10-10 on `origin/main` `feb21972`)

**The plug path already exists.** `lance-graph-ogar::plug_readings` (`lib.rs:452`) gives every plugged concept `ReadMode::PLUG_AND_PLAY_V3` unless `concept_override` (`lib.rs:477`) names an exception. `Activation::read_mode_for` fails closed (`NoReadingFor`), never defaulting.

**The second path that must go:** `canonical_node::classid_read_mode(classid)` (`canonical_node.rs:1813`) looks the full `u32` up in `BUILTIN_READ_MODES` (`:1743`). It has two faults:

- the V3 reading is chosen by the `0x1000` marker in the classview bits (the `*_V3` keys);
- an unknown classid falls through to `ReadMode::DEFAULT`, which is a **V1** tail. That is exactly the silent V1 fallback the 2026-09-07 ruling forbade on the plug path.

**Callers of `classid_read_mode` / the `*_V3` constants** (`grep -rn`, then each file opened):

| repo | file | use |
|---|---|---|
| lance-graph | `contract/src/ocr.rs:105,124` | `to_node_row(classid, identity)` reads schema + tail by classid |
| lance-graph | `contract/src/aiwar.rs:81,119` | mints OSINT rows on `CLASSID_OSINT_V3`, tail by classid |
| lance-graph | `contract/src/nan_projection.rs:167` | the non-`_resolved` variants look the schema up by classid |
| lance-graph | `contract/src/soa_graph.rs:204,457` | `DomainSpec` consts on `*_V3` classids, tail by classid |
| lance-graph | `contract/src/hhtl.rs`, `selection.rs`, `ogar_codebook.rs`, `classid_scan.rs` | tests / docs / legacy-form classification |
| lance-graph | `callcenter/src/graph_table.rs`, `deepnsm-v2/src/promote.rs`, `lance-graph/tests/canonical_witness_identity_probe.rs`, `contract/tests/v3_mint_reachability_probe.rs` | mint or read on `*_V3` |
| lance-graph | `lance-graph-ogar/src/lib.rs:735` | a test pinning that the old path yields V1 for an unplugged id |
| q2 | `osint-bake/src/bin/{fma,body}.rs`, `tools/body-soa-wire` | bake FMA on `CLASSID_FMA_V3`, tail by classid |
| q2 | `geo/src/bso2.rs` | `CLASSID_GEO_V3 = 0x0F01_1000` |
| q2 | `cockpit-server/src/osint_classview.rs:782` | a literal `0x0701_1000` key in a fixture |
| MedCare-rs | `medcare-cohorts/src/graph_feed.rs:25`, `spog_masks.rs` | `CLASSID_CPIC_V3`; mask tests over `0x0701_1000` |
| OGAR | `ogar-osm` (`CLASSVIEW_V3_SUBSTRATE`), `ogar-ro`, `ogar-dismech`, `ogar-loco` tests | mint concepts at classview `0x1000` |

**Persisted data carrying `0x1000` keys:** q2's FMA / body `.soa` bakes (`0x0A01_1000`), osm slabs (`0x0F01_1000`), the MedCare medication lane (`CPIC_V3`). These must stay readable (rule 5).

## Waves

- [x] **W0 — D-HPR-0: record.** This plan, the ruling (`entries/2026-10-10-v3-mandatory-hotplug-reading.md`), `INTEGRATION_PLANS.md`, `STATUS_BOARD.md`.
- [x] **W1 — D-HPR-1: contract takes the reading as a parameter.** `ocr`, `aiwar`, `nan_projection`, `soa_graph` get variants that take a `ReadMode` (or a `ResolvedReading`) instead of looking the classid up. `DomainSpec` carries its reading. The classid-lookup forms stay, documented as legacy, until W3. Additive: no consumer breaks.
- Falsifier: a test per module that the new form yields the reading it was handed, including one where the handed reading differs from what `classid_read_mode` would return (so a caller that silently re-looks-up fails).
- **Shipped:** `ocr::LayoutBlock::to_node_row_with`, `aiwar::aiwar_node_rows_with`, `soa_graph::{project_snapshot_with, nearest_anchor_with}`, `nan_projection::project_energy_nonfinite_plugged` (resolves each run through `Activation::resolve_tenant_reading`; an unplugged concept is `NoReadingFor`). Tests use classid `0x1718_0000`, which the lookup reads as V1 / `Full`; each is red when its form is changed to re-look-up (disable runs, 4/4).
- [ ] **W2 — D-HPR-2: the canon domains get their reading from the authority.** The domain value models (`OSINT`/`PROJECT`/`ERP` Cognitive, `FMA`/`CPIC` Compressed) move into `concept_override`, keyed by concept, all on a V3 tail. `OgarAuthority` then answers for those concepts with no classview involved.
- Falsifier: `read_mode_for(concept)` equals today's `classid_read_mode(<V3 classid>)` for each canon domain, so the move changes no reading.
- [ ] **W3 — D-HPR-3: consumers move.** lance-graph callcenter / deepnsm-v2 / probes, then q2 osint-bake + geo, MedCare cohorts. Each takes its reading from its `Activation`. Then `classid_read_mode` gets `#[deprecated]`.
- [x] **W4 — D-HPR-4: document the fossils as deprecated.** Done in `lance-graph-contract`: the five `CLASSID_*_V3` constants and `classid_read_mode` carry a "Deprecated, kept" doc note. A doc note, not `#[deprecated]`: the attribute would turn every existing caller red under `-D warnings`, which is the forced migration the ruling rules out. OGAR (`ogar-osm` `CLASSVIEW_V3_SUBSTRATE`, ro, dismech, loco) and q2 `geo` get the same note when next touched; existing mints stay.
- [ ] **W5 — D-HPR-5: legacy reads stay.** Stored `0x1000` and pre-flip keys keep resolving through `BUILTIN_READ_MODES`; no forced removal. A slab may additionally declare its reading (`SlabDeclaration`), which wins for its own data. Removing a table key is optional and only after nothing reads it.
- [ ] **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.

## Open questions (operator)

- **O1.** What classview should a NEW mint of a canon domain use instead of `0x1000`: `0x0000` (shared canonical core), or an app-owned value? Not blocking: existing mints keep `0x1000`.
- **O2.** Does WoA's `ogit.WorkOrder:TimeSheet` resolve to `0x0103`, with "Stundenzettel" as its label, instead of today's `TimesheetActivity` (plan `cross-glove-business-parity-v1.md` §C.9.5)? Rule 4 settles that the German name is a label; it does not settle which WoA row the label names.

## What this plan does not do

- It does not re-key stored data. Rule 5 keeps old keys readable.
- It does not change the `domain:appid` layout (§C.9.4 audit: do not reverse `render_classid`).
36 changes: 34 additions & 2 deletions crates/lance-graph-contract/src/aiwar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! `GraphSnapshot` to the Quadro-2 visual. Run it on the real graph with
//! `cargo run -p lance-graph-contract --example aiwar_family_poc`.

use crate::canonical_node::{classid_read_mode, EdgeBlock, NodeGuid, NodeRow};
use crate::canonical_node::{classid_read_mode, EdgeBlock, NodeGuid, NodeRow, ReadMode};
use crate::literal_graph::LiteralGraph;
use std::collections::{BTreeMap, BTreeSet};

Expand Down Expand Up @@ -81,7 +81,19 @@ pub fn aiwar_node_rows(graph: &LiteralGraph) -> Vec<NodeRow> {
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))
}

/// [`aiwar_node_rows`] with the classid and reading supplied by the caller,
/// the reading resolved from its plug (`Activation::read_mode_for`) rather
/// than looked up by classid. The form new code uses (plan
/// `v3-mandatory-hotplug-reading-v1`, D-HPR-1); the lookup in
/// [`aiwar_node_rows`] is legacy.
pub fn aiwar_node_rows_with(
graph: &LiteralGraph,
osint_classid: u32,
mode: ReadMode,
) -> Vec<NodeRow> {
let view = AiwarClassView::from_graph(graph);
let ids = graph.all_node_ids();
let fam_of =
Expand Down Expand Up @@ -116,7 +128,7 @@ pub fn aiwar_node_rows(graph: &LiteralGraph) -> Vec<NodeRow> {
// Masked to u16 — fits both the V3 basin tail and the V1 fallback
// (POC cardinalities are tiny: category count + node count).
key: NodeGuid::mint_for(
classid_read_mode(osint_classid).tail_variant,
mode.tail_variant,
osint_classid,
0,
0,
Expand Down Expand Up @@ -213,4 +225,24 @@ mod tests {
assert_eq!(row.value, [0u8; 480], "head-only: value slab stays zero");
}
}

/// 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};
let g = ingest_aiwar_json(SAMPLE).unwrap();
let keys = |rows: &[NodeRow]| rows.iter().map(|r| r.key).collect::<Vec<_>>();
let legacy = aiwar_node_rows(&g);
let same = aiwar_node_rows_with(&g, NodeGuid::CLASSID_OSINT_V3, ReadMode::OSINT_V3);
assert_eq!(keys(&same), keys(&legacy));
let v1 = ReadMode {
tail_variant: TailVariant::V1,
value_schema: ValueSchema::Cognitive,
edge_codec: EdgeCodecFlavor::CoarseOnly,
};
let other = aiwar_node_rows_with(&g, NodeGuid::CLASSID_OSINT_V3, v1);
assert_ne!(keys(&other), keys(&legacy));
}
}
Loading
Loading