Skip to content

feat(loop): publish the loop instruments, and retire escalations whose cause was fixed - #111

Merged
gHashTag merged 2 commits into
feat/queen-supervisorfrom
feat/loop-stale-escalations
Sep 4, 2026
Merged

gHashTag merged 2 commits into
feat/queen-supervisorfrom
feat/loop-stale-escalations

Conversation

@gHashTag

@gHashTag gHashTag commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Two things, and the second is why the first stopped being optional.

An escalation raised by a defect that was fixed four hours later

browseros-ai#1216, browseros-ai#1240 and browseros-ai#1244 sat 91 hours waiting on a person, each with the reason "the task has no acceptance criteria, so there is nothing to judge it against."

All three visibly state four. browseros-ai#1216 has an English ## Success Criteria section ending in a grep that must exit 0. browseros-ai#1240 and browseros-ai#1244 use ## Готово, когда.

The cause was QueenSpecQuality.criteriaHeadings knowing four headings and none of theirs. It was fixed the same day in edbc05e11, whose title is "fix: the bees were judged against criteria nobody ever gave them" and whose own comment says a perfectly written spec yielded zero criteria. Compile today's parser, feed it today's bodies:

1244.md  source=stated  items=4
1240.md  source=stated  items=4
1216.md  source=stated  items=4

Every dispatch since 2026-09-01 records stated. The three none rows in the entire table are exactly the three escalations. Nothing ever re-examined them.

This is not the wait valve wearing a new hat

sendBack and wait are released by a clock because their input can never change. An escalation names a cause, and a cause is a claim about the world that can be measured again. This re-measures. Three of the six escalations are "returned twice already, criteria still unmet" — a fact about a conversation, not about the issue text — and it leaves every one of them alone, at any age.

Two gates, and the near miss that built the second

The first working run called browseros-ai#1244 stale and would have returned it to the pool. Its recorded reason is indeed void. But its body carries a section headed ## Почему жду слова — why I am waiting for your word — explaining that the change rewrites the main window's tabs and that the bee will not do that silently while the operator sleeps.

The escalation was right for a reason the review never recorded. Retiring it on the recorded reason would have overruled a deliberate request with a database column — precisely the failure this tool exists to stop. A body that asks for a person is never auto-released, whatever the review recorded. Released 2, not 3.

It calls the shipping Swift parser, rebuilt whenever the source is newer than the binary. A JavaScript copy would agree until someone edited one, which is this repository's most repeated defect.

The instruments themselves

git status showed all twenty-one as untracked. They have existed on one laptop — the same defect this loop keeps finding in the swarm it supervises: work that exists and cannot be seen.

heal.mjs eleven steps in a fixed order; four act, seven read
loop.mjs state, the age-based lock, the append-only ledger, the dashboard
reap lease free the volume, then the path fences
push-work close-done make finished work visible, then clear it from the pool
stale-escalations the above
clocks fields two defect classes that each cost an outage, checked every round
fp-check coverage the checkers, run against material the world calls good
verdict-audit what the swarm claims, against what it actually pushed
judge-packet assemble the unauditable for a judge; assembles, never judges
brief-gate author refuse an unworkable brief; file from a measured deficit
snapshot trend the readings, and whether a series has enough points to slope
selftest 43 cases, every one proving the negative first

A disagreement the new cases exposed

lockHolder() returned any lock file whatever its age, while acquire() was already entitled to reclaim one past the 45-minute window. So heal.mjs could name a run that finished 45 minutes earlier as the reason it stood down. Worse, the lock test — on finding that disagreement — took the live lock and kept it, rewriting the holder to selftest. A check that damages the thing it checks is worse than no check. Both fixed.

Verification

  • node selftest.mjs → 43 passed, 0 failed, no network
  • node heal.mjs → all eleven steps ok; 20 known-good inputs, 0 falsely accused; 43 claims supported by the diff, none unsupported
  • No credential in any file: every database reference is process.env.DATABASE_URL, read inside the container
  • Runtime gitignored — state.json, ledger.jsonl, the lock, packets/, state/, rendered dashboards

These have existed only on one laptop. `git status` on the supervisor branch
showed every one of them as untracked, which is the same defect this loop keeps
finding in the swarm it supervises: work that exists and cannot be seen. 63 bee
branches and 1321 files once sat in a container with 2 pushed.

