Skip to content

RFC 0001 wave 2: action-addressed feedback (0.9.0) - #34

Merged
BitmapAsset merged 15 commits into
rfc1-wave1-sensesfrom
rfc1-wave2-credit
Oct 15, 2026
Merged

BitmapAsset merged 15 commits into
rfc1-wave1-sensesfrom
rfc1-wave2-credit

Conversation

@BitmapAsset

Copy link
Copy Markdown
Owner

RFC 0001 Design B, on top of the senses branch (base rfc1-wave1-senses, so the diff shows Design B only). Draft for CI on Linux, macOS and Windows.

What it adds

An optional learning.credit block (docs/persona.md §2.8, docs/rfcs/0001-senses-and-reward.md §B):

  • Event keys await (the host will report this decision's outcome), for (T, or {turn: T, stance: "sha256:..."} bound to the paid stance's digest) and cancel (from env or a person). Read, echoed in inputs, never compiled.
  • The credit step, after the drive step: open decisions expire after 4 half-lives, cancels close theirs, feedback pays the named decision (or the previous stance when that is unambiguous) with its own credit, scaled by an eligibility 0.5^((t − n − 1) / half_life_events); beyond max_open the oldest is evicted. At e = 1 the step is 0.8's.
  • Refusals B-R1 to B-R6 (ambiguous, not open, for without feedback, bad cancel, malformed, digest mismatch), each refused whole at its path. The RFC copy records question 2 as decided (B-R6).
  • Surfaces: stance credit, state open_credit / prev_stance / control_closed; control lines close open decisions; live verify adds open, and live verify --actions FILE compares a host's action log with the released levels; monitor draws the open decisions with their eligibility; fuzz adds late, reordered, duplicated, cancelled and unknown payments; the MCP turn tools refuse cancel; examples/agent-harness/addressed.json with late.py and a cross-OS golden.

A persona without the block is unchanged: the P0 corpus (653 outputs of the released 0.8.2 binary) gives 0 differing outputs.

Tests

  • Unit: eligibility at ages 0, 3, 10, 32 and 127 (1, 0.937084, 0.805245, 0.5, 0.063869) with the step scaled by it; expiry at age 128; eviction; every refusal row at its path; absorbed; the strict credit state; e = 1 equals 0.8 (Δ and learned_from after every turn) on every shipped learning persona.
  • Live: P3c, 40 individuals × 300 random events with random payments, where a separate model of the bookkeeping predicts every acceptance and refusal (0 disagreements) and each run equals the run without its refused events, byte for byte; each refusal row byte for byte; control lines; probbit check on a credit persona.
  • A fuzz fix found on the way: re-running a shrunk counterexample with its payments resolved no longer counts as searched turns (the 0.5.0 tutor golden's count is back).

Benchmark (bench/individuality, BENCHMARKS.md §11)

  • Metric c, frozen before it was ever run: c1 100 % of the applied step equal to an independent Python learner's (8,393 payments), c2 100 % of 15,000 events with its Δ, c3 100 % of 3,802 outcomes whose decision was open, c4 100 % of 2,793 duplicate, cancelled, unknown and ambiguous payments refused, 50 of 50 runs byte-identical without their refused events. The stream turned out busier than designed: 67 % of the addressed decisions were evicted before their outcome arrived (reported, not gated; the fixture is not changed).
  • Metric a re-measured with Design B on the frozen fixture and bars (twins_addressed.py): a1 0.1725 (interval 0.1175 to 0.235) and a2 0.1223 against 0.1366, still below the bars; a plain-inputs control gives 0.155 and 0.0295.
  • Wall time: on a heavily loaded machine run.py --only c,d,e took 422 s (44.5 s of CPU), over its 60 s bar; c, d and e pass.

Not in this PR: receipts and the lineage drift budget (Design C).

…-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.
… --actions; mcp refuses cancel; the one stderr line for credit keys without the block; p0 check accepts it (WIP)
…, closed, unknown, mismatch; cancels), refused ones change nothing, exported scripts concrete; monitor: open decisions with eligibility, the payment and closures (WIP)
…, late.py, its test) and its cross-OS golden
…s not a searched turn, and a persona without a credit block keeps its script as found
…es 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
…edit, §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
…aml 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
…ide, re-runs only the runs that refused events, and reports evictions, expiries and payment ages per run (logged in the README)
…ign B on the frozen fixture and bars, with the plain-inputs control; BENCHMARKS.md §11 (metric c and the re-measured twins)
…s, bench and comments: plainer wording (law check)
@BitmapAsset
BitmapAsset marked this pull request as ready for review October 15, 2026 23:30
@BitmapAsset
BitmapAsset merged commit 55e0db6 into rfc1-wave1-senses Oct 15, 2026
15 checks passed
@BitmapAsset
BitmapAsset deleted the rfc1-wave2-credit branch October 15, 2026 23:30
@BitmapAsset
BitmapAsset restored the rfc1-wave2-credit branch October 15, 2026 23:31
BitmapAsset added a commit that referenced this pull request Oct 15, 2026
* bench/individuality: P0 corpus frozen with the released 0.8.2 binary

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.

* persona: senses (RFC 0001 Design A)

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).

* live: readings in events, verify normals, a warning for readings without 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 and mcp: readings in fuzz events, MCP refuses readings (S7)

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.

* bench/individuality: p0 check reports the senses warning line apart

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: the sensed persona, its golden and its rules

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).

* bench/individuality: fixtures of metrics a, b, d and e frozen before 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).

* tests: senses in live, the refusal property, the sensed golden, MCP refuses 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.

* monitor: a row per sense, the moved-normal alert, the inputs panel named 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).

* bench/individuality: run.py with metrics a, b, d and e

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.

* docs: senses (persona.md §2.11), the RFC, CHEATSHEET, CHANGELOG 0.9.0, 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.

* persona: a test comment names the RFC's arithmetic, not an internal file; docs wording

* examples, docs: plainer wording of what a sense does

* RFC 0001 wave 2: action-addressed feedback (0.9.0) (#34)

* 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>

---------

Co-authored-by: BitmapAsset <bitmapasset@users.noreply.github.com>
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