Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,59 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Added — Nucleus Basalis Phase 1 (schema + read+CRUD tools)

Pairs with the Locus Coeruleus subsystem (sibling PR on
`brain-regions-lc-phase-1`). NB-ACh complements LC-NE as the dual
gain/attention control axes the May 15 brain-region coverage audit
flagged as missing. LC fires on surprise (broadly, indiscriminately);
NB fires on attention shifts (target-locked, narrow). NE widens the
aperture; ACh sharpens the focus inside it.

Phase 1 is **inspection-only / additive** per the established Phase
pattern (Phase 1 schema → Phase 2 shadow → Phase 3 closed-loop → Phase
4 enforcement). No behavior change to retrieval, write gates, or any
existing subsystem.

- **Migration 068 — `db/migrations/068_nucleus_basalis.sql`** adds 3
tables (`nb_attention_targets`, `nb_firings`, `nb_state`) plus an
`acetylcholine REAL DEFAULT 0.5` column on `bg_modulators` (the 4th
neuromod dial; mirrors `tonic_da`, `lc_ne`, `serotonin`). 4 thalamic
sectors seeded as targets (cognitive, episodic, semantic,
pii_sensitive). Single-row `nb_state` seed (id=1, mode='tonic_mid',
ach_reservoir=0.5). Idempotent; rollback DDL in header.

- **`agentmemory.mcp_tools_nucleus_basalis`** — 5 MCP tools:
- `nb_status(agent_id=None)` — current state + 24h firing summary +
last 5 firings
- `nb_register_target(name, channel_kind, default_ach_gain, ...)` —
idempotent UPSERT on `nb_attention_targets`
- `nb_fire(target_name, attention_magnitude, mode='phasic', ...)` —
record cholinergic broadcast; updates `nb_firings` + `nb_state`.
Does NOT yet write `bg_modulators.acetylcholine` (Phase 2).
- `nb_attend_sector(sector_name, attention_magnitude, ...)` —
convenience wrapper resolving sector → target
- `nb_signal_history(limit, since, agent_id, target_id)` — paginated
firing log

- **Tests** — `tests/test_mcp_tools_nucleus_basalis.py`. 10 tests
covering migration seeds, empty-state defaults, idempotent target
registration, fire round-trip + state update, sector wrapper
resolution, mode/channel-kind validation, and history
filtering/pagination.

- **Design proposal** — `docs/proposals/nucleus_basalis.md` lays out
the 4 biological invariants, architectural placement diagram, and
Phase 2/3/4 sketch (NOT in this PR).

- **Coverage tracker** — `docs/proposals/brain_region_coverage.md`
updated: NB row gains a "Phase 1 shipped" footnote.

Phase 2 (separate PR, daytime) wires NB into the shadow consult at
`mcp_server.py:3265` to fire on `thalamic_salience` activations above
threshold and broadcast ACh delta. Phase 3 closes the loop. Phase 4
enforces.

### Added — issue #116 Phase 1-A: retrieval pathway log

