Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"check:queen-construction": "node qa/queen-construction-game-contract.mjs",
"check:queen-hardware": "node qa/queen-hardware-foundry-contract.mjs",
"check:queen-review": "node qa/queen-review-lifecycle-contract.mjs",
"check:queen-viewport": "node qa/queen-viewport-contract.mjs"
"check:queen-viewport": "node qa/queen-viewport-contract.mjs",
"check:queen-honesty": "node --experimental-strip-types qa/queen-honesty-contract.mjs"
},
"dependencies": {
"@monaco-editor/react": "^4.7.0",
Expand Down
37 changes: 37 additions & 0 deletions apps/website/qa/queen-honesty-contract.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Queen HUD honesty contract - executable, not a regex. Runs the pure
// functions the round line and the A2A copy depend on, then checks the two
// source facts a regex CAN state. Node 22 needs --experimental-strip-types
// to import the TypeScript module; package.json passes it.
import { readFileSync } from "node:fs";
import { decisionDetail, rewriteEndpoints } from "../src/components/queenHud.ts";

const fails = [];
const check = (cond, msg) => { if (!cond) fails.push(msg); };
const L = { queueMeaning: "QUEUE-MEANING", executing: "executing now" };

check(decisionDetail({ allowed: true, refusal: null }, 4, 1386, L) === "4 executing now · #1386", "allow: running + latest");
check(decisionDetail({ allowed: true, refusal: null }, null, null, L) === "— executing now", "allow with no status: dash, no latest");
check(decisionDetail({ allowed: true, refusal: "stale text" }, 2, 7, L) === "2 executing now · #7", "allow never prints a refusal");
check(decisionDetail({ allowed: false, refusal: "nothing to choose" }, 0, 1, L) === "nothing to choose", "refusal text wins");
check(decisionDetail({ allowed: false, refusal: null }, 0, 1, L) === "QUEUE-MEANING", "refusal fallback only on a refusal");

const rewritten = rewriteEndpoints(
{ research: "https://api.t27.ai/queen/public-research", board: "https://api.t27.ai/queen/public-board?x=1", odd: "not a url" },
"https://trios-agent-server-production.up.railway.app/",
);
check(rewritten.research === "https://trios-agent-server-production.up.railway.app/queen/public-research", "origin swapped, path kept");
check(rewritten.board === "https://trios-agent-server-production.up.railway.app/queen/public-board?x=1", "query kept");
check(rewritten.odd === "not a url", "a non-URL is left alone");

const src = readFileSync(new URL("../src/pages/Queen.tsx", import.meta.url), "utf8");
check(/rounds:\s*null,/.test(src), "rounds / 24h is a dash (pulse.rounds is a lease-row count)");
check(!/c\.hudAccepted\b/.test(src), "no tile is labelled ACCEPTED");
check(/stat-verdicts[\s\S]{0,260}c\.hud24h/.test(src) && /beesStarted:\s*"Bees started \/ 24h"/.test(src), "24h figures name their window");
check(/summary\.online\}\/\$\{hardware\.summary\.total\}/.test(src), "FOUNDRY tile is online/total");
check(/rewriteEndpoints\(bootstrap\.endpoints, QUEEN_API\)/.test(src), "the copied bootstrap names the page's own origin");

// self-test: the contract must be able to fail
check(decisionDetail({ allowed: false, refusal: null }, 0, 1, L) !== "0 executing now", "self-test");

if (fails.length) { for (const f of fails) console.log(" ✗ " + f); console.log(`Queen honesty contract: FAIL (${fails.length})`); process.exit(1); }
console.log("Queen honesty contract: PASS (13 checks)");
42 changes: 42 additions & 0 deletions apps/website/src/components/queenHud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,45 @@ export function latestEventFor(events: HudEvent[], issue: number): HudEvent | nu
}
return best;
}

