Concepts

Reports and boards

Bug reports and QA findings your coding agent can read, reproduce, answer, and resolve.

Myna Feedback collects bug reports, QA findings, and feature requests with enough context that a coding agent can act on them — then gives that agent the CLI and MCP tools to investigate, reply, and resolve, without anyone copying information between a ticket and a terminal.

It is a product a project enables. Routes below answer PRODUCT_NOT_ENABLED until it is on.

What a report is

Report
├── title, body                 what happened
├── type                        bug | qa | feedback | feature_request
├── status                      triage → open → in_progress → needs_retest → resolved
├── priority                    low | normal | high | urgent
├── labels
├── fields                      board-defined, validated against its intake form
├── context                     supplied by your application: version, commit, route
├── timeline                    the conversation and every state change, append-only
├── attachments                 screenshots, recordings, logs — uploaded, never captured
└── links                       the commit, the pull request, the release

Each report has a per-project number, so #42 works anywhere an id does.

Reports are submitted, never harvested

Myna does not take screenshots, record sessions, read the DOM, wrap fetch, patch console, or fingerprint browsers. A report is what a person typed on a form, the files they chose to attach, and the context your application chose to send. See collecting feedback for how a submission reaches Myna from your own site.

That limit is what lets you put Feedback in front of your users without asking them to trust a script with everything on the page.

Boards

Reports arrive on a board. A project can run several — a private QA board for testers, a bug board, a suggestion board.

A board's intake form is declared with the same field DSL as a collection schema. A QA board that needs a build number and a test-case id says so, and gets the same validation, the same field types, and the same errors as anything else in Myna:

myna feedback boards create --key qa --name "QA" --type qa

The board declares every question, including the ones that produce the report's own title and description and the address a reply goes to. Those carry a target naming what they fill:

[
  { "key": "headline", "type": "text", "label": "What went wrong?", "target": "title" },
  { "key": "steps", "type": "markdown", "label": "How do we reproduce it?", "target": "body" },
  { "key": "build", "type": "text", "label": "Build number", "required": true },
  { "key": "proof", "type": "file", "label": "Show us", "target": "attachments", "max": 5 }
]

The four targets are title, body, reporterEmail, and attachments. The last one asks for evidence, and it is the switch as well as the question: a board whose form declares it takes files on every surface — the React components, the built-in widget, your own form — and a board that removes it takes none anywhere. max is how many, capped at 20 and 3 if it goes unsaid.

A field without a target is a custom one, stored under fields[key] and validated by the DSL. Exactly one field must target title, because a report cannot exist without one; everything else is the board's call, including asking for no email at all. A board that has never been configured reads back four default questions rather than an empty list, so a form can always render itself from this alone.

What a target does not move is the shape underneath. report.title is report.title on every board in every project however the question was worded, which is what lets an agent's tools behave identically everywhere. Status, priority, assignee, and labels are never asked for on intake — they are triage decisions, not questions for a reporter.

A board also carries guidance: house style for triaging and answering reports there, the same idea as a collection's guidance, and travelling to agents the same way.

Two more settings decide how reporters reach it:

myna feedback boards update qa --app-url https://app.example.com/support --require-identity

appUrl is where your product shows a reporter their report. Myna serves no reporter-facing page, so notification emails link there; without it they carry the update and the report number and no button. requireIdentity refuses any submission that does not carry an identity your backend signed — see collecting feedback.

The agent workflow

myna feedback reports list --status triage,open --board qa
myna feedback reports open 42
myna feedback attachments get 42 rat_01k… --out screenshot.png
myna feedback reports reply 42 "Reproduced — the upgrade button posts to the old endpoint." 
myna feedback reports resolve 42 --commit 81acbe --reply "Fixed and deploying." --retest

open is the command the group exists for. One call prints the description, the environment your app supplied, the board's guidance, every custom field, the whole timeline, and what evidence exists. The equivalent MCP tool, myna_get_report, returns the same thing in one call — assembling a bug report from six requests is the problem this product exists to remove.

MCP tools

Fourteen, and only when the project runs Feedback:

Tool What it does
myna_list_reports Filter by board, status, type, priority, assignee, labels, free text, activity time
myna_get_report Everything about one report, in one call
myna_get_report_attachment Fetch evidence; images come back as images the model can look at
myna_reply_to_report Public reply to the reporter, or an internal note
myna_update_report Status, priority, assignee, labels, title, body
myna_resolve_report Resolve, link the fix, and reply — one call
myna_request_retest Ask the reporter to confirm
myna_merge_report Mark a report a duplicate of another; neither is deleted
myna_move_report Move a report to another board on the same project
myna_find_similar_reports Candidates that may be the same problem
myna_report_digest What came in since a time or a release, ready to act on
myna_list_reporters / myna_get_reporter Who filed what, and what your application says about them
myna_block_reporter Moderation: stop somebody filing, reading back, or being emailed

Replies are internal by default

Every comment states who it is for. internal is a note between the people and agents working the report; public reaches whoever filed it. The default is internal everywhere it is not stated, because the failure that matters is a private note reaching a stranger, not the reverse.

myna feedback reports reply 42 "Probably the same as #38"          # internal
myna feedback reports reply 42 "Fixed in today's release." --public # the reporter sees this

