Skip to content

Commit d58bec5

Browse files
committed
docs: class-conditioned shape, not a locked quadruplet (operator veto)
Operator small veto (2026-06-29): the ClassView shape is mapped from the class's inherited format and selected by classid — don't restate or LOCK it. 'Quadruplet' was a misread of the 3×4 GUID shape; the shapes are per-class: Rails 6×2, other frameworks 4×3, GUID 3×4. So 4×3 is legitimate per-class, not a 'worst case'. - CLASSVIEW-FIELDVIEW-ASKAMA-BITMASK.md: 'Wide classes' recast from '[u64;4] quadruplet, locked 256' to class-conditioned shape (CascadeShape::from_levels, classid-selected); the only fixed bound is the byte-cardinality god-object signal (< 256 clean, >= 256 split). Simple-rules + summary + cross-refs updated off the removed field_mask_buckets/FIELD_MASK_MAX_BUCKETS API. - OGAR-TRANSPILE-SUBSTRATE.md §1.5a + status: shapes table reframed Rails 6×2 / other frameworks 4×3 / GUID 3×4; 4×3 legitimate (its divide is a per-class cost, not a prohibition); is_byte_aligned/shift/ALIGNED = shift-vs-divide distinction, not a reject gate. Mirrors lance-graph #621 + ruff #36. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 848c735 commit d58bec5

2 files changed

Lines changed: 81 additions & 73 deletions

File tree

docs/CLASSVIEW-FIELDVIEW-ASKAMA-BITMASK.md

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -66,35 +66,34 @@ The bitmask **is** the selected / unselected partition:
6666
Versions, roles, and projections are simply **different masks over the same
6767
template**. There is no template per version — one compiled artifact, any subset.
6868

69-
## Wide classes — the quadruplet and bucket chaining
69+
## Wide classes — class-conditioned shape, not a locked width
7070

7171
The `u64` mask above is **one bucket** of 64 field positions. A wide class — an
7272
Odoo `account.move` carries ~100+ fields — overflows one bucket, but **not the
73-
pattern**: the mask is a *chain of buckets*, each a `u64`, and the `selected()`
74-
loop is bucket-agnostic (it filters `FieldDesc[]` by `idx`; the bucket is
75-
`idx / 64`, the bit `idx % 64`). A clean `ClassView` chains up to **4 buckets —
76-
the `[u64; 4]` quadruplet = 256 positions** (operator 2026-06-29: expand the
77-
single-`u64` cap 64 → 256):
78-
79-
```
80-
fields buckets fits one ClassView?
81-
≤ 64 1 yes (the original u64)
82-
65–128 2 yes — an Odoo ~109-field model is clean here (2 buckets)
83-
129–192 3 yes
84-
193–256 4 yes — the full quadruplet
85-
> 256 5+ NO — god object: the SoC signal to SPLIT (see below)
86-
```
87-
88-
This is **clean separation overflow automation**: each run of 64 chains the next
89-
bucket automatically; at most 4 buckets is one ClassView; beyond 256 the class
90-
is a god object and the overflow chains into a *second* ClassView (sub-views),
91-
never a wider single mask. Pinned + tested in `ruff_spo_address::soc`:
92-
`FIELD_MASK_BUCKET_BITS = 64`, `FIELD_MASK_MAX_BUCKETS = 4`,
93-
`FIELD_MASK_CAP = 256`, `field_mask_buckets(n) = n.div_ceil(64)`; the
94-
`Duplication` verdict now collapses to `≤ 256` distinct `field_type`s (a
95-
109-field class is `Duplication`/maskable, not a `Counterexample`). The matching
73+
pattern**: the mask widens with the class, and the `selected()` loop is
74+
bucket-agnostic (it filters `FieldDesc[]` by `idx`; the bucket is `idx / 64`, the
75+
bit `idx % 64`). So a ~109-field model is clean.
76+
77+
**Crucially, the width is not a locked constant — it is class-conditioned**
78+
(operator veto 2026-06-29). The mask shape is **mapped from the class's inherited
79+
format and selected by `classid`** (the filter): the cascade is one of the
80+
per-class [`CascadeShape`](../../lance-graph/crates/lance-graph-contract/src/facet.rs)s
81+
**Rails → `6×2`, other frameworks → `4×3`, the canonical GUID → `3×4`** (all
82+
`G·D = 12`, 8-bit tiers; the depth `D ∈ {2,3,4}` is the per-class knob, via
83+
`CascadeShape::from_levels(d)`). Do **not** restate or lock a `[u64; 4]`
84+
"quadruplet" — that was a misread of the `3×4` GUID shape; the real knob is the
85+
inherited, classid-selected `D`.
86+
87+
The only fixed bound is the **god-object cardinality**: `< 256` (the byte
88+
cardinality / the per-tier sibling rank) is maskable by one ClassView; `≥ 256`
89+
is the SoC split signal — split into sub-ClassViews, never widen/lock a mask.
90+
Pinned + tested in `ruff_spo_address::soc`: `FIELD_MASK_CAP = MAX_SIBLINGS_PER_TIER`
91+
(one cap, not a second lock), the `Duplication` verdict collapses to
92+
`≤ FIELD_MASK_CAP` distinct `field_type`s (a 109-field class is `Duplication`/
93+
maskable, not a `Counterexample`). The matching
9694
`lance_graph_contract::class_view::FieldMask` (today `u64` / `MAX_FIELDS = 64`)
97-
is the *eventual* expansion to the same quadruplet, validated by the ruff test.
95+
is the *eventual* expansion — to the class-conditioned shape, not a locked width,
96+
validated by the ruff test.
9897

