Repository navigation
M-MEM: multi-event-per-tick mapper (merge + arbitration) - #11
Merged
Merged
Conversation
…mapper Engine-only capability (reusable, not moral-specific) that lifts the one-RawEvent-per-tick constraint (run_scenario's events_by_t dict silently drops same-tick collisions). Records the design: events list, map+merge with per-source attribution, deterministic primary-provoker arbitration, opt-in multi-event trace field, ≤1-event/tick byte-identical. Slices M-MEM.0 plumbing -> .1 arbitration -> .2 fan-out helper. Unblocks the moral M-J.3.3 witness fan-out (review R7) once merged to main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
…cal for <=1 event/tick
Lift the one-RawEvent-per-tick constraint. run_scenario's events_by_t was a dict {ev.t: ev}, silently
dropping same-tick collisions; tick() took a single event and every per-tick signal derived from it.
- schema: EffectiveInputVector is now dict[str, list[SemanticInput]] -- a channel may carry SEVERAL inputs
in one tick (multiple sources firing the same channel). A single-event tick yields one-element lists.
- simulation.run_scenario: group events sharing a tick into a list (scenario order), not a colliding dict.
- simulation.tick(events): accepts a bare RawEvent / list / None (backward-compatible); maps + filters EACH
event and merges into eff (channel -> list). A PRIMARY event (first; M-MEM.1 will arbitrate the primary
provoker) drives the per-source reactive signals (provocation/kindness/bystander/refractory/reaction_target/
bookkeeping) -- byte-identical for <=1 event. history_log appends ALL events.
- update.compute: global gains, event_sources, and relation gains sum over the per-channel input lists.
- simulation provocation/stressor/kindness helpers iterate the lists.
- debug._inputs_dict: a single input on a channel serializes as the bare object (byte-identical with pre-
M-MEM goldens); several inputs serialize as a list (only appears in multi-source traces).
- tests/test_multi_event.py (3): two same-tick events from different sources BOTH land (today one dropped);
empty tick is idle; single-event trace keeps the scalar event field.
Full suite 288 passed +4 skipped; goldens byte-identical (test_tick_golden unchanged); ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
When several events land on one tick, the per-source reactive signals (reaction_target, last_provocation_source, refractory/bystander policy) must key on the RIGHT source. M-MEM.0 used the first event; M-MEM.1 elects the STRONGEST provoker as `primary`. - simulation._provocation_score(event, ev_eff): how strongly ONE event provokes from its OWN filtered inputs -- the anger/frustration contribution (case 1) or, for a gesture from a resented source, that resentment (case 2); 0 for a sourceless stressor or benign non-resented gesture. Mirrors _event_is_provocation for a single event. - tick: during the perception merge, track the max-score provoker (strictly-greater so ties keep the earlier scenario-order event) and elect it `primary`; fall back to the first event when nothing provokes (benign gesture / stressor / activity). A <=1-event tick is unchanged -> byte-identical. - tests/test_multi_event.py (2): the strongest of two same-tick insults becomes primary (weak listed first); an insult beats a co-occurring benign gesture for primary (gesture listed first). Full suite 290 passed +4 skipped; goldens byte-identical; ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
….2 to the consumer The reusable engine seam (several events on one tick, correctly merged + arbitrated) is complete with M-MEM.0/.1. The fan-out EXPANSION rule (a public accusation => each witness emits suspicion) is domain logic and belongs to the moral consumer M-J.3.3, not a generic engine helper -- so M-MEM.2 adds no speculative engine code. Branch is merge-ready: merging to main unblocks M-J.3.3. - tests/test_multi_event.py: add the witness-fan-out shape test (one accuser + three witnesses on one tick, every source's deposit lands) -- a concrete proof the seam suffices without an engine fan-out helper. Suite 290 passed +4 skipped; goldens byte-identical; ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
…docs Update the spec + block diagrams + README so the event model matches the implemented engine: a tick may carry 0/1/MANY events, each mapped+filtered per event and MERGED into the effective input (channel -> list of inputs, summed by update), with the per-source reactive signals keyed on the strongest provoker (primary). A <=1-event tick is byte-identical. - spec_v1: EffectiveInputVector is dict[str, list[SemanticInput]]; tick order loops events + merges + picks primary; update equation sums a channel's inputs; section 1/5 notes; stage-2 note records the M-MEM seam. - diagrams: simulation (invariant + tick steps 3/10), mapper (per-event, merged in tick), update (eff is list-valued; gain * sum-of-inputs), relation_filter (per-event filter, merge in tick), wiring (note + seam). - README: M-MEM bullet under current work. No HTML architecture doc exists (the only .html is the regenerable burst-regression report). No code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
Integrate the advanced main (believability/eval work, PRs #9/#10) before merging M-MEM back. Conflict-free. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016rWVvvhWijSuLMEhzjiUhu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M-MEM — multi-event per tick
Lifts the one-
RawEvent-per-tick constraint so a tick can carry 0, 1, or many events, each mapped+filtered and merged into the effective input, with deterministic multi-source arbitration. Engine-only, reusable; unblocks the moral M-J.3.3 witness fan-out (review R7).Slices
EffectiveInputVector→dict[str, list[SemanticInput]];run_scenariogroups same-tick events;tickmaps+merges every event;updatesums per-channel input lists;debug._inputs_dictserializes one input as the bare object (byte-identical) and several as a list._provocation_scoreelects the strongest provoker asprimary(ties keep scenario order); the per-source reactive signals key on it.docs/m_mem_PLAN.md).Invariant
A ≤1-event tick is byte-identical with the pre-M-MEM engine — existing goldens unchanged.
Tests
Full suite 309 passed, 4 skipped after integrating current
main; goldens byte-identical; ruff clean. Newtests/test_multi_event.py(6): two same-tick events both land; strongest-provoker arbitration; gesture-vs-provoker; N-source fan-out shape; empty/single-event paths.🤖 Generated with Claude Code