Spaces:
Running on Zero
Running on Zero
| """ContextBuilder — the shared blackboard reaches the prompt (ADR-0023). | |
| Before this, an agent saw only the world text and its own past lines, so small | |
| models looped on one clue and never reacted to anyone. The builder now surfaces | |
| ``projection.agent_notes`` (peers' public lines) so the table is actually shared. | |
| """ | |
| from __future__ import annotations | |
| from src.core.context import ContextBuilder | |
| from src.core.projections import StageProjection | |
| def test_blackboard_surfaces_peer_lines(): | |
| proj = StageProjection(goal="g", current_scene="scene") | |
| proj.agent_notes = ["spy-cara: a morning fuel", "spy-nil: warm and comforting"] | |
| prompt = ContextBuilder().build(agent_name="spy-bex", persona="(bex)", projection=proj, all_events=()) | |
| assert "WHAT'S BEEN SAID" in prompt | |
| assert "a morning fuel" in prompt | |
| assert "warm and comforting" in prompt | |
| # nudges toward a fresh contribution, not an echo | |
| assert "new angle" in prompt.lower() and "echo" in prompt.lower() | |
| def test_blackboard_prompts_the_first_speaker_to_open(): | |
| proj = StageProjection(goal="g", current_scene="scene") # no notes yet | |
| prompt = ContextBuilder().build(agent_name="spy-cara", persona="(cara)", projection=proj, all_events=()) | |
| assert "WHAT'S BEEN SAID" in prompt | |
| assert "you go first" in prompt.lower() | |
| def test_persona_and_goal_still_lead(): | |
| proj = StageProjection(goal="catch the spy", current_scene="scene") | |
| prompt = ContextBuilder().build(agent_name="a", persona="I am A.", projection=proj, all_events=()) | |
| # IDENTITY and SHARED GOAL must still come before the blackboard. | |
| assert prompt.index("I am A.") < prompt.index("catch the spy") < prompt.index("WHAT'S BEEN SAID") | |
| def _spoke(actor, text, turn=1, kind="agent.spoke"): | |
| from src.core.events import Event | |
| return Event(run_id="r", turn=turn, kind=kind, actor=actor, payload={"text": text}) | |
| def test_judge_gets_the_full_ordered_transcript_not_just_the_tail(): | |
| # A judge rules on the WHOLE discussion — every spoken line, in order — not the | |
| # recency-biased blackboard tail a worker reacts to (ADR-0023 follow-up). | |
| events = tuple(_spoke("debater-a" if i % 2 == 0 else "debater-b", f"point number {i}", turn=i) for i in range(12)) | |
| proj = StageProjection(goal="g", current_scene="scene") | |
| prompt = ContextBuilder().build( | |
| agent_name="debate-judge", persona="(judge)", projection=proj, all_events=events, role="judge" | |
| ) | |
| assert "THE EXCHANGE TO JUDGE" in prompt and "WHAT'S BEEN SAID" not in prompt | |
| # every line is present, oldest first | |
| assert "point number 0" in prompt and "point number 11" in prompt | |
| assert prompt.index("point number 0") < prompt.index("point number 11") | |
| def test_judge_transcript_excludes_private_thoughts(): | |
| # Thoughts are the mind-reader's alone; a judge rules on what was SAID. | |
| events = (_spoke("a", "said aloud"), _spoke("b", "secret scheming", kind="agent.thought")) | |
| prompt = ContextBuilder().build( | |
| agent_name="j", | |
| persona="(judge)", | |
| projection=StageProjection(current_scene="s"), | |
| all_events=events, | |
| role="judge", | |
| ) | |
| assert "said aloud" in prompt | |
| assert "secret scheming" not in prompt | |
| def test_worker_gets_blackboard_not_transcript(): | |
| proj = StageProjection(current_scene="scene") | |
| proj.agent_notes = ["a: hello there"] | |
| prompt = ContextBuilder().build( | |
| agent_name="w", persona="(w)", projection=proj, all_events=(_spoke("a", "hello there"),), role="worker" | |
| ) | |
| assert "WHAT'S BEEN SAID" in prompt and "THE EXCHANGE TO JUDGE" not in prompt | |
| def test_memory_does_not_repeat_a_line_already_in_the_discussion(): | |
| # A spoken line shown in the blackboard must not be printed again in YOUR MEMORY: | |
| # the union is unchanged, we just don't duplicate (blackboard=recent, memory=earlier). | |
| proj = StageProjection(current_scene="scene") | |
| proj.agent_notes = ["a: the bench needs shade"] | |
| memory_text = "[turn 003][agent.spoke] the bench needs shade\n[turn 001][world.observed] an older beat" | |
| prompt = ContextBuilder().build( | |
| agent_name="w", persona="(w)", projection=proj, all_events=(), memory_text=memory_text, role="worker" | |
| ) | |
| # the duplicated line appears once (in the blackboard), the unique earlier beat survives in memory | |
| assert prompt.count("the bench needs shade") == 1 | |
| assert "an older beat" in prompt | |
| def test_memory_shows_a_pointer_when_fully_covered_by_the_discussion(): | |
| proj = StageProjection(current_scene="scene") | |
| proj.agent_notes = ["a: only line"] | |
| prompt = ContextBuilder().build( | |
| agent_name="w", | |
| persona="(w)", | |
| projection=proj, | |
| all_events=(), | |
| memory_text="[turn 003][agent.spoke] only line", | |
| role="worker", | |
| ) | |
| assert "nothing beyond the exchange above" in prompt | |
| def test_a_peer_thought_never_reaches_another_agent(): | |
| # A spoken event carries a private `thought` (the mind-reader content). It must | |
| # ride only on its own payload — peers see the public `text`, never the thought. | |
| from src.core.events import Event | |
| proj = StageProjection(goal="g", current_scene="scene") | |
| proj.apply( | |
| Event( | |
| run_id="r", | |
| turn=1, | |
| kind="agent.spoke", | |
| actor="spy-nil", | |
| payload={"text": "warm and comforting", "thought": "I'm the spy — keep it vague!"}, | |
| ) | |
| ) | |
| assert any("warm and comforting" in n for n in proj.agent_notes) | |
| assert not any("spy" in n.lower() and "vague" in n.lower() for n in proj.agent_notes) | |
| prompt = ContextBuilder().build(agent_name="spy-bex", persona="(bex)", projection=proj, all_events=()) | |
| assert "warm and comforting" in prompt | |
| assert "keep it vague" not in prompt | |