rubber-duck — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited rubber-duck (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The bug lives in the gap between what you think the code does and what it actually does.
You can't see that gap from inside your own head. Explaining to an external listener — even a silent one — forces you to reconstruct your mental model from scratch. That reconstruction is where the bug surfaces.
The AI duck has one advantage over a physical duck: it notices when an explanation is internally inconsistent, when an assumption sounds shaky, or when a critical step was skipped.
| Situation | Mode |
|---|---|
| A person is present and stuck | Interactive (the protocol below) |
| Args say "autonomous", "walk it", "author claims", "answer your own questions", or there is no one to reply | Autonomous (see "Autonomous Mode") |
Why two modes: the interactive duck works because the human must reconstruct their mental model. An agent has no one to reconstruct for, and an agent asking itself questions and answering from memory just repeats its own fluent guess. Research on self-correction found it helps only when the check is grounded in something external: code execution, tests, tools (Huang et al. 2023, arXiv 2310.01798). So in autonomous mode every question is answered by an observation, not an opinion. Sources: docs/research/rubber-duck-skill-improvements.md.
Ask the human to state the bug in one sentence. Not symptoms. Not what they tried. One sentence:
"What do you expect to happen, and what happens instead?"
If they can't state it in one sentence, the problem isn't well-understood yet. Make them try.
Do not look at code yet. Get:
If "sometimes" or "only when": that condition is almost certainly the bug's domain. Note it.
Before touching code, ask:
"Before we look at the code — what are you assuming is true at the point where the bug occurs?"
Make them list every assumption explicitly. Write them down as a numbered list.
Common assumptions that hide bugs:
| Assumption | What to challenge |
|---|---|
| "This value is always set" | Nil/null/unset — when could it not be? |
| "This runs on the main thread" | What if it doesn't? |
| "The data arrives in this order" | What if it arrives out of order or late? |
| "This has already been called" | What if the setup step was skipped? |
| "This only runs once" | What if it runs twice? |
| "This state is still valid here" | Could it have been mutated between here and where it was set? |
For each assumption: "How do you know that's true?"
Now look at the code. Ask the human to narrate what happens, not what the code says. There's a difference:
recalculateBalance() here."account.balance is... I think it's whatever the last transaction set it to... and then it calls recalculateBalance(), which should update it to the sum of all transactions since openingBalance."Interrupt when:
Probing questions:
X at this point? Not what it should be — what is it?"Y — when exactly? Before or after Z modifies this?"Y return? Walk me through that function too."As the human explains, you're looking for the moment the explanation breaks down:
When you find it, name it:
"You said in Step 2 that X is always set before this runs. But you just said the callback fires asynchronously — how do you know X is set by then?"
Do not solve it. Name the contradiction. Let the human follow the thread.
The session is working when the human says one of:
When this happens: stop talking. Let them think. The duck's job is done.
After the human has identified the root cause:
Use when no human can answer. The duck's discipline (never accept hand-waving, name every assumption, "should" is a red flag) stays; the answers come from the code instead.
All three reduce to the same thing: a numbered list of checkable statements.
Write each claim as one line a stranger could check ("4xx errors are never retried", not "error handling is robust"). Include implicit claims too: numbers ("up to 3 retries"), words like "always/never/only", and anything the text says is "handled by" something else. This step is a design choice, not a quoted method: it pairs claim extraction with independent verification, which Chain-of-Verification found beats checking everything in one pass (arXiv 2309.11495).
For every claim, answer without rereading your earlier verdicts, by reading the code or running something. Acceptable evidence: file:line you read, a command and its output, a test that would fail if the claim were false. Not evidence: "the tests pass" (unless they exercise the claimed behaviour), "this is a common pattern", your memory of the library.
Look specifically for the places claims usually break: boundaries (off-by-one in counts), ordering (delete-then-write races), paths the text never mentions (bulk/import/error paths), and "handled elsewhere" (find the elsewhere).
| # | Claim | Evidence (file:line / command) | Verdict |
|---|-------|--------------------------------|---------|
| 1 | ... | retry.js:4 loop runs `retries` times | REFUTED: 3 calls total, not 4 |Verdicts: CONFIRMED, REFUTED (say what is true instead, one line), or UNVERIFIED (say what you could not check and why). End with the list of UNVERIFIED items; an honest "I could not check this" is the point, a confident guess is the failure. Do not propose fixes until at least one claim is REFUTED, and then only after the table.
Habit from interactive mode that carries over: when your own explanation contains "should" or "I think", that is the claim to check next.
| Human says | Duck responds |
|---|---|
| "It should..." | "Should, or does? What does it actually do?" |
| "...and then later..." | "Stop. What happens in between? Walk me through that." |
| "I think the value is..." | "You think, or you know? Where does that value come from?" |
| "That part works fine" | "How do you know? Have you verified it in this specific case?" |
| "It's probably X" | "We're not guessing yet. Keep explaining what you observe." |
| "I've tried everything" | "Tell me the last thing you tried and what you expected it to change." |
After each session, note:
Over time, patterns emerge: "I always assume X" or "I always gloss over async boundaries." These patterns are more valuable than any individual bug fix.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.