DEV Community

Cover image for A Channel That Does Not Log Is Invisible to APX

A Channel That Does Not Log Is Invisible to APX

A Channel That Does Not Log Is Invisible to APX

A runtime can claim it supports many surfaces, but that claim breaks fast if each surface keeps its own private history.

APX takes a stricter approach.

If a channel wants cross-channel continuity, it must log its turns into the shared runtime message store. If it does not log, APX cannot search it, retrieve it for memory, or hint that another thread is already active somewhere else.

That sounds like an implementation detail. It is not. It is a design rule.

APC and APX split responsibilities on purpose.

APC is the portable context layer. It keeps project truth in committed files like AGENTS.md, .apc/project.json, agent files, and skills.

APX is the runtime and tooling layer. It owns execution, sessions, channels, local memory, and message history under ~/.apx/.

That is why channel logs belong in APX, not APC.

One global place for cross-channel turns

The APX message store code documents two different lanes.

Project-specific runtime channels such as runtime, a2a, and exec live under:

~/.apx/projects/<project-id>/messages/YYYY-MM-DD.jsonl
Enter fullscreen mode Exit fullscreen mode

Cross-project channels such as Telegram or other daemon-level surfaces live under:

~/.apx/messages/<channel>/YYYY-MM-DD.jsonl
Enter fullscreen mode Exit fullscreen mode

The key function is appendGlobalMessage(). It writes one JSONL line per turn into ~/.apx/messages/<channel>/YYYY-MM-DD.jsonl.

That shared store is not just for archival.

In the APX project guide, the rule is explicit: the cross-channel message store is the spine. Every surface that wants to participate in shared awareness logs through that path. A channel that does not log is invisible cross-channel.

What APX gets from that rule

Three concrete features depend on this.

First, active thread awareness.

buildActiveThreadsBlock() reads recent turns from other channel folders under the global messages directory and injects a short block like “Recent chatter on other surfaces.” It skips the current channel, looks for recent meaningful turns, and tells the agent what may already be happening somewhere else.

That means a web conversation can notice that Telegram recently asked for a deploy, or a desktop voice session can notice that another surface already started the same task.

Second, retrieval.

The memory indexer walks ~/.apx/messages/<channel>/YYYY-MM-DD.jsonl for every channel, turns relevant records into chunks, and embeds them into the vector store. User and agent turns become retrievable context. Tool output can also be indexed in capped form.

No log, no chunk. No chunk, no recall.

Third, explicit search.

The search_messages tool resolves a project and runs full-text search over project messages so an agent can answer questions like “what did we decide yesterday?” without pretending to remember. Cross-channel awareness stays lightweight, while exact recovery stays searchable.

Why this should stay out of APC

It would be tempting to put all of this into the repository and call it “shared context.” That would be a mistake.

Raw channel traffic is runtime state. It can contain noise, private details, ephemeral commands, and local operational history. APC should stay portable, reviewable, and durable. It should not become a dumping ground for every chat surface.

So the boundary stays clean.

APC carries project contract.

APX carries live traffic.

Then APX can build useful runtime behavior on top: active thread hints, retrieval, search, compaction, and audit trails across surfaces.

That is deeper reason this logging rule matters.

Without one shared runtime log, “multi-channel” mostly means several disconnected inboxes.

With it, APX can treat channels as different entry points into one working system, while APC remains the portable layer that any compatible tool can read.

Top comments (0)