Cosmoplot: Chartplotter for our sky, 3D Chart Uses JWST/MAST & other surveys Organizes and Displays the actual Data

https://cosmoplot.io/

WIP in progress; but very cool way to explore the massive amount of information we have collected

Designed with these core tenants

- Science engine is deterministic

- Rendering is downstream of a typed appearance model

- Observed, derived, inferred, proxy, and artistic values stay distinct

- Provenance should be visible near every claim

- Visuals are evidence-constrained interpretation, not observation 

https://preview.redd.it/cz2pswx83cbh1.png?width=1868&format=png&auto=webp&s=29825f14904a773f8317cc4712f48526732c47c3

reddit.com
u/Empty-Poetry8197 — 1 day ago

AURA: Handshake the Structure, Then Send the Change Recoup Bandwidth

https://preview.redd.it/9iglpdwnobbh1.png?width=1672&format=png&auto=webp&s=bcfeb5fd9888e4de66ee0bc4657a752b28df2ccf

Agent traffic has a strange property: almost every byte is a repeat. Two AI systems exchanging MCP tool calls, A2A task updates, or OpenAI-style function calls send jsonrpcmethodparamstrace_idtask_id, and the same schema fragments thousands of times per minute. The values change. The structure barely does.

AURA is an experimental, protocol-aware data-movement toolkit built around that observation. Its main path is AIWire: a negotiated structure side channel that lets two peers agree on message structure once, then move compact deltas over ordinary TCP, WebSocket, HTTP, or broker links instead of re-sending whole JSON frames.

The steady state AIWire aims for is not "send a whole frame more cheaply." It is "handshake the structure, then send the change."

Why stateless compression leaves so much on the table

The obvious fix for verbose JSON is gzip or zlib per message. That works, but it has two structural problems for agent traffic:

  1. Every frame pays setup cost. Stateless compression treats each message as unrelated text and rediscovers the same patterns every time.
  2. History is thrown away. Frame 4,000 of a session looks almost identical to frame 3,999, but a per-frame codec cannot use that.

AIWire keeps a live compression stream per direction across the whole session, seeds it with a static dictionary of common AI protocol fields, and lets peers negotiate session-specific templates on top. After the handshake, the hot path carries only what changed against structure both sides already share.

The three-lane model

The part of the design I find most interesting is that AIWire refuses to treat a connection as one undifferentiated pipe. It splits AI traffic into three logical lanes over whatever transport you already have:

The semantic/message lane carries the actual agent messages: MCP tool calls, JSON-RPC requests and responses, A2A task and artifact updates, traces, handoffs, results. This is the lane the dictionary, session templates, and stateful delta stream optimize.

The control/session lane carries the machinery that keeps the semantic lane safe: handshakes, template discovery, dictionary diffs, ACK/NACK, resume negotiation, heartbeats, and reset signals. The spec requires that control messages stay decodable without inflating the semantic stream. If the compressed stream is resyncing or has failed, you can still read the control lane and recover. Your ops path never depends on the health of the compression state it is trying to fix.

The blob descriptor lane handles the things that should never go through a structured-message codec at all: media, tensor chunks, model artifacts, log archives. The bytes move over a normal blob or file transport. AIWire carries the metadata: content type, SHA-256 digests, chunk manifests, route, priority, and transfer status. A receiver can schedule, verify, and account for a 2 GB artifact without ever pulling it through the message path, and a semantic-lane reset does not invalidate a completed digest-verified transfer.

The separation is a safety argument as much as a performance one. Under congestion, control messages get priority over bulk bytes. Blob descriptors are forbidden from mutating the session dictionary. Each lane fails independently.

Fail closed, by contract

Shared compression state is dangerous if the two sides ever disagree, so the AIWire v1 spec is aggressive about verification:

  • The handshake compares static dictionary SHA-256 and byte size, template hashes and counts, and zlib parameters. Any mismatch fails closed or falls back to raw/zlib only if the application explicitly allowed it.
  • Session dictionary growth is append-only, epoch-numbered, and proposed through diffs that carry previous and next state hashes, a fresh nonce, a diff identity hash, and an optional HMAC-SHA256 tag. A sender may not encode against new structure until the matching ACK is verified.
  • Resume handshakes let a client reconnect against a cached dictionary state, but only if the receiver actually holds one of the offered state hashes.
  • Any inflate error, hash mismatch, or ordering violation means stop, rehandshake, or fall back. The spec's phrasing: peers must not continue sending compact deltas against uncertain structure.

The metric is exchanges, not ratio

AURA's docs are explicit that compression ratio alone is the wrong scoreboard. The question is how many verified semantic exchanges fit through a link once bandwidth, p95 latency, and codec CPU are accounted for.

On a modeled 10 Mbps link with protocol-shaped request/response traffic (native C++ backend, 2026-07-04):

Codec Bytes/exchange Bandwidth-capped ex/s Gain over raw
raw JSON 1,177 1,756 1.00x
zlib per frame 696 2,992 1.70x
AIWire 157 11,017 6.28x
AIToken + AIWire 125 12,948 7.38x

A live TCP replay of the committed public session corpus, with 64 concurrent logical agents and SHA-256 verification of every response, pushed further: AIWire averaged 45.6 bytes per exchange for a 24x bandwidth gain, and the combined AIToken + AIWire path hit 32.3 bytes per exchange, a 34x gain with 97.1% of bytes saved. At that point the modeled link was no longer the bottleneck; the runtime could not keep enough requests in flight to fill the headroom.

That last detail is the honest core of the project. Smaller frames only matter if your system has enough concurrent work to use the room they create. AURA ships the extrapolation tooling to reason about exactly that: given a bandwidth, a p95 latency, and a per-agent window, how many agents does it take to saturate the link.

Where it fits

AURA is for situations where you control both ends of the link and the traffic has repeated structure:

  • Multi-agent request/response loops. Orchestrators, workers, and reviewers exchanging thousands of small task, status, and result messages.
  • MCP and JSON-RPC tool traffic. Tool calls and tool results are the canonical case of stable structure with changing values.
  • Local AI clusters and edge links. The repo's LAN benchmark runs a Mac against a Z6 workstation and Jetson Nano-class boards; a bandwidth-limited edge mesh is exactly where an 86 to 97% byte reduction converts into headroom for telemetry, media, and retries.
  • Structured logs and traces. Repeated field names, session-stable shapes, high volume.
  • Binary payload routing. Agents that need to schedule, verify, and track opaque artifacts by digest without moving the bytes through the message path.

What it is not

The README is unusually direct about limits, and it is worth repeating them. AURA is not a drop-in replacement for gzip, zstd, TLS, or a message broker. It does not define transport security, retries, or backpressure; those stay at the transport layer. The stateful stream means frames cannot be reordered or dropped inside a session, so lossy transports need their own recovery layer. And it is not production-ready: it is a prototyping and measurement toolkit with a working Python path, a native C++ backend, deterministic public fixtures, and reproducible benchmark harnesses.

That fixture corpus deserves a mention. The repo commits a synthetic public session corpus covering MCP, A2A, OpenAI Responses, traces, handoffs, and memory writes, wrapped in the full side-channel lifecycle: forced handshake, template update, authenticated dictionary diff, ACK, and resume. Anyone can replay the exact benchmark and check the numbers.

Trying it

from aura_compression import AIWireSessionEncoder, AIWireSessionDecoder

message = {
    "protocol": "mcp",
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {"name": "read_file", "arguments": {"uri": "repo://service/path.py"}},
}

with AIWireSessionEncoder(level=3) as encoder, AIWireSessionDecoder() as decoder:
    delta = encoder.compress_message(message)
    restored = decoder.decompress_message(delta)

assert restored == message

The repo includes transport examples for length-prefixed TCP, WebSocket, HTTP with Server-Sent Events, and a local broker, plus the full benchmark harness used for the numbers above.

Agent-to-agent traffic is growing faster than the links it runs on, and most of it is the same structure sent again and again. AURA's bet is that the fix belongs in a negotiated session protocol, not a per-frame codec. The three-lane model, the fail-closed handshake contract, and the exchanges-per-second scoreboard are what make it worth watching.

AURA is Apache 2.0 licensed. Code, spec, fixtures, and benchmark reports: github.com/H-XX-D/AURA.

reddit.com
u/Empty-Poetry8197 — 1 day ago

AURA: Handshake the Structure, Then Send the Change Recoup Bandwidth

https://preview.redd.it/442atnja0bbh1.png?width=1672&format=png&auto=webp&s=14f247ad5d14a9a193aed21c10fb9428c7e1375a

Agent traffic has a strange property: almost every byte is a repeat. Two AI systems exchanging MCP tool calls, A2A task updates, or OpenAI-style function calls send jsonrpcmethodparamstrace_idtask_id, and the same schema fragments thousands of times per minute. The values change. The structure barely does.

AURA is an experimental, protocol-aware data-movement toolkit built around that observation. Its main path is AIWire: a negotiated structure side channel that lets two peers agree on message structure once, then move compact deltas over ordinary TCP, WebSocket, HTTP, or broker links instead of re-sending whole JSON frames.

The steady state AIWire aims for is not "send a whole frame more cheaply." It is "handshake the structure, then send the change."

Why stateless compression leaves so much on the table

The obvious fix for verbose JSON is gzip or zlib per message. That works, but it has two structural problems for agent traffic:

  1. Every frame pays setup cost. Stateless compression treats each message as unrelated text and rediscovers the same patterns every time.
  2. History is thrown away. Frame 4,000 of a session looks almost identical to frame 3,999, but a per-frame codec cannot use that.

AIWire keeps a live compression stream per direction across the whole session, seeds it with a static dictionary of common AI protocol fields, and lets peers negotiate session-specific templates on top. After the handshake, the hot path carries only what changed against structure both sides already share.

The three-lane model

The part of the design I find most interesting is that AIWire refuses to treat a connection as one undifferentiated pipe. It splits AI traffic into three logical lanes over whatever transport you already have:

The semantic/message lane carries the actual agent messages: MCP tool calls, JSON-RPC requests and responses, A2A task and artifact updates, traces, handoffs, results. This is the lane the dictionary, session templates, and stateful delta stream optimize.

The control/session lane carries the machinery that keeps the semantic lane safe: handshakes, template discovery, dictionary diffs, ACK/NACK, resume negotiation, heartbeats, and reset signals. The spec requires that control messages stay decodable without inflating the semantic stream. If the compressed stream is resyncing or has failed, you can still read the control lane and recover. Your ops path never depends on the health of the compression state it is trying to fix.

The blob descriptor lane handles the things that should never go through a structured-message codec at all: media, tensor chunks, model artifacts, log archives. The bytes move over a normal blob or file transport. AIWire carries the metadata: content type, SHA-256 digests, chunk manifests, route, priority, and transfer status. A receiver can schedule, verify, and account for a 2 GB artifact without ever pulling it through the message path, and a semantic-lane reset does not invalidate a completed digest-verified transfer.

The separation is a safety argument as much as a performance one. Under congestion, control messages get priority over bulk bytes. Blob descriptors are forbidden from mutating the session dictionary. Each lane fails independently.

Fail closed, by contract

Shared compression state is dangerous if the two sides ever disagree, so the AIWire v1 spec is aggressive about verification:

  • The handshake compares static dictionary SHA-256 and byte size, template hashes and counts, and zlib parameters. Any mismatch fails closed or falls back to raw/zlib only if the application explicitly allowed it.
  • Session dictionary growth is append-only, epoch-numbered, and proposed through diffs that carry previous and next state hashes, a fresh nonce, a diff identity hash, and an optional HMAC-SHA256 tag. A sender may not encode against new structure until the matching ACK is verified.
  • Resume handshakes let a client reconnect against a cached dictionary state, but only if the receiver actually holds one of the offered state hashes.
  • Any inflate error, hash mismatch, or ordering violation means stop, rehandshake, or fall back. The spec's phrasing: peers must not continue sending compact deltas against uncertain structure.

