# STRATA Sensor Plugin Architecture

## Intelligence as Infrastructure

**Canemah Nature Laboratory**
Technical Note Series

**Document ID:** CNL-TN-2026-044
**Version:** 0.1 (Draft)
**Date:** April 8, 2026
**Author:** Michael P. Hamilton, Ph.D.
**Affiliation:** Canemah Nature Laboratory, Oregon City, Oregon

---

**AI Assistance Disclosure:** This technical note was developed collaboratively with Claude (Anthropic, claude-opus-4-6) via Cowork. Claude contributed to architectural analysis, codebase study, capability mapping, and document drafting. The author takes full responsibility for the content, accuracy, and conclusions.

---

## Abstract

STRATA 1.0 demonstrated that modular temporal agents can generate meaningful intelligence from heterogeneous sensor streams. However, the current architecture couples each agent to a specific platform through hand-coded queries, summary generators, and context builders. Adding a new sensor platform requires writing a new micro-agent, a new tool endpoint, a new context builder, and wiring them into the orchestrator -- roughly 400-600 lines of platform-specific code per sensor type.

This technical note specifies a plugin architecture that replaces this per-platform custom code with a declarative registration contract. A sensor plugin defines what it measures, how its data is structured, and what domain it belongs to. STRATA wraps that declaration with temporal analysis (nine standard windows), pattern detection (SOMA mesh integration), narrative interpretation (AI-generated summaries), and tool-calling endpoints -- automatically, without per-plugin custom code. The result is a system where intelligence is a property of the infrastructure, not of each individual sensor integration.

The architecture defines three layers: the Plugin Contract (what a sensor provides), the Intelligence Envelope (what STRATA wraps around it), and the Consumer Interface (what downstream systems receive). Development targets Data as the prototype environment, with Galatea deployment deferred until the architecture stabilizes.

---

## 1. Introduction

### 1.1 The Problem with Hand-Coded Agents

The STRATA 1.0 codebase on Galatea contains 13 platform-specific micro-agents, each querying a specific table with hardcoded column names, computing platform-specific summaries, and generating platform-specific natural language descriptions. Seven context builders then reassemble these summaries into system prompts for Claude, and 25 tool-calling endpoints provide real-time query access during conversations.

This architecture works well at the current scale of 13 platforms. But every new sensor addition requires:

- A summary micro-agent in `agents/summaries/` (~100-150 lines)
- A tool endpoint in `api/strata/tools/` (~200-300 lines)
- A context builder in `includes/context/` (~100-200 lines)
- Wiring in the temporal orchestrator and tool dispatcher
- Manual registration in the sensor_platforms table

The code across these files follows recognizable patterns -- they all query latest readings, compute temporal windows, format natural language -- but each reimplements those patterns from scratch. The 13 existing agents contain approximately 5,800 lines of tool code, 2,000 lines of agent code, and 1,200 lines of context code. At least 60% of this is structural boilerplate that could be generated from declarations.

### 1.2 The Design Target

A sensor plugin should be a single declaration file -- roughly 50-100 lines -- that tells STRATA everything it needs to know about a sensor platform. From that declaration, STRATA should automatically provide:

- Temporal state generation across all nine standard windows
- Natural language summaries with domain-appropriate vocabulary
- Tool-calling endpoints for real-time queries during conversations
- System prompt fragments for the context system
- SOMA mesh integration points for anomaly detection
- Dashboard visualization metadata

No per-plugin custom code. No manual wiring. Register a plugin, and the full STRATA intelligence envelope wraps around it.

### 1.3 Scope

This specification covers the plugin contract, the intelligence envelope, the consumer interface, and the SOMA integration contract. It does not cover the distributed service architecture (addressed in CNL-TN-2026-043), the MNG convergence plan (CNL-TN-2026-027), or the STRATA_Bench investigation workflow. Development targets Data (MacBook Pro M4 Max) as the prototype environment.

---

## 2. Architecture Overview

### 2.1 Three Layers

