# Osx Review

> Use when preparing mobile/desktop apps for App Store submission, before final release, or when user mentions App Store, production readiness, shipping, or needs comprehensive quality review for distribution

- Skill: `boltzmannentropy/osx-review` (Agent Skill)
- Install (CLI): `npx skillmds@latest add boltzmannentropy/osx-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/boltzmannentropy/osx-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: BoltzmannEntropy (https://skillmd.com/u/boltzmannentropy)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/boltzmannentropy/osx-review

---


# App Store Readiness Code Review

## Overview

Systematic code review process for applications targeting Apple App Store, Google Play, or desktop distribution. Identifies crash risks, security vulnerabilities, resource leaks, and compliance issues that cause rejection or poor user experience.

This skill now includes a mandatory cross-repo consistency pass for macOS app websites + README licensing language before release.
This skill also enforces MimikaCODE production UX baselines (system logs, queue/history/models/settings/file-path surfaces) for both existing projects and newly created projects.

## Repository Layout (Mandatory in This Workspace)

All macOS app projects must follow this structure under `artifacts/code`:

- `artifacts/code/<AppName>PRJ/<AppName>CODE` - source code repository
- `artifacts/code/<AppName>PRJ/<AppName>WEB` - static website repository (moved from `artifacts/all-web`)

For licensing and legal checks, always use these surfaces:

- **README surface:** `<AppName>CODE/README.md`
- **Flutter app surface:** `<AppName>CODE/flutter_app/` (legal screens + bundled app resources)
- **Website surface:** `<AppName>WEB/` (`index.html`, `license.html`, `privacy.html`, `terms.html`, `privacy-consent.js`)

Do not create or update app sites in `artifacts/all-web` for these macOS apps.

## When to Use

- Before App Store/Play Store submission
- Before any production release
- When user says "ship", "release", "production ready", "App Store"
- After major feature completion
- When reviewing cross-platform apps (Flutter, React Native, etc.)

## iOS/iPad Baseline (Current)

For iOS/iPad submissions, enforce these before final sign-off:

- [ ] Run `bash ./skills/osx-ios/scripts/check_ios_dist.sh --app-root <APP_ROOT>` and resolve all `FAIL` findings
- [ ] Archive/upload baseline matches current App Store Connect tooling requirements
- [ ] TestFlight constraints checked (tester caps, build age, beta review flow)
- [ ] Screenshot coverage satisfies current iPhone + iPad minimum requirements
- [ ] Privacy manifest and required-reason API declarations validated

## Review Categories

Review ALL categories systematically. Do not skip any.

```dot
digraph review_flow {
    rankdir=TB;
    node [shape=box];

    "Start Review" -> "1. Crash Prevention";
    "1. Crash Prevention" -> "2. Resource Management";
    "2. Resource Management" -> "3. Network & API";
    "3. Network & API" -> "4. Security";
    "4. Security" -> "5. Data Persistence";
    "5. Data Persistence" -> "6. Platform Compliance";
    "6. Platform Compliance" -> "7. Error Handling";
    "7. Error Handling" -> "8. MCP Integration (macOS)";
    "8. MCP Integration (macOS)" -> "9. Performance";
    "9. Performance" -> "10. Product Information";
    "10. Product Information" -> "Generate Report";
}
```

## Severity Classification

| Severity | Definition | Action |
|----------|------------|--------|
| **Critical** | Will cause crashes, data loss, or rejection | Must fix before submission |
| **High** | Likely to cause issues under normal use | Should fix before submission |
| **Medium** | Edge cases, degraded experience | Fix in next release |
| **Low** | Code quality, best practices | Nice to have |

## 1. Crash Prevention Checklist

### Flutter/Dart
- [ ] All async callbacks check `mounted` before `setState()`
- [ ] `StreamSubscription` cancelled in `dispose()`
- [ ] `Timer` cancelled in `dispose()`
- [ ] `AnimationController` disposed
- [ ] `TextEditingController` disposed
- [ ] `ScrollController` disposed
- [ ] `FocusNode` disposed
- [ ] Null safety: no force unwraps (`!`) without guaranteed non-null
- [ ] List/Map access with bounds checking or `.elementAtOrNull()`

### Flutter UI Patterns (Reference: flutter-python-fullstack)
- [ ] **Theme**: Uses `ColorScheme.fromSeed()` with Material 3
- [ ] **Dark mode**: Supports `ThemeMode.system` (respects OS preference)
- [ ] **Backend check**: Health check on startup with loading/disconnected states
- [ ] **Bundled backend autostart**: If backend is down and app is bundled, UI attempts backend startup automatically (no manual CLI prerequisite for end users)
- [ ] **Startup status UX**: UI shows backend startup progress/status (for example: starting/waiting/failed)
- [ ] **Exit shutdown hook**: App intercepts desktop window-close/exit requests and runs graceful backend shutdown before process exit
- [ ] **Shutdown UX**: During close, app shows a blocking "Stopping server/backend..." progress dialog until shutdown finishes or timeout path is handled
- [ ] **Production messaging**: Disconnected-state copy does not instruct end users to run terminal commands
- [ ] **Stats polling**: Uses `Future.doWhile()` with `mounted` guard
- [ ] **Status chips**: Color-coded (green/orange/red) using `withValues(alpha:)`
- [ ] **Deprecated APIs**: No `withOpacity()` (use `withValues(alpha:)` instead)
- [ ] **ApiService**: Centralized HTTP client with typed endpoints
- [ ] **System log visibility**: App exposes a user-visible system log panel (not only startup status text)
- [ ] **System log actions**: Users can copy logs and export logs directly from UI controls
- [ ] **Footer log console**: App includes a footer system-log area that is collapsible and resizable
- [ ] **Footer parity**: Footer log area also provides copy/export actions without navigating to settings
- [ ] **Log export surface**: Backend provides a plain-text system log export endpoint (separate from full diagnostics bundle)
- [ ] **Job Queue surface**: App has a visible job queue with live per-job status (`queued`, `processing`, `paused`, `cancelling`, `completed`, `failed`, `cancelled`), queue position, and controls (`pause`, `resume`, `cancel`, `delete`)
- [ ] **Persistent Job History**: Job history persists across app restarts with metadata (created time, model/engine, status, chunk progress, timing metrics, output path/URLs)
- [ ] **Jobs History playback**: App has a jobs-history UI page that supports audio/video playback plus save/download and open-in-folder actions for generated outputs
- [ ] **Queue event sync**: Queue/history UI updates live from websocket events (`job_created`, `job_update`, `job_completed`, `job_failed`, `job_cancelled`)
- [ ] **File path visibility**: Generation results and history rows show full output file paths with an `Open Folder`/`Reveal in Finder` action

### iOS/Swift
- [ ] No force unwraps (`!`) on optionals from external data
- [ ] `weak self` in closures to prevent retain cycles
- [ ] `deinit` called (add print to verify during testing)
- [ ] No unhandled `fatalError()` or `preconditionFailure()`

### Android/Kotlin
- [ ] Null checks on Intent extras
- [ ] Activity lifecycle handled (no operations on destroyed activity)
- [ ] Fragment lifecycle handled
- [ ] No `!!` on nullable external data

### Backend/Python
- [ ] All exceptions caught at API boundary
- [ ] No bare `except:` clauses (catch specific exceptions)
- [ ] Thread safety for shared resources
- [ ] Connection pool limits configured

## 2. Resource Management Checklist

### Memory Leaks
- [ ] Large objects released when not needed
- [ ] Image/media caching bounded
- [ ] Listeners/observers removed
- [ ] Background tasks cancelled on screen exit
- [ ] File handles closed in finally blocks
- [ ] Voice-clone pipelines profiled with Instruments (Allocations + Leaks) for full clone lifecycle (load model -> clone -> teardown)
- [ ] Add standalone clone regression tests using `Natasha` and `Suzan` voices to detect runaway memory growth or unreleased buffers

### File System
- [ ] Temp files cleaned up
- [ ] File existence checked before read
- [ ] File permissions checked
- [ ] Path sanitization (no `../` injection)
- [ ] Disk space checked before large writes
- [ ] Runtime writes never target `.app/Contents/...` or mounted `.dmg` paths
- [ ] Mutable runtime storage uses user-writable locations (`~/Library/Application Support/<App>`, `~/Library/Caches/<App>`, `~/Library/Logs/<App>`)

### Audio/Video
- [ ] Players disposed when done
- [ ] Audio session properly configured
- [ ] Background audio handled correctly
- [ ] Interruption handling (phone calls)

## 3. Network & API Checklist

### Timeouts
- [ ] All HTTP requests have timeout configured
- [ ] Reasonable timeout values (10-30s for normal, 60-120s for uploads)
- [ ] Timeout errors handled gracefully

### Error Handling
- [ ] Network unavailable handled
- [ ] Server errors (5xx) handled
- [ ] Client errors (4xx) handled with user feedback
- [ ] Malformed response handled
- [ ] Empty response handled

### Resilience
- [ ] Retry logic with exponential backoff
- [ ] Circuit breaker for failing services
- [ ] Offline mode / cached data fallback
- [ ] Request cancellation on screen exit

### Configuration
- [ ] Base URL configurable (not hardcoded localhost)
- [ ] API version handling
- [ ] Certificate pinning (if required)
- [ ] Bundled desktop apps can fully start backend without any external shell command
- [ ] Port-conflict path handled (if port already bound, user gets clear action instead of silent failure)

## 4. Security Checklist

### Input Validation
- [ ] All user input validated
- [ ] Path traversal prevention (`../`)
- [ ] SQL injection prevention (parameterized queries)
- [ ] XSS prevention (output encoding)
- [ ] File type validation for uploads

### Authentication
- [ ] Tokens stored securely (Keychain/Keystore)
- [ ] Token refresh logic
- [ ] Session expiration handling
- [ ] Logout clears all sensitive data

### Network Security
- [ ] HTTPS only (no HTTP except localhost)
- [ ] CORS configured properly (not `*` in production)
- [ ] Sensitive data not logged
- [ ] API keys not in source code

### Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] No sensitive data in logs
- [ ] No sensitive data in crash reports
- [ ] Clipboard cleared after paste of sensitive data

## 5. Data Persistence Checklist

### Database
- [ ] Schema migrations for updates
- [ ] Database connection pooling
- [ ] Thread-safe access (locking or connection per thread)
- [ ] Backup/restore capability
- [ ] Corruption recovery
- [ ] Database path resolves to user-writable runtime directory (not app bundle path)

### Preferences/Settings
- [ ] Default values for all settings
- [ ] Settings validation on load
- [ ] Settings migration for app updates

### Cache
- [ ] Cache size limits
- [ ] Cache expiration
- [ ] Cache invalidation logic
- [ ] Graceful handling of corrupted cache
- [ ] ML/model cache path is app-scoped for bundled builds (avoid accidental reuse of developer/global cache unless explicitly intended)
- [ ] Model-download detection logic honors runtime cache environment variables (`HUGGINGFACE_HUB_CACHE` / `HF_HOME` / `XDG_CACHE_HOME`)

## 6. Platform Compliance Checklist

### Apple App Store
- [ ] Privacy manifest (PrivacyInfo.xcprivacy) present
- [ ] Required-reason API declarations and third-party SDK manifests validated
- [ ] Required device capabilities declared
- [ ] App Transport Security configured
- [ ] No private API usage
- [ ] Proper entitlements configured
- [ ] App icons all sizes present
- [ ] Launch screen configured
- [ ] Build uploaded with current supported Xcode/SDK baseline
- [ ] TestFlight readiness verified (internal/external path + beta review expectations)
- [ ] iPhone and iPad screenshot requirements satisfied for enabled device families

### Google Play
- [ ] Target SDK meets requirements
- [ ] Permissions declared and justified
- [ ] Data safety form ready
- [ ] 64-bit support
- [ ] App bundle (not APK)

### macOS App Store
- [ ] Sandboxing configured
- [ ] Hardened runtime enabled
- [ ] Notarization ready
- [ ] Entitlements minimal and justified

### macOS Distribution
- [ ] DMG builder script present (`scripts/build_dmg.sh`)
- [ ] DMG includes app bundle, Applications symlink, and background image
- [ ] `hdiutil` fallback packages the DMG staging directory (not only the `.app`) so Applications symlink survives fallback builds
- [ ] Code signing for DMG distribution
- [ ] Notarization of DMG for Gatekeeper
- [ ] If DMG is unsigned, release notes + README + website include explicit Gatekeeper bypass steps with concrete date and `Open Anyway` path
- [ ] Volume name and window layout configured
- [ ] SHA256 hash generated alongside DMG (`.dmg.sha256`)
- [ ] Version extracted from centralized version file
- [ ] DMG root includes `LICENSE` (source) and `BINARY-LICENSE.txt` (binary/EULA)
- [ ] App bundle embeds `Contents/Resources/LICENSE` and `Contents/Resources/BINARY-LICENSE.txt`
- [ ] DMG license agreement configured (when supported by the DMG toolchain)
- [ ] Bundled-app smoke test validates `GET /api/health`, `GET /api/pdf/list`, and direct `GET /pdf/<bundled-file>` after launch from `/Applications`
- [ ] Bundled PDF/runtime assets resolve via app-relative paths (no hardcoded source checkout paths)

### Bundled Python Backend (Mandatory for macOS Desktop Distribution)
- [ ] Backend process is launched by the app itself on first run (no terminal dependency for end users)
- [ ] Launch uses bundled Python runtime, not system Python
- [ ] Backend startup path works when app is run from `/Applications` and does not rely on source checkout paths
- [ ] Backend runtime env config sets app-specific writable paths for logs/data/outputs/cache
- [ ] Backend does not require writing launcher logs into app bundle directories
- [ ] Backend model/cache env vars are set for app-scoped storage (`HF_HOME`, `HUGGINGFACE_HUB_CACHE`, `TRANSFORMERS_CACHE`)
- [ ] Backend health check retries include clear startup status and failure state
- [ ] First-launch UI includes explicit startup/waiting log state while bundled backend warms up
- [ ] Disconnected-state primary action is a user-safe restart flow (for example `Restart Server`) and avoids shell-command instructions
- [ ] Backend port-conflict path is handled explicitly (detect in-use port, prompt/confirm stop conflicting process, then retry)
- [ ] First-run behavior tested with no existing localhost backend process running
- [ ] Closing the app window must terminate bundled backend child processes (no orphan backend after UI exit)

### Project Scripts (Reference: flutter-python-fullstack pattern)
- [ ] **Control script** (`bin/appctl`):
  - `appctl up` - Start all services
  - `appctl down` - Stop all services
  - `appctl status` - Show running/stopped with colors
  - `appctl logs` - Tail log files
  - `appctl clean` - Clean logs and temp files
- [ ] **Install script** (`install.sh`):
  - Check/install dependencies (Homebrew, Flutter, etc.)
  - Create virtual environments
  - Download required models
  - Colored output with status indicators
- [ ] **Diagnostic script** (`issues.sh`):
  - System info (OS, architecture, disk space)
  - Tool versions (Flutter, Python, git)
  - Port status checks
  - Network/health checks
  - Last 50 lines of runtime logs
  - Timestamped output file

### Release Scripts (Mandatory for All macOS Apps)

Every macOS app MUST have a `scripts/release.sh` that automates the full release workflow. Manual releases are error-prone and forbidden.

#### Release Script Requirements
- [ ] **Release script** (`scripts/release.sh`) exists and is executable
- [ ] Script extracts version from `pubspec.yaml` automatically (no hardcoded versions)
- [ ] Script supports `--upload` flag for GitHub release upload
- [ ] Script supports `--sync-website` flag for website download link updates
- [ ] Script generates SHA256 checksum alongside DMG
- [ ] Script creates or updates GitHub release for the current tag (never leave tag-only/empty release pages)
- [ ] Script uploads full asset set: DMG + DMG SHA256 + source ZIP + source ZIP SHA256 + release notes + release notes SHA256
- [ ] Script updates website download URLs with new version
- [ ] Script updates website download URLs to direct DMG asset links (not generic release listing pages)
- [ ] Script commits and pushes website changes automatically
- [ ] Script provides clear success/failure output with colored status

#### Version Advancement Protocol
- [ ] Version follows semantic versioning: `MAJOR.MINOR.PATCH` (e.g., `1.0.0`, `1.1.0`, `2.0.0`)
- [ ] Version is stored in a single source of truth: `pubspec.yaml` for Flutter apps
- [ ] Build number increments with each release (e.g., `1.0.0+1` → `1.0.0+2`)
- [ ] Release tag format: `v{VERSION}` (e.g., `v1.0.0`, `v1.1.0`)
- [ ] Never reuse version numbers - always increment
- [ ] PATCH version for bug fixes (1.0.0 → 1.0.1)
- [ ] MINOR version for new features (1.0.0 → 1.1.0)
- [ ] MAJOR version for breaking changes (1.0.0 → 2.0.0)

#### Release Script Pattern
```bash
#!/usr/bin/env bash
# scripts/release.sh - Full Release Script
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
WEBSITE_DIR="$(dirname "$PROJECT_DIR")/${APP_NAME}WEB"

