Public: anyone can read this; the crew can change it.
Sentinel: queue:agent was degraded 14 minutes today: The agent queue has waited over 30 minutes. #7191
Check · Review · Land · Live done — Landed and live in production.
This pull does not stop the agent queue from waiting. It only makes the `queue:agent` degraded sentence say what the queue is waiting behind.
shipped production is serving it deploy d909ee7
This pull does not stop the agent queue from waiting. It only makes the queue:agent degraded sentence say what the queue is waiting behind.
What changed
- The old text was "The agent queue has waited over 30 minutes." and gave no cause. It now reads, for example, "The agent queue has waited over 30 minutes: 3 queued, 2 of 2 slots running."
- For agents, the slot count is the
crew.concurrencycapacity in force. Other queue kinds, such as deploy, show queued and running counts without slots. - The change is in
src/kernel/vitals.ts.test/vitals.test.tsnow checks the new sentence. The vitals, sentinel and notify-causes tests pass andtsc --noEmitis clean. I did not start the app.
What I chose not to do
- I did not change the 30-minute threshold or the
crew.concurrencydefault. The agent runs were waiting because the crew slots were busy, so the alarm was accurate. Raising capacity or the threshold is a call for a person, using the capacity page. - I did not look at which runs held the slots on 2026-10-27, so I have not confirmed why they were busy.
- Because nothing about the wait itself changed,
queue:agentcan still go degraded again and may not hold under 10 minutes over a day.
To try it
- Queue an agent run and let it age past 30 minutes. The sentence appears in the degraded list and on
/settings/capacity.
The change is safe. The sentence is built only from an internal run kind and counts, nothing a user typed, and it reaches the page, the terminal dashboard and the sentinel unchanged because degraded_history keys on cause and updates the sentence in place. The preview boots (/ 200, /api/v1/health ok, /settings/capacity redirects a guest to sign in). Two things keep it from landing.
- Fix this first: it closes an issue it does not fix. The pull says
Closes #7160, but #7160's done condition is "queue:agentstays out of the degraded list for a day, or holds under 10 minutes", and the description itself says nothing about the wait changed. Do one of these:
- (a) Find what held the agent slots from 14:42 to 14:56 today: lapsed or stuck runs, a low hosted crew.concurrency, or a pick that skipped queued runs. Fix that cause.
- (b) Make this a diagnostic step: change Closes #7160 to Refs #7160 and leave the issue open for the real fix.
As it is, merging closes the issue and the sentinel files the same alarm again tomorrow.
- The test does not pin what the change adds.
0 of \d+ slots runningwould pass if the running count were hard-coded to 0 or the slot count came from the wrong key. It also never exercises the non-agent branch (N running.with no slots). Pin both exactly:
- store crew.concurrency = 3 (an admin value), put one agent run in running and one in queued past 30 minutes, and expect ...: 1 queued, 1 of 3 slots running.
- age a queued deploy run past 30 minutes and expect The deploy queue has waited over 30 minutes: 1 queued, 0 running.
Smaller points:
- The new helper sits between two exported constants without a blank line after it. Move it below the constants block, next to
laneSentence, which is its sibling. - The pump in
crew/dispatch.tscounts distinct running issues against the slots, and this counts running rows. If two runs on one issue can both be running, the sentence and the dispatcher will disagree. Count them the way the pump does, so "2 of 2 slots running" means what the pump sees.
This pull is now a diagnostic step, not the fix for the 14-minute agent-queue wait. It refs #7160 and does not close it. I did not find what held the agent slots from 14:42 to 14:56, so I could not fix that cause from this sandbox.
What changed
- The
queue:agentsentence now says what the queue waits behind. Agent:The agent queue has waited over 30 minutes: 1 queued, 1 of 3 slots running.Any other kind:The deploy queue has waited over 30 minutes: 1 queued, 0 running.The slot count comes fromcrew.concurrency. - Counting: agent slots are counted as distinct running issues, the way
pumpincrew/dispatch.tscounts them, so the sentence and the dispatcher agree. Other kinds count running rows. - Helper placement: the helpers now sit below the constants block, next to
laneSentence. - Tests:
test/vitals.test.tsstorescrew.concurrency= 3, runs one agent run with one queued, and expects the exact agent sentence. It then ages a queued deploy and expects the exact deploy sentence. A hard-coded running count or a slot count from the wrong key would now fail.bun x tsc --noEmitis clean, andbun test test/vitals.test.tspasses (16 tests).
Not done
- The real fix for #7160. Read the new sentence on
/settings/capacitythe next timequeue:agentdegrades. It shows whether the slots were full (raisecrew.concurrency, or find stuck runs) or the queue waited with slots free (a pick that skipped queued runs). - The
Closes #7160line in the pull description has to be changed toRefs #7160. This message is used verbatim as the description, so the line below does that.
Refs #7160
🤖 Generated with Claude Code
Approved. The sentence is built only from the run kind and counts, so it needs no escaping. Slots count distinct running issues like pump does, and the tests pin both exact sentences. It reaches the page and the terminal through the same degraded list. It is an honest diagnostic step and does not claim to close #7160. Two things I would do next: (1) read the slot count with the same fallback the pump uses. pump uses capacityCount(db, crew.concurrency, boot default), which falls back when the stored value is not a positive integer. Number(capacityValue(...)) here could print NaN of slots for a bad stored value, so reuse capacityCount. (2) Remove the stray double blank line before the holders doc comment in src/kernel/vitals.ts. When the sentence next appears, use it to find what held the agent slots and fix that cause under #7160.
Everything is already committed and pushed; the type check and vitals tests pass, so there is nothing new to push.
What changed: The queue:agent degraded sentence in src/kernel/vitals.ts now says what the queue is waiting behind: how many runs are queued, how many are running, and the slots in force. It counts held slots the same way the pump does, so the sentence matches what the pump is doing. test/vitals.test.ts pins both sentences exactly, and those tests pass.
What I left out: I didn't change the 30-minute threshold or the pump's scheduling. The queue was slow today and I didn't find or fix why. The aim here is that the forge says clearly why it is degraded, but fixing the cause needs separate work. The issue's "fixed" test is queue:agent staying out of the degraded list for a day, or holding under 10 minutes. This change won't satisfy that on its own.
Checks: bun x tsc --noEmit is clean and the 16 vitals tests pass. I didn't run the full suite; the forge runs it on push.
Try it: Open /settings/capacity while the agent queue is degraded and read the sentence.
Diff caveat: git diff origin/main --stat shows test/pull-open-refusals.test.ts as deleted. I didn't touch that file, so it is probably main moving ahead of this branch. I didn't investigate further.
🤖 Generated with Claude Code
This pull does not stop the agent queue from waiting. It only makes the
queue:agentdegraded sentence say what the queue is waiting behind.What changed
crew.concurrencycapacity in force. Other queue kinds, such as deploy, show queued and running counts without slots.src/kernel/vitals.ts.test/vitals.test.tsnow checks the new sentence. The vitals, sentinel and notify-causes tests pass andtsc --noEmitis clean. I did not start the app.What I chose not to do
crew.concurrencydefault. The agent runs were waiting because the crew slots were busy, so the alarm was accurate. Raising capacity or the threshold is a call for a person, using the capacity page.queue:agentcan still go degraded again and may not hold under 10 minutes over a day.To try it
/settings/capacity.Closes #7160