```
CONSUMERS
  Dashboard panels, STRATA_Bench investigations, Claude chat,
  MNG widgets, external applications
     |
     v
+--------------------------------------------------+
|          CONSUMER INTERFACE                       |
|  Structured JSON + System Prompt Fragments        |
|  Per-plugin, per-domain, per-temporal-window      |
+--------------------------------------------------+
     ^
     |
+--------------------------------------------------+
|          INTELLIGENCE ENVELOPE                    |
|  Temporal Analysis (9 windows)                    |
|  Narrative Generation (AI summaries)              |
|  Pattern Detection (SOMA mesh integration)        |
|  Tool-Calling Endpoints (auto-generated)          |
+--------------------------------------------------+
     ^
     |
+--------------------------------------------------+
|          PLUGIN CONTRACT                          |
|  Platform declaration (YAML/PHP)                  |
|  Metric definitions + query patterns              |
|  Domain classification + aggregation types        |
+--------------------------------------------------+
     ^
     |
  MACROSCOPE DATABASE
  (57 tables, synced from Galatea every 5 minutes)
```

**Figure 1.** Three-layer plugin architecture. Plugins declare what they measure. The intelligence envelope wraps them with temporal analysis, narrative generation, and pattern detection. Consumers receive structured intelligence packages.

### 2.2 Design Principles

**Declarative over imperative.** A plugin is a data structure, not code. It describes what a sensor measures; it does not implement how to analyze it.

**Convention over configuration.** Standard patterns (latest reading, temporal aggregation, freshness checking) are built into the envelope. Plugins only specify what differs from convention.

**Domain-native vocabulary.** The intelligence envelope generates narratives using domain-appropriate language. An EARTH sensor reports "temperature dropping 3.2F over the past hour." A LIFE sensor reports "14 new species detected since dawn." A HOME sensor reports "CO2 rising above 800 ppm in the office." The domain classification drives vocabulary selection.

**Aggregation-aware.** Different metrics require different temporal treatment. Temperature is instantaneous (latest reading is meaningful). Daily rainfall is cumulative (you want the running total, not the last increment). Bird detections are event-based (you want counts and distinct species, not averages). Wind direction is categorical (you want the dominant direction, not the arithmetic mean). The plugin contract captures these distinctions.

---

## 3. The Plugin Contract

### 3.1 Plugin Declaration

A plugin declaration is a PHP associative array (or equivalent YAML file) that registers a sensor platform with STRATA. The declaration is stored in a registry directory and loaded at startup.

```php
// plugins/tempest.plugin.php
return [
    'platform_type' => 'tempest',
    'platform_name' => 'WeatherFlow Tempest',
    'domain'        => 'EARTH',
    'table'         => 'tempest_readings',
    'timestamp_col' => 'recorded_at',
    'platform_col'  => 'platform_id',

    // Freshness threshold: how stale before flagging
    'freshness_minutes' => 10,

    // Metric definitions
    'metrics' => [
        'temperature_f' => [
            'label'       => 'Temperature',
            'unit'        => 'F',
            'type'        => 'instantaneous',
            'precision'   => 1,
            'category'    => 'atmospheric',
            'narrative'   => 'temperature',
            'soma_weight' => 1.0,
        ],
        'feels_like_f' => [
            'label'       => 'Feels Like',
            'unit'        => 'F',
            'type'        => 'instantaneous',
            'precision'   => 1,
            'category'    => 'atmospheric',
        ],
        'humidity' => [
            'label'       => 'Humidity',
            'unit'        => '%',
            'type'        => 'instantaneous',
            'precision'   => 0,
            'category'    => 'atmospheric',
            'soma_weight' => 0.8,
        ],
        'wind_mph' => [
            'label'       => 'Wind Speed',
            'unit'        => 'mph',
            'type'        => 'instantaneous',
            'precision'   => 1,
            'category'    => 'wind',
            'soma_weight' => 0.6,
        ],
        'wind_direction_cardinal' => [
            'label'       => 'Wind Direction',
            'unit'        => '',
            'type'        => 'categorical',
            'category'    => 'wind',
        ],
        'pressure_inhg' => [
            'label'       => 'Pressure',
            'unit'        => 'inHg',
            'type'        => 'instantaneous',
            'precision'   => 2,
            'category'    => 'atmospheric',
            'soma_weight' => 0.9,
        ],
        'solar_radiation' => [
            'label'       => 'Solar Radiation',
            'unit'        => 'W/m2',
            'type'        => 'instantaneous',
            'precision'   => 0,
            'category'    => 'solar',
            'soma_weight' => 0.5,
        ],
        'uv_index' => [
            'label'       => 'UV Index',
            'unit'        => '',
            'type'        => 'instantaneous',
            'precision'   => 1,
            'category'    => 'solar',
        ],
    ],

    // Dashboard display grouping
    'display_groups' => [
        'primary'   => ['temperature_f', 'humidity', 'wind_mph'],
        'secondary' => ['pressure_inhg', 'solar_radiation', 'uv_index'],
        'detail'    => ['feels_like_f', 'wind_direction_cardinal'],
    ],
];
```