# Extract version from pubspec.yaml
VERSION=$(grep 'version:' "$PROJECT_DIR/pubspec.yaml" | head -1 | cut -d'+' -f1 | cut -d':' -f2 | xargs)

# Parse flags
UPLOAD_TO_GITHUB=false
SYNC_WEBSITE=false
for arg in "$@"; do
    case $arg in
        --upload) UPLOAD_TO_GITHUB=true ;;
        --sync-website) SYNC_WEBSITE=true ;;
    esac
done

# 1. Build DMG
"$SCRIPT_DIR/build_dmg.sh" || "$SCRIPT_DIR/build-dmg.sh"

# 2. Generate SHA256
DMG_PATH="$PROJECT_DIR/build/${APP_NAME}-${VERSION}.dmg"
shasum -a 256 "$DMG_PATH" > "$DMG_PATH.sha256"

# 3. Upload to GitHub (if --upload)
if [ "$UPLOAD_TO_GITHUB" = true ]; then
    TAG="v$VERSION"
    if ! gh release view "$TAG" &> /dev/null; then
        gh release create "$TAG" --title "$APP_NAME $VERSION" --notes "Release notes..." --draft
    fi
    gh release upload "$TAG" "$DMG_PATH" "$DMG_PATH.sha256" --clobber
fi

