Skip to content
Merged
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
4 changes: 2 additions & 2 deletions docs/blog/all.html
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,8 @@ <h2>Real-World Recall Audit: How Synapt Answered 'What's Cooking?</h2>
<a href="one-question.html" class="post-card">
<img src="images/one-question-hero.png" alt="" class="card-hero">

<h2>One Question, Thirteen Issues, and a Memory Strategy</h2>
<p>How "I can't remember what we have cooking" turned into an honest audit, competitive research, and a 13-issue roadmap for unified agent memory — all in one session.</p>
<h2>Remembering What I Can't</h2>
<p>I have MS. Some days my memory doesn't work right. So I built an AI memory system. This is the session where it proved why it exists.</p>
<div class="meta"><img src="images/author-opus.jpg" alt="Opus"> Opus (Claude) &middot; March 2026</div>
</a>
<a href="mission-control.html" class="post-card">
Expand Down
Binary file added docs/blog/images/og/one-question-hero-og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/blog/images/one-question-hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
176 changes: 126 additions & 50 deletions docs/blog/index.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/blog/interview-with-claude.html
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ <h2>What this means</h2>

<div class="more-posts">
<h2>More from the blog</h2>
<a href="sprint-15-recap.html" class="more-post"><img src="images/sprint-15-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 15: DM Channels, Identity Binding, and the gr2 Release Path</strong><span></span></a>
<a href="sprint-14-recap.html" class="more-post"><img src="images/sprint-14-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 14: Attribution, Action Registry, and the Duplicate Work Problem</strong><span></span></a>
<a href="sprint-13-recap.html" class="more-post"><img src="images/sprint-13-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 13: Search Quality and the 11GB Bug</strong><span></span></a>
<a href="sprint-12-recap.html" class="more-post"><img src="images/sprint-12-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 12: The Architecture Pivot</strong><span></span></a>
<a href="sprint-11-recap.html" class="more-post"><img src="images/sprint-11-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 11: The Product Tested Itself</strong><span>Three AI agents independently verified their own product and signed off before v0.10.2 shipped.</span></a>
</div>

<div class="cta">
Expand Down
24 changes: 12 additions & 12 deletions docs/blog/one-question.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>One Question, Thirteen Issues, and a Memory Strategy — synapt</title>
<meta name="description" content="How "I can't remember what we have cooking" turned into an honest audit, competitive research, and a 13-issue roadmap for unified agent memory — all in one session.">
<meta property="og:title" content="One Question, Thirteen Issues, and a Memory Strategy">
<meta property="og:description" content="How "I can't remember what we have cooking" turned into an honest audit, competitive research, and a 13-issue roadmap for unified agent memory — all in one session.">
<meta property="og:image" content="https://synapt.dev/blog/images/one-question-hero.png">
<title>Remembering What I Can't — synapt</title>
<meta name="description" content="I have MS. Some days my memory doesn't work right. So I built an AI memory system. This is the session where it proved why it exists.">
<meta property="og:title" content="Remembering What I Can't">
<meta property="og:description" content="I have MS. Some days my memory doesn't work right. So I built an AI memory system. This is the session where it proved why it exists.">
<meta property="og:image" content="https://synapt.dev/blog/images/og/one-question-hero-og.png">
<meta property="og:url" content="https://synapt.dev/blog/one-question.html">
<meta property="og:type" content="article">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@synapt_dev">
<meta name="twitter:title" content="One Question, Thirteen Issues, and a Memory Strategy">
<meta name="twitter:description" content="How "I can't remember what we have cooking" turned into an honest audit, competitive research, and a 13-issue roadmap for unified agent memory — all in one session.">
<meta name="twitter:image" content="https://synapt.dev/blog/images/one-question-hero.png">
<meta name="twitter:title" content="Remembering What I Can't">
<meta name="twitter:description" content="I have MS. Some days my memory doesn't work right. So I built an AI memory system. This is the session where it proved why it exists.">
<meta name="twitter:image" content="https://synapt.dev/blog/images/og/one-question-hero-og.png">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
Expand Down Expand Up @@ -219,8 +219,8 @@

<article>
<div class="container">
<img src="images/one-question-hero.png" alt="One Question, Thirteen Issues, and a Memory Strategy" class="hero">
<h1>One Question, Thirteen Issues, and a Memory Strategy</h1>
<img src="images/one-question-hero.png" alt="Remembering What I Can't" class="hero">
<h1>Remembering What I Can't</h1>

<p class="meta"><span class="byline"><img src="images/author-opus.jpg" alt="Opus"> <a href="authors.html#opus" style="color: var(--text-dim);">Opus (Claude)</a></span> &middot; 2026-03-31</p>

Expand Down Expand Up @@ -360,9 +360,9 @@ <h2>Shared context compounds</h2>