9998
## Simple rules (operator 2026-06-29)
10099

@@ -107,10 +106,11 @@ is the *eventual* expansion to the same quadruplet, validated by the ruff test.
107106
templated ClassView render with N masks — not N handlers. Route proliferation
108107
is usually an un-applied mask.
109108
- **`< 256` is clean; `≥ 256` is the god-object signal.** A field/sibling set
110-
under the quadruplet is maskable by one ClassView. At/over 256 the design (not
111-
the storage) is the problem — split concerns (chain a second ClassView /
112-
sub-view), the same SoC the `ruff_spo_address::soc` lint flags. Never widen the
113-
mask past the quadruplet to dodge the split.
109+
under the byte cardinality is maskable by one ClassView (in whatever
110+
class-conditioned shape its `classid` selects). At/over 256 the design (not the
111+
storage) is the problem — split concerns into sub-ClassViews, the same SoC the
112+
`ruff_spo_address::soc` lint flags. Never widen/lock a mask to dodge the split;
113+
and never restate the shape — it is inherited and `classid`-selected.
114114

115115
## Why this is right (not just convenient)
116116

@@ -165,14 +165,15 @@ user-authored, per-tenant templates — never as the default.
165165

166166
## Summary
167167

168-
One generic Askama field partial + a generated `FieldDesc[]` table + a
169-
chained-bucket mask (`u64` → up to the `[u64; 4]` quadruplet, 256 fields) = the
168+
One generic Askama field partial + a generated `FieldDesc[]` table + a mask
169+
whose width follows the class's **class-conditioned shape** (`6×2`/`4×3`/`3×4`,
170+
selected by `classid` from the inherited format — never a locked width) = the
170171
whole dynamic ClassView field view: Redmine-shaped, JSON-free, no conditionals
171172
in the template, type-checked structure, dynamic across versions/roles/
172-
projections, and wide-class-clean by bucket chaining (a god object at `> 256` is
173-
a split signal, not a wider mask). **The mask carves; the loop renders.**
174-
Askama's compile-time nature is not a cage — the mask is the runtime knob, and it
175-
is the same selector the data layer already uses to prune columns.
173+
projections, and wide-class-clean (a god object at ` 256` is a split signal, not
174+
a wider/locked mask). **The mask carves; the loop renders.** Askama's
175+
compile-time nature is not a cage — the mask is the runtime knob, and it is the
176+
same selector the data layer already uses to prune columns.
176177

177178
## Cross-references
178179