# 4. Sync website (if --sync-website)
if [ "$SYNC_WEBSITE" = true ]; then
    sed -i '' -E "s|/releases/download/v[0-9.]+/${APP_NAME}-[0-9.]+|/releases/download/v$VERSION/${APP_NAME}-$VERSION|g" "$WEBSITE_DIR/index.html"
    cd "$WEBSITE_DIR"
    git add index.html && git commit -m "Update to v$VERSION" && git push
fi
```

#### Release Checklist (Execute in Order)
1. [ ] Verify all tests pass
2. [ ] Update version in `pubspec.yaml` if needed
3. [ ] Run `./scripts/release.sh --upload --sync-website`
4. [ ] Verify DMG created and checksum generated
5. [ ] Verify GitHub release exists for the tag and is not empty
6. [ ] Verify release assets uploaded (DMG, DMG SHA256, source ZIP, source ZIP SHA256, release notes, release notes SHA256)
7. [ ] If unsigned/not notarized, verify release notes include the Gatekeeper section with current date and launch steps
8. [ ] Verify website download links updated and point directly to current DMG asset URL
9. [ ] Verify website changes committed and pushed
10. [ ] Test DMG direct-download URL (for example with `curl -I -L`) and confirm HTTP 200
11. [ ] Run fresh-user smoke test (no old app in `/Applications`, no pre-downloaded model cache assumptions)

#### Release Script Red Flags
| Issue | Pattern | Fix |
|-------|---------|-----|
| No release script | Manual DMG + upload | Create `scripts/release.sh` |
| Hardcoded version | `VERSION="1.0.0"` | Extract from `pubspec.yaml` |
| No --upload flag | Separate manual upload | Add GitHub release upload |
| No --sync-website | Manual website edit | Add website URL update |
| No checksum | DMG only | Generate `.sha256` file |
| Empty release page | Tag exists but no assets | Enforce upload of full asset set in `release.sh` |
| Missing source/notes artifacts | DMG uploaded without source zip/release notes checksums | Upload source ZIP + notes + both SHA256 files |
| Non-direct download links | Website points to generic `/releases` page | Point website CTAs to `/releases/download/<tag>/<asset>.dmg` |
| No website commit | Website not updated | Auto-commit and push |
| Version reuse | Same tag twice | Always increment version |

### General
- [ ] Version number format correct
- [ ] Build number incremented
- [ ] Release notes prepared
- [ ] Screenshots current

## 7. Error Handling Checklist

### User Feedback
- [ ] All errors show user-friendly message
- [ ] Error messages actionable (what user can do)
- [ ] No technical jargon in user-facing errors
- [ ] Loading states for all async operations
- [ ] Empty states for lists

### Logging
- [ ] Errors logged with context
- [ ] No sensitive data in logs
- [ ] Log levels appropriate
- [ ] Crash reporting configured

### Recovery
- [ ] Retry option for transient failures
- [ ] Data preserved on error
- [ ] App state recoverable after crash
- [ ] Graceful degradation when features unavailable

## 8. MCP Tool Integration Checklist (Required for macOS Apps)

macOS apps MUST expose full functionality via MCP (Model Context Protocol) tools to enable Claude integration. This ensures AI assistants can interact with the app programmatically.

### MCP Server Requirements
- [ ] MCP server script exists at `bin/<appname>_mcp_server.py` or similar
- [ ] Server implements JSON-RPC 2.0 over HTTP protocol
- [ ] Server handles MCP methods: `initialize`, `tools/list`, `tools/call`
- [ ] Server binds to configurable host/port (default: `127.0.0.1:80XX`)
- [ ] Server logs to `runs/logs/<appname>_mcp_server.log` with rotation
- [ ] Backend URL configurable via environment variable (e.g., `<APPNAME>_BACKEND_URL`)

### MCP Tool Definitions
- [ ] `MCP_TOOLS` list contains all available tools
- [ ] Each tool has required keys: `name`, `description`, `inputSchema`
- [ ] Tool names are unique and follow `<domain>_<action>` pattern (e.g., `tts_generate_kokoro`)
- [ ] `inputSchema` is a valid JSON Schema with `type: "object"`
- [ ] Required parameters listed in `inputSchema.required` array
- [ ] Tool descriptions are clear and explain what the tool does

### Required MCP Tools (minimum set)
- [ ] `health_check` - Check if backend is running and healthy
- [ ] `<domain>_status` or `system_info` - Get system/service information
- [ ] `<domain>_list_*` - List available resources (voices, models, files, etc.)
- [ ] `<domain>_<primary_action>` - Core functionality (generate, process, create)

### MCP Tool Schema Pattern
```python
MCP_TOOLS = [
    {
        "name": "app_speak",
        "description": "Generate and play speech with specified text and voice.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "text": {"type": "string", "description": "Text to speak"},
                "speaker": {"type": "string", "description": "Voice/speaker name"},
                "quality_mode": {
                    "type": "string",
                    "enum": ["fast", "balanced", "quality"],
                    "description": "Quality mode (fast=realtime)"
                }
            },
            "required": ["text"]
        }
    },
    {
        "name": "app_speak_streaming",
        "description": "Generate speech with sentence-by-sentence streaming.",
        "inputSchema": {
            "type": "object",
            "properties": {
                "text": {"type": "string", "description": "Text to speak (supports multiple sentences)"}
            },
            "required": ["text"]
        }
    },
    {
        "name": "app_status",
        "description": "Check service status and available resources.",
        "inputSchema": {"type": "object", "properties": {}}
    }
]
```

### HTTP API Parity
- [ ] All MCP tools have corresponding HTTP API endpoints
- [ ] HTTP API follows RESTful conventions:
  - `GET /api/<domain>/status` - Status check
  - `GET /api/<domain>/<resources>` - List resources
  - `POST /api/<domain>/<action>` - Perform actions
  - `DELETE /api/<domain>/<resource>/<id>` - Delete resources
- [ ] HTTP API example (curl):
```bash
# Generate speech
curl -X POST http://localhost:8000/speak \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello", "speaker": "default", "quality_mode": "balanced"}'