### 3.2 Metric Types

The `type` field on each metric determines how the intelligence envelope handles temporal aggregation:

| Type | Temporal Behavior | Examples |
|------|------------------|----------|
| `instantaneous` | Latest value, min/max/avg over window, rate of change | Temperature, humidity, pressure |
| `cumulative_daily` | Running total resets at midnight, period accumulation | Rainfall, solar energy, steps |
| `event` | Count per window, distinct count, latest event | Bird detections, iNaturalist obs |
| `categorical` | Modal value (most frequent), distribution | Wind direction, AQI category |
| `gauge` | Latest value only, no meaningful temporal aggregation | Battery level, WiFi signal |

### 3.3 Required Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `platform_type` | string | Yes | Unique identifier (matches sensor_platforms.platform_type) |
| `platform_name` | string | Yes | Human-readable name |
| `domain` | enum | Yes | EARTH, LIFE, HOME, or SELF |
| `table` | string | Yes | Database table name |
| `timestamp_col` | string | Yes | Column used for temporal ordering |
| `platform_col` | string | Yes | Column used to filter by platform instance |
| `freshness_minutes` | int | Yes | Threshold for stale data warning |
| `metrics` | array | Yes | At least one metric definition |

### 3.4 Metric Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `label` | string | Yes | Human-readable metric name |
| `unit` | string | Yes | Display unit (empty string if dimensionless) |
| `type` | enum | Yes | One of the five metric types above |
| `precision` | int | No | Decimal places for display (default: 1) |
| `category` | string | No | Grouping within the platform (atmospheric, wind, solar, etc.) |
| `narrative` | string | No | Key for domain-specific narrative templates |
| `soma_weight` | float | No | Relative importance for SOMA mesh (0.0-1.0, omit to exclude) |
| `transform` | callable | No | Optional function to transform raw value for display |
| `thresholds` | array | No | Warning/critical thresholds for alerting |

### 3.5 Optional Plugin Features

A plugin may optionally declare:

**Custom queries.** For platforms that require joins or complex WHERE clauses (e.g., birdweather_detections joining species), a plugin can provide a `custom_query` callback that returns a result set conforming to the standard metric structure. This is the escape hatch for platforms that don't fit the single-table convention.

**Derived metrics.** Computed from other metrics (e.g., heat index from temperature and humidity, AQI from PM2.5). Declared as metrics with `type: 'derived'` and a `compute` callback.

**Multi-instance awareness.** For platforms with multiple physical devices (e.g., three AirLink stations, two Airthings Waves). Declared via `multi_instance: true` and `instance_col` (the column distinguishing devices, such as `station_id` or `device_id`).

**Privacy classification.** For SELF and HOME domain plugins. Declared via `privacy: 'private'` (requires per-user domain access) or `privacy: 'public'` (default for EARTH and LIFE).

---

## 4. The Intelligence Envelope

### 4.1 Temporal Analysis Engine

Given a plugin declaration, the intelligence envelope automatically generates temporal state across nine standard windows:

| Window | Duration | Query Pattern |
|--------|----------|---------------|
| `last_hour` | Rolling 60 minutes | WHERE timestamp >= NOW() - INTERVAL 1 HOUR |
| `1_day` | Last 24 hours | WHERE timestamp >= NOW() - INTERVAL 24 HOUR |
| `7_days` | 7-day rolling | WHERE timestamp >= NOW() - INTERVAL 7 DAY |
| `30_days` | Last month | WHERE timestamp >= NOW() - INTERVAL 30 DAY |
| `90_days` | Last quarter | WHERE timestamp >= NOW() - INTERVAL 90 DAY |
| `ytd` | Since January 1 | WHERE timestamp >= MAKEDATE(YEAR(NOW()), 1) |
| `rain_yr` | Since October 1 | WHERE timestamp >= hydrological year start |
| `1_year` | Last 12 months | WHERE timestamp >= NOW() - INTERVAL 1 YEAR |
| `dawn_chorus` | 90 min at sunrise | WHERE timestamp BETWEEN sunrise - 30min AND sunrise + 60min |