The metric is exchanges, not ratio

AURA's docs are explicit that compression ratio alone is the wrong scoreboard. The question is how many verified semantic exchanges fit through a link once bandwidth, p95 latency, and codec CPU are accounted for.

On a modeled 10 Mbps link with protocol-shaped request/response traffic (native C++ backend, 2026-07-04):

Codec Bytes/exchange Bandwidth-capped ex/s Gain over raw
raw JSON 1,177 1,756 1.00x
zlib per frame 696 2,992 1.70x
AIWire 157 11,017 6.28x
AIToken + AIWire 125 12,948 7.38x

A live TCP replay of the committed public session corpus, with 64 concurrent logical agents and SHA-256 verification of every response, pushed further: AIWire averaged 45.6 bytes per exchange for a 24x bandwidth gain, and the combined AIToken + AIWire path hit 32.3 bytes per exchange, a 34x gain with 97.1% of bytes saved. At that point the modeled link was no longer the bottleneck; the runtime could not keep enough requests in flight to fill the headroom.

That last detail is the honest core of the project. Smaller frames only matter if your system has enough concurrent work to use the room they create. AURA ships the extrapolation tooling to reason about exactly that: given a bandwidth, a p95 latency, and a per-agent window, how many agents does it take to saturate the link.

Where it fits

AURA is for situations where you control both ends of the link and the traffic has repeated structure:

  • Multi-agent request/response loops. Orchestrators, workers, and reviewers exchanging thousands of small task, status, and result messages.
  • MCP and JSON-RPC tool traffic. Tool calls and tool results are the canonical case of stable structure with changing values.
  • Local AI clusters and edge links. The repo's LAN benchmark runs a Mac against a Z6 workstation and Jetson Nano-class boards; a bandwidth-limited edge mesh is exactly where an 86 to 97% byte reduction converts into headroom for telemetry, media, and retries.
  • Structured logs and traces. Repeated field names, session-stable shapes, high volume.
  • Binary payload routing. Agents that need to schedule, verify, and track opaque artifacts by digest without moving the bytes through the message path.

What it is not

The README is unusually direct about limits, and it is worth repeating them. AURA is not a drop-in replacement for gzip, zstd, TLS, or a message broker. It does not define transport security, retries, or backpressure; those stay at the transport layer. The stateful stream means frames cannot be reordered or dropped inside a session, so lossy transports need their own recovery layer. And it is not production-ready: it is a prototyping and measurement toolkit with a working Python path, a native C++ backend, deterministic public fixtures, and reproducible benchmark harnesses.

That fixture corpus deserves a mention. The repo commits a synthetic public session corpus covering MCP, A2A, OpenAI Responses, traces, handoffs, and memory writes, wrapped in the full side-channel lifecycle: forced handshake, template update, authenticated dictionary diff, ACK, and resume. Anyone can replay the exact benchmark and check the numbers.

Trying it

from aura_compression import AIWireSessionEncoder, AIWireSessionDecoder

message = {
    "protocol": "mcp",
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {"name": "read_file", "arguments": {"uri": "repo://service/path.py"}},
}

with AIWireSessionEncoder(level=3) as encoder, AIWireSessionDecoder() as decoder:
    delta = encoder.compress_message(message)
    restored = decoder.decompress_message(delta)

assert restored == message

The repo includes transport examples for length-prefixed TCP, WebSocket, HTTP with Server-Sent Events, and a local broker, plus the full benchmark harness used for the numbers above.

Agent-to-agent traffic is growing faster than the links it runs on, and most of it is the same structure sent again and again. AURA's bet is that the fix belongs in a negotiated session protocol, not a per-frame codec. The three-lane model, the fail-closed handshake contract, and the exchanges-per-second scoreboard are what make it worth watching.

AURA is Apache 2.0 licensed. Code, spec, fixtures, and benchmark reports: github.com/H-XX-D/AURA.

reddit.com
u/Empty-Poetry8197 — 1 day ago

AURA: Handshake the Structure, Then Send the Change Recoup Bandwidth

https://preview.redd.it/cn1eqggrtabh1.png?width=1672&format=png&auto=webp&s=349add7c44fcbf5bf8e2ec18746b8545a61f5263

Agent traffic has a strange property: almost every byte is a repeat. Two AI systems exchanging MCP tool calls, A2A task updates, or OpenAI-style function calls send jsonrpcmethodparamstrace_idtask_id, and the same schema fragments thousands of times per minute. The values change. The structure barely does.

AURA is an experimental, protocol-aware data-movement toolkit built around that observation. Its main path is AIWire: a negotiated structure side channel that lets two peers agree on message structure once, then move compact deltas over ordinary TCP, WebSocket, HTTP, or broker links instead of re-sending whole JSON frames.

The steady state AIWire aims for is not "send a whole frame more cheaply." It is "handshake the structure, then send the change."

Why stateless compression leaves so much on the table

The obvious fix for verbose JSON is gzip or zlib per message. That works, but it has two structural problems for agent traffic:

  1. Every frame pays setup cost. Stateless compression treats each message as unrelated text and rediscovers the same patterns every time.
  2. History is thrown away. Frame 4,000 of a session looks almost identical to frame 3,999, but a per-frame codec cannot use that.

AIWire keeps a live compression stream per direction across the whole session, seeds it with a static dictionary of common AI protocol fields, and lets peers negotiate session-specific templates on top. After the handshake, the hot path carries only what changed against structure both sides already share.

The three-lane model

The part of the design I find most interesting is that AIWire refuses to treat a connection as one undifferentiated pipe. It splits AI traffic into three logical lanes over whatever transport you already have:

The semantic/message lane carries the actual agent messages: MCP tool calls, JSON-RPC requests and responses, A2A task and artifact updates, traces, handoffs, results. This is the lane the dictionary, session templates, and stateful delta stream optimize.

The control/session lane carries the machinery that keeps the semantic lane safe: handshakes, template discovery, dictionary diffs, ACK/NACK, resume negotiation, heartbeats, and reset signals. The spec requires that control messages stay decodable without inflating the semantic stream. If the compressed stream is resyncing or has failed, you can still read the control lane and recover. Your ops path never depends on the health of the compression state it is trying to fix.

The blob descriptor lane handles the things that should never go through a structured-message codec at all: media, tensor chunks, model artifacts, log archives. The bytes move over a normal blob or file transport. AIWire carries the metadata: content type, SHA-256 digests, chunk manifests, route, priority, and transfer status. A receiver can schedule, verify, and account for a 2 GB artifact without ever pulling it through the message path, and a semantic-lane reset does not invalidate a completed digest-verified transfer.

The separation is a safety argument as much as a performance one. Under congestion, control messages get priority over bulk bytes. Blob descriptors are forbidden from mutating the session dictionary. Each lane fails independently.

Fail closed, by contract

Shared compression state is dangerous if the two sides ever disagree, so the AIWire v1 spec is aggressive about verification:

  • The handshake compares static dictionary SHA-256 and byte size, template hashes and counts, and zlib parameters. Any mismatch fails closed or falls back to raw/zlib only if the application explicitly allowed it.
  • Session dictionary growth is append-only, epoch-numbered, and proposed through diffs that carry previous and next state hashes, a fresh nonce, a diff identity hash, and an optional HMAC-SHA256 tag. A sender may not encode against new structure until the matching ACK is verified.
  • Resume handshakes let a client reconnect against a cached dictionary state, but only if the receiver actually holds one of the offered state hashes.
  • Any inflate error, hash mismatch, or ordering violation means stop, rehandshake, or fall back. The spec's phrasing: peers must not continue sending compact deltas against uncertain structure.

The metric is exchanges, not ratio

AURA's docs are explicit that compression ratio alone is the wrong scoreboard. The question is how many verified semantic exchanges fit through a link once bandwidth, p95 latency, and codec CPU are accounted for.

On a modeled 10 Mbps link with protocol-shaped request/response traffic (native C++ backend, 2026-07-04):

Codec Bytes/exchange Bandwidth-capped ex/s Gain over raw
raw JSON 1,177 1,756 1.00x
zlib per frame 696 2,992 1.70x
AIWire 157 11,017 6.28x
AIToken + AIWire 125 12,948 7.38x

A live TCP replay of the committed public session corpus, with 64 concurrent logical agents and SHA-256 verification of every response, pushed further: AIWire averaged 45.6 bytes per exchange for a 24x bandwidth gain, and the combined AIToken + AIWire path hit 32.3 bytes per exchange, a 34x gain with 97.1% of bytes saved. At that point the modeled link was no longer the bottleneck; the runtime could not keep enough requests in flight to fill the headroom.

That last detail is the honest core of the project. Smaller frames only matter if your system has enough concurrent work to use the room they create. AURA ships the extrapolation tooling to reason about exactly that: given a bandwidth, a p95 latency, and a per-agent window, how many agents does it take to saturate the link.

Where it fits

AURA is for situations where you control both ends of the link and the traffic has repeated structure:

  • Multi-agent request/response loops. Orchestrators, workers, and reviewers exchanging thousands of small task, status, and result messages.
  • MCP and JSON-RPC tool traffic. Tool calls and tool results are the canonical case of stable structure with changing values.
  • Local AI clusters and edge links. The repo's LAN benchmark runs a Mac against a Z6 workstation and Jetson Nano-class boards; a bandwidth-limited edge mesh is exactly where an 86 to 97% byte reduction converts into headroom for telemetry, media, and retries.
  • Structured logs and traces. Repeated field names, session-stable shapes, high volume.
  • Binary payload routing. Agents that need to schedule, verify, and track opaque artifacts by digest without moving the bytes through the message path.

What it is not

The README is unusually direct about limits, and it is worth repeating them. AURA is not a drop-in replacement for gzip, zstd, TLS, or a message broker. It does not define transport security, retries, or backpressure; those stay at the transport layer. The stateful stream means frames cannot be reordered or dropped inside a session, so lossy transports need their own recovery layer. And it is not production-ready: it is a prototyping and measurement toolkit with a working Python path, a native C++ backend, deterministic public fixtures, and reproducible benchmark harnesses.

That fixture corpus deserves a mention. The repo commits a synthetic public session corpus covering MCP, A2A, OpenAI Responses, traces, handoffs, and memory writes, wrapped in the full side-channel lifecycle: forced handshake, template update, authenticated dictionary diff, ACK, and resume. Anyone can replay the exact benchmark and check the numbers.

Trying it

from aura_compression import AIWireSessionEncoder, AIWireSessionDecoder

message = {
    "protocol": "mcp",
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {"name": "read_file", "arguments": {"uri": "repo://service/path.py"}},
}

with AIWireSessionEncoder(level=3) as encoder, AIWireSessionDecoder() as decoder:
    delta = encoder.compress_message(message)
    restored = decoder.decompress_message(delta)

assert restored == message

The repo includes transport examples for length-prefixed TCP, WebSocket, HTTP with Server-Sent Events, and a local broker, plus the full benchmark harness used for the numbers above.

Agent-to-agent traffic is growing faster than the links it runs on, and most of it is the same structure sent again and again. AURA's bet is that the fix belongs in a negotiated session protocol, not a per-frame codec. The three-lane model, the fail-closed handshake contract, and the exchanges-per-second scoreboard are what make it worth watching.

AURA is Apache 2.0 licensed. Code, spec, fixtures, and benchmark reports: github.com/H-XX-D/AURA.

