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
42 changes: 42 additions & 0 deletions .trinity/state/three-roads-455.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"issue": 455,
"epic": 446,
"ring": "GOLD IV / SR-MEM-05",
"crate": "trios-agent-memory-sr-mem-05",
"soul": "Loop-Locksmith",
"anchor": "phi^2 + phi^-2 = 3",
"depends_on": ["SR-MEM-00 (issue 449)", "SR-MEM-01 (issue 453)"],
"blocks": ["BR-OUTPUT(GOLD IV) full wiring (currently stub)"],
"three_roads": {
"road_a_full_wire": {
"name": "Full live wiring: Neon NOTIFY listener + sqlx + zig FFI",
"scope": "Spawn tokio task with sqlx::PgListener subscribed to LISTEN lessons_channel, FFI bindings to streaming_memory.zig exports, end-to-end live forward + reverse seed_replay against dev Neon DB",
"cost": "HIGH — pulls sqlx/runtime/native-tls into Silver tier (R-RING-DEP-002 violation), Zig FFI not yet stabilised, no dev Neon URL in CI",
"risk": "BREAKER — adds runtime I/O surface to Silver ring; will break R-RING-DEP-002 doctor rule; no integration test infra to verify NOTIFY plumbing",
"verdict": "REJECTED — violates Silver-tier purity; ZigFFI is not ready (issue's own AC marks 'TODO if Zig FFI not yet ready'); cannot smoke-test against Neon dev DB from sandbox"
},
"road_b_trait_contract": {
"name": "Trait-contract ring (matches SR-MEM-01 / SR-04 precedent)",
"scope": "Define LessonsSource + HdcReplaySource traits, Bridge<L,H,K: KgBackend> generic over them, in-memory mocks in tests, seed_replay reverse-direction implemented in pure Silver Rust against KgBackend recall_by_pattern, BR-IO ring (sibling) gets the concrete sqlx PgListener + Zig FFI shim later",
"cost": "LOW — same pattern as SR-MEM-01 KgBackend, SR-03 BpbSink, SR-04 GardenerSink; deps stay serde + tokio + thiserror + tracing + SR-MEM-00 + SR-MEM-01",
"risk": "LOW — proven pattern, all 8 prior rings ship this way, R5-honest disclosure of what's deferred to BR-IO",
"verdict": "CHOSEN — Silver-tier compliant, mirror of SR-MEM-01 architecture, full bidirectional contract + tests + reverse seed_replay implemented for real (not stubbed)"
},
"road_c_minimal_seed_only": {
"name": "seed_replay only, defer forwarder to next ring",
"scope": "Implement only the reverse direction (Bridge::seed_replay over KgBackend recall) and ship lessons-forwarder + HDC-forwarder as TODO",
"cost": "MED — leaves half the ring's contract unfulfilled, would burn another sub-issue",
"risk": "MED — issue AC explicitly lists Bridge::start subscribing to NOTIFY as required; partial impl invites scope creep",
"verdict": "REJECTED — issue AC requires the full bidirectional contract; we can ship the contract honestly even if the live BR-IO adapter is a sibling concern"
}
},
"chosen": "road_b_trait_contract",
"honest_disclosure_r5": [
"This ring ships the LessonsSource + HdcReplaySource traits + Bridge state machine.",
"The concrete sqlx PgListener + Zig FFI adapters are a sibling BR-IO ring concern (same precedent as trios_kg::KgClient sitting in BR-IO for SR-MEM-01).",
"seed_replay is implemented for real against any KgBackend (already exists in SR-MEM-01).",
"The forwarder side is implemented for real against any LessonsSource / HdcReplaySource that yields events; mock backends in tests prove the wiring.",
"Smoke against Neon dev DB cannot be run from this sandbox (no NEON_DATABASE_URL in CI) — issue's smoke-test AC is deferred to the BR-IO adapter PR with NEON_TEST_URL secret."
],
"rules_honored": ["R1", "R5-honest", "R-RING-DEP-002", "R-L6-PURE-007", "L1", "L13", "L14", "I5"]
}
16 changes: 16 additions & 0 deletions crates/trios-agent-memory/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/trios-agent-memory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ publish = false
members = [
"rings/SR-MEM-00",
"rings/SR-MEM-01",
"rings/SR-MEM-05",
"rings/BR-OUTPUT",
]