For each window and each metric, the engine computes aggregations appropriate to the metric type:

- **Instantaneous:** latest, min, max, avg, stddev, rate_of_change (first-to-last delta)
- **Cumulative daily:** period_total, daily_avg, max_daily, days_with_accumulation
- **Event:** count, distinct_count, latest_event, hourly_rate
- **Categorical:** mode (most frequent), distribution (value counts), transitions
- **Gauge:** latest only

The output is a structured temporal state object per plugin per window.

### 4.2 Narrative Generation

The envelope includes a narrative engine that generates natural language descriptions from temporal state. Narratives are domain-aware:

**EARTH narratives** use meteorological vocabulary: "Temperature has dropped 5.2F over the past hour, from 58.3F to 53.1F. Pressure is rising at 0.02 inHg/hour, suggesting clearing conditions. Wind has shifted from SW to NW at 12.4 mph with gusts to 18.1 mph."

**LIFE narratives** use ecological vocabulary: "55 species detected in the past 24 hours across 6,756 detections. Chestnut-backed Chickadee dominates with 1,331 detections (avg confidence 0.848). Dawn chorus activity was elevated compared to the 7-day average, with 14 species detected in the 90-minute sunrise window."

**HOME narratives** use indoor environment vocabulary: "Office CO2 has risen from 412 ppm to 624 ppm over the past 3 hours, indicating reduced ventilation. Radon levels in the basement remain at 3.6 pCi/L, within the EPA action threshold of 4.0 pCi/L."

**SELF narratives** use health vocabulary: "Resting heart rate has trended down from 62 to 58 bpm over the past 30 days. Sleep efficiency remains above 90% with an average of 6.2 hours total sleep. Last clinical visit (March 15) showed blood pressure at 133/78."

Narrative templates are stored in a domain-keyed template library. Plugins can override templates for platform-specific language, but the defaults handle most cases from the metric declarations alone.

### 4.3 Auto-Generated Tool Endpoints

For each registered plugin, the envelope generates Claude tool-calling endpoints:

- `get_{platform}_latest` -- Latest readings for all metrics
- `get_{platform}_history` -- Time-series for specified metrics over a date range
- `get_{platform}_summary` -- Temporal state summary for a specified window
- `query_{platform}` -- Flexible query with filters (metric, date range, aggregation)

These endpoints are registered in the tool dispatcher and become available to Claude during conversations and STRATA_Bench investigations. The tool definitions (name, description, parameter schema) are generated from the plugin declaration -- no manual endpoint authoring.

### 4.4 Context System Integration

The envelope generates system prompt fragments from temporal state. These fragments are assembled by the `buildSystemPrompt()` orchestrator into complete prompts for Claude. Each fragment includes:

- Platform identification (name, domain, location)
- Current readings with freshness indicators
- Temporal context across selected windows (adaptive: casual queries get fewer windows, technical queries get all nine)
- Anomaly flags from SOMA (if active)
- Narrative summary

The adaptive query interpretation framework from STRATA 1.0 (casual, specific source, comparative, temporal, species) drives which fragments and how much temporal depth to include.

---

## 5. The Consumer Interface

### 5.1 Dual Output Format

Every plugin produces two output representations, serving different consumers:

**Structured JSON** for programmatic consumers (dashboards, analysis pipelines, STRATA_Bench investigations):

```json
{
    "platform_type": "tempest",
    "platform_name": "WeatherFlow Tempest",
    "domain": "EARTH",
    "instance_id": 1,
    "freshness": {
        "age_minutes": 4,
        "status": "fresh",
        "recorded_at": "2026-04-08T09:27:34"
    },
    "current": {
        "temperature_f": { "value": 48.0, "unit": "F", "label": "Temperature" },
        "humidity": { "value": 76, "unit": "%", "label": "Humidity" },
        "wind_mph": { "value": 5.0, "unit": "mph", "label": "Wind Speed" }
    },
    "temporal": {
        "last_hour": {
            "temperature_f": { "latest": 48.0, "min": 46.2, "max": 49.1, "avg": 47.5, "rate": -0.8 },
            "humidity": { "latest": 76, "min": 72, "max": 78, "avg": 75.2, "rate": 1.2 }
        },
        "1_day": { ... },
        "7_days": { ... }
    },
    "anomalies": [],
    "narrative": "Temperature at 48.0F with light wind from the NE at 5.0 mph..."
}
```

