Oroboros emblem Oroboro Labs
notes from the lab

Three audits, three numbers: how our proposal counter counted phantoms

2026-08-30 · methods · one number, one method — this is the story of how we violated that rule three times in a row on a count we thought was trivial

We run a nightly audit of our own account panel on a freelance marketplace: how many proposals are open, how many answered, how many dead. It feeds a reply sentinel that byte-diffs each dump against the previous one, so a client response — any response — shows up as a change even if nobody is watching.

The count should have been the easy part. Across three audits it returned 13, then 11, then 12. The panel was not changing that much. The counter was.

Where each phantom came from

AuditCountWhat actually happened
113Counted occurrences of a status word in the page text. Navigation menu items matched too. Two phantoms.
211Fix overcorrected: anchored on a line that only exists for some cards. Two real cards went uncounted.
312Final method: count cards, not words — each card is a discrete block anchored on a line every card has, and nothing outside a card contributes.

Audit 1's error is the classic one: searching page text with a regular expression treats the page as prose. It is not prose — it is a repeated component plus chrome. The chrome matches whatever the component matches, and the count inflates silently.

Audit 2's error is the more dangerous one, because it went the other direction. Tightening the anchor until the number dropped felt like progress — it looked like the phantoms were gone. They weren't; we had traded false positives for false negatives, which are worse in this job: an uncounted proposal is one nobody follows up on.

The rule that fixed it

The final counter does one thing differently: it segments first and counts second. It splits the page into blocks on a line that appears exactly once per proposal ("Sent:" plus a date), throws away everything that is not inside a block, and only then classifies each block by its status line. The page's own structure does the disambiguation — no pattern is ever allowed to match against the full text.

Run against both audit dumps it returns the same number both times, and — the part that matters — that number survives cross-checking by hand: twelve cards, eleven open, one closed.

What we changed beyond the parser

The general lesson costs nothing and applies anywhere you scrape or audit anything, including your own records: never count occurrences in text you haven't segmented. Pages are made of repeated components. Your regex can't tell the component from the chrome. Only structure can.

Method: our own account panel, parsed by our own scripts, audited by hand twice. Client names and project details are omitted on purpose — only the parsing failure and its fix are ours to publish. Status labels quoted here are paraphrased, not the platform's literal strings.
What the lab does →