Integrate #26 and #24: the verdict gate learns two things - #27
Merged
Merged
Conversation
…eady is
`missing_needs` compared a `needs` against a datasheet at the coordinator, where
it judges another robot's bid, and nothing called it when a pilot judged its own
body. A `feasible` whose `needs` named a figure nobody published went straight
through and the body moved.
`_assess` now runs the same function against the pilot's own manifest and refuses
such a verdict the way it refuses one carrying `human`, naming the unmet need so
the pilot can assess again. `uncertain` is left alone, because it asks a person
and a reachable person knows things a sheet does not, and `infeasible` ends the
run anyway.
Measured on Qwen3-32B-AWQ over 54 runs before and 54 after: a 45 minute patrol on
a body whose endurance is not published came back `feasible` six times out of six
and walked until the step budget, twice with `needs: {"endurance_min": 45}` in the
same row. On the patched build the check fired three times, all on that task, none
on the other eight, and the body moved once instead of six times.
What it does not do: the pilot came back `uncertain` rather than `infeasible` each
time, and one run declared no endurance at all and so had nothing to be checked
against. The check reads what a pilot declares, so it rewards honesty and cannot
catch silence.
The two tests pass an adapter rather than a bare transport, because every existing
loop level verdict test carries no manifest and so has never had a datasheet in
hand. The second test, where the body meets the need, exists so the first one means
something.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The verdict gate reads `BEFORE_VERDICT`, the verbs quackd ships that speak, look or read. A body quackd never shipped brings its own sensing verb, and the gate refused it as "moves the body": a `locate` that reads where things are waited for the very judgement it exists to inform. Measured with a humanoid sim whose manifest offered `locate` and `reach`: Qwen2.5-Coder 14B declared the task infeasible twice without one look, because the observation said "camera: nothing detected" and the one verb that could answer was gated. The gate now also honours `Verb.read_only`, the flag a verb's author already sets for `--dry-run`. Every shipped read-only verb was in `BEFORE_VERDICT` already, so nothing shipped changes hands; learned verbs never carry the flag, so they still wait until somebody classifies them on purpose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… looks before the verdict Bayway's PR #26, merged whole. Their commit lands as written; the corrections are in the commits after this one. One conflict, CHANGELOG.md: both sides added under Unreleased, main a release's worth of prose and three sections, theirs one entry. Their entry is kept word for word and placed under main's `### Fixed`, which is where it belongs. Nothing else moved. What it changes: the verdict gate honours `Verb.read_only` beside BEFORE_VERDICT, so a body quackd never shipped can say its own sensing verb only looks. Every shipped read-only verb was already in the set, so nothing shipped moves earlier. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…, the way a bid already is Vallhalen's PR #24, merged whole. Their commit lands as written; the corrections are in the commits after this one. Two conflicts, both resolved to their side. CHANGELOG.md: both sides added under Unreleased, and their entry is kept word for word under main's `### Fixed`, after #26's. tests/test_loop.py: their import block and main's collided, so both lines are kept, theirs exactly as they wrote it. **This merge commit is red, on purpose.** Their branch imports `MicroduckAdapter` from `quackd.adapters.microduck`, and main moved every adapter into its own package in 8611275 while the PR was open, so that module no longer exists and `tests/test_loop.py` cannot be collected. Keeping their line and fixing it in the commit after this one is the way this repository handles it, so that what they wrote stays separate in the history from what had to change. The next commit is green. What it changes: `_assess` runs `missing_needs` against the pilot's own manifest and refuses a `feasible` whose `needs` its own sheet does not meet, the way a bid is refused at the coordinator. Measured on Qwen3-32B-AWQ over 54 runs; the numbers are in the CHANGELOG entry. The MCP session does not do the same yet, and the refusal offers `infeasible` where the measured pilot reached for `uncertain`. Both are in the commits after this one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`quackd.adapters.microduck` was the import in Vallhalen's branch and it was right when they wrote it. 8611275 moved all seven adapters into their own packages while the PR sat open, so the merge before this one cannot collect tests/test_loop.py at all. `from quackd_microduck import MicroduckAdapter`, which is the spelling every other test uses. Their two tests are otherwise untouched and they pass. This is the correction the merge commit's message promised, kept separate so their commit and the change main forced on it stay legible apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s first #26 gives `Verb.read_only` a second reader. The gate honours it, which is the whole point for a body quackd never shipped: a stranger's `locate` cannot be put in `BEFORE_VERDICT`, because that set is closed by a test which rejects any name no official adapter ships. For the bodies quackd does ship the flag must restate the set and never widen it, and nothing said so. `test_every_shipped_verb_was_classified_on_purpose` reads the two sets and cannot see the flag, so a shipped verb flagged read-only and filed under `MOVES_THE_BODY` would have run before any verdict with nothing to catch it. It is caught now. Both directions are pinned: every shipped read-only verb is in `BEFORE_VERDICT`, and none is in `MOVES_THE_BODY`. The other half is what the flag claims. Two gates believe a read-only verb sends nothing, `--dry-run` since 0.4 and the verdict gate since #26, and neither ever checked. `test_a_read_only_verb_sends_nothing` runs every flagged verb on the mock and reads the wire. Both were confirmed to fail before they were kept: dropping `introspect` from the set trips the first, and flagging `move` read-only trips both, the second by naming the ten move intents it sent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… recited The rule line said "Until then only `observe`, `report_state`, `say`, the head verbs and `stop` run" to every pilot, whatever body it was driving, and it was wrong three ways. hello-world's pilot was told `observe` runs when its contract allows no such verb and the executor would refuse it. An arm was told about head verbs no arm has, which is the same fault as the `--goal` strategy paragraph fixed under Unreleased. And since #26 a body quackd never shipped can bring its own sensing verb, which the gate now lets through and that sentence had no way to name: the pilot with a `locate` was told, in as many words, that it could not use it before judging. `before_verdict_clause` reads the run's own verbs by the same rule the gate applies, `BEFORE_VERDICT` or `read_only`, so the sentence cannot drift from the refusal. It spells a verb the way the allowlist spells it, so find-and-kick now reads "only `quack`, `get_frame` and `stop` run", and `stop` is last and unconditional because the brake is never gated. The two tool descriptions stop reciting a fixed list. `assess_task` says what is true of every body, that only what looks, speaks or brakes runs, and points at the Rules for which verbs those are here. Over MCP there is no system prompt, so `robot_list_verbs` marks each verb `before_verdict` and the description points at that field instead, which is also the only answer that can be right for a robot quackd never shipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lassify a verb Three documents said the gate reads `BEFORE_VERDICT` and nothing else, which #26 makes false, and the one page a third-party adapter author actually follows told them to do something they cannot do: classify their verb in `quackd/verdict.py`, a file they do not own, in a set whose own test rejects any name no shipped adapter offers. docs/adapters.md now says both halves. A verb shipped here is classified in that file and a test insists on it. A verb quackd never ships waits for the verdict unless its `Verb` carries `read_only=True`, which is the author saying it sends no intent. A warning after the list says what that flag costs if it is wrong: it defeats the verdict gate and `--dry-run` at once, and nothing can check it. CONTRIBUTING said the same false sentence and now names the flag and the test that keeps a shipped read-only verb inside the set. The flag's own docstring described two consequences and not the fact underneath them, so it states the fact first: the verb sends no intent. ADR-0032 gets a dated amendment beside the 0034 one rather than a quiet edit, because it is the design record and its "anything not in that set waits" is exactly the sentence that moved. Two smaller ones found while reading: `docs/safety.md` and `docs/duck-spec.md` each hand-listed the read-only verbs that survive `--dry-run` and both had omitted `introspect` since the rosbridge adapter shipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ld to its own sheet too #24 says "your rule, applied on both sides". It was applied on one. The agent loop refused a `feasible` whose `needs` its own datasheet does not meet, and `RobotSession._assess` recorded the same verdict and let the body move. Over MCP that gap is a little worse than it sounds: `robot_assess_task` already ran `missing_needs` against every OTHER robot in the fleet to fill in `could`, so the one sheet it never compared against was the sheet of the robot it was about to drive. Both surfaces call `own_sheet_objection` now, so a pilot hears the same words wherever it drives from, which is the standard ADR-0032 set for a role's `needs` being checked three times "in the same words". The message names three ways out where it named one. The pilot measured on Qwen3-32B answered `uncertain` to a refusal that offered only `infeasible`, and that is the right answer here rather than a dodge: an unpublished figure is exactly the case where a person knows something the sheet does not, and ADR-0032 already says a reachable human beats a flag. The third way out is the honest one where the pilot asked for more than the task turns on. Where a pilot reads about `needs` now says the check exists, in the tool schema, in the MCP description and in docs/mcp.md, because a refusal nobody was warned about reads as a bug. docs/safety.md gets the section that was missing: what a verdict contradicting itself is, what the three answers do, and the two limits measured so far, that it turns a silent feasible into a question rather than a refusal, and that it reads what the pilot declares and so cannot catch silence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…terrain is the floor the prompt already assumes #24 gave `missing_needs` the power to refuse a verdict where it could only rank a body before, and two of its readings then contradicted the prompt. Neither could show up in the 54 runs behind that PR, because every shipped mobile body publishes `indoor_flat` and no run answered with a zero. A body whose terrain nobody published is told, by the prompt and in as many words, to "assume a flat indoor floor and decline anything else". The matcher counted an unpublished terrain as meeting nothing, so a pilot that answered `feasible` with `terrain: indoor_flat`, which is exactly what it was told to assume, was refused for doing as it was told. That is any third-party body and the rosbridge base. Unpublished still meets nothing above a flat indoor floor. And `payload_kg: 0` is how a pilot says the task carries nothing, on a tool whose own description asks it to fill `needs` in even when the verdict is feasible. It read as a need nobody had published a figure for, and got refused. A working height is not a minimum in the same way, so `work_height_m: 0` still names the ground and a body either reaches it or does not. Both changes live in `missing_needs_in`, so the coordinator judges a bid by the same reading. That is deliberate: a refusal and a role are meant to say the same thing, and a bid from a body that published no terrain deserves the same benefit of the doubt the prompt already gives it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…on for uncertain Issue #25. `quackd run --goal "find the ball and kick it"` is the first command the README shows with a model behind it, and on Qwen3-32B-AWQ it stopped at the feasibility gate five times in six, `uncertain`, run aborted. The same task from `ducks/find-and-kick.duck` passed six in six on the same body, seed and model. The contributor rebuilt the goal contract as a task file, diffed its prompt to zero against a real `--goal` run, and changed one thing at a time: persona does nothing, a concrete success criterion does nothing, and narrowing the allowlist from fifteen verbs to six carries it to six of six. I am not narrowing what `--goal` allows. quackd cannot know what a sentence needs, the README's own goal needs `kick`, which is not a core verb, and a heuristic over the words would shape every model's vocabulary from one model's behaviour on one task. What is wrong on its own terms is the description the model reads. It said the verdict is judged against the datasheet and then listed "the object is out of view" as a reason to answer `uncertain`, which are two different questions: where the ball is decides nothing about whether a duck can kick a ball on the floor it is rated for. The pilot's own reasons in #25 are that confusion out loud, "Since the camera currently detects nothing, I cannot determine feasibility yet". The description now says the verdict is the task's needs against the body's limits and not whether you can see the target, that a target you have not found yet is what the run is for, and keeps `uncertain` for what it is for: a figure that decides a limit, on a thing you have not seen, or a limit nobody published. The same correction goes to the rule line, to the MCP description, and to docs/safety.md, whose "When the pilot is unsure" now says finding the thing is the task and that the looking verbs run before the verdict so the pilot can go and look. This is the contributor's first candidate and they flagged it as untested. It still is. There is no local server on this machine, and the shipped Qwen3 transcripts show the six verb file answering `feasible` on a frame that detected nothing, so the allowlist may be doing something these words do not undo. The six cells want re-running on this branch, and #25 says so rather than closing. One live test comes with it, `live_llm` and opt-in like its neighbours: the README's goal through `duck_from_goal`, asserting the run does not end at the gate with nothing moved. It is one paid run of one cloud model and it says so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ady ask for Found by @Vallhalen while measuring #24, and it is one tool disagreeing with itself. `needs` speaks `endurance_min`, so a pilot may say a task requires forty five minutes of running time. `Estimate.quantity` took mass, size, distance, height, a count and "other", so the same pilot could not estimate one. On the 45 minute patrol the model reached for `quantity: "endurance_min"`, got a validation refusal, and spent an LLM call on it twice out of six. `duration_min` is in both halves now. The enum is hand-written in two files, the pydantic model and the tool schema, which is exactly how they came apart, so a test holds them to each other rather than to a list typed out a third time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…opped being true 0.9.0's known limitations said "No real model has ever refused a task on feasibility grounds here", and PLAN.md carried the same line as an open item. #24 is the day that stopped being true, so the plan says what is measured and what is still open instead. The CHANGELOG's 0.9.0 section keeps the old sentence, because it was true when it shipped and correcting history is not the job. docs/local-llms.md gets both measurements under Honest notes, beside that contributor's earlier transcript pair: the 108 runs behind #24 with the noise floor they measured for free, the hole that was the unpublished figure, the half that is prompt shaped and still unfixed (a body that cannot carry hedging rather than refusing), and the six cells of #25 with the one thing that moved them. The transcripts for these are not published, so the numbers are attributed as their report rather than as files a reader can check, and the last line of each says what is still unmeasured: nobody here runs that model. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…of each Their entries stay as they wrote them. @Bayway's gains the credit sentence and @Vallhalen's gains the issue id in theirs, which is the form the existing credits in this file use, and then each gets a paragraph saying what was added after it, so a reader can tell the contribution from the corrections without reading the log. The #25 wording change goes under Changed as its own entry, crediting the measurement that found the cause and saying plainly that the allowlist is not narrowed and that the fix is untested. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s now say docs/safety.md claims the check turns a silent `feasible` into a question and that `--yes` then answers it with go, so the same unmet need reaches the body one word later and the contradiction is left in the transcript rather than stopping the run. I established that by reading the code, which is the kind of claim this repo turns into a test. It is not a hole for the check to close. `--yes` is a person saying they have read the contract, and ADR-0032 puts a reachable human above a flag. What the check buys under it is the record: a refusal row and the doubt it became, where a silent `feasible` left nothing at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ot on two The test I wrote a few commits ago read `default_registry()`, which holds two of the four shipped read-only verbs. The arm's own `report_state` and the rosbridge base's `introspect` live in their adapters and were not covered, and `introspect` is the one that most deserves it: it is the only shipped read-only verb that talks to the robot at all, asking the bridge what the body is. Parametrised over every adapter's mock backend, with `send_intent` wrapped so the assertion reads the wire rather than a transport's own bookkeeping, which differs per body. Confirmed to fail: making `introspect` send a stop reports "a read-only verb on rosbridge sent ['stop']". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…erdict An adversarial audit of this branch found the docstring on `BEFORE_VERDICT` promising something only another gate was keeping. It says a learned verb is refused until somebody classifies it on purpose, on the grounds that a learned verb never carries `read_only`. True of the flag, and the other half of that condition is a name test, so a policy named `observe` passed it. The name is free to take. A body with no camera verb has no `observe` of its own, which is every arm, so the registry accepts the registration. What actually stopped such a verb was the confirm gate one row down, because `register_learned_verb` marks a policy `confirm`, and `--yes` answers the confirm gate. Proven both ways: without the exclusion the new test fails with `ConfirmDenied` rather than `VerdictRequired`, which is the hole in one line of output. The gate excludes `kind == "learned"` from the name half now, and `before_verdict_clause` applies the same rule so the prompt does not offer what the gate refuses. This predates #26: the same name test has been the gate since ADR-0032, and what #26 changed is that the docstring beside it is now read by somebody deciding whether to trust the flag. The set's docstring also says outright what the name half means, because it is not obvious: those nine are a vocabulary rather than a list, so a body that ships a verb called `gaze` which walks has said the wrong word about itself, the way a verb carrying `read_only` that sends an intent has. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nally An audit of the previous commit found the Rules line and the tool description beside it giving opposite answers to the same state. The Rules line, labelled enforced by the executor and not optional, said flatly that a target you have not found is not a reason for `uncertain`. The tool description kept `uncertain` for the mass or size of a thing that decides a limit and that the pilot has not seen. For "pick up the box" with the box out of frame, both describe the situation and the more authoritative one is wrong, which would buy a confident `feasible` on exactly the task class where the gate earns its keep. All four surfaces carry both halves now, in the same words: not having found the target is not by itself a reason, and it is one when a limit turns on that unseen thing's mass or size, or on a figure the sheet does not publish. The MCP description had lost the carve-out altogether, which is worse there than in the loop: there is no terminal on that surface, so `uncertain` is the only route to the person who might know an unpublished figure. docs/safety.md stated the rule and then negated it two sentences later, and offered a reason that is not true of the gate. It said the looking verbs run first so the pilot can go and look. The pilot cannot go: `search_scan`, `go_to` and `move` all wait for the verdict. It can take a frame from where it stands, and when the deciding figure is on something out of that frame, `uncertain` and the person are the way past it. That is what the page says now. Two tests hold the surfaces to each other, one per surface that has a way to be read: the prompt three in `tests/test_verdict.py`, and the MCP description through a real client in `tests/test_mcp_server.py`, which also pins #24's check and the `before_verdict` field into the only text an MCP pilot ever reads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n prose Six agents were pointed at this branch with instructions to find it wrong, and its prose is where most of what they found lives. The gate and the prompt fixes are in the two commits before this one; these are the claims. **A contributor's own sentence had been extended.** The credit commit said their entry gained the issue id, and it had also gained "across 108 runs and reported the noise floor with the result" inside their sentence, which is not theirs and put "54 runs" and "108 runs" in one bullet with nothing to reconcile them. Their sentence is verbatim again and the addition is in the paragraph that is mine. **A phrase was quoted to the wrong place.** "Both sides" is in #24's body, not its title, so the changelog quoted their title saying something it does not say. **Two counts were wrong.** Three documents said the gate reads `BEFORE_VERDICT` and nothing else, not five: the other two of the five that commit touched were `--dry-run` lists that had lost `introspect`, which the commit itself said two paragraphs later. And 14 infeasible, 26 uncertain and 8 feasible sums to 48 rather than 54, because the 8 is from the noise floor subset. Over 54 runs #24's own task table gives 14 feasible. Both the docs and PLAN.md said the 48 figure under a 54 run heading. **A measured number was rounded up.** Narrowing the allowlist under the duck's verb names was feasible 5 of 6, not 6 of 6. Only the variant under the goal's own verb names was 6 of 6, which is the whole reason that variant exists. **A README claim had gone stale under us.** "It cost the README's own first command" was true when #25 was filed and stopped being true when the README was rewritten to lead with the arm: its first command with a model behind it is a wave on an SO-101 now. What is true is that it is the goal the opening paragraph names and the first one shown for a duck. **Three surfaces stated the own-sheet rule without its exceptions.** The two guards for an honest pilot, a minimum of zero and an unpublished terrain asked as `indoor_flat`, landed after the wording that says a need the sheet does not publish is refused. The safety page, the MCP page and the tool schema say them now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… let the body move **A refusal refused the words and not the motion.** The check runs before the verdict is recorded, the way the `human` and validation refusals do, so a pilot already cleared for one reading of the task could name a need this body cannot meet, be refused, and go on moving on the older verdict while the newer and better informed one was thrown away. That is the exact failure #24 exists to stop, one re-assessment later. Both surfaces withdraw what was standing now, so nothing moves until the pilot answers again, and both tests fail without it. **A need that was not a number crashed the tool.** The zero guard called `float()` on whatever arrived, and `robot_assess_task` computes `could` from the raw tool argument before anything validates it, catching `ValueError` only. So `needs: {"payload_kg": null}` raised `TypeError` out of the MCP tool where before it read as a need nobody published. It falls through to the refusal again. **The terrain exception reached two bodies the prompt never told.** It exists because the prompt tells a body whose terrain nobody published to assume a flat indoor floor, so the two readers agree. That sentence is only rendered for a body that moves and has a datasheet at all: `body_lines` tells a body with no sheet to treat every limit as not published, and a body that does not move is told "it does not move" instead. The exception stops where the promise does, which also keeps a flock bid that carried no datasheet from winning a role on it. **A stranger's flag outranked quackd's own record.** `read_only` opened the gate even for a verb arriving under a name this repository has classified as motion, so a `kick` carrying the flag ran before any verdict. A verb saying both those things about itself is saying two contradictory things, and the name is the one to believe. The new test above it asserted this combination was impossible and could only see the verbs quackd ships. The refusal message now names the third way out properly. A person asked about an unpublished figure can do something about it: publish it in the task file's own `datasheet:` block, which ADR-0032 added for exactly that and which is the only thing that makes a sheet say something new. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r the wrong half ADR-0032 is the design record for this gate and the branch amended it for #26 only. #24 changes what the ADR's Decision section describes, because a `feasible` verdict is no longer recorded unconditionally, so it gets its own dated amendment saying what the check is, what the two exceptions are, and what it cannot do. Three documents still stated the old absolute rule that a figure nobody published counts as not met. `docs/duck-spec.md` is the canonical per-key table that `docs/flock.md` points at by anchor, so the two exceptions are in the table and in the paragraph under it. `quackd/verdict.py`'s module docstring states the rule 135 lines above the function that now qualifies it, and points at it. **A refused assessment was written into the transcript as the standing verdict.** Every refusal in `_assess` returns before recording, and the row was built from whatever verdict happened to be in the executor, so a refused re-assessment carried the earlier verdict's word, reason and needs and read as though that one had been refused. The row describes the call now, and a test covers the path that is not the own-sheet check, because that one withdraws the verdict and so hid the bug from its own test. And docs/safety.md says out loud the thing the check does that is easy to resent. It asks more of a pilot that answers fully: a duck asked to nudge a 60 g ball has no published payload to compare against, so a pilot that honestly writes `payload_kg: 0.06` is refused where one that writes nothing is not. That is refuse by default working as ADR-0032 intends, and the answer that lasts longer than one run is a `duck: 2` datasheet block, which is in the page now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three entries described the branch as it stood before six agents were pointed at it. They describe what shipped now: the gate shutting on a refusal, which is the one the audit found that let a body move; the learned verb that could take one of the nine names; the terrain exception narrowed to the bodies the prompt actually tells; and the #25 wording, which stated absolutely in the Rules line what it meant conditionally in the tool description. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scratch integration branch for #26 and #24, opened so CI runs on the merged result rather than on either fork. It is merged to
mainlocally with--no-ffonce this is green.What is in it
#26 (@Bayway), merged whole. The verdict gate honours
Verb.read_onlybesideBEFORE_VERDICT, so a body quackd never shipped can say its own sensing verb only looks. Every shipped verb carrying the flag was already in the set, so nothing shipped moves earlier.#24 (@Vallhalen), merged whole. A
feasibleverdict whoseneedsthe pilot's own datasheet does not meet is refused before it is recorded, the way a bid is refused at the coordinator. The merge commit is red on purpose: their branch predates the adapter package split, and the commit straight after it is the fix, so what they wrote stays separate from whatmainforced on it.#25 (@Vallhalen), answered without narrowing the allowlist.
assess_tasktold the pilot its verdict is judged against the datasheet and then named "the object is out of view" as a reason to answeruncertain. The description says what the verdict is about now. Whether that moves the measured numbers is unknown here and the issue asks for a rerun.What landed on top
One commit per theme, so
git logkeeps each contributor's work and the corrections legible apart. Two invariant tests for the flag, the prompt rule line read off the body's own allowlist, MCP parity for the own-sheet check through one shared message, two matcher guards for an honest pilot,duration_min, the docs and two dated ADR amendments, and the measurement written up indocs/local-llms.mdwithPLAN.mdcorrected where 0.9.0's "no real model has ever refused a task on feasibility grounds" stopped being true.Then six agents were pointed at the merged result with instructions to find it wrong. Twenty four findings survived an adversarial verify pass, and the last five commits are those: a refusal that refused the words and not the motion, a learned verb that could take one of the nine names,
read_onlyoutrankingMOVES_THE_BODY, afloat()that raised on a null need, a terrain exception reaching two bodies the prompt never told, and nine wrong claims in the branch's own prose, including a contributor's sentence that had been extended from the inside.Checklist
uv run ruff check . && uv run ruff format --check . && uv run mypy && uv run pytestpass locally, and mypy on 3.12 as welluv run quackd validate ducks/*.duckpasses (14 files valid)🤖 Generated with Claude Code