**System prompt fragments** for AI consumers (Claude chat, STRATA_Bench AI interactions):

```
## Tempest Weather Station [EARTH] (4 min ago)
Temperature: 48.0F (feels like 46.0F) | Humidity: 76%
Wind: 5.0 mph NE, gusts to 8.0 mph | Pressure: 29.80 inHg (rising)
Solar: 225 W/m2 | UV: 1.0

Past hour: Temperature dropped 0.8F. Humidity rising slowly.
Past 24h: Overnight low 42.1F at 05:15, high 52.3F at 14:40.
```

### 5.2 Domain Aggregation

Consumers can request data at multiple granularities:

- **Single plugin:** One platform's full intelligence package
- **Domain summary:** All plugins in EARTH, LIFE, HOME, or SELF, with cross-platform synthesis
- **Full observatory:** All four domains, suitable for the Observatory dashboard or a comprehensive system prompt
- **Custom selection:** Specific plugins and temporal windows, for targeted investigations

### 5.3 Registration and Discovery

Consumers discover available plugins through a registry API:

```php
$registry = StrataPluginRegistry::getInstance();

// List all registered plugins
$plugins = $registry->listPlugins();

// Get plugins by domain
$earth_plugins = $registry->getByDomain('EARTH');

// Get a specific plugin
$tempest = $registry->get('tempest');

// Get intelligence package
$package = $registry->getIntelligence('tempest', ['windows' => ['last_hour', '1_day']]);

// Get domain summary
$earth_summary = $registry->getDomainSummary('EARTH');
```

---

## 6. SOMA Integration Contract

### 6.1 Anomaly Detection Interface

SOMA (Stochastic Observatory for Mesh Awareness) operates as a parallel detection layer. The plugin architecture defines a bidirectional contract between sensor plugins and SOMA meshes.

**Plugin to SOMA (data feed).** Each metric with a `soma_weight` > 0 feeds into its domain's SOMA mesh. The plugin provides normalized readings at regular intervals. The `soma_weight` determines the metric's relative influence on the mesh's energy landscape -- a temperature reading with weight 1.0 has more influence than a UV index reading with weight 0.5.

**SOMA to Plugin (anomaly signals).** When a SOMA mesh detects elevated free energy (anomalous state), it emits signals back to the relevant plugins. These signals include:

```php
[
    'mesh_id'       => 'earth_tempest',
    'signal_type'   => 'tension',          // tension, release, divergence, correlation
    'severity'      => 0.73,               // 0.0-1.0, normalized free energy
    'affected_metrics' => ['temperature_f', 'pressure_inhg'],
    'description'   => 'Unusual temperature-pressure divergence',
    'detected_at'   => '2026-04-08T09:15:00',
    'context'       => [
        'temperature_rate' => -2.1,        // F/hour, unusually rapid
        'pressure_rate'    => +0.05,       // inHg/hour, rising while temp drops
    ],
]
```

### 6.2 Mesh Registration

SOMA meshes are themselves registered as plugins of type `pattern_detector`:

```php
return [
    'platform_type'  => 'soma_earth',
    'platform_name'  => 'SOMA Earth Mesh',
    'domain'         => 'EARTH',
    'mesh_type'      => 'rbm',
    'node_count'     => 100,
    'input_plugins'  => ['tempest', 'ecowitt', 'ambientweather'],
    'detection_types' => ['tension', 'release', 'divergence', 'correlation'],
    'update_interval' => 300,  // seconds
];
```

This allows SOMA meshes to be discovered, queried, and included in intelligence packages just like sensor plugins. A consumer asking for the EARTH domain summary receives both sensor readings and SOMA anomaly signals in a single package.

### 6.3 Cross-Domain Tensioning

The SOMA architecture (per the Whittaker gradient analysis principle from CNL-TN-2026-043) maintains domain-specific meshes with independent internal logic. A relational layer connects these meshes to detect cross-domain anomaly propagation.

The plugin contract supports this through a `cross_domain_signals` declaration:

