AI Feedback Coder
Automates coding feedback items marked as ai_code in public.app_feedback.
Workflow
1. Query the candidate shortlist
Use mcp__plugin_supabase_supabase__execute_sql with project ID iryqgmjauybluwnqhxbg.
Fetch a short list (not one row) plus the total count, so the user can choose
which item to take — not just the front of the queue:
SELECT id, feedback_type, system_id, subject,
left(message, 140) AS preview, satisfaction_rating, created_at
FROM public.app_feedback
WHERE status = 'ai_code'
ORDER BY created_at ASC
LIMIT 4;
SELECT count(*) FROM public.app_feedback WHERE status = 'ai_code';
ORDER BY created_at ASC = oldest first (front of the FIFO queue). The oldest is
the default pick, but the user may take any of the shown items.
2. If none found
Report "Ei ai_code-palautteita jonossa" and stop.
3. Let the user pick which feedback to take
MANDATORY — never silently grab the first row. Present the shortlist with
AskUserQuestion (single-select) and use the preview field so the user can
compare the candidates side by side:
- One option per feedback (up to 4). Label =
[feedback_type]+ subject (or message) truncated to ~40 chars. Preview = a monospace block withfeedback_type,system_id,created_at(date),satisfaction_rating(as ★ if present), and the message/preview text. - The oldest (front of queue) must be the first option so Enter takes it.
- Add a final "Lopeta" option.
Handle the response:
- A feedback option → that row becomes the active feedback; continue to step 4.
- "Lopeta" → stop.
Even if only one candidate exists, still show it (single option + "Lopeta") so the user confirms the target before any work begins.
4. Fetch the full record + comments
The shortlist query returned only summary columns. Re-select every field for the chosen id, and load the comments (they often carry the real detail):
SELECT * FROM public.app_feedback WHERE id = '<id>';
SELECT * FROM public.feedback_comments
WHERE feedback_id = '<id>'
ORDER BY created_at ASC;
admin_notes (field on app_feedback) and any comment with is_internal = true
are admin directives — the admin's own instructions on how to fix the item.
Treat them as authoritative (see step 5's "Admin directives" block). Regular
non-internal comments are usually from the reporter; internal ones are admin.
5. Build context block
Assemble all feedback fields into a structured context:
- id, system_id, feedback_type, subject, message
- satisfaction_rating, email, allow_contact
- status, admin_notes, promoted_idea_id
- page_url, user_agent, browser_history
- kipina_id, created_at
- All comments (message, is_internal, created_at)
The system_id field tells which app the feedback concerns — see src/lib/systems.ts for the app registry.
Admin directives (authoritative)
Extract a separate Admin-ohjeet group from these two sources and treat it as the admin steering the fix — the admin uses these fields to tell the AI how to implement, not just what the reporter said:
admin_notes— the admin's implementation instructions. These are frequently concrete and directive (real examples: "Tee ilman taustaa. Ikonityyppinen.", "Vasen yläkulma. Kun kipinäkortti on pienennetty, näytä pieni ikoni…").- Internal comments (
is_internal = true) — admin-only notes, often a threaded refinement of the instruction.
Priority rule: admin directives outrank the raw user message. When they add
detail, follow it. When they conflict with the reporter's literal request,
follow the admin and flag the conflict in the plan (step 7). If there are no
admin directives, proceed from the user message as before.
6. Clarity gate — is the feedback actionable?
MANDATORY before planning or routing. Do NOT start coding on a vague item. Judge whether the feedback is specific enough to implement correctly. Treat it as UNCLEAR when any of these hold:
- The request is generic ("ei toimi", "korjaa tämä", "huono") with no specifics.
- The target is ambiguous — you cannot tell which page/component/flow from the
message +
page_url+ comments. - The desired outcome has multiple reasonable interpretations.
- For a bug, reproduction cannot be inferred.
Admin directives resolve ambiguity. Judge clarity from the combined
picture: user message + admin directives (step 5). If admin_notes or an
internal comment already names the target and the intended fix, treat the item
as CLEAR and follow the admin's instruction — even when the raw user message is
vague. Only fall through to the UNCLEAR path when both the user message and the
admin directives are insufficient.
If the feedback IS clear, continue to step 7.
If UNCLEAR → use AskUserQuestion to resolve it BEFORE any plan. Ask one crisp,
targeted question (what exactly is broken, on which page, what is the expected
result) — not a barrage. Offer:
- "Tarkenna" (Other / free text) → user supplies the missing detail → re-run this gate with the new information.
- "Ohita" → mark the feedback
read(see step 9's UPDATE, statusread) so a human can triage it, then return to the picker (step 1). - "Koodaa silti" → the user accepts a best-effort interpretation; proceed, but state your assumptions explicitly in the plan (step 7) and in the audit comment (step 10).
Never guess silently and start editing files — asking one question is cheap; shipping the wrong fix is not.
7. Announce and present plan
Tell the user:
Palaute: [subject or message truncated to ~80 chars] Tyyppi: [feedback_type] | Sovellus: [system_id] | Jaljella: N kpl
Admin-ohje: [admin_notes / internal-kommentti sanatarkasti — TAI "ei admin-ohjetta"]
Suunnitelma:
- [Konkreettinen askel 1 — esim. "Muokkaa ComponentX.tsx: lisaa null-tarkistus"]
- [Konkreettinen askel 2]
- ...
Tiedostot joihin koskee: file1.tsx, file2.ts
The Admin-ohje line is mandatory: quote the admin directive verbatim (or state there is none). The plan steps must implement what the admin directed — if the admin's instruction and the reporter's literal request differ, follow the admin and add a short note in the plan (e.g. "Admin ohjaa X:ään, ei käyttäjän pyytämään Y:hyn").
MANDATORY — always present this plan and wait for user confirmation using the AskUserQuestion tool before proceeding.
Use AskUserQuestion with these options:
- "Jatka" (description: "Toteuta suunnitelma sellaisenaan")
- "Anna lisäohjetta" (description: "Muokkaa suunnitelmaa ennen toteutusta")
- "Peruuta" (description: "Ohita tämä palaute, merkitse luetuksi")
Handle the response:
- Jatka → proceed to step 8 (route to implementation)
- Anna lisäohjetta → user types instructions via "Other" → adjust plan and re-present with AskUserQuestion again
- Peruuta → mark feedback as
readinstead offixedand stop - Other (free text) → treat as instructions, adjust plan and re-present
To build a good plan, use the Explore agent or Grep/Glob to find the relevant files before presenting. The plan should be concrete enough that the user can judge if the approach is correct.
8. Route to the right implementation skill
After user approves the plan, invoke the appropriate skill(s) directly based on what the feedback requires. Do NOT delegate to using-superpowers — route directly.
Routing table
| Feedback concerns | Invoke skill | Context to pass |
|-------------------|-------------|-----------------|
| Bug / unexpected behavior | systematic-debugging | Error description, reproduction steps, page_url |
| UI / layout / component issue | frontend-design | Component name, expected vs actual behavior |
| Admin panel issue | admin-panel-builder | Admin page, tab, what's broken/missing |
| Database / RLS / migration needed | supabase-migration-writer | Table, operation, expected behavior |
| Edge Function issue | edge-function-generator | Function name, error, expected behavior |
| AI feature / prompt issue | ai-prompt-manager | Feature name, prompt, expected behavior |
| Idea Machina specific | idea-machina | Evolution stage, component, issue |
| Practice / gamification | practice-gamification | Practice type, scheduling, completion |
| New feature (general) | brainstorming first, then implementation skill | Full feedback context + plan |
| Type errors / Supabase types | supabase-typing-architect | File, type error, schema |
| i18n / translation | language-specialist | Missing key, language, component |
| Performance | performance-auditor | Slow query, component, metric |
Multiple skills may apply. For example, a feature request may need brainstorming → supabase-migration-writer → frontend-design. Run them in sequence.
If the feedback doesn't clearly match any skill, use code-guru for general implementation guidance.
9. Update feedback status
MANDATORY — always run after implementation, even if implementation was partial:
UPDATE public.app_feedback SET status = 'fixed' WHERE id = '<id>';
10. Add admin comment documenting what was done
MANDATORY — write a concise Finnish summary of every change made (files modified, features added, bugs fixed). This is the audit trail.
feedback_comments.user_id is uuid and NOT NULL, so the author must be a
real uuid. Use the oldest auth user (an admin) — do NOT cast to text or fall
back to a text literal (the column is uuid; a text expression errors with
42804: column "user_id" is of type uuid but expression is of type text). The
feedback's own user_id is often NULL, so it is not a usable fallback.
INSERT INTO public.feedback_comments (feedback_id, user_id, message, is_internal)
VALUES (
'<id>',
(SELECT id FROM auth.users ORDER BY created_at ASC LIMIT 1),
'AI-toteutettu: <1-3 lauseen yhteenveto tehdyistä muutoksista, esim. "Korjattu sidebar-komponentin renderöintiongelma. Muokattu sidebar.tsx: lisätty null-tarkistus props-arvoille.">',
true
);
The comment message must include:
- What was changed (file names or component names)
- Why (link back to the feedback problem)
- If an admin directive steered the work, note it (e.g. "admin-ohjeen mukaan ikoni vasempaan yläkulmaan") — and, if the admin instruction overrode the reporter's literal request, say so
- Keep it under 300 characters
11. Report and continue
Report the final count, then use AskUserQuestion to offer continuing:
- "Seuraava" (description: "Hae ja käsittele seuraava ai_code-palaute") — this must be the first option so Enter accepts it
- "Lopeta" (description: "Lopeta palautekierros")
If user picks "Seuraava", go back to step 1. If "Lopeta", stop.
Data Models
app_feedback (public schema)
| Field | Type | Notes | |-------|------|-------| | id | uuid | PK | | user_id | uuid | nullable (often NULL — anon feedback) | | system_id | text | e.g. 'raamattu-nyt', 'idea-machina' | | feedback_type | text | 'bug', 'feature', 'general', 'question' | | subject | text | nullable | | message | text | required | | satisfaction_rating | int | 1-5, nullable | | email | text | nullable | | allow_contact | boolean | | | status | text | 'new', 'read', 'handled', 'fixed', 'ai_fix', 'ai_code', 'idea', 'idea_implemented' | | admin_notes | text | nullable — admin directive: the admin's fix instructions (authoritative, see step 5) | | promoted_idea_id | text | nullable, FK to feature_suggestions | | kipina_id | text | nullable | | page_url | text | nullable | | user_agent | text | nullable | | browser_history | jsonb | nullable | | created_at | timestamptz | |
feedback_comments (public schema)
| Field | Type | Notes |
|-------|------|-------|
| id | uuid | PK |
| feedback_id | uuid | FK to app_feedback.id |
| user_id | uuid | NOT NULL — use oldest auth.users id (see step 10) |
| message | text | |
| is_internal | boolean | true = admin-only internal note → admin directive (see step 5). false = usually the reporter |
| created_at | timestamptz | |
Configuration
- Supabase project ID:
iryqgmjauybluwnqhxbg - Tool:
mcp__plugin_supabase_supabase__execute_sql