Reporting a Bug or Asking for a Feature¶
Telling a config mistake from a defect, and filing the ones that are real. It ships with the potato-skill skill:
/plugin marketplace add davidjurgens/potato-skill
/plugin install potato-skill@potato
When a task will not do what the researcher needs, the answer is one of three things, and only the third belongs on GitHub as a feature request.
| What it is | How you know | What to do |
|---|---|---|
| A config mistake | --strict fails, or the boot log says Loaded 0 <something> |
troubleshooting.md. Fix it yourself |
| A bug in Potato | It does something other than what it documents | File a bug |
| A gap | It does exactly what it documents, and that is not enough | File a feature request |
The order matters. The first is by far the most common, it is the only one you can fix in the next minute, and a bug report that turns out to be a typo costs a maintainer more than it costs you.
Before you call it a bug¶
Three checks, none of which take long.
Run potato validate config.yaml --strict. A misspelled key inside a block
Potato treats as opaque is accepted, ignored, and never mentioned again, so the
feature it was meant to configure simply does not happen. Without --strict
that is a warning you will not read. This is the single most common cause of
"the config is right and nothing happens".
Read the boot log. Loaded 0 training items, Loaded 0 attention checks
and their siblings mean the side file was found and rejected, usually for a
missing required field. The count is the diagnosis.
Check what you measured, not what you inferred. A rendered page tells you
what an annotator sees and not what survives; user_state.json tells you what
survives and not what they saw. Where the claim spans both — "the answer is not
being saved" does — look at both before writing it down. Reading four strings
out of a served page is not the same as four things having rendered, because
the page also carries the JavaScript that would have rendered them.
Before you ask for a feature¶
Say what the closest existing thing is and why it does not fit. That sentence is what a maintainer acts on, and writing it is also how you find out the feature already exists, which happens often enough to be worth the two minutes.
annotation-types.mdhas every annotation type with its fields.config-keys.mdandconfig-keys-nested.mdhave every documented key.modalities.mdandagent-traces.mdroute a medium or a trace shape to the display and scheme that handle it.find_design.py --query "<what you are doing>"searches published designs.- With the MCP server connected,
describe_annotation_typeanddescribe_display_typeanswer the same question against the live registry.
A request that names the type it tried, quotes the field list, and says which field is missing is a different document from one that says Potato cannot do dialogue.
The helper¶
scripts/report_issue.py assembles the report. It collects the things every
report needs and a reporter never has to hand, and it will not file anything on
its own.
python <skill-dir>/scripts/report_issue.py bug \
--title "Waveform never generates for media_directory audio" \
--observed "every POST to /api/waveform/generate returns use_client_fallback: true" \
--expected "a cached waveform, as audio_annotation documents" \
--repro "potato start config.yaml -p 8000, open an item, watch the network tab" \
--config config.yaml --log server.log --data data/items.json
python <skill-dir>/scripts/report_issue.py feature \
--title "Per-utterance audio in a dialogue display" \
--goal "annotate a voice-agent call where each turn is its own recording" \
--tried "audio_dialogue and speech_transcript both resolve one file per item" \
--needed "turn entries that each carry their own audio url"
It writes potato-issue-<kind>.md, prints the body, and prints a GitHub URL
with the title and body already filled in. What it adds on top of what you
typed:
- The version, asked of Potato rather than reconstructed. If the installed
Potato answers
potato --version, that output goes into the report verbatim. Nothing outside Potato can work the answer out reliably: on one machine on 2026-09-09 the installed metadata said 2.7.0 while the source said 2.8.2, because dist-info is only rewritten on install; the same call answered 2.8.2 from inside the Potato repository, where a localpotato_annotation.egg-infoshadows the site-packages record; andpotato.__version__was absent from every other directory, because a stalesite-packages/potato/holding onlytemplates/madepotatoresolve as a namespace package while each submodule still imported fine. Releases up to 2.7 have no--version-- it exits 2 with an argparse usage block -- and the helper then falls back to the metadata version plus the commit, which pip records for an editable install, and says in the report that the number may lag the source. - Whether the config passes
--strict, with the output. This is the check above, done for you and shown to the reader. - The data by shape — field names and value types of the first record, with a count. Never the values, because those are someone's corpus.
- A duplicate search. GitHub's issue search ANDs bare terms, so the obvious query matches nothing however many duplicates exist; the helper ORs the distinctive words and ranks what comes back by how much of your title each result shares. It says so when the search could not run, which is not the same answer as finding nothing.
Redaction¶
Configs hold secret_key, ai_support.ai_config.api_key, crowd credentials
and annotator email addresses, and log tails hold bearer tokens. The helper
removes them before anything is written: any value under a key named like a
secret, anything shaped like an API key or a token, email addresses, and the
home directory out of every path. It then prints what it removed and how often,
because a redactor you cannot audit is worse than no redactor.
It does not remove things that are not secrets but might still be yours:
endpoint hostnames, task and file names, label vocabularies, item ids, and any
prose you typed into --observed. Read the body before it goes anywhere. That
is what the printed copy and the report file are for.
Filing it¶
Two paths, and both of them need the person whose study it is to say yes first.
By hand. Open the prefilled URL. Nothing needs installing and the person sees the form before anything is public.
With gh. Add --submit --confirm. Both flags are required, and the helper
refuses to file a body still holding a _(fill this in)_ placeholder.
python <skill-dir>/scripts/report_issue.py bug \
--title "..." --observed "..." --expected "..." --repro "..." \
--config config.yaml --submit --confirm
It files under whichever account gh is logged in as, on a public repository,
and it cannot be withdrawn. Show the body and get an answer before passing
--confirm. --label needs write access on the repository, which a reporter
usually does not have; the kind is in the title as [bug] or [feature]
either way.
What I have and have not verified¶
Run against Potato 2.7.0 on 2026-09-09, with gh 2.85.0:
- Redaction, on a config carrying
secret_key, anapi_key, an authorized-user list of email addresses, a bearer token in the log, and absolute paths under the home directory. None of the five appears in the report. - Both version branches. Against a Potato that answers
potato --version, the report carries that output unchanged. Against one that does not -- a stub onPATHexiting 2 with a usage block, which is what 2.7 and earlier do -- the fallback gives the metadata version, the commit from pip's editable record, and a line saying the number can lag the source. - The duplicate search, both ways. A title sharing three words with an existing issue returned it; four titles with no duplicate returned nothing rather than filler. Rate-limited and offline both report themselves.
- Both refusals:
--submitwithout--confirm, and--submiton a body with placeholders. Each exits 2 and files nothing. - The URL falls back to the bare
/issues/newwhen the body is too long to carry in a query string.
Not verified: the gh issue create call itself, because verifying it means
filing a real issue on a public repository. The flags it passes are the ones
gh issue create --help documents, and the body goes through --body-file
rather than an argument. Treat the first real --submit as the test.