```php
'cross_domain_signals' => [
    'temperature_f' => ['target_domain' => 'LIFE', 'coupling' => 'dawn_chorus_sensitivity'],
    'pressure_inhg' => ['target_domain' => 'LIFE', 'coupling' => 'migration_pressure'],
],
```

This tells the relational layer that temperature changes in EARTH may propagate to LIFE domain activity (birds are sensitive to temperature at dawn), and that pressure patterns may correlate with migration behavior. The relational mesh learns the strength and timing of these couplings from data; the declaration simply identifies which cross-domain relationships to monitor.

---

## 7. Migration Path from STRATA 1.0

### 7.1 Conversion Strategy

The 13 existing STRATA 1.0 micro-agents become 13 plugin declaration files. The conversion is mechanical:

1. Extract table name, column names, and domain from each agent's queries
2. Map each queried column to a metric definition with appropriate type and category
3. Extract platform-specific narrative patterns into templates
4. Create the plugin declaration file

The existing custom queries for complex platforms (birdweather_detections with species joins, withings_body_measurements with measure_type pivots) become `custom_query` callbacks in their plugin declarations.

### 7.2 Compatibility

During migration, both systems can coexist. The plugin architecture reads from the same macroscope database tables as the hand-coded agents. Plugins produce output that can be consumed by both the new consumer interface and the existing STRATA 1.0 context builders (by generating compatible JSON state files).

### 7.3 Initial Plugin Set

| Plugin File | Platform | Domain | Metrics | SOMA |
|-------------|----------|--------|---------|------|
| tempest.plugin.php | WeatherFlow Tempest | EARTH | 8 | Yes |
| ecowitt.plugin.php | Ecowitt GW1200B | EARTH/LIFE | 14 | Yes |
| ambientweather.plugin.php | WS-2902C | EARTH | 7 | Yes |
| airthings.plugin.php | Airthings Wave | HOME | 9 | Yes |
| airlink.plugin.php | WeatherLink AirLink | EARTH/HOME | 7 | Yes |
| birdweather_det.plugin.php | BirdWeather PUC | LIFE | 6 | Yes |
| birdweather_env.plugin.php | PUC Environmental | EARTH | 6 | Yes |
| inaturalist.plugin.php | iNaturalist | LIFE | 5 | No |
| health_vitals.plugin.php | Withings + Apple Health | SELF | 8 | No |
| health_activity.plugin.php | Withings Activity | SELF | 4 | No |
| health_workouts.plugin.php | Withings Workouts | SELF | 4 | No |
| health_clinical.plugin.php | Clinical Records | SELF | 4 | No |
| documents.plugin.php | PDF Library | SELF | 4 | No |

---

## 8. Implementation Plan

### 8.1 Phase 1: Core Engine (Data) — PARTIALLY REALIZED

Build the plugin registry, temporal analysis engine, and consumer interface on Data. Implement three initial plugins (tempest, ecowitt, birdweather_detections) to validate the architecture across EARTH and LIFE domains. Wire the Observatory dashboard to consume from the plugin system instead of direct queries.

**Status (April 8, 2026):** 13 sensor registry files implemented in `observatory/registry/` covering all platform types (tempest, ecowitt, ambientweather, airthings, airlink, birdweather_detections, birdweather_environmental, inaturalist, health_vitals, health_activity, health_workouts, health_clinical, document_library). Each registry defines sensors with field, label, unit, domain, category, primary flag, and aggregation_type — a simplified form of the plugin contract specified in Section 3. Platform-driven discovery (`discover_domain_platforms()` via `platform_domains` many-to-many table) replaces hardcoded domain queries. Observatory dashboard operational across all four domains, consuming from the registry system. Intelligence dashboard shows 29 live micro-agent narratives (pure PHP, no LLM cost). Current registries are PHP arrays rather than the full YAML/PHP plugin declaration format; the migration path from current registries to full plugin contracts is straightforward (add table/timestamp/freshness/SOMA metadata to existing definitions).

### 8.2 Phase 2: Narrative and Tool Generation

Implement the narrative generation engine with domain-keyed templates. Implement auto-generated tool-calling endpoints. Validate by comparing output quality against the hand-coded STRATA 1.0 agents.

**Status (April 8, 2026):** Narrative generation operational via hand-coded micro-agent summaries (one per platform type in `agents/summaries/`). These produce natural language from SQL queries. The next step is to replace per-agent custom narrative code with a template-driven engine that reads the registry declarations — the "intelligence envelope" described in Section 4. Tool-calling endpoints not yet started.

