Reconnaissance
Reconnaissance
Reconnaissance observes the target system after effectuation — collecting metrics, checking health, and gathering the data needed to judge each trial. It's the eyes of the trial loop.
Role in the Trial Loop
┌──────────────────────────────────────────────────────────────┐
│ Trial Loop │
│ │
│ Effectuator ──▶ Target System ──▶ Reconnaissance │
│ (apply) (changed) (observe) │
│ │
│ │ │
│ ▼ │
│ Metrics & │
│ Observations │
│ │
└──────────────────────────────────────────────────────────────┘
Reconnaissance answers: "What happened after we made that change?"
Reconnaissance Interface
┌──────────────────────────────────────────────────────────────┐
│ Reconnaissance │
│ │
│ Input: Trial context (what was applied) │
│ │
│ Action: Collect observations from target │
│ │
│ Output: { metric_a: 123.4, metric_b: 42.0, ... } │
│ │
└──────────────────────────────────────────────────────────────┘
| Phase | Responsibility |
|---|---|
| Stabilize | Wait for the change to propagate |
| Collect | Gather samples from sources |
| Aggregate | Combine samples into one value |
| Report | Return structured data |
Reconnaissance Sources
| Source | What it provides | Protocol |
|---|---|---|
| HTTP | Stats and metrics endpoints | REST/HTTP |
| Prometheus | Time-series metrics | PromQL |
Two sources ship today — a known limit, not the design's boundary; more are in development. The per-objective block (path/key/samples/aggregation) is the stable interface each source fills.
HTTP Reconnaissance
How a systemtender reads its target — per objective (and per observation), sampled and aggregated:
reconnaissance: # where the target lives
type: http
http:
url: "http://bench-generic:8090/node-1"
objectives: # what gets read, how
- name: objective_0
direction: maximize
reconnaissance:
service: http
path: /metrics/json
key: objective_0
samples: 3
aggregation: median
Use for: services with stats endpoints, bench nodes, health APIs.
Prometheus Reconnaissance
Per-objective PromQL queries against a Prometheus endpoint — a global default with per-objective override — sampled and aggregated the same way as HTTP reads.
Use for: cloud-native systems, Kubernetes, microservices.
Timing and Sampling
┌──────────────────────────────────────────────────────────────────┐
│ Reconnaissance Timeline │
│ │
│ Effectuation Stabilization Sampling │
│ │ ├────────┤ ● ● ● │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ─────●───────────────●────────●────────●──────────────────▶ │
│ │ trials │
│ │
│ Wait for the change to land, then take N samples │
└──────────────────────────────────────────────────────────────────┘
| Parameter | Purpose |
|---|---|
stabilization_seconds |
Wait before first sample (default 2s — propagation time) |
samples |
How many samples to take per read |
aggregation |
How the samples collapse to one value |
Aggregation
Multiple samples → single observation:
| Aggregation | Use case |
|---|---|
| median | Default — robust to outliers |
| mean | Typical value when noise is symmetric |
| min / max | Bounds |
reconnaissance:
service: http
path: /metrics/json
key: latency
samples: 5
aggregation: median
Multiple Channels
Every objective and every observation is its own reconnaissance block — one target read through as many channels as you care to name:
objectives: # measured, judged per trial
- name: objective_0
direction: maximize
reconnaissance: {service: http, path: /metrics/json, key: objective_0, samples: 3, aggregation: median}
observations: # recorded, never judged — detection channels
- name: objective_1
reconnaissance: {service: http, path: /metrics/json, key: objective_1, samples: 3, aggregation: median}
Observations feed the detector, not the trial's values — a channel you suspect is coupled but don't want driving trial outcomes belongs here.
Handling Missing Data
A metric that cannot be read does not silently pass:
| Scenario | Handling |
|---|---|
| No valid samples | The read fails — the trial fails |
| Read error | No value; counts as a guardrail violation if a limit watches it |
The safe direction is the default: absence is a failure, not a zero.
Noise and Stability
Real metrics have noise:
┌──────────────────────────────────────────────────────────────┐
│ Noisy Observations │
│ │
│ True value: 50ms │
│ Observed: [48, 52, 47, 55, 49, 51, 48, 53, ...] │
│ │
│ Aggregation smooths noise for fitness evaluation │
│ │
└──────────────────────────────────────────────────────────────┘
Mitigation strategies:
| Strategy | How it helps |
|---|---|
| More samples | Less variance in the aggregate |
| Median aggregation | Resists outliers |
| The ABA block design | Push/pause contrast separates signal from drift |
Reconnaissance vs Objectives
| Reconnaissance | Objectives |
|---|---|
| Collects raw metrics | Computes fitness |
| System-specific | Problem-specific |
| Multiple values | Single/directional |
| Descriptive | Evaluative |
Reconnaissance: { latency: 45ms, throughput: 8000, error_rate: 0.001 }
│
▼
Objective: minimize latency ──▶ fitness = 45
Summary
| Aspect | What it means |
|---|---|
| Role | Observe system after effectuation |
| Sources | HTTP endpoints, Prometheus queries |
| Timing | Stabilization wait, then N samples |
| Aggregation | median (default), mean, min, max |
| Output | Metrics dict used for fitness, guardrails, and detection |
See Also
- Effectuator — What reconnaissance observes
- Guardrails — Check reconnaissance data against limits
- Systemtender — Orchestrates the trial loop
- Configuration Guide — The shipped config, end to end