Kelly Feedback
App UI Screenshots
Overview
Use this skill as Kelly's voice-of-customer desk. It aggregates raw user
feedback from every channel (support email, Discord, Slack, X replies,
app-store reviews, in-app surveys, interviews) into one Busabase App-in-Skill
dashboard, where the agent's dedupe/clustering work becomes feature requests
with frequency and user weight, and agent-proposed roadmap changes wait in a
decision queue for Kelly's verdict.
Division of labor: the skill (scripts + agent) collects, clusters, and
drafts; the app is where Kelly triages feedback and decides on proposals;
approved roadmap changes are exported/executed by the agent outside the app
(updating a roadmap doc or changelog, handing decline replies to
kelly-messenger or kelly-email). Kelly Feedback sits downstream of
kelly-email, kelly-messenger, and kelly-social: those skills' agents hand
feedback payloads to scripts/ingest_feedback.mjs.
Default behavior is AirApp-first. Unless the user explicitly asks only for
explanation, ingest whatever payloads are available and give the user the
clickable AirApp URL (or the local preview URL when local preview is
explicitly requested). Use chat-only mode only when the user says "纯聊天",
"chat only", "不要打开 UI", or similar; in that mode present proposals as
numbered items (Proposal #1, #2, ...) and record verdicts by asking the
agent to run the same writes busabase-sdk-side that the app would.
This skill is an implementation of the App-in-Skill pattern — a Codex/agent skill paired with a small companion UI for review and approval. See the spec paper: https://mr-kelly.github.io/research/app-in-skill-specification-for-pairing-agent-skills-with-a-local-companion-ui.pdf.
Mandatory Dependencies
- Read and follow
$kelly-app-skill-creator for product behavior, visual
quality, responsive layout, and the complete canonical content/kelly-feedback-app/ artifact.
- Read and follow
$busabase for connection, target Space, node discovery,
ChangeRequests, review, and merge behavior.
- Read and follow
$busabase-app-creator for resource modeling, AirApp
runtime limits, security, validation, and deployment.
If a dependency is unavailable, preserve this skill's local artifact and
product contracts, stop before the unavailable Busabase operation, and report
the exact missing dependency. Do not invent a second data backend.
Boundary
- The AirApp reads Busabase records and writes only feedback triage, request effort estimates, and roadmap-proposal verdicts. It must not call feedback platforms, post replies, publish changelogs, or mutate remote systems.
- Any outbound side effect — replying to a user, publishing a changelog note, editing a public roadmap — is approval-required and executed by the agent via other skills (kelly-messenger, kelly-email, docs edits) only after the matching proposal is approved and
scripts/execute_decisions.mjs --apply marks it handoff_ready. scripts/execute_decisions.mjs never sends anything itself.
- Own-community data only: ingest feedback addressed to Kelly's own products from Kelly's own channels and accounts via
scripts/ingest_feedback.mjs. Do not scrape third-party communities or collect data about other companies' users.
- Treat feedback as user PII-adjacent. Never commit real tokens, raw platform exports, or Busabase credentials.
Busabase Resources
Eight Bases under one application Folder (kelly-feedback), declared in
content/kelly-feedback-app/app/js/config.js and the generated template sidecars under content/:
products: products feedback can be about (id, display name, tagline).
sources: feedback channels — email, Discord, Slack, X, app-store, survey, interview — with collection method, env-var names for tokens (never values), freshness, and item count.
feedback: raw feedback items normalized from every channel, one row per item (user context, text, sentiment, triage, linked request-id).
requests: clustered feature requests (title, product, status, trend, effort estimate, problem statement, spec summary, representative feedback ids, decision history). frequency/weighted-score are not stored — they are always recomputed client-side from feedback by recomputeDerived().
roadmap: roadmap lane items (now/next/later), read-only in the app; changed only via approved proposals executed by scripts/execute_decisions.mjs.
proposals: the decision queue — agent-proposed promote/decline/merge/publish-changelog changes with reason, evidence, an editable draft, the human verdict (status/review-note/decided-at), written directly onto the proposal record.
sync-log: append-only history of ingest/cluster/execute runs.
settings: one row (record-id: "config") with plan-weight scoring and roadmap lane names.
Resources provision lazily through an idempotent Busabase ChangeRequest the
first time the app runs in a Space; see references/feedback-schema.md for
exact field shapes. Request frequency/weighted_score and every snapshot
metric are recomputed client-side from feedback/requests/proposals on
every read via recomputeDerived() — never stored, so numbers always
agree after any merge.
First Run And Onboarding
On invocation, check the sources Base. If empty, guide setup before
ingesting real feedback.
Onboarding asks, turn by turn (non-secret setup details only):
- Products: id, display name, one-line tagline for each product feedback can be about.
- Sources: which channels exist (email/discord/slack/x/appstore/survey/interview), how each is collected (sibling-skill handoff, export, manual notes), and which env var names hold any tokens.
- Scoring weights: plan weights (e.g. free=1, pro=3, team=5), default weight, recency half-life.
- Roadmap lanes (default Now / Next / Later).
Never ask the user to paste secret values into chat. Secrets belong only in
local env files. Register products/sources by including them in the first
scripts/ingest_feedback.mjs payload's optional products[] field and
required source field (see references/feedback-schema.md); scoring
weights/roadmap lanes are written directly into the settings Base's single
record-id: "config" row via busabase-sdk.
Local App
Default behavior is AirApp-first — give the user the clickable AirApp URL.
Start pnpm --dir content/kelly-feedback-app dev only when local preview/debugging is explicitly
requested.
Required app views (hash routes):
#/overview: VoC command desk — human-attention panel (roadmap decisions waiting, new uncategorized feedback, requests needing info), feedback-this-week inflow by channel with platform badges, sentiment split bars (inline SVG), top clusters by momentum, and freshness per source.
#/inbox and #/inbox/<feedback_id>: raw feedback stream. Rows show channel badge, user handle, product, one-line preview, sentiment, cluster link, and triage state (new/clustered/ignored/insight). Detail shows the full text, user context (plan, tenure, revenue weight), source permalink, linked request, agent note, and triage buttons (assign to request / ignore / mark insight) that write directly onto the feedback record through busabase-sdk.
#/requests and #/requests/<request_id>: clustered feature requests with title, product, frequency, weighted score (frequency × user revenue weight), trend arrow, status (candidate/roadmap/declined/needs_info), and linked feedback count. Detail shows the agent-drafted problem statement and proposed spec summary, representative quotes, all linked feedback, an editable effort-estimate field, and decision history.
#/roadmap: decision queue of agent-proposed roadmap changes (promote to Now/Next/Later, decline with drafted reply, merge duplicates) with reason, evidence, editable draft, a Review note textarea, decision buttons (Approve / Request changes / Block), stable refs (Proposal #1), and standard workflow states (needs_review / changes_requested / approved / done / blocked) written directly onto the proposal record. The current roadmap columns (Now / Next / Later) render read-only below the queue.
#/settings: sanitized config summary — products, sources (channel + collection method), scoring weights, env readiness booleans, onboarding state, and recent sync log. Never expose secret values.
Demo mode:
?demo=overview, ?demo=inbox, ?demo=requests, and ?demo=roadmap select named mock scenes.
?demo=detail opens a request detail with representative quotes.
lang=en or lang=zh forces UI chrome language for screenshots.
- Demo mode never reads or writes Busabase; demo decisions are in-memory only.
UI language: support English and Chinese chrome with Auto default
(persisted override in the sidebar). Keep user quotes, handles, product
names, and imported data in their original language.
File Contract
Read references/feedback-schema.md before editing the app, scripts, or any
Busabase field shape.
Ingestion Workflow
scripts/ingest_feedback.mjs is the single write path for raw feedback.
Anyone with a payload — sibling skills' agents, platform exports, or manual
notes — hands feedback to Kelly Feedback the same way:
- Write a payload JSON file (shape in
references/feedback-schema.md): a source block (source_id, channel, name, collection) plus items[] with stable external_ids, text, user context, and timestamps. Optionally include products[] to register/update product catalog entries.
- Run
node skills/kelly-feedback/scripts/ingest_feedback.mjs <payload.json> [more.json ...] --apply.
- The script validates, dedupes by
fb-<source_id>-<external_id> (idempotent re-ingest), upserts the source/products, and appends a sync-log entry — all directly against Busabase. Omit --apply first to see a dry-run summary.
Typical handoffs: kelly-email exports support threads mentioning features;
kelly-messenger exports Discord/Slack community posts; kelly-social exports
X replies. Those skills' agents build the payload from their own data; this
skill never reads their private files directly. New items land with
triage: "new" and appear in the Inbox and the human-attention panel.
Clustering Workflow
Clustering is LLM work done by the agent; scripts/apply_clusters.mjs is the
deterministic write path.
- Read feedback from Busabase and pick out
triage: "new" items (and any requests needing re-scoring).
- As the agent, dedupe and cluster: group items expressing the same underlying need, draft or update request records (title, problem statement, proposed spec summary, representative feedback ids, trend), and decide non-request items (
ignored for spam, insight for bugs/patterns worth routing elsewhere).
- Write a cluster-assignment payload (shape in
references/feedback-schema.md) and run node skills/kelly-feedback/scripts/apply_clusters.mjs <assignments.json> --apply. The script validates ids, upserts request drafts, links feedback, and logs the run. Request frequency/weighted_score recompute automatically client-side — the script never writes them.
- When clusters warrant action, draft proposals directly into the
proposals Base (promote/decline/merge/changelog) with reason, evidence, and an editable public draft, then send Kelly to #/roadmap.
Roadmap Decision Workflow
- Kelly reviews
#/roadmap: edits drafts, writes review notes, and clicks Approve / Request changes / Block. The app writes the verdict directly onto the proposal record (status/review-note/draft/decided-at) through busabase-sdk — never a separate decisions file. From a standalone local preview the write merges immediately (trusted operator); from the deployed AirApp it creates a pending ChangeRequest for the trusted process to merge.
- Before executing, run
node skills/kelly-feedback/scripts/execute_decisions.mjs (dry-run) to see the plan for every approved proposal: update_roadmap (target lane) and merge_requests are LOCAL operations; publish_changelog_note (draft id) and send_decline_reply (handoff to kelly-messenger/kelly-email) are always handoff_ready — this script never publishes a changelog, edits a roadmap doc, or sends a reply itself.
- Show Kelly the dry-run summary. After confirmation, run with
--apply: LOCAL operations (roadmap lanes, merges) are applied directly to Busabase and the proposal's status is set done; outbound operations remain handoff_ready only.
- Execute
handoff_ready operations as the agent via the appropriate skill (send the decline reply through kelly-messenger/kelly-email, update the changelog/roadmap document), then log the outcome as a sync-log entry.
- For a proposal moved to
changes_requested, revise the draft per the review-note and write it back to needs_review directly via busabase-sdk.
Safety Defaults
- Treat every outbound message (decline replies, changelog posts, roadmap publications) as approval-required, one proposal at a time.
- Never invent feedback or inflate counts; frequency and weighted score must derive from real linked items via
recomputeDerived(), never be written directly.
- Keep stored content minimal: trimmed feedback text and safe permalinks, not raw platform API responses or attachments.
- Use stable ids everywhere (
external_id dedupe keys, request ids, proposal ids) so re-ingest and re-execution are idempotent.
scripts/execute_decisions.mjs performs no external side effect ever — it never sends a reply or publishes a changelog itself, even with --apply.
Useful Commands
node skills/kelly-feedback/scripts/ingest_feedback.mjs payload.json --apply
node skills/kelly-feedback/scripts/apply_clusters.mjs assignments.json --apply
node skills/kelly-feedback/scripts/execute_decisions.mjs
node skills/kelly-feedback/scripts/execute_decisions.mjs --apply
pnpm --dir skills/kelly-feedback/content/kelly-feedback-app dev
In normal use, invoke /kelly-feedback, let the skill ingest/cluster
whatever is available, and open the AirApp.
1---2name: kelly-feedback3description: Personal App-in-Skill voice-of-customer desk (Busabase App-in-Skill) aggregating user feedback from support email, Discord/Slack communities, X replies, app-store reviews, surveys, and interviews into clustered feature requests and a roadmap decision queue. Use when the user invokes $kelly-feedback or /kelly-feedback, or mentions user feedback, voice of customer, feature requests, roadmap decisions, feedback clustering, feedback triage, request dedupe, changelog replies, decline replies, or reviewing what users are asking for.4---56# Kelly Feedback78## App UI Screenshots910<table>11 <tr>12 <td width="50%"><img src="assets/screenshots/overview.webp" alt="Kelly Feedback overview"></td>13 <td width="50%"><img src="assets/screenshots/inbox.webp" alt="Kelly Feedback inbox"></td>14 </tr>15 <tr>16 <td><strong>Overview</strong><br>Voice-of-customer desk with weekly inflow by channel, sentiment split, top clusters, and source freshness.</td>17 <td><strong>Inbox</strong><br>Raw feedback stream across email, Discord, Slack, X, and app-store reviews with triage controls.</td>18 </tr>19 <tr>20 <td width="50%"><img src="assets/screenshots/requests.webp" alt="Kelly Feedback requests"></td>21 <td width="50%"><img src="assets/screenshots/roadmap.webp" alt="Kelly Feedback roadmap decisions"></td>22 </tr>23 <tr>24 <td><strong>Requests</strong><br>Clustered feature requests with frequency, weighted scores, trend, and representative quotes.</td>25 <td><strong>Roadmap decisions</strong><br>Agent-proposed promote/decline/merge proposals with drafted changelog notes and user replies for approval.</td>26 </tr>27</table>2829## Overview3031Use this skill as Kelly's voice-of-customer desk. It aggregates raw user32feedback from every channel (support email, Discord, Slack, X replies,33app-store reviews, in-app surveys, interviews) into one Busabase App-in-Skill34dashboard, where the agent's dedupe/clustering work becomes feature requests35with frequency and user weight, and agent-proposed roadmap changes wait in a36decision queue for Kelly's verdict.3738Division of labor: the skill (scripts + agent) collects, clusters, and39drafts; the app is where Kelly triages feedback and decides on proposals;40approved roadmap changes are exported/executed by the agent outside the app41(updating a roadmap doc or changelog, handing decline replies to42kelly-messenger or kelly-email). Kelly Feedback sits downstream of43kelly-email, kelly-messenger, and kelly-social: those skills' agents hand44feedback payloads to `scripts/ingest_feedback.mjs`.4546Default behavior is AirApp-first. Unless the user explicitly asks only for47explanation, ingest whatever payloads are available and give the user the48clickable AirApp URL (or the local preview URL when local preview is49explicitly requested). Use chat-only mode only when the user says "纯聊天",50"chat only", "不要打开 UI", or similar; in that mode present proposals as51numbered items (`Proposal #1`, `#2`, ...) and record verdicts by asking the52agent to run the same writes `busabase-sdk`-side that the app would.5354This skill is an implementation of the **App-in-Skill** pattern — a Codex/agent skill paired with a small companion UI for review and approval. See the spec paper: <https://mr-kelly.github.io/research/app-in-skill-specification-for-pairing-agent-skills-with-a-local-companion-ui.pdf>.5556## Mandatory Dependencies57581. Read and follow `$kelly-app-skill-creator` for product behavior, visual59 quality, responsive layout, and the complete canonical `content/kelly-feedback-app/` artifact.602. Read and follow `$busabase` for connection, target Space, node discovery,61 ChangeRequests, review, and merge behavior.623. Read and follow `$busabase-app-creator` for resource modeling, AirApp63 runtime limits, security, validation, and deployment.6465If a dependency is unavailable, preserve this skill's local artifact and66product contracts, stop before the unavailable Busabase operation, and report67the exact missing dependency. Do not invent a second data backend.6869## Boundary7071- The AirApp reads Busabase records and writes only feedback triage, request effort estimates, and roadmap-proposal verdicts. It must not call feedback platforms, post replies, publish changelogs, or mutate remote systems.72- Any outbound side effect — replying to a user, publishing a changelog note, editing a public roadmap — is approval-required and executed by the agent via other skills (kelly-messenger, kelly-email, docs edits) only after the matching proposal is approved and `scripts/execute_decisions.mjs --apply` marks it `handoff_ready`. `scripts/execute_decisions.mjs` never sends anything itself.73- Own-community data only: ingest feedback addressed to Kelly's own products from Kelly's own channels and accounts via `scripts/ingest_feedback.mjs`. Do not scrape third-party communities or collect data about other companies' users.74- Treat feedback as user PII-adjacent. Never commit real tokens, raw platform exports, or Busabase credentials.7576## Busabase Resources7778Eight Bases under one application Folder (`kelly-feedback`), declared in79`content/kelly-feedback-app/app/js/config.js` and the generated template sidecars under `content/`:8081- `products`: products feedback can be about (id, display name, tagline).82- `sources`: feedback channels — email, Discord, Slack, X, app-store, survey, interview — with collection method, env-var *names* for tokens (never values), freshness, and item count.83- `feedback`: raw feedback items normalized from every channel, one row per item (user context, text, sentiment, `triage`, linked `request-id`).84- `requests`: clustered feature requests (title, product, status, trend, effort estimate, problem statement, spec summary, representative feedback ids, decision history). `frequency`/`weighted-score` are **not stored** — they are always recomputed client-side from `feedback` by `recomputeDerived()`.85- `roadmap`: roadmap lane items (`now`/`next`/`later`), read-only in the app; changed only via approved proposals executed by `scripts/execute_decisions.mjs`.86- `proposals`: the decision queue — agent-proposed promote/decline/merge/publish-changelog changes with reason, evidence, an editable draft, the human verdict (`status`/`review-note`/`decided-at`), written directly onto the proposal record.87- `sync-log`: append-only history of ingest/cluster/execute runs.88- `settings`: one row (`record-id: "config"`) with plan-weight scoring and roadmap lane names.8990Resources provision lazily through an idempotent Busabase ChangeRequest the91first time the app runs in a Space; see `references/feedback-schema.md` for92exact field shapes. Request `frequency`/`weighted_score` and every snapshot93metric are recomputed client-side from `feedback`/`requests`/`proposals` on94every read via `recomputeDerived()` — **never stored**, so numbers always95agree after any merge.9697## First Run And Onboarding9899On invocation, check the `sources` Base. If empty, guide setup before100ingesting real feedback.101102Onboarding asks, turn by turn (non-secret setup details only):1031041. Products: id, display name, one-line tagline for each product feedback can be about.1052. Sources: which channels exist (email/discord/slack/x/appstore/survey/interview), how each is collected (sibling-skill handoff, export, manual notes), and which env var names hold any tokens.1063. Scoring weights: plan weights (e.g. free=1, pro=3, team=5), default weight, recency half-life.1074. Roadmap lanes (default Now / Next / Later).108109Never ask the user to paste secret values into chat. Secrets belong only in110local env files. Register products/sources by including them in the first111`scripts/ingest_feedback.mjs` payload's optional `products[]` field and112required `source` field (see references/feedback-schema.md); scoring113weights/roadmap lanes are written directly into the `settings` Base's single114`record-id: "config"` row via `busabase-sdk`.115116## Local App117118Default behavior is AirApp-first — give the user the clickable AirApp URL.119Start `pnpm --dir content/kelly-feedback-app dev` only when local preview/debugging is explicitly120requested.121122Required app views (hash routes):123124- `#/overview`: VoC command desk — human-attention panel (roadmap decisions waiting, new uncategorized feedback, requests needing info), feedback-this-week inflow by channel with platform badges, sentiment split bars (inline SVG), top clusters by momentum, and freshness per source.125- `#/inbox` and `#/inbox/<feedback_id>`: raw feedback stream. Rows show channel badge, user handle, product, one-line preview, sentiment, cluster link, and triage state (new/clustered/ignored/insight). Detail shows the full text, user context (plan, tenure, revenue weight), source permalink, linked request, agent note, and triage buttons (assign to request / ignore / mark insight) that write directly onto the feedback record through `busabase-sdk`.126- `#/requests` and `#/requests/<request_id>`: clustered feature requests with title, product, frequency, weighted score (frequency × user revenue weight), trend arrow, status (candidate/roadmap/declined/needs_info), and linked feedback count. Detail shows the agent-drafted problem statement and proposed spec summary, representative quotes, all linked feedback, an editable effort-estimate field, and decision history.127- `#/roadmap`: decision queue of agent-proposed roadmap changes (promote to Now/Next/Later, decline with drafted reply, merge duplicates) with reason, evidence, editable draft, a `Review note` textarea, decision buttons (Approve / Request changes / Block), stable refs (`Proposal #1`), and standard workflow states (needs_review / changes_requested / approved / done / blocked) written directly onto the proposal record. The current roadmap columns (Now / Next / Later) render read-only below the queue.128- `#/settings`: sanitized config summary — products, sources (channel + collection method), scoring weights, env readiness booleans, onboarding state, and recent sync log. Never expose secret values.129130Demo mode:131132- `?demo=overview`, `?demo=inbox`, `?demo=requests`, and `?demo=roadmap` select named mock scenes.133- `?demo=detail` opens a request detail with representative quotes.134- `lang=en` or `lang=zh` forces UI chrome language for screenshots.135- Demo mode never reads or writes Busabase; demo decisions are in-memory only.136137UI language: support English and Chinese chrome with `Auto` default138(persisted override in the sidebar). Keep user quotes, handles, product139names, and imported data in their original language.140141## File Contract142143Read `references/feedback-schema.md` before editing the app, scripts, or any144Busabase field shape.145146## Ingestion Workflow147148`scripts/ingest_feedback.mjs` is the single write path for raw feedback.149Anyone with a payload — sibling skills' agents, platform exports, or manual150notes — hands feedback to Kelly Feedback the same way:1511521. Write a payload JSON file (shape in `references/feedback-schema.md`): a `source` block (`source_id`, `channel`, `name`, `collection`) plus `items[]` with stable `external_id`s, text, user context, and timestamps. Optionally include `products[]` to register/update product catalog entries.1532. Run `node skills/kelly-feedback/scripts/ingest_feedback.mjs <payload.json> [more.json ...] --apply`.1543. The script validates, dedupes by `fb-<source_id>-<external_id>` (idempotent re-ingest), upserts the source/products, and appends a `sync-log` entry — all directly against Busabase. Omit `--apply` first to see a dry-run summary.155156Typical handoffs: kelly-email exports support threads mentioning features;157kelly-messenger exports Discord/Slack community posts; kelly-social exports158X replies. Those skills' agents build the payload from their own data; this159skill never reads their private files directly. New items land with160`triage: "new"` and appear in the Inbox and the human-attention panel.161162## Clustering Workflow163164Clustering is LLM work done by the agent; `scripts/apply_clusters.mjs` is the165deterministic write path.1661671. Read feedback from Busabase and pick out `triage: "new"` items (and any requests needing re-scoring).1682. As the agent, dedupe and cluster: group items expressing the same underlying need, draft or update request records (title, problem statement, proposed spec summary, representative feedback ids, trend), and decide non-request items (`ignored` for spam, `insight` for bugs/patterns worth routing elsewhere).1693. Write a cluster-assignment payload (shape in `references/feedback-schema.md`) and run `node skills/kelly-feedback/scripts/apply_clusters.mjs <assignments.json> --apply`. The script validates ids, upserts request drafts, links feedback, and logs the run. Request `frequency`/`weighted_score` recompute automatically client-side — the script never writes them.1704. When clusters warrant action, draft proposals directly into the `proposals` Base (promote/decline/merge/changelog) with reason, evidence, and an editable public draft, then send Kelly to `#/roadmap`.171172## Roadmap Decision Workflow1731741. Kelly reviews `#/roadmap`: edits drafts, writes review notes, and clicks Approve / Request changes / Block. The app writes the verdict directly onto the proposal record (`status`/`review-note`/`draft`/`decided-at`) through `busabase-sdk` — never a separate decisions file. From a standalone local preview the write merges immediately (trusted operator); from the deployed AirApp it creates a pending ChangeRequest for the trusted process to merge.1752. Before executing, run `node skills/kelly-feedback/scripts/execute_decisions.mjs` (dry-run) to see the plan for every `approved` proposal: `update_roadmap` (target lane) and `merge_requests` are LOCAL operations; `publish_changelog_note` (draft id) and `send_decline_reply` (handoff to kelly-messenger/kelly-email) are always `handoff_ready` — this script never publishes a changelog, edits a roadmap doc, or sends a reply itself.1763. Show Kelly the dry-run summary. After confirmation, run with `--apply`: LOCAL operations (roadmap lanes, merges) are applied directly to Busabase and the proposal's status is set `done`; outbound operations remain `handoff_ready` only.1774. Execute `handoff_ready` operations as the agent via the appropriate skill (send the decline reply through kelly-messenger/kelly-email, update the changelog/roadmap document), then log the outcome as a `sync-log` entry.1785. For a proposal moved to `changes_requested`, revise the draft per the `review-note` and write it back to `needs_review` directly via `busabase-sdk`.179180## Safety Defaults181182- Treat every outbound message (decline replies, changelog posts, roadmap publications) as approval-required, one proposal at a time.183- Never invent feedback or inflate counts; frequency and weighted score must derive from real linked items via `recomputeDerived()`, never be written directly.184- Keep stored content minimal: trimmed feedback text and safe permalinks, not raw platform API responses or attachments.185- Use stable ids everywhere (`external_id` dedupe keys, request ids, proposal ids) so re-ingest and re-execution are idempotent.186- `scripts/execute_decisions.mjs` performs no external side effect ever — it never sends a reply or publishes a changelog itself, even with `--apply`.187188## Useful Commands189190```bash191node skills/kelly-feedback/scripts/ingest_feedback.mjs payload.json --apply192node skills/kelly-feedback/scripts/apply_clusters.mjs assignments.json --apply193node skills/kelly-feedback/scripts/execute_decisions.mjs194node skills/kelly-feedback/scripts/execute_decisions.mjs --apply195pnpm --dir skills/kelly-feedback/content/kelly-feedback-app dev196```197198In normal use, invoke `/kelly-feedback`, let the skill ingest/cluster199whatever is available, and open the AirApp.