Elnor Repo Reader

Q_PLUGIN_SYSTEM_ARCHITECTURE_R2.md

Current Specs/Connector and Integration Specs/Q_PLUGIN_SYSTEM_ARCHITECTURE_R2.md

Short text page b619a7082db5. Generated 2026-06-09T01:23:58.539Z from commit dbaa25962edc11ab30e8d4ca1715f9ae5bf77331. Worktree: clean.

Open readable HTML page · Open raw txt · Open path URL

ELNOR REPO READER TEXT MIRROR
Original path: Current Specs/Connector and Integration Specs/Q_PLUGIN_SYSTEM_ARCHITECTURE_R2.md
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z

---

# Q Dashboard Plugin System Architecture R2

**Date:** 2026-04-11
**Version:** R2 — consolidated from R1.1 + Addendum A. Full plugin framework with learning, memory, error handling, self-improvement, DOC72 entity graph integration, and DOC24 tool capability alignment.
**Status:** Architecture specification
**Scope:** Extensible plugin framework for Q Dashboard
**Owns:** Plugin manifest schema, plugin lifecycle, tab registration, [+] menu integration, plugin settings surface, plugin scaffold template, Elnor plugin builder workflow, plugin DOC72 knowledge layer, execution telemetry, error learning, self-improvement proposals
**Related:** DOC3 (skill/connector registry), DOC24 (capability registry), DOC23 (task system), DOC20 (tab system)

---

## 1. Design Philosophy

### 1.1 What a Plugin Is

A plugin is a self-contained extension package that adds capabilities to Q and ELNOR. A plugin can be as simple as a single tool or as complex as a full application with its own UI, multiple API connections, background services, and AI-driven automation.

The plugin system is designed with one critical requirement: **Elnor must be able to build new plugins from natural language instructions.** The manifest schema and scaffold template are the contract that makes this possible. If the contract is well-defined, a coding agent can produce conforming plugins from a conversation like "Build me a plugin that monitors Westlaw alerts and downloads new cases."

### 1.2 Plugin Complexity Spectrum

| Complexity | Example | Connectors | Service | Tab | Agent Instructions |
|-----------|---------|-----------|---------|-----|-------------------|
| **Minimal** | Hue lighting | 1 (local bridge) | ❌ | ❌ | ❌ |
| **Tool-only** | Screenshot/OCR | 0 (native APIs) | ❌ | ❌ | ❌ |
| **Light** | Apple Reminders | 0 (AppleScript) | ❌ | ❌ | ✅ (push deadlines) |
| **Medium** | Spotify | 1 (Spotify API) | ✅ Weekly intake | ❌ | ✅ (music preferences) |
| **Medium** | YouTube | 1 (YouTube API) | ❌ | ❌ | ✅ (transcript processing) |
| **Full** | PACER | 2 (PACER + CourtListener) | ✅ Case monitor | ✅ Dashboard | ✅ (per-case rules) |
| **Full** | Pipedrive | 1 (Pipedrive API) | ✅ CRM sync | ✅ Pipeline view | ✅ (lead processing) |
| **Complex** | Custom research tool | 3+ (multiple APIs) | ✅ Multiple services | ✅ | ✅ |

### 1.3 Key Principles

1. **Programmatic services are free.** No LLM tokens for scheduled checks, API calls, file downloads, data sync. AI enters only when the user configures agent instructions.
2. **Connectors are referenced, not embedded.** A plugin declares which connectors it needs. The connector handles auth. The plugin handles logic.
3. **Everything is optional.** Tab, service, agent instructions, connectors — a plugin only declares what it needs.
4. **Plugins are portable.** A plugin directory is self-contained. Copy it to another Q installation, configure credentials, done.
5. **Elnor can build them.** The manifest + template + contract is the spec a coding agent follows.

---

## 2. Plugin Manifest Schema (Comprehensive)

```typescript
interface PluginManifest {
  // ═══════════════════════════════════════════════
  // IDENTITY
  // ═══════════════════════════════════════════════
  plugin_id: string;                 // Unique ID: "pacer", "spotify", "westlaw-monitor"
  display_name: string;              // Human-readable: "PACER Federal Courts"
  description: string;               // Short description for plugin directory
  long_description?: string;         // Detailed description for plugin detail view
  version: string;                   // Semver: "1.0.0"
  author: string;                    // "ELNOR System", "Will Schall", or third-party
  icon: string;                      // Lucide icon name or path to custom SVG
  category: PluginCategory;
  tags?: string[];                   // Searchable tags: ["legal", "court", "monitoring"]
  
  // ═══════════════════════════════════════════════
  // DATA SOURCES — what external things does this plugin talk to?
  // ═══════════════════════════════════════════════
  connectors?: PluginConnectorRef[]; // External API connections (can be multiple)
  local_apps?: LocalAppRef[];        // Desktop apps this plugin interacts with
  web_access?: WebAccessConfig;      // Does this plugin need to access the web?
  filesystem_access?: FilesystemAccessConfig; // Does this plugin read/write files?
  
  // ═══════════════════════════════════════════════
  // CAPABILITIES — what can this plugin do?
  // ═══════════════════════════════════════════════
  tools: PluginToolDefinition[];     // Tools Elnor can call (always required, at least 1)
  
  // ═══════════════════════════════════════════════
  // SERVICES — background programmatic processes (no LLM)
  // ═══════════════════════════════════════════════
  services?: PluginServiceDefinition[];  // Can have multiple services
  
  // ═══════════════════════════════════════════════
  // PROCESSES — external programs this plugin runs
  // ═══════════════════════════════════════════════
  processes?: ExternalProcessDefinition[]; // Separate programs managed by the plugin
  
  // ═══════════════════════════════════════════════
  // AGENT INTELLIGENCE — how does Elnor use this plugin smartly?
  // ═══════════════════════════════════════════════
  agent_instructions?: AgentInstructionTemplate[]; // Default/configurable automation rules
  doc24_triggers: string[];          // Semantic triggers for DOC24 routing (required)
  elnor_awareness_prompt?: string;   // Natural language description for Elnor's system context
  
  // ═══════════════════════════════════════════════
  // KNOWLEDGE — what does this plugin remember?
  // ═══════════════════════════════════════════════
  doc72_config?: Doc72Config;        // Knowledge graph integration
  
  // ═══════════════════════════════════════════════
  // UI — what does this plugin show?
  // ═══════════════════════════════════════════════
  tab?: PluginTabDefinition;         // Optional custom tab in workspace
  config_ui?: PluginConfigUI;        // Configuration panel (always has one, but can be auto-generated)
  
  // ═══════════════════════════════════════════════
  // OUTPUTS — what does this plugin produce?
  // ═══════════════════════════════════════════════
  outputs?: PluginOutputConfig;      // Notifications, calendar events, tasks, files, etc.
  
  // ═══════════════════════════════════════════════
  // DEPENDENCIES & REQUIREMENTS
  // ═══════════════════════════════════════════════
  requires_internet: boolean;
  requires_plugins?: string[];       // Other plugins this depends on
  platform?: ("macos" | "windows" | "linux")[]; // Platform restrictions
  min_q_version?: string;           // Minimum Q Dashboard version
  
  // ═══════════════════════════════════════════════
  // LIFECYCLE
  // ═══════════════════════════════════════════════
  lifecycle?: PluginLifecycleConfig;
  
  // ═══════════════════════════════════════════════
  // INTELLIGENCE — how does the plugin learn and improve?
  // ═══════════════════════════════════════════════
  intelligence?: {
    // Telemetry
    telemetry_level: "full" | "summary" | "errors_only" | "none";
    
    // Error handling
    error_handling: {
      auto_retry: boolean;
      max_retries: number;
      retry_delay_seconds: number;
      escalate_after_failures: number;    // Alert user after N consecutive failures
      self_healing_enabled: boolean;      // Can Elnor attempt to fix errors autonomously?
    };
    
    // Learning
    learning: {
      track_usage_patterns: boolean;      // Build tool preference model
      track_effectiveness: boolean;       // Track whether results are acted on
      participate_in_dream_cycle: boolean;
      generate_improvement_proposals: boolean;
    };
    
    // Self-improvement boundaries
    self_improvement: {
      allowed_changes: (
        | "config_values"               // Can adjust config (e.g., timeout, retry count)
        | "agent_instructions"          // Can modify/add agent instructions
        | "service_schedule"            // Can adjust service frequency
        | "tool_parameters"             // Can adjust default parameters
        | "code"                        // Can modify plugin code (requires coding agent)
      )[];
      requires_approval: boolean;         // Must user approve changes?
      max_auto_changes_per_week?: number; // Limit on autonomous changes
    };
  };
}

// ─────────────────────────────────────────────────
// CATEGORY
// ─────────────────────────────────────────────────
type PluginCategory = 
  | "legal"           // PACER, Westlaw, court systems
  | "productivity"    // CRM, project management, task tools
  | "media"           // Spotify, YouTube, podcast apps
  | "communication"   // Slack, Teams, email
  | "smart_home"      // Hue, HomeKit
  | "finance"         // Billing, time tracking, accounting
  | "development"     // GitHub, CI/CD, code tools
  | "research"        // Academic databases, search engines
  | "system"          // Screenshots, clipboard, OS utilities
  | "custom";         // User-built, doesn't fit a category
```

### 2.1 Connector References

A plugin can reference one or more connectors. Each connector handles its own auth independently.

```typescript
interface PluginConnectorRef {
  connector_id: string;              // Unique ID: "pacer_api", "courtlistener_api"
  display_name: string;              // "PACER API", "CourtListener RECAP"
  required: boolean;                 // Must this connector be configured for the plugin to work?
  
  // Auth configuration
  auth: ConnectorAuth;
  
  // API configuration
  base_url?: string;                 // Default API base URL
  endpoints?: Record<string, string>; // Named endpoints for the service
  
  // Rate limiting
  rate_limit?: {
    requests_per_minute?: number;
    requests_per_day?: number;
    concurrent_max?: number;
  };
  
  // Cost tracking (if the API costs money)
  cost_model?: ConnectorCostModel;
  
  // Tools this connector provides
  tools: string[];                   // Tool IDs from the tools array that use this connector
}

interface ConnectorAuth {
  type: "oauth2" | "api_key" | "credentials" | "local" | "none";
  
  // OAuth2
  oauth2_config?: {
    authorization_url?: string;
    token_url?: string;
    scopes?: string[];
    redirect_uri?: string;
    pkce?: boolean;
  };
  
  // API key
  api_key_config?: {
    header_name?: string;            // "Authorization", "X-API-Key", etc.
    prefix?: string;                 // "Bearer ", "Token ", etc.
  };
  
  // Username/password
  credentials_config?: {
    fields: { name: string; label: string; type: "text" | "password" }[];
  };
  
  // Local (e.g., Hue bridge — discovered on LAN)
  local_config?: {
    discovery_method: "mdns" | "ip_scan" | "manual";
    pairing_method?: "button_press" | "pin" | "none";
  };
}

interface ConnectorCostModel {
  type: "free" | "per_use" | "per_page" | "subscription" | "metered";
  unit_cost?: number;                // Cost per unit in cents
  unit_label?: string;               // "page", "request", "document"
  max_per_item?: number;             // Cost cap per item in cents (e.g., $3 = 300)
  free_threshold?: {
    amount: number;                  // In cents
    period: "monthly" | "quarterly" | "yearly";
  };
  track_costs: boolean;              // Should Q track cumulative costs?
}
```

