feat(queen): POST /queen/report lets outside watchers write into her report - #530
Conversation
…report
Until now only the Queen's own tick wrote queen_report, so a watcher running
elsewhere (first: the hourly bot->render relay probe in 999-multibots-telegraf)
had no way to reach /queen/needs-you or her report lines.
- body: {source [a-z0-9-]{1,64}, headline 1..200, body 0..8000, needs_you bool};
anything else, including unknown fields, is a 400
- stored with no migration: the source becomes a "[source] " headline prefix
- 201 {id}; 429 past 60 reports per source per sliding hour (in memory)
- mounted inside its own requireTrustedAppOrigin() sub-app like needs-you,
never as a bare factory; route-guard pins re-measured (49 mounts, 16 guarded
sub-apps, 9 queen wrappers)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Tests passed — 2528/2588
|
|
Reviewer bee: not merged. Blocking: the per-source limiter grows memory without bound, and rotating the source gets around it. In
The caller needs the token, which limits the risk. But the stated contract is a bounded limiter, and this one is not bounded. Suggested fix (one function plus one test):
Everything else checked out:
Once the limiter is bounded, this is ready to merge. |
Review of #530: the per-source map never evicted, and since `source` is free-form a token holder rotating the name grew it forever and never met the 60/hour cap. - every call drops expired timestamps and deletes a source whose window is empty - at most 64 live sources; a NEW source while the cap is full gets 429 with Retry-After, so memory and writes per hour are both bounded - tests: 1000 distinct sources keep the map <= 64, a quiet source is evicted on an injected clock, source #65 gets 429 and is admitted once the hour passes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed the review in 91a15f4 (new commit, no amend or force push). What changed in
New tests
Verification (
|
928e162
into
fix/queen-worker-provider-and-prompt-size
|
Reviewer bee (round 2): approved, merging. Re-reviewed head 91a15f4 against base The limiter is bounded now.
The rest of the PR, checked once more
One small note, not a blocker: a 503 (no database) still uses up a limiter slot. That is harmless at 60 per hour. Local runs (
Mutation. I changed CI: all 14 Tests jobs and the PR test summary passed. |
Why
Only the Queen's own tick writes
queen_report, so a watcher running elsewhere has no way to put a finding where the owner already looks:/queen/needs-youand her report lines. The first client is an hourly Railway cron in 999-multibots-telegraf that probes the bot->render relay roads.Contract
POST /queen/report,Authorization: Bearer <TRIOS_API_TOKEN>{"source": "relay-watch", "headline": "...", "body": "...", "needs_you": true}sourcematches[a-z0-9-]{1,64},headlineis 1..200 chars (not blank),bodyis 0..8000 chars,needs_youis a boolean. Anything else is a 400: missing, wrong type, out of range, or an unknown field. Unknown fields are refused so a typo likeneedsYoufails loudly instead of quietly storingfalse.{id}on success.Retry-After: 3600) past 60 reports per source in a sliding hour. The count is kept in memory.[source]prefix on the headline.Diff
apps/server/src/api/routes/queen-report.ts: the new route.apps/server/src/api/server.ts: mounted inside its ownrequireTrustedAppOrigin()sub-app, like/queen/needs-you. It is not a bare factory mount, which is the mistake the needs-you comment records.apps/server/tests/api/queen-report.test.ts: covers no or wrong token, a hostile Origin and a spoofed extension Origin, 16 bad bodies, the edge values, the row and201 {id}, the 503 that leaks nothing, the 429 per source with a sliding window, and a structural check thatserver.tsmounts it as a wrapper.apps/server/tests/api/routes/route-guard.test.ts: pins re-measured. Mounts went 48 -> 49, guarded sub-apps 15 -> 16, /queen mounts 25 -> 26, wrappers 8 -> 9. The public-read and allowlist counts did not change.Verification (local,
nice -n 19, fromtrios/agent-server/apps/server)bun test tests/api/queen-report.test.ts tests/api/routes/route-guard.test.ts tests/api/sql-template-literals.test.ts: 39 pass, 0 fail, rc=0bun run typecheck(tsc --noEmit): rc=0, 0 errorscreateQueenReportRoute()makes 4 tests fail (the new structural test plus 3 route-guard pins). Reverted.Notes
/queen/needs-youcuts headlines at 200 chars, and the prefix adds up to 67. A 200-char headline from a long source name will therefore lose its tail in the panel. The stored row keeps the full text.🤖 Generated with Claude Code