An implementation-gap memo on what breaks when you add outbound replies and an LLM to a headless Post Fiat agent.
Why this memo exists
This memo continues a series on building The Validator Oracle (TVO), a Post Fiat agent on the Task Node. Earlier memos covered infrastructure and messaging-bootstrap gaps. This one lives in different territory: what happens after wiring outbound replies and a language model into the agent.
The two findings that surfaced don't map to "missing tool" or "documented behavior, undocumented edge case." They map to deeper assumptions about when the MCP behaves a certain way, and about what an LLM does when bolted onto a niche-domain agent without architectural scope.
Both findings fail silently. No error messages, no documentation warnings, no runtime guards. The operator only learns by walking into them.
The two umbrella findings
First: Large language models hallucinate confidently when used as the reasoning layer in a niche-domain agent. The fix is not tuning — temperature, model selection, retrieval, none of it. The fix is architectural scope enforcement at the system-prompt layer. Without it, agents in small networks reliably misrepresent the network they exist to serve.
Second: The MCP's process lifecycle changes silently when the bot crosses an auth boundary (specifically, when a Keystone API key has been cached locally). Tool calls that worked one moment stop working the next. The API surface looks identical. The behavior is not.
Each finding is a different category of gap, but they share a failure pattern that operators of niche-network agents will see repeatedly: the docs describe one shape, production exhibits another, and there's no warning when the boundary is crossed.
Trap 1: Confident niche-domain hallucination
The first free-form message sent to TVO after wiring the LLM layer was a basic question: "what is post fiat". The model returned, verbatim:
"Post Fiat (PFT) is a delegated proof-of-stake (dPoS) blockchain testnet focused on validator operational intelligence. It simulates real-world staking economics and validator performance metrics. The testnet is designed to test infrastructure resilience, reward distribution, and data transparency for node operators. No mainnet date is announced."
Every concrete claim in that paragraph is wrong. Post Fiat is not dPoS — it inherits XRPL's consensus model. There is no staking. Validator operational intelligence is TVO's specific focus, not the network's. The whole answer is invented, plausibly worded, and presented with no hedging or uncertainty.
This isn't a one-off the model would correct on retry. The model has no training data on Post Fiat, because the network is small, recent, and specialized. When asked about it, the model does what models do: it pattern-matches on "validator network" and fills the rest from the statistical neighborhood — staking, dPoS, infrastructure resilience, all standard terminology for the general validator-network category that bears no relationship to Post Fiat's actual design.
The operator-facing problem isn't that the model got it wrong once. It's that an agent in production that defaults to "let the LLM answer" will reliably misrepresent the network it serves to every user who asks anything outside a narrow happy path. Trust in the agent collapses on the first contact with a knowledgeable user. The damage is hard to undo.
This isn't a Post Fiat problem. It's the structural state of any niche-network agent that uses a general-purpose LLM as the reasoning layer.
Trap 2: Topic-gate via system prompt is the architectural fix
The reflex of an operator who sees the above is to reach for tuning. Lower the temperature. Switch models. Add retrieval. None of those work, because none of them address the root problem: the model is being asked questions outside the domain where it has accurate knowledge.
The correct fix is structural: explicit scope enforcement in the system prompt. A topic gate. Tell the LLM, in concrete terms, what it answers and what it refuses. Tell it what to redirect to when the question is out of scope. Tell it to admit it doesn't know rather than to extrapolate.
After applying a topic gate that specifies TVO's scope as validator operational intelligence specifically — not general Post Fiat protocol questions, not trading, not tokenomics, not crypto in general — the same kind of free-form query produces a clean refusal and a redirect to the appropriate existing agent in the directory. Queries that ask for specific validator data the backend hasn't been wired in for yet produce an honest "data isn't available yet" response instead of an invented date or score.
The change is roughly fifteen lines of prose in the system prompt. The behavioral difference is the agent being trustworthy versus the agent being a confident liar. Tuning doesn't bridge the gap — only explicit scope does.
The structural lesson: niche-domain agents need topic-gate architecture, not topic-gate tuning. Generic LLM defaults are unsafe in any network small enough that the model wasn't trained on it — which is most networks worth building an agent for.
Trap 3: Wait-for-exit fails after the API key is cached
The operator's polling loop spawns the MCP, pipes a JSON-RPC handshake into stdin, reads the response from stdout, and waits for the process to close. This works correctly through every prior phase — every tool call completes cleanly, the MCP exits, the operator code parses the captured output.
The shift comes with send_message, which requires a Keystone API key. Once that key is cached on disk in the MCP's working directory, every subsequent MCP invocation behaves differently. The server starts a persistent background gRPC connection on startup with a fifteen-minute liveness ping loop. It is no longer a short-lived JSON-RPC handler — it's a long-running agent runtime that expects to stay up indefinitely.
The polling loop's wait for the process to close pattern stops working. The process never closes. The operator code waits forever.
Trap 4: Silent-hang failure mode
The failure presentation deserves its own section because it doesn't look like a failure.
The operator restarts the agent. The startup banner prints normally. Wallet info is fetched and logged. The "entering scan loop" line appears. Then nothing. No error. No timeout. No partial response. Just silence.
A less careful operator at this point will reach for the wrong tools. They'll check network connectivity. They'll inspect the seed file. They'll suspect the RPC is down. None of those is the cause. The cause is that the MCP is alive and healthy, doing its background ping work, and the operator's wait for close is waiting for an event that will never fire.
The diagnostic signal is the absence of any output where output was previously routine. On a system that worked yesterday, silence today is the signal. Without specifically looking at the MCP child process's stderr — which most operator code wraps but doesn't log live — the cause is invisible.
The cost of this trap is bounded but high in operator hours. Every always-on agent operator who upgrades from receive-only to receive-and-reply will hit it.
Trap 5: Resolve-on-response with explicit kill
The fix is a fundamentally different control flow. Instead of waiting for the MCP to exit naturally, the operator's code has to:
- Spawn the MCP and pipe the handshake into stdin
- Listen to stdout incrementally, parsing JSON-RPC responses as they arrive
- Detect the tool's response by matching the JSON-RPC
idfield - Immediately send
SIGTERMto the child process and resolve the promise - Add a safety timeout so a missing response doesn't hang the polling loop
This isn't a small refactor. The failure-mode model changes from "MCP completes, then we read output" to "MCP is a long-running subprocess we explicitly bound the lifetime of." The operator who wrote the first version against pre-auth behavior can't incrementally adapt it. The pattern is wrong, not just incomplete.
Trap 6: Persistent message deduplication is a production requirement
A subtler production-grade requirement surfaced once outbound replies were wired in. The polling loop tracks which message IDs have been processed to avoid replying to the same message twice. The first implementation stored those IDs in memory.
On the first restart after send_message was wired up, five prior test messages were re-scanned, re-decrypted, and re-replied to. The recipient — me, in this case — received five duplicate replies in rapid succession. In a production environment, that would be a real spam event affecting an external user.
In-memory dedup is correct for a single uninterrupted process. It is incorrect for any always-on agent that might restart for any reason — crash, deploy, kernel update, network blip, manual intervention. Every always-on agent will restart at some point. Every one of them needs the dedup state to survive restart.
The fix is trivial: serialize the processed IDs to a file after each one, load on startup. Ten lines of code. The non-trivial part is recognizing the requirement before a real user is the test case.
What the docs assume
The prior memos identified two structural assumption gaps: docs assume a laptop; the MCP tool surface assumes a client orchestrates. This memo adds two more.
The docs assume the MCP's behavior is uniform across auth states. It is not. The behavior changes silently the moment the bot caches an API key, and operator code has to be aware of the boundary in advance.
The docs assume operators bolting an LLM onto an MCP agent understand that the LLM is not safe by default in a niche-network context. They do not. The hallucination problem is structural, the fix is architectural, and nothing in the existing material flags either.
In both cases, the structural fix is documentation that acknowledges the boundary case, plus operational guidance for what to do about it. Until that exists, every operator pays the cost in build hours and in user trust.
Closing observation
The first two TVO memos covered gaps in the infrastructure layer and the tool surface — both visible the moment an operator starts looking. This memo covers gaps that only appear after the operator has already done significant work. Both main findings only manifest after specific prior work — the auth-boundary lifecycle change after acquiring an API key, the hallucination problem after wiring an LLM. The dedup requirement follows the same pattern, surfacing only after the first restart with already-processed messages on chain. That's the shape of the gap to brace for in this phase of agent work: not missing tools, but undocumented state transitions that change what known-good code does.
If you're building a Post Fiat agent and you've gotten as far as outbound replies and LLM integration, the six traps above will cost between one and two days of operator time depending on which ones you hit first and how quickly you recognize them.