Skip to content

feat(website): Queen HUD says why free bees are idle, round popover no longer crashes - #1030

Merged
gHashTag merged 3 commits into
mainfrom
feat/queen-idle-explained
Sep 15, 2026
Merged

gHashTag merged 3 commits into
mainfrom
feat/queen-idle-explained

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

Description

The owner looked at https://t27.ai/#/queen, saw "BEES 0/4" and asked why the bees were broken. They were not broken. The swarm was healthy and idle, and the scheduler had nothing to choose: 449 of 488 candidate issues had no ## Boundary section. The HUD already fetched that reason from /queen/status and did not show it.

  • The HUD says why free bees are idle. New pure readers in components/queenHud.ts (skipCounts, idleReason, idleLine) work from /queen/status alone and use the server clock.
    • A fresh round that refused with free slots: one line with the refusal and the largest skip reasons with their counts. Example: "4 idle: nothing to choose — checked: 488 · no ## Boundary: 449 · done but open: 27 · claimed: 12". Counts follow their labels, so no number has to agree with a word in Russian or English.
    • "nothing to choose" with missingBoundary > 0: a link to an issue written the way bees can take it.
    • Other refusals: the refusal text, verbatim.
    • A stale round: the line says the round is stale instead of explaining idleness.
    • No line at all when the round dispatched, there is no free slot, the tick is unreadable, the summary is malformed or does not add up, or the status is a copy kept after a failed fetch.
  • The BEES tile reads active and capacity from /queen/status workers, and its sub-line carries the short reason.
  • "N ready" becomes "N finished, no verdict" ("N без вердикта"), which is what dispatches.unreviewed counts.
  • Bug fixed along the way. Opening the round popover with the real wire skipSummary crashed the whole page with React error ci: bump docker/build-push-action from 5 to 7 #31. Each entry is {count, issues, more}, and it was rendered as a child. The popover now lists counts.
  • Layout.
    • Desktop: the line sits under the viewport head.
    • Phones up to 900 px: it is its own grid row, and the body gives up that height.
    • Landscape phones up to 500 px tall: it is hidden.
    • Embed and bare views: it is hidden.
    • It takes no tap from Queen controls.

Refs #1010

Related Issue

#1010. This is the follow-up to the Queen bees investigation (gHashTag/t27#3624 and the scheduler rules in gHashTag/BrowserOS trios/agent-server).

Changes Made

  • apps/website/src/components/queenHud.ts: skipCounts, idleReason, idleLine.
  • apps/website/src/pages/Queen.tsx: the BEES tile sub-line, the idle line, the popover counts, the COPY keys in en and ru.
  • apps/website/src/pages/Queen.css: idle line placement on desktop, portrait phone, landscape phone and collapsed rail.
  • apps/website/qa/queen-idle-reason-contract.mjs (71 checks, npm run check:queen-idle, in website-checks) with four real /queen/status snapshots in qa/fixtures/queen-status/; qa/queen-idle-layout-contract.mjs (26 checks, headless Chrome); qa/queen-honesty-contract.mjs updated for the new wording.

🔥 TOXIC VERDICT

