DEV Community

Nabbil Khan
Nabbil Khan

Posted on Originally published at nabbilkhan.com

What Not to Hear

Five minutes. That is how long the recruiting system waits after anything in a candidate's file changes before it rewrites the summary at the top. A recruiter adds a note, a resume comes in, a call gets logged, and five minutes later the summary catches up. It is a good number. It is also a trap, and I did not see the trap until I asked a dumb question: what counts as a change? The summary lives in the file. Rewriting the summary changes the file. So five minutes after the summary rewrites itself, it rewrites itself again. Forever.

The loop

There is nothing special about this loop. Any system that reacts to a store and also writes to that store has it. The recruiting system reads the candidate file and writes a summary into it. The pipeline that publishes my worklog reads my commits and writes a journal entry, which is itself a commit. The denial agent in the billing operation reads a claim and writes a note back onto it. Three different businesses, and the shape is the same in all of them. The machine reads a thing, says something about it, and puts what it said in the same place it was reading from.

The wasted tokens are the obvious cost. The worse cost is quieter. Every signal built on top of "did this change" goes bad. The hashes in a candidate file exist to answer one question: has the evidence for this person moved since the last pass? If the summary is in the hash, the answer is always yes. The hash stops meaning the candidate changed and starts meaning the machine did something. And the machine always did something.

What broke: My first instinct was to hash the whole file, summary included. That hash would have flipped every five minutes on every candidate. A recruiter looking for what actually moved would have seen everything flagged, and would have stopped trusting the flag within a day.

Fix the write, not the trigger

The fix is not a smarter trigger. I spent a while thinking about it that way. Filter on who made the change. Check a timestamp. Compare the new summary to the old one and skip if they match. Each of those works until the next write path shows up, and there is always a next write path. The real fix lives where the write happens. When the system writes something, it decides right there whether that write is something the system is allowed to hear later.

There are three of these silent writes in the recruiting system now.

The dossier write-back emits no audit event. When the machine updates a candidate's dossier, nothing goes into the stream the trigger listens to. A recruiter's edit goes in. An inbound resume goes in. The machine's own update does not.

The AI summary notes are kept out of the evidence and out of the hashes. The note still exists. You can open it and read what changed and why. But it is not evidence, so it does not count toward what we know about this person. And it is not hashed, so it does not count toward whether this person changed.

A bulk item-create emits nothing. Extraction pulls a resume apart into dozens of items. If each item fired a create event, extraction would wake up extraction. So the bulk path is silent on purpose. One resume, one pass, done.

Key insight: A system that reads and writes the same store will loop on its own output unless you decide, write by write, which of its own changes it is deaf to.

  • 5 min wait after a change before the summary rewrites
  • 3 kinds of writes the system cannot hear
  • 0 audit events from a dossier write-back
  • 159 journal entries the site pipeline reads and writes

The same shape everywhere

Once I had the list, I started seeing it in every system I run. The site pipeline is the clearest case. It reads what I shipped and writes a worklog entry, refreshes the operation graph, and adds to an insight journal that is now 159 entries long. Every one of those outputs is a commit. If the pipeline treated its own commits as work, it would publish an entry about publishing an entry. So its own commits are the writes it is deaf to.

The billing side has the same shape and higher stakes. A voice agent calls a payer and writes the outcome onto the claim. The denial agent watches claims. If the call note counted as a claim change, the denial agent would re-evaluate the claim and write its own note, and the voice agent's scheduler would see a changed claim and call the payer again. Nobody designs that loop on purpose. It falls out of two reasonable agents sharing one table. So the same question gets asked there before anything ships: which of these writes does the other agent get to hear?

Anyone can wire a trigger. The hard part is choosing which of its own writes the machine is not allowed to see.

Silence leaves no trace

Here is the part that still bothers me. A write that emits no event is a write the audit log does not show. I chose that on purpose, and I would choose it again. But it means the silence is a fact I have to carry somewhere, because it does not carry itself. Logs show what happened. They never show what was deliberately not heard. So the list of deaf writes is written down next to the trigger, and it is the first thing I read when the summary does something I do not expect.

The result: The summary now rewrites within five minutes of a real change and stays quiet after its own. The audit stream shows recruiter edits and intake events, not the machine talking to itself.

  1. Write down every write the system makes before you wire the trigger. The trigger takes an afternoon. The list is the design.
  2. Make your own output silent by default. If the machine wrote it, the machine does not get to hear it, unless you can say why it should.
  3. Keep your output out of the hash. A hash that includes the summary says "changed" every time the machine runs.
  4. Bulk writes emit nothing. If a step creates a hundred items, it must not wake the step that creates items.
  5. Keep the deaf list where a person will read it. Silence leaves nothing in the logs, so it needs to leave something somewhere else.

The trigger took me an afternoon, and it would take you one too. The week went into sitting with a list of every write the machine makes and asking, for each one, whether the machine should be allowed to hear it. A system that listens to everything ends up mostly hearing itself.

If your agent writes to the same table it watches, how do you mark its own writes so it skips them?


Originally published at nabbilkhan.com.

Top comments (0)