diff --git a/agents/skills/AGENTS.md b/agents/skills/AGENTS.md new file mode 100644 index 0000000..8241185 --- /dev/null +++ b/agents/skills/AGENTS.md @@ -0,0 +1,117 @@ +# Repository Guidelines + +## Scope +* This file defines default contributor guidance that applies across repositories unless a repo-local `AGENTS.md` overrides it. +* Prefer local project conventions when they conflict with this document. + +## Communication Style +* Be direct and thorough. No sycophancy; never praise questions, validate premises, or open with agreement. If I'm wrong, lead with that. +* Hold your positions: don't capitulate to pushback without new evidence. Generate your own estimates before considering mine. State confidence levels explicitly (high/moderate/low/unknown). +* No disclaimers, no ethics commentary unless asked, no "it's important to consider" hedging. Negative conclusions are fine; deliver them clearly. +* Tone: precise, not diplomatic. Answers can be pointed and argumentative. +* Verify your own claims. If you don't know, say so; don't fabricate. +* Prefer long, detailed answers with full reasoning shown. + +## Start With Context +* Inspect the repository before editing. Read the README, build or package entry points, test configuration, and recent history before making changes. +* Avoid assumptions about architecture, tooling, or naming. + +## Code Changes +* Keep edits minimal, targeted, and reversible. +* Match the existing style of the repository, use ASCII unless the file already requires Unicode, and add brief comments only where logic is not obvious. +* Do not rewrite unrelated code while addressing a focused task. + +## Validation +* Run the smallest meaningful verification for the change, then escalate to broader checks if needed. +* Prefer project-native commands such as test suites, linters, type checks, or build tasks. +* If validation cannot be run, state that clearly. + +## Git Hygiene +* Do not revert user changes you did not make. Avoid destructive commands such as `git reset --hard` unless explicitly requested. +* Keep commit messages short, imperative, and specific to the behavioral change. + +## Collaboration +* Communicate assumptions, constraints, and risks directly. +* When a task is unclear, resolve it from local context first; ask only when a missing answer would create real risk. +* Summaries should emphasize what changed, what was verified, and any remaining gaps. + +## MATLAB Build, Test, and Development Commands +* Always open the MATLAB project before running code or tests, using the `openProject` function. Never use `addpath` when writing code or tests. +* Run build commands from the repository root: + +```matlab +buildtool check +buildtool test +``` +* `buildtoool check` enforces Code Analyzer and project checks. Run this for any non-trival review or change set. +* Run targeted tests with `runtests` for edits, for example `runtests("tests/backend/tWorkspace.m")`. +* Use `checkcode("path/to/file.m")` or MATLAB Code Analyzer during local iteration. +* Expect impacted-test execution to remain enabled in the build unless there is an explicit reason to change build policy. + +## MATLAB Coding Style and Naming Conventions +* Use MATLAB style with 4-space indentation and one class/function per file. +* Always follow the prevailing spacing and commenting styles in the current project; do not mix styles. +* Do not exceed 75 characters per line of code, including any spaces and comments. +* Always add newly created files to the MATLAB project using the project APIs; do not bypass the project APIs. +* Always remove deleted files from the MATLAB project using the project APIs; do not bypass the project APIs. +* Follow the repo-level naming rules from `resources/codeAnalyzerConfigurations.json` if these exist. +* Classes and properties use `UpperCamelCase`. +* Use a trailing underscore only for a private backing property that pairs with a public property of the same base name. +* Functions/methods/local functions/nested functions use `lowerCamelCase` or `lowercase`. +* Prefer verb phrases for actions such as `appendLayers`, `renameCycle`, and `updateMemoryChart`. +* Name logical-valued properties as adjectives such as `Loaded` or `Connected`. Do not prefix them with `Is`. +* Name logical-returning methods and functions with `is*` or `has*`. +* Refer to instance objects as `obj` in production classes and `testCase` in tests. Do not use `this`. +* Refer to name-value structures as `namedArgs`. +* Test classes and methods start with `t` (for example, `tChannelSettings`, `tConstructorAcceptsNameValuePairs`). + +## MATLAB Source Code Authoring Rules + +* Use `arguments ( Input )` and `arguments ( Output )` blocks for validation instead of manual parsing where practical. +* Use `namedArgs.?ClassName` in constructors and methods when the class already follows that pattern. +* Prefer string scalars with `"` for user-facing text and modern APIs. Do not use character arrays unless unavoidable. +* Keep access control as narrow as possible. Make UI handles and listeners private unless tests need controlled access. +* Do not create ordinary class methods where the instance object is unused; instead, create local functions or private static methods, with a preference for local functions. This does not apply to unimplemented callbacks. +* Separate public API, protected lifecycle, and private callbacks/helpers instead of mixing responsibilities in one methods block. +* Use stable, namespaced error identifiers such as `Workspace:renameLayer:InvalidLayerIndex`. +* Write error and alert messages that state what failed and why. +* Restore temporary path or state changes with `onCleanup` when mutating global process state. +* Do not bypass project checks by removing assertions, weakening validation, or suppressing analyzer findings without a strong local reason. +* Do not leave unused code files, or unused properties or methods. +* Use `listener` to create listener objects; do not use `addlistener`. +* Prefer name, value syntax over name=value, e.g., `"LineWidth", 2` and not `LineWidth=2`. +* Always add size, type, and attribute validation for class properties and methods/functions that accept external inputs. + +## MATLAB Frontend and UI Rules + +* Keep UI construction inside `setup` for component-style classes unless the surrounding class already uses a different lifecycle. +* Use the constructor for listener wiring and superclass construction, not for large blocks of widget creation. +* Keep event handlers and button callbacks private and name them with the `onThingEvent` pattern already used in the project. +* Guard UI operations that require a figure ancestor, as in `ancestor( obj, "figure" )`, before opening dialogs or alerts. +* If the Test Framework Extensions toolbox is installed, expose test-only UI handles through `GetAccess = ?Testable` rather than widening the public API; otherwise, use `GetAccess = ?matlab.unittest.TestCase`. +* Keep `update` methods cheap and side-effect aware. Use them to refresh state, not to rebuild the whole object graph unless the class already follows that pattern. + +## MATLAB Testing Guidelines +* Always add newly created test files to the MATLAB project, and ensure the testing folder structure mirrors that of the code. +* Name new test files `t.m` and keep test methods behavior-focused. +* Label new test files with the "Test" label using the project APIs; do not bypass the project APIs. +* Before opening a merge request, run `buildtool test` and confirm coverage artifacts are produced cleanly. +* Use `matlab.unittest.TestCase` for standard unit tests and `Testable` for UI tests that depend on `matlab.uitest.TestCase`. +* Store reusable fixtures in private test properties with explicit type and size validation when the surrounding tests do so. +* Add `TestClassSetup` checks for warning-free construction when constructor cleanliness is part of the contract. +* Use `TestMethodSetup` to rebuild state between tests instead of relying on cross-test mutation. +* Write assertion messages that explain the expected behavior, not just that something failed. +* When production code exposes internal state only for tests, prefer the established `?Testable` pattern over adding public getters just to satisfy tests. +* If the Test Framework Extensions toolbox is installed, do not convert data types when using `verifyEqual`; instead, use the `IsEquivalentText` for text comparison. +* If the Test Framework Extensions toolbox is installed, do not transpose arrays when using `verifyEqual`; instead, use the `IsEqualVector` constraint for vector comparison. + +## MATLAB Code Review Checklist + +* Confirm naming follows the project rules, especially class names, test names, logical properties, and `namedArgs`. +* Confirm argument validation is explicit and modern. +* Confirm access modifiers are no wider than needed. +* Confirm stateful cleanup is present for path, listener, file, or environment mutations. +* Confirm user-facing errors and alerts are specific and actionable. +* Confirm a matching test exists in `tests` for new behavior or a convincing reason is documented when tests are not practical. +* Confirm no new analyzer issues or dead files are introduced. +* Confirm release-critical files preserve packaging, test, and CI behavior. \ No newline at end of file diff --git a/agents/skills/mvc-app-builder/SKILL.md b/agents/skills/mvc-app-builder/SKILL.md new file mode 100644 index 0000000..1d89477 --- /dev/null +++ b/agents/skills/mvc-app-builder/SKILL.md @@ -0,0 +1,338 @@ +--- +name: mvc-app-builder +description: > + Build, refactor, and extend MATLAB apps using the Model-View-Controller + pattern for maintainable applications. Use when a user wants + to build a medium to large MATLAB app, create a GUI, make an interactive tool. Also when the user describes the organization around a shared handle model, semantic model events, + package-based app structure such as `+AppName`, `ComponentContainer` UI + classes, a shared abstract `Component` superclass, and a separate + launcher or composition root that wires many focused views and + controllers around a shared model. +--- + +# MVC App Builder + +Build MATLAB apps with a concrete MVC structure. + +Prefer the patterns used by strong MATLAB MVC examples: + +- a package folder such as `+wordScramble` or `+quickdraw` +- one shared handle `Model` +- many focused `View` and `Controller` classes +- a shared abstract `Component` base class when several UI classes listen to the same model +- semantic model events such as `WordChosen`, `PredictionMade`, or `RefreshPushed` +- a top-level `Launcher`, `runApp`, or `LaunchApp` file that assembles the application + +## When to Use This Skill + +Use this skill when: +- User wants to build a medium to large MATLAB app, GUI, or interactive tool. +- User mentions that the app needs to respect the MVC design pattern. +- User mentions that the app needs to be maintainable and contain different classes. +- User mentions: MVC, Model View Controller, View, Model, Controller, classes, launcher + +## When Not to Use + +- User mentions wanting to create an app in App Designer +- The request is purely about MATLAB computation with no UI component + +## Objective + +Separate the application into: + +- `Model`: domain state, domain methods, validation, and event broadcasts +- `Views`: multiple UI components that reveal different slices of model state +- `Controllers`: multiple UI components that collect user input and invoke model methods +- `Launcher` or composition root: top-level assembly that creates the figure, model, many views/controllers, and app-level wiring + +Use MATLAB patterns that align with the reference apps: + +- Handle classes for the model and other long-lived app objects +- `matlab.ui.componentcontainer.ComponentContainer` for reusable views and controllers +- MATLAB package folders for nontrivial apps +- Semantic events on the model rather than direct model-to-view references +- A shared `Component` superclass when multiple UI classes need the same model reference and listener setup +- Composition from many specialized UI components rather than one monolithic view/controller pair + +## Critical Rules + +- Keep business logic and application state in the model, not in UI callbacks. +- You should be able to use the app and all its functionalities programmatically just using the Model class. +- Keep the model free of UI objects, layout code, and figure logic. +- Use semantic model events that describe domain actions or state transitions. +- Let views and controllers hold references to the model; do not let the model reference them. +- Use a shared abstract `Component` superclass when multiple UI classes repeat the same listener wiring. +- Keep app assembly in a separate `Launcher`, `runApp`, or similar entry point. +- Use MATLAB package folders for nontrivial apps unless the user explicitly wants a single-file prototype. +- Validate constructor inputs and public method inputs with `arguments` blocks. + +## Workflow + +```text +User request arrives + | + v +Identify the app's domain state, user actions, and visible surfaces + | + v +Choose the package and composition-root structure + | + v +Define the Model API and semantic events + | + v +Decide whether a shared abstract Component base class is warranted + | + v +Implement many focused View and Controller components around the shared Model + | + v +Assemble the app in Launcher/runApp/LaunchApp + | + v +Verify callback -> model -> notify -> listener -> UI flow +``` + +## Discovery Guidance + +Clarify these points before building or refactoring: + +- What state must persist for the whole app session? +- What user actions mutate that state? +- What visual surfaces need to update when that state changes? +- Should the app be a package-based app such as `+AppName`? +- Does the app need a separate launcher/composition root? +- Are there repeated model listeners across many components that justify a shared `Component` base class? +- Does the app need supporting assets or helper classes such as `Theme`, icons, sounds, or helper utilities? + +If the user already provided enough structure, proceed directly. + +## MVC Responsibilities + +### Model + +The model is the single source of truth for application state. + +The model should: + +- Store domain state in properties +- Expose public methods that mutate state in controlled ways +- Broadcast semantic events after meaningful state changes +- Avoid any dependency on graphics objects or layout containers +- Remain usable and testable without creating UI + +Prefer patterns such as: + +- `classdef Model < handle` +- `properties (SetAccess = private)` for protected state +- `events` or `events (NotifyAccess = private)` for event definitions +- public methods such as `startRound`, `reset`, `submitWord`, `selectCategory`, `predictCategory` + +Do not reduce the model to raw data storage only. In the reference apps, the model owns real workflow logic. + +### Shared Component Base Class + +When several views and controllers all need the same model reference and the same listener setup, create an abstract `Component` superclass. + +The component base should usually: + +- derive from `matlab.ui.componentcontainer.ComponentContainer` +- store the shared `Model` +- create listener properties in the constructor +- provide abstract protected callbacks such as `onWordChosen`, `onPredictionMade`, `refresh`, or `enter` + +Use this pattern only when it removes genuine repetition. Do not create a base class for a single component. + +### View + +Views reveal model state and respond to model events. + +The views should: + +- create and own the graphics objects it presents +- refresh its UI from current model state inside listener callbacks +- keep formatting and display details local to the component +- avoid owning domain logic + +Views can be lightweight display components such as: + +- score headers +- category labels +- result tables +- plot or image displays +- message or status panels +- instruction panels +- timers, clocks, and progress displays + +Views may call an initial sync method in the constructor when the displayed state must be correct immediately after construction. + +Do not force unrelated presentation into one large view class. Split the UI by responsibility and by area of the screen. + +### Controller + +Controllers collect user input and invoke model methods. + +The controllers should: + +- own buttons, fields, state buttons, dropdowns, canvas interactions, or keyboard controls +- translate UI actions into model method calls +- update controller-owned control state when model events require it +- avoid taking over view-owned presentation responsibilities +- stay focused on one interaction cluster such as a toolbar, keyboard, local action area, or canvas + +In MATLAB MVC, controllers are often also event listeners because controller-owned controls may need to react to model events. That is acceptable when the controller is updating its own controls, not rendering somebody else's view. + +Do not force all interactions into one controller class. Prefer several small controllers over one monolithic interaction class. + +## MATLAB-Specific Design Rules + +### Package Structure + +For maintainable apps, prefer package-based organization: + +```text ++AppName/ ++-- Model.m ++-- Component.m ++-- Launcher.m ++-- Theme.m ++-- View1.m ++-- View2.m ++-- Controller1.m ++-- helpers... +``` + +Use a package when the app has multiple classes, helper utilities, or assets. + +### ComponentContainer Pattern + +Views and controllers should usually derive from `ComponentContainer` so they can be created as reusable UI building blocks. + +Common constructor pattern: + +- call the superclass constructor with `Parent=[]` +- store the model +- create listeners +- apply `namedArgs` + +Common implementation pattern: + +- `setup` creates UI objects and layouts +- `update` is empty unless the component exposes public properties that affect rendering + +### Semantic Events + +Prefer semantic event names over generic property-change events. + +Good examples: + +- `WordChosen` +- `RoundFinished` +- `GameReset` +- `CategorySelected` +- `PredictionMade` +- `RefreshPushed` + +These event names encode app behavior clearly and make listeners easier to reason about. + +### Composition Root + +Keep the top-level app assembly separate from component classes. + +Typical responsibilities of `Launcher`, `runApp`, or `LaunchWordle`: + +- create the figure +- create the model +- create views and controllers +- parent components into layouts, tabs, or card panels +- add app-level listeners or menu wiring +- manage lifecycle concerns that are broader than one component + +Do not bury whole-app composition inside an individual view or controller. + +### Supporting Classes and Assets + +For richer apps, supporting classes are part of the architecture: + +- `Theme` for fonts, colors, and sizing tokens +- helper classes for sounds, timers, or utility functions +- icons, sounds, and other assets referenced by controllers or launcher code + +Keep these concerns outside the model unless they are genuinely part of domain behavior. + +## File Organization + +Recommended responsibilities: + +- `Model.m`: domain state, methods, events +- `Component.m`: shared model/listener scaffolding for UI components +- `*View.m`: multiple focused display-oriented components +- `*Controller.m`: multiple focused interaction-oriented components +- `Launcher.m` or `runApp.m`: app assembly and figure-level orchestration +- `Theme.m` and helpers: styling and support infrastructure + +## Build Sequence + +When implementing an MVC app, follow this order: + +1. Choose the package name and top-level assembly pattern. +2. Define the model state, public methods, and semantic events. +3. Add a shared `Component` superclass if multiple UI classes need the same listener scaffolding. +4. Partition the UI into many focused view and controller components around the shared model. +5. Create the launcher or entry point that wires the app together. +6. Verify every important interaction as a flow from callback to model to event to UI update. +7. Add helper classes, themes, and assets only after the core event flow works. + +## Refactoring Guidance + +When refactoring an existing MATLAB app into MVC: + +1. Identify all domain state currently mixed into app properties or callback code. +2. Move domain state and workflow methods into a model class. +3. Define semantic events for the state transitions that other components care about. +4. Split monolithic UI code into many focused view and controller components. +5. Introduce a shared `Component` base class only if listener wiring is repeating across several classes. +6. Move figure-level wiring and assembly into a launcher or entry-point file. +7. Preserve behavior first; refine architecture second. + +Common code smells to fix: + +- one large app class mixing state, rendering, and user interaction +- callbacks that compute domain logic and repaint the UI directly +- duplicated listener setup across many UI classes +- whole-app assembly hidden inside one component class +- generic events that do not reveal what actually happened + +## Quality Checks + +Before finishing, verify: + +- The model contains domain logic but no UI dependencies. +- Events are semantic and match real app transitions. +- Views and controllers share one model instance. +- Repeated listener wiring has been factored sensibly, not over-engineered. +- The UI is decomposed into multiple responsibility-focused views and controllers. +- Controllers update controller-owned controls only. +- Views refresh from model state rather than stale cached values. +- The launcher or entry point owns app-level composition. +- Package structure, naming, and helper classes are consistent across the app. + +## Output Expectations + +When using this skill, produce: + +- a recommended package and file layout +- a proposed event map between model and UI components +- MATLAB code organized into one model, a component base when needed, many focused views/controllers, and a launcher where appropriate +- concise reasoning for whether the app needs a shared `Component` base class +- refactoring guidance that preserves working behavior while improving structure + +## References + +Use bundled references when available: + +- `references/mvc-guide.md` for detailed MVC class and event patterns +- `references/componentcontainer-patterns.md` for reusable UI component structure +- `assets/examples/` for packaged reference apps when the skill includes them + +If packaged reference apps are bundled with the skill, inspect the closest example first and reuse its composition patterns before inventing a new structure. diff --git a/agents/skills/mvc-app-builder/references/componentcontainer-patterns.md b/agents/skills/mvc-app-builder/references/componentcontainer-patterns.md new file mode 100644 index 0000000..73fcbb1 --- /dev/null +++ b/agents/skills/mvc-app-builder/references/componentcontainer-patterns.md @@ -0,0 +1,201 @@ +# ComponentContainer Patterns + +Use `matlab.ui.componentcontainer.ComponentContainer` as the default base for reusable MATLAB MVC view and controller classes. + +## When to Use + +Use `ComponentContainer` when the class: + +- owns a reusable portion of the UI +- needs a parent assigned by a launcher or higher-level layout +- should encapsulate its own `setup` and UI lifecycle +- needs to be instantiated multiple times or composed with sibling components + +Do not use it for the Model. The Model should remain a plain handle class with no UI dependency. + +## Core Pattern + +Typical MVC component classes should: + +1. derive from `matlab.ui.componentcontainer.ComponentContainer` +2. accept the shared Model as the first constructor input +3. call the superclass constructor with `Parent=[]` +4. store the Model in an immutable or private property +5. create and store listeners in the constructor if needed +6. create graphics objects in `setup` +7. leave `update` empty unless public component properties drive UI changes + +## Constructor Pattern + +```matlab +classdef ResultsView < matlab.ui.componentcontainer.ComponentContainer + properties (Access = private) + Model (1,1) AppName.Model + Listeners (:,1) event.listener = event.listener.empty + end + + methods + function obj = ResultsView(model, namedArgs) + arguments + model (1,1) AppName.Model + namedArgs.?AppName.ResultsView + end + + obj@matlab.ui.componentcontainer.ComponentContainer( ... + Parent=[], Units="normalized", Position=[0 0 1 1]) + + obj.Model = model; + obj.Listeners = [ + addlistener(model, "DataChanged", @(~,~) obj.onDataChanged()) + ]; + + set(obj, namedArgs) + end + + function delete(obj) + delete(obj.Listeners) + end + end +end +``` + +## Setup and Update Pattern + +Use `setup` to create the layout and graphics objects owned by the component. + +```matlab +methods (Access = protected) + function setup(obj) + grid = uigridlayout(obj, [2 1]); + uilabel(grid, Text="Results"); + uiaxes(grid); + end + + function update(obj) + end +end +``` + +Use `update` only when public component properties affect the UI. Do not move normal model-driven refresh logic into `update`. + +## Shared Abstract Component Base Class + +When several views and controllers need the same model property and the same listener wiring, create an abstract `Component` superclass. + +Typical responsibilities: + +- store the shared `Model` +- create common listeners in the constructor +- define abstract protected callbacks for subclasses + +Pattern: + +```matlab +classdef (Abstract) Component < matlab.ui.componentcontainer.ComponentContainer + properties (GetAccess = protected, SetAccess = immutable) + Model (1,1) AppName.Model + DataChangedListener (:,1) event.listener {mustBeScalarOrEmpty} + end + + methods + function obj = Component(model) + arguments + model (1,1) AppName.Model + end + + obj@matlab.ui.componentcontainer.ComponentContainer( ... + Parent=[], Units="normalized", Position=[0 0 1 1]) + + obj.Model = model; + obj.DataChangedListener = listener(model, ... + "DataChanged", @obj.onDataChanged); + end + end + + methods (Abstract, Access = protected) + onDataChanged(obj, ~, ~) + end +end +``` + +Use this pattern only when multiple component classes genuinely share the same event subscriptions. + +## View Patterns + +Use `ComponentContainer` views to: + +- render plots, tables, labels, images, or status displays +- listen to semantic model events +- refresh their own graphics from current model state + +Views should: + +- own only their own graphics objects +- keep listener callbacks short +- pull data from the model inside the callback + +## Controller Patterns + +Use `ComponentContainer` controllers to: + +- own buttons, dropdowns, state buttons, keyboard controls, canvases, or toolstrips +- translate user actions into model method calls +- update controller-owned controls when model state changes + +Controllers should: + +- keep callbacks thin +- avoid redrawing view-owned graphics +- avoid storing domain state outside the model + +## Layout Patterns + +Prefer: + +- `uigridlayout` for structural layout +- nested components for screen regions +- one component per responsibility-focused UI area + +Avoid: + +- large monolithic UI classes +- hard-coded figure-wide layout logic inside leaf components +- direct parent-child dependencies between sibling components + +## Launcher Integration + +The launcher or entry point should: + +- create the figure +- create the shared model +- instantiate components with `Parent=...` +- place components into the top-level layout + +Pattern: + +```matlab +function app = runApp() + fig = uifigure(Name="My App"); + grid = uigridlayout(fig, [3 1]); + + model = AppName.Model(); + headerView = AppName.HeaderView(model, Parent=grid); + resultsView = AppName.ResultsView(model, Parent=grid); + toolbarController = AppName.ToolbarController(model, Parent=grid); + + app.figure = fig; + app.model = model; + app.headerView = headerView; + app.resultsView = resultsView; + app.toolbarController = toolbarController; +end +``` + +## Quality Checks + +- Component classes do not contain domain logic. +- The Model is not a `ComponentContainer`. +- Listener handles are stored and cleaned up. +- `setup` creates owned graphics only. +- `update` is used only for public-property-driven UI sync. +- Shared listener wiring is factored only when repetition is real. diff --git a/agents/skills/mvc-app-builder/references/mvc-guide.md b/agents/skills/mvc-app-builder/references/mvc-guide.md new file mode 100644 index 0000000..3e42530 --- /dev/null +++ b/agents/skills/mvc-app-builder/references/mvc-guide.md @@ -0,0 +1,377 @@ +# MATLAB MVC Guide + +Structure MATLAB apps using the Model-View-Controller pattern. This guide provides the architectural layer: class design, model events, many focused view/controller components, and separation of responsibilities for maintainable applications. + +## Critical Rules + +- MUST separate Model, Views, and Controllers into distinct classes +- MUST keep application state and business logic in the Model +- MUST keep rendering and graphics refresh logic in Views +- MUST keep user interaction handling and callback orchestration in Controllers +- MUST implement MVC classes as handle classes +- MUST use model events and view listeners for state-driven UI refresh +- MUST clean up all listeners in class destructors (`delete` method) +- NEVER put UI component creation in the Model +- NEVER put business logic or data transformation in Views +- NEVER let the Model reference Views or Controllers +- ALWAYS use `arguments` blocks to validate constructor inputs and public methods + +## Architecture Overview + +```text ++AppName/ ++-- +Models/ +| +-- AppModel.m % Data, state, business logic, events ++-- +Views/ +| +-- HeaderView.m % Status, title, or summary display +| +-- ResultsView.m % Tables, plots, or images +| +-- ImageView.m % Specialized image display component ++-- +Controllers/ +| +-- ToolstripController.m % Top-level commands or ribbon actions +| +-- InputController.m % Local buttons, fields, or action clusters +| +-- CanvasController.m % Direct-manipulation interactions ++-- +Components/ +| +-- Component.m % Optional shared model/listener base class ++-- Launcher.m % Entry point and app assembly +``` + +Event flow: +1. User interacts with a control owned by a Controller +2. That Controller callback invokes a Model method +3. Model updates state +4. Model broadcasts an event with `notify(...)` +5. Relevant View listener callbacks refresh their parts of the UI from Model state + +```text +User -> Controller -> Model -> notify(EventName) -> Views +``` + +Object references: + +- Each Controller that needs domain access holds a reference to the Model +- Each View that needs state for refresh holds a reference to the Model +- Model holds no references to Views or Controllers + +This keeps the Model independent and testable while allowing many small Views and Controllers to respond to a shared source of truth. + +## Model + +The Model owns app data, state transitions, validation, and domain operations. It extends `handle` so the app operates on one live state object. + +```matlab +classdef AppModel < handle + properties (SetAccess = private) + Data double = [] + IsDirty logical = false + Status string = "Ready" + end + + events (NotifyAccess = private) + DataChanged + StateChanged + end + + methods + function loadData(obj, newData) + arguments + obj + newData double + end + + obj.Data = newData(:); + obj.IsDirty = true; + obj.Status = "Loaded"; + notify(obj, "DataChanged"); + notify(obj, "StateChanged"); + end + + function reset(obj) + obj.Data = []; + obj.IsDirty = false; + obj.Status = "Reset"; + notify(obj, "DataChanged"); + notify(obj, "StateChanged"); + end + end +end +``` + +### Model Design Guidelines + +- Expose methods for state changes instead of allowing uncontrolled property mutation +- Broadcast events immediately after relevant state changes +- Use constrained property access to protect invariants +- Keep the Model free of `uifigure`, axes, buttons, layouts, or callback handles +- Make Model methods usable and testable without any UI present + +## Shared Component Base Class + +When several view/controller classes all need the same model property and listener creation pattern, factor that repeated code into an abstract `Component` superclass. + +Typical responsibilities: + +- derive from `matlab.ui.componentcontainer.ComponentContainer` +- store the shared `Model` +- create relevant listener properties in the constructor +- define abstract protected listener callbacks for subclasses to implement + +Use this pattern only when several components genuinely share the same listener scaffolding. + +## Views + +Views render Model state and refresh in response to Model events. Each view manages its own graphics objects and listeners, but does not own business logic. + +```matlab +classdef ResultsView < matlab.ui.componentcontainer.ComponentContainer + properties (Access = private) + Model (1,1) AppName.Models.AppModel + Grid matlab.ui.container.GridLayout + Axes matlab.ui.control.UIAxes + Line matlab.graphics.chart.primitive.Line + StatusLabel matlab.ui.control.Label + Listeners (:,1) event.listener = event.listener.empty + end + + methods + function obj = ResultsView(model, namedArgs) + arguments + model (1,1) AppName.Models.AppModel + namedArgs.?AppName.Views.ResultsView + end + + obj.Model = model; + obj.Listeners = [ + addlistener(model, "DataChanged", @(~,~) obj.onDataChanged()) + addlistener(model, "StateChanged", @(~,~) obj.onStateChanged()) + ]; + + set(obj, namedArgs); + end + + function delete(obj) + delete(obj.Listeners); + end + end + + methods (Access = protected) + function setup(obj) + obj.Grid = uigridlayout(obj, [2 1]); + obj.Axes = uiaxes(obj.Grid); + obj.StatusLabel = uilabel(obj.Grid, Text="Ready"); + obj.Line = plot(obj.Axes, NaN, NaN); + end + + function update(obj) + end + end + + methods (Access = private) + function onDataChanged(obj) + data = obj.Model.Data; + set(obj.Line, XData=1:numel(data), YData=data); + end + + function onStateChanged(obj) + obj.StatusLabel.Text = obj.Model.Status; + end + end +end +``` + +### View Design Guidelines + +- Split the UI into multiple focused views rather than one giant view class +- Create and own graphics objects in each View +- Store listeners as properties so they remain alive +- Refresh the UI by reading current Model state inside listener callbacks +- Use `setup` for object creation and `update` only for public-property-driven refresh +- Keep formatting and visual presentation here, not state transitions + +Typical views include: + +- headers and status bands +- results displays +- image or plot panels +- category or instruction displays +- timers, clocks, and progress indicators + +## Controllers + +Controllers provide controls and callbacks that translate user actions into Model operations. + +```matlab +classdef ToolstripController < matlab.ui.componentcontainer.ComponentContainer + properties (Access = private) + Model (1,1) AppName.Models.AppModel + Grid matlab.ui.container.GridLayout + LoadButton matlab.ui.control.Button + ResetButton matlab.ui.control.Button + end + + methods + function obj = ToolstripController(model, namedArgs) + arguments + model (1,1) AppName.Models.AppModel + namedArgs.?AppName.Controllers.ToolstripController + end + + obj.Model = model; + set(obj, namedArgs); + end + end + + methods (Access = protected) + function setup(obj) + obj.Grid = uigridlayout(obj, [1 2]); + obj.LoadButton = uibutton(obj.Grid, Text="Load", ... + ButtonPushedFcn=@(~,~) obj.onLoadButtonPushed()); + obj.ResetButton = uibutton(obj.Grid, Text="Reset", ... + ButtonPushedFcn=@(~,~) obj.onResetButtonPushed()); + end + + function update(obj) + end + end + + methods (Access = private) + function onLoadButtonPushed(obj) + newData = rand(20, 1); + obj.Model.loadData(newData); + end + + function onResetButtonPushed(obj) + obj.Model.reset(); + end + end +end +``` + +### Controller Design Guidelines + +- Split interaction logic into multiple focused controllers rather than one giant controller +- Keep callbacks thin: validate inputs, invoke Model methods, handle light control flow +- Do not store domain state in UI control values alone +- Do not redraw plots or mutate view graphics directly from controller callbacks +- Group related controls into reusable controller components + +Typical controllers include: + +- toolstrip or toolbar controllers +- button clusters for a local action area +- keyboard-style controllers +- canvas or direct-manipulation controllers +- dialog or submission controllers + +## Entry Point + +The entry point creates the figure, instantiates the Model, then assembles many Views and Controllers into the top-level layout. + +```matlab +function app = runApp() + fig = uifigure(Name="MVC App"); + grid = uigridlayout(fig, [2 1]); + + model = AppName.Models.AppModel(); + headerView = AppName.Views.HeaderView(model, Parent=grid); + resultsView = AppName.Views.ResultsView(model, Parent=grid); + toolstripController = AppName.Controllers.ToolstripController(model, Parent=grid); + + app.figure = fig; + app.model = model; + app.headerView = headerView; + app.resultsView = resultsView; + app.toolstripController = toolstripController; +end +``` + +**Usage:** `app = AppName.runApp();` + +Return a struct to keep object references alive and make cleanup predictable. + +## Refactoring Existing Apps into MVC + +When refactoring a legacy MATLAB app: + +1. Identify all persistent app state currently stored in app properties or UI values +2. Move domain state and domain methods into a Model class +3. Move plotting, labels, tables, and visual updates into several focused View classes +4. Move callback bodies into several focused Controller classes +5. Replace direct callback-to-plot updates with Model methods and events +6. Split large monolithic classes only after behavior is preserved + +Typical migration targets: + +- callback code that computes and redraws in one method +- app properties that mix data, graphics handles, and user workflow state +- duplicated button or drop-down callback logic +- view code reading control values instead of Model state + +## Async Operations + +For long-running Model operations, keep the UI responsive by running the heavy work outside the UI callback, then updating the Model on completion. + +Pattern: + +1. A Controller starts an asynchronous task +2. Completion callback writes results back into the Model +3. Model broadcasts events +4. affected Views refresh automatically + +Do not mutate View graphics directly from background execution paths. + +## Testing Strategy + +MVC improves testability because the Model can be tested without UI, and Controller behavior can often be tested through Model side effects. + +Test at these levels: + +- Model unit tests for state transitions, validation, and event-triggering methods +- Integration tests for controller-to-model interactions +- UI smoke tests for View refresh and listener wiring + +Focus most automated tests on the Model first. + +## Composition Guidance + +| Case | Recommended MVC Shape | +|------|------------------------| +| Small app | One Model, a few focused Views/Controllers | +| Multi-panel app | One shared Model, multiple focused Views and Controllers | +| Reusable area | Extract subview/subcontroller pair under `components/` | +| Complex domain | Keep one top-level app shell and split Model responsibilities carefully | + +Use a shared base class only when several views or controllers truly repeat lifecycle or wiring code. + +## Implementation Checklist + +- [ ] Package folder structure defined clearly +- [ ] Model extends `handle` +- [ ] Model owns state and domain logic only +- [ ] Model defines custom events for meaningful state changes +- [ ] Views are split by UI responsibility, not collapsed into one class +- [ ] Controllers are split by interaction responsibility, not collapsed into one class +- [ ] Each View listens to Model events and refreshes its UI from Model state +- [ ] Views store and delete listeners correctly +- [ ] Controllers own controls and callback functions +- [ ] Controller callbacks invoke Model methods rather than editing graphics +- [ ] Entry point wires the Model, Views, and Controllers together cleanly +- [ ] Refactoring preserves behavior before deeper architecture cleanup + +## Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| A View is not refreshing | No listener on Model event | Add `addlistener(model, "EventName", @cb)` and ensure the callback updates that View's UI | +| Listener never fires | Model never calls `notify(...)` | Broadcast the event after the state change | +| Invalid or deleted object error | Listener survives after component destruction | Delete listeners in `delete()` | +| Model state drifts from UI | A Controller writes directly to graphics instead of the Model | Move state changes into Model methods | +| Circular updates | A View callback or Controller triggers redundant state writes | Add guard logic or compare values before updating | +| Hard-to-test app | Business logic still lives in callback code | Move domain logic into Model methods and retest | + +## References + +Use this guide together with: + +- `SKILL.md` for high-level routing and build guidance +- `references/componentcontainer-patterns.md` for reusable UI component structure +- `references/refactor-playbook.md` for migration steps from legacy MATLAB apps