diff --git a/docs/now/2026-09-20-issues-that-meet-the-queens-own-bar.md b/docs/now/2026-09-20-issues-that-meet-the-queens-own-bar.md new file mode 100644 index 0000000000..b969219370 --- /dev/null +++ b/docs/now/2026-09-20-issues-that-meet-the-queens-own-bar.md @@ -0,0 +1,9 @@ +# NOW -- Issues that meet the Queen's own bar (2026-09-20) + +## Every feeder issue was missing two of the four sections she checks (Closes #4444) + +- `QueenSpecQuality` checks four things and names them: a `## Boundary`, a **scenario** (a Given/When/Then, in either language), a **requirement** written as an obligation (`FR-001 ... MUST`), and success criteria with a measurable outcome. Every issue the feeders have ever opened carried the boundary and the criteria and neither of the other two. +- Measured on the live tick: `incompleteSpec` skipped **69** candidates, against 25 the day before -- not because anything got worse, but because 102 more issues had been filed in the same shape. A skip is not a refusal, and the swarm still dispatches them when nothing better is there; but a candidate skipped first is a candidate that waits, and these waited for two sections derivable from numbers the issue had already measured. +- All three shapes now carry them: `single_issue`, `part_issues` and the untested-function feeder, from one shared `scenario_and_requirements()`. The scenario names the file, what is wrong with it and the functions by name; the requirements are the three that were already true and unwritten -- keep the signature, keep the file parsing, do not delete a function to satisfy a count -- plus one per feeder about tests. +- Verified against the Swift predicates rather than by eye: the four checks read `## Boundary`, `**given**`/`**then**`, `## Requirements` with ` must `, and an `acceptance criteria` heading. All four are true for all three shapes. +- What this does NOT do: it does not make the work easier. It makes the issue legible to the policy that was already reading it. diff --git a/tools/queen/feed_empty_bodies.py b/tools/queen/feed_empty_bodies.py index 685a8602c6..7d0a864e27 100644 --- a/tools/queen/feed_empty_bodies.py +++ b/tools/queen/feed_empty_bodies.py @@ -236,6 +236,41 @@ def tests_in(t): return len(re.findall(TEST_RE, t)) "report and name the error rather than working around it. The rest of the " "toolbelt is in `docs/BEE_TOOLBELT.md`.\n" if BELT else "")) +# THE QUEEN'S OWN BAR, which the issues this file writes did not meet. +# +# QueenSpecQuality checks four things and names them: a `## Boundary`, a +# SCENARIO (a Given/When/Then, in either language), a REQUIREMENT written as an +# obligation (`FR-001 ... MUST`), and success criteria with a measurable +# outcome. Every issue this feeder has ever opened carried the boundary and the +# criteria and neither of the other two, so every one of them was counted in +# the tick's `incompleteSpec` skip - 69 of them on 2026-09-20, against 25 the +# day before, purely because more had been filed. +# +# A skip is not a refusal: the swarm still dispatches them when nothing better +# is there. But a candidate that is skipped first is a candidate that waits, and +# there is no reason for it to wait when the two sections are derivable from +# what the issue already measured. +def scenario_and_requirements(rel, what, names, checks): + """The two sections the Queen asks for, from numbers this issue measured.""" + named = ", ".join(f"`{n}`" for n in names[:6]) + ( + f" and {len(names) - 6} more" if len(names) > 6 else "") + return [ + "## User Scenarios & Testing\n", + f"- **Given** `{rel}`, where {what},", + f" **when** {named} {'is' if len(names) == 1 else 'are'} written and the " + "commands under Acceptance criteria are run from the repository root,", + " **then** each of those commands prints the value stated beside it.\n", + "## Requirements\n", + "- FR-001: every signature quoted below MUST stay exactly as it is - the " + "signature is the contract, and a caller elsewhere is holding it.", + "- FR-002: the spec MUST still parse afterwards. `t27c parse` is the check, " + "and a spec that stops parsing generates nothing, so every test it already " + "carried stops running too.", + "- FR-003: no function MUST be deleted to satisfy a count. The function " + "count is itself a criterion below.", + ] + ([f"- FR-004: {checks}"] if checks else []) + [""] + + def single_issue(rel, t, emp, status): names = [e[0] for e in emp]; n = len(emp) stubs, have, tests = gen_stubs_cmd(rel), names_cmd(rel, names), f"{TEST_GREP} {rel}" @@ -256,6 +291,13 @@ def single_issue(rel, t, emp, status): "IMPLEMENTED, PARTIAL, UNWRITTEN, NOPARSE or NOFN. It exits 0 whatever it says, so " "compare the word it prints, not the exit code. The `&&` in the second command is " "deliberate: if generation fails it prints nothing, not `0`.\n", + *scenario_and_requirements( + rel, + f"{n} function{'' if n == 1 else 's'} {'is' if n == 1 else 'are'} " + "declared with a body that holds no statement", + names, + "every function implemented MUST gain at least one `test`; a body with " + "nothing asserting on it is a claim, not a result."), "## What to write\n", "Keep every signature exactly as it is - the signature is the contract. Quoted verbatim from the file:\n"] for i, (nm, sig, line) in enumerate(emp, 1): L.append(f"{i}. line {line} - `{sig}`") @@ -304,6 +346,13 @@ def part_issues(rel, t, emp, status): f"$ {stubs}", f"{before} # if it is not {before}, this part is not yours yet - stop", "```\n", "The `&&` is deliberate: if generation fails the command prints nothing, not a number.\n", + *scenario_and_requirements( + rel, + f"{len(emp)} functions are declared with a body that holds no " + f"statement and this part covers {len(ch)} of them", + names, + "this part MUST leave the other parts' functions alone; they share " + "one boundary and one file."), "## What to write\n", "Keep every signature exactly as it is. Quoted verbatim:\n"] for k, (nm, sig, line) in enumerate(ch, 1): L.append(f"{k}. line {line} - `{sig}`") L += ["", "Add a `test` declaration for each function you implement " diff --git a/tools/queen/feed_untested.py b/tools/queen/feed_untested.py index 5fe4f303b0..d55b200762 100644 --- a/tools/queen/feed_untested.py +++ b/tools/queen/feed_untested.py @@ -62,7 +62,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from feed_empty_bodies import ( # noqa: E402 BEE_T27C, OUTD, PREAMBLE, REPO, T27C, TEST_GREP, WORK, - bee_shell, claims_hold, log, open_boundaries, queue_idle, run, sync_master, + bee_shell, claims_hold, log, open_boundaries, queue_idle, run, + scenario_and_requirements, sync_master, ) MAX_UNTESTED = 8 @@ -165,6 +166,14 @@ def build(rel): "## Current state - re-run these yourself, from the repository root\n", "```", f"$ {BEE_T27C} coverage {rel}", f"Functions: {total}", f"Untested: {untested}", "```\n", + *scenario_and_requirements( + rel, + f"{untested} of its {total} function" + f"{'' if total == 1 else 's'} {'has' if untested == 1 else 'have'} no test", + names, + "each test MUST assert on a result. `assert true` and a body of only " + "comments are counted as vacuous by `t27c validate-vacuity`, and the " + "corpus ratchet fails a file that gains one."), "## What has no test\n", "Quoted verbatim from the file - keep every signature exactly as it is:\n"] for i, (name, sig) in enumerate(sigs, 1):