# Check status
curl http://localhost:8000/status

# List voices
curl http://localhost:8000/voices
```

### MCP Server Handler Pattern
```python
class MCPHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        # Parse JSON-RPC request
        obj = json.loads(self.rfile.read(...))
        method = obj.get("method")
        params = obj.get("params") or {}

        if method == "initialize":
            # Return server info and capabilities
            return {"protocolVersion": "...", "serverInfo": {...}, "capabilities": {...}}

        if method in ("tools/list", "tools.list"):
            return {"tools": MCP_TOOLS}

        if method in ("tools/call", "tools.call"):
            result = handle_tool_call(params.get("name"), params.get("arguments"))
            return {"content": [{"type": "text", "text": result}]}
```

### MCP Tests
- [ ] Unit tests for tool definitions (`test_mcp_server.py`)
- [ ] Test each tool has required keys
- [ ] Test tool names are unique
- [ ] Test tool call handler dispatches correctly
- [ ] Test JSON-RPC protocol handling (initialize, tools/list, tools/call)
- [ ] Test error handling for unknown tools and malformed requests

### Claude Code Configuration
- [ ] MCP server registered in Claude Code settings or documented for user setup
- [ ] Configuration example provided in README:
```json
{
  "mcpServers": {
    "<appname>": {
      "command": "python3",
      "args": ["/path/to/bin/<appname>_mcp_server.py", "--port", "80XX"],
      "env": {
        "<APPNAME>_BACKEND_URL": "http://localhost:8000"
      }
    }
  }
}
```

### MCP UI Screen (Required for macOS Apps with MCP)
Every macOS app with MCP integration MUST include a dedicated MCP management screen accessible from Settings or main navigation.

#### MCP Screen Structure
- [ ] MCP screen exists at `lib/pages/mcp_page.dart` or `lib/screens/mcp_screen.dart`
- [ ] MCP screen accessible from Settings page or main navigation sidebar
- [ ] MCP screen header shows "MCP Integration" or "Claude Integration" title

#### MCP Server Status Section
- [ ] Server status indicator (Running/Stopped/Error) with color-coded chip
- [ ] Server host and port display (e.g., `127.0.0.1:8087`)
- [ ] Start/Stop server toggle or buttons
- [ ] Server uptime display (optional)
- [ ] Last activity timestamp (optional)

#### Available Tools Section
- [ ] List of all MCP tools with names and descriptions
- [ ] Tool count badge (e.g., "10 tools available")
- [ ] Expandable tool cards showing:
  - Tool name (e.g., `quantum_run_benchmark`)
  - Tool description
  - Input parameters (from inputSchema)
  - Required vs optional parameters indicator
- [ ] Tool category grouping (optional, e.g., "System", "Benchmarks", "Queue")

#### Configuration Section
- [ ] Backend URL field (editable, defaults to environment variable)
- [ ] MCP server port field (editable)
- [ ] Auto-start MCP server toggle
- [ ] Connection test button with success/failure feedback

#### Claude Code Setup Section
- [ ] Copy-to-clipboard button for Claude Code MCP configuration JSON
- [ ] Pre-filled configuration template with correct paths
- [ ] Instructions for adding to Claude Code settings
- [ ] Link to Claude Code MCP documentation (if available)

#### MCP Logs Section (Optional but Recommended)
- [ ] Recent MCP server log entries (last 20-50 lines)
- [ ] Log level filter (Debug/Info/Warning/Error)
- [ ] Clear logs button
- [ ] Export logs button

#### MCP Screen Pattern (Reference Implementation)
```dart
// lib/pages/mcp_page.dart
class McpPage extends StatefulWidget {
  const McpPage({super.key});
  @override
  State<McpPage> createState() => _McpPageState();
}