### 2.2 Local App References

Plugins can interact with desktop applications:

```typescript
interface LocalAppRef {
  app_id: string;                    // "microsoft_word", "apple_reminders", "terminal"
  display_name: string;              // "Microsoft Word"
  
  // How does the plugin interact with this app?
  interaction: "applescript" | "jxa" | "cli" | "shortcuts" | "url_scheme" | "ipc";
  
  // AppleScript / JXA
  applescript_bundle_id?: string;    // "com.microsoft.Word"
  
  // CLI
  cli_command?: string;              // "open", "osascript", custom binary path
  
  // URL scheme
  url_scheme?: string;               // "maps://", "shortcuts://", "x-apple-reminder://"
  
  // Is the app required?
  required: boolean;
  fallback_message?: string;         // "Install Microsoft Word to use this feature"
}
```

### 2.3 Web Access Configuration

```typescript
interface WebAccessConfig {
  enabled: boolean;
  purpose: string;                   // "Scrape court docket pages", "Fetch RSS feeds"
  
  // Allowed domains (security — plugin can only access these)
  allowed_domains?: string[];        // ["pacer.uscourts.gov", "courtlistener.com"]
  
  // Or unrestricted (requires explicit user approval)
  unrestricted?: boolean;
  
  // Methods
  methods: ("fetch" | "scrape" | "browser_automation")[];
  
  // If browser automation (Playwright, Puppeteer)
  browser_config?: {
    headless: boolean;
    persistent_session?: boolean;    // Keep cookies/session across runs
  };
}
```

### 2.4 Filesystem Access Configuration

```typescript
interface FilesystemAccessConfig {
  read_paths?: string[];             // Paths the plugin can read from
  write_paths?: string[];            // Paths the plugin can write to
  watch_paths?: string[];            // Paths the plugin watches for changes
  
  // Path variables (resolved at runtime)
  // Available: {home}, {documents}, {desktop}, {onedrive_firm}, {onedrive_personal},
  //            {elnor_memory}, {plugin_data}, {project_root}
  
  // Examples:
  // "{onedrive_firm}/Cases/{case_name}/PACER Docs" 
  // "{elnor_memory}/plugins/pacer/cache"
  // "{home}/Desktop"
}
```

### 2.5 Tool Definitions

```typescript
interface PluginToolDefinition {
  tool_id: string;                   // "pacer_search_cases"
  display_name: string;              // "Search PACER Cases"
  description: string;               // For LLM tool discovery and DOC24 routing
  
  // Which connector provides this tool (if any)?
  connector_ref?: string;            // "pacer_api" — matches a connector_id
  
  // Parameters
  parameters: Record<string, ToolParameter>;
  
  // Return type
  returns: {
    type: string;                    // "json", "file", "text", "void"
    schema?: Record<string, any>;    // JSON schema of return value
  };
  
  // Execution
  requires_ai: boolean;             // Does this tool need an LLM to execute?
  execution_type: "sync" | "async"; // Sync returns immediately, async returns a job ID
  timeout_seconds?: number;         // Max execution time
  
  // Cost (if the tool costs money)
  cost?: {
    model: "free" | "per_use" | "per_page" | "variable";
    estimate?: string;               // "$0.10/page, $3 max" — displayed to user
    track: boolean;                  // Add to cost tracker
  };
  
  // Safety
  destructive: boolean;             // Does this tool modify/delete external data?
  requires_confirmation?: boolean;  // Prompt user before executing?
  confirmation_message?: string;    // "This will download from PACER ($0.10/page). Proceed?"
}

interface ToolParameter {
  type: "string" | "number" | "boolean" | "enum" | "date" | "file_path" | "array";
  description: string;
  required: boolean;
  default?: any;
  enum_values?: string[];            // For type: "enum"
  validation?: string;               // Regex or validation rule
}
```

### 2.6 Service Definitions

```typescript
interface PluginServiceDefinition {
  service_id: string;                // "pacer_case_monitor"
  display_name: string;              // "PACER Case Monitor"
  description: string;
  
  // Is this a programmatic service or does it need AI?
  requires_ai: false;                // Services are ALWAYS programmatic. 
                                     // AI is triggered via agent_instructions, not services.
  
  // Entry point
  entry_point: string;               // "services/PacerMonitorService.ts" — relative to plugin dir
  
  // Schedule (user-configurable, these are defaults)
  schedule: {
    type: "cron" | "interval" | "event_driven" | "manual_only";
    default_cron?: string;           // "0 7,12,17 * * 1-5"
    default_interval_minutes?: number;
    event_trigger?: string;          // "file_changed", "email_received", etc.
    user_configurable: boolean;      // Can user change the schedule?
    min_interval_minutes?: number;   // Minimum allowed interval (prevent abuse)
  };
  
  // Resource management
  max_runtime_seconds?: number;      // Kill if exceeds this
  max_memory_mb?: number;            // Memory limit
  retry_on_failure: boolean;
  max_retries?: number;
  retry_delay_seconds?: number;
  
  // What the service produces
  output_events: ServiceOutputEvent[];
  
  // Health monitoring
  health_check?: {
    endpoint?: string;               // Health check URL for HTTP-based services
    interval_seconds: number;
    failure_threshold: number;       // How many failures before marking unhealthy
  };
}

interface ServiceOutputEvent {
  event_type: string;                // "new_document_downloaded", "sync_complete", "error"
  description: string;
  payload_schema?: Record<string, any>; // What data the event carries
  can_trigger_agent: boolean;        // Can this event trigger agent instructions?
}
```

### 2.7 External Process Definitions

Some plugins need to run entirely separate programs (like OnlyOffice Document Server):

```typescript
interface ExternalProcessDefinition {
  process_id: string;                // "onlyoffice_document_server"
  display_name: string;              // "OnlyOffice Document Server"
  description: string;
  
  // How to start
  command: string;                   // "node", "python3", binary path
  args: string[];                    // ["server.js", "--port", "8443"]
  cwd?: string;                     // Working directory (relative to plugin dir)
  env?: Record<string, string>;     // Environment variables
  
  // Lifecycle
  start_with_plugin: boolean;       // Auto-start when plugin is enabled
  start_on_demand?: boolean;        // Start only when first needed
  restart_on_crash: boolean;
  max_restart_attempts?: number;
  
  // Resource management
  port?: number;                    // Port the process listens on
  port_range?: [number, number];    // Try ports in this range if default is taken
  health_check_url?: string;        // "http://localhost:8443/health"
  startup_timeout_seconds?: number; // How long to wait for process to be ready
  
  // Bundling
  bundled: boolean;                 // Is the binary/package included in the plugin?
  install_command?: string;         // "npm install" or "pip install -r requirements.txt"
  size_estimate_mb?: number;        // For user awareness
}
```

### 2.8 Agent Instructions

```typescript
interface AgentInstructionTemplate {
  instruction_id: string;            // "summarize_new_filing"
  display_name: string;              // "Summarize New Filings"
  description: string;               // Shown to user in config UI
  
  // Trigger
  trigger: {
    type: "service_event" | "schedule" | "manual" | "file_change" | "tool_result";
    event_type?: string;             // Matches ServiceOutputEvent.event_type
    schedule_cron?: string;          // For schedule-type triggers
    file_pattern?: string;           // Glob pattern for file_change triggers
    tool_id?: string;                // For tool_result triggers
  };
  
  // Condition (optional — filter which events trigger the instruction)
  condition?: {
    expression: string;              // User-editable condition string
    description: string;             // "Only trigger for motions, orders, and opinions"
    examples: string[];              // Example conditions for user guidance
  };
  
  // What Elnor does
  instruction: string;               // Natural language instruction
  instruction_editable: boolean;     // Can the user modify the instruction?
  
  // Execution
  execution: {
    method: "direct_agent" | "doc23_task";
    task_template?: string;          // DOC23 task template ID if method is doc23_task
    model_preference?: string;       // Preferred model for this instruction
    think_level?: string;            // Preferred think level
    max_tokens?: number;             // Token budget
    timeout_seconds?: number;
  };
  
  // Output routing
  output_routing?: {
    save_to_doc72: boolean;          // Store result in knowledge graph
    notify_user: boolean;            // Toast/notice on completion
    save_to_file?: string;           // File path template to save output
    add_to_calendar?: boolean;       // If deadlines are extracted
    push_to_reminders?: boolean;     // Push to Apple Reminders
    create_task?: boolean;           // Create DOC23 task
  };
  
  // User can configure
  enabled_by_default: boolean;
  scope_configurable: boolean;       // Can user set per-item (e.g., per case)?
}
```

### 2.9 DOC72 Knowledge Configuration

```typescript
interface Doc72Config {
  domains: string[];                 // ["litigation", "legal"] — domain tags for DOC72 nodes
  
  // Entity schemas this plugin writes
  entity_schemas: {
    schema_id: string;               // "federal_case", "judge", "expert_witness"
    node_type: string;               // DOC72 canonical type: "entity", "preference", "observation"
    subtype: string;                 // "federal_case", "music_artist", etc.
    fields: Record<string, {
      type: string;
      description: string;
      required: boolean;
    }>;
    dedup_key?: string;              // Field used for deduplication: "case_number", "spotify_uri"
  }[];
  
  // Retention
  retention?: {
    auto_decay: boolean;             // Decay old data (reduce confidence over time)
    decay_period_days?: number;      // After how many days of inactivity
    max_nodes?: number;              // Hard limit on nodes this plugin can create
  };
}
```

### 2.10 Tab Definition

