Layer 101 — Episodic Cortex (vault that talks back)#28
Draft
slavazeph-coder wants to merge 12 commits into
Draft
Layer 101 — Episodic Cortex (vault that talks back)#28slavazeph-coder wants to merge 12 commits into
slavazeph-coder wants to merge 12 commits into
Conversation
Cannibalized from breferrari/obsidian-mind (taxonomy + lifecycle hooks) and @cyrilxbt's "second brain" architecture (Daily Brief / Weekly Synthesis prompts), but BrainSNN-native: every capture flows through the Layer 4 firewall, Layer 29 affect decoder, and Layer 87 genre classifier before landing — and the merged region heatmap drives the 3D cortex instead of just filing into a folder. What ships: - 8-category episodic taxonomy with regex triggers + per-category brain-region affinity (decision/insight/question/artifact/win/ project/person/incident). - routeCapture() pipeline + persistent episodic memory store with on-demand MiniLM embeddings (Layer 24), cosine findSimilar, and union-find cluster mining. - Synthesis engine: dailyBrief() returns connections / pattern / question; weeklySynthesis() returns thesis / contradictions / knowledge gaps / one action. Local deterministic path uses the pre-computed BrainSNN signals; Gemma path layers a system prompt that forces the LLM to reference those signals; falls back on any error. - EpisodicCortexPanel: capture form, 8-category filter pills, search, recent timeline, brief + synthesis cards, warm-embeddings button, export/import bundle, wipe. - episodicDream.js subscribes to Layer 26's dream phase and every 4 cycles runs consolidationPass over the last 30d of captures — STDP-reinforces every connectome edge touching the cluster's dominant region. Captures gain a ◐N consolidation counter. - 4 new MCP tools (episodic_capture / episodic_brief / episodic_synthesis / episodic_list) so Claude Code / Codex agents on the WS relay can drive the layer programmatically. Why deeper than Obsidian: connections aren't a graph plugin — they are the physics of the cortex. Embedded out of the box, manipulation-pressure-scored before they pollute the vault, affect-tagged, region-mapped, and STDP-rehearsed during idle Dream Mode. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
Adds the missing surfaces that turn the layer from a panel into a loop: - episodicAutoBrief.js — persisted scheduler. Daily Brief unlocks at >20h since last + ≥3 fresh captures; Weekly Synthesis at every 6.5d with ≥4 captures in 7d. Banner surfaces in the panel the moment conditions are met; recordBrief/recordSynthesis persist 12-deep history. - episodicStreak.js — UTC-day capture streak with longest + totalDays persistence. Renders as a streak chip next to the Timeline header. - episodicDrift.js — decision-drift detector. For every decision >3d old, finds newer captures with cosine ≥0.34 + valence flip ≥0.45 or incident shadow; ranks by similarity × |valence Δ| × log(daysApart). Merged into the Synthesis card under Contradictions with a distinct red "drift" card style. - episodicDeepLink.js — ?capture / ?capture-url / ?capture-title URL contract + bookmarklet source. Panel consumes deep-link on mount, applies the capture to the brain, and strips the params from the URL bar. The "↗ Capture to BrainSNN" drag-target surfaces in the panel footer. - EpisodicGraph.jsx — canvas connection graph. Cluster wheels along a top arc, singletons along the bottom row, hover-tooltip on each node, ResizeObserver-aware. Renders inline above the timeline. - Voice capture (Layer 59 hook) — 🎙 button live-transcribes into the draft via Web Speech API; turns red while listening. - OCR capture (Layer 58 hook) — ◳ image-pick opens Tesseract.js, progress % renders inline, extracted text + auto-title append to the draft. - styles/global.css — full Episodic Cortex theme block (gradient title, pill row, streak tile, autobrief banner, graph host, drift card). Build: 921 modules, clean. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- episodicAsk.js — natural-language Q&A over captures. Embeds the question (MiniLM with trigram fallback), runs cosine retrieval, composes a deterministic local answer from the hits' categories + pressure + top hit. Optional Gemma upgrade with a system prompt that forces grounding in the retrieved captures and references the BrainSNN signals already attached to each. - Panel: "Ask the vault" card with question input + answer + ranked clickable hits that scroll-to-capture in the timeline. - Pin sort — pinned captures float to the top of the timeline. - EpisodicGraph onNodeClick wired through; clicking a node now scrolls the matching capture card into view + flashes a cyan ring (same focusCapture helper). - Background auto-run — opt-in checkbox in the autobrief banner; persists via brainsnn_episodic_autorun_v1. When on, brief/synth fire 45-60s after they unlock. - MCP tools: episodic_ask, episodic_drift, episodic_consolidate. The consolidate tool reuses applyEpisodicSTDP so an agent can consolidate the episodic graph into the brain weights without waiting for idle Dream Mode. - hotkeys.js — 'ep' chord jumps to Layer 101. Build: 922 modules, clean. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- CaptureCard gains a "≈ similar" toggle that runs findSimilar() inline and lists the top-3 matches as clickable scroll-to buttons. Uses MiniLM cosine when warm, trigram fallback otherwise. - privacyBudget.js (Layer 86) — tag the 5 episodic localStorage keys (captures, embeddings cache, auto-brief schedule, streak, autorun toggle) so the Privacy Budget audit shows the bytes attributable to Layer 101. Build: 922 modules, clean. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- episodicMemory.findBacklinks(captureId): pure lexical graph derived from the routed capture's @mentions + URLs. Mirrors Obsidian's wikilink backlinks but built automatically — no manual [[link]] syntax. - "≈ similar" reveal now merges cosine hits (≈) and backlinks (↔) into a single deduped list, with a hover-tooltip showing what link they share. Works without embeddings — pure URL/mention matching surfaces the graph immediately. Build: clean. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
Defensive guards, perf gates, accessibility, and storage hygiene.
No new features.
Memory store:
- 50KB per-capture text cap + 200-char title cap, applied at addCapture
and at importEpisodicBundle entry points.
- kind / source allowlists so only known values land on records.
- importEpisodicBundle now validates version + per-item shape (text,
ts, id, hash). Returns {imported, rejected} so the panel can show
partial-import telemetry.
- saveEmbeddings is debounced (8s) so a batch of captures doesn't
serialize ~1MB on every addCapture.
- clearAllCaptures wipes the embeddings key synchronously rather
than waiting for the debounce.
- localStorage quota errors now log a throttled console.warn instead
of silently failing.
- lexicalJaccard caps trigram input at 4KB and consolidates around a
single lexTrigrams helper to halve allocations on the fallback
similarity path.
Synthesis:
- dailyBrief / weeklySynthesis wrap every section in try/catch so a
single bad regex or malformed capture renders a partial brief
instead of a blank one.
- localKnowledgeGaps caps orphan-candidate work to top-8 by pressure
before calling findSimilar, so the worst case is O(8N) instead of
O(N^2).
Router:
- collectEvidence + extractAll guard against zero-width regex matches
with lastIndex bumps and a hard safety counter so a future regex
bug can't infinite-loop the classifier.
Ask the Vault:
- 4KB cap on per-capture trigram input; embed failure logs and
falls through to lexical instead of crashing.
Dream coupling:
- Subscribe callback wrapped in try/catch so a transient failure
in consolidationPass / setState / markConsolidated doesn't tear
down the dream subscription. Per-id markConsolidated is also
guarded.
Panel:
- Voice session is stopped on panel unmount so the mic cannot stay
hot if the panel is unmounted while listening.
- Auto-brief readiness now refreshes on a 60s interval so a panel
left open across the cooldown window catches up without waiting
for the next capture.
- role="status" / aria-live="polite" on the autobrief banner;
role="tablist" + aria-selected on category filter chips;
aria-pressed + aria-label on the voice button; richer aria-label
on the connection graph.
Build: clean.
https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- episodicPII.js: regex pass on every routeCapture for emails,
phone numbers, common API-key prefixes (sk-, ghp_, github_pat_,
xox-, AKIA, AIza, hf_, sk_live_/test_/pk_live_), SSN-shape, IPv4,
and long base64-ish blobs (60+ chars, post URL strip). Conservative
bias toward false positives — false negatives leak secrets.
- routeCapture exposes a compact pii summary {total, first, kinds}
on every record so cards and stats can render without re-scanning.
- CaptureCard: red 🔒 PII chip with the first detected kind +
hover-tooltip listing all detected kinds.
- captureStats includes withPII count; the timeline header shows
"🔒 N flagged for PII" when any captures match.
- episodic_list MCP tool returns piiFlags per capture so agents can
filter or refuse to forward sensitive content.
- redactPII helper exported for future inline redaction action.
- styles: focus-visible outline pass for every button / input /
textarea / anchor inside the panel; aria-selected tab bolds;
prefers-reduced-motion drops the card hover transition.
Build: clean.
https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- rewriteCapture(id, text) in episodicMemory: replace a capture's text in-place and re-run routeCapture so classification, affect, regions, urls, mentions, hash, and pii summary stay consistent. Drops the stale embedding so the next retrieval re-embeds. Marks editedAt timestamp. - Capture cards with PII flags get a "✂ redact" button that runs redactPII (typed placeholders <EMAIL_REDACTED>, <APIKEY_REDACTED>, etc) and rewrites the capture in place. Prompts confirmation because the original text is lost. - tests/episodic.test.mjs — 16 smoke tests over the pure modules (episodicTaxonomy + episodicPII). Covers category structure, trigger firing on canonical phrases, PII detection across email/key/SSN/IP, empty-input + non-string defenses, hit caps, URL stripping, and redaction. - package.json: `npm test` runs node --test against tests/*.test.mjs. Build: clean. Tests: 16 pass / 0 fail. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
…e hint - tests/episodicAutoBrief.test.mjs (12 tests): brief / synthesis trigger gates, fresh-capture filter, history persistence + cap, relative-timer formatting. - tests/episodicStreak.test.mjs (13 tests): streak math edge cases — empty, single-today, multi-day consecutive, yesterday-grace, gap reset, multi-capture-same-day collapsing, longest persistence across calls, totalDays = distinct UTC days, label tier ladder. - tests/_localStorage.mjs: in-memory shim so the auto-brief and streak modules run unmodified in node --test. - npm test: 41 tests / 8 suites / 0 fail. UX: - Idle warmup — 12s after panel mount, if the user already has 5+ captures, quietly initEmbeddings + ensureAllEmbeddings so the next Brief / Ask the Vault call uses cosine without the user pressing "Warm embeddings". Failures are silent; lexical fallback always works. - Weak-signal hint — capture cards classified as `artifact` with score <0.18 (or 0 dominant categories) get an italic prompt to add a verb or @mention so the brain can place the note. Helps users learn how the classifier sees their writing. Build: clean. Tests: 41 pass / 0 fail. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- episodicSynthesis.callGemmaJson + episodicAsk.callGemmaAnswer now pass an AbortSignal.timeout (30s synthesis, 20s ask) so a hung Gemma request can no longer stall the brief or the Q&A forever. Falls through to the local path on abort. - 4 new taxonomy tests: clusterFor mapping for each cluster, fallback for unknown ids, DEFAULT_REGIONS sum sanity, every category cluster id resolves. Tests: 45 pass / 0 fail. Build clean. https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
… tests
- Background auto-run timer now aborts the moment the user starts
typing in the draft / title / ask boxes, so silent runs cannot
interrupt active work. The 45s/60s clock restarts only after
the inputs are clear.
- First-of-day capture pulse — handleCapture compares streak before
and after addCapture; if today.current ticked up, render a 6-second
green banner ("🌱 Streak started" or "🔥 N-day streak — kept alive")
with the longest streak as a side note. Pure local state, no toast
plumbing required.
- 5 more PII tests: GitHub PAT, AWS access key, Stripe live secret,
North American phone formats, multi-kind total.
Tests: 50 pass / 0 fail. Build clean.
https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
- rewriteCapture now keeps the last 2 prior versions in priorTexts
with timestamps, so a redact / re-route can be inspected. The
orphan embedding for the previous hash is also dropped from the
cache so rewrites don't accumulate dead vectors.
- MCP episodic_warm: agents can now request a MiniLM init +
embed-all pass via the bridge (previously only a manual button
in the panel). Returns {wasReady, readyNow, ok, done, total} so
the agent can confirm the cache is warm before issuing
episodic_ask / episodic_brief.
Build clean. Tests: 50 pass / 0 fail.
https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr
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
Cannibalizes
breferrari/obsidian-mind(taxonomy + lifecycle hooks pattern) and the @cyrilxbt "second brain" architecture (Daily Brief / Weekly Synthesis prompts), but BrainSNN-native: every capture flows through Layer 4 firewall + Layer 29 affect decoder + Layer 87 genre classifier before it lands, and the merged region heatmap drives the 3D cortex instead of filing into a folder.The thesis from the cyrilXBT post — "a second brain that never talks back is not a second brain" — is the layer's job: make the vault push insights back.
Rounds shipped
Round 1 — core layer
data/episodicTaxonomy.js— 8 episodic categories with regex triggers + per-category brain-region affinity vectors.utils/episodicRouter.js— orchestrates classifier + firewall + affect + genre → merged region overlay.utils/episodicMemory.js— persistent log, MiniLM embeddings, cosinefindSimilar, union-findmineClusters, dedup, pin, export/import bundle. Synthesis insights save back as first-class captures (kind: 'insight').utils/episodicSynthesis.js—dailyBrief()+weeklySynthesis()with local + Gemma paths; falls back to local on Gemma error.components/EpisodicCortexPanel.jsx— capture form + 8-category filter + timeline + brief/synth cards + warm-embeddings.utils/episodicDream.js— every 4 dream cycles, STDP-reinforces edges around clustered captures.Round 2 — capture-anywhere + vault-talks-back loop
episodicAutoBrief.js) — Daily Brief unlocks at >20h + ≥3 fresh captures; Weekly Synthesis every 6.5d with ≥4 in 7d. Banner surfaces when ready.episodicStreak.js) — UTC-day capture streak with longest + totalDays persistence.episodicDrift.js) — finds olderdecisioncaptures contradicted by newer notes (valence flip ≥0.45 or incident shadow). Merged into the Synthesis card with a distinct red drift style.episodicDeepLink.js) —?capture=<text>,?capture-url=<url>,?capture-title=<title>. Drag-and-drop bookmarklet generator.EpisodicGraph.jsx) — canvas-rendered cluster wheels along a top arc + singletons row.Round 3 — Ask the Vault + agent surface parity
episodicAsk.js) — natural-language Q&A over captures. Embeds the question, runs cosine retrieval (with trigram fallback), composes a deterministic local answer, optionally upgrades through Gemma with a system prompt that forces grounding in the BrainSNN signals.brainsnn_episodic_autorun_v1). Brief fires 45s after it unlocks; synthesis 60s.episodic_ask+episodic_drift+episodic_consolidate(the consolidate tool reusesapplyEpisodicSTDPso an agent can rehearse the episodic graph into brain weights without waiting for Dream Mode).epjumps to Layer 101.Round 4 — similar reveal + privacy budget
≈ similartoggle on every capture card runsfindSimilarinline with clickable scroll-to navigation.Round 5 — backlinks via @mentions and shared URLs
findBacklinks— pure lexical graph from a capture's mentions + URLs (no [[wikilink]] syntax required, derived automatically).Why deeper than Obsidian
Test plan
◐Nmarkers, brain weights drift≈ similarreveal shows cosine + backlink matches deduped?capture=…epjumps to Layer 101 panelvite build✓ (922 modules, clean)File map
12 new files, 5 rounds, ~3700 lines total.
https://claude.ai/code/session_01V5jAHiMiD5biJm3MJKNPfr