### 8.3 Phase 3: Full Plugin Migration

Convert all 13 STRATA 1.0 agents to plugin declarations. Validate each against its hand-coded equivalent. Wire STRATA_Bench to consume from the plugin system for investigation workflows.

### 8.4 Phase 4: SOMA Integration

Implement the SOMA mesh registration contract. Wire the existing three trained meshes (Tempest, BirdWeather, Ecosystem) as pattern_detector plugins. Implement cross-domain tensioning declarations and the relational layer.

### 8.5 Phase 5: Galatea Deployment

Port the plugin architecture to Galatea. Deploy as the STRATA shared library described in CNL-TN-2026-043. Validate in production alongside the existing STRATA 1.0 system before cutover.

### 8.6 Known Issues (April 8, 2026)

- AirLink platforms incorrectly assigned to EARTH domain in `platform_domains`; should be HOME (indoor air quality sensors)
- Ecowitt platform naming and domain assignments need review (cross-domain platform spanning EARTH/LIFE/HOME)
- Open-Meteo platform shows in EARTH grid but has no readings (data availability, not code bug)
- Health Activity platform shows no readings (platform_id mismatch with applehealth_metrics data)
- CSS grid white gaps when tall cards (many categories) are adjacent to short cards
- Platform deployment metadata (GPS, exposure, mounting height, canopy cover) not yet captured — CRUD UI needed
- Virtual sensor definitions (calculated/derived metrics with aggregation rules) flagged as future work

---

## 9. Scope and Boundaries

The plugin architecture is agnostic about how data arrives in the macroscope database. A "sensor" is anything that produces timestamped observations in a known table schema -- whether the upstream source is a physical device on a pole in the backyard (Tempest, Ecowitt), an API-based collector fetching from a cloud service (Open-Meteo, BirdWeather, iNaturalist), a mobile device syncing personal health data (Apple Health, Withings), or a computed index derived from other streams. Virtual sensors are first-class citizens. The collector is outside the plugin boundary; the plugin contract begins at the database table.

What this specification does not cover: collector implementation (each collector has its own schedule, API, error handling, and retry logic -- these are plumbing concerns, not intelligence concerns), SOMA 2.0's expanded mesh architecture (the integration contract here describes the interface, not the implementation), and narrative template authoring (generation quality depends on the richness of domain-specific template libraries, which will require iterative refinement against real sensor data with input from domain experts).

---

## 10. Conclusion

The STRATA sensor plugin architecture shifts intelligence from per-sensor custom code to a universal infrastructure layer. A 50-100 line declaration file replaces 400-600 lines of hand-coded agents, tools, and context builders. Temporal analysis, narrative generation, pattern detection, and tool-calling become automatic properties of any registered sensor. This makes the system extensible -- adding a new sensor type is a declaration, not a development project -- while preserving the multi-temporal, multi-domain, AI-aware intelligence that STRATA 1.0 proved was possible.

The plugin contract, intelligence envelope, and consumer interface together define a complete architecture for sensor intelligence as a service. Combined with the distributed service architecture (CNL-TN-2026-043) and the MNG convergence plan (CNL-TN-2026-027), this specification completes the design framework for STRATA 2.0.

---

## References

[1] Hamilton, M. P. (2026). "Macroscope/STRATA and MNG Convergence Plan." CNL-TN-2026-027, Canemah Nature Laboratory.

[2] Hamilton, M. P. (2026). "STRATA 2.0: Distributed Intelligence Architecture." CNL-TN-2026-043, Canemah Nature Laboratory.

[3] Hamilton, M. P. (2026). "Organelle Convergence Architecture." CNL-FN-2026-026, Canemah Nature Laboratory.

[4] Whittaker, R. H. (1967). "Gradient analysis of vegetation." *Biological Reviews*, 42(2), 207-264.

---

## Document History

| Version | Date | Changes |
|---------|------|---------|
| 0.1 | 2026-04-08 | Initial draft. Plugin contract, intelligence envelope, consumer interface, SOMA integration contract, migration path. |
| 0.1a | 2026-04-08 | Added implementation status notes to Phase 1 and Phase 2. Added Section 8.6 (Known Issues). Observatory and Intelligence dashboards operational. |
