Control Map & Config Audit — mcp-ariel-memory¶
UPDATE 2026-08-24 (59c798a): the audit below triggered a config rework.
MCP_CONFIG_PATHnow enables per-agent configs; formerly-dead sections embeddings/binary/typed_memory(TTL)/forgetting.consolidate_weight_threshold/ performance.wal_mode/logging/dashboard/layers-gate/cors are WIRED and live. Hooks: yaml is the single source (known_hooks duplicate removed). Phantom keys removed: webhooks, cross_user_learning, versioning, features.auth, api_keys_enabled, rag fts/vec flags, graph temporal/epistemic. Section table below kept as the historical audit record; live-status column is superseded by this banner for the listed sections.
Audited 2026-08-24 against master @ c2c7c19. Every claim verified by grep over consumers.
1. Control planes (who controls what)¶
┌──────────────────────────────────────────────┐
ENV VARS │ MCP_MEMORY_DATA_DIR → DB root per agent │
────────────► │ MCP_MASTER_KEY → crypto master key │
│ MCP_AUTH_TOKEN / _DISABLED → HTTP auth │
│ ARIEL_EXPOSE → tool surface │
│ BACKUP_CRON_DISABLED → tests │
└───────────────┬──────────────────────────────┘
│
config.yaml ──► Config() ─────┤ (singleton, loaded from REPO dir —
(repo-root) │ shared by ALL agent instances!)
┌───────────────┴──────────────────────────────┐
CLI FLAGS │ --transport stdio|http --host --port │
────────────► │ --dashboard --no-auth │
└───────────────┬──────────────────────────────┘
│
lifespan(server):
1. alembic migrations (per data-dir DB)
2. read_only_replica.sync
3. background: backup_cron + importance_scheduler
4. periodic loop (15-min tick) ← see §2
│
CODE REGISTRY ────────────────┤ base.register_layer(name, LayerBinding)
(new layers) │ server.PRIMITIVE_TOOLS + ARIEL_EXPOSE
└──────────────────────────────────────────────
2. Background loops (guaranteed housekeeping)¶
| Loop | Interval | Does |
|---|---|---|
backup_cron |
backup_interval_hours=24 ± jitter 3600s | DB backups, retention cleanup; fires nightly hook; wiki sync every wiki_sync_interval_minutes=30 |
importance_scheduler |
internal cadence | importance decay scheduling |
| periodic tick | 15 min | forgetting_system.cleanup() |
| ↳ compaction | hourly | run_cleanup(user_id="default") — archive/decay |
| ↳ consolidation sweep | hourly | consolidate_episodes("default") per layer (user+agent): high-weight episodes → L4 facts |
All loops run inside the SERVER process — housekeeping never depends on an agent calling tools.
3. Event hooks (reactive loop)¶
Tool calls fire named hooks (_fire_hook) → hook_registry.fire → gate: config.is_hook_enabled(layer, hook) → AgentHooks/UserHooks handlers.
Fired from: episodic save (consolidation), session end (consolidation), think (message_received, emotion_trigger), dream (auto_context, dream_buffer, retrieval_router), evolve (personality_shift), remember (state_delta, conflict_resolver, error_occurred, decision_made...).
Known noise: consolidation handler reads staging_items from ctx which no caller provides → effectively no-op (real promotion lives in the hourly sweep, see §2).
3.1 External events + auto-hooks (Phase C, 2026-08-29)¶
Harnesses push lifecycle events without an agent tool call →
hooks/external.py::dispatch_event (transports: POST /api/hooks/{event},
MCP memory_hook, in-process python -m autohooks dispatch) → same
HookRegistry.fire gate. 8 events: session_started, session_ended,
new_message, auto_save_candidate, post_session_diff,
post_context_compression, context_threshold, memory_pressure.
Full wiring guide: docs/hooks/autohooks-platforms.md.
Knobs added by Phase C:
| Key / Env | Default | Controls |
|---|---|---|
hooks.auto_save_threshold |
0.5 | external new_message → L3+graph (≥0.8 tier → staged L4) |
staging.enabled |
true | risk-tier staging: L4 saves / consolidation promos / archive sweeps → mutation_proposals; false = direct writes |
staging.expire_days |
7 | proposal lazy expiry |
staging.dream_markers |
true | DREAM: memory:/fact:/skill: marker routing |
rehydrate.enabled |
true | compaction drift log + rehydrate inject block (D3.5) |
rehydrate.window_hours |
6.0 | compaction older than this no longer rehydrates |
inject.token_budget |
2000 | critical-set inject cap |
inject.important_min |
0.8 | L4 facts in inject block |
ARIEL_EXPOSE=primitives,review |
primitives |
opt-in tier exposing memory_proposals + memory_report_card to agents |
ARIEL_HASH_EMBEDDINGS=1 |
unset | hash-embedding fast path — REQUIRED in daemon/hook units (ST model >15 s per CLI process) |
Observability surfaces: memory_dispatch_log + mutation_proposals tables
(alembic 20260829_1300_d1c110, 20260829_1600_c111), tools memory_watch
/ memory_proposals / memory_report_card, inject block kinds
relevant|recent|gap|proposals|important, script
scripts/verify_autohooks.py (24 checks).
4. config.yaml — section audit¶
| Section | Status | Consumer / note |
|---|---|---|
layers.*.enabled |
☠️ DEAD | zero readers |
limits.l1_buffer_size |
✅ LIVE | ReflexBuffer via get_limit; l2/l3/l4 limits unread |
hooks.user/agent.* |
✅ LIVE | is_hook_enabled; but code ALSO duplicates lists (known_hooks) — drift risk, unknown hooks default False |
forgetting.decay_rate / archive_threshold_days / archive_min_importance |
✅ LIVE | ForgettingSystem |
forgetting.consolidate_weight_threshold |
☠️ DEAD | hardcoded min_weight=0.7 in consolidate_episodes |
rag.* |
☠️ DEAD | RAGEngine uses constructor defaults; no config readers |
typed_memory.* |
☠️ DEAD | policies hardcoded in shared/memory_types.py; TTL 30d hardcoded in CoreMemory._prepare_save_params |
binary.* |
☠️ DEAD | dim=384/mode=naive hardcoded defaults |
embeddings.* |
☠️ DEAD | DEFAULT_MODEL hardcoded multilingual-e5-small in shared/embeddings.py |
graph.max_depth |
✅ LIVE | EpistemicGraph |
graph.temporal_enabled/epistemic_enabled |
☠️ DEAD | zero readers |
wiki.<layer> type flags + external_dirs |
✅ LIVE | wiki/shared.get_external_dirs + enabled-types check |
auth.bearer_token_enabled |
✅ LIVE | endpoints/common |
auth.api_keys_enabled |
☠️ DEAD | zero readers (API keys feature self-managed) |
features.rate_limiting |
✅ LIVE | rate_limiting feature gate |
features.backup_cron |
⚠️ INDIRECT | gate is env BACKUP_CRON_DISABLED, yaml flag unread |
features.dashboard vs dashboard.enabled |
☠️ DEAD ×2 | server uses --dashboard CLI flag only; two yaml flags contradict each other and neither is read |
features.* rest (import_export, versioning, cross_user_learning, compression, webhooks, audit_trail, auth, embeddings) |
☠️ DEAD | is_feature_enabled has NO callers at all |
performance.* |
☠️ DEAD | WAL hardcoded ON in connection.py; pool/cache sizes unread |
security.rate_limit_per_user / max_ws_per_user / max_ws_total |
✅ LIVE | RateLimiter / middlewares (max_ws_* questionable: no WebSocket surface in stdio mode) |
middleware.rate_limit_per_minute |
✅ LIVE | shared.middleware.RateLimitMiddleware — a DIFFERENT limiter from security.rate_limit_per_user: this one counts every hook dispatch and tool call through the pipeline, and a block drops the event before any handler runs. 0 or less disables it |
security.input_validation / sql_injection_prevention / per_user_isolation / audit_logging |
☠️ DECORATIVE | zero readers |
backup.interval/jitter/wiki_sync/retention |
✅ LIVE | backup_cron |
dashboard.port |
☠️ DEAD | argparse --port default 8000 |
logging.* |
☠️ DEAD | zero readers |
(not in yaml) cors.allowed_origins |
✅ LIVE | middlewares read it with localhost defaults — consider adding to yaml |
(not in yaml) crypto.master_key_hex |
⚠️ fallback | secrets.py chain: .env file → crypto.master_key_hex → env MCP_MASTER_KEY |
Score: of ~60 yaml keys only ~15 are live. The dead weight is v1-era aspiration config kept after refactors.
5. Gotchas¶
- config.yaml is repo-root-relative (
Path(__file__).parent) — all three agent instances (hermes/mimocode/cowagent) share ONE config even though their DATA dirs differ. Changing hooks flags affects every agent. - Hook gating double-default: known hooks default True, unknown default False — adding a new hook handler requires remembering to add it to BOTH yaml and
known_hooks. get_wiki_types()in config.py expects a list and returns [] on the actual dict-shaped config — misleading dead method (real mechanism: wiki/shared.py).
6. Recommendations¶
- Delete or implement dead sections (biggest: rag/embeddings/binary/typed_memory/performance/logging/dashboard).
- Single source for hooks: drop
known_hooksduplication, make yaml explicit. - Add
cors:to yaml explicitly. - Resolve dashboard triple-switch (keep CLI flag only).
- If multi-instance divergence ever needed: move config path resolution to
MCP_CONFIG_PATHenv override.