Configuration_Guide
Configuration Guide
Two surfaces carry intent into a running godon. The systemtender config is YAML — what a tender measures and moves. A wish declaration is made at runtime — what a holder asks the system to keep, or to search.
Systemtender configuration is YAML with four concepts: objectives (the readings trials are judged on), guardrails (safety), observations (extra channels to read), and effectuation (the registered targets its acts land on). Parameters live under settings with per-parameter constraints. Interference characterization is a section, not a mode — every systemtender in a group walks and holds concurrently.
The Shape of a Systemtender Config
meta:
configVersion: "0.3"
strict_validation: false
systemtender:
type: bench_generic
settings: # parameter search space
generic:
param_0:
constraints:
- {step: 20.0, lower: 0.0, upper: 100.0}
param_1:
constraints:
- {step: 20.0, lower: 0.0, upper: 100.0}
param_2:
constraints:
- {step: 20.0, lower: 0.0, upper: 100.0}
interference_detection: # characterization group membership
group: bench-characterization
mode: active
convergence_threshold: 0.02 # the ONE tuning knob
refinement_depth: 3
push_block_size: 10
pause_block_size: 10
cooldown_trials: 5
hold_params: # neutral position for hold/pause blocks
param_0: 50.0
param_1: 50.0
param_2: 50.0
reconnaissance: # how this systemtender reads its target
type: http
http:
url: "http://bench-generic:8090/node-1"
objectives: # the readings trials are judged on
- name: objective_0
direction: maximize
reconnaissance:
service: http
path: /metrics/json
key: objective_0
samples: 3
aggregation: median
observations: # recorded but NOT judged — detection channels
- name: objective_1
reconnaissance:
service: http
path: /metrics/json
key: objective_1
effectuation: # where its acts land
targetRefs: [generic-node-1] # registered targets, by name or ID
Sections
settings — the search space
Each parameter carries constraints. step sets the grid resolution; the characterization walk derives its level set from lower/upper (and refines between grid points when curves stay unresolved).
How a run ends
A systemtender runs until one of three things happens:
- Work complete — the characterization has nothing left to measure (all parameters converged or the refinement depth is spent) and the systemtender finishes on its own
- Stop requested — an explicit stop via the API: the current trial completes, then the systemtender finishes
- Failure — the worker crashes
interference_detection — characterization
Any systemtenders sharing a group characterize each other. convergence_threshold is the single tuning knob: smaller means more re-measurement before a curve retires. Block sizes set the push/pause trial counts per probe. hold_params is the neutral position every systemtender returns to when holding or pausing.
objectives vs observations
The separation is deliberate and load-bearing: objectives carry each trial's judged values; observations are recorded per trial and never judged — the detector reads both. A channel you suspect is coupled but don't want driving trial outcomes should be an observation, not an objective.
reconnaissance
How a systemtender reads its target: HTTP endpoints (per-service path/key, sampling, aggregation), or Prometheus queries. See Reconnaissance.
effectuation and the targets catalog
A systemtender's acts land on targets from the targets catalog. Targets are registered at runtime — REST POST /targets or the target_create MCP tool — with a name, a targetType (ssh or http), and a spec (ssh carries an address, http carries a url). The config names them in effectuation.targetRefs, by name or ID; a config with no reference is refused at validation, before anything runs. What an act does once it lands is the Effectuator's story.
Guardrails
Safety limits with automatic response (fail trial, rollback, or skip target) are configured per strain and effectuator — see Guardrails. The impulse protocol's pushes stay inside the same guardrails as ordinary trials: same knobs, same bounds, no special permissions.
The Shape of a Wish Declaration
Wishes are declared at runtime, not written into a config file: a wish is a payload sent to the REST API (/wishes) or the wish_declare MCP tool. The payload states its type — steer (the hold) or seek (the walk), each with its own shape below. Both types also carry two family options: answer (now or later, default later — when the reachability consult answers) and standard (all or best — one kept state, or the best of several; holds default all, walks default best).
Steer — the hold's shape
One wish = one or more claims and optional terms, one verdict. Kept means every claim in its band and every term honored.
type: steer
claims: # the aims — N >= 1
- outcome: chainend.shift
band: {lo: -0.14, hi: -0.06, target: -0.10}
terms: # the protected readings — M >= 0
- outcome: chainend.offset
band: {lo: -0.02, hi: 0.02}
limits:
exclude: [param_2]
maxChange: 0.5
budget: 2
regime: standing
claims vs terms
Claims and terms share one shape — a named measured value plus a band — and mirror the objectives/observations split above: claims are what the wish keeps; terms are protected readings it must never push out of band while keeping them. A broken term is a miss with the same standing as a broken claim. Bands carry the outcome's own measurement units — the reading, never a movement. The target inside a band is for evidence and reporting, never judged.
The serving fields
limits— parameters excluded from movement, and a maximum change per act; checked at plan time, a refusal names the binding onebudget— the re-act allowance after drift events; omitted means upkeep indefinitelyregime— the closing rule;standingtoday (held until closed)
An unkeepable wish is refused by name before anything acts.
Seek — the walk's shape
A commissioned search: the region to walk, and either a criterion with a budget (find_best) or neither (cover_now, which walks to extend coverage and carries no budget). Semantics and lifecycle in Wish.
type: seek
form: find_best # or cover_now — no criterion, no budget
region:
chainend.push: [-0.4, 0.4] # the dials to walk, each [lo, hi]...
chainend.gain: [1, 8, true] # ...or [lo, hi, is_int] — whole numbers only
criterion:
outcome: chainend.shift # what counts: the reading
direction: minimize # ... and the direction
budget: 12 # measured trials the walk may spend
candidates: 2 # measured launch points — counted in the budget
answer: later # family option, both types — 'now' answers in the same breath
standard: best # family option, both types — walks default to best
The walk is served, not aimed: the region's dials are a registered systemtender's own parameters, and the plan names the serving systemtender — a declaration never picks one itself.
A declared wish is a durable, withdrawable record — lifecycle, event history, and the holder's correction of a standing wish are covered in Wish.
Working Examples
Runnable, maintained examples live in the scenario library:
examples/bench/ in the godon repository — each scenario ships systemtender configs, target definitions, and the planted ground truth. The characterization scenarios are the reference implementations for the config above. For wish declarations, examples/bench/scenario-wish-stack/ is the reference — a bench whose corridors couple the aims and the protected readings, so both claims and terms bind.
See Also
- Systemtender — how configurations are executed
- Wish — what a declaration triggers, and its lifecycle
- Interference Detection — what the interference section triggers
- Getting Started — a full walkthrough against the generic bench