```typescript
interface PluginTabDefinition {
  component_path: string;            // "tabs/PacerTab.tsx" — relative to plugin dir
  label: string;                     // "PACER Monitor" — shown in [+] submenu
  icon: string;                      // Lucide icon name
  
  // Tab behavior
  singleton: boolean;                // Only one instance allowed? (e.g., PACER dashboard = true)
  allow_multiple?: boolean;          // Can open multiple tabs? (e.g., different case views)
  
  // Props the tab component receives (see PluginTabProps in §4)
  receives_context: boolean;         // Does the tab need current workspace context?
}
```

### 2.11 Output Configuration

```typescript
interface PluginOutputConfig {
  // Notification channels
  notifications?: {
    toast: boolean;                  // Q toast notifications
    notices_inbox: boolean;          // Floating Palette notices
    morning_briefing: boolean;       // Include in daily briefing
    email?: boolean;                 // Send via DOC16 email
    apple_reminders?: boolean;       // Push to Apple Reminders plugin
  };
  
  // Data outputs
  files?: {
    output_directory: string;        // Default output path (with variables)
    naming_template?: string;        // "doc_{docket_number}_{title}.pdf"
    organize_by?: string;            // "case", "date", "type"
  };
  
  // Calendar integration
  calendar?: {
    create_events: boolean;
    default_reminders?: number[];    // Days before: [14, 7, 1]
    calendar_target?: string;        // "q_calendar" | "m365" | "both"
  };
  
  // Task creation
  tasks?: {
    create_tasks: boolean;
    task_template?: string;          // DOC23 task template
    default_priority?: string;
  };
}
```

### 2.12 Lifecycle Configuration

```typescript
interface PluginLifecycleConfig {
  on_install?: {
    script?: string;                 // Script path to run
    install_dependencies?: string;   // "npm install" or "pip install -r requirements.txt"
    setup_prompts?: {                // Questions to ask user during install
      prompt_id: string;
      question: string;
      type: "text" | "password" | "select" | "confirm";
      options?: string[];            // For select type
    }[];
  };
  
  on_enable?: {
    script?: string;
    start_services?: boolean;        // Auto-start services
    register_tools?: boolean;        // Register tools with DOC3/DOC24
  };
  
  on_disable?: {
    script?: string;
    stop_services?: boolean;
    deregister_tools?: boolean;
    preserve_data?: boolean;         // Keep DOC72 data and config
  };
  
  on_uninstall?: {
    script?: string;
    cleanup_doc72?: boolean;         // Remove DOC72 nodes (with confirmation)
    cleanup_files?: boolean;         // Remove downloaded files
    cleanup_config?: boolean;        // Remove saved credentials
  };
  
  on_update?: {
    migration_script?: string;       // Run when plugin is updated to new version
  };
}
```

---

## 3. Plugin Directory Structure

```
plugins/
├── pacer/
│   ├── manifest.json                ← Plugin manifest (schema above)
│   ├── README.md                    ← Human-readable description
│   ├── CHANGELOG.md                 ← Version history
│   │
│   ├── tools/                       ← Tool implementations
│   │   ├── pacer_search.ts
│   │   ├── pacer_docket.ts
│   │   ├── pacer_download.ts
│   │   ├── recap_search.ts
│   │   └── index.ts                 ← Exports all tools
│   │
│   ├── services/                    ← Programmatic services (no LLM)
│   │   ├── PacerMonitorService.ts
│   │   └── CmEcfEmailCapture.ts
│   │
│   ├── tabs/                        ← Custom tab components
│   │   ├── PacerTab.tsx
│   │   ├── CaseDetailPanel.tsx
│   │   ├── SearchPanel.tsx
│   │   └── CostDashboard.tsx
│   │
│   ├── config/                      ← Configuration UI components
│   │   └── PacerConfig.tsx
│   │
│   ├── types/                       ← TypeScript interfaces
│   │   └── index.ts
│   │
│   ├── data/                        ← Mock data for development
│   │   └── mockCases.ts
│   │
│   └── assets/                      ← Icons, images
│       └── pacer-icon.svg
```

---

## 4. Plugin Tab Props Interface

Every plugin tab component receives a standard props interface:

```typescript
interface PluginTabProps {
  // Identity
  pluginId: string;
  pluginConfig: Record<string, any>;
  
  // Data access
  dataProvider: DataProvider;         // Access to EC data (or mock data)
  doc72: Doc72Client;                // Read/write knowledge graph
  
  // Actions
  commands: CommandDispatcher;        // Emit EC commands
  openTab: (type: string, title: string, meta?: any) => void;
  openDocument: (path: string) => void;
  showToast: (message: string, options?: ToastOptions) => void;
  showConfirmDialog: (message: string, buttons: string[]) => Promise<string>;
  
  // Plugin service status
  serviceStatus: Record<string, ServiceStatus>;
  
  // Agent interaction
  askAgent: (instruction: string, context?: any) => Promise<string>;
  
  // Cross-plugin
  callPluginTool: (pluginId: string, toolId: string, params: any) => Promise<any>;
}

interface ServiceStatus {
  state: "running" | "stopped" | "error" | "starting";
  last_run?: string;
  next_run?: string;
  last_error?: string;
  stats?: Record<string, any>;
}
```

---

## 5. Elnor-Buildable Plugins

### 5.1 The Core Idea

The plugin manifest schema IS the spec that a coding agent follows to create new plugins. If Elnor (via Claude Code, Codex, or any coding agent) knows the manifest schema and the scaffold template, it can build a conforming plugin from natural language instructions.

"Elnor, build me a plugin that monitors my firm's Westlaw alerts and downloads new cases."

Elnor:
1. Creates a new plugin directory from the scaffold template
2. Defines the manifest (connectors, tools, services, agent instructions)
3. Implements the tools and service
4. Optionally builds a tab component
5. Registers everything
6. Tests the plugin
7. Installs it in Q

### 5.2 Plugin Scaffold Template

The scaffold template lives at a known location. Elnor (or any coding agent) copies it to start a new plugin:

```
plugins/_template/
├── manifest.json.template           ← Manifest with placeholders
├── README.md.template
├── tools/
│   └── example_tool.ts.template     ← Example tool implementation
├── services/
│   └── example_service.ts.template  ← Example service implementation
├── tabs/
│   └── ExampleTab.tsx.template      ← Example tab component
├── config/
│   └── ExampleConfig.tsx.template   ← Example config panel
├── types/
│   └── index.ts.template
└── BUILDING_A_PLUGIN.md             ← Documentation for the coding agent
```

### 5.3 BUILDING_A_PLUGIN.md

This document is what the coding agent reads. It explains:

```markdown
# Building a Q Plugin

## Overview
A Q plugin is a directory containing a manifest.json and implementation files.
The manifest declares what the plugin does. The files implement it.

## Step-by-Step

### 1. Copy the template
Copy `plugins/_template/` to `plugins/your-plugin-id/`

### 2. Define the manifest
Fill in manifest.json with:
- Identity (id, name, description, icon, category)
- Connectors (what APIs does this talk to? what auth?)
- Tools (what can Elnor do with this plugin?)
- Services (any background monitoring/sync? what schedule?)
- Agent instructions (what should Elnor do when events happen?)
- Tab (does this need a dashboard UI?)
- DOC72 config (what should be remembered in the knowledge graph?)
- Outputs (notifications, files, calendar events, tasks?)
- DOC24 triggers (what phrases should route to this plugin?)

### 3. Implement tools
Each tool is a function that takes parameters and returns a result.
Tools can call connector APIs, run local commands, read files, etc.

### 4. Implement services (if any)
Services are programmatic functions that run on a schedule.
NO LLM calls in services. Services check APIs, download files, compare data.
Services produce events. Events can trigger agent instructions (which DO use LLM).

### 5. Implement tab (if any)
Tab is a React component that receives PluginTabProps.
Use the standard Q design tokens and components.
The tab renders inside the workspace tab frame.

### 6. Test
Run the plugin locally. Verify tools work, service runs, tab renders.

### 7. Register
Place in plugins/ directory. Q auto-discovers on next launch.

## Contract Rules
- Tools MUST have descriptive names and descriptions (Elnor uses these for routing)
- Services MUST NOT call LLMs — they are programmatic only
- Agent instructions MUST have clear trigger conditions
- Connectors MUST handle auth token refresh
- Tab components MUST use PluginTabProps interface
- File writes MUST use the configured output directory
- All external API calls MUST go through the connector's rate limiter
- DOC72 writes MUST include principal_id and scope
- Cost-incurring operations MUST be tracked and surfaced to the user
```

### 5.4 Elnor's Plugin Builder Awareness

Add to Elnor's system context:

```
You can BUILD new plugins for Q Dashboard. When Will asks you to create a new 
integration, capability, or monitoring system, consider whether it should be a plugin.

A plugin should be built when:
- The capability involves an external API or service
- It benefits from scheduled background processing
- It would benefit from a dedicated management UI (tab)
- It involves recurring automation with configurable rules
- Will says "build me a plugin" or "I want to add [capability] to Q"

To build a plugin:
1. Read the plugin scaffold template at plugins/_template/BUILDING_A_PLUGIN.md
2. Copy the template to plugins/{new_plugin_id}/
3. Define the manifest.json with all required sections
4. Implement tools, services, tabs as needed
5. Register DOC24 semantic triggers
6. Test everything
7. Install by placing in the plugins/ directory

The manifest schema is the contract. Follow it exactly. Every field has a purpose.
If you're unsure about a field, read the schema comments in the manifest.json.template.

You can also MODIFY existing plugins — add new tools, change service schedules,
update agent instructions, expand the tab UI. Treat plugin code like any other
code in the Q Dashboard project.
```

### 5.5 Example: Elnor Builds a Westlaw Plugin

User: "Elnor, I want a plugin that monitors my Westlaw alerts. When new cases matching my search criteria appear, download them and summarize them."

Elnor (via coding agent):
1. Creates `plugins/westlaw-alerts/`
2. Manifest:
   - Connector: Westlaw API (OAuth2, requires Westlaw credentials)
   - Tools: `westlaw_search`, `westlaw_get_document`, `westlaw_list_alerts`, `westlaw_get_alert_results`
   - Service: `WestlawAlertMonitor` — checks alerts daily at 7 AM
   - Agent instruction: "When new alert results found, download documents and summarize holdings"
   - Tab: `WestlawTab` — shows configured alerts, recent results, downloaded documents
   - DOC72: `legal_opinion` entities with holdings, citations, court, date
   - DOC24 triggers: "Westlaw", "case law", "legal research", "case alert"
