[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CT5AQRDSG19SBJACXGPMY9":3,"skill-same:01M6CT5AQRDSG19SBJACXGPMY9":75},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":47,"linkOnly":48,"markdownContent":49,"markdownBody":50,"frontMatter":51,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":55,"repositoryHtmlUrl":56,"repositoryHomepageUrl":57,"ownerHtmlUrl":58,"ownerBlogUrl":59,"ownerDescription":60,"updatedAt":61,"install":62,"resources":74},"01M6CT5AQRDSG19SBJACXGPMY9","junior-qa",null,"Validate Junior changes through local app-facing paths. Use for local client or agent QA, dashboard mock reporting UI QA, sharing a preview URL of a running UI, Docker sandbox provider QA (also from inside a Vercel Sandbox), PR readiness, plugin CLI commands, skill\u002Ftool\u002Fprompt\u002Fplugin behavior, and behavior that tests do not cover well but can be exercised with `pnpm cli -- chat ...`, another `pnpm cli` command, or `JUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true pnpm dev`.","getsentry\u002Fjunior","getsentry","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1396951?v=4","01M6CHRN2S1A588Q46ASWA5335","Sentry","01M6CT1V0TF72B1X0MQT5MSYXF",418,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fskills\u002Fjunior-qa\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],"junior qa","junior","qa","running","pnpm","but","ui","cover","paths","commands","pr readiness","reporting","command","cli","dashboard","preview","URL","local","conversations","app","2026-11-10 10:12:30",false,"---\nname: junior-qa\ndescription: Validate Junior changes through local app-facing paths. Use for local client or agent QA, dashboard mock reporting UI QA, sharing a preview URL of a running UI, Docker sandbox provider QA (also from inside a Vercel Sandbox), PR readiness, plugin CLI commands, skill\u002Ftool\u002Fprompt\u002Fplugin behavior, and behavior that tests do not cover well but can be exercised with `pnpm cli -- chat ...`, another `pnpm cli` command, or `JUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true pnpm dev`.\n---\n\nUse the local Junior CLI to exercise behavior the test suite does not prove well.\nThe goal is to run the same app-facing path a developer or operator would use\nfrom `apps\u002Fexample`, inspect the result, and report concrete evidence.\n\nRead the package or module `AGENTS.md` that owns the changed behavior.\n\n## Running the Local CLI\n\nUse the repo wrapper so commands run from `apps\u002Fexample` with root and app env\nloaded. Pick the command and prompt that exercise the changed behavior; do not\ntreat any example prompt here as the required QA:\n\n```sh\npnpm cli -- chat -p \"\u003Ctargeted prompt>\"\n```\n\nFor agent behavior, prompts, skills, tools, and model-facing plugin behavior,\nuse `chat -p` or interactive `chat` with a prompt that naturally exercises the\nchange. A trivial exact-output prompt is only useful when the requested check is\nlimited to proving the local runner starts and delivers one response.\n\nFor host or plugin CLI behavior, call the command directly through the same\nwrapper:\n\n```sh\npnpm cli -- memory search --scope private --scope-key local:local-cli --limit 5\n```\n\nUse example app discovery probes when the change touches skill or plugin\ndiscovery:\n\n```sh\npnpm cli -- chat -p \"\u002Fexample-local confirm local QA discovery\"\npnpm cli -- chat -p \"\u002Fexample-bundle-help\"\n```\n\nHealthy startup usually logs `SOUL.md`, `WORLD.md`, loaded plugins, and\ndiscovered skills. Treat those logs as useful evidence that the example app path\nwas exercised.\n\n## Dashboard UI QA\n\nFor dashboard UI changes that depend on reporting payload shape, use the typed\nmock reporting overlay before relying on ad-hoc local conversations:\n\n```sh\nJUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true pnpm dev\n```\n\nThen open the dashboard in a browser and exercise the relevant conversation,\ntranscript, search, or conversation stats surface. The mock overlay returns\nread-only `@sentry\u002Fjunior\u002Freporting` conversation API-shaped data, including\ndashboard QA edge cases such as activity-only tool rows and inverted tool\ntimestamps. It also includes an advisor tool call\u002Fresult paired with advisor\nsubagent activity so transcript rendering can be checked against nested tool\nactivity without manufacturing a live agent run. Use it when a UI change needs\ndeterministic reporting records that are hard to produce through a live local\nchat. Plugin report data is pass-through from the configured reporting provider\nand needs separate validation.\n\nMessages sent from the composer or a Quick question do not run the agent. The\nmock shows a working Turn for 2 seconds, then a fixed reply. Sent messages\nstay until the dev server restarts.\n\nThe root dev command owns local auth-disabled defaults and the production-like\nNitro preset. If it fails to start, diagnose or report the dev runner failure;\ndo not change `apps\u002Fexample\u002Fnitro.config.ts`, dashboard auth settings, or OAuth\nconfiguration to make visual QA run.\n\nDo not treat mock dashboard data as proof of runtime ingestion, Slack delivery,\ncredential behavior, or model behavior. Pair it with local CLI or integration\ntests when the changed contract crosses those boundaries.\n\n### Share a Preview\n\nWhen a person wants to open the UI that you changed, send a preview URL:\n\n1. Push the branch, so a later Turn can make the preview again.\n2. Start the server in the background on all interfaces:\n   `(HOST=0.0.0.0 JUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true JUNIOR_DASHBOARD_LIVE_ORIGIN=https:\u002F\u002Fjunior-prod.sentry.dev JUNIOR_BOT_NAME=\"junior (Preview)\" setsid nohup pnpm dev > \u002Ftmp\u002Fdev.log 2>&1 &)`\n   `JUNIOR_BOT_NAME` replaces the `junior-example` name from `.env.example`\n   in the tab title.\n3. Wait until `curl -s -o \u002Fdev\u002Fnull -w '%{http_code}' localhost:3000\u002F` returns\n   `200`.\n4. Call `createPreview` with port `3000`, and send the URL.\n\nRules:\n\n- Serve mock data only. Do not give the server provider credentials. A viewer\n  can choose **Use live data** in the banner and connect it to their account.\n  Never ask for a token, and never put one in the Sandbox.\n- Live data uses the API of the live deployment. When the change touches an\n  API, tell the person that only mock data shows it.\n- After more edits, run the `curl` check first. If it returns `200`, do not\n  restart the server. It rebuilds `packages\u002Fjunior` edits by itself. It does\n  not rebuild the dashboard browser bundle, so after edits in\n  `packages\u002Fjunior-dashboard`, run\n  `pnpm --filter @sentry\u002Fjunior-dashboard run build:css && pnpm --filter @sentry\u002Fjunior-dashboard run build:client && pnpm --filter @sentry\u002Fjunior-dashboard run build:server`.\n  The server restarts on the new build. Then tell the person to refresh the\n  same URL.\n- When the person asks to refresh the preview after the Sandbox stopped, check\n  out the branch, start the server again, and send the new URL.\n\n## Docker Sandbox QA\n\nUse the Docker sandbox provider when the change touches sandbox, egress, or\nsnapshot behavior, or when no Vercel Sandbox credentials are available. Give\nthe settings to the process that starts Junior:\n\n```sh\nJUNIOR_SANDBOX_PROVIDER=docker pnpm dev\n```\n\nTo prove the provider itself, run its live test. It needs a Docker daemon, and\nthe first run builds the `junior-sandbox-runtime` image:\n\n```sh\nJUNIOR_DOCKER_SANDBOX_TEST=1 pnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Fdocker-sandbox-provider.test.ts\n```\n\nA Docker sandbox has a small toolset: `gh`, `jq`, and `rg`. It does not have\nother plugin CLIs, browsers, or Docker. Install a missing tool with `sudo`, for\nexample `sudo npm i -g vercel`. A plain `npm i -g` fails with `EACCES`. Do not\nQA browser or Docker-in-sandbox behavior with this provider.\n\n### Inside a Vercel Sandbox\n\nA Vercel Sandbox sends some HTTPS traffic through a TLS proxy. For example,\n`ai-gateway.vercel.sh` gets a certificate from `Vercel Network Proxy CA`. A\nDocker sandbox inside the Vercel Sandbox has its own trust store. It does not\ntrust that CA, so its requests to those hosts fail certificate checks.\n\nBefore you start Junior, find out if you are in this environment:\n\n```sh\nopenssl x509 -noout -subject \\\n  -in \u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem\n```\n\nIf the subject is `CN=Vercel Network Proxy CA`, give that one file to the\nprocess as the extra CA:\n\n```sh\nJUNIOR_SANDBOX_PROVIDER=docker \\\nJUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS=\u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem \\\n  pnpm dev\n```\n\nUse the same settings for `pnpm cli -- chat ...` and the live provider test.\n\nRules:\n\n- Set `JUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS` only when the file exists and has\n  that subject. On a developer workstation, do not set it, unless that machine\n  has its own TLS proxy.\n- Do not write the setting to `.env`, `.env.local`, or other repo files. It is\n  correct only for this environment.\n- Give the proxy CA file, not the full system CA bundle. Junior always adds its\n  own egress CA, and the Docker sandbox keeps its public CAs.\n\n### Prewarm the Sandbox Snapshot\n\nThe first sandbox builds a dependency snapshot. This build takes about 1\nminute and makes an image of about 840 MB. Later sandboxes start from that\nsnapshot in a few seconds. Build the snapshot before the first QA turn, so the\nturn does not wait for it.\n\nAfter you pull new commits, build all packages before you start the dev\nserver. `pnpm dev` rebuilds only core and the dashboard, so other plugin\npackages can keep old builds:\n\n```sh\npnpm build\n```\n\nStart `pnpm dev` with the Docker settings. When the dev server is ready, run\n`snapshot create` with the same state settings as the dev server. With the\nBullMQ queue, the dev server keeps the snapshot pointer in the Compose Redis.\n`pnpm cli` uses memory state by default, so it cannot find that pointer:\n\n```sh\nJUNIOR_STATE_ADAPTER=redis \\\nREDIS_URL=redis:\u002F\u002Flocalhost:6382 \\\nJUNIOR_SANDBOX_PROVIDER=docker \\\nJUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS=\u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem \\\n  pnpm cli -- snapshot create\n```\n\nOn a developer workstation, remove the\n`JUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS` line. Use the rules in \"Inside a Vercel\nSandbox\".\n\nThe last output line shows the result. `cache_hit=true` means the snapshot was\nalready ready. `resolve_outcome=rebuilt` means the command built it.\n\nRules:\n\n- Set the state settings only for this command. Do not change the defaults of\n  `pnpm cli`. Local chat uses memory state, so it works without Redis.\n- If you run the command without the Redis settings, it builds a second\n  snapshot that the dev server does not use.\n- A snapshot stays valid until its dependency list changes or its image is\n  deleted.\n\n## Choosing a Probe\n\nPick the smallest local CLI run that demonstrates the changed behavior:\n\n- Prefer a targeted prompt or direct CLI command tied to the modified feature.\n- Use exact-output prompts for simple agent routing or prompt-context checks.\n- Use natural-language prompts when the behavior is an agent\u002Ftool workflow.\n- Use direct plugin commands when the behavior is an operator CLI surface.\n- Use interactive `pnpm cli -- chat` when continuity across turns matters.\n- Use dashboard mock reporting when the behavior is dashboard rendering,\n  filtering, search, or metrics over reporting API payloads.\n- Do not use local CLI to claim Slack-only behavior, such as Slack formatting,\n  delivery retries, reactions, files, or OAuth UI.\n\nAutomated tests, typechecks, linters, and evals are separate validation. They do\nnot replace local QA evidence from running the client or agent.\n\n## OAuth Flow QA (MCP and Plugin)\n\nJunior has two OAuth pause\u002Fresume flows:\n\n- Plugin (non-MCP) OAuth: sandbox egress `auth_required` signal resumes via\n  `\u002Fapi\u002Foauth\u002Fcallback\u002F\u003Cprovider>`. In `apps\u002Fexample` the `sentry` plugin is\n  the OAuth-manifest provider (`SENTRY_CLIENT_ID`\u002F`SENTRY_CLIENT_SECRET`).\n- MCP OAuth: a remote MCP server 401 challenge resumes via\n  `\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002F\u003Cprovider>`. In `apps\u002Fexample` the `linear`,\n  `notion`, and `hex` plugins use remote MCP URLs.\n\nThe local CLI prints the private authorization URL, waits for the provider\ncallback, and resumes the same turn as the `local-cli` user. Keep `pnpm dev`\nrunning so the configured public callback and tunnel can verify the signed\nlocal state and redirect the browser to the CLI's loopback listener.\n\nBefore starting the chat command:\n\n1. Keep one `pnpm dev` process running in the foreground.\n2. Confirm only one Cloudflare tunnel for this development hostname is active.\n3. Probe both callback hops. Each request should reach Junior and return `400`\n   because the probe intentionally omits OAuth state:\n\n```sh\ncurl -sS -o \u002Fdev\u002Fnull -w '%{http_code}\\n' \\\n  http:\u002F\u002F127.0.0.1:3000\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002Flinear\ncurl -sS -o \u002Fdev\u002Fnull -w '%{http_code}\\n' \\\n  https:\u002F\u002Fjunior-dev.sentry.cool\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002Flinear\n```\n\nDo not present an authorization URL until both probes return `400`. A public\n`502` means the tunnel is running without a reachable local server; restart the\ndev command and re-run both probes before creating a fresh OAuth attempt.\n\n```sh\npnpm cli -- chat -p \"Use the linear skill to list Linear teams.\"\n```\n\nExpect an authorization URL on stderr. Complete the provider flow in a browser;\nthe same CLI process should then continue the blocked request and print its\nreply without requiring the prompt again.\n\nUse the integration tests as the check for local resume behavior and for the\ncallback route. Use the auth integration evals as the check for Slack and\ndashboard resume behavior:\n\n```sh\npnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Flocal-agent-runner.test.ts\npnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Foauth-callback.test.ts\npnpm --filter @sentry\u002Fjunior-evals evals:integration evals\u002Fintegration\u002Fauth\n```\n\nFor SQL conversation storage changes, verify the resumed turn rebuilds context\nfrom SQL, not `thread-state` mirrors: conversation context must hydrate from\n`junior_conversation_messages` (`hydrateConversationMessages`) and pi history\nfrom `junior_conversation_events` through the Pi adapter (`loadProjection`). In\nthose tests, transcripts seeded only into `thread-state` must also be persisted\nto SQL (`persistConversationMessages`) before the callback runs, and the\nresumed agent-run input `conversationContext` must contain the SQL-seeded\nmessages.\n\n## Failure Handling\n\nIf local chat fails because credentials are missing or expired, refresh the\nenvironment when appropriate with `pnpm dev:env`, then rerun the same command.\nIf local chat fails with a `junior_conversation_messages` or\n`junior_conversation_events` query error, the local Postgres schema predates the\nSQL conversation storage cutover; run `pnpm cli -- upgrade`, then rerun.\nIf commands in a Docker sandbox fail certificate checks for a host that the\nVercel Sandbox proxies, the extra CA is not set. See \"Inside a Vercel Sandbox\".\nIf startup fails with `Junior plugin \"\u003Cname>\" events is invalid` or a similar\nplugin error after a pull, the plugin packages have old builds. Run\n`pnpm build`, then start the dev server again.\nIf the first sandbox command waits for about a minute, the dependency snapshot\nis being built. See \"Prewarm the Sandbox Snapshot\".\nIf Redis errors appear during ordinary local QA, check whether\n`JUNIOR_STATE_ADAPTER=redis` was set; local chat normally defaults to memory\nstate.\nIf OAuth returns `502`, verify port 3000 is listening and remove stale duplicate\ndev servers or tunnel clients before retrying. If the provider says the\nauthorization expired, discard that URL and start a fresh chat attempt after\nthe callback health checks pass.\n\nIf the model answer is too loose to prove the behavior, use a narrower prompt,\nan exact-output prompt, interactive mode, or a direct plugin CLI command. If the\nbehavior cannot be exercised through the local client\u002Fagent, say local QA is\ninsufficient and name the runtime surface that still needs manual coverage.\n\n## Reporting\n\nReport:\n\n- the exact `pnpm cli -- ...` commands run\n- for dashboard mock QA, the dev-server command, URL, mock conversation or page\n  inspected, and the visible UI evidence\n- exit status and the key output that proves the behavior\n- whether `apps\u002Fexample` loaded the expected app\u002Fplugin\u002Fskill path\n- whether local QA was sufficient, or what remains unproven locally\n\nKeep any automated test\u002Flint\u002Ftypecheck\u002Feval results in a separate validation\nsection so they are not confused with local QA.\n","Use the local Junior CLI to exercise behavior the test suite does not prove well.\nThe goal is to run the same app-facing path a developer or operator would use\nfrom `apps\u002Fexample`, inspect the result, and report concrete evidence.\n\nRead the package or module `AGENTS.md` that owns the changed behavior.\n\n## Running the Local CLI\n\nUse the repo wrapper so commands run from `apps\u002Fexample` with root and app env\nloaded. Pick the command and prompt that exercise the changed behavior; do not\ntreat any example prompt here as the required QA:\n\n```sh\npnpm cli -- chat -p \"\u003Ctargeted prompt>\"\n```\n\nFor agent behavior, prompts, skills, tools, and model-facing plugin behavior,\nuse `chat -p` or interactive `chat` with a prompt that naturally exercises the\nchange. A trivial exact-output prompt is only useful when the requested check is\nlimited to proving the local runner starts and delivers one response.\n\nFor host or plugin CLI behavior, call the command directly through the same\nwrapper:\n\n```sh\npnpm cli -- memory search --scope private --scope-key local:local-cli --limit 5\n```\n\nUse example app discovery probes when the change touches skill or plugin\ndiscovery:\n\n```sh\npnpm cli -- chat -p \"\u002Fexample-local confirm local QA discovery\"\npnpm cli -- chat -p \"\u002Fexample-bundle-help\"\n```\n\nHealthy startup usually logs `SOUL.md`, `WORLD.md`, loaded plugins, and\ndiscovered skills. Treat those logs as useful evidence that the example app path\nwas exercised.\n\n## Dashboard UI QA\n\nFor dashboard UI changes that depend on reporting payload shape, use the typed\nmock reporting overlay before relying on ad-hoc local conversations:\n\n```sh\nJUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true pnpm dev\n```\n\nThen open the dashboard in a browser and exercise the relevant conversation,\ntranscript, search, or conversation stats surface. The mock overlay returns\nread-only `@sentry\u002Fjunior\u002Freporting` conversation API-shaped data, including\ndashboard QA edge cases such as activity-only tool rows and inverted tool\ntimestamps. It also includes an advisor tool call\u002Fresult paired with advisor\nsubagent activity so transcript rendering can be checked against nested tool\nactivity without manufacturing a live agent run. Use it when a UI change needs\ndeterministic reporting records that are hard to produce through a live local\nchat. Plugin report data is pass-through from the configured reporting provider\nand needs separate validation.\n\nMessages sent from the composer or a Quick question do not run the agent. The\nmock shows a working Turn for 2 seconds, then a fixed reply. Sent messages\nstay until the dev server restarts.\n\nThe root dev command owns local auth-disabled defaults and the production-like\nNitro preset. If it fails to start, diagnose or report the dev runner failure;\ndo not change `apps\u002Fexample\u002Fnitro.config.ts`, dashboard auth settings, or OAuth\nconfiguration to make visual QA run.\n\nDo not treat mock dashboard data as proof of runtime ingestion, Slack delivery,\ncredential behavior, or model behavior. Pair it with local CLI or integration\ntests when the changed contract crosses those boundaries.\n\n### Share a Preview\n\nWhen a person wants to open the UI that you changed, send a preview URL:\n\n1. Push the branch, so a later Turn can make the preview again.\n2. Start the server in the background on all interfaces:\n   `(HOST=0.0.0.0 JUNIOR_DASHBOARD_MOCK_CONVERSATIONS=true JUNIOR_DASHBOARD_LIVE_ORIGIN=https:\u002F\u002Fjunior-prod.sentry.dev JUNIOR_BOT_NAME=\"junior (Preview)\" setsid nohup pnpm dev > \u002Ftmp\u002Fdev.log 2>&1 &)`\n   `JUNIOR_BOT_NAME` replaces the `junior-example` name from `.env.example`\n   in the tab title.\n3. Wait until `curl -s -o \u002Fdev\u002Fnull -w '%{http_code}' localhost:3000\u002F` returns\n   `200`.\n4. Call `createPreview` with port `3000`, and send the URL.\n\nRules:\n\n- Serve mock data only. Do not give the server provider credentials. A viewer\n  can choose **Use live data** in the banner and connect it to their account.\n  Never ask for a token, and never put one in the Sandbox.\n- Live data uses the API of the live deployment. When the change touches an\n  API, tell the person that only mock data shows it.\n- After more edits, run the `curl` check first. If it returns `200`, do not\n  restart the server. It rebuilds `packages\u002Fjunior` edits by itself. It does\n  not rebuild the dashboard browser bundle, so after edits in\n  `packages\u002Fjunior-dashboard`, run\n  `pnpm --filter @sentry\u002Fjunior-dashboard run build:css && pnpm --filter @sentry\u002Fjunior-dashboard run build:client && pnpm --filter @sentry\u002Fjunior-dashboard run build:server`.\n  The server restarts on the new build. Then tell the person to refresh the\n  same URL.\n- When the person asks to refresh the preview after the Sandbox stopped, check\n  out the branch, start the server again, and send the new URL.\n\n## Docker Sandbox QA\n\nUse the Docker sandbox provider when the change touches sandbox, egress, or\nsnapshot behavior, or when no Vercel Sandbox credentials are available. Give\nthe settings to the process that starts Junior:\n\n```sh\nJUNIOR_SANDBOX_PROVIDER=docker pnpm dev\n```\n\nTo prove the provider itself, run its live test. It needs a Docker daemon, and\nthe first run builds the `junior-sandbox-runtime` image:\n\n```sh\nJUNIOR_DOCKER_SANDBOX_TEST=1 pnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Fdocker-sandbox-provider.test.ts\n```\n\nA Docker sandbox has a small toolset: `gh`, `jq`, and `rg`. It does not have\nother plugin CLIs, browsers, or Docker. Install a missing tool with `sudo`, for\nexample `sudo npm i -g vercel`. A plain `npm i -g` fails with `EACCES`. Do not\nQA browser or Docker-in-sandbox behavior with this provider.\n\n### Inside a Vercel Sandbox\n\nA Vercel Sandbox sends some HTTPS traffic through a TLS proxy. For example,\n`ai-gateway.vercel.sh` gets a certificate from `Vercel Network Proxy CA`. A\nDocker sandbox inside the Vercel Sandbox has its own trust store. It does not\ntrust that CA, so its requests to those hosts fail certificate checks.\n\nBefore you start Junior, find out if you are in this environment:\n\n```sh\nopenssl x509 -noout -subject \\\n  -in \u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem\n```\n\nIf the subject is `CN=Vercel Network Proxy CA`, give that one file to the\nprocess as the extra CA:\n\n```sh\nJUNIOR_SANDBOX_PROVIDER=docker \\\nJUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS=\u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem \\\n  pnpm dev\n```\n\nUse the same settings for `pnpm cli -- chat ...` and the live provider test.\n\nRules:\n\n- Set `JUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS` only when the file exists and has\n  that subject. On a developer workstation, do not set it, unless that machine\n  has its own TLS proxy.\n- Do not write the setting to `.env`, `.env.local`, or other repo files. It is\n  correct only for this environment.\n- Give the proxy CA file, not the full system CA bundle. Junior always adds its\n  own egress CA, and the Docker sandbox keeps its public CAs.\n\n### Prewarm the Sandbox Snapshot\n\nThe first sandbox builds a dependency snapshot. This build takes about 1\nminute and makes an image of about 840 MB. Later sandboxes start from that\nsnapshot in a few seconds. Build the snapshot before the first QA turn, so the\nturn does not wait for it.\n\nAfter you pull new commits, build all packages before you start the dev\nserver. `pnpm dev` rebuilds only core and the dashboard, so other plugin\npackages can keep old builds:\n\n```sh\npnpm build\n```\n\nStart `pnpm dev` with the Docker settings. When the dev server is ready, run\n`snapshot create` with the same state settings as the dev server. With the\nBullMQ queue, the dev server keeps the snapshot pointer in the Compose Redis.\n`pnpm cli` uses memory state by default, so it cannot find that pointer:\n\n```sh\nJUNIOR_STATE_ADAPTER=redis \\\nREDIS_URL=redis:\u002F\u002Flocalhost:6382 \\\nJUNIOR_SANDBOX_PROVIDER=docker \\\nJUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS=\u002Fetc\u002Fpki\u002Fca-trust\u002Fsource\u002Fanchors\u002Fvercel-proxy-ca.pem \\\n  pnpm cli -- snapshot create\n```\n\nOn a developer workstation, remove the\n`JUNIOR_DOCKER_SANDBOX_EXTRA_CA_CERTS` line. Use the rules in \"Inside a Vercel\nSandbox\".\n\nThe last output line shows the result. `cache_hit=true` means the snapshot was\nalready ready. `resolve_outcome=rebuilt` means the command built it.\n\nRules:\n\n- Set the state settings only for this command. Do not change the defaults of\n  `pnpm cli`. Local chat uses memory state, so it works without Redis.\n- If you run the command without the Redis settings, it builds a second\n  snapshot that the dev server does not use.\n- A snapshot stays valid until its dependency list changes or its image is\n  deleted.\n\n## Choosing a Probe\n\nPick the smallest local CLI run that demonstrates the changed behavior:\n\n- Prefer a targeted prompt or direct CLI command tied to the modified feature.\n- Use exact-output prompts for simple agent routing or prompt-context checks.\n- Use natural-language prompts when the behavior is an agent\u002Ftool workflow.\n- Use direct plugin commands when the behavior is an operator CLI surface.\n- Use interactive `pnpm cli -- chat` when continuity across turns matters.\n- Use dashboard mock reporting when the behavior is dashboard rendering,\n  filtering, search, or metrics over reporting API payloads.\n- Do not use local CLI to claim Slack-only behavior, such as Slack formatting,\n  delivery retries, reactions, files, or OAuth UI.\n\nAutomated tests, typechecks, linters, and evals are separate validation. They do\nnot replace local QA evidence from running the client or agent.\n\n## OAuth Flow QA (MCP and Plugin)\n\nJunior has two OAuth pause\u002Fresume flows:\n\n- Plugin (non-MCP) OAuth: sandbox egress `auth_required` signal resumes via\n  `\u002Fapi\u002Foauth\u002Fcallback\u002F\u003Cprovider>`. In `apps\u002Fexample` the `sentry` plugin is\n  the OAuth-manifest provider (`SENTRY_CLIENT_ID`\u002F`SENTRY_CLIENT_SECRET`).\n- MCP OAuth: a remote MCP server 401 challenge resumes via\n  `\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002F\u003Cprovider>`. In `apps\u002Fexample` the `linear`,\n  `notion`, and `hex` plugins use remote MCP URLs.\n\nThe local CLI prints the private authorization URL, waits for the provider\ncallback, and resumes the same turn as the `local-cli` user. Keep `pnpm dev`\nrunning so the configured public callback and tunnel can verify the signed\nlocal state and redirect the browser to the CLI's loopback listener.\n\nBefore starting the chat command:\n\n1. Keep one `pnpm dev` process running in the foreground.\n2. Confirm only one Cloudflare tunnel for this development hostname is active.\n3. Probe both callback hops. Each request should reach Junior and return `400`\n   because the probe intentionally omits OAuth state:\n\n```sh\ncurl -sS -o \u002Fdev\u002Fnull -w '%{http_code}\\n' \\\n  http:\u002F\u002F127.0.0.1:3000\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002Flinear\ncurl -sS -o \u002Fdev\u002Fnull -w '%{http_code}\\n' \\\n  https:\u002F\u002Fjunior-dev.sentry.cool\u002Fapi\u002Foauth\u002Fcallback\u002Fmcp\u002Flinear\n```\n\nDo not present an authorization URL until both probes return `400`. A public\n`502` means the tunnel is running without a reachable local server; restart the\ndev command and re-run both probes before creating a fresh OAuth attempt.\n\n```sh\npnpm cli -- chat -p \"Use the linear skill to list Linear teams.\"\n```\n\nExpect an authorization URL on stderr. Complete the provider flow in a browser;\nthe same CLI process should then continue the blocked request and print its\nreply without requiring the prompt again.\n\nUse the integration tests as the check for local resume behavior and for the\ncallback route. Use the auth integration evals as the check for Slack and\ndashboard resume behavior:\n\n```sh\npnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Flocal-agent-runner.test.ts\npnpm --filter @sentry\u002Fjunior exec vitest run tests\u002Fintegration\u002Foauth-callback.test.ts\npnpm --filter @sentry\u002Fjunior-evals evals:integration evals\u002Fintegration\u002Fauth\n```\n\nFor SQL conversation storage changes, verify the resumed turn rebuilds context\nfrom SQL, not `thread-state` mirrors: conversation context must hydrate from\n`junior_conversation_messages` (`hydrateConversationMessages`) and pi history\nfrom `junior_conversation_events` through the Pi adapter (`loadProjection`). In\nthose tests, transcripts seeded only into `thread-state` must also be persisted\nto SQL (`persistConversationMessages`) before the callback runs, and the\nresumed agent-run input `conversationContext` must contain the SQL-seeded\nmessages.\n\n## Failure Handling\n\nIf local chat fails because credentials are missing or expired, refresh the\nenvironment when appropriate with `pnpm dev:env`, then rerun the same command.\nIf local chat fails with a `junior_conversation_messages` or\n`junior_conversation_events` query error, the local Postgres schema predates the\nSQL conversation storage cutover; run `pnpm cli -- upgrade`, then rerun.\nIf commands in a Docker sandbox fail certificate checks for a host that the\nVercel Sandbox proxies, the extra CA is not set. See \"Inside a Vercel Sandbox\".\nIf startup fails with `Junior plugin \"\u003Cname>\" events is invalid` or a similar\nplugin error after a pull, the plugin packages have old builds. Run\n`pnpm build`, then start the dev server again.\nIf the first sandbox command waits for about a minute, the dependency snapshot\nis being built. See \"Prewarm the Sandbox Snapshot\".\nIf Redis errors appear during ordinary local QA, check whether\n`JUNIOR_STATE_ADAPTER=redis` was set; local chat normally defaults to memory\nstate.\nIf OAuth returns `502`, verify port 3000 is listening and remove stale duplicate\ndev servers or tunnel clients before retrying. If the provider says the\nauthorization expired, discard that URL and start a fresh chat attempt after\nthe callback health checks pass.\n\nIf the model answer is too loose to prove the behavior, use a narrower prompt,\nan exact-output prompt, interactive mode, or a direct plugin CLI command. If the\nbehavior cannot be exercised through the local client\u002Fagent, say local QA is\ninsufficient and name the runtime surface that still needs manual coverage.\n\n## Reporting\n\nReport:\n\n- the exact `pnpm cli -- ...` commands run\n- for dashboard mock QA, the dev-server command, URL, mock conversation or page\n  inspected, and the visible UI evidence\n- exit status and the key output that proves the behavior\n- whether `apps\u002Fexample` loaded the expected app\u002Fplugin\u002Fskill path\n- whether local QA was sufficient, or what remains unproven locally\n\nKeep any automated test\u002Flint\u002Ftypecheck\u002Feval results in a separate validation\nsection so they are not confused with local QA.\n",{"name":5,"description":7},"main","fffdb7966ca9b37ada1d76615f20f8535f446584","skills\u002Fjunior-qa",45,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior","https:\u002F\u002Fjunior.sentry.dev","https:\u002F\u002Fgithub.com\u002Fgetsentry","https:\u002F\u002Fsentry.io","Real-time crash reporting for your web apps, mobile apps, and games.","2026-11-13 03:38:20",{"source":8,"skillName":5,"commands":63,"prompt":72,"zipAvailable":73},[64,67,70],{"tool":65,"command":66},"npx","npx skills add getsentry\u002Fjunior --skill junior-qa",{"tool":68,"command":69},"bunx","bunx skills add getsentry\u002Fjunior --skill junior-qa",{"tool":31,"command":71},"pnpm dlx skills add getsentry\u002Fjunior --skill junior-qa","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fskills\u002Fjunior-qa\u002FSKILL.md",true,[],[76,104,127,151,170,195],{"skillNo":77,"name":28,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":82,"publishedAt":47},"01M6CT5E87W8P93FGAVYDWD6RD","Build, review, or update Junior extension skills and plugins. Use when users ask to create a Junior SKILL.md, app skill, plugin, plugin.yaml, packaged plugin, MCP-backed plugin, OAuth or credentialed plugin, or to validate Junior extension files. Do not use for ordinary Junior usage, provider workflows, generic code editing, or non-Junior agent-skill authoring.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fskills\u002Fjunior\u002FSKILL.md",[81],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[28,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,46,99,100,101,102,103],"do","provider","editing","skill authoring","files","MCP","use","OAuth","workflows","ask","code","review","create","plugin","build","update","usage","users","generic","for","when",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":111,"publishedAt":47},"01M6CT58D1SJNFZ19ZXK64ZX0R","eval-auth","Use for `\u002Feval-auth` requests in MCP auth-resume integration tests. Always connect through the disclosed MCP tool before answering, then continue the user's actual request using prior thread context when needed.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fpackages\u002Fjunior\u002Ftests\u002Ffixtures\u002Fplugins\u002Feval-auth\u002Fskills\u002Feval-auth\u002FSKILL.md",[110],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[112,113,114,115,116,117,118,119,88,89,120,121,122,123,102,124,125,126,103],"eval auth","auth","eval","resume","integration tests","tool","integration","continue","connect","user","context","requests","request","tests","thread",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":133,"publishedAt":47},"01M6CT55MZKCVBJH2X7T1HCRPX","jr-rpc","Manage low-level config and plugin introspection flows via jr-rpc bash commands. Use only when the user explicitly asks to read or update provider defaults\u002Fconfig or list installed plugins. Do not use for PR, branch, push, or auth-order questions; load the matching provider skill instead.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fpackages\u002Fjunior\u002Fskills\u002Fjr-rpc\u002FSKILL.md",[],[134,135,83,136,96,137,138,139,113,84,140,98,141,142,143,144,145,89,146,121,102,147,148,103,149,150],"jr rpc","rpc","branch","push","pr","introspection","manage","read","config","plugins","only","list","Bash","flows","load","via","questions",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":47},"01M6PQN1EZPHRJ5XAJP2J3ZS2T","xdotcom","Read public X (Twitter) data through the read-only X API v2. Use when users share an x.com or twitter.com link, or ask what someone posted on X, what people are saying about a topic, product, or launch on X, how much chatter something got, or for a post's text, metrics, replies, or an account's recent posts. Do not use it to post, reply, like, repost, follow, or send DMs, or to read private or protected data.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fpackages\u002Fjunior-xdotcom\u002Fskills\u002Fxdotcom\u002FSKILL.md",[],[153,83,158,102,159,160,161,162,144,163,89,164,92,146,165,166,167,100,168,169,103],"posts","read-only","x api","send","twitter","chatter","people","api","share","replies","text","post",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":47},"01M6CT5386D90KE6YH8Q8WGQZ0","vercel","Deploy and inspect Vercel apps, assign aliases, delete deployments, investigate logs, and monitor deployment outcomes. Use for Vercel deployment and log requests, not GitHub code changes or other cloud providers.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fpackages\u002Fjunior-vercel\u002Fskills\u002Fvercel\u002FSKILL.md",[],[172,177,178,179,180,181,182,183,184,185,186,89,187,93,188,189,190,102,191,192,193,194,123],"investigate","delete","outcomes","github","deployment","monitor","vercel deployment","github code","deployments","cloud","deploy","Providers","logs","log","inspect","assign","apps","changes",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":199,"categories":200,"keywords":204,"publishedAt":47},"01M6CT503YFHNM9YZRDRD59WGC","tts","Generates spoken narration with Gemini TTS through Vercel AI Gateway. Use for text-to-speech, audio samples, voiceovers, or speaking as Junior with the reusable Junior voice. Do not use for transcription, voice cloning, music generation, or rendering a video without a narration request.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fjunior\u002Fblob\u002Fmain\u002Fpackages\u002Fjunior-tts\u002Fskills\u002Ftts\u002FSKILL.md",[201],{"categoryNo":202,"categoryName":203,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[197,168,205,206,83,207,208,209,210,211,212,172,213,214,89,215,216,217,102,218,219,220,28,221],"Video","gateway","ai gateway","Audio","speech","narration","gemini","cloning","voice","transcription","reusable","music","as","text-to-speech","with","music generation","generation"]