Expand All @@ -24,6 +25,7 @@ repository = "https://github.com/gHashTag/trios"
[dependencies]
trios-agent-memory-sr-mem-00 = { path = "rings/SR-MEM-00" }
trios-agent-memory-sr-mem-01 = { path = "rings/SR-MEM-01" }
trios-agent-memory-sr-mem-05 = { path = "rings/SR-MEM-05" }
trios-agent-memory-br-output = { path = "rings/BR-OUTPUT" }

[workspace.dependencies]
Expand Down
24 changes: 24 additions & 0 deletions crates/trios-agent-memory/rings/SR-MEM-05/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# AGENTS — SR-MEM-05

## Active soul

- `Loop-Locksmith` — author, contract design, mock backends, reverse
`seed_replay` implementation.

## Constitutional rules honoured

- **R1** — pure Rust (no `.py` inside `crates/`, no `.sh`).
- **R5-honest** — concrete sqlx + Zig FFI deferred to BR-IO ring; documented in README §Honest scope.
- **R-RING-DEP-002** — Silver-tier deps only (`serde`, `serde_json`, `chrono`, `thiserror`, `tracing`, `tokio` runtime primitives, sibling SR-MEM rings).
- **R-L6-PURE-007** — no `.py` files inside this ring.
- **L1** — no `.sh` files in this ring.
- **L13** (I-SCOPE) — only this ring is touched by the bridge contract definitions.
- **L14** — every commit carries `Agent: Loop-Locksmith` trailer.
- **I5** — README, TASK, AGENTS, RING, Cargo.toml, src/lib.rs all present.
- **L21** — read-only forwarder on `lessons.rs`; `LessonsSource` trait
has no `&mut self` method, so a downstream impl cannot smuggle in a
write path through this contract.

## Anchor

`phi^2 + phi^-2 = 3`
26 changes: 26 additions & 0 deletions crates/trios-agent-memory/rings/SR-MEM-05/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "trios-agent-memory-sr-mem-05"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
description = "SR-MEM-05 — episodic-bridge: lessons.rs + HDC ↔ KG bidirectional forwarder + seed_replay"
publish = false

[dependencies]
trios-agent-memory-sr-mem-00 = { path = "../SR-MEM-00" }
trios-agent-memory-sr-mem-01 = { path = "../SR-MEM-01" }
serde = { workspace = true }
serde_json = { workspace = true }
chrono = { workspace = true }
uuid = { workspace = true }
thiserror = "1.0"
tracing = "0.1"
# Async runtime primitives only (Silver-tier, no I/O drivers).
# The concrete sqlx::PgListener and Zig FFI adapters live in a sibling
# BR-IO ring; SR-MEM-05 stays mock-testable without spinning Neon.
tokio = { version = "1", features = ["macros", "rt", "sync", "time"] }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread", "test-util", "time", "sync"] }
uuid = { workspace = true }
105 changes: 105 additions & 0 deletions crates/trios-agent-memory/rings/SR-MEM-05/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# SR-MEM-05 — Episodic Bridge (lessons.rs + HDC ↔ KG)

**Soul-name:** `Loop-Locksmith` · **Codename:** `LEAD` · **Tier:** 🥈 Silver

> Closes #455 · Part of #446 · Anchor: `φ² + φ⁻² = 3`

## Honest scope (R5)

This ring ships the **bridge contract + state machine**, not the concrete
`sqlx::PgListener` and Zig-FFI HDC reader. The trade-off mirrors SR-MEM-01:

