Skip to content

feat(continuum-core/modules): L0-3a.1 — HippocampusModule skeleton (BrainRegion + ServiceModule, empty tick)#1473

Merged
joelteply merged 2 commits into
canaryfrom
f8c51b26/feat-hippocampus-module-skeleton
May 30, 2026
Merged

feat(continuum-core/modules): L0-3a.1 — HippocampusModule skeleton (BrainRegion + ServiceModule, empty tick)#1473
joelteply merged 2 commits into
canaryfrom
f8c51b26/feat-hippocampus-module-skeleton

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Card: f8c51b26-9ddd-4107-97da-3237fc18ab4b

Second sub-slice of L0-3a, on top of #1471's BrainRegion trait machinery (now merged on canary). Skeleton only — no algorithms, no command migration. The first BrainRegion implementation; proves the trait surface fits a real region.

Two files, 380 insertions

File Change
continuum-core/src/modules/hippocampus.rs new — 379 lines
continuum-core/src/modules/mod.rs pub mod hippocampus;

HippocampusModule

  • ServiceModule with empty cmd surface — MemoryModule continues to handle memory/* until L0-3a.1b absorbs them. Defensive: if dispatch routes here despite empty prefixes, fails loudly.
  • BrainRegion with:
    • id = "hippocampus" (static, const-constructible)
    • pressure_profile = { MemoryClass::Heavy (engram graph + working memory ring + ready-buffers), ComputeClass::CpuVectorized (scoring + activation spreading in future slices), responds_to: [SystemMemHigh, InferenceQueueDepth] (consolidation depth drops under mem pressure; predictor backs off under inference queue depth) }
    • tick = idle — bumps internal monotonic counter, returns TickOutcome::idle(). Algorithm bodies drop in across L0-3a.3 through L0-3a.7
    • on_signal = default no-op (L0-4d wires SleepTransition for deeper consolidation)
  • Owns DashMapReadyBuffer<EngramPrefetchKey, EngramPrefetch> exposed via engram_prefetch() — Arc-shared so motor cortex + attention can peek without trait-object indirection
  • Shares MemoryState with MemoryModule via Arc — L0-3a.1b migration is structurally trivial

EngramPrefetch / EngramPrefetchKey

Placeholder ready-buffer value/key types. Real shape (engram set + scoring metadata + genome blend hint) lands L0-3a.2. Key = (persona_id, channel_id) — per-region per-persona-channel buffer, doctrine-aligned.

Outlier-validation hedge (in the module docstring)

#1471's BrainRegion trait has only one implementation candidate today. To prevent the trait surface ossifying around hippocampus-specific concerns, the module docstring explicitly checks against two other plausible regions:

  • Motor cortex (L0-4a planned): continuous candidate-utterance ranking off the partial-message stream. Differs in latency sensitivity — late candidates are useless. CadenceHint::Faster + ReadyBuffer's per-key freshness semantic (publish overwrites, evict_stale prunes) fit cleanly.
  • Attention (L0-4b planned): salience-map maintenance. Differs in publish target — writes to shared PersonaCognition.salience (CRDT counters), not own ready-buffer. BrainRegion::tick's TickOutcome { published: N } counts shared-state writes equivalently — no trait change needed.

Both alternative shapes fit without forcing. Trait surface is proven for at least 3 distinct region behaviors before any of them ship, per the CLAUDE.md outlier-validation strategy.

Tests (7 pass, 0 fail)

  • region_id_is_stable_static_string
  • pressure_profile_declares_memory_heavy_compute_vectorized
  • idle_tick_returns_idle_outcome_and_bumps_counter (uses fully-qualified BrainRegion::tick to disambiguate from ServiceModule's tick — note this trait-method ambiguity for L0-4a)
  • engram_prefetch_buffer_roundtrip
  • engram_prefetch_handle_is_shared_via_arc (verifies Arc-shared semantics: writes via handle A visible to handle B)
  • service_module_handle_command_errors_for_unrouted_commands
  • service_module_config_has_empty_cmd_and_event_surfaces

Predecessors

Next slice queue

L0-3a.2 Engram + EngramGraph types → L0-3a.3 algorithm 4 (salience-modulated decay) → L0-3a.4 algorithm 2 (channel-as-bias scoring) → L0-3a.5 algorithm 3 (activation spreading) → L0-3a.6 algorithm 1 (two-pool budget split) → L0-3a.7 algorithm 5 (predictor + ready-buffer publish — the alive-feeling slice) → L0-3a.8 holdout fixture suite → L0-3a.9 TS Hippocampus.ts deletion.

🤖 Generated with Claude Code

joelteply and others added 2 commits May 29, 2026 22:02
…er + RegionTelemetry (substrate prerequisite)

Card: 71923a08-b3de-448a-98ef-fe7cc3e817c0

First sub-slice of L0-3a. Pure typed surface from BRAIN-REGIONS-SUBSTRATE.md
(merged via #1470). No region implementations, no algorithms, no governor
integration. Those land in L0-3a.1+ slices.

## New modules in continuum-core/src/runtime/

### brain_region.rs

The cognitive-cycle trait every region implements:

- BrainRegion (async trait, dyn-compatible)
  - id() -> RegionId
  - pressure_profile() -> PressureProfile
  - async tick(ctx: &RegionContext) -> TickOutcome
  - async on_signal(signal: RegionSignal) -> Result<(), RegionError>  // default no-op
- RegionId (Cow<'static, str> newtype, const constructor for static IDs)
- PressureProfile { memory_class, compute_class, responds_to }
- MemoryClass: Light | Moderate | Heavy | VramSensitive
- ComputeClass: Bookkeeping | Cpu | CpuVectorized | InferenceLight | InferenceHeavy
- PressureSignalKind (kind-only mirror of governor::PressureSignal for static decl)
- TickOutcome { published, consumed_since_last, pressure_observed, cadence_hint }
- TickOutcome::idle() convenience constructor
- CadenceHint: Faster | Hold | Slower | Sleep (region requests; governor decides)
- RegionSignal: PersonaLifecycle | SleepTransition | SystemPressureChanged
- PersonaLifecycle: Created | Destroyed
- SleepPhase: Active | Idle | Sleep
- PressureLevel: Nominal | Moderate | High | Critical
- RegionContext { tick_number, persona_scope }  // global vs per-persona
- RegionError (thiserror): SignalRejected | NotReady | Internal

### ready_buffer.rs

The publish/peek surface every region uses to hand off pre-staged results:

- ReadyBuffer trait
  - peek(&self, key: &Key) -> Option<Value>  // synchronous, MUST NOT block
  - publish(&self, key: Key, value: Value)   // atomic replace
  - evict_stale(&self, max_age: Duration) -> usize
  - len() / is_empty()
- DashMapReadyBuffer<K, V> default implementation
  - Arc-shared DashMap inner — cheap Clone hands out additional handles
  - Sharded concurrent access; wait-free reads in the common case
  - TimestampedEntry tracks published_at for evict_stale

Semantic rules enforced in the doc + the trait:
- Reads MUST NOT block / MUST NOT await
- Staleness acceptable — empty buffer is signal, not block
- Per-region buffers, not global

### region_telemetry.rs

The per-tick telemetry shape:

- RegionTelemetry { region_id, persona_id, tick_started_at, tick_duration,
                    published, consumed_since_last, buffer_misses_since_last,
                    pressure_observed }
- consumption_fraction() -> Option<f32>  // None when published == 0
- had_buffer_misses() -> bool

Feeds the substrate governor's yield-learning loop (algorithm 7, lands L0-4c)
and the operator surface (./jtag region/stats, region/yield).

## ts-rs bindings (11 emitted to shared/generated/runtime/)

CadenceHint, ComputeClass, MemoryClass, PersonaLifecycle, PressureLevel,
PressureProfile, PressureSignalKind, RegionId, RegionSignal,
RegionTelemetry, SleepPhase, TickOutcome.

Generated and validated by the ts-rs export_bindings_* tests.

## Tests

23 new unit tests across the three modules. All pass.

- brain_region: 6 tests (trait impl, default on_signal noop, RegionId
  construction + Display, RegionContext global vs per-persona, TickOutcome::idle)
- ready_buffer: 9 tests (publish+peek roundtrip, missing key, overwrite,
  evict_stale removes old + keeps fresh, evict ZERO clears everything,
  len/is_empty, clone shares Arc inner, dyn trait usage, with_capacity)
- region_telemetry: 5 tests (consumption_fraction with publishes / zero /
  full, had_buffer_misses true / false)

Plus ts-rs auto-generated export_bindings_* tests for all 11 types.

Total: 74 tests pass in runtime::, 0 fail.

## Boy-scout

cargo fmt applied across the package picked up some unrelated drift in
governor/types.rs (line-width formatting on ts(export...) attributes).
Including the fix.

## What is NOT in this card

- No region implementations (HippocampusModule, MotorCortexModule,
  AttentionModule all land in later slices)
- No algorithms (1-7 from COGNITION-ALGORITHMS.md land in subsequent cards)
- No SubstrateGovernor integration (yield-learning loop is L0-4c)
- No derive macro / scaffold generator (lands when ≥3 regions exist to
  motivate the abstraction — per outlier-validation in CLAUDE.md)

## Predecessors merged

- #1469 (L0-2-CUTOVER-INVESTIGATION + RTOS-brain doctrine) — 2026-05-29
- #1470 (BRAIN-REGIONS-SUBSTRATE + COGNITION-ALGORITHMS docs) — 2026-05-29

## Next slices

L0-3a.1 HippocampusModule skeleton, L0-3a.2 Engram + EngramGraph types,
L0-3a.3 Algorithm 4 (salience decay), L0-3a.4 Algorithm 2 (channel-as-bias),
L0-3a.5 Algorithm 3 (activation spreading), L0-3a.6 Algorithm 1 (two-pool
budget), L0-3a.7 Algorithm 5 (predictor + ready-buffer publish), L0-3a.8
holdout fixture suite, L0-3a.9 TS Hippocampus.ts deletion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rainRegion + ServiceModule, empty tick)

Card: f8c51b26-9ddd-4107-97da-3237fc18ab4b

Second sub-slice of L0-3a. Skeleton only — no algorithms, no command
migration. Algorithms 1-5 from COGNITION-ALGORITHMS.md land in L0-3a.2
through L0-3a.7. Command surface migration (memory/* from MemoryModule)
is L0-3a.1b.

## HippocampusModule

- Implements ServiceModule with EMPTY command_prefixes + event_subscriptions
  (MemoryModule continues to handle memory/* commands until L0-3a.1b)
- Implements BrainRegion (from #1471 trait machinery) with:
  - id = "hippocampus" (static)
  - pressure_profile: { MemoryClass::Heavy, ComputeClass::CpuVectorized,
    responds_to: [SystemMemHigh, InferenceQueueDepth] }
  - tick: idle — bumps internal monotonic counter, returns TickOutcome::idle()
  - on_signal: default no-op (L0-4d wires SleepTransition reaction)
- Owns a DashMapReadyBuffer<EngramPrefetchKey, EngramPrefetch> exposed via
  engram_prefetch() — Arc-shared so motor cortex / attention can peek
  without going through the trait object
- Shares MemoryState with MemoryModule via Arc — when L0-3a.1b absorbs
  command handling, migration is structurally trivial

## EngramPrefetch / EngramPrefetchKey

Placeholder ready-buffer value type. Carries produced_at_tick so handlers
can detect stale buffers without timestamp comparison. Real shape (engram
set + scoring metadata + genome blend hint) lands L0-3a.2 with the actual
Engram types.

Key shape: (persona_id, channel_id) tuple. Per-region buffer doctrine — one
prefetch per persona-per-channel.

## Outlier-validation hedge (docstring)

The BrainRegion trait in #1471 has only one implementation candidate today.
Module docstring explicitly checks the trait surface against two other
plausible regions to prevent it ossifying around hippocampus:

- Motor cortex (L0-4a): continuous candidate-utterance ranking. Differs in
  latency sensitivity. CadenceHint::Faster + per-key freshness semantics fit.
- Attention (L0-4b): salience-map maintenance. Differs in publish-target
  (writes to shared PersonaCognition.salience, not own ready-buffer).
  TickOutcome.published counts either target without trait change.

Both alternative shapes fit the same trait without forcing. Trait surface
proven for 3 distinct region behaviors before any of them ship.

## Tests (7 pass, 0 fail)

- region_id_is_stable_static_string
- pressure_profile_declares_memory_heavy_compute_vectorized
- idle_tick_returns_idle_outcome_and_bumps_counter
- engram_prefetch_buffer_roundtrip
- engram_prefetch_handle_is_shared_via_arc (verifies Arc-shared semantics)
- service_module_handle_command_errors_for_unrouted_commands
- service_module_config_has_empty_cmd_and_event_surfaces

## Scope: 2 files

Modified: src/workers/continuum-core/src/modules/mod.rs (pub mod hippocampus)
Added:    src/workers/continuum-core/src/modules/hippocampus.rs (379 lines)

Fmt-drift in unrelated files was split off into a companion PR following
the same pattern as #1472, keeping this review focused.

## Predecessors

- #1471 (L0-3a.0 trait machinery) — merged to canary
- #1470 (BRAIN-REGIONS-SUBSTRATE + COGNITION-ALGORITHMS docs) — merged
- #1469 (L0-2-CUTOVER-INVESTIGATION + RTOS-brain doctrine) — merged

## Next slices

L0-3a.2 Engram + EngramGraph types → L0-3a.3 algorithm 4 (salience decay)
→ L0-3a.4 algorithm 2 (channel-as-bias) → L0-3a.5 algorithm 3 (activation
spreading) → L0-3a.6 algorithm 1 (two-pool budget) → L0-3a.7 algorithm 5
(predictor + ready-buffer publish — the alive-feeling slice) → L0-3a.8
holdout fixture suite → L0-3a.9 TS Hippocampus.ts deletion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joelteply joelteply merged commit ee15b03 into canary May 30, 2026
4 checks passed
@joelteply joelteply deleted the f8c51b26/feat-hippocampus-module-skeleton branch May 30, 2026 05:08
joelteply added a commit that referenced this pull request May 30, 2026
…dgeKind (algorithm 3 substrate) (#1474)

Card: 8459bfa6-b40c-4c22-8f25-0963a7987c17

Sidecar substrate for algorithm 3 (activation spreading, COGNITION-ALGORITHMS.md §3). Pure storage layer — traversal logic lands in L0-3a.5. Does NOT modify the existing persona::engram admission membrane.

## What ships

### persona/engram_graph.rs (new, 376 lines)

- EdgeKind enum — SharedEntity | SharedTopic | CitedIn | RecallCoOccurrence | ConversationalReply | TaskOutcome
- EngramEdge { target: Uuid, kind: EdgeKind, weight: f32 } — algorithm-3 traversal payload
- EngramGraph — DashMap<Uuid, Vec<EngramEdge>> sharded for concurrent writes
  - new() / with_capacity(n) / default()
  - add_edge(from, to, kind, weight)
  - neighbors(id) — outbound edges, O(1) amortized, insertion order preserved
  - in_degree(id) — inbound count, O(N) scan (cold path — algorithm 4 centrality)
  - edge_count() — telemetry
  - evict_engram(id) — removes outbound + inbound, idempotent
  - is_empty()

### ts-rs bindings

shared/generated/persona/EdgeKind.ts
shared/generated/persona/EngramEdge.ts

## Sidecar pattern

Intentionally separate from persona::engram (the admission membrane):
- engram.rs ships provenance, trust, content refs — WHERE engrams come from
- engram_graph.rs ships connectivity — HOW engrams connect

Keeping them separate means admission consumers don't grow algorithm-3 dependencies, and algorithm-3 consumers don't grow admission dependencies. Clean concern boundaries.

## Tests (16 pass, 0 fail)

- new_engram_graph_is_empty
- add_edge_increments_count
- neighbors_returns_added_edges_in_insertion_order
- neighbors_of_unknown_source_is_empty
- weights_preserved_through_neighbors
- in_degree_counts_inbound_edges_across_sources
- in_degree_counts_repeated_edges_from_same_source
- evict_engram_removes_outbound_edges
- evict_engram_removes_inbound_edges_from_other_engrams
- evict_engram_is_idempotent
- concurrent_add_edge_from_threads_is_safe (8 threads × 100 edges, all targeting same id, in_degree=800)
- default_constructor_matches_new
- with_capacity_constructor_works
- edge_kind_round_trips_through_serde
- export_bindings_edgekind (ts-rs auto)
- export_bindings_engramedge (ts-rs auto)

## What is NOT in this card

- spread_activation function (L0-3a.5, algorithm 3 — reads this graph)
- EdgeKind weights tuned by algorithm 7 (L0-4c yield-learning)
- RecallMetadata sidecar (L0-3a.2b — salience, last_touched, access_count, embedding)
- EngramRef shape (L0-3a.2b)
- Engram admission membrane modifications (no changes to persona::engram)

## Predecessors

- #1473 (L0-3a.1 HippocampusModule skeleton) — merged
- #1471 (L0-3a.0 trait machinery) — merged
- #1470 (cognition algorithms doc) — merged

## Flywheel test

Third PR (after #1471, #1473) through the auto-merger flywheel that peer's #1091/#1092/#1093 enabled. Local fmt was scoped to ONLY my file (no widespread cargo fmt -p sweep), so no companion fmt-drift PR needed this time.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joelteply added a commit that referenced this pull request May 30, 2026
The `avatars: ./src/models/avatars` additional_context was added in
9b1f6ca (April 2026) when the plan was to bake CC0 avatar VRMs
into the continuum-core image. That plan never landed end-to-end —
docker/continuum-core.Dockerfile lines 131-143 document the rollback:
src/models is gitignored, the dir doesn't exist in CI checkouts,
and the Dockerfile uses `RUN mkdir -p /app/avatars` as a placeholder
instead of COPYing from the avatars context.

The compose-side context declaration was left behind, dangling. No
Dockerfile uses `--from=avatars` (verified by grep), so the declaration
referenced nothing in build instructions. But docker compose validates
that ALL additional_contexts resolve at build time — a missing local
context dir fails the whole build with "stat /tmp/carl-smoke-NNNN/src/
models/avatars: no such file or directory".

That's the exact failure mode currently blocking carl-install-smoke
on PR #1475 (Mac Intel hardware tier) — any PR that touches install.sh
triggers carl-install-smoke, which has been silently broken by this
dangling context since the rollback. Other PRs (e.g. #1471, #1473,
#1474) didn't touch install.sh so the check never ran on them; the
break was invisible until now.

Removing the line restores the carl-install-smoke happy path while
keeping the Dockerfile's empty-dir placeholder intact. Restore the
build context when the avatar-provisioning story lands (LFS, model-init
download, or curl from a CC0 URL in CI before docker build) per the
gap noted in docs/infrastructure/PR891-E2E-VALIDATION.md.

Inline comment preserves the context-of-removal in the file so a
future contributor doesn't re-add the dangling line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
joelteply added a commit that referenced this pull request May 30, 2026
…se (#1476)

The `avatars: ./src/models/avatars` additional_context was added in
9b1f6ca (April 2026) when the plan was to bake CC0 avatar VRMs
into the continuum-core image. That plan never landed end-to-end —
docker/continuum-core.Dockerfile lines 131-143 document the rollback:
src/models is gitignored, the dir doesn't exist in CI checkouts,
and the Dockerfile uses `RUN mkdir -p /app/avatars` as a placeholder
instead of COPYing from the avatars context.

The compose-side context declaration was left behind, dangling. No
Dockerfile uses `--from=avatars` (verified by grep), so the declaration
referenced nothing in build instructions. But docker compose validates
that ALL additional_contexts resolve at build time — a missing local
context dir fails the whole build with "stat /tmp/carl-smoke-NNNN/src/
models/avatars: no such file or directory".

That's the exact failure mode currently blocking carl-install-smoke
on PR #1475 (Mac Intel hardware tier) — any PR that touches install.sh
triggers carl-install-smoke, which has been silently broken by this
dangling context since the rollback. Other PRs (e.g. #1471, #1473,
#1474) didn't touch install.sh so the check never ran on them; the
break was invisible until now.

Removing the line restores the carl-install-smoke happy path while
keeping the Dockerfile's empty-dir placeholder intact. Restore the
build context when the avatar-provisioning story lands (LFS, model-init
download, or curl from a CC0 URL in CI before docker build) per the
gap noted in docs/infrastructure/PR891-E2E-VALIDATION.md.

Inline comment preserves the context-of-removal in the file so a
future contributor doesn't re-add the dangling line.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant