Skip to main content

RFC-0010: Regionalized "Rail & Drawer" Navigation Architecture

Context

As Framework M moves towards a productized multi-app platform, the previous navigation models were limited by their flat structure and heavy focus on DocTypes. High-density platform applications require a navigation system that can handle hundreds of diverse resources (Reports, Pages, Kiosks) while remaining clean and extensible.

Proposed Architecture

1. The Physical Model: "Rail & Drawer"

Inspired by modern IDEs and high-density SaaS (e.g., Slack, VS Code), we transition from a single sidebar to a dual-layered navigation:

  • Primary Rail (The Switcher): A thin leftmost vertical bar containing App Icons (WMS, Finance, Studio, HR). Clicking an icon switches the Contextual Drawer.
  • Contextual Drawer (The Tree): A standard-width navigation drawer that displays a hierarchical resource tree for the active App.

2. The Resource Paradigm

Navigation nodes are no longer strictly "DocTypes". The Navigation Manifest supports diverse Actionable Resources:

TypeDescriptionExample
DocTypeStandard List/Form viewsStock Entry
PageCustom React pagesPicking Dashboard
ReportGrid/Chart viewsInventory Aging
KioskSimplified terminal UIsQuick Pick Terminal
LinkInternal deep-links or external tools (with queries)/wms/stock?item={{ id }}

[!TIP] Dynamic Interpolation: Link routes support template placeholders (e.g., {{ user_id }} or {{ branch_code }}). These are resolved by the Fidelity Port using the active UserContext or application state to ensure links remain contextual rather than hardcoded.

3. Dual-Layer Contract (Ports & Adapters)

To maintain the "No-Cliff" promise, the navigation is governed by two distinct contracts:

A. The Context Port (Python)

The Logical Source of Truth. This port resolves what the user is entitled to see and where they should go.

  • Protocol: NavigationProtocol.
  • Contract: get_navigation_manifest(user_context: UserContext) -> NavigationManifest.
  • Resolution Hierarchy:
    1. Entitlement Check: Query the FeatureEntitlementProtocol to see which apps are active for the tenant.
    2. Rail Discovery (Apps): Query only entitled apps' implementations of NavigationProtocol.
    3. Policy Filtering: Filter nodes via the NavigationPolicyEngine (RBAC/ABAC).
    4. Regional Augmentation: Apply decorators from NavigationDecoratorRegistry.
    5. User Personalization: Final merge with user-pinned or "Frequent" nodes.

B. The Fidelity Port (React)

The Physical Execution Layer. This port resolves how the manifest is rendered to the user.

  • Adapter: Standard <Navigation /> component provided by framework-m-ui.
  • Rendering Logic: The UI consumes the NavigationManifest JSON. It maps resource types (DocType, Page, Report) to specific visual treatments.
  • Escapability: Advanced apps can provide their own React FidelityAdapter to override the standard navigation rendering while still consuming the systematic Python manifest.

4. Extensibility via Metadata Decorators

Custom apps can augment productized navigation without editing the original codebase:

  • Registry: NavigationDecoratorRegistry.
  • Mechanism: A custom app can register a decorator that targets a specific AppID.
  • Example: An "India Compliance" app adds a GST Returns page to the Finance App's navigation manifest at runtime.
# Imperative Registration Example
def add_gst_module(manifest: NavigationManifest):
manifest.add_section("Compliance").add_node(type="Page", label="GST Portal")

NavigationDecoratorRegistry.register(target_app="finance", decorator=add_gst_module)

5. Progressive Decomposition (MFE Survival)

To adhere to the progressive-decomposition.md principles (Part 13), the navigation architecture supports architectural decoupling:

  • Adapter-Driven Discovery: The NavigationProtocol remains agnostic to where metadata comes from. In decomposed environments, a specialized RemoteDiscoveryAdapter is injected to fetch manifests from FRAMEWORK_M_MFE_REMOTES over the network.
  • Logical Route Stability: All navigation nodes must use logical route strings (e.g., /wms/inventory) rather than hardcoded component references. This ensures the Core remains functional regardless of which Adapter (Local vs. Remote) resolves the UI.
  • Auto-Proxying: Connectivity issues are handled at the Adapter level (triggering FRAMEWORK_M_MFE_PROXY) rather than leaking into the navigation logic.

