From ba72b52941db7715d674e81f937bfe9458c9336f Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Sun, 20 Sep 2026 16:23:00 +0700 Subject: [PATCH 1/3] feat(queen): a criterion may ask whether the spec builds The allowlist held five subcommands, so every criterion filed against this repository was a grep -- and a grep is satisfied by a file that never builds: 388 of the 837 specs the oracle reaches generate Zig that does not compile while satisfying every criterion ever written against them. `test-report` generates the reviewed spec and has Zig build and run that spec's own tests, which is the oracle's question asked before the oracle. The bee already has a shell in this container, so nothing new is reachable to it; what the allowlist prevents is a criterion rigging its own measurement by running a script from the commit under review. The readers added beside it -- coverage, lint, symbols, outline, validate-vacuity and the rest -- were measured on the corpus and answer in under 30 s. `dupes` (~50 s) and `check-calls` (~140 s) are deliberately refused, with tests pinning the refusal: past the 60 s timeout a criterion is inconclusive, which spends a review and establishes nothing. Co-Authored-By: Claude Opus 5 --- .../src/api/services/queen-criteria-run.ts | 70 +++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/trios/agent-server/apps/server/src/api/services/queen-criteria-run.ts b/trios/agent-server/apps/server/src/api/services/queen-criteria-run.ts index c5315d6595..b9f3268121 100644 --- a/trios/agent-server/apps/server/src/api/services/queen-criteria-run.ts +++ b/trios/agent-server/apps/server/src/api/services/queen-criteria-run.ts @@ -152,19 +152,43 @@ export const CRITERION_PROGRAMS: ReadonlySet = new Set([ /** * The `t27c` subcommands a criterion may name. * - * `t27c --help` lists 180 of them, and they are not all readers. `battery` and + * `t27c --help` lists 155 of them, and they are not all readers. `battery` and * `gates` RUN every `check_*.py` in the tree, `silicon` drives a bitstream * build, `fpga-flash` writes to hardware, `serve` and `bridge` open sockets, * `fmt`, `rename` and `seal` edit files. The tree they would act on is the bee's * own commit, so an allowlist of programs alone hands a bee the ability to run * a script it just wrote by putting `t27c battery` in an issue's criteria. * - * So the subcommand is allowlisted too, to the ones that only READ a spec and - * print an answer - which is all an acceptance criterion in this repository has - * ever needed (measured over the open backlog: spec-status, gen, parse and - * typecheck cover every criterion that names the compiler). + * So the subcommand is allowlisted too. What belongs on this list is a command + * that READS a spec, prints an answer, and finishes well inside + * CRITERION_COMMAND_TIMEOUT_MS. The first version held five names, and the + * criteria written against it were greps - which is how 388 of the 837 specs + * the oracle reaches came to satisfy every criterion ever filed against them + * and still generate Zig that does not compile. + * + * Measured 2026-09-20 against master, on the corpus, per command: everything + * below except `test-report` answers in under 30 s and most in under one. + * + * `test-report` is the one that runs anything: it generates the reviewed spec + * and asks Zig to build and run that spec's own tests, which is the question + * the oracle asks and the only one that distinguishes a file that greps clean + * from a file that works. It is admitted deliberately. The bee already has a + * shell in this container - that is what a bee IS - so nothing here is reachable + * to it that was not reachable before; what the allowlist prevents is a + * criterion RIGGING its own measurement by running a script from the commit + * under review, and `test-report` runs the compiler on one named spec. + * + * Deliberately absent, with the reason: + * - `dupes` (~50 s) and `check-calls` (~140 s) read the whole corpus. Past + * the timeout a criterion is inconclusive, which spends a review and + * establishes nothing. A bee may still run them; its shell has no + * allowlist. + * - `corpus`, `backlog`, `suite`, `battery`, `gates`: minutes, or they + * execute the tree. + * - `fmt`, `rename`, `seal`, `init`, `compile`, `doc`: they write. */ export const CRITERION_T27C_SUBCOMMANDS: ReadonlySet = new Set([ + // The verdicts. 'spec-status', 'impl-status', 'classify', @@ -172,6 +196,42 @@ export const CRITERION_T27C_SUBCOMMANDS: ReadonlySet = new Set([ 'parse-complete', 'parse-conform', 'typecheck', + 'test-report', + // What the spec declares. + 'symbols', + 'inspect', + 'outline', + 'exports', + 'depends', + 'test', + 'tree', + 'strings', + // What it measures. + 'count', + 'loc', + 'size', + 'metrics', + 'depth', + 'stack', + 'hash', + 'coverage', + // What is wrong with it. + 'lint', + 'deadcode', + 'orphans', + 'spellcheck', + 'validate', + 'validate-vacuity', + 'todo', + 'check-deps', + 'deps-tree', + 'analyze', + // Comparisons and evaluation. + 'xref', + 'api-diff', + 'diff', + 'eval', + // What the generators emit. 'gen', 'gen-c', 'gen-rust', From bb7d43788f1d72b3ad69d955b6c03b5e82adb01d Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Sun, 20 Sep 2026 16:23:02 +0700 Subject: [PATCH 2/3] test(queen): pin what the criteria allowlist admits and refuses Co-Authored-By: Claude Opus 5 --- .../tests/api/queen-criteria-run.test.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/trios/agent-server/apps/server/tests/api/queen-criteria-run.test.ts b/trios/agent-server/apps/server/tests/api/queen-criteria-run.test.ts index 58d7c6775d..bdeab31574 100644 --- a/trios/agent-server/apps/server/tests/api/queen-criteria-run.test.ts +++ b/trios/agent-server/apps/server/tests/api/queen-criteria-run.test.ts @@ -260,7 +260,7 @@ describe('criteria become checks', () => { }) describe('t27c is allowed only where it reads', () => { - // `t27c --help` lists 180 subcommands and they are not all readers: `battery` + // `t27c --help` lists 155 subcommands and they are not all readers: `battery` // and `gates` run every `check_*.py` in the tree, and the tree is the bee's // own commit. A program allowlist alone would let a criterion run a script // the bee wrote in the same attempt. @@ -275,6 +275,13 @@ describe('t27c is allowed only where it reads', () => { ['t27c fpga-flash'], ['t27c'], ['grep -c x specs/a.t27 && t27c battery'], + // Readers, and still refused: both walk the whole corpus. Measured + // 2026-09-20 on master, `dupes` takes ~50 s and `check-calls` ~140 s + // against a 60 s per-command timeout, and a criterion that times out is + // inconclusive - it spends a review and establishes nothing. The bee's own + // shell has no allowlist and can run either. + ['t27c dupes --name magadd'], + ['t27c check-calls --specs-dir specs'], ])('refuses %s', (cmd) => { expect(isSafeCommand(cmd)).toBe(false) }) @@ -285,6 +292,18 @@ describe('t27c is allowed only where it reads', () => { ['t27c parse specs/a.t27 2>&1 | grep -c x'], ['t27c typecheck specs/a.t27'], ['t27c impl-status'], + // The compile question, which no grep can ask: `test-report` generates the + // reviewed spec and has Zig build and run that spec's own tests. Admitted + // deliberately - see the comment on CRITERION_T27C_SUBCOMMANDS. + ['t27c test-report specs/a.t27 2>&1 | grep -c BLOCKED'], + // The readers a criterion could not name until now. + ['t27c coverage specs/a.t27'], + ['t27c lint specs/a.t27 | grep -c WARN'], + ['t27c symbols specs/a.t27 | wc -l'], + ['t27c outline specs/a.t27'], + ['t27c validate-vacuity --specs-dir specs'], + ['t27c api-diff specs/a.t27 specs/b.t27'], + ['t27c eval 1 + 2'], ])('allows %s', (cmd) => { expect(isSafeCommand(cmd)).toBe(true) }) From 8971a2f7b775d272f6469fbf0f5744e60f2ea654 Mon Sep 17 00:00:00 2001 From: Vasilev Dmitrii Date: Sun, 20 Sep 2026 16:54:58 +0700 Subject: [PATCH 3/3] feat(agent): the container can run the gates that judge its work Every gate that decides whether a bee's pull request lands is a Python script: tools/dupe_scan.py is the Duplicate Body Ratchet, check_assertionless_spec_tests.py is the corpus ratchet, tools/ci/check_specs_still_parse.py is the required Spec Parse Ratchet, and the twenty-one reporting helpers under scripts/tri_loop/ are Python as well. The image declared git, perl-base and tini and said nothing about python3, while the brief has been telling bees since 2026-09-20 to run `python3 tools/dupe_scan.py --name `. python3 rather than python3-minimal: those scripts import json, hashlib, difflib and subprocess, and Debian's minimal package carries the subset dpkg's maintainer scripts need. Nothing outside the standard library, so no pip and no venv. `python3 --version` runs in the same layer, so a base image that stops shipping it fails the build instead of the swarm. Co-Authored-By: Claude Opus 5 --- trios/agent-server/Dockerfile | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/trios/agent-server/Dockerfile b/trios/agent-server/Dockerfile index aaba6d26c7..ff4b298bfc 100644 --- a/trios/agent-server/Dockerfile +++ b/trios/agent-server/Dockerfile @@ -192,9 +192,25 @@ WORKDIR /app # # tini as PID 1 reaps orphans and forwards signals. It is what `docker run # --init` installs, and it is the standard answer to exactly this. +# +# python3 is here for the same reason perl-base is: declared, not assumed. +# Every gate that decides whether a bee's pull request lands is a Python +# script -- tools/dupe_scan.py is the Duplicate Body Ratchet, +# tools/check_assertionless_spec_tests.py is the corpus ratchet, +# tools/ci/check_specs_still_parse.py is the required Spec Parse Ratchet, and +# the twenty-one reporting helpers under scripts/tri_loop/ are Python too. A +# bee that cannot run the gate that will fail it is a bee guessing, and the +# brief has been telling it to run `python3 tools/dupe_scan.py --name ` +# since 2026-09-20. +# +# `python3`, not `python3-minimal`: those scripts import json, hashlib, +# difflib and subprocess, and Debian's minimal package carries a subset of the +# standard library chosen for dpkg's maintainer scripts. Nothing here needs a +# module from outside the standard library, so there is no pip and no venv. RUN apt-get update \ - && apt-get install -y --no-install-recommends git ca-certificates openssh-client tini perl-base \ - && rm -rf /var/lib/apt/lists/* + && apt-get install -y --no-install-recommends git ca-certificates openssh-client tini perl-base python3 \ + && rm -rf /var/lib/apt/lists/* \ + && python3 --version # An unprivileged account for the agents' shell commands. #