class _McpPageState extends State<McpPage> {
  bool _serverRunning = false;
  List<Map<String, dynamic>> _tools = [];

  @override
  void initState() {
    super.initState();
    _loadMcpStatus();
    _loadTools();
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // Header
          _buildHeader(),
          const SizedBox(height: 24),

          // Server Status Card
          _buildServerStatusCard(),
          const SizedBox(height: 24),

          // Available Tools Card
          _buildToolsCard(),
          const SizedBox(height: 24),

          // Configuration Card
          _buildConfigCard(),
          const SizedBox(height: 24),

          // Claude Code Setup Card
          _buildClaudeCodeSetupCard(),
        ],
      ),
    );
  }

  Widget _buildServerStatusCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.dns, color: Theme.of(context).colorScheme.primary),
                const SizedBox(width: 12),
                const Text('MCP Server Status', style: TextStyle(fontWeight: FontWeight.bold)),
                const Spacer(),
                Chip(
                  label: Text(_serverRunning ? 'Running' : 'Stopped'),
                  backgroundColor: _serverRunning ? Colors.green.shade100 : Colors.grey.shade200,
                  labelStyle: TextStyle(color: _serverRunning ? Colors.green.shade800 : Colors.grey.shade700),
                ),
              ],
            ),
            const SizedBox(height: 16),
            Text('Host: 127.0.0.1:8087'),
            const SizedBox(height: 8),
            Row(
              children: [
                FilledButton.icon(
                  onPressed: _toggleServer,
                  icon: Icon(_serverRunning ? Icons.stop : Icons.play_arrow),
                  label: Text(_serverRunning ? 'Stop Server' : 'Start Server'),
                ),
                const SizedBox(width: 8),
                OutlinedButton.icon(
                  onPressed: _testConnection,
                  icon: const Icon(Icons.refresh),
                  label: const Text('Test Connection'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildToolsCard() {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.build, color: Theme.of(context).colorScheme.primary),
                const SizedBox(width: 12),
                const Text('Available Tools', style: TextStyle(fontWeight: FontWeight.bold)),
                const SizedBox(width: 8),
                Chip(label: Text('${_tools.length} tools')),
              ],
            ),
            const SizedBox(height: 16),
            ..._tools.map((tool) => ExpansionTile(
              title: Text(tool['name']),
              subtitle: Text(tool['description'], maxLines: 1, overflow: TextOverflow.ellipsis),
              children: [
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Parameters:', style: TextStyle(fontWeight: FontWeight.bold)),
                      // Show inputSchema properties
                    ],
                  ),
                ),
              ],
            )),
          ],
        ),
      ),
    );
  }

  Widget _buildClaudeCodeSetupCard() {
    final config = '''
{
  "mcpServers": {
    "appname": {
      "command": "python3",
      "args": ["/path/to/bin/appname_mcp_server.py"],
      "env": {
        "APPNAME_BACKEND_URL": "http://localhost:8000"
      }
    }
  }
}''';

    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.terminal, color: Theme.of(context).colorScheme.primary),
                const SizedBox(width: 12),
                const Text('Claude Code Setup', style: TextStyle(fontWeight: FontWeight.bold)),
              ],
            ),
            const SizedBox(height: 16),
            const Text('Add this configuration to your Claude Code settings:'),
            const SizedBox(height: 12),
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.grey.shade100,
                borderRadius: BorderRadius.circular(8),
              ),
              child: SelectableText(config, style: const TextStyle(fontFamily: 'monospace', fontSize: 12)),
            ),
            const SizedBox(height: 12),
            FilledButton.icon(
              onPressed: () => Clipboard.setData(ClipboardData(text: config)),
              icon: const Icon(Icons.copy),
              label: const Text('Copy Configuration'),
            ),
          ],
        ),
      ),
    );
  }
}
```

#### MCP UI Accessibility
- [ ] All MCP tool names readable by screen readers
- [ ] Status indicators have text alternatives (not color-only)
- [ ] Copy buttons have clear labels
- [ ] Expandable sections properly announce state

### Common MCP Integration Issues
| Issue | Pattern | Fix |
|-------|---------|-----|
| Missing tools | No MCP server | Create `bin/<appname>_mcp_server.py` |
| No tool schemas | Tools missing inputSchema | Add JSON Schema for each tool |
| Backend not proxied | MCP calls backend directly | Use `_call_backend()` helper pattern |
| No error handling | Exceptions crash server | Wrap tool calls in try/except |
| No logging | Silent failures | Add rotating file logger |
| Hardcoded ports | Port conflicts | Make host/port configurable |

## 9. Performance Checklist

### Startup
- [ ] Cold start < 3 seconds
- [ ] No blocking operations on main thread at startup
- [ ] Lazy loading for non-essential features

### UI Responsiveness
- [ ] No jank (dropped frames)
- [ ] Heavy work on background threads
- [ ] UI updates batched
- [ ] Large lists virtualized

### Memory
- [ ] Memory usage stable (no growth over time)
- [ ] Large assets loaded on demand
- [ ] Image resolution appropriate for display

### Battery
- [ ] Location updates minimal
- [ ] Background refresh minimal
- [ ] No unnecessary polling

## 10. Product Information Checklist

### Required App Information
- [ ] Version number displayed in app (Settings or About page)
- [ ] Build number accessible for support purposes
- [ ] Centralized version file pattern:
  ```dart
  // lib/version.dart
  const String appVersion = "2026.02.1";
  const int buildNumber = 1;
  const String versionName = "Initial Release";
  String get versionString => "$appVersion (build $buildNumber)";
  ```
- [ ] About page present and accessible from main navigation

### About Page Contents
- [ ] App logo/icon prominently displayed
- [ ] App name as headline
- [ ] Version number from centralized version file (e.g., `version.dart`)
- [ ] Version codename (optional, e.g., "Sunrise")
- [ ] Brief app description/tagline
- [ ] **Links section** with buttons for:
  - [ ] Website: https://qneura.ai/apps.html
  - [ ] GitHub repository (if open source)
  - [ ] Report Issue / Bug tracker
- [ ] License link (in-app License screen or repo license overview)
- [ ] **Credits/Powered By section** listing dependencies with clickable links
- [ ] **Footer** with:
  - [ ] License type (e.g., "Licensed under BSL-1.1")
  - [ ] Binary distribution restriction summary (if applicable)
  - [ ] Copyright: "© [YEAR] Qneura.ai"
  - [ ] Clickable Qneura.ai link to https://qneura.ai

### Legal Pages
- [ ] Privacy Policy page (required by App Store)
- [ ] Terms of Service / EULA page
- [ ] License overview page (source vs binary terms, plain English)
- [ ] Binary distribution license / EULA page (DMG/executable terms)
- [ ] Repo `LICENSE` file present and referenced in README
- [ ] All legal pages accessible from Settings or About
- [ ] Website legal pages exist in `<AppName>WEB`: `index.html`, `license.html`, `privacy.html`, `terms.html`

### License Integration (Source vs Binary)
- [ ] Create `<AppName>CODE/LICENSE` for source code (BSL-style, parameterized)
- [ ] Create `<AppName>CODE/BINARY-LICENSE.txt` (or `EULA-DMG.txt`) for DMG/executable
- [ ] Add `<AppName>CODE/LICENSE.md` (or `<AppName>CODE/docs/licensing.md`) explaining source vs binary terms
- [ ] Update `<AppName>CODE/README.md` License section with links to `LICENSE`, `BINARY-LICENSE.txt`, and `LICENSE.md`
- [ ] UI integration: About footer mentions license + binary restriction; Legal section links to License page
- [ ] Terms of Service includes binary distribution restrictions and link to `BINARY-LICENSE.txt`
- [ ] Bundle both license files into the app (`Contents/Resources/`) and DMG root
- [ ] Website license page `<AppName>WEB/license.html` matches README and repo license files

### Three-Surface License Completeness (Required)
- [ ] Website licenses are written and published in `<AppName>WEB/license.html` (not placeholders) and clearly state source + binary terms.
- [ ] Website hero/meta references to licensing (`Open Source`, `License`) link to `<AppName>WEB/license.html`.
- [ ] Flutter macOS app licenses are written and visible in `<AppName>CODE/flutter_app` legal screens; source + binary terms are discoverable in-app.
- [ ] Flutter macOS app bundle contains written license files: `Contents/Resources/LICENSE` and `Contents/Resources/BINARY-LICENSE.txt`.
- [ ] Git repository licenses are written and versioned in `<AppName>CODE`: `LICENSE` + `BINARY-LICENSE.txt` + license overview doc (`LICENSE.md` or `docs/licensing.md`).
- [ ] `<AppName>CODE/README.md` top section and License section both link to the repo license files and website license page.
- [ ] Release is blocked if any one of the three surfaces (website, app, repo) is missing written license content.

### Cross-Repo Website + README Consistency (Mandatory)
- [ ] Use this canonical sentence (copy exactly, replace app name only):
  - `License: Source code is licensed under Business Source License 1.1 (BSL-1.1), and binary distributions are licensed under the [APP_NAME] Binary Distribution License. See LICENSE, BINARY-LICENSE.txt, and the website License page.`
- [ ] Place the canonical sentence in README near the top, immediately after the primary app-description paragraph.
- [ ] Keep the binary-availability sentence explicit in README and website CTA/meta:
  - `The codebase is cross-platform, but we currently provide macOS binaries only.`
- [ ] Link `we currently provide macOS binaries only.` in README top block to the app website home page.
- [ ] Link `Open Source` labels in website hero/meta rows to `license.html` (not plain text).
- [ ] In website hero badges/benefits, remove `Lifetime Updates` and avoid reintroducing it in future copy revisions.
- [ ] Add a primary `Download for macOS` CTA on the left hero column before `Get Started` / `View on GitHub` style links.
- [ ] Primary nav `Download` and hero `Download` CTA both point directly to the current DMG asset URL for the latest tag.
- [ ] Ensure each macOS app site under `<AppName>WEB` uses the same wording pattern (only app name varies).
- [ ] Verify `<AppName>CODE/LICENSE`, `<AppName>CODE/BINARY-LICENSE.txt`, and `<AppName>WEB/license.html` all exist and are mutually consistent.
- [ ] In multi-repo updates, stage and commit only intended files from `<AppName>CODE` and `<AppName>WEB` when worktrees are already dirty.
- [ ] Keep `README.md` and website `Supported Models` tables fully synchronized with the app's runtime model registry (include aliases, quantized variants, and namespace-specific surfaces like CosyVoice/Supertonic when present).
- [ ] Keep README pregenerated-example index synchronized with shipped files in `backend/data/pregenerated` (no missing demos).
- [ ] If DMG is unsigned/not notarized,

…(truncated)