Twenty-one files, no dependencies, no network in the calibration run:

  heal.mjs             eleven steps in a fixed order; four act, seven read
  loop.mjs             state, the age-based lock, the append-only ledger, the dashboard
  reap lease           free the volume, then the path fences
  push-work close-done make finished work visible, then clear it from the pool
  stale-escalations    retire an escalation whose stated cause no longer reproduces
  clocks fields        two defect classes that each cost an outage, checked every round
  fp-check coverage    the checkers, run against material the WORLD calls good
  verdict-audit        what the swarm claims, against what it actually pushed
  judge-packet         assemble the unauditable for a judge; assembles, never judges
  brief-gate author    refuse an unworkable brief; file from a measured deficit
  snapshot trend       the readings, and whether a series has enough points to slope
  selftest             43 cases, every one proving the negative first

Three rules are written into them and into the README:

  A checker never shown FAILING has not been tested. Six false accusations
  shipped in one night while synthetic fixtures agreed with the checkers that
  wrote them.

  Unreadable is not clean. A step that cannot reach its evidence reports ??,
  never ok - which is the only reason the first stale-escalation run did not
  retire six escalations on a probe that had failed to compile.

  A clock may only release what a clock can settle.

Runtime is gitignored: state.json, ledger.jsonl, the lock, packets/, state/ and
the rendered dashboards are produced by a run and mean nothing off the machine
that produced them. No credential appears in any file; every database reference
is process.env.DATABASE_URL, read inside the container.
The swarm sat at zero bees of four while the tick refused with "nothing to
choose" against 22 candidates and reported `claimed: 11`.

stateOfDispatch had no case for 'failed'. It fell through to the bottom and
returned 'awaitingReview', which QueenDelegationPolicy.claimOnIssue counts as a
LIVE claim. So a dispatch set to 'failed' - the state the policy's own comment
calls the one that most obviously means 'do this again' - held its issue instead
of releasing it.

The function could already PRODUCE that state two lines below, for a send-back
that outlived its idle floor, and did not recognise what it produced.

Measured 2026-09-04: five dispatches were set to 'failed' to return their issues
to the pool (browseros-ai#1133, browseros-ai#1175, browseros-ai#1216, browseros-ai#1240, browseros-ai#1311). All five stayed in 'claimed'
across four consecutive rounds. Every worker slot idle, fourteen real deficits
the author could not file against because each already had an issue - one of the
held ones. Work existed and was locked by the row recording its release.

'cancelled' is included for the same reason: it is a dead state and the registry
treats it as free.

Six new assertions in send-back-lease.test.ts, including the three that must NOT
be swept up by it (accept, sendBack, and an unfinished dispatch), and one
proving the case ignores the lease entirely - a failure is a failure at zero
idle and above the ceiling, because nothing about it waits for a clock.

173 pass, 0 fail across the queen suite.
@gHashTag
gHashTag merged commit 63de496 into feat/queen-supervisor Sep 4, 2026
11 of 14 checks passed
@gHashTag
gHashTag deleted the feat/loop-stale-escalations branch September 4, 2026 09:35
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

❌ Tests failed — 5/1582 failed

Suite Passed Failed Skipped
✅ agent 80/80 0 0
✅ build 9/9 0 0
✅ cdp-protocol 5/5 0 0
✅ eval 93/93 0 0
✅ server-agent 262/262 0 0
❌ server-api 475/501 2 24
✅ server-browser 4/4 0 0
✅ server-integration 10/11 0 1
✅ server-lib 271/271 0 0
✅ server-root 64/64 0 0
✅ server-skills 31/31 0 0
❌ server-tools 234/237 3 0
✅ shared 14/14 0 0
Failed tests
  • server-api — the migration block, applied to a real PostgreSQL > creates every object it promises, and survives a second boot
  • server-api — the live gate > ran, or its absence is on the record
  • server-tools — navigation tools > new_hidden_page opens a hidden tab
  • server-tools — navigation tools > show_page restores a hidden page to visible
  • server-tools — window tools > create_hidden_window creates and closes a hidden window

View workflow run

@gHashTag
gHashTag restored the feat/loop-stale-escalations branch September 28, 2026 11:04
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