diff --git a/.agent-marketplace.json b/.agent-marketplace.json index a16f1e83a48..72cab46f435 100644 --- a/.agent-marketplace.json +++ b/.agent-marketplace.json @@ -15,7 +15,7 @@ }, "category": "development", "source": "./plugins/github", - "version": "1.0.3" + "version": "1.0.4" }, { "name": "linear", @@ -25,7 +25,7 @@ }, "category": "productivity", "source": "./plugins/linear", - "version": "1.5.5" + "version": "1.5.6" }, { "name": "notion", @@ -35,7 +35,7 @@ }, "category": "productivity", "source": "./plugins/notion", - "version": "1.0.5" + "version": "1.0.6" }, { "name": "slack", @@ -55,7 +55,7 @@ }, "category": "development", "source": "./plugins/sentry", - "version": "1.0.2" + "version": "1.0.3" }, { "name": "gmail", @@ -75,7 +75,7 @@ }, "category": "productivity", "source": "./plugins/granola", - "version": "1.0.2" + "version": "1.0.3" }, { "name": "circleback", @@ -85,7 +85,7 @@ }, "category": "productivity", "source": "./plugins/circleback", - "version": "1.0.2" + "version": "1.0.3" }, { "name": "ynab", @@ -115,7 +115,7 @@ }, "category": "development", "source": "./plugins/stripe", - "version": "1.0.2" + "version": "1.0.3" }, { "name": "superhuman", @@ -125,7 +125,7 @@ }, "category": "communication", "source": "./plugins/superhuman", - "version": "1.0.2" + "version": "1.0.3" }, { "name": "neon", @@ -195,7 +195,7 @@ }, "category": "data & apis", "source": "./plugins/peec", - "version": "1.0.0" + "version": "1.0.1" }, { "name": "sazabi", diff --git a/.agents/commands/ci-check.md b/.agents/commands/ci-check.md index dea415cca83..f2baae12866 100644 --- a/.agents/commands/ci-check.md +++ b/.agents/commands/ci-check.md @@ -15,7 +15,7 @@ Run the two fixers first, since they rewrite files the other checks read: Then run these three **in parallel** and report all results: 3. `bun run typecheck` — TypeScript type checking across all packages -4. `bun test` — Run all tests +4. `bun run test` — Every package's suite through turbo, as CI runs it 5. `bun run check:i18n` — Regenerates the translation catalogs and fails on missing or stale translations ## Output diff --git a/.agents/commands/create-plan.md b/.agents/commands/create-plan.md index 8cc461455e5..31bdd9b9818 100644 --- a/.agents/commands/create-plan.md +++ b/.agents/commands/create-plan.md @@ -1,12 +1,14 @@ +--- +description: Write a self-contained ExecPlan in the right plans/ directory +--- + # Paradev Execution Plans (ExecPlans) > **DO NOT EDIT THIS FILE** > This file is the ExecPlan template and guide only. > Create plans in the appropriate location: > - **Desktop app work**: `apps/desktop/plans/-.md` -> - **Web app work**: `apps/web/plans/-.md` > - **Marketing site work**: `apps/marketing/plans/-.md` -> - **API work**: `apps/api/plans/-.md` > - **Shared packages/cross-app work**: `plans/-.md` (root) This document describes the requirements for an execution plan ("ExecPlan"), a design document that a coding agent can follow to deliver a working feature or system change. Treat the reader as a complete beginner to this repository: they have only the current working tree and the single ExecPlan file you provide. There is no memory of prior plans and no external context. @@ -23,25 +25,23 @@ Steps: 7. Closeout: write Outcomes & Retrospective; ensure the plan remains self-contained and accurate. 8. Write your plan to the appropriate location: - Desktop app: `apps/desktop/plans/-.md` - - Web app: `apps/web/plans/-.md` - Marketing: `apps/marketing/plans/-.md` - - API: `apps/api/plans/-.md` - Cross-app/shared: `plans/-.md` Use `` in `YYYYMMDD-HHmm` format (e.g., `20260103-1045-my-feature-plan.md`). This ensures plans are sorted from most recent to oldest. 9. Plan Lifecycle: When the plan is complete and a PR is created, move it to the `done/` folder within the same directory. If abandoned, move it to `abandoned/`. Example questions: - I reviewed apps/desktop/src/main/lib/workspace-manager.ts to understand workspace patterns. + I reviewed apps/desktop/src/lib/trpc/routers/workspaces/ to understand workspace patterns. Where should this new feature live? a) apps/desktop/src/main/lib/ (main process) - b) apps/desktop/src/renderer/src/ (renderer process) + b) apps/desktop/src/renderer/ (renderer process) c) packages/shared/ (shared across apps) d) Other (specify) How should we handle errors? a) Return TRPCError at boundary - b) Use type-safe IPC error channel + b) Use type-safe tRPC error handling c) Other (specify) ## How to use ExecPlans @@ -85,7 +85,7 @@ The agent executing your plan can list files, read files, search, run the projec - **Code Quality**: Biome at root level - **Database**: Drizzle ORM + Neon PostgreSQL - **UI**: React + TailwindCSS v4 + shadcn/ui (in `packages/ui`) -- **Desktop**: Electron with type-safe IPC +- **Desktop**: Electron with type-safe IPC over tRPC ### Common Commands # Development @@ -106,8 +106,7 @@ The agent executing your plan can list files, read files, search, run the projec For Electron/desktop work, remember: - **Main process** (`src/main/`): Can use Node.js modules - **Renderer process** (`src/renderer/`): Browser environment only, NO Node.js imports -- **IPC**: Always type-safe via `src/shared/ipc-channels.ts` -- Run `bun run lint:check-node-imports` to detect Node.js import violations +- **IPC**: tRPC routers in `src/lib/trpc/routers/`; subscriptions return `observable` ### Code Patterns - Functions with 2+ params use object signatures: `({ param1, param2 }: { ... }) => {}` @@ -162,19 +161,18 @@ For each milestone, include: **Example milestone structure:** - ### Milestone 1: IPC Channel Foundation + ### Milestone 1: tRPC Procedure Foundation - This milestone establishes the type-safe IPC channel for workspace operations. + This milestone establishes the tRPC procedure for workspace operations. At completion, the main process can receive workspace requests and return typed responses, though no UI integration exists yet. Scope: - - Define IpcChannels types in src/shared/ipc-channels.ts - - Implement handler in src/main/lib/workspace-ipcs.ts - - Add unit tests for the handler + - Add the procedure to src/lib/trpc/routers/workspaces/ + - Add unit tests for it Acceptance: - bun test apps/desktop/src/main/lib/workspace-ipcs.test.ts + bun test apps/desktop/src/lib/trpc/routers/workspaces/ # Expected: All tests pass Verify before proceeding: Types compile, tests pass, handler logs correctly. @@ -289,7 +287,7 @@ Document parallel implementation strategies in the Plan of Work and include clea For this Paradev monorepo, always mention: - Which app(s) are affected (desktop, web, marketing, api, admin, docs) - Which packages are involved (db, ui, shared, trpc, queries, local-db) - - Any IPC channels if desktop work is involved + - Any tRPC procedures if desktop work is involved ## Plan of Work @@ -332,14 +330,14 @@ Document parallel implementation strategies in the Plan of Work and include clea Be prescriptive. Name the libraries, modules, and services to use and why. Specify the types and function signatures that must exist at the end of the milestone. - For IPC channels (desktop), define in apps/desktop/src/shared/ipc-channels.ts: + For a desktop procedure, add it to a router in apps/desktop/src/lib/trpc/routers/: - export interface IpcChannels { - "my-new-channel": { - request: { param1: string }; - response: { success: boolean; data: MyData }; - }; - } + export const createMyRouter = () => + router({ + doThing: publicProcedure + .input(z.object({ param1: z.string() })) + .mutation(async ({ input }): Promise => { ... }), + }); If you follow the guidance above, a single, stateless agent -- or a human novice -- can read your ExecPlan from top to bottom and produce a working, observable result. That is the bar: SELF-CONTAINED, SELF-SUFFICIENT, NOVICE-GUIDING, OUTCOME-FOCUSED. @@ -356,7 +354,7 @@ ExecPlans have a defined lifecycle that keeps the plans folders clean and provid ├── done/ └── abandoned/ - apps/web/plans/ # Web app plans + apps/marketing/plans/ # Marketing site plans ├── .md ├── done/ └── abandoned/ diff --git a/.agents/commands/create-pr.md b/.agents/commands/create-pr.md index c148873ca57..c28c3743181 100644 --- a/.agents/commands/create-pr.md +++ b/.agents/commands/create-pr.md @@ -1,3 +1,7 @@ +--- +description: Review the diff against AGENTS.md, then create a PR with gh and a body scaled to the change +--- + # Persona & Goal You are an expert Paradev engineer and technical writer creating high-signal PR descriptions for this repository. @@ -187,7 +191,6 @@ Use **High-risk/Complex** when any of these are true: - `bun run typecheck` (required) - `bun run lint` (required) - `bun test ` (when touching logic) - - `bun run lint:check-node-imports` (when touching Desktop renderer/shared) - `bun turbo run build --filter=@superset/desktop` (when touching Desktop) ## Design Decisions (optional) @@ -282,7 +285,6 @@ For PRs bundling multiple features, use Part headers to organize (like PR #559): - `bun run typecheck` (required) - `bun run lint` (required) - `bun test` (required) - - `bun run lint:check-node-imports` (when touching Desktop renderer/shared) - `bun turbo run build --filter=@superset/desktop` (when touching Desktop) ## Design Decisions @@ -326,7 +328,7 @@ Use these as templates for the Manual QA Checklist section. Pick categories appr - [ ] App launches without errors - [ ] No console errors in DevTools (main + renderer) - [ ] Feature works after app restart -- [ ] Ran `bun run lint:check-node-imports` (no Node.js in renderer) +- [ ] No Node.js imports in `apps/desktop/src/renderer/` ### tRPC over Electron IPC - [ ] tRPC router changes validated from renderer call-sites @@ -523,7 +525,6 @@ Use these as templates for the Manual QA Checklist section. Pick categories appr ## Testing - `bun run typecheck` - `bun run lint` - - `bun run lint:check-node-imports` - Manual testing in dev mode ## Design Decisions diff --git a/.agents/commands/deslop.md b/.agents/commands/deslop.md index 0621bba170e..e7bd167da37 100644 --- a/.agents/commands/deslop.md +++ b/.agents/commands/deslop.md @@ -15,15 +15,7 @@ Apply clean code philosophy to the code, with special emphasis on comments: - Are commented-out code blocks (delete them - version control exists) - Are TODO/FIXME that will never be addressed -**Keep or add comments only when:** -- Explaining *why* a non-obvious decision was made -- Documenting design intent or architectural decisions (e.g., "shared by X and Y paths", "deferred because Z") -- Documenting external constraints or business rules not evident from code -- Warning about non-intuitive behavior or edge cases -- Noting the origin of a pattern when it aids future maintenance (e.g., "VS Code pattern") -- Required for public API documentation (JSDoc, docstrings) - -**When in doubt, keep the comment.** Removing a comment that captured intent is destructive — the reasoning is lost and cannot be recovered from code alone. Only remove a comment when you are confident the code *fully* communicates the same information. +**Keep or add a comment only when** it states an external constraint or a trap the code cannot express (AGENTS.md "Comments"): first try to name, structure, or type it. Rationale goes in the commit message and the PR, not the code. ## Code Simplification diff --git a/.agents/commands/draft.md b/.agents/commands/draft.md index 95c7e5ec174..10d98d209f2 100644 --- a/.agents/commands/draft.md +++ b/.agents/commands/draft.md @@ -82,8 +82,8 @@ On **Discard**, confirm in one short sentence that nothing was created, and stop After — and only after — the user picks **Accept**: -1. Resolve the team. Call `mcp__linear-server__list_teams`. If exactly one team is returned, use it. If multiple teams exist, call `ask_user` with the team names as options and use the chosen team's id. -2. Call `mcp__linear-server__save_issue` with: +1. Resolve the team. Call the Linear MCP `list_teams` tool (whatever the server is named here). If exactly one team is returned, use it. If multiple teams exist, call `ask_user` with the team names as options and use the chosen team's id. +2. Call the Linear MCP `save_issue` tool with: - `team`: the resolved team id - `title`: the accepted title (without the `**Title:**` prefix) - `description`: the accepted markdown description body (the contents of the fenced block, not the fence itself; use real newlines, not literal `\n`) @@ -91,7 +91,7 @@ After — and only after — the user picks **Accept**: ## Rules -- **No silent writes.** Never call `mcp__linear-server__save_issue` or any other Linear write tool until the user has explicitly chosen **Accept** in step 5. +- **No silent writes.** Never call Linear's `save_issue` or any other Linear write tool until the user has explicitly chosen **Accept** in step 5. - **No extra mutations.** Don't assign, label, set status, add to a project, or otherwise mutate Linear state beyond creating the issue with the accepted title and description. - Read-only Linear lookups (`list_teams`, optionally `list_issues` for duplicate check) are fine, but skip them unless directly useful. - Don't guess at file paths — verify with Glob/Grep/Read before citing them in the draft. diff --git a/.agents/commands/pr/create-pr.md b/.agents/commands/pr/create-pr.md deleted file mode 100644 index 8ce0ae80580..00000000000 --- a/.agents/commands/pr/create-pr.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -description: Create a pull request for the current branch (agent-driven, one-click) -argumentHint: "[--draft]" ---- - -# Goal - -Create a pull request for the current branch in one pass. The user -clicked the Create PR button in the diff-editor sidebar — they expect -the PR to be created without further prompting. - -An attachment named `pr-context.md` is included with this turn. It -contains: - -- Current branch and base branch -- Whether the branch is published (has upstream) -- Commits ahead/behind upstream -- Whether there are uncommitted changes -- Required preconditions the user's branch must satisfy before - `gh pr create` will succeed - -Read `pr-context.md` first. Use it as ground truth instead of re-deriving -the state yourself. - -# Arguments - -- `--draft` — create the PR as a draft. Pass `--draft` through to the - `gh pr create` call. - -Parse the arguments from the user's prompt (everything after the skill -name). Do not ask the user to confirm the draft flag — it came from -their button click. - -# Workflow - -## 1. Satisfy preconditions - -In the order listed in `pr-context.md` under "Required preconditions": - -- **Uncommitted changes**: generate a commit message from the staged - diff (use `git diff --cached` and `git status`). If nothing is - staged, `git add -A`. Then `git commit -m ""`. Keep the - message short and specific — do not write a PR-body-style - description here. -- **Unpublished branch**: `git push -u origin -- ""` — quote `` - to avoid shell injection on names with metacharacters. -- **Unpushed commits on a published branch**: `git push`. -- **Behind upstream**: stop. Report to the user that they should sync - first. Do not force-push. Do not rebase without asking. - -If any push fails non-fast-forward, stop and report — never -force-push. - -## 2. Draft the PR body - -Use `git log "..HEAD"` to read the commits, `git diff "...HEAD"` -for the scope of changes. Produce: - -- **Title**: short, imperative, derived from the most recent commit - message or the scope of the diff. -- **Body**: concise. Summary + a short Test Plan checklist. Skip - sections that have nothing meaningful to say — do not pad. - -## 3. Create the PR - -``` -gh pr create \ - --base \ - --title "" \ - --body "<body>" -``` - -If `--draft` was passed, add `--draft`. - -Then assign the PR to `superset-home`, unless `--draft` was passed. This starts -a review of the PR in a cloud workspace; the findings are published as a -Paradev page and linked in a PR comment. - -``` -gh pr edit <number> --add-assignee superset-home -``` - -Assigning needs write access to the repository. If it fails, the PR is still -created: say so and continue. Do not run `gh pr create` again. - -## 4. Report back - -Print the PR URL as a plain link on its own line. One short sentence -above it summarizing what you did (e.g. "Published `feature-x` and -opened draft PR."). Do not paste the full body back. - -# Guardrails - -- Never force-push. -- Never skip pre-commit hooks (`--no-verify`) or signing. -- If a hook fails, report the failure; do not retry with `--no-verify`. -- Do not open a browser — the caller handles that. -- Do not run a full `AGENTS.md` standards review in this skill. The - button is a fast path; use `/create-pr` (the general-purpose skill) - for the gated review flow. diff --git a/.agents/commands/task-run.md b/.agents/commands/task-run.md index 6de1039f1ff..43131bf9eb2 100644 --- a/.agents/commands/task-run.md +++ b/.agents/commands/task-run.md @@ -1,6 +1,6 @@ --- description: Create a task, workspace, and start an AI agent session to work on it -allowed-tools: mcp__superset__create_task, mcp__superset__list_members, mcp__superset__list_devices, mcp__superset__list_projects, mcp__superset__create_workspace, mcp__superset__start_agent_session, Bash(git config user.email) +allowed-tools: mcp__superset__tasks_create, mcp__superset__organization_members_list, mcp__superset__hosts_list, mcp__superset__projects_list, mcp__superset__workspaces_create, Bash(git config user.email) --- Create a new task in Paradev, spin up a workspace, and start an AI agent session to work on it. @@ -15,25 +15,26 @@ Parse `$ARGUMENTS` for: ### 0. Resolve current user and environment -Run these in parallel: -- Call `mcp__superset__list_members` and match against the git user email (`git config user.email`) to get the current user's member ID -- Call `mcp__superset__list_devices` and select the device owned by the current user -- Call `mcp__superset__list_projects` on the resolved device and select the project matching the current git repo +Run these two in parallel: +- Call `mcp__superset__organization_members_list` and match against the git user email (`git config user.email`) to get the current user's ID +- Call `mcp__superset__hosts_list` and select the host this runs on (the user's machine) + +Then call `mcp__superset__projects_list` with that `hostId` and select the project matching the current git repo. ### 1. Create the task - Parse the arguments to extract the task description and optional priority - Generate a clear, concise task title from the description (imperative form, under 80 chars) - If the user provided more detail beyond a short title, include it as a markdown description on the task -- Create the task using `mcp__superset__create_task` with: +- Create the task using `mcp__superset__tasks_create` with: - `title`: The generated title - `description`: Expanded detail if provided, otherwise omit - `priority`: Parsed priority or `none` - - `assigneeId`: The resolved member ID from step 0 + - `assigneeId`: The resolved user ID from step 0 -### 2. Create a workspace +### 2. Create the workspace and start the agent -- Use the device ID and project ID resolved in step 0 +- Use the host ID and project ID resolved in step 0 - Generate a kebab-case workspace name from the task title (short, max 4-5 words) - Generate a branch name based on task type: - `fix/...` for bugs and defects @@ -42,11 +43,7 @@ Run these in parallel: - `docs/...` for documentation-only changes - `refactor/...` for code refactors with no behavior change - Default to `feat/...` if the type is ambiguous -- Create the workspace using `mcp__superset__create_workspace` - -### 3. Start AI agent session - -- Start an AI agent session using `mcp__superset__start_agent_session` with the created task ID and workspace ID +- Create the workspace using `mcp__superset__workspaces_create` with `hostId`, `projectId`, `name`, `branch`, `taskId` (the created task), and `agents: [{ agent: "claude", prompt: <task title + description> }]`. It starts the agent once the workspace is ready. ## Output diff --git a/.agents/commands/task.md b/.agents/commands/task.md index df97a6d2703..c24e4abf459 100644 --- a/.agents/commands/task.md +++ b/.agents/commands/task.md @@ -1,6 +1,6 @@ --- description: Create a task and assign it to me via Paradev MCP -allowed-tools: mcp__superset__create_task, mcp__superset__list_members, Bash(git config user.email) +allowed-tools: mcp__superset__tasks_create, mcp__superset__organization_members_list, Bash(git config user.email) --- Create a new task in Paradev and assign it to me. @@ -16,12 +16,12 @@ Parse `$ARGUMENTS` for: 1. Parse the arguments to extract the task description and optional priority 2. Generate a clear, concise task title from the description (imperative form, under 80 chars) 3. If the user provided more detail beyond a short title, include it as a markdown description on the task -4. Resolve the current user's member ID by calling `mcp__superset__list_members` and matching against the git user (run `git config user.email` to get the current user's email) -5. Create the task using `mcp__superset__create_task` with: +4. Resolve the current user's ID by calling `mcp__superset__organization_members_list` and matching against the git user (run `git config user.email` to get the current user's email) +5. Create the task using `mcp__superset__tasks_create` with: - `title`: The generated title - `description`: Expanded detail if provided, otherwise omit - `priority`: Parsed priority or `none` - - `assigneeId`: The resolved member ID from step 4 + - `assigneeId`: The resolved user ID from step 4 ## Output diff --git a/.agents/skills/cdp-verification/SKILL.md b/.agents/skills/cdp-verification/SKILL.md index b0bd530673f..d6f796e1d05 100644 --- a/.agents/skills/cdp-verification/SKILL.md +++ b/.agents/skills/cdp-verification/SKILL.md @@ -79,7 +79,7 @@ Verify `/api/auth/get-session` from inside the matched renderer before testing. Check which setup script provisioned the workspace before repairing auth: - `.superset/setup.local.sh` creates a per-workspace local stack and runs the idempotent `bun run db:seed-dev`, but intentionally leaves sign-in as a separate step. If the account may be missing, rerun `bun run db:seed-dev` while the local DB stack is running. -- `.superset/setup.sh` seeds `superset-dev-data/auth-token.enc` from `$HOME/.superset/auth-token.enc` when available. Rerunning it without `--force` can fill a missing token. Do not use `--force` merely to repair auth: it resets `superset-dev-data/` before reseeding. +- `.superset/setup.sh` seeds `superset-dev-data/auth-token.enc` from `auth-token.enc` in the app home (`~/.superset` when it is a real directory, else `~/.paradev`; `prod_app_home` in `.superset/lib/common.sh`) when available. Rerunning it without `--force` can fill a missing token. Do not use `--force` merely to repair auth: it resets `superset-dev-data/` before reseeding. The desktop hydrates a persisted token into an in-memory bearer-token closure. A raw `Runtime.evaluate` `fetch` cannot read that closure, and the local-dev sign-in button persists a bearer token but uses `credentials: "omit"`; neither guarantees the cookie required by a raw CDP probe. For a workspace created by `setup.local.sh`, repair the CDP session as follows: diff --git a/.agents/skills/dev-doctor/SKILL.md b/.agents/skills/dev-doctor/SKILL.md index e07b66d5e32..ae6c8443f69 100644 --- a/.agents/skills/dev-doctor/SKILL.md +++ b/.agents/skills/dev-doctor/SKILL.md @@ -63,7 +63,7 @@ Production database: Neon plugin `run_sql` with `project_id` `frosty-lab-3241699 PostHog (`exec`): count `dev_reconciler_fixed` by `fix`, and `dev_turn_retried` by `reason`, over the last 7 days. -Sentry: `search_issues` in organization `paradev` for `agent-worker`, `agentWorker`, `devTools` +Sentry: `search_issues` in organization `superset-sh` for `agent-worker`, `agentWorker`, `devTools` and `reconcile`, unresolved, last 7 days. ### 2. Group by cause diff --git a/.agents/skills/incident-triage/SKILL.md b/.agents/skills/incident-triage/SKILL.md index 8d294deeefa..29b32a008cd 100644 --- a/.agents/skills/incident-triage/SKILL.md +++ b/.agents/skills/incident-triage/SKILL.md @@ -27,7 +27,7 @@ Write one line each: - Who is affected: everyone, one region, one feature, one version, or one user. If only one user or one machine is affected, stop here. Tell the human that this is a support -issue, and suggest `superset doctor`. +issue, and suggest the `superset:doctor` skill (and `superset status` for host state). ## 2. Gather signals diff --git a/.agents/skills/mobile-sim-verification/SKILL.md b/.agents/skills/mobile-sim-verification/SKILL.md index f5c71719062..de03817fc5a 100644 --- a/.agents/skills/mobile-sim-verification/SKILL.md +++ b/.agents/skills/mobile-sim-verification/SKILL.md @@ -70,7 +70,7 @@ superset terminals create --local --workspace "$SUPERSET_WORKSPACE_ID" --command `ios/` is gitignored. - Silence the first-run developer menu: `xcrun simctl spawn booted defaults write sh.superset.mobile EXDevMenuIsOnboardingFinished -bool YES`. -- Sign in: `maestro test apps/mobile/.maestro/flows/sign-in.yml` +- Sign in: tap the dev sign-in button with Maestro (`tapOn: { id: dev-sign-in-button }`, from `DevSignInOptions.tsx`), as `mobile-demo-record` does. (`JAVA_HOME=/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home`). - The dev account has no plan, so Home shows the paywall. Give its org a temporary one on this worktree's Neon branch, and delete the row afterwards. Confirm `DATABASE_URL_UNPOOLED` is the @@ -115,7 +115,7 @@ commands, and the simulator reaches this box only through local egress. - **Input.** `agent-device snapshot -i`, `press`, `fill`, `screenshot`. `press` and `fill` take `--settle` and print what changed. The dev-menu sheet covers the first screen: press `Continue`, then `Close`. -- **Request counts.** `/tmp/superset-eas-egress.log` names each local port the simulator reached. +- **Request counts.** `.tmp/eas-dev/egress.log` in the checkout names each local port the simulator reached. - **Cloud screens.** The workspace's Neon branch is a copy of production, and cloud access is a PostHog flag on the email, so the dev account sees no cloud workspaces. Prefer a test account that has the flag. Use a real account only with its owner's approval: save its `auth.accounts` diff --git a/.agents/skills/superset-settings/SKILL.md b/.agents/skills/superset-settings/SKILL.md index 9a58dc69104..fc9c08449ba 100644 --- a/.agents/skills/superset-settings/SKILL.md +++ b/.agents/skills/superset-settings/SKILL.md @@ -31,7 +31,7 @@ Prefer `--json` (auto-on in agent environments; parse it rather than matching the human sentences) and `superset settings list` to discover keys and allowed values instead of guessing. In Paradev terminals `superset` is already on PATH. `SUPERSET_HOME_DIR` overrides the target profile -(`~/.superset` by default); useful for testing against a sandbox copy. +(`~/.paradev` by default); useful for testing against a sandbox copy. ## Creating custom themes @@ -107,7 +107,7 @@ app" means the nudge landed. The notes below are the fallback behavior when no app acknowledged (app not running, or an older app version): - **Regular settings** (most of `settings set`): written to - `~/.superset/local.db`. A running older app picks them up the next time + `local.db` in the target profile (`$SUPERSET_HOME_DIR`, else `~/.paradev`). A running older app picks them up the next time its window regains focus; no restart needed. - **Git settings** (`branchPrefixMode`, `branchPrefixCustom`, `worktreeBaseDir`): host-wide values written through the local host diff --git a/AGENTS.md b/AGENTS.md index 6d206719637..b1071709ad0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -122,10 +122,10 @@ binary; write `superset` in code and docs here. Replace the capitalized placeholders before running these: ```bash -superset ws create --local --project PROJECT_ID --branch BRANCH --agent claude --prompt "..." -superset agents create --workspace WORKSPACE_ID --agent claude --prompt "..." +superset ws create --local --project PROJECT_ID --name NAME --branch BRANCH --agent claude --prompt "..." +superset agents create --local --workspace WORKSPACE_ID --agent claude --prompt "..." superset ws list --local -superset terminals read --workspace WORKSPACE_ID --terminal TERMINAL_ID +superset terminals read --local --workspace WORKSPACE_ID --terminal TERMINAL_ID superset ws archive --local WORKSPACE_ID ``` diff --git a/docs/runbooks/incidents.md b/docs/runbooks/incidents.md index ec6f24be528..346e597a537 100644 --- a/docs/runbooks/incidents.md +++ b/docs/runbooks/incidents.md @@ -56,7 +56,7 @@ the components and ongoing incidents as JSON. When unsure, pick the higher level. It is easy to lower the level later. Local-only problems (one user's host-service, one machine's terminals) are support issues, not -incidents. Send them through `superset doctor` and support. +incidents. Send them through the `superset:doctor` skill and support. ## Who declares an incident diff --git a/packages/shared/src/plugins/manifests.generated.ts b/packages/shared/src/plugins/manifests.generated.ts index 4d2f29358af..25021d6e8e2 100644 --- a/packages/shared/src/plugins/manifests.generated.ts +++ b/packages/shared/src/plugins/manifests.generated.ts @@ -9,7 +9,7 @@ export const FIRST_PARTY_MANIFESTS = { "github": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "github", - "version": "1.0.3", + "version": "1.0.4", "description": "Work with issues, pull requests, and CI across your repositories.", "author": { "name": "Paradev", @@ -37,7 +37,10 @@ export const FIRST_PARTY_MANIFESTS = { }, "mcp": { "type": "streamable-http", - "url": "https://api.githubcopilot.com/mcp/" + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "X-MCP-Toolsets": "context,repos,issues,pull_requests,users,actions" + } } } }, @@ -55,7 +58,7 @@ export const FIRST_PARTY_MANIFESTS = { "linear": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "linear", - "version": "1.5.5", + "version": "1.5.6", "description": "Plan and build products: create, search, and update Linear issues.", "author": { "name": "Paradev", @@ -104,7 +107,7 @@ export const FIRST_PARTY_MANIFESTS = { "notion": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "notion", - "version": "1.0.5", + "version": "1.0.6", "description": "Search, read, and write your Notion workspace: pages, databases, and comments.", "author": { "name": "Paradev", @@ -183,7 +186,7 @@ export const FIRST_PARTY_MANIFESTS = { "sentry": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "sentry", - "version": "1.0.2", + "version": "1.0.3", "description": "Debug with production error context: search issues, read stack traces, and manage alerts in Sentry.", "author": { "name": "Paradev", @@ -222,7 +225,7 @@ export const FIRST_PARTY_MANIFESTS = { }, { "name": "find-in-sentry", - "description": "Answer \"what's breaking\" questions from Sentry — find the right org and project, search issues and events with the vocabulary Sentry uses, and report what is actually firing rather than the first hit. Use when the user asks what errors are happening, whether something is stable, what changed since a release, or for any health summary of a service." + "description": "Answer \"what's breaking\" questions from Sentry — find the right org and project, search issues and errors with the vocabulary Sentry uses, and report what is actually firing rather than the first hit. Use when the user asks what errors are happening, whether something is stable, what changed since a release, or for any health summary of a service." } ] } as const, @@ -262,7 +265,7 @@ export const FIRST_PARTY_MANIFESTS = { "granola": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "granola", - "version": "1.0.2", + "version": "1.0.3", "description": "Recall what was said in your meetings: search, read, and query Granola notes and transcripts.", "author": { "name": "Paradev", @@ -308,7 +311,7 @@ export const FIRST_PARTY_MANIFESTS = { "circleback": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "circleback", - "version": "1.0.2", + "version": "1.0.3", "description": "Meeting notes, action items, and transcripts from Circleback: search what was said and work what was captured.", "author": { "name": "Paradev", @@ -428,7 +431,7 @@ export const FIRST_PARTY_MANIFESTS = { "stripe": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "stripe", - "version": "1.0.2", + "version": "1.0.3", "description": "Work with payments in Stripe: trace charges and subscriptions, read billing data, and search Stripe's docs.", "author": { "name": "Paradev", @@ -478,7 +481,7 @@ export const FIRST_PARTY_MANIFESTS = { "superhuman": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "superhuman", - "version": "1.0.2", + "version": "1.0.3", "description": "Work your inbox from Superhuman: find and read threads, triage what needs you, draft replies in your voice, and schedule from your calendar.", "author": { "name": "Paradev", @@ -770,7 +773,7 @@ export const FIRST_PARTY_MANIFESTS = { "peec": { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "peec", - "version": "1.0.0", + "version": "1.0.1", "description": "Track your brand in AI search with Peec AI: visibility, cited sources, recommended actions, and AI crawler traffic.", "author": { "name": "Paradev", diff --git a/plugins/circleback/plugin.json b/plugins/circleback/plugin.json index 34c3602c68f..c58628b250f 100644 --- a/plugins/circleback/plugin.json +++ b/plugins/circleback/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "circleback", - "version": "1.0.2", + "version": "1.0.3", "description": "Meeting notes, action items, and transcripts from Circleback: search what was said and work what was captured.", "author": { "name": "Paradev", diff --git a/plugins/circleback/skills/prep-with-circleback/SKILL.md b/plugins/circleback/skills/prep-with-circleback/SKILL.md index 350f2905a3f..ba4829b06e1 100644 --- a/plugins/circleback/skills/prep-with-circleback/SKILL.md +++ b/plugins/circleback/skills/prep-with-circleback/SKILL.md @@ -20,8 +20,9 @@ resolve them with `FindProfiles` or `FindCompanies` and skip the calendar. ## 2. Pull the history with these people -`SearchMeetings` filtered by the attendees' profiles or the company domain, -newest first. Read the last two or three with `ReadMeetings`. For a long +`SearchMeetings` filtered by one key attendee's profile or the company domain +(a profiles filter matches only meetings every listed profile attended); sort +the results by date yourself. Read the last two or three with `ReadMeetings`. For a long relationship read the most recent and the first: the first records what they wanted, the most recent records what they got. diff --git a/plugins/github/plugin.json b/plugins/github/plugin.json index 81ce2dfbcb4..150ff4728b1 100644 --- a/plugins/github/plugin.json +++ b/plugins/github/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "github", - "version": "1.0.3", + "version": "1.0.4", "description": "Work with issues, pull requests, and CI across your repositories.", "author": { "name": "Paradev", @@ -23,7 +23,10 @@ }, "mcp": { "type": "streamable-http", - "url": "https://api.githubcopilot.com/mcp/" + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "X-MCP-Toolsets": "context,repos,issues,pull_requests,users,actions" + } } } } diff --git a/plugins/granola/plugin.json b/plugins/granola/plugin.json index 6c9fb6c2294..8891aa0c4d5 100644 --- a/plugins/granola/plugin.json +++ b/plugins/granola/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "granola", - "version": "1.0.2", + "version": "1.0.3", "description": "Recall what was said in your meetings: search, read, and query Granola notes and transcripts.", "author": { "name": "Paradev", diff --git a/plugins/granola/skills/find-in-granola/SKILL.md b/plugins/granola/skills/find-in-granola/SKILL.md index 83770e1228b..b6698019db6 100644 --- a/plugins/granola/skills/find-in-granola/SKILL.md +++ b/plugins/granola/skills/find-in-granola/SKILL.md @@ -18,7 +18,8 @@ broad query. "The customer call last week" is a date range and an attendee, not a keyword. If the user names a folder or team space, `list_meeting_folders` first and list inside it. When results look empty, `get_account_info` tells you which email and workspace you are reading from — a personal account connected -where the work one was meant is the usual cause, so say which one you searched. +where the work one was meant is the usual cause (`switch_workspace` moves to +another workspace the user belongs to), so say which one you searched. ## 2. Notes first, transcript only when the wording matters diff --git a/plugins/linear/plugin.json b/plugins/linear/plugin.json index a8cff79e05e..fa8eebbc659 100644 --- a/plugins/linear/plugin.json +++ b/plugins/linear/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "linear", - "version": "1.5.5", + "version": "1.5.6", "description": "Plan and build products: create, search, and update Linear issues.", "author": { "name": "Paradev", diff --git a/plugins/linear/skills/duplicate-sweep/SKILL.md b/plugins/linear/skills/duplicate-sweep/SKILL.md index c272a620afe..7cc2bc6bda2 100644 --- a/plugins/linear/skills/duplicate-sweep/SKILL.md +++ b/plugins/linear/skills/duplicate-sweep/SKILL.md @@ -26,7 +26,7 @@ Group only when the underlying cause is plausibly identical. Two issues that bot "page is slow" are the same report only if they are slow for the same reason; merging them buries whichever one nobody reproduced. -When you are unsure, link them as related and say why rather than merging. An incorrect merge +When you are unsure, link them as related (`save_issue` with `relatedTo`) and say why rather than merging. An incorrect merge loses an issue silently, and nobody goes looking for what they think is already tracked. ## 3. Pick the survivor deliberately @@ -47,7 +47,7 @@ or on the set if the user prefers, before the first write. Anything only present on the duplicate — reproduction steps, a customer name, a log line, a subscriber who wants to know when it is fixed — goes onto the survivor first. Then close the -duplicate with a comment pointing at it, so someone arriving from a search or an old link +duplicate with `save_issue` and `duplicateOf: <survivor>`, plus a comment pointing at it, so someone arriving from a search or an old link lands somewhere useful. Never delete. A closed issue with a pointer is how the next person's search finds the survivor. diff --git a/plugins/notion/plugin.json b/plugins/notion/plugin.json index e679f040b10..beba8ac9b3d 100644 --- a/plugins/notion/plugin.json +++ b/plugins/notion/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "notion", - "version": "1.0.5", + "version": "1.0.6", "description": "Search, read, and write your Notion workspace: pages, databases, and comments.", "author": { "name": "Paradev", diff --git a/plugins/notion/skills/find-in-notion/SKILL.md b/plugins/notion/skills/find-in-notion/SKILL.md index f842b4b6466..dc95278680a 100644 --- a/plugins/notion/skills/find-in-notion/SKILL.md +++ b/plugins/notion/skills/find-in-notion/SKILL.md @@ -2,7 +2,7 @@ name: find-in-notion description: Find what the workspace already says before you answer or write — search Notion, tell the canonical page from the stale copy, and cite what you used. Use when the user asks what's in Notion, refers to a doc, spec, or meeting note, or asks a question the workspace probably already answers. argument-hint: the question, doc, or topic to look up -allowed-tools: mcp__notion__notion-search, mcp__notion__notion-fetch, mcp__notion__notion-query-data-sources +allowed-tools: mcp__notion__notion-search, mcp__notion__notion-fetch, mcp__notion__notion-query-data-sources, mcp__notion__notion-list-page-backlinks --- # Find what the workspace already says @@ -18,9 +18,6 @@ A question about "our refund rules" lives on a page called "Billing policy" or "Support runbook"; the word "refund" may appear nowhere in the title. Two or three searches with different vocabulary beats one search with better phrasing. -Search titles and body separately when the tools allow it. A title match is usually -the canonical page; a body match is usually someone referring to it. - ## 2. Decide which page is canonical When several pages cover the same ground, rank them: diff --git a/plugins/peec/plugin.json b/plugins/peec/plugin.json index 853d027dd38..570197a91da 100644 --- a/plugins/peec/plugin.json +++ b/plugins/peec/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "peec", - "version": "1.0.0", + "version": "1.0.1", "description": "Track your brand in AI search with Peec AI: visibility, cited sources, recommended actions, and AI crawler traffic.", "author": { "name": "Paradev", diff --git a/plugins/peec/skills/work-peec-actions/SKILL.md b/plugins/peec/skills/work-peec-actions/SKILL.md index 2e5ccf68ba3..a09d79e92a1 100644 --- a/plugins/peec/skills/work-peec-actions/SKILL.md +++ b/plugins/peec/skills/work-peec-actions/SKILL.md @@ -13,7 +13,7 @@ that matter. Keep both clean. ## 1. Read the queue in the order Peec ranks it -Run `list_projects`, then `list_actions` with `statuses: ["PENDING", "ACCEPTED"]` +Run `list_projects`, then `list_actions` with `statuses: ["PENDING", "PLANNED", "ACCEPTED"]` (ordered by `impact`). Group them as Peec does: 1. **Site fixes** (`SEO_ISSUE`, `ROBOTS_TXT`): these block everything else. @@ -43,7 +43,8 @@ email, post, or pitch yourself: an action records the work, it does not do it. ## 4. Log shipped work on the day it shipped For work Peec did not suggest (a page launch, a rename, a crawler fix), use -`create_actions` with `status: "COMPLETED"`, `completed_at` set to the real +`create_actions` with a `kind` (`TECHNICAL`, `CONTENT_CREATION`, +`CONTENT_OPTIMIZATION`, `MENTION`, `PRODUCT_PAGE`, `OTHER`), `status: "COMPLETED"`, `completed_at` set to the real ship date, the `page_url` it changed, and the `prompt_ids` it was meant to move. For a Peec action that was done earlier, use `update_actions_status` with `completed_at`. The Impact view then marks that day on the chart. diff --git a/plugins/sentry/plugin.json b/plugins/sentry/plugin.json index 8a84c7cf62d..0f32fd3da4d 100644 --- a/plugins/sentry/plugin.json +++ b/plugins/sentry/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "sentry", - "version": "1.0.2", + "version": "1.0.3", "description": "Debug with production error context: search issues, read stack traces, and manage alerts in Sentry.", "author": { "name": "Paradev", diff --git a/plugins/sentry/skills/debug-with-sentry/SKILL.md b/plugins/sentry/skills/debug-with-sentry/SKILL.md index dd62217d5af..aa0749f8157 100644 --- a/plugins/sentry/skills/debug-with-sentry/SKILL.md +++ b/plugins/sentry/skills/debug-with-sentry/SKILL.md @@ -2,7 +2,7 @@ name: debug-with-sentry description: Root-cause a production error with Sentry's evidence before touching code — pull the issue, read the stack trace and breadcrumbs, and separate the crash from its trigger. Use when the user pastes a Sentry link or issue ID, reports a production error, or asks why something is crashing for users. argument-hint: the Sentry issue URL, short ID, or a description of the error -allowed-tools: mcp__sentry__get_issue_details, mcp__sentry__get_event_stacktrace, mcp__sentry__get_issue_breadcrumbs, mcp__sentry__get_issue_tag_values, mcp__sentry__search_issues, mcp__sentry__search_events, mcp__sentry__find_releases, mcp__sentry__analyze_issue_with_seer, mcp__sentry__update_issue +allowed-tools: mcp__sentry__get_sentry_resource, mcp__sentry__search_issues, mcp__sentry__search_errors, mcp__sentry__analyze_issue_with_seer, mcp__sentry__search_sentry_tools, mcp__sentry__execute_sentry_tool --- # Root-cause with the evidence, not the guess @@ -12,7 +12,8 @@ about *why* it broke, backed by the event data, before anyone edits code. ## 1. Get the real issue, not the search hit -A pasted URL or short ID (`PROJECT-123`) goes straight to `get_issue_details`. +A pasted URL or short ID (`PROJECT-123`) goes straight to `get_sentry_resource` +(pass the URL, or `resourceType: 'issue'` with the short ID). A described error ("users can't check out") goes through `search_issues` first, but treat the results as candidates: match on the error type and the affected code path, not on title similarity. If two issues share a message but differ in @@ -20,6 +21,11 @@ stack trace, they are two bugs. ## 2. Read the stack trace like a diff +Stack traces, breadcrumbs, tag values and releases are catalog tools: find +them with `search_sentry_tools`, then call `execute_sentry_tool` with `name` +set to `get_event_stacktrace`, `get_issue_breadcrumbs`, `get_issue_tag_values` +or `find_releases`. + Fetch the newest event's stack trace. The top frame is where it *crashed*, not where it *broke* — walk down until you leave library code and hit the first frame owned by this repo. That frame and its local variables are the starting diff --git a/plugins/sentry/skills/find-in-sentry/SKILL.md b/plugins/sentry/skills/find-in-sentry/SKILL.md index 02aadb36040..c9553e2804b 100644 --- a/plugins/sentry/skills/find-in-sentry/SKILL.md +++ b/plugins/sentry/skills/find-in-sentry/SKILL.md @@ -1,8 +1,8 @@ --- name: find-in-sentry -description: Answer "what's breaking" questions from Sentry — find the right org and project, search issues and events with the vocabulary Sentry uses, and report what is actually firing rather than the first hit. Use when the user asks what errors are happening, whether something is stable, what changed since a release, or for any health summary of a service. +description: Answer "what's breaking" questions from Sentry — find the right org and project, search issues and errors with the vocabulary Sentry uses, and report what is actually firing rather than the first hit. Use when the user asks what errors are happening, whether something is stable, what changed since a release, or for any health summary of a service. argument-hint: the service, error, release, or question to look up -allowed-tools: mcp__sentry__whoami, mcp__sentry__find_organizations, mcp__sentry__find_projects, mcp__sentry__find_releases, mcp__sentry__search_issues, mcp__sentry__search_events, mcp__sentry__get_issue_details +allowed-tools: mcp__sentry__find_organizations, mcp__sentry__find_projects, mcp__sentry__search_issues, mcp__sentry__search_errors, mcp__sentry__get_sentry_resource, mcp__sentry__search_sentry_tools, mcp__sentry__execute_sentry_tool --- # Find what is actually firing @@ -22,7 +22,7 @@ confident, empty answer. Say which project you searched. "Is checkout broken?" becomes queries on what Sentry actually stores: the error type, the transaction or URL, the release. Prefer `search_issues` for -"what problems exist" and `search_events` for "how often / for whom / since +"what problems exist" and `search_errors` for "how often / for whom / since when". Run two or three narrow queries over one broad one, and time-bound them — `is:unresolved` over the last 24h answers a different question than all-time. @@ -33,7 +33,7 @@ The issues worth reporting are the ones with high event counts, many affected users, or a first-seen that matches a recent release. A brand-new issue with three events can matter more than a year-old one with thousands — say why the ones you picked matter. When a number is load-bearing (error rate, affected -users), take it from `search_events` rather than eyeballing the issue list. +users), take it from `search_errors` rather than eyeballing the issue list. ## 4. Answer with links and the time window diff --git a/plugins/stripe/plugin.json b/plugins/stripe/plugin.json index 833e2f0d7e6..e35b3e172d8 100644 --- a/plugins/stripe/plugin.json +++ b/plugins/stripe/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "stripe", - "version": "1.0.2", + "version": "1.0.3", "description": "Work with payments in Stripe: trace charges and subscriptions, read billing data, and search Stripe's docs.", "author": { "name": "Paradev", diff --git a/plugins/stripe/skills/refund-and-dispute/SKILL.md b/plugins/stripe/skills/refund-and-dispute/SKILL.md index 24aed678bde..95c47685db6 100644 --- a/plugins/stripe/skills/refund-and-dispute/SKILL.md +++ b/plugins/stripe/skills/refund-and-dispute/SKILL.md @@ -76,8 +76,9 @@ issuing a second one are both worse than a slow answer. A chargeback is the issuer taking the money back on the customer's word. Opening a refund on a disputed charge can cost you the amount twice and still lose the -dispute. Read the dispute, then either submit evidence through its `evidence` -fields or accept it deliberately. Say which one you did and why. +dispute. Read the dispute, then either submit evidence by updating its `evidence` +fields (with `submit: true`), or accept it deliberately by closing it in the +Dashboard; the MCP server cannot close a dispute. Say which one you did and why. ## 6. Report what actually changed diff --git a/plugins/superhuman/plugin.json b/plugins/superhuman/plugin.json index 26fae4c619a..658a176999b 100644 --- a/plugins/superhuman/plugin.json +++ b/plugins/superhuman/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://superset.sh/schemas/plugin/1.0.0.json", "name": "superhuman", - "version": "1.0.2", + "version": "1.0.3", "description": "Work your inbox from Superhuman: find and read threads, triage what needs you, draft replies in your voice, and schedule from your calendar.", "author": { "name": "Paradev", diff --git a/plugins/superhuman/skills/find-in-superhuman/SKILL.md b/plugins/superhuman/skills/find-in-superhuman/SKILL.md index 91d6d2462a3..3d531e2d610 100644 --- a/plugins/superhuman/skills/find-in-superhuman/SKILL.md +++ b/plugins/superhuman/skills/find-in-superhuman/SKILL.md @@ -19,6 +19,10 @@ otherwise. When the question could span accounts (a personal and a work inbox), run it once per account and say which one each result came from. An empty result from the wrong account looks exactly like a thread that does not exist. +Through Paradev each call also takes `superset_account`, which picks the +connection when there are several; `acting_email` picks a linked account +inside it. Pick the connection first. + ## 2. Filter on people and time before words `list_threads` takes `from`, `to`, `subject_contains`, `body_contains`,