02 — Open Axis Model: From Current Domains to Future Axes (Branch B) (2026-06-01)
02 — Open Axis Model: From Current Domains to Future Axes (Branch B)
Generalizes One-Roof Governance from "today's known domains" to an open-ended axis model so future axes/modules/object-types/registries/DOTs/documents/IU-structures are pulled into coverage automatically, with no hardcoded axis array. No SQL is designed here; this is the law/design model. New definition: M-DEF-8 (Axis) and M-DEF-9 (Axis Registry) (consolidated in doc 13).
2.0 The problem this solves
The prior pack enumerated the current axes (grouping, classification, threshold, pin, phantom, pivot coverage, render). That is a hardcoded list — it cannot answer the mission's question 4 ("how is a new domain/axis/object type automatically pulled into governance coverage?") or question 6 ("how do we avoid hardcoding current domains/axes?"). The fix is to stop enumerating axes and instead define what an axis is, make an axis a governed object, and detect any un-registered axis the way the system detects orphans.
2.1 What is an axis (M-DEF-8)
An axis is any dimension along which system objects are classified, counted, grouped, pivoted, ordered, related, or displayed — i.e. any dimension that can change shared truth, classification, counting, display, or interpretation. An axis is distinct from the objects it organizes:
unit_kindis an axis overinformation_unitrows;legal_domaintags are an axis; the parent→child→grandchild containment is an axis; a pivot's group-by column is an axis.
The membership predicate is the same shared-truth-reachability test as M-DEF-1, narrowed by a structural test: a thing is an axis if changing its definition, vocabulary, or grouping policy changes what is classified/counted/grouped/displayed as truth. No enumeration is needed — the predicate decides, so unknown future axes are in-scope automatically.
An axis is a governed object (coverage profile AXIS, doc 06 class 4). Therefore everything One-Roof says about governed objects — owner, approval, audit, rollback, issue/event path, detection — applies to axes by inheritance of the model, not by re-statement per axis.
2.2 The nine attributes of a governed axis
Every axis, present or future, is described by nine attributes. These are the registration record, not a fixed list of axes:
| Attribute | Meaning | Example (IU legal_domain tag axis) |
|---|---|---|
| axis | the dimension's stable code | axis.iu.legal_domain |
| axis family | the registry/domain it belongs to | iu (Information Unit) |
| axis owner (per scope) | accountable owner for policy (vocab/grouping) + supporting health/execution/render |
policy: GOV-COUNCIL; substrate/health: GOV-KG-SYS |
| axis scope | who/what it spans: global / domain-local / object-local; cross-surface or single-surface | global, cross-surface (KB + pivot + display) |
| axis source registry | the registry that holds the axis's vocabulary (the allowed values) | Đ24 taxonomy / dot_config (deferred — OPEN P38-X-11) |
| axis grouping policy | the COUNCIL-owned policy governing grouping ceilings, pin, thresholds for this axis | max_ungrouped ≤ 50 row (Đ24/29) |
| axis coverage rule | the per-profile mandatory links (owner + risk-required) the axis must carry to be covered |
owner + vocab-source-registry + grouping-policy + issue-path |
| axis issue path | the registered issue/event types fired when the axis is orphan/unregistered/island | axis_unregistered, axis_owner_gap, axis_vocab_unowned |
| axis lifecycle | birth → register → active → deprecate → retire (Đ0-G + Đ30) | draft vocab → APR-medium enacted → retire reversible |
A future axis is registered by filling these nine attributes — data, not code. No law rewrite, no array edit.
2.3 The Axis Registry (M-DEF-9) — the missing governed registry
The Axis Registry is a governed registry object (coverage profile
POLICY/REGISTRY) that enumerates every active axis with its nine attributes. It is the ground-truth inventory of axes that the coverage scanner reconciles against (doc 01 T1-7; doc 10).
Live status (verified this session, doc 03/00): NO axis-registry table or governed registry exists. Axis vocabulary is scattered — unit_kind (a controlled-draft vocab), axis_b_tags (an open JSONB tag-bag with namespaces unit_kind/section_type/legal_document/topic/legal_domain), the pivot definitions, dot_config, Đ24 facets. The IU design itself flags this: "there is no single axis-registry table; axis-B vocab anchoring is an OPEN item (Đ24 vs dot_config, deferred APR)." This absence is itself a governance gap — until the registry exists, the coverage scanner cannot reconcile axes, so axes are detected case-by-case and unknown axes escape. The registry's absence is detectable as an inventory_gap (critical) the moment the model is adopted.
The Axis Registry is NOT a new island: it is owned per-scope under the existing spine (policy → GOV-COUNCIL; substrate/vocab → GOV-KG-SYS; detection → GOV-SIV; execution → GOV-DOT), reuses the registry pattern (Điều 2), and is born via the Birth Registry (Điều 0-G).
2.4 Worked map of the current axes (illustrative, not a closed list)
These are the axes that exist or are implied today, each mapped to the nine attributes. This table is an example of applying the model, not a hardcoded set — the model would generate the same record for any new axis.
| Axis | Family | Source registry (vocab) | Policy owner | Substrate/health owner | Open-ended? | How new value added |
|---|---|---|---|---|---|---|
IU original-source / order (axis A: doc_code/section_code/sort_order) |
iu | information_unit source cols |
COUNCIL | GOV-KG-SYS | values open; schema fixed | data (any doc_code) |
IU specialty/domain (axis B: axis_b_tags namespaces) |
iu | Đ24 / dot_config (deferred) | COUNCIL | GOV-KG-SYS | fully open tag-bag | data (new namespace key) |
| IU parent-child-grandchild (axis C: containment) | iu | iu_relation (contains) |
COUNCIL | GOV-KG-SYS | depth open; schema fixed | data (new edge) |
| Composition / species | composition | Đ0-B levels + Đ29 species | COUNCIL | GOV-KG-SYS | open via Đ29 vocab | data (APR-medium) |
| Registry / pivot group-by | registry | pivot_definitions / meta_catalog |
COUNCIL | GOV-SIV | open | data (new pivot row) |
| Workflow / task | assembly | BPMN (Đ34) | COUNCIL | GOV-MOW/MOT (draft) | open | data |
| DOT operation / domain | dot | dot_tools domain |
COUNCIL | GOV-DOT | open | data (register DOT) |
| Label / taxonomy | taxonomy | Đ24 facets / label_rules |
COUNCIL | GOV-KG-SYS | open | data |
| Event / queue | event | event_type_registry |
COUNCIL | GOV-SIV | open (register-before-emit) | data |
The point of the table is that every row was produced by the same nine-attribute template. A tenth, eleventh, hundredth axis is produced identically — the law does not grow.
2.5 How a future axis is born, registered, and covered
- Born (Điều 0-G). A new axis is created like any object: it gets a birth-registry entry. Before birth it is a birth-orphan (handled by the birth detector, precedence per M-DEF-4).
- Registered (Axis Registry, M-DEF-9). Its nine attributes are filled. Registration is an APR-gated change (it can change classification/counting/display truth) —
assign_axis_owneraction-type (doc 01 T1-6: prerequisite). Vocabulary anchors to a named source registry (Đ24 or a declareddot_config/registry surface). - Owner-assigned (per scope). policy owner (COUNCIL), substrate/vocab owner (GOV-KG-SYS or the family's substrate owner), health (GOV-SIV), execution (GOV-DOT), render (GOV-MOUT after activation; interim COUNCIL delegation).
- Coverage rule attached (profile
AXIS, doc 06). Mandatory links: owner + source-registry + grouping-policy + issue-path; risk-required: approval-path (vocab edits are APR-gated), audit, rollback. - Covered ⟺ all profile-mandatory links resolve. Until then it is
axis_orphan/axis_unregistered/axis_vocab_unowned.
2.6 Minimum governance coverage for any axis
| Required | Why |
|---|---|
| Accountable policy owner (one) | a dimension that changes truth must have one accountable definer |
| Named source registry for its vocabulary | no axis may invent its own classification outside a registered vocab (Đ24) — else axis_vocab_unowned/classification-island |
| Grouping/threshold policy as COUNCIL-owned rows | numeric ceilings (e.g. max_ungrouped ≤ 50) are governed rows, never literals |
| Issue/event path registered (register-before-emit) | orphan/island detection must have a place to write |
| Approval path for vocab/grouping edits | adding a value or changing a ceiling can change counts/display → APR-gated |
| Audit + rollback | vocab/grouping changes are reversible and logged |
| Coverage inheritance: owner-link ONLY | an axis-family container may pass its owner to member axes; risk links computed per axis (anti-hiding) |
2.7 How an unregistered axis is detected (the orphan analog)
The mission requires "the system must detect anything outside governance like it detects orphans." For axes this is concrete:
axis_unregistered(critical): a thing functioning as an axis — appearing as a pivot group-by column, a classification facet, a tag-namespace driving display, a newunit_kindvalue, a new grouping dimension — that is not present in the Axis Registry. Detected by reconciling ground-truth axis-bearing surfaces (pivot_definitions,axis_b_tagsnamespaces, Đ24 facets,meta_catalogdimensions,dot_configgrouping keys) against the Axis Registry. This is the axis-levelinventory_gap.axis_owner_gap(high): registered axis missing an accountable policy owner.axis_vocab_unowned(high): axis whose vocabulary is not anchored to a registered source registry (a classification island — red-team #6/#25).axis_grouping_island(high): axis whose grouping/threshold policy lives in a local table with no central owner (red-team #4/#29).
Detection is the same six-layer scanner as for objects (doc 10), reading axis-bearing surfaces as just another input. No per-axis code.
2.8 How Registries-Pivot displays an axis
Registries-Pivot is the render surface for the Axis Registry and axis coverage, under Đ28 (render owner GOV-MOUT, interim COUNCIL delegation). It must:
- list every registered axis with its nine attributes and coverage state (
covered/orphan/island/exception) — pivotable by family, owner, scope, lifecycle; - surface
axis_unregistered/axis_owner_gap/axis_vocab_unownedas first-class rows alongside object orphans (not a separate page); - compute no axis truth in Nuxt — it reads L5/pivot/coverage views only (Đ28 NT-D1-ext, doc 04). A hardcoded axis list in the UI is itself a red-team target (#37) →
hardcode_violation.
2.9 How DOTs scan / propose / apply / audit an axis
Axes reuse the coverage-DOT lifecycle (Đ35 §6.2-bis): DETECT (scan axis-bearing surfaces vs Axis Registry) → PROPOSE (assign_axis_owner / register_axis APR — prerequisite action-type) → APPROVE (Đ32 quorum) → APPLY (write axis ownership edge — needs §5.4-EXT object/axis edge, T1-6 prerequisite) → VERIFY → CLOSE. Separation of duty: the DOT never approves its own axis registration. The same two-mode-apply limitation as objects applies: axis-grain owner edges are apply_blocked until §5.4-EXT (T1-6).
2.10 No fixed axis arrays — compliance statement
This model contains no enumerated axis array that the system depends on. The current-axis table (§2.4) is illustrative; the runtime depends only on (a) the shared-truth/axis predicate (M-DEF-1/8), (b) the Axis Registry (M-DEF-9, data), and (c) the inventory-completeness reconciliation (doc 10). A future axis is a new registry row; the laws, the scanner, and the display do not change. This satisfies mission §5 ("do not create fixed axis arrays") and questions 4/6.
Branch-B verdict
The open axis model converts "governance of the current axes" into "governance of whatever functions as an axis." The one new dependency is the Axis Registry (M-DEF-9) — a governed registry object that does not exist live; its absence is the first thing the model detects (inventory_gap). With it, future axes are covered by data, and an unregistered axis is caught exactly like a birth-orphan.