fragment = "ui"
[aliases]
root = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/**'"
view = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:render/*:view/**'"
terminal_shell = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:shell/*:terminal/**'"
panels = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:panel/**'"
panel_renderer = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:panel' OR moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:panel/*:render'"
msg_owner = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:events/**' OR moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:app/*:/^(action|state|runtime)$/'"
app_command_boundary = "moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui' OR moniker ~ '**/dir:crates/dir:cli/dir:src/*:ui/*:app/*:/^(mod|state|effect)$/'"
mcp_target = "target ~ '**/dir:crates/dir:cli/dir:src/module:mcp/**'"
[[refs.where]]
id = "ui-does-not-consume-mcp"
expr = """
$root AND (kind = 'imports_symbol' OR kind = 'imports_module' OR kind = 'uses_type' OR kind = 'calls' OR kind = 'method_call')
=> NOT $mcp_target
"""
message = "`code-moniker ui` must not depend on MCP modules; shared runtime state belongs in an explicit CLI-level contract."
rationale = """
The TUI is one projection over the workspace and MCP is another. Sharing the index through a
neutral CLI contract keeps the UI from treating MCP as a facade and prevents MCP-specific
wrappers from leaking into the human interface.
"""
[[refs.where]]
id = "store-boundary"
expr = """
$root AND kind = 'imports_symbol'
=> NOT target ~ '**/path:/^(SessionIndex|IndexedFile|CodeGraph|DefRecord|RefRecord|ChangeIndex|ChangeEntry)$/'
"""
message = "`code-moniker ui` must consume workspace read models; raw graph/index/Git records stay inside `workspace`."
rationale = """
The UI shell should render stable workspace read models, not internal extraction or Git
records. That keeps navigation state independent from storage details and lets workspace
refreshes reconcile data without panel code knowing graph internals.
"""
[[refs.where]]
id = "panels-ratatui-renderers-only"
expr = """
$panels AND (kind = 'imports_symbol' OR kind = 'imports_module')
=> ($panel_renderer OR NOT target ~ '**/external_pkg:ratatui/**')
"""
message = "`ui::panels` view models and builders must stay ratatui-free; only panel renderers may import ratatui."
rationale = """
Panel construction should be testable as plain view-model logic. Restricting ratatui to
renderers keeps feature code from depending on terminal widgets.
"""
[[refs.where]]
id = "view-renders-view-models-only"
expr = """
$view AND (kind = 'imports_symbol' OR kind = 'imports_module')
=> NOT (target ~ '**/path:/^(App|ExplorerFeature)$/'
OR target ~ '**/*:contracts/**')
"""
message = "`ui::view` renders feature view-models; App/Feature/Screen orchestration belongs outside the renderer."
rationale = """
The renderer should be a terminal presentation layer. Importing app or feature orchestration
would make rendering own navigation and lifecycle decisions.
"""
[[refs.where]]
id = "terminal-backend-owned-by-shell-terminal"
expr = """
$root AND (kind = 'imports_symbol' OR kind = 'imports_module')
=> ($terminal_shell OR $rust_inline_tests
OR NOT target ~ '**/path:/^(CrosstermBackend|EnterAlternateScreen|LeaveAlternateScreen|enable_raw_mode|disable_raw_mode)$/')
"""
message = "Terminal backend setup belongs to `ui::shell::terminal`; UI root/app code should not own terminal raw-mode or alternate-screen wiring."
rationale = """
Terminal mode setup is global process state. Keeping it in one shell module avoids scattered
raw-mode cleanup paths and makes UI tests less dependent on terminal side effects.
"""
[[refs.where]]
id = "elm-orchestration-boundary"
expr = """
$root AND kind = 'imports_symbol'
=> (NOT target ~ '**/path:Msg' OR $msg_owner OR $rust_inline_tests)
AND (NOT target ~ '**/path:AppCommand' OR $app_command_boundary OR $rust_inline_tests)
"""
message = "`Msg` is owned by `ui::events` and reduced in `ui::app`; `AppCommand` is emitted by the reducer and interpreted by the runtime shell."
rationale = """
The TUI follows a typed reducer/effect flow. Restricting `Msg` and `AppCommand` imports
keeps event creation, state reduction, and runtime interpretation in their intended layers.
"""
[[views]]
id = "cli-tui-elm"
title = "CLI TUI Elm architecture"
scope = "."
intent = "Understand the TUI event, state, workspace-read, view-model, and render boundaries before changing navigation or panels."
summary = """
The TUI is an Elm-style terminal application. Input keys become typed messages, the app reducer
updates shell state and emits effects, the runtime interprets effects, workspace data is exposed
through read models, and ratatui rendering consumes view models only.
"""
[[views.boundaries]]
id = "events"
owns = [
"terminal key decoding",
"typed UI messages",
]
forbids = [
"state mutation",
"workspace reads",
"rendering decisions",
]
rationale = """
The events layer translates terminal input into intent. It should not know whether a message
changes a tree, opens a panel, schedules work, or renders differently.
"""
symbols = [
"module:events/enum:Msg",
"module:events/fn:key_to_msg",
"module:events/fn:normal_key_to_msg",
]
rules = [
"elm-orchestration-boundary",
]
[[views.boundaries]]
id = "state-runtime"
owns = [
"AppState transitions",
"effect emission",
"runtime interpretation",
"command dispatch",
]
rationale = """
The reducer owns deterministic state transitions. Runtime code applies the resulting commands
and side effects, which keeps terminal IO and background work out of pure transition logic.
"""
symbols = [
"module:app/module:state/fn:reduce_ui_msg",
"module:app/module:action/enum:AppAction",
"module:app/module:effect/enum:Effect",
"module:app/module:runtime/fn:handle_ui_transition_msg",
"module:app/module:runtime/fn:dispatch_and_apply",
]
rules = [
"elm-orchestration-boundary",
]
[[views.boundaries]]
id = "workspace-read"
owns = [
"workspace read-model adaptation",
"navigation tree construction",
"contextual view synchronization",
]
forbids = [
"raw graph and extraction records in TUI panels",
]
rationale = """
The TUI should consume stable workspace read models and navigation state. Raw graph, linkage,
and extraction records remain workspace internals so panels stay resilient to index changes.
"""
symbols = [
"module:workspace_read/fn:symbol_summary",
"module:store/module:navigation_tree/fn:build_navigator",
"module:app/module:navigation/fn:sync_contextual_view",
]
rules = [
"store-boundary",
]
[[views.boundaries]]
id = "view-model-render"
owns = [
"Explorer view model composition",
"active panel view models",
"ratatui shell rendering",
]
rationale = """
Feature and panel modules build plain view models; render modules turn them into ratatui
widgets. Keep this split when adding a visual feature so tests can assert durable screen
contracts without depending on terminal widget internals.
"""
symbols = [
"module:explorer/module:vm/struct:ExplorerVm",
"module:explorer/module:panel_content/fn:active_panel(app:&App)",
"module:render/module:view/fn:render_shell",
]
rules = [
"panels-ratatui-renderers-only",
"view-renders-view-models-only",
]
[[views.boundaries]]
id = "terminal-shell"
owns = [
"terminal setup",
"event loop",
"raw mode cleanup",
]
rationale = """
Terminal raw mode and alternate-screen state are process-wide resources. The shell owns those
lifetime concerns so app, feature, and renderer code can be exercised without real terminal
side effects.
"""
symbols = [
"module:shell/module:terminal/fn:boot",
"module:shell/module:terminal/fn:run_session",
]
rules = [
"terminal-backend-owned-by-shell-terminal",
]
[[views.gotchas]]
id = "key-change-crosses-three-layers"
rationale = """
A keybinding change usually crosses three contracts: key decoding in `events`, state/effect
handling in `app`, and an acceptance assertion over visible behavior. Updating only one layer
creates keys that parse but do nothing, or behavior that cannot be reached from the terminal.
"""
symbols = [
"module:events/fn:normal_key_to_msg",
"module:app/module:runtime/fn:handle_ui_transition_msg",
"module:acceptance/test:usage_lens_tab_cycle_visits_panel_before_usage_lens_and_backtab_reverses()",
]
rules = [
"elm-orchestration-boundary",
]
check = "cargo test -p code-moniker --features tui --no-default-features usage_lens_tab_cycle_visits_panel_before_usage_lens_and_backtab_reverses --lib"
[[views.gotchas]]
id = "focus-is-shell-state"
rationale = """
Navigator, panel, usage lens, and view lens focus are shell state. Renderers may style the
active region, but focus order and focus transitions belong to the app state path.
"""
symbols = [
"module:app/module:panel_focus/fn:cycle_focus_region",
"module:app/module:state/enum:FocusRegion",
"module:acceptance/test:usage_lens_tab_cycle_visits_panel_before_usage_lens_and_backtab_reverses()",
]
check = "cargo test -p code-moniker --features tui --no-default-features usage_lens_tab_cycle_visits_panel_before_usage_lens_and_backtab_reverses --lib"
[[views.gotchas]]
id = "acceptance-tests-observe-screen-contracts"
rationale = """
TUI tests should assert user-visible contracts through rendered text and interaction flows.
They should not freeze private renderer composition or ratatui widget structure.
"""
symbols = [
"module:acceptance/struct:TuiAcceptance",
"module:acceptance/struct:TuiAcceptance/method:render_text(width:u16,height:u16)",
"module:acceptance/fn:assert_visible(screen:&str,expected:&str)",
]
check = "cargo test -p code-moniker --features tui --no-default-features ui::acceptance --lib"