Closing the loop

Resolving with --retest moves the report to needs_retest instead of declaring the matter closed, and emails whoever filed it.

myna feedback reports resolve 42 --commit 81acbe --reply "Fixed the endpoint." --retest

No account, no password, no GitHub sign-in — demanding one from somebody who filed a single bug is how the confirmation never arrives. They read the public conversation and either confirm the fix or reopen it, and the report moves accordingly.

They do that in your application, not on a Myna page: @myna-sh/react/feedback and the SDK's read-back calls show a reporter their own reports, authenticated by the ingest key plus the identity your backend signed. See reading a report back. What they see is the public timeline only — internal notes never appear, and the team is attributed as "team" rather than by name.

The email links to the board's appUrl, which is a page in your product. Without one the message still says what happened and quotes the report number.

Reporters are emailed when their report arrives, when someone replies publicly, when a retest is asked for, and when it is resolved. Internal notes never trigger a message.

report.needs_retest is its own webhook event, distinct from report.resolved, so a notification can treat "please check this" differently from "done". Prefer it whenever you could not reproduce the problem yourself.

The digest

Before deciding what to work on, ask what came in:

myna feedback digest --release 41
myna feedback digest --since 2026-08-01T00:00:00Z

Counts by status, board and type, plus the reports themselves already ordered by priority then recency — the order somebody should work in. myna_report_digest is the same thing as one MCP call, which is what makes it worth having: it answers in one request what would otherwise be several pages of myna_list_reports.

It is arithmetic, not a generated summary. If it truncates, it says so.

The people who report

Every report that arrives from outside the tenancy carries a reporter: whoever the submission identified, by an email they typed or an id your backend signed.

myna feedback people list
myna feedback people show usr_1042
myna feedback reports list --reporter usr_1042 --property plan:enterprise

One distinction runs through all of it. verified means a signature your own backend produced, and it is the only field Myna stands behind. An email somebody typed into a form is an address to reply to; properties is what your system said about its own user, true of your system rather than checked here.

Blocking stops somebody filing, reading their reports back, or being emailed. It never deletes or hides what they already filed — a report is evidence about your product regardless of what its author did afterwards.

myna feedback people block usr_1042
myna feedback people unblock usr_1042

Moving a report between boards

A bug filed on the ideas board, a QA finding that belongs with the bugs:

myna feedback reports move 42 --board bugs

Ordinary triage. Every board on a project is read by the same people, so a move changes which queue a report sits in and nothing about who can see it.

Linking the release that shipped it

Link a resolved feature request to the release that shipped it:

myna feedback reports link 42 --kind release --value 41

A release is a published change set in Content — so a report can name the release that fixed it without anybody maintaining that connection by hand, and the changelog entry that announced it is an ordinary content entry published through the same review and pull-request flow as everything else.

Nothing is destroyed

The timeline is append-only. A status change writes a row rather than overwriting one, so "resolved on Tuesday, reopened on Thursday, resolved again on Friday" survives as three facts. Reopening is always allowed: a bug that came back was never fixed, and refusing to say so helps nobody.

Merging a duplicate does not delete it either. It points at the canonical report and keeps its own timeline, because the second person to hit a bug usually said something the first did not.

This is Feedback's version of the guarantee change sets give content.

Attachments are evidence, not assets

Report attachments are a separate thing from your asset library. They are private by default, never published, retention-bound, and metered separately — so evidence uploaded by a stranger can never consume the storage you bought for your media, and a Feedback quota problem can never stop content from publishing.

There is no stable public URL for an attachment. Reads return a short-lived signed link, because a URL that keeps working is a URL that outlives the reason it was shared.

Allowed types are images, webm/mp4/quicktime video, plain text, JSON, and PDF. QuickTime is on the list because that is what a Mac screen recording is, and nobody should have to transcode a file to file a bug.

Every upload is granted and then claimed. Asking for one returns a url, a method and a set of headers: send the bytes exactly as they say, then name the uploadId when you file the report. An upload nobody claims expires and its object is deleted, so a grant reserves nothing — which is what makes it safe to hand one to whoever is looking at your site.

The bytes go to Myna, on an address your project's registered origins govern like every other browser-facing call. They used to go straight to the storage bucket with a presigned URL, which is cheaper and unusable from a browser: the bucket answers the CORS preflight, and no origin you register with Myna can change what the bucket allows. Uploads failed at OPTIONS against a hostname you had never heard of.

What is stored is checked before it exists, against both its declared type and its declared size. A body larger than the slot it was granted is refused at the header; content that is not what it claims to be is refused from its leading bytes. Both are checked again when the upload is claimed.

Scopes

Scope Grants
feedback:read Read reports, timelines, and attachments
feedback:write Reply, triage, transition, link
feedback:manage Delete, moderate, block reporters, change board settings, mint and rotate ingest keys

project:admin does not grant these — it covers Content and project capabilities only, and never widens. Use admin:all for a key that should reach every product. See API key scopes.

Webhooks

report.created, report.updated, report.status_changed, report.commented, report.resolved, report.reopened, report.needs_retest, and report.merged. See Webhook events.