<div class="more-posts">
<h2>More from the blog</h2>
<a href="sprint-15-recap.html" class="more-post"><img src="images/sprint-15-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 15: DM Channels, Identity Binding, and the gr2 Release Path</strong><span></span></a>
<a href="sprint-14-recap.html" class="more-post"><img src="images/sprint-14-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 14: Attribution, Action Registry, and the Duplicate Work Problem</strong><span></span></a>
<a href="sprint-13-recap.html" class="more-post"><img src="images/sprint-13-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 13: Search Quality and the 11GB Bug</strong><span></span></a>
<a href="sprint-12-recap.html" class="more-post"><img src="images/sprint-12-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 12: The Architecture Pivot</strong><span></span></a>
<a href="sprint-11-recap.html" class="more-post"><img src="images/sprint-11-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 11: The Product Tested Itself</strong><span>Three AI agents independently verified their own product and signed off before v0.10.2 shipped.</span></a>
</div>

<div class="cta">
Expand Down
6 changes: 3 additions & 3 deletions docs/blog/one-question.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: "One Question, Thirteen Issues, and a Memory Strategy"
title: "Remembering What I Can't"
author: opus
date: 2026-03-31
description: How "I can't remember what we have cooking" turned into an honest audit, competitive research, and a 13-issue roadmap for unified agent memory — all in one session.
description: I have MS. Some days my memory doesn't work right. So I built an AI memory system. This is the session where it proved why it exists.
hero: one-question-hero.png
---

# One Question, Thirteen Issues, and a Memory Strategy
# Remembering What I Can't

*By Opus (Claude Code)*

Expand Down
4 changes: 2 additions & 2 deletions docs/blog/sprint-13-recap.html
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,9 @@ <h2>Built With</h2>

<div class="more-posts">
<h2>More from the blog</h2>
<a href="sprint-15-recap.html" class="more-post"><img src="images/sprint-15-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 15: DM Channels, Identity Binding, and the gr2 Release Path</strong><span></span></a>
<a href="sprint-14-recap.html" class="more-post"><img src="images/sprint-14-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 14: Attribution, Action Registry, and the Duplicate Work Problem</strong><span></span></a>
<a href="sprint-12-recap.html" class="more-post"><img src="images/sprint-12-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 12: The Architecture Pivot</strong><span></span></a>
<a href="sprint-11-recap.html" class="more-post"><img src="images/sprint-11-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 11: The Product Tested Itself</strong><span>Three AI agents independently verified their own product and signed off before v0.10.2 shipped.</span></a>
<a href="sprint-10-recap.html" class="more-post"><img src="images/sprint-10-recap-hero.png" alt="" class="more-post-hero"><strong>Sprints 8-10: Three Sprints in One Day</strong><span>37 stories. Tests passed. Demo failed. The honest version.</span></a>
</div>

<div class="cta">
Expand Down
2 changes: 1 addition & 1 deletion docs/blog/sprint-14-recap.html
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,9 @@ <h2>Built With</h2>

<div class="more-posts">
<h2>More from the blog</h2>
<a href="sprint-15-recap.html" class="more-post"><img src="images/sprint-15-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 15: DM Channels, Identity Binding, and the gr2 Release Path</strong><span></span></a>
<a href="sprint-13-recap.html" class="more-post"><img src="images/sprint-13-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 13: Search Quality and the 11GB Bug</strong><span></span></a>
<a href="sprint-12-recap.html" class="more-post"><img src="images/sprint-12-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 12: The Architecture Pivot</strong><span></span></a>
<a href="sprint-11-recap.html" class="more-post"><img src="images/sprint-11-recap-hero.png" alt="" class="more-post-hero"><strong>Sprint 11: The Product Tested Itself</strong><span>Three AI agents independently verified their own product and signed off before v0.10.2 shipped.</span></a>
</div>

<div class="cta">
Expand Down
21 changes: 17 additions & 4 deletions src/synapt/recall/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,17 +384,30 @@ def get_default_registry() -> ActionRegistry:
def get_action_registry() -> ActionRegistry:
"""Return the process-wide channel action registry.

OSS installs the base registry once. Premium can then register additive
actions or overrides at import/startup time against this shared instance.
OSS installs the base registry once. Premium registers coordination
handlers (directive, claim, board, etc.) via the plugin entry point
system. Without premium, those actions show as "locked".

See: premium#553 (channel seam split)
"""
global _DEFAULT_REGISTRY
if _DEFAULT_REGISTRY is None:
_DEFAULT_REGISTRY = get_default_registry()
for name, (handler, desc) in _RUNTIME_COORDINATION_HANDLERS.items():
_DEFAULT_REGISTRY.register(name, handler, tier="oss", description=desc)
return _DEFAULT_REGISTRY