/**
* The detail under the round clock. On ALLOW the refusal is by definition
* absent, so say what the round did - how many Bees run and the latest
* dispatch - instead of printing the "no eligible specification" fallback
* that only means anything on a refusal. Measured 2026-09-04 01:39Z: an
* allowed tick (4 dispatched) rendered the refusal explanation.
*/
export function decisionDetail(
decision: { allowed: boolean; refusal: string | null },
running: number | null,
latestIssue: number | null,
labels: { queueMeaning: string; executing: string },
): string {
if (decision.allowed) {
const tail = latestIssue !== null ? ` · #${latestIssue}` : "";
return `${running ?? "—"} ${labels.executing}${tail}`;
}
return decision.refusal ?? labels.queueMeaning;
}

/**
* The A2A bootstrap names its endpoints on api.t27.ai, an origin that fails
* TLS and 404s (measured 2026-09-04). The page itself reads from QUEEN_API,
* so the copy hands out that origin with each endpoint's own path and query.
*/
export function rewriteEndpoints(
endpoints: Record<string, string>,
origin: string,
): Record<string, string> {
const base = origin.replace(/\/+$/, "");
const out: Record<string, string> = {};
for (const [key, value] of Object.entries(endpoints)) {
try {
const url = new URL(value);
out[key] = `${base}${url.pathname}${url.search}`;
} catch {
out[key] = value;
}
}
return out;
}
45 changes: 32 additions & 13 deletions apps/website/src/pages/Queen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
ALERT_WINDOW_MS,
HUD_VIEWS,
alertCount,
decisionDetail,
rewriteEndpoints,
isAlertKind,
latestEventFor,
sectorRows,
Expand Down Expand Up @@ -286,7 +288,7 @@ const COPY = {
"The routes show Queen lifecycle movement; only the Technology Tree below claims prerequisite links.",
sector: "sector",
rounds: "rounds / 24h",
beesStarted: "Bees started",
beesStarted: "Bees started / 24h",
verdicts: "verdicts",
empty: "Nothing here",
criteria: "criteria",
Expand Down Expand Up @@ -349,8 +351,9 @@ const COPY = {
// ---- single-screen HUD ----
hudBrand: "TRINITY QUEEN",
hudBees: "BEES",
hudAccepted: "ACCEPTED",
hudDone: "DONE",
hudVerdicts: "VERDICTS",
hud24h: "24h",
hudResearch: "RESEARCH",
hudFoundry: "FOUNDRY",
hudNextRound: "NEXT ROUND",
Expand Down Expand Up @@ -503,7 +506,7 @@ const COPY = {
"Маршруты показывают движение по циклу Queen; реальные зависимости есть только в Дереве технологий ниже.",
sector: "сектор",
rounds: "циклов / 24ч",
beesStarted: "Bees запущено",
beesStarted: "Bees запущено / 24ч",
verdicts: "вердиктов",
empty: "Здесь пусто",
criteria: "критерия",
Expand Down Expand Up @@ -568,8 +571,9 @@ const COPY = {
// ---- single-screen HUD ----
hudBrand: "TRINITY QUEEN",
hudBees: "ПЧЁЛЫ",
hudAccepted: "ПРИНЯТО",
hudDone: "ГОТОВО",
hudVerdicts: "ВЕРДИКТЫ",
hud24h: "24ч",
hudResearch: "ИССЛЕДОВАНИЯ",
hudFoundry: "ВЕРФЬ",
hudNextRound: "СЛЕДУЮЩИЙ ЦИКЛ",
Expand Down Expand Up @@ -1038,7 +1042,11 @@ function agentBootstrapText(
"# TRINITY RESEARCH · A2A BOOTSTRAP",
mission,
"",
JSON.stringify(bootstrap, null, 2),
JSON.stringify(
{ ...bootstrap, endpoints: rewriteEndpoints(bootstrap.endpoints, QUEEN_API) },
null,
2,
),
].join("\n");
}

Expand Down Expand Up @@ -1678,6 +1686,11 @@ export default function Queen() {
const reviewQueueCounts = reviewCounts(board);
const reviewColumnTitle =
boardColumns.find((column) => column.key === "review")?.title ?? "review";
// The tile is named by the column the number comes from - the wire's own
// title - not by a word ("accepted") no endpoint carries.
const doneColumnTitle = (
boardColumns.find((column) => column.key === "done")?.title ?? c.hudDone
).toUpperCase();
const roundSeconds =
board?.pulse.roundSeconds ?? data?.scheduler.intervalSeconds ?? 0;
const lastRoundAt = board?.pulse.lastRoundAt ?? decision?.decidedAt ?? null;
Expand Down Expand Up @@ -1739,8 +1752,12 @@ export default function Queen() {
pickedCard && repo
? `https://github.com/${repo}/issues/${pickedCard.number}`
: null;
// On ALLOW the detail is what the round did; on a refusal it is the refusal.
const decisionInfo = decision
? decisionDetail(decision, data?.dispatches.running ?? null, latest?.issue ?? null, c)
: null;
const decisionLine = decision
? `${decision.allowed ? c.chose : c.stoodDown} · ${decision.refusal ?? c.queueMeaning}`
? `${decision.allowed ? c.chose : c.stoodDown} · ${decisionInfo}`
: c.noDecision;
const heldCount = doneCount + runningCards.length;
const device = hardware?.devices[0] ?? null;
Expand Down Expand Up @@ -1958,7 +1975,7 @@ export default function Queen() {
</strong>
{decision && (
<p>
{decision.refusal ?? c.queueMeaning} · {decision.skippedCount}{" "}
{decisionInfo} · {decision.skippedCount}{" "}
{c.reasons}.
</p>
)}
Expand Down Expand Up @@ -2007,7 +2024,7 @@ export default function Queen() {

<div className="queen27-hud-res">
<i aria-hidden="true">✓</i>
<small>{c.hudAccepted}</small>
<small>{doneColumnTitle}</small>
<strong id="stat-accepted">{board ? doneCount : "—"}</strong>
<span>
+{pulse?.bees ?? "—"} {c.beesStarted}
Expand All @@ -2019,7 +2036,7 @@ export default function Queen() {
<small>{c.hudVerdicts}</small>
<strong id="stat-verdicts">{pulse?.verdicts ?? "—"}</strong>
<span>
{board ? `${reviewCards.length} ${reviewColumnTitle}` : "—"}
{c.hud24h} · {board ? `${reviewCards.length} ${reviewColumnTitle}` : "—"}
</span>
</div>

Expand All @@ -2043,12 +2060,12 @@ export default function Queen() {
<small>{c.hudFoundry}</small>
<strong id="stat-foundry">
{hardware
? `${hardware.summary.programmed}/${hardware.summary.total}`
? `${hardware.summary.online}/${hardware.summary.total}`
: "—"}
</strong>
<span title={hardware ? hardware.keyId : hardwareState.error ?? undefined}>
{hardware
? hardware.keyId
? `${hardware.summary.programmed} ${c.foundryProgrammed} · ${hardware.keyId}`
: hardwareState.error
? c.foundryUnavailable
: c.checking}
Expand Down Expand Up @@ -2359,7 +2376,9 @@ export default function Queen() {
verdicts: pulse?.verdicts ?? null,
running: data?.dispatches.running ?? null,
capacity: workers?.capacity ?? null,
rounds: pulse?.rounds ?? null,
// pulse.rounds counts the rows of a one-row lease table (0 or 1); a
// dash until the server counts real rounds (backlog P3-2).
rounds: null,
beesStarted: pulse?.bees ?? null,
}}
describe={describe}
Expand All @@ -2375,7 +2394,7 @@ export default function Queen() {
backend: c.hudBackend,
live: c.hudLive,
offline: c.hudOffline,
accepted: c.hudAccepted,
accepted: doneColumnTitle,
verdicts: c.verdicts,
bees: c.hudBees,
rounds: c.rounds,
Expand Down
Loading