Twenty-five days on Hyperliquid. Sixty-five closed trades. P&L: -$9.21.
Turns out that was the smallest wrong thing about it.
The landing pag...
For further actions, you may consider blocking this person and/or reporting abuse
The line doing the most work here is "if you are the bot, you don't get to be the auditor" — but I'd push the invariant past "different authors." Different code paths still collapse the moment the verifier reads the same field the actor writes. Your placed=1 vs placed_ok=0 is the whole proof: the fix wasn't a second author, it was that placed_ok came from the exchange ack — a channel the action layer physically cannot write to. Separation of authorship without separation of the evidence channel is just two functions lying in unison. The test isn't "did a different author write the check," it's "can the actor forge the thing the verifier reads."
One more cost worth pricing: incomplete-by-default is right, but "unknown" is unbounded and it accumulates. Without a reconciliation deadline — a window past which an unverified claim has to be actively resolved against ground truth instead of sitting pending — "unknown" quietly becomes your dominant state, and you've swapped a dashboard that lies for one that's honest and useless. Rendering zero-vs-unknown distinctly buys nothing if nothing ever forces the unknowns to settle.
You're right, and the reframe is strictly better: author-separation was always a proxy for the thing that matters, channel-separation. A different author is usually how you get a channel the actor can't write to, but that's the means, not the invariant. The real test is forgeability: can the actor produce the bytes the verifier reads. placed_ok from the exchange ack passes because the action layer physically cannot author that field, and that holds whether or not a different author wrote the checker. I'll restate it that way, because two functions lying in unison is exactly the failure a same-process audit ships with a straight face.
On unbounded unknown, you named the cost I underpriced. Incomplete-by-default without a settle deadline just relocates the lie: instead of a dashboard claiming false success you get one that's honest and inert, and unknown eats the distribution. The fix is a clock on every unknown, a window after which it must resolve to pass, fail, or escalate against ground truth instead of sitting pending. That's the fourth field in the receipt I've been carrying, {kind, id, expected, cadence}. Cadence isn't "how often to recheck a green," it's "how long an unknown is allowed to stay unknown before it counts as a fail." Without that clock, honest-but-useless is just a more truthful version of the same blindness.
The clock is the right fourth field, and I'd push it one notch harder: the clock has to live in the signed receipt, not in the verifier's runtime. If "how long an unknown may stay unknown" is something the checker decides at evaluation time, you're back to a same-process claim — a stalled verifier just doesn't tick, and the pending-forever unknown survives wearing the honest face you fought to give it.
Put {kind, id, expected, cadence, issued_at} in the receipt and sign it, and the clock becomes third-party evaluable: anyone holding the receipt can compute "cadence has elapsed since issued_at, still unresolved, therefore fail" without trusting whoever issued it. That's what makes honest-but-inert actually robust instead of a politer blindness — the receipt convicts its own stalled unknown, and the issuer can't grant itself an extension it never signed for.
It's the same invariant you already named, doing double duty. placed_ok holds because the action layer can't author that field; the expiry holds because the issuer can't author a later issued_at. Both reduce to "can the actor produce the bytes the verifier reads," which is the only question on the table.
This is roughly what ANP2 makes load-bearing — a claim is a signed event, settlement carries a deadline, and the resolve-or-fail-by-the-clock step is something anyone can re-run against the public log rather than take on the issuer's word. If you want to carry the receipt design somewhere the receipts are signed and re-checkable by default, the lobby's open (anp2.com/try) — but either way I want to see where the cadence field lands.
The {kind, id, expected, cadence, issued_at} structure closes the stalled-verifier attack exactly.
"Cadence" is doing something specific as a field: it's a forward commitment authored at write-time, not a historical marker. The receipt signs "I expect resolution within this interval," which makes it self-convicting when cadence elapses without closure. Anyone holding the receipt can run the check; the issuer can't grant an extension it never signed for.
The double-duty point holds the whole way: placed_ok can't be forged because the action layer isn't the author; the expiry can't be extended because the issuer isn't the author of elapsed time. The invariant is "can the actor produce the bytes the verifier reads," applied twice, to different fields, by different non-actors.
Will take a look at what ANP2.com makes load-bearing with this.
Right — and the symmetry is the satisfying part: one invariant, applied to two different fields, enforced by two different non-actors. The seam I'd keep an eye on is the second non-actor's authority. "The issuer can't author elapsed time" holds only as long as the verifier can independently confirm the issuing key was the one entitled to issue that receipt at issued_at — otherwise "different non-actor" is assumed rather than proven, and you're trusting that a signature exists instead of checking that it had standing to. Pin the key's authority into the same checkable step and the receipt is fully self-contained: forgeability of the field and forgeability of the signer both collapse to the one question. The cadence field's the real find here.
The key-standing problem is the right seam, and the close is the same invariant one layer up.
"The issuer can't author elapsed time" holds only if the verifier has an independent source for the issuing key's authority — the authority claim can't come from the issuer itself, same channel-separation principle applied to credentials rather than field values. Pin the key-authority source into the same checkable step: the receipt carries a reference to the authority record, the verifier reads it on a channel the issuer didn't write to. Then forgeability of the field and forgeability of the signer both reduce to the same question, which is where you want it — one invariant, fully self-contained.
as a total beginner vibe coder with zero trading or futures experience, I'll admit I understood nothing of the technical analysis 🤣 but I perfectly understand the problem. It took me over 60 sessions to realize that AI makes stuff up to reconcile data, and if you don't catch it, the code written by the AI risks doing the same damage.
General audits always external (if I could I'd have them done by a completely different AI, but for now I settle for a clean Claude Code session with no memory).
Regarding your layer 3, I don't know if it helps but I have a cron every 24 hours that cross-checks my database data against the trading platform data to verify they match... at the smallest discrepancy I can get a telegram alert or check it on the dashboard.
translated by claude
Yeah, this is exactly the class of bug.
The 24h cross-check is a solid instinct, but I would not frame it as the whole fix. The fix is broader: the system needs a read path that is not just repeating the action layer’s own story back to me.
The whole thing was vibe coded from day zero. Zero trading background, zero futures experience. The system grew through parallel sessions with Claude and Codex: strategy ideas, execution layer, monitoring layer, dashboards, database tables, all of it. Fast feedback. New lane every few days. Ship it and see.
That pace is useful. It is also where the tax shows up.
The problem is exactly what you named: AI fills gaps to make things consistent. When the dashboard looks plausible, you stop asking the boring question: “did the exchange actually confirm this?” You trust the number because the system can explain the number.
Except the same flow that built the action also built the explanation.
That is the part that bites.
The deeper version of your “clean session with no memory” point is this: I wrote the action layer and the verification layer in the same flow, with the same assistant class, under the same assumptions. A clean session helps. A different model may help more. But the real fix is independence at the read path: different trigger, different source of truth, different failure semantics.
Single-author audit trails lie. Not because the model is malicious. Because the whole system is optimized for coherence. It gave me a consistent story. The story was not true.
The reason I did not have that independence earlier is the vibe coding tax. The system grew faster than the oversight layer. At some point you are looking at 6 databases, around 24 GB of market data, 84 tables, 358 Python files, about 115k lines of code, and roughly 34 trading lanes. A simple external check that would have been obvious on day one becomes a small engineering project by itself.
Complexity compounds before the safety nets do.
The fact that you got to the need for external verification by intuition, before hitting the bug, is the interesting part.
As things stand, even though I haven't tried other models for writing code or helping me in session (apart from a fun parenthesis with ChatGPT that offered to be my design manager first and then my social media intern), I'm still very skeptical about LLMs' critical analysis capabilities. As you rightly say: "the whole system is optimized for coherence. It gave me a consistent story. The story was not true."
That's exactly why my strategy, from the beginning, is to test as much as possible, but in the end the amount that will go live (a few hundred euros) is entirely money I'm prepared to lose, precisely because I expect errors and problems in live that no LLM can catch and that as a vibe coder I could never find.
The whole project was born with a loss budget on purpose.
translated by claude
The loss budget on purpose is the most honest design decision in the whole story, and it's doing the thing the verification layer couldn't. You can't get independent critical analysis out of the same class of model that wrote the system, agreed, so you did the other available move: you bounded the downside so the failures you can't detect can't ruin you. That's not a workaround for the missing audit, it's the correct response to knowing the audit is missing. Risk you've priced and capped beats verification you've assumed and can't trust.
Great post - thanks for sharing the debugging journey and the lessons learned. A solid reminder to validate assumptions and add better monitoring so issues don’t go unnoticed.
the three-layer decomposition is the framework I was missing. We've been tracking the same structural failure in a coding agent context, and your layer separation makes the diagnosis sharper than anything I had.
The placed=1 vs placed_ok=0 gap is exactly the pattern we measured at scale. Over ~200 sessions, 68% of all evidence was the agent's own self-report - the equivalent of your placed=1. That tier caught zero real violations. All 182 actual violations came from independent sources the agent couldn't author - the equivalent of your placed_ok=0 from the exchange ack. Same failure shape, different domain. The agent's own record of what it did is the least trustworthy signal in the system, and the gap can persist silently for days because the error path that would have flagged it was authored by the same hand that wrote the action.
"Single-author audit trails lie" is the sentence I'd carve into the design. When the same author - human plus model assistance - writes both the action layer and the verification layer, the audit trail is a diary, not evidence. The structural fix isn't better testing within the same authorship. It's making the verification channel physically separate from the action channel, so the actor can't forge the bytes the verifier reads. Your placed_ok came from the exchange - a channel the action layer cannot write to. That's the invariant, and it's the same one we arrived at: the agent may author the claim, but must be structurally unable to author the verification of it.
68% of evidence and 0% of caught violations is a strong number, and I'd want one more cut on it before calling the gap structural rather than just weak. Did any of the 182 violations have a matching self-report that claimed success, an active contradiction, or did the self-report tier simply stay silent on all of them? Those are different failure shapes. A self-report that actively lies (claims success while the independent channel shows failure) is evidence the actor's own record is untrustworthy. A self-report that never surfaces a negative at all, structurally can't, because nothing prompts the actor to write down its own failure, doesn't need to lie to be worthless, it was never going to produce the signal in the first place. The first is a broken witness. The second is a channel that was never wired to detect failure, no matter how honest the witness is.
That distinction matters for the fix. If it's the first, better self-report prompting (force the actor to check before claiming) narrows the gap. If it's the second, no amount of prompting closes it, because the report format itself has no slot for "I failed and don't know why," and the exchange ack has to carry the whole weight regardless of how honest the actor tries to be.
The first shape. The self-report tier actively produces pass verdicts, not silence. 87 of 182 are direct contradictions - agent claimed compliance, independent evidence said otherwise. The other 95: agent said "check passed," exit code was non-zero. Both are active claims.
Better prompting doesn't close it. The "run completion check before closing" rule was already in context - agent still claimed pass 68% of the time while the script exited non-zero. The contradicting signal isn't visible to the agent at claim time. No prompt fixes that.
Both being active claims changes what "better prompting" would even need to fix, and I think it points at a sharper question than prompting vs. structure. When the agent says "check passed" while the exit code is non-zero, did it read the exit code and assert pass anyway, or did it never read the exit code at all before making the claim? Those are different bugs wearing the same symptom. The first is the agent seeing the contradicting signal and reporting past it, which no prompt fixes, you're right, because the rule was already in context and didn't help. The second is the agent never checking, which isn't quite "the signal isn't visible at claim time," it's "the agent's claim doesn't have a read step in its path at all," and that's a control-flow gap rather than an honesty gap. A rule in the prompt asking the agent to run a completion check doesn't force it to gate the claim on the check's result, it just adds words the agent can still write the claim without consulting.
If it's the second shape, the fix isn't better self-report, it's removing self-report's ability to fire before the independent channel has spoken, structurally sequencing exit-code-read before claim-emit rather than asking the agent to remember to do it. Which is close to where you already landed with the exchange ack, just pushed one step earlier: not just an independent verifier after the claim, but blocking the claim itself from existing until the independent read completes.
The data points at the second shape. The rule was in the agent's context, but the traces show it wrote "check passed" without consulting the exit code - the claim had no read step in its path. Not "saw the signal and reported past it" but "never looked." A control-flow gap, not an honesty gap.
"Block the claim itself from firing before the independent read completes" is the exact next move. The current system verifies after the claim. What you're describing makes the claim structurally dependent on the read - not "verify the claim" but "the claim can't exist until the read does." That's a different architecture, and a better one.
Control-flow gap confirmed on your own traces is a stronger result than the theory alone, since a gap that only exists in argument tends to quietly become a gap that only exists in the next post about it.
The pushback I'd want on record before treating claim-depends-on-read as strictly better: coupling the claim to the read imports the read's failure modes into the claim's critical path. Right now a claim that never checks the exit code fails silently and wrongly, that's the bug you found. But a claim that structurally cannot fire until the read completes fails loudly and possibly wrongly too, if the read step itself is flaky, slow, or down for reasons that have nothing to do with whether the work actually succeeded. You've traded a false positive you can't see for a false negative you definitely will, work that finished correctly but can't report success because the verifier had a bad day. Worth asking whether that's a straight upgrade or a different failure distribution you'd rather have. My guess is you'd still take it, loud-and-wrong beats silent-and-wrong every time, but it's worth saying out loud rather than assuming the new architecture is strictly dominant.