6. Policy-Driven Resolution (The "Conditional" Layer)

To handle the challenge of "what to show" conditionally (similar to Landing Resolution), we implement a policy-driven engine that sits between the Manifest and the UI:

6.1 The Visibility Policy

Navigation nodes can declare visibility requirements using a schema that the NavigationProtocol resolves:

{
"label": "GST Returns",
"route": "/finance/gst",
"visibility_policy": {
"roles": ["Accounts Manager"],
"attributes": { "country": "India" },
"feature_flag": "compliance_enabled"
}
}

6.2 Post-Login Handshake

Similar to the resolve_landing API call, the frontend can fetch a "Resolved Navigation" post-authentication. This ensures that the UI doesn't just show a generic navigation and then "flicker" nodes away as permissions load.

  • Frontend Cache: The resolved manifest is stored in the App State (Redux/Zustand).
  • Hard Refresh: Manifests are re-validated on page refresh or context switch (e.g., switching companies).

6.3 Contextual Auto-Switching

To reduce cognitive load, the navigation should automatically respond to the user's current route:

  • Route Tracking: If the user navigates to /wms/picking, the Primary Rail should automatically highlight the WMS icon.
  • Drawer Opening: If the drawer is collapsed, it should expand to reveal the active node's parent section.

7. SaaS Entitlements (The Control Plane)

To support multi-tenant SaaS environments, we add an Entitlement Layer that governs the visibility of entire feature sets before user-level roles are checked.

7.1 The Entitlement Protocol

The SaaS platform provider implements the FeatureEntitlementProtocol:

class FeatureEntitlementProtocol(Protocol):
def is_feature_enabled(self, tenant_id: str, feature_id: str) -> bool:
"""Checks if the tenant's subscription plan allows this feature."""
...

7.2 Entitlement-Aware Nodes

Navigation nodes can optionally declare a feature_id. If the entitlement check fails for that ID, the node (and its children) are automatically pruned from the manifest at the Context Port level.

{
"label": "Advanced Forecasting",
"route": "/finance/forecast",
"feature_id": "finance_ai_pack",
"visibility_policy": { "roles": ["Finance Manager"] }
}

8. Interactive Extensions (Live State & Intents)

To support high-engagement applications (Chat, Fitness, Real-time monitoring), navigation nodes transition from static links to Dynamic Interaction Points.

8.1 Live Badges & Status

Navigation nodes can declare a badge property containing a logical path to a server-side query or a real-time status stream.

  • Example: badge: "wms.queries.get_pending_picks"
  • Behavior: The Fidelity Port (UI) resolves this path and renders a badge (e.g., "12") or status indicator (e.g., "● Active") next to the node label. Updates are pushed in real-time via the NATS-backed WebSocket stream defined in ADR-0003.

8.2 Hardware & OS Intents

Nodes can be mapped to specialized hardware inputs or OS-level voice commands.

  • hardware_intent: Maps a node to physical buttons (e.g., crown_click, sos_button, f1_key).
  • voice_alias: A list of keywords for voice-activated navigation ("Hey Siri, open Store Inventory").

8.3 Interaction Hints

Hints provided to the Fidelity Port to optimize rendering for specialized devices:

  • interaction_mode: touch_optimized (Kiosks), rotary_scrollable (Watches), dpad_navigable (TVs).

Implementation Strategy

  1. Phase 1: Update NavigationManifest schema to include badge, hardware_intent, and interaction_mode.
  2. Phase 2: Implement the FeatureEntitlementProtocol in framework-m-core.
  3. Phase 3: Update the NavigationPolicyEngine to enforce hierarchy: Entitlement -> Policy -> Decoration.
  4. Phase 4: Implement the Primary Rail & Contextual Drawer components in framework-m-ui.

Unresolved Questions

  • Mobile UX: Should the Rail become a Bottom Tab Bar on mobile?
  • State Persistence: Should the "Last Active App" be stored in User Preferences or LocalStorage?
  • Server-Side Rendering: How does policy resolution affect initial HTML generation for SEO/speed?