Skip to content

RFC 0001 wave 1: senses (0.9.0) - #33

Merged
BitmapAsset merged 14 commits into
mainfrom
rfc1-wave1-senses
Oct 15, 2026
Merged

BitmapAsset merged 14 commits into
mainfrom
rfc1-wave1-senses

Conversation

@BitmapAsset

Copy link
Copy Markdown
Owner

Draft: RFC 0001 Design A (senses) for 0.9.0. Not ready for review or merge yet; metric a of the benchmark does not pass its bar (below).

What the block does

An optional senses block (docs/persona.md §2.11, design notes in docs/rfcs/0001-senses-and-reward.md). The host sends measured readings (a tool failure, a latency, a message tempo, a silence) in declared units with a named source. Per sense the engine keeps a normal (mean, variance, count; a half-life in events; the mean held within prior.mean ± drift_cap, the variance within [0, var_cap]) and turns each reading into a capped z-score against it, which moves moods and traits like a number input. The stance gains senses and noticed; why names the normal a reading was judged against and, with learning, what the learned weights came from (learned_from in the state).

  • Rules: undeclared senses, out-of-range numbers, flags that are not true/false and sources the sense does not list are refused at inputs.senses.<id> or inputs.src, and a refused event changes nothing; habits read sense.<id>.raw or .z, never the normal; normals move only on readings; a state whose normals leave their caps is refused; the MCP turn tools refuse events that carry readings.
  • prove reads the lowered inputs as boxes, so a habit on sense.<id>.raw holds by construction; fuzz adds readings.
  • live verify adds normals (mean, n, max_move_50); monitor draws a row per sense and an alert when a normal moved over drift_cap / 2 within 50 events; its panel of the event's inputs is now labelled INPUTS.

Personas without the block

bench/individuality/p0.py check: 653 outputs recorded with the released 0.8.2 binary before any code change (8 personas × seeds 0-2 × 4 random scripts: 9,600 live turns and as many replayed; describe, lint, fuzz and prove documents): 0 differ. 629 are byte-identical; 24 are the stderr of live runs whose scripts send a senses key, where live now names the undeclared key once; they equal the record once that line is removed. All 8 recorded strands verify, and the tutor's continues with the recorded bytes.

Tests and checks (local, Apple M4)

  • cargo test --workspace --locked (stable): 348 passed, 1 ignored; one timing test in probbit-decide failed under a load average of 16.7 and passes alone (this branch does not touch that crate). Rust 1.78: release build and test build OK. wasm32 build and playground/check.mjs OK.
  • New: the sensed golden (24 turns, byte for byte), tests/live.rs (readings logged and normals recomputed by verify, a split life equals one run, the stderr line, refusal property over 40 individuals × 300 random events: 5,083 refused and 6,917 accepted, each run byte-identical to the run without its refused events, probbit check on a sensed persona), one test per refusal row and per rule, MCP refusal in python/test_mcp.py, a monitor test.
  • persona prove|fuzz --depth 16|lint examples/persona/sensed.yaml --props examples/persona/sensed-props.json --seeds 0-99: exit 0 (fuzz: 480,817 turns, nothing found).

Benchmark (python3 bench/individuality/run.py --only a,b,d,e, 10 s; fixtures and bars frozen before any run)

metric measured bar
a1 share of probes whose host action differs between an individual's two lives 0.145 (95 % interval 0.096-0.199) >= 0.30, interval low >= 0.20 fails
a2 odds distance between two lives vs two individuals in one room 0.1307 vs 0.1329 / 0.1411 >= the smaller fails
b1 habituation zd ratio; values against an independent Python sense step 0.304149; 0 of 4,522 differ <= 0.35; 0 passes
b2 surprise ratio 3.422977 >= 2 passes
b3, b4, b5 genes, named past 100 %, 100 % of 232, 100 % of 116 100 %, >= 95 %, >= 95 % passes
d replay (240 strands, third and split copies; the 0.8.2 strand) 0 differing bytes, 0 verify failures, equal 0, 0, equal passes
e habit breaks over 9,442 stances 0 violations, 0 breaks, 72 bound 0, 0, >= 50 passes