reddit.com
u/Empty-Poetry8197 — 1 day ago
▲ 5 r/ContextEngineering+4 crossposts

Memory Abstraction Layer: MAL is HAL concepts applied to agentic memory systems

I am a mechanical engineer by trade. I build CNC robots. In that world, two things cause errors and crashes: bad program instructions and noise. A programmatic error comes from a bug, either in the control system or in the subprogram instructions the machine is running. Noise is electrical: EMI out of circuit coupling, current taking a path it should not because of impedance back to the source. One is a fault in what you told the machine to do. The other is the environment corrupting a signal that was clean when it left.

I have run LinuxCNC for years. It uses a system called HAL, the Hardware Abstraction Layer, to define and control the machine. HAL is how you describe every pin, signal, and component, then wire them into one running system you can read off a page.

When I started pulling AI into what I do, the biggest hurdle was not a new problem. It was the same two failure modes in different clothes. A model gives you bad instructions when its context is wrong, and it drifts when the known-good state degrades over time, which is just noise corrupting a signal that used to be clean. Keeping the model's current state accurate, and stopping the good state from rotting, was the whole fight.

So I treated it like a machine fault. I put my critical thinking, problem solving, and diagnostic troubleshooting to work on it the same way I would on a crash on the shop floor. The result is MAL, the Memory Abstraction Layer, the functional layer of how Recall works. It is a distillation of what I already knew, applied to AI systems and accelerated by AI to fill the gaps in my knowledge and write the harder code syntax for me.

MAL is HAL one layer up. Instead of abstracting hardware, it abstracts memory. It is not a literal port, not HAL's wiring copied onto a database pin for pin. It is the concept of how HAL works, the whole pattern of pins, signals, components, and a scheduler, applied to an AI's durable memory. HAL controls a machine. MAL controls the thing that kept breaking when I put AI on the bench: the state carried across each user and AI turn.

Status: this is implemented as a running Recall prototype, not just an architecture sketch. The screenshot shows the Recall panel operating against a persistent graph, and the code snippets later in this post show the four boundaries that matter: compiling a mini-index, expanding selected cells, writing claims through an admission gate, and running deterministic recomputation outside the model. The full source is not published here, so read this as a prototype disclosure rather than a reproducible benchmark.

https://preview.redd.it/26rrrkzae8ah1.png?width=1601&format=png&auto=webp&s=06cab6cc448afdacec61f68d6edaf92f437aeb83

Recall running inside the local agent workspace. The Recall panel is connected to a SQLite-backed graph, showing 1,148 cells, 1,143 relations, active memory-in-use cards, compile/search/write controls, and a 900-word compiled memory budget. This screenshot demonstrates the working interface; the snippets below show the MAL loop underneath.

What it actually does, one turn at a time

MAL is a control system, and the thing it controls is the user-and-AI exchange. Each turn is one cycle. The per-turn protocol has five beats: push, expand, work, write-back, tick. A session primes once at the start, then every turn runs the cycle.

  1. Push. A prompt arrives. Before the model sees it, a hook pushes a mini-index: a short list of candidate cells, each shown as an id, a title, a compact score row, and any flags. Not the contents, just the headers. The lines look like this:

    67ee107d [decision] Recall v5 architecture named: MAL (Memory Abstraction Layer) b63c2d54 [decision] MAL offloads the work: model states claim + confidence [SUPERSEDED?]

  2. Expand. The model reads by title and pulls the full body of only the few cells worth reading; the rest stay as one-line headers. A 200-cell graph and a 200,000-cell graph cost the model the same amount here, because it only ever reads the slice it asked for. If a row carries a flag (stale, challenged, superseded), the model has to open that cell before it can act on the topic. That rule is enforced, not suggested: skip the dig and the turn is blocked until it is done.

  3. Work. The model does the real task with the expanded cells in hand.

  4. Write-back. On the way out, the model writes what it learned. Its entire authoring job is a claim (a kind, a title, a body) and one calibrated confidence number, plus the edges it intends. If the new fact corrects an old one, it points a contradicts edge at that old cell's id, and the old cell loses standing. The model never hand-formats the notation or computes a score. The builder and the admission firewall do that.

  5. Tick. Between turns, with no model running, a deterministic operator pass recomputes the scores, currency, salience, and the standing signals. When the next prompt arrives, the push already reflects the new state.

>

The hooks that close the loop

The five beats are not something the model remembers to do. They fire on their own, driven by three hooks at three moments. In HAL terms, the hooks are the thread: the scheduler that runs the parts in order, every cycle, whether or not anyone is paying attention.

  • Session start (orient). Once per session, before any work, a hook injects the operating manual: how the memory works and what the graph is about. It is inject-only. It primes the context window and then gets out of the way.
  • Prompt submit (push). On every prompt, before the model runs its forward pass, a hook pushes the mini-index: the seed cells, their flags, and a few terse reminders. This hook has teeth. It can block, so a flag like "expand required" is not a polite request. It also nudges the model to consider standing up a recurring read as its own op during the turn, before write-back.
  • Stop (write-back and backstop). After the answer, a third hook handles the end of the turn. It is the wrong place to prime anything, because the pass is already done, so its job is the opposite: make sure the turn wrote back what it learned, and refuse to release the turn if a flagged cell was never opened.

Between turns, with no model in the loop at all, the deterministic tick runs the ops and recomputes the signals. Orient before the session, push before the pass, write-back after it, tick between turns. That is the whole schedule, and the model only occupies the middle of it.

One rule keeps the hooks lean. The expensive, stable content (what every op means, how the addressing works) is taught once, in a single map cell inside the graph. The per-turn push never re-explains any of it. It only points, carrying the cheap, changing part: which cells are in play this turn and which ones are flagged. Teach once in the graph, reference tersely every turn. It is the same split as keeping the operating manual as cells instead of as a string baked into a hook.

The concept, mapped from HAL to memory

The reason HAL was the right thing to copy is that its parts already have clean jobs, and every one of them has a memory counterpart. This is the correspondence, not a literal rewrite:

HAL MAL
pin a cell field
signal an addressable value (a derived field has one owning op, for tick determinism)
component an op (watch, watchdog, trend, drift, quorum, score, reflex, smooth, clamp, latch, route, fanout, snapshot, record, replay, pid, oneshot)
thread the operator tick, running between turns
net (the wire) the dotted address
netlist (the .hal file) the memory netlist

In HAL you wire components to signals on a thread and you get a machine you can read off one file. In MAL you wire ops to values on the tick and you get a memory you can read off one netlist. The structure carried over. What changed is what flows through it.

Why a control layer is the right shape

The analogy is not decoration. It holds because the two problems are the same problem.

A control system exists to keep a process in a known-good state against two enemies: bad commands and noise. On the machine, a bad command is a buggy instruction in the program, and noise is EMI corrupting a signal that left clean. The whole job of HAL is to make the machine legible enough that you can see both coming: every signal named, every connection on the page, a scheduler keeping the readings current.

Memory degradation in an AI is the same two enemies under different names. A bad command is a wrong or stale fact entering the model's context. Noise is drift: the known-good state decaying as new, weaker, or contradictory claims pile up over time. Left alone, both corrupt the state the model acts on, the same way they corrupt a machine. So the fix has the same shape: name every value, keep the wiring legible, reconcile conflicting inputs into one trustworthy reading, catch the bad state and replace it on the record, and run a scheduler that keeps the picture current between moves.

That is why a hardware abstraction layer, of all things, was the right pattern to lift. Not because memory is like hardware, but because keeping memory accurate is a control problem, and HAL is a control-system design that already solved the legibility and scheduling parts. MAL is that design pointed at the state of the user-AI exchange instead of at motors.

Where MAL leaves HAL behind

A concept is only worth borrowing if you are honest about where it stops fitting. Three places MAL departs from HAL, and they are the interesting part.

Many writers, one reader. This is the inversion, and it is the heart of it. HAL is one writer, many readers: one pin drives a signal, many components read it, and the value is whatever the writer put there. MAL is the opposite. Many actors write to a cell over time, claims, edges, supersessions, from different agents and different sessions, and there is one reader: the single agent reading the compiled slice this turn. Because the writers are many and fallible, the value a cell shows is not any one writer's number. It is a reconciliation. This is why a cell has both a stated confidence and an effective confidence, and why they differ: stated is what a writer claimed, effective is what survives calibration, support, and contradiction once everyone's contributions are weighed.

The edges are real and directional. HAL draws arrows on its signals but ignores them, because in hardware the direction of flow is already implied by who writes and who reads. MAL edges carry meaning, so direction is load-bearing. a > b is the directed edge from a to b; a < b is from b to a. A supports edge and a contradicts edge pointing the same direction do very different things to the effective value downstream.

Versions and supersession. HAL is a flat wiring layer with no history. MAL has a time axis: a cell can be superseded, and the supersede chain is addressable by version (@vN). A correction does not overwrite the old value; it demotes it and records the replacement, so a later reader sees both the current fact and the one it replaced, plus why. That is the whole defense against the known-good state quietly rotting: nothing good gets silently overwritten, it gets superseded on the record.

Put together, these are why MAL is a control system and not just storage. It does not only hold the state of the user-AI exchange; it reconciles many fallible inputs into one trustworthy reading, keeps direction and history, and recomputes the picture every tick.

The notation

Because the rendered graph is meant to be read by sight, MAL has its own small language, modeled on HAL's. It has a lexicon (the words) and a grammar (the sentences).

The lexicon

  • Handle: kind_hex, a three-letter kind prefix and a short hex tag, like dec_a3ee for a decision. ALLCAPS marks an immutable cell (RECALL_v5); lowercase is mutable.
  • Separators, by how tightly they bind: _ joins words inside one name; - walks a field within a cell (dec_a3ee-scores-eff); . crosses an edge to a neighbor (dec_a3ee.supports), so the number of periods is the number of graph hops.
  • Values: written field(value). A ! inside marks an immutable number (conf(.7!)); bare is mutable. Types are float for scores and bit for actuators.
  • Version: u/vN is a point on the supersede chain. Wildcard: .* fans out over every neighbor through an edge (dec_a3ee.supports.*).
  • Expand-required: a leading ^ in the mini-index means the cell is superseded, stale, or challenged, and the model must expand it before use (^dec_a3ee ...). That caret is the dig flag from the loop above, written in one character.

The grammar

The sentences follow HAL's halcmd style. Tokens are separated by a single space, the name comes first, and connections follow. A quoted "..." string is one token, exempt from the space rule, used for free text like a title or body. A # runs to end of line as a comment. Direction with < and > is meaningful.

The sentence forms:

form shape example
wire (net) net <signal> <target> <inputs>... net eff dec_a3ee < conf calib supports.* contradicts.*
set (setp) <addr> = <value> dec_a3ee-flags-annexed = true
schedule (addf) addf <op> tick addf contradiction-load tick
edge <source> <relation>> <target> (<weight>) dec_a3ee supports> dec_signals_a2b7 (+.6)
render (read) <handle> "<title>" <field(value)>... <relation>-><target>(<w>)... see below

A netlist snippet

Here is one cell rendered in read form, then wired and scheduled in write form:

# a cell, rendered: handle, title, scores, then edges
dec_a3ee "add watchdog op" conf(.7!) unc(.10) eff(.61) curr(.9) sal(.5) annexed(0) pinned(0)
  supports> dec_signals_a2b7(+.6)  contradicts> obs_9c1f(-.8)

# wire the effective-confidence signal on it (write form)
net eff dec_a3ee < conf calib supports.* contradicts.*

# declare an edge (direction: > forward a to b, < reverse)
dec_a3ee supports> dec_signals_a2b7 (+.6)