External architecture memo (issue #116, "Thalamus, Basal Ganglia, and
Expand Down
3 changes: 2 additions & 1 deletion MCP_SERVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ docker run -v ~/.agentmemory:/data -e BRAIN_DB=/data/brain.db brainctl
The `CMD` defaults to `brainctl-mcp`, so the container runs the MCP
server over stdio.

## Available Tools (260)
## Available Tools (265)

| Tool | Description |
|------|-------------|
Expand Down Expand Up @@ -164,6 +164,7 @@ server over stdio.
| Insula (Phase 1, interoception) | `insula_sample`, `insula_state`, `insula_subscribe`, `insula_check_triggers` | Self-state vector (write_pressure, retrieval_strain, consolidation_debt, embedding_health, attention_load, certainty) with EMA baseline + deviation. Subscriber registry routes signals to subsystems |
| PFC sub-regions (Phase 1, named slots) | `pfc_slot_set`, `pfc_slot_get`, `pfc_status` | 4 named slots per agent: dlPFC (active task), vmPFC (outcome-utility), OFC (realized-outcome log), frontopolar (meta-monitor). Mostly aggregation |
| Entorhinal grid (Phase 1, conceptual indexing) | `entorhinal_activate`, `entorhinal_lookup`, `entorhinal_status` | 48 grid cells across 3 scales (fine/medium/coarse). Deterministic hash maps content → cell activations; sub-linear pattern lookup |
| Nucleus Basalis (Phase 1, ACh attention broadcaster) | `nb_status`, `nb_register_target`, `nb_fire`, `nb_attend_sector`, `nb_signal_history` | Basal-forebrain cholinergic broadcaster — target-locked attention complement to LC's broad NE arousal. Phase 1 ships schema (4 thalamic sectors pre-seeded as targets) + read+CRUD tools. `bg_modulators` gains 4th dial (`acetylcholine`). Phase 2 wires the shadow consult (see `docs/proposals/nucleus_basalis.md`) |

### Tier 3: Specialist (~150 tools)

Expand Down
107 changes: 107 additions & 0 deletions db/migrations/068_nucleus_basalis.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
-- Migration 068: nucleus basalis subsystem — Phase 1 schema
--
-- Pairs with migration 067 (locus coeruleus). NB-ACh complements LC-NE
-- as the dual gain/attention control axes the May 15 brain-region
-- coverage audit explicitly flagged as missing.
--
-- LC fires on surprise (broadly); NB fires on attention shifts
-- (target-locked). Both feed bg_modulators — LC writes lc_ne, NB
-- writes a new acetylcholine column added by this migration.
--
-- Phase 1 is inspection-only / additive: schema + read+CRUD tools.
-- No behavior change to retrieval, write gates, or any existing
-- subsystem. Phase 2 (separate PR) wires NB into the shadow consult
-- at mcp_server.py:3265 to fire on thalamic_salience above threshold.
-- Phase 3 closes the loop. Phase 4 enforces.
--
-- Four biological invariants encoded here (see docs/proposals/nucleus_basalis.md):
-- 1. Basal-forebrain cholinergic projection is broad to cortex,
-- target-modulated by attention.
-- 2. Phasic vs tonic ACh: phasic = target-locked spike,
-- tonic = sustained baseline.
-- 3. ACh widens what's attended, narrows what's not.
-- 4. Firing on attention SHIFTS, not steady-state attention.
--
-- Rollback, if needed before live adoption:
-- ALTER TABLE bg_modulators DROP COLUMN acetylcholine; -- SQLite >= 3.35
-- DROP TABLE IF EXISTS nb_state;
-- DROP TABLE IF EXISTS nb_firings;
-- DROP TABLE IF EXISTS nb_attention_targets;
-- DELETE FROM schema_version WHERE version = 68;
--
-- IDEMPOTENT: IF NOT EXISTS guards object creation; seed rows use
-- INSERT OR IGNORE so repeated application does not duplicate state.
-- The ALTER TABLE ADD COLUMN uses IF NOT EXISTS (SQLite 3.35+, which
-- brainctl already requires per migration 023's pattern).

-- Catalog of channels NB can attend to. Seedable; new targets
-- registered idempotently via tool_nb_register_target.
CREATE TABLE IF NOT EXISTS nb_attention_targets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
channel_kind TEXT NOT NULL CHECK(channel_kind IN (
'thalamic_sector', 'agent_scope', 'intent_class', 'entity_type', 'other'
)),
default_ach_gain REAL NOT NULL DEFAULT 0.10 CHECK(default_ach_gain BETWEEN 0.0 AND 1.0),
description TEXT,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%S', 'now')),
last_attended_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_nb_targets_kind ON nb_attention_targets(channel_kind);

-- Log of NB firings (cholinergic broadcasts). Each row = one phasic
-- ACh burst directed at a target.
CREATE TABLE IF NOT EXISTS nb_firings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fired_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%S', 'now')),
agent_id TEXT,
target_id INTEGER NOT NULL,
target_source_event_id INTEGER,
attention_magnitude REAL NOT NULL,
ach_delta_applied REAL NOT NULL,
mode TEXT NOT NULL CHECK(mode IN ('phasic', 'tonic_shift')),
context_hash TEXT,
notes TEXT,
FOREIGN KEY (target_id) REFERENCES nb_attention_targets(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_nb_firings_recent ON nb_firings(fired_at);
CREATE INDEX IF NOT EXISTS idx_nb_firings_agent ON nb_firings(agent_id, fired_at);
CREATE INDEX IF NOT EXISTS idx_nb_firings_target ON nb_firings(target_id, fired_at);

-- Single-row reservoir + current attention focus.
CREATE TABLE IF NOT EXISTS nb_state (
id INTEGER PRIMARY KEY CHECK (id = 1),
mode TEXT NOT NULL DEFAULT 'tonic_mid' CHECK(mode IN (
'phasic_locked', 'tonic_high', 'tonic_mid', 'tonic_low'
)),
ach_reservoir REAL NOT NULL DEFAULT 0.5 CHECK(ach_reservoir BETWEEN 0.0 AND 1.0),
last_attended_target_id INTEGER,
last_phasic_at TEXT,
last_tonic_shift_at TEXT,
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%S', 'now')),
FOREIGN KEY (last_attended_target_id) REFERENCES nb_attention_targets(id)
);
INSERT OR IGNORE INTO nb_state (id, mode, ach_reservoir) VALUES (1, 'tonic_mid', 0.5);

-- Seed the 4 thalamic sectors brainctl already uses (sourced from the
-- thalamic_relays.sector enum in migration 050). Other channel kinds
-- (agent_scope, intent_class, entity_type) get registered later via
-- nb_register_target as the operator decides what to attend to.
INSERT OR IGNORE INTO nb_attention_targets (name, channel_kind, default_ach_gain, description) VALUES
('cognitive', 'thalamic_sector', 0.15, 'planning, reasoning, deliberation'),
('episodic', 'thalamic_sector', 0.10, 'event recall and timeline'),
('semantic', 'thalamic_sector', 0.08, 'concept / fact retrieval'),
('pii_sensitive', 'thalamic_sector', 0.20, 'PII / credential / wallet — high attention so W(m) sees it');

-- Extend bg_modulators with the 4th neuromod dial.
-- Re-run safety: the brainctl migrate runner gates re-application by
-- schema_version (this row gets the version=68 entry below), so the
-- ALTER only fires once per DB. If you're applying the migration via
-- raw sqlite3 against a brain.db that already has the column, this
-- ALTER will fail with a duplicate-column error — that's by design;
-- always go through `brainctl migrate` for live application.
ALTER TABLE bg_modulators ADD COLUMN acetylcholine REAL NOT NULL DEFAULT 0.5;

INSERT OR IGNORE INTO schema_version (version, description, applied_at)
VALUES (68, 'nucleus basalis Phase 1: 3 tables (targets, firings, state) + bg_modulators.acetylcholine',
strftime('%Y-%m-%dT%H:%M:%S', 'now'));
2 changes: 1 addition & 1 deletion docs/proposals/brain_region_coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Verdict per region: ✅ well-modelled · 🟡 partial / under-wired · 🟥 miss
|---|---|---|---|
| **Brainstem / Ascending Reticular Activating System** | global arousal broadcast via diffuse fan-out | `neuromodulation_state` table holds org-level arousal/focus | Not wired into retrieval/admission. The proposed thalamus mode-broadcast layer is the missing fan-out. |
| **Locus Coeruleus (NE)** | global surprise / reset signal | `neurostate.norepinephrine` in proposed schema | No concrete LC-analog signal currently *fires* on prediction-error to reset attention. |
| **Nucleus Basalis (ACh)** | broaden receptive fields, raise responsiveness | Same — `neurostate.acetylcholine` proposed, not yet emitting | No actual cholinergic-mode admission-loosening tied to attended sectors. |
| **Nucleus Basalis (ACh) — Phase 1** | broaden receptive fields, raise responsiveness | `nb_attention_targets`, `nb_firings`, `nb_state` tables + `nb_*` MCP tools + `bg_modulators.acetylcholine` dial. Phase 1 is manual/read+CRUD; automatic thalamic-sector wiring + bg_modulators writes are Phase 2 (see `docs/proposals/nucleus_basalis.md`). |
| **Hypothalamus / allostasis** | homeostatic set-points + drives | `mcp_tools_allostatic.py` — demand_forecast, allostatic_prime | Has *prediction*; has no set-points (need-states) that *generate drives*. The system can't "feel hungry for data" or "need consolidation." |
| **Amygdala** | rapid valence tagging, fear conditioning, one-shot threat learning | `affect_*` tools classify valence/arousal lexically | Affect is a classifier, not a memory modulator. No "this kind of input previously caused a problem → preemptively bias suppression on that channel" loop. No fast-track fear learning that bypasses W(m). |
| **Hippocampal subfields (DG / CA3 / CA1)** | DG = pattern separation, CA3 = pattern completion, CA1 = output | One flat hippocampus abstraction | `memory_search` is implicitly pattern-completion. There's no explicit **pattern-separation step at write time** deciding "store as distinct" vs "merge into existing." Memory dedup happens at the embedding-cosine level, which is the *wrong* end of the loop. |
Expand Down
Loading