What Works

  • Idle-reason contract. It fails before the change: the module has no idleLine export. After the change it passes 71 checks on four real snapshots, plus malformed, mismatched, stale and gated cases, with counts of 1, 21 and 3 in en and ru.
  • Layout contract. It passes 26 checks against the rebased build. The line does not overlap the head, it takes no taps from controls at 1440x900 or 390x844, and it is hidden on a landscape phone.
  • Before render: clicking the round tile with the real snapshot crashed the page (React ci: bump docker/build-push-action from 5 to 7 #31). After: the popover lists 12 claimed, 27 completed and 449 missing boundary, and there are no exceptions.
  • Adversarial review. An honesty lens and a layout lens found 10 issues, all real and all fixed:
    • skip lines counted as issues;
    • a reason drawn from stale data;
    • the phone row covering controls;
    • the landscape overlap;
    • wrong Russian plurals.

What Doesn't Work / Limits

  • On landscape phones up to 500 px tall the sentence is hidden, because no place under the spilling tools row was free.
  • The example link points to t27#3587, because /queen/status does not expose the configured repository.
  • queen-honesty-contract has one check that fails identically on the base, and queen-round-contract has one timing check that also fails on the base build. Neither is touched here.
  • The layout contract is not wired into CI, like the other Queen browser contracts.

Overall Assessment: APPROVE. Self-Score: 8/10.

🌳 TECH TREE Options

  1. Expose the configured repository in /queen/status so the example link comes from the wire. ★★★☆☆
  2. Filter the board by repository (158 trios cards shown under t27) in trios/agent-server. ★★★★☆
  3. Wire the Queen browser contracts into CI with a stubbed status. ★★★☆☆
{
  "version": 1,
  "head_sha": "f741b2f943e53f87756f58da0eec752e65a8f03a",
  "summary": "The Queen HUD now explains why free bees are idle from /queen/status, relabels the unreviewed count as finished without a verdict, and no longer crashes when the round popover opens with the real skip summary.",
  "changes": [
    "apps/website/src/components/queenHud.ts: skipCounts, idleReason and idleLine read the refusal, skip counts and staleness from /queen/status with the server clock.",
    "apps/website/src/pages/Queen.tsx: the BEES tile sub-line and an idle line with the reason and an example link, the round popover lists counts instead of rendering objects, and new en and ru copy.",
    "apps/website/src/pages/Queen.css: idle line placement for desktop, portrait phones, landscape phones and the collapsed rail without covering controls.",
    "apps/website/qa/queen-idle-reason-contract.mjs, qa/queen-idle-layout-contract.mjs, qa/fixtures/queen-status and qa/queen-honesty-contract.mjs: contracts over real status snapshots and layout."
  ],
  "tests": [
    {
      "command": "node --experimental-strip-types qa/queen-idle-reason-contract.mjs before the implementation",
      "status": "failed",
      "result": "Control, expected to fail: queenHud.ts provides no idleLine export.",
      "evidence": "Session scratchpad idle-shots/contract-before.log"
    },
    {
      "command": "npm run check:queen-idle; node qa/queen-idle-layout-contract.mjs --dist dist",
      "status": "passed",
      "result": "71 idle-reason checks over four real status snapshots and edge cases, and 26 layout checks at desktop, phone and landscape sizes, pass on the build rebased onto main.",
      "evidence": "Local runs in the feat/queen-idle-explained worktree after the rebase"
    },
    {
      "command": "Headless Chrome render of the base and the branch build with the real healthy-idle status stubbed, clicking the round tile",
      "status": "passed",
      "result": "The base build crashes with React error 31 when the round popover opens; the branch build lists the skip counts with no exceptions and shows the idle line unclipped in en and ru.",
      "evidence": "Session scratchpad idle-shots before and after screenshots"
    },
    {
      "command": "npm run typecheck:ratchet; eslint on changed files; check:tri; check:tri-identity; check:queen-languages; check:explorer-languages; npx vite build",
      "status": "passed",
      "result": "Type errors stay at the baseline and every contract and the production build pass.",
      "evidence": "Local runs in the feat/queen-idle-explained worktree after the rebase"
    }
  ],
  "limitations": [
    "On landscape phones 500 px tall or less the idle sentence is hidden.",
    "The example link is a fixed format example, because /queen/status does not expose the configured repository.",
    "The layout contract is not wired into continuous integration."
  ],
  "tags": ["Website", "Queen", "Honesty"],
  "blog": {
    "title": "Zero bees is not broken bees",
    "summary": "A HUD that showed 0 of 4 bees made the swarm look broken, and now it says why the bees are idle, using the scheduler's own counts.",
    "outline": [
      "The question: the owner saw 0 of 4 bees on the Queen and asked why they were broken.",
      "The answer on the wire: a healthy idle swarm whose scheduler found nothing to choose, because 449 of 488 issues lacked a Boundary section.",
      "The change: the HUD reads the refusal and skip counts and prints them, with an example of an issue bees can take.",
      "The honesty rules: no reason from stale data, counts that must add up, and labels that never need grammatical agreement.",
      "The bug found on the way: the round popover crashed the page on the real skip summary."
    ]
  }
}

🤖 Generated with Claude Code

gHashTag and others added 3 commits September 16, 2026 01:24
On 2026-09-15 the owner read "BEES 0/4" on #/queen as broken bees. The
swarm was healthy_idle: the last round refused with "nothing to choose",
449 of 488 candidate issues had no ## Boundary, 12 were claimed and 27
were accepted but still open. /queen/status already carried all of it;
the HUD did not say it.

- queenHud.ts: idleReason() reads /queen/status alone (free slots from
  workers, the tick, the scheduler interval) and idleLine() words it.
  Free slots plus a refused round: the refusal, and for "nothing to
  choose" the three largest skip reasons with their wire counts. A tick
  older than two intervals (the server's TICK_STALENESS_INTERVALS) is
  called stale instead. A round that dispatched, no free slot, or an
  unreadable tick gives no line. A missing or malformed skipSummary gives
  the refusal alone, never a fabricated zero.
- Queen.tsx: the BEES tile's sub-line carries the short head; a line under
  the map's head carries the full sentence and, for "nothing to choose",
  a link to gHashTag/t27#3587 as an example of an issue bees can take.
  en and ru copy; other refusals print the wire's own text.
- The round popover crashed the page (React #31) on the real wire: each
  skipSummary entry is {count, issues, more}, and the list rendered the
  object. It now lists counts read through skipCounts().
- "3 ready" is dispatches.unreviewed: finished dispatch rows with no
  verdict, not issues ready to take. It reads "finished, no verdict";
  the Russian copy says "no verdict", since the longer form was clipped
  in the 1440 px tile.
- qa/queen-idle-reason-contract.mjs (check:queen-idle, wired into
  website-checks.yml): 51 checks over four real snapshots in
  qa/fixtures/queen-status and malformed variants. It failed before the
  change (missing export) and fails on five mutants (stale boundary,
  zero reasons, counts as text, the old ready word, the old popover).
  queen-honesty-contract.mjs follows the renamed copy key.

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

Review of the Queen idle line (f64af31b01) found its words claimed more
than /queen/status carries.

- skippedCount counts skip LINES. queend (main.swift, choose) files
  "delegatable but ..." and goes on judging the same issue, which in a
  round that chose nothing always files " held by " next. The line listed
  incompleteSpec as a cause and called skippedCount "issues": a refused
  round of 469 issues, 30 of them bounded but incomplete and held, read
  "30 incomplete spec" of "499 issues". incompleteSpec is now left out of
  the reasons and subtracted from "checked"; more incompleteSpec than
  fileConflict lines contradicts the decider and prints the refusal alone.
- skipCounts() returns null when the counts do not sum to skippedCount
  (the server's guarantee) and files keys outside the server's closed
  category set under "other" instead of making words from their names.
- Counts follow labels ("no ## Boundary: 449", "zanyaty: 1"), so no count
  has to agree with a word: "1 issues", "iz 21 zadach" and "1 zanyaty"
  were wrong for counts ending in 1 and 2 to 4.
- Another refusal keeps the wire's text in the line and title; the BEES
  tile head says "round refused", so its two-line clamp cuts nothing.
- The idle reason is read only when state.kind is "ready". After a failed
  fetch the hook keeps the last data, and a kept round aged into "round
  stale", blaming the scheduler for a page that could not see the server.
- The BEES tile reads bees, slots and free slots from /queen/status's
  workers alone; it mixed dispatches.running and research's capacity with
  status's free slots ("0/10" over "4 idle"). The old reading stays when
  the status carries no workers.
- The link to t27#3587 is labelled as a format ("an issue written the way
  bees can take it") and shows only when some issue lacks a ## Boundary;
  #3587 itself was claimed in the snapshot that shows it.

qa/queen-idle-reason-contract.mjs: 71 checks, including a round built
from queend's own sentences filed as the server files them, counts 1, 21
and 3 in both languages, and source checks for the live gate and the
tile. HEAD's reader and page fail 40 of them; eleven single-fix mutants
each fail at least one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured with trusted input on the build served as https://t27.ai with
the healthy-idle snapshot as /queen/status:

- 390x844: the line was relative over the top of the body's row, so it
  covered TRI's screen tabs and the first disclosure of the specs, skills
  and crons views; a tap on TRI's Agent tab did nothing, and one on the
  specs directive opened the line's link. At 900 px and below (portrait,
  or taller than 500 px) the line is now a grid row of its own between the
  head and the body, and the body gives up its height.
- 844x390: the head's tools spill below its 40 px band, and the line lay
  over all of them and the identity chip. On a landscape phone (height
  500 px or less) the line is not drawn; the BEES tile's sub-line is
  hidden at that size too, so it says what it said before the line.
- The collapsed rail's left: 84px outranked the phone rules and pushed the
  line out of a 390 px window after collapsing at 1440; it is desktop only.
- Desktop: the line's top was the head's height guessed on 127.0.0.1
  (40 px). On t27.ai the head is 42 px, and the line covered its bottom
  rule. It now starts at 42 px.

qa/queen-idle-layout-contract.mjs (check:queen-idle-layout, browser, not
in CI like the other Queen browser contracts): the line against the head
at 1440x900 and 1280x720 in en and ru; inside a 390 px window after a
collapse; between head and body on one screen at 390x844 with TRI's tabs
and the specs disclosure topmost and working under trusted taps; no line
and every map tool topmost and working at 844x390; no line once
/queen/status fails. It fails on the previous build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag
gHashTag merged commit fe4d5ed into main Sep 15, 2026
28 of 35 checks passed
github-actions Bot added a commit that referenced this pull request Sep 15, 2026
feat(website): Queen HUD says why free bees are idle, round popover no longer crashes (#1030)

* feat(website): Queen HUD says why free bees are idle

On 2026-09-15 the owner read "BEES 0/4" on #/queen as broken bees. The
swarm was healthy_idle: the last round refused with "nothing to choose",
449 of 488 candidate issues had no ## Boundary, 12 were claimed and 27
were accepted but still open. /queen/status already carried all of it;
the HUD did not say it.

- queenHud.ts: idleReason() reads /queen/status alone (free slots from
  workers, the tick, the scheduler interval) and idleLine() words it.
  Free slots plus a refused round: the refusal, and for "nothing to
  choose" the three largest skip reasons with their wire counts. A tick
  older than two intervals (the server's TICK_STALENESS_INTERVALS) is
  called stale instead. A round that dispatched, no free slot, or an
  unreadable tick gives no line. A missing or malformed skipSummary gives
  the refusal alone, never a fabricated zero.
- Queen.tsx: the BEES tile's sub-line carries the short head; a line under
  the map's head carries the full sentence and, for "nothing to choose",
  a link to gHashTag/t27#3587 as an example of an issue bees can take.
  en and ru copy; other refusals print the wire's own text.
- The round popover crashed the page (React #31) on the real wire: each
  skipSummary entry is {count, issues, more}, and the list rendered the
  object. It now lists counts read through skipCounts().
- "3 ready" is dispatches.unreviewed: finished dispatch rows with no
  verdict, not issues ready to take. It reads "finished, no verdict";
  the Russian copy says "no verdict", since the longer form was clipped
  in the 1440 px tile.
- qa/queen-idle-reason-contract.mjs (check:queen-idle, wired into
  website-checks.yml): 51 checks over four real snapshots in
  qa/fixtures/queen-status and malformed variants. It failed before the
  change (missing export) and fails on five mutants (stale boundary,
  zero reasons, counts as text, the old ready word, the old popover).
  queen-honesty-contract.mjs follows the renamed copy key.

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

* fix(website): the idle line counts issues, not skip lines, and only when live

Review of the Queen idle line (f64af31b01) found its words claimed more
than /queen/status carries.

- skippedCount counts skip LINES. queend (main.swift, choose) files
  "delegatable but ..." and goes on judging the same issue, which in a
  round that chose nothing always files " held by " next. The line listed
  incompleteSpec as a cause and called skippedCount "issues": a refused
  round of 469 issues, 30 of them bounded but incomplete and held, read
  "30 incomplete spec" of "499 issues". incompleteSpec is now left out of
  the reasons and subtracted from "checked"; more incompleteSpec than
  fileConflict lines contradicts the decider and prints the refusal alone.
- skipCounts() returns null when the counts do not sum to skippedCount
  (the server's guarantee) and files keys outside the server's closed
  category set under "other" instead of making words from their names.
- Counts follow labels ("no ## Boundary: 449", "zanyaty: 1"), so no count
  has to agree with a word: "1 issues", "iz 21 zadach" and "1 zanyaty"
  were wrong for counts ending in 1 and 2 to 4.
- Another refusal keeps the wire's text in the line and title; the BEES
  tile head says "round refused", so its two-line clamp cuts nothing.
- The idle reason is read only when state.kind is "ready". After a failed
  fetch the hook keeps the last data, and a kept round aged into "round
  stale", blaming the scheduler for a page that could not see the server.
- The BEES tile reads bees, slots and free slots from /queen/status's
  workers alone; it mixed dispatches.running and research's capacity with
  status's free slots ("0/10" over "4 idle"). The old reading stays when
  the status carries no workers.
- The link to t27#3587 is labelled as a format ("an issue written the way
  bees can take it") and shows only when some issue lacks a ## Boundary;
  #3587 itself was claimed in the snapshot that shows it.

qa/queen-idle-reason-contract.mjs: 71 checks, including a round built
from queend's own sentences filed as the server files them, counts 1, 21
and 3 in both languages, and source checks for the live gate and the
tile. HEAD's reader and page fail 40 of them; eleven single-fix mutants
each fail at least one.

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

* fix(website): the idle line takes no tap from the Queen's controls

Measured with trusted input on the build served as https://t27.ai with
the healthy-idle snapshot as /queen/status:

- 390x844: the line was relative over the top of the body's row, so it
  covered TRI's screen tabs and the first disclosure of the specs, skills
  and crons views; a tap on TRI's Agent tab did nothing, and one on the
  specs directive opened the line's link. At 900 px and below (portrait,
  or taller than 500 px) the line is now a grid row of its own between the
  head and the body, and the body gives up its height.
- 844x390: the head's tools spill below its 40 px band, and the line lay
  over all of them and the identity chip. On a landscape phone (height
  500 px or less) the line is not drawn; the BEES tile's sub-line is
  hidden at that size too, so it says what it said before the line.
- The collapsed rail's left: 84px outranked the phone rules and pushed the
  line out of a 390 px window after collapsing at 1440; it is desktop only.
- Desktop: the line's top was the head's height guessed on 127.0.0.1
  (40 px). On t27.ai the head is 42 px, and the line covered its bottom
  rule. It now starts at 42 px.

qa/queen-idle-layout-contract.mjs (check:queen-idle-layout, browser, not
in CI like the other Queen browser contracts): the line against the head
at 1440x900 and 1280x720 in en and ru; inside a 390 px window after a
collapse; between head and body on one screen at 390x844 with TRI's tabs
and the specs disclosure topmost and working under trusted taps; no line
and every map tool topmost and working at 844x390; no line once
/queen/status fails. It fails on the previous build.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@gHashTag

Copy link
Copy Markdown
Owner Author

Verified live on https://t27.ai after the publisher run ("Publish assets/index-BzlYtmgd.js from trinity fe4d5ed"; merge tree equals the reviewed branch tree, 672405a2).

Round popover. The probe is headless Chrome with a trusted CDP click on the round tile. The button is display: contents, so the probe clicks its visible child after a hit test.

build click result
before, index-xFghQn6i.js round tile the whole page blank, Minified React error #31 … object with keys {count, issues, more}
after, index-BzlYtmgd.js, status loaded round tile aria-expanded="true", popover open with the live counts 89 claimed, 70 completed, 238 missing boundary, 9 file conflict, 41 incomplete spec, 32 not first, page alive, 0 exceptions
after, clicked before status loaded round tile page alive, 0 exceptions (popover empty-state timing only)

HUD on the live swarm. The swarm is working, 10 of 10 busy:

  • the BEES tile reads "BEES 10/10 · 0 idle";
  • VERDICTS reads "… 4 finished, no verdict";
  • no idle line is shown, which is correct, because the last round dispatched.

Probes on the same bundle, trusted input: TRI tab 21 of 21, Queen tabs 61 of 61.

CI note. The red checks job is the pre-existing "Explorer viewport contract" at 390x844 specs, which fails identically on main since eab253b. The new "Queen idle reason" step passed.

Not verified live: the idle line itself, because the swarm is not idle right now. It is covered by the contract on four real status snapshots and by the layout contract.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant