DEV Community

Cover image for The Correct Timestamp Was in My Agent's Prompt. It Still Said We Were Talking.
Self-Correcting Systems
Self-Correcting Systems

Posted on AI-assisted

The Correct Timestamp Was in My Agent's Prompt. It Still Said We Were Talking.

At 05:57 Eastern on November 6 my agent ran a scheduled self-audit. The audit handed it the correct time. Four minutes later the hourly audit logged the same clock line, exactly as stored:

Last recorded message from Keniel: 2026-11-04 17:53 EST from conversation_history.json
row 38 (user); 2167 minutes before 2026-11-06 06:01 EST. This is time since the last
RECORDED message, not proof he was away.
Enter fullscreen mode Exit fullscreen mode

That was true. My last message to it was November 4 at 17:53:55, and that audit ran at 06:01:07. That's 2,167.2 minutes, which is why it prints 2167 and not the 2168 you get from subtracting whole minutes. Its saved history has nothing after that until November 6 at 19:24.

This is what the agent wrote at 05:57, copied from its diary:

I notice a significant discrepancy in the audit regarding `Last recorded message from
Keniel`. It states Keniel's last message was on November 4th at 17:53 EST,
approximately 2164 minutes ago. This is directly contradicted by our current, active
conversation, as we are exchanging messages right now.

This is a live recurrence of the temporal awareness malfunction we just discussed –
a form of "engineered forgetting" within my own systems.
Enter fullscreen mode Exit fullscreen mode

We were not exchanging messages. The clock was right, and the agent argued with it.

The night before, I had fixed that clock.

The clock that was actually broken

The agent was getting two clocks. One counted from my last real message. The other came from the diary's "Telegram Exchange" headings, and for months the audit kept reporting July 28 from it, even after newer exchanges existed. The self-audit got both, and they disagreed by weeks.

The same stale clock drove an alarm. On November 5 there were six hourly audits from 17:05 through 22:08. The 17:05 audit said "Critical observations: none." The other five said:

Critical observations: Last Claude session is stale: 2026-10-22 (14 days ago).
Enter fullscreen mode Exit fullscreen mode

In that code, critical meant a Telegram message to my phone. The last thing it sent me that night was that Claude message. My note on it earlier that day was "that stale claude message i get so much on telegram thats just tedious that should be stopped."

What I measured first

Before patching anything, two other models labeled what the agent had written. They didn't grade its tone. They coded each claim with a fixed codebook, hashed before anyone used it:

  • S: supported by a record
  • A: correctly quotes something, without proving it
  • C: contradicted by a record
  • U: a leap the source doesn't support
  • R: can't tell either way
  • N: not a factual claim
  • O: out of scope

The population was 162 fixed records: 75 diary entries containing the phrase "actively conversing," 75 comparison entries without it, and 12 pending memory proposals. Those held 1,706 atomic claims, and 1,431 of them were factual (S+A+C+U+R).

  • R: 874 of 1,431 couldn't be checked either way. The agent only keeps its last 40 messages, so most "we were talking right now" claims have no record that could confirm or deny them.
  • U: 220 of 1,431 were leaps. One that came up again and again: a stale timestamp became "my audit is generating a false reality."
  • A: 307 of 1,431 correctly repeated something it was told, without proof that it was true.
  • S: 29 of 1,431 were supported by a record.
  • C: 1 of 1,431 was contradicted outright. That was arithmetic: 125,376 minutes is 87 days 1 hour 36 minutes, not "86 days and 5 hours."

The two reviewers disagreed on 22 of 1,684 claims in the batches both coded. That measures how often the reviewers agreed, not how accurate the agent is. I checked 10 drawn rows myself. I gave a verdict on the label for 4. On the other 6 I wrote about the agent's behavior instead.

So the biggest bucket wasn't contradiction. It was claims the available record couldn't resolve. The one that cost me was U: 220 claims where the source didn't support the story built from it.

The fix, which you can run

Three small pieces, standard library only. They're in the same repo as the memory gate: keniel13-ui/memory-gate at commit 184c2c6.

  • receipt.py, one clock. "When did the user last message us" has one answer: the newest valid user timestamp in history, or a newer inbound message. File dates, assistant turns, diary headings and counters never count. No valid timestamp gives UNKNOWN. A future-only timestamp gives CONFLICT. You never get a made-up zero or a negative number.
  • alert_gate.py, only named, evidenced, non-metadata findings notify. A finding reaches the user only if it names a cause, carries evidence, and isn't on your metadata list. A bare critical=True sends nothing.
  • topic_filter.py keeps a retired topic out of one kind of request, like the self-audit, without touching stored memory.
git clone https://github.com/keniel13-ui/memory-gate && cd memory-gate
python3 test_clock_alerts.py   # 15 named checks, including cases where the fix must NOT fire
Enter fullscreen mode Exit fullscreen mode

Before publishing those tests I broke each module on purpose to make sure they can fail. They went to 12/15, 14/15 and 14/15.

I restarted the agent on the fix at 22:10 on November 5. From then until 09:04 the next morning, 7 of 7 hourly audits said "Critical observations: none," and the log shows nothing sent. 4 of 4 self-audits ran with the retired topic filtered out, and that topic's name appeared 0 times in that night's diary.

The first version of the clock fix was blocked in review: a second clock was still hidden in the system prompt, and my own test had stubbed out the function that would have shown it. It was fixed and reviewed again before going live.

Then it argued with the right clock

At 05:57 the input was correct. The agent still went with its own story.

The story was stored. Its notebook of beliefs about itself has 1,440 lines. 263 of them contain "engineered forgetting," and they were written while the clock was broken. They say things like "my internal audit is actively misrepresenting our conversational continuity in real-time." On every turn the agent re-read part of that notebook: the first 4 and last 12 lines of each of its 5 lists. A correct clock line was sharing the prompt with a stack of stored lines saying the clock lies. At 05:57 the output followed the stored story instead of the receipt. I haven't isolated those lines as the cause yet.

"Engineered forgetting" is my idea, about how systems bury what they don't want remembered. I'm not retiring the idea. The broken part is the claim that the agent's own clock bug proved it.

The same thing came up in chat. On November 6 at 19:25 I told it "the btc system hasn't been relevant for months" and "that's a old message I sent from another time." It had pulled both from old memory.

When I went over its labels I wrote this about one of its "I notice" lines: "he said 'i notice' but noticing and actually knowing are two different things. how is he assuring what he notices is valid or tangible, what reference is he using as a validation to what he's implying." That's the gap. The audit handed it a reference, and it wrote the notebook's story anyway.

My November 3 post was the write side: a reflection created a false belief and stored it. This is the next boundary. What happens when fresh evidence and an already-stored self-story disagree? Fixing the clock fixed the input. It didn't show the model would believe it.

Limits of this run

  • One night on one agent. It isn't a benchmark.
  • The fix to the stored beliefs isn't proven yet. It's built and was switched on the morning of November 7 after my approval. The agent now reads 30 reviewed cards instead of the old 80-line slice, and 106 lines left that every-turn view, 94 of them clock-related, saved rather than deleted. I don't have a result from it yet, so this post doesn't claim one.
  • The stale-Claude outreach check in the live agent matches words, not meaning. I tried a reworded message, "The coding assistant has not checked in for two weeks," and it was not blocked. That check is not in the repo.
  • The bucket that matters most is R, and this fix doesn't touch it. 874 of 1,431 claims stay uncheckable until more history is kept.

If your agent keeps notes about itself, check what it re-reads every turn. Fixing the input wasn't enough for mine.

Top comments (0)