3. Implements each piece
4. Installs and tests

Total time: ~2-4 hours for the coding agent. Will reviews and customizes.

---

## 6. [+] Menu Integration

```
┌──────────────────────────┐
│ CREATE                   │
│   📝 New Note            │
│   ☐ New To-Do List       │
│   💬 New Chat            │
│   🌐 New Web Browser     │
│   🔒 New Incognito Web   │
│   📁 New Tab Group       │
├──────────────────────────┤
│ OPEN                     │
│   🏠 Home                │
│   📊 Tasks               │
│   📅 Calendar            │
│   ⚙ Settings             │
│   ...                    │
├──────────────────────────┤
│ PLUGINS                ▸ │
└──────────────────────────┘

    ┌──────────────────────┐
    │  ⚖ PACER Monitor     │  ← Only plugins with tab defined
    │  📊 Pipedrive         │
    │  📋 Westlaw Alerts    │
    │  ─────────────────── │
    │  ⚙ Manage Plugins    │  ← Opens Settings > Plugins
    └──────────────────────┘
```

Only plugins with `tab` defined appear in the PLUGINS submenu. Plugins without tabs (Hue, Apple Reminders) are tools only — configured in Settings, used via chat.

---

## 7. Connector ↔ Plugin Relationship in Skills Page

### 7.1 Connectors Tab Update

The existing Connectors & Accounts tab in the Skills page shows all connectors. Connectors that belong to a plugin show a badge:

```
┌──────────────────────────────────────────────────────────┐
│ ⚖ PACER API                                  ● Active   │
│ wbrody · 10 tools · Part of PACER plugin                 │
│ [Configure via Plugin]                                    │
├──────────────────────────────────────────────────────────┤
│ 📚 CourtListener RECAP                       ● Active   │
│ API token · 4 tools · Part of PACER plugin               │
│ [Configure via Plugin]                                    │
├──────────────────────────────────────────────────────────┤
│ ✉ Gmail                                      ● Active   │
│ will@schallfirm.com · 8 tools · Standalone connector     │
│ [Configure]                                               │
├──────────────────────────────────────────────────────────┤
│ ◆ Microsoft 365                               ● Active   │
│ will@schallfirm.com · 14 tools · Standalone connector    │
│ [Configure]                                               │
└──────────────────────────────────────────────────────────┘
```

- "Part of PACER plugin" — clicking Configure opens the PACER plugin config, not a standalone connector config
- "Standalone connector" — not managed by any plugin, configured independently

### 7.2 My Capabilities Tab Update