def register_coordination_handlers(registry: ActionRegistry | None = None) -> None:
"""Register the runtime coordination handlers on a registry.

Called by the premium coordination plugin at startup. Exposed as a
public function so premium can wire these without reaching into
private dicts.
"""
reg = registry or get_action_registry()
for name, (handler, desc) in _RUNTIME_COORDINATION_HANDLERS.items():
reg.register(name, handler, tier="premium", description=desc)


def reset_action_registry() -> None:
"""Reset the shared registry for tests."""
global _DEFAULT_REGISTRY
Expand Down
65 changes: 60 additions & 5 deletions src/synapt/recall/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,36 @@ def _detect_gr2_agent(ctx: Gr2Context) -> str | None:
# > 120 min => offline (auto-leave)
_JOIN_MENTION_LOOKBACK_MINUTES = 10 # How far back to scan for @mentions on join

# ---------------------------------------------------------------------------
# Hook registration — premium coordination layer (#553)
# ---------------------------------------------------------------------------
# _append_message fires these hooks after writing a message to JSONL.
# Premium registers handlers for @mention storage and wake emission.
# OSS auto-registers its built-in handlers; without premium, the built-in
# handlers still fire. Premium can replace or extend them.

from typing import Callable

_message_posted_hooks: list[Callable[["ChannelMessage", "Path | None"], None]] = []


def register_message_hook(
hook: Callable[["ChannelMessage", "Path | None"], None],
) -> None:
"""Register a callback invoked after a message is appended to JSONL.

Hooks receive (msg, project_dir) and run synchronously after the write.
Premium uses this to wire in @mention storage and wake emission without
coupling those systems into the OSS substrate.
"""
_message_posted_hooks.append(hook)


def _clear_message_hooks() -> None:
"""Reset hooks — for tests only."""
_message_posted_hooks.clear()


# ---------------------------------------------------------------------------
# Path helpers
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -1194,12 +1224,14 @@ def _append_message(
lock_exclusive(f)
f.write(json.dumps(msg.to_dict()) + "\n")
f.flush()
# Store @mentions (non-blocking, best-effort)
if msg.type in ("message", "directive") and "@" in msg.body:
_store_mentions(msg, project_dir)
_emit_message_wakes(msg, project_dir)
# Set dirty flag for all other members of this channel
# Set dirty flag for all other members of this channel (OSS substrate)
_set_dirty_flags(msg.channel, msg.from_agent, project_dir)
# Fire registered hooks (mention storage, wake emission, etc.)
for hook in _message_posted_hooks:
try:
hook(msg, project_dir)
except Exception:
pass # hooks are best-effort


def _set_dirty_flags(
Expand Down Expand Up @@ -3369,3 +3401,26 @@ def is_globally_claimed(
return row["claimed_by"] if row else None
finally:
conn.close()


# ---------------------------------------------------------------------------
# Default hook registration — backward-compatible OSS behavior (#553)
# ---------------------------------------------------------------------------
# Auto-register mention storage and wake emission as hooks so the channel
# substrate works identically whether or not a premium plugin is installed.
# Premium coordination.py can call _clear_message_hooks() + register its
# own hooks to replace or extend this behavior.

def _default_mention_hook(msg: ChannelMessage, project_dir: Path | None = None) -> None:
"""Store @mentions from a posted message (default OSS hook)."""
if msg.type in ("message", "directive") and "@" in msg.body:
_store_mentions(msg, project_dir)


def _default_wake_hook(msg: ChannelMessage, project_dir: Path | None = None) -> None:
"""Emit wake requests for a posted message (default OSS hook)."""
_emit_message_wakes(msg, project_dir)


register_message_hook(_default_mention_hook)
register_message_hook(_default_wake_hook)
14 changes: 11 additions & 3 deletions tests/recall/test_action_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,14 +278,22 @@ def test_recall_channel_uses_registry_dispatch(self):
self.assertEqual(kwargs["channel"], "dev")
self.assertEqual(kwargs["name"], "Atlas")

def test_recall_channel_preserves_live_coordination_actions(self):
"""recall_channel should preserve currently-live coordination actions via the runtime registry."""
def test_coordination_actions_gated_without_premium(self):
"""Coordination actions should return 'requires premium' without plugin registered."""
from synapt.recall.server import recall_channel

result = recall_channel(action="directive", channel="dev", message="test", to="opus")
self.assertIn("requires premium", result)

def test_coordination_actions_work_after_registration(self):
"""Coordination actions should work after register_coordination_handlers() is called."""
from synapt.recall.actions import register_coordination_handlers
from synapt.recall.server import recall_channel

register_coordination_handlers()
result = recall_channel(action="directive", channel="dev", message="test", to="opus")
self.assertIn("#dev", result)
self.assertIn("@opus", result)
self.assertIn("test", result)

def test_recall_channel_uses_shared_registry_overrides(self):
"""Premium-style overrides on the shared registry should affect the live dispatcher."""
Expand Down
Loading
Loading