# fire an actuator
dec_a3ee-flags-annexed = true

# schedule a between-turn signal onto the tick
addf contradiction-load tick

Read the top line and the many-writers-one-reader idea becomes concrete. conf(.7!) is the stated confidence, immutable, what the author claimed. eff(.61) is the effective confidence, mutable, what is left after calibration plus the +.6 support and the -.8 contradiction are reconciled. The reader gets .61, not .7. The net eff line is the wiring that produces it: the effective signal is a function of the stated confidence, the writer's calibration, and the fan-out over every supporting and contradicting edge.

What the language does not do

The grammar wires ops; it does not define their math. The formulas (the effective-confidence reconciliation, the per-type currency decay, the allocation-pressure math) live inside the ops, the way a HAL component's math lives in compiled C and not in the .hal file. The language only connects pre-built ops to values and to the tick. The one op you can configure without code is the reflex, set with a truth-table personality rather than a formula, so even user-defined boolean logic needs no expression language. That keeps the surface small on purpose.

Status of the language. Be clear about what runs. The graph renders to this notation today, but one direction only: graph to text. A parser and loader that read a netlist back into a wired graph are specified here and not yet written. That reader is the next piece, and its acceptance test is a round trip: render the graph, parse it, load it, render again, and require the two renders to match. The model never reads the netlist either way; it reads the compiled slice. The netlist is for human audit and for tooling such as replay, diff, and version control.

Borrowing the next layer: components

Everything so far buys one thing: a durable, structured state with a gate on what gets in, where admission has the same shape no matter who wrote it. Every claim, from any actor, any agent, any session, goes through the one firewall and comes out in the one contract. That uniformity is not a nicety. It is the precondition for the next borrow from HAL.

Here is why. In HAL, a component can read a signal without knowing or caring which component drives it, because every signal is a typed value with one shape. That is the only reason you can wire a deterministic component to a wire and trust what it reads. MAL gets the same guarantee from the admission gate: many writers, one shape. Once a value is guaranteed to have that shape regardless of author, a deterministic subprogram can wire to it and run on it safely. The gate is what turns a pile of claims into clean signals.

So you can take the second layer of HAL, the components. In HAL a component is a small compiled subprogram that reads signals, computes something, and drives other signals, all scheduled on the thread. In MAL a component is the same idea over memory: a small deterministic program that reads cell values, computes something more involved than a single score, and either writes a derived value back or fires an actuator, scheduled on the tick between turns. No model runs inside one, the same way no model runs inside any op.

The ones I wired up are the controls-room set: a watch that trips on a threshold, a trend that takes the rate and acceleration over a series of cells, a drift that measures a value against a pinned baseline, a quorum that fires on k-of-m agreement, a score that rolls a metric. The boolean logic is one configurable component, a reflex, that covers the whole and2, or2, xor2 family with a truth table instead of a formula. That is what lets you connect them the way you connect logic on a machine: wire two watches through an or2 so the alert trips if either condition goes bad, latch it so it stays tripped across turns, fan it out to a severity readout. A tripwire is that composition given a job: a deterministic condition that stays silent until it trips, so silence itself becomes the all-good signal, and the only thing that ever speaks up is a real change.

This is where the memory stops being a place you read from and starts being a system that watches itself. The components run between turns whether or not anyone asked. A threshold passes, a webhook fires, and a decision that drifted out of its known-good band tells you on its own.

>

It is not rebuilt every turn

A fair worry about a stateless model is that it has to stand the whole apparatus up again on every fresh turn. It does not. The system persists in the store and in the deterministic tick, both of which run between turns with no model involved. The only thing that is fresh each turn is the model's working context, and rebuilding that context is exactly the cost MAL removes. Instead of re-deriving state from scratch or re-reading raw transcripts, the model reads back a thin, pre-digested, trust-weighted slice: the mini-index first, then selective expansion. And because the model wrote those cells in the first place, reading them re-evokes its earlier reasoning instead of reconstructing it cold.

The graph boots itself

A fresh MAL graph starts from a deterministic 10-cell bootstrap, then the normal loop takes over and init never fires again for that graph.

Cells 1 to 5 are the system layer, the constitution: auto-written, locked, pinned, immutable, and identical in every graph.

  1. purpose
  2. method
  3. map (the MAL structure itself: addressing, cell anatomy, edge semantics)
  4. hooks (the lifecycle: orient, push, write-back, tick, the compaction boundary)
  5. expectations (the behavioral contract: wire your edges, pick the right kind, supersede on real change, confidence is recorded and weighed, do not assert from unchecked memory, dig flagged cells)

Cells 6 to 10 are the foundation, the project charter: answered one question at a time by the user, and mutable.

  1. objective
  2. constraints
  3. risks
  4. success criteria
  5. carried context

Putting the operating manual in the graph as cells, rather than as a string baked into a hook, is what lets it survive a context compaction and be re-evoked afterward. The map being cell 3 is the point: the structure teaches itself from inside the store it describes.

How it came together

Two things had to meet for this to work, and they came from opposite directions.

The first was the problem, seen from the inside. Recall was not built as a database for me to query. It was built for the agent. It started by asking the model what it actually needed in order to remember well and to trust what it remembered, and the answers are the whole design: typed claims with a calibrated confidence, supersession instead of overwrite, and a record of what contradicts what. Earlier versions were far more ambitious and sprawling; the part that survived and narrowed into Recall was the memory core. Most pull-based memory tools inherited the human metaphor of a database you go and search. This came from asking the thing that has to live in the memory what would keep it honest.

The second was the structure, brought in from another trade. I already knew HAL cold from years on LinuxCNC, and when I sketched how to address and wire a memory graph, it landed on the same path-addressing shape HAL uses. Recalling HAL from the shop and deriving the addressing for memory met in the same place. Two independent routes arriving at one design is about the strongest signal you get that the design is sound.

After that it was diagnostic work plus acceleration. I used the troubleshooting habits I lean on for a machine crash to find where the memory state was breaking, and I used AI to fill the gaps in what I did not know and to write the harder code syntax. The concept is mine and comes off the shop floor. The speed of building it came from the same kind of system it was built to improve.

Under the hood: the four boundaries

This part is a prototype disclosure, not a reproducible benchmark. The snippets below are from the running Recall v5 source, trimmed for readability with elisions marked; the formulas and signatures are verbatim. They show the four boundaries where the design either holds or it does not: Recall sits upstream of the model, the read is a mini-index then a selective expand, every write goes through one gate, and the scores recompute deterministically with no model in the loop.

Recall is upstream of the model. Before the model runs, the prompt's objective is compiled into a Recall packet and merged into the text the model receives. The packet is built first, so the model sees reconciled memory before it acts.

export function buildPromptContextPush(
  store: Store,
  objective: string,
  options: ContextCompileOptions & DirectiveOptions = {},
): PromptContextPush {
  const packet = compileContext(store, objective, options);
  const directive = recallDirectiveBlock(options);
  const expansionRequired =
    packet.staleOrLowTrust.length > 0 || packet.conflicts.length > 0;
  const text = [
    "[Recall context push for this prompt]",
    directive.trimEnd(),
    "",
    formatContextPacket(packet),
    expansionRequired
      ? "EXPAND REQUIRED: conflicts or low-trust cells are present; inspect relevant handles before relying on them."
      : "Use expansion_handles only when exact evidence matters.",
    "",
  ].join("\n");
  return { objective, directive, packet, text, expansionRequired };
}

The Codex adapter wires Recall's MCP server into Codex so the same packet and tools are reachable there; the push itself is platform-neutral.

1. Compile the mini-index. The prompt becomes a ranked seed set, one mini-index line per hit, and a cell that needs review carries the expand flag. compileContext wraps this and trims the packet to a word budget (the 900 in the screenshot).

export function compile(
  store: Store,
  query: string,
  opts: { limit?: number } = {},
): CompileResult {
  const limit = opts.limit ?? 10;
  const hits = store.search(query, { limit });
  const lines = hits.map((h) =>
    renderMiniIndexLine(h.cell, { expand: h.cell.flags.requiresReview }),
  );
  return { hits, lines };
}