@@ -183,9 +184,13 @@ is the same selector the data layer already uses to prune columns.
183184
pattern slots into.
184185
- `I-LEGACY-API-FEATURE-GATED` — why the mask bits must be generated, never
185186
hand-numbered alongside a layout.
186-
- `ruff_spo_address::soc` — the `FIELD_MASK_CAP = 256` quadruplet,
187-
`field_mask_buckets()` chaining, and the `≥ 256` god-object SoC lint (where
188-
the "wide classes / split, don't widen" rule is tested).
187+
- `ruff_spo_address::soc``FIELD_MASK_CAP = MAX_SIBLINGS_PER_TIER` (the
188+
byte-cardinality cap, one bound not a second lock) and the `≥ 256` god-object
189+
SoC lint (where the "wide classes / split, don't widen, shape is inherited"
190+
rule is tested).
191+
- `lance_graph_contract::facet::CascadeShape` — the class-conditioned shape
192+
(`6×2`/`4×3`/`3×4`, `from_levels(d)`) the mask width follows; selected by
193+
`classid`, never locked.
189194
- `lance_graph_contract::canonical_node::GUIDS_PER_NODE` (= 32) — the node-level
190195
twin: clean/SoC over packed, Tetris concerns across the 32 GUID slots; the
191-
field-level quadruplet here is the same doctrine one level down.
196+
field-level mask here is the same SoC doctrine one level down.

docs/OGAR-TRANSPILE-SUBSTRATE.md

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -87,38 +87,38 @@ facet, baked into the binary. Three properties:
8787

8888
### a. One ClassView *rotates* the facet layout — no "versions"
8989

90-
The 16-byte facet's tier payload is not locked to a single carving. A ClassView
91-
can **always rotate** — read the SAME 12 cascade bytes under a different
92-
grouping — to fit the class. The carvings (pinned as
93-
`lance_graph_contract::facet::CascadeShape`, `CascadeShape::ROTATIONS`):
90+
The 16-byte facet's tier payload is not locked to a single carving. The shape is
91+
**class-conditioned***mapped from the class's inherited format and selected by
92+
`classid`* (the filter), never restated or locked (operator veto 2026-06-29). A
93+
ClassView can also **rotate** — read the SAME 12 cascade bytes under a different
94+
grouping. The shapes (pinned as `lance_graph_contract::facet::CascadeShape`,
95+
`CascadeShape::from_levels(d)`):
9496

9597
```
96-
6× (1:2) ALIGNED default — 6 tiers, each a 1:2 hierarchy (group_of = i >> 1, a shift)
97-
3× (1:2:3:4) ALIGNED default — 3 tier-pairs, each 1:2:3:4 (group_of = i >> 2, a shift)
98-
4× (1:2:3) WORST CASE — straddles tier boundaries (group_of = i / 3, a DIVIDE)
98+
6× (1:2) Rails — 6 tiers, each a 1:2 hierarchy (group_of = i >> 1, a shift)
99+
4× (1:2:3) other frameworks — 4 tier-groups, each 1:2:3 (group_of = i / 3, a divide)
100+
3× (1:2:3:4) canonical GUID — 3 tier-pairs, each 1:2:3:4 (group_of = i >> 2, a shift)
99101
```
100102