Tools from plugins show the `Plugin` badge (already in mockup's `TY` object):

```
┌──────────────────────────────────────────────────────────┐
│ pacer_search_cases                            ● Active   │
│ [Plugin] Search PACER by party, case number, court       │
│ Cost: $0.10/page · Uses: 34 · Last: 4h ago               │
└──────────────────────────────────────────────────────────┘
```

---

## 8. Plugin Settings Page

```
Settings > Plugins & Extensions

INSTALLED
┌──────────────────────────────────────────────────────────┐
│ ⚖ PACER Federal Courts                      ● Active    │
│ Federal court records, docket monitoring, case research  │
│ Connectors: PACER API (●), CourtListener (●)             │
│ Service: Running — last check 2h ago, next in 4h         │
│ Tab: PACER Monitor · Cases: 20 · Cost: $14.30/qtr       │
│ [Configure]  [View Tab]  [Disable]                       │
├──────────────────────────────────────────────────────────┤
│ 🎵 Spotify                                   ● Active    │
│ Connectors: Spotify API (●)                               │
│ Service: Weekly intake — last: Sun 3:01 AM                │
│ [Configure]  [Disable]                                    │
├──────────────────────────────────────────────────────────┤
│ 💡 Philips Hue                               ○ Disabled  │
│ Connectors: Hue Bridge (○ disconnected)                   │
│ [Enable]  [Uninstall]                                     │
└──────────────────────────────────────────────────────────┘

BUILD NEW
┌──────────────────────────────────────────────────────────┐
│ 🔧 Ask Elnor to build a plugin                           │
│ Describe what you want and Elnor will create it.          │
│ [Start Building...]                                       │
│                                                           │
│ Or load from folder: [Choose Folder...]                   │
└──────────────────────────────────────────────────────────┘

AVAILABLE
┌──────────────────────────────────────────────────────────┐
│ Browse community plugins on ClawHub...                    │
└──────────────────────────────────────────────────────────┘
```

---


---

## 9. Plugin Entity Nodes — Base Knowledge Layer

### 9.1 The Missing Layer

The telemetry, error, and build record layers all describe what a plugin *does*. But DOC72 also needs a fundamental entity node for each plugin that describes what it *is*. This is the base layer — without it, Elnor can't reason about plugins as first-class things in its knowledge graph.

**No new canonical node types.** Everything in this section uses DOC72's existing canonical node types. Plugins are `type: "entity", subtype: "plugin"`. Connectors are `type: "entity", subtype: "connector"`. Build records are `type: "procedure"`. Telemetry is `type: "observation"`. Errors are `type: "observation"`. Proposals are `type: "fact"`. The `tool_capability` node type already exists per the DOC24 Tool Capability Nodes Amendment — plugin tools use that same type. No 12th canonical type is introduced.

**This node is auto-created for EVERY installed plugin, regardless of how it was installed:**

| Installation Method | Entity Created By |
|--------------------|------------------|
| Elnor builds it via coding agent | Elnor creates entity + build record |
| User installs from ClawHub | Auto-created by plugin registration system on install |
| Claude Code drops it in the plugins folder | Auto-created on next Q launch when plugin is discovered |
| User loads from folder via Settings | Auto-created when user clicks [Load from folder] |
| Pre-bundled with Q | Auto-created on first launch |

The trigger is simple: **whenever the plugin system detects a new manifest.json in the plugins directory, it reads the manifest and creates (or updates) a DOC72 plugin entity node.**

### 9.2 Plugin Entity Schema

```typescript
interface PluginEntityNode {
  node_id: string;                    // "plugin_entity_pacer"
  type: "entity";
  subtype: "plugin";
  domain: "plugin_registry";
  
  // ═══ Identity (from manifest) ═══
  plugin_id: string;                  // "pacer"
  display_name: string;               // "PACER Federal Courts"
  description: string;                // Full description
  version: string;                    // "1.0.0"
  author: string;                     // "ELNOR System"
  category: string;                   // "legal"
  icon: string;                       // "Scale"
  
  // ═══ What it can do (capability summary) ═══
  capabilities_summary: string;       // Natural language: "Searches federal court records,
                                      //   monitors cases for new filings, downloads documents,
                                      //   tracks expert witnesses and judges, extracts deadlines."
  tool_count: number;                 // 10
  tool_names: string[];               // ["pacer_search_cases", "pacer_get_docket", ...]
  has_service: boolean;               // true
  service_description?: string;       // "Monitors 20 cases on configurable schedule"
  has_tab: boolean;                   // true
  tab_description?: string;           // "Case monitoring dashboard with docket browser"
  has_agent_instructions: boolean;    // true
  
  // ═══ What it connects to ═══
  connector_names: string[];          // ["PACER API", "CourtListener RECAP"]
  connector_count: number;            // 2
  local_apps?: string[];              // ["Microsoft Word"] — if it interacts with desktop apps
  web_domains?: string[];             // ["pacer.uscourts.gov", "courtlistener.com"]
  
  // ═══ How it was installed ═══
  install_method: "elnor_built" | "clawhub" | "manual_folder" | "pre_bundled" | "coding_agent";
  installed_at: string;               // ISO timestamp
  installed_by: string;               // "elnor", "will", "system"
  source_url?: string;                // ClawHub URL or GitHub repo if applicable
  
  // ═══ Current state ═══
  state: "active" | "disabled" | "error" | "updating";
  last_state_change: string;
  
  // ═══ Relationship refs ═══
  build_record_ref?: string;          // DOC72 node_id of build record (if Elnor-built)
  connector_entity_refs?: string[];   // DOC72 node_ids of connector entities
  related_domain_refs?: string[];     // DOC72 node_ids of related domains
                                      // e.g., PACER plugin → linked to case entities
  
  // ═══ Usage summary (updated by dream cycle) ═══
  total_tool_calls: number;           // Lifetime
  total_cost_cents: number;           // Lifetime
  last_used: string;                  // ISO timestamp
  avg_daily_calls?: number;           // Rolling 30-day average
  error_rate?: number;                // Rolling 30-day error rate (0-1)
  
  // ═══ User notes ═══
  user_notes?: string;                // Free-text notes the user has added about this plugin
  
  // ═══ Standard DOC72 fields ═══
  principal_id: "will";
  scope: "personal";
  created_at: string;
  updated_at: string;
  schema_version: 1;
}
```

### 9.3 Auto-Registration Flow

```typescript
// Runs on Q launch and when plugins directory changes

async function syncPluginEntities(): Promise<void> {
  const installedPlugins = await discoverPlugins("plugins/");
  const existingEntities = await doc72.query({ 
    type: "entity", subtype: "plugin", domain: "plugin_registry" 
  });
  
  for (const manifest of installedPlugins) {
    const existing = existingEntities.find(e => e.plugin_id === manifest.plugin_id);
    
    if (!existing) {
      // ═══ 1. Create plugin entity node ═══
      const pluginNodeId = `plugin_entity_${manifest.plugin_id}`;
      await doc72.write({
        node_id: pluginNodeId,
        type: "entity",
        subtype: "plugin",
        domain: "plugin_registry",
        plugin_id: manifest.plugin_id,
        display_name: manifest.display_name,
        description: manifest.description,
        version: manifest.version,
        author: manifest.author,
        category: manifest.category,
        icon: manifest.icon,
        capabilities_summary: generateCapabilitySummary(manifest),
        tool_count: manifest.tools.length,
        tool_names: manifest.tools.map(t => t.tool_id),
        has_service: !!manifest.services?.length,
        has_tab: !!manifest.tab,
        has_agent_instructions: !!manifest.agent_instructions?.length,
        connector_names: manifest.connectors?.map(c => c.display_name) || [],
        connector_count: manifest.connectors?.length || 0,
        install_method: detectInstallMethod(manifest),
        installed_at: new Date().toISOString(),
        installed_by: "system",
        state: "active",
        last_state_change: new Date().toISOString(),
        total_tool_calls: 0,
        total_cost_cents: 0,
        last_used: "never",
        principal_id: "will",
        scope: "personal",
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
        schema_version: 1,
      });
      
      // ═══ 2. Create tool_capability nodes (per DOC24 amendment) ═══
      // Each tool in the manifest gets a tool_capability node in DOC72.
      // This is the SAME node type defined in the DOC24 Tool Capability 
      // Nodes Amendment — not a new type. Plugin tools follow the same
      // pattern as MCP tools, OpenClaw wrappers, and AppleScript tools.
      for (const tool of manifest.tools) {
        const toolNodeId = `tool_cap_${manifest.plugin_id}_${tool.tool_id}`;
        await doc72.write({
          node_id: toolNodeId,
          type: "tool_capability",  // Existing DOC24 amendment node type
          canonical_name: tool.display_name,
          payload: {
            tool_source: "plugin",
            tool_id: tool.tool_id,
            plugin_id: manifest.plugin_id,
            description: tool.description,
            capabilities_summary: [tool.tool_id],
            connector_ref: tool.connector_ref || null,
            cost_model: tool.cost?.model || "free",
            requires_ai: tool.requires_ai,
          },
          principal_id: "will",
          scope: "personal",
        });
        
        // Edge: plugin --has_tool--> tool_capability
        await doc72.createEdge(pluginNodeId, toolNodeId, "has_tool");
      }
      
      // ═══ 3. Create connector entity nodes ═══
      for (const conn of (manifest.connectors || [])) {
        const connNodeId = `connector_entity_${manifest.plugin_id}_${conn.connector_id}`;
        await doc72.write({
          node_id: connNodeId,
          type: "entity",
          subtype: "connector",
          domain: "plugin_registry",
          connector_id: conn.connector_id,
          display_name: conn.display_name,
          service_name: conn.display_name,
          managed_by_plugin: manifest.plugin_id,
          standalone: false,
          auth_type: conn.auth.type,
          auth_status: "unconfigured",
          tool_count: conn.tools.length,
          total_calls: 0,
          total_cost_cents: 0,
          last_used: "never",
          error_rate: 0,
          api_base_url: conn.base_url || null,
          rate_limit_info: conn.rate_limit 
            ? `${conn.rate_limit.requests_per_minute || "?"} req/min` : null,
          cost_info: conn.cost_model 
            ? `${conn.cost_model.type}: $${(conn.cost_model.unit_cost || 0) / 100}/${conn.cost_model.unit_label || "unit"}` : null,
          principal_id: "will",
          scope: "personal",
          created_at: new Date().toISOString(),
          updated_at: new Date().toISOString(),
        });
        
        // Edge: plugin --uses_connector--> connector
        await doc72.createEdge(pluginNodeId, connNodeId, "uses_connector");
        
        // Edge: tool_capability --provided_by--> connector (for each tool this connector provides)
        for (const toolId of conn.tools) {
          const toolNodeId = `tool_cap_${manifest.plugin_id}_${toolId}`;
          await doc72.createEdge(toolNodeId, connNodeId, "provided_by");
        }
      }
      
      // ═══ 4. Create alternative_to edges between tools that do similar things ═══
      // e.g., pacer_search_cases and recap_search both search cases
      // DOC24's routing cascade uses these edges + experience to pick the best tool
      if (manifest.tools.length > 1) {
        await createAlternativeEdges(manifest);
      }
      
      // ═══ 5. Register tools in DOC24 capability registry (operational) ═══
      for (const tool of manifest.tools) {
        await doc24.capabilityRegistry.register({
          id: tool.tool_id,
          display_name: tool.display_name,
          description: tool.description,
          source_type: "plugin",
          plugin_id: manifest.plugin_id,
          capability_tags: [manifest.category, ...manifest.tags || []],
          graph_node_id: `tool_cap_${manifest.plugin_id}_${tool.tool_id}`,
        });
      }
      
      // ═══ 6. Notify Elnor ═══
      addNotice({
        source: "Plugin System",
        title: `New plugin discovered: ${manifest.display_name}`,
        desc: `${manifest.description}. ${manifest.tools.length} tools available.`,
      });
      
    } else if (existing.version !== manifest.version) {
      // Plugin updated — update entity and re-sync tools
      await doc72.update(existing.node_id, {
        version: manifest.version,
        tool_count: manifest.tools.length,
        tool_names: manifest.tools.map(t => t.tool_id),
        capabilities_summary: generateCapabilitySummary(manifest),
        updated_at: new Date().toISOString(),
      });
      // Also update/create any new tool_capability nodes for added tools
      // and remove nodes for removed tools
      await syncToolCapabilityNodes(manifest, existing.node_id);
    }
  }
  
  // Check for removed plugins
  for (const entity of existingEntities) {
    if (!installedPlugins.find(m => m.plugin_id === entity.plugin_id)) {
      await doc72.update(entity.node_id, {
        state: "removed",
        last_state_change: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      });
      // Deregister tools from DOC24 capability registry
      await doc24.capabilityRegistry.deregisterByPlugin(entity.plugin_id);
      // Don't delete tool_capability or connector nodes — preserve history
    }
  }
}

// Create alternative_to edges between plugin tools that serve similar functions
async function createAlternativeEdges(manifest: PluginManifest): Promise<void> {
  // Example: pacer_search_cases and recap_search both search for cases
  // DOC24's experience-informed routing uses these edges to pick the best option
  // 
  // Detection: tools with overlapping capability descriptions or explicitly
  // declared alternatives in the manifest
  //
  // For PACER plugin: pacer_search_cases <--alternative_to--> recap_search
  //                   pacer_get_docket <--alternative_to--> recap_get_docket
  //                   pacer_download_document <--alternative_to--> recap_download_document
}

function generateCapabilitySummary(manifest: PluginManifest): string {
  // Generate a natural language summary of what the plugin can do
  // This is what Elnor reads to understand the plugin
  const parts: string[] = [];
  
  if (manifest.tools.length > 0) {
    const toolDescs = manifest.tools.slice(0, 5).map(t => t.description).join(", ");
    parts.push(`Provides ${manifest.tools.length} tools: ${toolDescs}`);
  }
  if (manifest.services?.length) {
    parts.push(`Runs ${manifest.services.length} background service(s): ${manifest.services.map(s => s.display_name).join(", ")}`);
  }
  if (manifest.tab) {
    parts.push(`Has a management tab: ${manifest.tab.label}`);
  }
  if (manifest.connectors?.length) {
    parts.push(`Connects to: ${manifest.connectors.map(c => c.display_name).join(", ")}`);
  }
  
  return parts.join(". ") + ".";
}
```

### 9.4 What Elnor Can Do With Plugin Entities

Because plugins are DOC72 entities, Elnor can reason about them like any other knowledge:

**"What plugins do I have?"**
Elnor queries DOC72 for `{type: "entity", subtype: "plugin", state: "active"}` and lists them with their capability summaries. No hardcoded list needed.

**"What can the PACER plugin do?"**
Elnor retrieves the PACER plugin entity node and reads `capabilities_summary`, `tool_names`, `service_description`, `tab_description`. Elnor can also cross-reference with telemetry to add: "You've used it 247 times, mainly for docket pulls and party searches."

**"Which plugin handles court filings?"**
Elnor searches plugin entities by description/capabilities matching "court filings" → finds PACER plugin. This is DOC24 semantic routing using DOC72 knowledge.

**"I just installed a new plugin from ClawHub. Does Elnor know about it?"**
Yes. `syncPluginEntities()` runs on launch and detects the new manifest. Creates the entity node. Elnor gets a notice. Next time Elnor's context loads, the plugin entity is in the knowledge graph and Elnor can route queries to its tools.

**"Elnor, can you build something like the PACER plugin but for state courts?"**
Elnor retrieves the PACER plugin entity (capabilities, connector structure, tool list) PLUS the PACER build record (design decisions, implementation notes, issues encountered). Uses both to scaffold a new state court plugin informed by prior experience.

**"Which plugins haven't I used in a while?"**
Elnor queries plugin entities sorted by `last_used`. Dream cycle also detects this automatically.

### 9.5 Connector Entity Nodes

Connectors — whether standalone or plugin-managed — also get DOC72 entity nodes:

```typescript
interface ConnectorEntityNode {
  node_id: string;                    // "connector_entity_pacer_api"
  type: "entity";
  subtype: "connector";
  domain: "plugin_registry";
  
  connector_id: string;              // "pacer_api"
  display_name: string;              // "PACER API"
  service_name: string;              // "PACER (uscourts.gov)"
  
  // Relationship
  managed_by_plugin?: string;        // "pacer" — or null if standalone
  standalone: boolean;               // true if not part of any plugin
  
  // Auth
  auth_type: string;                 // "credentials", "oauth2", "api_key"
  auth_status: "healthy" | "expired" | "invalid" | "unconfigured";
  last_auth_refresh?: string;
  
  // Usage
  tool_count: number;
  total_calls: number;
  total_cost_cents: number;
  last_used: string;
  error_rate: number;
  
  // Metadata
  api_base_url?: string;
  rate_limit_info?: string;          // "180 req/min"
  cost_info?: string;                // "$0.10/page, $3 max/doc"
  
  principal_id: "will";
  scope: "personal";
  created_at: string;
  updated_at: string;
}
```

This means Elnor knows: "I have 6 connectors. 4 are healthy, 1 has expired auth (PACER — needs password refresh), 1 is unconfigured (Zoom — waiting for OAuth). The PACER connector is managed by the PACER plugin. Gmail is standalone."

### 9.6 Keeping Entities Current

Plugin and connector entity nodes are updated by:

| Trigger | What Updates |
|---------|-------------|
| Plugin installed/uninstalled | Entity created/marked removed |
| Plugin enabled/disabled | `state` field |
| Plugin updated (new version) | `version`, `capabilities_summary`, `tool_names`, etc. |
| Tool called | `total_tool_calls`, `last_used` (batched, not per-call) |
| Cost incurred | `total_cost_cents` (batched) |
| Auth refreshed/expired | Connector entity `auth_status` |
| Dream cycle runs | `avg_daily_calls`, `error_rate` (rolling calculations) |
| User adds notes | `user_notes` field |

### 9.7 Full Graph Structure for a Plugin

When a plugin is fully registered, the DOC72 graph contains these nodes and edges:

```
Plugin Entity (type: entity, subtype: plugin)
│
│ ──has_tool──→ Tool Capability Node (type: tool_capability)     ← DOC24 amendment pattern
│                │
│                │ ──provided_by──→ Connector Entity (type: entity, subtype: connector)
│                │
│                │ ──alternative_to──→ Tool Capability Node (another tool, same function)
│                │                     ↑
│                │                     │ DOC24's experience-informed routing uses
│                │                     │ Beta confidence on these nodes to pick
│                │                     │ the best tool when alternatives exist
│                │
│                │ ──executed_via──← Procedure Node (type: procedure)     ← DOC3 skills
│                │                    (when a learned procedure uses this tool)
│                │
│                └── Experience record (α/β confidence, success_count, total_usage_count)
│                    Updated by DOC24's execution receipt handler on every tool call
│
│ ──uses_connector──→ Connector Entity (type: entity, subtype: connector)
│
│ ──has_build_record──→ Build Record (type: procedure, domain: plugin_development)
│                        (only for Elnor-built plugins)
│
│ ──related_to──→ Domain Entities (type: entity, subtype: federal_case / music_artist / etc.)
│                  (links plugin to the domain it serves)
│
├── Telemetry Records (type: observation, domain: plugin_telemetry)
├── Error Records (type: observation, domain: plugin_errors)  
└── Improvement Proposals (type: fact, domain: plugin_proposals)
```

**Example: PACER plugin graph:**

```
plugin_entity_pacer (entity/plugin)
├── has_tool → tool_cap_pacer_search_cases (tool_capability)
│              ├── provided_by → connector_entity_pacer_api (entity/connector)
│              ├── alternative_to → tool_cap_recap_search (tool_capability)
│              │                    └── provided_by → connector_entity_courtlistener (entity/connector)
│              └── experience: α=42, β=3, confidence=0.93
├── has_tool → tool_cap_pacer_get_docket (tool_capability)
│              ├── provided_by → connector_entity_pacer_api
│              ├── alternative_to → tool_cap_recap_get_docket
│              └── experience: α=28, β=1, confidence=0.97
├── has_tool → tool_cap_pacer_download_document (tool_capability)
│              ├── provided_by → connector_entity_pacer_api
│              ├── alternative_to → tool_cap_recap_download_document
│              └── experience: α=55, β=4, confidence=0.93
├── has_tool → tool_cap_recap_search (tool_capability)
│              ├── provided_by → connector_entity_courtlistener
│              └── experience: α=30, β=8, confidence=0.79
├── uses_connector → connector_entity_pacer_api (entity/connector)
│                     auth_status: healthy, total_cost: $14.30
├── uses_connector → connector_entity_courtlistener (entity/connector)
│                     auth_status: healthy, total_cost: $0 (free)
├── has_build_record → plugin_build_pacer_v1 (procedure)
├── related_to → case_entity_brooge (entity/federal_case)
├── related_to → case_entity_narayanan (entity/federal_case)
├── telemetry → tel_20260411_* (47 observation nodes this week)
├── errors → err_pacer_ratelimit_* (2 observation nodes this month)
└── proposals → prop_reduce_inactive_frequency (1 pending fact node)
```

DOC24's routing cascade traverses this graph: when Will asks "check for new filings," DOC24 matches the query to the PACER plugin's semantic triggers, finds the `pacer_get_docket` and `recap_get_docket` tool_capability nodes via the `has_tool` edges, checks the `alternative_to` edge between them, compares experience (RECAP confidence 0.79 vs PACER 0.97), but also checks cost (RECAP free vs PACER $0.10/page). The routing logic in DOC24's cascade weighs both factors — for a docket check, RECAP-first is the right call because the cost advantage outweighs the slightly lower confidence. This is the RECAP-first pattern expressed as graph knowledge rather than hardcoded `if (recap) { try recap first }` logic.

### 9.8 Node Type Summary

For clarity — here is every DOC72 node used by the plugin system and which canonical type it maps to:

| Plugin Concept | DOC72 Canonical Type | Subtype / Domain | New Type? |
|---------------|---------------------|------------------|-----------|
| Plugin itself | `entity` | `subtype: "plugin"`, `domain: "plugin_registry"` | ❌ No |
| Connector | `entity` | `subtype: "connector"`, `domain: "plugin_registry"` | ❌ No |
| Individual tool | `tool_capability` | (per DOC24 amendment) | ❌ Already exists |
| Build record | `procedure` | `domain: "plugin_development"` | ❌ No |
| Telemetry record | `observation` | `domain: "plugin_telemetry"` | ❌ No |
| Error record | `observation` | `domain: "plugin_errors"` | ❌ No |
| Error pattern | `observation` | `domain: "plugin_errors"` | ❌ No |
| Usage pattern | `observation` | `domain: "plugin_telemetry"` | ❌ No |
| Improvement proposal | `fact` | `domain: "plugin_proposals"` | ❌ No |
| Plugin schema reference | `fact` | `domain: "plugin_development"` | ❌ No |
| Cost tracking | `observation` | `domain: "plugin_telemetry"` | ❌ No |

**Zero new canonical node types. Everything fits the existing DOC72 schema.**

---

## 10. Execution Telemetry

### 10.1 What Gets Recorded

Every plugin tool call and service execution produces a telemetry record. This is NOT the full response payload — it's lightweight metadata about the operation.

```typescript
interface PluginTelemetryRecord {
  record_id: string;
  timestamp: string;                 // ISO
  
  // What happened
  plugin_id: string;                 // "pacer"
  operation_type: "tool_call" | "service_run" | "agent_instruction";
  operation_id: string;              // Tool ID, service ID, or instruction ID
  
  // Context
  trigger: "user_chat" | "agent_instruction" | "scheduled" | "manual" | "cross_plugin";
  conversation_id?: string;          // If triggered from chat
  case_ref?: string;                 // If associated with a specific case/project
  
  // Parameters (sanitized — no secrets, no full document content)
  params_summary: string;            // "search_cases(party='Brooge', court='cacd')"
  
  // Result
  outcome: "success" | "failure" | "partial" | "timeout" | "cancelled";
  result_summary?: string;           // "4 cases found" or "Downloaded 22-page PDF"
  error?: PluginErrorRecord;         // If outcome is failure (see §3)
  
  // Performance
  duration_ms: number;
  cost_cents?: number;               // If the operation cost money
  tokens_used?: number;              // If AI was involved (agent instructions)
  
  // Usefulness signal
  user_acted_on_result?: boolean;    // Did user open the document, click the link, etc.?
  user_feedback?: "positive" | "negative" | "none";
  
  // DOC72 storage
  principal_id: "will";
  scope: "personal";
}
```

### 10.2 Storage

Telemetry records are stored as DOC72 observation nodes with `domain: "plugin_telemetry"`:

```typescript
// DOC72 node
{
  node_id: "tel_20260411_143022_pacer_search",
  type: "observation",
  domain: "plugin_telemetry",
  subtype: "tool_call",
  plugin_id: "pacer",
  content: "pacer_search_cases(party='Brooge Energy', court='cacd') → 4 results, 340ms, $0.10",
  outcome: "success",
  timestamp: "2026-04-11T14:30:22Z",
  // ... other fields
}
```

### 10.3 Retention Policy

Telemetry nodes are lightweight but accumulate. Retention:

- **Last 30 days:** Full detail retained
- **30-90 days:** Aggregated to daily summaries per plugin (e.g., "PACER: 12 tool calls, 11 success, 1 failure, $1.40 cost, avg 280ms")
- **90+ days:** Aggregated to monthly summaries
- **Error records:** Never aggregated — kept individually for pattern detection

### 10.4 What Elnor Learns from Telemetry

DOC3's extraction pipeline periodically reviews telemetry and extracts patterns:

**Tool preference patterns:**
```
Observation: "When Will asks about case status, pacer_get_docket is called 85% of 
the time and pacer_get_case_summary 15%. Docket is preferred — use it first."
→ DOC72 preference node: {domain: "plugin_usage", subject: "case_status_tool", 
   preferred: "pacer_get_docket", confidence: 0.85}
```

**Temporal patterns:**
```
Observation: "PACER searches spike on Monday mornings (avg 8 calls vs 2 on other days).
Will likely checks cases at start of week."
→ DOC72 observation node: {domain: "plugin_usage", pattern: "monday_morning_pacer_spike"}
```

**Cost patterns:**
```
Observation: "80% of PACER costs come from 3 cases (Brooge, Narayanan, Henderson).
Other 17 watched cases rarely incur charges."
→ DOC72 fact node: {domain: "plugin_cost", content: "Top 3 cases drive 80% of PACER spend"}
```

**Effectiveness patterns:**
```
Observation: "When pacer_search_cases returns >10 results, Will only clicks the first 3.
Consider limiting default results to 5."
→ DOC72 observation node: {domain: "plugin_effectiveness", suggestion: "reduce_default_results"}
```

---

## 11. Error Handling & Error Learning

### 11.1 Error Classification

Every plugin error is classified into a taxonomy:

```typescript
interface PluginErrorRecord {
  error_id: string;
  timestamp: string;
  plugin_id: string;
  operation_id: string;              // Which tool/service failed
  
  // Classification
  category: PluginErrorCategory;
  severity: "critical" | "warning" | "info";
  
  // Detail
  message: string;                   // Human-readable error message
  technical_detail?: string;         // Stack trace, API response, etc.
  
  // Context
  params_at_failure?: string;        // What parameters were used
  environment?: string;              // Network state, time of day, etc.
  
  // Resolution
  resolved: boolean;
  resolution_method?: "auto_retry" | "config_change" | "user_action" | "code_fix" | "unresolved";
  resolution_detail?: string;        // What fixed it
  resolution_timestamp?: string;
  
  // Learning
  recurrence_count: number;          // How many times this same error has occurred
  first_seen: string;                // When this error type first appeared
  pattern_id?: string;               // Groups related errors into a pattern
}

type PluginErrorCategory =
  | "auth_expired"           // Token expired, need re-auth
  | "auth_invalid"           // Credentials wrong
  | "rate_limited"           // API rate limit hit
  | "network_error"          // Connection failed, timeout
  | "api_error"              // API returned error response
  | "not_found"              // Requested resource doesn't exist
  | "permission_denied"      // Insufficient permissions
  | "data_format"            // Unexpected response format
  | "service_unavailable"    // External service is down
  | "local_error"            // Local file/process issue
  | "config_error"           // Plugin misconfigured
  | "resource_exhausted"     // Disk full, memory limit, etc.
  | "logic_error"            // Bug in plugin code
  | "user_cancelled"         // User aborted the operation
  | "unknown";               // Unclassified
```

### 11.2 Error Display

Each plugin shows its error state in multiple places:

**Plugin Settings page:**
```
⚖ PACER Federal Courts                        ● Active
Errors: 1 warning in last 24h
├─ ⚠ Rate limited at 2:14 PM (auto-resolved, backed off 60s)
└─ Last 7 days: 2 warnings, 0 critical, 98% success rate
[View error log]
```

**Plugin tab (if it has one):**
```
ERRORS
├─ ● Healthy — last 5 checks clean
│
│ Recent:
│ ⚠ Apr 11 2:14 PM — Rate limited by PACER. Backed off 60s. Auto-resolved.
│ ✓ Apr 10 — All 3 checks clean
│ ⚠ Apr 9 7:01 AM — CourtListener timeout. Fell back to direct PACER.
│ ✓ Apr 8 — All 3 checks clean
│
│ Recurring patterns:
│ ℹ CourtListener timeouts occur ~1x/week, usually resolved by retry.
│   Elnor auto-falls back to PACER (costs extra ~$0.30/occurrence).
```

**Floating Palette notices (critical errors only):**
```
⚠ PACER plugin: Authentication expired. Re-enter credentials in Settings.
```

**Toast (critical errors only):**
```
"PACER auth expired — monitoring paused. [Fix in Settings]"
```

### 11.3 Error Learning in DOC72

Errors are stored as DOC72 observation nodes with `domain: "plugin_errors"`:

```typescript
{
  node_id: "err_pacer_ratelimit_20260411",
  type: "observation",
  domain: "plugin_errors",
  plugin_id: "pacer",
  content: "PACER API rate limited at 2:14 PM. 47 requests in preceding 60 seconds. 
            Auto-backed off 60s. Resumed successfully.",
  category: "rate_limited",
  severity: "warning",
  resolved: true,
  resolution_method: "auto_retry",
  recurrence_count: 3,
  pattern_id: "pacer_rate_limit_pattern",
}
```

### 11.4 Error Pattern Detection

DOC8's dream cycle reviews error records and identifies patterns:

**Recurring errors:**
```
Pattern: "pacer_rate_limit_pattern"
Occurrences: 3 in last 30 days
Context: Always during bulk docket pulls (>30 requests in <2 minutes)
Learning: "When pulling dockets for multiple cases, add 2-second delay between cases
           to stay under PACER's rate limit."
→ DOC72 procedure node: rate limiting strategy for bulk PACER operations
→ Elnor applies this automatically on next bulk pull
```

**Auth cycle errors:**
```
Pattern: "pacer_auth_expiry_pattern"
Occurrences: 12 in last 90 days (every ~7 days)
Context: PACER auth token expires weekly
Learning: "PACER tokens expire every ~7 days. Proactively refresh at 6 days."
→ Plugin service updated: add pre-emptive token refresh
```

**Unfixable errors:**
```
Pattern: "courtlistener_intermittent_timeout"
Occurrences: 6 in last 30 days
Context: CourtListener API occasionally times out during peak hours (2-4 PM ET)
Learning: "CourtListener is less reliable 2-4 PM ET. Schedule RECAP checks for 
           off-peak hours or increase timeout to 30s."
→ DOC72 observation: service reliability pattern
→ Suggestion surfaced to user: "Move PACER monitoring to 7 AM to avoid CourtListener peak"
```

### 11.5 Error Handling for Elnor-Built Plugins

When Elnor builds a plugin and it has errors:

1. **Build-time errors** (syntax, missing dependencies): Elnor sees the error immediately during the build process and fixes it — standard coding agent behavior.

2. **Runtime errors** (API failures, unexpected data): Elnor logs the error via the telemetry system (§2). If the user reports the error ("Elnor, the Westlaw plugin isn't working"), Elnor:
   - Reads the error log from DOC72
   - Identifies the error category and pattern
   - Proposes a fix (code change, config change, or user action)
   - If it's a code fix, uses the coding agent to apply it
   - Tests the fix
   - Records the resolution in DOC72 for future reference

3. **Design errors** (wrong API, bad schema, missing tool): These surface as user complaints ("it doesn't do what I wanted"). Elnor:
   - Reviews the original build conversation from DOC72
   - Identifies the gap between intent and implementation
   - Proposes manifest/code changes
   - Applies and tests

---

## 12. Plugin-Building Knowledge

### 12.1 What Gets Stored When a Plugin Is Built

Every plugin build (whether by Elnor or manually) should capture:

```typescript
interface PluginBuildRecord {
  record_id: string;
  plugin_id: string;
  
  // Build context
  built_by: "elnor" | "user" | "community";
  build_method: "coding_agent" | "manual" | "imported";
  build_timestamp: string;
  build_duration_minutes?: number;
  
  // Original request
  user_request?: string;             // Natural language: "Build me a plugin that monitors Westlaw"
  
  // Design decisions
  design_decisions: {
    decision: string;                // "Used OAuth2 instead of API key because Westlaw requires it"
    rationale: string;               // "Westlaw's API only supports OAuth2 for user-scoped access"
    alternatives_considered?: string; // "Considered scraping but too fragile"
  }[];
  
  // Implementation notes
  implementation_notes: string[];    // "Rate limit is 100 req/min", "Auth token expires in 1 hour"
  
  // Issues encountered during build
  build_issues: {
    issue: string;
    resolution: string;
    time_to_resolve_minutes?: number;
  }[];
  
  // Manifest snapshot (for reference)
  manifest_version: string;
  
  // Post-build modifications
  modifications: {
    date: string;
    description: string;
    reason: string;
  }[];
}
```

### 12.2 DOC72 Storage

Build records become DOC72 procedural knowledge nodes:

```typescript
{
  node_id: "plugin_build_pacer_v1",
  type: "procedure",
  domain: "plugin_development",
  content: "Built PACER plugin: 2 connectors (PACER API + CourtListener), 
            monitoring service, custom tab with case management dashboard. 
            Key decisions: RECAP-first retrieval pattern to minimize cost, 
            keyword-based filing type classification (no LLM), per-case 
            agent instructions for conditional AI processing.",
  build_issues: [
    "PACER auth token format changed — needed to parse nextGenCSO from response",
    "CourtListener pagination uses cursor, not offset — had to adjust"
  ],
  tools_created: ["pacer_search_cases", "pacer_get_docket", ...],
  total_build_hours: 6,
  principal_id: "will",
  scope: "personal",
}
```

### 12.3 How Elnor Uses Build Knowledge

When building a new plugin, Elnor queries DOC72 for prior build records:

**"Build me a plugin that monitors state court filings"**

Elnor retrieves:
- PACER plugin build record (similar: court monitoring)
- Spotify plugin build record (similar: scheduled intake service)
- Error patterns from existing plugins (common pitfalls)

Elnor applies lessons learned:
- "PACER plugin used RECAP-first pattern to minimize costs. State court probably doesn't have a free alternative, but I should check."
- "PACER plugin's monitoring service needed rate limiting after getting blocked. I'll add rate limiting from the start."
- "PACER plugin's filing type classifier used keyword matching — worked well, I'll reuse that pattern."

### 12.4 Plugin Manifest Knowledge in DOC72

The plugin manifest schema itself should be stored in DOC72 as a reference document:

```typescript
{
  node_id: "plugin_schema_reference",
  type: "fact",
  domain: "plugin_development",
  subtype: "schema_reference",
  content: "Plugin manifest schema v1.1 — defines identity, connectors, tools, 
            services, processes, agent instructions, DOC72 config, tab, outputs, 
            lifecycle. Located at plugins/_template/manifest.json.template.",
  schema_version: "1.1",
  last_updated: "2026-04-11",
}
```

This means when Elnor is asked to build a plugin, it can query DOC72 for the schema reference rather than having it hardcoded in its system prompt. If the schema evolves, the DOC72 node is updated and Elnor always uses the latest version.

---

## 13. DOC8 Dream Cycle — Plugin Health Review

### 13.1 Nightly Review (Lightweight)

Every night, the dream cycle reviews plugin health:

```typescript
interface PluginNightlyReview {
  tasks: [
    // 1. Check each active plugin's service health
    "review_service_health",         // Any services crashed? Errors in last 24h?
    
    // 2. Review error patterns
    "check_error_patterns",          // Any new recurring errors? Any resolved patterns?
    
    // 3. Cost check
    "daily_cost_summary",            // How much did plugins cost today? Any anomalies?
    
    // 4. Telemetry aggregation
    "aggregate_daily_telemetry",     // Roll up detailed telemetry into daily summary
  ];
}
```

### 13.2 Weekly Review (Heavier)

Weekly, the dream cycle does a deeper plugin analysis:

```typescript
interface PluginWeeklyReview {
  tasks: [
    // 1. Usage analysis
    "identify_unused_tools",         // Tools not called in 30+ days → suggest disabling
    "identify_unused_plugins",       // Plugins not used in 30+ days → suggest disabling
    "identify_heavy_usage",          // Tools called 100+ times → candidate for optimization
    
    // 2. Performance analysis
    "slow_tool_detection",           // Tools with avg latency >5s → investigate
    "cost_trend_analysis",           // Week-over-week cost changes
    "error_rate_analysis",           // Error rate trending up or down?
    
    // 3. Agent instruction review
    "stale_instructions",            // Instructions that never trigger → suggest removing
    "ineffective_instructions",      // Instructions that trigger but user ignores output → adjust
    "missing_instruction_candidates",// Patterns that suggest a new instruction would help
    
    // 4. Cross-plugin analysis
    "plugin_interaction_patterns",   // Which plugins are used together? Any missing connections?
    "redundant_tool_detection",      // Two plugins providing similar capabilities?
    
    // 5. Generate weekly digest entry
    "plugin_digest_entry",           // Summary for the weekly digest
  ];
}
```

### 13.3 Weekly Digest Entry

The dream cycle generates a plugin health section for the weekly digest:

```
PLUGIN HEALTH — Week of Apr 7-13

Active plugins: 5 (PACER, Spotify, Hue, Reminders, YouTube)
Total tool calls: 247 (↑12% from last week)
Total cost: $8.40 (PACER: $7.20, Spotify: $0, others: $0)
Error rate: 2.1% (5 of 247 calls, all auto-resolved)

Notable:
• PACER monitoring caught 12 new filings across 20 cases
• Spotify intake updated 8 artist profiles and identified 
  a new genre pattern (classical during Henderson prep)
• Hue "focus mode" activated 14 times this week (↑ from 9 last week)

Suggestions:
• YouTube plugin unused for 22 days — consider disabling or 
  using for CLE webinar transcripts
• PACER agent instruction "summarize_new_filing" triggered 8 times 
  but you only opened 3 summaries — adjust trigger condition?
• New pattern detected: you always search PACER after Zoom calls 
  about active cases. Auto-add PACER search to post-meeting workflow?
```

---

## 14. Self-Improvement Proposals

### 14.1 How Proposals Are Generated

Based on accumulated telemetry, error patterns, and usage data, the system generates improvement proposals. These are NOT auto-applied — they go into a review queue (DOC3's promotion pipeline).

```typescript
interface PluginImprovementProposal {
  proposal_id: string;
  timestamp: string;
  
  // What's being proposed
  type: "new_agent_instruction" | "modify_instruction" | "new_plugin" | 
        "modify_plugin" | "disable_plugin" | "optimize_tool" | 
        "new_cross_plugin_workflow" | "config_change";
  
  plugin_id?: string;                // Which plugin (if applicable)
  
  // The proposal
  title: string;                     // "Add auto-PACER-search after Zoom calls"
  description: string;               // Full explanation
  evidence: string[];                // What data supports this proposal
  estimated_benefit: string;         // "Saves ~5 minutes per meeting"
  estimated_risk: string;            // "May trigger unnecessary searches for non-case calls"
  
  // Implementation
  implementation: string;            // What would change (manifest update, new instruction, etc.)
  reversible: boolean;               // Can this be undone easily?
  
  // Review
  status: "pending" | "approved" | "rejected" | "deferred";
  reviewed_at?: string;
  review_notes?: string;
}
```

### 14.2 Proposal Types

**New agent instruction:**
```
Proposal: "Add post-meeting PACER check"
Evidence: "In 8 of the last 10 Zoom calls about active cases, you searched 
PACER within 15 minutes of the call ending."
Implementation: Add agent instruction to Zoom plugin: 
  "When meeting transcript mentions an active case, check PACER for recent filings."
Benefit: Eliminates manual search step after meetings
Risk: May trigger for meetings that mention cases casually
```

**New plugin suggestion:**
```
Proposal: "Build a Clio time tracking plugin"
Evidence: "You spend 3-5 minutes per day manually entering time in Clio. 
Q already knows what you worked on (tab history, document opens, chat topics)."
Implementation: Build plugin that auto-generates time entries from Q activity data
Benefit: Saves ~20 min/week on time entry
Risk: Time entries may need manual adjustment for accuracy
```

**Plugin optimization:**
```
Proposal: "Reduce PACER monitoring frequency for low-activity cases"
Evidence: "12 of 20 watched cases had zero new filings in the last 30 days. 
These are checked 3x daily, costing 36 unnecessary API calls/day."
Implementation: Auto-reduce check frequency to weekly for cases with 
no activity in 14+ days. Restore to normal when activity resumes.
Benefit: Reduces unnecessary API calls by ~60%, saves $2-3/quarter
Risk: May miss a filing by up to 7 days for low-activity cases
```

**Cross-plugin workflow:**
```
Proposal: "Trial prep mode should include PACER check"
Evidence: "When you activate 'trial prep mode' (Hue + Spotify), you also 
open the case docket within 10 minutes 70% of the time."
Implementation: Add PACER docket check to the trial prep mode procedure
Benefit: One command sets up your complete trial prep environment
Risk: None — docket check is read-only and free from RECAP
```

### 14.3 Where Proposals Surface

1. **Weekly digest:** "3 plugin improvement proposals this week — review in Settings > Plugins"
2. **Floating Palette notices:** Proposals appear as notices (low priority, not urgent)
3. **Plugin Settings page:** Proposals section at the bottom of each plugin's config
4. **Chat:** "Elnor, any suggestions for my plugins?" → Elnor lists pending proposals

### 14.4 Proposal Lifecycle

```
Generated by DOC8 dream cycle
    ↓
Stored in DOC72 as proposal node
    ↓
Surfaced to user (digest, notices, settings)
    ↓
User reviews: [Approve] [Reject] [Defer] [Modify]
    ↓
If approved:
  - Simple changes (config, instruction): applied automatically
  - Code changes (new tool, modified service): Elnor builds via coding agent
  - New plugin: Elnor scaffolds and builds
    ↓
Result recorded in DOC72 (outcome of the proposal)
    ↓
Future proposals learn from approval/rejection patterns
  "User always approves cost-reduction proposals but rejects frequency changes"
```

---

---

## 15. Elnor Awareness — Combined Plugin Prompts

### 15.1 Plugin Builder Prompt

Add to Elnor's system context for plugin building capability:

```
You can BUILD new plugins for Q Dashboard. When Will asks you to create a new 
integration, capability, or monitoring system, consider whether it should be a plugin.

A plugin should be built when:
- The capability involves an external API or service
- It benefits from scheduled background processing
- It would benefit from a dedicated management UI (tab)
- It involves recurring automation with configurable rules
- Will says "build me a plugin" or "I want to add [capability] to Q"

To build a plugin:
1. Read the plugin scaffold template at plugins/_template/BUILDING_A_PLUGIN.md
2. Copy the template to plugins/{new_plugin_id}/
3. Define the manifest.json with all required sections
4. Implement tools, services, tabs as needed
5. Create tool_capability nodes following the DOC24 amendment pattern
6. Create edges: plugin→tools, plugin→connectors, tool→connector, tool→alternative_to
7. Register tools in DOC24 capability registry with graph_node_id references
8. Register DOC24 semantic triggers
9. Test everything
10. Install by placing in the plugins/ directory
11. Create a build record in DOC72 for future reference

The manifest schema is the contract. Follow it exactly. Every field has a purpose.
If you're unsure about a field, read the schema comments in the manifest.json.template.

You can also MODIFY existing plugins — add new tools, change service schedules,
update agent instructions, expand the tab UI. Treat plugin code like any other
code in the Q Dashboard project.
```

### 15.2 Plugin Intelligence Prompt

Add to Elnor's system context for plugin operations and learning:

```
PLUGIN INTELLIGENCE:

You maintain operational knowledge of all active plugins:
- Plugin entities are stored in DOC72 under {type: "entity", subtype: "plugin"}
- Individual tool knowledge is stored as tool_capability nodes (per DOC24 amendment)
- Connectors are stored as {type: "entity", subtype: "connector"}
- Usage telemetry is stored under domain "plugin_telemetry"
- Errors are stored under domain "plugin_errors"
- Build history is stored under domain "plugin_development"
- Improvement proposals are stored under domain "plugin_proposals"

Graph structure: plugin --has_tool--> tool_capability --provided_by--> connector
Tool alternatives linked via alternative_to edges with experience-based confidence.
DOC24's routing cascade uses this graph for experience-informed tool selection.

When a plugin tool fails:
1. Check DOC72 for prior occurrences of this error pattern
2. If a resolution exists, apply it automatically
3. If no resolution exists, diagnose the error and either fix it or escalate to Will
4. Always log the error and resolution for future reference
5. If an alternative_to tool exists, fall back to it and note the fallback in telemetry

When asked about plugin performance:
- Query plugin entity nodes for overview (tool count, state, last used)
- Query tool_capability nodes for per-tool experience (confidence, success rate)
- Query telemetry for usage stats, costs, and patterns
- Reference error history for reliability assessment
- Check improvement proposals for pending suggestions

When building a new plugin:
- Query DOC72 for prior plugin build records
- Apply lessons learned from previous builds
- Create tool_capability nodes following the DOC24 amendment pattern
- Create edges: plugin→tools, plugin→connectors, tool→connector, tool→alternative_to
- Register tools in DOC24 capability registry with graph_node_id references
- After building, create a build record in DOC72 for future reference

During dream cycle participation:
- Review all plugin telemetry from the past day/week
- Review tool_capability experience records (identify declining confidence)
- Identify patterns, anomalies, and opportunities
- Generate improvement proposals when evidence is strong
- Flag critical issues for immediate user attention
```

---

## 16. Cross-Doc References

| Document | Relevance |
|----------|-----------|
| DOC3 R11.3 | Skill/connector registry — plugins register tools and connectors here. Register `plugin_telemetry` as a learning signal source. Add plugin error patterns as learning input alongside conversation corrections. |
| DOC24 | Capability registry — plugin semantic triggers registered here. Plugin tools registered with `graph_node_id` linking to DOC72 nodes. Experience-informed routing applies to plugin tools same as any other tool. |
| DOC24 Tool Capability Nodes Amendment | Plugin tools create `tool_capability` nodes following this amendment's schema. Same experience tracking, confidence scoring, execution receipt handling. `alternative_to` edges enable graph-based routing (e.g., RECAP-first). |
| DOC23 | Task system — AI tasks triggered by plugin agent instructions. |
| DOC20 §6.20 | Tab system — plugin tabs registered as new tab types. |
| DOC20 [+] menu | Plugin submenu with arrow slide. |
| DOC72 R5.6 | Entity graph — domains: `plugin_registry`, `plugin_telemetry`, `plugin_errors`, `plugin_development`, `plugin_proposals`. No new canonical node types. |
| DOC8 | Dream cycle — plugin health review (nightly + weekly). Plugin section in weekly digest. |
| DOC16 | Email/M365 — plugins can trigger on email events. CM/ECF notification capture. |
| DOC10 | Unified engagement ledger — plugin telemetry events traceable in operations log. |

---

## 17. Implementation Notes

1. **Start with the manifest schema and registration flow.** A plugin that can register tools and appear in Settings is the minimum viable system.
2. **Add DOC72 entity auto-registration early.** Without plugin entity nodes, Elnor doesn't know plugins exist.
3. **Add tab support third.** The tab rendering infrastructure is Q-side work.
4. **Add service management fourth.** EC or a standalone service manager handles scheduled programmatic services.
5. **Add agent instructions fifth.** Requires DOC23 task triggering and LLM dispatch.
6. **Add telemetry and error tracking sixth.** Lightweight — just logging observation nodes to DOC72.
7. **Add dream cycle participation seventh.** Nightly and weekly plugin health review.
8. **Add self-improvement proposals eighth.** Requires accumulated telemetry data to generate meaningful proposals.
9. **Add Elnor builder last.** Once the system works manually, teach Elnor to build plugins by reading the scaffold.
10. **PACER is the reference implementation.** Build PACER as the first full plugin — it exercises every feature of the system.
11. **Retrofit existing integrations.** Spotify, Hue, Reminders should become plugins for consistency.
12. **The scaffold template is the most important artifact.** If the template is good, Elnor can build plugins. If it's bad, every plugin is custom work.