In this release rewards pay the previous stance; at these settings a different individual moves the host action more than a different life does. The bars stay as frozen.

Not in this PR

Action-addressed feedback (learning.credit, await, for, cancel; benchmark metric c), signed rewards and the lineage budget (metric f), and a version bump.

BitmapAsset and others added 14 commits October 14, 2026 07:13
Every shipped persona and example (tutor, ops-engineer, trader-assistant, the agent-harness
retry policies, the monitor's drives demo persona) and two engine test fixtures x seeds 0-2 x
four committed random scripts of 100 events (9,600 live turns and as many replayed), plus
init, check, describe, compile, lint --seeds 0-99, and lint/fuzz/prove documents of the
shipped rules: 653 outputs, recorded with probbit 0.8.2 before any 0.9.0 change. The scripts
carry the keys 0.9.0 adds (senses, await, for, cancel) as undeclared inputs, which a persona
without the new blocks must keep listing in ignored. fixtures/MANIFEST records the binary's
sha256 and every frozen file's; p0.py check compares the binary under test.
An optional senses block (docs/persona.md 2.11): readings the host measures in declared units with
a named source, each judged against a learned normal per sense (mean, variance, count; half-life in
events; drift and variance caps anchored to a declared prior) and moving moods and traits by its
capped, dead-zoned z. Read strictly (every lint row of RFC A.1 at its path), lowered to reserved
sense_<id>_up/down number inputs and sense_<id>_c<N> condition flags; the sense step runs before the
drive step; state senses {genes, norm} checked on read (S4); stance senses and noticed; why gains the
normal clause and, with line.why_past and a learning block, the learned clause (learned_from in the
state). Refusals S1-S3, S5-S6 in the turn; props, fuzz alphabet, forcing and prove read the lowering.
A persona without the block gives every document byte for byte as before (the P0 corpus).
…out the block

verify's summary gains normals {mean, n, max_move_50} for a persona with senses; the strand logs the
readings as given; live warns once on stderr when senses reaches a persona without the block (an
undeclared input, listed in ignored). Tests: P3s (40 individuals x 300 random events mixing sources,
readings, self, clock and out-of-range readings: each run equals its run without the refused events,
byte for byte), normals recomputed by verify, a life split over two runs is one strand.
fuzz adds readings to its events (flags both ways, numbers on the grid x range and at the raw
conditions' thresholds), and patterns per reading (repeated 1, 5 and 20 times; range ends
alternating); --fixture-src labels readings too. probbit_persona_turn and probbit_live_event refuse
an event that carries readings for a persona with a senses block; the schemas declare senses.
A persona without a senses block that receives a `senses` key gets one stderr line from `live` (an undeclared
input, listed in ignored as before). The P0 scripts that send the new keys (k=1) get it at their first `senses`
event; documents, states and strands stay byte-identical. The check accepts exactly that line, once, where the
script carries `senses`, and counts those outputs apart.
examples/persona/sensed.yaml: four senses (tool failures, tool latency, message tempo, silence), two habits on raw
readings, learning on caution. The golden drives it through 24 turns with readings; sensed-props.json holds the two
habit rules (held by construction for seeds 0-99).
…their first run

The sensed persona (and a copy with every sense gene spread at 0), the seeded generator lives.py and its streams
(rooms A and B, the month-away tail, 20 probes, habituation and surprise), and the gated bars, each with its sha256
in fixtures/MANIFEST. MANIFEST's p0.py line follows the check's report of the senses warning line (its own commit,
logged in the README).
…efuses readings

tests/live.rs: readings logged as used and verify recomputing the normals (max_move_50), a sensed life split across
runs equal to one run, the one stderr line for readings sent to a persona without the block, P3s (40 individuals x
300 random events: the run equals the run without its refused events, byte for byte), probbit check on a sensed
persona. tests/persona.rs: the sensed golden byte for byte. python/test_mcp.py: S7 through both turn tools.
…med inputs

Per declared sense: its normal as a tick over the range, this event's reading, z and n; an alert when a normal moved
over drift_cap / 2 within 50 events (terminal board and page). The panel of the event's inputs was labelled SENSES;
it is now INPUTS, so it does not collide with the senses block (the two pinned frames change by that label only).
twins.py (a: 40 individuals x rooms A and B, 20 probes each), habituation.py (b: the engine against an independent
implementation of the sense step, surprise, genes, and the named past rendered from each document), replay.py (d:
third and split copies byte for byte, live verify, the P0 strand), habits.py (e: every stance against the habits).
run.py checks fixtures/MANIFEST first and exits 0 only when every gated bar passes.
…, BENCHMARKS §10, the bench README

persona.md: §2.11 Senses (schema, refusals, the sense step, rules S1-S7, the stance and why), and senses in §3 step 2,
§4, §5.1, §5.6, §5.7 and §7; §5.8 names the inputs panel and adds the senses rows; §6 adds the limit. The RFC is
docs/rfcs/0001-senses-and-reward.md. BENCHMARKS §10 and the bench README record the measured bars, metric a's
failure included.
* bench/individuality: p0 passes repository-relative paths; EXPECTED re-recorded with 0.8.2

A fuzz document that finds a counterexample names the persona path as given in its
explain and replay strings, so fuzz/adaptive-fixture depended on the clone's location.
Every command already runs at the repository root; personas and rules are now passed
relative to it. Re-recorded with the same released 0.8.2 binary: one line changed
(fuzz/adaptive-fixture, 43,646 bytes). Logged in the bench README.

* persona: learning.credit read strictly; await/for/cancel read and echoed; the credit step and B-R1..B-R6 (WIP)

* live: control lines close open decisions, verify summary open, verify --actions; mcp refuses cancel; the one stderr line for credit keys without the block; p0 check accepts it (WIP)

* bench/individuality: MANIFEST p0.py line and the README log for the credit-keys stderr line

* fuzz: payments resolved against each state (late, reordered, previous, closed, unknown, mismatch; cancels), refused ones change nothing, exported scripts concrete; monitor: open decisions with eligibility, the payment and closures (WIP)

* examples/agent-harness: the addressed-outcome variant (addressed.json, late.py, its test) and its cross-OS golden

* fuzz: re-running a shrunk counterexample with its payments resolved is not a searched turn, and a persona without a credit block keeps its script as found

* persona, live: tests for action-addressed feedback: eligibility at ages 0/3/10/32/127, expiry at 128, eviction, every refusal row B-R1..B-R6 (and byte for byte in live), absorbed, the strict credit state, e = 1 equals 0.8 on every shipped learning persona, control lines, P3c 40 x 300 with a model of the bookkeeping, probbit check on a credit persona

* docs: action-addressed feedback (persona.md §2.8 credit, §4 stance credit, §5.1 state, §5.6 payments in fuzz, §5.7 control lines and credit in live, §5.8 monitor, §7), CHEATSHEET, CHANGELOG, the RFC copy (question 2 decided, B-R6), man page for live verify --actions

* bench/individuality: metric c frozen before its first run (dispatch.yaml with a credit block and its no-block twin, the seeded draws and their generator, bars-c.json; the plain-inputs control persona for metric a); credit.py and run.py run it

* bench/individuality: credit.py runs its 50 host-driven runs side by side, re-runs only the runs that refused events, and reports evictions, expiries and payment ages per run (logged in the README)

* bench/individuality: twins_addressed.py re-measures metric a with Design B on the frozen fixture and bars, with the plain-inputs control; BENCHMARKS.md §11 (metric c and the re-measured twins)

* examples/agent-harness: README section for the addressed variant; docs, bench and comments: plainer wording (law check)

* bench/individuality: README intro: this release runs metric c; metric a's frozen streams and twins_addressed.py

* bench/individuality: record the frozen metric a result on this release

---------

Co-authored-by: BitmapAsset <bitmapasset@users.noreply.github.com>
@BitmapAsset
BitmapAsset marked this pull request as ready for review October 15, 2026 23:50
@BitmapAsset
BitmapAsset merged commit 9660e0f into main Oct 15, 2026
15 checks passed
@BitmapAsset
BitmapAsset deleted the rfc1-wave1-senses branch October 15, 2026 23:50
@BitmapAsset
BitmapAsset restored the rfc1-wave1-senses branch October 15, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant