@@ -66,35 +66,34 @@ The bitmask **is** the selected / unselected partition:
6666Versions, roles, and projections are simply ** different masks over the same
6767template** . 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
7171The ` u64 ` mask above is ** one bucket** of 64 field positions. A wide class — an
7272Odoo ` 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
170171whole dynamic ClassView field view: Redmine-shaped, JSON-free, no conditionals
171172in 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.
0 commit comments