kelly-feedback · diff

git:20260711.c9add96 to git:20260806.bed1b85

126 added, 85 removed. Audit A to A.

---
name: kelly-feedback
- description: Personal App-in-Skill voice-of-customer desk for 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.
+ description: 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.
---
# Kelly Feedback
- ## 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 file-backed 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 this skill's ingest script.
-
- Default interaction mode: App UI. Unless the user explicitly asks for chat-only handling, check onboarding/config, refresh or load the local feedback snapshot, start/reuse the local app with `app/start.sh`, and give the actual local URL. 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 in `decisions.json`.
-
## App UI Screenshots
<table>
<tr>
<td width="50%"><img src="assets/screenshots/overview.webp" alt="Kelly Feedback overview"></td>
<td width="50%"><img src="assets/screenshots/inbox.webp" alt="Kelly Feedback inbox"></td>
</tr>
<tr>
<td><strong>Overview</strong><br>Voice-of-customer desk with weekly inflow by channel, sentiment split, top clusters, and source freshness.</td>
<td><strong>Inbox</strong><br>Raw feedback stream across email, Discord, Slack, X, and app-store reviews with triage controls.</td>
</tr>
<tr>
<td width="50%"><img src="assets/screenshots/requests.webp" alt="Kelly Feedback requests"></td>
<td width="50%"><img src="assets/screenshots/roadmap.webp" alt="Kelly Feedback roadmap decisions"></td>
</tr>
<tr>
<td><strong>Requests</strong><br>Clustered feature requests with frequency, weighted scores, trend, and representative quotes.</td>
<td><strong>Roadmap decisions</strong><br>Agent-proposed promote/decline/merge proposals with drafted changelog notes and user replies for approval.</td>
</tr>
</table>
- ## Boundary
+ ## Overview
- - Aggregation is local. The skill may read exports/handoff payloads, normalize feedback, cluster it, validate schemas, and write local handoff files.
- - The app reads and writes local files only. 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 in the decision queue.
- - Own-community data only: ingest feedback addressed to Kelly's own products from Kelly's own channels and accounts. Do not scrape third-party communities or collect data about other companies' users.
- - Treat feedback as user PII-adjacent. Do not commit `config.local.json`, env files, `app/.data/`, exports, tokens, or raw platform responses.
+ 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.
- ## First Run And Onboarding
+ 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`.
- On invocation, check `app/.data/onboarding.json` and private config readiness. If onboarding is absent/incomplete, guide setup before ingesting real feedback.
+ 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.
- Private config priority:
+ 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>.
- 1. `KELLY_FEEDBACK_CONFIG=/absolute/path/to/config.json`
- 2. `skills/kelly-feedback/config.local.json`
- 3. `~/.config/kelly-feedback/config.json`
- 4. `skills/kelly-feedback/config.example.json` as template only
+ ## Mandatory Dependencies
- Env priority:
+ 1. Read and follow `$kelly-app-skill-creator` for product behavior, visual
+ quality, responsive layout, and the complete canonical `app/` artifact.
+ 2. Read and follow `$busabase` for connection, target Space, node discovery,
+ ChangeRequests, review, and merge behavior.
+ 3. Read and follow `$busabase-app-creator` for resource modeling, AirApp
+ runtime limits, security, validation, and deployment.
- 1. Existing environment variables
- 2. `KELLY_FEEDBACK_ENV_FILE=/absolute/path/to/.env`
- 3. Repository root `.env`
- 4. `skills/kelly-feedback/.env.local`
- 5. `~/.config/kelly-feedback/.env`
+ 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.
- Onboarding asks for non-secret setup details only:
+ ## Boundary
- - 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) and, optionally, the path of the roadmap document the agent maintains.
+ - 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.
- Never ask the user to paste secret values into chat. Secrets belong only in local env files. When setup is complete and the user confirms, write `app/.data/onboarding.json`:
+ ## Busabase Resources
- ```json
- {
- "completed": true,
- "completed_at": "ISO timestamp",
- "config_version": "1"
- }
- ```
+ Eight Bases under one application Folder (`kelly-feedback`), declared in
+ `app/app/js/config.js` and `app/resource-map.json`:
- ## Local App
+ - `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.
- Start the dashboard with:
+ 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.
- ```bash
- skills/kelly-feedback/app/start.sh
- ```
+ ## First Run And Onboarding
- The app uses local HTTP on `127.0.0.1`, preferring port `3000` through `4000`, or `KELLY_FEEDBACK_UI_PORT` when set. `/api/state` reports `app: "kelly-feedback"`.
+ On invocation, check the `sources` Base. If empty, guide setup before
+ ingesting real feedback.
- Required app views:
+ Onboarding asks, turn by turn (non-secret setup details only):
+ 1. Products: id, display name, one-line tagline for each product feedback can be about.
+ 2. 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.
+ 3. Scoring weights: plan weights (e.g. free=1, pro=3, team=5), default weight, recency half-life.
+ 4. 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 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).
+ - `#/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). The current roadmap columns (Now / Next / Later) render read-only below the queue. Decision writes honor `agent.lock`.
- - `#/settings`: sanitized config summary — products, sources (channel + collection method), scoring weights, env readiness booleans, data provider, onboarding state, and recent sync log. Never expose secret values.
+ - `#/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=1` opens a deterministic mock voice-of-customer desk for documentation and screenshots.
- `?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 API responses must never read or write `app/.data/` or any live platform data; demo decisions are in-memory only.
+ - 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.
+ 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 generated JSON.
-
- Primary local files:
-
- - `app/.data/feedback_snapshot.json`: canonical snapshot — sources, raw feedback, clustered requests, roadmap lanes, proposals, derived metrics, sync log.
- - `app/.data/decisions.json`: Kelly's verdicts from the UI (proposal approve/request_changes/block with review notes and edited drafts; feedback triage; effort estimates).
- - `app/.data/agent_tasks.json`: queued agent work — proposals in `changes_requested` land here for revision.
- - `app/.data/execution_report.json`: latest `execute_decisions.ts` output with concrete operations.
- - `app/.data/onboarding.json`: onboarding completion marker.
- - `app/.data/agent.lock`: temporary lock while scripts rewrite files; the app rejects decision writes and disables editing while it exists.
- - `config.local.json`: private products/sources/scoring configuration, ignored by git.
-
- Use `scripts/validate_ui_schema.ts app/.data/feedback_snapshot.json` before relying on a snapshot in the UI. The app shows an empty setup state when no snapshot exists.
+ Read `references/feedback-schema.md` before editing the app, scripts, or any
+ Busabase field shape.
## Ingestion Workflow
- `scripts/ingest_feedback.ts` 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:
+ `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:
- 1. 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. Suggested drop location: `app/.data/inbox/*.json` or any temp path.
- 2. Run `node skills/kelly-feedback/scripts/ingest_feedback.ts <payload.json> [more.json ...]`.
- 3. The script validates, dedupes by `fb-<source_id>-<external_id>` (idempotent re-ingest), merges into the snapshot, updates source freshness and derived metrics, and appends a `sync_log` entry — all under `agent.lock`.
+ 1. 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.
+ 2. Run `node skills/kelly-feedback/scripts/ingest_feedback.mjs <payload.json> [more.json ...] --apply`.
+ 3. 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.
+ 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.ts` is the deterministic write path.
+ Clustering is LLM work done by the agent; `scripts/apply_clusters.mjs` is the
+ deterministic write path.
- 1. Read the snapshot and pick out `triage: "new"` items (and any requests needing re-scoring).
+ 1. Read feedback from Busabase and pick out `triage: "new"` items (and any requests needing re-scoring).
2. 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).
- 3. Write a cluster-assignment payload (shape in `references/feedback-schema.md`) and run `node skills/kelly-feedback/scripts/apply_clusters.ts <assignments.json>`. The script validates ids, upserts request drafts, links feedback, recomputes frequency and weighted score from the raw stream, and logs the run — under `agent.lock`.
- 4. When clusters warrant action, draft proposals into the snapshot's `proposals[]` (promote/decline/merge/changelog) with reason, evidence, and an editable public draft, then send Kelly to `#/roadmap`. Items with `request_changes` verdicts return to the agent via `agent_tasks.json`; after revision set them back to `needs_review`.
+ 3. 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.
+ 4. 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
- 1. Kelly reviews `#/roadmap`: edits drafts, writes review notes, and clicks Approve / Request changes / Block. The app records verdicts in `decisions.json` (never mutating the snapshot) and enqueues `agent_tasks.json` entries for change requests.
- 2. Before executing, re-read `decisions.json` and run `node skills/kelly-feedback/scripts/execute_decisions.ts` (dry-run) to produce `execution_report.json` with concrete operations: `update_roadmap` (target lane), `publish_changelog_note` (draft id), `send_decline_reply` (handoff to kelly-messenger/kelly-email), `merge_requests`.
- 3. Show Kelly the dry-run summary. After confirmation, run with `--apply`: local operations (roadmap lanes, merges, request/proposal statuses) are applied to the snapshot; outbound operations are marked `handoff_ready` only.
- 4. 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 record the outcome back into the snapshot's sync log.
- 5. Poll `agent_tasks.json` for `revise_proposal` tasks; revise drafts per the review note and return proposals to `needs_review`.
+ 1. 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.
+ 2. 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.
+ 3. 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.
+ 4. 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.
+ 5. 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.
- - Keep local content minimal: store trimmed feedback text and safe permalinks, not raw platform API responses or attachments.
- - Use stable ids everywhere (`external_id` dedupe keys, request ids, proposal refs) so re-ingest and re-execution are idempotent.
- - If the schema and UI disagree, stop and fix the schema or UI before executing decisions.
+ - 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
+
+ ```bash
+ 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/app dev
+ ```
+
+ In normal use, invoke `/kelly-feedback`, let the skill ingest/cluster
+ whatever is available, and open the AirApp.