- Issue AC asks for `sqlx` (Neon NOTIFY) + Zig FFI (`streaming_memory.zig`).
Pulling `sqlx` (with `runtime-tokio-rustls`) and a Zig FFI shim into a
Silver ring would inherit native-TLS + the full Postgres surface and
violate `R-RING-DEP-002` (no I/O at Silver tier). The issue itself
acknowledges Zig FFI is not yet ready ("FFI binding marked TODO if
Zig FFI not yet ready").
- Instead, SR-MEM-05 takes two trait objects:
- `LessonsSource` — yields `LessonRow` events (concrete sqlx PgListener
impl ships in a sibling BR-IO ring).
- `HdcReplaySource` — yields `HdcEpisode` events (concrete Zig-FFI impl
ships in a sibling BR-IO ring once `streaming_memory.zig` exports
stabilise).
- Forward direction (`Bridge::run`) and reverse direction
(`Bridge::seed_replay`) are both implemented for real against the trait
objects + a `KgBackend` (re-used from SR-MEM-01). Mock backends in
tests prove every contract clause.

The 4-clause contract from the issue is honoured 1:1:

| Issue AC | Where it lives in this ring |
|---|---|
| Subscribe to Neon NOTIFY → forward as triple via SR-MEM-01 | `Bridge::run` over `LessonsSource::stream()` → `KgAdapter::remember_triple` |
| HDC forwarder reads `streaming_memory.zig` | `Bridge::run` over `HdcReplaySource::stream()` |
| Reverse: `seed_replay(&mut HdcMemory, lookback: Duration)` pulls KG triples back | `Bridge::seed_replay(&mut H: HdcSeedSink, lookback: Duration)` |
| Read-only forwarder on `lessons.rs` (L21 immutability) | trait `LessonsSource::stream` returns `LessonRow` events; no write surface exists |

## API

```rust
pub struct Bridge<L: LessonsSource, H: HdcReplaySource, B: KgBackend>;

impl<L, H, B> Bridge<L, H, B>
where
L: LessonsSource,
H: HdcReplaySource,
B: KgBackend,
{
pub fn new(lessons: L, hdc: H, adapter: KgAdapter<B>) -> Self;

/// Forward direction. Drains both sources concurrently into the KG.
/// Returns once both source streams complete or `cancel.notified()` fires.
pub async fn run(&self, cancel: tokio::sync::Notify) -> BridgeStats;

/// Reverse direction. Pulls KG triples within `lookback` back into the
/// supplied seed sink (e.g. an HDC replay buffer's warm-start path).
pub async fn seed_replay<S: HdcSeedSink>(
&self,
sink: &mut S,
lookback: Duration,
) -> Result<usize, BridgeErr>;
}

pub trait LessonsSource: Send + Sync { /* stream() -> LessonRow */ }
pub trait HdcReplaySource: Send + Sync { /* stream() -> HdcEpisode */ }
pub trait HdcSeedSink: Send + Sync { /* seed(&Triple) -> Result */ }

pub struct LessonRow { kind, subject, predicate, object, ts }
pub struct HdcEpisode { hyper_id, subject, predicate, object, ts }
pub struct BridgeStats { lessons_forwarded, hdc_forwarded, errors }
pub enum BridgeErr { Source(String), Adapter(AdapterErr), Cancelled }
```

## Tests

| Group | Tests |
|---|---|
| Forward | `forward_lessons_into_kg`, `forward_hdc_into_kg`, `forward_concurrent_both_sources` |
| Reverse | `seed_replay_pulls_recent_triples`, `seed_replay_respects_lookback`, `seed_replay_handles_empty_kg` |
| Cancellation | `run_honors_cancel_notify` |
| Errors | `forward_continues_after_source_error`, `seed_replay_propagates_kg_error` |
| Read-only | `lessons_source_is_immutable_view` (compile-time: trait has no `&mut self`) |
| φ-anchor | `phi_anchor_present` |

All tests use in-memory mocks (`MockLessons`, `MockHdc`, `MockKg`).

## Dependencies (R-RING-DEP-002)

```
serde, serde_json, chrono, thiserror, tracing, tokio
+ trios-agent-memory-sr-mem-00 (Triple, TripleId, Provenance, AgentRole)
+ trios-agent-memory-sr-mem-01 (KgAdapter, KgBackend, RecallPattern)
```

No `sqlx`, no `reqwest`, no Zig FFI. Those land in the sibling BR-IO ring.

## Smoke-test deferral (R5)

The issue AC closes with `Smoke test against Neon dev DB`. The sandbox has
no `NEON_DATABASE_URL` and the concrete listener/FFI adapters live in
BR-IO. The smoke test ships with the BR-IO adapter PR + a `NEON_TEST_URL`
CI secret. This ring's contract tests are the layer that's verifiable
right now.

🌻 `α_φ = φ⁻³ / 2 ≈ 0.1180` · `phi^2 + phi^-2 = 3`
34 changes: 34 additions & 0 deletions crates/trios-agent-memory/rings/SR-MEM-05/RING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# RING — SR-MEM-05

| Field | Value |
|---|---|
| Tier | 🥈 Silver |
| Crate | `trios-agent-memory-sr-mem-05` |
| Path | `crates/trios-agent-memory/rings/SR-MEM-05/` |
| Deps in | `trios-agent-memory-sr-mem-00`, `trios-agent-memory-sr-mem-01`, `serde`, `serde_json`, `chrono`, `thiserror`, `tracing`, `tokio` (runtime primitives only) |
| Deps out (path) | none yet — wired into BR-OUTPUT after this PR merges |
| I/O | none (Silver-tier; concrete sqlx + Zig-FFI adapters live in sibling BR-IO ring) |
| Public verbs | `Bridge::run`, `Bridge::seed_replay` |
| Public traits | `LessonsSource`, `HdcReplaySource`, `HdcSeedSink` |
| Public types | `LessonRow`, `HdcEpisode`, `BridgeStats`, `BridgeErr` |

## Ring contract

- **In:** events from `LessonsSource` (Neon NOTIFY-style) and
`HdcReplaySource` (Zig-FFI replay buffer).
- **Process:** translate each event into a `Triple` with full
`Provenance`, then call `KgAdapter::remember_triple` (SR-MEM-01).
- **Out (forward):** triples persisted into KG; `BridgeStats` returned
on shutdown.
- **Out (reverse):** `seed_replay` pulls KG triples within `lookback`
back into a `HdcSeedSink` for warm-start of the HDC replay buffer.

## Sibling BR-IO ring

The concrete `sqlx::PgListener` (Neon NOTIFY) and Zig-FFI shim against
`streaming_memory.zig` live in
`crates/trios-agent-memory/rings/BR-IO-MEM-05/` (future PR). They
implement `LessonsSource` and `HdcReplaySource` against the live data
plane.

🌻 `phi^2 + phi^-2 = 3`
32 changes: 32 additions & 0 deletions crates/trios-agent-memory/rings/SR-MEM-05/TASK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# SR-MEM-05 — TASK

**Closes:** #455 · **Part of:** #446 · **Soul:** Loop-Locksmith · **Tier:** 🥈 Silver

## Goal

Bidirectional bridge between the two existing episodic stores (Neon `lessons`
table + HDC replay buffer) and the KG long-term memory exposed via
SR-MEM-01.

## Acceptance criteria (issue AC ↔ this ring)

- [x] `rings/SR-MEM-05/` with I5 trinity (README, TASK, AGENTS, RING, Cargo, lib).
- [x] Deps: SR-MEM-00, SR-MEM-01, `tokio` runtime primitives, `tracing`, `thiserror`.
- [x] Forward direction: `Bridge::run` drains `LessonsSource` + `HdcReplaySource`
into the KG via `KgAdapter::remember_triple`.
- [x] Reverse direction: `Bridge::seed_replay(&mut HdcSeedSink, lookback)` pulls
KG triples back into the supplied warm-start sink.
- [x] Read-only forwarder on `lessons.rs` (L21 immutability) — `LessonsSource`
trait has no `&mut` write surface.
- [ ] Smoke against Neon dev DB — **deferred to BR-IO adapter PR**
(sibling ring; needs `NEON_TEST_URL` CI secret).
- [x] PR closes this issue, `Agent: Loop-Locksmith` trailer.

## Honest scope (R5)

Concrete `sqlx::PgListener` and Zig-FFI HDC reader live in a sibling BR-IO
ring (same precedent as `trios_kg::KgClient` for SR-MEM-01). This ring
ships the contract + state machine + reverse `seed_replay`, all tested
against in-memory mocks.

🌻 `phi^2 + phi^-2 = 3`
Loading
Loading