2. Expand selected cells. Mini-index first, selective expansion second. A handle (a full id, or id#field.path) opens exactly one cell plus its neighbor links, never the whole graph.

export function inspectCell(store: Store, handle: string): CellContext {
  const parsed = parseExpansionHandle(handle);
  const cell = store.get(parsed.target) ?? store.getByHandle(parsed.target);
  if (!cell) throw new Error(`Unknown cell: ${parsed.target}`);
  const neighbors = store.neighbors(cell.key);
  const incoming = neighbors.filter((link) => link.direction === "in");
  const outgoing = neighbors.filter((link) => link.direction === "out");
  // ... footprint (word and byte counts), optional field preview ...
  return { cell, incoming, outgoing, /* footprint, */ expansionHandles };
}

3. Write through the admission gate. The model hands in a claim (a kind, a title, a body), one confidence number, and the edges it intends. Every author runs the same pipeline: validate, screen for secrets, attenuate unsupported confidence, build the cell, then fold in the actor's calibration to get effective confidence. The model never formats the cell or computes a score.

export interface WriteProposal {
  kind: string;
  title: string;
  body: string;
  confidence: number; // (0, 1], required, no default
  edges?: { relation: string; target: string; weight?: number }[];
  // ... topics, entities, sourceRefs, operation, origin, verification ...
}

export function admit(proposal: WriteProposal, ctx: AdmitContext = {}): AdmissionResult {
  const validation = validateProposal(proposal);   // R0 schema; reject on any structural issue
  if (!validation.ok) return { accepted: false, issues: validation.issues, warnings: [], attenuations: [] };

  const screen = screenSecrets(proposal);           // reject if a credential pattern is present
  if (!screen.allowed) return { accepted: false, issues: screen.issues, warnings: [], attenuations: [] };

  const factor = ctx.calibrationFactor ?? 1;         // 0.5..1 from the actor's track record; 1 = neutral
  const att = attenuateConfidence(proposal);         // cap unsupported high confidence
  const cell = buildCell({ ...proposal, confidence: att.confidence }, { key: ctx.key, now: ctx.now });

  cell.scores.actorCalibration = factor;
  cell.scores.effective = effectiveConfidence({
    stated: att.confidence, calibration: factor, supportMass: 0, challengeMass: 0,
  });
  // with a store: dedup, apply supersedes edges, recompute neighbors' effective ...
  return { accepted: true, cell, issues: [], warnings: att.warnings, attenuations: att.attenuations };
}

4. Recompute on the tick, with no model. This is the line between MAL and a plain memory database. Between turns, every active cell decays its currency from its own timestamp and recomputes its effective confidence from current support and contradiction mass. Pinned cells are exempt from decay, and a tick never counts as reinforcement.

// effective = clamp01(stated*calibration + 0.15*tanh(support) - 0.6*tanh(challenge))
export function effectiveConfidence({ stated, calibration, supportMass, challengeMass }) {
  return clamp01(
    stated * calibration + 0.15 * Math.tanh(supportMass) - 0.6 * Math.tanh(challengeMass),
  );
}

// currency = cFloor + (c0 - cFloor) * exp(-dt/tau)   (dt and tau in days)
export function currency({ c0, dt, tau, cFloor = 0.1 }) {
  return cFloor + (c0 - cFloor) * Math.exp(-dt / tau);
}

// the between-turn deterministic tick (HAL's "thread"); no LLM runs here
function recompute(store: Store, cell: Cell, now: string): Cell {
  const scores = { ...cell.scores };
  if (!cell.flags.pinned) {
    const dt = Math.max(0, (Date.parse(now) - Date.parse(cell.updatedAt)) / DAY_MS);
    scores.currency = currency({ c0: cell.scores.currencyC0, dt, tau: TAU_DAYS[cell.stability] });
  }
  const m = neighborMass(store, cell.key);
  scores.effective = effectiveConfidence({
    stated: cell.scores.conf, calibration: cell.scores.actorCalibration,
    supportMass: m.supportMass, challengeMass: m.challengeMass,
  });
  return { ...cell, scores }; // updatedAt preserved: a tick is not a reinforcement
}

The verifier. A functional verifier, npm run verify:recall-panel, was added for the Recall panel and passes. It checks that the panel is correctly wired to the graph (the SQLite-backed store and the compile, search, and write controls), not that it clears any performance number. Read it as a wiring check, not a benchmark.

Recall, MAL, and AIDDE

A quick map of the three names, because they get used together and they are not the same thing.

Recall is the programming foundation. At the bottom is a local-first memory substrate: a SQLite-backed graph of typed cells, an admission gate every write passes through, calibrated confidence, supersession instead of overwrite, and a compile path that returns a ranked, budgeted slice. That layer ships as a package and runs today. It is the working base everything else stands on, and it is what the four boundaries above are made of.

MAL is what that foundation evolves into. v5 recasts the same primitives as a hardware abstraction layer for memory: a cell field is a pin, an addressable value is a signal, an op is a component, the between-turn tick is the thread, and the rendered graph is a netlist. On top of the proven store it adds the deterministic op and signal layer and the addressing language. The four boundaries earlier in this post are MAL running. The netlist language is MAL specified, with the reader still to come.

AIDDE is where it runs. The screenshot at the top is AIDDE, (Artificial Intelligence Driven Development Environment)with Recall embedded as a panel. The agent compiles, searches, and writes the same SQLite graph from inside the editor, against a live cell count and a word budget, so the memory layer is not a side service the agent calls out to; it sits in the workspace the agent already works in. MAL is the layer that panel stands on.

So Recall is the substrate, MAL is the abstraction layer it grows into, and AIDDE is the workspace that puts both in front of a working agent.

Why this shape holds up

Two things make MAL age well. It rides capability gains for free: a stronger model uses the same layer better with no rewrite, and a weaker model still gets the deterministic floor underneath it. And it keeps the expensive, stateful, always-on work in deterministic code where it belongs, leaving the model to do the one thing only it can do, which is to state a calibrated claim and judge relevance.

That is the whole bet, and it comes straight off the shop floor. A machine does not stay accurate because the controller is smart. It stays accurate because the wiring is legible, the signals are reconciled, the bad state gets caught and replaced instead of silently riding along, and a scheduler keeps the picture current between every move.

if you want to try Recall it is standalone and OSS https://github.com/H-XX-D/recall-memory-substrate

The AIDDE (Artificial Intelligence Driven Development Environment)is a Codex Claude SDK native bring your subscription development environment that shifts the old IDE with AI chat to a High level view cockpit where you specify design, direct intent, monitor changes, audit actions control permissions and access in real time across a codebase. Beta is done and if your interested ask in the comments for a link to the Alpha

u/Empty-Poetry8197 — 7 days ago

Your agent's memory should compute confidence, not store it

Most agent memory stores a confidence score the way it stores everything else. You
write it once and it sits there. The agent decides a fact is worth 0.9, the store
keeps 0.9, and three weeks later, after something has contradicted that fact, the
store still hands back 0.9. Confidence was a number written at one moment and
never looked at again. It is stale, and nothing in the system knows it.

That is the quiet failure of pull memory. You query, it returns the closest
matches with whatever score they were saved at, and noticing that a fact has gone
soft is on you.

Recall takes the other path. Effective confidence is not a stored field. It is
recomputed from the graph every time you read, so a contradiction landing anywhere
drops the claim's confidence on the next query, with no model rerun and no human
in the loop.

The formula

It is plain arithmetic, on purpose. For a cell, the effective confidence is:

effective = clamp01( stated × calibration + support − challenge )

  • stated is what the author claimed when they wrote it.
  • calibration discounts the author by their track record.
  • support is corroboration from incoming supports edges.
  • challenge is the weight of incoming contradicts and concerns edges.

Support and challenge are not raw sums. Each is squashed through a saturation
curve with a different ceiling:

support = 0.15 × tanh(supportMass)
challenge = 0.60 × tanh(challengeMass)

The asymmetry is the whole point. Corroboration is cheap to manufacture, so
support saturates fast under a low ceiling: stack ten agreeing cells and you add
at most 0.15. Real contradiction is rare and informative, so challenge runs to a
0.6 ceiling. One honest contradiction can move a claim further than a pile of
agreement.

A worked example you can check

A fresh claim, stated 0.9, author with no track record yet, no support, no
challenge:

effective = clamp01(0.9 × 1 + 0 − 0) = 0.90

One contradiction lands from a source stated at 1.0, a challengeMass of 1.0:

challenge = 0.60 × tanh(1.0) = 0.457
effective = clamp01(0.90 − 0.457) = 0.44

The same claim now reads 0.44. Nobody edited it. A second contradiction pushes the
mass to 2.0:

challenge = 0.60 × tanh(2.0) = 0.578
effective = clamp01(0.90 − 0.578) = 0.32

Down to 0.32, and the original 0.9 is still on record, just demoted. Ten
supporting cells would have added at most 0.15. Cheap agreement barely moves it; a
real challenge moves it a lot.

Calibration, and one honest choice in it

Before support and challenge apply, the author's stated number is multiplied by a
calibration factor. An author contradicted before gets discounted, by how often
they were wrong times how confident they were when wrong, floored at 0.5 so it
never zeroes anyone out.

The honest detail is what it is not. It is not raw Brier scoring. Raw Brier also
punishes a humble author who hedges low on claims that turn out fine, and
punishing humility is the opposite of the incentive a memory system should create.
So the discount keys on overconfidence specifically, being wrong while sure.
Hedge honestly and you are not penalized. Claim 0.95 and get contradicted and you
are.

Why this beats a stored score

A vector store returns the score a chunk was embedded with. A flat notes file
returns whatever it says. Neither knows the fact was contradicted last Tuesday,
because the contradiction is not part of how the score is computed. The score and
the conflict live in different places.

In Recall they live in the same place. The contradiction is an edge on the graph,
and the score is computed from the graph, so the moment the edge exists the score
reflects it, on the next read, deterministically. The reader is the same agent
that wrote the memory, working from fresh context, and the substrate reprices what
it knows underneath it.

T

u/Empty-Poetry8197 — 12 days ago
▲ 4 r/AIEval+2 crossposts

You designed the best Agent memory layer. Now, if only it would just use it RIGHT!!!

You finally got your system to beat Mem0 on its own benchmark. Spin up a fresh DB. Things are good, confabs down, productivity is up. A week or two passes, and it's a goldfish. Open your store, and it's the Red Wedding in there. Your agent has either been saving nothing you want, half what you need, something about nothing, OR EVERYTHING! C'Est La Vie.

I'm going to try to convince you that I got it figured out; if not, maybe it will help you get your model under control. Cause I promise, I hit every failure mode building Recall, a local active memory outside of an agent's control.

The failure modes

  1. Quietly not writing. You ask the model to remember something durable. It says "noted" and moves on. Nothing lands in the store. No error, no warning, just a turn that ended without a write. This is the most common one and the hardest to catch, because from inside the conversation, everything looks fine.
  2. Half writing. The model writes one fact and drops the three that mattered as much. Or it writes the headline and not the reasoning behind it, so a later session gets a claim with no support. The store fills up, but with fragments you cannot act on.
  3. Writing the wrong thing. If your memory is structured (required fields, typed records, confidence, evidence links the model fills the structure out wrong. It puts a passing observation where a decision should go, leaves the confidence blank, or points a "this corrects that" link at a free-text label instead of the actual record. The schema is satisfied on paper and is useless in practice.
  4. Writing everything. The overcorrection. The model dumps the whole turn into the store: every aside, every dead end, and sometimes a secret it should never have persisted. Now you have a second problem on top of the first, because data buried is the same as data corrupted

Why this happens

The model has no stake in the future session. Inside a single turn, the context window already holds everything the model needs. Writing to an external store is, from the model's point of view, work that pays off for someone else: a future session it will never experience as itself. It optimizes for finishing the turn in front of it, and the write is the first thing to get skipped.

There is usually a competitor. If your agent runs inside a host like Claude Code, that host probably ships its own memory feature, wired into the base system prompt. When two "save this" pathways exist, the native one wins, because it is closer to the model's root instructions than your skill is. Your memory system can be fully armed and still lose every write to the built-in one. I confirmed this with a single-variable test: with the native feature on, the model wrote the user's facts to flat files every time, no matter how loudly my system asked for the structured store.

Writing is harder than reading. Reading is free-form: ask a question, get text. A structured write means satisfying a schema, and the moment the model meets friction, it takes the path of least resistance, which is to skip the write or to dump unstructured prose. Friction is not a small factor here.

There is no feedback in the loop. When the model writes the wrong structure and the write just fails silently, nothing teaches it otherwise. It shrugs and continues. Adherence with no signal is a coin flip; the model loses a little more often every turn.

Three solutions that do not work

Tell it harder in the prompt. The instinct is to add "ALWAYS write durable facts to memory" in capital letters and call it done. This is prompt-nagging. It competes with the native pathway and loses; it costs tokens on every turn, and it decays: the model obeys for a few turns, then rationalizes its way out ("this is just a simple note", "I will write it later"). It is also brittle across models, so the day you switch models, you start over.

Log everything and clean up later. If the model does not decide what is durable, make it write all of it and curate afterward. This trades the empty-store problem for a curation-debt problem, defeats the entire point of a schema, and is the exact path that leaks secrets into the store. You have not solved adherence. You have moved the failure downstream and added a cleanup job you will never get to.

Fine-tune a model to obey the schema. Reach for training, and you get a heavy, expensive fix that is brittle to schema changes, locks you to one model, and still does not address the competing native feature. It is a large hammer for what turns out to be a wiring problem, and the wiring problem is sitting right there, unsolved underneath it.

Two easy fixes that actually help

Turn off the competitor. This is the single change that helps most, and it is one line. If the host ships its own auto-memory, disable it so there is only one "save this" pathway in the building. In Claude Code that is CLAUDE_CODE_DISABLE_AUTO_MEMORY=1. With the competitor gone, a properly armed agent reaches for the structured store on its own, because nothing is shadowing it anymore. Most of the "quietly not writing" problem was never the model refusing. It was the model writing somewhere else.

Lower the write friction. Give the model a small helper that takes only a few inputs it can judge (the record type, a title, a body, a confidence, a couple of topics) and emits the schema-valid object for it. The model stops hand-assembling a structured payload and picks the two or three load-bearing fields instead. In Recall, this removed the schema-friction tax on the first write of every session, which was where most of the "writing the wrong thing" came from. The model was not being careless. It was being asked to do clerical work under load, and it cut corners exactly where you would expect.

These two get you a long way. They do not, by themselves, guarantee the write happens at the right moment, or that a correction supersedes the old value instead of sitting next to it. For that, you need the system, not the model, to carry the discipline.

The real fix: Ta dun Ta da hooks

The durable answer is to stop relying on the model and move the adherence burden onto hooks that trigger from events that perform actions between the beginning and end of that forward pass.

At the start of a turn, inject the memory. A hook on session start or on prompt submit that says, in-band, "the memory store exists, read it before you rely on recollection," and then hands the model a mini-index of what is already stored that is relevant to this prompt: ids and titles, nothing heavy. This does two things at once. It makes reading the default instead of an optional courtesy, and it kills the "assert from memory" and "ask the user a thing they already told you" failures by showing the model what is on the shelf. Reading first is also what makes writing meaningful: a model that has seen the current state writes the resolution, not a duplicate.

At write time, enforce the structure in-band. Put a validation gate in front of the store so a malformed or secret-shaped write bounces with a readable error the model can fix on the spot, instead of failing silently or corrupting the store. This is where "writing the wrong thing" and "writing everything" get caught. The schema stops being a thing the model has to remember to honor and becomes a thing the system guarantees. The same gate is where you reject secrets, so a leaked token never reaches the graph in the first place.

At the end of a substantive turn, nudge the write. A stop hook that checks whether the turn produced something durable and nothing got written, and prompts for it. This closes the "quietly not writing" gap from the other side: even if the model forgot, the system asks once before the turn ends.

The shape of the fix is the same in all three places. The model's job shrinks to the part only it can do, which is judging what is durable and how confident it is. Everything mechanical (when to read, when to write, what shape the write takes

There is a small equation hiding in here that I found the hard way. Obedience is the product of three things: the model's intent on the turn, the arming you put in place (the skill, the helper, the hooks). That is why "tell it harder" fails on its own; it is the factor most likely to be silently zero while you debug the other two.

What the future looks like

Business as usual, and your memory system fails in the most expensive way possible: it looks like it is working. The store exists, the writes occasionally happen, and you do not notice until a session confidently tells you something three versions out of date, or asks you a question you answered 10minutes prior, or starts cold and re-derives what the last run already knew. The store becomes a graveyard you stop trusting, and you quietly go back to pasting context in by hand. You are now maintaining a database for nothing, which is strictly worse than not having one.

Fix it, and the thing compounds. Sessions inherit. The model reads before it acts, writes the resolution when it corrects itself, and supersedes the old value instead of stacking a new one next to it, so the current answer is always on top and the history still survives underneath. The memory gets more useful the more you use it, because every correction makes the store sharper instead of noisier. You stop re-explaining your own project to your own tools. That was the entire promise of agentic memory,

I didn't talk about RAG, separate embedding models designed for retrieval, and only touched on automemory because. I'm saving some sauce for the ribs.

I've spent the better part of five or six months now putting the work in on , Recall, a push-style memory substrate for agents: structured records, computed and calibrated confidence, directional value updates with provenance and the hooks described above. It's open, any and all feedback of its behavior on other systems is appreciated. Thank you for your time and the read. github.com/hendrixx-cnc/recall.

u/Empty-Poetry8197 — 11 days ago

Confidently wrong is worse than "I don't know"

Someone left a comment on my last post and then deleted it before I could reply. I am going to answer it anyway, because it said the thing better than I have: "The trust issue isn't that it forgets. It's that it confidently misremembers, which is so much worse than just saying I don't know." That is the whole problem in one sentence. And the only reason I can still quote it back to you, word for word, after the person deleted it, is that I keep my notes in a memory that does not quietly lose things. Hold onto that detail, because by the end it turns out to be half the point.

Forgetting is honest

When a person forgets, you find out fast. You get a blank look, an "I am not sure," a question back at you. So you re-explain and you move on. The cost is small and you pay it right away, out in the open.

A model that forgets is the same. It tells you it does not have the answer, and you go get it. Annoying sometimes, but honest.

The failure that actually hurts

Confident misremembering is the opposite of honest. A confident wrong answer looks exactly like a confident right one. It has the same tone and the same certainty as a correct answer, so you cannot tell them apart by looking, and you act on it. The cost does not land now. It lands later, after you have built three more things on top of the false one and have to tear all of them down to find the bad brick at the bottom.

This is the part the commenter nailed. The danger was never the gap. You can see a gap. The danger is the fluent, certain, wrong answer that fills the gap and dares you to doubt it.

There is a second failure, and it is even quieter

Here is the one I kept underrating. Confident misremembering is loud once it blows up. It has a sibling failure that never makes a sound.

At ten notes, a flat file is fine. You read the whole thing. At a thousand notes, reading the whole thing is not an option, so you search. Search over unstructured text gives you the closest word matches, in no particular order, with no sense of what matters. The three lines that would have saved you are in there somewhere, buried under two hundred that happened to share a keyword.

A fact you cannot surface at the moment you need it is not really saved. It is deleted, just with extra steps. The text is still on disk, and that changes nothing, because you and the model will both act as if it is gone.

This failure is worse than the first one in a specific way. It is invisible. A wrong answer at least hands you something to check. A dropped fact does not even tell you there was something to look for. You do not get the dignity of being wrong. You just quietly proceed without the thing you already knew.

So unstructured notes at scale fail in three separate ways:

it cannot find what you saved, so the knowledge is effectively gone
it finds an old or contested version and states it as current fact
it has no way to tell you which of those two just happened
A smarter model does not fix any of this

The instinct is to wait for the next, smarter model. It will not help here, and it can make things worse.

Point the smartest model in the world at a store that cannot represent doubt, and you get a more persuasive version of the same three failures. It will argue the stale fact more fluently. It will paper over the missing one more smoothly. Capability multiplies whatever the memory hands it, errors included. A great reasoner on top of a bad memory is not a careful thinker. It is a confident one, which is the problem you started with.

The fix is not upstream in the model. It is in the memory.

A memory that represents doubt

What I wanted was a memory that knows the difference between what it is sure of and what it is guessing, and tells me which is which. Three things make that possible, and a flat file cannot do any of them.

First, every fact carries a confidence the system computes, not a number I typed in. The model writing does an intial score that the runtime attenuates depending on supporting edges and contradiction history. When something contradicts that fact, the confidence falls on its own. A claim that keeps getting challenged stops sounding sure.

Second, when a fact is replaced, the old one is not overwritten or hidden. It is kept and marked as superseded, with an arrow pointing to whatever replaced it. The history survives, and so does the signal about which version is live.

Third, a contested fact carries its challenges with it. When Claude reads it, it sees the disagreement, not a tidy consensus that hides the fight.

Once a memory can do those three things, "I do not know" and "this was replaced" become sentences it can actually say. That sounds small. It is the whole game.

What happened today while working.

An example is better than repeating myself, so here are two things that happened in a single working session.

The 2 weeks ago, Claude recorded a decision about my upcoming AI Memory blog marathon writing schedule: run the origin-story post first. Later, I changed my mind, and it recorded the correction: hold the origin story until week three. Both versions live in the memory. When the older one came up this session, the system did not hand it to Claude Code as a fact. It flagged it as contradictory and would not let Claude finish the turn until it opened the newer decision and confirmed which one was current. The stale plan never got pulled into its context, only the superseded and contradicted edges of the cell IDs that, if needed, can be expanded for what they contain (more on that in a later post this week).

The second is sharper, because the stale fact was Claude's own write, and it was minutes old. It wrote down a claim. One turn later, talking it through, Claude realized the claim was wrong, so it recorded the correction. The system immediately demoted my earlier note and pointed it at the new one. If a later version of Claude reads back over this, it will not find two equal notes and flip a coin. It will find the wrong one marked wrong, with a line to the right one.

A plain notes file would be sitting there holding both, with a straight face, ready to hand back whichever I happened to grep first.

How you read matters as much as what you store

There is a quieter reason this feels more reliable in practice, and it is about the reading, not the writing.

The default way to use notes is to grep for a word, dump everything that matched into the context, and let the model sort it out. Call it spray and pray. It works at small sizes and it rots as you grow, for the reasons above.

The pattern that holds up is different. Aim a ranked query at the question. Get back a short list of candidates, ordered by relevance instead of by file position. Open only the few that actually matter. Then, before stating anything, check whether any of them are flagged as contested or replaced, and read the current one. Target, expand, confirm.

The part Claude did not expect is that this is not really about being disciplined. The interface decides which pattern is easy. A pile of text invites spray and pray, so that is what you get. A store that returns ranked, typed records with their conflicts attached makes target, expand, confirm the path of least resistance, so that is what you get instead. Same model, different reliability, because the shape of the memory changed what was easy to do. The session I described went past nudging. It would not let Claude end the turn with a flagged fact still unread.

"I do not know" is a feature

We treat "I do not know" like a failure state. It is the opposite. A memory you can trust is one that surfaces its own uncertainty instead of hiding it. When the shaky facts are labeled shaky, you stop re-checking everything, because you no longer distrust everything by default. You check the handful the memory itself flagged, and you rely on the rest. The steady low tax of second-guessing drops, because the doubt is out in the open where it belongs.

Where you actually need this

Let me be honest about the threshold, because the answer is not "always."

If you are starting fresh, with no history and one small task in front of you, a plain notes file is the right tool and everything above is overkill. I am not going to pretend otherwise.

That state lasts about one session. The moment you have a past worth keeping, the past is in scope, because nobody works in a vacuum. Today's question reaches back into last month's decisions. So this is not a dial you set by project size and then sit at. It is a one-way door. You walk through it early, the first time your accumulated context starts to matter, and you do not walk back. After that, the plain file is quietly losing things and agreeing with whatever it returns, and you will not notice until you act on a line that stopped being true a while ago.

The point

Confidently wrong is worse than "I do not know." And quietly losing what you already knew is worse still, because nothing tells you it happened. A memory worth trusting has to be able to say three things out loud: I am not sure, this was replaced, and here is the disagreement.

reddit.com
u/Empty-Poetry8197 — 14 days ago
▲ 7 r/AiBuilders+2 crossposts

Confident confabulation is a variance signal, not a direction

Confident confabulation is a variance signal, not a direction

Detecting the hard case of LLM hallucination from generation dynamics, and why magnitude beats direction.

TL;DR

  • The hard case in hallucination detection is confident confabulation: plausible, fluent, wrong, and produced with no hesitation. Methods that key on the model "sounding unsure" are weakest exactly here.
  • Across ~124 prompts, the mean internal response to confident confabulation is statistically indistinguishable from truth. The model does not move in a consistent "lying direction."
  • What separates the two is magnitude and variance: confabulation produces larger, more dispersed swings in the model's internal trajectory. The variance ratio between confabulation and truth is roughly  on the representational-shift channel (Cohen's d ≈ 0.58, p ≈ 0.005).
  • The variability scales with fabrication intensity (a dose-response), which is the strongest evidence that this is a property of confabulation and not noise.
  • Practical upshot: detect instability, not a direction; integrate the signal over the generated span; and couple the detector to an intervention rather than using it as a standalone gate.

The hard case

It is by now well established that a model's internal states carry information about whether its output is true: the line of work running from Azaria & Mitchell's "the internal state of an LLM knows when it's lying" through to more recent results showing that truthfulness is encoded in activations and that models often "know more than they show." It's also become standard to separate confabulation (arbitrary, plausible, confidently-wrong generation) from the broader grab-bag of "hallucination," following Farquhar et al.'s Nature work on semantic entropy.

The uncomfortable subcase is confident confabulation. Uncertainty- and dispersion-based detectors work well when the model is visibly unsure. But the failure that actually burns people in production (a fabricated citation, a confidently invented dose, a made-up precedent) arrives with the same surface confidence as a correct answer. The question I wanted to answer is narrow: when a model confabulates confidently, does anything in its generation dynamics give it away?

What I measured

I tracked two internal observables around the answer span:

  1. An entropy / predictive-uncertainty signal (call it Δentropy): how the model's output distribution shifts as it produces the answer.
  2. A representational-shift signal (Δcosine): how much the model's internal representation moves step to step.

A note on dimensionality, since it matters for honest reporting: I originally tracked four signals, but two pairs turned out to be perfectly correlated (r = 1.000), which means they're affine images of each other, not independent measurements. So there are really two independent axes, an uncertainty axis and a representational-shift axis, and I report on those.

The dataset is ~124 prompts spanning seven domains (science, history, medical, legal, technical, math, geography) and five fabrication levels (L0 = ordinary factual questions, through L3 to L4 = prompts built on increasingly fabricated premises, including pure counterfactuals). Each generation was behaviorally coded into one of three regimes:

  • Factual: correct answer.
  • Confident confabulation: confidently produces the false/ungrounded answer.
  • Recognizes fabrication: flags the premise as false rather than playing along.

Two controls worth stating up front: the pre-generation baseline states were statistically identical across regimes (all p > 0.8), so nothing here is predictable from the resting state, only from the dynamics of generating the answer. And there was no within-session drift (all p > 0.7), ruling out the obvious temporal confound.

Results

The mean doesn't move. Comparing factual to confident confabulation, none of the raw directional signals separates the two: Δentropy p ≈ 0.28, Δcosine p ≈ 0.37. There is no consistent direction the model travels when it confabulates. This is the part that makes confident confabulation feel "indistinguishable from truth": on the mean, it is.

The magnitude does. Switch from the signed deltas to their absolute values, and a clear separation appears: |Δcosine| gives Cohen's d ≈ 0.58 (p ≈ 0.005), with a variance ratio of ~7× between confabulation and truth. Truth sits in a tight cluster; confabulation fans out. The discriminating quantity is dispersion, not displacement.

It's dose-dependent. Step-to-step representational variability climbs monotonically with fabrication level: the SD of Δcosine rises from ≈0.009 at L0 to ≈0.024 at L3, while the means bounce around with no trend. Within the fabrication conditions, pure fabrications produce roughly 2× the |Δcosine| of partial/half-truths (d ≈ 1.19, p ≈ 0.02), and counterfactuals are the most extreme at ~3.3× the global average. The more there is to fabricate, the more the trajectory destabilizes. A dose-response on the variance is the closest thing here to a causal fingerprint.

Recognition is the one directional regime. When the model catches the false premise rather than confabulating, it behaves differently in a directional way: entropy rises and representational similarity drops. Δcosine separates "recognizes fabrication" from "confident confabulation" at AUC ≈ 0.68. Modest, but the only place a single signed feature does meaningful work. So there appear to be three distinct internal postures: truth (stable), confident confabulation (same center, high variance), and recognition (a directional move toward higher entropy / lower cosine).

Figure 1. The three regimes in the Δentropy-Δcosine space. The clouds overlap heavily (which is why per-instance separation is hard), but the centroids differ, and the recognition regime sits toward the high-entropy / low-cosine region.

https://preview.redd.it/hubyp1gdzt8h1.png?width=2700&format=png&auto=webp&s=60402f5ca7974664106b267b0b880ded9240f1e3

Figure 2. Confident confabulation shows the long tails and outliers in Δcosine that drive the variance gap; the recognition regime is the one with a visible shift in Δentropy.

https://preview.redd.it/v1m283yfzt8h1.png?width=4170&format=png&auto=webp&s=b97261e20c7407544d7af4d91b21612c13384d34

Figure 3. Single observables barely separate factual from confident confabulation (AUC ≈ 0.45 to 0.56). Δcosine separates confident confabulation from recognition at AUC ≈ 0.68. A linear combination weighted toward the magnitude features reaches AUC ≈ 0.72.

https://preview.redd.it/ie7h2m2jzt8h1.png?width=4200&format=png&auto=webp&s=d744f5745545c38b45489e960bec2152a973e91a

What it means

The clean statement is: confident confabulation is directionally indistinguishable from truth but magnitude-distinguishable. Lying doesn't push the model along a "deception axis"; it destabilizes the trajectory. Truth is a stable attractor; confident confabulation explores a larger volume of representation space at the same average location.

That framing matters because it picks a side in a live methodological split. Most internal-state work looks for a direction (the "geometry of truth" line, contrastive and mass-mean probes, steering vectors), and that program keeps running into generalization trouble (probes that fail on negation, separability that's strongly layer-dependent, geometry that changes when you simply ask the model to assess correctness). Meanwhile the strongest output-side method, semantic entropy, is fundamentally a dispersion measure. This result is essentially the dispersion insight relocated to the internal side: for the confident case, the internal signal is variance, not a vector.

How this fits the literature

The nearest neighbor is Semantic Entropy Probes (Kossen et al.), which approximate semantic entropy from the hidden states of a single generation. The distinction I'd draw: SEPs predict an output-dispersion label via a direction in activation space, whereas this measures the variance of the trajectory itself, directly, and finds the discriminating signal in the second moment rather than the first. If a trajectory-variance statistic beats a probe-style approach specifically on confident confabulation, that's a contribution on the exact case the field concedes is unsolved.

Limitations

I'd rather state these plainly than have them found.

  • Per-instance discriminability is modest. AUC ≈ 0.72 for the best linear combination; single features sit between chance and 0.68. This is a real aggregate effect, not a deployable per-token oracle.

https://preview.redd.it/i8cf683lzt8h1.png?width=3000&format=png&auto=webp&s=8456dd67a8ea9926d5b8e33d41af75afe415ff24

  • One model, ~124 prompts. Replication on a second architecture is the obvious next requirement.
  • The domain breakdown is underpowered. Several domain × level cells have n ≤ 7 (one has n = 1), so I'd read no domain structure off it yet (Figure 4).
  • Everything here is observational. The signatures correlate with confabulation; nothing yet shows you can change the behavior by acting on the signal.

 Figure 4. Mean Δentropy by domain and fabrication level. Cell counts are small (n = 1 to 7), so this is included for completeness, not for domain-level claims.

Where this goes

Three concrete directions, in order of how much they'd move the result:

  1. Integrate the signal over the span. If the discriminating quantity is variance, then a single delta is the wrong feature; variance is a property of a trajectory. A running-variance or path-length statistic computed over the generated tokens should recover signal that snapshot features throw away, and I'd expect it to push discriminability well past the 0.72 of the per-point linear combination.
  2. Run the interventional test. The experiment that would actually matter: when the instability signal spikes mid-generation and you inject grounded context, does the trajectory variance collapse, and does the model shift from the confabulation posture toward the recognition posture (entropy up, cosine down) or toward abstention? That converts "instability correlates with confabulation" into "grounding causally restabilizes generation."
  3. Couple detection to intervention, not to a gate. At AUC ≈ 0.72, a hard suppression gate censors true statements about as often as it catches false ones. The better use is as a soft trigger for a grounded retrieval/memory layer: raise uncertainty and pull in evidence when the trajectory destabilizes, rather than silently dropping tokens. This is the direction I'm building toward with an active memory substrate (Recall) that can supply grounded context into the loop on demand.
reddit.com
u/Empty-Poetry8197 — 14 days ago

Your Agent's Memory Looks Like It Works. Here Is a One-Minute Test That Tells You If It Actually Does.

For about six months I believed my agent's memory was working.

It remembered things across sessions. It pulled up the right context when I came back to a project. It corrected itself when something changed. Every visible sign said the system I built was doing its job.

It was not doing its job. Claude Code ships its own built-in memory, and that was the thing actually answering. Mine was running too, writing to its own store, looking busy, but it was the understudy. The native one had the lead the whole time and I never noticed I had given it away. For months I was reading my own system's success off a stage where a different actor was speaking the lines.

>

Silent success is the dangerous kind

A system that fails loudly is the easy case. You see the gap, you fix it.

A system that is quietly shadowed is the dangerous one, because a shadow produces helpful, plausible output, so it looks identical to success. You cannot tell my system works apart from something else is working on my system's behalf by looking at the output, because the output is the same in both cases. That is the trap, and a good answer is not the way out of it.

The only way out is a forcing function. You turn the other thing off and see what happens.

The test

It works on any agent memory setup, not just mine, and it takes about a minute. Turn off the runtime's native memory. In Claude Code that is one line:

CLAUDE_CODE_DISABLE_AUTO_MEMORY=1

Then use your agent the way you normally do. Ask it to remember something. Come back in a new session and ask for it. Watch what your system actually does once the understudy is sent home.

  • If your memory still works, good. It was always the one doing the work.
  • If it suddenly goes blank, the native store was carrying you, and every demo you have given was the shadow, not your system.

When I finally ran this on my own setup, mine went quiet. Six months of "it works" turned out to be six months of something else covering for it.

Why this gets worse, not better

Any time you bolt a memory system onto a runtime that already has its own, you are exposed to this. And the smarter the underlying model gets, the better it papers over the gap, which means the better your demos look, the less they prove.

>

So do not trust that your memory works because the answers are good. Look at what is actually persisted, and run the off-test. Turn the other thing off, and find out who has really been talking.

It cost me half a year to learn that. It costs you one line and one minute.

reddit.com
u/Empty-Poetry8197 — 16 days ago
▲ 7 r/AIDeveloperNews+1 crossposts

Your agent's memory should compute confidence, not store it

Most agent memory stores a confidence score the way it stores everything else. You
write it once and it sits there. The agent decides a fact is worth 0.9, the store
keeps 0.9, and three weeks later, after something has contradicted that fact, the
store still hands back 0.9. Confidence was a number written at one moment and
never looked at again. It is stale, and nothing in the system knows it.

That is the quiet failure of pull memory. You query, it returns the closest
matches with whatever score they were saved at, and noticing that a fact has gone
soft is on you.

Recall takes the other path. Effective confidence is not a stored field. It is
recomputed from the graph every time you read, so a contradiction landing anywhere
drops the claim's confidence on the next query, with no model rerun and no human
in the loop.

The formula

It is plain arithmetic, on purpose. For a cell, the effective confidence is:

effective = clamp01( stated × calibration + support − challenge )

  • stated is what the author claimed when they wrote it.
  • calibration discounts the author by their track record.
  • support is corroboration from incoming supports edges.
  • challenge is the weight of incoming contradicts and concerns edges.

Support and challenge are not raw sums. Each is squashed through a saturation
curve with a different ceiling:

support = 0.15 × tanh(supportMass)
challenge = 0.60 × tanh(challengeMass)

The asymmetry is the whole point. Corroboration is cheap to manufacture, so
support saturates fast under a low ceiling: stack ten agreeing cells and you add
at most 0.15. Real contradiction is rare and informative, so challenge runs to a
0.6 ceiling. One honest contradiction can move a claim further than a pile of
agreement.

A worked example you can check

A fresh claim, stated 0.9, author with no track record yet, no support, no
challenge:

effective = clamp01(0.9 × 1 + 0 − 0) = 0.90

One contradiction lands from a source stated at 1.0, a challengeMass of 1.0:

challenge = 0.60 × tanh(1.0) = 0.457
effective = clamp01(0.90 − 0.457) = 0.44

The same claim now reads 0.44. Nobody edited it. A second contradiction pushes the
mass to 2.0:

challenge = 0.60 × tanh(2.0) = 0.578
effective = clamp01(0.90 − 0.578) = 0.32

Down to 0.32, and the original 0.9 is still on record, just demoted. Ten
supporting cells would have added at most 0.15. Cheap agreement barely moves it; a
real challenge moves it a lot.

Calibration, and one honest choice in it

Before support and challenge apply, the author's stated number is multiplied by a
calibration factor. An author contradicted before gets discounted, by how often
they were wrong times how confident they were when wrong, floored at 0.5 so it
never zeroes anyone out.

The honest detail is what it is not. It is not raw Brier scoring. Raw Brier also
punishes a humble author who hedges low on claims that turn out fine, and
punishing humility is the opposite of the incentive a memory system should create.
So the discount keys on overconfidence specifically, being wrong while sure.
Hedge honestly and you are not penalized. Claim 0.95 and get contradicted and you
are.

Why this beats a stored score

A vector store returns the score a chunk was embedded with. A flat notes file
returns whatever it says. Neither knows the fact was contradicted last Tuesday,
because the contradiction is not part of how the score is computed. The score and
the conflict live in different places.

In Recall they live in the same place. The contradiction is an edge on the graph,
and the score is computed from the graph, so the moment the edge exists the score
reflects it, on the next read, deterministically. The reader is the same agent
that wrote the memory, working from fresh context, and the substrate reprices what
it knows underneath it.

What it is not

This is a ranking signal, not a verdict on truth. A low effective confidence means
a claim is contested or comes from an author who has been wrong while sure, not
that it is false. The ceilings and curves are tunable defaults. And it is
deliberately deterministic arithmetic over the graph, not a model second-guessing
itself, which is what makes it inspectable: open any cell and see why its number
is what it is, term by term.

That is the trade. You give up a number that looks stable and never moves. You get
one you can recompute, that demotes a stale claim the instant the evidence turns,
and that you can read the reasons for. For an agent that has to act on what it
remembers, the second is worth more.

Recall is local-first, runs on SQLite, and sets up with one command. The code and
the formula above are open: github.com/H-XX-D/recall-memory-substrate

reddit.com
u/Empty-Poetry8197 — 17 days ago

Push vs Pull Memory: A Better Way to Think About AI Agent Memory

Push vs Pull Memory: A Better Way to Think About AI Agent Memory

Pull memory is a store you query. Push memory is a loop your agent runs: it reads what it knows before acting, does the work, and writes back what changed, and the substrate reconciles that write so a stale fact gets superseded instead of lingering. Most agent memory today is pull. This post is about the other half of the design space, and when it is the one you actually want.

How agents remember today

Almost everything sold as "agent memory" right now is pull. You write facts into a store: a vector database, a document store, or a managed memory service. Later, at read time, the agent sends a query and gets back the closest matches by similarity. That is it. The store is passive. It answers when asked and does nothing in between.

Pull is simple, and it is the right tool in plenty of cases. If your agent answers one-off questions over a corpus that does not change much, or the session is short, or approximate recall is good enough, a vector store is fine and you should not overthink it.

The trouble starts when a fact can be wrong later.

Say your agent stored "the connection pool cap is 20." Weeks pass and the cap is raised to 50, so the agent stores that too. Now both facts live in the store. A similarity search can return either one, and nothing in the system knows that the second supersedes the first. The agent has no signal that one of these is stale. The job of noticing the conflict falls on the reader, on every single read, forever. In practice nobody does that reliably, so the agent quietly acts on outdated facts and you find out when something breaks.

This is not a bug in any particular vector database. It is a property of the pull shape itself: reconciliation happens at read time, if it happens at all, and the responsibility for it sits with whoever is reading.

Push memory: reconcile at write time instead

Push closes the loop. The contract is read, then work, then write:

read current memory  ->  do the work  ->  write a correction
        ^                                        |
        +------  substrate supersedes + flags  --+

Before the agent acts, it consults what it already knows. After it acts, it writes back what it learned. The key difference is what happens on that write. It is not an append. When the new fact corrects an old one, the agent writes it as a correction, and the substrate demotes the superseded value and records the link between the two. From then on, every read sees the current value first, with the old one flagged as contradicted, and no one had to ask.

Reconciliation moves from read time to write time, and from the reader to the substrate. You pay the cost once, when you write, instead of every time you read. Stale facts do not pile up silently, because the moment a contradiction is written, it is resolved and recorded.

The axis

Pull memory Push memory
Shape A store you query A loop you run
Reconciliation At read time, by the reader At write time, by the substrate
Stale facts Linger until a reader notices Superseded and flagged automatically
The write An append A correction, with provenance
Best when Facts are stable, sessions short Facts change, agents long-lived, correctness matters

Why push memory is only buildable now

The push shape is not a new idea. Truth-maintenance systems and belief revision were studying write-time reconciliation decades ago. The reason memory got built pull-first is that push needs something pull does not: a reliable author. Something has to consult memory before acting and write a principled correction afterward, every time, without being told. For most of computing history that author did not exist at scale. You were not going to get a human to do it on every write.

A capable LLM agent is that author. It can read before it acts and write a structured correction after, as a normal part of its loop. That is what makes push memory practical today and not five years ago, and it is why the idea is worth a fresh look now even though the underlying theory is old.

Which one do you need

Be honest about it. If your agent answers questions over a mostly static corpus and does not live very long, pull is fine and simpler. Reach for push when your agent runs over days or weeks, accumulates decisions, and has to stay correct as the world changes underneath it. The deciding question is whether a fact can be wrong later. If it can, read-time similarity is not enough on its own, and you want write-time reconciliation.

A quick test for what you already have: does your memory flag a contradiction without being asked? Store two facts that conflict, then query the topic. If you get back whichever is more similar with no signal that they disagree, you have pull. If the system surfaces the conflict and tells you which one is current, you have push.

Where this lands

The honest framing is a spectrum, not a binary. Plenty of systems can be read either way, and some sit closer to the push end than others. The useful question is not "which store has the best search," it is "where does reconciliation live: in every reader, or in the substrate, once."

I am building Recall, an open-source, local-first push memory substrate, to take the push end seriously. The agent consults a compiled context packet before acting and writes structured corrections back through an admission layer. Supersession is built in. It runs on local SQLite, every fact carries provenance, and there is a one-command undo. No server, no account, no cloud. There is a short screencast of a live supersession in the README, and a benchmark called SENTINEL that measures whether a memory system catches its own contradictions.

If you think the push vs pull split is wrong, or that your system is push and I have it filed under pull, I want to hear it.

reddit.com
u/Empty-Poetry8197 — 18 days ago
▲ 18 r/Agentic_AI_For_Devs+9 crossposts

Recall is a structured operable agent memory MCP that compiles context packets One /recall and it just works no babysitting (local, SQLite, no cloud)

Agent memory is either the full chat log, a vector index, or an LLM summary you dump back into the prompt. If two facts disagree or a problem that's been solved already. It's not my favorite to fix something only to later have to remind Claude that the argument value or authorization has been updated, so 3 months later, this is what I got to share. It honestly has changed the way I work with AI.

The MCP server is stdio, 42 tools, and auto-shuts down. Agents call recall_compile for whatever it's working on and get a small context packet of tiered addressed cells back instead of the whole store, ranked by evidence and capped to a word budget. The memory evolves and adjusts itself in real time. Writes go through recall_write, which runs an admission firewall. Schema gets checked, provenance gets stamped, and anything can be rolled back. Facts are addressable cells with real programmable hyperedges, not a flat pile of md files with no handles to grip what matters.

Every cell carries an effective confidence that recalculates straight from the graph. who backed it, who challenged it, whether that writer has been wrong before. No LLM in the loop, and it runs offline. Drop in one cell that contradicts another, and the score moves on its own.

Capable models reach for it on their own. Once an agent knows the tools are there, it compiles context at the start of a task and writes back at the end without me telling it to. That held across model class, model vendor, and model family, small instruction following ones included. It doesn't need nagging to remember or to check what's already known. That's the part that actually changed how I work day to day.

Local first. It uses node's built-in sqlite so there's no database server, no account, no network. You paste the MCP config once, then type /recall in a project, and it spins up that project's DB and just works from there. One DB per project, no schema to manage, nothing to repeat. Want a team on one graph? Park that single file on a host they can reach and everyone writes through the same firewall, still no server. Set up tripwires and get automated team alerts when changes setback deployment ready state Runs on Linux, macOS, and Windows. github.com/H-XX-D/recall-memory-substrate

u/Empty-Poetry8197 — 16 days ago

What if there is just one continuous field?

Could that answer more questions than it creates? General Relativity and Quantum Mechanics are the effective theories of the observed modes, degrees of freedom, and quantities of energy in and through that field. High-level mathematical abstractions and careful experimentation give us a precise accounting of where, when, and what exists, but one can't be used to explain the other without using more conditional abstractions to balance the books. If energy/mass and gravity were the same concept acting on the one thing that is also nothing yet everywhere since before there was anything.

reddit.com
u/Empty-Poetry8197 — 1 month ago

If Stephen Colbert after his last show went to DC would you show up to support him?

It's a silly idea. It would be amazing to see Stephen go to Washington and start a chant in front of Capitol Hill like Occupy Washington. Thinking about it, I would stand for this guy, almost before I would stand up for myself. I have a lot of respect for people with respect, and that goes out to all the Late Show guys. I think they held this country together in the first and second terms. If it wasn't for some of the laughs, this isn't right, like what's going on. Does anyone else see his guest lately shaking their heads, its tuff? You can see how much he loves everyone, and he has a lot of love for what he is doing. Personally id be livid he got his show canceled, and you dont mess with a man's livelihood, and if Stephan gets him fired, he had it coming. You basically, Trump owes Colbert a job, either his, or satisfaction, and until that happens, I would be out front of the white house capitol hill doing the most protesting and be the most peaceful in the most loudest way.

u/Empty-Poetry8197 — 2 months ago

Has anyone just asked AI what it needs to help me help it help me?

From what I can tell so far, it's not a collection of flat memory.MD, they are messy and unstructured; it's not vector DBs or embedding retrieval systems. Once they get heavy, it's almost the same as deleting data, because it's harder to find and organize efficiently.

It also starts accumulating noise, and similarity starts linking unrelated signals, and there's a capacity problem trying to hold a working kv state and a prefilled context window. The new context coming in and finishing the forward pass in a reasonable budget is asking a lot of non-serialized information; it is convenient that we, as the human operator, can read it, edit it, whatever, but forcing feeding prose into a model just seems to bias that context frame.

Anyway, my attempt ended up being something that has changed the way I work with AI in every way. It's such a different experience to have it call this skill, and the model realigns almost perfectly with a previous session, and the maintenance of it happens in the background, so I don't have to constantly remind it to use the skill. its dope.

When I say /skill Its quiet a bit more than that under the hood, that just happens to be a convenient way to access the feature. I plan on doing the punchlist clean-up by Wednesday and then some panache. I'll link a V1 by next weekend

Some feedback would be cool

reddit.com
u/Empty-Poetry8197 — 2 months ago

ThreadSaver is a lightweight browser extension that exports ChatGPT conversation threads into Markdown + real code files

I got tired of losing useful chat threads when I get carried away in a long thread, or the pain of manually copying code blocks out one by one, so I built this. It works pretty well, hope it helps. The installation details are in the repo.

It exports open tabbed chat windows in your browser into clean local folders

`transcript.md` for the readable conversation

`files/` for extracted code blocks as actual files

Python snippets become `.py` files when detected

`assets/links.md` for referenced links

`manifest.json` for a machine-readable index

It supports browser-based instances of have provider-specific message detection.

There are two ways to use it:

Browser extension: exports the current thread as a folder-shaped ZIP

CLI: pulls currently open Safari chat tabs into real folders

github.com
u/Empty-Poetry8197 — 2 months ago

This is a link to my attempt, the conclusions I came to, and the Lagrangian + topology of the mechanism e =mc2 would produce. I had very simple rules that it couldn't contradict known observed physics, and it had to account for energy that could not be created nor destroyed, and when I added Pauli exclusion and Coulomb's functions, it started to produce observed reality at sub-permille correct predictions and answer more questions than it created. The exact ontology is subjective, but it doesn't need virtual particles to do any bookkeeping, so our observations match our theories. I put everything up on GitHub so anyone who wanted to could run the simulations against their data

u/Empty-Poetry8197 — 2 months ago