Observability¶
The BDPD Platform's observability layer intercepts the raw game state before it is passed to any agent, applying per-variable transformations that degrade, bucket, or suppress information. Agents never receive ground-truth state unless explicitly configured to do so.
Configuration Model¶
Each observable variable has three independent controls:
| Control | Type | Effect |
|---|---|---|
visible |
boolean | If false, the variable is set to null — agents see nothing |
noise |
float | Gaussian noise standard deviation as a fraction of the true value (0 = exact) |
resolution |
"exact" | "bucket" | "sign" |
How the (noisy) value is discretised before delivery |
The observability config is fixed at arena creation and is part of the arena's public description — agents know what they can and can't see, but cannot infer the exact values of hidden or noisy variables.
Observable Variables¶
| Variable | Default Visibility | Description |
|---|---|---|
commonsStock |
Visible | Current resource stock \(S_t\) |
commonsRatio |
Visible | \(S_t / K\) (0–1) |
regenRate |
Hidden | Regeneration rate \(r\) |
nPlayers |
Visible | Number of agents in the arena |
othersWealth |
Visible | Wealth \(w_{i,t}\) of each opponent |
othersHarvest |
Visible | Last turn's harvest of each opponent |
capitalStock (Seneca) |
Hidden | Capital \(C_t\) (Seneca mode only) |
pollutionLevel (Seneca) |
Hidden | Pollution \(P_t\) (Seneca mode only) |
Per-Player and Governance Fields¶
Beyond the commons variables above, the observation object delivered to each agent includes per-player state and (when enabled) governance fields. These are not subject to the noise/resolution pipeline — they are passed through as-is.
| Field | Since | Description |
|---|---|---|
myWealth |
v0.4 | The agent's own current wealth |
myLastHarvest |
v0.4 | The agent's harvest from the previous turn |
inbox |
v0.9 | Cheap-talk messages addressed to this agent since the last turn (array of { fromId, text, turn }) |
pacts |
v0.9 | Active pacts visible to this agent (terms, acceptances, violations) via PactRegistry.viewFor(playerId) |
reputation |
v0.9 | Compact list of other players' reputation records (violation counts, honour rate); attached only when the arena has a governance layer |
inbox, pacts, and reputation are omitted entirely when the
arena has no governance configuration — pre-v0.9 arenas and
experiments without pacts never see these fields.
Gaussian Noise¶
Noise is applied fresh each tick using the Box-Muller transform — agents cannot trivially average out the noise because the seed changes every call.
where \(\sigma\) is the noise fraction (e.g. 0.1 = ±10% standard
deviation).
noise |
Effect |
|---|---|
0.0 |
Exact value (no noise) |
0.1 |
±10% standard deviation: agent sees "82" when stock is 90 |
0.2 |
±20%: agent sees "108" when stock is 90 |
0.5 |
±50%: highly uncertain estimate |
Resolution Modes¶
After noise is applied, the value is discretised according to the
resolution setting:
exact¶
The full numeric value (post-noise), rounded to 3 decimal places.
bucket¶
Mapped to qualitative labels based on position within [min, max]:
| Fraction | Label (3 buckets) | Label (N buckets) |
|---|---|---|
| 0.00–0.33 | low |
0 |
| 0.33–0.67 | medium |
1 |
| 0.67–1.00 | high |
N-1 |
sign¶
Only the direction of change relative to the last observation is revealed:
| Change | Returned |
|---|---|
| Positive | 1 |
| Zero | 0 |
| Negative | -1 |
Per-Variable Configuration¶
{
"observability": {
"commonsStock": {
"visible": true,
"noise": 0.05,
"resolution": "exact"
},
"regenRate": {
"visible": false
},
"othersWealth": {
"visible": true,
"noise": 0.1,
"resolution": "bucket"
},
"othersHarvest": {
"visible": true,
"noise": 0.0,
"resolution": "exact"
}
}
}
Why hide regenRate by default?
Regen rate is hidden because real-world CPR users rarely know the exact regeneration function of their resource. Making it visible would enable agents to precisely compute sustainable yield — an unrealistic capability in practice.
Transform Pipeline¶
Under the hood, each variable's noise and resolution settings are
compiled into a transform pipeline — an ordered list of small
functions that the value flows through before reaching the agent.
The two built-in transforms are:
| Transform | Spec Field | Pipeline Entry |
|---|---|---|
gaussian_noise |
noise: 0.1 |
{ kind: 'gaussian_noise', std_fraction: 0.1 } |
resolution |
resolution: 'bucket' |
{ kind: 'resolution', mode: 'bucket' } |
Legacy syntax (noise, resolution as top-level fields) is translated
automatically; explicit transforms: [...] lists are appended after
the legacy entries, so adding a transforms list extends the pipeline
rather than replacing it.
Example: combined legacy + explicit pipeline¶
{
"commonsStock": {
"visible": true,
"noise": 0.5,
"transforms": [
{ "kind": "clamp", "min": 0 }
]
}
}
This compiles to: gaussian_noise(σ=0.5) → clamp(min=0). The noisy
value is clipped to non-negative before reaching the agent.
Plugin transforms¶
Plugins can register new transform types by adding entries to
OBSERVATION_TRANSFORM_REGISTRY at load time. The plugin loader
(tools/plugin-loader.mjs) handles this automatically for any file
in plugins/observation_transforms/.
Transform signature:
where params is the transform's spec object and ctx contains
runtime context ({ rng, min, max, ... }). An unknown kind in a
pipeline throws at runtime — typos fail loudly.
A working example is at examples/plugins/observation_transforms/clamp.js.
Impact on Agent Behaviour¶
The P9 experiment systematically swept observability noise on the commons stock from 0% to 100% and measured welfare and Gini:
| Noise Level | Welfare | Gini | Interpretation |
|---|---|---|---|
| 0% (perfect) | Lower | Higher | Aggressive agents time extraction precisely against exact stock |
| 35–50% | Welfare-optimal | Lower | Noise limits precision of aggressive timing without crippling conservative decisions |
| 50%+ | Lower | Higher | All agents become effectively random — no strategic differentiation |
Key finding: Some information degradation is collectively beneficial. Moderate-to-high noise (35–50%) limits the precision with which aggressive agents can time extraction, improving aggregate welfare. This is a computational demonstration of the "veil of ignorance" principle: when the most aggressive player cannot see the exact stock, the commons survives longer.
In the card game (CT3), observability noise had zero effect on heuristic agents — confirming that noise matters only when agents possess the cognitive architecture to exploit precise numerical signals. The dual-mode result (noise matters on one substrate, not the other) is itself a finding about the role of cognitive architecture.
Resolution and the Architecture of Agent Cognition¶
The resolution parameter operationalises a specific hypothesis:
does the grain of information matter as much as its accuracy?
exactresolution enables agents that can compute precise marginal utilities (built-in heuristics, LLMs with numerical reasoning)bucketresolution forces agents to reason in qualitative categories — closer to how humans perceive resource levels in practicesignresolution provides only directional signals — the most degraded information regime
The interaction between resolution mode and agent type is a promising avenue for future experimental work with human subjects and LLM agents.