feat(0.9.1): Wire-A — cluster-bias annotation prompt section#253
Merged
Conversation
Stage 2 of release_0_9_1.md. The interim signal-surfacing fix for the Roy-2c finding: NAc has the right tool keys (sense_food_source) but wrong cluster keys (priming clusters are structurally disjoint from test-fixture clusters under the LinguisticEncoder gap). The llm-primary proposer never sees the cluster-keyed bias map otherwise. Wire-A renders agent-wide aggregated NAc reward bias per tool into the LLM prompt at IMPORTANT priority. The aggregation is deliberately agent-wide (not active-cluster-restricted) because Roy-2c proved active clusters disjoint from priming clusters; restricting Wire-A to the active-cluster intersection reproduces exactly the bug it exists to fix. Per the plan's revised 2026-05-13 design: - NAc.get_agent_tool_biases(agent_id=, top_n=5): aggregates across all clusters per agent, keeping max(|bias|) per tool, returning sorted by |bias| descending with stable tie-breaker. - compose_cluster_bias_annotation_section(): renders top-N as a structured block with 5 bands (strongly rewarding / mildly rewarding / neutral / mildly aversive / strongly aversive). All-neutral blocks are skipped (no signal); mixed blocks preserve neutral entries so the ordering itself is informative. - StructuredContext.cluster_bias_annotations field: additive, populated upstream at the LLM-submission site in agent_loop.py. - PromptBuilder._add_cluster_bias_annotation_section: reads the context field, renders via the composer, adds to budgeter at IMPORTANT priority (between entity_context and guidance sections). - Producer site at agent_loop.py:~2761: reads MAXIM_DISABLE_CLUSTER_BIAS_ ANNOTATION env var (default OFF = annotation ON). Truthy values (1/true/t/yes/y/on, case-insensitive) disable the annotation for the Roy-3 ablation iteration. - Conftest autouse scrub per CLAUDE.md "opt-in env vars in hot startup paths need autouse scrubs" rule. Frozen contract impact: none. _cluster_reward_bias already persists; we're adding a read path. No new dataclass fields on frozen types (StructuredContext is mutable). Test surface (34 tests, 4 layers): - Layer 1 (NAc.get_agent_tool_biases): 8 tests including the Roy-2c regression guard (disjoint-cluster scenario must still produce annotation), max(|bias|) aggregation, per-agent isolation, empty agent_id rejection. - Layer 2 (bias_to_band + composer): 10 tests pinning all 5 band boundaries (0.5 / 0.1 / -0.1 / -0.5 inclusive/exclusive semantics), tool: prefix stripping, mixed-neutral preservation. - Layer 3 (PromptBuilder helper): 3 tests covering None/empty short-circuit + IMPORTANT priority. - Layer 4 (env-var gate semantics): 12 parametric tests covering the truthy-value match table the producer reads. All 34 Wire-A tests pass. Ruff clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-lens pre-merge review of feat/0-9-1-wire-a landed one Critical
bio-fidelity finding and four cross-confirmed Important findings. No
blockers from either lens; folding before opening the PR per
feedback_review_before_ship.md.
Critical (bio-fidelity):
- _cluster_reward_bias had no per-tick decay. Without it, Wire-A's
annotation becomes a permanent fossil of every reward the substrate
ever saw — claiming "from prior experience" while actually being
"from forever ago." The bio reviewer correctly flagged this as a
by-accretion contamination of the substrate-voice thesis.
Fix: NAc.decay_cluster_reward_biases() mirrors
decay_goal_reward_biases (bidirectional, abs-value prune below
0.001), wired into the per-tick decay block in agent_loop.py
alongside decay_reward_biases() and decay_goal_reward_biases().
4 new tests in TestClusterBiasDecay covering shrinkage, prune
threshold, negative-bias preservation, empty-map no-op.
Important (architecture):
- except Exception in the producer was a band-aid. Per CLAUDE.md
"loud-failure recurring bugs can stay on helper-discipline; silent-
failure bugs in correctness-critical paths jump to structural
enforcement." Narrowed to except ValueError so an invalid agent_id
surfaces as WARNING, not silent annotation-off. Other exceptions
propagate — the producer is on the LLM-submission hot path and a
real bug here is Roy-3 evidence-integrity-critical.
- Truthy-set definition was duplicated between agent_loop.py and the
test. Extracted to TRUTHY_DISABLE_VALUES + annotation_disabled_via_
env() in prompts/cluster_bias_annotation.py. The conftest scrub,
the producer, and the test suite now all consult the same constant.
Important (cross-confirmed):
- The 34-test suite verified the truthy-set mathematically but did
not exercise the producer-block end-to-end. New
TestProducerSiteSemantics covers: producer-populates-context,
empty-agent-id-raises-ValueError, env-var-disable-short-circuits,
no-NAc-skips-quietly.
Important (bio-fidelity):
- The 5-band bias-to-text mapping is hand-coded thresholds. Added a
"Why hand-coded bias bands are bio-defensible" section to the
module docstring: bias values are substrate-earned via
reward_bias_alpha accumulation; band labels are display-layer
translation; no band-derived signal flows back into encoders /
EC / NAc / any substrate write path. Closes the contamination
audit trail per feedback_interim_contamination.md.
Nice-to-have:
- compose_cluster_bias_annotation_section's max() now uses
default=0 so a future-caller predicate filtering rendered to
empty doesn't raise.
- Producer docstring clarifies the deliberation-cycle reuse pattern:
Wire-A's read fires once per build_context, not per submission, so
cycles 2-N see the snapshot at first submission. Bias change within
a 1-tick deliberation cycle is bounded by alpha=0.15 per call — the
stale-by-one-tick read is acceptable and avoids per-cycle NAc lock
contention.
Total test count: 47 (up from 34). All passing.
Deferred to follow-up:
- Annotation-tone refinement ("substrate associations" vs "felt
familiarity") — subjective rendering decision; defer to user.
- Budget-eviction regression test under WMS-at-capacity — natural
surface test is Roy-3, not a unit test.
- Section name singular/plural mismatch (cosmetic).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
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.
Summary
Stage 2 of
release_0_9_1.md. The interim signal-surfacing fix for the Roy-2c finding: NAc's_cluster_reward_biasmap has the right tool keys (tool:sense_food_source) but the wrong cluster keys (priming clusters are structurally disjoint from test-fixture clusters under the LinguisticEncoder gap). The llm-primary proposer never reads the cluster-keyed bias map otherwise.Wire-A renders agent-wide aggregated NAc reward bias per tool into the LLM prompt at IMPORTANT priority. The aggregation is deliberately agent-wide (NOT active-cluster-restricted) per the plan's 2026-05-13 revision — restricting to the active-cluster intersection reproduces exactly the bug Wire-A exists to fix.
What ships
src/maxim/decisions/nac.pyNAc.get_agent_tool_biases(agent_id=, top_n=5)+NAc.decay_cluster_reward_biases()(per-tick decay, added in fold)src/maxim/prompts/cluster_bias_annotation.pyTRUTHY_DISABLE_VALUESconstant +annotation_disabled_via_envparsersrc/maxim/agents/bus.pyStructuredContext.cluster_bias_annotations: list[tuple[str, float]] | Nonefieldsrc/maxim/agents/prompt_builder.pyPromptBuilder._add_cluster_bias_annotation_sectionat IMPORTANT priority, betweenentity_contextandguidancesrc/maxim/runtime/agent_loop.pyMAXIM_DISABLE_CLUSTER_BIAS_ANNOTATION, callsnac.get_agent_tool_biases. Per-tickdecay_cluster_reward_biases()(fold). Narrowexcept ValueError(fold) — loud-fail on misconfigured per-agent stashtests/conftest.pyMAXIM_DISABLE_CLUSTER_BIAS_ANNOTATIONtests/unit/test_wire_a_cluster_bias_annotation.pyCLAUDE.mdTotal: +906 / -1, 8 files.
Two-lens pre-merge review
Per
feedback_review_before_ship.md, spawned parallel architecture + bio-fidelity reviews before opening this PR. No critical blockers from either lens. Folded one Critical + four Important findings into commitbee42ca:_cluster_reward_biashad no per-tick decay → Wire-A becomes a permanent fossilNAc.decay_cluster_reward_biases()+ wired into per-tick block alongside the other decay calls. 4 new tests.except Exceptionswallows misconfigured-agent-id errors that should surface for Roy-3 integrityexcept ValueErrorwith WARNING log.TRUTHY_DISABLE_VALUES+annotation_disabled_via_env()shared parser.TestProducerSiteSemantics(4 tests).max([])defensivedefault=0Deferred (subjective / out-of-scope):
"Substrate associations"vs"Felt familiarity") — subjective; user override later.Frozen contract impact
None.
_cluster_reward_biasalready persists; we're adding a read path + per-tick decay path.StructuredContextis a plain@dataclass(not frozen). No new persisted dataclass fields, no_format_versionbumps, no signature changes on frozen types.Stage 0b/0c dependency
Wire-A reads
_cluster_reward_biaswhich is populated by the existingupdate_cluster_rewardwrite path. Stages 0b/0c are Roy-3 MEASUREMENT telemetry only — no runtime dependency. Shipping Wire-A first is safe; 0b/0c can land in a separate PR before Roy-3 begins.Test plan
python -m pytest tests/unit/test_wire_a_cluster_bias_annotation.py -v— 47 passed (8 NAc aggregation + 8 bias-band boundary + 5 composer + 3 PromptBuilder helper + 15 env-var gate + 4 decay + 4 producer-site).python -m pytest tests/unit/test_wire_a_cluster_bias_annotation.py tests/unit/test_nac*.py tests/unit/test_prompt_builder*.py -q— 172 passed (no regression).python -m pytest tests/ -x -q -m "not slow" --ignore=tests/integration/test_memory_hub.py— 6607 passed (full fast suite before fold; fold added pure test additions).ruff check + ruff formatclean on touched files.test_disjoke_cluster_regression_guard): a sim where active EC clusters are DISJOINT from priming clusters MUST still produce the annotation block. Pinned.release_0_9_1.mdStage 5. NOT this PR.What's next in 0.9.1
Per
release_0_9_1.md:🤖 Generated with Claude Code