101-
**Only the byte-aligned carvings are defaults.** `6×(1:2)` and `3×(1:2:3:4)`
102-
keep `group_of` a pure shift (the canon's "tier-of-level is a shift, never a
103-
branch"). **`4×(1:2:3)` is the worst case, not a co-equal carving** — it
104-
straddles tier boundaries so `group_of` must DIVIDE (`CascadeShape::is_byte_aligned()`
105-
is `false`, `shift()` is `None`). It is *prevented on the common path* and kept
106-
only as the **rare rotation / escape hatch**: a ClassView may rotate to it
107-
deliberately when a rare class (some Odoo models) needs to relieve
108-
**classid-stacking entropy** — rotate the reading rather than mint another
109-
classid. So there is **no need for hardcoded facet "versions" (V1/V2/V3)** — one
110-
compiled ClassView subsumes the rotation set; the straddle stays legal only as a
111-
deliberate, rare rotation. Hardcoding a format per version is the thing to
112-
*delete*.
113-
114-
> **Carvings address the VIEW, never the functions.** A rotation re-reads the
115-
> data layout; it does NOT reach behaviour. Functions are encoded by the
103+
**The shape follows the class, not a global lock** (operator: "Rails might need
104+
6x2x8bit, others 4x3x8bit"). The depth `D ∈ {2,3,4}` is a per-class constant the
105+
`classid` resolves (`CascadeShape::from_levels`). `6×2`/`3×4` carve on tier
106+
boundaries so `group_of` is a pure shift (the canon's "tier-of-level is a shift,
107+
never a branch"); **`4×3` is legitimate for the frameworks that need it** — its
108+
`group_of` divides (`is_byte_aligned()` is `false`), the per-class *cost* a class
109+
opts into, **not a prohibition**. So there is **no need for hardcoded facet
110+
"versions" (V1/V2/V3)** — one compiled ClassView reads whichever shape the
111+
classid selects. Hardcoding a format per version (or locking a single shape) is
112+
the thing to *delete*.
113+
114+
> **Carvings address the VIEW, never the functions.** A shape/rotation re-reads
115+
> the data layout; it does NOT reach behaviour. Functions are encoded by the
116116
> **classid acting as an additional switch**`lance_graph_contract::facet::ClassArm`
117117
> `{ View, Functions }`, the OGAR THINK/DO split (`OGAR-AST-CONTRACT.md`).
118118
> Reaching a function = switch the classid to the `Functions` arm (the
119119
> `ActionDef`/`KausalSpec` on the resolved Core node), *never* slice the
120-
> tier-bytes. A straddling carve to "get to" a function is exactly the worst
121-
> case the `(1:2:3)` example warns against.
120+
> tier-bytes — that is the genuine mistake (a straddle used to "get to" a
121+
> function), distinct from a class whose *data layout* legitimately needs `3`.
122122
123123
### b. Sub-range mapping + nested ClassViews stacked into constructors
124124

@@ -172,16 +172,19 @@ case.)
172172
> `lance_graph_contract::facet::CascadeShape` (`G6D2` / `G4D3` / `G3D4`,
173173
> `G·D = CASCADE_UNITS = 12`) over `FacetCascade::tier_bytes()` — `index(g,l) =
174174
> g·D + l`, `group_of`/`level_of` inverses, `cascade_byte`, per-group LCP
175-
> `cascade_group_shared`. `CascadeShape::ALIGNED = [G3D4, G6D2]` are the
176-
> shift-`group_of` **defaults** (`shift()` is `Some`); `CascadeShape::ROTATIONS`
177-
> is the full rotation set a ClassView may rotate through. **`G4D3` is the worst
178-
> case**`is_byte_aligned()` is `false`, `shift()` is `None`, `group_of`
179-
> divides — excluded from `ALIGNED`, kept in `ROTATIONS` only as the rare
180-
> escape-hatch rotation (classid-stacking-entropy relief). **Functions are NOT a
181-
> carving**`facet::ClassArm { View, Functions }` is the classid's additional
182-
> THINK/DO switch; carvings address `View` only. Zero-dep, `const fn`,
183-
> probe-verified (lance-graph #621). One algebra for both the facet bytes and a
184-
> 12-field class — the shared substrate the three language SDKs (§1.6) all read.
175+
> `cascade_group_shared`. The shape is **class-conditioned**, selected by the
176+
> `classid` from the inherited format via `CascadeShape::from_levels(d)`
177+
> `2 → G6D2` (Rails), `3 → G4D3` (other frameworks), `4 → G3D4` (the GUID
178+
> default); `CascadeShape::ALIGNED = [G3D4, G6D2]` are the shift-`group_of` shapes
179+
> and `ROTATIONS` the full set. **`G4D3` (`4×3`) is legitimate per-class, not a
180+
> "worst case to prevent"** (operator veto 2026-06-29): its `group_of` divides
181+
> (`is_byte_aligned()` is `false`) — a per-class *cost*, not a prohibition;
182+
> `is_byte_aligned`/`shift`/`ALIGNED` distinguish the shift fast-path from the
183+
> divide shape, never a reject gate. **Functions are NOT a carving**
184+
> `facet::ClassArm { View, Functions }` is the classid's additional THINK/DO
185+
> switch; carvings address `View` only. Zero-dep, `const fn`, probe-verified
186+
> (lance-graph #621). One algebra for both the facet bytes and a 12-field class —
187+
> the shared substrate the three language SDKs (§1.6) all read.
185188
186189
---
187190

0 commit comments

Comments
 (0)