Skip to content

feat(loop): an attempt that charges no retry budget can be repeated for ever - #471

Open
gHashTag wants to merge 2 commits into
feat/queen-supervisorfrom
loop/silent-loop-ceiling-v2
Open

gHashTag wants to merge 2 commits into
feat/queen-supervisorfrom
loop/silent-loop-ceiling-v2

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What this found

The round set out to prove sendBack is a dead end. The first measurement seemed to confirm it — 391 dispatches across 391 issues, every issue with exactly one row.

That measurement was worthless. queen_dispatch is written ON CONFLICT (issue) DO UPDATE, so it holds one row per issue by schema. Re-dispatch overwrites; it does not insert. Counting rows in a table keyed by the thing being counted answers nothing. The archive holds the real history: 605 attempts across 170 issues, 113 attempted more than once, one attempted thirteen times. Work is re-dispatched constantly.

The real defect is the opposite one

Not a loop that never runs — a loop that never stops. The deployed reviewer decides whether an attempt counts against the ceiling:

const countsAgainstTheIssue = !(failed.length === 0 && unjudged.length > 0)

An attempt whose unmet criteria are all unjudged does not spend the budget. The reasoning is sound and documented (browseros-ai#1420 FR-003): the bee went silent, not wrong, and counting silence was escalating work to a person that had never been assessed — the oldest after 91 hours. That fix was right.

What nobody measured is the other side. A bee silent once should not be charged; a bee silent every time now loops with no ceiling at all, because the counter that would end it never moves.

159 send-back issue(s) examined
6 of 159 spent retry budget - on these the ceiling is working
80 of 159 have been attempted 3+ times and charged NOTHING
   #1558  13 attempt(s), 0 charged, looping 26h
   #1665  11 attempt(s), 0 charged, looping 12h
   ... and 68 more
73 have been attempted once or twice - the rule is meant to forgive those

Eleven of those were reviewed within the last hour — that is the swarm's four slots, looping now.

Three corrections this cost, all mine

  • send_backs = 0 looked like a broken counter. It is the rule working. Checked against information_schema rather than assumed: the column is NOT NULL DEFAULT 0, so the zeros are real zeros and not an absence wearing a coalesce.
  • The first draft measured age from reviewed_at. clocks.mjs refused it and was right — the review sweep rewrites that field, so an issue looping for a day reads as "1h old" the moment it is re-reviewed. Age now comes from the archive's dispatched_at, written once per attempt.
  • The first draft called 51 issues "no readable history" when a successful LEFT JOIN with no partner is positive evidence of a first attempt. That is the reverse of this directory's usual mistake, and still a mistake: unknown must mean the query failed, not that it succeeded and said zero.

Keeping the branch that refutes it

An issue whose attempts did charge the ceiling is evidence against this tool's thesis, so counted is reported first and the summary says the thesis failed outright when that group is larger. On this board it is 6 of 159, so it holds.

Report-only

silent-loop.mjs changes no state and runs in the heal chain as reportsOnly. It does not decide what to do about the 80 — a ceiling for persistent silence is a design question for the operator.

Verification

  • node .trinity/loop/selftest.mjs254 passed, 0 failed (3 new cases, each proving the negative first)
  • tri silent-loop runs live against the board; exit 2 when it finds any
  • clocks.mjs and the rest of the audit chain pass
  • ASCII-only, English-only (L3)

🤖 Generated with Claude Code

…or ever

This round set out to prove that `sendBack` is a dead end - 158 issues sit in
it, and `lease.mjs` says plainly that nothing reopens a worker on a send-back.
The first measurement seemed to confirm it: 391 dispatches across 391 issues,
every issue with exactly one row, nothing ever dispatched twice.

That measurement was worthless. `queen_dispatch` is written with
`ON CONFLICT (issue) DO UPDATE`, so it holds one row per issue BY SCHEMA. A
re-dispatch overwrites; it does not insert. Counting rows in a table keyed by
the thing being counted answers nothing, and it answered nothing here. The
archive holds the real history: 605 attempts across 170 issues, 113 attempted
more than once, one attempted thirteen times. Work is re-dispatched constantly.

The real defect is the opposite one - not a loop that never runs, a loop that
never stops. The deployed reviewer decides whether an attempt counts against
the retry ceiling:

    const countsAgainstTheIssue = !(failed.length === 0 && unjudged.length > 0)

An attempt whose unmet criteria are ALL unjudged does not spend the budget, and
the reasoning is sound and documented (browseros-ai#1420 FR-003): the bee went silent
rather than wrong, and counting silence was escalating work to a person that
had never been assessed, the oldest after 91 hours. Fixing that was right. What
nobody measured is the other side of it: a bee silent ONCE should not be
charged, but a bee silent EVERY time now loops with no ceiling at all, because
the counter that would end it never moves. 80 of 159 send-back issues have been
attempted three or more times and charged nothing. browseros-ai#1558 has been attempted
thirteen times over twenty-six hours.

Three corrections this cost, all of them mine:

- `send_backs = 0` looked like a broken counter. It is not; it is the rule
  working. Checked against information_schema rather than assumed - the column
  is NOT NULL DEFAULT 0, so the zeros are real zeros and not an absence wearing
  a coalesce.
- The first draft measured age from `reviewed_at`. `clocks.mjs` refused it, and
  was right: the review sweep rewrites that field, so an issue looping for a
  day reads as "1h old" the moment it is re-reviewed. Age now comes from the
  archive's `dispatched_at`, written once per attempt and never updated.
- The first draft called 51 issues "no readable history" when a successful
  LEFT JOIN returning no partner is positive evidence of a first attempt. That
  is the reverse of this directory's usual mistake and still a mistake:
  `unknown` has to mean the query failed, not that it succeeded and said zero.

Keep the branch that refutes you: an issue whose attempts DID charge the
ceiling is evidence against this tool's thesis, so `counted` is reported first
and the summary says the thesis failed outright when that group is the larger
one. On this board it is 6 of 159, so it holds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`dash.mjs` measures the fact, but the box the operator actually reads is
rendered by `snapshot.mjs` from its own metric list, so the number was measured
into a file nobody opens.

Read the record, do not recompute it - the rule `provenCounts` already follows.
`silent-loop` asks the production database and takes about a minute; snapshot
runs on the tick path and must not grow a remote query. `dash.mjs --record`
measures it and the box shows what was last measured.

An absent reading renders as "not measured", never as zero, and the anchor is
not written for it - otherwise the next iteration would compare against a
fabricated value and show an equally fabricated recovery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant